ARM 64 Assembly Series — Data Processing (Part 1)
Previous posts: Basic definitions and registers, lab setup, offset and addressing modes, Load And Store, Branch
So far we talked about load, store and branch instructions and it is time to discuss about a (long) set of instructions that can be used to process data. To quickly refresh your memory on what has been discussed so far, you can refer to the table below or you can simply navigate to the previous posts by following the links in the subtitle section:
General format and operands
In mathematics, an operand is the object of a mathematical operation. For example, in the following addition y and x are the operands of an addition where a is the result : y + x= a. Similarly, in arm assembly most of the data processing instructions require two operands and a destination register:
op Rd, Rn, Rm
op defines the type of the operations, Rd is the result destination register, Rn is the first operand and Rm is the second.
As you probably notice in the figure above, the first operand enters directly the ALU while the second one may be processed before entering (e.g. shifted as in the example above). Indeed, this flexible second operand, also known as operand2, can be one of the following:
- A register with an optional shift or extend operation (LSL, LSR, ASR e.t.c)
- A 12bit immediate value or 13bit pattern (used only for logical instructions)
Lets see some examples:
add x1, x4, x5 // x1 = x4 + x5
sub x0, x0, #1 // x0 = x0 - 1
neg x3, x4, lsl #3 // x3 = -(x4 << 3)
We will go into details regarding the shift and extend operations, but for now and for the sake of simplicity we will refer to them as shift_op and extend_op. That being said, here is a summary of the operand2 formats:
Arithmetic operations
The basic arithmetic instructions are the add, sub and neg corresponding to addition, subtraction and negation. In addition to those, we have the adc, sbc and ngc which are adding the carry bit of the PSTATE register to the two operands. These (carry) instructions can be used only with unsifted/unextended operands. Finally, an s can be appended to each instruction (adds, subs and so on) in order to affect the flags of the PSTATE:
Logical operations
Similarly to the arithmetic operations, the general syntax of the logical operations is op Rd, Rn, operand2 except the bitwise-not which uses only the operand2 and the destination register. As before, appending an s to the instruction can affect the PSTATE flags. Here are the most basic logical operations and their usage:
Examples:
More on the role of s and the PSTATE
Contrary to AArch32, AArch64 doesn’t not allow other than the branch instructions to be conditionally executed.
Need to know (n2k): Conditional execution controls whether or not the core will execute an instruction. Most instructions have a condition attribute that determines if the core will execute it based on the setting of the condition flags. Prior to execution, the processor compares the condition attribute with the condition flags in the cpsr. If they match, then the instruction is executed; otherwise the instruction is ignored.
As being said before, appending an s to the mnemonic of the operation can be used to set the flags of the PSTATE register which in conjunction with a branch instruction can be used to modify the flow of a program. Let’s see some examples:
As it is depicted above, since the result of the subtraction operation is negative, the N (Negative) flag of the program status register (cpsr in this case) will be set to 1. This will make the bmi instruction (branch minus) to set the program counter to the address of the neg label. The Z (Zero) denotes another flag of the status register which is set when a result is equal to zero:
The V (oVerflow) flag is set if the result of an addition or subtraction operation can overflow the range of the result. The flag will be set to 1 if overflow occurs and 0 if not:
The C (Carry) flag is used to indicate whether the result of an unsigned operation is not representable. For example adding 1 to 4294967295 will normally has as a result the value 4294967296. However, if the destination register can hold up to 32 bits then the result will be zero with carry:
Move and Shift Operations
The move operations are used to copy data from a register to a register or from an immediate to a register. The particular set includes the following instructions:
The shift operations are used to shift or rotate the contents of a register. They can be used either as standalone instructions or for flexible second operands similar to the ones we saw above. The standalone syntax is as follows:
op Rd, Rn, Rm
Where op can be either lsl, lsr, asr or ror:
As it is depicted above, the lsl instruction shifts the contents to left while padding the shifted positions with 0. A single shift to left is like multiplying by 2, a double shift is like multiplying by 4 and so on:
Similarly to lsl, the lsr instruction shifts the contents to right while padding the shifted positions with 0. A single shift to right is like dividing by 2, a double shift is like multiplying by 4 and so on:
The ror instruction will rotate the contents, moving the shifted bit to the most significant bit of the register:
Finally, the asr instruction will shift a number of bits to the right padding with zeros but maintaining the sign bit:
That’s all for now, I hope to see you in part 2 of the data processing instructions.