GB: Support for GBX ROMs

This commit is contained in:
Vicki Pfau 2022-02-10 14:25:21 -08:00
parent fcd2664761
commit 8ac1ece17a
7 changed files with 734 additions and 19 deletions

View File

@ -13,6 +13,7 @@ Features:
- Additional scaling shaders
- Support for GameShark Advance SP (.gsv) save file importing
- Support for multiple saves per game using .sa2, .sa3, etc.
- Support for GBX format Game Boy ROMs
- New unlicensed GB mappers: NT (newer type), Sachen (MMC1, MMC2)
Emulation fixes:
- ARM7: Fix unsigned multiply timing

View File

@ -72,6 +72,19 @@ enum GBSGBCommand {
SGB_OBJ_TRN
};
struct GBXMetadata {
enum GBMemoryBankControllerType mbc;
bool battery;
bool rumble;
bool timer;
uint32_t romSize;
uint32_t ramSize;
union {
uint8_t u8[32];
uint32_t u32[8];
} mapperVars;
};
struct SM83Core;
struct mCoreSync;
struct mAVStream;
@ -85,6 +98,7 @@ struct GB {
struct GBAudio audio;
struct GBSIO sio;
enum GBModel model;
struct GBXMetadata gbx;
struct mCoreSync* sync;
struct mTiming timing;
@ -165,6 +179,7 @@ bool GBLoadSave(struct GB* gb, struct VFile* vf);
void GBUnloadROM(struct GB* gb);
void GBSynthesizeROM(struct VFile* vf);
void GBYankROM(struct GB* gb);
bool GBLoadGBX(struct GBXMetadata* metadata, struct VFile* vf);
void GBLoadBIOS(struct GB* gb, struct VFile* vf);

View File

@ -24,6 +24,8 @@ void GBMBCSwitchHalfBank(struct GB* gb, int half, int bank);
void GBMBCSwitchSramBank(struct GB* gb, int bank);
void GBMBCSwitchSramHalfBank(struct GB* gb, int half, int bank);
enum GBMemoryBankControllerType GBMBCFromGBX(const void* fourcc);
enum GBCam {
GBCAM_WIDTH = 128,
GBCAM_HEIGHT = 112

View File

@ -30,6 +30,7 @@ set(DEBUGGER_FILES
set(TEST_FILES
test/core.c
test/gbx.c
test/mbc.c
test/memory.c
test/rtc.c)

View File

@ -84,6 +84,8 @@ static void GBInit(void* cpu, struct mCPUComponent* component) {
gb->pristineRomSize = 0;
gb->yankedRomSize = 0;
memset(&gb->gbx, 0, sizeof(gb->gbx));
mCoreCallbacksListInit(&gb->coreCallbacks, 0);
gb->stream = NULL;
@ -101,13 +103,79 @@ static void GBDeinit(struct mCPUComponent* component) {
mTimingDeinit(&gb->timing);
}
bool GBLoadGBX(struct GBXMetadata* metadata, struct VFile* vf) {
uint8_t footer[16];
if (vf->seek(vf, -sizeof(footer), SEEK_END) < 0) {
return false;
}
if (vf->read(vf, footer, sizeof(footer)) < (ssize_t) sizeof(footer)) {
return false;
}
int32_t gbxSize = 0;
uint32_t vers;
LOAD_32BE(gbxSize, 0, footer);
LOAD_32BE(vers, 4, footer);
if (memcmp(&footer[12], "GBX!", 4) != 0 || gbxSize != 0x40 || vers != 1) {
return false;
}
if (vf->seek(vf, -gbxSize, SEEK_END) < 0) {
return false;
}
if (vf->read(vf, footer, sizeof(footer)) != (ssize_t) sizeof(footer)) {
return false;
}
memset(metadata, 0, sizeof(*metadata));
metadata->mbc = GBMBCFromGBX(footer);
if (footer[4] == 1) {
metadata->battery = true;
}
if (footer[5] == 1) {
metadata->rumble = true;
if (metadata->mbc == GB_MBC5) {
metadata->mbc = GB_MBC5_RUMBLE;
}
}
if (footer[6] == 1) {
metadata->timer = true;
if (metadata->mbc == GB_MBC3) {
metadata->mbc = GB_MBC3_RTC;
}
}
LOAD_32BE(metadata->romSize, 8, footer);
LOAD_32BE(metadata->ramSize, 12, footer);
vf->read(vf, &metadata->mapperVars, 0x20);
// There's no dedicated mapper type for MBC1M so let's stash some data here
if (memcmp(footer, "MBC1", 4) == 0) {
metadata->mapperVars.u8[0] = 5;
} else if (memcmp(footer, "MB1M", 4) == 0) {
metadata->mapperVars.u8[0] = 4;
}
return true;
}
bool GBLoadROM(struct GB* gb, struct VFile* vf) {
if (!vf) {
return false;
}
GBUnloadROM(gb);
gb->romVf = vf;
if (!GBLoadGBX(&gb->gbx, vf)) {
// GBX handles the pristine size itself, but other formats don't
gb->pristineRomSize = vf->size(vf);
} else {
uint32_t fileSize = vf->size(vf);
if (gb->gbx.romSize <= fileSize - 0x40) {
gb->pristineRomSize = gb->gbx.romSize;
} else {
// TODO: Should we make a temporary buffer?
mLOG(GB, WARN, "GBX file size %d is larger than real file size %d", gb->gbx.romSize, fileSize - 0x40);
gb->pristineRomSize = fileSize - 0x40;
}
}
gb->romVf = vf;
vf->seek(vf, 0, SEEK_SET);
gb->isPristine = true;
gb->memory.rom = vf->map(vf, gb->pristineRomSize, MAP_READ);
@ -921,6 +989,21 @@ bool GBIsROM(struct VFile* vf) {
// Sachen MMC2 scrambled header
return true;
}
uint8_t footer[16];
vf->seek(vf, -sizeof(footer), SEEK_END);
if (vf->read(vf, footer, sizeof(footer)) < (ssize_t) sizeof(footer)) {
return false;
}
uint32_t size;
uint32_t vers;
LOAD_32BE(size, 0, footer);
LOAD_32BE(vers, 4, footer);
if (memcmp(&footer[12], "GBX!", 4) == 0 && size == 0x40 && vers == 1) {
// GBX file
return true;
}
return false;
}

View File

@ -131,6 +131,55 @@ void GBMBCSwitchHalfBank(struct GB* gb, int half, int bank) {
}
}
static struct {
const char* fourcc;
enum GBMemoryBankControllerType mbc;
} _gbxToMbc[] = {
{"ROM", GB_MBC_NONE},
{"MBC1", GB_MBC1},
{"MBC2", GB_MBC2},
{"MBC3", GB_MBC3},
{"MBC5", GB_MBC5},
{"MBC6", GB_MBC6},
{"MBC7", GB_MBC7},
{"MB1M", GB_MBC1},
{"MMM1", GB_MMM01},
{"CAMR", GB_POCKETCAM},
{"HUC1", GB_HuC1},
{"HUC3", GB_HuC3},
{"TAM5", GB_TAMA5},
{"M161", GB_MBC_AUTODETECT}, // TODO
{"BBD", GB_UNL_BBD},
{"HITK", GB_UNL_HITEK},
{"SNTX", GB_MBC_AUTODETECT}, // TODO
{"NTO1", GB_MBC_AUTODETECT}, // TODO
{"NTO2", GB_MBC_AUTODETECT}, // TODO
{"NTN", GB_UNL_NT_NEW},
{"LICH", GB_MBC_AUTODETECT}, // TODO
{"LBMC", GB_MBC_AUTODETECT}, // TODO
{"LIBA", GB_MBC_AUTODETECT}, // TODO
{"PKJD", GB_UNL_PKJD},
{"WISD", GB_UNL_WISDOM_TREE},
{"SAM1", GB_UNL_SACHEN_MMC1},
{"SAM2", GB_UNL_SACHEN_MMC2},
{"ROCK", GB_MBC_AUTODETECT}, // TODO
{"NGHK", GB_MBC_AUTODETECT}, // TODO
{"GB81", GB_MBC_AUTODETECT}, // TODO
{"TPP1", GB_MBC_AUTODETECT}, // TODO
{NULL, GB_MBC_AUTODETECT},
};
enum GBMemoryBankControllerType GBMBCFromGBX(const void* fourcc) {
size_t i;
for (i = 0; _gbxToMbc[i].fourcc; ++i) {
if (memcmp(fourcc, _gbxToMbc[i].fourcc, 4) == 0) {
break;
}
}
return _gbxToMbc[i].mbc;
}
static bool _isMulticart(const uint8_t* mem) {
bool success;
struct VFile* vf;
@ -249,6 +298,10 @@ void GBMBCInit(struct GB* gb) {
cart = cartFooter;
}
}
if (gb->gbx.romSize) {
gb->sramSize = gb->gbx.ramSize;
gb->memory.mbcType = gb->gbx.mbc;
} else {
switch (cart->ramSize) {
case 0:
gb->sramSize = 0;
@ -267,6 +320,7 @@ void GBMBCInit(struct GB* gb) {
gb->sramSize = 0x10000;
break;
}
}
if (gb->memory.mbcType == GB_MBC_AUTODETECT) {
gb->memory.mbcType = _detectUnlMBC(gb->memory.rom, gb->memory.romSize);
}
@ -350,7 +404,9 @@ void GBMBCInit(struct GB* gb) {
break;
case GB_MBC1:
gb->memory.mbcWrite = _GBMBC1;
if (gb->memory.romSize >= GB_SIZE_CART_BANK0 * 0x31 && _isMulticart(gb->memory.rom)) {
if (gb->gbx.mapperVars.u8[0]) {
gb->memory.mbcState.mbc1.multicartStride = gb->gbx.mapperVars.u8[0];
} else if (gb->memory.romSize >= GB_SIZE_CART_BANK0 * 0x31 && _isMulticart(gb->memory.rom)) {
gb->memory.mbcState.mbc1.multicartStride = 4;
} else {
gb->memory.mbcState.mbc1.multicartStride = 5;

557
src/gb/test/gbx.c Normal file
View File

@ -0,0 +1,557 @@
/* Copyright (c) 2013-2021 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 "util/test/suite.h"
#include <mgba/core/core.h>
#include <mgba/gb/core.h>
#include <mgba/internal/gb/gb.h>
#include <mgba/internal/gb/mbc.h>
#include <mgba-util/vfs.h>
#define GBX_FOOTER_SIZE 0x40
struct GBXParams {
uint32_t major;
uint32_t minor;
char fourcc[4];
bool battery;
bool rumble;
bool timer;
uint32_t romSize;
uint32_t ramSize;
union {
uint8_t u8[32];
uint32_t u32[8];
} mapperVars;
};
static struct VFile* makeGBX(const struct GBXParams* params, unsigned padding) {
struct VFile* vf = VFileMemChunk(NULL, padding + GBX_FOOTER_SIZE);
uint8_t bool2flag[2] = {0, 1};
vf->seek(vf, -GBX_FOOTER_SIZE, SEEK_END);
vf->write(vf, params->fourcc, 4);
vf->write(vf, &bool2flag[(int) params->battery], 1);
vf->write(vf, &bool2flag[(int) params->rumble], 1);
vf->write(vf, &bool2flag[(int) params->timer], 1);
vf->write(vf, &bool2flag[0], 1); // Reserved
uint32_t beint;
STORE_32BE(params->romSize, 0, &beint);
vf->write(vf, &beint, 4);
STORE_32BE(params->ramSize, 0, &beint);
vf->write(vf, &beint, 4);
vf->write(vf, &params->mapperVars, 0x20);
STORE_32BE(0x40, 0, &beint); // Footer size
vf->write(vf, &beint, 4);
STORE_32BE(params->major, 0, &beint);
vf->write(vf, &beint, 4);
STORE_32BE(params->minor, 0, &beint);
vf->write(vf, &beint, 4);
vf->write(vf, "GBX!", 4); // Magic
return vf;
}
M_TEST_SUITE_SETUP(GBGBX) {
struct mCore* core = GBCoreCreate();
core->init(core);
mCoreInitConfig(core, NULL);
*state = core;
return 0;
}
M_TEST_SUITE_TEARDOWN(GBGBX) {
if (!*state) {
return 0;
}
struct mCore* core = *state;
mCoreConfigDeinit(&core->config);
core->deinit(core);
return 0;
}
M_TEST_DEFINE(failTooSmall) {
struct mCore* core = *state;
struct GB* gb = core->board;
char truncGbx[0x3F] = {
[0x32] = 0x40,
[0x36] = 0x1,
[0x3B] = 'G',
[0x3C] = 'B',
[0x3D] = 'X',
[0x3E] = '!',
};
struct VFile* vf = VFileFromConstMemory(truncGbx, sizeof(truncGbx));
bool loaded = core->loadROM(core, vf);
assert_false(loaded && gb->pristineRomSize == 0);
}
M_TEST_DEFINE(failNoMagic) {
struct mCore* core = *state;
struct GB* gb = core->board;
char gbx[0x40] = {
[0x0] = 'R',
[0x1] = 'O',
[0x2] = 'M',
[0x33] = 0x40,
[0x37] = 0x1,
[0x3C] = 'G',
[0x3D] = 'B',
[0x3E] = 'X',
};
struct VFile* vf = VFileFromConstMemory(gbx, sizeof(gbx));
bool loaded = core->loadROM(core, vf);
assert_false(loaded && gb->pristineRomSize == 0);
}
M_TEST_DEFINE(invalidVersionLow) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 0,
.fourcc = "ROM",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_false(loaded && gb->pristineRomSize == 0x8000);
}
M_TEST_DEFINE(invalidVersionHigh) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 2,
.fourcc = "ROM",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_false(loaded && gb->pristineRomSize == 0x8000);
}
M_TEST_DEFINE(mbcInvalidNone) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "INVL",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
assert_int_equal(gb->memory.mbcType, GB_MBC_NONE);
}
M_TEST_DEFINE(mbcInvalidFallback) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "INVL",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
vf->seek(vf, 0x147, SEEK_SET);
char one = 1;
vf->write(vf, &one, 1);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
assert_int_equal(gb->memory.mbcType, GB_MBC1);
}
M_TEST_DEFINE(mbcRom) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "ROM",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
assert_int_equal(gb->memory.mbcType, GB_MBC_NONE);
}
M_TEST_DEFINE(mbc1) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "MBC1",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
assert_int_equal(gb->memory.mbcType, GB_MBC1);
assert_int_equal(gb->memory.mbcState.mbc1.multicartStride, 5);
}
M_TEST_DEFINE(mbc2) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "MBC2",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
assert_int_equal(gb->memory.mbcType, GB_MBC2);
}
M_TEST_DEFINE(mbc3) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "MBC3",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
assert_int_equal(gb->memory.mbcType, GB_MBC3);
}
M_TEST_DEFINE(mbc3Rtc) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "MBC3",
.timer = true,
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
assert_int_equal(gb->memory.mbcType, GB_MBC3_RTC);
}
M_TEST_DEFINE(mbc5) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "MBC5",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
assert_int_equal(gb->memory.mbcType, GB_MBC5);
}
M_TEST_DEFINE(mbc5Rumble) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "MBC5",
.romSize = 0x8000,
.rumble = true
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
assert_int_equal(gb->memory.mbcType, GB_MBC5_RUMBLE);
}
M_TEST_DEFINE(mbc6) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "MBC6",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
assert_int_equal(gb->memory.mbcType, GB_MBC6);
}
M_TEST_DEFINE(mbc7) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "MBC7",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
assert_int_equal(gb->memory.mbcType, GB_MBC7);
}
M_TEST_DEFINE(mbc1m) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "MB1M",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
assert_int_equal(gb->memory.mbcType, GB_MBC1);
assert_int_equal(gb->memory.mbcState.mbc1.multicartStride, 4);
}
M_TEST_DEFINE(mmm01) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "MMM1",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
assert_int_equal(gb->memory.mbcType, GB_MMM01);
}
M_TEST_DEFINE(pocketCam) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "CAMR",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
assert_int_equal(gb->memory.mbcType, GB_POCKETCAM);
}
M_TEST_DEFINE(huc1) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "HUC1",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
assert_int_equal(gb->memory.mbcType, GB_HuC1);
}
M_TEST_DEFINE(huc3) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "HUC3",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
assert_int_equal(gb->memory.mbcType, GB_HuC3);
}
M_TEST_DEFINE(tama5) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "TAM5",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
assert_int_equal(gb->memory.mbcType, GB_TAMA5);
}
M_TEST_DEFINE(bbd) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "BBD",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
assert_int_equal(gb->memory.mbcType, GB_UNL_BBD);
}
M_TEST_DEFINE(hitek) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "HITK",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
assert_int_equal(gb->memory.mbcType, GB_UNL_HITEK);
}
M_TEST_DEFINE(ntNew) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "NTN",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
assert_int_equal(gb->memory.mbcType, GB_UNL_NT_NEW);
}
M_TEST_DEFINE(pkjd) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "PKJD",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
assert_int_equal(gb->memory.mbcType, GB_UNL_PKJD);
}
M_TEST_DEFINE(wisdomTree) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "WISD",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
assert_int_equal(gb->memory.mbcType, GB_UNL_WISDOM_TREE);
}
M_TEST_DEFINE(sachenMmc1) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "SAM1",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
assert_int_equal(gb->memory.mbcType, GB_UNL_SACHEN_MMC1);
}
M_TEST_DEFINE(sachenMmc2) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "SAM2",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
assert_int_equal(gb->memory.mbcType, GB_UNL_SACHEN_MMC2);
}
M_TEST_DEFINE(resetMbc1m) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "MB1M",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
core->reset(core);
assert_int_equal(gb->memory.mbcType, GB_MBC1);
assert_int_equal(gb->memory.mbcState.mbc1.multicartStride, 4);
}
M_TEST_DEFINE(fakeRomSize) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "MBC1",
.romSize = 0x8000
};
struct VFile* vf = makeGBX(&params, 0x10000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
}
M_TEST_DEFINE(fakeRamSize) {
struct mCore* core = *state;
struct GB* gb = core->board;
struct GBXParams params = {
.major = 1,
.fourcc = "MBC1",
.romSize = 0x8000,
.ramSize = 0x4000
};
struct VFile* vf = makeGBX(&params, 0x8000);
bool loaded = core->loadROM(core, vf);
assert_true(loaded && gb->pristineRomSize == 0x8000);
assert_true(gb->sramSize == 0x4000);
}
M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(GBGBX,
cmocka_unit_test(failTooSmall),
cmocka_unit_test(failNoMagic),
cmocka_unit_test(invalidVersionLow),
cmocka_unit_test(invalidVersionHigh),
cmocka_unit_test(mbcInvalidNone),
cmocka_unit_test(mbcInvalidFallback),
cmocka_unit_test(mbcRom),
cmocka_unit_test(mbc1),
cmocka_unit_test(mbc2),
cmocka_unit_test(mbc3),
cmocka_unit_test(mbc3Rtc),
cmocka_unit_test(mbc5),
cmocka_unit_test(mbc5Rumble),
cmocka_unit_test(mbc6),
cmocka_unit_test(mbc7),
cmocka_unit_test(mbc1m),
cmocka_unit_test(mmm01),
cmocka_unit_test(pocketCam),
cmocka_unit_test(huc1),
cmocka_unit_test(huc3),
cmocka_unit_test(tama5),
cmocka_unit_test(bbd),
cmocka_unit_test(hitek),
cmocka_unit_test(ntNew),
cmocka_unit_test(pkjd),
cmocka_unit_test(wisdomTree),
cmocka_unit_test(sachenMmc1),
cmocka_unit_test(sachenMmc2),
cmocka_unit_test(resetMbc1m),
cmocka_unit_test(fakeRomSize),
cmocka_unit_test(fakeRamSize))