The Alpha AXP, part 3: Integer constants
The Alpha AXP does not have a “load immediate 32-bit integer” instruction. If you need to load an immediate 32-bit integer, you need to use some tricks.
We saw last time that loading 8-bit constants can be done by using the
SUB instructions. But there are also instructions that can be repurposed to generate signed 16-bit constants.
Effective address instructions are basically arithmetic operations disguised as memory operations. (Yes, I know we haven’t learned about memory operations yet.)
LDA Ra, disp16(Rb) ; Ra = Rb + (int16_t)disp16 LDAH Ra, disp16(Rb) ; Ra = Rb + (int16_t)disp16 * 65536
The first instruction applies a signed 16-bit displacement to a value in a register and puts the result in the Ra register.
The second one is a little trickier. It takes the signed 16-bit displacement and shifts it left 16 positions before adding it to the Rb register.
Both of these operations operate on the full 64-bit register, so they can produce non-canonical results.
The basic idea behind loading a 32-bit constant (in canonical form) is as follows:
- Use the
LDAHrelative to the zero register to load the high-order 48 bits of the 32-bit constant.
- Use the
LDAinstruction relative to the destination register of the previous instruction to load the low-order 16 bits.
However, the fact that the 16-bit values are sign-extended makes things a bit more complicated.
Let’s say that the 32-bit constant we want to load into the t0 register is
xxxx be the result you get when you treat
XXXX as a signed 16-bit value. Similarly,
S be the sign bit of
XXXX. The canonical form of the constant we want to load is
yyyy is nonnegative, then we can just load up the two halves of our constant and they won’t interact with each other.
LDAH t0, XXXX(zero) ; t0 = 0xSSSSSSSS`XXXX0000 LDA t0, YYYY(t0) ; t0 = 0xSSSSSSSS`XXXXYYYY
(Throughout, I will leave out the obvious simplifications if
YYYY is zero.)
yyyy is negative, then the
LDA is going to undershoot by
0x10000, so we compensate by adding one more to
LDAH t0, xxxx+1(zero) ; t0 = 0xSSSSSSSS`XXXX0000 + 0x10000 LDA t0, yyyy(t0) ; t0 = 0xSSSSSSSS`XXXXYYYY
Aha, but this trick doesn’t work if
xxxx is exactly
0x7FFF + 1 =
0x8000, which has the wrong sign bit. In that case, we need a final adjustment step to put the result into canonical form.
LDAH t0, -32768(zero) ; t0 = 0xFFFFFFFF`80000000 LDA t0, yyyy(t0) ; t0 = 0xFFFFFFFF`7FFFYYYY ADDL zero, t0, t0 ; t0 = 0x00000000`7FFFYYYY
Constants that are in the range
0x7FFFFFFF suffer from this problem.¹
All of this hassle about creating 32-bit constants has consequences for the Windows NT memory manager, as I discussed a few years ago.
Okay, so that’s it for loading constants. Next time, we’ll start looking at memory access.
¹ There is a special shortcut for the value
LDA t0, -1(zero) ; t0 = 0xFFFFFFFF`FFFFFFFF SRL t0, #33, t0 ; t0 = 0x00000000`7FFFFFFF