No SNK BIOS image is read here. Everything below comes from RACE’s replacement BIOS, a small program its author wrote (and a snapshot of RAM taken after a real BIOS had started), which has to behave as the real one does for games to run. Where the real BIOS puts a routine is not known from it; the numbers that a game uses (the vector numbers, the call numbers, the RAM addresses) are what RACE has to match.
System calls
A program calls the BIOS with SWI 1. In RACE’s replacement the SWI 1 handler (at 0xFFF000, reached through the vector at 0xFFFF04) switches to register bank 3 (LDF 3), takes the call number from W of that bank (RW3), masks it to 5 bits, multiplies it by 4 and calls the address
found there in a table of long addresses at 0xFFFE00. The parameters are in the other registers of bank 3 (RACE’s comments name RA3, RB3, RC3, BC3, XDE3, XHL3); the call ends with RET and the handler’s RETI goes back.
| Number | Name | What it does |
|---|---|---|
0x00 | VECT_SHUTDOWN | Power off the unit. No parameters. |
0x01 | VECT_CLOCKGEARSET | Set the CPU speed: RB3 = 0 6.144 MHz, 1 3.072 MHz, 2 1.536 MHz, 3 0.768 MHz, 4 0.384 MHz; RC3 non-zero: regenerate the speed automatically. |
0x02 | VECT_RTCGET | Read the clock to the address in XHL3: year (00 to 90 = 2000 to 2090), month, day, hour, minute, second, and a byte with the years after a leap year (high nibble) and the weekday (low nibble); all BCD. |
0x04 | VECT_INTLVSET | Set an interrupt level: RB3 = level (0 to 5), RC3 = the number (0 RTC alarm, 1 Z80, 2–5 the 8-bit timers 0–3, 6–9 end of DMA 0–3); see the I/O registers 0x70–0x7A. |
0x05 | VECT_SYSFONTSET | Load the system font into the tile area. RACE reads one parameter from RA3. |
0x06 | VECT_FLASHWRITE | Write to the save flash: RA3 = chip, BC3 pages of 0x100 bytes from the address in XHL3 to the flash offset in XDE3. |
0x07 | VECT_FLASHALLERS | Erase the whole save flash of the chip in RA3. |
0x08 | VECT_FLASHERS | Erase one 64 KiB block: RA3 = chip, RB3 = the block. |
0x09 | VECT_ALARMSET | Set the RTC alarm; RACE reads the day, hour and minute from bytes 2, 1 and 0 of XBC3. |
0x0E | VECT_GEMODESET | Set the K1GE/K2GE mode; RACE always selects colour (a comment says the real BIOS’s implementation looks wrong there). |
RACE also answers the numbers 0x14, 0x17, 0x18 and 0x19 with fixed values (A = 1, WA = 0, WA = 0, B = 0) without naming them; they are not listed as calls here. The disassembly does not mark a SWI 1 with its call number: the number is loaded into RW3 by an earlier instruction.
The interrupt vector table
The TLCS-900/H takes an interrupt through a table of 32-bit addresses in the BIOS at 0xFFFF00. RACE fills these entries; every other offset points at 0xFFF800, a bare RETI:
| Address | Interrupt |
|---|---|
0xFFFF04 | SWI 1: the system call entry (RACE’s 0xFFF000) |
0xFFFF0C | SWI 3 |
0xFFFF10 | SWI 4 |
0xFFFF14 | SWI 5 |
0xFFFF18 | SWI 6 |
0xFFFF28 | RTC alarm |
0xFFFF2C | V-blank |
0xFFFF30 | Interrupt from the Z80 |
0xFFFF40, 44, 48, 4C | 8-bit timers 0, 1, 2, 3 |
0xFFFF60, 64 | Serial receive, serial communication |
0xFFFF74, 78, 7C, 80 | End of DMA channels 0, 1, 2, 3 |
The handler pointers
Each of the 18 interrupts above (from SWI 3 on) has a small stub in the BIOS that pushes the 32-bit pointer stored in RAM at 0x6FB8 + 4 × n and executes RET, which jumps to it. A program installs a handler by writing its address there; ROMs that carry a
handler table at 0x40 copy it to 0x6FB8 in their start-up code. The order (SWI 3, SWI 4, SWI 5, SWI 6, RTC alarm, V-blank, Z80, timers 0 to 3, serial receive, serial communication, reserved, end of DMA 0 to 3) is the order of RACE’s stubs and agrees with the position of the V-blank handler in Snake’s table.
BIOS variables
| Address | Name | Meaning (RACE’s initial values) |
|---|---|---|
0x6F80 | BAT_VOLT | Battery level; RACE starts it at 0x03FF, full |
0x6F82 | JOYPAD | Joypad state (RACE’s debugger reader returns its input here) |
0x6F84 | USR_BOOT | Start-up reason: 0x40 is power on |
0x6F85 | USR_SHUTDOWN | Shutdown request: 0, none |
0x6F86 | USR_ANSWER | User answer (RACE’s comment has a question mark: “No user answer (?)”) |
0x6F87 | LANGUAGE | Language: 1 is English |
0x6F91 | COLOR_FLAG | 0x10 on a Pocket Color (RACE’s colour flag) |
0x6F95 | GE_MODE_COPY | RACE sets 0x10 here on a colour start |
The names are ours: they label the disassembly (the Snake ROM sets bit 6 of 0x6F86 before it starts, which the viewer shows as USR_ANSWER); RACE’s source calls them by comments only.
Not described
- The real BIOS: its addresses, its full list of system calls (RACE implements ten and treats a few more as stubs) and the use of the flash calls’ result registers.
- The save flash chip numbers and sizes, and the layout of a game’s save data.
- The start-up sequence before a cartridge’s start address is jumped to (RACE skips it by loading a snapshot of RAM).