all repos — mgba @ 87d4dad893d5df04fac5575c12a1c7b82fc07a7b

mGBA Game Boy Advance Emulator

GB: Save auto-sizing
Jeffrey Pfau jeffrey@endrift.com
Tue, 06 Sep 2016 11:15:27 -0700
commit

87d4dad893d5df04fac5575c12a1c7b82fc07a7b

parent

f5bf1221eba8fcc572c1f66ce74de31f6049451e

M src/gb/core.csrc/gb/core.c

@@ -9,6 +9,7 @@ #include "core/core.h"

#include "gb/cheats.h" #include "gb/cli.h" #include "gb/gb.h" +#include "gb/mbc.h" #include "gb/renderers/software.h" #include "gb/serialize.h" #include "lr35902/debugger/debugger.h"

@@ -427,9 +428,9 @@ *sram = malloc(vf->size(vf));

vf->seek(vf, 0, SEEK_SET); return vf->read(vf, *sram, vf->size(vf)); } - *sram = malloc(0x20000); - memcpy(*sram, gb->memory.sram, 0x20000); - return 0x20000; + *sram = malloc(gb->sramSize); + memcpy(*sram, gb->memory.sram, gb->sramSize); + return gb->sramSize; } static bool _GBCoreSavedataLoad(struct mCore* core, const void* sram, size_t size) {

@@ -442,7 +443,8 @@ }

if (size > 0x20000) { size = 0x20000; } - memcpy(gb->memory.sram, sram, 0x20000); + GBResizeSram(gb, size); + memcpy(gb->memory.sram, sram, size); return true; }
M src/gb/gb.csrc/gb/gb.c

@@ -100,30 +100,66 @@

bool GBLoadSave(struct GB* gb, struct VFile* vf) { gb->sramVf = vf; gb->sramRealVf = vf; - if (vf) { - // TODO: Do this in bank-switching code - if (vf->size(vf) < 0x20000) { - vf->truncate(vf, 0x20000); - } - gb->memory.sram = vf->map(vf, 0x20000, MAP_WRITE); - } - return gb->memory.sram; + return vf; } static void GBSramDeinit(struct GB* gb) { if (gb->sramVf) { - gb->sramVf->unmap(gb->sramVf, gb->memory.sram, 0x20000); + gb->sramVf->unmap(gb->sramVf, gb->memory.sram, gb->sramSize); gb->sramVf = 0; } else if (gb->memory.sram) { - mappedMemoryFree(gb->memory.sram, 0x20000); + mappedMemoryFree(gb->memory.sram, gb->sramSize); } gb->memory.sram = 0; } +void GBResizeSram(struct GB* gb, size_t size) { + struct VFile* vf = gb->sramVf; + if (vf) { + if (vf == gb->sramRealVf) { + if (vf->size(vf) >= 0 && (size_t) vf->size(vf) < size) { + uint8_t extdataBuffer[0x100]; + if (vf->size(vf) & 0xFF) { + // Copy over appended data, e.g. RTC data + memcpy(extdataBuffer, &gb->memory.sram[gb->sramSize - (vf->size(vf) & 0xFF)], vf->size(vf) & 0xFF); + } + vf->unmap(vf, gb->memory.sram, gb->sramSize); + vf->truncate(vf, size); + gb->memory.sram = vf->map(vf, size, MAP_WRITE); + memset(&gb->memory.sram[gb->sramSize], 0xFF, size - gb->sramSize); + if (size & 0xFF) { + memcpy(&gb->memory.sram[gb->sramSize - (size & 0xFF)], extdataBuffer, size & 0xFF); + } + } else { + vf->unmap(vf, gb->memory.sram, gb->sramSize); + gb->memory.sram = vf->map(vf, size, MAP_WRITE); + } + } else { + vf->unmap(vf, gb->memory.sram, gb->sramSize); + gb->memory.sram = vf->map(vf, size, MAP_READ); + } + } else { + uint8_t* newSram = anonymousMemoryMap(size); + if (gb->memory.sram) { + if (size > gb->sramSize) { + memcpy(newSram, gb->memory.sram, gb->sramSize); + memset(&newSram[gb->sramSize], 0xFF, size - gb->sramSize); + } else { + memcpy(newSram, gb->memory.sram, size); + } + mappedMemoryFree(gb->memory.sram, gb->sramSize); + } else { + memset(newSram, 0xFF, size); + } + gb->memory.sram = newSram; + } + gb->sramSize = size; +} + void GBSavedataMask(struct GB* gb, struct VFile* vf) { GBSramDeinit(gb); gb->sramVf = vf; - gb->memory.sram = vf->map(vf, 0x20000, MAP_READ); + gb->memory.sram = vf->map(vf, gb->sramSize, MAP_READ); } void GBSavedataUnmask(struct GB* gb) {

@@ -132,7 +168,7 @@ return;

} GBSramDeinit(gb); gb->sramVf = gb->sramRealVf; - gb->memory.sram = gb->sramVf->map(gb->sramVf, 0x20000, MAP_WRITE); + gb->memory.sram = gb->sramVf->map(gb->sramVf, gb->sramSize, MAP_WRITE); } void GBUnloadROM(struct GB* gb) {
M src/gb/gb.hsrc/gb/gb.h

@@ -67,6 +67,7 @@ struct VFile* romVf;

struct VFile* biosVf; struct VFile* sramVf; struct VFile* sramRealVf; + uint32_t sramSize; struct mAVStream* stream;

@@ -109,6 +110,7 @@

struct VFile; bool GBLoadROM(struct GB* gb, struct VFile* vf); bool GBLoadSave(struct GB* gb, struct VFile* vf); +void GBResizeSram(struct GB* gb, size_t size); void GBYankROM(struct GB* gb); void GBUnloadROM(struct GB* gb);
A src/gb/mbc.c

@@ -0,0 +1,539 @@

+/* Copyright (c) 2013-2016 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "mbc.h" + +#include "gb/gb.h" +#include "gb/memory.h" + +#include <time.h> + +mLOG_DEFINE_CATEGORY(GB_MBC, "GB MBC"); + +static void _GBMBCNone(struct GB* gb, uint16_t address, uint8_t value) { + UNUSED(gb); + UNUSED(address); + UNUSED(value); + + mLOG(GB_MBC, GAME_ERROR, "Wrote to invalid MBC"); +} + +static void _GBMBC1(struct GB*, uint16_t address, uint8_t value); +static void _GBMBC2(struct GB*, uint16_t address, uint8_t value); +static void _GBMBC3(struct GB*, uint16_t address, uint8_t value); +static void _GBMBC5(struct GB*, uint16_t address, uint8_t value); +static void _GBMBC6(struct GB*, uint16_t address, uint8_t value); +static void _GBMBC7(struct GB*, uint16_t address, uint8_t value); +static void _GBHuC3(struct GB*, uint16_t address, uint8_t value); + +void GBMBCSwitchBank(struct GBMemory* memory, int bank) { + size_t bankStart = bank * GB_SIZE_CART_BANK0; + if (bankStart + GB_SIZE_CART_BANK0 > memory->romSize) { + mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank); + bankStart &= (memory->romSize - 1); + bank = bankStart / GB_SIZE_CART_BANK0; + } + memory->romBank = &memory->rom[bankStart]; + memory->currentBank = bank; +} + +void GBMBCSwitchSramBank(struct GB* gb, int bank) { + size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM; + GBResizeSram(gb, (bank + 1) * GB_SIZE_EXTERNAL_RAM + (gb->sramSize & 0xFF)); + gb->memory.sramBank = &gb->memory.sram[bankStart]; + gb->memory.sramCurrentBank = bank; +} + +void GBMBCInit(struct GB* gb) { + const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100]; + switch (cart->type) { + case 0: + case 8: + case 9: + gb->memory.mbc = _GBMBCNone; + gb->memory.mbcType = GB_MBC_NONE; + gb->sramSize = 0; + return; + case 1: + case 2: + case 3: + gb->memory.mbc = _GBMBC1; + gb->memory.mbcType = GB_MBC1; + gb->sramSize = 0x2000; + break; + case 5: + case 6: + gb->memory.mbc = _GBMBC2; + gb->memory.mbcType = GB_MBC2; + gb->sramSize = 0x200; + break; + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + gb->memory.mbc = _GBMBC3; + gb->memory.mbcType = GB_MBC3; + gb->sramSize = 0x2048; + break; + default: + mLOG(GB_MBC, WARN, "Unknown MBC type: %02X", cart->type); + // Fall through + case 0x19: + case 0x1A: + case 0x1B: + gb->memory.mbc = _GBMBC5; + gb->memory.mbcType = GB_MBC5; + gb->sramSize = 0x2000; + break; + case 0x1C: + case 0x1D: + case 0x1E: + gb->memory.mbc = _GBMBC5; + gb->memory.mbcType = GB_MBC5_RUMBLE; + gb->sramSize = 0x2000; + break; + case 0x20: + gb->memory.mbc = _GBMBC6; + gb->memory.mbcType = GB_MBC6; + gb->sramSize = 0; // TODO + break; + case 0x22: + gb->memory.mbc = _GBMBC7; + gb->memory.mbcType = GB_MBC7; + gb->sramSize = 0x2000; + break; + case 0xFE: + gb->memory.mbc = _GBHuC3; + gb->memory.mbcType = GB_HuC3; + gb->sramSize = 0x2000; + break; + } + + GBResizeSram(gb, gb->sramSize); +} + +static void _latchRtc(struct GBMemory* memory) { + time_t t; + struct mRTCSource* rtc = memory->rtc; + if (rtc) { + if (rtc->sample) { + rtc->sample(rtc); + } + t = rtc->unixTime(rtc); + } else { + t = time(0); + } + struct tm date; + localtime_r(&t, &date); + memory->rtcRegs[0] = date.tm_sec; + memory->rtcRegs[1] = date.tm_min; + memory->rtcRegs[2] = date.tm_hour; + memory->rtcRegs[3] = date.tm_yday; // TODO: Persist day counter + memory->rtcRegs[4] &= 0xF0; + memory->rtcRegs[4] |= date.tm_yday >> 8; +} + +void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + int bank = value & 0x1F; + switch (address >> 13) { + case 0x0: + switch (value) { + case 0: + memory->sramAccess = false; + break; + case 0xA: + memory->sramAccess = true; + GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC1 unknown value %02X", value); + break; + } + break; + case 0x1: + if (!bank) { + ++bank; + } + GBMBCSwitchBank(memory, bank | (memory->currentBank & 0x60)); + break; + case 0x2: + bank &= 3; + if (!memory->mbcState.mbc1.mode) { + GBMBCSwitchBank(memory, (bank << 5) | (memory->currentBank & 0x1F)); + } else { + GBMBCSwitchSramBank(gb, bank); + } + break; + case 0x3: + memory->mbcState.mbc1.mode = value & 1; + if (memory->mbcState.mbc1.mode) { + GBMBCSwitchBank(memory, memory->currentBank & 0x1F); + } else { + GBMBCSwitchSramBank(gb, 0); + } + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC1 unknown address: %04X:%02X", address, value); + break; + } +} + +void _GBMBC2(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + int bank = value & 0xF; + switch (address >> 13) { + case 0x0: + switch (value) { + case 0: + memory->sramAccess = false; + break; + case 0xA: + memory->sramAccess = true; + GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC1 unknown value %02X", value); + break; + } + break; + case 0x1: + if (!bank) { + ++bank; + } + GBMBCSwitchBank(memory, bank); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC2 unknown address: %04X:%02X", address, value); + break; + }} + +void _GBMBC3(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + int bank = value & 0x7F; + switch (address >> 13) { + case 0x0: + switch (value) { + case 0: + memory->sramAccess = false; + break; + case 0xA: + memory->sramAccess = true; + GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC3 unknown value %02X", value); + break; + } + break; + case 0x1: + if (!bank) { + ++bank; + } + GBMBCSwitchBank(memory, bank); + break; + case 0x2: + if (value < 4) { + GBMBCSwitchSramBank(gb, value); + memory->rtcAccess = false; + } else if (value >= 8 && value <= 0xC) { + memory->activeRtcReg = value - 8; + memory->rtcAccess = true; + } + break; + case 0x3: + if (memory->rtcLatched && value == 0) { + memory->rtcLatched = false; + } else if (!memory->rtcLatched && value == 1) { + _latchRtc(memory); + memory->rtcLatched = true; + } + break; + } +} + +void _GBMBC5(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + int bank; + switch (address >> 12) { + case 0x0: + case 0x1: + switch (value) { + case 0: + memory->sramAccess = false; + break; + case 0xA: + memory->sramAccess = true; + GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC5 unknown value %02X", value); + break; + } + break; + case 0x2: + bank = (memory->currentBank & 0x100) | value; + GBMBCSwitchBank(memory, bank); + break; + case 0x3: + bank = (memory->currentBank & 0xFF) | ((value & 1) << 8); + GBMBCSwitchBank(memory, bank); + break; + case 0x4: + case 0x5: + if (memory->mbcType == GB_MBC5_RUMBLE && memory->rumble) { + memory->rumble->setRumble(memory->rumble, (value >> 3) & 1); + value &= ~8; + } + GBMBCSwitchSramBank(gb, value & 0xF); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC5 unknown address: %04X:%02X", address, value); + break; + } +} + +void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) { + // TODO + mLOG(GB_MBC, STUB, "MBC6 unimplemented"); + UNUSED(gb); + UNUSED(address); + UNUSED(value); +} + +void _GBMBC7(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + int bank = value & 0x7F; + switch (address >> 13) { + case 0x1: + GBMBCSwitchBank(memory, bank); + break; + case 0x2: + if (value < 0x10) { + GBMBCSwitchSramBank(gb, value); + } + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC7 unknown address: %04X:%02X", address, value); + break; + } +} + +uint8_t GBMBC7Read(struct GBMemory* memory, uint16_t address) { + struct GBMBC7State* mbc7 = &memory->mbcState.mbc7; + switch (address & 0xF0) { + case 0x00: + case 0x10: + case 0x60: + case 0x70: + return 0; + case 0x20: + if (memory->rotation && memory->rotation->readTiltX) { + int32_t x = -memory->rotation->readTiltX(memory->rotation); + x >>= 21; + x += 2047; + return x; + } + return 0xFF; + case 0x30: + if (memory->rotation && memory->rotation->readTiltX) { + int32_t x = -memory->rotation->readTiltX(memory->rotation); + x >>= 21; + x += 2047; + return x >> 8; + } + return 7; + case 0x40: + if (memory->rotation && memory->rotation->readTiltY) { + int32_t y = -memory->rotation->readTiltY(memory->rotation); + y >>= 21; + y += 2047; + return y; + } + return 0xFF; + case 0x50: + if (memory->rotation && memory->rotation->readTiltY) { + int32_t y = -memory->rotation->readTiltY(memory->rotation); + y >>= 21; + y += 2047; + return y >> 8; + } + return 7; + case 0x80: + return (mbc7->sr >> 16) & 1; + default: + return 0xFF; + } +} + +void GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value) { + if ((address & 0xF0) != 0x80) { + return; + } + struct GBMBC7State* mbc7 = &memory->mbcState.mbc7; + GBMBC7Field old = memory->mbcState.mbc7.field; + mbc7->field = GBMBC7FieldClearIO(value); + if (!GBMBC7FieldIsCS(old) && GBMBC7FieldIsCS(value)) { + if (mbc7->state == GBMBC7_STATE_WRITE) { + if (mbc7->writable) { + memory->sramBank[mbc7->address * 2] = mbc7->sr >> 8; + memory->sramBank[mbc7->address * 2 + 1] = mbc7->sr; + } + mbc7->sr = 0x1FFFF; + mbc7->state = GBMBC7_STATE_NULL; + } else { + mbc7->state = GBMBC7_STATE_IDLE; + } + } + if (!GBMBC7FieldIsSK(old) && GBMBC7FieldIsSK(value)) { + if (mbc7->state > GBMBC7_STATE_IDLE && mbc7->state != GBMBC7_STATE_READ) { + mbc7->sr <<= 1; + mbc7->sr |= GBMBC7FieldGetIO(value); + ++mbc7->srBits; + } + switch (mbc7->state) { + case GBMBC7_STATE_IDLE: + if (GBMBC7FieldIsIO(value)) { + mbc7->state = GBMBC7_STATE_READ_COMMAND; + mbc7->srBits = 0; + mbc7->sr = 0; + } + break; + case GBMBC7_STATE_READ_COMMAND: + if (mbc7->srBits == 2) { + mbc7->state = GBMBC7_STATE_READ_ADDRESS; + mbc7->srBits = 0; + mbc7->command = mbc7->sr; + } + break; + case GBMBC7_STATE_READ_ADDRESS: + if (mbc7->srBits == 8) { + mbc7->state = GBMBC7_STATE_COMMAND_0 + mbc7->command; + mbc7->srBits = 0; + mbc7->address = mbc7->sr; + if (mbc7->state == GBMBC7_STATE_COMMAND_0) { + switch (mbc7->address >> 6) { + case 0: + mbc7->writable = false; + mbc7->state = GBMBC7_STATE_NULL; + break; + case 3: + mbc7->writable = true; + mbc7->state = GBMBC7_STATE_NULL; + break; + } + } + } + break; + case GBMBC7_STATE_COMMAND_0: + if (mbc7->srBits == 16) { + switch (mbc7->address >> 6) { + case 0: + mbc7->writable = false; + mbc7->state = GBMBC7_STATE_NULL; + break; + case 1: + mbc7->state = GBMBC7_STATE_WRITE; + if (mbc7->writable) { + int i; + for (i = 0; i < 256; ++i) { + memory->sramBank[i * 2] = mbc7->sr >> 8; + memory->sramBank[i * 2 + 1] = mbc7->sr; + } + } + break; + case 2: + mbc7->state = GBMBC7_STATE_WRITE; + if (mbc7->writable) { + int i; + for (i = 0; i < 256; ++i) { + memory->sramBank[i * 2] = 0xFF; + memory->sramBank[i * 2 + 1] = 0xFF; + } + } + break; + case 3: + mbc7->writable = true; + mbc7->state = GBMBC7_STATE_NULL; + break; + } + } + break; + case GBMBC7_STATE_COMMAND_SR_WRITE: + if (mbc7->srBits == 16) { + mbc7->srBits = 0; + mbc7->state = GBMBC7_STATE_WRITE; + } + break; + case GBMBC7_STATE_COMMAND_SR_READ: + if (mbc7->srBits == 1) { + mbc7->sr = memory->sramBank[mbc7->address * 2] << 8; + mbc7->sr |= memory->sramBank[mbc7->address * 2 + 1]; + mbc7->srBits = 0; + mbc7->state = GBMBC7_STATE_READ; + } + break; + case GBMBC7_STATE_COMMAND_SR_FILL: + if (mbc7->srBits == 16) { + mbc7->sr = 0xFFFF; + mbc7->srBits = 0; + mbc7->state = GBMBC7_STATE_WRITE; + } + break; + default: + break; + } + } else if (GBMBC7FieldIsSK(old) && !GBMBC7FieldIsSK(value)) { + if (mbc7->state == GBMBC7_STATE_READ) { + mbc7->sr <<= 1; + ++mbc7->srBits; + if (mbc7->srBits == 16) { + mbc7->srBits = 0; + mbc7->state = GBMBC7_STATE_NULL; + } + } + } +} + +void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + int bank = value & 0x3F; + if (address & 0x1FFF) { + mLOG(GB_MBC, STUB, "HuC-3 unknown value %04X:%02X", address, value); + } + + switch (address >> 13) { + case 0x0: + switch (value) { + case 0xA: + memory->sramAccess = true; + GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + break; + default: + memory->sramAccess = false; + break; + } + break; + case 0x1: + GBMBCSwitchBank(memory, bank); + break; + case 0x2: + GBMBCSwitchSramBank(gb, bank); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "HuC-3 unknown address: %04X:%02X", address, value); + break; + } +}
A src/gb/mbc.h

@@ -0,0 +1,24 @@

+/* Copyright (c) 2013-2016 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef GB_MBC_H +#define GB_MBC_H + +#include "util/common.h" + +#include "core/log.h" + +mLOG_DECLARE_CATEGORY(GB_MBC); + +struct GB; +struct GBMemory; +void GBMBCInit(struct GB* gb); +void GBMBCSwitchBank(struct GBMemory* memory, int bank); +void GBMBCSwitchSramBank(struct GB* gb, int bank); + +uint8_t GBMBC7Read(struct GBMemory*, uint16_t address); +void GBMBC7Write(struct GBMemory*, uint16_t address, uint8_t value); + +#endif
M src/gb/memory.csrc/gb/memory.c

@@ -8,34 +8,14 @@

#include "core/interface.h" #include "gb/gb.h" #include "gb/io.h" +#include "gb/mbc.h" #include "gb/serialize.h" #include "util/memory.h" -#include <time.h> - -mLOG_DEFINE_CATEGORY(GB_MBC, "GB MBC"); mLOG_DEFINE_CATEGORY(GB_MEM, "GB Memory"); static void _pristineCow(struct GB* gba); - -static void _GBMBCNone(struct GBMemory* memory, uint16_t address, uint8_t value) { - UNUSED(memory); - UNUSED(address); - UNUSED(value); - - mLOG(GB_MBC, GAME_ERROR, "Wrote to invalid MBC"); -} - -static void _GBMBC1(struct GBMemory*, uint16_t address, uint8_t value); -static void _GBMBC2(struct GBMemory*, uint16_t address, uint8_t value); -static void _GBMBC3(struct GBMemory*, uint16_t address, uint8_t value); -static void _GBMBC5(struct GBMemory*, uint16_t address, uint8_t value); -static void _GBMBC6(struct GBMemory*, uint16_t address, uint8_t value); -static void _GBMBC7(struct GBMemory*, uint16_t address, uint8_t value); -static uint8_t _GBMBC7Read(struct GBMemory*, uint16_t address); -static void _GBMBC7Write(struct GBMemory*, uint16_t address, uint8_t value); -static void _GBHuC3(struct GBMemory*, uint16_t address, uint8_t value); static uint8_t GBFastLoad8(struct LR35902Core* cpu, uint16_t address) { if (UNLIKELY(address > cpu->memory.activeRegionEnd)) {

@@ -112,11 +92,7 @@ gb->memory.wram = anonymousMemoryMap(GB_SIZE_WORKING_RAM);

GBMemorySwitchWramBank(&gb->memory, 1); gb->memory.romBank = &gb->memory.rom[GB_SIZE_CART_BANK0]; gb->memory.currentBank = 1; - if (!gb->memory.sram) { - gb->memory.sram = anonymousMemoryMap(0x20000); - } gb->memory.sramCurrentBank = 0; - gb->memory.sramBank = gb->memory.sram; gb->memory.ime = false; gb->memory.ie = 0;

@@ -140,60 +116,8 @@

memset(&gb->memory.hram, 0, sizeof(gb->memory.hram)); memset(&gb->memory.mbcState, 0, sizeof(gb->memory.mbcState)); - const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100]; - switch (cart->type) { - case 0: - case 8: - case 9: - gb->memory.mbc = _GBMBCNone; - gb->memory.mbcType = GB_MBC_NONE; - break; - case 1: - case 2: - case 3: - gb->memory.mbc = _GBMBC1; - gb->memory.mbcType = GB_MBC1; - break; - case 5: - case 6: - gb->memory.mbc = _GBMBC2; - gb->memory.mbcType = GB_MBC2; - break; - case 0x0F: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - gb->memory.mbc = _GBMBC3; - gb->memory.mbcType = GB_MBC3; - break; - default: - mLOG(GB_MBC, WARN, "Unknown MBC type: %02X", cart->type); - case 0x19: - case 0x1A: - case 0x1B: - gb->memory.mbc = _GBMBC5; - gb->memory.mbcType = GB_MBC5; - break; - case 0x1C: - case 0x1D: - case 0x1E: - gb->memory.mbc = _GBMBC5; - gb->memory.mbcType = GB_MBC5_RUMBLE; - break; - case 0x20: - gb->memory.mbc = _GBMBC6; - gb->memory.mbcType = GB_MBC6; - break; - case 0x22: - gb->memory.mbc = _GBMBC7; - gb->memory.mbcType = GB_MBC7; - break; - case 0xFE: - gb->memory.mbc = _GBHuC3; - gb->memory.mbcType = GB_HuC3; - break; - } + GBMBCInit(gb); + gb->memory.sramBank = gb->memory.sram; if (!gb->memory.wram) { GBMemoryDeinit(gb);

@@ -233,7 +157,7 @@ return memory->rtcRegs[memory->activeRtcReg];

} else if (memory->sramAccess) { return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)]; } else if (memory->mbcType == GB_MBC7) { - return _GBMBC7Read(memory, address); + return GBMBC7Read(memory, address); } else if (memory->mbcType == GB_HuC3) { return 0x01; // TODO: Is this supposed to be the current SRAM bank? }

@@ -279,7 +203,7 @@ case GB_REGION_CART_BANK1:

case GB_REGION_CART_BANK1 + 1: case GB_REGION_CART_BANK1 + 2: case GB_REGION_CART_BANK1 + 3: - memory->mbc(memory, address, value); + memory->mbc(gb, address, value); cpu->memory.setActiveRegion(cpu, cpu->pc); return; case GB_REGION_VRAM:

@@ -294,7 +218,7 @@ memory->rtcRegs[memory->activeRtcReg] = value;

} else if (memory->sramAccess) { memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)] = value; } else if (memory->mbcType == GB_MBC7) { - _GBMBC7Write(memory, address, value); + GBMBC7Write(memory, address, value); } return; case GB_REGION_WORKING_RAM_BANK0:

@@ -361,7 +285,7 @@ } else {

return memory->sram[(address & (GB_SIZE_EXTERNAL_RAM - 1)) + segment *GB_SIZE_EXTERNAL_RAM]; } } else if (memory->mbcType == GB_MBC7) { - return _GBMBC7Read(memory, address); + return GBMBC7Read(memory, address); } else if (memory->mbcType == GB_HuC3) { return 0x01; // TODO: Is this supposed to be the current SRAM bank? }

@@ -616,438 +540,8 @@ *old = oldValue;

} } -static void _switchBank(struct GBMemory* memory, int bank) { - size_t bankStart = bank * GB_SIZE_CART_BANK0; - if (bankStart + GB_SIZE_CART_BANK0 > memory->romSize) { - mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank); - bankStart &= (memory->romSize - 1); - bank = bankStart / GB_SIZE_CART_BANK0; - } - memory->romBank = &memory->rom[bankStart]; - memory->currentBank = bank; -} - -static void _switchSramBank(struct GBMemory* memory, int bank) { - size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM; - memory->sramBank = &memory->sram[bankStart]; - memory->sramCurrentBank = bank; -} - -static void _latchRtc(struct GBMemory* memory) { - time_t t; - struct mRTCSource* rtc = memory->rtc; - if (rtc) { - if (rtc->sample) { - rtc->sample(rtc); - } - t = rtc->unixTime(rtc); - } else { - t = time(0); - } - struct tm date; - localtime_r(&t, &date); - memory->rtcRegs[0] = date.tm_sec; - memory->rtcRegs[1] = date.tm_min; - memory->rtcRegs[2] = date.tm_hour; - memory->rtcRegs[3] = date.tm_yday; // TODO: Persist day counter - memory->rtcRegs[4] &= 0xF0; - memory->rtcRegs[4] |= date.tm_yday >> 8; -} - -void _GBMBC1(struct GBMemory* memory, uint16_t address, uint8_t value) { - int bank = value & 0x1F; - switch (address >> 13) { - case 0x0: - switch (value) { - case 0: - memory->sramAccess = false; - break; - case 0xA: - memory->sramAccess = true; - _switchSramBank(memory, memory->sramCurrentBank); - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC1 unknown value %02X", value); - break; - } - break; - case 0x1: - if (!bank) { - ++bank; - } - _switchBank(memory, bank | (memory->currentBank & 0x60)); - break; - case 0x2: - bank &= 3; - if (!memory->mbcState.mbc1.mode) { - _switchBank(memory, (bank << 5) | (memory->currentBank & 0x1F)); - } else { - _switchSramBank(memory, bank); - } - break; - case 0x3: - memory->mbcState.mbc1.mode = value & 1; - if (memory->mbcState.mbc1.mode) { - _switchBank(memory, memory->currentBank & 0x1F); - } else { - _switchSramBank(memory, 0); - } - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC1 unknown address: %04X:%02X", address, value); - break; - } -} - -void _GBMBC2(struct GBMemory* memory, uint16_t address, uint8_t value) { - int bank = value & 0xF; - switch (address >> 13) { - case 0x0: - switch (value) { - case 0: - memory->sramAccess = false; - break; - case 0xA: - memory->sramAccess = true; - _switchSramBank(memory, memory->sramCurrentBank); - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC1 unknown value %02X", value); - break; - } - break; - case 0x1: - if (!bank) { - ++bank; - } - _switchBank(memory, bank); - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC2 unknown address: %04X:%02X", address, value); - break; - }} - -void _GBMBC3(struct GBMemory* memory, uint16_t address, uint8_t value) { - int bank = value & 0x7F; - switch (address >> 13) { - case 0x0: - switch (value) { - case 0: - memory->sramAccess = false; - break; - case 0xA: - memory->sramAccess = true; - _switchSramBank(memory, memory->sramCurrentBank); - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC3 unknown value %02X", value); - break; - } - break; - case 0x1: - if (!bank) { - ++bank; - } - _switchBank(memory, bank); - break; - case 0x2: - if (value < 4) { - _switchSramBank(memory, value); - memory->rtcAccess = false; - } else if (value >= 8 && value <= 0xC) { - memory->activeRtcReg = value - 8; - memory->rtcAccess = true; - } - break; - case 0x3: - if (memory->rtcLatched && value == 0) { - memory->rtcLatched = false; - } else if (!memory->rtcLatched && value == 1) { - _latchRtc(memory); - memory->rtcLatched = true; - } - break; - } -} - -void _GBMBC5(struct GBMemory* memory, uint16_t address, uint8_t value) { - int bank; - switch (address >> 12) { - case 0x0: - case 0x1: - switch (value) { - case 0: - memory->sramAccess = false; - break; - case 0xA: - memory->sramAccess = true; - _switchSramBank(memory, memory->sramCurrentBank); - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC5 unknown value %02X", value); - break; - } - break; - case 0x2: - bank = (memory->currentBank & 0x100) | value; - _switchBank(memory, bank); - break; - case 0x3: - bank = (memory->currentBank & 0xFF) | ((value & 1) << 8); - _switchBank(memory, bank); - break; - case 0x4: - case 0x5: - if (memory->mbcType == GB_MBC5_RUMBLE && memory->rumble) { - memory->rumble->setRumble(memory->rumble, (value >> 3) & 1); - value &= ~8; - } - _switchSramBank(memory, value & 0xF); - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC5 unknown address: %04X:%02X", address, value); - break; - } -} - -void _GBMBC6(struct GBMemory* memory, uint16_t address, uint8_t value) { - // TODO - mLOG(GB_MBC, STUB, "MBC6 unimplemented"); -} - -void _GBMBC7(struct GBMemory* memory, uint16_t address, uint8_t value) { - int bank = value & 0x7F; - switch (address >> 13) { - case 0x1: - _switchBank(memory, bank); - break; - case 0x2: - if (value < 0x10) { - _switchSramBank(memory, value); - } - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC7 unknown address: %04X:%02X", address, value); - break; - } -} - -uint8_t _GBMBC7Read(struct GBMemory* memory, uint16_t address) { - struct GBMBC7State* mbc7 = &memory->mbcState.mbc7; - switch (address & 0xF0) { - case 0x00: - case 0x10: - case 0x60: - case 0x70: - return 0; - case 0x20: - if (memory->rotation && memory->rotation->readTiltX) { - int32_t x = -memory->rotation->readTiltX(memory->rotation); - x >>= 21; - x += 2047; - return x; - } - return 0xFF; - case 0x30: - if (memory->rotation && memory->rotation->readTiltX) { - int32_t x = -memory->rotation->readTiltX(memory->rotation); - x >>= 21; - x += 2047; - return x >> 8; - } - return 7; - case 0x40: - if (memory->rotation && memory->rotation->readTiltY) { - int32_t y = -memory->rotation->readTiltY(memory->rotation); - y >>= 21; - y += 2047; - return y; - } - return 0xFF; - case 0x50: - if (memory->rotation && memory->rotation->readTiltY) { - int32_t y = -memory->rotation->readTiltY(memory->rotation); - y >>= 21; - y += 2047; - return y >> 8; - } - return 7; - case 0x80: - return (mbc7->sr >> 16) & 1; - default: - return 0xFF; - } -} - -void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value) { - if ((address & 0xF0) != 0x80) { - return; - } - struct GBMBC7State* mbc7 = &memory->mbcState.mbc7; - GBMBC7Field old = memory->mbcState.mbc7.field; - mbc7->field = GBMBC7FieldClearIO(value); - if (!GBMBC7FieldIsCS(old) && GBMBC7FieldIsCS(value)) { - if (mbc7->state == GBMBC7_STATE_WRITE) { - if (mbc7->writable) { - memory->sramBank[mbc7->address * 2] = mbc7->sr >> 8; - memory->sramBank[mbc7->address * 2 + 1] = mbc7->sr; - } - mbc7->sr = 0x1FFFF; - mbc7->state = GBMBC7_STATE_NULL; - } else { - mbc7->state = GBMBC7_STATE_IDLE; - } - } - if (!GBMBC7FieldIsSK(old) && GBMBC7FieldIsSK(value)) { - if (mbc7->state > GBMBC7_STATE_IDLE && mbc7->state != GBMBC7_STATE_READ) { - mbc7->sr <<= 1; - mbc7->sr |= GBMBC7FieldGetIO(value); - ++mbc7->srBits; - } - switch (mbc7->state) { - case GBMBC7_STATE_IDLE: - if (GBMBC7FieldIsIO(value)) { - mbc7->state = GBMBC7_STATE_READ_COMMAND; - mbc7->srBits = 0; - mbc7->sr = 0; - } - break; - case GBMBC7_STATE_READ_COMMAND: - if (mbc7->srBits == 2) { - mbc7->state = GBMBC7_STATE_READ_ADDRESS; - mbc7->srBits = 0; - mbc7->command = mbc7->sr; - } - break; - case GBMBC7_STATE_READ_ADDRESS: - if (mbc7->srBits == 8) { - mbc7->state = GBMBC7_STATE_COMMAND_0 + mbc7->command; - mbc7->srBits = 0; - mbc7->address = mbc7->sr; - if (mbc7->state == GBMBC7_STATE_COMMAND_0) { - switch (mbc7->address >> 6) { - case 0: - mbc7->writable = false; - mbc7->state = GBMBC7_STATE_NULL; - break; - case 3: - mbc7->writable = true; - mbc7->state = GBMBC7_STATE_NULL; - break; - } - } - } - break; - case GBMBC7_STATE_COMMAND_0: - if (mbc7->srBits == 16) { - switch (mbc7->address >> 6) { - case 0: - mbc7->writable = false; - mbc7->state = GBMBC7_STATE_NULL; - break; - case 1: - mbc7->state = GBMBC7_STATE_WRITE; - if (mbc7->writable) { - int i; - for (i = 0; i < 256; ++i) { - memory->sramBank[i * 2] = mbc7->sr >> 8; - memory->sramBank[i * 2 + 1] = mbc7->sr; - } - } - break; - case 2: - mbc7->state = GBMBC7_STATE_WRITE; - if (mbc7->writable) { - int i; - for (i = 0; i < 256; ++i) { - memory->sramBank[i * 2] = 0xFF; - memory->sramBank[i * 2 + 1] = 0xFF; - } - } - break; - case 3: - mbc7->writable = true; - mbc7->state = GBMBC7_STATE_NULL; - break; - } - } - break; - case GBMBC7_STATE_COMMAND_SR_WRITE: - if (mbc7->srBits == 16) { - mbc7->srBits = 0; - mbc7->state = GBMBC7_STATE_WRITE; - } - break; - case GBMBC7_STATE_COMMAND_SR_READ: - if (mbc7->srBits == 1) { - mbc7->sr = memory->sramBank[mbc7->address * 2] << 8; - mbc7->sr |= memory->sramBank[mbc7->address * 2 + 1]; - mbc7->srBits = 0; - mbc7->state = GBMBC7_STATE_READ; - } - break; - case GBMBC7_STATE_COMMAND_SR_FILL: - if (mbc7->srBits == 16) { - mbc7->sr = 0xFFFF; - mbc7->srBits = 0; - mbc7->state = GBMBC7_STATE_WRITE; - } - break; - default: - break; - } - } else if (GBMBC7FieldIsSK(old) && !GBMBC7FieldIsSK(value)) { - if (mbc7->state == GBMBC7_STATE_READ) { - mbc7->sr <<= 1; - ++mbc7->srBits; - if (mbc7->srBits == 16) { - mbc7->srBits = 0; - mbc7->state = GBMBC7_STATE_NULL; - } - } - } -} - -void _GBHuC3(struct GBMemory* memory, uint16_t address, uint8_t value) { - int bank = value & 0x3F; - if (address & 0x1FFF) { - mLOG(GB_MBC, STUB, "HuC-3 unknown value %04X:%02X", address, value); - } - - switch (address >> 13) { - case 0x0: - switch (value) { - case 0xA: - memory->sramAccess = true; - _switchSramBank(memory, memory->sramCurrentBank); - break; - default: - memory->sramAccess = false; - break; - } - break; - case 0x1: - _switchBank(memory, bank); - break; - case 0x2: - _switchSramBank(memory, bank); - break; - default: - // TODO - mLOG(GB_MBC, STUB, "HuC-3 unknown address: %04X:%02X", address, value); - break; - } -} - -void GBMemorySerialize(const struct GBMemory* memory, struct GBSerializedState* state) { +void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) { + const struct GBMemory* memory = &gb->memory; memcpy(state->wram, memory->wram, GB_SIZE_WORKING_RAM); memcpy(state->hram, memory->hram, GB_SIZE_HRAM); STORE_16LE(memory->currentBank, 0, &state->memory.currentBank);

@@ -1076,16 +570,17 @@ flags = GBSerializedMemoryFlagsSetActiveRtcReg(flags, memory->activeRtcReg);

STORE_16LE(flags, 0, &state->memory.flags); } -void GBMemoryDeserialize(struct GBMemory* memory, const struct GBSerializedState* state) { +void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { + struct GBMemory* memory = &gb->memory; memcpy(memory->wram, state->wram, GB_SIZE_WORKING_RAM); memcpy(memory->hram, state->hram, GB_SIZE_HRAM); LOAD_16LE(memory->currentBank, 0, &state->memory.currentBank); memory->wramCurrentBank = state->memory.wramCurrentBank; memory->sramCurrentBank = state->memory.sramCurrentBank; - _switchBank(memory, memory->currentBank); + GBMBCSwitchBank(memory, memory->currentBank); GBMemorySwitchWramBank(memory, memory->wramCurrentBank); - _switchSramBank(memory, memory->sramCurrentBank); + GBMBCSwitchSramBank(gb, memory->sramCurrentBank); LOAD_32LE(memory->dmaNext, 0, &state->memory.dmaNext); LOAD_16LE(memory->dmaSource, 0, &state->memory.dmaSource);

@@ -1116,5 +611,5 @@ }

gb->memory.rom = anonymousMemoryMap(GB_SIZE_CART_MAX); memcpy(gb->memory.rom, gb->pristineRom, gb->memory.romSize); memset(((uint8_t*) gb->memory.rom) + gb->memory.romSize, 0xFF, GB_SIZE_CART_MAX - gb->memory.romSize); - _switchBank(&gb->memory, gb->memory.currentBank); + GBMBCSwitchBank(&gb->memory, gb->memory.currentBank); }
M src/gb/memory.hsrc/gb/memory.h

@@ -70,7 +70,7 @@ GB_MBC5_RUMBLE = 0x105

}; struct GBMemory; -typedef void (*GBMemoryBankController)(struct GBMemory*, uint16_t address, uint8_t value); +typedef void (*GBMemoryBankController)(struct GB*, uint16_t address, uint8_t value); DECL_BITFIELD(GBMBC7Field, uint8_t); DECL_BIT(GBMBC7Field, SK, 6);

@@ -177,7 +177,7 @@

void GBPatch8(struct LR35902Core* cpu, uint16_t address, int8_t value, int8_t* old); struct GBSerializedState; -void GBMemorySerialize(const struct GBMemory* memory, struct GBSerializedState* state); -void GBMemoryDeserialize(struct GBMemory* memory, const struct GBSerializedState* state); +void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state); +void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state); #endif
M src/gb/serialize.csrc/gb/serialize.c

@@ -58,7 +58,7 @@ flags = GBSerializedCpuFlagsSetIrqPending(flags, gb->cpu->irqPending);

flags = GBSerializedCpuFlagsSetDoubleSpeed(flags, gb->doubleSpeed); STORE_32LE(flags, 0, &state->cpu.flags); - GBMemorySerialize(&gb->memory, state); + GBMemorySerialize(gb, state); GBIOSerialize(gb, state); GBVideoSerialize(&gb->video, state); GBTimerSerialize(&gb->timer, state);

@@ -160,7 +160,7 @@ } else {

gb->audio.style = GB_AUDIO_CGB; } - GBMemoryDeserialize(&gb->memory, state); + GBMemoryDeserialize(gb, state); GBIODeserialize(gb, state); GBVideoDeserialize(&gb->video, state); GBTimerDeserialize(&gb->timer, state);