From d5f9c6950c44d951b9a93b987042e3d38e5170eb Mon Sep 17 00:00:00 2001 From: Ben Kurtovic Date: Tue, 11 Jul 2017 18:17:21 -0400 Subject: [PATCH] Add support for on-cartridge RAM (#3) --- src/mmu.c | 73 +++++++++++++++++++++++++++++++++++++++++++++------------------ src/mmu.h | 8 +++++-- 2 files changed, 58 insertions(+), 23 deletions(-) diff --git a/src/mmu.c b/src/mmu.c index d356f2f..dc17869 100644 --- a/src/mmu.c +++ b/src/mmu.c @@ -15,9 +15,12 @@ void mmu_init(MMU *mmu) { mmu->system_ram = cr_malloc(sizeof(uint8_t) * MMU_SYSTEM_RAM_SIZE); + mmu->cart_ram = cr_malloc(sizeof(uint8_t) * MMU_CART_RAM_SIZE); + mmu->cart_ram_slot = mmu->cart_ram; + mmu->cart_ram_mapped = false; for (size_t slot = 0; slot < MMU_NUM_SLOTS; slot++) - mmu->map_slots[slot] = NULL; + mmu->rom_slots[slot] = NULL; for (size_t bank = 0; bank < MMU_NUM_ROM_BANKS; bank++) mmu->rom_banks[bank] = NULL; @@ -29,6 +32,7 @@ void mmu_init(MMU *mmu) void mmu_free(MMU *mmu) { free(mmu->system_ram); + free(mmu->cart_ram); } /* @@ -84,10 +88,10 @@ void mmu_load_rom(MMU *mmu, const uint8_t *data, size_t size) /* Map the given RAM slot to the given ROM bank. */ -static inline void map_slot(MMU *mmu, size_t slot, size_t bank) +static inline void map_rom_slot(MMU *mmu, size_t slot, size_t bank) { - TRACE("MMU mapping memory slot %zu to bank 0x%02zX", slot, bank) - mmu->map_slots[slot] = mmu->rom_banks[bank]; + TRACE("MMU mapping memory slot %zu to ROM bank 0x%02zX", slot, bank) + mmu->rom_slots[slot] = mmu->rom_banks[bank]; } /* @@ -99,9 +103,10 @@ static inline void map_slot(MMU *mmu, size_t slot, size_t bank) void mmu_power(MMU *mmu) { for (size_t slot = 0; slot < MMU_NUM_SLOTS; slot++) - map_slot(mmu, slot, slot); + map_rom_slot(mmu, slot, slot); memset(mmu->system_ram, 0xFF, MMU_SYSTEM_RAM_SIZE); + memset(mmu->cart_ram, 0xFF, MMU_CART_RAM_SIZE); } /* @@ -121,18 +126,21 @@ static inline uint8_t bank_byte_read(const uint8_t* bank, uint16_t addr) */ uint8_t mmu_read_byte(const MMU *mmu, uint16_t addr) { - if (addr < 0x0400) // First kilobyte is unpaged, for interrupt handlers + if (addr < 0x0400) { // First kilobyte is unpaged, for interrupt handlers return bank_byte_read(mmu->rom_banks[0], addr); - else if (addr < 0x4000) // Slot 0 (0x0400 - 0x3FFF) - return bank_byte_read(mmu->map_slots[0], addr); - else if (addr < 0x8000) // Slot 1 (0x4000 - 0x7FFF) - return bank_byte_read(mmu->map_slots[1], addr - 0x4000); - else if (addr < 0xC000) // Slot 2 (0x8000 - 0xBFFF) - return bank_byte_read(mmu->map_slots[2], addr - 0x8000); - else if (addr < 0xE000) // System RAM (0xC000 - 0xDFFF) + } else if (addr < 0x4000) { // Slot 0 (0x0400 - 0x3FFF) + return bank_byte_read(mmu->rom_slots[0], addr); + } else if (addr < 0x8000) { // Slot 1 (0x4000 - 0x7FFF) + return bank_byte_read(mmu->rom_slots[1], addr - 0x4000); + } else if (addr < 0xC000) { // Slot 2 (0x8000 - 0xBFFF) + if (mmu->cart_ram_mapped) + return mmu->cart_ram_slot[addr - 0x8000]; + return bank_byte_read(mmu->rom_slots[2], addr - 0x8000); + } else if (addr < 0xE000) { // System RAM (0xC000 - 0xDFFF) return mmu->system_ram[addr - 0xC000]; - else // System RAM, mirrored (0xE000 - 0xFFFF) + } else { // System RAM, mirrored (0xE000 - 0xFFFF) return mmu->system_ram[addr - 0xE000]; + } } /* @@ -156,6 +164,25 @@ uint32_t mmu_read_quad(const MMU *mmu, uint16_t addr) } /* + Write to the cartridge RAM mapping control register at 0xFFFC. +*/ +static void write_ram_control_register(MMU *mmu, uint8_t value) +{ + // TODO: 0x03 = bank shift, 0x10 = 0xC000-0xFFFF enable, 0x80 = ROM write + bool bank_select = value & 0x04; + bool slot2_enable = value & 0x08; + + if (slot2_enable) + TRACE("MMU enabling cart RAM bank %d in memory slot 2", bank_select) + else if (!slot2_enable && mmu->cart_ram_mapped) + TRACE("MMU disabling cart RAM in memory slot 2") + + mmu->cart_ram_slot = + bank_select ? (mmu->cart_ram + 0x4000) : mmu->cart_ram; + mmu->cart_ram_mapped = slot2_enable; +} + +/* Write a byte of memory to the given address. Return true if the byte was written, and false if it wasn't. Writes will @@ -163,20 +190,24 @@ uint32_t mmu_read_quad(const MMU *mmu, uint16_t addr) */ bool mmu_write_byte(MMU *mmu, uint16_t addr, uint8_t value) { - if (addr < 0xC000) { // TODO: implement writes to on-cartridge RAM + if (addr < 0xC000) { + if (addr >= 0x8000 && mmu->cart_ram_mapped) { + mmu->cart_ram_slot[addr - 0x8000] = value; + return true; + } return false; } else if (addr < 0xE000) { // System RAM (0xC000 - 0xDFFF) mmu->system_ram[addr - 0xC000] = value; return true; } else { // System RAM, mirrored (0xE000 - 0xFFFF) - if (addr == 0xFFFC) { - // TODO: handle cartridge RAM mapping control - } else if (addr == 0xFFFD) - map_slot(mmu, 0, value & 0x3F); + if (addr == 0xFFFC) + write_ram_control_register(mmu, value); + else if (addr == 0xFFFD) + map_rom_slot(mmu, 0, value & 0x3F); else if (addr == 0xFFFE) - map_slot(mmu, 1, value & 0x3F); + map_rom_slot(mmu, 1, value & 0x3F); else if (addr == 0xFFFF) - map_slot(mmu, 2, value & 0x3F); + map_rom_slot(mmu, 2, value & 0x3F); mmu->system_ram[addr - 0xE000] = value; return true; } diff --git a/src/mmu.h b/src/mmu.h index 1b987bf..816af96 100644 --- a/src/mmu.h +++ b/src/mmu.h @@ -10,14 +10,18 @@ #define MMU_NUM_SLOTS (3) #define MMU_NUM_ROM_BANKS (64) #define MMU_ROM_BANK_SIZE (16 * 1024) -#define MMU_SYSTEM_RAM_SIZE (8 * 1024) +#define MMU_SYSTEM_RAM_SIZE ( 8 * 1024) +#define MMU_CART_RAM_SIZE (32 * 1024) /* Structs */ typedef struct { uint8_t *system_ram; - const uint8_t *map_slots[MMU_NUM_SLOTS]; + uint8_t *cart_ram; + const uint8_t *rom_slots[MMU_NUM_SLOTS]; const uint8_t *rom_banks[MMU_NUM_ROM_BANKS]; + uint8_t *cart_ram_slot; + bool cart_ram_mapped; } MMU; /* Functions */