If I have a CPU/system with the following characteristics...
And assuming my assembly instructions follow the format...
OPCode (6 bits) + Register (3 bits) + Register (3 bits) + Unused (4 bits)
** Example Instructions (below) **
Assembly: LOAD R1, R7 (Loads value of address stored in R1 into destination register R7)
Machine: 110000 001 111 0000
Assembly: STORE R1, R7 (Stores value in R1 into destination address stored in register R7)
Machine: 110001 001 111 0000
These types of instructions make sense to me because all of the required bits fit nicely into a 16 bit format and thus into the instruction register (which hold 16 bits), but I am confused on how one gets the desired address into a register to begin with due to this instruction length constraint?
If an address is 16 bits on this system, it seems to me like I would need more than 16 bits to represent an instruction that would assign an address value to any given register before I could even use something like a LOAD or STORE instruction...
OPCode (6bits) + destinationRegister (3 bits) + addressLiteral (16 bits) ???
However, something like this would not fit in my 16 bit instruction register. What am I not understanding here? Any help is greatly appreciated, thanks!
That's correct, in a RISC-like machine with fixed-width instructions the same word size as addresses and registers, it will take multiple instructions to generate an arbitrary constant in a register.
You can do
ldr r0, =0x1234567
)auipc
adrp
(go by pages) / add
(fix it up with the offset within page, or use the low 12 bits as an offset within an ldr
instruction)lui
/addi
for arbitrary non-address constants, with two immediates of widths that add up to the word size (like MIPS 16+16, or RISC-V high 20 from lui
+ low 12 from a normal I-type instruction)You typically want one opcode for an instruction format that takes 1 register, and uses the rest as immediate bits, giving you the max space.
In your case, 6 opcode bits + 1 register would take 9 bits, leaving only 7 immediate bits.
So that's not very much, not enough to even generate a 16-bit constant in 2 instructions even with a 2nd opcode that ADDs or ORs into the bottom of one register. Unless you want to sacrifice multiple opcodes (e.g. use the low couple bits of an opcode as extra immediate bits), that's not very good.
So you might want to use PC-relative loads as the primary way to generate large constants. (So one opcode, 1 register, leaving 7 bits of offset, maybe scaled by 2 so it's word-aligned).
Or a special instruction that reads the whole next instruction word as an immediate. Decoding could consider this instruction like a jump over the data as well as a load of that data. (In a simple scalar pipelined design, maybe pulling it out of the fetch stage and sending a NOP down the rest of the pipe. It would need a bunch of special cases, and maybe have performance potholes if your pipeline's hazard detection still looks at it before replacing with NOP. And puts an extra muxer or AND gate in the path instruction bits take in the decode stage.) I don't know if any real ISAs actually do this; some 32-bit ISAs with 16-bit compressed instructions (like ARM Thumb mode or RV32c) have variable-width instructions that are either 2 or 4 bytes, signalled by some easy-to-decode bits in the first 2-byte chunk.