ARM data processing
Data Processing opcodes
For x86, see adc, add, and, cmp, neg, not, or, sbb, sub, test, xor, and for other help misdirections/ambiguities see JavaScript’s classList.add(), or the hll and, or, xor, not logical operators| --- Instruction --- | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| ROTATE | LIT8 | |||||||||||||||||||||||||||||||
| SHIFTH | ||||||||||||||||||||||||||||||||
| COND | OOO | OPCODE | S | RN | RD | RS | V | SHFT | Z | RM | ||||||||||||||||||||||
| and Rd, Rn, Rm OP # | cond | 0 | 0 | 0 | 0 | 0 | 0 | 0 | S | Rn | Rd | shift# | shft | 0 | Rm | |||||||||||||||||
| and Rd, Rn, Rm OP Rs | cond | 0 | 0 | 0 | 0 | 0 | 0 | 0 | S | Rn | Rd | Rs | 0 | shft | 1 | Rm | ||||||||||||||||
| and Rd, Rn, # | cond | 0 | 0 | 1 | 0 | 0 | 0 | 0 | S | Rn | Rd | rotate | # | |||||||||||||||||||
The above is copied from the main table. Equivalently, perhaps a smidge clearer:
| ---- Operation ---- | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| COND | TA | I | OPCODE | S | RN | RD | shifter_operand | |||||||||||||||||||||||||
| add Rd, Rn, Rm Op # | cond | 0 | 0 | 0 | 0 | 1 | 0 | 0 | S | Rn | Rd | IS | ST | 0 | Rm | |||||||||||||||||
| add Rd, Rn, Rm Op Rs | cond | 0 | 0 | 0 | 0 | 1 | 0 | 0 | S | Rn | Rd | Rs | 0 | ST | 1 | Rm | ||||||||||||||||
| add Rd, Rn, # | cond | 0 | 0 | 1 | 0 | 1 | 0 | 0 | S | Rn | Rd | rotate | immediate | |||||||||||||||||||
I bit Distinguishes between the immediate and register forms of <shifter_operand>.
S bit Signifies that the instruction updates the condition codes.
Note that S must be 1 and Rd 0 for cmp/cmn/tst/teq instructions.
If I is 0 and both V and Z are 1, it is (probably) a multiply instruction.
Rn Specifies the first source operand register.
Rd Specifies the destination register (must be 0 for tst/teq/cmp/cmn).
shifter_operand Specifies the second source operand (see ARM barrel shifter).
opcodes
| Opcode | Mnemonic | Operation | Action |
| 0000 | and | Logical bitwise and_bits | Rd := and_bits(Rn,shifter_operand) |
| 0001 | eor | Logical bitwise xor_bits | Rd := xor_bits(Rn,shifter_operand) |
| 0010 | sub | Subtract | Rd := Rn - shifter_operand |
| 0011 | rsb | Reverse Subtract | Rd := shifter_operand - Rn |
| 0100 | add | Add | Rd := Rn + shifter_operand |
| 0101 | adc | Add with Carry | Rd := Rn + shifter_operand + Carry Flag |
| 0110 | sbc | Subtract with Carry | Rd := Rn - shifter_operand - NOT(Carry Flag) |
| 0111 | rsc | Reverse Subtract with Carry | Rd := shifter_operand - Rn - NOT(Carry Flag) |
| 1000 | tst | Test | Update flags as per and_bits(Rn,shifter_operand) |
| 1001 | teq | Test Equivalence | Update flags as per xor_bits(Rn,shifter_operand) |
| 1010 | cmp | Compare | Update flags as per Rn - shifter_operand |
| 1011 | cmn | Compare Negated | Update flags as per Rn + shifter_operand |
| 1100 | orr | Logical bitwise or_bits | Rd := or_bits(Rn,shifter_operand) |
| 1101 | mov | Move | Rd := shifter_operand [no first operand] |
| 1110 | bic | Bit Clear | Rd := and_bits(Rn,not_bits(shifter_operand)) |
| 1111 | mvn | Move bitwise not_bits | Rd := not_bits(shifter_operand) [no first operand] |
See ARM barrel shifter for more details about shifter_operand.
examples
| Instruction | Description |
| adds r0, r2, r4 | r0 := r2+r4, setting carry bit |
| adcs r1, r3, r5 | r1 := r3+r5+c ["" for>64bits] |
| adc r0,r1 | shorthand for adc r0,r0,r1 |
| add r0, r1, 256 | r0 := r1 + 256 |
| add r0, r2, r3 lsl 1 | r0 := r2 + r3*2 |
| add r4, r4, r4 lsl 4 | r4 *= 17 (aka r4*16+r4) |
| and r7, r5, r9 | r7 := and_bits(r5,r9) |
| and r0, r0, 3 | r0 := and_bits(r0,0b0011) |
| bic r0, 0b1011 | r0 := and_bits(r0,not_bits(0b1011) |
| and r0, not r2 | (phix-only-style unconditional bics) |
| cmn r1, r2 | status_flags:= r1 - (-r2) |
| cmn r1, 1 | equivalent to/emitted for cmp r1,-1 |
| cmp r1, r2 | status_flags:= r1 - r2 |
| cmp r0, 42 | compare r0 to 42 |
| cmn r0, 42 | compare r0 to -42 |
| eor r1, r2, r3 | r1 := xor_bits(r2,r3) |
| xor r1, r2, r3 | (phix-only-style unconditional eors) |
| orr r1, r2, r3 | r1 := or_bits(r2,r3) |
| or r1, r2, r3 | (phix-only-style unconditional orrs) |
| neg r0 | r0 := -r0 (""/== rsb r0,0) |
| not r0 | r0 := not_bits(r0) (""/== mvn r0,r0) |
| rsb r0, 4 | r0 := 4 - r0 |
| rsb r0, r1 | r0 := r1 - r0 |
| rsb r0, r1, 16 | r0 := 16 - r1 |
| rsb r0, r1, r2 | r0 := r2 - r1 |
| rsb r5, r5, r5 lsl 5 | r5 *= 31 (aka r5*32-r5) |
| rsc r0, r1, r2 | r0 := r2 - r1 - not(carry) |
| sbc r0, r1, r2 | r0 := r1 - r2 - not(carry) |
| sub r0, r1, r2 | r0 := r1 - r2 |
| subs r0, r1, 42 | r0 := r1 - 42, setting flags |
| teq r1, r2 | status_flags := xor_bits(r1,r2) |
| tst r1, r2 | status_flags := and_bits(r1,r2) |
| test r1, r2 | (phix-only-style unconditional tst) |
The first two entries add two 64 bit numbers, with the result in r0 and r1, and the first 64 bit source in r2/3 and the second in r3/4. It would of course be trivial to extend that to a 128-bit addition of r0..3 := r4..7 + r8..11.
Note the phix-only-style use of and/or/xor/test may not have a conditional suffix, and the flags are always updated when using those aliases, an s suffix is implied/illegal/non-removable.
sub and sbc generate the carry flag the wrong way around, if a borrow is required then the carry flag is UNSET.
Thus, the sbc and rsc instructions require a NOT Carry flag - they invert the flag automatically during the instruction.
While mvn uses a 1’s compliment of it’s input (for good reason), cmn uses a (perhaps more sensible) 2’s compliment.
(Aside: I’m not passing any judgement on those design decisions, merely explaining their behaviour in the best way that I can think of.)
Unfortunately I am not confident enough to say much/anything at all about what happens when cmn uses the barrel shifter.
One thing I can repeat is that when Rn is 0 or #80000000, cmp Rn,0 leaves the C flag = 1 but cmn Rn,0 leaves the C flag = 0,
so it’s almost like cmp Rn,-op2, but not quite.
Both cmp a,b and teq a,b can be used as an equality test, the former affects the V flag, whereas the latter does not.
The teq instruction may? set the C flag from "shifter_carry_out", whereas cmp/cmn would immediately blat that.
The teq instruction updates the Z and N flags such that Z=1 if they are equal, and N=1 if their signs differ.