i386 and amd64 HAL code is so close, yet so far apart...

The HAL code for the i386 and amd64 kernels are a fabric of 5 interwoven files,
2 common, 3 specific, which are the equivalent of a regular "hal.fs" file on
another architecture. On i386, the HAL is a concatenation of:

1. xcomp/i386/hal1.fs
2. xcomp/x86/hal2.fs
3. xcomp/i386/hal3.fs
4. xcomp/x86/hal4.fs
5. xcomp/i386/hal5.fs

and on amd64, it's:

1. xcomp/amd64/hal1.fs
2. xcomp/x86/hal2.fs
3. xcomp/amd64/hal3.fs
4. xcomp/x86/hal4.fs
5. xcomp/amd64/hal5.fs

## API

The words defined in both the common and CPU-specific parts of the HAL have
very similar semantics, so they are mostly documented here. Docs at
[doc/xcomp/i386/hal] and [doc/xcomp/amd64/hal] have some CPU-specific
documentation, but it's mostly to document hardcoded binary codes.

HAL operand structure (basically same as asm/x86)
b2:0   src regid
b5:3   dst regid
b7:6   mod ( displacement in bank if present )
b8     0=8b 1=32/16b
b15:9  opcode's lower byte  (b8 is also part of it)
b16    "&)" flag
b17    16b?
b18    immediate? ( value in bank )
b19    "<>)" flag
b23:20 Number bank index
b27:24 zeroes
b31:28 REX flags (if 0, no REX prefix) (amd64 only)

reg! ( op reg -- op )
  Set the "reg" modrm field, that is b5:3, to the value of "reg".

single! ( op reg -- op )
  Transform modrm in "op" into a "single operand" op targeting HAL dst. We
  therefore move the "reg" field to "rm", change "mod" to 3 and set b5:3 to
  "reg".

modrm! ( op mod r/m -- op )
  Set the "mod" and "r/m" fields of "op" to the specified values.

deref! ( op -- op )
  If HALDEREF flag is set operate the necessary changes in opmod to dereference
  it. If a LEA is necessary, write it to DI, then mangle operand to reference
  it.

  TODO: HAL error on mod=3

rel, ( tgt -- )
  Write an offset, which is supposed to go after a 4b jump or a call, which
  targets address "tgt".

?dispupg ( operand n -- operand )
  If operand's mod is 1 (disp8) and that n >= $100, upgrade mod to 2 (disp32)

?, ( n operand -- ) Calls c, w, or , depending on HAL16B/HAL8B.
(sz ( operand -- n ) Yields 4 2 or 1 depending on HAL16B/HAL8B.

modrm, ( modrm -- )
  Write "modrm" with disp8/disp32/SIB if appropriate.
  When mod<>0 (or modrm=mem), add disp8 or disp32
  When targeting ESP, mangle op appropriately to add SIB byte.

op, ( operand -- )
  Write "regular" operation, that is, a modrm op with b0=8b and b1=direction
  Opcode is in b15:8 and modrm in b7:0.
  Add 16b prefix when 16b flag is set.
  If the HALINV flag is set, invert direction bit from instruction.

brc,
  A non-immediate brc, is tricky because there are no conditional jumps for
  modrm. We need to wrap a regular br, inside an inverted brc jump.

xchgLH, ( reg -- )
  Compile a 8-bit XCHG of the LO and HI parts of a register, which must be
  between 0 and 3.

rol16, ( reg -- )
  Compile a rotate left by 16 of register.

@+, ( operand -- )
  Essentially @, with +n,.

-@, ( operand -- )
  Essentially +n, with @,.

ari, ( operand reg -- )
  Write arithmetic operand with specified modrm "reg", handling the imm/register
  complexity. opcodes are supplied as 2 bytes: b15:8 is the "not imm" opcode
  and b5:3 is the "/reg" argument to supply to the $81 immediate operation.

  When the operation is 16-bit or 8-bit *and* that it's not inverted (if it's
  inverted, we can perform the 16-bit/8-bit operation directly), we want to load
  operand's src in a register so that the operation is upscaled to 32-bit.

swap-, ( operand -- )
  In i386, we do swap- as a "neg" of dst followed by a "add".
  $f6/3 is "neg" opcode.

  When op is inverted, we negate HAL src, which is a straightforward halop
  transformation for neg.

  When it's not inverted, its the dest that we negate. The halop transformation
  for neg is a bit more involved.

shift, ( operand reg -- )
  Compile a "shift" operation, which all have the same encoding, for HAL operand
  and a "reg" field corresponding to the type of shift instruction to execute.

?immDI! ( op -- op )
  Before writing the operand MUL/DIV operation, we check if the operand is
  immediate. If it is, we load that immediate in DI and mangle the operand so
  that the source becomes DI.

?swapAX, ( op -- op )
  If dst is not EAX, write a xchg of EAX with dst.

/*, ( op -- )
  What's happening in here? a lot.
  The MUL op is hardcoded with a AX (W) and DX (S) dest. Unless dst is W or S,
  we need to save those registers, but we don't have a whole lot of options.
  We have CX and DI, but we need one of them for possibly storing i). We choose
  DI for this, leaving us with CX, which we assign for saving S>).
  When halop is not inverted, we can use the dst register to save AX into.
  When it's inverted, we can't, but we can use DI as a save register for AX
  because <>) can't be used with i). Whew! it's tight, but it fits!

  89 c7 di ax mov,
  89 d1 cx dx mov, (save S register)
  89 ca dx cx xchg,
  89 f8 ax di mov,

/mod, ( op -- )
  As with the *, instruction, we're tight on registers and we have to shuffle
  them around. For example, if source is EDX , we have to copy it to ECX and
  mangle the operand.

  89 d1 cx dx mov,
  31 d2 dx dx xor,

C>W,
  ax 0 i) mov, al setXX,

## Range words

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

move, prelude
  89 d1 cx dx mov,
  87 fb di bx xchg,
  $96   si ax xchg,

[]=, and move, postlude
  96    si ax xchg,
  87 fb di dx xchg,

move,
  f3 a5 rep, movs,
wmove,
  $f3 66 a5 rep, movsw,
cmove,
  f3 a4 rep, movs,

[]=, prelude
  89 d1 cx dx mov,
  87 fb di dx xchg,
  96    si ax xchg,
  39 c0 ax ax cmp, (set Z for zero guard)

xcode []=,
  f3 a7 repz, cmps,
w[]=,
  f3 66 a7 repz, cmpsw,
c[]=,
  f3 a6 repz, cmpsb,

idx, prelude
  89 d1 cx dx mov,
  89 df di bx mov,

idx, postlude
  75 05 jnz(
  41    cx inc,
  29 ca dx cx sub,
  39 c0 ax ax cmp,

idx,
  f2 af repnz, scas,
widx,
  f2 66 af repnz, scasw,
cidx,
  f2 ae repnz, scasb,

fill, prelude
  87 fb di bx xchg,
  89 d1 cx dx mov,

fill, postlude
  87 fb di bx xchg,

fill,
  f3 ab rep, stos,
wfill,
  f3 66 ab rep, stosw,
cfill,
  f3 aa rep, stosb,
