;----------------------------------------------------------------------------;
; RAZE-x86 Z80 emulator.
; version 1.06
;
; Copyright (c) 1999 Richard Mitton
;
; This may only be distributed as part of the complete RAZE package.
; See RAZE.TXT for license information.
;----------------------------------------------------------------------------;

%macro cglobal 1 
  global @UNDERSCORE@%1 
  %define %1 @UNDERSCORE@%1 
%endmacro

; You are not expected to understand this.


;----------------------------------------------------------------------------;
; Comment these in/out for faster speed (less accurate):
%define EMULATE_UNDOC_FLAGS      ; a couple of undocumented flags
%define EMULATE_BITS_3_5         ; bits 3/5 of the flags (undocumented)
%define EMULATE_WEIRD_STUFF      ; misc *very obscure* undocumented behaviour
%define EMULATE_R_REGISTER       ; precise R register (not usually needed)
;;;;%define USE_FETCH_CALLBACK       ; call a callback for every fetch (slow!)
;----------------------------------------------------------------------------;

; Registers:
; AF = ax (flags in AH)
; HL = bx
; BC = cx
; PC = si
; R = ebp
;
; GCC wants EBX, ESI, EDI, ESP, and EBP to be preserved at all times
; DH should always be clear between macros
; The high 16-bits of all registers should be clear at all times (except EBP)
; This assumes LAHF will set bit 1 of AH. (supposed to be undefined...)

BITS 32
SECTION .bss USE32

; Maximum number of memory handlers
%define MAX_MEM_HANDLERS 64

; The current context
align 16
context_start:
z80_AF  resd 1
z80_BC  resd 1
z80_DE  resd 1
z80_HL  resd 1
z80_IX  resd 1
z80_IY  resd 1
z80_PC  resd 1
z80_SP  resd 1
z80_AF2 resd 1
z80_BC2 resd 1
z80_DE2 resd 1
z80_HL2 resd 1
z80_IFF1   resb 1
z80_IFF2   resb 1
z80_R      resd 1
z80_R2     resd 1
z80_I      resb 1
z80_flag35 resb 1
z80_IM     resb 1
z80_IRQVector resb 1
z80_IRQLine   resb 1
z80_halted    resb 1
z80_Extra_Cycles resd 1
registers_end:

z80_Fetch  resd 256
z80_Read   resd (256*2)
z80_Write  resd (256*2)
z80_In     resd 1
z80_Out    resd 1
z80_RetI   resd 1
z80_Fetch_Callback  resd 1
_read_handlers       resd 4*MAX_MEM_HANDLERS
_write_handlers      resd 4*MAX_MEM_HANDLERS
context_end:
resd 1     ; safety gap, so z80_reset can use 32-bit transfers

; Variables
z80_ICount          resd 1
z80_Initial_ICount  resd 1
z80_TempICount      resd 1
z80_afterEI         resb 1

SECTION .text USE32
;----------------------------------------------------------------------------;
; Prepare to call a GCC function
%macro CALLGCC_START 0
   mov [z80_AF], ezAF        ; eax
   mov [z80_BC], ezBC        ; ecx
   mov [z80_PC], ezPC        ; esi
   mov [z80_R],  zR          ; ebp
%endmacro

; Return from a GCC function
%macro CALLGCC_END 0
   mov ezAF, [z80_AF]        ; eax
   mov ezBC, [z80_BC]        ; ecx
   mov ezPC, [z80_PC]        ; esi
   mov zR,   [z80_R]         ; ebp
%endmacro

; Read a byte from the PC, into DL
; GETBYTE
%macro GETBYTE 0
%ifdef USE_FETCH_CALLBACK
   CALLGCC_START
   push ezPC
   call [z80_Fetch_Callback]
   add esp, 4
   CALLGCC_END
%endif
   mov edx, ezPC
   shr edx, 8
   mov edi, [z80_Fetch+edx*4]

   mov dl, [edi+ezPC]
   inc zPC
   xor edi, edi
%endmacro

; Read a byte from the PC, sign-extended into DI
; GETDISP
%macro GETDISP 0
%ifdef USE_FETCH_CALLBACK
   CALLGCC_START
   push ezPC
   call [z80_Fetch_Callback]
   add esp, 4
   CALLGCC_END
%endif
   mov edx, ezPC
   shr edx, 8
   mov edx, [z80_Fetch+edx*4]

   movsx di, [edx+ezPC]
   inc zPC
   xor edx, edx
%endmacro

; Read a word from the PC, into DI
; GETWORD
%macro GETWORD 0
%ifdef USE_FETCH_CALLBACK
   CALLGCC_START
   push ezPC
   call [z80_Fetch_Callback]
   add esp, 4
   CALLGCC_END
%endif
   mov [z80_BC], ezBC
   mov edx, ezPC           ; low byte
   shr edx, 8
   mov edi, [z80_Fetch+edx*4]

   mov dl, [edi+ezPC]
   inc zPC
   
%ifdef USE_FETCH_CALLBACK
   push edx
   CALLGCC_START
   push ezPC
   call [z80_Fetch_Callback]
   add esp, 4
   CALLGCC_END
   pop edx
%endif
   mov ecx, ezPC           ; high byte
   shr ecx, 8
   mov edi, [z80_Fetch+ecx*4]

   mov dh, [edi+ezPC]
   inc zPC
   mov edi, edx
   mov ezBC, [z80_BC]
   xor edx, edx
%endmacro

; Read a byte from memory (DI) into DL
; MEMREAD
%macro MEMREAD 0
   CALLGCC_START
   mov edx, edi
   shr edx, 8
   mov ecx, [z80_Read+edx*8]

   test ecx, ecx     ; if handler exists, call it
   jnz %%handler
                     ; no handler, so go direct
   mov ecx, [z80_Read+edx*8+4]
   mov dl, [ecx+edi]
   jmp %%finish

%%handler:
   push edi
   call ecx
   xor edx, edx
   add esp, 4
   mov dl, al
%%finish:
   CALLGCC_END
%endmacro

; Write a byte in DL to memory (DI)
%macro MEMWRITE 0
   CALLGCC_START
   xor eax, eax
   mov al, dl

   mov edx, edi
   shr edx, 8
   mov ecx, [z80_Write+edx*8]

   test ecx, ecx        ; if handler exists, call it
   jnz %%handler
                        ; no handler, so go direct
   mov ecx, [z80_Write+edx*8+4]
   mov [ecx+edi], al
   jmp %%finish

%%handler:
   push eax
   push edi
   call ecx
   add esp, 8
%%finish:
   CALLGCC_END
   xor edx, edx
%endmacro

; Read a byte from port (DI) into DL
; IOREAD
%macro IOREAD 0
   push edi
   CALLGCC_START
   call [z80_In]
   xor edx, edx
   add esp, 4
   mov dl, al
   CALLGCC_END
%endmacro

; Write a byte in DL to port (DI)
%macro IOWRITE 0
   CALLGCC_START
   xor dh, dh
   push edx
   push edi
   call [z80_Out]
   add esp, 8
   CALLGCC_END
   xor edx, edx
%endmacro

; Align code to the default boundary
%macro ALIGN 0
   align 4
%endmacro

; Define an instruction
; DEF prefix, opcode, std_cycles, extra_cycles
%macro DEF 4
   ALIGN
   %define std_cycles %3
   %define extra_cycles %4
%{1}_%2:
%endmacro

; Subtract the clock cycles for this instruction from the ICount
%macro DO_CYCLES 0
   sub dword [z80_ICount], std_cycles
   jbe near z80_finish
%endmacro

; Subtract the extra clock cycles for this instruction from the ICount
%macro DO_EXTRA_CYCLES 0
   sub dword [z80_ICount], extra_cycles
   jbe near z80_finish
%endmacro

; Finish straight away
%macro EXIT 0
   jmp z80_finish
%endmacro

; Jump to the next instruction
%macro NEXT 0
%ifdef EMULATE_R_REGISTER
   inc zR
%endif
   GETBYTE
   jmp [OpTable+edx*4]
%endmacro

; Check the IRQ line, cause an interrupt if needed
%macro CHECK_IRQ 0
   cmp [z80_IRQLine], byte 0    ; check the IRQ line
   je near %%finish
   cmp [z80_IFF1], byte 0       ; check ints are enabled
   je near %%finish

   ; disable interrupts
   mov [z80_IFF1], byte 0
   mov [z80_IFF2], byte 0
%ifdef EMULATE_R_REGISTER
   inc zR
%endif

   mov [z80_halted], byte 0 ; Un-HALT the Z80

   mov edx, ezPC        ; write the PC
   dec zSP

   xchg dl, dh
   mov edi, ezSP
   MEMWRITE
   dec zSP
   mov edx, ezPC
   mov edi, ezSP
   MEMWRITE

   cmp [z80_IM], byte 0
   jne %%not_im0
   ; IM 0, 13 T-states
   add [z80_Extra_Cycles], dword 13
   mov dl, [z80_IRQVector]
   sub dl, 0c7h
   movzx ezPC, dl
   jmp %%finish

%%not_im0:
   cmp [z80_IM], byte 1
   jne %%not_im1
   ; IM 1, 13 T-states
   add [z80_Extra_Cycles], dword 13
   mov ezPC, 38h
   jmp %%finish

%%not_im1:
   ; IM 2, 19 T-states
   add [z80_Extra_Cycles], dword 19
   mov dl, [z80_IRQVector]
   mov dh, zI
   mov edi, edx
   MEMREAD
   inc di
   mov ezPC, edx
   MEMREAD
   xchg dl, dh
   or ezPC, edx
   xor dh, dh
%%finish:
%endmacro

;----------------------------------------------------------------------------;
; Helper functions

; Add A, %1
%macro DO_ADD 1
   add zA, %1
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zA
   %endif
   lahf
   jo %%set_overflow

   and zF, FLAG_S|FLAG_Z|FLAG_H|FLAG_C
   jmp %%finish

 %%set_overflow:
   and zF, FLAG_S|FLAG_Z|FLAG_H|FLAG_C
   or zF, FLAG_P
 %%finish:
%endmacro

; Sub A, %1
%macro DO_SUB 1
   sub zA, %1
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zA
   %endif
   lahf
   jo %%set_overflow

   and zF, FLAG_S|FLAG_Z|FLAG_H|FLAG_C|FLAG_N
   jmp %%finish

 %%set_overflow:
   or zF, FLAG_P
 %%finish:
%endmacro

; Adc A, %1
%macro DO_ADC 1
   ror zF, 1
   adc zA, %1
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zA
   %endif
   lahf
   jo %%set_overflow

   and zF, FLAG_S|FLAG_Z|FLAG_H|FLAG_C
   jmp %%finish

 %%set_overflow:
   and zF, FLAG_S|FLAG_Z|FLAG_H|FLAG_C
   or zF, FLAG_P
 %%finish:
%endmacro

; Sbc A, %1
%macro DO_SBC 1
   ror zF, 1
   sbb zA, %1
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zA
   %endif
   lahf
   jo %%set_overflow

   and zF, FLAG_S|FLAG_Z|FLAG_H|FLAG_C|FLAG_N
   jmp %%finish

 %%set_overflow:
   or zF, FLAG_P
 %%finish:
%endmacro

; And A, %1
%macro DO_AND 1
   and zA, %1
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zA
   %endif
   lahf
   and zF, ~(FLAG_N|FLAG_C)      ; clear N and carry
   or  zF, FLAG_H                ; set half-carry
%endmacro

; Or A, %1
%macro DO_OR 1
   or zA, %1
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zA
   %endif
   lahf
   and zF, ~(FLAG_N|FLAG_C|FLAG_H)  ; clear N, carry and half-carry
%endmacro

; Xor A, %1
%macro DO_XOR 1
   xor zA, %1
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zA
   %endif
   lahf
   and zF, ~(FLAG_N|FLAG_C|FLAG_H)  ; clear N, carry and half-carry
%endmacro

; Cp A, %1
%macro DO_CP 1
   %ifdef EMULATE_BITS_3_5
      mov ah, %1
      mov [z80_flag35], ah
   %endif
   cmp zA, %1  
   lahf
   jo %%overflow

   and zF, ~FLAG_P              ; clear the overflow
   or  zF, FLAG_N               ; set the N flag
   jmp %%finish

%%overflow: 
   or  zF, FLAG_P|FLAG_N          ; set the overflow and N flag
%%finish:
%endmacro

; Inc %1
%macro DO_INC 1
   and zF, FLAG_C      ; leave carry alone
   %ifnidn %1, dl
      mov dl, %1
   %endif
   or  zF, [INC_Table+edx]
   inc %1
   %ifdef EMULATE_BITS_3_5
      %ifnidn %1, dl
         inc dl
      %endif
      mov [z80_flag35], dl
   %endif
%endmacro

; Dec %1
%macro DO_DEC 1
   and zF, FLAG_C      ; leave carry alone
   %ifnidn %1, dl
      mov dl, %1
   %endif
   or  zF, [DEC_Table+edx]
   dec %1
   %ifdef EMULATE_BITS_3_5
      %ifnidn %1, dl
         dec dl
      %endif
      mov [z80_flag35], dl
   %endif
%endmacro

; Add16 %1, %2
%macro DO_ADD16 2
   %ifidn %2, dx
      %error "DX used as parameter for DO_ADD16!"
   %endif
   mov edi, e%2
   %ifdef EMULATE_UNDOC_FLAGS
      mov edx, e%1
   %endif
   and zF, FLAG_S|FLAG_Z|FLAG_P

   add %1, di     ; do it
   adc zF, 0      ; merge the new carry in

   %ifdef EMULATE_UNDOC_FLAGS
      xor edx, e%1     ; work out our new half-carry
      xor edx, edi
      and dh, FLAG_H
      or zF, dh
   %endif

   %ifdef EMULATE_BITS_3_5
      mov dl, h%1
      mov [z80_flag35], dl
   %endif
   %ifdef EMULATE_UNDOC_FLAGS
      xor dh, dh
   %endif
%endmacro

; Adc16 %1, %2
%macro DO_ADC16 2
   %ifidn %2, dx
      %error "DX used as parameter for DO_ADC16!"
   %endif                                     
   mov edi, e%2
   %ifdef EMULATE_UNDOC_FLAGS
      mov edx, e%1
   %endif
   ror zF, 1      ; load the carry
   adc %1, %2
   lahf
   jo %%overflow

   and zF, ~(FLAG_N|FLAG_H|FLAG_P)      ; clear N,H and overflow
   jmp %%finish

%%overflow:
   and zF, ~(FLAG_N|FLAG_H)   ; clear N and H
   or  zF,  FLAG_P            ; set overflow
%%finish:

   %ifdef EMULATE_UNDOC_FLAGS
      xor edx, e%1     ; work out our new half-carry
      xor edx, edi
      and dh, FLAG_H
      or zF, dh
   %endif

   %ifdef EMULATE_BITS_3_5
      mov dl, h%1
      mov [z80_flag35], dl
   %endif
   %ifdef EMULATE_UNDOC_FLAGS
      xor dh, dh
   %endif
%endmacro

; Sbc16 %1, %2
%macro DO_SBC16 2
%ifidn %2, dx
   %error "DX used as parameter for DO_SBC16!"
%endif                                     
   %ifdef EMULATE_UNDOC_FLAGS
      mov edi, e%2
   %endif
   mov edx, e%1
   ror zF, 1      ; load the carry
   sbb %1, %2
   lahf
   jo %%overflow

   and zF, ~(FLAG_P|FLAG_H)   ; clear H and overflow
   or  zF,  FLAG_N            ; set N
   jmp %%finish

%%overflow:
   or zF, FLAG_N|FLAG_P ; set overflow and N
   and zF, ~FLAG_H      ; clear H
%%finish:

   %ifdef EMULATE_UNDOC_FLAGS
      xor edx, e%1     ; work out our new half-carry
      xor edx, edi
      and dh, FLAG_H
      or zF, dh
   %endif

   %ifdef EMULATE_BITS_3_5
      mov dl, h%1
      mov [z80_flag35], dl
   %endif
   %ifdef EMULATE_UNDOC_FLAGS
      xor dh, dh
   %endif
%endmacro

; Rotate method, reg8
%macro DO_ROT 2
   %ifidn %1, RLA
      mov dl, zF
      and dl, FLAG_S|FLAG_Z|FLAG_P
      sahf
      rcl zA, 1
      adc dl, 0
      %ifdef EMULATE_BITS_3_5
         mov [z80_flag35], zA
      %endif
      mov zF, dl
   %elifidn %1, RRA
      mov dl, zF
      and dl, FLAG_S|FLAG_Z|FLAG_P
      sahf
      rcr zA, 1
      adc dl, 0
      %ifdef EMULATE_BITS_3_5
         mov [z80_flag35], zA
      %endif
      mov zF, dl
   %elifidn %1, RLCA
      and zF, FLAG_S|FLAG_Z|FLAG_P
      rol zA, 1
      adc zF, 0
      %ifdef EMULATE_BITS_3_5
         mov [z80_flag35], zA
      %endif
   %elifidn %1, RRCA
      and zF, FLAG_S|FLAG_Z|FLAG_P
      ror zA, 1
      adc zF, 0
      %ifdef EMULATE_BITS_3_5
         mov [z80_flag35], zA
      %endif
   %else
      %ifnidn %2, dl
         mov dl, %2
      %endif

      %ifidn %1, RLC
         rol dl, 1
      %elifidn %1, RRC
         ror dl, 1
      %elifidn %1, RL
         sahf
         rcl dl, 1
      %elifidn %1, RR
         sahf
         rcr dl, 1
      %endif

      adc dh, dh
      test dl, dl
      lahf
      and zF, FLAG_S|FLAG_Z|FLAG_P
      or zF, dh
      %ifdef EMULATE_BITS_3_5
         mov [z80_flag35], dl
      %endif
      %ifnidn %2, dl
         mov %2, dl
      %endif
      xor dh, dh
   %endif
%endmacro

; Shift method, reg8
%macro DO_SHF 2
   %ifnidn %2, dl
      mov dl, %2
   %endif

   %ifidn %1, SLA
      add dl, dl
      adc dh, dh
   %elifidn %1, SLL
      add dl, dl
      adc dh, dh
      inc dl
   %elifidn %1, SRA
      sar dl, 1
      adc dh, dh
   %elifidn %1, SRL
      shr dl, 1
      adc dh, dh
   %endif

   test dl, dl
   lahf
   and zF, FLAG_S|FLAG_Z|FLAG_P
   or zF, dh
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], dl
   %endif
   %ifnidn %2, dl
      mov %2, dl
   %endif
   xor dh, dh
%endmacro

; BIT r,n      n = bit (0-7)
;              r = 8-bit register
%macro DO_BIT 2
   and zF, FLAG_C           
   test %1, (1 << %2)
  %ifdef EMULATE_BITS_3_5
   ; bit 3 and 5 are set from the register
   ; (actually they are bizarrely set, but
   ; there's no point emulating that, it's just
   ; plain silly...)
   %ifnidn %1, dl
    mov dl, %1    ; put in DL if not there already
   %endif
   mov [z80_flag35], dl
  %endif

   jz %%bit_is_clear
   %ifidn %2, 7
      or zF, FLAG_H|FLAG_S       ; set sign bit, if testing bit 7
   %else
      or zF, FLAG_H              ; set half-carry, and parity
   %endif
   jmp %%finish

%%bit_is_clear:
   or zF, FLAG_H|FLAG_Z|FLAG_P   ; set half-carry, parity, and zero
%%finish:
%endmacro

; SET r,n      n = bit (0-7)
;              r = 8-bit register
%macro DO_SET 2
   or %1, (1 << %2)
%endmacro

; RES r,n      n = bit (0-7)
;              r = 8-bit register
%macro DO_RES 2
   and %1, ~(1 << %2)
%endmacro

;----------------------------------------------------------------------------;
; Here are the interface functions:

; Move a register to another
; LD_R_R d8, s8      s8 = src reg, d8 = dest reg
%macro LD_R_R 2
%ifnidn %1, %2
   %ifidn %2, D
      mov dl, zD
      mov z%1, dl
   %elifidn %2, E
      mov dl, zE
      mov z%1, dl
   %else
      mov z%1, z%2
   %endif
%endif
   DO_CYCLES
   NEXT
%endmacro

; Move a register to another (IXh/IXl)
; LD_XYr_XYr d8, s8      s8 = src reg, d8 = dest reg
%macro LD_XYr_XYr 2
%ifnidn %1, %2
   mov dl, z%2
   mov z%1, dl
%endif
   DO_CYCLES
   NEXT
%endmacro

; Move an immediate byte into a register
; LD_R_N d8      d8 = dest reg
%macro LD_R_N 1
   GETBYTE
   mov z%1, dl
   DO_CYCLES
   NEXT
%endmacro

; Move from (RR) into a register
; LD_R_mRR d8, s16      d8 = dest reg, s16 = src reg
%macro LD_R_mRR 2
   mov edi, ez%2
   MEMREAD
   mov z%1, dl
   DO_CYCLES
   NEXT
%endmacro

; Move from (XY+dd) into a register
; LD_R_mXY d8, s16      d8 = dest reg, s16 = src reg
%macro LD_R_mXY 2
   GETDISP
   add di, z%2
   MEMREAD
   mov z%1, dl
   DO_CYCLES
   NEXT
%endmacro

; Move from a register into (RR)
; LD_mRR_R d16, s8      d16 = destreg, s8 = src reg
%macro LD_mRR_R 2
   mov edi, ez%1
   mov dl, z%2
   MEMWRITE
   DO_CYCLES
   NEXT
%endmacro

; Move from a register into (XY+dd)
; LD_mXY_R d16, s8      d16 = dest reg, s8 = src reg
%macro LD_mXY_R 2
   GETDISP
   add di, z%1
   mov dl, z%2
   MEMWRITE
   DO_CYCLES
   NEXT
%endmacro

; Move an immediate into (RR)
; LD_mRR_N d16          d16 = dest reg
%macro LD_mRR_N 1
   GETBYTE
   mov edi, ez%1
   MEMWRITE
   DO_CYCLES
   NEXT
%endmacro

; Move an immediate into (XY+dd)
; LD_mXY_N d16          d16 = dest reg
%macro LD_mXY_N 1
   GETDISP
   add di, z%1
   push edi
   GETBYTE
   pop edi
   MEMWRITE
   DO_CYCLES
   NEXT
%endmacro

; Move from (NN) into a register
; LD_R_mNN d8          d8 = dest reg
%macro LD_R_mNN 1
   GETWORD
   MEMREAD
   mov z%1, dl
   DO_CYCLES
   NEXT
%endmacro

; Move from a register into (NN)
; LD_mNN_R s8          s8 = src reg
%macro LD_mNN_R 1
   GETWORD
   mov dl, z%1
   MEMWRITE
   DO_CYCLES
   NEXT
%endmacro

; Move the Interrupt register into a register
; LD_R_I d8             d8 = dest reg
%macro LD_R_I 1
   mov z%1, zI
   and zF, FLAG_C       ; keep old carry
   mov dl, zF

   test z%1, z%1
	lahf
   and zF, FLAG_S|FLAG_Z   ; set S/Z from the I register
   or zF, dl
   or zF, [z80_IFF2]
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], z%1
   %endif
   DO_CYCLES
   NEXT
%endmacro

; Move a register into the Interrupt register
; LD_I_R s8             s8 = src reg
%macro LD_I_R 1
   mov zI, z%1
   DO_CYCLES
   NEXT
%endmacro

; Move the Refresh register into a register
; LD_R_Rf d8             d8 = dest reg
%macro LD_R_Rf 1
   mov edx, [z80_R2]
   and edx, 80h         ; take bits 0-6 from R, bit 7 from R2

   %ifndef EMULATE_R_REGISTER
      ; attempt to fake the R-register
      ; take the ICount, invert it, divide by 4
      ; this produces an increasing counter, roughly proportional to
      ; the number of instructions done
      mov zR, [z80_ICount]
      not zR
      shr zR, 2
   %endif

   and zR, 7fh
   or edx, zR
   mov z%1, dl

   mov dl, zF
   and dl, FLAG_C       ; keep the old carry

   test z%1, z%1
	lahf
   and zF, FLAG_S|FLAG_Z
   or zF, dl
   or zF, [z80_IFF2]
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], z%1
   %endif
   DO_CYCLES
   NEXT
%endmacro

; Move a register into the Refresh register
; LD_Rf_R s8             s8 = src reg
%macro LD_Rf_R 1
   mov dl, z%1
   mov zR, edx
   mov [z80_R2], edx
   DO_CYCLES
   NEXT
%endmacro

; Move a 16-bit immediate into a register
; LD_RR_NN d16             d16 = dest reg
%macro LD_RR_NN 1
   GETWORD
   mov ez%1, edi
   DO_CYCLES
   NEXT
%endmacro

; Move from (NN) into a 16-bit register
; LD_RR_mNN d16             d16 = dest reg
%macro LD_RR_mNN 1
   GETWORD
   MEMREAD
   mov lz%1, dl
   inc di
   MEMREAD
   mov hz%1, dl
   DO_CYCLES
   NEXT
%endmacro

; Move from a 16-bit register into (NN)
; LD_mNN_RR s16             s16 = src reg
%macro LD_mNN_RR 1
   GETWORD
   mov dl, lz%1
   MEMWRITE
   inc di
   mov dl, hz%1
   MEMWRITE
   DO_CYCLES
   NEXT
%endmacro

; Move from a 16-bit register into another
; LD_RR_RR d16, s16             s16 = src reg, d16 = dest reg
%macro LD_RR_RR 2
   mov ez%1, ez%2
   DO_CYCLES
   NEXT
%endmacro

; Move from a 16-bit register into another
; LD_RR_XY d16, s16             s16 = src reg, d16 = dest reg
%macro LD_RR_XY 2
   mov edi, ez%2
   mov ez%1, edi
   DO_CYCLES
   NEXT
%endmacro

; Push a word on the stack
; PUSH_RR      s16               s16 = src reg
%macro PUSH_RR 1
   mov dl, hz%1
   dec zSP
   mov edi, ezSP
   MEMWRITE
   dec zSP
   mov edi, ezSP
   %ifidn %1, AF
      ; merge bits 3 and 5 in
      and zF, ~(FLAG_3|FLAG_5)
      mov dl, [z80_flag35]
      and dl, FLAG_3|FLAG_5
      or zF, dl
   %endif
   mov dl, lz%1
   MEMWRITE
   DO_CYCLES
   NEXT
%endmacro

; Pop a word from the stack
; POP_RR      s16               s16 = src reg
%macro POP_RR 1
   mov edi, ezSP
   MEMREAD
   %ifidn %1, AF
      mov [z80_flag35], dl      ; take out bits 3/5
   %endif
   mov lz%1, dl
   inc zSP
   mov edi, ezSP
   MEMREAD
   mov hz%1, dl
   inc zSP
   DO_CYCLES
   NEXT
%endmacro

; Exchange 2 16-bit registers
; EX_RR_RR      s16, d16      s16 = src reg, d16 = dest reg
%macro EX_RR_RR 2
   xchg ez%1, ez%2
   DO_CYCLES
   NEXT
%endmacro

; Exchange AF with AF'
; EX_AF_AF2
%macro EX_AF_AF2 0
   ; Merge bits 3+5 of the flags in first
   and zF, ~(FLAG_3|FLAG_5)
   mov dl, [z80_flag35]
   and dl, FLAG_3|FLAG_5
   or zF, dl
   xchg ezAF, ezAF2

   ; Un-merge the flags again
   mov [z80_flag35], zF
   DO_CYCLES
   NEXT
%endmacro

; Exchange the 16-bit registers with their primes
; EXX
%macro EXX 0
   mov edi, ezDE
   xchg ezBC, ezBC2
   xchg ezHL, ezHL2
   xchg edi, ezDE2
   mov ezDE, edi
   DO_CYCLES
   NEXT
%endmacro

; Exchange a 16-bit register with (RR)
; EX_RR_mRR     d16, m16      d16 = dest reg, m16 = memory reg
%macro EX_RR_mRR 2
   mov edi, ez%2     ; swap low byte
   MEMREAD
   xchg dl, lz%1
   MEMWRITE

   inc di            ; swap high byte
   MEMREAD
   xchg dl, hz%1
   MEMWRITE

   DO_CYCLES
   NEXT
%endmacro

; Move from (HL) to (DE), post-increment
; LDI
%macro LDI 0
   mov edi, ezHL
   MEMREAD
   %ifdef EMULATE_BITS_3_5
      push edx
   %endif
   mov edi, ezDE
   MEMWRITE

   %ifdef EMULATE_BITS_3_5
      pop edx
      add dl, zA
      mov dh, dl
      shl dl, 4
      and edx, FLAG_5 | (FLAG_3 << 8)
      or dl, dh
      mov [z80_flag35], dl
      xor dh, dh
   %endif

   and zF, FLAG_S|FLAG_Z|FLAG_C
   inc zHL
   inc zDE
   dec zBC
   jz %%ldi_zero
   or zF, FLAG_P
%%ldi_zero:     
   DO_CYCLES
   NEXT
%endmacro

; Move from (HL) to (DE), post-decrement
; LDD
%macro LDD 0
   mov edi, ezHL
   MEMREAD
   %ifdef EMULATE_BITS_3_5
      push edx
   %endif
   mov edi, ezDE
   MEMWRITE

   %ifdef EMULATE_BITS_3_5
      pop edx
      add dl, zA
      mov dh, dl
      shl dl, 4
      and edx, FLAG_5 | (FLAG_3 << 8)
      or dl, dh
      mov [z80_flag35], dl
      xor dh, dh
   %endif

   and zF, FLAG_S|FLAG_Z|FLAG_C
   dec zHL
   dec zDE
   dec zBC
   jz %%ldd_zero
   or zF, FLAG_P
%%ldd_zero:     
   DO_CYCLES
   NEXT
%endmacro

; Move from (HL) to (DE), post-increment, repeat
; LDIR
%macro LDIR 0
%ifdef EMULATE_WEIRD_STUFF
   ; Code marked with CG is from Charles Gallagher, to fix the
   ; Spectrum game 'Quazatron'
   push ezPC                     ; CG
   sub zPC, 2                    ; CG
   mov edi, ezPC                 ; CG
   shr edi, 8                    ; CG
   add ezPC, [z80_Fetch+edi*4]  ; CG
%endif

%%ldir_loop:
%ifdef EMULATE_WEIRD_STUFF
   cmp [ezPC], word 0b0edh       ; CG - check for self modifiying code!
   jne near %%ldir_modified
%endif

   mov edi, ezHL
   MEMREAD
   %ifdef EMULATE_BITS_3_5
      push edx
   %endif
   mov edi, ezDE
   MEMWRITE
   %ifdef EMULATE_BITS_3_5
      pop edx
   %endif

   inc zHL
   inc zDE
   dec zBC
   jz %%ldir_zero

%ifdef EMULATE_R_REGISTER
   inc zR
%endif
   sub [z80_ICount], dword extra_cycles
   ja near %%ldir_loop

%ifdef EMULATE_WEIRD_STUFF
   pop ezPC       ; CG
%endif

   and zF, FLAG_S|FLAG_Z|FLAG_C
   %ifdef EMULATE_BITS_3_5
      add dl, zA
      mov dh, dl
      shl dl, 4
      and edx, FLAG_5 | (FLAG_3 << 8)
      or dl, dh
      mov [z80_flag35], dl
      xor dh, dh
   %endif
   or zF, FLAG_P
   sub zPC, 2           ; rewind to instruction start
   EXIT

%ifdef EMULATE_WEIRD_STUFF
%%ldir_modified:
   sub [esp], word 2    ; CG
%endif

%%ldir_zero:
%ifdef EMULATE_WEIRD_STUFF
   pop ezPC             ; CG
%endif
   and zF, FLAG_S|FLAG_Z|FLAG_C
   %ifdef EMULATE_BITS_3_5
      add dl, zA
      mov dh, dl
      shl dl, 4
      and edx, FLAG_5 | (FLAG_3 << 8)
      or dl, dh
      mov [z80_flag35], dl
      xor dh, dh
   %endif
   DO_CYCLES
   NEXT
%endmacro

; Move from (HL) to (DE), post-decrement, repeat
; LDDR
%macro LDDR 0
%%lddr_loop:
   mov edi, ezHL
   MEMREAD
   %ifdef EMULATE_BITS_3_5
      push edx
   %endif
   mov edi, ezDE
   MEMWRITE
   %ifdef EMULATE_BITS_3_5
      pop edx
   %endif

   dec zHL
   dec zDE
   dec zBC
   jz %%lddr_zero

%ifdef EMULATE_R_REGISTER
   inc zR
%endif
   sub [z80_ICount], dword extra_cycles
   ja near %%lddr_loop

   and zF, FLAG_S|FLAG_Z|FLAG_C
   %ifdef EMULATE_BITS_3_5
      add dl, zA
      mov dh, dl
      shl dl, 4
      and edx, FLAG_5 | (FLAG_3 << 8)
      or dl, dh
      mov [z80_flag35], dl
      xor dh, dh
   %endif
   or zF, FLAG_P
   sub zPC, 2           ; rewind to instruction start
   EXIT

%%lddr_zero:
   and zF, FLAG_S|FLAG_Z|FLAG_C
   %ifdef EMULATE_BITS_3_5
      add dl, zA
      mov dh, dl
      shl dl, 4
      and edx, FLAG_5 | (FLAG_3 << 8)
      or dl, dh
      mov [z80_flag35], dl
      xor dh, dh
   %endif
   DO_CYCLES
   NEXT
%endmacro

; Compare A against (HL), post-increment
; CPI
%macro CPI 0
   mov edi, ezHL
   MEMREAD

   and zF, FLAG_C       ; keep the old carry
   mov dh, zF
   cmp zA, dl          
   lahf
   and zF, FLAG_S|FLAG_Z|FLAG_H|FLAG_N
   or zF, dh            ; SZHN from compare, C unchanged, rest are zero

   inc zHL
   dec zBC
   jz %%cpi_zero
   or zF, FLAG_P        ; set P if BC != 0
%%cpi_zero:

   %ifdef EMULATE_BITS_3_5
      mov dh, zA
      sub dh, dl
      test zF, FLAG_H
      jz %%cpi_hzero
      dec dh
   %%cpi_hzero:

      mov dl, dh
      shl dl, 4
      and edx, FLAG_5 | (FLAG_3 << 8)
      or dl, dh
      mov [z80_flag35], dl
   %endif
   xor dh, dh

   DO_CYCLES
   NEXT
%endmacro

; Compare A against (HL), post-decrement
; CPD
%macro CPD 0
   mov edi, ezHL
   MEMREAD

   and zF, FLAG_C       ; keep the old carry
   mov dh, zF
   cmp zA, dl          
   lahf
   and zF, FLAG_S|FLAG_Z|FLAG_H|FLAG_N
   or zF, dh            ; SZHN from compare, C unchanged, rest are zero

   dec zHL
   dec zBC
   jz %%cpd_zero
   or zF, FLAG_P        ; set P if BC != 0
%%cpd_zero:

   %ifdef EMULATE_BITS_3_5
      mov dh, zA
      sub dh, dl
      test zF, FLAG_H
      jz %%cpd_hzero
      dec dh
   %%cpd_hzero:
      
      mov dl, dh
      shl dl, 4
      and edx, FLAG_5 | (FLAG_3 << 8)
      or dl, dh
      mov [z80_flag35], dl
   %endif
   xor dh, dh

   DO_CYCLES
   NEXT
%endmacro

; Compare A against (HL), post-increment, repeat
; CPIR
%macro CPIR 0
   and zF, FLAG_C       ; keep the old carry
%%cpir_loop:
   mov edi, ezHL
   MEMREAD

   inc zHL
   dec zBC
   jz %%cpir_end_bc       ; end due to BC=0

%ifdef EMULATE_R_REGISTER
   inc zR
%endif
   cmp zA, dl
   je %%cpir_end_equal    ; end due to A=(HL)

   sub [z80_ICount], dword extra_cycles
   ja %%cpir_loop

   ; out of cycles, but not finished
   mov dh, zF
   cmp zA, dl
   lahf
   and zF, FLAG_S|FLAG_Z|FLAG_H|FLAG_N
   or zF, dh            ; SZHN from compare, C unchanged, rest are zero

   %ifdef EMULATE_BITS_3_5
      mov dh, zA
      sub dh, dl
      test zF, FLAG_H
      jz %%cpir_hzero
      dec dh
   %%cpir_hzero:

      mov dl, dh
      shl dl, 4
      and edx, FLAG_5 | (FLAG_3 << 8)
      or dl, dh
      mov [z80_flag35], dl
   %endif
   or zF, FLAG_P        ; set P to indicate not finished
   sub zPC, 2           ; rewind to instruction start
   xor dh, dh
   EXIT

%%cpir_end_equal:
   or zF, FLAG_P        ; set P to indicate not finished
%%cpir_end_bc:
   mov dh, zF
   cmp zA, dl
   lahf
   and zF, FLAG_S|FLAG_Z|FLAG_H|FLAG_N
   or zF, dh            ; SZHN from compare, C unchanged, rest are zero

   %ifdef EMULATE_BITS_3_5
      mov dh, zA
      sub dh, dl
      test zF, FLAG_H
      jz %%cpir_hzero2
      dec dh
   %%cpir_hzero2:

      mov dl, dh
      shl dl, 4
      and edx, FLAG_5 | (FLAG_3 << 8)
      or dl, dh
      mov [z80_flag35], dl
   %endif
   xor dh, dh
   DO_CYCLES
   NEXT
%endmacro

; Compare A against (HL), post-decrement, repeat
; CPDR
%macro CPDR 0
   and zF, FLAG_C       ; keep the old carry
%%cpdr_loop:
   mov edi, ezHL
   MEMREAD

   dec zHL
   dec zBC
   jz %%cpdr_end_bc       ; end due to BC=0

%ifdef EMULATE_R_REGISTER
   inc zR
%endif
   cmp zA, dl
   je %%cpdr_end_equal    ; end due to A=(HL)

   sub [z80_ICount], dword extra_cycles
   ja %%cpdr_loop

   ; out of cycles, but not finished
   mov dh, zF
   cmp zA, dl
   lahf
   and zF, FLAG_S|FLAG_Z|FLAG_H|FLAG_N
   or zF, dh            ; SZHN from compare, C unchanged, rest are zero

   %ifdef EMULATE_BITS_3_5
      mov dh, zA
      sub dh, dl
      test zF, FLAG_H
      jz %%cpdr_hzero
      dec dh
   %%cpdr_hzero:

      mov dl, dh
      shl dl, 4
      and edx, FLAG_5 | (FLAG_3 << 8)
      or dl, dh
      mov [z80_flag35], dl
   %endif
   or zF, FLAG_P        ; set P to indicate not finished
   sub zPC, 2           ; rewind to instruction start
   xor dh, dh
   EXIT

%%cpdr_end_equal:
   or zF, FLAG_P        ; set P to indicate not finished
%%cpdr_end_bc:
   mov dh, zF
   cmp zA, dl
   lahf
   and zF, FLAG_S|FLAG_Z|FLAG_H|FLAG_N
   or zF, dh            ; SZHN from compare, C unchanged, rest are zero

   %ifdef EMULATE_BITS_3_5
      mov dh, zA
      sub dh, dl
      test zF, FLAG_H
      jz %%cpdr_hzero2
      dec dh
   %%cpdr_hzero2:

      mov dl, dh
      shl dl, 4
      and edx, FLAG_5 | (FLAG_3 << 8)
      or dl, dh
      mov [z80_flag35], dl
   %endif
   xor dh, dh
   DO_CYCLES
   NEXT
%endmacro

; Perform arithmetic on the A register
; ART_R method, src8
%macro ART_R 2
   %ifidn %1, ADD
      DO_ADD z%2
   %elifidn %1, ADC
      DO_ADC z%2
   %elifidn %1, SUB
      DO_SUB z%2
   %elifidn %1, SBC
      DO_SBC z%2
   %endif

   DO_CYCLES
   NEXT
%endmacro

; Perform arithmetic on the A register (using an immediate)
; ART_N method
%macro ART_N 1
   GETBYTE
   %ifidn %1, ADD
      DO_ADD dl
   %elifidn %1, ADC
      DO_ADC dl
   %elifidn %1, SUB
      DO_SUB dl
   %elifidn %1, SBC
      DO_SBC dl
   %endif

   DO_CYCLES
   NEXT
%endmacro

; Perform arithmetic on the A register, using (XY)
; ART_mXY method, src16
%macro ART_mXY 2
   GETDISP
   add di, z%2
   MEMREAD
   %ifidn %1, ADD
      DO_ADD dl
   %elifidn %1, ADC
      DO_ADC dl
   %elifidn %1, SUB
      DO_SUB dl
   %elifidn %1, SBC
      DO_SBC dl
   %endif

   DO_CYCLES
   NEXT
%endmacro

; Perform arithmetic on the A register using (RR)
; ART_mRR method, src16
%macro ART_mRR 2
   mov edi, ez%2
   MEMREAD
   %ifidn %1, ADD
      DO_ADD dl
   %elifidn %1, ADC
      DO_ADC dl
   %elifidn %1, SUB
      DO_SUB dl
   %elifidn %1, SBC
      DO_SBC dl
   %endif

   DO_CYCLES
   NEXT
%endmacro

; Perform logic on the A register
; LOG_R method, src8
%macro LOG_R 2
   %ifidn %1, AND
      DO_AND z%2
   %elifidn %1, OR
      DO_OR z%2
   %elifidn %1, XOR
      DO_XOR z%2
   %endif

   DO_CYCLES
   NEXT
%endmacro

; Perform logic on the A register (using an immediate)
; LOG_N method
%macro LOG_N 1
   GETBYTE
   %ifidn %1, AND
      DO_AND dl
   %elifidn %1, OR
      DO_OR dl
   %elifidn %1, XOR
      DO_XOR dl
   %endif

   DO_CYCLES
   NEXT
%endmacro

; Perform logic on the A register, using (RR)
; LOG_mRR method, src16
%macro LOG_mRR 2
   mov edi, ez%2
   MEMREAD
   %ifidn %1, AND
      DO_AND dl
   %elifidn %1, OR
      DO_OR dl
   %elifidn %1, XOR
      DO_XOR dl
   %endif

   DO_CYCLES
   NEXT
%endmacro

; Perform logic on the A register, using (XY+dd)
; LOG_mXY method, src16
%macro LOG_mXY 2
   GETDISP
   add di, z%2
   MEMREAD
   %ifidn %1, AND
      DO_AND dl
   %elifidn %1, OR
      DO_OR dl
   %elifidn %1, XOR
      DO_XOR dl
   %endif

   DO_CYCLES
   NEXT
%endmacro

; Compare against the A register
; CP_R src8
%macro CP_R 1
   DO_CP z%1

   DO_CYCLES
   NEXT
%endmacro

; Compare against the A register, using an immediate
; CP_N
%macro CP_N 0
   GETBYTE
   DO_CP dl

   DO_CYCLES
   NEXT
%endmacro

; Compare against the A register, using (RR)
; CP_mRR src16
%macro CP_mRR 1
   mov edi, ez%1
   MEMREAD
   DO_CP dl

   DO_CYCLES
   NEXT
%endmacro

; Compare against the A register, using (XY+dd)
; CP_mXY src16
%macro CP_mXY 1
   GETDISP
   add di, z%1
   MEMREAD
   DO_CP dl

   DO_CYCLES
   NEXT
%endmacro

; Inc/dec a register
; DECINC_R method, reg8
%macro DECINC_R 2
   %ifidn %1, INC
      DO_INC z%2
   %elifidn %1, DEC
      DO_DEC z%2
   %endif

   DO_CYCLES
   NEXT
%endmacro

; Inc/dec (RR)
; DECINC_mRR method
%macro DECINC_mRR 2
   mov edi, ez%2
   MEMREAD
   %ifidn %1, INC
      DO_INC dl
   %elifidn %1, DEC
      DO_DEC dl
   %endif
   MEMWRITE

   DO_CYCLES
   NEXT
%endmacro

; Inc/dec (XY+dd)
; DECINC_mXY method
%macro DECINC_mXY 2
   GETDISP
   add di, z%2
   MEMREAD
   %ifidn %1, INC
      DO_INC dl
   %elifidn %1, DEC
      DO_DEC dl
   %endif
   MEMWRITE

   DO_CYCLES
   NEXT
%endmacro

; Add two 16-bit registers
; ADD_RR_RR dest16, src16
%macro ADD_RR_RR 2
   DO_ADD16 z%1, z%2

   DO_CYCLES
   NEXT
%endmacro

; Add two 16-bit registers, with carry
; ADC_RR_RR dest16, src16
%macro ADC_RR_RR 2
   DO_ADC16 z%1, z%2

   DO_CYCLES
   NEXT
%endmacro

; Subtract two 16-bit registers, with carry
; SBC_RR_RR dest16, src16
%macro SBC_RR_RR 2
   DO_SBC16 z%1, z%2

   DO_CYCLES
   NEXT
%endmacro

; Inc/dec a 16-bit register
; DECINC_RR method, reg16
%macro DECINC_RR 2
   %ifidn %1, INC
      inc z%2
   %elifidn %1, DEC
      dec z%2
   %endif

   DO_CYCLES
   NEXT
%endmacro

; Rotate a register
; ROT_R method, reg8
%macro ROT_R 2
   DO_ROT %1, z%2

   DO_CYCLES
   NEXT
%endmacro

; Rotate (RR)
; ROT_mRR method, src16
%macro ROT_mRR 2
   mov edi, ez%2
   MEMREAD
   DO_ROT %1, dl
   MEMWRITE

   DO_CYCLES
   NEXT
%endmacro

; Rotate (XY+dd)
; ROT_mXY method
%macro ROT_mXY 1
   MEMREAD
   DO_ROT %1, dl
   MEMWRITE

   DO_CYCLES
   NEXT
%endmacro

; Rotate (XY+dd), and store into a register
; ROT_mXY_R method, dest8
%macro ROT_mXY_R 2
   MEMREAD
   DO_ROT %1, dl
   mov z%2, dl
   MEMWRITE

   DO_CYCLES
   NEXT
%endmacro

; Shift a register
; SHF_R method, reg8
%macro SHF_R 2
   DO_SHF %1, z%2

   DO_CYCLES
   NEXT
%endmacro

; Shift (RR)
; SHF_mRR method, src16
%macro SHF_mRR 2
   mov edi, ez%2
   MEMREAD
   DO_SHF %1, dl
   MEMWRITE

   DO_CYCLES
   NEXT
%endmacro

; Shift (XY+dd)
; SHF_mXY method
%macro SHF_mXY 1
   MEMREAD
   DO_SHF %1, dl
   MEMWRITE

   DO_CYCLES
   NEXT
%endmacro

; Shift (XY+dd), and store into a register
; SHF_mXY_R method, dest8
%macro SHF_mXY_R 2
   MEMREAD
   DO_SHF %1, dl
   mov z%2, dl
   MEMWRITE

   DO_CYCLES
   NEXT
%endmacro

; Rotate A/(HL) left, via nibbles
; RLD
%macro RLD 0
   mov edi, ezHL
   MEMREAD
   mov dh, zA
   and zF, FLAG_C
   shl dh, 4
   rol dx, 4
   and zA, 0f0h
   or zA, dh
   mov dh, zF
   lahf
   and zF, FLAG_S|FLAG_Z|FLAG_P
   or zF, dh
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zA
   %endif
   MEMWRITE

   DO_CYCLES
   NEXT
%endmacro

; Rotate A/(HL) right, via nibbles
; RRD
%macro RRD 0
   mov edi, ezHL
   MEMREAD
   mov dh, zA
   and zF, FLAG_C
   ror dx, 4
   shr dh, 4
   and zA, 0f0h
   or zA, dh
   mov dh, zF
   lahf
   and zF, FLAG_S|FLAG_Z|FLAG_P
   or zF, dh
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zA
   %endif
   MEMWRITE

   DO_CYCLES
   NEXT
%endmacro

; Bit-test a register
; BIT_R src8, bit
%macro BIT_R_b 2
   DO_BIT z%1, %2

   DO_CYCLES
   NEXT
%endmacro

; Bit-test (RR)
; BIT_mRR_b src16, bit
%macro BIT_mRR_b 2
   and zF, FLAG_C
   mov edi, ez%1
   MEMREAD

   test dl, (1 << %2)

   ; bit 3 and 5 are always cleared
   ; (actually they are bizarrely set, but
   ; there's no point emulating that, it's just
   ; plain silly...)

   jz %%bit_is_clear
   %ifidn %2, 7
      or zF, FLAG_H|FLAG_S       ; set sign bit, if testing bit 7
   %else
      or zF, FLAG_H              ; set half-carry, and parity
   %endif
   jmp %%finish

%%bit_is_clear:
   or zF, FLAG_H|FLAG_Z|FLAG_P   ; set half-carry, parity, and zero
%%finish:

   DO_CYCLES
   NEXT
%endmacro

; Bit-test (XY+dd)
; BIT_mXY_b bit
%macro BIT_mXY_b 1
  %ifdef EMULATE_BITS_3_5
   mov edx, edi
   mov [z80_flag35], dh
  %endif

   MEMREAD
   and zF, FLAG_C           
   test dl, (1 << %1)

   jz %%bit_is_clear
   %ifidn %1, 7
      or zF, FLAG_H|FLAG_S       ; set sign bit, if testing bit 7
   %else
      or zF, FLAG_H              ; set half-carry, and parity
   %endif
   jmp %%finish

%%bit_is_clear:
   or zF, FLAG_H|FLAG_Z|FLAG_P   ; set half-carry, parity, and zero
%%finish:

   DO_CYCLES
   NEXT
%endmacro

; Bit-clear a register
; RES_R_b src8, bit
%macro RES_R_b 2
   DO_RES z%1, %2

   DO_CYCLES
   NEXT
%endmacro

; Bit-clear (RR)
; RES_mRR_b src16, bit
%macro RES_mRR_b 2
   mov edi, ez%1
   MEMREAD
   DO_RES dl, %2
   MEMWRITE

   DO_CYCLES
   NEXT
%endmacro

; Bit-clear (XY+dd)
; RES_mXY_b bit
%macro RES_mXY_b 1
   MEMREAD
   DO_RES dl, %1
   MEMWRITE

   DO_CYCLES
   NEXT
%endmacro

; Bit-clear (XY+dd), with extra store
; RES_mXY_b_R bit, dest8
%macro RES_mXY_b_R 2
   MEMREAD
   DO_RES dl, %1
   mov z%2, dl
   MEMWRITE

   DO_CYCLES
   NEXT
%endmacro

; Bit-set a register
; SET_R_b src8, bit
%macro SET_R_b 2
   DO_SET z%1, %2

   DO_CYCLES
   NEXT
%endmacro

; Bit-set (RR)
; SET_mRR_b src16, bit
%macro SET_mRR_b 2
   mov edi, ez%1
   MEMREAD
   DO_SET dl, %2
   MEMWRITE

   DO_CYCLES
   NEXT
%endmacro

; Bit-set (XY+dd)
; SET_mXY_b bit
%macro SET_mXY_b 1
   MEMREAD
   DO_SET dl, %1
   MEMWRITE

   DO_CYCLES
   NEXT
%endmacro

; Bit-set (XY+dd), with extra store
; SET_mXY_b_R bit, dest8
%macro SET_mXY_b_R 2
   MEMREAD
   DO_SET dl, %1
   mov z%2, dl
   MEMWRITE

   DO_CYCLES
   NEXT
%endmacro

; Jump to an absolute address
; JP_NN
%macro JP_NN 0
   GETWORD
   mov ezPC, edi
   DO_CYCLES
   NEXT
%endmacro

; Jump to an absolute address, conditionally
; JP_cc_NN nz,cc
%macro JP_cc_NN 2
   test zF, FLAG_%2
   j%1 near %%dont_take_jump
   GETWORD
   mov ezPC, edi
   DO_CYCLES
   NEXT

   ALIGN
%%dont_take_jump:
   add zPC, 2     ; skip address
   DO_CYCLES
   NEXT
%endmacro

; Jump to an relative address
; JR_N
%macro JR_N 0
   GETDISP
   add zPC, di
   DO_CYCLES
   NEXT
%endmacro

; Jump to an relative address, conditionally
; JR_cc_N nz,cc
%macro JR_cc_N 2
   test zF, FLAG_%2
   j%1 near %%dont_take_jump
   GETDISP
   add zPC, di
   DO_EXTRA_CYCLES
   NEXT

   ALIGN
%%dont_take_jump:
   inc zPC           ; skip over the displacement
   DO_CYCLES
   NEXT
%endmacro

; Jump to an absolute address (in a register)
; JP_RR addr16
%macro JP_RR 1
   mov ezPC, ez%1
   DO_CYCLES
   NEXT
%endmacro

; Decrease B, if non-zero, jump to a relative address
; DJNZ_N
%macro DJNZ_N 0
   dec zB
   jz near %%dont_take_jump
   GETDISP
   add zPC, di
   DO_EXTRA_CYCLES
   NEXT

   ALIGN
%%dont_take_jump:
   inc zPC           ; skip over the displacement
   DO_CYCLES
   NEXT
%endmacro

; Call an absolute address
; CALL_NN
%macro CALL_NN 0
   GETWORD
   mov edx, ezPC        ; write the PC
   dec zSP
   push edi

   xchg dl, dh
   mov edi, ezSP
   MEMWRITE
   dec zSP
   mov edx, ezPC
   mov edi, ezSP
   MEMWRITE

   pop ezPC             ; get the new PC
   DO_CYCLES
   NEXT
%endmacro

; Call an absolute address, conditionally
; CALL_cc_NN
%macro CALL_cc_NN 2
   test zF, FLAG_%2
   j%1 near %%dont_take_jump
   GETWORD
   mov edx, ezPC        ; write the PC
   push edi

   dec zSP
   xchg dl, dh
   mov edi, ezSP
   MEMWRITE
   dec zSP
   mov edx, ezPC
   mov edi, ezSP
   MEMWRITE

   pop ezPC             ; get the new PC
   DO_CYCLES
   NEXT

   ALIGN
%%dont_take_jump:
   add zPC, 2     ; skip address
   DO_CYCLES
   NEXT
%endmacro

; Pop the PC
; RET
%macro RET 0
   mov edi, ezSP
   MEMREAD
   inc zSP
   mov ezPC, edx
   mov edi, ezSP
   MEMREAD
   xchg dl, dh
   or ezPC, edx
   inc zSP

   xor dh, dh
   DO_CYCLES
   NEXT
%endmacro

; Pop the PC, conditionally
; RET_cc
%macro RET_cc 2
   test zF, FLAG_%2
   j%1 near %%dont_take_jump
   mov edi, ezSP
   MEMREAD
   inc zSP
   mov ezPC, edx
   mov edi, ezSP
   MEMREAD
   xchg dl, dh
   or ezPC, edx
   inc zSP

   xor dh, dh
   DO_CYCLES
   NEXT

   ALIGN
%%dont_take_jump:
   DO_CYCLES
   NEXT
%endmacro

; Pop the PC, restore IFF1, signal the BUS
; RETI        
%macro RETI 0
   mov edi, ezSP
   MEMREAD
   mov ezPC, edx
   inc zSP
   mov edi, ezSP
   MEMREAD
   xchg dl, dh
   or ezPC, edx
   inc zSP

   mov dl, [z80_IFF2]
   mov [z80_IFF1], dl
   call [z80_RetI]           ; signal the user handler

   DO_CYCLES
   NEXT
%endmacro

; Pop the PC, restore IFF1
; RETN
%macro RETN 0
   mov edi, ezSP
   MEMREAD
   mov ezPC, edx
   inc zSP
   mov edi, ezSP
   MEMREAD
   xchg dl, dh
   or ezPC, edx
   inc zSP

   mov dl, [z80_IFF2]
   mov [z80_IFF1], dl
   DO_CYCLES
   NEXT
%endmacro

; Call an absolute address
; RST addr8
%macro RST 1
   dec zSP
   mov edx, ezPC        ; write the PC
   xchg dl, dh
   mov edi, ezSP
   MEMWRITE
   dec zSP
   mov edx, ezPC
   mov edi, ezSP
   MEMWRITE

   mov ezPC, %1         ; get the new PC
   DO_CYCLES
   NEXT
%endmacro

; Out R to R*256+N
; OUT_N_R src8
%macro OUT_N_R 1
   GETBYTE
   mov dh, z%1
   mov edi, edx
   mov dl, z%1
   IOWRITE
   DO_CYCLES
   NEXT
%endmacro

; In from R*256+N, into R
; IN_R_N dest8
%macro IN_R_N 1
   GETBYTE
   mov dh, z%1
   mov edi, edx
   IOREAD
   mov z%1, dl
   DO_CYCLES
   NEXT
%endmacro

; In from BC
; IN_R dest8
%macro IN_R 1
   mov edi, ezBC
   IOREAD
   mov dh, zF
   and dh, FLAG_C       ; keep the old carry
   mov z%1, dl
   test dl, dl          ; other flags come from the result
   lahf
   and zF, FLAG_S|FLAG_Z|FLAG_P
   or zF, dh
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], dl
   %endif
   xor dh, dh
   
   DO_CYCLES
   NEXT
%endmacro

; In from BC, ignore result
; IN_F
%macro IN_F 0
   mov edi, ezBC
   IOREAD
   mov dh, zF
   and dh, FLAG_C       ; keep the old carry
   test dl, dl          ; other flags come from the result
   lahf
   and zF, FLAG_S|FLAG_Z|FLAG_P
   or zF, dh
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], dl
   %endif
   xor dh, dh
   
   DO_CYCLES
   NEXT
%endmacro

; Out to BC
; OUT_R src8
%macro OUT_R 1
   mov edi, ezBC
   mov dl, z%1
   IOWRITE
   DO_CYCLES
   NEXT
%endmacro

; Out zero to BC
; OUT_0
%macro OUT_0 0
   mov edi, ezBC
   xor dl, dl
   IOWRITE
   DO_CYCLES
   NEXT
%endmacro

; In from BC into (HL), increase HL, decrease B
%macro INI 0
; FIXME - Parity flag is not emulated
   movzx edi, zC           ; high byte clear? (documentation is fuzzy)

   IOREAD                  ; read from the port
   mov edi, ezHL
   %ifdef EMULATE_WEIRD_STUFF
      push edx             ; preserve the value
   %endif
   MEMWRITE                ; write to (HL)
   %ifdef EMULATE_WEIRD_STUFF
      pop edx
   %endif

   inc zHL                 ; increase HL
   dec zB                  ; decrease B
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zB
   %endif

   lahf
   and zF, FLAG_S|FLAG_Z|FLAG_P

   ; This is a prime example of the weirdness of the Z80...
   %ifdef EMULATE_WEIRD_STUFF
      mov dh, zC
      inc dh
      add dh, dl
      jnc %%no_carry
      or zF, FLAG_H|FLAG_C
   %%no_carry:
      shr dl, 6
      and edx, FLAG_N
      or zF, dl
   %endif
   DO_CYCLES
   NEXT
%endmacro

; In from BC into (HL), decrease HL, decrease B
%macro IND 0
; FIXME - Parity flag is not emulated
   movzx edi, zC           ; high byte clear? (documentation is fuzzy)

   IOREAD                  ; read from the port
   mov edi, ezHL
   %ifdef EMULATE_WEIRD_STUFF
      push edx             ; preserve the value
   %endif
   MEMWRITE                ; write to (HL)
   %ifdef EMULATE_WEIRD_STUFF
      pop edx
   %endif

   dec zHL                 ; increase HL
   dec zB                  ; decrease B
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zB
   %endif

   lahf
   and zF, FLAG_S|FLAG_Z|FLAG_P

   ; This is a prime example of the weirdness of the Z80...
   %ifdef EMULATE_WEIRD_STUFF
      mov dh, zC
      dec dh
      add dh, dl
      jnc %%no_carry
      or zF, FLAG_H|FLAG_C
   %%no_carry:
      shr dl, 6
      and edx, FLAG_N
      or zF, dl
   %endif
   DO_CYCLES
   NEXT
%endmacro

; In from BC into (HL), increase HL, decrease B, repeat
%macro INIR 0
; FIXME - Parity flag is not emulated
%%inir_loop:
   movzx edi, zC           ; high byte clear? (documentation is fuzzy)

   IOREAD                  ; read from the port
   mov edi, ezHL
   %ifdef EMULATE_WEIRD_STUFF
      push edx             ; preserve the value
   %endif
   MEMWRITE                ; write to (HL)
   %ifdef EMULATE_WEIRD_STUFF
      pop edx
   %endif

   inc zHL                 ; increase HL
   dec zB                  ; decrease B
   jz %%inir_zero

   sub [z80_ICount], dword extra_cycles
   ja near %%inir_loop            ; repeat while cycles left and B>0

   ; out of cycles, but operation not finished
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zB
   %endif

   mov zF, zB
   and zF, FLAG_S

   ; This is a prime example of the weirdness of the Z80...
   %ifdef EMULATE_WEIRD_STUFF
      mov dh, zC
      inc dh
      add dh, dl
      jnc %%no_carry
      or zF, FLAG_H|FLAG_C
   %%no_carry:
      shr dl, 6
      and edx, FLAG_N
      or zF, dl
   %endif
   sub zPC, 2           ; rewind to instruction start
   EXIT

; B=0
%%inir_zero:
   mov zF, FLAG_Z|FLAG_P
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zF
   %endif

   ; This is a prime example of the weirdness of the Z80...
   %ifdef EMULATE_WEIRD_STUFF
      mov dh, zC
      inc dh
      add dh, dl
      jnc %%no_carry2
      or zF, FLAG_H|FLAG_C
   %%no_carry2:
      shr dl, 6
      and edx, FLAG_N
      or zF, dl
   %endif
   DO_CYCLES
   NEXT
%endmacro

; In from BC into (HL), decrease HL, decrease B, repeat
%macro INDR 0
; FIXME - Parity flag is not emulated
%%indr_loop:
   movzx edi, zC           ; high byte clear? (documentation is fuzzy)

   IOREAD                  ; read from the port
   mov edi, ezHL
   %ifdef EMULATE_WEIRD_STUFF
      push edx             ; preserve the value
   %endif
   MEMWRITE                ; write to (HL)
   %ifdef EMULATE_WEIRD_STUFF
      pop edx
   %endif

   dec zHL                 ; decrease HL
   dec zB                  ; decrease B
   jz %%indr_zero

   sub [z80_ICount], dword extra_cycles
   ja near %%indr_loop            ; repeat while cycles left and B>0

   ; out of cycles, but operation not finished
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zB
   %endif

   mov zF, zB
   and zF, FLAG_S

   ; This is a prime example of the weirdness of the Z80...
   %ifdef EMULATE_WEIRD_STUFF
      mov dh, zC
      inc dh
      add dh, dl
      jnc %%no_carry
      or zF, FLAG_H|FLAG_C
   %%no_carry:
      shr dl, 6
      and edx, FLAG_N
      or zF, dl
   %endif
   sub zPC, 2           ; rewind to instruction start
   EXIT

; B=0
%%indr_zero:
   mov zF, FLAG_Z|FLAG_P
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zF
   %endif

   ; This is a prime example of the weirdness of the Z80...
   %ifdef EMULATE_WEIRD_STUFF
      mov dh, zC
      inc dh
      add dh, dl
      jnc %%no_carry2
      or zF, FLAG_H|FLAG_C
   %%no_carry2:
      shr dl, 6
      and edx, FLAG_N
      or zF, dl
   %endif
   DO_CYCLES
   NEXT
%endmacro

; Out from (HL) into BC, increase HL, decrease B
%macro OUTI 0
; FIXME - Parity flag is not emulated
   mov edi, ezHL

   MEMREAD                 ; read from (HL)
   movzx edi, zC           ; high byte clear? (documentation is fuzzy)
   %ifdef EMULATE_WEIRD_STUFF
      push edx             ; preserve the value
   %endif
   IOWRITE                 ; write to the port
   %ifdef EMULATE_WEIRD_STUFF
      pop edx
   %endif

   inc zHL                 ; increase HL
   dec zB                  ; decrease B
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zB
   %endif

   lahf
   and zF, FLAG_S|FLAG_Z|FLAG_P

   ; This is a prime example of the weirdness of the Z80...
   %ifdef EMULATE_WEIRD_STUFF
      mov dh, zC
      inc dh
      add dh, dl
      jnc %%no_carry
      or zF, FLAG_H|FLAG_C
   %%no_carry:
      shr dl, 6
      and edx, FLAG_N
      or zF, dl
   %endif
   DO_CYCLES
   NEXT
%endmacro

; Out from (HL) into BC, decrease HL, decrease B
%macro OUTD 0
; FIXME - Parity flag is not emulated
   mov edi, ezHL

   MEMREAD                 ; read from (HL)
   movzx edi, zC           ; high byte clear? (documentation is fuzzy)
   %ifdef EMULATE_WEIRD_STUFF
      push edx             ; preserve the value
   %endif
   IOWRITE                 ; write to the port
   %ifdef EMULATE_WEIRD_STUFF
      pop edx
   %endif

   dec zHL                 ; decrease HL
   dec zB                  ; decrease B
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zB
   %endif

   lahf
   and zF, FLAG_S|FLAG_Z|FLAG_P

   ; This is a prime example of the weirdness of the Z80...
   %ifdef EMULATE_WEIRD_STUFF
      mov dh, zC
      inc dh
      add dh, dl
      jnc %%no_carry
      or zF, FLAG_H|FLAG_C
   %%no_carry:
      shr dl, 6
      and edx, FLAG_N
      or zF, dl
   %endif
   DO_CYCLES
   NEXT
%endmacro

; Out from (HL) into BC, increase HL, decrease B, repeat
%macro OTIR 0
%%otir_loop:
   mov edi, ezHL
   MEMREAD
   movzx edi, zC           ; high byte clear? (documentation is fuzzy)
   %ifdef EMULATE_WEIRD_STUFF
      push edx             ; preserve the value
   %endif
   IOWRITE
   %ifdef EMULATE_WEIRD_STUFF
      pop edx
   %endif

   inc zHL
   dec zB
   jz %%otir_zero

%ifdef EMULATE_R_REGISTER
   inc zR
%endif
   sub [z80_ICount], dword extra_cycles
   ja near %%otir_loop

; out of cycles, but not finished
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zB
   %endif

   mov zF, zB
   and zF, FLAG_S

   %ifdef EMULATE_WEIRD_STUFF
      mov dh, zC
      inc dh
      add dh, dl
      jnc %%otir_nocarry
      or zF, FLAG_H|FLAG_C
   %%otir_nocarry:
      shr dl, 6
      and edx, FLAG_N
      or zF, dl
   %endif
   sub zPC, 2           ; rewind to instruction start
   EXIT

%%otir_zero:
   mov zF, FLAG_Z|FLAG_P
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zF
   %endif
   %ifdef EMULATE_WEIRD_STUFF
      mov dh, zC
      inc dh
      add dh, dl
      jnc %%otir_nocarry2
      or zF, FLAG_H|FLAG_C
   %%otir_nocarry2:
      shr dl, 6
      and edx, FLAG_N
      or zF, dl
   %endif
   DO_CYCLES
   NEXT
%endmacro

; Out from (HL) into BC, decrease HL, decrease B, repeat
%macro OTDR 0
%%otdr_loop:
   mov edi, ezHL
   MEMREAD
   movzx edi, zC           ; high byte clear? (documentation is fuzzy)
   %ifdef EMULATE_WEIRD_STUFF
      push edx             ; preserve the value
   %endif
   IOWRITE
   %ifdef EMULATE_WEIRD_STUFF
      pop edx
   %endif

   dec zHL
   dec zB
   jz %%otdr_zero

%ifdef EMULATE_R_REGISTER
   inc zR
%endif
   sub [z80_ICount], dword extra_cycles
   ja near %%otdr_loop

; out of cycles, but not finished
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zB
   %endif

   mov zF, zB
   and zF, FLAG_S

   %ifdef EMULATE_WEIRD_STUFF
      mov dh, zC
      inc dh
      add dh, dl
      jnc %%otdr_nocarry
      or zF, FLAG_H|FLAG_C
   %%otdr_nocarry:
      shr dl, 6
      and edx, FLAG_N
      or zF, dl
   %endif
   sub zPC, 2           ; rewind to instruction start
   EXIT

%%otdr_zero:
   mov zF, FLAG_Z|FLAG_P
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zF
   %endif
   %ifdef EMULATE_WEIRD_STUFF
      mov dh, zC
      inc dh
      add dh, dl
      jnc %%otdr_nocarry2
      or zF, FLAG_H|FLAG_C
   %%otdr_nocarry2:
      shr dl, 6
      and edx, FLAG_N
      or zF, dl
   %endif
   DO_CYCLES
   NEXT
%endmacro
	
; BCD adjust
%macro DAA 0
   test zF, FLAG_H
   jnz %%daa_h_set
   and eax, 03ffh
   mov ax, [DAA_Table + eax*2]
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zF
   %endif
   DO_CYCLES
   NEXT

%%daa_h_set:
   and eax, 03ffh
   mov ax, [DAA_Table + eax*2 + 4*512]
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zF
   %endif
   DO_CYCLES
   NEXT
%endmacro

; Complement A
%macro CPL 0
   not zA
   or zF, FLAG_H|FLAG_N
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zA
   %endif
   DO_CYCLES
   NEXT
%endmacro

; Negate A
%macro NEG 0
   neg zA
   lahf
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zA
   %endif
   jo %%neg_overflow
   and zF, ~FLAG_P
   DO_CYCLES
   NEXT

%%neg_overflow:
   or zF, FLAG_P
   DO_CYCLES
   NEXT
%endmacro

; Set carry flag
%macro SCF 0
   and zF, FLAG_S|FLAG_Z|FLAG_P
   or zF, FLAG_C
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zA
   %endif
   DO_CYCLES
   NEXT
%endmacro

; Complement carry flag
%macro CCF 0
   test zF, FLAG_C
   jz %%set_carry
   and zF, FLAG_S|FLAG_Z|FLAG_P
   or zF, FLAG_H
   %ifdef EMULATE_BITS_3_5
      mov [z80_flag35], zA
   %endif
   DO_CYCLES
   NEXT

%%set_carry:
   SCF
%endmacro

; No operation
%macro NOP 0
   DO_CYCLES
   NEXT
%endmacro

; Halt the processor
%macro HALT 0
   and [z80_ICount], dword 3
   sub [z80_ICount], dword 4
   mov [z80_halted], byte 1
   EXIT
%endmacro

; Enable interrupts
%macro EI 0
   mov dl, FLAG_P      ; set the P/V bit, for use in "LD I,A" etc
   mov [z80_IFF1], dl
   mov [z80_IFF2], dl

   ; force the next instruction to be executed, regardless of ICount
   ; then force an IRQ check after it.
   mov [z80_afterEI], byte 1
   push eax
   mov eax, [z80_ICount]        ; set ICount to zero, so it will terminate
   mov [z80_TempICount], eax    ; after the next instruction.
   mov [z80_ICount], dword 1    ; we can then pick it up again and carry on.
   pop eax
   NEXT
%endmacro

; Disable interrupts
%macro DI 0
   xor edx, edx
   mov [z80_IFF1], dl
   mov [z80_IFF2], dl
   DO_CYCLES
   NEXT
%endmacro

; Set interrupt mode
%macro IM 1
   mov [z80_IM], byte %1
   DO_CYCLES
   NEXT
%endmacro

;----------------------------------------------------------------------------;
; Bring in the opcodes...
%include 'raze.inc'
;----------------------------------------------------------------------------;

;----------------------------------------------------------------------------;
; Support functions:

DEF op, cb, 0, 0
%ifdef EMULATE_R_REGISTER
   inc zR
%endif
   GETBYTE
   jmp [CBTable+edx*4]

DEF op, ed, 0, 0
%ifdef EMULATE_R_REGISTER
   inc zR
%endif
   GETBYTE
   jmp [EDTable+edx*4]

DEF op, dd, 0, 0
%ifdef EMULATE_R_REGISTER
   inc zR
%endif
   GETBYTE
   jmp [DDTable+edx*4]

DEF op, fd, 0, 0
%ifdef EMULATE_R_REGISTER
   inc zR
%endif
   GETBYTE
   jmp [FDTable+edx*4]

DEF dd, cb, 0, 0
%ifdef EMULATE_R_REGISTER
   inc zR
%endif
   GETDISP
   add di, zIX
   push edi
   GETBYTE
   pop edi
   jmp [DDCBTable+edx*4]

DEF fd, cb, 0, 0
%ifdef EMULATE_R_REGISTER
   inc zR
%endif
   GETDISP
   add di, zIY
   push edi
   GETBYTE
   pop edi
   jmp [DDCBTable+edx*4]

DEF ed, xx, 8, 0
%ifdef EMULATE_R_REGISTER
   inc zR
%endif
   DO_CYCLES
   NEXT

DEF dd, xx, 4, 0
   dec zPC
   DO_CYCLES
   NEXT

DEF fd, xx, 0, 0
   dec zPC
   DO_CYCLES
   NEXT

; called when out-of-cycles
z80_finish:
   cmp [z80_afterEI], byte 1    ; test for the post-EI behaviour
   jne near really_finish
   push eax
   mov eax, [z80_TempICount]
   mov [z80_ICount], eax
   mov [z80_afterEI], byte 0
   pop eax
   CHECK_IRQ
   sub dword [z80_ICount], 4    ; subtract EI's cycles
   jbe near z80_finish
   NEXT

really_finish:   
   ; merge bits 3 and 5 of the flags into the regs
   and zF, ~(FLAG_3|FLAG_5)
   mov dl, [z80_flag35]
   and dl, FLAG_3|FLAG_5
   or zF, dl

   mov [z80_AF], ezAF
   mov [z80_HL], ezHL
   mov [z80_BC], ezBC
   mov [z80_PC], ezPC
   mov [z80_R], zR
   pop ebp
   pop edi
   pop esi
   pop ebx
   mov eax, [z80_Initial_ICount]
   sub eax, [z80_ICount]
   ret

z80_default_in:
z80_default_read:
   mov eax, 0ffh
   ret

z80_default_out:
z80_default_write:
z80_default_reti:
z80_default_fetch_callback:
   ret

; called to look up a read handler
; address is on the stack
read_dispatcher:
   mov eax, [esp+4]
   mov edx, _read_handlers
rd_find:
   add edx, 16
   cmp [edx-16], eax
   ja rd_find
   cmp [edx-12], eax
   jb rd_find

   cmp [edx-8], dword 1
   je rd_handler

   add eax, [edx-4]
   movzx eax, byte [eax]
   ret

rd_handler:
   jmp [edx-4]    ; call the GCC function

; called to look up a write handler
; address+value are on the stack
write_dispatcher:
   mov eax, [esp+4]
   mov edx, _write_handlers
wd_find:
   add edx, 16
   cmp [edx-16], eax
   ja wd_find
   cmp [edx-12], eax
   jb wd_find

   cmp [edx-8], dword 1
   je wd_handler

   add eax, [edx-4]
   mov ecx, [esp+8]
   mov [eax], cl
   ret

wd_handler:
   jmp [edx-4]    ; call the GCC function

; int z80_emulate(int cycles)
; Emulate for 'cycles' T-states,
; Returns the number of cycles actually emulated.
cglobal z80_emulate
z80_emulate:
   mov eax, [esp+4]
   push ebx
   push esi
   push edi
   push ebp

   add eax, [z80_Extra_Cycles]
   mov [z80_ICount], eax
   mov [z80_Initial_ICount], eax
   xor edx, edx
   xor edi, edi
   mov [z80_Extra_Cycles], edx
   mov [z80_afterEI], dl
   mov ezAF, [z80_AF]
   mov ezHL, [z80_HL]
   mov ezBC, [z80_BC]
   mov ezPC, [z80_PC]
   mov zR,   [z80_R]
   mov [z80_flag35], zF      ; take out bits 3/5
   cmp [z80_halted], byte 0
   je not_halted
   and [z80_ICount], dword 3 ; If we're halted, just skip it all.
   sub [z80_ICount], dword 4
   EXIT
not_halted:
   NEXT

; void z80_reset(void)
; Resets the Z80.
cglobal z80_reset
z80_reset:
   push edi
   xor eax, eax      ; just zero it all
   mov edi, context_start
   mov ecx, (registers_end - context_start)
   rep stosb
   pop edi
   mov [z80_IX], word 0ffffh
   mov [z80_IY], word 0ffffh
   mov [z80_AF], word 04000h
   ret

; void z80_raise_IRQ(UBYTE vector)
; Causes an interrupt with the given vector.
; In IM 0, the vector has to be an RST opcode.
; If interrupts are disabled, the interrupt will still happen as long as
; the IRQ line stays raised.
cglobal z80_raise_IRQ
z80_raise_IRQ:
   mov [z80_IRQLine], byte 1
   mov eax, [esp+4]
   mov [z80_IRQVector], al

   xor edx, edx
   push ebx
   push esi
   push edi
   push ebp
   CALLGCC_END          ; grab our Z80 regs

   CHECK_IRQ

   CALLGCC_START
   pop ebp
   pop edi
   pop esi
   pop ebx
   ret

; void z80_lower_IRQ(void)
; Lowers the IRQ line.
cglobal z80_lower_IRQ
z80_lower_IRQ:
   mov [z80_IRQLine], byte 0
   ret

; void z80_cause_NMI(void)
; Causes a non-maskable interrupt.
; This will always be accepted.
; There is no raise/lower functions, as the NMI is edge-triggered.
cglobal z80_cause_NMI
z80_cause_NMI:
   xor edx, edx
   push ebx
   push esi
   push edi
   push ebp
   CALLGCC_END          ; grab our Z80 regs

   ; disable interrupts
   mov [z80_IFF1], byte 0

   ; take up 11 T-states
   add [z80_Extra_Cycles], dword 11
%ifdef EMULATE_R_REGISTER
   inc zR
%endif

   mov [z80_halted], byte 0  ; Un-HALT us.

   mov edx, ezPC        ; write the PC
   dec zSP

   xchg dl, dh
   mov edi, ezSP
   MEMWRITE
   dec zSP
   mov edx, ezPC
   mov edi, ezSP
   MEMWRITE

   mov ezPC, 66h        ; set the new PC

   CALLGCC_START
   pop ebp
   pop edi
   pop esi
   pop ebx
   ret

; int z80_get_context_size(void)
; Returns the size of the context, in bytes
cglobal z80_get_context_size
z80_get_context_size:
   mov eax, (context_end - context_start) + 4   ; +4 for a safety buffer
   ret


; void z80_set_context(void *context)
; Copy the given context to the current Z80.
cglobal z80_set_context
z80_set_context:
   push esi
   push edi
   mov ecx, (context_end - context_start) / 4
   mov esi, [esp+12]
   mov edi, context_start
   rep movsd
   pop edi
   pop esi
   ret

; void z80_get_context(void *context)
; Copy the current Z80 to the given context.
cglobal z80_get_context
z80_get_context:
   push esi
   push edi
   mov ecx, (context_end - context_start) / 4
   mov edi, [esp+12]
   mov esi, context_start
   rep movsd
   pop edi
   pop esi
   ret

; int z80_get_cycles_elapsed(void)
; Returns the number of cycles emulated since z80_emulate was called.
cglobal z80_get_cycles_elapsed
z80_get_cycles_elapsed:
   mov eax, [z80_Initial_ICount]
   sub eax, [z80_ICount]
   cmp [z80_afterEI], byte 0
   je z80_gcd_fin
   sub eax, [z80_TempICount]
z80_gcd_fin:
   ret

; void z80_stop_emulating(void)
; Stops the emulation dead. (waits for the current instruction to finish
; first though).
; The return value from z80_execute will obviously be lower than expected.
cglobal z80_stop_emulating
z80_stop_emulating:
   mov eax, [z80_ICount]
   sub [z80_Initial_ICount], eax
   cmp [z80_afterEI], byte 0
   je z80_se_fin
   mov ecx, [z80_TempICount]
   sub [z80_Initial_ICount], ecx
z80_se_fin:
   mov [z80_ICount], dword 0
   mov [z80_TempICount], dword 0
   ret

; void z80_skip_idle(void)
; Stops the emulation dead. (waits for the current instruction to finish
; first though).
; The return value from z80_execute will appear as though it had executed
; all the instructions like normal.
cglobal z80_skip_idle
z80_skip_idle:
   mov [z80_ICount], dword 0
   mov [z80_TempICount], dword 0
   ret

; void z80_do_wait_states(int n)
; Halts the CPU temporarily, to execute 'n' memory wait states.
cglobal z80_do_wait_states
z80_do_wait_states:
   add [z80_Initial_ICount], eax
   ret

; UWORD z80_get_reg(z80_register reg)
; Return the value of the specified register.
cglobal z80_get_reg
z80_get_reg:
   mov ecx, [esp+4]
   cmp ecx, Z80_REG_HL2
   mov eax, [context_start + ecx*4]
   jbe getreg_simple
   
   cmp ecx, Z80_REG_IFF1
   mov al, [z80_IFF1]
   je getreg_boolean
   
   cmp ecx, Z80_REG_IFF2
   mov al, [z80_IFF2]
   je getreg_boolean

   cmp ecx, Z80_REG_IRQLine
   mov al, [z80_IRQLine]
   je getreg_boolean

   cmp ecx, Z80_REG_Halted
   mov al, [z80_halted]
   je getreg_boolean

   cmp ecx, Z80_REG_IM
   movzx eax, byte [z80_IM]
   je getreg_finish

   cmp ecx, Z80_REG_IRQVector
   movzx eax, byte [z80_IRQVector]
   je getreg_finish

   ; construct IR
   mov eax, [z80_R]
   and eax, 7fh
   mov edx, [z80_R2]
   and edx, 80h
   or eax, edx
   mov ah, byte [z80_I]
   ret

getreg_simple:
   cmp ecx, Z80_REG_AF
   je getreg_swap
   cmp ecx, Z80_REG_AF2
   je getreg_swap
getreg_finish:
   ret
getreg_swap:
   xchg al, ah          ; swap A and F
   ret

getreg_boolean:
   and eax, 0ffh
   jz getreg_finish
   mov al, 1
   ret

; void z80_set_reg(z80_register reg, UWORD value)
; Set the specified register to a new value.
cglobal z80_set_reg
z80_set_reg:
   mov ecx, [esp+4]
   mov eax, [esp+8]
   and eax, 0ffffh
   cmp ecx, Z80_REG_HL2
   jbe setreg_simple
   
   cmp ecx, Z80_REG_IFF1
   lea edx, [z80_IFF1]
   jbe setreg_boolean

   cmp ecx, Z80_REG_IFF2
   lea edx, [z80_IFF2]
   jbe setreg_boolean

   cmp ecx, Z80_REG_IRQLine
   lea edx, [z80_IRQLine]
   je setreg_boolean

   cmp ecx, Z80_REG_Halted
   lea edx, [z80_halted]
   je setreg_boolean

   cmp ecx, Z80_REG_IM
   lea edx, [z80_IM]
   je setreg_value

   cmp ecx, Z80_REG_IRQVector
   lea edx, [z80_IRQVector]
   je setreg_value

   ; deconstruct IR
   mov [z80_I], ah
   mov [z80_R2], eax
   mov [z80_R], eax
   ret

setreg_simple:
   cmp ecx, Z80_REG_AF
   je setreg_swap
   cmp ecx, Z80_REG_AF2
   je setreg_swap
setreg_finish:
   mov [context_start + ecx*4], eax
   ret
setreg_swap:
   xchg al, ah          ; swap A and F
   mov [context_start + ecx*4], eax
   ret

setreg_boolean:
   test eax, eax
   jz setreg_value
   mov al, FLAG_P

setreg_value:
   mov [edx], al
   ret

; void z80_init_memmap(void)
; Reset the current memory map
cglobal z80_init_memmap
z80_init_memmap:
   mov [z80_In], dword z80_default_in
   mov [z80_Out], dword z80_default_out
   mov [z80_RetI], dword z80_default_reti
   mov [z80_Fetch_Callback], dword z80_default_fetch_callback

   push edi    ; clear the memory handlers
   mov eax, -1
   mov ecx, MAX_MEM_HANDLERS*4
   mov edi, _read_handlers
   rep stosd
   mov ecx, MAX_MEM_HANDLERS*4
   mov edi, _write_handlers
   rep stosd

   ; reset the read table
   mov edi, z80_Read
   mov ecx, 100h
clear_read:
   mov [edi], dword z80_default_read
   add edi, 8
   loop clear_read

   ; reset the write table
   mov edi, z80_Write
   mov ecx, 100h
clear_write:
   mov [edi], dword z80_default_write
   add edi, 8
   loop clear_write

   pop edi   
   ret

; void z80_end_memmap(void)
; Finishes the current memory map
cglobal z80_end_memmap
z80_end_memmap:
   ; put the entry at the end of the read table
   mov edx, _read_handlers
er_find:
   add edx, 16
   cmp [edx-16], dword (-1)
   jne er_find
   sub edx, 16

   ; put it in there
   mov [edx], dword 0
   mov [edx+4], dword 0ffffh
   mov [edx+8], dword 0
   mov [edx+12], dword z80_default_read

   ; put the entry at the end of the write table
   mov edx, _write_handlers
ew_find:
   add edx, 16
   cmp [edx-16], dword (-1)
   jne ew_find
   sub edx, 16

   ; put it in there
   mov [edx], dword 0
   mov [edx+4], dword 0ffffh
   mov [edx+8], dword 0
   mov [edx+12], dword z80_default_write
   ret

; void z80_map_fetch(UWORD start, UWORD end, UBYTE *memory)
; Set the given area for op-code fetching
; start/end = the area it covers
; memory = the ROM/RAM to read from
cglobal z80_map_fetch
z80_map_fetch:
   push ebx
   mov eax, [esp+8]
   mov ebx, [esp+12]
   mov ecx, [esp+16]
   and eax, 0ffffh
   and ebx, 0ffffh
   sub ecx, eax

mf_loop:
; if there's no area left inbetween, stop now.
   mov edx, eax
   shr edx, 8

   cmp eax, ebx
   ja mf_finish

   mov [z80_Fetch+edx*4], ecx
   add eax, 0100h
   jmp mf_loop

mf_finish:
   pop ebx
   ret

; void z80_map_read(UWORD start, UWORD end, UBYTE *memory)
; Moves a Z80_MAP_MAPPED area to use a different region of memory
; start/end = the area it covers (must be page-aligned)
; memory = the ROM/RAM to read from
cglobal z80_map_read
z80_map_read:
   push ebx
   mov eax, [esp+8]
   mov ebx, [esp+12]
   mov ecx, [esp+16]
   and eax, 0ffffh
   and ebx, 0ffffh
   sub ecx, eax

mr_loop:
; if there's no area left inbetween, stop now.
   mov edx, eax
   shr edx, 8

   cmp eax, ebx
   ja mr_finish

   mov [z80_Read+edx*8], dword 0
   mov [z80_Read+edx*8+4], ecx
   add eax, 0100h
   jmp mr_loop

mr_finish:
   pop ebx
   ret

; void z80_map_write(UWORD start, UWORD end, UBYTE *memory)
; Moves a Z80_MAP_MAPPED area to use a different region of memory
; start/end = the area it covers (must be page-aligned)
; memory = the ROM/RAM to read from
cglobal z80_map_write
z80_map_write:
   push ebx
   mov eax, [esp+8]
   mov ebx, [esp+12]
   mov ecx, [esp+16]
   and eax, 0ffffh
   and ebx, 0ffffh
   sub ecx, eax

mw_loop:
; if there's no area left inbetween, stop now.
   mov edx, eax
   shr edx, 8

   cmp eax, ebx
   ja mw_finish

   mov [z80_Write+edx*8], dword 0
   mov [z80_Write+edx*8+4], ecx
   add eax, 0100h
   jmp mw_loop

mw_finish:
   pop ebx
   ret

; void z80_add_read(UWORD start, UWORD end, int method, void *data)
; Add a READ memory handler to the memory map
; start/end = the area it covers
; method = 0 for direct, 1 for handled
; data = RAM for direct, handler for handled
cglobal z80_add_read
z80_add_read:
   push ebx
   mov eax, [esp+8]
   mov ebx, [esp+12]
   mov ecx, [esp+20]
   and eax, 0ffffh
   and ebx, 0ffffh

   ; put the entry in the read table first
   mov edx, _read_handlers
ar_find:
   add edx, 16
   cmp [edx-16], dword (-1)
   jne ar_find
   sub edx, 16

   ; put it in there
   mov [edx], eax
   mov [edx+4], ebx
   mov eax, [esp+16]
   mov [edx+8], eax

   mov eax, ecx         ; if direct, subtract the start from the memory area
   push ebx
   mov ebx, [esp+12]
   and ebx, 0ffffh
   cmp [esp+20], dword 1
   je ar_dont_adjust
   sub eax, ebx
ar_dont_adjust:
   mov [edx+12], eax

   mov eax, ebx
   pop ebx
   
; process the start, so it's page aligned
   test al, al
   jz ar_start_done

   ; put the start handler to go to the dispatcher
   mov edx, eax
   shr edx, 8
   mov [z80_Read+edx*8], dword read_dispatcher
   xor al, al
   add eax, 0100h

ar_start_done:
; process the end, so it's page aligned
   cmp bl, 0ffh
   je ar_end_done

   ; put the end handler to go to the dispatcher
   mov edx, ebx
   shr edx, 8
   mov [z80_Read+edx*8], dword read_dispatcher
   mov bl, 0ffh
   sub ebx, 0100h

ar_end_done:

   sub ecx, eax
ar_loop:
; if there's no area left inbetween, stop now.
   mov edx, eax
   shr edx, 8

   cmp eax, ebx
   jg ar_finish

   cmp [esp+16], dword 0
   je ar_put_direct

   push ecx
   mov ecx, [esp+24]
   mov [z80_Read+edx*8], ecx
   pop ecx
   add eax, 0100h
   jmp ar_loop

ar_put_direct:
   mov [z80_Read+edx*8], dword 0
   mov [z80_Read+edx*8+4], ecx
   add eax, 0100h
   jmp ar_loop

ar_finish:
   pop ebx
   ret

; void z80_add_write(UWORD start, UWORD end, int method, void *data)
; Add a WRITE memory handler to the memory map
; start/end = the area it covers
; method = 0 for direct, 1 for handled
; data = RAM for direct, handler for handled
cglobal z80_add_write
z80_add_write:
   push ebx
   mov eax, [esp+8]
   mov ebx, [esp+12]
   mov ecx, [esp+20]
   and eax, 0ffffh
   and ebx, 0ffffh

   ; put the entry in the read table first
   mov edx, _write_handlers
aw_find:
   add edx, 16
   cmp [edx-16], dword (-1)
   jne aw_find
   sub edx, 16

   ; put it in there
   mov [edx], eax
   mov [edx+4], ebx
   mov eax, [esp+16]
   mov [edx+8], eax

   mov eax, ecx         ; if direct, subtract the start from the memory area
   push ebx
   mov ebx, [esp+12]
   and ebx, 0ffffh
   cmp [esp+20], dword 1
   je aw_dont_adjust
   sub eax, ebx
aw_dont_adjust:
   mov [edx+12], eax
   
   mov eax, ebx
   pop ebx

; process the start, so it's page aligned
   test al, al
   jz aw_start_done

   ; put the start handler to go to the dispatcher
   mov edx, eax
   shr edx, 8
   mov [z80_Write+edx*8], dword write_dispatcher
   xor al, al
   add eax, 0100h

aw_start_done:
; process the end, so it's page aligned
   cmp bl, 0ffh
   je aw_end_done

   ; put the end handler to go to the dispatcher
   mov edx, ebx
   shr edx, 8
   mov [z80_Write+edx*8], dword write_dispatcher
   mov bl, 0ffh
   sub ebx, 0100h

aw_end_done:

   sub ecx, eax
aw_loop:
; if there's no area left inbetween, stop now.
   mov edx, eax
   shr edx, 8

   cmp eax, ebx
   jg aw_finish

   cmp [esp+16], dword 0
   je aw_put_direct

   push ecx
   mov ecx, [esp+24]
   mov [z80_Write+edx*8], ecx
   pop ecx
   add eax, 0100h
   jmp aw_loop

aw_put_direct:
   mov [z80_Write+edx*8], dword 0
   mov [z80_Write+edx*8+4], ecx
   add eax, 0100h
   jmp aw_loop

aw_finish:
   pop ebx
   ret

; void z80_set_in(UBYTE (*handler)(UWORD port))
; Set the IN port handler to the given function
cglobal z80_set_in
z80_set_in:
   mov eax, [esp+4]
   mov [z80_In], eax
   ret

; void z80_set_out(void (*handler)(UWORD port, UBYTE value))
; Set the OUT port handler to the given function
cglobal z80_set_out
z80_set_out:
   mov eax, [esp+4]
   mov [z80_Out], eax
   ret

; void z80_set_reti(void (*handler)(void))
; Set the RETI handler to the given function
cglobal z80_set_reti
z80_set_reti:
   mov eax, [esp+4]
   mov [z80_RetI], eax
   ret

; void z80_set_fetch_callback(void (*handler)(UWORD pc))
; Set the fetch callback to the given function
cglobal z80_set_fetch_callback
z80_set_fetch_callback:
   mov eax, [esp+4]
   mov [z80_Fetch_Callback], eax
   ret

;----------------------------------------------------------------------------;
; the jump tables
OpTable:
   dd op_00, op_01, op_02, op_03, op_04, op_05, op_06, op_07
   dd op_08, op_09, op_0a, op_0b, op_0c, op_0d, op_0e, op_0f
   dd op_10, op_11, op_12, op_13, op_14, op_15, op_16, op_17
   dd op_18, op_19, op_1a, op_1b, op_1c, op_1d, op_1e, op_1f
   dd op_20, op_21, op_22, op_23, op_24, op_25, op_26, op_27
   dd op_28, op_29, op_2a, op_2b, op_2c, op_2d, op_2e, op_2f
   dd op_30, op_31, op_32, op_33, op_34, op_35, op_36, op_37
   dd op_38, op_39, op_3a, op_3b, op_3c, op_3d, op_3e, op_3f
   dd op_40, op_41, op_42, op_43, op_44, op_45, op_46, op_47
   dd op_48, op_49, op_4a, op_4b, op_4c, op_4d, op_4e, op_4f
   dd op_50, op_51, op_52, op_53, op_54, op_55, op_56, op_57
   dd op_58, op_59, op_5a, op_5b, op_5c, op_5d, op_5e, op_5f
   dd op_60, op_61, op_62, op_63, op_64, op_65, op_66, op_67
   dd op_68, op_69, op_6a, op_6b, op_6c, op_6d, op_6e, op_6f
   dd op_70, op_71, op_72, op_73, op_74, op_75, op_76, op_77
   dd op_78, op_79, op_7a, op_7b, op_7c, op_7d, op_7e, op_7f
   dd op_80, op_81, op_82, op_83, op_84, op_85, op_86, op_87
   dd op_88, op_89, op_8a, op_8b, op_8c, op_8d, op_8e, op_8f
   dd op_90, op_91, op_92, op_93, op_94, op_95, op_96, op_97
   dd op_98, op_99, op_9a, op_9b, op_9c, op_9d, op_9e, op_9f
   dd op_a0, op_a1, op_a2, op_a3, op_a4, op_a5, op_a6, op_a7
   dd op_a8, op_a9, op_aa, op_ab, op_ac, op_ad, op_ae, op_af
   dd op_b0, op_b1, op_b2, op_b3, op_b4, op_b5, op_b6, op_b7
   dd op_b8, op_b9, op_ba, op_bb, op_bc, op_bd, op_be, op_bf
   dd op_c0, op_c1, op_c2, op_c3, op_c4, op_c5, op_c6, op_c7
   dd op_c8, op_c9, op_ca, op_cb, op_cc, op_cd, op_ce, op_cf
   dd op_d0, op_d1, op_d2, op_d3, op_d4, op_d5, op_d6, op_d7
   dd op_d8, op_d9, op_da, op_db, op_dc, op_dd, op_de, op_df
   dd op_e0, op_e1, op_e2, op_e3, op_e4, op_e5, op_e6, op_e7
   dd op_e8, op_e9, op_ea, op_eb, op_ec, op_ed, op_ee, op_ef
   dd op_f0, op_f1, op_f2, op_f3, op_f4, op_f5, op_f6, op_f7
   dd op_f8, op_f9, op_fa, op_fb, op_fc, op_fd, op_fe, op_ff

CBTable:
   dd cb_00, cb_01, cb_02, cb_03, cb_04, cb_05, cb_06, cb_07
   dd cb_08, cb_09, cb_0a, cb_0b, cb_0c, cb_0d, cb_0e, cb_0f
   dd cb_10, cb_11, cb_12, cb_13, cb_14, cb_15, cb_16, cb_17
   dd cb_18, cb_19, cb_1a, cb_1b, cb_1c, cb_1d, cb_1e, cb_1f
   dd cb_20, cb_21, cb_22, cb_23, cb_24, cb_25, cb_26, cb_27
   dd cb_28, cb_29, cb_2a, cb_2b, cb_2c, cb_2d, cb_2e, cb_2f
   dd cb_30, cb_31, cb_32, cb_33, cb_34, cb_35, cb_36, cb_37
   dd cb_38, cb_39, cb_3a, cb_3b, cb_3c, cb_3d, cb_3e, cb_3f
   dd cb_40, cb_41, cb_42, cb_43, cb_44, cb_45, cb_46, cb_47
   dd cb_48, cb_49, cb_4a, cb_4b, cb_4c, cb_4d, cb_4e, cb_4f
   dd cb_50, cb_51, cb_52, cb_53, cb_54, cb_55, cb_56, cb_57
   dd cb_58, cb_59, cb_5a, cb_5b, cb_5c, cb_5d, cb_5e, cb_5f
   dd cb_60, cb_61, cb_62, cb_63, cb_64, cb_65, cb_66, cb_67
   dd cb_68, cb_69, cb_6a, cb_6b, cb_6c, cb_6d, cb_6e, cb_6f
   dd cb_70, cb_71, cb_72, cb_73, cb_74, cb_75, cb_76, cb_77
   dd cb_78, cb_79, cb_7a, cb_7b, cb_7c, cb_7d, cb_7e, cb_7f
   dd cb_80, cb_81, cb_82, cb_83, cb_84, cb_85, cb_86, cb_87
   dd cb_88, cb_89, cb_8a, cb_8b, cb_8c, cb_8d, cb_8e, cb_8f
   dd cb_90, cb_91, cb_92, cb_93, cb_94, cb_95, cb_96, cb_97
   dd cb_98, cb_99, cb_9a, cb_9b, cb_9c, cb_9d, cb_9e, cb_9f
   dd cb_a0, cb_a1, cb_a2, cb_a3, cb_a4, cb_a5, cb_a6, cb_a7
   dd cb_a8, cb_a9, cb_aa, cb_ab, cb_ac, cb_ad, cb_ae, cb_af
   dd cb_b0, cb_b1, cb_b2, cb_b3, cb_b4, cb_b5, cb_b6, cb_b7
   dd cb_b8, cb_b9, cb_ba, cb_bb, cb_bc, cb_bd, cb_be, cb_bf
   dd cb_c0, cb_c1, cb_c2, cb_c3, cb_c4, cb_c5, cb_c6, cb_c7
   dd cb_c8, cb_c9, cb_ca, cb_cb, cb_cc, cb_cd, cb_ce, cb_cf
   dd cb_d0, cb_d1, cb_d2, cb_d3, cb_d4, cb_d5, cb_d6, cb_d7
   dd cb_d8, cb_d9, cb_da, cb_db, cb_dc, cb_dd, cb_de, cb_df
   dd cb_e0, cb_e1, cb_e2, cb_e3, cb_e4, cb_e5, cb_e6, cb_e7
   dd cb_e8, cb_e9, cb_ea, cb_eb, cb_ec, cb_ed, cb_ee, cb_ef
   dd cb_f0, cb_f1, cb_f2, cb_f3, cb_f4, cb_f5, cb_f6, cb_f7
   dd cb_f8, cb_f9, cb_fa, cb_fb, cb_fc, cb_fd, cb_fe, cb_ff

EDTable:
   dd ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx
   dd ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx
   dd ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx
   dd ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx
   dd ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx
   dd ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx
   dd ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx
   dd ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx
   dd ed_40, ed_41, ed_42, ed_43, ed_44, ed_45, ed_46, ed_47
   dd ed_48, ed_49, ed_4a, ed_4b, ed_4c, ed_4d, ed_4e, ed_4f
   dd ed_50, ed_51, ed_52, ed_53, ed_54, ed_55, ed_56, ed_57
   dd ed_58, ed_59, ed_5a, ed_5b, ed_5c, ed_5d, ed_5e, ed_5f
   dd ed_60, ed_61, ed_62, ed_63, ed_64, ed_65, ed_66, ed_67
   dd ed_68, ed_69, ed_6a, ed_6b, ed_6c, ed_6d, ed_6e, ed_6f
   dd ed_70, ed_71, ed_72, ed_73, ed_74, ed_75, ed_76, ed_xx
   dd ed_78, ed_79, ed_7a, ed_7b, ed_7c, ed_7d, ed_7e, ed_xx
   dd ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx
   dd ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx
   dd ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx
   dd ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx
   dd ed_a0, ed_a1, ed_a2, ed_a3, ed_xx, ed_xx, ed_xx, ed_xx
   dd ed_a8, ed_a9, ed_aa, ed_ab, ed_xx, ed_xx, ed_xx, ed_xx
   dd ed_b0, ed_b1, ed_b2, ed_b3, ed_xx, ed_xx, ed_xx, ed_xx
   dd ed_b8, ed_b9, ed_ba, ed_bb, ed_xx, ed_xx, ed_xx, ed_xx
   dd ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx
   dd ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx
   dd ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx
   dd ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx
   dd ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx
   dd ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx
   dd ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx
   dd ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx, ed_xx

DDTable:
   dd dd_xx, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx
   dd dd_xx, dd_09, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx
   dd dd_xx, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx
   dd dd_xx, dd_19, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx
   dd dd_xx, dd_21, dd_22, dd_23, dd_24, dd_25, dd_26, dd_xx
   dd dd_xx, dd_29, dd_2a, dd_2b, dd_2c, dd_2d, dd_2e, dd_xx
   dd dd_xx, dd_xx, dd_xx, dd_xx, dd_34, dd_35, dd_36, dd_xx
   dd dd_xx, dd_39, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx
   dd dd_xx, dd_xx, dd_xx, dd_xx, dd_44, dd_45, dd_46, dd_xx
   dd dd_xx, dd_xx, dd_xx, dd_xx, dd_4c, dd_4d, dd_4e, dd_xx
   dd dd_xx, dd_xx, dd_xx, dd_xx, dd_54, dd_55, dd_56, dd_xx
   dd dd_xx, dd_xx, dd_xx, dd_xx, dd_5c, dd_5d, dd_5e, dd_xx
   dd dd_60, dd_61, dd_62, dd_63, dd_xx, dd_65, dd_66, dd_67
   dd dd_68, dd_69, dd_6a, dd_6b, dd_6c, dd_xx, dd_6e, dd_6f
   dd dd_70, dd_71, dd_72, dd_73, dd_74, dd_75, dd_xx, dd_77
   dd dd_xx, dd_xx, dd_xx, dd_xx, dd_7c, dd_7d, dd_7e, dd_xx
   dd dd_xx, dd_xx, dd_xx, dd_xx, dd_84, dd_85, dd_86, dd_xx
   dd dd_xx, dd_xx, dd_xx, dd_xx, dd_8c, dd_8d, dd_8e, dd_xx
   dd dd_xx, dd_xx, dd_xx, dd_xx, dd_94, dd_95, dd_96, dd_xx
   dd dd_xx, dd_xx, dd_xx, dd_xx, dd_9c, dd_9d, dd_9e, dd_xx
   dd dd_xx, dd_xx, dd_xx, dd_xx, dd_a4, dd_a5, dd_a6, dd_xx
   dd dd_xx, dd_xx, dd_xx, dd_xx, dd_ac, dd_ad, dd_ae, dd_xx
   dd dd_xx, dd_xx, dd_xx, dd_xx, dd_b4, dd_b5, dd_b6, dd_xx
   dd dd_xx, dd_xx, dd_xx, dd_xx, dd_bc, dd_bd, dd_be, dd_xx
   dd dd_xx, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx
   dd dd_xx, dd_xx, dd_xx, dd_cb, dd_xx, dd_xx, dd_xx, dd_xx
   dd dd_xx, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx
   dd dd_xx, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx
   dd dd_xx, dd_e1, dd_xx, dd_e3, dd_xx, dd_e5, dd_xx, dd_xx
   dd dd_xx, dd_e9, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx
   dd dd_xx, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx
   dd dd_xx, dd_f9, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx, dd_xx

FDTable:
   dd fd_xx, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx
   dd fd_xx, fd_09, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx
   dd fd_xx, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx
   dd fd_xx, fd_19, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx
   dd fd_xx, fd_21, fd_22, fd_23, fd_24, fd_25, fd_26, fd_xx
   dd fd_xx, fd_29, fd_2a, fd_2b, fd_2c, fd_2d, fd_2e, fd_xx
   dd fd_xx, fd_xx, fd_xx, fd_xx, fd_34, fd_35, fd_36, fd_xx
   dd fd_xx, fd_39, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx
   dd fd_xx, fd_xx, fd_xx, fd_xx, fd_44, fd_45, fd_46, fd_xx
   dd fd_xx, fd_xx, fd_xx, fd_xx, fd_4c, fd_4d, fd_4e, fd_xx
   dd fd_xx, fd_xx, fd_xx, fd_xx, fd_54, fd_55, fd_56, fd_xx
   dd fd_xx, fd_xx, fd_xx, fd_xx, fd_5c, fd_5d, fd_5e, fd_xx
   dd fd_60, fd_61, fd_62, fd_63, fd_xx, fd_65, fd_66, fd_67
   dd fd_68, fd_69, fd_6a, fd_6b, fd_6c, fd_xx, fd_6e, fd_6f
   dd fd_70, fd_71, fd_72, fd_73, fd_74, fd_75, fd_xx, fd_77
   dd fd_xx, fd_xx, fd_xx, fd_xx, fd_7c, fd_7d, fd_7e, fd_xx
   dd fd_xx, fd_xx, fd_xx, fd_xx, fd_84, fd_85, fd_86, fd_xx
   dd fd_xx, fd_xx, fd_xx, fd_xx, fd_8c, fd_8d, fd_8e, fd_xx
   dd fd_xx, fd_xx, fd_xx, fd_xx, fd_94, fd_95, fd_96, fd_xx
   dd fd_xx, fd_xx, fd_xx, fd_xx, fd_9c, fd_9d, fd_9e, fd_xx
   dd fd_xx, fd_xx, fd_xx, fd_xx, fd_a4, fd_a5, fd_a6, fd_xx
   dd fd_xx, fd_xx, fd_xx, fd_xx, fd_ac, fd_ad, fd_ae, fd_xx
   dd fd_xx, fd_xx, fd_xx, fd_xx, fd_b4, fd_b5, fd_b6, fd_xx
   dd fd_xx, fd_xx, fd_xx, fd_xx, fd_bc, fd_bd, fd_be, fd_xx
   dd fd_xx, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx
   dd fd_xx, fd_xx, fd_xx, fd_cb, fd_xx, fd_xx, fd_xx, fd_xx
   dd fd_xx, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx
   dd fd_xx, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx
   dd fd_xx, fd_e1, fd_xx, fd_e3, fd_xx, fd_e5, fd_xx, fd_xx
   dd fd_xx, fd_e9, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx
   dd fd_xx, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx
   dd fd_xx, fd_f9, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx, fd_xx

DDCBTable:
   dd ddcb_00, ddcb_01, ddcb_02, ddcb_03, ddcb_04, ddcb_05, ddcb_06, ddcb_07
   dd ddcb_08, ddcb_09, ddcb_0a, ddcb_0b, ddcb_0c, ddcb_0d, ddcb_0e, ddcb_0f
   dd ddcb_10, ddcb_11, ddcb_12, ddcb_13, ddcb_14, ddcb_15, ddcb_16, ddcb_17
   dd ddcb_18, ddcb_19, ddcb_1a, ddcb_1b, ddcb_1c, ddcb_1d, ddcb_1e, ddcb_1f
   dd ddcb_20, ddcb_21, ddcb_22, ddcb_23, ddcb_24, ddcb_25, ddcb_26, ddcb_27
   dd ddcb_28, ddcb_29, ddcb_2a, ddcb_2b, ddcb_2c, ddcb_2d, ddcb_2e, ddcb_2f
   dd ddcb_30, ddcb_31, ddcb_32, ddcb_33, ddcb_34, ddcb_35, ddcb_36, ddcb_37
   dd ddcb_38, ddcb_39, ddcb_3a, ddcb_3b, ddcb_3c, ddcb_3d, ddcb_3e, ddcb_3f
   dd ddcb_40, ddcb_41, ddcb_42, ddcb_43, ddcb_44, ddcb_45, ddcb_46, ddcb_47
   dd ddcb_48, ddcb_49, ddcb_4a, ddcb_4b, ddcb_4c, ddcb_4d, ddcb_4e, ddcb_4f
   dd ddcb_50, ddcb_51, ddcb_52, ddcb_53, ddcb_54, ddcb_55, ddcb_56, ddcb_57
   dd ddcb_58, ddcb_59, ddcb_5a, ddcb_5b, ddcb_5c, ddcb_5d, ddcb_5e, ddcb_5f
   dd ddcb_60, ddcb_61, ddcb_62, ddcb_63, ddcb_64, ddcb_65, ddcb_66, ddcb_67
   dd ddcb_68, ddcb_69, ddcb_6a, ddcb_6b, ddcb_6c, ddcb_6d, ddcb_6e, ddcb_6f
   dd ddcb_70, ddcb_71, ddcb_72, ddcb_73, ddcb_74, ddcb_75, ddcb_76, ddcb_77
   dd ddcb_78, ddcb_79, ddcb_7a, ddcb_7b, ddcb_7c, ddcb_7d, ddcb_7e, ddcb_7f
   dd ddcb_80, ddcb_81, ddcb_82, ddcb_83, ddcb_84, ddcb_85, ddcb_86, ddcb_87
   dd ddcb_88, ddcb_89, ddcb_8a, ddcb_8b, ddcb_8c, ddcb_8d, ddcb_8e, ddcb_8f
   dd ddcb_90, ddcb_91, ddcb_92, ddcb_93, ddcb_94, ddcb_95, ddcb_96, ddcb_97
   dd ddcb_98, ddcb_99, ddcb_9a, ddcb_9b, ddcb_9c, ddcb_9d, ddcb_9e, ddcb_9f
   dd ddcb_a0, ddcb_a1, ddcb_a2, ddcb_a3, ddcb_a4, ddcb_a5, ddcb_a6, ddcb_a7
   dd ddcb_a8, ddcb_a9, ddcb_aa, ddcb_ab, ddcb_ac, ddcb_ad, ddcb_ae, ddcb_af
   dd ddcb_b0, ddcb_b1, ddcb_b2, ddcb_b3, ddcb_b4, ddcb_b5, ddcb_b6, ddcb_b7
   dd ddcb_b8, ddcb_b9, ddcb_ba, ddcb_bb, ddcb_bc, ddcb_bd, ddcb_be, ddcb_bf
   dd ddcb_c0, ddcb_c1, ddcb_c2, ddcb_c3, ddcb_c4, ddcb_c5, ddcb_c6, ddcb_c7
   dd ddcb_c8, ddcb_c9, ddcb_ca, ddcb_cb, ddcb_cc, ddcb_cd, ddcb_ce, ddcb_cf
   dd ddcb_d0, ddcb_d1, ddcb_d2, ddcb_d3, ddcb_d4, ddcb_d5, ddcb_d6, ddcb_d7
   dd ddcb_d8, ddcb_d9, ddcb_da, ddcb_db, ddcb_dc, ddcb_dd, ddcb_de, ddcb_df
   dd ddcb_e0, ddcb_e1, ddcb_e2, ddcb_e3, ddcb_e4, ddcb_e5, ddcb_e6, ddcb_e7
   dd ddcb_e8, ddcb_e9, ddcb_ea, ddcb_eb, ddcb_ec, ddcb_ed, ddcb_ee, ddcb_ef
   dd ddcb_f0, ddcb_f1, ddcb_f2, ddcb_f3, ddcb_f4, ddcb_f5, ddcb_f6, ddcb_f7
   dd ddcb_f8, ddcb_f9, ddcb_fa, ddcb_fb, ddcb_fc, ddcb_fd, ddcb_fe, ddcb_ff


;----------------------------------------------------------------------------;
; flag tables ('borrowed' from MAZE, by Ishmair)
INC_Table:
DB  0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,16,0,0,0,0
DB  0,0,0,8,8,8,8,8,8,8,8,48,32,32,32,32,32,32,32,40
DB  40,40,40,40,40,40,40,48,32,32,32,32,32,32,32,40,40,40,40,40
DB  40,40,40,16,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,16
DB  0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,48,32,32,32,32
DB  32,32,32,40,40,40,40,40,40,40,40,48,32,32,32,32,32,32,32,40
DB  40,40,40,40,40,40,40,148,128,128,128,128,128,128,128,136,136,136,136,136
DB  136,136,136,144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136,176
DB  160,160,160,160,160,160,160,168,168,168,168,168,168,168,168,176,160,160,160,160
DB  160,160,160,168,168,168,168,168,168,168,168,144,128,128,128,128,128,128,128,136
DB  136,136,136,136,136,136,136,144,128,128,128,128,128,128,128,136,136,136,136,136
DB  136,136,136,176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168,176
DB  160,160,160,160,160,160,160,168,168,168,168,168,168,168,168,80

DEC_Table:
DB  186,66,2,2,2,2,2,2,2,10,10,10,10,10,10,10,26,2,2,2
DB  2,2,2,2,2,10,10,10,10,10,10,10,26,34,34,34,34,34,34,34
DB  34,42,42,42,42,42,42,42,58,34,34,34,34,34,34,34,34,42,42,42
DB  42,42,42,42,58,2,2,2,2,2,2,2,2,10,10,10,10,10,10,10
DB  26,2,2,2,2,2,2,2,2,10,10,10,10,10,10,10,26,34,34,34
DB  34,34,34,34,34,42,42,42,42,42,42,42,58,34,34,34,34,34,34,34
DB  34,42,42,42,42,42,42,42,62,130,130,130,130,130,130,130,130,138,138,138
DB  138,138,138,138,154,130,130,130,130,130,130,130,130,138,138,138,138,138,138,138
DB  154,162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186,162,162,162
DB  162,162,162,162,162,170,170,170,170,170,170,170,186,130,130,130,130,130,130,130
DB  130,138,138,138,138,138,138,138,154,130,130,130,130,130,130,130,130,138,138,138
DB  138,138,138,138,154,162,162,162,162,162,162,162,162,170,170,170,170,170,170,170
DB  186,162,162,162,162,162,162,162,162,170,170,170,170,170,170,170

; DAA table ('borrowed' from Z80Emul, by unknown)
DAA_Table:
DW  04400h, 00001h, 00002h, 00403h, 00004h, 00405h, 00406h, 00007h
DW  00808h, 00C09h, 01010h, 01411h, 01412h, 01013h, 01414h, 01015h
DW  00010h, 00411h, 00412h, 00013h, 00414h, 00015h, 00016h, 00417h
DW  00C18h, 00819h, 03020h, 03421h, 03422h, 03023h, 03424h, 03025h
DW  02020h, 02421h, 02422h, 02023h, 02424h, 02025h, 02026h, 02427h
DW  02C28h, 02829h, 03430h, 03031h, 03032h, 03433h, 03034h, 03435h
DW  02430h, 02031h, 02032h, 02433h, 02034h, 02435h, 02436h, 02037h
DW  02838h, 02C39h, 01040h, 01441h, 01442h, 01043h, 01444h, 01045h
DW  00040h, 00441h, 00442h, 00043h, 00444h, 00045h, 00046h, 00447h
DW  00C48h, 00849h, 01450h, 01051h, 01052h, 01453h, 01054h, 01455h
DW  00450h, 00051h, 00052h, 00453h, 00054h, 00455h, 00456h, 00057h
DW  00858h, 00C59h, 03460h, 03061h, 03062h, 03463h, 03064h, 03465h
DW  02460h, 02061h, 02062h, 02463h, 02064h, 02465h, 02466h, 02067h
DW  02868h, 02C69h, 03070h, 03471h, 03472h, 03073h, 03474h, 03075h
DW  02070h, 02471h, 02472h, 02073h, 02474h, 02075h, 02076h, 02477h
DW  02C78h, 02879h, 09080h, 09481h, 09482h, 09083h, 09484h, 09085h
DW  08080h, 08481h, 08482h, 08083h, 08484h, 08085h, 08086h, 08487h
DW  08C88h, 08889h, 09490h, 09091h, 09092h, 09493h, 09094h, 09495h
DW  08490h, 08091h, 08092h, 08493h, 08094h, 08495h, 08496h, 08097h
DW  08898h, 08C99h, 05500h, 01101h, 01102h, 01503h, 01104h, 01505h
DW  04500h, 00101h, 00102h, 00503h, 00104h, 00505h, 00506h, 00107h
DW  00908h, 00D09h, 01110h, 01511h, 01512h, 01113h, 01514h, 01115h
DW  00110h, 00511h, 00512h, 00113h, 00514h, 00115h, 00116h, 00517h
DW  00D18h, 00919h, 03120h, 03521h, 03522h, 03123h, 03524h, 03125h
DW  02120h, 02521h, 02522h, 02123h, 02524h, 02125h, 02126h, 02527h
DW  02D28h, 02929h, 03530h, 03131h, 03132h, 03533h, 03134h, 03535h
DW  02530h, 02131h, 02132h, 02533h, 02134h, 02535h, 02536h, 02137h
DW  02938h, 02D39h, 01140h, 01541h, 01542h, 01143h, 01544h, 01145h
DW  00140h, 00541h, 00542h, 00143h, 00544h, 00145h, 00146h, 00547h
DW  00D48h, 00949h, 01550h, 01151h, 01152h, 01553h, 01154h, 01555h
DW  00550h, 00151h, 00152h, 00553h, 00154h, 00555h, 00556h, 00157h
DW  00958h, 00D59h, 03560h, 03161h, 03162h, 03563h, 03164h, 03565h
DW  02560h, 02161h, 02162h, 02563h, 02164h, 02565h, 02566h, 02167h
DW  02968h, 02D69h, 03170h, 03571h, 03572h, 03173h, 03574h, 03175h
DW  02170h, 02571h, 02572h, 02173h, 02574h, 02175h, 02176h, 02577h
DW  02D78h, 02979h, 09180h, 09581h, 09582h, 09183h, 09584h, 09185h
DW  08180h, 08581h, 08582h, 08183h, 08584h, 08185h, 08186h, 08587h
DW  08D88h, 08989h, 09590h, 09191h, 09192h, 09593h, 09194h, 09595h
DW  08590h, 08191h, 08192h, 08593h, 08194h, 08595h, 08596h, 08197h
DW  08998h, 08D99h, 0B5A0h, 0B1A1h, 0B1A2h, 0B5A3h, 0B1A4h, 0B5A5h
DW  0A5A0h, 0A1A1h, 0A1A2h, 0A5A3h, 0A1A4h, 0A5A5h, 0A5A6h, 0A1A7h
DW  0A9A8h, 0ADA9h, 0B1B0h, 0B5B1h, 0B5B2h, 0B1B3h, 0B5B4h, 0B1B5h
DW  0A1B0h, 0A5B1h, 0A5B2h, 0A1B3h, 0A5B4h, 0A1B5h, 0A1B6h, 0A5B7h
DW  0ADB8h, 0A9B9h, 095C0h, 091C1h, 091C2h, 095C3h, 091C4h, 095C5h
DW  085C0h, 081C1h, 081C2h, 085C3h, 081C4h, 085C5h, 085C6h, 081C7h
DW  089C8h, 08DC9h, 091D0h, 095D1h, 095D2h, 091D3h, 095D4h, 091D5h
DW  081D0h, 085D1h, 085D2h, 081D3h, 085D4h, 081D5h, 081D6h, 085D7h
DW  08DD8h, 089D9h, 0B1E0h, 0B5E1h, 0B5E2h, 0B1E3h, 0B5E4h, 0B1E5h
DW  0A1E0h, 0A5E1h, 0A5E2h, 0A1E3h, 0A5E4h, 0A1E5h, 0A1E6h, 0A5E7h
DW  0ADE8h, 0A9E9h, 0B5F0h, 0B1F1h, 0B1F2h, 0B5F3h, 0B1F4h, 0B5F5h
DW  0A5F0h, 0A1F1h, 0A1F2h, 0A5F3h, 0A1F4h, 0A5F5h, 0A5F6h, 0A1F7h
DW  0A9F8h, 0ADF9h, 05500h, 01101h, 01102h, 01503h, 01104h, 01505h
DW  04500h, 00101h, 00102h, 00503h, 00104h, 00505h, 00506h, 00107h
DW  00908h, 00D09h, 01110h, 01511h, 01512h, 01113h, 01514h, 01115h
DW  00110h, 00511h, 00512h, 00113h, 00514h, 00115h, 00116h, 00517h
DW  00D18h, 00919h, 03120h, 03521h, 03522h, 03123h, 03524h, 03125h
DW  02120h, 02521h, 02522h, 02123h, 02524h, 02125h, 02126h, 02527h
DW  02D28h, 02929h, 03530h, 03131h, 03132h, 03533h, 03134h, 03535h
DW  02530h, 02131h, 02132h, 02533h, 02134h, 02535h, 02536h, 02137h
DW  02938h, 02D39h, 01140h, 01541h, 01542h, 01143h, 01544h, 01145h
DW  00140h, 00541h, 00542h, 00143h, 00544h, 00145h, 00146h, 00547h
DW  00D48h, 00949h, 01550h, 01151h, 01152h, 01553h, 01154h, 01555h
DW  00550h, 00151h, 00152h, 00553h, 00154h, 00555h, 00556h, 00157h
DW  00958h, 00D59h, 03560h, 03161h, 03162h, 03563h, 03164h, 03565h
DW  04600h, 00201h, 00202h, 00603h, 00204h, 00605h, 00606h, 00207h
DW  00A08h, 00E09h, 00204h, 00605h, 00606h, 00207h, 00A08h, 00E09h
DW  00210h, 00611h, 00612h, 00213h, 00614h, 00215h, 00216h, 00617h
DW  00E18h, 00A19h, 00614h, 00215h, 00216h, 00617h, 00E18h, 00A19h
DW  02220h, 02621h, 02622h, 02223h, 02624h, 02225h, 02226h, 02627h
DW  02E28h, 02A29h, 02624h, 02225h, 02226h, 02627h, 02E28h, 02A29h
DW  02630h, 02231h, 02232h, 02633h, 02234h, 02635h, 02636h, 02237h
DW  02A38h, 02E39h, 02234h, 02635h, 02636h, 02237h, 02A38h, 02E39h
DW  00240h, 00641h, 00642h, 00243h, 00644h, 00245h, 00246h, 00647h
DW  00E48h, 00A49h, 00644h, 00245h, 00246h, 00647h, 00E48h, 00A49h
DW  00650h, 00251h, 00252h, 00653h, 00254h, 00655h, 00656h, 00257h
DW  00A58h, 00E59h, 00254h, 00655h, 00656h, 00257h, 00A58h, 00E59h
DW  02660h, 02261h, 02262h, 02663h, 02264h, 02665h, 02666h, 02267h
DW  02A68h, 02E69h, 02264h, 02665h, 02666h, 02267h, 02A68h, 02E69h
DW  02270h, 02671h, 02672h, 02273h, 02674h, 02275h, 02276h, 02677h
DW  02E78h, 02A79h, 02674h, 02275h, 02276h, 02677h, 02E78h, 02A79h
DW  08280h, 08681h, 08682h, 08283h, 08684h, 08285h, 08286h, 08687h
DW  08E88h, 08A89h, 08684h, 08285h, 08286h, 08687h, 08E88h, 08A89h
DW  08690h, 08291h, 08292h, 08693h, 08294h, 08695h, 08696h, 08297h
DW  08A98h, 08E99h, 02334h, 02735h, 02736h, 02337h, 02B38h, 02F39h
DW  00340h, 00741h, 00742h, 00343h, 00744h, 00345h, 00346h, 00747h
DW  00F48h, 00B49h, 00744h, 00345h, 00346h, 00747h, 00F48h, 00B49h
DW  00750h, 00351h, 00352h, 00753h, 00354h, 00755h, 00756h, 00357h
DW  00B58h, 00F59h, 00354h, 00755h, 00756h, 00357h, 00B58h, 00F59h
DW  02760h, 02361h, 02362h, 02763h, 02364h, 02765h, 02766h, 02367h
DW  02B68h, 02F69h, 02364h, 02765h, 02766h, 02367h, 02B68h, 02F69h
DW  02370h, 02771h, 02772h, 02373h, 02774h, 02375h, 02376h, 02777h
DW  02F78h, 02B79h, 02774h, 02375h, 02376h, 02777h, 02F78h, 02B79h
DW  08380h, 08781h, 08782h, 08383h, 08784h, 08385h, 08386h, 08787h
DW  08F88h, 08B89h, 08784h, 08385h, 08386h, 08787h, 08F88h, 08B89h
DW  08790h, 08391h, 08392h, 08793h, 08394h, 08795h, 08796h, 08397h
DW  08B98h, 08F99h, 08394h, 08795h, 08796h, 08397h, 08B98h, 08F99h
DW  0A7A0h, 0A3A1h, 0A3A2h, 0A7A3h, 0A3A4h, 0A7A5h, 0A7A6h, 0A3A7h
DW  0ABA8h, 0AFA9h, 0A3A4h, 0A7A5h, 0A7A6h, 0A3A7h, 0ABA8h, 0AFA9h
DW  0A3B0h, 0A7B1h, 0A7B2h, 0A3B3h, 0A7B4h, 0A3B5h, 0A3B6h, 0A7B7h
DW  0AFB8h, 0ABB9h, 0A7B4h, 0A3B5h, 0A3B6h, 0A7B7h, 0AFB8h, 0ABB9h
DW  087C0h, 083C1h, 083C2h, 087C3h, 083C4h, 087C5h, 087C6h, 083C7h
DW  08BC8h, 08FC9h, 083C4h, 087C5h, 087C6h, 083C7h, 08BC8h, 08FC9h
DW  083D0h, 087D1h, 087D2h, 083D3h, 087D4h, 083D5h, 083D6h, 087D7h
DW  08FD8h, 08BD9h, 087D4h, 083D5h, 083D6h, 087D7h, 08FD8h, 08BD9h
DW  0A3E0h, 0A7E1h, 0A7E2h, 0A3E3h, 0A7E4h, 0A3E5h, 0A3E6h, 0A7E7h
DW  0AFE8h, 0ABE9h, 0A7E4h, 0A3E5h, 0A3E6h, 0A7E7h, 0AFE8h, 0ABE9h
DW  0A7F0h, 0A3F1h, 0A3F2h, 0A7F3h, 0A3F4h, 0A7F5h, 0A7F6h, 0A3F7h
DW  0ABF8h, 0AFF9h, 0A3F4h, 0A7F5h, 0A7F6h, 0A3F7h, 0ABF8h, 0AFF9h
DW  04700h, 00301h, 00302h, 00703h, 00304h, 00705h, 00706h, 00307h
DW  00B08h, 00F09h, 00304h, 00705h, 00706h, 00307h, 00B08h, 00F09h
DW  00310h, 00711h, 00712h, 00313h, 00714h, 00315h, 00316h, 00717h
DW  00F18h, 00B19h, 00714h, 00315h, 00316h, 00717h, 00F18h, 00B19h
DW  02320h, 02721h, 02722h, 02323h, 02724h, 02325h, 02326h, 02727h
DW  02F28h, 02B29h, 02724h, 02325h, 02326h, 02727h, 02F28h, 02B29h
DW  02730h, 02331h, 02332h, 02733h, 02334h, 02735h, 02736h, 02337h
DW  02B38h, 02F39h, 02334h, 02735h, 02736h, 02337h, 02B38h, 02F39h
DW  00340h, 00741h, 00742h, 00343h, 00744h, 00345h, 00346h, 00747h
DW  00F48h, 00B49h, 00744h, 00345h, 00346h, 00747h, 00F48h, 00B49h
DW  00750h, 00351h, 00352h, 00753h, 00354h, 00755h, 00756h, 00357h
DW  00B58h, 00F59h, 00354h, 00755h, 00756h, 00357h, 00B58h, 00F59h
DW  02760h, 02361h, 02362h, 02763h, 02364h, 02765h, 02766h, 02367h
DW  02B68h, 02F69h, 02364h, 02765h, 02766h, 02367h, 02B68h, 02F69h
DW  02370h, 02771h, 02772h, 02373h, 02774h, 02375h, 02376h, 02777h
DW  02F78h, 02B79h, 02774h, 02375h, 02376h, 02777h, 02F78h, 02B79h
DW  08380h, 08781h, 08782h, 08383h, 08784h, 08385h, 08386h, 08787h
DW  08F88h, 08B89h, 08784h, 08385h, 08386h, 08787h, 08F88h, 08B89h
DW  08790h, 08391h, 08392h, 08793h, 08394h, 08795h, 08796h, 08397h
DW  08B98h, 08F99h, 08394h, 08795h, 08796h, 08397h, 08B98h, 08F99h
DW  00406h, 00007h, 00808h, 00C09h, 00C0Ah, 0080Bh, 00C0Ch, 0080Dh
DW  0080Eh, 00C0Fh, 01010h, 01411h, 01412h, 01013h, 01414h, 01015h
DW  00016h, 00417h, 00C18h, 00819h, 0081Ah, 00C1Bh, 0081Ch, 00C1Dh
DW  00C1Eh, 0081Fh, 03020h, 03421h, 03422h, 03023h, 03424h, 03025h
DW  02026h, 02427h, 02C28h, 02829h, 0282Ah, 02C2Bh, 0282Ch, 02C2Dh
DW  02C2Eh, 0282Fh, 03430h, 03031h, 03032h, 03433h, 03034h, 03435h
DW  02436h, 02037h, 02838h, 02C39h, 02C3Ah, 0283Bh, 02C3Ch, 0283Dh
DW  0283Eh, 02C3Fh, 01040h, 01441h, 01442h, 01043h, 01444h, 01045h
DW  00046h, 00447h, 00C48h, 00849h, 0084Ah, 00C4Bh, 0084Ch, 00C4Dh
DW  00C4Eh, 0084Fh, 01450h, 01051h, 01052h, 01453h, 01054h, 01455h
DW  00456h, 00057h, 00858h, 00C59h, 00C5Ah, 0085Bh, 00C5Ch, 0085Dh
DW  0085Eh, 00C5Fh, 03460h, 03061h, 03062h, 03463h, 03064h, 03465h
DW  02466h, 02067h, 02868h, 02C69h, 02C6Ah, 0286Bh, 02C6Ch, 0286Dh
DW  0286Eh, 02C6Fh, 03070h, 03471h, 03472h, 03073h, 03474h, 03075h
DW  02076h, 02477h, 02C78h, 02879h, 0287Ah, 02C7Bh, 0287Ch, 02C7Dh
DW  02C7Eh, 0287Fh, 09080h, 09481h, 09482h, 09083h, 09484h, 09085h
DW  08086h, 08487h, 08C88h, 08889h, 0888Ah, 08C8Bh, 0888Ch, 08C8Dh
DW  08C8Eh, 0888Fh, 09490h, 09091h, 09092h, 09493h, 09094h, 09495h
DW  08496h, 08097h, 08898h, 08C99h, 08C9Ah, 0889Bh, 08C9Ch, 0889Dh
DW  0889Eh, 08C9Fh, 05500h, 01101h, 01102h, 01503h, 01104h, 01505h
DW  00506h, 00107h, 00908h, 00D09h, 00D0Ah, 0090Bh, 00D0Ch, 0090Dh
DW  0090Eh, 00D0Fh, 01110h, 01511h, 01512h, 01113h, 01514h, 01115h
DW  00116h, 00517h, 00D18h, 00919h, 0091Ah, 00D1Bh, 0091Ch, 00D1Dh
DW  00D1Eh, 0091Fh, 03120h, 03521h, 03522h, 03123h, 03524h, 03125h
DW  02126h, 02527h, 02D28h, 02929h, 0292Ah, 02D2Bh, 0292Ch, 02D2Dh
DW  02D2Eh, 0292Fh, 03530h, 03131h, 03132h, 03533h, 03134h, 03535h
DW  02536h, 02137h, 02938h, 02D39h, 02D3Ah, 0293Bh, 02D3Ch, 0293Dh
DW  0293Eh, 02D3Fh, 01140h, 01541h, 01542h, 01143h, 01544h, 01145h
DW  00146h, 00547h, 00D48h, 00949h, 0094Ah, 00D4Bh, 0094Ch, 00D4Dh
DW  00D4Eh, 0094Fh, 01550h, 01151h, 01152h, 01553h, 01154h, 01555h
DW  00556h, 00157h, 00958h, 00D59h, 00D5Ah, 0095Bh, 00D5Ch, 0095Dh
DW  0095Eh, 00D5Fh, 03560h, 03161h, 03162h, 03563h, 03164h, 03565h
DW  02566h, 02167h, 02968h, 02D69h, 02D6Ah, 0296Bh, 02D6Ch, 0296Dh
DW  0296Eh, 02D6Fh, 03170h, 03571h, 03572h, 03173h, 03574h, 03175h
DW  02176h, 02577h, 02D78h, 02979h, 0297Ah, 02D7Bh, 0297Ch, 02D7Dh
DW  02D7Eh, 0297Fh, 09180h, 09581h, 09582h, 09183h, 09584h, 09185h
DW  08186h, 08587h, 08D88h, 08989h, 0898Ah, 08D8Bh, 0898Ch, 08D8Dh
DW  08D8Eh, 0898Fh, 09590h, 09191h, 09192h, 09593h, 09194h, 09595h
DW  08596h, 08197h, 08998h, 08D99h, 08D9Ah, 0899Bh, 08D9Ch, 0899Dh
DW  0899Eh, 08D9Fh, 0B5A0h, 0B1A1h, 0B1A2h, 0B5A3h, 0B1A4h, 0B5A5h
DW  0A5A6h, 0A1A7h, 0A9A8h, 0ADA9h, 0ADAAh, 0A9ABh, 0ADACh, 0A9ADh
DW  0A9AEh, 0ADAFh, 0B1B0h, 0B5B1h, 0B5B2h, 0B1B3h, 0B5B4h, 0B1B5h
DW  0A1B6h, 0A5B7h, 0ADB8h, 0A9B9h, 0A9BAh, 0ADBBh, 0A9BCh, 0ADBDh
DW  0ADBEh, 0A9BFh, 095C0h, 091C1h, 091C2h, 095C3h, 091C4h, 095C5h
DW  085C6h, 081C7h, 089C8h, 08DC9h, 08DCAh, 089CBh, 08DCCh, 089CDh
DW  089CEh, 08DCFh, 091D0h, 095D1h, 095D2h, 091D3h, 095D4h, 091D5h
DW  081D6h, 085D7h, 08DD8h, 089D9h, 089DAh, 08DDBh, 089DCh, 08DDDh
DW  08DDEh, 089DFh, 0B1E0h, 0B5E1h, 0B5E2h, 0B1E3h, 0B5E4h, 0B1E5h
DW  0A1E6h, 0A5E7h, 0ADE8h, 0A9E9h, 0A9EAh, 0ADEBh, 0A9ECh, 0ADEDh
DW  0ADEEh, 0A9EFh, 0B5F0h, 0B1F1h, 0B1F2h, 0B5F3h, 0B1F4h, 0B5F5h
DW  0A5F6h, 0A1F7h, 0A9F8h, 0ADF9h, 0ADFAh, 0A9FBh, 0ADFCh, 0A9FDh
DW  0A9FEh, 0ADFFh, 05500h, 01101h, 01102h, 01503h, 01104h, 01505h
DW  00506h, 00107h, 00908h, 00D09h, 00D0Ah, 0090Bh, 00D0Ch, 0090Dh
DW  0090Eh, 00D0Fh, 01110h, 01511h, 01512h, 01113h, 01514h, 01115h
DW  00116h, 00517h, 00D18h, 00919h, 0091Ah, 00D1Bh, 0091Ch, 00D1Dh
DW  00D1Eh, 0091Fh, 03120h, 03521h, 03522h, 03123h, 03524h, 03125h
DW  02126h, 02527h, 02D28h, 02929h, 0292Ah, 02D2Bh, 0292Ch, 02D2Dh
DW  02D2Eh, 0292Fh, 03530h, 03131h, 03132h, 03533h, 03134h, 03535h
DW  02536h, 02137h, 02938h, 02D39h, 02D3Ah, 0293Bh, 02D3Ch, 0293Dh
DW  0293Eh, 02D3Fh, 01140h, 01541h, 01542h, 01143h, 01544h, 01145h
DW  00146h, 00547h, 00D48h, 00949h, 0094Ah, 00D4Bh, 0094Ch, 00D4Dh
DW  00D4Eh, 0094Fh, 01550h, 01151h, 01152h, 01553h, 01154h, 01555h
DW  00556h, 00157h, 00958h, 00D59h, 00D5Ah, 0095Bh, 00D5Ch, 0095Dh
DW  0095Eh, 00D5Fh, 03560h, 03161h, 03162h, 03563h, 03164h, 03565h
DW  0BEFAh, 0BAFBh, 0BEFCh, 0BAFDh, 0BAFEh, 0BEFFh, 04600h, 00201h
DW  00202h, 00603h, 00204h, 00605h, 00606h, 00207h, 00A08h, 00E09h
DW  01E0Ah, 01A0Bh, 01E0Ch, 01A0Dh, 01A0Eh, 01E0Fh, 00210h, 00611h
DW  00612h, 00213h, 00614h, 00215h, 00216h, 00617h, 00E18h, 00A19h
DW  01A1Ah, 01E1Bh, 01A1Ch, 01E1Dh, 01E1Eh, 01A1Fh, 02220h, 02621h
DW  02622h, 02223h, 02624h, 02225h, 02226h, 02627h, 02E28h, 02A29h
DW  03A2Ah, 03E2Bh, 03A2Ch, 03E2Dh, 03E2Eh, 03A2Fh, 02630h, 02231h
DW  02232h, 02633h, 02234h, 02635h, 02636h, 02237h, 02A38h, 02E39h
DW  03E3Ah, 03A3Bh, 03E3Ch, 03A3Dh, 03A3Eh, 03E3Fh, 00240h, 00641h
DW  00642h, 00243h, 00644h, 00245h, 00246h, 00647h, 00E48h, 00A49h
DW  01A4Ah, 01E4Bh, 01A4Ch, 01E4Dh, 01E4Eh, 01A4Fh, 00650h, 00251h
DW  00252h, 00653h, 00254h, 00655h, 00656h, 00257h, 00A58h, 00E59h
DW  01E5Ah, 01A5Bh, 01E5Ch, 01A5Dh, 01A5Eh, 01E5Fh, 02660h, 02261h
DW  02262h, 02663h, 02264h, 02665h, 02666h, 02267h, 02A68h, 02E69h
DW  03E6Ah, 03A6Bh, 03E6Ch, 03A6Dh, 03A6Eh, 03E6Fh, 02270h, 02671h
DW  02672h, 02273h, 02674h, 02275h, 02276h, 02677h, 02E78h, 02A79h
DW  03A7Ah, 03E7Bh, 03A7Ch, 03E7Dh, 03E7Eh, 03A7Fh, 08280h, 08681h
DW  08682h, 08283h, 08684h, 08285h, 08286h, 08687h, 08E88h, 08A89h
DW  09A8Ah, 09E8Bh, 09A8Ch, 09E8Dh, 09E8Eh, 09A8Fh, 08690h, 08291h
DW  08292h, 08693h, 02334h, 02735h, 02736h, 02337h, 02B38h, 02F39h
DW  03F3Ah, 03B3Bh, 03F3Ch, 03B3Dh, 03B3Eh, 03F3Fh, 00340h, 00741h
DW  00742h, 00343h, 00744h, 00345h, 00346h, 00747h, 00F48h, 00B49h
DW  01B4Ah, 01F4Bh, 01B4Ch, 01F4Dh, 01F4Eh, 01B4Fh, 00750h, 00351h
DW  00352h, 00753h, 00354h, 00755h, 00756h, 00357h, 00B58h, 00F59h
DW  01F5Ah, 01B5Bh, 01F5Ch, 01B5Dh, 01B5Eh, 01F5Fh, 02760h, 02361h
DW  02362h, 02763h, 02364h, 02765h, 02766h, 02367h, 02B68h, 02F69h
DW  03F6Ah, 03B6Bh, 03F6Ch, 03B6Dh, 03B6Eh, 03F6Fh, 02370h, 02771h
DW  02772h, 02373h, 02774h, 02375h, 02376h, 02777h, 02F78h, 02B79h
DW  03B7Ah, 03F7Bh, 03B7Ch, 03F7Dh, 03F7Eh, 03B7Fh, 08380h, 08781h
DW  08782h, 08383h, 08784h, 08385h, 08386h, 08787h, 08F88h, 08B89h
DW  09B8Ah, 09F8Bh, 09B8Ch, 09F8Dh, 09F8Eh, 09B8Fh, 08790h, 08391h
DW  08392h, 08793h, 08394h, 08795h, 08796h, 08397h, 08B98h, 08F99h
DW  09F9Ah, 09B9Bh, 09F9Ch, 09B9Dh, 09B9Eh, 09F9Fh, 0A7A0h, 0A3A1h
DW  0A3A2h, 0A7A3h, 0A3A4h, 0A7A5h, 0A7A6h, 0A3A7h, 0ABA8h, 0AFA9h
DW  0BFAAh, 0BBABh, 0BFACh, 0BBADh, 0BBAEh, 0BFAFh, 0A3B0h, 0A7B1h
DW  0A7B2h, 0A3B3h, 0A7B4h, 0A3B5h, 0A3B6h, 0A7B7h, 0AFB8h, 0ABB9h
DW  0BBBAh, 0BFBBh, 0BBBCh, 0BFBDh, 0BFBEh, 0BBBFh, 087C0h, 083C1h
DW  083C2h, 087C3h, 083C4h, 087C5h, 087C6h, 083C7h, 08BC8h, 08FC9h
DW  09FCAh, 09BCBh, 09FCCh, 09BCDh, 09BCEh, 09FCFh, 083D0h, 087D1h
DW  087D2h, 083D3h, 087D4h, 083D5h, 083D6h, 087D7h, 08FD8h, 08BD9h
DW  09BDAh, 09FDBh, 09BDCh, 09FDDh, 09FDEh, 09BDFh, 0A3E0h, 0A7E1h
DW  0A7E2h, 0A3E3h, 0A7E4h, 0A3E5h, 0A3E6h, 0A7E7h, 0AFE8h, 0ABE9h
DW  0BBEAh, 0BFEBh, 0BBECh, 0BFEDh, 0BFEEh, 0BBEFh, 0A7F0h, 0A3F1h
DW  0A3F2h, 0A7F3h, 0A3F4h, 0A7F5h, 0A7F6h, 0A3F7h, 0ABF8h, 0AFF9h
DW  0BFFAh, 0BBFBh, 0BFFCh, 0BBFDh, 0BBFEh, 0BFFFh, 04700h, 00301h
DW  00302h, 00703h, 00304h, 00705h, 00706h, 00307h, 00B08h, 00F09h
DW  01F0Ah, 01B0Bh, 01F0Ch, 01B0Dh, 01B0Eh, 01F0Fh, 00310h, 00711h
DW  00712h, 00313h, 00714h, 00315h, 00316h, 00717h, 00F18h, 00B19h
DW  01B1Ah, 01F1Bh, 01B1Ch, 01F1Dh, 01F1Eh, 01B1Fh, 02320h, 02721h
DW  02722h, 02323h, 02724h, 02325h, 02326h, 02727h, 02F28h, 02B29h
DW  03B2Ah, 03F2Bh, 03B2Ch, 03F2Dh, 03F2Eh, 03B2Fh, 02730h, 02331h
DW  02332h, 02733h, 02334h, 02735h, 02736h, 02337h, 02B38h, 02F39h
DW  03F3Ah, 03B3Bh, 03F3Ch, 03B3Dh, 03B3Eh, 03F3Fh, 00340h, 00741h
DW  00742h, 00343h, 00744h, 00345h, 00346h, 00747h, 00F48h, 00B49h
DW  01B4Ah, 01F4Bh, 01B4Ch, 01F4Dh, 01F4Eh, 01B4Fh, 00750h, 00351h
DW  00352h, 00753h, 00354h, 00755h, 00756h, 00357h, 00B58h, 00F59h
DW  01F5Ah, 01B5Bh, 01F5Ch, 01B5Dh, 01B5Eh, 01F5Fh, 02760h, 02361h
DW  02362h, 02763h, 02364h, 02765h, 02766h, 02367h, 02B68h, 02F69h
DW  03F6Ah, 03B6Bh, 03F6Ch, 03B6Dh, 03B6Eh, 03F6Fh, 02370h, 02771h
DW  02772h, 02373h, 02774h, 02375h, 02376h, 02777h, 02F78h, 02B79h
DW  03B7Ah, 03F7Bh, 03B7Ch, 03F7Dh, 03F7Eh, 03B7Fh, 08380h, 08781h
DW  08782h, 08383h, 08784h, 08385h, 08386h, 08787h, 08F88h, 08B89h
DW  09B8Ah, 09F8Bh, 09B8Ch, 09F8Dh, 09F8Eh, 09B8Fh, 08790h, 08391h
DW  08392h, 08793h, 08394h, 08795h, 08796h, 08397h, 08B98h, 08F99h

NEG_Table:
DB  0,66,255,187,254,187,253,187,252,187,251,187,250,187,249,187,248,187,247,179
DB  246,179,245,179,244,179,243,179,242,179,241,179,240,163,239,187,238,187,237,187
DB  236,187,235,187,234,187,233,187,232,187,231,179,230,179,229,179,228,179,227,179
DB  226,179,225,179,224,163,223,155,222,155,221,155,220,155,219,155,218,155,217,155
DB  216,155,215,147,214,147,213,147,212,147,211,147,210,147,209,147,208,131,207,155
DB  206,155,205,155,204,155,203,155,202,155,201,155,200,155,199,147,198,147,197,147
DB  196,147,195,147,194,147,193,147,192,131,191,187,190,187,189,187,188,187,187,187
DB  186,187,185,187,184,187,183,179,182,179,181,179,180,179,179,179,178,179,177,179
DB  176,163,175,187,174,187,173,187,172,187,171,187,170,187,169,187,168,187,167,179
DB  166,179,165,179,164,179,163,179,162,179,161,179,160,163,159,155,158,155,157,155
DB  156,155,155,155,154,155,153,155,152,155,151,147,150,147,149,147,148,147,147,147
DB  146,147,145,147,144,131,143,155,142,155,141,155,140,155,139,155,138,155,137,155
DB  136,155,135,147,134,147,133,147,132,147,131,147,130,147,129,147,128,135,127,59
DB  126,59,125,59,124,59,123,59,122,59,121,59,120,59,119,51,118,51,117,51
DB  116,51,115,51,114,51,113,51,112,35,111,59,110,59,109,59,108,59,107,59
DB  106,59,105,59,104,59,103,51,102,51,101,51,100,51,99,51,98,51,97,51
DB  96,35,95,27,94,27,93,27,92,27,91,27,90,27,89,27,88,27,87,19
DB  86,19,85,19,84,19,83,19,82,19,81,19,80,3,79,27,78,27,77,27
DB  76,27,75,27,74,27,73,27,72,27,71,19,70,19,69,19,68,19,67,19
DB  66,19,65,19,64,3,63,59,62,59,61,59,60,59,59,59,58,59,57,59
DB  56,59,55,51,54,51,53,51,52,51,51,51,50,51,49,51,48,35,47,59
DB  46,59,45,59,44,59,43,59,42,59,41,59,40,59,39,51,38,51,37,51
DB  36,51,35,51,34,51,33,51,32,35,31,27,30,27,29,27,28,27,27,27
DB  26,27,25,27,24,27,23,19,22,19,21,19,20,19,19,19,18,19,17,19
DB  16,3,15,27,14,27,13,27,12,27,11,27,10,27,9,27,8,27,7,19
DB  6,19,5,19,4,19,3,19,2,19,1,19

;- the end ------ (blimey) --------------------------------------------------;
