                   External Hardware Interface Access
                     for Z80 Emulators for RISC OS
                   ==================================
                Version 1.01 - J.G.Harston - 19-Sep-2004

Introduction
~~~~~~~~~~~~
There are many applications that emulate the Z80 CPU on RISC OS systems,
mostly Sinclair Spectrum emulators. Each application provides access to
emulated hardware to varing degrees. The minimum a Spectrum emulator
ususally provides is access to the system ULA to read the keyboard and set
the border colour.  This document specifies a method of allowing emulators
for additonal hardware to be accessed from a Z80 emulator.

The Z80 CPU has an I/O address range seperate from the memory address map.
It is accessed with IN and OUT instructions.  This specification defines a
method by which an emulator can ask other software to service the IN or OUT
access.

In essence, if an emulator does not support the I/O access itself it should
issue a service call to allow other software to respond. The external
software could, for instance, respond to IN 31 to provide an emulated
Kempston joystick or OUT 253 to emulate a printer port.


Z80 Service Calls
~~~~~~~~~~~~~~~~~
All calls should be made with R0 holding the ARM address of location zero of
the emulated Z80's memory and with R4 holding the ARM address of the calling
program's base address. If the calling program is a module, this will be the
module's start address


&80AC0  Service_Z80IN
---------------------
In:  R0=ARM address of Z80 memory address 0
     R1=&80AC0
     R2=port
     R3=value to use if no hardware present
     R4=address of module/program issuing call

Out: R1=0 if claimed
     R3=value to return

Issued by an IN A,(n) or IN r,(BC) instruction.


&80AC1  Service_Z80OUT
----------------------
In:  R0=ARM address of Z80 memory address 0
     R1=&80AC1
     R2=port
     R3=output value
     R4=address of module/program issuing call

Out: R1=0 if claimed

Issued by an OUT (n),A or OUT (BC),r instruction.


&80AC2  Service_Z80Reset
------------------------
In:  R0=ARM address of Z80 memory address 0
     R1=&80AC2
     R2 undefined
     R3 undefined
     R4=address of module/program issuing call

Out: R1 must be preserved

Issued at Reset, when all registers have been cleared, and before any data
has been loaded to memory.  This call allows default values to be set up and
is the emulated equivalent of the ~RESET line.


&80AC3  Service_Z80Halt
------------------------
In:  R0=ARM address of Z80 memory address 0
     R1=&80AC3
     R2 undefined
     R3 undefined
     R4=address of module/program issuing call

Out: R1 must be preserved

Issued when HALT opcode is executed. Can be seen as the emulated equivalent
of the the ~HALT line.


&80AC4  Service_Z80Page
------------------------
In:  R0=ARM address of Z80 memory address 0
     R1=&80AC4
     R2=Z80 program counter
     R3 undefined
     R4=address of module/program issuing call

Out: R1 must be preserved

Spectrum-specific call. Issued when the Z80's program counter is at one of
the locations that pages in and out shadow ROMs such as with the Interface
1 or the Disciple. The Interface 1 pages in the Shadow ROM at &0008 and
&1708, it pages the main ROM back in at &0700. Rather than check the program
counter value on every opcode fetch, most emulators do this check only when
the usual opcode at these location is fetched. That is, LD HL,(nnnn) at &0008,
INC HL at &1708 and RET at &0700. Obviously, won't be issued by emulators that
implement Interface 1 support themselves.


Coding Examples
~~~~~~~~~~~~~~~
Z80Tube uses the following code construction to pass all IN accesses to the
Service_Z80IN call. Z80Tube does not implement any hardware itself.

; &ED40 - IN   B,(BC)
STRB  Areg,[z80mem,#Acopy]    ; Save Areg to use R3
STRB  Freg,[z80mem,#Fcopy]    ; Save Freg to use R4
MOV   R0,z80mem               ; r0=mem[0]
LDR   R1,[pc,#13*4]           ; r1=service call number
ORR   R2,Creg,Breg,LSL #8     ; r2=port
MOV   R3,#&FF                 ; r3=&FF in case no response
LDR   R4,[z80mem,#ModuleBase] ; r4=my module
SWI   "OS_ServiceCall"        ; Do an IN call
MOVS  Breg,R3                 ; Store returned value, set flags
LDRB  Freg,[z80mem,#Fcopy]    ; Restore Freg to R4
AND   Freg,Freg,#&29          ; Set Z80's flags
ORREQ Freg,Freg,#Zflag
ORRMI Freg,Freg,#Sflag
SUB   R0,z80mem,#&200         ; Index into parity table
LDRB  R1,[R0,R3]              ; Get parity flag
LDRB  Areg,[z80mem,#Acopy]    ; Restore Areg to R3
ORR   Freg,Freg,R1            ; Set parity
MOV   pc,link
;
EQUD  Srv_Z80IN               ; Service call number

Speculator 1.04 uses the following code to pass OUTs to ports other than
254, the ULA, to the Service_Z80OUT call.  Speculator implements the ULA
hardware itself.

; &D3 - OUT (nn),A
LDRB    R0,[R11],#1           ; Get n, R3=Areg=value to output
CMP     R0,#&FE
BNE     OUT_A                 ; Jump to external if not &FE
LDRB    R0,[R12,#-100]
AND     R1,R3,#&17
EORS    R2,R0,R1
SWINE   &C0000                ; ULA IN/OUT call
MOV     PC,R14                ; Back to opcode fetch loop
:
.OUT_A                        ; r0=port low, r3=value
STRB   R4,[r12,#-252]         ; Save R4
MOV    R2,R14                 ; Save link
BL     FindModuleBase         ; R4=ModuleBase
MOV    R14,R2                 ; Get link back
ORR    R2,R0,R3,LSL #8        ; R2=port
LDR    R1,[PC,#3*4]           ; R1=Srv_Z80OUT
MOV    R0,R12                 ; R0=mem[0]
SWI    "OS_ServiceCall"
LDRB   R4,[r12,#-252]         ; Restore R4
MOV    pc,link                ; Back to opcode fetch loop
;
EQUD   Srv_Z80TubeOUT


Implementation
~~~~~~~~~~~~~~
Z80Tube and Speculator 1.04 implement Service_Z80 calling. ROMBox provides
an emulated ROMBox for a Spectrum, copying the specified ROM image to
Spectrum memory at &0000


Distribution and use
~~~~~~~~~~~~~~~~~~~~
While I have registered the Service_Z80* allocation block, anybody may use
this service call specification in their own emulators or in writing
emulated hardware themselves. I would encourage this as it will provide a
common interface for software and can provide a high degree of
interusability.


Notes
~~~~~
Some emulators hold different areas of the emulated memory in non-contiguous
area of ARM memory. For instance, a Spectrum 128K emulator could hold each
16k page of Spectrum memory in different parts of ARM memory and indirect
the Z80's memory access via emulated paging registers.  This can make it
difficult for emulated hardware that change the Spectrum's memory map, such
as the ROMBox to operate. In practice, emulators that implement this type of
memory paging will not need access to emulated memory paging.


Version History
~~~~~~~~~~~~~~~
1.01 19-Sep-2004 Added Service_Z80Page.
1.00 11-Jul-2003 First version typed up and uploaded to website
0.10 30-Jun-2000 Service calls allocated, updated code in Z80Tube and
                 Speculator to use the allocated numbers.
