Chapter 8: Writing Full Values to Registers
This chapter will teach you how to write in any value (64-bits limited ofc) to a GPR. Knowing this will help you get around invalid Immediate Value ranges as that can be a problem sometimes. There are two different methods that we will discuss.
Literal Pools~
A Literal Pool is basically an Assembler "trick" that allows us to write in values to a register withOUT using a mov-type instruction(s).
Example: We want to load 0x5FFF0FFF into w10
ldr w10, =0x5FFF0FFF
Example: We want to load 0x111122223333CCCC into x16
ldr x16, =0x111122223333CCCC
Take NOTE of the Equals (=) symbol that is attached to the Immediate Value of the above instructions. This will signal to the Assembler that you are using a Literal Pool.
We won't go into the "technicals" of how this works as some of the aspects of this would cover items that you have not learned about yet. Just know that you have this "trick" in your toolbox to use.
Using "move" type instructions~
We've talked about the mov instruction in the previous Chapter. There is another "move" type instruction called movk.
movk stands for Move Then Keep. The instruction only accepts a UNSIGNED 16-bit Immediate Value as it's only input. There is no Source Register usage. In contrast to a typical mov instruction, when the desired Immediate Value is written to the register, all 3 other halfwords present in the Register are LEFT ALONE. This may seem confusing at first, this will make sense shortly.
movk has the following options...
movk xD, 0xXXXX, lsl #48
movk xD, 0xXXXX, lsl #32
movk xD, 0xXXXX, lsl #16
movk xD, 0xXXXX //Implies lsl #0
lsl stands for a leftward bit shift. We won't go into detail of what bit shifting is. You will learn about it in Chapter 14. Just understand that...
lsl #48 is to write the Immediate Value of 0xXXXX000000000000
lsl #32 is to write the Immediate Value of 0x0000XXXX00000000
lsl #16 is to write the Immediate Value of 0x00000000XXXX0000
lsl #0 is to write the Immediate Value of 0x000000000000XXXX
Once again, remember that when the Immediate Value is written, the other 3 halfwords are LEFT ALONE. As you can see, since other values in the Register are Kept, this gives us the ability to write any value from scratch that we want to.
The movk instruction has two non-extended variants as well...
movk wD, 0xXXXX, lsl #16
movk wD, 0xXXXX //Implies lsl #0
lsl #16 is to write the Immediate Value of 0xXXXX0000
lsl #0 is to write the Immediate Value of 0x0000XXXX
With both mov and movk, you can use the following template to fill in any custom 64-bit value to a GPR~
// xD = Register
// Value = 0xWWWWXXXXYYYYZZZZ
mov xD, 0xWWWW000000000000
movk xD, 0xXXXX, lsl #32
movk xD, 0xYYYY, lsl #16
movk xD, 0xZZZZ
Using the template, let's say we want to write the value of 0x123456789ABCDEF0 to x8. We can use the template and write it out like this...
mov x8, 0x1234000000000000
movk x8, 0x5678, lsl #32
movk x8, 0x9ABC, lsl #16
movk x8, 0xDEF0
The first instruction in the template has to be a regular mov as we want any previous data in the Register to be overwritten. The following 5 pics show us what occurs with the above 4 instructions. In the pics below, the Destination Register is outlined in magenta and the instruction (that is going to execute) is outlined in green except for the final pic.
1st pic (right before mov is executed)
2nd pic (once mov executed)
3rd pic (once 1st movk has executed)
4th pic (once 2nd movk has executed)
5th pic (once final movk has executed)
Writing out the first mov instruction with 12 appended zeros on the Immediate Value for Extended register usage may be annoying. You can use an alternate instruction instead. That is movz. You have 6 total options with the movz instruction (first 4 for Extended usage, last 2 for non-Extended)...
movz xD, 0xXXXX, lsl #48
movz xD, 0xXXXX, lsl #32
movz xD, 0xXXXX, lsl #16
movz xD, 0xXXXX //Implies lsl #0
movz wD, 0xXXXX, lsl #16
movz wD, 0xXXXX //Implies lsl #0
This instruction works the same as movk **ONLY** in regards to the lsl mechanism. Excluding that, the instruction will set the rest of the register (excluding the Immediate Value used ofc) to zero. Therefore, for writing full values to registers, you can replace the usage of the regular mov instruction with this...
movz xD, 0xXXXX, lsl #48
Thus, you can write out any 64-bit value in an extended register with this updated template that utilizes movz....
// xD = Register
// Value = 0xWWWWXXXXYYYYZZZZ
movz xD, 0xWWWW, lsl #48
movk xD, 0xXXXX, lsl #32
movk xD, 0xYYYY, lsl #16
movk xD, 0xZZZZ
Please note that the Assembler will likely convert the movz instruction to a mov instruction upon Assembling. Therefore, on something such as the GNU Debugger, you will see a regular mov instruction.