mirror of https://github.com/mgba-emu/mgba.git
GB: Add overrides
This commit is contained in:
parent
d8a6d940ed
commit
38e3858852
|
@ -10,9 +10,11 @@
|
|||
#include "gb/cli.h"
|
||||
#include "gb/gb.h"
|
||||
#include "gb/mbc.h"
|
||||
#include "gb/overrides.h"
|
||||
#include "gb/renderers/software.h"
|
||||
#include "gb/serialize.h"
|
||||
#include "lr35902/debugger/debugger.h"
|
||||
#include "util/crc32.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/patch.h"
|
||||
#include "util/vfs.h"
|
||||
|
@ -22,6 +24,7 @@ struct GBCore {
|
|||
struct GBVideoSoftwareRenderer renderer;
|
||||
uint8_t keys;
|
||||
struct mCPUComponent* components[CPU_COMPONENT_MAX];
|
||||
const struct Configuration* overrides;
|
||||
struct mDebuggerPlatform* debuggerPlatform;
|
||||
struct mCheatDevice* cheatDevice;
|
||||
};
|
||||
|
@ -38,6 +41,7 @@ static bool _GBCoreInit(struct mCore* core) {
|
|||
}
|
||||
core->cpu = cpu;
|
||||
core->board = gb;
|
||||
gbcore->overrides = NULL;
|
||||
gbcore->debuggerPlatform = NULL;
|
||||
gbcore->cheatDevice = NULL;
|
||||
|
||||
|
@ -107,6 +111,8 @@ static void _GBCoreLoadConfig(struct mCore* core, const struct mCoreConfig* conf
|
|||
if (bios) {
|
||||
GBLoadBIOS(gb, bios);
|
||||
}
|
||||
struct GBCore* gbcore = (struct GBCore*) core;
|
||||
gbcore->overrides = mCoreConfigGetOverridesConst(config);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -200,6 +206,16 @@ static void _GBCoreReset(struct mCore* core) {
|
|||
if (gbcore->renderer.outputBuffer) {
|
||||
GBVideoAssociateRenderer(&gb->video, &gbcore->renderer.d);
|
||||
}
|
||||
|
||||
struct GBCartridgeOverride override;
|
||||
const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
|
||||
if (cart) {
|
||||
override.headerCrc32 = doCrc32(cart, sizeof(*cart));
|
||||
if (GBOverrideFind(gbcore->overrides, &override)) {
|
||||
GBOverrideApply(gb, &override);
|
||||
}
|
||||
}
|
||||
|
||||
LR35902Reset(core->cpu);
|
||||
}
|
||||
|
||||
|
|
101
src/gb/gb.c
101
src/gb/gb.c
|
@ -115,6 +115,10 @@ static void GBSramDeinit(struct GB* gb) {
|
|||
}
|
||||
|
||||
void GBResizeSram(struct GB* gb, size_t size) {
|
||||
if (gb->memory.sram && size <= gb->sramSize) {
|
||||
return;
|
||||
}
|
||||
mLOG(GB, INFO, "Resizing SRAM to %"PRIz"u bytes", size);
|
||||
struct VFile* vf = gb->sramVf;
|
||||
if (vf) {
|
||||
if (vf == gb->sramRealVf) {
|
||||
|
@ -254,27 +258,11 @@ void GBInterruptHandlerInit(struct LR35902InterruptHandler* irqh) {
|
|||
|
||||
void GBReset(struct LR35902Core* cpu) {
|
||||
struct GB* gb = (struct GB*) cpu->master;
|
||||
|
||||
GBDetectModel(gb);
|
||||
if (gb->biosVf) {
|
||||
gb->biosVf->seek(gb->biosVf, 0, SEEK_SET);
|
||||
gb->memory.romBase = malloc(GB_SIZE_CART_BANK0);
|
||||
ssize_t size = gb->biosVf->read(gb->biosVf, gb->memory.romBase, GB_SIZE_CART_BANK0);
|
||||
uint32_t biosCrc = doCrc32(gb->memory.romBase, size);
|
||||
switch (biosCrc) {
|
||||
case 0x59C8598E:
|
||||
gb->model = GB_MODEL_DMG;
|
||||
gb->audio.style = GB_AUDIO_DMG;
|
||||
break;
|
||||
case 0x41884E46:
|
||||
gb->model = GB_MODEL_CGB;
|
||||
gb->audio.style = GB_AUDIO_CGB;
|
||||
break;
|
||||
default:
|
||||
free(gb->memory.romBase);
|
||||
gb->memory.romBase = gb->memory.rom;
|
||||
gb->biosVf = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(&gb->memory.romBase[size], &gb->memory.rom[size], GB_SIZE_CART_BANK0 - size);
|
||||
if (size > 0x100) {
|
||||
|
@ -290,36 +278,41 @@ void GBReset(struct LR35902Core* cpu) {
|
|||
cpu->sp = 0;
|
||||
cpu->pc = 0;
|
||||
}
|
||||
|
||||
cpu->b = 0;
|
||||
cpu->d = 0;
|
||||
|
||||
if (!gb->biosVf) {
|
||||
const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
|
||||
if (cart->cgb & 0x80) {
|
||||
gb->model = GB_MODEL_CGB;
|
||||
gb->audio.style = GB_AUDIO_CGB;
|
||||
cpu->a = 0x11;
|
||||
cpu->f.packed = 0x80;
|
||||
cpu->c = 0;
|
||||
cpu->e = 0x08;
|
||||
cpu->h = 0;
|
||||
cpu->l = 0x7C;
|
||||
} else {
|
||||
switch (gb->model) {
|
||||
case GB_MODEL_DMG:
|
||||
// TODO: SGB
|
||||
case GB_MODEL_SGB:
|
||||
case GB_MODEL_AUTODETECT: // Silence warnings
|
||||
gb->model = GB_MODEL_DMG;
|
||||
gb->audio.style = GB_AUDIO_DMG;
|
||||
cpu->a = 1;
|
||||
cpu->f.packed = 0xB0;
|
||||
cpu->c = 0x13;
|
||||
cpu->e = 0xD8;
|
||||
cpu->h = 1;
|
||||
cpu->l = 0x4D;
|
||||
break;
|
||||
case GB_MODEL_CGB:
|
||||
cpu->b = 1;
|
||||
// Fall through
|
||||
case GB_MODEL_AGB: // Silence warnings
|
||||
cpu->a = 0x11;
|
||||
cpu->f.packed = 0x80;
|
||||
cpu->c = 0;
|
||||
cpu->e = 0x08;
|
||||
cpu->h = 0;
|
||||
cpu->l = 0x7C;
|
||||
break;
|
||||
}
|
||||
|
||||
cpu->sp = 0xFFFE;
|
||||
cpu->pc = 0x100;
|
||||
}
|
||||
|
||||
cpu->b = 0;
|
||||
cpu->d = 0;
|
||||
|
||||
gb->eiPending = INT_MAX;
|
||||
gb->doubleSpeed = 0;
|
||||
|
||||
|
@ -339,6 +332,50 @@ void GBReset(struct LR35902Core* cpu) {
|
|||
GBSavedataUnmask(gb);
|
||||
}
|
||||
|
||||
void GBDetectModel(struct GB* gb) {
|
||||
if (gb->model != GB_MODEL_AUTODETECT) {
|
||||
return;
|
||||
}
|
||||
if (gb->biosVf) {
|
||||
gb->biosVf->seek(gb->biosVf, 0, SEEK_SET);
|
||||
void* bios = malloc(GB_SIZE_CART_BANK0);
|
||||
ssize_t size = gb->biosVf->read(gb->biosVf, bios, GB_SIZE_CART_BANK0);
|
||||
uint32_t biosCrc = doCrc32(gb->memory.romBase, size);
|
||||
switch (biosCrc) {
|
||||
case 0x59C8598E:
|
||||
gb->model = GB_MODEL_DMG;
|
||||
break;
|
||||
case 0x41884E46:
|
||||
gb->model = GB_MODEL_CGB;
|
||||
break;
|
||||
default:
|
||||
gb->biosVf->close(gb->biosVf);
|
||||
gb->biosVf = NULL;
|
||||
}
|
||||
free(bios);
|
||||
}
|
||||
if (gb->model == GB_MODEL_AUTODETECT && gb->memory.rom) {
|
||||
const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
|
||||
if (cart->cgb & 0x80) {
|
||||
gb->model = GB_MODEL_CGB;
|
||||
} else {
|
||||
gb->model = GB_MODEL_DMG;
|
||||
}
|
||||
}
|
||||
|
||||
switch (gb->model) {
|
||||
case GB_MODEL_DMG:
|
||||
case GB_MODEL_SGB:
|
||||
case GB_MODEL_AUTODETECT: //Silence warnings
|
||||
gb->audio.style = GB_AUDIO_DMG;
|
||||
break;
|
||||
case GB_MODEL_AGB:
|
||||
case GB_MODEL_CGB:
|
||||
gb->audio.style = GB_AUDIO_CGB;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GBUpdateIRQs(struct GB* gb) {
|
||||
int irqs = gb->memory.ie & gb->memory.io[REG_IF];
|
||||
if (!irqs) {
|
||||
|
|
|
@ -103,6 +103,7 @@ void GBCreate(struct GB* gb);
|
|||
void GBDestroy(struct GB* gb);
|
||||
|
||||
void GBReset(struct LR35902Core* cpu);
|
||||
void GBDetectModel(struct GB* gb);
|
||||
|
||||
void GBUpdateIRQs(struct GB* gb);
|
||||
void GBHalt(struct LR35902Core* cpu);
|
||||
|
|
|
@ -9,10 +9,27 @@
|
|||
#include "util/common.h"
|
||||
|
||||
enum GBModel {
|
||||
GB_MODEL_AUTODETECT = 0xFF,
|
||||
GB_MODEL_DMG = 0x00,
|
||||
GB_MODEL_SGB = 0x40,
|
||||
GB_MODEL_CGB = 0x80,
|
||||
GB_MODEL_AGB = 0xC0
|
||||
};
|
||||
|
||||
enum GBMemoryBankControllerType {
|
||||
GB_MBC_AUTODETECT = -1,
|
||||
GB_MBC_NONE = 0,
|
||||
GB_MBC1 = 1,
|
||||
GB_MBC2 = 2,
|
||||
GB_MBC3 = 3,
|
||||
GB_MBC5 = 5,
|
||||
GB_MBC6 = 6,
|
||||
GB_MBC7 = 7,
|
||||
GB_MMM01 = 0x10,
|
||||
GB_HuC1 = 0x11,
|
||||
GB_HuC3 = 0x12,
|
||||
GB_MBC3_RTC = 0x103,
|
||||
GB_MBC5_RUMBLE = 0x105
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
114
src/gb/mbc.c
114
src/gb/mbc.c
|
@ -64,62 +64,100 @@ void GBMBCInit(struct GB* gb) {
|
|||
break;
|
||||
}
|
||||
|
||||
switch (cart->type) {
|
||||
case 0:
|
||||
case 8:
|
||||
case 9:
|
||||
if (gb->memory.mbcType == GB_MBC_AUTODETECT) {
|
||||
const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
|
||||
switch (cart->type) {
|
||||
case 0:
|
||||
case 8:
|
||||
case 9:
|
||||
gb->memory.mbcType = GB_MBC_NONE;
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
gb->memory.mbcType = GB_MBC1;
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
gb->memory.mbcType = GB_MBC2;
|
||||
break;
|
||||
case 0x0F:
|
||||
case 0x10:
|
||||
gb->memory.mbcType = GB_MBC3_RTC;
|
||||
break;
|
||||
case 0x11:
|
||||
case 0x12:
|
||||
case 0x13:
|
||||
gb->memory.mbcType = GB_MBC3;
|
||||
break;
|
||||
default:
|
||||
mLOG(GB_MBC, WARN, "Unknown MBC type: %02X", cart->type);
|
||||
// Fall through
|
||||
case 0x19:
|
||||
case 0x1A:
|
||||
case 0x1B:
|
||||
gb->memory.mbcType = GB_MBC5;
|
||||
break;
|
||||
case 0x1C:
|
||||
case 0x1D:
|
||||
case 0x1E:
|
||||
gb->memory.mbcType = GB_MBC5_RUMBLE;
|
||||
break;
|
||||
case 0x20:
|
||||
gb->memory.mbcType = GB_MBC6;
|
||||
break;
|
||||
case 0x22:
|
||||
gb->memory.mbcType = GB_MBC7;
|
||||
break;
|
||||
case 0xFE:
|
||||
gb->memory.mbcType = GB_HuC3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (gb->memory.mbcType) {
|
||||
case GB_MBC_NONE:
|
||||
gb->memory.mbc = _GBMBCNone;
|
||||
gb->memory.mbcType = GB_MBC_NONE;
|
||||
return;
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
gb->memory.mbc = _GBMBC1;
|
||||
gb->memory.mbcType = GB_MBC1;
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
case GB_MBC1:
|
||||
gb->memory.mbc = _GBMBC1;
|
||||
break;
|
||||
case GB_MBC2:
|
||||
gb->memory.mbc = _GBMBC2;
|
||||
gb->memory.mbcType = GB_MBC2;
|
||||
gb->sramSize = 0x200;
|
||||
break;
|
||||
case 0x0F:
|
||||
case 0x10:
|
||||
case 0x11:
|
||||
case 0x12:
|
||||
case 0x13:
|
||||
case GB_MBC3:
|
||||
gb->memory.mbc = _GBMBC3;
|
||||
gb->memory.mbcType = GB_MBC3;
|
||||
gb->sramSize += 0x48;
|
||||
break;
|
||||
default:
|
||||
mLOG(GB_MBC, WARN, "Unknown MBC type: %02X", cart->type);
|
||||
// Fall through
|
||||
case 0x19:
|
||||
case 0x1A:
|
||||
case 0x1B:
|
||||
case GB_MBC5:
|
||||
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:
|
||||
case GB_MBC6:
|
||||
mLOG(GB_MBC, WARN, "unimplemented MBC: MBC6");
|
||||
gb->memory.mbc = _GBMBC6;
|
||||
gb->memory.mbcType = GB_MBC6;
|
||||
gb->sramSize = 0; // TODO
|
||||
break;
|
||||
case 0x22:
|
||||
case GB_MBC7:
|
||||
gb->memory.mbc = _GBMBC7;
|
||||
gb->memory.mbcType = GB_MBC7;
|
||||
gb->sramSize = 0x2000;
|
||||
break;
|
||||
case 0xFE:
|
||||
case GB_MMM01:
|
||||
mLOG(GB_MBC, WARN, "unimplemented MBC: MMM01");
|
||||
gb->memory.mbc = _GBMBC1;
|
||||
break;
|
||||
case GB_HuC1:
|
||||
mLOG(GB_MBC, WARN, "unimplemented MBC: HuC-1");
|
||||
gb->memory.mbc = _GBMBC1;
|
||||
break;
|
||||
case GB_HuC3:
|
||||
gb->memory.mbc = _GBHuC3;
|
||||
gb->memory.mbcType = GB_HuC3;
|
||||
break;
|
||||
case GB_MBC3_RTC:
|
||||
gb->memory.mbc = _GBMBC3;
|
||||
break;
|
||||
case GB_MBC5_RUMBLE:
|
||||
gb->memory.mbc = _GBMBC5;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ void GBMemoryInit(struct GB* gb) {
|
|||
gb->memory.romBank = 0;
|
||||
gb->memory.romSize = 0;
|
||||
gb->memory.sram = 0;
|
||||
gb->memory.mbcType = GB_MBC_NONE;
|
||||
gb->memory.mbcType = GB_MBC_AUTODETECT;
|
||||
gb->memory.mbc = 0;
|
||||
|
||||
gb->memory.rtc = NULL;
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include "util/common.h"
|
||||
|
||||
#include "core/log.h"
|
||||
|
||||
#include "gb/interface.h"
|
||||
#include "lr35902/lr35902.h"
|
||||
|
||||
mLOG_DECLARE_CATEGORY(GB_MBC);
|
||||
|
@ -55,20 +55,6 @@ enum {
|
|||
GB_SIZE_HRAM = 0x7F,
|
||||
};
|
||||
|
||||
enum GBMemoryBankControllerType {
|
||||
GB_MBC_NONE = 0,
|
||||
GB_MBC1 = 1,
|
||||
GB_MBC2 = 2,
|
||||
GB_MBC3 = 3,
|
||||
GB_MBC5 = 5,
|
||||
GB_MBC6 = 6,
|
||||
GB_MBC7 = 7,
|
||||
GB_MMM01 = 0x10,
|
||||
GB_HuC1 = 0x11,
|
||||
GB_HuC3 = 0x12,
|
||||
GB_MBC5_RUMBLE = 0x105
|
||||
};
|
||||
|
||||
struct GBMemory;
|
||||
typedef void (*GBMemoryBankController)(struct GB*, uint16_t address, uint8_t value);
|
||||
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
/* Copyright (c) 2013-2015 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 "overrides.h"
|
||||
|
||||
#include "gb/gb.h"
|
||||
|
||||
#include "util/configuration.h"
|
||||
#include "util/crc32.h"
|
||||
|
||||
static const struct GBCartridgeOverride _overrides[] = {
|
||||
// None yet
|
||||
{ 0, 0, 0 }
|
||||
};
|
||||
|
||||
bool GBOverrideFind(const struct Configuration* config, struct GBCartridgeOverride* override) {
|
||||
override->model = GB_MODEL_AUTODETECT;
|
||||
override->mbc = GB_MBC_AUTODETECT;
|
||||
bool found = false;
|
||||
|
||||
int i;
|
||||
for (i = 0; _overrides[i].headerCrc32; ++i) {
|
||||
if (override->headerCrc32 == _overrides[i].headerCrc32) {
|
||||
*override = _overrides[i];
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (config) {
|
||||
char sectionName[24] = "";
|
||||
snprintf(sectionName, sizeof(sectionName), "gb.override.%08X", override->headerCrc32);
|
||||
const char* model = ConfigurationGetValue(config, sectionName, "model");
|
||||
const char* mbc = ConfigurationGetValue(config, sectionName, "mbc");
|
||||
|
||||
if (model) {
|
||||
if (strcasecmp(model, "DMG") == 0) {
|
||||
found = true;
|
||||
override->model = GB_MODEL_DMG;
|
||||
} else if (strcasecmp(model, "CGB") == 0) {
|
||||
found = true;
|
||||
override->model = GB_MODEL_CGB;
|
||||
} else if (strcasecmp(model, "AGB") == 0) {
|
||||
found = true;
|
||||
override->model = GB_MODEL_AGB;
|
||||
} else if (strcasecmp(model, "SGB") == 0) {
|
||||
found = true;
|
||||
override->model = GB_MODEL_DMG; // TODO
|
||||
} else if (strcasecmp(model, "MGB") == 0) {
|
||||
found = true;
|
||||
override->model = GB_MODEL_DMG; // TODO
|
||||
}
|
||||
}
|
||||
|
||||
if (mbc) {
|
||||
char* end;
|
||||
long type = strtoul(mbc, &end, 0);
|
||||
if (end && !*end) {
|
||||
override->mbc = type;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
void GBOverrideSave(struct Configuration* config, const struct GBCartridgeOverride* override) {
|
||||
char sectionName[24] = "";
|
||||
snprintf(sectionName, sizeof(sectionName), "gb.override.%08X", override->headerCrc32);
|
||||
const char* model = 0;
|
||||
switch (override->model) {
|
||||
case GB_MODEL_DMG:
|
||||
model = "DMG";
|
||||
break;
|
||||
case GB_MODEL_SGB:
|
||||
model = "SGB";
|
||||
break;
|
||||
case GB_MODEL_CGB:
|
||||
model = "CGB";
|
||||
break;
|
||||
case GB_MODEL_AGB:
|
||||
model = "AGB";
|
||||
break;
|
||||
case GB_MODEL_AUTODETECT:
|
||||
break;
|
||||
}
|
||||
ConfigurationSetValue(config, sectionName, "model", model);
|
||||
|
||||
if (override->mbc != GB_MBC_AUTODETECT) {
|
||||
ConfigurationSetIntValue(config, sectionName, "mbc", override->mbc);
|
||||
} else {
|
||||
ConfigurationClearValue(config, sectionName, "mbc");
|
||||
}
|
||||
}
|
||||
|
||||
void GBOverrideApply(struct GB* gb, const struct GBCartridgeOverride* override) {
|
||||
if (override->model != GB_MODEL_AUTODETECT) {
|
||||
gb->model = override->model;
|
||||
}
|
||||
|
||||
if (override->mbc != GB_MBC_AUTODETECT) {
|
||||
gb->memory.mbcType = override->mbc;
|
||||
}
|
||||
}
|
||||
|
||||
void GBOverrideApplyDefaults(struct GB* gb) {
|
||||
struct GBCartridgeOverride override;
|
||||
override.headerCrc32 = doCrc32(&gb->memory.rom[0x100], sizeof(struct GBCartridge));
|
||||
if (GBOverrideFind(0, &override)) {
|
||||
GBOverrideApply(gb, &override);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/* 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_OVERRIDES_H
|
||||
#define GB_OVERRIDES_H
|
||||
|
||||
#include "util/common.h"
|
||||
|
||||
#include "gb/interface.h"
|
||||
|
||||
struct GBCartridgeOverride {
|
||||
int headerCrc32;
|
||||
enum GBModel model;
|
||||
enum GBMemoryBankControllerType mbc;
|
||||
};
|
||||
|
||||
struct Configuration;
|
||||
bool GBOverrideFind(const struct Configuration*, struct GBCartridgeOverride* override);
|
||||
void GBOverrideSave(struct Configuration*, const struct GBCartridgeOverride* override);
|
||||
|
||||
struct GB;
|
||||
void GBOverrideApply(struct GB*, const struct GBCartridgeOverride*);
|
||||
void GBOverrideApplyDefaults(struct GB*);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue