pete > courses > CS 202 Spring 24 > Lecture 16: LDR and STR
Lecture 16: LDR and STR
Goals
- define word size
- describe the purpose of load and store instructions
- define addressing modes
- describe the base register method of specifying an address
- describe the base + immediate method of specifying an address
- describe the base + register method of specifying an address
- describe the base + scaled register method of specifying an address
where are we going with this?
all these instructions will be combined to form higher-level language constructs
and you may be surprised when you see C and learn that it was once considered a "high-level language"
but we’ll also look at how C is used to implement even higher-level languages like Python
recall that in a RISC architecture, of which ARM32 is a representative, instructions don’t perform operations on memory
we need to use dedicated instructions to grab values from memory into a register (load)
and to save values from registers into memory (store)
it’s worth revisiting the entire idea of memory (and especially how it contrasts with that of registers)
because memory and the operations involving it are without question the most confusing aspect of this course
registers are a very small set of storage elements whose values we can operate on
(ARM32 has 16 general-purpose registers, the processor you’ll build has 8)
registers typically store a number of bits matching the word size of the ISA
in the case of ARM32, this means registers are 32 bits
memory is a huge array of storage elements
we identify an element of the memory array by its location, which we call an address
the amount of data we can store at each address is called the addressability of the memory
the word size is the number of bits the ISA uses to store an address
in ARM32, the word size is 32 bits
(again, the "word size" is often defined differently: this is the definition we’re using in this course; if you encounter the term elsewhere, verify its definition in that context)
for a load, we need to specify the source address in memory and the target register
for a store, we need to specify the source register and the target address in memory
problem: if addresses are 32 bits and instructions are 32 bits (which they are), how do we specify a) the load/store opcode, b) the register, and c) the memory address all in the same instruction?
this is indeed a conundrum!
we’re going to talk about the three methods used in ARM32, collectively referred to as "addressing modes"
ie, ways we can specify addresses
(arm ref manual F, pdf p18)
base register
3 2 1 0 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +---------------------------------------------------------------+ | |0 1 0 1 1|0 0 1| Rn | Rt | | +---------------------------------------------------------------+ assembly: ldr Rt, Rn effect: Rt <- mem[Rn] example: ldr r7, r9
this is the simplest one: a register holds 32 bits, so we set a register to the address we want and read that register to get the address
base + immediate
3 2 1 0 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +---------------------------------------------------------------+ | |0 1 0 1 1|0 0 1| Rn | Rt | imm12 | +---------------------------------------------------------------+ assembly: ldr Rt, Rn, #imm12 effect: Rt <- mem[Rn + zero-extend(imm12)] example: ldr r5, r6, #12
this is a more general version of the former instruction, and in fact they’re one and the same
it’s referred to as "load (immediate)" because of the immediate value used as the offset
NOTE! this immediate value is zero-extended rather than sign-extended
(same for all immediate values in load/store instructions)
base + register offset
3 2 1 0 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +---------------------------------------------------------------+ | |0 1 1 1 1|0 0 1| Rn | Rt | | |0| Rm | +---------------------------------------------------------------+ assembly: ldr Rt, Rn, Rm effect: Rt <- mem[Rn + Rm] example: ldr r9, r1, r0
instead of an immediate value, we can use another register as the offset
base + scaled register
3 2 1 0 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +---------------------------------------------------------------+ | |0 1 1 1 1|0 0 1| Rn | Rt | imm5 | |0| Rm | +---------------------------------------------------------------+ assembly: ldr Rt, Rn, Rm, shift effect: Rt <- mem[Rn + (Rm << imm5)] example: ldr r3, r2, r5, 2
finally, the most complicated: we can use a base register plus the value of another register shifted by an immediate value
(yep, this is again a general case of the previous instruction)
I could go over the store instructions, but they’re EXACTLY THE SAME
except bit 20 is a zero instead of a one
and they result in values going from registers to memory
why all these different modes?
surely they are redundant!
yes, they most certainly are, but some operations are so frequent that it’s advantageous to have dedicated operations to perform them
this is, in fact, a pattern that I’ve discussed before but bears repeating in this context
if you notice a particular operation being performed over and over and over again, optimize it
I promise all of these are useful
and I will prove it
but not for a couple weeks
that will be part of the discussion demonstrating how we use all these tiny assembly-language building blocks to make a fancy high-level language like C
byte-addressable memory but 32-bit registers?
all of the instructions discussed today operate on 32-bit values in memory
but memory is byte-addressable, how does this even work?
first, if the calculated memory address to load or store is X, these instructions actually load from/store to X through X+3
why X+3? because X, X+1, X+2, and X+3 are each 8 bits (because this is byte-addressable memory) and a register holds 32 bits
bytes at the higher memory addresses go in the higher bits of the register
so mem[X+3] will end up at the left end of the register and mem[X] will end up at the right end of the register
second, there are variants of all these instructions that load or store single bytes or pairs of bytes (the latter are sometimes called "half words")
there are also variants that let us subtract the offsets instead of add them (bit 23)
you won’t need to do anything with these variants, just know that they exist
next: how can we do interesting things with assembly instructions?
Definitions
The following definitions introduced in this lecture are fair-game for future quizzes. You will be expected to give the exact definition as provided in these lecture notes.
- word size
- addressing mode