External Hardware Interface Access for 6502 Emulators for RISC OS ================================== Version 0.11 - J.G.Harston - 01-May-2004 Second Draft Proposal Introduction ~~~~~~~~~~~~ There are many applications that emulate the 6502 CPU on RISC OS systems, mostly BBC emulators. Each application provides access to emulated hardware to varing degrees. The minimum a BBC emulator usually provides is access to some of the on-board hardware - the system VIA for timing and keyboard, etc. This document specifies a method of allowing additional hardware to be accessed from a 6502 emulator. The 6502 CPU accesses I/O via the memory map, using LDx and STx instructions. This specification defines a method by which an emulator can ask other software to service the access to memory locations that map on unsupported hardware. 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 access to &FC4x to provide an emulated hard drive or access to &FDxx to emulate a page-wide RAM card. 6502 Service Calls ~~~~~~~~~~~~~~~~~~ All calls should be made with R0 holding the ARM address of location zero of the emulated 6502'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 &80AD0 Service_6502RD ---------------------- In: R0=ARM address of 6502 memory address 0 R1=&80AD0 R2=6502 address R3=value to return if no hardware present R4=address of module/program issuing call Out: R1=0 if claimed R3=value to return Issued by a read from a memory location unsupported by the calling program. For example, LDA &FC00 would call with R2=&FC00. &80AD1 Service_6502WR ---------------------- In: R0=ARM address of 6502 memory address 0 R1=&80AD1 R2=6502 address R3=output value R4=address of module/program issuing call Out: R1=0 if claimed Issued by a write to a memory location unsupported by the calling program. For example, LDA #&66:STA &FC00 would result in a call with R2=&FC00 and R3=&66. &80AD2 Service_6502Reset ------------------------- In: R0=ARM address of 6502 memory address 0 R1=&80AD2 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 ~RST line. Coding Examples ~~~~~~~~~~~~~~~ Sample assembler code --------------------- 6502Em can be patched with the following code to pass access to &FCxx and &FDxx to the Service_6502 calls. ; Read from &FC00-&FDFF ; --------------------- ; On entry, ; R0 =byte to read/write ; R1 =corruptable ; R2 =corruptable ; R3 =ARM address of 6502 locatio &0000 - must be preserved ; R4 =must be preserved ; R12=address to access .IORead STR R3,[PC,#R3Store-P%-8] ; Save R3 STR R4,[PC,#R4Store-P%-8] ; Save R4 MOV R4,R0 ; Hold default byte for a moment MOV R0,R3 ; Address of mem[0] LDR R1,[PC,#Srv65RD-P%-8] ; Service call number MOV R2,R12 ; Address to read from MOV R3,R4 ; Default byte to read MOV R4,#&8000 ; R4=address of program SWI "OS_ServiceCall" ; Access external hardware, R0-R4 corrupted MOV R0,R3 ; Byte read LDR R4,[PC,#R4Store-P%-8] ; Restore R4 LDR R3,[PC,#R3Store-P%-8] ; Restore R3 MOV PC,R14 ; Return to code .Srv65RD EQUD Service_6502RD ; Service call number : ; Write to &FC00-&FDFF ; -------------------- ; On entry, ; R0 =byte to write ; R1 =address to access ; R2 =corruptable ; R3 =ARM address of 6502 locatio &0000 - must be preserved ; R4 =must be preserved ; .IOWrite STR R3,[PC,#R3Store-P%-8] ; Save R3 STR R4,[PC,#R4Store-P%-8] ; Save R4 MOV R4,R0 ; Hold output byte for a moment MOV R0,R3 ; Address of mem[0] MOV R2,R1 ; Address to read from LDR R1,[PC,#Srv65WR-P%-8] ; Service call number MOV R3,R4 ; Byte to write MOV R4,#&8000 ; R4=address of program SWI "OS_ServiceCall" ; Access external hardware, R0-R4 corrupted LDR R4,[PC,#R4Store-P%-8] ; Restore R4 LDR R3,[PC,#R3Store-P%-8] ; Restore R3 MOV PC,R14 ; Return to code .Srv65WR EQUD Service_6502WR ; Service call number : .R3Store:EQUD &00000000 .R4Store:EQUD &00000000 : Sample C code ------------- /* Access i/o device */ io_wr(addr, data) int16 addr; int8 data; { switch (addr) { case 0xFE00, 0xFE01: io_crtc(); return; case 0xFE08, 0xFE09: io_acia(); return; case 0xFE30: io_romsel(); return; otherwise kernel.r0=&mem[0]; kernel.r1=Service_6502WR; kernel.r2=addr; kernel.r3=0xFE; kernel.r4=ModuleBase; kernel_swi(OS_ServiceCall); return; } } Distribution and use ~~~~~~~~~~~~~~~~~~~~ I have registered the Service_6502* 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. I would like to see a similar interface specification developed for other platforms, such as Windows DLLs and something for Unix platforms. Version History ~~~~~~~~~~~~~~~ 0.10 14-Feb-2004 First initial draft based on concepts in emulated Z80 I/O specification. 0.11 01-May-2004 Allocated service call numbers.