hal.fs can't have comments and can't use asm/arm, which means that there are
numerical opcode literals around. Those opcodes are documented here.

HAL opcode structure (close to ARM structure)
b3:0   Number bank index
b4     Zero
b5     Rn is an absolute memory addr in bank if set
b11:6  Zeroes
b15:12 Rd
b19:16 Rn
b21:20 Zeroes
b22    8-bit if set
b23    Inverted destination <>)
b24    Dereferenced source &)
b25    Immediate if set
b26    32-bit if set, 16-bit if unset
b27    Zero
b31:28 Cond

Register assignment:
r13=RSP r10=PSP r9=W r11=A r8=S r7=SYSVARS

Rn< ( n -- n<<16 )
Rd< ( n -- n<<12 )
Rn! ( op n -- op )
Rd! ( op n -- op )
Rn@ ( op -- n )
Rd@ ( op -- n )

bbl, ( a o -- )
  Write a B or BL instr, with "o" being either $ea000000 or $eb000000, targeting
  absolute addr "a".
b, ( a -- ) $ea000000 bbl,
bl, ( a -- ) $eb000000 bbl,
brc', ( cond -- )
  calls "'" to find a word then writes a conditional jump to it.

?off, ( op instr -- op instr off )
  If "op" indicates that we should add an offset to LDR/STR "instr", do so,
  while possibly writing down instructions and yield the offset that was
  written.

ldrstr, ( op instr -- )
  Write down a LDR or STR instruction corresponding to "op", with "instr" as
  a base. A "neutral" instr is e5900000, but it can be something else if you
  wand to add, for example, pre/post increments.

immrot ( n -- imm+rot rest ) *Z*
  Spits "n" into an immediate and rotation field compatible with arithmetic
  instructions of the Data Processing type.

  Z if set if rest is 0.

addlit, ( dstreg n -- )
  Compile an addition of number "n" to register "dstreg".

imm, ( dstreg n -- )
  Compile number "n" being set to register "dstreg".

op, ( op instr -- )
  merge operand with instr and write

ariinner, ( op instr -- )
  Proxy "op," with this processing:
  1. Remove OP8B which messes with arithmetic opcodes
  2. If opcode is one of the "comparison" opcodes, forcefully set Rd to 0
     before writing it off.
  3. Also check for "mul". In that case, we move Rn to Rs.

ari, ( op instr -- )
  Write arithmetic operation, that is:
  1. Load operand's src in r0
  2. Apply instr with operand's dst as Rd and Rn and r0 as Rm.

  Stack state is a bit difficult to follow, so here is it:
  _imm ( op instr -- )
  _complex ( op instr n imm+rot -- )
  _complex2 ( op instr n -- )
  _mul ( op instr n -- )
  _imm2 ( op instr -- )
  _inv ( op instr -- )

eor, ( rd rm -- )
  Write a "eor) rd rdn) rm rm) ,)".

shift, ( op mask -- )
  Write a "mov) dst rd) dst rm) src shiftmask)". That's what e1b00000 is.
  After the initial Rd and mask orr-ing in, we're set for everything except
  b11:6 and b4, which values depend on whether we have HALIMM set. Stack before
  the HALIMM check is ( op opcode ).

?mem>rn ( op -- op )
  Checks if operand is m). If it is, compile a write of this value to r1 and
  set Rn to r1.

rn>r1, ( op -- op )
  Compile a @, on "op" being dereferenced and stripped of HALINV with r1 as a
  destination. Then, yield "op" witn Rn being r1 and its offset field being
  zeroed out.

?rn>r1 ( op -- op )
  If op's Rn is the same as Rd, run rn>r1,

?bigoff>r1 ( op -- op )
  If op's offset doesn't fit in a single LDR/STR, run rn>r1.

dropf,
  same as drop,: rW ppop,
C>W,
  e3a09000 mov) rW rd) 0 imm)
  02899001 add) z) rW rdn) 1 imm)
@!,
  e1a00000 mov) opdst rd) r0 rm)
brr, and br,
  e1a0e00f mov) rLR rd) rPC rm)
  e1a0f000 mov) rPC rd) r0 rm)

/mod,
  e1a00000 mov). The first one is for mov dst-to-r0. Then, mov r0-to-dst.

le!, be!,
  _lsr8, ( op -- ) shift dst right by 8

le@, be@,
  _r0+@, ( op off -- )
  _orr ( reg shift -- ) or shifted r0 in reg
  _16 ( op -- op )

## Range words

Range words have a prelude (except fill), a body and a postlude. The numbers
we write correspond to assembler output which we describe here.

move, prelude
  e3580000 cmp) rS rn) 0 imm)

move, postlude
  12588001 sub) ne) rS rdn) 1 imm) f)
  1afffffb -12 b) ne)

move,
  14990004 ldr) ne) r0 rd) rW rn) 4 +i) post)
  148b0004 str) ne) r0 rd) rA rn) 4 +i) post)

wmove,
  10d900b2 ldrh) ne) r0 rd) rW rn) 2 +i) post)
  10cb00b2 strh) ne) r0 rd) rA rn) 2 +i) post)

cmove,
  14d90001 ldr) ne) r0 rd) rW rn) byte) 1 +i) post)
  14cb0001 str) ne) r0 rd) rA rn) byte) 1 +i) post)

[]=, prelude
  e3580000 cmp) rS rn) 0 imm)
  0a000004 24 b) eq)

[]=, postlude
  e2488001 sub) rS rdn) 1 imm)
  e1500001 cmp) r0 rn) r1 rm)
  0afffff8 -24 b) eq)

[]=,
  e4990004 ldr) r0 rd) rW rn) 4 +i) post)
  e49b1004 ldr) r1 rd) rA rn) 4 +i) post)

w[]=,
  e0d900b2 ldrh) r0 rd) rW rn) 2 +i) post)
  e0db10b2 ldrh) r1 rd) rA rn) 2 +i) post)

c[]=,
  e4d90001 ldr) r0 rd) rW rn) byte) 1 +i) post)
  e4db1001 ldr) r1 rd) rA rn) byte) 1 +i) post)

idx, prelude:
  e2881001 add) r1 rd) rS rn) 1 imm)
  e2511001 sub) r1 rdn) 1 imm) f)
  02890001 add) eq) r0 rd) rW rn) 1 imm) ( never matches )

idx, postlude:
  11500009 cmp) ne) r0 rn) rW rm)
  1afffffa -16 b) ne)
  e1500009 cmp) r0 rn) rW rm)
  e0488001 sub) rS rdn) r1 rm)

idx,
  149b0004 ldr) ne) r0 rd) rA rn) 4 +i) post)

widx,
  10db00b2 ldrh) ne) r0 rd) rA rn) 2 +i) post)

cidx,
  14db0001 ldr) ne) r0 rd) rA rn) byte) 1 +i) post)

fill, postlude:
  e2588001 sub) rS rdn) 1 imm) f)
  1afffffc -8 b) ne)

fill,
  e48b9004 str) rW rd) rA rn) 4 +i) post)

wfill,
  e0cb90b2 strh) rW rd) rA rn) 2 +i) post)

cfill,
  e4cb9001 str) rW rd) rA rn) byte) 1 +i) post)
