Registers and MOV
Moving Data Around
The MOV instruction copies a value into a register. It is the most fundamental ARM64 instruction -- almost every program starts by moving values into registers.
MOV with Immediates
You can move a constant value (an "immediate") directly into a register:
MOV X0, #42 // X0 = 42
MOV X1, #0xFF // X1 = 255
MOV X2, #0b1010 // X2 = 10 (binary)
The # prefix indicates an immediate value. You can use decimal, hex (0x), or binary (0b) notation.
MOV Between Registers
You can copy a value from one register to another:
MOV X0, #10 // X0 = 10
MOV X1, X0 // X1 = 10 (copied from X0)
Important:
MOVcopies the value -- it does not move it. AfterMOV X1, X0, both X0 and X1 contain the same value.
Think of
MOVas the transporter beam of ARM64 -- it beams a copy of a value from one register to another, and the original stays right where it was.
X Registers vs W Registers
Each 64-bit X register has a 32-bit alias called W:
| 64-bit | 32-bit | Bits used |
|---|---|---|
X0 | W0 | Full 64 bits vs lower 32 bits |
X1 | W1 | Full 64 bits vs lower 32 bits |
Writing to a W register zero-extends the result to 64 bits -- the upper 32 bits are automatically cleared:
MOV X0, #0xFFFFFFFF00000000 // X0 has upper bits set
MOV W0, #5 // X0 = 5 (upper 32 bits cleared!)
You will use W registers when working with bytes (STRB W0) and 32-bit values.
MOVZ and MOVK
ARM64 instructions are 32 bits wide, so you cannot always encode a large immediate in a single MOV. For larger values, ARM64 provides:
MOVZ-- Move with Zero: loads a 16-bit immediate into a register, clearing all other bitsMOVK-- Move with Keep: loads a 16-bit immediate into a specific position, keeping other bits unchanged
You specify the position with LSL #n where n is 0, 16, 32, or 48:
MOVZ X0, #0x1234 // X0 = 0x0000000000001234
MOVK X0, #0x5678, LSL #16 // X0 = 0x0000000056781234
This is how the assembler handles large constants -- MOV X0, #0x56781234 is actually assembled into a MOVZ + MOVK pair behind the scenes.
The Zero Register
XZR always reads as zero and discards writes. It is useful as a source of zero:
MOV X0, XZR // X0 = 0 (same as MOV X0, #0)
Writing to XZR is a no-op -- the value is thrown away. This is useful for instructions that set flags as a side effect (like CMP, which is really SUBS XZR, Xn, Xm).
Your Task
Write a program that:
- Loads the value
72into X0 (ASCII 'H') - Loads the value
105into X1 (ASCII 'i') - Stores them into memory and prints
Hi\n
Use the data section for the newline, and store the character bytes into a buffer before printing.