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 ADD
and 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
LDAH
relative to the zero register to load the high-order 48 bits of the 32-bit constant. - Use the
LDA
instruction 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
0xXXXXYYYY
.
Let xxxx
be the result you get
when you treat XXXX
as a signed 16-bit value.
Similarly, yyyy
and YYYY
.
Let S
be the sign bit of XXXX
.
The canonical form of the constant we want to load is
0xSSSSSSSS`XXXXYYYY
.
If 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
XXXX
or YYYY
is zero.)
If
yyyy
is negative, then the
LDA
is going to undershoot by 0x10000
,
so we compensate by adding one more to xxxx
.
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
,
because 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
0x7FFF8000
to
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 0x7FFFFFFF
:
LDA t0, -1(zero) ; t0 = 0xFFFFFFFF`FFFFFFFF SRL t0, #33, t0 ; t0 = 0x00000000`7FFFFFFF