ISADesign:ARMDesign:B

Copyright 2018, Clinton A Staley

Concepts

  1. Ldr/str instructions
  2. Stmfd and its relatives

Overview

In this segment we'll continue our discussion of ARM instruction design, focusing on instructions for loading/storing registers.

Ldr and Str

The format for ldr and str is on the line of the instruction format chart entitled Single Data Transfer. Several things are worth noting:

  1. ldr and str have a cond field like every other instruction and can be done conditionally.
  2. They have fixed bits 01, which distinguishes them from the data processing instructions, which have fixed bits 00.
  3. They have no fewer than six 1-bit fields: I, P, U, B, W, and L. We'll discuss all of these.
  4. They have Rn and Rd fields that indicate the target register (Rd) and the index register (Rn). Rd once again is not the first register in the instruction word, even though it appears first in the assembly instruction. (Indeed, note that Rd is in that position for many instructions.)
  5. They have a 12-bit offset value.

The 1-bit fields

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

This is store, not a load, with a constant offset, preincrementing before writing back, negative offset, with writeback, and writing just one byte. So the IPUBWL flags are 110110

Block Transfer Instructions

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

First, there is the choice of load or store. Second, full or empty stack. Third, increasing or decreasing order. Fourth, save back into the address register, or don't.

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

The meanng of the ldr/str bit fields is a good basis to guess from. L would be a Load/store choice. W indicates whether to write a changed address into the address register. U indicates Upward or increasing addresses. And P (Preincrement) indicates a full vs empty stack.

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

They're not relevant. The I flag tells how to use the 12 offset bits, but there is no offset. The B flag says to load a byte, but the multiple register instructions always load/store full registers.

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

Those 16 bits can tell us which registers to store, but not the order. To describe an order, we'd need something like 16 4-bit fields, each one giving a register to store. Definitely not worth it.

Block copying

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

The first two instructions point r10 and r12 to 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

Because of the ! modifiers, r10 and r12 increase as they load and store registers, so they're positioned to copy the next 40 bytes, and the next...

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.