mirror of https://github.com/mgba-emu/mgba.git
GB: Support for GBX ROMs
This commit is contained in:
parent
fcd2664761
commit
8ac1ece17a
1
CHANGES
1
CHANGES
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
85
src/gb/gb.c
85
src/gb/gb.c
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
58
src/gb/mbc.c
58
src/gb/mbc.c
|
@ -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;
|
||||
|
|
|
@ -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, ¶ms->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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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(¶ms, 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))
|
Loading…
Reference in New Issue