Expand/Shrink

ARM instruction set

Almost certainly more for my benefit that anyone else’s, but if I am about to take on the ludicrously difficult task of porting Phix to the Raspberry Pi (specifically that is on RaspbianOS running on an ARM mfpu=vfpv2+ chip, as opposed to bare-metal LED blinking stuff) then I need a decent lookup to be one (F1) keystroke away, rather than wade though all 1,949+ pages of the official references, or sixteen shorter ones that each miss something, every single time!

Should you want to learn (the Phix subset of) ARM assembly, you are probably better off searching builtins\ for "[ARM]", most of what follows is likely and in fact deliberately intended to make much more sense within such a context, specifically when faced with (say) "ermah du,da,de", pressing F1 with the cursor on that should give you some kind of hint (when a real/known instruction).

These pages are naturally biased towards binary [dis]assembly of instructions, rather than usage, and deliberately omit the 75% or more of instructions that Phix does not [yet] make any use of.

Supported instructions in bitwise-layout-ish order:

Opcode 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
OPCODE LIT12
O7/8 ROTATE LIT8
OOOO S R SHIFTH
COND OOO P U I W S RN RD RS V SHFT Z RM
and[cond][s] Rd, Rn, Rm OP # cond 0 0 0 0 0 0 0 S Rn Rd shift# shft 0 Rm
and[cond][s] Rd, Rn, Rm OP Rs cond 0 0 0 0 0 0 0 S Rn Rd Rs 0 shft 1 Rm
and[cond][s] Rd, Rn, # cond 0 0 1 0 0 0 0 S Rn Rd rotate #
eor, sub, rsb, add, adc, sbc, rsc, tst, teq, cmp, cmn, orr, bic - as and but with OPCODE 0001 to 1100, & 1110
mul[cond][s] Rd, Rm, Rs cond 0 0 0 0 0 0 0 S Rd SBZ Rs 1 0 0 1 Rm
mla[cond][s] Rd, Rm, Rs, Rn cond 0 0 0 0 0 0 1 S Rd Rn Rs 1 0 0 1 Rm
smull[cond][s] RdLo, RdHi, Rm, Rs cond 0 0 0 0 1 1 0 S RdHi RdLo Rs 1 0 0 1 Rm
umull, umlal, smlal - as smull but with OPCODE 0100, 0101, 0111 respectively
mov[cond][s] Rd, Rm OP # cond 0 0 0 1 1 N 1 S SBZ Rd shift# shft 0 Rm
mov[cond][s] Rd, Rm OP Rs cond 0 0 0 1 1 N 1 S SBZ Rd Rs 0 shft 1 Rm
mov[cond][s] Rd, # cond 0 0 1 1 1 N 1 S SBZ Rd rotate #
(ie mov, mvn are the same as 'add' and friends, but with Rn SBZ)
str[cond]<x> Rd, Rn, # cond 0 1 0 P U B W 0 Rn Rd #
str[cond]<b> Rd, Rn, # cond 0 1 1 P U B W 0 Rn Rd shift# shft 0 Rm
stm[cond]<addrmode> Rm<!>, reglst cond 1 0 0 P U 0 W 0 Rn register list
(ldr/ldm as str/stm but with S set to 1)
bx[cond] Rm cond 0 0 0 1 0 0 1 0 SBO SBO SBO 0 0 0 1 Rm
b<l>[cond] <target addr> cond 1 0 1 L 24_bit_offset
fls Rd,CRd,# cond 1 1 0 P U N W S Rn CRd cp_num #
fpu CRd,CRn,CRm cond 1 1 1 0 op1 CRn CRd cp_num op2 CRm
(fls and fpu are pseudo-squared-opcodes for floating point load/store and other instructions)
swi <swi_number> cond 1 1 1 1 swi_number

You may recognise that as a somewhat cut-down "rE Ejected" table, however I am only interested in documenting opcodes that are actually used and supported/properly tested, and deliberately omitting distractions (via a prefix of '-'): for instance there are four different types of stack, garnished with pre- and post- increment, so for my own sanity I’m just sticking with push and pop [erm... I think I spoke too soon on that, but I think you get the idea]. The full/original "rE Ejected" was used to write the dissassembler, and therefore should perhaps be trusted slightly more (but lacks any fpu stuff), the above was/will be used to write the inline assembler (ie extensions to pilasm.e/ilasm() and friends).

For my own sanity I have hidden the dataprocessing opcodes EOR..BIC in the main table as they are very similar to and, ditto mvn/ldr/ldm, mov/str/stm, and all fpu.
Likewise I don’t support the adr pseudo-instruction, and have replaced overloaded ldr pseudo-forms with lea.
It is probably fair to describe my (Phix-specific) opcodes as "pre-UAL", I’ve certainly avoided any jack-of-all-trades such as vmov, since they may be nice for a coder but a royal pain to de/encode and test. The (P/L)U(I/D/N/B)W (etc) bits may/are not really properly documented and unlikely to ever be.

I will of course happily put things back in, once my 310+ immediate needs are done, but if Euphoria 4.0 has taught me anything, the one thing you should never do is just dump in 500 untested things and leave it to someone else to (never) come along and fix them all later.

These are (most of) the internal mask constants, as used in filedump.exw and similar:
--- Mask --- -- Hex -- ---------------- Binary ---------------- ---------------- Description ----------------
COND #F000_0000 0b1111_0000_0000_0000_0000_0000_0000_0000 condition code
TA #0C00_0000 0b0000_1100_0000_0000_0000_0000_0000_0000 zero for all data instructions
I #0200_0000 0b0000_0010_0000_0000_0000_0000_0000_0000 immediate flag
OOO #0E00_0000 0b0000_1110_0000_0000_0000_0000_0000_0000 (as per the rEjected original)
OOOO #0F00_0000 0b0000_1111_0000_0000_0000_0000_0000_0000 (for branches and some ldr/str instructions)
OPCODE #01E0_0000 0b0000_0001_1110_0000_0000_0000_0000_0000 opcode (see here)
O7 #0FE0_0000 0b0000_1111_1110_0000_0000_0000_0000_0000 (as used on multiply instructions)
O8 #0FF0_0000 0b0000_1111_1111_0000_0000_0000_0000_0000 (as used on some unsupported instructions)
S #0010_0000 0b0000_0000_0001_0000_0000_0000_0000_0000 set status bits (s suffix)
RN #000F_0000 0b0000_0000_0000_1111_0000_0000_0000_0000 First operand register
RD #0000_F000 0b0000_0000_0000_0000_1111_0000_0000_0000 Destination register
IS #0000_0F80 0b0000_0000_0000_0000_0000_1111_1000_0000 Immediate shift, second operand is Rm Op IS
ROTATE aka IR #0000_0F00 0b0000_0000_0000_0000_0000_1111_0000_0000 Immediate rotate
IN #0000_00FF 0b0000_0000_0000_0000_0000_0000_1111_1111 Immediate, second operand is IN ror 2*IR
SHIFTH #0000_0F80 0b0000_0000_0000_0000_0000_1111_1000_0000 Shift immediate
RS #0000_0F00 0b0000_0000_0000_0000_0000_1111_0000_0000 Shift register, second operand is Rm Op Rs
V #0000_0080 0b0000_0000_0000_0000_0000_0000_1000_0000 (should be 0 when Z is 1)
SHIFT #0000_0060 0b0000_0000_0000_0000_0000_0000_0110_0000 Shift type, 0: lsl, 1: lsr 2: asr, 3: ror
Z #0000_0010 0b0000_0000_0000_0000_0000_0000_0001_0000 Shift-operand flag, 1: IS, 0: Rs
RM #0000_000F 0b0000_0000_0000_0000_0000_0000_0000_1111 Second operand register
LIT8 #0000_00FF 0b0000_0000_0000_0000_0000_0000_1111_1111 8 bit immediate
LIT12 #0000_0FFF 0b0000_0000_0000_0000_0000_1111_1111_1111 12 bit immediate
OFFSET #0000_0FFF 0b0000_0000_1111_1111_1111_1111_1111_1111 24 bit immediate offset