ideal
p386

extrn input:near,output:near,int09handler:near,readjoystick:near, \
      Screen$SetModeX:far,Screen$SetColours:far, \
      Snapshot$ReadSnap:far,Snapshot$WriteSnap:far, \
      Snapshot$ReadLastSnap:far,Snapshot$:far, \
      specrom:far,current:far,ctr1:far,ctr2:far


segment fastemu

group emulator fastemu

extrn Screen$Update:near, Screen$WaitInt:near, \
      jtimer:byte,ports@clickcycle:word,ports@clicktime:word

public fastemu@z80regs,fastemu$setup,fastemu$run, \
       fastemu@ds,fastemu@es,fastemu@fs,fastemu@gs,need_int, \
       fastemu$,intctr,cycles,cyc_2,fastemu@failure

assume cs:emulator,ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing

fastemu@ds dw 0
fastemu@es dw 0
fastemu@fs dw 0
fastemu@gs dw 0

;
; definitions for register structure
;
label fastemu@z80regs far
label reg_af word
reg_f db 0
reg_a db 0
label reg_bc word
reg_c db 0
reg_b db 0
label reg_de word
reg_e db 0
reg_d db 0
label reg_hl word
reg_l db 0
reg_h db 0
reg_aaf dw 0
reg_abc dw 0
reg_ade dw 0
reg_ahl dw 0
reg_sp dw 0
reg_pc dw 0
label reg_ix word
reg_ixl db 0
reg_ixh db 0
label reg_iy word
reg_iyl db 0
reg_iyh db 0
reg_i db 0
reg_r db 0
label reg_iff word
reg_iff1 db 0
reg_iff2 db 0
reg_im db 0

fastemu@failure dw 0

;
; equates for practical use
;
r_a equ al
r_f equ ah
;r_af equ ax ; no! a is high-byte, f is low
r_c equ cl
r_b equ ch
r_bc equ cx
r_e equ dl
r_d equ dh
r_de equ dx
r_l equ bl
r_h equ bh
r_hl equ bx
r_iyl equ [reg_iyl]
r_iyh equ [reg_iyh]
r_iy equ [reg_iy]
r_ixl equ [reg_ixl]
r_ixh equ [reg_ixh]
r_ix equ [reg_ix]
r_aaf equ [reg_aaf]
r_abc equ [reg_abc]
r_ade equ [reg_ade]
r_ahl equ [reg_ahl]
r_sp equ bp
r_i equ [reg_i]
r_r equ [reg_r]

need_int db 0
  even
cycles dw 0
cyc_2 db 0
cyc_wrap dw 0

ifdef exact

macro tb reg,time
  if time eq 0
  else
    add [cycles],time
  endif
  test [byte ptr es:reg],255
  ifidni <reg>,<bp>
    jnz wb_bp
  endif
  ifidni <reg>,<bx>
    jnz wb_bx
  endif
  ifidni <reg>,<di>
    jnz wb_di
  endif
endm

macro tw reg,time
  if time eq 0
  else
    add [cycles],time
  endif
  test [word ptr es:reg],65535
  ifidni <reg>,<bp>
    jnz ww_bp
  endif
  ifidni <reg>,<bx>
    jnz ww_bx
  endif
  ifidni <reg>,<di>
    jnz ww_di
  endif
endm

macro dojump time
  if time eq 0
  else
    add [cycles],time
  endif
  jmp _dojump
endm

even

_dojump:
  cmp [cycles],35000
  jb _dj2
  sub [cycles],35000
  add [ports@clicktime],11875
  xor di,di
  xchg di,[ports@clickcycle]
  add di,35000
  jc _dj1
  mov [ports@clickcycle],di
_dj1:
  add [cyc_wrap],68
  xor [cyc_2],1
  jz _dj2

  push ax
  push cx
  push dx
  mov di,offset speed_buf
  xor ax,ax
  xor cx,cx
  xor dx,dx
_dj1a:
  mov dl,[cs:di]
  test dx,dx
  jz _dj1b
  add ax,dx
  inc cx
  inc di
  cmp di,offset speed_ptr
  jb _dj1a
_dj1b:
  test cx,cx
  jnz _dj1c
  inc cx
_dj1c:
  xor dx,dx
  div cx
  mov di,ax

  pop dx
  pop cx
  pop ax
  call Screen$WaitInt

  jmp interrupt
_dj2:
  test si,1
  jz _dj3
  jmp [word ptr gs:si-1]
_dj3:
  jmp [word ptr fs:si]

else

macro tb reg,time
  test [byte ptr es:reg],255
  ifidni <reg>,<bp>
    jnz wb_bp
  endif
  ifidni <reg>,<bx>
    jnz wb_bx
  endif
  ifidni <reg>,<di>
    jnz wb_di
  endif
endm

macro tw reg,time
  test [word ptr es:reg],65535
  ifidni <reg>,<bp>
    jnz ww_bp
  endif
  ifidni <reg>,<bx>
    jnz ww_bx
  endif
  ifidni <reg>,<di>
    jnz ww_di
  endif
endm

macro dojump time
  jmp _dojump
  even
endm

_dojump:
  test [need_int],255
  jnz interrupt
  test si,1
  jz _dj1
  jmp [word ptr gs:si-1]
_dj1:
  jmp [word ptr fs:si]

endif

include "decode.asm"

proc evenaddress near

ifdef exact

macro nextop time,oe
  add [cycles],time
  if oe eq 0
    jmp [word ptr fs:si]
  else
    jmp [word ptr gs:si-1]
  endif
  even
endm

else

macro nextop time,oe
  if oe eq 0
    jmp [word ptr fs:si]
  else
    jmp [word ptr gs:si-1]
  endif
  even
endm

endif


even_optable:

include "tables.asm"
include "various.asm"
include "ld.asm"
include "addsub.asm"
include "andorxor.asm"
include "bits.asm"
include "jump.asm"
include "incdec.asm"
include "pushpop.asm"

purge nextop

endp evenaddress

proc oddaddress near

ifdef exact

macro nextop time,oe
  add [cycles],time
  if oe eq 0
    jmp [word ptr gs:si-1]
  else
    jmp [word ptr fs:si]
  endif
  even
endm

else

macro nextop time,oe
  if oe eq 0
    jmp [word ptr gs:si-1]
  else
    jmp [word ptr fs:si]
  endif
  even
endm

endif

odd_optable:

include "tables.asm"
include "various.asm"
include "ld.asm"
include "addsub.asm"
include "andorxor.asm"
include "bits.asm"
include "jump.asm"
include "incdec.asm"
include "pushpop.asm"

endp oddaddress

; reset the z80

proc fastemu$setup far
  push ds
  push es
  pusha

  mov [fastemu@ds],ax
  mov [fastemu@es],bx
  mov [fastemu@fs],cx
  mov [fastemu@gs],dx

  call cleardecode

  popa
  pop es
  pop ds
  ret

endp fastemu$setup



proc cleardecode near
  xor di,di
  xor si,si
  mov es,[fastemu@ds]
  mov dx,seg specrom
  mov ds,dx
  cld
  mov cx,8192
  rep movsw

  mov es,[fastemu@es]
  mov ax,8080h
  mov cx,8192
  xor di,di
  rep stosw
  mov cx,24576
  xor ax,ax
  rep stosw

  mov es,[fastemu@fs]
  mov ax,offset decodeandexec
  mov cx,32768
  xor di,di
  rep stosw

  mov es,[fastemu@gs]
  mov ax,offset decodeandexec
  mov cx,32768
  xor di,di
  rep stosw

  ret
endp cleardecode

proc grabints near

  call Screen$SetModeX
  cli

  mov ax,2508h
  mov dx,cs
  mov ds,dx
  mov dx,offset int08handler
  int 21h
  mov ax,2509h
  mov dx,cs
  mov ds,dx
  mov dx,offset int09handler
  int 21h

  mov al,34h
  out 43h,al
  mov al,2fh
  out 40h,al
  mov al,5dh
  out 40h,al

  sti
  ret

endp grabints

proc freeints

  mov al,34h
  out 43h,al
  xor al,al
  out 40h,al
  out 40h,al

  mov ax,2508h
  mov dx,[word ptr offset oldint08]
  mov ds,[word ptr offset oldint08+2]
  int 21h
  mov ax,2509h
  mov dx,[word ptr offset oldint09]
  mov ds,[word ptr offset oldint09+2]
  int 21h

  ret

endp freeints

; start the z80

proc fastemu$run far
  push bp
  push ds
  push es
  push fs
  push gs
  pusha
  pushf
  mov [sp386],sp

startz80:
  mov ah,21h            ;sync Qcache
  int 13h
  mov ah,0a1h           ;sync PC-Kwik / PC-Cache
  mov si,4358h
  int 13h

  mov [int_ok],0
  mov [need_int],0

  call grabints

restart:
  cli

  mov ds,[fastemu@ds]
  mov es,[fastemu@es]
  mov fs,[fastemu@fs]
  mov gs,[fastemu@gs]

  mov ax,[reg_af]
  xchg al,ah
  mov bx,[reg_hl]
  mov cx,[reg_bc]
  mov dx,[reg_de]
  mov bp,[reg_sp]
  mov si,[reg_pc]

ifdef exact
  mov [cycles],0
  mov [cyc_2],0
  call Screen$Update
endif

  mov [int_ok],1
  sti

  test si,1
  jz @@_start_even
  jmp [word ptr gs:si-1]
@@_start_even:
  jmp [word ptr fs:si]

illop:
  cli

  mov [int_ok],0

  mov sp,[sp386]

  mov [reg_pc],si
  mov [reg_sp],bp
  mov [reg_de],dx
  mov [reg_bc],cx
  mov [reg_hl],bx
  xchg al,ah
  mov [reg_af],ax

  cmp [need_int],2
  je writesnap
  cmp [need_int],3
  je readsnap
  cmp [need_int],4
  je readlastsnap

  call freeints
  mov ax,3      ;set 80*25 text mode
  int 10h

  mov al,[need_int]
  xor ah,ah
  mov [fastemu@failure],ax

  popf
  popa
  pop gs
  pop fs
  pop es
  pop ds
  pop bp

  ret

oldint08 dd 0
oldint09 dd 0

intctr db 2

int_ok db 0

sp386 dw 0

interrupt:
  mov [int_ok],0
  cmp [need_int],1
  ja illop
  mov [need_int],0

ifdef exact
  ;don't do video emulation here
else
  test [intctr],255
  jnz @@_int_1
  mov [intctr],3
  call Screen$Update
@@_int_1:
  dec [intctr]

@@_int_2:
endif

  test [reg_iff1],255
  jz @@_exec
  mov [reg_iff1],0
  dec bp
  dec bp
  mov [ds:bp],si
  cmp [reg_im],1
  ja @@_im2
  mov si,38h
  inc [int_ok]
  jmp [word ptr fs:si]
@@_im2:
  mov si,ax
  mov ah,r_i
  mov al,255
  xchg ax,si
  mov si,[ds:si]
@@_exec:
  inc [int_ok]
  test si,1
  jz @@_im2_1
  jmp [word ptr gs:si-1]
@@_im2_1:
  jmp [word ptr fs:si]


readsnap:
  call freeints
  mov ax,3      ;set 80*25 text mode
  int 10h
  call Snapshot$ReadSnap
  jmp startz80

writesnap:
  call freeints
  mov ax,3      ;set 80*25 text mode
  int 10h
  call Snapshot$WriteSnap
  jmp startz80

readlastsnap:
  call freeints
  mov ax,3      ;set 80*25 text mode
  int 10h
  call Snapshot$ReadLastSnap
  jmp startz80

endp fastemu$run

restorescreen db 0
popuptimer dd 0

emuspeed db 0
timer50 db 0

ifdef exact

speed_buf db 25 dup (0)
speed_ptr dw offset speed_buf

endif


assume cs:emulator,ds:nothing,es:nothing,fs:nothing,gs:nothing,ss:nothing

proc int08handler near
ifdef exact
  push ax
  push bx
  mov ax,[cycles]
  shr ax,9
  mov bx,ax
  neg bx
  xchg bx,[cyc_wrap]
  add ax,bx
  mov bx,[speed_ptr]
  mov [cs:bx],al
  inc bx
  cmp bx,offset speed_ptr
  jb @@_1
  mov bx,offset speed_buf
@@_1:
  mov [speed_ptr],bx
  pop bx
  pop ax
endif
  test [int_ok],255
  jz @@_exit
  test [need_int],255
  jnz @@_exit
  mov [need_int],1
  call readjoystick
@@_exit:
  add [intfreqctr],5d2fh
  jnc @@_quit
  jmp [dword ptr oldint08]
@@_quit:
  push ax
  mov al,20h
  out 20h,al
  pop ax
  iret

intfreqctr dw 0

endp int08handler

proc fastemu$init far
  push ds
  push es
  push bx
  push ax
  pushf

  mov ax,3508h
  int 21h
  mov [word ptr offset oldint08],bx
  mov [word ptr offset oldint08+2],es
  mov ax,3509h
  int 21h
  mov [word ptr offset oldint09],bx
  mov [word ptr offset oldint09+2],es

  popf
  pop ax
  pop bx
  pop es
  pop ds
  ret

endp fastemu$init


fastemu$:
  dd fastemu$init
  dd Snapshot$
  dd -1

ends fastemu

end
