diff --git a/Cocoa/Document.m b/Cocoa/Document.m index 3f99b38..c663927 100644 --- a/Cocoa/Document.m +++ b/Cocoa/Document.m @@ -53,6 +53,8 @@ enum model { MODEL_AGB, MODEL_SGB, MODEL_MGB, + + MODEL_QUICK_RESET = -1, }; @interface Document () @@ -253,6 +255,7 @@ static void infraredStateChanged(GB_gameboy_t *gb, bool on) return (GB_model_t)[[NSUserDefaults standardUserDefaults] integerForKey:@"GBDMGModel"]; case MODEL_NONE: + case MODEL_QUICK_RESET: case MODEL_CGB: return (GB_model_t)[[NSUserDefaults standardUserDefaults] integerForKey:@"GBCGBModel"]; @@ -623,7 +626,12 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency) current_model = (enum model)[sender tag]; } - GB_switch_model_and_reset(&gb, [self internalModel]); + if ([sender tag] == MODEL_QUICK_RESET) { + GB_quick_reset(&gb); + } + else { + GB_switch_model_and_reset(&gb, [self internalModel]); + } if (old_width != GB_get_screen_width(&gb)) { [self.view screenSizeChanged]; @@ -631,7 +639,7 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency) [self updateMinSize]; - if ([sender tag] != 0) { + if ([sender tag] > MODEL_NONE) { /* User explictly selected a model, save the preference */ [[NSUserDefaults standardUserDefaults] setBool:current_model == MODEL_DMG forKey:@"EmulateDMG"]; [[NSUserDefaults standardUserDefaults] setBool:current_model == MODEL_SGB forKey:@"EmulateSGB"]; @@ -1176,7 +1184,7 @@ static bool is_path_writeable(const char *path) [(NSMenuItem *)anItem setState:self.isPaused]; return !GB_debugger_is_stopped(&gb); } - else if ([anItem action] == @selector(reset:) && anItem.tag != MODEL_NONE) { + else if ([anItem action] == @selector(reset:) && anItem.tag != MODEL_NONE && anItem.tag != MODEL_QUICK_RESET) { [(NSMenuItem *)anItem setState:anItem.tag == current_model]; } else if ([anItem action] == @selector(interrupt:)) { diff --git a/Cocoa/MainMenu.xib b/Cocoa/MainMenu.xib index 53a15a5..08e0771 100644 --- a/Cocoa/MainMenu.xib +++ b/Cocoa/MainMenu.xib @@ -202,6 +202,12 @@ + + + + + + diff --git a/Core/gb.c b/Core/gb.c index c346288..62cff34 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -1604,6 +1604,10 @@ static void reset_ram(GB_gameboy_t *gb) GB_palette_changed(gb, false, i * 2); } } + + if (!gb->cartridge_type->has_battery) { + memset(gb->mbc_ram, 0xFF, gb->mbc_ram_size); + } } static void request_boot_rom(GB_gameboy_t *gb) @@ -1646,8 +1650,29 @@ static void request_boot_rom(GB_gameboy_t *gb) } } -void GB_reset(GB_gameboy_t *gb) +static void GB_reset_internal(GB_gameboy_t *gb, bool quick) { + struct { + uint8_t hram[sizeof(gb->hram)]; + uint8_t background_palettes_data[sizeof(gb->background_palettes_data)]; + uint8_t object_palettes_data[sizeof(gb->object_palettes_data)]; + uint8_t oam[sizeof(gb->oam)]; + uint8_t extra_oam[sizeof(gb->extra_oam)]; + uint8_t dma, obp0, obp1; + } *preserved_state = NULL; + + if (quick) { + preserved_state = alloca(sizeof(*preserved_state)); + memcpy(preserved_state->hram, gb->hram, sizeof(gb->hram)); + memcpy(preserved_state->background_palettes_data, gb->background_palettes_data, sizeof(gb->background_palettes_data)); + memcpy(preserved_state->object_palettes_data, gb->object_palettes_data, sizeof(gb->object_palettes_data)); + memcpy(preserved_state->oam, gb->oam, sizeof(gb->oam)); + memcpy(preserved_state->extra_oam, gb->extra_oam, sizeof(gb->extra_oam)); + preserved_state->dma = gb->io_registers[GB_IO_DMA]; + preserved_state->obp0 = gb->io_registers[GB_IO_OBP0]; + preserved_state->obp1 = gb->io_registers[GB_IO_OBP1]; + } + uint32_t mbc_ram_size = gb->mbc_ram_size; GB_model_t model = gb->model; GB_update_clock_rate(gb); @@ -1679,14 +1704,9 @@ void GB_reset(GB_gameboy_t *gb) update_dmg_palette(gb); } - reset_ram(gb); gb->serial_mask = 0x80; gb->io_registers[GB_IO_SC] = 0x7E; - - /* These are not deterministic, but 00 (CGB) and FF (DMG) are the most common initial values by far */ - gb->io_registers[GB_IO_DMA] = gb->io_registers[GB_IO_OBP0] = gb->io_registers[GB_IO_OBP1] = GB_is_cgb(gb)? 0x00 : 0xFF; - gb->accessed_oam_row = -1; gb->dma_current_dest = 0xA1; @@ -1718,10 +1738,37 @@ void GB_reset(GB_gameboy_t *gb) gb->nontrivial_jump_state = NULL; } + if (!quick) { + reset_ram(gb); + /* These are not deterministic, but 00 (CGB) and FF (DMG) are the most common initial values by far. + The retain their previous values on quick resets */ + gb->io_registers[GB_IO_DMA] = gb->io_registers[GB_IO_OBP0] = gb->io_registers[GB_IO_OBP1] = GB_is_cgb(gb)? 0x00 : 0xFF; + } + else { + memcpy(gb->hram, preserved_state->hram, sizeof(gb->hram)); + memcpy(gb->background_palettes_data, preserved_state->background_palettes_data, sizeof(gb->background_palettes_data)); + memcpy(gb->object_palettes_data, preserved_state->object_palettes_data, sizeof(gb->object_palettes_data)); + memcpy(gb->oam, preserved_state->oam, sizeof(gb->oam)); + memcpy(gb->extra_oam, preserved_state->extra_oam, sizeof(gb->extra_oam)); + gb->io_registers[GB_IO_DMA] = preserved_state->dma; + gb->io_registers[GB_IO_OBP0] = preserved_state->obp0; + gb->io_registers[GB_IO_OBP1] = preserved_state->obp1; + } + gb->magic = state_magic(); request_boot_rom(gb); } +void GB_reset(GB_gameboy_t *gb) +{ + GB_reset_internal(gb, false); +} + +void GB_quick_reset(GB_gameboy_t *gb) +{ + GB_reset_internal(gb, true); +} + void GB_switch_model_and_reset(GB_gameboy_t *gb, GB_model_t model) { gb->model = model; diff --git a/Core/gb.h b/Core/gb.h index dc7e5d6..de4c331 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -881,6 +881,7 @@ bool GB_is_sgb(GB_gameboy_t *gb); // Returns true if the model is SGB or SGB2 bool GB_is_hle_sgb(GB_gameboy_t *gb); // Returns true if the model is SGB or SGB2 and the SFC/SNES side is HLE'd GB_model_t GB_get_model(GB_gameboy_t *gb); void GB_reset(GB_gameboy_t *gb); +void GB_quick_reset(GB_gameboy_t *gb); // Similar to the cart reset line void GB_switch_model_and_reset(GB_gameboy_t *gb, GB_model_t model); /* Returns the time passed, in 8MHz ticks. */ diff --git a/Core/mbc.c b/Core/mbc.c index 49c75d6..5f9373f 100644 --- a/Core/mbc.c +++ b/Core/mbc.c @@ -196,12 +196,9 @@ void GB_configure_cart(GB_gameboy_t *gb) } } - if (gb->mbc_ram) { - free(gb->mbc_ram); - gb->mbc_ram = NULL; - gb->mbc_ram_size = 0; - } - + size_t old_mbc_ram_size = gb->mbc_ram_size; + gb->mbc_ram_size = 0; + if (gb->cartridge_type->has_ram) { if (gb->cartridge_type->mbc_type == GB_MBC2) { gb->mbc_ram_size = 0x200; @@ -224,12 +221,16 @@ void GB_configure_cart(GB_gameboy_t *gb) } } - if (gb->mbc_ram_size) { - gb->mbc_ram = malloc(gb->mbc_ram_size); + if (gb->mbc_ram && old_mbc_ram_size != gb->mbc_ram_size) { + free(gb->mbc_ram); + gb->mbc_ram = NULL; + } + + if (gb->mbc_ram_size && !gb->mbc_ram) { + gb->mbc_ram = malloc(gb->mbc_ram_size); + /* Todo: Some games assume unintialized MBC RAM is 0xFF. It this true for all cartridge types? */ + memset(gb->mbc_ram, 0xFF, gb->mbc_ram_size); } - - /* Todo: Some games assume unintialized MBC RAM is 0xFF. It this true for all cartridge types? */ - memset(gb->mbc_ram, 0xFF, gb->mbc_ram_size); } /* MBC1 has at least 3 types of wiring (We currently support two (Standard and 4bit-MBC1M) of these).