;
; Interface to use the Z80 simulator without cpmbios
;
; (c) 1992 Jrgen Weber
;
; Assemble:   tasm /mx Z80IFACE
; (without option /mx there would be the upper case
;  a label _EMULATE which is not equal to emulate() )

EXTRN bios88ret:FAR,ctrl_break_req:FAR,interrupt_request:FAR,nmi_irq:FAR
;EXTRN set_read_dist:FAR,set_write_dist:FAR

PUBLIC bios88,prg_exit,port_in,port_out,get_rand
PUBLIC _emulate,_genint,_genhalt,get_rand,port_out,port_in
PUBLIC _setcfg

DGROUP    GROUP _DATA,_BSS
_DATA SEGMENT WORD PUBLIC 'DATA'
_DATA ENDS
_BSS SEGMENT WORD PUBLIC 'BSS'
_BSS ENDS

_TEXT segment para public 'Code'
assume cs:_TEXT,es:nothing,ds:nothing


calln_hnd dw 0,0
halt_hnd dw 0,0

; _emulate(unsigned z80seg,
;          void far (*calln_handler)(int far *),
;          void far (*halt_handler)(void))

spsave dw (?)

_emulate proc far
ARG z80seg:WORD,calln_handler:DWORD,halt_handler:DWORD
        mov cs:spsave,sp
        push bp
        mov bp,sp
        lds ax,calln_handler
        mov cs:calln_hnd+0,ax
        mov ax,ds
        mov cs:calln_hnd+2,ax
        lds ax,halt_handler
        mov cs:halt_hnd+0,ax
        mov ax,ds
        mov cs:halt_hnd+2,ax
        mov ax,[z80seg]
        pop bp
        mov es,ax
        mov ds,ax
        mov si,0      ; start at 0
        jmp bios88ret
emexit:
        mov ax,DGROUP
        mov ds,ax
        mov sp,cs:spsave
        ret
_emulate endp

; call this function to generate an interrupt of the z80
; for example you could hook the timer interrupt to
; call this function
; void genint(void)
_genint proc far
ARG databus:WORD
       push bp
       mov bp,sp
       mov ax,[databus]  ; the value in al simulates the value put
       pop bp            ; on the data bus at z80 irq
       call interrupt_request
       ret
_genint endp


; call this function to force the z80 to execute HALT (-> prg_exit)
; for example you could hook the ctrl_break interrupt to
; call this function
; void genhalt(void)
_genhalt proc far
       call ctrl_break_req
       ret
_genhalt endp

; bios88 is called if the bytes ED ED xx are met
; use ED ED xx to execute an operating system call xx (al=xx)
; if xx==0ffh goto emulation exit
bios88 proc far
local rsi,rdx,rcx,rbx,rax:WORD=AUTO_SIZE
        cmp al,0ffh
        jz emexit     ; end emulation
        push bp
        mov bp,sp
        sub sp,AUTO_SIZE
        mov di,sp
        push ds
        push es
        mov rax,ax
        mov rbx,bx
        mov rcx,cx
        mov rdx,dx
        mov rsi,si
        push ss  ; int far *regs
        push di  ; stack grows downwards
        mov ax,DGROUP
        mov ds,ax
; void far calln_handler(int far *regs);
        call dword ptr cs:[calln_hnd]
        add sp,4
        mov ax,rax
        mov bx,rbx
        mov cx,rcx
        mov dx,rdx
        mov si,rsi
        pop es
        pop ds
        mov sp,bp
        pop bp
        jmp bios88ret
bios88 endp

; prg_exit is called, if a HALT operation is executed
prg_exit proc far
      push ds
      push es
      push si
      push ax
      push bx
      push cx
      push dx ; save all registers
        mov ax,DGROUP
        mov ds,ax
; void far halt_handler(void);
       call dword ptr cs:[halt_hnd]
     pop dx
     pop cx
     pop bx
     pop ax
     pop si
     pop es
     pop ds
     ret
prg_exit endp

port_in proc far
        mov al,0ffh
        ret
port_in endp

port_out proc far
        ret
port_out endp

get_rand proc far
        mov al,0
        ret
get_rand endp


; _setcfg(unsigned rdseg,rdoffs,wrseg,wroffs);
; all read accesses are done in segment rdseg by adding rdoffs
; all write accesses are done in segment wrseg by adding wroffs

_setcfg proc far
ARG rdseg:WORD,rdoffs:WORD,wrseg:WORD,wroffs:WORD
        push bp
        mov bp,sp
        mov bx,rdseg
        mov ax,rdoffs
;        call set_read_dist
	mov bx,wrseg
	mov ax,wroffs
;        call set_write_dist

        pop bp
        ret
_setcfg endp

_TEXT ends

end
 
