From 5cc208f07e43030401bec6feba2c938fabd7da16 Mon Sep 17 00:00:00 2001 From: Ben Kurtovic Date: Thu, 26 Mar 2015 20:20:34 -0400 Subject: [PATCH] Implement most MMU functions. --- src/mmu.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- src/mmu.h | 11 ++++++++-- 2 files changed, 69 insertions(+), 12 deletions(-) diff --git a/src/mmu.c b/src/mmu.c index ab808aa..86c26fd 100644 --- a/src/mmu.c +++ b/src/mmu.c @@ -1,17 +1,30 @@ /* Copyright (C) 2014-2015 Ben Kurtovic Released under the terms of the MIT License. See LICENSE for details. */ +#include +#include + +#include "logging.h" #include "z80.h" /* - Initialize a MMU object. + Initialize a MMU object. This must be called before using the MMU. Return true if initialization was successful, or false if the required amount of memory could not be allocated. */ bool mmu_init(MMU *mmu) { - // TODO + mmu->system_ram = malloc(sizeof(uint8_t) * MMU_SYSTEM_RAM_SIZE); + if (!mmu->system_ram) + return false; + + for (size_t slot = 0; slot < MMU_NUM_SLOTS; slot++) + mmu->map_slots[slot] = NULL; + + for (size_t bank = 0; bank < MMU_NUM_ROM_BANKS; bank++) + mmu->rom_banks[bank] = NULL; + return true; } @@ -20,29 +33,62 @@ bool mmu_init(MMU *mmu) */ void mmu_free(MMU *mmu) { - // TODO + free(mmu->system_ram); } /* - Load a block ROM into the MMU. + Load a block of ROM into the MMU. + + size must be a multiple of MMU_ROM_BANK_SIZE (16 KB), the load will fail + silently. It should also be a power of two, or problems might occur with + ROM mirroring logic. It should not be larger than + MMU_ROM_BANK_SIZE * MMU_NUM_ROM_BANKS, or the extra banks will be ignored. - size should be a power of two. + This function will still work if called while the system is running, but it + will likely cause unexpected behavior. */ void mmu_load_rom(MMU *mmu, const uint8_t *data, size_t size) { - // TODO + if (size % MMU_ROM_BANK_SIZE) + return; + + size_t banks = size / MMU_ROM_BANK_SIZE; + if (banks > MMU_NUM_ROM_BANKS) + banks = MMU_NUM_ROM_BANKS; + + for (size_t bank = 0; bank < banks; bank++) { + for (size_t mirror = bank; mirror < banks; mirror += bank + 1) { + mmu->rom_banks[mirror] = data + (bank * MMU_ROM_BANK_SIZE); + } + } +} + +/* + Map the given RAM slot to the given ROM bank. +*/ +static inline void map_slot(MMU *mmu, size_t slot, size_t bank) +{ + DEBUG("MMU mapping memory slot %zu to bank %zu", slot, bank) + mmu->map_slots[slot] = mmu->rom_banks[bank]; } /* Power on the MMU, setting initial memory values. + + This must be called before memory is read from or written to. If no ROM has + been loaded, those regions will be read as 0xFF and will not accept writes. */ void mmu_power(MMU *mmu) { - // TODO + for (size_t slot = 0; slot < MMU_NUM_SLOTS; slot++) + map_slot(mmu, slot, slot); + + memset(mmu->system_ram, 0xFF, MMU_SYSTEM_RAM_SIZE); + // TODO: set system_ram to correct values at 0xFFFC-0xFFFF } /* - Read a byte of memory. + Read a byte of memory from the given address. */ uint8_t mmu_read_byte(MMU *mmu, uint16_t addr) { @@ -51,9 +97,13 @@ uint8_t mmu_read_byte(MMU *mmu, uint16_t addr) } /* - ... + Write a byte of memory to the given address. + + Return true if the byte was written, and false if it wasn't. Writes will + fail when attempting to write to read-only memory. */ -void mmu_write_byte(MMU *mmu, uint16_t addr, uint8_t value) +bool mmu_write_byte(MMU *mmu, uint16_t addr, uint8_t value) { // TODO + return true; } diff --git a/src/mmu.h b/src/mmu.h index 5aaa8b1..a76d4c5 100644 --- a/src/mmu.h +++ b/src/mmu.h @@ -7,10 +7,17 @@ #include #include +#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) + /* Structs */ typedef struct { - // + uint8_t *system_ram; + const uint8_t *map_slots[MMU_NUM_SLOTS]; + const uint8_t *rom_banks[MMU_NUM_ROM_BANKS]; } MMU; /* Functions */ @@ -20,4 +27,4 @@ void mmu_free(MMU*); void mmu_load_rom(MMU*, const uint8_t*, size_t); void mmu_power(MMU*); uint8_t mmu_read_byte(MMU*, uint16_t); -void mmu_write_byte(MMU*, uint16_t, uint8_t); +bool mmu_write_byte(MMU*, uint16_t, uint8_t);