Initial code to support SGB, command “parsing”, replacement SGB boot ROM

This commit is contained in:
Lior Halphon 2018-11-11 01:16:32 +02:00
parent a47e3cc62c
commit 44891d5c4a
10 changed files with 346 additions and 19 deletions

209
BootROMs/sgb_boot.asm Normal file
View File

@ -0,0 +1,209 @@
; Sameboy CGB bootstrap ROM
; Todo: use friendly names for HW registers instead of magic numbers
SECTION "BootCode", ROM0[$0]
Start:
; Init stack pointer
ld sp, $fffe
; Clear memory VRAM
ld hl, $8000
.clearVRAMLoop
ldi [hl], a
bit 5, h
jr z, .clearVRAMLoop
; Init Audio
ld a, $80
ldh [$26], a
ldh [$11], a
ld a, $f3
ldh [$12], a
ldh [$25], a
ld a, $77
ldh [$24], a
; Init BG palette to white
ld a, $0
ldh [$47], a
; Load logo from ROM.
; A nibble represents a 4-pixels line, 2 bytes represent a 4x4 tile, scaled to 8x8.
; Tiles are ordered left to right, top to bottom.
ld de, $104 ; Logo start
ld hl, $8010 ; This is where we load the tiles in VRAM
.loadLogoLoop
ld a, [de] ; Read 2 rows
ld b, a
call DoubleBitsAndWriteRow
call DoubleBitsAndWriteRow
inc de
ld a, e
xor $34 ; End of logo
jr nz, .loadLogoLoop
; Load trademark symbol
ld de, TrademarkSymbol
ld c,$08
.loadTrademarkSymbolLoop:
ld a,[de]
inc de
ldi [hl],a
inc hl
dec c
jr nz, .loadTrademarkSymbolLoop
; Set up tilemap
ld a,$19 ; Trademark symbol
ld [$9910], a ; ... put in the superscript position
ld hl,$992f ; Bottom right corner of the logo
ld c,$c ; Tiles in a logo row
.tilemapLoop
dec a
jr z, .tilemapDone
ldd [hl], a
dec c
jr nz, .tilemapLoop
ld l,$0f ; Jump to top row
jr .tilemapLoop
.tilemapDone
; Turn on LCD
ld a, $91
ldh [$40], a
ld a, $f1 ; Packet magic, increases by 2 for every packet
ldh [$80], a
ld hl, $104 ; Header start
xor a
ld c, a ; JOYP
.sendCommand
xor a
ldh [c], a
ld a, $30
ldh [c], a
ldh a, [$80]
call SendByte
push hl
ld b, $e
ld d, 0
.checksumLoop
call ReadHeaderByte
add d
ld d, a
dec b
jr nz, .checksumLoop
; Send checksum
call SendByte
pop hl
ld b, $e
.sendLoop
call ReadHeaderByte
call SendByte
dec b
jr nz, .sendLoop
; Done bit
ld a, $20
ldh [c], a
ld a, $30
ldh [c], a
; Update command
ldh a, [$80]
add 2
ldh [$80], a
ld a, $58
cp l
jr nz, .sendCommand
; Write to sound registers for DMG compatibility
ld c, $13
ld a, $c1
ldh [c], a
inc c
ld a, 7
ldh [c], a
; Init BG palette
ld a, $fc
ldh [$47], a
; Set registers to match the original SGB boot
ld a, 1
ld hl, $c060
; Boot the game
jp BootGame
ReadHeaderByte:
ld a, $4F
cp l
jr c, .zero
ld a, [hli]
ret
.zero:
inc hl
xor a
ret
SendByte:
ld e, a
ld d, 8
.loop
ld a, $10
rr e
jr c, .zeroBit
add a ; 10 -> 20
.zeroBit
ldh [c], a
ld a, $30
ldh [c], a
dec d
ret z
jr .loop
DoubleBitsAndWriteRow:
; Double the most significant 4 bits, b is shifted by 4
ld a, 4
ld c, 0
.doubleCurrentBit
sla b
push af
rl c
pop af
rl c
dec a
jr nz, .doubleCurrentBit
ld a, c
; Write as two rows
ldi [hl], a
inc hl
ldi [hl], a
inc hl
ret
WaitFrame:
push hl
ld hl, $FF0F
res 0, [hl]
.wait
bit 0, [hl]
jr z, .wait
pop hl
ret
TrademarkSymbol:
db $3c,$42,$b9,$a5,$b9,$a5,$42,$3c
SECTION "BootGame", ROM0[$fe]
BootGame:
ldh [$50], a

View File

@ -16,13 +16,15 @@ enum model {
MODEL_DMG,
MODEL_CGB,
MODEL_AGB,
MODEL_SGB,
};
static const GB_model_t cocoa_to_internal_model[] =
{
[MODEL_DMG] = GB_MODEL_DMG_B,
[MODEL_CGB] = GB_MODEL_CGB_E,
[MODEL_AGB] = GB_MODEL_AGB
[MODEL_AGB] = GB_MODEL_AGB,
[MODEL_SGB] = GB_MODEL_SGB,
};
@interface Document ()
@ -157,7 +159,7 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
return [[NSBundle mainBundle] pathForResource:name ofType:@"bin"];
}
/* Todo: Unify the 3 init functions */
/* Todo: Unify the 4 init functions */
- (void) initDMG
{
current_model = MODEL_DMG;
@ -166,6 +168,14 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
[self initCommon];
}
- (void) initSGB
{
current_model = MODEL_SGB;
GB_init(&gb, cocoa_to_internal_model[current_model]);
GB_load_boot_rom(&gb, [[self bootROMPathForName:@"sgb_boot"] UTF8String]);
[self initCommon];
}
- (void) initCGB
{
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"EmulateAGB"]) {
@ -274,7 +284,7 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
current_model = (enum model)[sender tag];
}
static NSString * const boot_names[] = {@"dmg_boot", @"cgb_boot", @"agb_boot"};
static NSString * const boot_names[] = {@"dmg_boot", @"cgb_boot", @"agb_boot", @"sgb_boot"};
GB_load_boot_rom(&gb, [[self bootROMPathForName:boot_names[current_model - 1]] UTF8String]);
if ([sender tag] == MODEL_NONE) {
@ -287,7 +297,8 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
if ([sender tag] != 0) {
/* User explictly selected a model, save the preference */
[[NSUserDefaults standardUserDefaults] setBool:current_model == MODEL_DMG forKey:@"EmulateDMG"];
[[NSUserDefaults standardUserDefaults] setBool:current_model== MODEL_AGB forKey:@"EmulateAGB"];
[[NSUserDefaults standardUserDefaults] setBool:current_model == MODEL_SGB forKey:@"EmulateSGB"];
[[NSUserDefaults standardUserDefaults] setBool:current_model == MODEL_AGB forKey:@"EmulateAGB"];
}
/* Reload the ROM, SAV and SYM files */
@ -382,6 +393,9 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"EmulateDMG"]) {
[self initDMG];
}
else if ([[NSUserDefaults standardUserDefaults] boolForKey:@"EmulateSGB"]) {
[self initSGB];
}
else {
[self initCGB];
}

View File

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9531" systemVersion="14F1713" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13771" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9531"/>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13771"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
@ -311,7 +312,13 @@
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="5GS-tt-E0a"/>
<menuItem title="Game Boy" tag="1" id="vc7-yy-ARW">
<menuItem title="Game Boy" tag="1" id="g7C-LA-VAr">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="reset:" target="-1" id="rxG-cz-s1S"/>
</connections>
</menuItem>
<menuItem title="Super Game Boy" tag="4" id="vc7-yy-ARW">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="reset:" target="-1" id="E4M-QG-ua9"/>

View File

@ -506,7 +506,12 @@ bool GB_is_inited(GB_gameboy_t *gb)
bool GB_is_cgb(GB_gameboy_t *gb)
{
return ((gb->model) & GB_MODEL_FAMILY_MASK) == GB_MODEL_CGB_FAMILY;
return (gb->model & GB_MODEL_FAMILY_MASK) == GB_MODEL_CGB_FAMILY;
}
bool GB_is_sgb(GB_gameboy_t *gb)
{
return (gb->model & ~GB_MODEL_PAL_BIT) == GB_MODEL_SGB || gb->model == GB_MODEL_SGB2;
}
void GB_set_turbo_mode(GB_gameboy_t *gb, bool on, bool no_frame_skip)
@ -541,6 +546,8 @@ static void reset_ram(GB_gameboy_t *gb)
break;
case GB_MODEL_DMG_B:
case GB_MODEL_SGB_NTSC: /* Unverified*/
case GB_MODEL_SGB_PAL: /* Unverified */
for (unsigned i = 0; i < gb->ram_size; i++) {
gb->ram[i] = (random() & 0xFF);
if (i & 0x100) {
@ -551,15 +558,13 @@ static void reset_ram(GB_gameboy_t *gb)
}
}
break;
#if 0
/* Not emulated yet, for documentation only */
case GB_MODEL_SGB2:
for (unsigned i = 0; i < gb->ram_size; i++) {
gb->ram[i] = 0x55;
gb->ram[i] ^= random() & random() & random();
}
break;
#endif
case GB_MODEL_CGB_C:
for (unsigned i = 0; i < gb->ram_size; i++) {
@ -729,5 +734,11 @@ void GB_set_clock_multiplier(GB_gameboy_t *gb, double multiplier)
uint32_t GB_get_clock_rate(GB_gameboy_t *gb)
{
if (gb->model == GB_MODEL_SGB_NTSC) {
return SGB_NTSC_FREQUENCY * gb->clock_multiplier;
}
if (gb->model == GB_MODEL_SGB_PAL) {
return SGB_PAL_FREQUENCY * gb->clock_multiplier;
}
return CPU_FREQUENCY * gb->clock_multiplier;
}

View File

@ -19,6 +19,7 @@
#include "rewind.h"
#include "z80_cpu.h"
#include "symbol_hash.h"
#include "sgb.h"
#define GB_STRUCT_VERSION 13
@ -27,6 +28,7 @@
#define GB_MODEL_DMG_FAMILY 0x000
#define GB_MODEL_MGB_FAMILY 0x100
#define GB_MODEL_CGB_FAMILY 0x200
#define GB_MODEL_PAL_BIT 0x1000
#endif
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
@ -48,15 +50,18 @@ typedef union {
uint8_t data[5];
} GB_rtc_time_t;
typedef enum {
// GB_MODEL_DMG_0 = 0x000,
// GB_MODEL_DMG_A = 0x001,
GB_MODEL_DMG_B = 0x002,
// GB_MODEL_DMG_C = 0x003,
// GB_MODEL_SGB = 0x004,
GB_MODEL_SGB = 0x004,
GB_MODEL_SGB_NTSC = GB_MODEL_SGB,
GB_MODEL_SGB_PAL = 0x1004,
// GB_MODEL_MGB = 0x100,
// GB_MODEL_SGB2 = 0x101,
GB_MODEL_SGB2 = 0x101,
// GB_MODEL_CGB_0 = 0x200,
// GB_MODEL_CGB_A = 0x201,
// GB_MODEL_CGB_B = 0x202,
@ -203,6 +208,8 @@ typedef enum {
#ifdef GB_INTERNAL
#define LCDC_PERIOD 70224
#define CPU_FREQUENCY 0x400000
#define SGB_NTSC_FREQUENCY (21477272 / 5)
#define SGB_PAL_FREQUENCY (21281370 / 5)
#define DIV_CYCLES (0x100)
#define INTERNAL_DIV_CYCLES (0x40000)
@ -459,6 +466,14 @@ struct GB_gameboy_internal_s {
uint8_t mode_for_interrupt;
bool lyc_interrupt_line;
);
/* Super Game Boy state, only dumped/loaded for relevant models */
GB_SECTION(sgb,
uint8_t sgb_command[16];
uint8_t sgb_command_write_index;
bool sgb_ready_for_pulse;
bool sgb_ready_for_write;
);
/* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */
/* This data is reserved on reset and must come last in the struct */
@ -584,6 +599,7 @@ __attribute__((__format__ (__printf__, fmtarg, firstvararg)))
void GB_init(GB_gameboy_t *gb, GB_model_t model);
bool GB_is_inited(GB_gameboy_t *gb);
bool GB_is_cgb(GB_gameboy_t *gb);
bool GB_is_sgb(GB_gameboy_t *gb);
GB_model_t GB_get_model(GB_gameboy_t *gb);
void GB_free(GB_gameboy_t *gb);
void GB_reset(GB_gameboy_t *gb);

View File

@ -47,12 +47,14 @@ void GB_update_joyp(GB_gameboy_t *gb)
default:
break;
}
if (previous_state != (gb->io_registers[GB_IO_JOYP] & 0xF)) {
/* The joypad interrupt DOES occur on CGB (Tested on CGB-CPU-06), unlike what some documents say. */
gb->io_registers[GB_IO_IF] |= 0x10;
gb->stopped = false;
}
gb->io_registers[GB_IO_JOYP] |= 0xC0; // No SGB support
gb->io_registers[GB_IO_JOYP] |= 0xC0;
}
void GB_set_key_state(GB_gameboy_t *gb, GB_key_t index, bool pressed)

View File

@ -271,6 +271,9 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
return gb->extra_oam[addr - 0xfea0];
case GB_MODEL_DMG_B:
case GB_MODEL_SGB_NTSC:
case GB_MODEL_SGB_PAL:
case GB_MODEL_SGB2:
;
}
}
@ -575,6 +578,9 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
gb->extra_oam[addr - 0xfea0] = value;
break;
case GB_MODEL_DMG_B:
case GB_MODEL_SGB_NTSC:
case GB_MODEL_SGB_PAL:
case GB_MODEL_SGB2:
case GB_MODEL_CGB_E:
case GB_MODEL_AGB:
break;
@ -727,6 +733,7 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
gb->io_registers[GB_IO_JOYP] &= 0x0F;
gb->io_registers[GB_IO_JOYP] |= value & 0xF0;
GB_update_joyp(gb);
GB_sgb_write(gb, value);
return;
case GB_IO_BIOS:

51
Core/sgb.c Normal file
View File

@ -0,0 +1,51 @@
#include "sgb.h"
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
{
if (!GB_is_sgb(gb)) return;
switch ((value >> 4) & 3 ) {
case 3:
gb->sgb_ready_for_pulse = true;
break;
case 2: // Zero
if (!gb->sgb_ready_for_pulse || !gb->sgb_ready_for_write) return;
if (gb->sgb_command_write_index >= sizeof(gb->sgb_command) * 8) {
GB_log(gb, "Got SGB command: ");
for (unsigned i = 0; i < 16; i++) {
GB_log(gb, "%02x ", gb->sgb_command[i]);
}
GB_log(gb, "\n");
gb->sgb_ready_for_pulse = false;
gb->sgb_ready_for_write = false;
}
else {
gb->sgb_command_write_index++;
gb->sgb_ready_for_pulse = false;
}
break;
case 1: // One
if (!gb->sgb_ready_for_pulse || !gb->sgb_ready_for_write) return;
if (gb->sgb_command_write_index >= sizeof(gb->sgb_command) * 8) {
GB_log(gb, "Corrupt SGB command.\n");
gb->sgb_ready_for_pulse = false;
gb->sgb_ready_for_write = false;
}
else {
gb->sgb_command[gb->sgb_command_write_index / 8] |= 1 << (gb->sgb_command_write_index & 7);
gb->sgb_command_write_index++;
gb->sgb_ready_for_pulse = false;
}
break;
case 0:
gb->sgb_ready_for_pulse = false;
gb->sgb_ready_for_write = true;
gb->sgb_command_write_index = 0;
memset(gb->sgb_command, 0, sizeof(gb->sgb_command));
break;
default:
break;
}
}

9
Core/sgb.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef sgb_h
#define sgb_h
#include "gb.h"
#ifdef GB_INTERNAL
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value);
#endif
#endif

View File

@ -106,9 +106,9 @@ endif
cocoa: $(BIN)/SameBoy.app
quicklook: $(BIN)/SameBoy.qlgenerator
sdl: $(SDL_TARGET) $(BIN)/SDL/dmg_boot.bin $(BIN)/SDL/cgb_boot.bin $(BIN)/SDL/agb_boot.bin $(BIN)/SDL/LICENSE $(BIN)/SDL/registers.sym $(BIN)/SDL/background.bmp $(BIN)/SDL/Shaders
bootroms: $(BIN)/BootROMs/agb_boot.bin $(BIN)/BootROMs/cgb_boot.bin $(BIN)/BootROMs/dmg_boot.bin
tester: $(TESTER_TARGET) $(BIN)/tester/dmg_boot.bin $(BIN)/tester/cgb_boot.bin $(BIN)/tester/agb_boot.bin
sdl: $(SDL_TARGET) $(BIN)/SDL/dmg_boot.bin $(BIN)/SDL/cgb_boot.bin $(BIN)/SDL/agb_boot.bin $(BIN)/SDL/sgb_boot.bin $(BIN)/SDL/LICENSE $(BIN)/SDL/registers.sym $(BIN)/SDL/background.bmp $(BIN)/SDL/Shaders
bootroms: $(BIN)/BootROMs/agb_boot.bin $(BIN)/BootROMs/cgb_boot.bin $(BIN)/BootROMs/dmg_boot.bin $(BIN)/BootROMs/sgb_boot.bin
tester: $(TESTER_TARGET) $(BIN)/tester/dmg_boot.bin $(BIN)/tester/cgb_boot.bin $(BIN)/tester/agb_boot.bin $(BIN)/tester/sgb_boot.bin
all: cocoa sdl tester libretro
# Get a list of our source files and their respective object file targets
@ -180,6 +180,7 @@ $(BIN)/SameBoy.app: $(BIN)/SameBoy.app/Contents/MacOS/SameBoy \
$(BIN)/SameBoy.app/Contents/Resources/dmg_boot.bin \
$(BIN)/SameBoy.app/Contents/Resources/cgb_boot.bin \
$(BIN)/SameBoy.app/Contents/Resources/agb_boot.bin \
$(BIN)/SameBoy.app/Contents/Resources/sgb_boot.bin \
$(patsubst %.xib,%.nib,$(addprefix $(BIN)/SameBoy.app/Contents/Resources/Base.lproj/,$(shell cd Cocoa;ls *.xib))) \
$(BIN)/SameBoy.qlgenerator \
Shaders
@ -307,7 +308,7 @@ $(BIN)/BootROMs/%.bin: BootROMs/%.asm
-@$(MKDIR) -p $(dir $@)
cd BootROMs && rgbasm -o ../$@.tmp ../$<
rgblink -o $@.tmp2 $@.tmp
head -c $(if $(findstring dmg,$@), 256, 2304) $@.tmp2 > $@
head -c $(if $(findstring dmg,$@)$(findstring sgb,$@), 256, 2304) $@.tmp2 > $@
@rm $@.tmp $@.tmp2
# Libretro Core (uses its own build system)