In this segment we'll continue our discussion of ARM instruction design, focusing on instructions for loading/storing registers.
The format for ldr and str is on the line of the instruction format chart entitled Single Data Transfer. Several things are worth noting:
cond field like every other instruction and can be done conditionally.Let's start by looking at those six 1-bit flags. All of them relate to choices we can make in the assembly instructions for ldr or str
First, the L flag determines whether the instruction is a load (1) or a store (0). In a sense, it's like the opcode field in the data processing instructions, but with just two choices.
Second, as we saw in an earlier example there are strb and ldrb variants that load only one byte. The B bit is set to indicate loading of just one byte.
Third, offset indexing could be positive or negative relative to the address register, but the offset field is purely positive. So, a 1 in the U bit indicates a positive ("Upwards") and a 0 indicates a negative ("downwards") offset. The offset is like a sign-magnitude value, with U as the sign bit, except that 1 indicates positive.
Fourth, we saw that the offset could be optionally applied to adjust the value in the address register (indicated in assembly with a ! or with a post-increment like ldr r0 [sp], #4). The W bit ("Writeback") indicates this.
Fifth, the writeback can be done as a preincrement or a postincrement (inside or outside the brackets). The P ("Preincrement") flag determines this.
And, sixth, the I flag indicates two different interpretations of the offset, either as a simple 12-bit unsigned integer (I = 1, for a range of 0 to 4095, with the U bit making it positive or negative) or as a register-and-shift value, as in the data processing operand2. We haven't seen this in code yet, but we will later in the optimized primes program. Here's an example:
ldr r2, [sp, r1, asl #2]
This loads r2 from the location indicated by sp, but offset by the value in r1, shifted left by 2 bits. The offset in this case works like operand2 in the data processing instructions and its format is exactly the same.
Question 1
What would those six bits (in order IPUBWL) be for this instruction? strb r3, [fp, #-12]!
Answer 1
The load/store multiple instruction format (labelled Block Data Transfer on most charts) is a close relative of the ldr/str format. The instructions stmfd, ldmfd and their cousins have this format. The questions that follow will ask you to analyze this format, based on what you already know about such instructions. You might want to review the discussion in ARMAssemblyInt:RTS.
Question 2
Consider all the variants of block transfer instructions. stmfd sp!, {...}, ldmea sp!, {...}, stmed fp, {...}. Describe as many two-choice (1 bit) options for this category of instructions that you can. You should be able to come up with four in all.
Answer 2
Question 3 Examine the diagram for load/store multiple instructions, and make a reasonable guess as to what four of those 1-bit fields signify. The fifth will be hard to guess -- we'll discuss it briefly below. Also note that an "empty stack" is effectively a post-increment, while a full stack is effectively a pre-increment.
Answer 3
This leaves the S, or in some charts B, flag. We'll discuss privilege levels in a module on OS concepts later, and this flag will be worth remembering then. Very briefly, the flag loads or stores the PSR, which includes changing privileged state. It's used by the operating system when handling a system call or returning control to a user-written function.
Question 4 The ldr/str format included an I, and a B flag (with a different meaning). Why are those missing in the multiple load/store format?
Answer 4
The 12 bits that were devoted to that offset, and also the four bits that were used for destination register, are used instead to indicate which register(s) to load or store. There is one bit for each register. Of those 16 bits, the LSB indicates register 0, and the MSB indicates r15 or pc.
Question 5
stmfd and ldmfd don't let you specify the order in which registers are stored. They must be stored in register-number order. Why is this? What would it take to make it possible to specify any register order you want, and is this worth it?
Answer 5
We've seen plenty of uses of stmfd and ldmfd to create and tear down stack frames. But, the instructions are also designed for another purpose.
Question 6
What does this code do?
ldr r10, =place1
ldr r12, =place2
ldmfd r10!, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9}
stmfd r12!, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9}
Answer 6
place and place2, respectively. Then the ldmfd loads 10 registers with content from 10 successive words at place1, 40 bytes in all. Then the stmfd instruction copies those words at place2.
Block copying of data -- copying large numbers of bytes from one location to another -- is a common task in code. Copying a string or array is an obvious example. But even copying a large struct requires many bytes to be loaded from one place and copied to another. The load/store multiple instructions are ideally suited to this. They can use as many registers as you have free to "suck up" a bunch of data from one location, and copy it into another.
Question 7
What useful thing happens if we repeat the last two lines from the prior question over and over?
Answer 7
Copying up to 10 words at a time, in a loop whose body is just two instructions, is a lot more efficient than the much larger loop you'd need to do this one word at a time.
Question 8 Let's wrap up with a couple more machine language <-> assembly translations. As for the prior segment, please translate this machine language instruction to binary, then field-breakdown, then English description, then equivalent assembly
E9AD40F0
Answer 8
In Hex: E9AD40F0
In Binary 1110100110101101 0100000011110000
By fields: 1110 100 1 1 0 1 0 1101 010000001111000
w/meaning: always ldm/stm full up Sbit ! st sp r4, r5, r6, r7, lr
In English: Always execute, ldm/stm, full stack, increasing addresses, no
system bit, writeback, store, using sp for address register. Store registers
4,5,6,7, and lr.
In assembly: stmfa sp!, {r4, r5, r6, r7, lr}
Question 9
And exactly the reverse, please, starting with assembly ldmea fp, {r0, r1, pc}
Answer 9
In assembly: ldmea fp, {r0, r1, pc}
In English: Execute always, ldm/stm, empty stack (so pre-increment to get to data before fetching), increasing addresses, no sys bit, no writeback, load, using fp as base address, store registers 0, 1, and pc.
By fields: 1110 100 1 0 0 0 1 1011 100000000000011
w/meaning: always ldm/stm empty up Sbit no! ld fp r0, r1, pc
In binary: 1110 1000 1001 1011 1000 0000 0000 0011
In hex: E91B8003
In the next segment we'll look at branches and various other instruction formats.