============================= Super Gameboy Technical Notes ============================= author: byuu date: 2009-04-30 ========== Registers: ========== The Super Gameboy responds to the following ranges on the address bus: $[00-3f]:[6000-7fff] $[80-bf]:[6000-7fff] $6000 (R): LY counter --------------------- This register returns the current value of the DMG LY counter (on the DMG, this corresponds to register $ff44.) The value ranges from 0 - 153, where 0 - 143 are visible scanlines, and 144 - 153 are vertical blank. I have no idea what happens when DMG software attempts to write to the LY counter, which is said to reset the counter. This would greatly confuse the SGB BIOS software as it is written. $6001 (W): unknown ------------------ Before transferring video RAM from $7800, the SGB BIOS writes to this register. The algorith used is: $6001 = ($6000 - tile_rows_left) & 3 $6002 (R): command ready ------------------------ When a command packet has been fully transferred from the DMG to the SGB, this register will return $01. Otherwise, it will return $00 to indicate there are no packets available. $6003 (W): control register --------------------------- d7 = ??? (generates six commands) d5 = 4-player enable d4 = 2-player enable d0 = ??? (always 1) When the BIOS first starts, it clears d7 and then sets it again right away. After this, it attempts to read sequential six commands from $6002. The command contains ROM data from the inserted Gameboy cartridge. This serves both to validate that a licensed Gameboy cartridge was inserted, as well as to display the "Nintendo" graphic below the Super Gameboy logo. After validating, it strobes d7 again and reads the six packets one more time. This time, it checks header flags to verify that the inserted cartridge is SGB compatible. If it is not, the BIOS will not respond to any commands coming from $6002, and will operate in classic DMG mode only. The six packets generated are in this format: Packet #1: $f1, $??, <14 bytes of GB ROM data, starting from $0104> Packet #2: $??, $??, <14 bytes of GB ROM data, starting from $0112> Packet #3: $??, $??, <14 bytes of GB ROM data, starting from $0120> Packet #4: $??, $??, <14 bytes of GB ROM data, starting from $012e> Packet #5: $??, $??, <14 bytes of GB ROM data, starting from $013c> Packet #6: $??, $??, <14 bytes of GB ROM data, starting from $014a> $?? values are not looked at by the SGB BIOS at all. They can be anything, but are most likely $f1,$00 for each packet. $f1 corresponds to command $1e with a length of 1, eg ($1e << 3) + 1 As for d5 and d4 ... these are updated whenever a MLT_REQ command is sent from the DMG. If the MLT_REQ asks for 4-player mode, d5 will be set and d4 will be clear. If it asks for 2-player mode, d5 will be clear and d4 will be set. If it asks for 1-player mode, both will be clear. For the sake of testing, I tried forcing d5 and d4 to be set ... this also results in 4-player mode being activated. $6004-$6007 (W): joypad controller data (ports 1-4) --------------------------------------------------- $6004 = joypad 1 $6005 = joypad 2 $6006 = joypad 3 $6007 = joypad 4 d7 = start button d6 = select button d5 = B button d4 = A button d3 = D-pad down d2 = D-pad up d1 = D-pad left d0 = D-pad right The actual bits are inverted; eg a value of 1 indicates the button is released, and a value of 0 indicates it is being pressed down. When $6003.d5 is set, the SGB BIOS will write controller data for multi-taps to $6004-$6007 in sequential order. Otherwise, when $6004.d4 is set, it will write data from controller ports 1 and 2 to $6004 and $6005. And when both of those bits are clear, it will only write to $6004. The DMG can strobe P15/P14 ($ff00) to increment the joypad read index, and the SGB will feed it the cached data for said controller. This is also how DMG games determine if the SGB is present: when both P15 and P14 are set, $ff00 returns the joypad ID, using: joypadID = 0x0f - (activeSuperGameboyJoypad). On a real DMG, having both bits set will always return 0x0f. Side note: DMG games also test the accumulator on reset to distinguish between SGB1 (A = $01) and SGB2 (A = $ff) hardware. The joypad ID test is still needed, as non-SGB hardware also returns these IDs. $700x (R): command read ----------------------- After the SGB BIOS reads $01 from $6002, it will read 16 bytes from $7000-$7fff, and then execute a callback function for the command specified in the first byte. I assume if the length bits indicate a size greater than one, it will repeat 16 more reads from these registers for each additional specified packet. However, to date I have yet to see a packet with a length greater than one used by a DMG cartridge. $7800 (R): VRAM data -------------------- This register returns video RAM data from the DMG. The SGB BIOS polls $6000 until it sees that the LY counter has reached a new tile row, and then it determines how many rows behind the SGB is from where the DMG is at (usually just one), and it will then write to $6001 and use DMA to perform a fixed DMA transfer from $7800 into SNES WRAM. The WRAM acts as a double buffering system and is eventually blitted to SNES video RAM for display on-screen. There is no register write to indicate where $7800 should read from. My only guess is that perhaps $6003.d7's strobe forces the read index from $7800 to synchronize to the LY counter from $6000. I have attempted to internally synchronize $7800's read index based on reads from $6000, on writes to $6001, and on the first of 320-byte block reads from $7800, all to no avail. The data read back does not correspond to the correct location expected by the BIOS, and data gets transferred to the wrong tile rows. My guess works perfectly for synchronization with pure DMG games, but falls apart when SGB games are used: quickly, the SGB BIOS gets out of sync and starts reading data from the wrong locations. This results in severe screen corruption, eg from reading SGB border tiledata and tilemap info from the wrong locations, as well as shifting the video output wildly. Even changing borders from the SGB main menu will cause the screen to shift. Essentially, there must be some sort of (additional?) method of synchronization between the SGB BIOS' automatic increments of DMG->SNES WRAM write locations, and the data fed back via $7800 ... perhaps involving $6001. After several days of testing hundreds of theories and studying trace logs of the SGB BIOS code, I was not able to determine how this would work. ========= Commands: ========= The DMG sends commands to the SGB to accomplish special effects. The way it does this is via writes to the JOYP register, located at $ff00. Register $ff00 maps P15 to d5, and P14 to d4. Commands consist of 16-byte packets in the following format: byte00.d7-d3 = command byte00.d2-d0 = length (1-7) byte01-byte15 = data To start a packet transmission, a pulse must be sent. This is accomplished by clearing both P15 and P14. Afterwards, one bit at a time is transmitted: Where P15=1, P14=0: bit = 0 Where P15=0, P14=1: bit = 1 After sending 128 bits, it will then stop transmission by sending one last stop bit, of zero (eg P15=1, P14=0) I assume if the first packet's length was greater than one, it would repeat the above process, including pulse and stop bits, only it would write data for the first byte of the next packet. It is also necessary to hold P15/P14 high again so that the SGB can detect when sending the same bit multiple times simply by monitoring the lines (and not the write bus signals themselves.) So for a complete example, here is how the DMG would send a MLT_REQ (command $11 packet, length = 1): //loop: //pulse: $ff00 = #$00 //pulse $ff00 = #$30 //raise //command+length pair: $ff00 = #$10 //command.d4 = 1 $ff00 = #$30 //raise $ff00 = #$20 //command.d3 = 0 $ff00 = #$30 //raise $ff00 = #$20 //command.d2 = 0 $ff00 = #$30 //raise $ff00 = #$20 //command.d1 = 0 $ff00 = #$30 //raise $ff00 = #$10 //command.d0 = 1 $ff00 = #$30 //raise $ff00 = #$20 //length.d2 = 0 $ff00 = #$30 //raise $ff00 = #$20 //length.d1 = 0 $ff00 = #$30 //raise $ff00 = #$10 //length.d0 = 1 $ff00 = #$30 //raise //byte 2 of 16: $ff00 = #$30 //raise $ff00 = #$?? //write one bit //bytes 3 of 16: //stop: $ff00 = #$30 //raise $ff00 = #$20 //stop //while --length: It is my belief that an entire command must be sent before the SGB BIOS will see $6002 set to $01. This is because it takes a very long time to write data one bit at a time like this from the DMG, and the SGB reads out the results from $700x very rapidly after $6002 is detected as set. List of commands: ----------------- $00 = PAL01 $01 = PAL23 $02 = PAL03 $03 = PAL12 $04 = ATTR_BLK $05 = ATTR_LIN $06 = ATTR_DIV $07 = ATTR_CHR $08 = SOUND $09 = SOU_TRN $0a = PAL_SET $0b = PAL_TRN $0c = ATRC_EN $0d = TEST_EN $0e = ICON_EN $0f = DATA_SND $10 = DATA_TRN $11 = MLT_REG $12 = JUMP $13 = CHR_TRN $14 = PCT_TRN $15 = ATTR_TRN $16 = ATTR_SET $17 = MASK_EN $18 = OBJ_TRN $19 = ??? (used by Zelda: DX, seems to toggle a bit in RAM) $1a = ??? $1b = ??? $1c = ??? $1d = ??? $1e = ??? (Gameboy ROM packet transfer) $1f = ??? Note: $1a-$1f all point to the same function, and the SGB BIOS expects there to be six commands of $1e after strobing $6003.d7. Aside from $1e, a detailed explanation of these registers isn't too important from the perspective of Super Gameboy emulation: the BIOS code itself is responsible for parsing these commands. Simply emulating that should be sufficient. Except perhaps for JUMP, which appears to have the ability to override the NMI vector address read from the cartridge itself via the SNES CPU.