Quick & Dirty conversion to a Mega Duck emulator

This commit is contained in:
Lior Halphon 2020-11-15 17:06:06 +02:00
parent c36bdc22f6
commit 316a282501
14 changed files with 146 additions and 1167 deletions

View File

@ -60,8 +60,6 @@
addr += 0x8000;
break;
case GBMemoryExternalRAM:
bank_backup = gb->mbc_ram_bank;
gb->mbc_ram_bank = self.selectedBank;
addr += 0xA000;
break;
case GBMemoryRAM:
@ -86,7 +84,6 @@
gb->cgb_vram_bank = bank_backup;
break;
case GBMemoryExternalRAM:
gb->mbc_ram_bank = bank_backup;
break;
case GBMemoryRAM:
gb->cgb_ram_bank = bank_backup;
@ -133,8 +130,6 @@
addr += 0x8000;
break;
case GBMemoryExternalRAM:
bank_backup = gb->mbc_ram_bank;
gb->mbc_ram_bank = self.selectedBank;
addr += 0xA000;
break;
case GBMemoryRAM:
@ -163,7 +158,6 @@
gb->cgb_vram_bank = bank_backup;
break;
case GBMemoryExternalRAM:
gb->mbc_ram_bank = bank_backup;
break;
case GBMemoryRAM:
gb->cgb_ram_bank = bank_backup;

View File

@ -33,16 +33,16 @@ bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index)
switch (index) {
case GB_SQUARE_1:
return gb->io_registers[GB_IO_NR12] & 0xF8;
return gb->io_registers[GB_IO_NR12] & 0x8F;
case GB_SQUARE_2:
return gb->io_registers[GB_IO_NR22] & 0xF8;
return gb->io_registers[GB_IO_NR22] & 0x8F;
case GB_WAVE:
return gb->apu.wave_channel.enable;
case GB_NOISE:
return gb->io_registers[GB_IO_NR42] & 0xF8;
return gb->io_registers[GB_IO_NR42] & 0x8F;
}
return false;
@ -258,7 +258,7 @@ static void update_square_sample(GB_gameboy_t *gb, unsigned index)
DMG, MGB or SGB/2 */
static void nrx2_glitch(uint8_t *volume, uint8_t value, uint8_t old_value)
{
if (value & 8) {
if (value & 0x80) {
(*volume)++;
}
@ -266,11 +266,11 @@ static void nrx2_glitch(uint8_t *volume, uint8_t value, uint8_t old_value)
*volume = 0x10 - *volume;
}
if ((value & 7) && !(old_value & 7) && *volume && !(value & 8)) {
if ((value & 0x70) && !(old_value & 0x70) && *volume && !(value & 0x80)) {
(*volume)--;
}
if ((old_value & 7) && (value & 8)) {
if ((old_value & 0x70) && (value & 0x880)) {
(*volume)--;
}
@ -281,7 +281,7 @@ static void tick_square_envelope(GB_gameboy_t *gb, enum GB_CHANNELS index)
{
uint8_t nrx2 = gb->io_registers[index == GB_SQUARE_1? GB_IO_NR12 : GB_IO_NR22];
if (gb->apu.square_channels[index].volume_countdown || (nrx2 & 7)) {
if (gb->apu.square_channels[index].volume_countdown || (nrx2 & 0x70)) {
if (!gb->apu.square_channels[index].volume_countdown || !--gb->apu.square_channels[index].volume_countdown) {
if (gb->cgb_double_speed) {
if (index == GB_SQUARE_1) {
@ -292,15 +292,15 @@ static void tick_square_envelope(GB_gameboy_t *gb, enum GB_CHANNELS index)
}
}
if ((nrx2 & 8) && gb->apu.square_channels[index].current_volume < 0xF) {
if ((nrx2 & 0x80) && gb->apu.square_channels[index].current_volume < 0xF) {
gb->apu.square_channels[index].current_volume++;
}
else if (!(nrx2 & 8) && gb->apu.square_channels[index].current_volume > 0) {
else if (!(nrx2 & 0x80) && gb->apu.square_channels[index].current_volume > 0) {
gb->apu.square_channels[index].current_volume--;
}
gb->apu.square_channels[index].volume_countdown = nrx2 & 7;
gb->apu.square_channels[index].volume_countdown = (nrx2 >> 4) & 7;
if (gb->apu.is_active[index]) {
update_square_sample(gb, index);
@ -313,20 +313,20 @@ static void tick_noise_envelope(GB_gameboy_t *gb)
{
uint8_t nr42 = gb->io_registers[GB_IO_NR42];
if (gb->apu.noise_channel.volume_countdown || (nr42 & 7)) {
if (gb->apu.noise_channel.volume_countdown || (nr42 & 0x70)) {
if (!gb->apu.noise_channel.volume_countdown || !--gb->apu.noise_channel.volume_countdown) {
if (gb->cgb_double_speed) {
gb->apu.pcm_mask[0] &= (gb->apu.noise_channel.current_volume << 2) | 0x1F;
}
if ((nr42 & 8) && gb->apu.noise_channel.current_volume < 0xF) {
if ((nr42 & 0x80) && gb->apu.noise_channel.current_volume < 0xF) {
gb->apu.noise_channel.current_volume++;
}
else if (!(nr42 & 8) && gb->apu.noise_channel.current_volume > 0) {
else if (!(nr42 & 0x80) && gb->apu.noise_channel.current_volume > 0) {
gb->apu.noise_channel.current_volume--;
}
gb->apu.noise_channel.volume_countdown = nr42 & 7;
gb->apu.noise_channel.volume_countdown = (nr42 >> 4) & 7;
if (gb->apu.is_active[GB_NOISE]) {
update_sample(gb, GB_NOISE,
@ -360,7 +360,7 @@ void GB_apu_div_event(GB_gameboy_t *gb)
}
}
if (gb->apu.is_active[GB_NOISE] && gb->apu.noise_channel.volume_countdown == 0 && (gb->io_registers[GB_IO_NR42] & 7)) {
if (gb->apu.is_active[GB_NOISE] && gb->apu.noise_channel.volume_countdown == 0 && (gb->io_registers[GB_IO_NR42] & 0x70)) {
tick_noise_envelope(gb);
}
}
@ -579,17 +579,17 @@ uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg)
return value;
}
static const char read_mask[GB_IO_WAV_END - GB_IO_NR10 + 1] = {
/* NRX0 NRX1 NRX2 NRX3 NRX4 */
0x80, 0x3F, 0x00, 0xFF, 0xBF, // NR1X
0xFF, 0x3F, 0x00, 0xFF, 0xBF, // NR2X
static const char read_mask[GB_IO_NR51 - GB_IO_NR10 + 1] = {
0x80, 0x00, 0x3F, 0xFF, 0xBF, // NR1X
0x3F, 0xFF, 0x00, 0xFF, 0xBF, // NR2X
0x7F, 0xFF, 0x9F, 0xFF, 0xBF, // NR3X
0xFF, 0xFF, 0x00, 0x00, 0xBF, // NR4X
0x00, 0x00, 0x70, 0xFF, 0xFF, // NR5X
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Unused
0xFF, // "NR40"
// Wave RAM
0, /* ... */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0x00, 0x00, 0xBF, // NR4X
0x00, 0x00, 0x70, // NR5X
};
if (reg >= GB_IO_WAV_START && reg <= GB_IO_WAV_END && gb->apu.is_active[GB_WAVE]) {
@ -695,11 +695,11 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
case GB_IO_NR12:
case GB_IO_NR22: {
unsigned index = reg == GB_IO_NR22? GB_SQUARE_2: GB_SQUARE_1;
if (((value & 0x7) == 0) && ((gb->io_registers[reg] & 0x7) != 0)) {
if ((((value >> 4) & 0x7) == 0) && (((gb->io_registers[reg] >> 4) & 0x7) != 0)) {
/* Envelope disabled */
gb->apu.square_channels[index].volume_countdown = 0;
}
if ((value & 0xF8) == 0) {
if ((value & 0x8F) == 0) {
/* This disables the DAC */
gb->io_registers[reg] = value;
gb->apu.is_active[index] = false;
@ -756,7 +756,7 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
/* Timing quirk: if already active, sound starts 2 (2MHz) ticks earlier.*/
gb->apu.square_channels[index].sample_countdown = (gb->apu.square_channels[index].sample_length ^ 0x7FF) * 2 + 4 - gb->apu.lf_div;
}
gb->apu.square_channels[index].current_volume = gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] >> 4;
gb->apu.square_channels[index].current_volume = gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] & 0xF;
/* The volume changes caused by NRX4 sound start take effect instantly (i.e. the effect the previously
started sound). The playback itself is not instant which is why we don't update the sample for other
@ -765,9 +765,9 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
update_square_sample(gb, index);
}
gb->apu.square_channels[index].volume_countdown = gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] & 7;
gb->apu.square_channels[index].volume_countdown = (gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] >> 4) & 7;
if ((gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] & 0xF8) != 0 && !gb->apu.is_active[index]) {
if ((gb->io_registers[index == GB_SQUARE_1 ? GB_IO_NR12 : GB_IO_NR22] & 0x8F) != 0 && !gb->apu.is_active[index]) {
gb->apu.is_active[index] = true;
update_sample(gb, index, 0, 0);
/* We use the highest bit in current_sample_index to mark this sample is not actually playing yet, */
@ -915,11 +915,11 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
}
case GB_IO_NR42: {
if (((value & 0x7) == 0) && ((gb->io_registers[reg] & 0x7) != 0)) {
if (((value & 0x70) == 0) && ((gb->io_registers[reg] & 0x70) != 0)) {
/* Envelope disabled */
gb->apu.noise_channel.volume_countdown = 0;
}
if ((value & 0xF8) == 0) {
if ((value & 0x8F) == 0) {
/* This disables the DAC */
gb->io_registers[reg] = value;
gb->apu.is_active[GB_NOISE] = false;
@ -936,10 +936,10 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
}
case GB_IO_NR43: {
gb->apu.noise_channel.narrow = value & 8;
unsigned divisor = (value & 0x07) << 1;
gb->apu.noise_channel.narrow = value & 0x80;
unsigned divisor = (value & 0x70) << 1;
if (!divisor) divisor = 1;
gb->apu.noise_channel.sample_length = (divisor << (value >> 4)) - 1;
gb->apu.noise_channel.sample_length = (divisor << (value & 0xF)) - 1;
/* Todo: changing the frequency sometimes delays the next sample. This is probably
due to how the frequency is actually calculated in the noise channel, which is probably

View File

@ -80,8 +80,6 @@ static uint16_t bank_for_addr(GB_gameboy_t *gb, uint16_t addr)
typedef struct {
uint16_t rom0_bank;
uint16_t rom_bank;
uint8_t mbc_ram_bank;
bool mbc_ram_enable;
uint8_t ram_bank;
uint8_t vram_bank;
} banking_state_t;
@ -90,8 +88,6 @@ static inline void save_banking_state(GB_gameboy_t *gb, banking_state_t *state)
{
state->rom0_bank = gb->mbc_rom0_bank;
state->rom_bank = gb->mbc_rom_bank;
state->mbc_ram_bank = gb->mbc_ram_bank;
state->mbc_ram_enable = gb->mbc_ram_enable;
state->ram_bank = gb->cgb_ram_bank;
state->vram_bank = gb->cgb_vram_bank;
}
@ -101,8 +97,6 @@ static inline void restore_banking_state(GB_gameboy_t *gb, banking_state_t *stat
gb->mbc_rom0_bank = state->rom0_bank;
gb->mbc_rom_bank = state->rom_bank;
gb->mbc_ram_bank = state->mbc_ram_bank;
gb->mbc_ram_enable = state->mbc_ram_enable;
gb->cgb_ram_bank = state->ram_bank;
gb->cgb_vram_bank = state->vram_bank;
}
@ -111,8 +105,6 @@ static inline void switch_banking_state(GB_gameboy_t *gb, uint16_t bank)
{
gb->mbc_rom0_bank = bank;
gb->mbc_rom_bank = bank;
gb->mbc_ram_bank = bank;
gb->mbc_ram_enable = true;
if (GB_is_cgb(gb)) {
gb->cgb_ram_bank = bank & 7;
gb->cgb_vram_bank = bank & 1;
@ -1530,58 +1522,8 @@ static bool mbc(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
return true;
}
const GB_cartridge_t *cartridge = gb->cartridge_type;
if (cartridge->has_ram) {
GB_log(gb, "Cartridge includes%s RAM: $%x bytes\n", cartridge->has_battery? " battery-backed": "", gb->mbc_ram_size);
}
else {
GB_log(gb, "No cartridge RAM\n");
}
if (cartridge->mbc_type) {
if (gb->is_mbc30) {
GB_log(gb, "MBC30\n");
}
else {
static const char *const mapper_names[] = {
[GB_MBC1] = "MBC1",
[GB_MBC2] = "MBC2",
[GB_MBC3] = "MBC3",
[GB_MBC5] = "MBC5",
[GB_HUC1] = "HUC-1",
[GB_HUC3] = "HUC-3",
};
GB_log(gb, "%s\n", mapper_names[cartridge->mbc_type]);
}
GB_log(gb, "Current mapped ROM bank: %x\n", gb->mbc_rom_bank);
if (cartridge->has_ram) {
GB_log(gb, "Current mapped RAM bank: %x\n", gb->mbc_ram_bank);
if (gb->cartridge_type->mbc_type != GB_HUC1) {
GB_log(gb, "RAM is curently %s\n", gb->mbc_ram_enable? "enabled" : "disabled");
}
}
if (cartridge->mbc_type == GB_MBC1 && gb->mbc1_wiring == GB_STANDARD_MBC1_WIRING) {
GB_log(gb, "MBC1 banking mode is %s\n", gb->mbc1.mode == 1 ? "RAM" : "ROM");
}
if (cartridge->mbc_type == GB_MBC1 && gb->mbc1_wiring == GB_MBC1M_WIRING) {
GB_log(gb, "MBC1 uses MBC1M wiring. \n");
GB_log(gb, "Current mapped ROM0 bank: %x\n", gb->mbc_rom0_bank);
GB_log(gb, "MBC1 multicart banking mode is %s\n", gb->mbc1.mode == 1 ? "enabled" : "disabled");
}
}
else {
GB_log(gb, "No MBC\n");
}
if (cartridge->has_rumble) {
GB_log(gb, "Cart contains a Rumble Pak\n");
}
if (cartridge->has_rtc) {
GB_log(gb, "Cart contains a real time clock\n");
}
GB_log(gb, "Current mapped ROM bank: %x\n", gb->mbc_rom_bank);
GB_log(gb, "Current mapped ROM0 bank: %x\n", gb->mbc_rom0_bank);
return true;
}
@ -1662,13 +1604,13 @@ static bool lcd(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
GB_log(gb, "LCDC:\n");
GB_log(gb, " LCD enabled: %s\n",(gb->io_registers[GB_IO_LCDC] & 128)? "Enabled" : "Disabled");
GB_log(gb, " %s: %s\n", (gb->cgb_mode? "Sprite priority flags" : "Background and Window"),
(gb->io_registers[GB_IO_LCDC] & 1)? "Enabled" : "Disabled");
GB_log(gb, " Objects: %s\n", (gb->io_registers[GB_IO_LCDC] & 2)? "Enabled" : "Disabled");
GB_log(gb, " Object size: %s\n", (gb->io_registers[GB_IO_LCDC] & 4)? "8x16" : "8x8");
GB_log(gb, " Background tilemap: %s\n", (gb->io_registers[GB_IO_LCDC] & 8)? "$9C00" : "$9800");
(gb->io_registers[GB_IO_LCDC] & 64)? "Enabled" : "Disabled");
GB_log(gb, " Objects: %s\n", (gb->io_registers[GB_IO_LCDC] & 1)? "Enabled" : "Disabled");
GB_log(gb, " Object size: %s\n", (gb->io_registers[GB_IO_LCDC] & 2)? "8x16" : "8x8");
GB_log(gb, " Background tilemap: %s\n", (gb->io_registers[GB_IO_LCDC] & 4)? "$9C00" : "$9800");
GB_log(gb, " Background and Window Tileset: %s\n", (gb->io_registers[GB_IO_LCDC] & 16)? "$8000" : "$8800");
GB_log(gb, " Window: %s\n", (gb->io_registers[GB_IO_LCDC] & 32)? "Enabled" : "Disabled");
GB_log(gb, " Window tilemap: %s\n", (gb->io_registers[GB_IO_LCDC] & 64)? "$9C00" : "$9800");
GB_log(gb, " Window tilemap: %s\n", (gb->io_registers[GB_IO_LCDC] & 8)? "$9C00" : "$9800");
GB_log(gb, "\nSTAT:\n");
static const char *modes[] = {"Mode 0, H-Blank", "Mode 1, V-Blank", "Mode 2, OAM", "Mode 3, Rendering"};

View File

@ -415,7 +415,7 @@ static void add_object_from_index(GB_gameboy_t *gb, unsigned index)
/* This reverse sorts the visible objects by location and priority */
GB_object_t *objects = (GB_object_t *) &gb->oam;
bool height_16 = (gb->io_registers[GB_IO_LCDC] & 4) != 0;
bool height_16 = (gb->io_registers[GB_IO_LCDC] & 2) != 0;
signed y = objects[index].y - 16;
if (y <= gb->current_line && y + (height_16? 16 : 8) > gb->current_line) {
unsigned j = 0;
@ -443,7 +443,7 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
if (fifo_size(&gb->oam_fifo)) {
oam_fifo_item = fifo_pop(&gb->oam_fifo);
if (oam_fifo_item->pixel && (gb->io_registers[GB_IO_LCDC] & 2)) {
if (oam_fifo_item->pixel && (gb->io_registers[GB_IO_LCDC] & 1)) {
draw_oam = true;
bg_priority |= oam_fifo_item->bg_priority;
}
@ -461,7 +461,7 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
/* Mixing */
if ((gb->io_registers[GB_IO_LCDC] & 0x1) == 0) {
if ((gb->io_registers[GB_IO_LCDC] & 0x40) == 0) {
if (gb->cgb_mode) {
bg_priority = false;
}
@ -583,10 +583,10 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb)
}
/* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong. */
if (gb->io_registers[GB_IO_LCDC] & 0x08 && !gb->wx_triggered) {
if (gb->io_registers[GB_IO_LCDC] & 0x04 && !gb->wx_triggered) {
map = 0x1C00;
}
else if (gb->io_registers[GB_IO_LCDC] & 0x40 && gb->wx_triggered) {
else if (gb->io_registers[GB_IO_LCDC] & 0x8 && gb->wx_triggered) {
map = 0x1C00;
}
@ -715,7 +715,7 @@ static uint16_t get_object_line_address(GB_gameboy_t *gb, const GB_object_t *obj
object = &blocked;
}
bool height_16 = (gb->io_registers[GB_IO_LCDC] & 4) != 0; /* Todo: Which T-cycle actually reads this? */
bool height_16 = (gb->io_registers[GB_IO_LCDC] & 2) != 0; /* Todo: Which T-cycle actually reads this? */
uint8_t tile_y = (gb->current_line - object->y) & (height_16? 0xF : 7);
if (object->flags & 0x40) { /* Flip Y */
@ -1044,7 +1044,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
gb->during_object_fetch = true;
while (gb->n_visible_objs != 0 &&
(gb->io_registers[GB_IO_LCDC] & 2 || GB_is_cgb(gb)) &&
(gb->io_registers[GB_IO_LCDC] & 1 || GB_is_cgb(gb)) &&
gb->obj_comparators[gb->n_visible_objs - 1] == (uint8_t)(gb->position_in_line + 8)) {
while (gb->fetcher_state < 5 || fifo_size(&gb->bg_fifo) == 0) {
@ -1376,7 +1376,7 @@ void GB_draw_tilemap(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette
break;
}
if (map_type == GB_MAP_9C00 || (map_type == GB_MAP_AUTO && gb->io_registers[GB_IO_LCDC] & 0x08)) {
if (map_type == GB_MAP_9C00 || (map_type == GB_MAP_AUTO && gb->io_registers[GB_IO_LCDC] & 0x04)) {
map = 0x1c00;
}
@ -1425,7 +1425,7 @@ void GB_draw_tilemap(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette
uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest, uint8_t *sprite_height)
{
uint8_t count = 0;
*sprite_height = (gb->io_registers[GB_IO_LCDC] & 4) ? 16:8;
*sprite_height = (gb->io_registers[GB_IO_LCDC] & 2) ? 16:8;
uint8_t oam_to_dest_index[40] = {0,};
for (unsigned y = 0; y < LINES; y++) {
GB_object_t *sprite = (GB_object_t *) &gb->oam;

393
Core/gb.c
View File

@ -161,7 +161,6 @@ void GB_init(GB_gameboy_t *gb, GB_model_t model)
gb->input_callback = default_input_callback;
gb->async_input_callback = default_async_input_callback;
#endif
gb->cartridge_type = &GB_cart_defs[0]; // Default cartridge type
gb->clock_multiplier = 1.0;
if (model & GB_MODEL_NO_SFC_BIT) {
@ -187,9 +186,6 @@ void GB_free(GB_gameboy_t *gb)
if (gb->vram) {
free(gb->vram);
}
if (gb->mbc_ram) {
free(gb->mbc_ram);
}
if (gb->rom) {
free(gb->rom);
}
@ -245,7 +241,6 @@ void GB_borrow_sgb_border(GB_gameboy_t *gb)
if (!gb->boot_rom_load_callback) return; // Can't borrow a border without this callback
GB_gameboy_t sgb;
GB_init(&sgb, GB_MODEL_SGB);
sgb.cartridge_type = gb->cartridge_type;
sgb.rom = gb->rom;
sgb.rom_size = gb->rom_size;
sgb.turbo = true;
@ -301,7 +296,6 @@ int GB_load_rom(GB_gameboy_t *gb, const char *path)
memset(gb->rom, 0xFF, gb->rom_size); /* Pad with 0xFFs */
fread(gb->rom, 1, gb->rom_size, f);
fclose(f);
GB_configure_cart(gb);
return 0;
}
@ -483,44 +477,7 @@ done:;
memset(gb->rom + gb->rom_size, 0, needed_size - gb->rom_size);
gb->rom_size = needed_size;
}
GB_configure_cart(gb);
// Fix a common wrong MBC error
if (gb->rom[0x147] == 3) { // MBC1 + RAM + Battery
bool needs_fix = false;
if (gb->rom_size >= 0x21 * 0x4000) {
for (unsigned i = 0x20 * 0x4000; i < 0x21 * 0x4000; i++) {
if (gb->rom[i]) {
needs_fix = true;
break;
}
}
}
if (!needs_fix && gb->rom_size >= 0x41 * 0x4000) {
for (unsigned i = 0x40 * 0x4000; i < 0x41 * 0x4000; i++) {
if (gb->rom[i]) {
needs_fix = true;
break;
}
}
}
if (!needs_fix && gb->rom_size >= 0x61 * 0x4000) {
for (unsigned i = 0x60 * 0x4000; i < 0x61 * 0x4000; i++) {
if (gb->rom[i]) {
needs_fix = true;
break;
}
}
}
if (needs_fix) {
gb->rom[0x147] = 0x10; // MBC3 + RTC + RAM + Battery
GB_configure_cart(gb);
gb->rom[0x147] = 0x3;
GB_log(gb, "ROM claims to use MBC1 but appears to require MBC3 or 5, assuming MBC3.\n");
}
}
if (old_rom) {
free(old_rom);
}
@ -553,7 +510,6 @@ void GB_load_rom_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t siz
gb->rom = malloc(gb->rom_size);
memset(gb->rom, 0xff, gb->rom_size);
memcpy(gb->rom, buffer, size);
GB_configure_cart(gb);
}
typedef struct {
@ -596,351 +552,27 @@ typedef union {
int GB_save_battery_size(GB_gameboy_t *gb)
{
if (!gb->cartridge_type->has_battery) return 0; // Nothing to save.
if (gb->mbc_ram_size == 0 && !gb->cartridge_type->has_rtc) return 0; /* Claims to have battery, but has no RAM or RTC */
if (gb->cartridge_type->mbc_type == GB_HUC3) {
return gb->mbc_ram_size + sizeof(GB_huc3_rtc_time_t);
}
GB_rtc_save_t rtc_save_size;
return gb->mbc_ram_size + (gb->cartridge_type->has_rtc ? sizeof(rtc_save_size.vba64) : 0);
return 0;
}
int GB_save_battery_to_buffer(GB_gameboy_t *gb, uint8_t *buffer, size_t size)
{
if (!gb->cartridge_type->has_battery) return 0; // Nothing to save.
if (gb->mbc_ram_size == 0 && !gb->cartridge_type->has_rtc) return 0; /* Claims to have battery, but has no RAM or RTC */
if (size < GB_save_battery_size(gb)) return EIO;
memcpy(buffer, gb->mbc_ram, gb->mbc_ram_size);
if (gb->cartridge_type->mbc_type == GB_HUC3) {
buffer += gb->mbc_ram_size;
#ifdef GB_BIG_ENDIAN
GB_huc3_rtc_time_t rtc_save = {
__builtin_bswap64(gb->last_rtc_second),
__builtin_bswap16(gb->huc3_minutes),
__builtin_bswap16(gb->huc3_days),
__builtin_bswap16(gb->huc3_alarm_minutes),
__builtin_bswap16(gb->huc3_alarm_days),
gb->huc3_alarm_enabled,
};
#else
GB_huc3_rtc_time_t rtc_save = {
gb->last_rtc_second,
gb->huc3_minutes,
gb->huc3_days,
gb->huc3_alarm_minutes,
gb->huc3_alarm_days,
gb->huc3_alarm_enabled,
};
#endif
memcpy(buffer, &rtc_save, sizeof(rtc_save));
}
else if (gb->cartridge_type->has_rtc) {
GB_rtc_save_t rtc_save = {{{{0,}},},};
rtc_save.vba64.rtc_real.seconds = gb->rtc_real.seconds;
rtc_save.vba64.rtc_real.minutes = gb->rtc_real.minutes;
rtc_save.vba64.rtc_real.hours = gb->rtc_real.hours;
rtc_save.vba64.rtc_real.days = gb->rtc_real.days;
rtc_save.vba64.rtc_real.high = gb->rtc_real.high;
rtc_save.vba64.rtc_latched.seconds = gb->rtc_latched.seconds;
rtc_save.vba64.rtc_latched.minutes = gb->rtc_latched.minutes;
rtc_save.vba64.rtc_latched.hours = gb->rtc_latched.hours;
rtc_save.vba64.rtc_latched.days = gb->rtc_latched.days;
rtc_save.vba64.rtc_latched.high = gb->rtc_latched.high;
#ifdef GB_BIG_ENDIAN
rtc_save.vba64.last_rtc_second = __builtin_bswap64(gb->last_rtc_second);
#else
rtc_save.vba64.last_rtc_second = gb->last_rtc_second;
#endif
memcpy(buffer + gb->mbc_ram_size, &rtc_save.vba64, sizeof(rtc_save.vba64));
}
errno = 0;
return errno;
return 0;
}
int GB_save_battery(GB_gameboy_t *gb, const char *path)
{
if (!gb->cartridge_type->has_battery) return 0; // Nothing to save.
if (gb->mbc_ram_size == 0 && !gb->cartridge_type->has_rtc) return 0; /* Claims to have battery, but has no RAM or RTC */
FILE *f = fopen(path, "wb");
if (!f) {
GB_log(gb, "Could not open battery save: %s.\n", strerror(errno));
return errno;
}
if (fwrite(gb->mbc_ram, 1, gb->mbc_ram_size, f) != gb->mbc_ram_size) {
fclose(f);
return EIO;
}
if (gb->cartridge_type->mbc_type == GB_HUC3) {
#ifdef GB_BIG_ENDIAN
GB_huc3_rtc_time_t rtc_save = {
__builtin_bswap64(gb->last_rtc_second),
__builtin_bswap16(gb->huc3_minutes),
__builtin_bswap16(gb->huc3_days),
__builtin_bswap16(gb->huc3_alarm_minutes),
__builtin_bswap16(gb->huc3_alarm_days),
gb->huc3_alarm_enabled,
};
#else
GB_huc3_rtc_time_t rtc_save = {
gb->last_rtc_second,
gb->huc3_minutes,
gb->huc3_days,
gb->huc3_alarm_minutes,
gb->huc3_alarm_days,
gb->huc3_alarm_enabled,
};
#endif
if (fwrite(&rtc_save, sizeof(rtc_save), 1, f) != 1) {
fclose(f);
return EIO;
}
}
else if (gb->cartridge_type->has_rtc) {
GB_rtc_save_t rtc_save = {{{{0,}},},};
rtc_save.vba64.rtc_real.seconds = gb->rtc_real.seconds;
rtc_save.vba64.rtc_real.minutes = gb->rtc_real.minutes;
rtc_save.vba64.rtc_real.hours = gb->rtc_real.hours;
rtc_save.vba64.rtc_real.days = gb->rtc_real.days;
rtc_save.vba64.rtc_real.high = gb->rtc_real.high;
rtc_save.vba64.rtc_latched.seconds = gb->rtc_latched.seconds;
rtc_save.vba64.rtc_latched.minutes = gb->rtc_latched.minutes;
rtc_save.vba64.rtc_latched.hours = gb->rtc_latched.hours;
rtc_save.vba64.rtc_latched.days = gb->rtc_latched.days;
rtc_save.vba64.rtc_latched.high = gb->rtc_latched.high;
#ifdef GB_BIG_ENDIAN
rtc_save.vba64.last_rtc_second = __builtin_bswap64(gb->last_rtc_second);
#else
rtc_save.vba64.last_rtc_second = gb->last_rtc_second;
#endif
if (fwrite(&rtc_save.vba64, 1, sizeof(rtc_save.vba64), f) != sizeof(rtc_save.vba64)) {
fclose(f);
return EIO;
}
}
errno = 0;
fclose(f);
return errno;
return 0;
}
void GB_load_battery_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size)
{
memcpy(gb->mbc_ram, buffer, MIN(gb->mbc_ram_size, size));
if (size <= gb->mbc_ram_size) {
goto reset_rtc;
}
if (gb->cartridge_type->mbc_type == GB_HUC3) {
GB_huc3_rtc_time_t rtc_save;
if (size - gb->mbc_ram_size < sizeof(rtc_save)) {
goto reset_rtc;
}
memcpy(&rtc_save, buffer + gb->mbc_ram_size, sizeof(rtc_save));
#ifdef GB_BIG_ENDIAN
gb->last_rtc_second = __builtin_bswap64(rtc_save.last_rtc_second);
gb->huc3_minutes = __builtin_bswap16(rtc_save.minutes);
gb->huc3_days = __builtin_bswap16(rtc_save.days);
gb->huc3_alarm_minutes = __builtin_bswap16(rtc_save.alarm_minutes);
gb->huc3_alarm_days = __builtin_bswap16(rtc_save.alarm_days);
gb->huc3_alarm_enabled = rtc_save.alarm_enabled;
#else
gb->last_rtc_second = rtc_save.last_rtc_second;
gb->huc3_minutes = rtc_save.minutes;
gb->huc3_days = rtc_save.days;
gb->huc3_alarm_minutes = rtc_save.alarm_minutes;
gb->huc3_alarm_days = rtc_save.alarm_days;
gb->huc3_alarm_enabled = rtc_save.alarm_enabled;
#endif
if (gb->last_rtc_second > time(NULL)) {
/* We must reset RTC here, or it will not advance. */
goto reset_rtc;
}
return;
}
GB_rtc_save_t rtc_save;
memcpy(&rtc_save, buffer + gb->mbc_ram_size, MIN(sizeof(rtc_save), size));
switch (size - gb->mbc_ram_size) {
case sizeof(rtc_save.sameboy_legacy):
memcpy(&gb->rtc_real, &rtc_save.sameboy_legacy.rtc_real, sizeof(gb->rtc_real));
memcpy(&gb->rtc_latched, &rtc_save.sameboy_legacy.rtc_real, sizeof(gb->rtc_real));
gb->last_rtc_second = rtc_save.sameboy_legacy.last_rtc_second;
break;
case sizeof(rtc_save.vba32):
gb->rtc_real.seconds = rtc_save.vba32.rtc_real.seconds;
gb->rtc_real.minutes = rtc_save.vba32.rtc_real.minutes;
gb->rtc_real.hours = rtc_save.vba32.rtc_real.hours;
gb->rtc_real.days = rtc_save.vba32.rtc_real.days;
gb->rtc_real.high = rtc_save.vba32.rtc_real.high;
gb->rtc_latched.seconds = rtc_save.vba32.rtc_latched.seconds;
gb->rtc_latched.minutes = rtc_save.vba32.rtc_latched.minutes;
gb->rtc_latched.hours = rtc_save.vba32.rtc_latched.hours;
gb->rtc_latched.days = rtc_save.vba32.rtc_latched.days;
gb->rtc_latched.high = rtc_save.vba32.rtc_latched.high;
#ifdef GB_BIG_ENDIAN
gb->last_rtc_second = __builtin_bswap32(rtc_save.vba32.last_rtc_second);
#else
gb->last_rtc_second = rtc_save.vba32.last_rtc_second;
#endif
break;
case sizeof(rtc_save.vba64):
gb->rtc_real.seconds = rtc_save.vba64.rtc_real.seconds;
gb->rtc_real.minutes = rtc_save.vba64.rtc_real.minutes;
gb->rtc_real.hours = rtc_save.vba64.rtc_real.hours;
gb->rtc_real.days = rtc_save.vba64.rtc_real.days;
gb->rtc_real.high = rtc_save.vba64.rtc_real.high;
gb->rtc_latched.seconds = rtc_save.vba64.rtc_latched.seconds;
gb->rtc_latched.minutes = rtc_save.vba64.rtc_latched.minutes;
gb->rtc_latched.hours = rtc_save.vba64.rtc_latched.hours;
gb->rtc_latched.days = rtc_save.vba64.rtc_latched.days;
gb->rtc_latched.high = rtc_save.vba64.rtc_latched.high;
#ifdef GB_BIG_ENDIAN
gb->last_rtc_second = __builtin_bswap64(rtc_save.vba64.last_rtc_second);
#else
gb->last_rtc_second = rtc_save.vba64.last_rtc_second;
#endif
break;
default:
goto reset_rtc;
}
if (gb->last_rtc_second > time(NULL)) {
/* We must reset RTC here, or it will not advance. */
goto reset_rtc;
}
if (gb->last_rtc_second < 852076800) { /* 1/1/97. There weren't any RTC games that time,
so if the value we read is lower it means it wasn't
really RTC data. */
goto reset_rtc;
}
goto exit;
reset_rtc:
gb->last_rtc_second = time(NULL);
gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */
gb->huc3_days = 0xFFFF;
gb->huc3_minutes = 0xFFF;
gb->huc3_alarm_enabled = false;
exit:
return;
}
/* Loading will silently stop if the format is incomplete */
void GB_load_battery(GB_gameboy_t *gb, const char *path)
{
FILE *f = fopen(path, "rb");
if (!f) {
return;
}
if (fread(gb->mbc_ram, 1, gb->mbc_ram_size, f) != gb->mbc_ram_size) {
goto reset_rtc;
}
if (gb->cartridge_type->mbc_type == GB_HUC3) {
GB_huc3_rtc_time_t rtc_save;
if (fread(&rtc_save, sizeof(rtc_save), 1, f) != 1) {
goto reset_rtc;
}
#ifdef GB_BIG_ENDIAN
gb->last_rtc_second = __builtin_bswap64(rtc_save.last_rtc_second);
gb->huc3_minutes = __builtin_bswap16(rtc_save.minutes);
gb->huc3_days = __builtin_bswap16(rtc_save.days);
gb->huc3_alarm_minutes = __builtin_bswap16(rtc_save.alarm_minutes);
gb->huc3_alarm_days = __builtin_bswap16(rtc_save.alarm_days);
gb->huc3_alarm_enabled = rtc_save.alarm_enabled;
#else
gb->last_rtc_second = rtc_save.last_rtc_second;
gb->huc3_minutes = rtc_save.minutes;
gb->huc3_days = rtc_save.days;
gb->huc3_alarm_minutes = rtc_save.alarm_minutes;
gb->huc3_alarm_days = rtc_save.alarm_days;
gb->huc3_alarm_enabled = rtc_save.alarm_enabled;
#endif
if (gb->last_rtc_second > time(NULL)) {
/* We must reset RTC here, or it will not advance. */
goto reset_rtc;
}
return;
}
GB_rtc_save_t rtc_save;
switch (fread(&rtc_save, 1, sizeof(rtc_save), f)) {
case sizeof(rtc_save.sameboy_legacy):
memcpy(&gb->rtc_real, &rtc_save.sameboy_legacy.rtc_real, sizeof(gb->rtc_real));
memcpy(&gb->rtc_latched, &rtc_save.sameboy_legacy.rtc_real, sizeof(gb->rtc_real));
gb->last_rtc_second = rtc_save.sameboy_legacy.last_rtc_second;
break;
case sizeof(rtc_save.vba32):
gb->rtc_real.seconds = rtc_save.vba32.rtc_real.seconds;
gb->rtc_real.minutes = rtc_save.vba32.rtc_real.minutes;
gb->rtc_real.hours = rtc_save.vba32.rtc_real.hours;
gb->rtc_real.days = rtc_save.vba32.rtc_real.days;
gb->rtc_real.high = rtc_save.vba32.rtc_real.high;
gb->rtc_latched.seconds = rtc_save.vba32.rtc_latched.seconds;
gb->rtc_latched.minutes = rtc_save.vba32.rtc_latched.minutes;
gb->rtc_latched.hours = rtc_save.vba32.rtc_latched.hours;
gb->rtc_latched.days = rtc_save.vba32.rtc_latched.days;
gb->rtc_latched.high = rtc_save.vba32.rtc_latched.high;
#ifdef GB_BIG_ENDIAN
gb->last_rtc_second = __builtin_bswap32(rtc_save.vba32.last_rtc_second);
#else
gb->last_rtc_second = rtc_save.vba32.last_rtc_second;
#endif
break;
case sizeof(rtc_save.vba64):
gb->rtc_real.seconds = rtc_save.vba64.rtc_real.seconds;
gb->rtc_real.minutes = rtc_save.vba64.rtc_real.minutes;
gb->rtc_real.hours = rtc_save.vba64.rtc_real.hours;
gb->rtc_real.days = rtc_save.vba64.rtc_real.days;
gb->rtc_real.high = rtc_save.vba64.rtc_real.high;
gb->rtc_latched.seconds = rtc_save.vba64.rtc_latched.seconds;
gb->rtc_latched.minutes = rtc_save.vba64.rtc_latched.minutes;
gb->rtc_latched.hours = rtc_save.vba64.rtc_latched.hours;
gb->rtc_latched.days = rtc_save.vba64.rtc_latched.days;
gb->rtc_latched.high = rtc_save.vba64.rtc_latched.high;
#ifdef GB_BIG_ENDIAN
gb->last_rtc_second = __builtin_bswap64(rtc_save.vba64.last_rtc_second);
#else
gb->last_rtc_second = rtc_save.vba64.last_rtc_second;
#endif
break;
default:
goto reset_rtc;
}
if (gb->last_rtc_second > time(NULL)) {
/* We must reset RTC here, or it will not advance. */
goto reset_rtc;
}
if (gb->last_rtc_second < 852076800) { /* 1/1/97. There weren't any RTC games that time,
so if the value we read is lower it means it wasn't
really RTC data. */
goto reset_rtc;
}
goto exit;
reset_rtc:
gb->last_rtc_second = time(NULL);
gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */
gb->huc3_days = 0xFFFF;
gb->huc3_minutes = 0xFFF;
gb->huc3_alarm_enabled = false;
exit:
fclose(f);
return;
}
@ -964,7 +596,6 @@ uint8_t GB_run(GB_gameboy_t *gb)
gb->cycles_since_run = 0;
GB_cpu_run(gb);
if (gb->vblank_just_occured) {
GB_rtc_run(gb);
GB_debugger_handle_async_commands(gb);
GB_rewind_push(gb);
}
@ -1361,7 +992,6 @@ static void request_boot_rom(GB_gameboy_t *gb)
void GB_reset(GB_gameboy_t *gb)
{
uint32_t mbc_ram_size = gb->mbc_ram_size;
GB_model_t model = gb->model;
memset(gb, 0, (size_t)GB_GET_SECTION((GB_gameboy_t *) 0, unsaved));
gb->model = model;
@ -1371,7 +1001,6 @@ void GB_reset(GB_gameboy_t *gb)
gb->last_rtc_second = time(NULL);
gb->cgb_ram_bank = 1;
gb->io_registers[GB_IO_JOYP] = 0xCF;
gb->mbc_ram_size = mbc_ram_size;
if (GB_is_cgb(gb)) {
gb->ram_size = 0x1000 * 8;
gb->vram_size = 0x2000 * 2;
@ -1474,9 +1103,9 @@ void *GB_get_direct_access(GB_gameboy_t *gb, GB_direct_access_t access, size_t *
*bank = gb->cgb_ram_bank;
return gb->ram;
case GB_DIRECT_ACCESS_CART_RAM:
*size = gb->mbc_ram_size;
*bank = gb->mbc_ram_bank;
return gb->mbc_ram;
*size = 0;
*bank = 0;
return NULL;
case GB_DIRECT_ACCESS_VRAM:
*size = gb->vram_size;
*bank = gb->cgb_vram_bank;
@ -1609,11 +1238,5 @@ void GB_set_boot_rom_load_callback(GB_gameboy_t *gb, GB_boot_rom_load_callback_t
unsigned GB_time_to_alarm(GB_gameboy_t *gb)
{
if (gb->cartridge_type->mbc_type != GB_HUC3) return 0;
if (!gb->huc3_alarm_enabled) return 0;
if (!(gb->huc3_alarm_days & 0x2000)) return 0;
unsigned current_time = (gb->huc3_days & 0x1FFF) * 24 * 60 * 60 + gb->huc3_minutes * 60 + (time(NULL) % 60);
unsigned alarm_time = (gb->huc3_alarm_days & 0x1FFF) * 24 * 60 * 60 + gb->huc3_alarm_minutes * 60;
if (current_time > alarm_time) return 0;
return alarm_time - current_time;
return 0;
}

113
Core/gb.h
View File

@ -13,7 +13,6 @@
#include "debugger.h"
#include "display.h"
#include "joypad.h"
#include "mbc.h"
#include "memory.h"
#include "printer.h"
#include "timing.h"
@ -128,66 +127,52 @@ typedef enum {
#define GB_MAX_IR_QUEUE 256
enum {
/* Joypad and Serial */
GB_IO_JOYP = 0x00, // Joypad (R/W)
GB_IO_SB = 0x01, // Serial transfer data (R/W)
GB_IO_SC = 0x02, // Serial Transfer Control (R/W)
/* Missing */
GB_IO_JOYP = 0x00,
GB_IO_SB = 0x01,
GB_IO_SC = 0x02,
GB_IO_DIV = 0x04,
GB_IO_TIMA = 0x05,
GB_IO_TMA = 0x06,
GB_IO_TAC = 0x07,
GB_IO_IF = 0x0F,
GB_IO_LCDC = 0x10,
GB_IO_STAT = 0x11,
GB_IO_SCY = 0x12,
GB_IO_SCX = 0x13,
GB_IO_OBP0 = 0x14,
GB_IO_OBP1 = 0x15,
GB_IO_WY = 0x16,
GB_IO_WX = 0x17,
GB_IO_LY = 0x18,
GB_IO_LYC = 0x19,
GB_IO_DMA = 0x1A,
GB_IO_BGP = 0x1B,
GB_IO_NR10 = 0x20,
GB_IO_NR12 = 0x21,
GB_IO_NR11 = 0x22,
GB_IO_NR13 = 0x23,
GB_IO_NR14 = 0x24,
GB_IO_NR21 = 0x25,
GB_IO_NR22 = 0x27,
GB_IO_NR23 = 0x28,
GB_IO_NR24 = 0x29,
GB_IO_NR30 = 0x2A,
GB_IO_NR31 = 0x2B,
GB_IO_NR32 = 0x2C,
GB_IO_NR33 = 0x2D,
GB_IO_NR34 = 0x2E,
GB_IO_WAV_START = 0x30,
GB_IO_WAV_END = 0x3F,
GB_IO_NR41 = 0x40,
GB_IO_NR43 = 0x41,
GB_IO_NR42 = 0x42,
GB_IO_NR44 = 0x43,
GB_IO_NR50 = 0x44,
GB_IO_NR52 = 0x45,
GB_IO_NR51 = 0x46,
GB_IO_IE = 0xFF,
/* Timers */
GB_IO_DIV = 0x04, // Divider Register (R/W)
GB_IO_TIMA = 0x05, // Timer counter (R/W)
GB_IO_TMA = 0x06, // Timer Modulo (R/W)
GB_IO_TAC = 0x07, // Timer Control (R/W)
/* Missing */
GB_IO_IF = 0x0f, // Interrupt Flag (R/W)
/* Sound */
GB_IO_NR10 = 0x10, // Channel 1 Sweep register (R/W)
GB_IO_NR11 = 0x11, // Channel 1 Sound length/Wave pattern duty (R/W)
GB_IO_NR12 = 0x12, // Channel 1 Volume Envelope (R/W)
GB_IO_NR13 = 0x13, // Channel 1 Frequency lo (Write Only)
GB_IO_NR14 = 0x14, // Channel 1 Frequency hi (R/W)
/* NR20 does not exist */
GB_IO_NR21 = 0x16, // Channel 2 Sound Length/Wave Pattern Duty (R/W)
GB_IO_NR22 = 0x17, // Channel 2 Volume Envelope (R/W)
GB_IO_NR23 = 0x18, // Channel 2 Frequency lo data (W)
GB_IO_NR24 = 0x19, // Channel 2 Frequency hi data (R/W)
GB_IO_NR30 = 0x1a, // Channel 3 Sound on/off (R/W)
GB_IO_NR31 = 0x1b, // Channel 3 Sound Length
GB_IO_NR32 = 0x1c, // Channel 3 Select output level (R/W)
GB_IO_NR33 = 0x1d, // Channel 3 Frequency's lower data (W)
GB_IO_NR34 = 0x1e, // Channel 3 Frequency's higher data (R/W)
/* NR40 does not exist */
GB_IO_NR41 = 0x20, // Channel 4 Sound Length (R/W)
GB_IO_NR42 = 0x21, // Channel 4 Volume Envelope (R/W)
GB_IO_NR43 = 0x22, // Channel 4 Polynomial Counter (R/W)
GB_IO_NR44 = 0x23, // Channel 4 Counter/consecutive, Inital (R/W)
GB_IO_NR50 = 0x24, // Channel control / ON-OFF / Volume (R/W)
GB_IO_NR51 = 0x25, // Selection of Sound output terminal (R/W)
GB_IO_NR52 = 0x26, // Sound on/off
/* Missing */
GB_IO_WAV_START = 0x30, // Wave pattern start
GB_IO_WAV_END = 0x3f, // Wave pattern end
/* Graphics */
GB_IO_LCDC = 0x40, // LCD Control (R/W)
GB_IO_STAT = 0x41, // LCDC Status (R/W)
GB_IO_SCY = 0x42, // Scroll Y (R/W)
GB_IO_SCX = 0x43, // Scroll X (R/W)
GB_IO_LY = 0x44, // LCDC Y-Coordinate (R)
GB_IO_LYC = 0x45, // LY Compare (R/W)
GB_IO_DMA = 0x46, // DMA Transfer and Start Address (W)
GB_IO_BGP = 0x47, // BG Palette Data (R/W) - Non CGB Mode Only
GB_IO_OBP0 = 0x48, // Object Palette 0 Data (R/W) - Non CGB Mode Only
GB_IO_OBP1 = 0x49, // Object Palette 1 Data (R/W) - Non CGB Mode Only
GB_IO_WY = 0x4a, // Window Y Position (R/W)
GB_IO_WX = 0x4b, // Window X Position minus 7 (R/W)
// Has some undocumented compatibility flags written at boot.
// Unfortunately it is not readable or writable after boot has finished, so research of this
// register is quite limited. The value written to this register, however, can be controlled
@ -396,9 +381,6 @@ struct GB_gameboy_internal_s {
/* MBC */
GB_SECTION(mbc,
uint16_t mbc_rom_bank;
uint8_t mbc_ram_bank;
uint32_t mbc_ram_size;
bool mbc_ram_enable;
union {
struct {
uint8_t bank_low:5;
@ -556,19 +538,12 @@ struct GB_gameboy_internal_s {
/* ROM */
uint8_t *rom;
uint32_t rom_size;
const GB_cartridge_t *cartridge_type;
enum {
GB_STANDARD_MBC1_WIRING,
GB_MBC1M_WIRING,
} mbc1_wiring;
bool is_mbc30;
unsigned pending_cycles;
/* Various RAMs */
uint8_t *ram;
uint8_t *vram;
uint8_t *mbc_ram;
/* I/O */
uint32_t *screen;

View File

@ -1,167 +0,0 @@
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "gb.h"
const GB_cartridge_t GB_cart_defs[256] = {
// From http://gbdev.gg8.se/wiki/articles/The_Cartridge_Header#0147_-_Cartridge_Type
/* MBC SUBTYPE RAM BAT. RTC RUMB. */
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 00h ROM ONLY
{ GB_MBC1 , GB_STANDARD_MBC, false, false, false, false}, // 01h MBC1
{ GB_MBC1 , GB_STANDARD_MBC, true , false, false, false}, // 02h MBC1+RAM
{ GB_MBC1 , GB_STANDARD_MBC, true , true , false, false}, // 03h MBC1+RAM+BATTERY
[5] =
{ GB_MBC2 , GB_STANDARD_MBC, true , false, false, false}, // 05h MBC2
{ GB_MBC2 , GB_STANDARD_MBC, true , true , false, false}, // 06h MBC2+BATTERY
[8] =
{ GB_NO_MBC, GB_STANDARD_MBC, true , false, false, false}, // 08h ROM+RAM
{ GB_NO_MBC, GB_STANDARD_MBC, true , true , false, false}, // 09h ROM+RAM+BATTERY
[0xB] =
/* Todo: Not supported yet */
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 0Bh MMM01
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 0Ch MMM01+RAM
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 0Dh MMM01+RAM+BATTERY
[0xF] =
{ GB_MBC3 , GB_STANDARD_MBC, false, true, true , false}, // 0Fh MBC3+TIMER+BATTERY
{ GB_MBC3 , GB_STANDARD_MBC, true , true, true , false}, // 10h MBC3+TIMER+RAM+BATTERY
{ GB_MBC3 , GB_STANDARD_MBC, false, false, false, false}, // 11h MBC3
{ GB_MBC3 , GB_STANDARD_MBC, true , false, false, false}, // 12h MBC3+RAM
{ GB_MBC3 , GB_STANDARD_MBC, true , true , false, false}, // 13h MBC3+RAM+BATTERY
[0x19] =
{ GB_MBC5 , GB_STANDARD_MBC, false, false, false, false}, // 19h MBC5
{ GB_MBC5 , GB_STANDARD_MBC, true , false, false, false}, // 1Ah MBC5+RAM
{ GB_MBC5 , GB_STANDARD_MBC, true , true , false, false}, // 1Bh MBC5+RAM+BATTERY
{ GB_MBC5 , GB_STANDARD_MBC, false, false, false, true }, // 1Ch MBC5+RUMBLE
{ GB_MBC5 , GB_STANDARD_MBC, true , false, false, true }, // 1Dh MBC5+RUMBLE+RAM
{ GB_MBC5 , GB_STANDARD_MBC, true , true , false, true }, // 1Eh MBC5+RUMBLE+RAM+BATTERY
[0xFC] =
{ GB_MBC5 , GB_CAMERA , true , true , false, false}, // FCh POCKET CAMERA
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // FDh BANDAI TAMA5 (Todo: Not supported)
{ GB_HUC3 , GB_STANDARD_MBC, true , true , true, false}, // FEh HuC3
{ GB_HUC1 , GB_STANDARD_MBC, true , true , false, false}, // FFh HuC1+RAM+BATTERY
};
void GB_update_mbc_mappings(GB_gameboy_t *gb)
{
switch (gb->cartridge_type->mbc_type) {
case GB_NO_MBC: return;
case GB_MBC1:
switch (gb->mbc1_wiring) {
case GB_STANDARD_MBC1_WIRING:
gb->mbc_rom_bank = gb->mbc1.bank_low | (gb->mbc1.bank_high << 5);
if (gb->mbc1.mode == 0) {
gb->mbc_ram_bank = 0;
gb->mbc_rom0_bank = 0;
}
else {
gb->mbc_ram_bank = gb->mbc1.bank_high;
gb->mbc_rom0_bank = gb->mbc1.bank_high << 5;
}
if ((gb->mbc_rom_bank & 0x1F) == 0) {
gb->mbc_rom_bank++;
}
break;
case GB_MBC1M_WIRING:
gb->mbc_rom_bank = (gb->mbc1.bank_low & 0xF) | (gb->mbc1.bank_high << 4);
if (gb->mbc1.mode == 0) {
gb->mbc_ram_bank = 0;
gb->mbc_rom0_bank = 0;
}
else {
gb->mbc_rom0_bank = gb->mbc1.bank_high << 4;
gb->mbc_ram_bank = 0;
}
if ((gb->mbc1.bank_low & 0x1F) == 0) {
gb->mbc_rom_bank++;
}
break;
}
break;
case GB_MBC2:
gb->mbc_rom_bank = gb->mbc2.rom_bank;
if ((gb->mbc_rom_bank & 0xF) == 0) {
gb->mbc_rom_bank = 1;
}
break;
case GB_MBC3:
gb->mbc_rom_bank = gb->mbc3.rom_bank;
gb->mbc_ram_bank = gb->mbc3.ram_bank;
if (!gb->is_mbc30) {
gb->mbc_rom_bank &= 0x7F;
}
if (gb->mbc_rom_bank == 0) {
gb->mbc_rom_bank = 1;
}
break;
case GB_MBC5:
gb->mbc_rom_bank = gb->mbc5.rom_bank_low | (gb->mbc5.rom_bank_high << 8);
gb->mbc_ram_bank = gb->mbc5.ram_bank;
break;
case GB_HUC1:
if (gb->huc1.mode == 0) {
gb->mbc_rom_bank = gb->huc1.bank_low | (gb->mbc1.bank_high << 6);
gb->mbc_ram_bank = 0;
}
else {
gb->mbc_rom_bank = gb->huc1.bank_low;
gb->mbc_ram_bank = gb->huc1.bank_high;
}
break;
case GB_HUC3:
gb->mbc_rom_bank = gb->huc3.rom_bank;
gb->mbc_ram_bank = gb->huc3.ram_bank;
break;
}
}
void GB_configure_cart(GB_gameboy_t *gb)
{
gb->cartridge_type = &GB_cart_defs[gb->rom[0x147]];
if (gb->rom[0x147] == 0 && gb->rom_size > 0x8000) {
GB_log(gb, "ROM header reports no MBC, but file size is over 32Kb. Assuming cartridge uses MBC3.\n");
gb->cartridge_type = &GB_cart_defs[0x11];
}
else if (gb->rom[0x147] != 0 && memcmp(gb->cartridge_type, &GB_cart_defs[0], sizeof(GB_cart_defs[0])) == 0) {
GB_log(gb, "Cartridge type %02x is not yet supported.\n", gb->rom[0x147]);
}
if (gb->cartridge_type->has_ram) {
if (gb->cartridge_type->mbc_type == GB_MBC2) {
gb->mbc_ram_size = 0x200;
}
else {
static const unsigned ram_sizes[256] = {0, 0x800, 0x2000, 0x8000, 0x20000, 0x10000};
gb->mbc_ram_size = ram_sizes[gb->rom[0x149]];
}
if (gb->mbc_ram_size) {
gb->mbc_ram = malloc(gb->mbc_ram_size);
}
/* Todo: Some games assume unintialized MBC RAM is 0xFF. It this true for all cartridges 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).
See http://forums.nesdev.com/viewtopic.php?f=20&t=14099 */
/* Attempt to "guess" wiring */
if (gb->cartridge_type->mbc_type == GB_MBC1) {
if (gb->rom_size >= 0x44000 && memcmp(gb->rom + 0x104, gb->rom + 0x40104, 0x30) == 0) {
gb->mbc1_wiring = GB_MBC1M_WIRING;
}
}
/* Detect MBC30 */
if (gb->cartridge_type->mbc_type == GB_MBC3) {
if (gb->rom_size > 0x200000 || gb->mbc_ram_size > 0x8000) {
gb->is_mbc30 = true;
}
}
/* Set MBC5's bank to 1 correctly */
if (gb->cartridge_type->mbc_type == GB_MBC5) {
gb->mbc5.rom_bank_low = 1;
}
}

View File

@ -1,32 +0,0 @@
#ifndef MBC_h
#define MBC_h
#include "gb_struct_def.h"
#include <stdbool.h>
typedef struct {
enum {
GB_NO_MBC,
GB_MBC1,
GB_MBC2,
GB_MBC3,
GB_MBC5,
GB_HUC1,
GB_HUC3,
} mbc_type;
enum {
GB_STANDARD_MBC,
GB_CAMERA,
} mbc_subtype;
bool has_ram;
bool has_battery;
bool has_rtc;
bool has_rumble;
} GB_cartridge_t;
#ifdef GB_INTERNAL
extern const GB_cartridge_t GB_cart_defs[256];
void GB_update_mbc_mappings(GB_gameboy_t *gb);
void GB_configure_cart(GB_gameboy_t *gb);
#endif
#endif /* MBC_h */

View File

@ -56,9 +56,6 @@ void GB_trigger_oam_bug(GB_gameboy_t *gb, uint16_t address)
gb->oam[gb->accessed_oam_row + 1] = bitwise_glitch(gb->oam[gb->accessed_oam_row + 1],
gb->oam[gb->accessed_oam_row - 7],
gb->oam[gb->accessed_oam_row - 3]);
for (unsigned i = 2; i < 8; i++) {
gb->oam[gb->accessed_oam_row + i] = gb->oam[gb->accessed_oam_row - 8 + i];
}
}
}
}
@ -120,14 +117,6 @@ static bool effective_ir_input(GB_gameboy_t *gb)
static uint8_t read_rom(GB_gameboy_t *gb, uint16_t addr)
{
if (addr < 0x100 && !gb->boot_rom_finished) {
return gb->boot_rom[addr];
}
if (addr >= 0x200 && addr < 0x900 && GB_is_cgb(gb) && !gb->boot_rom_finished) {
return gb->boot_rom[addr];
}
if (!gb->rom_size) {
return 0xFF;
}
@ -163,65 +152,7 @@ static uint8_t read_vram(GB_gameboy_t *gb, uint16_t addr)
static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr)
{
if (gb->cartridge_type->mbc_type == GB_HUC3) {
switch (gb->huc3_mode) {
case 0xC: // RTC read
if (gb->huc3_access_flags == 0x2) {
return 1;
}
return gb->huc3_read;
case 0xD: // RTC status
return 1;
case 0xE: // IR mode
return effective_ir_input(gb); // TODO: What are the other bits?
default:
GB_log(gb, "Unsupported HuC-3 mode %x read: %04x\n", gb->huc3_mode, addr);
return 1; // TODO: What happens in this case?
case 0: // TODO: R/O RAM? (or is it disabled?)
case 0xA: // RAM
break;
}
}
if ((!gb->mbc_ram_enable) &&
gb->cartridge_type->mbc_subtype != GB_CAMERA &&
gb->cartridge_type->mbc_type != GB_HUC1 &&
gb->cartridge_type->mbc_type != GB_HUC3) {
return 0xFF;
}
if (gb->cartridge_type->mbc_type == GB_HUC1 && gb->huc1.ir_mode) {
return 0xc0 | effective_ir_input(gb);
}
if (gb->cartridge_type->has_rtc && gb->cartridge_type->mbc_type != GB_HUC3 &&
gb->mbc3_rtc_mapped && gb->mbc_ram_bank <= 4) {
/* RTC read */
gb->rtc_latched.high |= ~0xC1; /* Not all bytes in RTC high are used. */
return gb->rtc_latched.data[gb->mbc_ram_bank];
}
if (gb->camera_registers_mapped) {
return GB_camera_read_register(gb, addr);
}
if (!gb->mbc_ram || !gb->mbc_ram_size) {
return 0xFF;
}
if (gb->cartridge_type->mbc_subtype == GB_CAMERA && gb->mbc_ram_bank == 0 && addr >= 0xa100 && addr < 0xaf00) {
return GB_camera_read_image(gb, addr - 0xa100);
}
uint8_t effective_bank = gb->mbc_ram_bank;
if (gb->cartridge_type->mbc_type == GB_MBC3 && !gb->is_mbc30) {
effective_bank &= 0x3;
}
uint8_t ret = gb->mbc_ram[((addr & 0x1FFF) + effective_bank * 0x2000) & (gb->mbc_ram_size - 1)];
if (gb->cartridge_type->mbc_type == GB_MBC2) {
ret |= 0xF0;
}
return ret;
return 0xFF;
}
static uint8_t read_ram(GB_gameboy_t *gb, uint16_t addr)
@ -442,7 +373,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
case GB_IO_UNKNOWN5:
return GB_is_cgb(gb)? gb->io_registers[addr & 0xFF] | 0x8F : 0xFF;
default:
if ((addr & 0xFF) >= GB_IO_NR10 && (addr & 0xFF) <= GB_IO_WAV_END) {
if ((addr & 0xFF) >= GB_IO_NR10 && (addr & 0xFF) <= GB_IO_NR51) {
return GB_apu_read(gb, addr & 0xFF);
}
return 0xFF;
@ -493,75 +424,9 @@ uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr)
static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
{
switch (gb->cartridge_type->mbc_type) {
case GB_NO_MBC: return;
case GB_MBC1:
switch (addr & 0xF000) {
case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break;
case 0x2000: case 0x3000: gb->mbc1.bank_low = value; break;
case 0x4000: case 0x5000: gb->mbc1.bank_high = value; break;
case 0x6000: case 0x7000: gb->mbc1.mode = value; break;
}
break;
case GB_MBC2:
switch (addr & 0x4100) {
case 0x0000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break;
case 0x0100: gb->mbc2.rom_bank = value; break;
}
break;
case GB_MBC3:
switch (addr & 0xF000) {
case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break;
case 0x2000: case 0x3000: gb->mbc3.rom_bank = value; break;
case 0x4000: case 0x5000:
gb->mbc3.ram_bank = value;
gb->mbc3_rtc_mapped = value & 8;
break;
case 0x6000: case 0x7000:
if (!gb->rtc_latch && (value & 1)) { /* Todo: verify condition is correct */
memcpy(&gb->rtc_latched, &gb->rtc_real, sizeof(gb->rtc_real));
}
gb->rtc_latch = value & 1;
break;
}
break;
case GB_MBC5:
switch (addr & 0xF000) {
case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break;
case 0x2000: gb->mbc5.rom_bank_low = value; break;
case 0x3000: gb->mbc5.rom_bank_high = value; break;
case 0x4000: case 0x5000:
if (gb->cartridge_type->has_rumble) {
if (!!(value & 8) != gb->rumble_state) {
gb->rumble_state = !gb->rumble_state;
}
value &= 7;
}
gb->mbc5.ram_bank = value;
gb->camera_registers_mapped = (value & 0x10) && gb->cartridge_type->mbc_subtype == GB_CAMERA;
break;
}
break;
case GB_HUC1:
switch (addr & 0xF000) {
case 0x0000: case 0x1000: gb->huc1.ir_mode = (value & 0xF) == 0xE; break;
case 0x2000: case 0x3000: gb->huc1.bank_low = value; break;
case 0x4000: case 0x5000: gb->huc1.bank_high = value; break;
case 0x6000: case 0x7000: gb->huc1.mode = value; break;
}
break;
case GB_HUC3:
switch (addr & 0xF000) {
case 0x0000: case 0x1000:
gb->huc3_mode = value & 0xF;
gb->mbc_ram_enable = gb->huc3_mode == 0xA;
break;
case 0x2000: case 0x3000: gb->huc3.rom_bank = value; break;
case 0x4000: case 0x5000: gb->huc3.ram_bank = value; break;
}
break;
if (addr == 0x0001) {
gb->mbc_rom_bank = value;
}
GB_update_mbc_mappings(gb);
}
static void write_vram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
@ -586,135 +451,12 @@ static void write_vram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
gb->vram[(addr & 0x1FFF) + (uint16_t) gb->cgb_vram_bank * 0x2000] = value;
}
static bool huc3_write(GB_gameboy_t *gb, uint8_t value)
{
switch (gb->huc3_mode) {
case 0xB: // RTC Write
switch (value >> 4) {
case 1:
if (gb->huc3_access_index < 3) {
gb->huc3_read = (gb->huc3_minutes >> (gb->huc3_access_index * 4)) & 0xF;
}
else if (gb->huc3_access_index < 7) {
gb->huc3_read = (gb->huc3_days >> ((gb->huc3_access_index - 3) * 4)) & 0xF;
}
else {
// GB_log(gb, "Attempting to read from unsupported HuC-3 register: %03x\n", gb->huc3_access_index);
}
gb->huc3_access_index++;
break;
case 2:
case 3:
if (gb->huc3_access_index < 3) {
gb->huc3_minutes &= ~(0xF << (gb->huc3_access_index * 4));
gb->huc3_minutes |= ((value & 0xF) << (gb->huc3_access_index * 4));
}
else if (gb->huc3_access_index < 7) {
gb->huc3_days &= ~(0xF << ((gb->huc3_access_index - 3) * 4));
gb->huc3_days |= ((value & 0xF) << ((gb->huc3_access_index - 3) * 4));
}
else if (gb->huc3_access_index >= 0x58 && gb->huc3_access_index <= 0x5a) {
gb->huc3_alarm_minutes &= ~(0xF << ((gb->huc3_access_index - 0x58) * 4));
gb->huc3_alarm_minutes |= ((value & 0xF) << ((gb->huc3_access_index - 0x58) * 4));
}
else if (gb->huc3_access_index >= 0x5b && gb->huc3_access_index <= 0x5e) {
gb->huc3_alarm_days &= ~(0xF << ((gb->huc3_access_index - 0x5b) * 4));
gb->huc3_alarm_days |= ((value & 0xF) << ((gb->huc3_access_index - 0x5b) * 4));
}
else if (gb->huc3_access_index == 0x5f) {
gb->huc3_alarm_enabled = value & 1;
}
else {
// GB_log(gb, "Attempting to write %x to unsupported HuC-3 register: %03x\n", value & 0xF, gb->huc3_access_index);
}
if ((value >> 4) == 3) {
gb->huc3_access_index++;
}
break;
case 4:
gb->huc3_access_index &= 0xF0;
gb->huc3_access_index |= value & 0xF;
break;
case 5:
gb->huc3_access_index &= 0x0F;
gb->huc3_access_index |= (value & 0xF) << 4;
break;
case 6:
gb->huc3_access_flags = (value & 0xF);
break;
default:
break;
}
return true;
case 0xD: // RTC status
// Not sure what writes here mean, they're always 0xFE
return true;
case 0xE: { // IR mode
bool old_input = effective_ir_input(gb);
gb->cart_ir = value & 1;
bool new_input = effective_ir_input(gb);
if (new_input != old_input) {
if (gb->infrared_callback) {
gb->infrared_callback(gb, new_input, gb->cycles_since_ir_change);
}
gb->cycles_since_ir_change = 0;
}
return true;
}
case 0xC:
return true;
default:
return false;
case 0: // Disabled
case 0xA: // RAM
return false;
}
}
static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
{
if (gb->cartridge_type->mbc_type == GB_HUC3) {
if (huc3_write(gb, value)) return;
if (addr == 0xb000) {
gb->mbc_rom0_bank = value * 2;
gb->mbc_rom_bank = value * 2 + 1;
}
if (gb->camera_registers_mapped) {
GB_camera_write_register(gb, addr, value);
return;
}
if ((!gb->mbc_ram_enable)
&& gb->cartridge_type->mbc_type != GB_HUC1) return;
if (gb->cartridge_type->mbc_type == GB_HUC1 && gb->huc1.ir_mode) {
bool old_input = effective_ir_input(gb);
gb->cart_ir = value & 1;
bool new_input = effective_ir_input(gb);
if (new_input != old_input) {
if (gb->infrared_callback) {
gb->infrared_callback(gb, new_input, gb->cycles_since_ir_change);
}
gb->cycles_since_ir_change = 0;
}
return;
}
if (gb->cartridge_type->has_rtc && gb->mbc3_rtc_mapped && gb->mbc_ram_bank <= 4) {
gb->rtc_latched.data[gb->mbc_ram_bank] = gb->rtc_real.data[gb->mbc_ram_bank] = value;
return;
}
if (!gb->mbc_ram || !gb->mbc_ram_size) {
return;
}
uint8_t effective_bank = gb->mbc_ram_bank;
if (gb->cartridge_type->mbc_type == GB_MBC3 && !gb->is_mbc30) {
effective_bank &= 0x3;
}
gb->mbc_ram[((addr & 0x1FFF) + effective_bank * 0x2000) & (gb->mbc_ram_size - 1)] = value;
}
static void write_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
@ -1121,7 +863,7 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
}
default:
if ((addr & 0xFF) >= GB_IO_NR10 && (addr & 0xFF) <= GB_IO_WAV_END) {
if ((addr & 0xFF) >= GB_IO_NR10 && (addr & 0xFF) <= GB_IO_NR51) {
GB_apu_write(gb, addr & 0xFF, value);
return;
}

View File

@ -15,13 +15,7 @@ void GB_handle_rumble(GB_gameboy_t *gb)
if (gb->rumble_mode == GB_RUMBLE_DISABLED) {
return;
}
if (gb->cartridge_type->has_rumble) {
if (gb->rumble_on_cycles + gb->rumble_off_cycles) {
gb->rumble_callback(gb, gb->rumble_on_cycles / (double)(gb->rumble_on_cycles + gb->rumble_off_cycles));
gb->rumble_on_cycles = gb->rumble_off_cycles = 0;
}
}
else if (gb->rumble_mode == GB_RUMBLE_ALL_GAMES) {
if (gb->rumble_mode == GB_RUMBLE_ALL_GAMES) {
unsigned volume = (gb->io_registers[GB_IO_NR50] & 7) + 1 + ((gb->io_registers[GB_IO_NR50] >> 4) & 7) + 1;
unsigned ch4_volume = volume * (!!(gb->io_registers[GB_IO_NR51] & 8) + !!(gb->io_registers[GB_IO_NR51] & 0x80));
unsigned ch1_volume = volume * (!!(gb->io_registers[GB_IO_NR51] & 1) + !!(gb->io_registers[GB_IO_NR51] & 0x10));

View File

@ -40,10 +40,6 @@ int GB_save_state(GB_gameboy_t *gb, const char *path)
if (!dump_section(f, gb->sgb, sizeof(*gb->sgb))) goto error;
}
if (fwrite(gb->mbc_ram, 1, gb->mbc_ram_size, f) != gb->mbc_ram_size) {
goto error;
}
if (fwrite(gb->ram, 1, gb->ram_size, f) != gb->ram_size) {
goto error;
}
@ -73,7 +69,6 @@ size_t GB_get_save_state_size(GB_gameboy_t *gb)
+ GB_SECTION_SIZE(rtc ) + sizeof(uint32_t)
+ GB_SECTION_SIZE(video ) + sizeof(uint32_t)
+ (GB_is_hle_sgb(gb)? sizeof(*gb->sgb) + sizeof(uint32_t) : 0)
+ gb->mbc_ram_size
+ gb->ram_size
+ gb->vram_size;
}
@ -109,7 +104,6 @@ void GB_save_state_to_buffer(GB_gameboy_t *gb, uint8_t *buffer)
}
buffer_write(gb->mbc_ram, gb->mbc_ram_size, &buffer);
buffer_write(gb->ram, gb->ram_size, &buffer);
buffer_write(gb->vram, gb->vram_size, &buffer);
}
@ -168,11 +162,6 @@ static bool verify_and_update_state_compatibility(GB_gameboy_t *gb, GB_gameboy_t
return false;
}
if (gb->mbc_ram_size < save->mbc_ram_size) {
GB_log(gb, "The save state has non-matching MBC RAM size.\n");
return false;
}
if (gb->vram_size != save->vram_size) {
GB_log(gb, "The save state has non-matching VRAM size. Try changing the emulated model.\n");
return false;
@ -265,13 +254,7 @@ int GB_load_state(GB_gameboy_t *gb, const char *path)
if (GB_is_hle_sgb(gb)) {
if (!read_section(f, gb->sgb, sizeof(*gb->sgb), false)) goto error;
}
memset(gb->mbc_ram + save.mbc_ram_size, 0xFF, gb->mbc_ram_size - save.mbc_ram_size);
if (fread(gb->mbc_ram, 1, save.mbc_ram_size, f) != save.mbc_ram_size) {
fclose(f);
return EIO;
}
if (fread(gb->ram, 1, gb->ram_size, f) != gb->ram_size) {
fclose(f);
return EIO;
@ -385,12 +368,7 @@ int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t le
if (GB_is_hle_sgb(gb)) {
if (!buffer_read_section(&buffer, &length, gb->sgb, sizeof(*gb->sgb), false)) return -1;
}
memset(gb->mbc_ram + save.mbc_ram_size, 0xFF, gb->mbc_ram_size - save.mbc_ram_size);
if (buffer_read(gb->mbc_ram, save.mbc_ram_size, &buffer, &length) != save.mbc_ram_size) {
return -1;
}
if (buffer_read(gb->ram, gb->ram_size, &buffer, &length) != gb->ram_size) {
return -1;
}

View File

@ -276,52 +276,3 @@ void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac)
}
}
}
void GB_rtc_run(GB_gameboy_t *gb)
{
if (gb->cartridge_type->mbc_type == GB_HUC3) {
time_t current_time = time(NULL);
while (gb->last_rtc_second / 60 < current_time / 60) {
gb->last_rtc_second += 60;
gb->huc3_minutes++;
if (gb->huc3_minutes == 60 * 24) {
gb->huc3_days++;
gb->huc3_minutes = 0;
}
}
return;
}
if ((gb->rtc_real.high & 0x40) == 0) { /* is timer running? */
time_t current_time = time(NULL);
while (gb->last_rtc_second + 60 * 60 * 24 < current_time) {
gb->last_rtc_second += 60 * 60 * 24;
if (++gb->rtc_real.days == 0) {
if (gb->rtc_real.high & 1) { /* Bit 8 of days*/
gb->rtc_real.high |= 0x80; /* Overflow bit */
}
gb->rtc_real.high ^= 1;
}
}
while (gb->last_rtc_second < current_time) {
gb->last_rtc_second++;
if (++gb->rtc_real.seconds == 60) {
gb->rtc_real.seconds = 0;
if (++gb->rtc_real.minutes == 60) {
gb->rtc_real.minutes = 0;
if (++gb->rtc_real.hours == 24) {
gb->rtc_real.hours = 0;
if (++gb->rtc_real.days == 0) {
if (gb->rtc_real.high & 1) { /* Bit 8 of days*/
gb->rtc_real.high |= 0x80; /* Overflow bit */
}
gb->rtc_real.high ^= 1;
}
}
}
}
}
}
}

View File

@ -4,7 +4,6 @@
#ifdef GB_INTERNAL
void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles);
void GB_rtc_run(GB_gameboy_t *gb);
void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac);
bool GB_timing_sync_turbo(GB_gameboy_t *gb); /* Returns true if should skip frame */
void GB_timing_sync(GB_gameboy_t *gb);

View File

@ -6,62 +6,42 @@
00:FF06 IO_TMA
00:FF07 IO_TAC
00:FF0F IO_IF
00:FF10 IO_NR10
00:FF11 IO_NR11
00:FF12 IO_NR12
00:FF13 IO_NR13
00:FF14 IO_NR14
00:FF16 IO_NR21
00:FF17 IO_NR22
00:FF18 IO_NR23
00:FF19 IO_NR24
00:FF1A IO_NR30
00:FF1B IO_NR31
00:FF1C IO_NR32
00:FF1D IO_NR33
00:FF1E IO_NR34
00:FF20 IO_NR41
00:FF21 IO_NR42
00:FF22 IO_NR43
00:FF23 IO_NR44
00:FF24 IO_NR50
00:FF25 IO_NR51
00:FF26 IO_NR52
00:FF10 IO_LCDC
00:FF11 IO_STAT
00:FF12 IO_SCY
00:FF13 IO_SCX
00:FF14 IO_OBP0
00:FF15 IO_OBP1
00:FF16 IO_WY
00:FF17 IO_WX
00:FF18 IO_LY
00:FF19 IO_LYC
00:FF1A IO_DMA
00:FF1B IO_BGP
00:FF20 IO_NR10
00:FF21 IO_NR12
00:FF22 IO_NR11
00:FF23 IO_NR13
00:FF24 IO_NR14
00:FF25 IO_NR21
00:FF27 IO_NR22
00:FF28 IO_NR23
00:FF29 IO_NR24
00:FF2A IO_NR30
00:FF2B IO_NR31
00:FF2C IO_NR32
00:FF2D IO_NR33
00:FF2E IO_NR34
00:FF30 IO_WAV_START
00:FF3F IO_WAV_END
00:FF40 IO_LCDC
00:FF41 IO_STAT
00:FF42 IO_SCY
00:FF43 IO_SCX
00:FF44 IO_LY
00:FF45 IO_LYC
00:FF46 IO_DMA
00:FF47 IO_BGP
00:FF48 IO_OBP0
00:FF49 IO_OBP1
00:FF4A IO_WY
00:FF4B IO_WX
00:FF40 IO_NR41
00:FF41 IO_NR43
00:FF42 IO_NR42
00:FF43 IO_NR44
00:FF44 IO_NR50
00:FF45 IO_NR52
00:FF46 IO_NR51
00:FF4C IO_KEY0
00:FF4D IO_KEY1
00:FF4F IO_VBK
00:FF50 IO_BANK
00:FF51 IO_HDMA1
00:FF52 IO_HDMA2
00:FF53 IO_HDMA3
00:FF54 IO_HDMA4
00:FF55 IO_HDMA5
00:FF56 IO_RP
00:FF68 IO_BGPI
00:FF69 IO_BGPD
00:FF6A IO_OBPI
00:FF6B IO_OBPD
00:FF6C IO_OPRI
00:FF70 IO_SVBK
00:FF72 IO_UNKNOWN2
00:FF73 IO_UNKNOWN3
00:FF74 IO_UNKNOWN4
00:FF75 IO_UNKNOWN5
00:FF76 IO_PCM_12
00:FF77 IO_PCM_34
00:FF7F IO_UNKNOWN8
00:FFFF IO_IE