From 2fcf0b8376475767700307fe19ea6ca106d790b1 Mon Sep 17 00:00:00 2001 From: flan Date: Wed, 6 Apr 2022 17:43:10 -0400 Subject: [PATCH] MBC5 support, fixed F-1 Race issue, broke Pinball Deluxe --- README.md | 3 +- gameboy/cart.c | 85 ++++++++++++++++++++++++++++++++++++++-------- gameboy/debugger.c | 10 ++++-- gameboy/lcd.c | 43 ++++++++++++++++++----- gameboy/lcd.h | 15 ++------ gameboy/ops.h | 2 +- 6 files changed, 116 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index c200804..309bc33 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ a game boy emulator written in C ## features - minimal UI (will upgrade eventually) -- support for no mapper, MBC1, MBC2, MBC3 (+RTC) +- support for no mapper, MBC1, MBC2, MBC3 (+RTC), MBC5 ## how to use @@ -45,7 +45,6 @@ i use a scanline renderer, no fifo (yet) ## todo -- add support for MBC5 mapper - add audio - refactor - add to UI diff --git a/gameboy/cart.c b/gameboy/cart.c index 32c4ff9..517be22 100644 --- a/gameboy/cart.c +++ b/gameboy/cart.c @@ -85,6 +85,11 @@ int cart_get_mbc_type(struct cart* cart) cart->MBC = 3; puts("mapper: mbc3"); break; + case 0x19: case 0x1a: case 0x1b: + case 0x1c: case 0x1d: case 0x1e: + cart->MBC = 5; + puts("mapper: mbc5"); + break; default: printf("unsupported, type: %x\n", cart->ROM[0x147]); cart->MBC = 0xff; @@ -114,25 +119,31 @@ void nombc_write(struct cart* cart, u16 addr, u8 val) u8 mbc1_read(struct cart* cart, u16 addr) { /* rom bank 00 */ - if (addr <= 0x3fff) + if (addr <= 0x3fff) { if (cart->mode_select) return cart->ROM[addr + (cart->zero_bank * 0x4000)]; else return cart->ROM[addr]; + } /* rom bank 01-7f */ if (addr >= 0x4000 && addr <= 0x7fff) return cart->ROM[(addr - 0x4000) + (cart->high_bank * 0x4000)]; /* ram bank 00-03 */ - if (addr >= 0xa000 && addr <= 0xbfff) - if (cart->ram_enable && cart->RAM) - if (cart->ram_bank_count <= 2) + if (addr >= 0xa000 && addr <= 0xbfff) { + if (cart->ram_enable && cart->RAM) { + if (cart->ram_bank_count <= 2) { return cart->RAM[(addr - 0xa000) % cart->ram_size]; - else if (cart->ram_bank_count == 3 && cart->mode_select) + } + else if (cart->ram_bank_count == 3 && cart->mode_select) { return cart->RAM[(addr - 0xa000) + (cart->ram_bank * 0x2000)]; - else + } + else { return cart->RAM[addr - 0xa000]; + } + } + } return 0xff; } @@ -173,15 +184,19 @@ void mbc1_write(struct cart* cart, u16 addr, u8 val) if (addr >= 0x6000 && addr <= 0x7fff) cart->mode_select = val & 1; - if (addr >= 0xa000 && addr <= 0xbfff) - if (cart->ram_enable && cart->RAM) - if (cart->ram_bank_count <= 2) + if (addr >= 0xa000 && addr <= 0xbfff) { + if (cart->ram_enable && cart->RAM) { + if (cart->ram_bank_count <= 2) { cart->RAM[(addr - 0xa000) % cart->ram_size] = val; - else if (cart->ram_bank_count == 3 && cart->mode_select) + } + else if (cart->ram_bank_count == 3 && cart->mode_select) { cart->RAM[(addr - 0xa000) + (cart->ram_bank * 0x2000)] = val; - else + } + else { cart->RAM[addr - 0xa000] = val; - + } + } + } } u8 mbc2_read(struct cart* cart, u16 addr) @@ -195,9 +210,10 @@ u8 mbc2_read(struct cart* cart, u16 addr) return cart->ROM[(addr - 0x4000) + (cart->rom_bank * 0x4000)]; /* internal RAM */ - if (addr >= 0xa000 && addr <= 0xbfff) + if (addr >= 0xa000 && addr <= 0xbfff) { if (cart->ram_enable) return 0xf0 | cart->RAM[addr & 0x1ff]; + } return 0xff; } @@ -279,7 +295,7 @@ u8 mbc3_read(struct cart* cart, u16 addr) if (addr >= 0x4000 && addr <= 0x7fff) return cart->ROM[(addr - 0x4000) + (cart->rom_bank * 0x4000)]; - if (addr >= 0xa000 && addr <= 0xbfff) + if (addr >= 0xa000 && addr <= 0xbfff) { if (cart->ram_enable) { if (cart->rtc_supported && cart->rtc_mode) switch (cart->rtc_register) { @@ -289,9 +305,10 @@ u8 mbc3_read(struct cart* cart, u16 addr) case 0x0b: return cart->rtc_days & 0xff; break; case 0x0c: return cart->rtc_ctrl & 0xc1; break; } - else if(cart->RAM && !cart->rtc_mode) + else if (cart->RAM && !cart->rtc_mode) return cart->RAM[(addr - 0xa000) + (cart->ram_bank * 0x2000)]; } + } return 0xff; } @@ -342,6 +359,42 @@ void mbc3_write(struct cart* cart, u16 addr, u8 val) } } +u8 mbc5_read(struct cart* cart, u16 addr) +{ + if (addr <= 0x3fff) + return cart->ROM[addr]; + + if(addr >= 0x4000 && addr <= 0x7fff) + return cart->ROM[((addr - 0x4000) + (cart->rom_bank * 0x4000)) % cart->rom_size]; + + if(addr >= 0xa000 && addr <= 0xbfff) { + if (cart->RAM && cart->ram_enable) + return cart->RAM[(addr - 0xa000) + (cart->ram_bank * 0x2000)]; + } + + return 0xff; +} + +void mbc5_write(struct cart* cart, u16 addr, u8 val) +{ + if (addr <= 0x1fff) + cart->ram_enable = (val & 0xf) == 0xa; + + if (addr >= 0x2000 && addr <= 0x2fff) + cart->rom_bank = (cart->rom_bank & 0x100) | val; + + if (addr >= 0x3000 && addr <= 0x3fff) + cart->rom_bank = ((val & 1) << 8) | (cart->rom_bank & 0xff); + + if (addr >= 0x4000 && addr <= 0x5fff) + cart->ram_bank = val & 0xf; + + if (addr >= 0xa000 && addr <= 0xbfff) { + if (cart->RAM && cart->ram_enable) + cart->RAM[(addr - 0xa000) + (cart->ram_bank * 0x2000)] = val; + } +} + u8 cart_read(struct cart* cart, u16 addr) { switch (cart->MBC) { @@ -349,6 +402,7 @@ u8 cart_read(struct cart* cart, u16 addr) case 1: return mbc1_read(cart, addr); case 2: return mbc2_read(cart, addr); case 3: return mbc3_read(cart, addr); + case 5: return mbc5_read(cart, addr); } return 0xff; @@ -361,5 +415,6 @@ void cart_write(struct cart* cart, u16 addr, u8 val) case 1: mbc1_write(cart, addr, val); break; case 2: mbc2_write(cart, addr, val); break; case 3: mbc3_write(cart, addr, val); break; + case 5: mbc5_write(cart, addr, val); break; } } \ No newline at end of file diff --git a/gameboy/debugger.c b/gameboy/debugger.c index 1512495..f363fe1 100644 --- a/gameboy/debugger.c +++ b/gameboy/debugger.c @@ -4,7 +4,7 @@ void dbg_print_cpu_data(struct cpu* cpu, u8 op) { #ifdef _DEBUG printf("AF=%04X BC=%04X DE=%04X " - "HL=%04X PC=%04X SP=%04X FL=%c%c%c%c iTIMA=%04X DIV=%02X TIMA=%02X : ", + "HL=%04X PC=%04X SP=%04X FL=%c%c%c%c iTIMA=%04X DIV=%02X TIMA=%02X (%02X %02X %02X %02X) : ", cpu->AF, cpu->BC, cpu->DE, cpu->HL, cpu->PC - 1, cpu->SP, ISF(ZF) ? 'Z' : '-', @@ -12,7 +12,11 @@ void dbg_print_cpu_data(struct cpu* cpu, u8 op) ISF(HF) ? 'H' : '-', ISF(CF) ? 'C' : '-', cpu->tima_internal, - cpu->div, cpu->tima + cpu->div, cpu->tima, + READ(cpu->PC - 1), + READ(cpu->PC), + READ(cpu->PC + 1), + READ(cpu->PC + 2) ); if (op != 0xcb) { @@ -26,7 +30,7 @@ void dbg_print_cpu_data(struct cpu* cpu, u8 op) } } else { - printf(op_cb_list[op].name); + printf(op_cb_list[READ(cpu->PC)].name); } printf("\n"); diff --git a/gameboy/lcd.c b/gameboy/lcd.c index 40e38d2..d85b5db 100644 --- a/gameboy/lcd.c +++ b/gameboy/lcd.c @@ -1,5 +1,4 @@ #include "lcd.h" -#include const u32 colors[4] = { 0xff4fbc9b, /*white*/ @@ -74,15 +73,41 @@ void lcd_destroy(struct lcd* lcd) void lcd_check_lyc(struct lcd* lcd) { - if (lcd->ly == lcd->lyc && lcd->stat & S_INT_LYC) { + if (lcd->ly == lcd->lyc) { lcd->stat |= S_LYC; - lcd->cpu->IF |= STAT_INTERRUPT; + if (lcd->stat & S_INT_LYC) + lcd->cpu->IF |= STAT_INTERRUPT; } else { lcd->stat &= ~S_LYC; } } +void lcd_mode(struct lcd* lcd, u8 mode) +{ + u8 int_enabled = 0; + + lcd->stat &= ~S_MODE; + lcd->stat |= mode; + + switch (mode) { + case HBLANK: + int_enabled = lcd->stat & S_INT_HBLANK; + break; + + case VBLANK: + int_enabled = lcd->stat & S_INT_VBLANK; + break; + + case OAMSCAN: + int_enabled = lcd->stat & S_INT_OAM; + break; + } + + if (int_enabled) + lcd->cpu->IF |= STAT_INTERRUPT; +} + void lcd_tick(struct lcd* lcd, u8 cycles) { lcd->cycles += cycles; @@ -92,7 +117,7 @@ void lcd_tick(struct lcd* lcd, u8 cycles) case OAMSCAN: if (lcd->cycles >= 80) { lcd->cycles -= 80; - MODE(LCDTRANSFER, 0); + lcd_mode(lcd, LCDTRANSFER); } break; @@ -100,7 +125,7 @@ void lcd_tick(struct lcd* lcd, u8 cycles) case LCDTRANSFER: if (lcd->cycles >= 172) { lcd->cycles -= 172; - MODE(HBLANK, 3); + lcd_mode(lcd, HBLANK); lcd_draw(lcd); } @@ -110,15 +135,16 @@ void lcd_tick(struct lcd* lcd, u8 cycles) if (lcd->cycles >= 204) { lcd->cycles -= 204; if (++lcd->ly >= 144) { - MODE(VBLANK, 4); + lcd_mode(lcd, VBLANK); lcd->cpu->IF |= VBLANK_INTERRUPT; } else { - MODE(OAMSCAN, 5); + lcd_mode(lcd, OAMSCAN); } lcd_check_lyc(lcd); } + break; case VBLANK: @@ -126,10 +152,9 @@ void lcd_tick(struct lcd* lcd, u8 cycles) lcd->cycles -= 456; if (++lcd->ly == 154) { lcd->ly = 0; - lcd->il = 0; lcd->wilc = 0xff; lcd_check_lyc(lcd); - MODE(OAMSCAN, 5); + lcd_mode(lcd, OAMSCAN); lcd_paint(lcd); } } diff --git a/gameboy/lcd.h b/gameboy/lcd.h index 7b7d2e4..e02db6c 100644 --- a/gameboy/lcd.h +++ b/gameboy/lcd.h @@ -28,17 +28,11 @@ #define S_MODE 3 #define S_LYC (1 << 2) +#define S_INT_HBLANK (1 << 3) +#define S_INT_VBLANK (1 << 4) +#define S_INT_OAM (1 << 5) #define S_INT_LYC (1 << 6) -#define MODE(n, i) \ - lcd->o_il = lcd->il; \ - lcd->stat &= ~S_MODE; \ - lcd->stat |= n; \ - if((lcd->il && !lcd->o_il) && lcd->stat & (1 << i)) { \ - lcd->il = 1; \ - lcd->cpu->IF |= STAT_INTERRUPT; \ - } - #define lREAD(a) mmu_read(lcd->cpu->mmu, a) #define lWRITE(a,w) mmu_write(lcd->cpu->mmu, a, w) @@ -70,9 +64,6 @@ struct lcd { u8 wx; u8 wilc; /* internal line counter */ - u8 il; /* interrupt on line */ - u8 o_il; - SDL_Window* window; SDL_Renderer* renderer; SDL_Texture* texture; diff --git a/gameboy/ops.h b/gameboy/ops.h index 7344c74..4032c45 100644 --- a/gameboy/ops.h +++ b/gameboy/ops.h @@ -178,7 +178,7 @@ void shift_rot(struct cpu*); void bit_res_set(struct cpu*); static struct cpu_op op_list[256] = { - {"NOP", 1, nop}, + {"NOP", 1, nop}, {"LD BC, $%04X", 3, ld_r16_u16}, {"LD (BC), A", 1, ld_r16_a}, {"INC BC", 1, inc_r16},