GB: Save auto-sizing

This commit is contained in:
Jeffrey Pfau 2016-09-06 11:15:27 -07:00
parent f5bf1221eb
commit 87d4dad893
8 changed files with 638 additions and 540 deletions

View File

@ -9,6 +9,7 @@
#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 @@ static size_t _GBCoreSavedataClone(struct mCore* core, void** sram) {
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 @@ static bool _GBCoreSavedataLoad(struct mCore* core, const void* sram, size_t siz
if (size > 0x20000) {
size = 0x20000;
}
memcpy(gb->memory.sram, sram, 0x20000);
GBResizeSram(gb, size);
memcpy(gb->memory.sram, sram, size);
return true;
}

View File

@ -100,30 +100,66 @@ bool GBLoadROM(struct GB* gb, struct VFile* vf) {
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 @@ void GBSavedataUnmask(struct GB* gb) {
}
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) {

View File

@ -67,6 +67,7 @@ struct GB {
struct VFile* biosVf;
struct VFile* sramVf;
struct VFile* sramRealVf;
uint32_t sramSize;
struct mAVStream* stream;
@ -109,6 +110,7 @@ void GBHalt(struct LR35902Core* cpu);
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);

539
src/gb/mbc.c Normal file
View File

@ -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;
}
}

24
src/gb/mbc.h Normal file
View File

@ -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

View File

@ -8,35 +8,15 @@
#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)) {
cpu->memory.setActiveRegion(cpu, address);
@ -112,11 +92,7 @@ void GBMemoryReset(struct GB* gb) {
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 @@ void GBMemoryReset(struct GB* gb) {
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 @@ uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address) {
} 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 @@ void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) {
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 @@ void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t 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 @@ uint8_t GBView8(struct LR35902Core* cpu, uint16_t address, int segment) {
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 @@ void GBPatch8(struct LR35902Core* cpu, uint16_t address, int8_t value, int8_t* o
}
}
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 @@ void GBMemorySerialize(const struct GBMemory* memory, struct GBSerializedState*
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 @@ void _pristineCow(struct GB* gb) {
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);
}

View File

@ -70,7 +70,7 @@ enum GBMemoryBankControllerType {
};
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 GBDMAStore8(struct LR35902Core* cpu, uint16_t address, int8_t value);
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

View File

@ -58,7 +58,7 @@ void GBSerialize(struct GB* gb, struct GBSerializedState* state) {
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 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
gb->audio.style = GB_AUDIO_CGB;
}
GBMemoryDeserialize(&gb->memory, state);
GBMemoryDeserialize(gb, state);
GBIODeserialize(gb, state);
GBVideoDeserialize(&gb->video, state);
GBTimerDeserialize(&gb->timer, state);