The final step in Prg3.s, before setting an exit code and returning at the bottom, is to store the result in r3 back into valA. Lines 39 and 40 accomplish this:
ldr R2, =valA @ Create and load a .word containing address valA str R3, [r2] @ Store valA = R3
How they do it deserves careful discussion, and introduces a couple of important new ideas.
A first reasonable question would be: why not just use the same arrangement as on line 15, and write this?
str R3, valA
That worked well enough for the ldr, and you recall that it operated by offsetting from the PC automatically. Given that valA is just a few lines down from the instruction, why wouldn't pc offset work?
Because in fact valA is millions of addresses away from the instruction.
It's time to talk about the assembler directives .text and .data on lines 1 and 46. These declare memory segments. The working memory of a program is divided into different areas or "segments". Typically, one area of memory is devoted to code, and to constant data values associated with the code. That's the text segment. Line 1 announces that the data and code that follow line 1 should go in the text segment.
But a different area of memory, the data segment, is reserved for changeable data, like valA. The .data assembly directive on line 46 declares that the valA variable is in the data segment.
Thus, valA is nowhere near the code at the end of the program, and pc-relative addressing won't work for the str.
Why didn't we just put valA in the text segment? Because the text segment is also marked read only.
Every modern computer has hardware called a memory management unit or MMU that allows the operating system to mark some areas of memory as read only. (It does a lot of other things too, which we'll get into later.) It makes sense to mark the text segment read only because code should not be modified while running. And any data that is constant should also go in the text segment. It's easier to load it from there via pc-relative ldr instructions, and you don't want any instruction to change the constant data, even accidentally. Note that valB, numStr, and numStrLoc are all treated as constant values -- the code never modified them.
Let's try modifying some of the .text data, say by changing line 20 from a ldr to a str. We'll assemble it and run it...
And the result is a segmentation fault. This is what happens any time we do something the MMU doesn't like, including modifying read-only memory segments.
Since we can't reach valA via a pc-relative address, the only choice is to put valA's address into a register (r2) so we can use it as the address register in a str. Line 39 does just that.
ldr R2, =valA @ Create and load a .word holding the address valA
The = before valA says to load the address that the label stands for, not the data at that address. Think of the = as saying "literally the label". (In C terms, it's like putting an & before a variable name to get the address of the variable.)
But, wait a second. Line 39 is strange on at least two counts. First, it's not loading anything from memory, despite being a ldr instruction. It's effectively copying a constant into r2. And second, the value in question is 32 bits long, which once again cannot fit into a machine instruction. We saw the same thing when loading r1 with the address of numStr on line 20. The only way to make it work was to put the value of the numStr label into a memory location itself, and load it. But now we can just "load an address"?
In fact, the approach used on line 20 is the only one that works: make a memory location with the desired address in it, and load that location to get the address into the register. Line 39 is yet another pseudoinstruction. When it sees =valA, the assembler automatically creates a memory location containing valA's location, just like the one we explicitly declared as numStrLoc earlier. It then rewrites the ldr instruction to load the address from that location, just as our line 20 does. Line 39 thus translates into:
ldr R2, valALoc @ Actually ldr R2, [pc, #20] str R3, [r2] @ valA = R3 mov R0, R3 @ Write value as exit code, too mov R7, #1 SWI 0 valALoc: .word valA .data valA: .word 0
That's a pretty sophisticated rewrite. To be clear, it:
Go over this carefully to be sure you understand it. It's the most complicated pseudoinstruction we'll use in the class, but it comes up any time we want to load an address.
So, all that extra work with numStrLoc earlier wasn't needed, apparently. Simplify the earlier code by revising line 20, and dropping two other lines.
Answer 1
Would this change make the machine language translation shorter? Would there be fewer data words in the translation? Why or why not?
Answer 2
When a location containing the address of valA is automatically created, to support the pseudoinstruction ldr r2, =valA, would the assembler put it in the text segment or the data segment? Why? There are two good reasons.
Answer 3
valA, also need to be near the current instruction for all this to work? Why or why not?
Answer 4
valALoc word.
Now that we have the address of valA in r2, the rest is easy. Line 40 stores the result we've computed, and the final three instructions return that value as the exit code.
Running the program, we find that the exit code is 103, confirming that the computations were correct.