Home > Blockchain >  Understanding address assignment to registers via assembly instructions
Understanding address assignment to registers via assembly instructions

Time:05-24

If I have a CPU/system with the following characteristics...

  • 16 bit architecture (16 bit registers and bus)
  • 8 total registers
  • A set of 64 assembly instructions

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!

CodePudding user response:

  • Fixed-length instruction sets:

    • LC-3, an 8 register machine, has fixed sized 16-bit instructions: it allows a 9-bit offset in certain 16-bit instructions.  The 9-bit offset is used as an immediate to form a pc-relative address, from which a full 16-bit value is loaded as data.  So, the trick there is to locate the full 16-bit value as data, somewhere nearby the code that is using it (e.g. within /-256 words).

    • MIPS is a 32-bit instruction set, in a 32-bit address space.  Using two instructions each having 16-bits of immediate value, a full 32-bit address can be composed.

    • Hack / nand2tetris has 16-bit instructions and has a special form for loading constants/address, the instruction form has one bit that says whether it is an A-type, which then allows 15 bits of constant or address.

    • MARIE, an accumulator machine, has 16-bit fixed length instructions, but only 4k of memory, so allows a 12-bit absolute address embedded in the 16-bit instructions.

    • PDP-8, an accumulator machine, has 12-bit instructions in a 12-bit address space.  Instructions can directly reference nearby memory (within the same 128 word page as the code), or any thing on the zero page (lowmem, also 128 words).

  • Variable length instruction sets often allow a full length immediate following the instruction, such are x86, 68000, others.  The processors will automatically include the size of such an immediate in the full length of the instruction.

To go more meta, instruction sets have formats, and the formats within an instruction set will vary to accommodate different kinds of operations, say, 3 reg, vs. 2 reg plus large-ish immediate.  It all goes to instruction encoding, and part of the idea here is to offer software the features it needs, while also keeping the hardware implementation manageable.

CodePudding user response:

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

  • a PC-relative load on an ISA that has it (like ARM where assemblers can automatically generate a nearby "literal pool" when you write things like ldr r0, =0x1234567)
  • add-with-PC like RISC-V auipc
  • PC-relative scaled offset like AArch64 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)
  • or the classic 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.

CodePudding user response:

I am assuming this is a fictional instruction set architecture since based of the question it doesn't seem real.

Addresses are commonly loaded from the program using "immediate" style instructions and therefore would require an instruction outside of the form of "op reg1 reg2 padding"

For example, in MIPS you can use Load Upper Immediate and Load Immediate to load a 32 bit value into a register with two 32 bit instructions.

  • Related