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.