; INTEL32. UNFINISHED...

macro align n, value:0
  db ((n-1)-($+n-1) mod n) dup(value)
end macro

macro put_dx i, size
  if size=1
    db i
  else if size=2
    dw i
  else if size=4
    dd i
  end if
end macro

RET equ $D, $A

;;;;;;;;;;;;;;;;;;;;; VERIFY ;;;;;;;;;;;;;;;;;;;;;

macro verify_n n, min, max
  if ~n eqtype 0
    err 'Number expected'
  end if
  if n<min | n>max
    err 'Number exceeds range'
  end if
end macro

irp <type,sign,max>,\
  u1,0,1, u2,0,11b, u3,0,111b,\
  u4,0,$F, u5,0,$1F, u6,0,$3F,\
  u7,0,$7F, u8,0,$FF, i8,1,$FF,\
  u16,0,$FFFF, i16,1,$FFFF,\
  u32,0,$FFFFFFFF, i32,1,$FFFFFFFF
  macro verify_#type n
    if sign=0
      verify_n n, min, max
    else
      verify_n n, -((max/2)+1), (max/2)
    end if
  end macro
end irp

macro get_size_i size, i
  size=0
  if i=>-$80 & i<=$FF
    size=1
  else if i>=-$8000 & i<=$FFFF
    size=2
  else
    size=4
  end if
end macro

macro get_size_t size, type
  size=0
  match =byte, type
    size=1
  else match =word, type
    size=2
  else match =dword, type
    size=4
  else match =qword, type
    size=8
  end match
end macro

macro name_length name, n
  virtual at 0
    db `name
    n=$
  end virtual
end macro

;;;;;;;;;;;;;;;;;;; REGISTERS ;;;;;;;;;;;;;;;;;;;;

numeric al, cl, dl, bl, ah, ch, dh, bh
numeric ax, cx, dx, bx, sp, bp, si, di
numeric eax, ecx, edx, ebx, esp, ebp, esi, edi

its_type=0
its_size=0
its_index=0
its_half=0
its_mode=0

macro is_in is, name, p&
  is=0
  irp q, p
    match =name, q
      is=1
      its_index=%-1
      break
    end match
  end irp
end macro

macro is_r is, name
  local n
  n=0
  is=0
  its_size=0
  its_half=0
  name_length name, n
  if n=3
    is_in is, name, eax, ecx, edx, ebx,\
      esp, ebp, esi, edi
    if is
      is=4
    end if
  else if n=2
    is_in is, name, al, cl, dl, bl
    if is
      is=1
      its_half='l'
    else
      is_in is, name, ah, ch, dh, bh
      if is
        is=1
        its_half='h'
      else
        is_in is, name, ax, cx, dx, bx,\
          sp, bp, si, di
        if is
          is=2
        end if
      end if
    end if
  end if
  its_size=is
end macro

;;;;;;;;;;;;;; OPCODE, TYPE, OPERAND ;;;;;;;;;;;;;

d_bit=0
s_bit=0

macro put_code opcode
  db (opcode or (d_bit shl 1) or s_bit)
end macro

macro get_type p&
  local is,\
    type, size, index
  is=0
  type=0
  size=0
  index=0
  match [m], p
    type='m'
  else match t[m], p
    type='m'
    get_size_t size, t
  else match name, p
    is_r is, name
    if is
      type='r'
      size=its_size
      index=its_index
    else if name eqtype 0
      type='i'
    else
      err 'Unknown type'
    end if
  end match
  its_type=type
  its_size=size
  its_index=index
end macro

macro get_type_x type, size, index, p&
  get_type p
  type=its_type
  size=its_size
  index=its_index
end macro

;;;;;;;;;;;;;;;;;; OPERAND MODE ;;;;;;;;;;;;;;;;;;

; * 'i'   = 0-9, A-F   ; $1234ABCD
; * 'r'   = eax..edi   ; eax
; * 'rm'  = [eax..edi] ; [esi]
; * 'im'  = [#]        ; [$12345678]
; * '+r'  = [r+r]      ; [eax+ecx]
; * '+i'  = [r+i]      ; [ecx+$1234]
; * '-i'  = [r-i]      ; [ebp-16]
; * '*'   = [r*s]      ; [eax*4]
; * '&+*' = [i+r*s]    ; [$ABCD1234+eax*4]
; * '+*'  = [r+r*s]    ; [edi+ecx*4]
; * '+*+' = [r+r*s+i]  ; [edi+ecx*8+$BABE]

define the_mode 0
define the_base 0
define the_index 0
define the_scale 0
define the_number 0

macro get_mode_m p
  local is
  is=0
  match a+b*c+d, p
    redefine the_mode '+*+'
    redefine the_base a
    redefine the_index b
    redefine the_scale c
    redefine the_number d
  else match a+b*c, p
    redefine the_base a
    redefine the_index b
    redefine the_scale c
    is_r is, a
    if is
      redefine the_mode '+*'
    else
      redefine the_mode '&+*'
    end if
  else match a+b, p
    redefine the_base a
    redefine the_index b
    is_r is, b
    if is
      redefine the_mode '+r'
    else
      redefine the_mode '+i'
    end if
  else match a-b, p
    redefine the_mode '-i'
    redefine the_base a
    redefine the_index b
  else match a*b, p
    redefine the_mode '*'
    redefine the_base a
    redefine the_scale b
  else match name, p
    get_type name
    is=its_type
    redefine the_base name
    if is='r'
      redefine the_mode 'rm'
    else if is='i'
      redefine the_mode 'im'
    else
      err ''
    end if
  end match
end macro

macro get_mode p
  local is
  is=0
  redefine the_mode 0
  redefine the_base 0
  redefine the_index 0
  redefine the_scale 0
  redefine the_number 0
  match [m], p
    get_mode_m m
    its_size=0
  else match t[m], p
    get_mode_m m
    get_size_t is, t
    its_size=is
  else match name, p ; 'r', 'i', 'm'
    get_type name    ; (not [memory])
    is=its_type
    redefine the_mode is
    redefine the_base name
  end match
end macro

macro put_mode a, b, c
  db ((a shl 6) or (b shl 3) or c)
end macro

macro write_number n
  if n>=-$80 & n<$80
    db n
  else
    dd n
  end if
end macro

macro get_scale mode
  if the_scale=2
    mode=01b
  else if the_scale=4
    mode=10b
  else if the_scale=8
    mode=11b
  else
    err 'Invalid scale'
  end if
end macro

macro write_mode a
  local n, mode
  n=0
  mode=0
  if the_mode='rm'
    if the_base<>ebp
      put_mode 00b, a, the_base
    else
      put_mode 01b, a, the_base
      db 0
    end if
  else if the_mode='im'
    put_mode 00b, a, 101b
    dd the_base
  else if the_mode='+r'
    put_mode 00b, a, 100b
    put_mode 00b, the_index, the_base
  else if the_mode='+*'
    put_mode 00b, a, 100b
    get_scale mode
    put_mode mode, the_index, the_base
  else if the_mode='&+*'
    put_mode 00b, a, 100b
    get_scale mode
    put_mode mode, the_index, 101b
    dd the_base
  else if the_mode='+*+'
    n=the_number
    if n>=-$80 & n<$80
      mode=01b
    else
      mode=10b
    end if
    put_mode mode, a, 100b
    get_scale mode
    put_mode mode, the_index, the_base
    write_number n
  else if the_mode='+i' | the_mode='-i'
    n=the_index
    if the_mode='-i'
      n=-n
    end if
    if n>=-$80 & n<$80
      mode=01b
    else
      mode=10b
    end if
    put_mode mode, a, the_base
    write_number n
  else
    err 'Invalid mode'
  end if
end macro

macro display_mode p
  display '* ', `p, ': '
  get_mode p
  display 'Mode: ', the_mode
  if its_size
    display '. Size=', its_size+'0'
  end if
  display '. Base=', the_base+'0'
  if the_index
    display '. Index=', the_index+'0'
  end if
  if the_scale
    display '. Scale=', the_scale+'0'
  end if
  if the_number
    display '. Number=', the_number+'0'
  end if
  display RET
end macro

macro test_modes
  display RET
  display_mode ecx
  display_mode 8
  display_mode [edi]
  display_mode [8]
  display_mode [ecx+edx]
  display_mode [ebp+8]
  display_mode [ecx-4]
  display_mode [ebx*4]
  display_mode [edi*2+8]
  display_mode [ecx+edx*4]
  display_mode [edi+ebx*4+8]
end macro

macro get_operands a, b,\
  type1, size1, index1,\
  type2, size2, index2
  get_type_x type1, size1, index1, a
  get_type_x type2, size2, index2, b
  if size1 & size2 & size1<>size2
    ; err 'Operands must be same size'
  end if
  if type1='m' & size1=0
    if type2='i'
      err 'Size must be specified'
    end if
    size1=size2
  end if
end macro

;;;;;;;;;;;;;;;;; INSTRUCTIONS ;;;;;;;;;;;;;;;;;;;

irp <name,value>,\
  aaa,$37, aas,$3F, cbw,$98, clc,$F8,\
  cld,$FC, cli,$FA, cmc,$F5, cmpsb,$A6,\
  cmps,$A7, cwd,$99, daa,$27, das,$2F,\
  hlt,$F4, int3,$CC, lodsb,$AC, lods,$DC,\
  movsb,$A4, movs,$A5, popf,$9D, pushf,$9C,\
  sahf,$9E, scasb,$AE, scas,$AF, stc,$F9,\
  std,$FD, sti,$FB, stosb,$AA,\
  stos,$AB, xlat,$D7
  macro name
    db value
  end macro
end irp

irp <name,value>,\
  aso,$67, lock,$F0, oso,$66,\
  rep,$F3, repne,$F2, wait,$9B
  macro name p&
    db value
    match next, p
      next
    end match
  end macro
end irp

macro aam i:10
  db $D4, i
end macro

macro aad i:10
  db $D5, i
end macro

macro nop n
  match , n
    db $90
  else
    repeat n
      db $90
    end repeat
  end match
end macro

;;;;;;;;;;;;;;;;;;;; PUSH, POP ;;;;;;;;;;;;;;;;;;;

; push rx  ; oso 50+r
; push rmx ; oso FF /6
; push i8  ; 6A ib
; push ix  ; oso 68 ix
; pop rx   ; oso 58+r
; pop rmx  ; oso 8F /0

macro push x
  local type
  type=0
  get_type x
  type=its_type
  if type='r'
    db $50+x
  else if type='i'
    if x<$80 & x>=-$80
      db $6A, x
    else
      db $68
      dd x
    end if
  else if type='m'
    db $FF
    get_mode x
    write_mode 6
  end if
end macro

macro push p&
  irp x, p
    push x
  end irp
end macro

macro pushr p&
  irp x, p
    indx (1+%%-%)
    push x
  end irp
end macro

macro pop x
  local type
  type=0
  get_type x
  type=its_type
  if type='r'
    db $58+x
  else if type='m'
    db $8F
    get_mode x
    write_mode 0
  else
    err 'Invalid operand'
  end if
end macro

macro pop p&
  irp x, p
    pop x
  end irp
end macro

macro popr p&
  irp x, p
    indx (1+%%-%)
    pop x
  end irp
end macro

macro test_push_pop
  push eax
  push $77
  push $12345678
  push [edx]
  push [esi]
  push [ebp]
  push [$12345678]
  push [edi+8]
  push [edi+$CAFEBABE]
  push [ecx+edx]
  push [esi+ecx*2]
  push [ecx+edx*4]
  push [ecx+edx*4+$77]
  push [ecx+edx*8+$BABE1234]
  pop ecx
  pop [edi+8]
  pop [edi+$CAFEBABE]
end macro

;;;;;;;;;;;;;;;;;;;;; BRANCH ;;;;;;;;;;;;;;;;;;;;;

; jmp i8  ; EB i8
; jmp ix  ; E9 ix
; jmp mx  ; oso FF /5
; jmp rmx ; oso FF /4

macro jxx i, size
  if size=1
    db (i-$-1)
  else
    dd (i-$-4)
  end if
end macro

macro jmp_i i
  local n
  n=(i-$)
  if n<$80 & n>=-$80
    db $EB
    jxx i, 1
  else
    db $E9
    jxx i, 4
  end if
end macro

macro jmp p
  jmp_i p
  ; ...
end macro

; jcc i8 ; 70+cc i8
; jcc ix ; 0F 80+cc ix

irp <name,opcode>,\
  jo,$70, jno,$71, jc,$72, jb,$72,\
  jnae,$72, jnc,$73, jnb,$73, jae,$73,\
  jz,$74, je,$74, jnz,$75, jne,$75,\
  jna,$76, jbe,$76, ja,$77, jnbe,$77,\
  js,$78, jns,$79, jp,$7A, jpe,$7A,\
  jnp,$7B, jpo,$7B, jl,$7C, jnge,$7C,\
  jnl,$7D, jge,$7D, jng,$7E, jle,$7E,\
  jg,$7F, jnle,$7F
  macro name i
    local n
    n=(i-$)
    if n<$80 & n>=-$80
      db opcode
      jxx i, 1
    else
      db $0F, (opcode+$10)
      jxx i, 4
    end if
  end macro
end irp

; unfinished...

; call ix   ; oso E8 ix
; call rm32 ; oso FF /2

macro call i
  db $E8
  jxx i, 4
end macro

macro call_p name
  db $FF, $15
  dd name
end macro

macro call_p name, p&
  pushr p
  call_p name
end macro

macro enter w, b
  db $C8
  dw w
  db b
end macro

macro leave
  db $C9
end macro

macro ret n
  match , n
    db $C3
  else
    db $C2
    dw n
  end match
end macro

macro test_branch
  jmp .a
  call .a
  je .a
  jne .a
  call .a
  jge .a
  jae .a
  .a:
  ret
  ret 8
  call .a
end macro

;;;;;;;;;;;;;;;;;;;; MOVEMENT ;;;;;;;;;;;;;;;;;;;;

; mov rm8,r8 ; 88 /r
; mov rmx,rx ; oso 89 /r
; mov r8,rm8 ; 8A /r
; mov rx,rmx ; oso 8B /r
; mov r8,i8  ; B0+r ib
; mov rx,ix  ; oso B8+r ix
; mov rm8,i8 ; C6 /0 ib
; mov rmx,ix ; oso C7 /0 ix

macro mov a, b
  local opcode, i_size, ext,\
    type1, type2, size1, size2,\
    index1, index2
  d_bit=0
  s_bit=0
  opcode=0
  i_size=0
  get_operands a, b,\
    type1, size1, index1,\
    type2, size2, index2
  i_size=size1
  if size1=2 | size2=2
    oso
  end if
  if size1>=2
    s_bit=1
  end if
  if type1='r' & type2='r'
    d_bit=1
    opcode=$88
    put_code opcode
    put_mode 11b, a, b
  else if type1='r' & type2='i'
    put_code $C7
    put_mode 11b, 0, a
    put_dx b, i_size
  else if type1='r' & type2='m'
    d_bit=1
    opcode=$8A
    put_code opcode
    get_mode b
    write_mode a
  else if type1='m' & type2='r'
    opcode=$88
    put_code opcode
    get_mode a
    write_mode b
  else if type1='m' & type2='i'
    put_code $C6
    get_mode a
    write_mode 0
    put_dx b, i_size
  end if
end macro

macro test_mov
  mov eax, ecx
  mov edx, $12345678
  mov ah, cl
  mov cx, bx
  mov ecx, [edi]
  mov edx, [ebp+8]
  mov ebx, [edi-32]
  mov esp, [ecx+edx*4]
  mov edi, [edx+ebx*8+$CAFEBABE]
  mov ecx, [esi]
  mov edx, [ebp]
  mov edi, [$12345678]
  mov eax, [edi+8]
  mov ecx, [edi+$CAFEBABE]
  mov eax, [ecx+edx]
  mov ecx, [esi+ecx*2]
  mov edx, [ecx+edx*4]
  mov ebx, [ecx+edx*4+$77]
  mov edi, [ecx+edx*8+$BABE1234]
  mov ax, [edx]
  mov bx, [$12345678]
  mov ch, [edi-$AAAAAA]
  mov al, [ecx+edx*8+$BABE1234]
  mov [ecx], ah
  mov [ecx+edx], cx
  mov [ecx+edx*4], ebx
  mov byte [eax], $FF
  mov word [ecx+edx], $FFFF
  mov dword [ecx+edx*8], $ABCD1234
  mov ecx, [$CAFEBABE+edi*8]
end macro

;;;;;;;;;;;;;;;;;; MOVZX, MOVSX ;;;;;;;;;;;;;;;;;;

; movzx r32,rm8  ; oso 0F B6 /r
; movzx r32,rm16 ; oso 0F B7 /r
; movsx r32,rm8  ; oso 0F BE /r
; movsx r32,rm16 ; oso 0F BF /r

macro movx sign, a, b
  local opcode, i_size, ext,\
    type1, type2, size1, size2,\
    index1, index2
  d_bit=0
  s_bit=0
  i_size=0
  get_operands a, b,\
    type1, size1, index1,\
    type2, size2, index2
  i_size=size1
  if size2=2
    s_bit=1
  end if
  db $0F
  opcode=$B6
  if sign=1
    opcode=$BE
  end if
  if type1='r' & type2='r'
    d_bit=1
    put_code opcode
    put_mode 11b, a, b
  else if type1='r' & type2='m'
    d_bit=1
    put_code opcode
    get_mode b
    write_mode a
  else
    err 'Invalid operands'
  end if
end macro

macro movsx a, b
  movx 1, a, b
end macro

macro movzx a, b
  movx 0, a, b
end macro

macro test_movx
  movzx esp, word [ecx+edx*4]
  movzx edi, word [edx+ebx*8+$CAFEBABE]
  movzx eax, cx
  movsx ecx, byte [edi]
  movsx edx, byte [ebp+8]
  movsx ebx, ah
end macro

;;;;;;;;;;;;;;;;;;;;;; LEA ;;;;;;;;;;;;;;;;;;;;;;;

; lea rx,m ; oso 8D /r

macro lea r, m
  db $8D
  get_mode m
  write_mode r
end macro

macro test_lea
  lea ecx, [edi]
  lea edx, [ebp+8]
  lea ebx, [edi-32]
  lea esp, [ecx+edx*4]
  lea edi, [edx+ebx*8+$CAFEBABE]
end macro

;;;;;;;;;;;;;;;;;;;; INC, DEC ;;;;;;;;;;;;;;;;;;;;

; inc rx  ; oso 40+r
; inc rm8 ; FE /0
; inc rmx ; oso FF /0
; dec rx  ; oso 48+r
; dec rm8 ; FE /1
; dec rmx ; oso FF /1

macro inc_dec name, p
  local type, opcode, digit
  type=0
  opcode=$40
  digit=0
  match =dec, name
    opcode=$48
    digit=1
  end match
  get_type p
  type=its_type
  if its_size=0
    err 'Size must be specified'
  end if
  if its_size=2
    oso
  end if
  s_bit=0
  if its_size>=2
    s_bit=1
  end if
  if type='r'
    db opcode+p
  else if type='m'
    db $FE or s_bit
    get_mode p
    write_mode digit
  else
    err 'Invalid operand'
  end if
end macro

macro inc p
  inc_dec inc, p
end macro

macro dec p
  inc_dec dec, p
end macro

macro test_inc_dec
  inc eax
  inc dword [edx]
  inc byte [$12345678]
  inc dword [edi+ecx*4]
  dec ebx
  dec byte [eax+$80]
  dec word [edi]
  dec dword [edi+ecx*8]
end macro

;;;;;;;;;;;;;;;;;; NEGATE, NOT ;;;;;;;;;;;;;;;;;;;

; neg rm8 ; F6 /3
; neg rmx ; oso F7 /3
; not rm8 ; F6 /2
; not rmx ; oso F7 /2

macro neg_not name, p&
  local type, opcode, digit
  type=0
  opcode=$F6
  digit=3
  match =not, name
    digit=2
  end match
  get_type p
  type=its_type
  if its_size=0
    err 'Size must be specified'
  end if
  if its_size=2
    oso
  end if
  s_bit=0
  if its_size>=2
    s_bit=1
  end if
  if type='r'
    db opcode or s_bit
    put_mode 11b, digit, p
  else if type='m'
    db opcode or s_bit
    get_mode p
    write_mode digit
  else
    err 'Invalid operand'
  end if
end macro

macro neg x
  neg_not neg, x
end macro

macro not x
  neg_not not, x
end macro

macro test_neg_not
  neg eax
  neg dword [edx]
  neg byte [$12345678]
  neg dword [edi+ecx*4]
  not ebx
  not byte [eax+$80]
  not word [edi]
  not dword [edi+ecx*8]
end macro

;;;;;;;;; SHIFT: SHL, SHR, SAR, ROL, ROR ;;;;;;;;;

; shl rm8,cl ; D2 /4
; shr rm8,cl ; D2 /5
; sar rm8,cl ; D2 /7
; rol rm8,cl ; D2 /0
; ror rm8,cl ; D2 /1
; shl rmx,cl ; oso D3 /4
; shr rmx,cl ; oso D3 /5
; sar rmx,cl ; oso D3 /7
; rol rmx,cl ; oso D3 /0
; ror rmx,cl ; oso D3 /1
; shl rm8,i8 ; C0 /4 ib
; shr rm8,i8 ; C0 /5 ib
; sar rm8,i8 ; C0 /7 ib
; rol rm8,i8 ; C0 /0 ib
; ror rm8,i8 ; C0 /1 ib
; shl rmx,i8 ; oso C1 /4 ib
; shr rmx,i8 ; oso C1 /5 ib
; sar rmx,i8 ; oso C1 /7 ib
; rol rmx,i8 ; oso C1 /0 ib
; ror rmx,i8 ; oso C1 /1 ib

macro shift_x name, digit, a, b
  local opcode,\
    type1, type2, size1, size2,\
    index1, index2
  opcode=0
  d_bit=0
  s_bit=0
  get_operands a, b,\
    type1, size1, index1,\
    type2, size2, index2
  if size1=2
    oso
  end if
  if size1>=2
    s_bit=1
  end if
  if type1='r' & type2='r'
    if size2<>1 | index2<>1
      err 'Operand 2 must be CL'
    end if
    opcode=$D2
    put_code opcode
    put_mode 11b, digit, a
  else if type1='r' & type2='i'
    opcode=$C0
    put_code opcode
    put_mode 11b, digit, a
    db b
  else if type1='m' & type2='r'
    if size2<>1 | index2<>1
      err 'Operand 2 must be CL'
    end if
    opcode=$D2
    put_code opcode
    get_mode a
    write_mode digit
  else if type1='m' & type2='i'
    opcode=$C0
    put_code opcode
    get_mode a
    write_mode digit
    db b
  else
    err 'Invalid operands'
  end if
end macro

irp <name,digit>,\
  shl,$4, shr,$5, sar,$7,\
  rol,$0, ror,$1
  macro name a, b
    shift_x name, digit, a, b
  end macro
end irp

macro test_shift
  shl eax, cl
  shr ebx, cl
  rol edi, cl
  shr edx, 24
  sar dword [edi], 4
  rol eax, 16
  ror dword [ebx], cl
  shl dword [eax+$CAFEBABE], cl
end macro

;;;;;;;;; BASIC: ADD, SUB, CMP, XOR, ETC ;;;;;;;;;

; opcode rm8,r8 ; 30 /r
; opcode rmx,rx ; oso 31 /r
; opcode r8,rm8 ; 32 /r
; opcode rx,rmx ; oso 33 /r
; opcode rm8,i8 ; 80 /6 ib
; opcode rmx,ix ; oso 81 /6 ix
; opcode rmx,i8 ; oso 83 /6 ib
; opcode al,i8  ; 34 ib
; opcode eax,ix ; oso 35 ix

macro write_basic opcode, a, b
  local i_size, ext,\
    type1, type2, size1, size2,\
    index1, index2
  d_bit=0
  s_bit=0
  i_size=0
  ext=((opcode shr 3) and 111b)
  get_operands a, b,\
    type1, size1, index1,\
    type2, size2, index2
  i_size=size1
  if size1=2 | size2=2
    oso
  end if
  if size1>=2
    s_bit=1
  end if
  if type1='r' & type2='r'
    d_bit=1
    put_code opcode
    put_mode 11b, a, b
  else if type1='r' & type2='i'
    put_code $80
    put_mode 11b, ext, a
    put_dx b, i_size
  else if type1='r' & type2='m'
    d_bit=1
    put_code opcode
    get_mode b
    write_mode a
  else if type1='m' & type2='r'
    put_code opcode
    get_mode a
    write_mode b
  else if type1='m' & type2='i'
    put_code $80
    get_mode a
    write_mode ext
    put_dx b, i_size
  end if
end macro

irp <name, opcode>,\
  adc,$10, add,0, and,$20, cmp,$38,\
  or,$8, sbb,$18, sub,$28, xor,$30
  macro name a, b
    write_basic opcode, a, b
  end macro
end irp

macro test_basic
  add eax, ecx
  adc cl, dl
  sub dh, bh
  sbb cx, dx
  cmp bx, dx
  and eax, esi
  or ecx, edx
  xor esp, edi
  add ecx, $12345678
  sub edx, $ABCD1234
  adc ebx, $CAFEBABE
  sbb cx, $1234
  cmp dx, $ABCD
  and bx, $BABE
  or ah, $77
  xor cl, $77
  add ecx, [esi]
  adc edx, [ebp]
  sub edi, [$12345678]
  sbb eax, [edi+8]
  cmp ecx, [edi+$CAFEBABE]
  and eax, [ecx+edx]
  or ecx, [esi+ecx*2]
  xor edx, [ecx+edx*4]
  add ebx, [ecx+edx*4+$77]
  adc edi, [ecx+edx*8+$BABE1234]
  sub ax, [edx]
  sbb bx, [$12345678]
  cmp ch, [edi-$AAAAAA]
  and al, [ecx+edx*8+$BABE1234]
  or [ecx], ah
  xor [ecx+edx], cx
  add [ecx+edx*4], ebx
  adc byte [eax], $FF
  sub word [ecx+edx], $FFFF
  sbb dword [ecx+edx*8], $ABCD1234
  cmp ecx, [$CAFEBABE+edi*8]
end macro

;;;;;;;;;;;;;;;; MULTIPLY, DIVIDE ;;;;;;;;;;;;;;;;

; 2-DO:

; mul rm8     ; F6 /4
; imul rm8    ; F6 /5
; div rm8     ; F6 /6
; idiv rm8    ; F6 /7
; mul rmx     ; oso F7 /4
; div rmx     ; oso F7 /6
; idiv rmx    ; oso F7 /7
; imul rmx    ; oso F7 /5
; imul rx,rmx ; oso 0F AF /r
; imul rx,i8  ; oso 6B /r ib
; imul rx,ix  ; oso 69 /r ix

macro mul_div name, digit, a, b
  ; ...
end macro

macro imul a, b
  ; ...
end macro

macro test_mul_div
  mul ah
  imul dword [ecx]
  idiv ch
  mul ebx
  div edi
  idiv dword [edi+ecx*8+$CAFEBABE]
end macro