diff --git a/CHANGES b/CHANGES
index e84cbc32b..960dd16fe 100644
--- a/CHANGES
+++ b/CHANGES
@@ -39,6 +39,18 @@ Misc:
- DS Core: Backport symbol loading changes from GBA core (fixes mgba.io/i/1834)
- DS I/O: Mark BGCNT registers as readable
+0.11.0: (Future)
+Features:
+ - New unlicensed GB mappers: NT (older types 1 and 2), Li Cheng
+ - Debugger: Add range watchpoints
+Other fixes:
+ - Qt: Manually split filename to avoid overzealous splitting (fixes mgba.io/i/2681)
+ - Qt: Expand criteria for tag branch names (fixes mgba.io/i/2679)
+ - Res: Fix species name location in Ruby/Sapphire revs 1/2 (fixes mgba.io/i/2685)
+Misc:
+ - GB Serialize: Add missing savestate support for MBC6 and NT (newer)
+ - macOS: Add category to plist (closes mgba.io/i/2691)
+
0.10.0: (2022-10-11)
Features:
- Preliminary Lua scripting support
diff --git a/README.md b/README.md
index af5dc1d04..01411625b 100644
--- a/README.md
+++ b/README.md
@@ -55,11 +55,10 @@ The following mappers are fully supported:
- MBC5+Rumble
- MBC7
- Wisdom Tree (unlicensed)
+- NT "old type" 1 and 2 (unlicensed multicart)
- NT "new type" (unlicensed MBC5-like)
- Pokémon Jade/Diamond (unlicensed)
-- BBD (unlicensed MBC5-like)
-- Hitek (unlicensed MBC5-like)
-- Sachen MMC1
+- Sachen MMC1 (unlicensed)
The following mappers are partially supported:
@@ -70,6 +69,9 @@ The following mappers are partially supported:
- HuC-1 (missing IR support)
- HuC-3 (missing IR support)
- Sachen MMC2 (missing alternate wiring support)
+- BBD (missing logo switching)
+- Hitek (missing logo switching)
+- Li Cheng (missing logo switching)
### Planned features
diff --git a/include/mgba/debugger/debugger.h b/include/mgba/debugger/debugger.h
index fc9ac0f65..b460623e3 100644
--- a/include/mgba/debugger/debugger.h
+++ b/include/mgba/debugger/debugger.h
@@ -88,8 +88,9 @@ struct mBreakpoint {
struct mWatchpoint {
ssize_t id;
- uint32_t address;
int segment;
+ uint32_t minAddress;
+ uint32_t maxAddress;
enum mWatchpointType type;
struct ParseTree* condition;
};
diff --git a/include/mgba/gb/interface.h b/include/mgba/gb/interface.h
index 6b7e1adac..7c23bb8dd 100644
--- a/include/mgba/gb/interface.h
+++ b/include/mgba/gb/interface.h
@@ -41,9 +41,12 @@ enum GBMemoryBankControllerType {
GB_MBC5_RUMBLE = 0x105,
GB_UNL_WISDOM_TREE = 0x200,
GB_UNL_PKJD = 0x203,
+ GB_UNL_NT_OLD_1 = 0x210,
+ GB_UNL_NT_OLD_2 = 0x211,
GB_UNL_NT_NEW = 0x212,
- GB_UNL_BBD = 0x220, // Also used as a mask for MBCs that need special read behavior
+ GB_UNL_BBD = 0x220,
GB_UNL_HITEK = 0x221,
+ GB_UNL_LI_CHENG = 0x222,
GB_UNL_SACHEN_MMC1 = 0x230,
GB_UNL_SACHEN_MMC2 = 0x231,
};
diff --git a/include/mgba/internal/gb/memory.h b/include/mgba/internal/gb/memory.h
index 6e02ffa4d..d3a42a229 100644
--- a/include/mgba/internal/gb/memory.h
+++ b/include/mgba/internal/gb/memory.h
@@ -191,7 +191,6 @@ struct GBMBC1State {
};
struct GBMBC6State {
- bool sramAccess;
bool flashBank0;
bool flashBank1;
};
@@ -238,6 +237,13 @@ struct GBPKJDState {
uint8_t reg[2];
};
+struct GBNTOldState {
+ bool swapped;
+ uint8_t baseBank;
+ uint8_t bankCount;
+ bool rumble;
+};
+
struct GBNTNewState {
bool splitMode;
};
@@ -263,6 +269,7 @@ union GBMBCState {
struct GBPocketCamState pocketCam;
struct GBTAMA5State tama5;
struct GBHuC3State huc3;
+ struct GBNTOldState ntOld;
struct GBNTNewState ntNew;
struct GBPKJDState pkjd;
struct GBBBDState bbd;
diff --git a/include/mgba/internal/gb/serialize.h b/include/mgba/internal/gb/serialize.h
index 3087e6b02..f4433c495 100644
--- a/include/mgba/internal/gb/serialize.h
+++ b/include/mgba/internal/gb/serialize.h
@@ -266,6 +266,10 @@ DECL_BITS(GBSerializedVideoFlags, Mode, 2, 2);
DECL_BIT(GBSerializedVideoFlags, NotModeEventScheduled, 4);
DECL_BIT(GBSerializedVideoFlags, NotFrameEventScheduled, 5);
+DECL_BITFIELD(GBSerializedMBC6Flags, uint8_t);
+DECL_BIT(GBSerializedMBC6Flags, FlashBank0, 0);
+DECL_BIT(GBSerializedMBC6Flags, FlashBank1, 1);
+
DECL_BITFIELD(GBSerializedMBC7Flags, uint8_t);
DECL_BITS(GBSerializedMBC7Flags, Command, 0, 2);
DECL_BIT(GBSerializedMBC7Flags, Writable, 2);
@@ -274,6 +278,10 @@ DECL_BITFIELD(GBSerializedSachenFlags, uint8_t);
DECL_BITS(GBSerializedSachenFlags, Transition, 0, 6);
DECL_BITS(GBSerializedSachenFlags, Locked, 6, 2);
+DECL_BITFIELD(GBSerializedNTOldFlags, uint8_t);
+DECL_BIT(GBSerializedNTOldFlags, Swapped, 0);
+DECL_BIT(GBSerializedNTOldFlags, Rumble, 1);
+
DECL_BITFIELD(GBSerializedMemoryFlags, uint16_t);
DECL_BIT(GBSerializedMemoryFlags, SramAccess, 0);
DECL_BIT(GBSerializedMemoryFlags, RtcAccess, 1);
@@ -392,6 +400,11 @@ struct GBSerializedState {
struct {
uint64_t lastLatch;
} rtc;
+ struct {
+ GBSerializedMBC6Flags flags;
+ uint8_t bank1;
+ uint8_t sramBank1;
+ } mbc6;
struct {
uint8_t state;
GBMBC7Field eeprom;
@@ -416,6 +429,15 @@ struct GBSerializedState {
uint8_t value;
uint8_t mode;
} huc3;
+ struct {
+ GBSerializedNTOldFlags flags;
+ uint8_t baseBank;
+ uint8_t bankCount;
+ } ntOld;
+ struct {
+ uint8_t splitMode;
+ uint8_t bank1;
+ } ntNew;
struct {
uint8_t dataSwapMode;
uint8_t bankSwapMode;
diff --git a/res/info.plist.in b/res/info.plist.in
index d99ab8870..40338ddcf 100644
--- a/res/info.plist.in
+++ b/res/info.plist.in
@@ -66,5 +66,7 @@
Viewer
+ LSApplicationCategoryType
+ public.app-category.games
diff --git a/res/scripts/pokemon.lua b/res/scripts/pokemon.lua
index 7ebe54bb8..d909396d0 100644
--- a/res/scripts/pokemon.lua
+++ b/res/scripts/pokemon.lua
@@ -412,6 +412,13 @@ local gameRubyEn = Generation3En:new{
_speciesNameTable=0x1f716c,
}
+local gameRubyEnR1 = Generation3En:new{
+ name="Ruby (USA)",
+ _party=0x3004360,
+ _partyCount=0x3004350,
+ _speciesNameTable=0x1f7184,
+}
+
local gameSapphireEn = Generation3En:new{
name="Sapphire (USA)",
_party=0x3004360,
@@ -419,6 +426,13 @@ local gameSapphireEn = Generation3En:new{
_speciesNameTable=0x1f70fc,
}
+local gameSapphireEnR1 = Generation3En:new{
+ name="Sapphire (USA)",
+ _party=0x3004360,
+ _partyCount=0x3004350,
+ _speciesNameTable=0x1f7114,
+}
+
local gameEmeraldEn = Generation3En:new{
name="Emerald (USA)",
_party=0x20244ec,
@@ -471,6 +485,10 @@ gameCrc32 = {
[0x7d527d62] = gameYellowEn,
[0x84ee4776] = gameFireRedEnR1,
[0xdaffecec] = gameLeafGreenEnR1,
+ [0x61641576] = gameRubyEnR1, -- Rev 1
+ [0xaeac73e6] = gameRubyEnR1, -- Rev 2
+ [0xbafedae5] = gameSapphireEnR1, -- Rev 1
+ [0x9cc4410e] = gameSapphireEnR1, -- Rev 2
}
function printPartyStatus(game, buffer)
diff --git a/src/arm/debugger/memory-debugger.c b/src/arm/debugger/memory-debugger.c
index 68b4b4175..85c9995ae 100644
--- a/src/arm/debugger/memory-debugger.c
+++ b/src/arm/debugger/memory-debugger.c
@@ -92,12 +92,13 @@ CREATE_MULTIPLE_WATCHPOINT_SHIM(storeMultiple, WATCHPOINT_WRITE)
CREATE_SHIM(setActiveRegion, void, (struct ARMCore* cpu, uint32_t address), address)
static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, struct mDebuggerEntryInfo* info, enum mWatchpointType type, uint32_t newValue, int width) {
- --width;
struct mWatchpoint* watchpoint;
size_t i;
+ uint32_t minAddress = address & ~(width - 1);
+ uint32_t maxAddress = minAddress + width;
for (i = 0; i < mWatchpointListSize(&debugger->watchpoints); ++i) {
watchpoint = mWatchpointListGetPointer(&debugger->watchpoints, i);
- if (!((watchpoint->address ^ address) & ~width) && watchpoint->type & type) {
+ if (watchpoint->type & type && watchpoint->minAddress < maxAddress && minAddress < watchpoint->maxAddress) {
if (watchpoint->condition) {
int32_t value;
int segment;
@@ -107,7 +108,7 @@ static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, st
}
uint32_t oldValue;
- switch (width + 1) {
+ switch (width) {
case 1:
oldValue = debugger->originalMemory.load8(debugger->cpu, address, 0);
break;
diff --git a/src/debugger/cli-debugger.c b/src/debugger/cli-debugger.c
index 7c827905a..12cd7af18 100644
--- a/src/debugger/cli-debugger.c
+++ b/src/debugger/cli-debugger.c
@@ -60,6 +60,10 @@ static void _setReadWriteWatchpoint(struct CLIDebugger*, struct CLIDebugVector*)
static void _setReadWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
static void _setWriteWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
static void _setWriteChangedWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
+static void _setReadWriteRangeWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
+static void _setReadRangeWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
+static void _setWriteRangeWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
+static void _setWriteChangedRangeWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
static void _listWatchpoints(struct CLIDebugger*, struct CLIDebugVector*);
static void _trace(struct CLIDebugger*, struct CLIDebugVector*);
static void _writeByte(struct CLIDebugger*, struct CLIDebugVector*);
@@ -114,6 +118,10 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = {
{ "watch/c", _setWriteChangedWatchpoint, "Is", "Set a change watchpoint" },
{ "watch/r", _setReadWatchpoint, "Is", "Set a read watchpoint" },
{ "watch/w", _setWriteWatchpoint, "Is", "Set a write watchpoint" },
+ { "watch-range", _setReadWriteRangeWatchpoint, "IIs", "Set a range watchpoint" },
+ { "watch-range/c", _setWriteChangedRangeWatchpoint, "IIs", "Set a change range watchpoint" },
+ { "watch-range/r", _setReadRangeWatchpoint, "IIs", "Set a read range watchpoint" },
+ { "watch-range/w", _setWriteRangeWatchpoint, "IIs", "Set a write range watchpoint" },
{ "x/1", _dumpByte, "Ii", "Examine bytes at a specified offset" },
{ "x/2", _dumpHalfword, "Ii", "Examine halfwords at a specified offset" },
{ "x/4", _dumpWord, "Ii", "Examine words at a specified offset" },
@@ -146,6 +154,14 @@ static struct CLIDebuggerCommandAlias _debuggerCommandAliases[] = {
{ "p/x", "print/x" },
{ "q", "quit" },
{ "w", "watch" },
+ { "watchr", "watch-range" },
+ { "wr", "watch-range" },
+ { "watchr/c", "watch-range/c" },
+ { "wr/c", "watch-range/c" },
+ { "watchr/r", "watch-range/r" },
+ { "wr/r", "watch-range/r" },
+ { "watchr/w", "watch-range/w" },
+ { "wr/w", "watch-range/w" },
{ ".", "source" },
{ 0, 0 }
};
@@ -647,8 +663,9 @@ static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector*
return;
}
struct mWatchpoint watchpoint = {
- .address = dv->intValue,
.segment = dv->segmentValue,
+ .minAddress = dv->intValue,
+ .maxAddress = dv->intValue + 1,
.type = type
};
if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) {
@@ -666,6 +683,48 @@ static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector*
}
}
+static void _setRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv, enum mWatchpointType type) {
+ if (!dv || dv->type != CLIDV_INT_TYPE) {
+ debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
+ return;
+ }
+ if (!dv->next || dv->next->type != CLIDV_INT_TYPE) {
+ debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
+ return;
+ }
+ if (!debugger->d.platform->setWatchpoint) {
+ debugger->backend->printf(debugger->backend, "Watchpoints are not supported by this platform.\n");
+ return;
+ }
+ if (dv->intValue >= dv->next->intValue) {
+ debugger->backend->printf(debugger->backend, "Range watchpoint end is before start. Note that the end of the range is not included.\n");
+ return;
+ }
+ if (dv->segmentValue != dv->next->segmentValue) {
+ debugger->backend->printf(debugger->backend, "Range watchpoint does not start and end in the same segment.\n");
+ return;
+ }
+ struct mWatchpoint watchpoint = {
+ .segment = dv->segmentValue,
+ .minAddress = dv->intValue,
+ .maxAddress = dv->next->intValue,
+ .type = type
+ };
+ if (dv->next->next && dv->next->next->type == CLIDV_CHAR_TYPE) {
+ struct ParseTree* tree = _parseTree((const char*[]) { dv->next->next->charValue, NULL });
+ if (tree) {
+ watchpoint.condition = tree;
+ } else {
+ debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
+ return;
+ }
+ }
+ ssize_t id = debugger->d.platform->setWatchpoint(debugger->d.platform, &watchpoint);
+ if (id > 0) {
+ debugger->backend->printf(debugger->backend, INFO_WATCHPOINT_ADDED, id);
+ }
+}
+
static void _setReadWriteWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
_setWatchpoint(debugger, dv, WATCHPOINT_RW);
}
@@ -682,6 +741,22 @@ static void _setWriteChangedWatchpoint(struct CLIDebugger* debugger, struct CLID
_setWatchpoint(debugger, dv, WATCHPOINT_WRITE_CHANGE);
}
+static void _setReadWriteRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
+ _setRangeWatchpoint(debugger, dv, WATCHPOINT_RW);
+}
+
+static void _setReadRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
+ _setRangeWatchpoint(debugger, dv, WATCHPOINT_READ);
+}
+
+static void _setWriteRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
+ _setRangeWatchpoint(debugger, dv, WATCHPOINT_WRITE);
+}
+
+static void _setWriteChangedRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
+ _setRangeWatchpoint(debugger, dv, WATCHPOINT_WRITE_CHANGE);
+}
+
static void _clearBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
if (!dv || dv->type != CLIDV_INT_TYPE) {
debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
@@ -717,9 +792,17 @@ static void _listWatchpoints(struct CLIDebugger* debugger, struct CLIDebugVector
for (i = 0; i < mWatchpointListSize(&watchpoints); ++i) {
struct mWatchpoint* watchpoint = mWatchpointListGetPointer(&watchpoints, i);
if (watchpoint->segment >= 0) {
- debugger->backend->printf(debugger->backend, "%" PRIz "i: %02X:%X\n", watchpoint->id, watchpoint->segment, watchpoint->address);
+ if (watchpoint->maxAddress == watchpoint->minAddress + 1) {
+ debugger->backend->printf(debugger->backend, "%" PRIz "i: %02X:%X\n", watchpoint->id, watchpoint->segment, watchpoint->minAddress);
+ } else {
+ debugger->backend->printf(debugger->backend, "%" PRIz "i: %02X:%X-%X\n", watchpoint->id, watchpoint->segment, watchpoint->minAddress, watchpoint->maxAddress);
+ }
} else {
- debugger->backend->printf(debugger->backend, "%" PRIz "i: 0x%X\n", watchpoint->id, watchpoint->address);
+ if (watchpoint->maxAddress == watchpoint->minAddress + 1) {
+ debugger->backend->printf(debugger->backend, "%" PRIz "i: 0x%X\n", watchpoint->id, watchpoint->minAddress);
+ } else {
+ debugger->backend->printf(debugger->backend, "%" PRIz "i: 0x%X-0x%X\n", watchpoint->id, watchpoint->minAddress, watchpoint->maxAddress);
+ }
}
}
mWatchpointListDeinit(&watchpoints);
diff --git a/src/debugger/gdb-stub.c b/src/debugger/gdb-stub.c
index dfc655054..ca8c0d1ec 100644
--- a/src/debugger/gdb-stub.c
+++ b/src/debugger/gdb-stub.c
@@ -532,7 +532,7 @@ static void _processQReadCommand(struct GDBStub* stub, const char* message) {
} else if (!strncmp("Xfer:memory-map:read::", message, 22)) {
if (strlen(stub->memoryMapXml) == 0) {
_generateMemoryMapXml(stub, stub->memoryMapXml);
- }
+ }
_processQXferCommand(stub, message + 22, stub->memoryMapXml);
} else if (!strncmp("Supported:", message, 10)) {
_processQSupportedCommand(stub, message + 10);
@@ -575,7 +575,8 @@ static void _setBreakpoint(struct GDBStub* stub, const char* message) {
.type = BREAKPOINT_HARDWARE
};
struct mWatchpoint watchpoint = {
- .address = address
+ .minAddress = address,
+ .maxAddress = address + 1
};
switch (message[0]) {
@@ -631,10 +632,11 @@ static void _clearBreakpoint(struct GDBStub* stub, const char* message) {
mWatchpointListInit(&watchpoints, 0);
stub->d.platform->listWatchpoints(stub->d.platform, &watchpoints);
for (index = 0; index < mWatchpointListSize(&watchpoints); ++index) {
- if (mWatchpointListGetPointer(&watchpoints, index)->address != address) {
+ struct mWatchpoint* watchpoint = mWatchpointListGetPointer(&watchpoints, index);
+ if (address >= watchpoint->minAddress && address < watchpoint->maxAddress) {
continue;
}
- stub->d.platform->clearBreakpoint(stub->d.platform, mWatchpointListGetPointer(&watchpoints, index)->id);
+ stub->d.platform->clearBreakpoint(stub->d.platform, watchpoint->id);
}
mWatchpointListDeinit(&watchpoints);
break;
diff --git a/src/gb/CMakeLists.txt b/src/gb/CMakeLists.txt
index 74e7800de..a02ab370a 100644
--- a/src/gb/CMakeLists.txt
+++ b/src/gb/CMakeLists.txt
@@ -7,6 +7,12 @@ set(SOURCE_FILES
input.c
io.c
mbc.c
+ mbc/huc-3.c
+ mbc/licensed.c
+ mbc/mbc.c
+ mbc/pocket-cam.c
+ mbc/tama5.c
+ mbc/unlicensed.c
memory.c
overrides.c
serialize.c
diff --git a/src/gb/mbc.c b/src/gb/mbc.c
index d6c39035c..f15cba519 100644
--- a/src/gb/mbc.c
+++ b/src/gb/mbc.c
@@ -3,12 +3,9 @@
* 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
+#include "gb/mbc/mbc-private.h"
-#include
-#include
#include
-#include
#include
#include
#include
@@ -17,12 +14,6 @@ const uint32_t GB_LOGO_HASH = 0x46195417;
mLOG_DEFINE_CATEGORY(GB_MBC, "GB MBC", "gb.mbc");
-static const uint8_t _tama6RTCMask[32] = {
- //0 1 2 3 4 5 6 7 8 9 A B C D E F
- 0xF, 0x7, 0xF, 0x7, 0xF, 0x3, 0x7, 0xF, 0x3, 0xF, 0x1, 0xF, 0xF, 0x0, 0x0, 0x0,
- 0x0, 0x0, 0xF, 0x7, 0xF, 0x3, 0x7, 0xF, 0x3, 0x0, 0x1, 0x3, 0x0, 0x0, 0x0, 0x0,
-};
-
static void _GBMBCNone(struct GB* gb, uint16_t address, uint8_t value) {
UNUSED(address);
UNUSED(value);
@@ -32,42 +23,6 @@ static void _GBMBCNone(struct GB* gb, uint16_t address, uint8_t value) {
}
}
-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 _GBMMM01(struct GB*, uint16_t address, uint8_t value);
-static void _GBHuC1(struct GB*, uint16_t address, uint8_t value);
-static void _GBHuC3(struct GB*, uint16_t address, uint8_t value);
-static void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value);
-static void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value);
-static void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value);
-static void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value);
-static void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value);
-static void _GBBBD(struct GB* gb, uint16_t address, uint8_t value);
-static void _GBHitek(struct GB* gb, uint16_t address, uint8_t value);
-static void _GBSachen(struct GB* gb, uint16_t address, uint8_t value);
-
-static uint8_t _GBMBC2Read(struct GBMemory*, uint16_t address);
-static uint8_t _GBMBC6Read(struct GBMemory*, uint16_t address);
-static uint8_t _GBMBC7Read(struct GBMemory*, uint16_t address);
-static void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value);
-
-static uint8_t _GBTAMA5Read(struct GBMemory*, uint16_t address);
-static uint8_t _GBHuC3Read(struct GBMemory*, uint16_t address);
-static uint8_t _GBPKJDRead(struct GBMemory*, uint16_t address);
-static uint8_t _GBBBDRead(struct GBMemory*, uint16_t address);
-static uint8_t _GBHitekRead(struct GBMemory*, uint16_t address);
-static uint8_t _GBSachenMMC1Read(struct GBMemory*, uint16_t address);
-static uint8_t _GBSachenMMC2Read(struct GBMemory*, uint16_t address);
-
-static uint8_t _GBPocketCamRead(struct GBMemory*, uint16_t address);
-static void _GBPocketCamCapture(struct GBMemory*);
-
-static void _GBMBC6MapChip(struct GB*, int half, uint8_t value);
-
void GBMBCSwitchBank(struct GB* gb, int bank) {
size_t bankStart = bank * GB_SIZE_CART_BANK0;
if (bankStart + GB_SIZE_CART_BANK0 > gb->memory.romSize) {
@@ -159,10 +114,10 @@ static struct {
{"BBD", GB_UNL_BBD},
{"HITK", GB_UNL_HITEK},
{"SNTX", GB_MBC_AUTODETECT}, // TODO
- {"NTO1", GB_MBC_AUTODETECT}, // TODO
- {"NTO2", GB_MBC_AUTODETECT}, // TODO
+ {"NTO1", GB_UNL_NT_OLD_1},
+ {"NTO2", GB_UNL_NT_OLD_2},
{"NTN", GB_UNL_NT_NEW},
- {"LICH", GB_MBC_AUTODETECT}, // TODO
+ {"LICH", GB_UNL_LI_CHENG},
{"LBMC", GB_MBC_AUTODETECT}, // TODO
{"LIBA", GB_MBC_AUTODETECT}, // TODO
{"PKJD", GB_UNL_PKJD},
@@ -255,6 +210,13 @@ static enum GBMemoryBankControllerType _detectUnlMBC(const uint8_t* mem, size_t
if (mem[0x7FFF] != 0x01) { // Make sure we're not using a "fixed" version
return GB_UNL_BBD;
}
+ break;
+ case 0x20d092e2:
+ case 0xd2b57657:
+ if (cart->type == 0x01) { // Make sure we're not using a "fixed" version
+ return GB_UNL_LI_CHENG;
+ }
+ break;
}
if (mem[0x104] == 0xCE && mem[0x144] == 0xED && mem[0x114] == 0x66) {
@@ -486,6 +448,12 @@ void GBMBCInit(struct GB* gb) {
case GB_UNL_WISDOM_TREE:
gb->memory.mbcWrite = _GBWisdomTree;
break;
+ case GB_UNL_NT_OLD_1:
+ gb->memory.mbcWrite = _GBNTOld1;
+ break;
+ case GB_UNL_NT_OLD_2:
+ gb->memory.mbcWrite = _GBNTOld2;
+ break;
case GB_UNL_NT_NEW:
gb->memory.mbcWrite = _GBNTNew;
break;
@@ -505,6 +473,9 @@ void GBMBCInit(struct GB* gb) {
gb->memory.mbcState.bbd.bankSwapMode = 7;
gb->memory.mbcReadBank1 = true;
break;
+ case GB_UNL_LI_CHENG:
+ gb->memory.mbcWrite = _GBLiCheng;
+ break;
case GB_UNL_SACHEN_MMC1:
gb->memory.mbcWrite = _GBSachen;
gb->memory.mbcRead = _GBSachenMMC1Read;
@@ -569,7 +540,6 @@ void GBMBCReset(struct GB* gb) {
case GB_MBC6:
GBMBCSwitchHalfBank(gb, 0, 2);
GBMBCSwitchHalfBank(gb, 1, 3);
- gb->memory.mbcState.mbc6.sramAccess = false;
GBMBCSwitchSramHalfBank(gb, 0, 0);
GBMBCSwitchSramHalfBank(gb, 0, 1);
break;
@@ -583,1626 +553,7 @@ void GBMBCReset(struct GB* gb) {
gb->memory.sramBank = gb->memory.sram;
}
-static void _latchRtc(struct mRTCSource* rtc, uint8_t* rtcRegs, time_t* rtcLastLatch) {
- time_t t;
- if (rtc) {
- if (rtc->sample) {
- rtc->sample(rtc);
- }
- t = rtc->unixTime(rtc);
- } else {
- t = time(0);
- }
- time_t currentLatch = t;
- t -= *rtcLastLatch;
- *rtcLastLatch = currentLatch;
-
- int64_t diff;
- diff = rtcRegs[0] + t % 60;
- if (diff < 0) {
- diff += 60;
- t -= 60;
- }
- rtcRegs[0] = diff % 60;
- t /= 60;
- t += diff / 60;
-
- diff = rtcRegs[1] + t % 60;
- if (diff < 0) {
- diff += 60;
- t -= 60;
- }
- rtcRegs[1] = diff % 60;
- t /= 60;
- t += diff / 60;
-
- diff = rtcRegs[2] + t % 24;
- if (diff < 0) {
- diff += 24;
- t -= 24;
- }
- rtcRegs[2] = diff % 24;
- t /= 24;
- t += diff / 24;
-
- diff = rtcRegs[3] + ((rtcRegs[4] & 1) << 8) + (t & 0x1FF);
- rtcRegs[3] = diff;
- rtcRegs[4] &= 0xFE;
- rtcRegs[4] |= (diff >> 8) & 1;
- if (diff & 0x200) {
- rtcRegs[4] |= 0x80;
- }
-}
-
-static void _GBMBC1Update(struct GB* gb) {
- struct GBMBC1State* state = &gb->memory.mbcState.mbc1;
- int bank = state->bankLo;
- bank &= (1 << state->multicartStride) - 1;
- bank |= state->bankHi << state->multicartStride;
- if (state->mode) {
- GBMBCSwitchBank0(gb, state->bankHi << state->multicartStride);
- GBMBCSwitchSramBank(gb, state->bankHi & 3);
- } else {
- GBMBCSwitchBank0(gb, 0);
- GBMBCSwitchSramBank(gb, 0);
- }
- if (!(state->bankLo & 0x1F)) {
- ++state->bankLo;
- ++bank;
- }
- GBMBCSwitchBank(gb, bank);
-}
-
-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 & 0xF) {
- 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:
- memory->mbcState.mbc1.bankLo = bank;
- _GBMBC1Update(gb);
- break;
- case 0x2:
- bank &= 3;
- memory->mbcState.mbc1.bankHi = bank;
- _GBMBC1Update(gb);
- break;
- case 0x3:
- memory->mbcState.mbc1.mode = value & 1;
- _GBMBC1Update(gb);
- 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 shift = (address & 1) * 4;
- int bank = value & 0xF;
- switch ((address & 0xC100) >> 8) {
- case 0x0:
- switch (value & 0x0F) {
- case 0:
- memory->sramAccess = false;
- break;
- case 0xA:
- memory->sramAccess = true;
- break;
- default:
- // TODO
- mLOG(GB_MBC, STUB, "MBC2 unknown value %02X", value);
- break;
- }
- break;
- case 0x1:
- if (!bank) {
- ++bank;
- }
- GBMBCSwitchBank(gb, bank);
- break;
- case 0x80:
- case 0x81:
- case 0x82:
- case 0x83:
- if (!memory->sramAccess) {
- return;
- }
- address &= 0x1FF;
- memory->sramBank[(address >> 1)] &= 0xF0 >> shift;
- memory->sramBank[(address >> 1)] |= (value & 0xF) << shift;
- gb->sramDirty |= mSAVEDATA_DIRT_NEW;
- break;
- default:
- // TODO
- mLOG(GB_MBC, STUB, "MBC2 unknown address: %04X:%02X", address, value);
- break;
- }
-}
-
-static uint8_t _GBMBC2Read(struct GBMemory* memory, uint16_t address) {
- if (!memory->sramAccess) {
- return 0xFF;
- }
- address &= 0x1FF;
- int shift = (address & 1) * 4;
- return (memory->sramBank[(address >> 1)] >> shift) | 0xF0;
-}
-
-void _GBMBC3(struct GB* gb, uint16_t address, uint8_t value) {
- struct GBMemory* memory = &gb->memory;
- int bank = value;
- switch (address >> 13) {
- case 0x0:
- switch (value & 0xF) {
- 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 (gb->memory.romSize < GB_SIZE_CART_BANK0 * 0x80) {
- bank &= 0x7F;
- }
- if (!bank) {
- ++bank;
- }
- GBMBCSwitchBank(gb, bank);
- break;
- case 0x2:
- bank &= 0xF;
- if (bank < 8) {
- GBMBCSwitchSramBank(gb, value);
- memory->rtcAccess = false;
- } else if (bank <= 0xC) {
- memory->activeRtcReg = bank - 8;
- memory->rtcAccess = true;
- }
- break;
- case 0x3:
- if (memory->rtcLatched && value == 0) {
- memory->rtcLatched = false;
- } else if (!memory->rtcLatched && value == 1) {
- _latchRtc(gb->memory.rtc, gb->memory.rtcRegs, &gb->memory.rtcLastLatch);
- 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(gb, bank);
- break;
- case 0x3:
- bank = (memory->currentBank & 0xFF) | ((value & 1) << 8);
- GBMBCSwitchBank(gb, 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) {
- struct GBMemory* memory = &gb->memory;
- int bank = value;
- switch (address >> 10) {
- case 0:
- switch (value) {
- case 0:
- memory->sramAccess = false;
- break;
- case 0xA:
- memory->sramAccess = true;
- break;
- default:
- // TODO
- mLOG(GB_MBC, STUB, "MBC6 unknown value %02X", value);
- break;
- }
- break;
- case 0x1:
- GBMBCSwitchSramHalfBank(gb, 0, bank);
- break;
- case 0x2:
- GBMBCSwitchSramHalfBank(gb, 1, bank);
- break;
- case 0x3:
- mLOG(GB_MBC, STUB, "MBC6 unimplemented flash OE write: %04X:%02X", address, value);
- break;
- case 0x4:
- mLOG(GB_MBC, STUB, "MBC6 unimplemented flash WE write: %04X:%02X", address, value);
- break;
- case 0x8:
- case 0x9:
- GBMBCSwitchHalfBank(gb, 0, bank);
- break;
- case 0xA:
- case 0xB:
- _GBMBC6MapChip(gb, 0, value);
- break;
- case 0xC:
- case 0xD:
- GBMBCSwitchHalfBank(gb, 1, bank);
- break;
- case 0xE:
- case 0xF:
- _GBMBC6MapChip(gb, 1, value);
- break;
- case 0x28:
- case 0x29:
- case 0x2A:
- case 0x2B:
- if (memory->sramAccess) {
- memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value;
- gb->sramDirty |= mSAVEDATA_DIRT_NEW;
- }
- break;
- case 0x2C:
- case 0x2D:
- case 0x2E:
- case 0x2F:
- if (memory->sramAccess) {
- memory->sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value;
- }
- break;
- default:
- mLOG(GB_MBC, STUB, "MBC6 unknown address: %04X:%02X", address, value);
- break;
- }
-}
-
-uint8_t _GBMBC6Read(struct GBMemory* memory, uint16_t address) {
- if (!memory->sramAccess) {
- return 0xFF;
- }
- switch (address >> 12) {
- case 0xA:
- return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)];
- case 0xB:
- return memory->sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)];
- }
- return 0xFF;
-}
-
-static void _GBMBC6MapChip(struct GB* gb, int half, uint8_t value) {
- if (!half) {
- gb->memory.mbcState.mbc6.flashBank0 = !!(value & 0x08);
- GBMBCSwitchHalfBank(gb, half, gb->memory.currentBank);
- } else {
- gb->memory.mbcState.mbc6.flashBank1 = !!(value & 0x08);
- GBMBCSwitchHalfBank(gb, half, gb->memory.currentBank1);
- }
-}
-
-void _GBMBC7(struct GB* gb, uint16_t address, uint8_t value) {
- int bank = value & 0x7F;
- switch (address >> 13) {
- case 0x0:
- switch (value) {
- default:
- case 0:
- gb->memory.mbcState.mbc7.access = 0;
- break;
- case 0xA:
- gb->memory.mbcState.mbc7.access |= 1;
- break;
- }
- break;
- case 0x1:
- GBMBCSwitchBank(gb, bank);
- break;
- case 0x2:
- if (value == 0x40) {
- gb->memory.mbcState.mbc7.access |= 2;
- } else {
- gb->memory.mbcState.mbc7.access &= ~2;
- }
- break;
- case 0x5:
- _GBMBC7Write(&gb->memory, address, value);
- gb->sramDirty |= mSAVEDATA_DIRT_NEW;
- 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;
- if (mbc7->access != 3) {
- return 0xFF;
- }
- switch (address & 0xF0) {
- case 0x20:
- if (memory->rotation && memory->rotation->readTiltX) {
- int32_t x = -memory->rotation->readTiltX(memory->rotation);
- x >>= 21;
- x += 0x81D0;
- return x;
- }
- return 0xFF;
- case 0x30:
- if (memory->rotation && memory->rotation->readTiltX) {
- int32_t x = -memory->rotation->readTiltX(memory->rotation);
- x >>= 21;
- x += 0x81D0;
- return x >> 8;
- }
- return 7;
- case 0x40:
- if (memory->rotation && memory->rotation->readTiltY) {
- int32_t y = -memory->rotation->readTiltY(memory->rotation);
- y >>= 21;
- y += 0x81D0;
- return y;
- }
- return 0xFF;
- case 0x50:
- if (memory->rotation && memory->rotation->readTiltY) {
- int32_t y = -memory->rotation->readTiltY(memory->rotation);
- y >>= 21;
- y += 0x81D0;
- return y >> 8;
- }
- return 7;
- case 0x60:
- return 0;
- case 0x80:
- return mbc7->eeprom;
- default:
- return 0xFF;
- }
-}
-
-static void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value) {
- struct GBMBC7State* mbc7 = &memory->mbcState.mbc7;
- if (mbc7->access != 3) {
- return;
- }
- switch (address & 0xF0) {
- case 0x00:
- mbc7->latch = (value & 0x55) == 0x55;
- return;
- case 0x10:
- mbc7->latch |= (value & 0xAA);
- if (mbc7->latch == 0xAB && memory->rotation && memory->rotation->sample) {
- memory->rotation->sample(memory->rotation);
- }
- mbc7->latch = 0;
- return;
- default:
- mLOG(GB_MBC, STUB, "MBC7 unknown register: %04X:%02X", address, value);
- return;
- case 0x80:
- break;
- }
- GBMBC7Field old = memory->mbcState.mbc7.eeprom;
- value = GBMBC7FieldFillDO(value); // Hi-Z
- if (!GBMBC7FieldIsCS(old) && GBMBC7FieldIsCS(value)) {
- mbc7->state = GBMBC7_STATE_IDLE;
- }
- if (!GBMBC7FieldIsCLK(old) && GBMBC7FieldIsCLK(value)) {
- if (mbc7->state == GBMBC7_STATE_READ_COMMAND || mbc7->state == GBMBC7_STATE_EEPROM_WRITE || mbc7->state == GBMBC7_STATE_EEPROM_WRAL) {
- mbc7->sr <<= 1;
- mbc7->sr |= GBMBC7FieldGetDI(value);
- ++mbc7->srBits;
- }
- switch (mbc7->state) {
- case GBMBC7_STATE_IDLE:
- if (GBMBC7FieldIsDI(value)) {
- mbc7->state = GBMBC7_STATE_READ_COMMAND;
- mbc7->srBits = 0;
- mbc7->sr = 0;
- }
- break;
- case GBMBC7_STATE_READ_COMMAND:
- if (mbc7->srBits == 10) {
- mbc7->state = 0x10 | (mbc7->sr >> 6);
- if (mbc7->state & 0xC) {
- mbc7->state &= ~0x3;
- }
- mbc7->srBits = 0;
- mbc7->address = mbc7->sr & 0x7F;
- }
- break;
- case GBMBC7_STATE_DO:
- value = GBMBC7FieldSetDO(value, mbc7->sr >> 15);
- mbc7->sr <<= 1;
- --mbc7->srBits;
- if (!mbc7->srBits) {
- mbc7->state = GBMBC7_STATE_IDLE;
- }
- break;
- default:
- break;
- }
- switch (mbc7->state) {
- case GBMBC7_STATE_EEPROM_EWEN:
- mbc7->writable = true;
- mbc7->state = GBMBC7_STATE_IDLE;
- break;
- case GBMBC7_STATE_EEPROM_EWDS:
- mbc7->writable = false;
- mbc7->state = GBMBC7_STATE_IDLE;
- break;
- case GBMBC7_STATE_EEPROM_WRITE:
- if (mbc7->srBits == 16) {
- if (mbc7->writable) {
- memory->sram[mbc7->address * 2] = mbc7->sr >> 8;
- memory->sram[mbc7->address * 2 + 1] = mbc7->sr;
- }
- mbc7->state = GBMBC7_STATE_IDLE;
- }
- break;
- case GBMBC7_STATE_EEPROM_ERASE:
- if (mbc7->writable) {
- memory->sram[mbc7->address * 2] = 0xFF;
- memory->sram[mbc7->address * 2 + 1] = 0xFF;
- }
- mbc7->state = GBMBC7_STATE_IDLE;
- break;
- case GBMBC7_STATE_EEPROM_READ:
- mbc7->srBits = 16;
- mbc7->sr = memory->sram[mbc7->address * 2] << 8;
- mbc7->sr |= memory->sram[mbc7->address * 2 + 1];
- mbc7->state = GBMBC7_STATE_DO;
- value = GBMBC7FieldClearDO(value);
- break;
- case GBMBC7_STATE_EEPROM_WRAL:
- if (mbc7->srBits == 16) {
- if (mbc7->writable) {
- int i;
- for (i = 0; i < 128; ++i) {
- memory->sram[i * 2] = mbc7->sr >> 8;
- memory->sram[i * 2 + 1] = mbc7->sr;
- }
- }
- mbc7->state = GBMBC7_STATE_IDLE;
- }
- break;
- case GBMBC7_STATE_EEPROM_ERAL:
- if (mbc7->writable) {
- int i;
- for (i = 0; i < 128; ++i) {
- memory->sram[i * 2] = 0xFF;
- memory->sram[i * 2 + 1] = 0xFF;
- }
- }
- mbc7->state = GBMBC7_STATE_IDLE;
- break;
- default:
- break;
- }
- } else if (GBMBC7FieldIsCS(value) && GBMBC7FieldIsCLK(old) && !GBMBC7FieldIsCLK(value)) {
- value = GBMBC7FieldSetDO(value, GBMBC7FieldGetDO(old));
- }
- mbc7->eeprom = value;
-}
-
-void _GBMMM01(struct GB* gb, uint16_t address, uint8_t value) {
- struct GBMemory* memory = &gb->memory;
- if (!memory->mbcState.mmm01.locked) {
- switch (address >> 13) {
- case 0x0:
- memory->mbcState.mmm01.locked = true;
- GBMBCSwitchBank0(gb, memory->mbcState.mmm01.currentBank0);
- break;
- case 0x1:
- memory->mbcState.mmm01.currentBank0 &= ~0x7F;
- memory->mbcState.mmm01.currentBank0 |= value & 0x7F;
- break;
- case 0x2:
- memory->mbcState.mmm01.currentBank0 &= ~0x180;
- memory->mbcState.mmm01.currentBank0 |= (value & 0x30) << 3;
- break;
- default:
- // TODO
- mLOG(GB_MBC, STUB, "MMM01 unknown address: %04X:%02X", address, value);
- break;
- }
- return;
- }
- 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(gb, value + memory->mbcState.mmm01.currentBank0);
- break;
- case 0x2:
- GBMBCSwitchSramBank(gb, value);
- break;
- default:
- // TODO
- mLOG(GB_MBC, STUB, "MMM01 unknown address: %04X:%02X", address, value);
- break;
- }
-}
-
-void _GBHuC1(struct GB* gb, uint16_t address, uint8_t value) {
- struct GBMemory* memory = &gb->memory;
- int bank = value & 0x3F;
- switch (address >> 13) {
- case 0x0:
- switch (value) {
- case 0xE:
- memory->sramAccess = false;
- break;
- default:
- memory->sramAccess = true;
- GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
- break;
- }
- break;
- case 0x1:
- GBMBCSwitchBank(gb, bank);
- break;
- case 0x2:
- GBMBCSwitchSramBank(gb, value);
- break;
- default:
- // TODO
- mLOG(GB_MBC, STUB, "HuC-1 unknown address: %04X:%02X", address, value);
- break;
- }
-}
-
-static void _latchHuC3Rtc(struct mRTCSource* rtc, uint8_t* huc3Regs, time_t* rtcLastLatch) {
- time_t t;
- if (rtc) {
- if (rtc->sample) {
- rtc->sample(rtc);
- }
- t = rtc->unixTime(rtc);
- } else {
- t = time(0);
- }
- t -= *rtcLastLatch;
- t /= 60;
-
- if (!t) {
- return;
- }
- *rtcLastLatch += t * 60;
-
- int minutes = huc3Regs[GBHUC3_RTC_MINUTES_HI] << 8;
- minutes |= huc3Regs[GBHUC3_RTC_MINUTES_MI] << 4;
- minutes |= huc3Regs[GBHUC3_RTC_MINUTES_LO];
- minutes += t % 1440;
- t /= 1440;
- if (minutes >= 1440) {
- minutes -= 1440;
- ++t;
- } else if (minutes < 0) {
- minutes += 1440;
- --t;
- }
- huc3Regs[GBHUC3_RTC_MINUTES_LO] = minutes & 0xF;
- huc3Regs[GBHUC3_RTC_MINUTES_MI] = (minutes >> 4) & 0xF;
- huc3Regs[GBHUC3_RTC_MINUTES_HI] = (minutes >> 8) & 0xF;
-
- int days = huc3Regs[GBHUC3_RTC_DAYS_LO];
- days |= huc3Regs[GBHUC3_RTC_DAYS_MI] << 4;
- days |= huc3Regs[GBHUC3_RTC_DAYS_HI] << 8;
-
- days += t;
-
- huc3Regs[GBHUC3_RTC_DAYS_LO] = days & 0xF;
- huc3Regs[GBHUC3_RTC_DAYS_MI] = (days >> 4) & 0xF;
- huc3Regs[GBHUC3_RTC_DAYS_HI] = (days >> 8) & 0xF;
-}
-
-static void _huc3Commit(struct GB* gb, struct GBHuC3State* state) {
- size_t c;
- switch (state->value & 0x70) {
- case 0x10:
- if ((state->index & 0xF8) == 0x10) {
- _latchHuC3Rtc(gb->memory.rtc, state->registers, &gb->memory.rtcLastLatch);
- }
- state->value &= 0xF0;
- state->value |= state->registers[state->index] & 0xF;
- mLOG(GB_MBC, DEBUG, "HuC-3 read: %02X:%X", state->index, state->value & 0xF);
- if (state->value & 0x10) {
- ++state->index;
- }
- break;
- case 0x30:
- mLOG(GB_MBC, DEBUG, "HuC-3 write: %02X:%X", state->index, state->value & 0xF);
- state->registers[state->index] = state->value & 0xF;
- if (state->value & 0x10) {
- ++state->index;
- }
- break;
- case 0x40:
- state->index &= 0xF0;
- state->index |= (state->value) & 0xF;
- mLOG(GB_MBC, DEBUG, "HuC-3 index (low): %02X", state->index);
- break;
- case 0x50:
- state->index &= 0x0F;
- state->index |= ((state->value) & 0xF) << 4;
- mLOG(GB_MBC, DEBUG, "HuC-3 index (high): %02X", state->index);
- break;
- case 0x60:
- switch (state->value & 0xF) {
- case GBHUC3_CMD_LATCH:
- _latchHuC3Rtc(gb->memory.rtc, state->registers, &gb->memory.rtcLastLatch);
- memcpy(state->registers, &state->registers[GBHUC3_RTC_MINUTES_LO], 6);
- mLOG(GB_MBC, DEBUG, "HuC-3 RTC latch");
- break;
- case GBHUC3_CMD_SET_RTC:
- memcpy(&state->registers[GBHUC3_RTC_MINUTES_LO], state->registers, 6);
- mLOG(GB_MBC, DEBUG, "HuC-3 set RTC");
- break;
- case GBHUC3_CMD_RO:
- mLOG(GB_MBC, STUB, "HuC-3 unimplemented read-only mode");
- break;
- case GBHUC3_CMD_TONE:
- if (state->registers[GBHUC3_SPEAKER_ENABLE] == 1) {
- for (c = 0; c < mCoreCallbacksListSize(&gb->coreCallbacks); ++c) {
- struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gb->coreCallbacks, c);
- if (callbacks->alarm) {
- callbacks->alarm(callbacks->context);
- }
- }
- mLOG(GB_MBC, DEBUG, "HuC-3 tone %i", state->registers[GBHUC3_SPEAKER_TONE] & 3);
- }
- break;
- default:
- mLOG(GB_MBC, STUB, "HuC-3 unknown command: %X", state->value & 0xF);
- break;
- }
- state->value = 0xE1;
- break;
- default:
- mLOG(GB_MBC, STUB, "HuC-3 unknown mode commit: %02X:%02X", state->index, state->value);
- break;
- }
-}
-
-void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) {
- struct GBMemory* memory = &gb->memory;
- struct GBHuC3State* state = &memory->mbcState.huc3;
- int bank = value & 0x7F;
- 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;
- }
- state->mode = value;
- break;
- case 0x1:
- GBMBCSwitchBank(gb, bank);
- break;
- case 0x2:
- GBMBCSwitchSramBank(gb, bank);
- break;
- case 0x5:
- switch (state->mode) {
- case GBHUC3_MODE_IN:
- state->value = 0x80 | value;
- break;
- case GBHUC3_MODE_COMMIT:
- _huc3Commit(gb, state);
- break;
- default:
- mLOG(GB_MBC, STUB, "HuC-3 unknown mode write: %02X:%02X", state->mode, value);
- }
- break;
- default:
- // TODO
- mLOG(GB_MBC, STUB, "HuC-3 unknown address: %04X:%02X", address, value);
- break;
- }
-}
-
-uint8_t _GBHuC3Read(struct GBMemory* memory, uint16_t address) {
- struct GBHuC3State* state = &memory->mbcState.huc3;
- switch (state->mode) {
- case GBHUC3_MODE_SRAM_RO:
- case GBHUC3_MODE_SRAM_RW:
- return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
- case GBHUC3_MODE_IN:
- case GBHUC3_MODE_OUT:
- return 0x80 | state->value;
- default:
- return 0xFF;
- }
-}
-
-void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value) {
- struct GBMemory* memory = &gb->memory;
- int bank = value & 0x3F;
- 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, "Pocket Cam unknown value %02X", value);
- break;
- }
- break;
- case 0x1:
- GBMBCSwitchBank(gb, bank);
- break;
- case 0x2:
- if (value < 0x10) {
- GBMBCSwitchSramBank(gb, value);
- memory->mbcState.pocketCam.registersActive = false;
- memory->directSramAccess = true;
- } else {
- memory->mbcState.pocketCam.registersActive = true;
- memory->directSramAccess = false;
- }
- break;
- case 0x5:
- if (!memory->mbcState.pocketCam.registersActive) {
- break;
- }
- address &= 0x7F;
- if (address == 0 && value & 1) {
- value &= 6; // TODO: Timing
- gb->sramDirty |= mSAVEDATA_DIRT_NEW;
- _GBPocketCamCapture(memory);
- }
- if (address < sizeof(memory->mbcState.pocketCam.registers)) {
- memory->mbcState.pocketCam.registers[address] = value;
- }
- break;
- default:
- mLOG(GB_MBC, STUB, "Pocket Cam unknown address: %04X:%02X", address, value);
- break;
- }
-}
-
-uint8_t _GBPocketCamRead(struct GBMemory* memory, uint16_t address) {
- if (memory->mbcState.pocketCam.registersActive) {
- if ((address & 0x7F) == 0) {
- return memory->mbcState.pocketCam.registers[0];
- }
- return 0;
- }
- return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
-}
-
-void _GBPocketCamCapture(struct GBMemory* memory) {
- if (!memory->cam) {
- return;
- }
- const void* image = NULL;
- size_t stride;
- enum mColorFormat format;
- memory->cam->requestImage(memory->cam, &image, &stride, &format);
- if (!image) {
- return;
- }
- memset(&memory->sram[0x100], 0, GBCAM_HEIGHT * GBCAM_WIDTH / 4);
- struct GBPocketCamState* pocketCam = &memory->mbcState.pocketCam;
- size_t x, y;
- for (y = 0; y < GBCAM_HEIGHT; ++y) {
- for (x = 0; x < GBCAM_WIDTH; ++x) {
- uint32_t gray;
- uint32_t color;
- switch (format) {
- case mCOLOR_XBGR8:
- case mCOLOR_XRGB8:
- case mCOLOR_ARGB8:
- case mCOLOR_ABGR8:
- color = ((const uint32_t*) image)[y * stride + x];
- gray = (color & 0xFF) + ((color >> 8) & 0xFF) + ((color >> 16) & 0xFF);
- break;
- case mCOLOR_BGRX8:
- case mCOLOR_RGBX8:
- case mCOLOR_RGBA8:
- case mCOLOR_BGRA8:
- color = ((const uint32_t*) image)[y * stride + x];
- gray = ((color >> 8) & 0xFF) + ((color >> 16) & 0xFF) + ((color >> 24) & 0xFF);
- break;
- case mCOLOR_BGR5:
- case mCOLOR_RGB5:
- case mCOLOR_ARGB5:
- case mCOLOR_ABGR5:
- color = ((const uint16_t*) image)[y * stride + x];
- gray = ((color << 3) & 0xF8) + ((color >> 2) & 0xF8) + ((color >> 7) & 0xF8);
- break;
- case mCOLOR_BGR565:
- case mCOLOR_RGB565:
- color = ((const uint16_t*) image)[y * stride + x];
- gray = ((color << 3) & 0xF8) + ((color >> 3) & 0xFC) + ((color >> 8) & 0xF8);
- break;
- case mCOLOR_BGRA5:
- case mCOLOR_RGBA5:
- color = ((const uint16_t*) image)[y * stride + x];
- gray = ((color << 2) & 0xF8) + ((color >> 3) & 0xF8) + ((color >> 8) & 0xF8);
- break;
- default:
- mLOG(GB_MBC, WARN, "Unsupported pixel format: %X", format);
- return;
- }
- uint16_t exposure = (pocketCam->registers[2] << 8) | (pocketCam->registers[3]);
- gray = (gray + 1) * exposure / 0x300;
- // TODO: Additional processing
- int matrixEntry = 3 * ((x & 3) + 4 * (y & 3));
- if (gray < pocketCam->registers[matrixEntry + 6]) {
- gray = 0x101;
- } else if (gray < pocketCam->registers[matrixEntry + 7]) {
- gray = 0x100;
- } else if (gray < pocketCam->registers[matrixEntry + 8]) {
- gray = 0x001;
- } else {
- gray = 0;
- }
- int coord = (((x >> 3) & 0xF) * 8 + (y & 0x7)) * 2 + (y & ~0x7) * 0x20;
- uint16_t existing;
- LOAD_16LE(existing, coord + 0x100, memory->sram);
- existing |= gray << (7 - (x & 7));
- STORE_16LE(existing, coord + 0x100, memory->sram);
- }
- }
-}
-
-static const int _daysToMonth[] = {
- [ 1] = 0,
- [ 2] = 31,
- [ 3] = 31 + 28,
- [ 4] = 31 + 28 + 31,
- [ 5] = 31 + 28 + 31 + 30,
- [ 6] = 31 + 28 + 31 + 30 + 31,
- [ 7] = 31 + 28 + 31 + 30 + 31 + 30,
- [ 8] = 31 + 28 + 31 + 30 + 31 + 30 + 31,
- [ 9] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
- [10] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
- [11] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
- [12] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
-};
-
-static int _tama6DMYToDayOfYear(int day, int month, int year) {
- if (month < 1 || month > 12) {
- return -1;
- }
- day += _daysToMonth[month];
- if (month > 2 && (year & 3) == 0) {
- ++day;
- }
- return day;
-}
-
-static int _tama6DayOfYearToMonth(int day, int year) {
- int month;
- for (month = 1; month < 12; ++month) {
- if (day <= _daysToMonth[month + 1]) {
- return month;
- }
- if (month == 2 && (year & 3) == 0) {
- if (day == 60) {
- return 2;
- }
- --day;
- }
- }
- return 12;
-}
-
-static int _tama6DayOfYearToDayOfMonth(int day, int year) {
- int month;
- for (month = 1; month < 12; ++month) {
- if (day <= _daysToMonth[month + 1]) {
- return day - _daysToMonth[month];
- }
- if (month == 2 && (year & 3) == 0) {
- if (day == 60) {
- return 29;
- }
- --day;
- }
- }
- return day - _daysToMonth[12];
-}
-
-static void _latchTAMA6Rtc(struct mRTCSource* rtc, struct GBTAMA5State* tama5, time_t* rtcLastLatch) {
- time_t t;
- if (rtc) {
- if (rtc->sample) {
- rtc->sample(rtc);
- }
- t = rtc->unixTime(rtc);
- } else {
- t = time(0);
- }
- time_t currentLatch = t;
- t -= *rtcLastLatch;
- *rtcLastLatch = currentLatch;
- if (!t || tama5->disabled) {
- return;
- }
-
- uint8_t* timerRegs = tama5->rtcTimerPage;
- bool is24hour = tama5->rtcAlarmPage[GBTAMA6_RTC_PA1_24_HOUR];
- int64_t diff;
- diff = timerRegs[GBTAMA6_RTC_PA0_SECOND_1] + timerRegs[GBTAMA6_RTC_PA0_SECOND_10] * 10 + t % 60;
- if (diff < 0) {
- diff += 60;
- t -= 60;
- }
- timerRegs[GBTAMA6_RTC_PA0_SECOND_1] = diff % 10;
- timerRegs[GBTAMA6_RTC_PA0_SECOND_10] = (diff % 60) / 10;
- t /= 60;
- t += diff / 60;
-
- diff = timerRegs[GBTAMA6_RTC_PA0_MINUTE_1] + timerRegs[GBTAMA6_RTC_PA0_MINUTE_10] * 10 + t % 60;
- if (diff < 0) {
- diff += 60;
- t -= 60;
- }
- timerRegs[GBTAMA6_RTC_PA0_MINUTE_1] = diff % 10;
- timerRegs[GBTAMA6_RTC_PA0_MINUTE_10] = (diff % 60) / 10;
- t /= 60;
- t += diff / 60;
-
- diff = timerRegs[GBTAMA6_RTC_PA0_HOUR_1];
- if (is24hour) {
- diff += timerRegs[GBTAMA6_RTC_PA0_HOUR_10] * 10;
- } else {
- int hour10 = timerRegs[GBTAMA6_RTC_PA0_HOUR_10];
- diff += (hour10 & 1) * 10;
- diff += (hour10 & 2) * 12;
- }
- diff += t % 24;
- if (diff < 0) {
- diff += 24;
- t -= 24;
- }
- if (is24hour) {
- timerRegs[GBTAMA6_RTC_PA0_HOUR_1] = (diff % 24) % 10;
- timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 24) / 10;
- } else {
- timerRegs[GBTAMA6_RTC_PA0_HOUR_1] = (diff % 12) % 10;
- timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 12) / 10 + (diff / 12) * 2;
- }
- t /= 24;
- t += diff / 24;
-
- int day = timerRegs[GBTAMA6_RTC_PA0_DAY_1] + timerRegs[GBTAMA6_RTC_PA0_DAY_10] * 10;
- int month = timerRegs[GBTAMA6_RTC_PA0_MONTH_1] + timerRegs[GBTAMA6_RTC_PA0_MONTH_10] * 10;
- int year = timerRegs[GBTAMA6_RTC_PA0_YEAR_1] + timerRegs[GBTAMA6_RTC_PA0_YEAR_10] * 10;
- int leapYear = tama5->rtcAlarmPage[GBTAMA6_RTC_PA1_LEAP_YEAR];
- int dayOfWeek = timerRegs[GBTAMA6_RTC_PA0_WEEK];
- int dayInYear = _tama6DMYToDayOfYear(day, month, leapYear);
- diff = dayInYear + t;
- while (diff <= 0) {
- // Previous year
- if (leapYear & 3) {
- diff += 365;
- } else {
- diff += 366;
- }
- --year;
- --leapYear;
- }
- while (diff > (leapYear & 3 ? 365 : 366)) {
- // Future year
- if (year % 4) {
- diff -= 365;
- } else {
- diff -= 366;
- }
- ++year;
- ++leapYear;
- }
- dayOfWeek = (dayOfWeek + diff) % 7;
- year %= 100;
- leapYear &= 3;
-
- day = _tama6DayOfYearToDayOfMonth(diff, leapYear);
- month = _tama6DayOfYearToMonth(diff, leapYear);
-
- timerRegs[GBTAMA6_RTC_PA0_WEEK] = dayOfWeek;
- tama5->rtcAlarmPage[GBTAMA6_RTC_PA1_LEAP_YEAR] = leapYear;
-
- timerRegs[GBTAMA6_RTC_PA0_DAY_1] = day % 10;
- timerRegs[GBTAMA6_RTC_PA0_DAY_10] = day / 10;
-
- timerRegs[GBTAMA6_RTC_PA0_MONTH_1] = month % 10;
- timerRegs[GBTAMA6_RTC_PA0_MONTH_10] = month / 10;
-
- timerRegs[GBTAMA6_RTC_PA0_YEAR_1] = year % 10;
- timerRegs[GBTAMA6_RTC_PA0_YEAR_10] = year / 10;
-}
-
-void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value) {
- struct GBMemory* memory = &gb->memory;
- struct GBTAMA5State* tama5 = &memory->mbcState.tama5;
- switch (address >> 13) {
- case 0x5:
- if (address & 1) {
- tama5->reg = value;
- } else {
- value &= 0xF;
- if (tama5->reg < GBTAMA5_MAX) {
- mLOG(GB_MBC, DEBUG, "TAMA5 write: %02X:%X", tama5->reg, value);
- tama5->registers[tama5->reg] = value;
- uint8_t address = ((tama5->registers[GBTAMA5_ADDR_HI] << 4) & 0x10) | tama5->registers[GBTAMA5_ADDR_LO];
- uint8_t out = (tama5->registers[GBTAMA5_WRITE_HI] << 4) | tama5->registers[GBTAMA5_WRITE_LO];
- switch (tama5->reg) {
- case GBTAMA5_BANK_LO:
- case GBTAMA5_BANK_HI:
- GBMBCSwitchBank(gb, tama5->registers[GBTAMA5_BANK_LO] | (tama5->registers[GBTAMA5_BANK_HI] << 4));
- break;
- case GBTAMA5_WRITE_LO:
- case GBTAMA5_WRITE_HI:
- case GBTAMA5_ADDR_HI:
- break;
- case GBTAMA5_ADDR_LO:
- switch (tama5->registers[GBTAMA5_ADDR_HI] >> 1) {
- case 0x0: // RAM write
- memory->sram[address] = out;
- gb->sramDirty |= mSAVEDATA_DIRT_NEW;
- break;
- case 0x1: // RAM read
- break;
- case 0x2: // Other commands
- switch (address) {
- case GBTAMA6_DISABLE_TIMER:
- tama5->disabled = true;
- tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] &= 0x7;
- tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] &= 0x7;
- tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] &= 0x7;
- tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] &= 0x7;
- break;
- case GBTAMA6_ENABLE_TIMER:
- tama5->disabled = false;
- tama5->rtcTimerPage[GBTAMA6_RTC_PA0_SECOND_1] = 0;
- tama5->rtcTimerPage[GBTAMA6_RTC_PA0_SECOND_10] = 0;
- tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] |= 0x8;
- tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] |= 0x8;
- tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] |= 0x8;
- tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] |= 0x8;
- break;
- case GBTAMA6_MINUTE_WRITE:
- tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_1] = out & 0xF;
- tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_10] = out >> 4;
- break;
- case GBTAMA6_HOUR_WRITE:
- tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_1] = out & 0xF;
- tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_10] = out >> 4;
- break;
- case GBTAMA6_DISABLE_ALARM:
- tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] &= 0xB;
- tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] &= 0xB;
- tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] &= 0xB;
- tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] &= 0xB;
- break;
- case GBTAMA6_ENABLE_ALARM:
- tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] |= 0x4;
- tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] |= 0x4;
- tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] |= 0x4;
- tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] |= 0x4;
- break;
- }
- break;
- case 0x4: // RTC access
- address = tama5->registers[GBTAMA5_WRITE_LO];
- if (address >= GBTAMA6_RTC_PAGE) {
- break;
- }
- out = tama5->registers[GBTAMA5_WRITE_HI];
- switch (tama5->registers[GBTAMA5_ADDR_LO]) {
- case 0:
- out &= _tama6RTCMask[address];
- tama5->rtcTimerPage[address] = out;
- break;
- case 2:
- out &= _tama6RTCMask[address | 0x10];
- tama5->rtcAlarmPage[address] = out;
- break;
- case 4:
- tama5->rtcFreePage0[address] = out;
- break;
- case 6:
- tama5->rtcFreePage1[address] = out;
- break;
- }
- break;
- default:
- mLOG(GB_MBC, STUB, "TAMA5 unknown address: %02X:%02X", address, out);
- break;
- }
- break;
- default:
- mLOG(GB_MBC, STUB, "TAMA5 unknown write: %02X:%X", tama5->reg, value);
- break;
- }
- } else {
- mLOG(GB_MBC, STUB, "TAMA5 unknown write: %02X", tama5->reg);
- }
- }
- break;
- default:
- mLOG(GB_MBC, STUB, "TAMA5 unknown address: %04X:%02X", address, value);
- }
-}
-
-uint8_t _GBTAMA5Read(struct GBMemory* memory, uint16_t address) {
- struct GBTAMA5State* tama5 = &memory->mbcState.tama5;
- if ((address & 0x1FFF) > 1) {
- mLOG(GB_MBC, STUB, "TAMA5 unknown address: %04X", address);
- }
- if (address & 1) {
- return 0xFF;
- } else {
- uint8_t value = 0xF0;
- uint8_t address = ((tama5->registers[GBTAMA5_ADDR_HI] << 4) & 0x10) | tama5->registers[GBTAMA5_ADDR_LO];
- switch (tama5->reg) {
- case GBTAMA5_ACTIVE:
- return 0xF1;
- case GBTAMA5_READ_LO:
- case GBTAMA5_READ_HI:
- switch (tama5->registers[GBTAMA5_ADDR_HI] >> 1) {
- case 0x1:
- value = memory->sram[address];
- break;
- case 0x2:
- mLOG(GB_MBC, STUB, "TAMA5 unknown read %s: %02X", tama5->reg == GBTAMA5_READ_HI ? "hi" : "lo", address);
- _latchTAMA6Rtc(memory->rtc, tama5, &memory->rtcLastLatch);
- switch (address) {
- case GBTAMA6_MINUTE_READ:
- value = (tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_10] << 4) | tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_1];
- break;
- case GBTAMA6_HOUR_READ:
- value = (tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_10] << 4) | tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_1];
- break;
- default:
- value = address;
- break;
- }
- break;
- case 0x4:
- if (tama5->reg == GBTAMA5_READ_HI) {
- mLOG(GB_MBC, GAME_ERROR, "TAMA5 reading RTC incorrectly");
- break;
- }
- _latchTAMA6Rtc(memory->rtc, tama5, &memory->rtcLastLatch);
- address = tama5->registers[GBTAMA5_WRITE_LO];
- if (address > GBTAMA6_RTC_PAGE) {
- value = 0;
- break;
- }
- switch (tama5->registers[GBTAMA5_ADDR_LO]) {
- case 1:
- value = tama5->rtcTimerPage[address];
- break;
- case 3:
- value = tama5->rtcTimerPage[address];
- break;
- case 5:
- value = tama5->rtcTimerPage[address];
- break;
- case 7:
- value = tama5->rtcTimerPage[address];
- break;
- }
- break;
- default:
- mLOG(GB_MBC, STUB, "TAMA5 unknown read %s: %02X", tama5->reg == GBTAMA5_READ_HI ? "hi" : "lo", address);
- break;
- }
- if (tama5->reg == GBTAMA5_READ_HI) {
- value >>= 4;
- }
- value |= 0xF0;
- return value;
- default:
- mLOG(GB_MBC, STUB, "TAMA5 unknown read: %02X", tama5->reg);
- return 0xF1;
- }
- }
-}
-
-void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value) {
- UNUSED(value);
- int bank = address & 0x3F;
- switch (address >> 14) {
- case 0x0:
- GBMBCSwitchBank0(gb, bank * 2);
- GBMBCSwitchBank(gb, bank * 2 + 1);
- break;
- default:
- // TODO
- mLOG(GB_MBC, STUB, "Wisdom Tree unknown address: %04X:%02X", address, value);
- break;
- }
-}
-
-void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value) {
- struct GBMemory* memory = &gb->memory;
- switch (address >> 13) {
- case 0x2:
- if (value < 8) {
- memory->directSramAccess = true;
- memory->activeRtcReg = 0;
- } else if (value >= 0xD && value <= 0xF) {
- memory->directSramAccess = false;
- memory->rtcAccess = false;
- memory->activeRtcReg = value - 8;
- }
- break;
- case 0x5:
- if (!memory->sramAccess) {
- return;
- }
- switch (memory->activeRtcReg) {
- case 0:
- memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)] = value;
- break;
- case 5:
- case 6:
- memory->mbcState.pkjd.reg[memory->activeRtcReg - 5] = value;
- break;
- case 7:
- switch (value) {
- case 0x11:
- memory->mbcState.pkjd.reg[0]--;
- break;
- case 0x12:
- memory->mbcState.pkjd.reg[1]--;
- break;
- case 0x41:
- memory->mbcState.pkjd.reg[0] += memory->mbcState.pkjd.reg[1];
- break;
- case 0x42:
- memory->mbcState.pkjd.reg[1] += memory->mbcState.pkjd.reg[0];
- break;
- case 0x51:
- memory->mbcState.pkjd.reg[0]++;
- break;
- case 0x52:
- memory->mbcState.pkjd.reg[1]--;
- break;
- }
- break;
- }
- return;
- }
- _GBMBC3(gb, address, value);
-}
-
-static uint8_t _GBPKJDRead(struct GBMemory* memory, uint16_t address) {
- if (!memory->sramAccess) {
- return 0xFF;
- }
- switch (memory->activeRtcReg) {
- case 0:
- return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
- case 5:
- case 6:
- return memory->mbcState.pkjd.reg[memory->activeRtcReg - 5];
- default:
- return 0;
- }
-}
-
-void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value) {
- struct GBMemory* memory = &gb->memory;
- if (address >> 8 == 0x14) {
- memory->mbcState.ntNew.splitMode = true;
- return;
- }
- if (memory->mbcState.ntNew.splitMode) {
- int bank = value;
- if (bank < 2) {
- bank = 2;
- }
- switch (address >> 10) {
- case 8:
- GBMBCSwitchHalfBank(gb, 0, bank);
- return;
- case 9:
- GBMBCSwitchHalfBank(gb, 1, bank);
- return;
- }
- }
- _GBMBC5(gb, address, value);
-}
-
-static uint8_t _reorderBits(uint8_t input, const uint8_t* reorder) {
- uint8_t newbyte = 0;
- int i;
- for(i = 0; i < 8; ++i) {
- int oldbit = reorder[i];
- int newbit = i;
- newbyte += ((input >> oldbit) & 1) << newbit;
- }
-
- return newbyte;
-}
-
-static const uint8_t _bbdDataReordering[8][8] = {
- { 0, 1, 2, 3, 4, 5, 6, 7 }, // 00 - Normal
- { 0, 1, 2, 3, 4, 5, 6, 7 }, // 01 - NOT KNOWN YET
- { 0, 1, 2, 3, 4, 5, 6, 7 }, // 02 - NOT KNOWN YET
- { 0, 1, 2, 3, 4, 5, 6, 7 }, // 03 - NOT KNOWN YET
- { 0, 5, 1, 3, 4, 2, 6, 7 }, // 04 - Garou
- { 0, 4, 2, 3, 1, 5, 6, 7 }, // 05 - Harry
- { 0, 1, 2, 3, 4, 5, 6, 7 }, // 06 - NOT KNOWN YET
- { 0, 1, 5, 3, 4, 2, 6, 7 }, // 07 - Digimon
-};
-
-static const uint8_t _bbdBankReordering[8][8] = {
- { 0, 1, 2, 3, 4, 5, 6, 7 }, // 00 - Normal
- { 0, 1, 2, 3, 4, 5, 6, 7 }, // 01 - NOT KNOWN YET
- { 0, 1, 2, 3, 4, 5, 6, 7 }, // 02 - NOT KNOWN YET
- { 3, 4, 2, 0, 1, 5, 6, 7 }, // 03 - 0,1 unconfirmed. Digimon/Garou
- { 0, 1, 2, 3, 4, 5, 6, 7 }, // 04 - NOT KNOWN YET
- { 1, 2, 3, 4, 0, 5, 6, 7 }, // 05 - 0,1 unconfirmed. Harry
- { 0, 1, 2, 3, 4, 5, 6, 7 }, // 06 - NOT KNOWN YET
- { 0, 1, 2, 3, 4, 5, 6, 7 }, // 07 - NOT KNOWN YET
-};
-
-void _GBBBD(struct GB* gb, uint16_t address, uint8_t value) {
- struct GBMemory* memory = &gb->memory;
- switch (address & 0xF0FF) {
- case 0x2000:
- value = _reorderBits(value, _bbdBankReordering[memory->mbcState.bbd.bankSwapMode]);
- break;
- case 0x2001:
- memory->mbcState.bbd.dataSwapMode = value & 0x07;
- if (!(memory->mbcState.bbd.dataSwapMode == 0x07 || memory->mbcState.bbd.dataSwapMode == 0x05 || memory->mbcState.bbd.dataSwapMode == 0x04 || memory->mbcState.bbd.dataSwapMode == 0x00)) {
- mLOG(GB_MBC, STUB, "Bitswap mode unsupported: %X", memory->mbcState.bbd.dataSwapMode);
- }
- break;
- case 0x2080:
- memory->mbcState.bbd.bankSwapMode = value & 0x07;
- if (!(memory->mbcState.bbd.bankSwapMode == 0x03 || memory->mbcState.bbd.bankSwapMode == 0x05 || memory->mbcState.bbd.bankSwapMode == 0x00)) {
- mLOG(GB_MBC, STUB, "Bankswap mode unsupported: %X", memory->mbcState.bbd.dataSwapMode);
- }
- break;
- }
- _GBMBC5(gb, address, value);
-}
-
-uint8_t _GBBBDRead(struct GBMemory* memory, uint16_t address) {
- switch (address >> 14) {
- case 0:
- default:
- return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
- case 1:
- return _reorderBits(memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)], _bbdDataReordering[memory->mbcState.bbd.dataSwapMode]);
- }
-}
-
-static const uint8_t _hitekDataReordering[8][8] = {
- { 0, 1, 2, 3, 4, 5, 6, 7 },
- { 0, 6, 5, 3, 4, 1, 2, 7 },
- { 0, 5, 6, 3, 4, 2, 1, 7 },
- { 0, 6, 2, 3, 4, 5, 1, 7 },
- { 0, 6, 1, 3, 4, 5, 2, 7 },
- { 0, 1, 6, 3, 4, 5, 2, 7 },
- { 0, 2, 6, 3, 4, 1, 5, 7 },
- { 0, 6, 2, 3, 4, 1, 5, 7 },
-};
-
-static const uint8_t _hitekBankReordering[8][8] = {
- { 0, 1, 2, 3, 4, 5, 6, 7 },
- { 3, 2, 1, 0, 4, 5, 6, 7 },
- { 2, 1, 0, 3, 4, 5, 6, 7 },
- { 1, 0, 3, 2, 4, 5, 6, 7 },
- { 0, 3, 2, 1, 4, 5, 6, 7 },
- { 2, 3, 0, 1, 4, 5, 6, 7 },
- { 3, 0, 1, 2, 4, 5, 6, 7 },
- { 2, 0, 3, 1, 4, 5, 6, 7 },
-};
-
-void _GBHitek(struct GB* gb, uint16_t address, uint8_t value) {
- struct GBMemory* memory = &gb->memory;
- switch (address & 0xF0FF) {
- case 0x2000:
- value = _reorderBits(value, _hitekBankReordering[memory->mbcState.bbd.bankSwapMode]);
- break;
- case 0x2001:
- memory->mbcState.bbd.dataSwapMode = value & 0x07;
- break;
- case 0x2080:
- memory->mbcState.bbd.bankSwapMode = value & 0x07;
- break;
- case 0x300:
- // See hhugboy src/memory/mbc/MbcUnlHitek.cpp for commentary on this return
- return;
- }
- _GBMBC5(gb, address, value);
-}
-
-uint8_t _GBHitekRead(struct GBMemory* memory, uint16_t address) {
- switch (address >> 14) {
- case 0:
- default:
- return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
- case 1:
- return _reorderBits(memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)], _hitekDataReordering[memory->mbcState.bbd.dataSwapMode]);
- }
-}
-
-void _GBSachen(struct GB* gb, uint16_t address, uint8_t value) {
- struct GBSachenState* state = &gb->memory.mbcState.sachen;
- uint8_t bank = value;
- switch (address >> 13) {
- case 0:
- if ((state->unmaskedBank & 0x30) == 0x30) {
- state->baseBank = bank;
- GBMBCSwitchBank0(gb, state->baseBank & state->mask);
- }
- break;
- case 1:
- if (!bank) {
- bank = 1;
- }
- state->unmaskedBank = bank;
- bank = (bank & ~state->mask) | (state->baseBank & state->mask);
- GBMBCSwitchBank(gb, bank);
- break;
- case 2:
- if ((state->unmaskedBank & 0x30) == 0x30) {
- state->mask = value;
- bank = (state->unmaskedBank & ~state->mask) | (state->baseBank & state->mask);
- GBMBCSwitchBank(gb, bank);
- GBMBCSwitchBank0(gb, state->baseBank & state->mask);
- }
- break;
- case 6:
- if (gb->memory.mbcType == GB_UNL_SACHEN_MMC2 && state->locked == GB_SACHEN_LOCKED_DMG) {
- state->locked = GB_SACHEN_LOCKED_CGB;
- state->transition = 0;
- }
- break;
- }
-}
-
-static uint16_t _unscrambleSachen(uint16_t address) {
- uint16_t unscrambled = address & 0xFFAC;
- unscrambled |= (address & 0x40) >> 6;
- unscrambled |= (address & 0x10) >> 3;
- unscrambled |= (address & 0x02) << 3;
- unscrambled |= (address & 0x01) << 6;
- return unscrambled;
-}
-
-uint8_t _GBSachenMMC1Read(struct GBMemory* memory, uint16_t address) {
- struct GBSachenState* state = &memory->mbcState.sachen;
- if (state->locked != GB_SACHEN_UNLOCKED && (address & 0xFF00) == 0x100) {
- ++state->transition;
- if (state->transition == 0x31) {
- state->locked = GB_SACHEN_UNLOCKED;
- } else {
- address |= 0x80;
- }
- }
-
- if ((address & 0xFF00) == 0x0100) {
- address = _unscrambleSachen(address);
- }
-
- if (address < GB_BASE_CART_BANK1) {
- return memory->romBase[address];
- } else if (address < GB_BASE_VRAM) {
- return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
- } else {
- return 0xFF;
- }
-}
-
-uint8_t _GBSachenMMC2Read(struct GBMemory* memory, uint16_t address) {
- struct GBSachenState* state = &memory->mbcState.sachen;
- if (address >= 0xC000 && state->locked == GB_SACHEN_LOCKED_DMG) {
- state->transition = 0;
- state->locked = GB_SACHEN_LOCKED_CGB;
- }
-
- if (state->locked != GB_SACHEN_UNLOCKED && (address & 0x8700) == 0x0100) {
- ++state->transition;
- if (state->transition == 0x31) {
- ++state->locked;
- state->transition = 0;
- }
- }
-
- if ((address & 0xFF00) == 0x0100) {
- if (state->locked == GB_SACHEN_LOCKED_CGB) {
- address |= 0x80;
- }
- address = _unscrambleSachen(address);
- }
-
- if (address < GB_BASE_CART_BANK1) {
- return memory->romBase[address];
- } else if (address < GB_BASE_VRAM) {
- return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
- } else {
- return 0xFF;
- }
-}
-
-static void _appendSaveSuffix(struct GB* gb, const void* buffer, size_t size) {
+void _GBMBCAppendSaveSuffix(struct GB* gb, const void* buffer, size_t size) {
struct VFile* vf = gb->sramVf;
if ((size_t) vf->size(vf) < gb->sramSize + size) {
// Writing past the end of the file can invalidate the file mapping
@@ -2245,7 +596,7 @@ void GBMBCRTCWrite(struct GB* gb) {
uint8_t rtcRegs[5];
memcpy(rtcRegs, gb->memory.rtcRegs, sizeof(rtcRegs));
time_t rtcLastLatch = gb->memory.rtcLastLatch;
- _latchRtc(gb->memory.rtc, rtcRegs, &rtcLastLatch);
+ _GBMBCLatchRTC(gb->memory.rtc, rtcRegs, &rtcLastLatch);
struct GBMBCRTCSaveBuffer rtcBuffer;
STORE_32LE(rtcRegs[0], 0, &rtcBuffer.sec);
@@ -2260,100 +611,5 @@ void GBMBCRTCWrite(struct GB* gb) {
STORE_32LE(gb->memory.rtcRegs[4], 0, &rtcBuffer.latchedDaysHi);
STORE_64LE(gb->memory.rtcLastLatch, 0, &rtcBuffer.unixTime);
- _appendSaveSuffix(gb, &rtcBuffer, sizeof(rtcBuffer));
-}
-
-void GBMBCHuC3Read(struct GB* gb) {
- struct GBMBCHuC3SaveBuffer buffer;
- struct VFile* vf = gb->sramVf;
- if (!vf) {
- return;
- }
- vf->seek(vf, gb->sramSize, SEEK_SET);
- if (vf->read(vf, &buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) {
- return;
- }
-
- size_t i;
- for (i = 0; i < 0x80; ++i) {
- gb->memory.mbcState.huc3.registers[i * 2] = buffer.regs[i] & 0xF;
- gb->memory.mbcState.huc3.registers[i * 2 + 1] = buffer.regs[i] >> 4;
- }
- LOAD_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix);
-}
-
-void GBMBCHuC3Write(struct GB* gb) {
- struct VFile* vf = gb->sramVf;
- if (!vf) {
- return;
- }
-
- struct GBMBCHuC3SaveBuffer buffer;
- size_t i;
- for (i = 0; i < 0x80; ++i) {
- buffer.regs[i] = gb->memory.mbcState.huc3.registers[i * 2] & 0xF;
- buffer.regs[i] |= gb->memory.mbcState.huc3.registers[i * 2 + 1] << 4;
- }
- STORE_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix);
-
- _appendSaveSuffix(gb, &buffer, sizeof(buffer));
-}
-
-void GBMBCTAMA5Read(struct GB* gb) {
- struct GBMBCTAMA5SaveBuffer buffer;
- struct VFile* vf = gb->sramVf;
- if (!vf) {
- return;
- }
- vf->seek(vf, gb->sramSize, SEEK_SET);
- if (vf->read(vf, &buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) {
- gb->memory.mbcState.tama5.disabled = false;
- return;
- }
-
- size_t i;
- for (i = 0; i < 0x8; ++i) {
- gb->memory.mbcState.tama5.rtcTimerPage[i * 2] = buffer.rtcTimerPage[i] & 0xF;
- gb->memory.mbcState.tama5.rtcTimerPage[i * 2 + 1] = buffer.rtcTimerPage[i] >> 4;
- gb->memory.mbcState.tama5.rtcAlarmPage[i * 2] = buffer.rtcAlarmPage[i] & 0xF;
- gb->memory.mbcState.tama5.rtcAlarmPage[i * 2 + 1] = buffer.rtcAlarmPage[i] >> 4;
- gb->memory.mbcState.tama5.rtcFreePage0[i * 2] = buffer.rtcFreePage0[i] & 0xF;
- gb->memory.mbcState.tama5.rtcFreePage0[i * 2 + 1] = buffer.rtcFreePage0[i] >> 4;
- gb->memory.mbcState.tama5.rtcFreePage1[i * 2] = buffer.rtcFreePage1[i] & 0xF;
- gb->memory.mbcState.tama5.rtcFreePage1[i * 2 + 1] = buffer.rtcFreePage1[i] >> 4;
- }
- LOAD_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix);
-
- gb->memory.mbcState.tama5.disabled = !(gb->memory.mbcState.tama5.rtcTimerPage[GBTAMA6_RTC_PAGE] & 0x8);
-
- gb->memory.mbcState.tama5.rtcTimerPage[GBTAMA6_RTC_PAGE] &= 0xC;
- gb->memory.mbcState.tama5.rtcAlarmPage[GBTAMA6_RTC_PAGE] &= 0xC;
- gb->memory.mbcState.tama5.rtcAlarmPage[GBTAMA6_RTC_PAGE] |= 1;
- gb->memory.mbcState.tama5.rtcFreePage0[GBTAMA6_RTC_PAGE] &= 0xC;
- gb->memory.mbcState.tama5.rtcFreePage0[GBTAMA6_RTC_PAGE] |= 2;
- gb->memory.mbcState.tama5.rtcFreePage1[GBTAMA6_RTC_PAGE] &= 0xC;
- gb->memory.mbcState.tama5.rtcFreePage1[GBTAMA6_RTC_PAGE] |= 3;
-}
-
-void GBMBCTAMA5Write(struct GB* gb) {
- struct VFile* vf = gb->sramVf;
- if (!vf) {
- return;
- }
-
- struct GBMBCTAMA5SaveBuffer buffer = {0};
- size_t i;
- for (i = 0; i < 8; ++i) {
- buffer.rtcTimerPage[i] = gb->memory.mbcState.tama5.rtcTimerPage[i * 2] & 0xF;
- buffer.rtcTimerPage[i] |= gb->memory.mbcState.tama5.rtcTimerPage[i * 2 + 1] << 4;
- buffer.rtcAlarmPage[i] = gb->memory.mbcState.tama5.rtcAlarmPage[i * 2] & 0xF;
- buffer.rtcAlarmPage[i] |= gb->memory.mbcState.tama5.rtcAlarmPage[i * 2 + 1] << 4;
- buffer.rtcFreePage0[i] = gb->memory.mbcState.tama5.rtcFreePage0[i * 2] & 0xF;
- buffer.rtcFreePage0[i] |= gb->memory.mbcState.tama5.rtcFreePage0[i * 2 + 1] << 4;
- buffer.rtcFreePage1[i] = gb->memory.mbcState.tama5.rtcFreePage1[i * 2] & 0xF;
- buffer.rtcFreePage1[i] |= gb->memory.mbcState.tama5.rtcFreePage1[i * 2 + 1] << 4;
- }
- STORE_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix);
-
- _appendSaveSuffix(gb, &buffer, sizeof(buffer));
+ _GBMBCAppendSaveSuffix(gb, &rtcBuffer, sizeof(rtcBuffer));
}
diff --git a/src/gb/mbc/huc-3.c b/src/gb/mbc/huc-3.c
new file mode 100644
index 000000000..522897517
--- /dev/null
+++ b/src/gb/mbc/huc-3.c
@@ -0,0 +1,218 @@
+/* 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 "gb/mbc/mbc-private.h"
+
+#include
+#include
+
+static void _latchHuC3Rtc(struct mRTCSource* rtc, uint8_t* huc3Regs, time_t* rtcLastLatch) {
+ time_t t;
+ if (rtc) {
+ if (rtc->sample) {
+ rtc->sample(rtc);
+ }
+ t = rtc->unixTime(rtc);
+ } else {
+ t = time(0);
+ }
+ t -= *rtcLastLatch;
+ t /= 60;
+
+ if (!t) {
+ return;
+ }
+ *rtcLastLatch += t * 60;
+
+ int minutes = huc3Regs[GBHUC3_RTC_MINUTES_HI] << 8;
+ minutes |= huc3Regs[GBHUC3_RTC_MINUTES_MI] << 4;
+ minutes |= huc3Regs[GBHUC3_RTC_MINUTES_LO];
+ minutes += t % 1440;
+ t /= 1440;
+ if (minutes >= 1440) {
+ minutes -= 1440;
+ ++t;
+ } else if (minutes < 0) {
+ minutes += 1440;
+ --t;
+ }
+ huc3Regs[GBHUC3_RTC_MINUTES_LO] = minutes & 0xF;
+ huc3Regs[GBHUC3_RTC_MINUTES_MI] = (minutes >> 4) & 0xF;
+ huc3Regs[GBHUC3_RTC_MINUTES_HI] = (minutes >> 8) & 0xF;
+
+ int days = huc3Regs[GBHUC3_RTC_DAYS_LO];
+ days |= huc3Regs[GBHUC3_RTC_DAYS_MI] << 4;
+ days |= huc3Regs[GBHUC3_RTC_DAYS_HI] << 8;
+
+ days += t;
+
+ huc3Regs[GBHUC3_RTC_DAYS_LO] = days & 0xF;
+ huc3Regs[GBHUC3_RTC_DAYS_MI] = (days >> 4) & 0xF;
+ huc3Regs[GBHUC3_RTC_DAYS_HI] = (days >> 8) & 0xF;
+}
+
+static void _huc3Commit(struct GB* gb, struct GBHuC3State* state) {
+ size_t c;
+ switch (state->value & 0x70) {
+ case 0x10:
+ if ((state->index & 0xF8) == 0x10) {
+ _latchHuC3Rtc(gb->memory.rtc, state->registers, &gb->memory.rtcLastLatch);
+ }
+ state->value &= 0xF0;
+ state->value |= state->registers[state->index] & 0xF;
+ mLOG(GB_MBC, DEBUG, "HuC-3 read: %02X:%X", state->index, state->value & 0xF);
+ if (state->value & 0x10) {
+ ++state->index;
+ }
+ break;
+ case 0x30:
+ mLOG(GB_MBC, DEBUG, "HuC-3 write: %02X:%X", state->index, state->value & 0xF);
+ state->registers[state->index] = state->value & 0xF;
+ if (state->value & 0x10) {
+ ++state->index;
+ }
+ break;
+ case 0x40:
+ state->index &= 0xF0;
+ state->index |= (state->value) & 0xF;
+ mLOG(GB_MBC, DEBUG, "HuC-3 index (low): %02X", state->index);
+ break;
+ case 0x50:
+ state->index &= 0x0F;
+ state->index |= ((state->value) & 0xF) << 4;
+ mLOG(GB_MBC, DEBUG, "HuC-3 index (high): %02X", state->index);
+ break;
+ case 0x60:
+ switch (state->value & 0xF) {
+ case GBHUC3_CMD_LATCH:
+ _latchHuC3Rtc(gb->memory.rtc, state->registers, &gb->memory.rtcLastLatch);
+ memcpy(state->registers, &state->registers[GBHUC3_RTC_MINUTES_LO], 6);
+ mLOG(GB_MBC, DEBUG, "HuC-3 RTC latch");
+ break;
+ case GBHUC3_CMD_SET_RTC:
+ memcpy(&state->registers[GBHUC3_RTC_MINUTES_LO], state->registers, 6);
+ mLOG(GB_MBC, DEBUG, "HuC-3 set RTC");
+ break;
+ case GBHUC3_CMD_RO:
+ mLOG(GB_MBC, STUB, "HuC-3 unimplemented read-only mode");
+ break;
+ case GBHUC3_CMD_TONE:
+ if (state->registers[GBHUC3_SPEAKER_ENABLE] == 1) {
+ for (c = 0; c < mCoreCallbacksListSize(&gb->coreCallbacks); ++c) {
+ struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gb->coreCallbacks, c);
+ if (callbacks->alarm) {
+ callbacks->alarm(callbacks->context);
+ }
+ }
+ mLOG(GB_MBC, DEBUG, "HuC-3 tone %i", state->registers[GBHUC3_SPEAKER_TONE] & 3);
+ }
+ break;
+ default:
+ mLOG(GB_MBC, STUB, "HuC-3 unknown command: %X", state->value & 0xF);
+ break;
+ }
+ state->value = 0xE1;
+ break;
+ default:
+ mLOG(GB_MBC, STUB, "HuC-3 unknown mode commit: %02X:%02X", state->index, state->value);
+ break;
+ }
+}
+
+void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) {
+ struct GBMemory* memory = &gb->memory;
+ struct GBHuC3State* state = &memory->mbcState.huc3;
+ int bank = value & 0x7F;
+ 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;
+ }
+ state->mode = value;
+ break;
+ case 0x1:
+ GBMBCSwitchBank(gb, bank);
+ break;
+ case 0x2:
+ GBMBCSwitchSramBank(gb, bank);
+ break;
+ case 0x5:
+ switch (state->mode) {
+ case GBHUC3_MODE_IN:
+ state->value = 0x80 | value;
+ break;
+ case GBHUC3_MODE_COMMIT:
+ _huc3Commit(gb, state);
+ break;
+ default:
+ mLOG(GB_MBC, STUB, "HuC-3 unknown mode write: %02X:%02X", state->mode, value);
+ }
+ break;
+ default:
+ // TODO
+ mLOG(GB_MBC, STUB, "HuC-3 unknown address: %04X:%02X", address, value);
+ break;
+ }
+}
+
+uint8_t _GBHuC3Read(struct GBMemory* memory, uint16_t address) {
+ struct GBHuC3State* state = &memory->mbcState.huc3;
+ switch (state->mode) {
+ case GBHUC3_MODE_SRAM_RO:
+ case GBHUC3_MODE_SRAM_RW:
+ return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
+ case GBHUC3_MODE_IN:
+ case GBHUC3_MODE_OUT:
+ return 0x80 | state->value;
+ default:
+ return 0xFF;
+ }
+}
+
+void GBMBCHuC3Read(struct GB* gb) {
+ struct GBMBCHuC3SaveBuffer buffer;
+ struct VFile* vf = gb->sramVf;
+ if (!vf) {
+ return;
+ }
+ vf->seek(vf, gb->sramSize, SEEK_SET);
+ if (vf->read(vf, &buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) {
+ return;
+ }
+
+ size_t i;
+ for (i = 0; i < 0x80; ++i) {
+ gb->memory.mbcState.huc3.registers[i * 2] = buffer.regs[i] & 0xF;
+ gb->memory.mbcState.huc3.registers[i * 2 + 1] = buffer.regs[i] >> 4;
+ }
+ LOAD_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix);
+}
+
+void GBMBCHuC3Write(struct GB* gb) {
+ struct VFile* vf = gb->sramVf;
+ if (!vf) {
+ return;
+ }
+
+ struct GBMBCHuC3SaveBuffer buffer;
+ size_t i;
+ for (i = 0; i < 0x80; ++i) {
+ buffer.regs[i] = gb->memory.mbcState.huc3.registers[i * 2] & 0xF;
+ buffer.regs[i] |= gb->memory.mbcState.huc3.registers[i * 2 + 1] << 4;
+ }
+ STORE_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix);
+
+ _GBMBCAppendSaveSuffix(gb, &buffer, sizeof(buffer));
+}
diff --git a/src/gb/mbc/licensed.c b/src/gb/mbc/licensed.c
new file mode 100644
index 000000000..22f0ae524
--- /dev/null
+++ b/src/gb/mbc/licensed.c
@@ -0,0 +1,84 @@
+/* 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 "gb/mbc/mbc-private.h"
+
+#include
+
+void _GBMMM01(struct GB* gb, uint16_t address, uint8_t value) {
+ struct GBMemory* memory = &gb->memory;
+ if (!memory->mbcState.mmm01.locked) {
+ switch (address >> 13) {
+ case 0x0:
+ memory->mbcState.mmm01.locked = true;
+ GBMBCSwitchBank0(gb, memory->mbcState.mmm01.currentBank0);
+ break;
+ case 0x1:
+ memory->mbcState.mmm01.currentBank0 &= ~0x7F;
+ memory->mbcState.mmm01.currentBank0 |= value & 0x7F;
+ break;
+ case 0x2:
+ memory->mbcState.mmm01.currentBank0 &= ~0x180;
+ memory->mbcState.mmm01.currentBank0 |= (value & 0x30) << 3;
+ break;
+ default:
+ // TODO
+ mLOG(GB_MBC, STUB, "MMM01 unknown address: %04X:%02X", address, value);
+ break;
+ }
+ return;
+ }
+ 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(gb, value + memory->mbcState.mmm01.currentBank0);
+ break;
+ case 0x2:
+ GBMBCSwitchSramBank(gb, value);
+ break;
+ default:
+ // TODO
+ mLOG(GB_MBC, STUB, "MMM01 unknown address: %04X:%02X", address, value);
+ break;
+ }
+}
+
+void _GBHuC1(struct GB* gb, uint16_t address, uint8_t value) {
+ struct GBMemory* memory = &gb->memory;
+ int bank = value & 0x3F;
+ switch (address >> 13) {
+ case 0x0:
+ switch (value) {
+ case 0xE:
+ memory->sramAccess = false;
+ break;
+ default:
+ memory->sramAccess = true;
+ GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
+ break;
+ }
+ break;
+ case 0x1:
+ GBMBCSwitchBank(gb, bank);
+ break;
+ case 0x2:
+ GBMBCSwitchSramBank(gb, value);
+ break;
+ default:
+ // TODO
+ mLOG(GB_MBC, STUB, "HuC-1 unknown address: %04X:%02X", address, value);
+ break;
+ }
+}
diff --git a/src/gb/mbc/mbc-private.h b/src/gb/mbc/mbc-private.h
new file mode 100644
index 000000000..b841839f8
--- /dev/null
+++ b/src/gb/mbc/mbc-private.h
@@ -0,0 +1,61 @@
+/* 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_PRIVATE_H
+#define GB_MBC_PRIVATE_H
+
+#include
+
+CXX_GUARD_START
+
+struct GB;
+struct GBMemory;
+struct mRTCSource;
+
+void _GBMBC1(struct GB*, uint16_t address, uint8_t value);
+void _GBMBC2(struct GB*, uint16_t address, uint8_t value);
+void _GBMBC3(struct GB*, uint16_t address, uint8_t value);
+void _GBMBC5(struct GB*, uint16_t address, uint8_t value);
+void _GBMBC6(struct GB*, uint16_t address, uint8_t value);
+void _GBMBC7(struct GB*, uint16_t address, uint8_t value);
+
+void _GBMMM01(struct GB*, uint16_t address, uint8_t value);
+void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value);
+void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value);
+
+void _GBHuC1(struct GB*, uint16_t address, uint8_t value);
+void _GBHuC3(struct GB*, uint16_t address, uint8_t value);
+
+void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value);
+void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value);
+void _GBNTOld1(struct GB* gb, uint16_t address, uint8_t value);
+void _GBNTOld2(struct GB* gb, uint16_t address, uint8_t value);
+void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value);
+void _GBBBD(struct GB* gb, uint16_t address, uint8_t value);
+void _GBHitek(struct GB* gb, uint16_t address, uint8_t value);
+void _GBLiCheng(struct GB* gb, uint16_t address, uint8_t value);
+void _GBSachen(struct GB* gb, uint16_t address, uint8_t value);
+
+uint8_t _GBMBC2Read(struct GBMemory*, uint16_t address);
+uint8_t _GBMBC6Read(struct GBMemory*, uint16_t address);
+uint8_t _GBMBC7Read(struct GBMemory*, uint16_t address);
+void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value);
+
+uint8_t _GBPocketCamRead(struct GBMemory*, uint16_t address);
+uint8_t _GBTAMA5Read(struct GBMemory*, uint16_t address);
+uint8_t _GBHuC3Read(struct GBMemory*, uint16_t address);
+
+uint8_t _GBPKJDRead(struct GBMemory*, uint16_t address);
+uint8_t _GBBBDRead(struct GBMemory*, uint16_t address);
+uint8_t _GBHitekRead(struct GBMemory*, uint16_t address);
+uint8_t _GBSachenMMC1Read(struct GBMemory*, uint16_t address);
+uint8_t _GBSachenMMC2Read(struct GBMemory*, uint16_t address);
+
+void _GBMBCLatchRTC(struct mRTCSource* rtc, uint8_t* rtcRegs, time_t* rtcLastLatch);
+void _GBMBCAppendSaveSuffix(struct GB* gb, const void* buffer, size_t size);
+
+CXX_GUARD_END
+
+#endif
diff --git a/src/gb/mbc/mbc.c b/src/gb/mbc/mbc.c
new file mode 100644
index 000000000..864b6e9f8
--- /dev/null
+++ b/src/gb/mbc/mbc.c
@@ -0,0 +1,565 @@
+/* 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 "gb/mbc/mbc-private.h"
+
+#include
+#include
+#include
+
+static void _GBMBC6MapChip(struct GB*, int half, uint8_t value);
+
+void _GBMBCLatchRTC(struct mRTCSource* rtc, uint8_t* rtcRegs, time_t* rtcLastLatch) {
+ time_t t;
+ if (rtc) {
+ if (rtc->sample) {
+ rtc->sample(rtc);
+ }
+ t = rtc->unixTime(rtc);
+ } else {
+ t = time(0);
+ }
+ time_t currentLatch = t;
+ t -= *rtcLastLatch;
+ *rtcLastLatch = currentLatch;
+
+ int64_t diff;
+ diff = rtcRegs[0] + t % 60;
+ if (diff < 0) {
+ diff += 60;
+ t -= 60;
+ }
+ rtcRegs[0] = diff % 60;
+ t /= 60;
+ t += diff / 60;
+
+ diff = rtcRegs[1] + t % 60;
+ if (diff < 0) {
+ diff += 60;
+ t -= 60;
+ }
+ rtcRegs[1] = diff % 60;
+ t /= 60;
+ t += diff / 60;
+
+ diff = rtcRegs[2] + t % 24;
+ if (diff < 0) {
+ diff += 24;
+ t -= 24;
+ }
+ rtcRegs[2] = diff % 24;
+ t /= 24;
+ t += diff / 24;
+
+ diff = rtcRegs[3] + ((rtcRegs[4] & 1) << 8) + (t & 0x1FF);
+ rtcRegs[3] = diff;
+ rtcRegs[4] &= 0xFE;
+ rtcRegs[4] |= (diff >> 8) & 1;
+ if (diff & 0x200) {
+ rtcRegs[4] |= 0x80;
+ }
+}
+
+static void _GBMBC1Update(struct GB* gb) {
+ struct GBMBC1State* state = &gb->memory.mbcState.mbc1;
+ int bank = state->bankLo;
+ bank &= (1 << state->multicartStride) - 1;
+ bank |= state->bankHi << state->multicartStride;
+ if (state->mode) {
+ GBMBCSwitchBank0(gb, state->bankHi << state->multicartStride);
+ GBMBCSwitchSramBank(gb, state->bankHi & 3);
+ } else {
+ GBMBCSwitchBank0(gb, 0);
+ GBMBCSwitchSramBank(gb, 0);
+ }
+ if (!(state->bankLo & 0x1F)) {
+ ++state->bankLo;
+ ++bank;
+ }
+ GBMBCSwitchBank(gb, bank);
+}
+
+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 & 0xF) {
+ 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:
+ memory->mbcState.mbc1.bankLo = bank;
+ _GBMBC1Update(gb);
+ break;
+ case 0x2:
+ bank &= 3;
+ memory->mbcState.mbc1.bankHi = bank;
+ _GBMBC1Update(gb);
+ break;
+ case 0x3:
+ memory->mbcState.mbc1.mode = value & 1;
+ _GBMBC1Update(gb);
+ 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 shift = (address & 1) * 4;
+ int bank = value & 0xF;
+ switch ((address & 0xC100) >> 8) {
+ case 0x0:
+ switch (value & 0x0F) {
+ case 0:
+ memory->sramAccess = false;
+ break;
+ case 0xA:
+ memory->sramAccess = true;
+ break;
+ default:
+ // TODO
+ mLOG(GB_MBC, STUB, "MBC2 unknown value %02X", value);
+ break;
+ }
+ break;
+ case 0x1:
+ if (!bank) {
+ ++bank;
+ }
+ GBMBCSwitchBank(gb, bank);
+ break;
+ case 0x80:
+ case 0x81:
+ case 0x82:
+ case 0x83:
+ if (!memory->sramAccess) {
+ return;
+ }
+ address &= 0x1FF;
+ memory->sramBank[(address >> 1)] &= 0xF0 >> shift;
+ memory->sramBank[(address >> 1)] |= (value & 0xF) << shift;
+ gb->sramDirty |= mSAVEDATA_DIRT_NEW;
+ break;
+ default:
+ // TODO
+ mLOG(GB_MBC, STUB, "MBC2 unknown address: %04X:%02X", address, value);
+ break;
+ }
+}
+
+uint8_t _GBMBC2Read(struct GBMemory* memory, uint16_t address) {
+ if (!memory->sramAccess) {
+ return 0xFF;
+ }
+ address &= 0x1FF;
+ int shift = (address & 1) * 4;
+ return (memory->sramBank[(address >> 1)] >> shift) | 0xF0;
+}
+
+void _GBMBC3(struct GB* gb, uint16_t address, uint8_t value) {
+ struct GBMemory* memory = &gb->memory;
+ int bank = value;
+ switch (address >> 13) {
+ case 0x0:
+ switch (value & 0xF) {
+ 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 (gb->memory.romSize < GB_SIZE_CART_BANK0 * 0x80) {
+ bank &= 0x7F;
+ }
+ if (!bank) {
+ ++bank;
+ }
+ GBMBCSwitchBank(gb, bank);
+ break;
+ case 0x2:
+ bank &= 0xF;
+ if (bank < 8) {
+ GBMBCSwitchSramBank(gb, value);
+ memory->rtcAccess = false;
+ } else if (bank <= 0xC) {
+ memory->activeRtcReg = bank - 8;
+ memory->rtcAccess = true;
+ }
+ break;
+ case 0x3:
+ if (memory->rtcLatched && value == 0) {
+ memory->rtcLatched = false;
+ } else if (!memory->rtcLatched && value == 1) {
+ _GBMBCLatchRTC(gb->memory.rtc, gb->memory.rtcRegs, &gb->memory.rtcLastLatch);
+ 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(gb, bank);
+ break;
+ case 0x3:
+ bank = (memory->currentBank & 0xFF) | ((value & 1) << 8);
+ GBMBCSwitchBank(gb, 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) {
+ struct GBMemory* memory = &gb->memory;
+ int bank = value;
+ switch (address >> 10) {
+ case 0:
+ switch (value) {
+ case 0:
+ memory->sramAccess = false;
+ break;
+ case 0xA:
+ memory->sramAccess = true;
+ break;
+ default:
+ // TODO
+ mLOG(GB_MBC, STUB, "MBC6 unknown value %02X", value);
+ break;
+ }
+ break;
+ case 0x1:
+ GBMBCSwitchSramHalfBank(gb, 0, bank);
+ break;
+ case 0x2:
+ GBMBCSwitchSramHalfBank(gb, 1, bank);
+ break;
+ case 0x3:
+ mLOG(GB_MBC, STUB, "MBC6 unimplemented flash OE write: %04X:%02X", address, value);
+ break;
+ case 0x4:
+ mLOG(GB_MBC, STUB, "MBC6 unimplemented flash WE write: %04X:%02X", address, value);
+ break;
+ case 0x8:
+ case 0x9:
+ GBMBCSwitchHalfBank(gb, 0, bank);
+ break;
+ case 0xA:
+ case 0xB:
+ _GBMBC6MapChip(gb, 0, value);
+ break;
+ case 0xC:
+ case 0xD:
+ GBMBCSwitchHalfBank(gb, 1, bank);
+ break;
+ case 0xE:
+ case 0xF:
+ _GBMBC6MapChip(gb, 1, value);
+ break;
+ case 0x28:
+ case 0x29:
+ case 0x2A:
+ case 0x2B:
+ if (memory->sramAccess) {
+ memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value;
+ gb->sramDirty |= mSAVEDATA_DIRT_NEW;
+ }
+ break;
+ case 0x2C:
+ case 0x2D:
+ case 0x2E:
+ case 0x2F:
+ if (memory->sramAccess) {
+ memory->sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value;
+ }
+ break;
+ default:
+ mLOG(GB_MBC, STUB, "MBC6 unknown address: %04X:%02X", address, value);
+ break;
+ }
+}
+
+uint8_t _GBMBC6Read(struct GBMemory* memory, uint16_t address) {
+ if (!memory->sramAccess) {
+ return 0xFF;
+ }
+ switch (address >> 12) {
+ case 0xA:
+ return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)];
+ case 0xB:
+ return memory->sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)];
+ }
+ return 0xFF;
+}
+
+static void _GBMBC6MapChip(struct GB* gb, int half, uint8_t value) {
+ if (!half) {
+ gb->memory.mbcState.mbc6.flashBank0 = !!(value & 0x08);
+ GBMBCSwitchHalfBank(gb, half, gb->memory.currentBank);
+ } else {
+ gb->memory.mbcState.mbc6.flashBank1 = !!(value & 0x08);
+ GBMBCSwitchHalfBank(gb, half, gb->memory.currentBank1);
+ }
+}
+
+void _GBMBC7(struct GB* gb, uint16_t address, uint8_t value) {
+ int bank = value & 0x7F;
+ switch (address >> 13) {
+ case 0x0:
+ switch (value) {
+ default:
+ case 0:
+ gb->memory.mbcState.mbc7.access = 0;
+ break;
+ case 0xA:
+ gb->memory.mbcState.mbc7.access |= 1;
+ break;
+ }
+ break;
+ case 0x1:
+ GBMBCSwitchBank(gb, bank);
+ break;
+ case 0x2:
+ if (value == 0x40) {
+ gb->memory.mbcState.mbc7.access |= 2;
+ } else {
+ gb->memory.mbcState.mbc7.access &= ~2;
+ }
+ break;
+ case 0x5:
+ _GBMBC7Write(&gb->memory, address, value);
+ gb->sramDirty |= mSAVEDATA_DIRT_NEW;
+ 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;
+ if (mbc7->access != 3) {
+ return 0xFF;
+ }
+ switch (address & 0xF0) {
+ case 0x20:
+ if (memory->rotation && memory->rotation->readTiltX) {
+ int32_t x = -memory->rotation->readTiltX(memory->rotation);
+ x >>= 21;
+ x += 0x81D0;
+ return x;
+ }
+ return 0xFF;
+ case 0x30:
+ if (memory->rotation && memory->rotation->readTiltX) {
+ int32_t x = -memory->rotation->readTiltX(memory->rotation);
+ x >>= 21;
+ x += 0x81D0;
+ return x >> 8;
+ }
+ return 7;
+ case 0x40:
+ if (memory->rotation && memory->rotation->readTiltY) {
+ int32_t y = -memory->rotation->readTiltY(memory->rotation);
+ y >>= 21;
+ y += 0x81D0;
+ return y;
+ }
+ return 0xFF;
+ case 0x50:
+ if (memory->rotation && memory->rotation->readTiltY) {
+ int32_t y = -memory->rotation->readTiltY(memory->rotation);
+ y >>= 21;
+ y += 0x81D0;
+ return y >> 8;
+ }
+ return 7;
+ case 0x60:
+ return 0;
+ case 0x80:
+ return mbc7->eeprom;
+ default:
+ return 0xFF;
+ }
+}
+
+void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value) {
+ struct GBMBC7State* mbc7 = &memory->mbcState.mbc7;
+ if (mbc7->access != 3) {
+ return;
+ }
+ switch (address & 0xF0) {
+ case 0x00:
+ mbc7->latch = (value & 0x55) == 0x55;
+ return;
+ case 0x10:
+ mbc7->latch |= (value & 0xAA);
+ if (mbc7->latch == 0xAB && memory->rotation && memory->rotation->sample) {
+ memory->rotation->sample(memory->rotation);
+ }
+ mbc7->latch = 0;
+ return;
+ default:
+ mLOG(GB_MBC, STUB, "MBC7 unknown register: %04X:%02X", address, value);
+ return;
+ case 0x80:
+ break;
+ }
+ GBMBC7Field old = memory->mbcState.mbc7.eeprom;
+ value = GBMBC7FieldFillDO(value); // Hi-Z
+ if (!GBMBC7FieldIsCS(old) && GBMBC7FieldIsCS(value)) {
+ mbc7->state = GBMBC7_STATE_IDLE;
+ }
+ if (!GBMBC7FieldIsCLK(old) && GBMBC7FieldIsCLK(value)) {
+ if (mbc7->state == GBMBC7_STATE_READ_COMMAND || mbc7->state == GBMBC7_STATE_EEPROM_WRITE || mbc7->state == GBMBC7_STATE_EEPROM_WRAL) {
+ mbc7->sr <<= 1;
+ mbc7->sr |= GBMBC7FieldGetDI(value);
+ ++mbc7->srBits;
+ }
+ switch (mbc7->state) {
+ case GBMBC7_STATE_IDLE:
+ if (GBMBC7FieldIsDI(value)) {
+ mbc7->state = GBMBC7_STATE_READ_COMMAND;
+ mbc7->srBits = 0;
+ mbc7->sr = 0;
+ }
+ break;
+ case GBMBC7_STATE_READ_COMMAND:
+ if (mbc7->srBits == 10) {
+ mbc7->state = 0x10 | (mbc7->sr >> 6);
+ if (mbc7->state & 0xC) {
+ mbc7->state &= ~0x3;
+ }
+ mbc7->srBits = 0;
+ mbc7->address = mbc7->sr & 0x7F;
+ }
+ break;
+ case GBMBC7_STATE_DO:
+ value = GBMBC7FieldSetDO(value, mbc7->sr >> 15);
+ mbc7->sr <<= 1;
+ --mbc7->srBits;
+ if (!mbc7->srBits) {
+ mbc7->state = GBMBC7_STATE_IDLE;
+ }
+ break;
+ default:
+ break;
+ }
+ switch (mbc7->state) {
+ case GBMBC7_STATE_EEPROM_EWEN:
+ mbc7->writable = true;
+ mbc7->state = GBMBC7_STATE_IDLE;
+ break;
+ case GBMBC7_STATE_EEPROM_EWDS:
+ mbc7->writable = false;
+ mbc7->state = GBMBC7_STATE_IDLE;
+ break;
+ case GBMBC7_STATE_EEPROM_WRITE:
+ if (mbc7->srBits == 16) {
+ if (mbc7->writable) {
+ memory->sram[mbc7->address * 2] = mbc7->sr >> 8;
+ memory->sram[mbc7->address * 2 + 1] = mbc7->sr;
+ }
+ mbc7->state = GBMBC7_STATE_IDLE;
+ }
+ break;
+ case GBMBC7_STATE_EEPROM_ERASE:
+ if (mbc7->writable) {
+ memory->sram[mbc7->address * 2] = 0xFF;
+ memory->sram[mbc7->address * 2 + 1] = 0xFF;
+ }
+ mbc7->state = GBMBC7_STATE_IDLE;
+ break;
+ case GBMBC7_STATE_EEPROM_READ:
+ mbc7->srBits = 16;
+ mbc7->sr = memory->sram[mbc7->address * 2] << 8;
+ mbc7->sr |= memory->sram[mbc7->address * 2 + 1];
+ mbc7->state = GBMBC7_STATE_DO;
+ value = GBMBC7FieldClearDO(value);
+ break;
+ case GBMBC7_STATE_EEPROM_WRAL:
+ if (mbc7->srBits == 16) {
+ if (mbc7->writable) {
+ int i;
+ for (i = 0; i < 128; ++i) {
+ memory->sram[i * 2] = mbc7->sr >> 8;
+ memory->sram[i * 2 + 1] = mbc7->sr;
+ }
+ }
+ mbc7->state = GBMBC7_STATE_IDLE;
+ }
+ break;
+ case GBMBC7_STATE_EEPROM_ERAL:
+ if (mbc7->writable) {
+ int i;
+ for (i = 0; i < 128; ++i) {
+ memory->sram[i * 2] = 0xFF;
+ memory->sram[i * 2 + 1] = 0xFF;
+ }
+ }
+ mbc7->state = GBMBC7_STATE_IDLE;
+ break;
+ default:
+ break;
+ }
+ } else if (GBMBC7FieldIsCS(value) && GBMBC7FieldIsCLK(old) && !GBMBC7FieldIsCLK(value)) {
+ value = GBMBC7FieldSetDO(value, GBMBC7FieldGetDO(old));
+ }
+ mbc7->eeprom = value;
+}
diff --git a/src/gb/mbc/pocket-cam.c b/src/gb/mbc/pocket-cam.c
new file mode 100644
index 000000000..c587dfa6a
--- /dev/null
+++ b/src/gb/mbc/pocket-cam.c
@@ -0,0 +1,149 @@
+/* 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 "gb/mbc/mbc-private.h"
+
+#include
+#include
+
+static void _GBPocketCamCapture(struct GBMemory*);
+
+void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value) {
+ struct GBMemory* memory = &gb->memory;
+ int bank = value & 0x3F;
+ 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, "Pocket Cam unknown value %02X", value);
+ break;
+ }
+ break;
+ case 0x1:
+ GBMBCSwitchBank(gb, bank);
+ break;
+ case 0x2:
+ if (value < 0x10) {
+ GBMBCSwitchSramBank(gb, value);
+ memory->mbcState.pocketCam.registersActive = false;
+ memory->directSramAccess = true;
+ } else {
+ memory->mbcState.pocketCam.registersActive = true;
+ memory->directSramAccess = false;
+ }
+ break;
+ case 0x5:
+ if (!memory->mbcState.pocketCam.registersActive) {
+ break;
+ }
+ address &= 0x7F;
+ if (address == 0 && value & 1) {
+ value &= 6; // TODO: Timing
+ gb->sramDirty |= mSAVEDATA_DIRT_NEW;
+ _GBPocketCamCapture(memory);
+ }
+ if (address < sizeof(memory->mbcState.pocketCam.registers)) {
+ memory->mbcState.pocketCam.registers[address] = value;
+ }
+ break;
+ default:
+ mLOG(GB_MBC, STUB, "Pocket Cam unknown address: %04X:%02X", address, value);
+ break;
+ }
+}
+
+uint8_t _GBPocketCamRead(struct GBMemory* memory, uint16_t address) {
+ if (memory->mbcState.pocketCam.registersActive) {
+ if ((address & 0x7F) == 0) {
+ return memory->mbcState.pocketCam.registers[0];
+ }
+ return 0;
+ }
+ return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
+}
+
+void _GBPocketCamCapture(struct GBMemory* memory) {
+ if (!memory->cam) {
+ return;
+ }
+ const void* image = NULL;
+ size_t stride;
+ enum mColorFormat format;
+ memory->cam->requestImage(memory->cam, &image, &stride, &format);
+ if (!image) {
+ return;
+ }
+ memset(&memory->sram[0x100], 0, GBCAM_HEIGHT * GBCAM_WIDTH / 4);
+ struct GBPocketCamState* pocketCam = &memory->mbcState.pocketCam;
+ size_t x, y;
+ for (y = 0; y < GBCAM_HEIGHT; ++y) {
+ for (x = 0; x < GBCAM_WIDTH; ++x) {
+ uint32_t gray;
+ uint32_t color;
+ switch (format) {
+ case mCOLOR_XBGR8:
+ case mCOLOR_XRGB8:
+ case mCOLOR_ARGB8:
+ case mCOLOR_ABGR8:
+ color = ((const uint32_t*) image)[y * stride + x];
+ gray = (color & 0xFF) + ((color >> 8) & 0xFF) + ((color >> 16) & 0xFF);
+ break;
+ case mCOLOR_BGRX8:
+ case mCOLOR_RGBX8:
+ case mCOLOR_RGBA8:
+ case mCOLOR_BGRA8:
+ color = ((const uint32_t*) image)[y * stride + x];
+ gray = ((color >> 8) & 0xFF) + ((color >> 16) & 0xFF) + ((color >> 24) & 0xFF);
+ break;
+ case mCOLOR_BGR5:
+ case mCOLOR_RGB5:
+ case mCOLOR_ARGB5:
+ case mCOLOR_ABGR5:
+ color = ((const uint16_t*) image)[y * stride + x];
+ gray = ((color << 3) & 0xF8) + ((color >> 2) & 0xF8) + ((color >> 7) & 0xF8);
+ break;
+ case mCOLOR_BGR565:
+ case mCOLOR_RGB565:
+ color = ((const uint16_t*) image)[y * stride + x];
+ gray = ((color << 3) & 0xF8) + ((color >> 3) & 0xFC) + ((color >> 8) & 0xF8);
+ break;
+ case mCOLOR_BGRA5:
+ case mCOLOR_RGBA5:
+ color = ((const uint16_t*) image)[y * stride + x];
+ gray = ((color << 2) & 0xF8) + ((color >> 3) & 0xF8) + ((color >> 8) & 0xF8);
+ break;
+ default:
+ mLOG(GB_MBC, WARN, "Unsupported pixel format: %X", format);
+ return;
+ }
+ uint16_t exposure = (pocketCam->registers[2] << 8) | (pocketCam->registers[3]);
+ gray = (gray + 1) * exposure / 0x300;
+ // TODO: Additional processing
+ int matrixEntry = 3 * ((x & 3) + 4 * (y & 3));
+ if (gray < pocketCam->registers[matrixEntry + 6]) {
+ gray = 0x101;
+ } else if (gray < pocketCam->registers[matrixEntry + 7]) {
+ gray = 0x100;
+ } else if (gray < pocketCam->registers[matrixEntry + 8]) {
+ gray = 0x001;
+ } else {
+ gray = 0;
+ }
+ int coord = (((x >> 3) & 0xF) * 8 + (y & 0x7)) * 2 + (y & ~0x7) * 0x20;
+ uint16_t existing;
+ LOAD_16LE(existing, coord + 0x100, memory->sram);
+ existing |= gray << (7 - (x & 7));
+ STORE_16LE(existing, coord + 0x100, memory->sram);
+ }
+ }
+}
diff --git a/src/gb/mbc/tama5.c b/src/gb/mbc/tama5.c
new file mode 100644
index 000000000..505a35ed8
--- /dev/null
+++ b/src/gb/mbc/tama5.c
@@ -0,0 +1,433 @@
+/* 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 "gb/mbc/mbc-private.h"
+
+#include
+#include
+#include
+
+static const uint8_t _tama6RTCMask[32] = {
+ //0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0xF, 0x7, 0xF, 0x7, 0xF, 0x3, 0x7, 0xF, 0x3, 0xF, 0x1, 0xF, 0xF, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xF, 0x7, 0xF, 0x3, 0x7, 0xF, 0x3, 0x0, 0x1, 0x3, 0x0, 0x0, 0x0, 0x0,
+};
+
+static const int _daysToMonth[] = {
+ [ 1] = 0,
+ [ 2] = 31,
+ [ 3] = 31 + 28,
+ [ 4] = 31 + 28 + 31,
+ [ 5] = 31 + 28 + 31 + 30,
+ [ 6] = 31 + 28 + 31 + 30 + 31,
+ [ 7] = 31 + 28 + 31 + 30 + 31 + 30,
+ [ 8] = 31 + 28 + 31 + 30 + 31 + 30 + 31,
+ [ 9] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
+ [10] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
+ [11] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
+ [12] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
+};
+
+static int _tama6DMYToDayOfYear(int day, int month, int year) {
+ if (month < 1 || month > 12) {
+ return -1;
+ }
+ day += _daysToMonth[month];
+ if (month > 2 && (year & 3) == 0) {
+ ++day;
+ }
+ return day;
+}
+
+static int _tama6DayOfYearToMonth(int day, int year) {
+ int month;
+ for (month = 1; month < 12; ++month) {
+ if (day <= _daysToMonth[month + 1]) {
+ return month;
+ }
+ if (month == 2 && (year & 3) == 0) {
+ if (day == 60) {
+ return 2;
+ }
+ --day;
+ }
+ }
+ return 12;
+}
+
+static int _tama6DayOfYearToDayOfMonth(int day, int year) {
+ int month;
+ for (month = 1; month < 12; ++month) {
+ if (day <= _daysToMonth[month + 1]) {
+ return day - _daysToMonth[month];
+ }
+ if (month == 2 && (year & 3) == 0) {
+ if (day == 60) {
+ return 29;
+ }
+ --day;
+ }
+ }
+ return day - _daysToMonth[12];
+}
+
+static void _latchTAMA6Rtc(struct mRTCSource* rtc, struct GBTAMA5State* tama5, time_t* rtcLastLatch) {
+ time_t t;
+ if (rtc) {
+ if (rtc->sample) {
+ rtc->sample(rtc);
+ }
+ t = rtc->unixTime(rtc);
+ } else {
+ t = time(0);
+ }
+ time_t currentLatch = t;
+ t -= *rtcLastLatch;
+ *rtcLastLatch = currentLatch;
+ if (!t || tama5->disabled) {
+ return;
+ }
+
+ uint8_t* timerRegs = tama5->rtcTimerPage;
+ bool is24hour = tama5->rtcAlarmPage[GBTAMA6_RTC_PA1_24_HOUR];
+ int64_t diff;
+ diff = timerRegs[GBTAMA6_RTC_PA0_SECOND_1] + timerRegs[GBTAMA6_RTC_PA0_SECOND_10] * 10 + t % 60;
+ if (diff < 0) {
+ diff += 60;
+ t -= 60;
+ }
+ timerRegs[GBTAMA6_RTC_PA0_SECOND_1] = diff % 10;
+ timerRegs[GBTAMA6_RTC_PA0_SECOND_10] = (diff % 60) / 10;
+ t /= 60;
+ t += diff / 60;
+
+ diff = timerRegs[GBTAMA6_RTC_PA0_MINUTE_1] + timerRegs[GBTAMA6_RTC_PA0_MINUTE_10] * 10 + t % 60;
+ if (diff < 0) {
+ diff += 60;
+ t -= 60;
+ }
+ timerRegs[GBTAMA6_RTC_PA0_MINUTE_1] = diff % 10;
+ timerRegs[GBTAMA6_RTC_PA0_MINUTE_10] = (diff % 60) / 10;
+ t /= 60;
+ t += diff / 60;
+
+ diff = timerRegs[GBTAMA6_RTC_PA0_HOUR_1];
+ if (is24hour) {
+ diff += timerRegs[GBTAMA6_RTC_PA0_HOUR_10] * 10;
+ } else {
+ int hour10 = timerRegs[GBTAMA6_RTC_PA0_HOUR_10];
+ diff += (hour10 & 1) * 10;
+ diff += (hour10 & 2) * 12;
+ }
+ diff += t % 24;
+ if (diff < 0) {
+ diff += 24;
+ t -= 24;
+ }
+ if (is24hour) {
+ timerRegs[GBTAMA6_RTC_PA0_HOUR_1] = (diff % 24) % 10;
+ timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 24) / 10;
+ } else {
+ timerRegs[GBTAMA6_RTC_PA0_HOUR_1] = (diff % 12) % 10;
+ timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 12) / 10 + (diff / 12) * 2;
+ }
+ t /= 24;
+ t += diff / 24;
+
+ int day = timerRegs[GBTAMA6_RTC_PA0_DAY_1] + timerRegs[GBTAMA6_RTC_PA0_DAY_10] * 10;
+ int month = timerRegs[GBTAMA6_RTC_PA0_MONTH_1] + timerRegs[GBTAMA6_RTC_PA0_MONTH_10] * 10;
+ int year = timerRegs[GBTAMA6_RTC_PA0_YEAR_1] + timerRegs[GBTAMA6_RTC_PA0_YEAR_10] * 10;
+ int leapYear = tama5->rtcAlarmPage[GBTAMA6_RTC_PA1_LEAP_YEAR];
+ int dayOfWeek = timerRegs[GBTAMA6_RTC_PA0_WEEK];
+ int dayInYear = _tama6DMYToDayOfYear(day, month, leapYear);
+ diff = dayInYear + t;
+ while (diff <= 0) {
+ // Previous year
+ if (leapYear & 3) {
+ diff += 365;
+ } else {
+ diff += 366;
+ }
+ --year;
+ --leapYear;
+ }
+ while (diff > (leapYear & 3 ? 365 : 366)) {
+ // Future year
+ if (year % 4) {
+ diff -= 365;
+ } else {
+ diff -= 366;
+ }
+ ++year;
+ ++leapYear;
+ }
+ dayOfWeek = (dayOfWeek + diff) % 7;
+ year %= 100;
+ leapYear &= 3;
+
+ day = _tama6DayOfYearToDayOfMonth(diff, leapYear);
+ month = _tama6DayOfYearToMonth(diff, leapYear);
+
+ timerRegs[GBTAMA6_RTC_PA0_WEEK] = dayOfWeek;
+ tama5->rtcAlarmPage[GBTAMA6_RTC_PA1_LEAP_YEAR] = leapYear;
+
+ timerRegs[GBTAMA6_RTC_PA0_DAY_1] = day % 10;
+ timerRegs[GBTAMA6_RTC_PA0_DAY_10] = day / 10;
+
+ timerRegs[GBTAMA6_RTC_PA0_MONTH_1] = month % 10;
+ timerRegs[GBTAMA6_RTC_PA0_MONTH_10] = month / 10;
+
+ timerRegs[GBTAMA6_RTC_PA0_YEAR_1] = year % 10;
+ timerRegs[GBTAMA6_RTC_PA0_YEAR_10] = year / 10;
+}
+
+void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value) {
+ struct GBMemory* memory = &gb->memory;
+ struct GBTAMA5State* tama5 = &memory->mbcState.tama5;
+ switch (address >> 13) {
+ case 0x5:
+ if (address & 1) {
+ tama5->reg = value;
+ } else {
+ value &= 0xF;
+ if (tama5->reg < GBTAMA5_MAX) {
+ mLOG(GB_MBC, DEBUG, "TAMA5 write: %02X:%X", tama5->reg, value);
+ tama5->registers[tama5->reg] = value;
+ uint8_t address = ((tama5->registers[GBTAMA5_ADDR_HI] << 4) & 0x10) | tama5->registers[GBTAMA5_ADDR_LO];
+ uint8_t out = (tama5->registers[GBTAMA5_WRITE_HI] << 4) | tama5->registers[GBTAMA5_WRITE_LO];
+ switch (tama5->reg) {
+ case GBTAMA5_BANK_LO:
+ case GBTAMA5_BANK_HI:
+ GBMBCSwitchBank(gb, tama5->registers[GBTAMA5_BANK_LO] | (tama5->registers[GBTAMA5_BANK_HI] << 4));
+ break;
+ case GBTAMA5_WRITE_LO:
+ case GBTAMA5_WRITE_HI:
+ case GBTAMA5_ADDR_HI:
+ break;
+ case GBTAMA5_ADDR_LO:
+ switch (tama5->registers[GBTAMA5_ADDR_HI] >> 1) {
+ case 0x0: // RAM write
+ memory->sram[address] = out;
+ gb->sramDirty |= mSAVEDATA_DIRT_NEW;
+ break;
+ case 0x1: // RAM read
+ break;
+ case 0x2: // Other commands
+ switch (address) {
+ case GBTAMA6_DISABLE_TIMER:
+ tama5->disabled = true;
+ tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] &= 0x7;
+ tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] &= 0x7;
+ tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] &= 0x7;
+ tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] &= 0x7;
+ break;
+ case GBTAMA6_ENABLE_TIMER:
+ tama5->disabled = false;
+ tama5->rtcTimerPage[GBTAMA6_RTC_PA0_SECOND_1] = 0;
+ tama5->rtcTimerPage[GBTAMA6_RTC_PA0_SECOND_10] = 0;
+ tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] |= 0x8;
+ tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] |= 0x8;
+ tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] |= 0x8;
+ tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] |= 0x8;
+ break;
+ case GBTAMA6_MINUTE_WRITE:
+ tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_1] = out & 0xF;
+ tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_10] = out >> 4;
+ break;
+ case GBTAMA6_HOUR_WRITE:
+ tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_1] = out & 0xF;
+ tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_10] = out >> 4;
+ break;
+ case GBTAMA6_DISABLE_ALARM:
+ tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] &= 0xB;
+ tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] &= 0xB;
+ tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] &= 0xB;
+ tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] &= 0xB;
+ break;
+ case GBTAMA6_ENABLE_ALARM:
+ tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] |= 0x4;
+ tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] |= 0x4;
+ tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] |= 0x4;
+ tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] |= 0x4;
+ break;
+ }
+ break;
+ case 0x4: // RTC access
+ address = tama5->registers[GBTAMA5_WRITE_LO];
+ if (address >= GBTAMA6_RTC_PAGE) {
+ break;
+ }
+ out = tama5->registers[GBTAMA5_WRITE_HI];
+ switch (tama5->registers[GBTAMA5_ADDR_LO]) {
+ case 0:
+ out &= _tama6RTCMask[address];
+ tama5->rtcTimerPage[address] = out;
+ break;
+ case 2:
+ out &= _tama6RTCMask[address | 0x10];
+ tama5->rtcAlarmPage[address] = out;
+ break;
+ case 4:
+ tama5->rtcFreePage0[address] = out;
+ break;
+ case 6:
+ tama5->rtcFreePage1[address] = out;
+ break;
+ }
+ break;
+ default:
+ mLOG(GB_MBC, STUB, "TAMA5 unknown address: %02X:%02X", address, out);
+ break;
+ }
+ break;
+ default:
+ mLOG(GB_MBC, STUB, "TAMA5 unknown write: %02X:%X", tama5->reg, value);
+ break;
+ }
+ } else {
+ mLOG(GB_MBC, STUB, "TAMA5 unknown write: %02X", tama5->reg);
+ }
+ }
+ break;
+ default:
+ mLOG(GB_MBC, STUB, "TAMA5 unknown address: %04X:%02X", address, value);
+ }
+}
+
+uint8_t _GBTAMA5Read(struct GBMemory* memory, uint16_t address) {
+ struct GBTAMA5State* tama5 = &memory->mbcState.tama5;
+ if ((address & 0x1FFF) > 1) {
+ mLOG(GB_MBC, STUB, "TAMA5 unknown address: %04X", address);
+ }
+ if (address & 1) {
+ return 0xFF;
+ } else {
+ uint8_t value = 0xF0;
+ uint8_t address = ((tama5->registers[GBTAMA5_ADDR_HI] << 4) & 0x10) | tama5->registers[GBTAMA5_ADDR_LO];
+ switch (tama5->reg) {
+ case GBTAMA5_ACTIVE:
+ return 0xF1;
+ case GBTAMA5_READ_LO:
+ case GBTAMA5_READ_HI:
+ switch (tama5->registers[GBTAMA5_ADDR_HI] >> 1) {
+ case 0x1:
+ value = memory->sram[address];
+ break;
+ case 0x2:
+ mLOG(GB_MBC, STUB, "TAMA5 unknown read %s: %02X", tama5->reg == GBTAMA5_READ_HI ? "hi" : "lo", address);
+ _latchTAMA6Rtc(memory->rtc, tama5, &memory->rtcLastLatch);
+ switch (address) {
+ case GBTAMA6_MINUTE_READ:
+ value = (tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_10] << 4) | tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_1];
+ break;
+ case GBTAMA6_HOUR_READ:
+ value = (tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_10] << 4) | tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_1];
+ break;
+ default:
+ value = address;
+ break;
+ }
+ break;
+ case 0x4:
+ if (tama5->reg == GBTAMA5_READ_HI) {
+ mLOG(GB_MBC, GAME_ERROR, "TAMA5 reading RTC incorrectly");
+ break;
+ }
+ _latchTAMA6Rtc(memory->rtc, tama5, &memory->rtcLastLatch);
+ address = tama5->registers[GBTAMA5_WRITE_LO];
+ if (address > GBTAMA6_RTC_PAGE) {
+ value = 0;
+ break;
+ }
+ switch (tama5->registers[GBTAMA5_ADDR_LO]) {
+ case 1:
+ value = tama5->rtcTimerPage[address];
+ break;
+ case 3:
+ value = tama5->rtcTimerPage[address];
+ break;
+ case 5:
+ value = tama5->rtcTimerPage[address];
+ break;
+ case 7:
+ value = tama5->rtcTimerPage[address];
+ break;
+ }
+ break;
+ default:
+ mLOG(GB_MBC, STUB, "TAMA5 unknown read %s: %02X", tama5->reg == GBTAMA5_READ_HI ? "hi" : "lo", address);
+ break;
+ }
+ if (tama5->reg == GBTAMA5_READ_HI) {
+ value >>= 4;
+ }
+ value |= 0xF0;
+ return value;
+ default:
+ mLOG(GB_MBC, STUB, "TAMA5 unknown read: %02X", tama5->reg);
+ return 0xF1;
+ }
+ }
+}
+
+
+void GBMBCTAMA5Read(struct GB* gb) {
+ struct GBMBCTAMA5SaveBuffer buffer;
+ struct VFile* vf = gb->sramVf;
+ if (!vf) {
+ return;
+ }
+ vf->seek(vf, gb->sramSize, SEEK_SET);
+ if (vf->read(vf, &buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) {
+ gb->memory.mbcState.tama5.disabled = false;
+ return;
+ }
+
+ size_t i;
+ for (i = 0; i < 0x8; ++i) {
+ gb->memory.mbcState.tama5.rtcTimerPage[i * 2] = buffer.rtcTimerPage[i] & 0xF;
+ gb->memory.mbcState.tama5.rtcTimerPage[i * 2 + 1] = buffer.rtcTimerPage[i] >> 4;
+ gb->memory.mbcState.tama5.rtcAlarmPage[i * 2] = buffer.rtcAlarmPage[i] & 0xF;
+ gb->memory.mbcState.tama5.rtcAlarmPage[i * 2 + 1] = buffer.rtcAlarmPage[i] >> 4;
+ gb->memory.mbcState.tama5.rtcFreePage0[i * 2] = buffer.rtcFreePage0[i] & 0xF;
+ gb->memory.mbcState.tama5.rtcFreePage0[i * 2 + 1] = buffer.rtcFreePage0[i] >> 4;
+ gb->memory.mbcState.tama5.rtcFreePage1[i * 2] = buffer.rtcFreePage1[i] & 0xF;
+ gb->memory.mbcState.tama5.rtcFreePage1[i * 2 + 1] = buffer.rtcFreePage1[i] >> 4;
+ }
+ LOAD_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix);
+
+ gb->memory.mbcState.tama5.disabled = !(gb->memory.mbcState.tama5.rtcTimerPage[GBTAMA6_RTC_PAGE] & 0x8);
+
+ gb->memory.mbcState.tama5.rtcTimerPage[GBTAMA6_RTC_PAGE] &= 0xC;
+ gb->memory.mbcState.tama5.rtcAlarmPage[GBTAMA6_RTC_PAGE] &= 0xC;
+ gb->memory.mbcState.tama5.rtcAlarmPage[GBTAMA6_RTC_PAGE] |= 1;
+ gb->memory.mbcState.tama5.rtcFreePage0[GBTAMA6_RTC_PAGE] &= 0xC;
+ gb->memory.mbcState.tama5.rtcFreePage0[GBTAMA6_RTC_PAGE] |= 2;
+ gb->memory.mbcState.tama5.rtcFreePage1[GBTAMA6_RTC_PAGE] &= 0xC;
+ gb->memory.mbcState.tama5.rtcFreePage1[GBTAMA6_RTC_PAGE] |= 3;
+}
+
+void GBMBCTAMA5Write(struct GB* gb) {
+ struct VFile* vf = gb->sramVf;
+ if (!vf) {
+ return;
+ }
+
+ struct GBMBCTAMA5SaveBuffer buffer = {0};
+ size_t i;
+ for (i = 0; i < 8; ++i) {
+ buffer.rtcTimerPage[i] = gb->memory.mbcState.tama5.rtcTimerPage[i * 2] & 0xF;
+ buffer.rtcTimerPage[i] |= gb->memory.mbcState.tama5.rtcTimerPage[i * 2 + 1] << 4;
+ buffer.rtcAlarmPage[i] = gb->memory.mbcState.tama5.rtcAlarmPage[i * 2] & 0xF;
+ buffer.rtcAlarmPage[i] |= gb->memory.mbcState.tama5.rtcAlarmPage[i * 2 + 1] << 4;
+ buffer.rtcFreePage0[i] = gb->memory.mbcState.tama5.rtcFreePage0[i * 2] & 0xF;
+ buffer.rtcFreePage0[i] |= gb->memory.mbcState.tama5.rtcFreePage0[i * 2 + 1] << 4;
+ buffer.rtcFreePage1[i] = gb->memory.mbcState.tama5.rtcFreePage1[i * 2] & 0xF;
+ buffer.rtcFreePage1[i] |= gb->memory.mbcState.tama5.rtcFreePage1[i * 2 + 1] << 4;
+ }
+ STORE_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix);
+
+ _GBMBCAppendSaveSuffix(gb, &buffer, sizeof(buffer));
+}
diff --git a/src/gb/mbc/unlicensed.c b/src/gb/mbc/unlicensed.c
new file mode 100644
index 000000000..157dd34e6
--- /dev/null
+++ b/src/gb/mbc/unlicensed.c
@@ -0,0 +1,469 @@
+/* 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 "gb/mbc/mbc-private.h"
+
+#include
+
+void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value) {
+ UNUSED(value);
+ int bank = address & 0x3F;
+ switch (address >> 14) {
+ case 0x0:
+ GBMBCSwitchBank0(gb, bank * 2);
+ GBMBCSwitchBank(gb, bank * 2 + 1);
+ break;
+ default:
+ // TODO
+ mLOG(GB_MBC, STUB, "Wisdom Tree unknown address: %04X:%02X", address, value);
+ break;
+ }
+}
+
+void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value) {
+ struct GBMemory* memory = &gb->memory;
+ switch (address >> 13) {
+ case 0x2:
+ if (value < 8) {
+ memory->directSramAccess = true;
+ memory->activeRtcReg = 0;
+ } else if (value >= 0xD && value <= 0xF) {
+ memory->directSramAccess = false;
+ memory->rtcAccess = false;
+ memory->activeRtcReg = value - 8;
+ }
+ break;
+ case 0x5:
+ if (!memory->sramAccess) {
+ return;
+ }
+ switch (memory->activeRtcReg) {
+ case 0:
+ memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)] = value;
+ break;
+ case 5:
+ case 6:
+ memory->mbcState.pkjd.reg[memory->activeRtcReg - 5] = value;
+ break;
+ case 7:
+ switch (value) {
+ case 0x11:
+ memory->mbcState.pkjd.reg[0]--;
+ break;
+ case 0x12:
+ memory->mbcState.pkjd.reg[1]--;
+ break;
+ case 0x41:
+ memory->mbcState.pkjd.reg[0] += memory->mbcState.pkjd.reg[1];
+ break;
+ case 0x42:
+ memory->mbcState.pkjd.reg[1] += memory->mbcState.pkjd.reg[0];
+ break;
+ case 0x51:
+ memory->mbcState.pkjd.reg[0]++;
+ break;
+ case 0x52:
+ memory->mbcState.pkjd.reg[1]--;
+ break;
+ }
+ break;
+ }
+ return;
+ }
+ _GBMBC3(gb, address, value);
+}
+
+uint8_t _GBPKJDRead(struct GBMemory* memory, uint16_t address) {
+ if (!memory->sramAccess) {
+ return 0xFF;
+ }
+ switch (memory->activeRtcReg) {
+ case 0:
+ return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
+ case 5:
+ case 6:
+ return memory->mbcState.pkjd.reg[memory->activeRtcReg - 5];
+ default:
+ return 0;
+ }
+}
+
+
+static uint8_t _reorderBits(uint8_t input, const uint8_t* reorder) {
+ uint8_t newbyte = 0;
+ int i;
+ for(i = 0; i < 8; ++i) {
+ int oldbit = reorder[i];
+ int newbit = i;
+ newbyte += ((input >> oldbit) & 1) << newbit;
+ }
+
+ return newbyte;
+}
+
+static const uint8_t _ntOld1Reorder[8] = {
+ 0, 2, 1, 4, 3, 5, 6, 7
+};
+
+void _ntOldMulticart(struct GB* gb, uint16_t address, uint8_t value, const uint8_t reorder[8]) {
+ struct GBMemory* memory = &gb->memory;
+ struct GBNTOldState* mbcState = &memory->mbcState.ntOld;
+ int bank = value;
+
+ switch (address & 3) {
+ case 0:
+ mLOG(GB_MBC, STUB, "Unimplemented NT Old 1 address 0");
+ break;
+ case 1:
+ value &= 0x3F;
+ mbcState->baseBank = value * 2;
+ if (mbcState->baseBank) {
+ GBMBCSwitchBank0(gb, mbcState->baseBank);
+ GBMBCSwitchBank(gb, mbcState->baseBank + 1);
+ }
+ break;
+ case 2:
+ if ((value & 0xF0) == 0xE0) {
+ gb->sramSize = 0x2000;
+ GBResizeSram(gb, gb->sramSize);
+ }
+ switch (value & 0xF) {
+ case 0x00:
+ mbcState->bankCount = 32;
+ break;
+ case 0x08:
+ mbcState->bankCount = 16;
+ break;
+ case 0xC:
+ mbcState->bankCount = 8;
+ break;
+ case 0xE:
+ mbcState->bankCount = 4;
+ break;
+ case 0xF:
+ mbcState->bankCount = 2;
+ break;
+ default:
+ mbcState->bankCount = 32;
+ break;
+ }
+ break;
+ case 3:
+ mbcState->swapped = !!(value & 0x10);
+
+ bank = memory->currentBank;
+ if (mbcState->swapped) {
+ bank = _reorderBits(bank, reorder);
+ }
+ GBMBCSwitchBank(gb, bank);
+ break;
+ }
+}
+
+void _GBNTOld1(struct GB* gb, uint16_t address, uint8_t value) {
+ struct GBMemory* memory = &gb->memory;
+ struct GBNTOldState* mbcState = &memory->mbcState.ntOld;
+ int bank = value;
+
+ switch (address >> 12) {
+ case 0x0:
+ case 0x1:
+ _GBMBC3(gb, address, value);
+ break;
+ case 0x2:
+ case 0x3:
+ bank &= 0x1F;
+ if (!bank) {
+ bank = 1;
+ }
+ if (mbcState->swapped) {
+ bank = _reorderBits(bank, _ntOld1Reorder);
+ }
+ if (mbcState->bankCount) {
+ bank &= mbcState->bankCount - 1;
+ }
+ GBMBCSwitchBank(gb, bank + mbcState->baseBank);
+ break;
+ case 0x5:
+ _ntOldMulticart(gb, address, value, _ntOld1Reorder);
+ break;
+ }
+}
+
+static const uint8_t _ntOld2Reorder[8] = {
+ 1, 2, 0, 3, 4, 5, 6, 7
+};
+
+void _GBNTOld2(struct GB* gb, uint16_t address, uint8_t value) {
+ struct GBMemory* memory = &gb->memory;
+ struct GBNTOldState* mbcState = &memory->mbcState.ntOld;
+ int bank = value;
+
+ switch (address >> 12) {
+ case 0x0:
+ case 0x1:
+ _GBMBC3(gb, address, value);
+ break;
+ case 0x2:
+ case 0x3:
+ if (!bank) {
+ bank = 1;
+ }
+ if (mbcState->swapped) {
+ bank = _reorderBits(bank, _ntOld2Reorder);
+ }
+ if (mbcState->bankCount) {
+ bank &= mbcState->bankCount - 1;
+ }
+ GBMBCSwitchBank(gb, bank + mbcState->baseBank);
+ break;
+ case 0x5:
+ _ntOldMulticart(gb, address, value, _ntOld2Reorder);
+ // Fall through
+ case 0x4:
+ if (address == 0x5001) {
+ mbcState->rumble = !!(value & 0x80);
+ }
+
+ if (mbcState->rumble) {
+ memory->rumble->setRumble(memory->rumble, !!(mbcState->swapped ? value & 0x08 : value & 0x02));
+ }
+ break;
+ }
+}
+
+void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value) {
+ struct GBMemory* memory = &gb->memory;
+ if (address >> 8 == 0x14) {
+ memory->mbcState.ntNew.splitMode = true;
+ return;
+ }
+ if (memory->mbcState.ntNew.splitMode) {
+ int bank = value;
+ if (bank < 2) {
+ bank = 2;
+ }
+ switch (address >> 10) {
+ case 8:
+ GBMBCSwitchHalfBank(gb, 0, bank);
+ return;
+ case 9:
+ GBMBCSwitchHalfBank(gb, 1, bank);
+ return;
+ }
+ }
+ _GBMBC5(gb, address, value);
+}
+
+static const uint8_t _bbdDataReordering[8][8] = {
+ { 0, 1, 2, 3, 4, 5, 6, 7 }, // 00 - Normal
+ { 0, 1, 2, 3, 4, 5, 6, 7 }, // 01 - NOT KNOWN YET
+ { 0, 1, 2, 3, 4, 5, 6, 7 }, // 02 - NOT KNOWN YET
+ { 0, 1, 2, 3, 4, 5, 6, 7 }, // 03 - NOT KNOWN YET
+ { 0, 5, 1, 3, 4, 2, 6, 7 }, // 04 - Garou
+ { 0, 4, 2, 3, 1, 5, 6, 7 }, // 05 - Harry
+ { 0, 1, 2, 3, 4, 5, 6, 7 }, // 06 - NOT KNOWN YET
+ { 0, 1, 5, 3, 4, 2, 6, 7 }, // 07 - Digimon
+};
+
+static const uint8_t _bbdBankReordering[8][8] = {
+ { 0, 1, 2, 3, 4, 5, 6, 7 }, // 00 - Normal
+ { 0, 1, 2, 3, 4, 5, 6, 7 }, // 01 - NOT KNOWN YET
+ { 0, 1, 2, 3, 4, 5, 6, 7 }, // 02 - NOT KNOWN YET
+ { 3, 4, 2, 0, 1, 5, 6, 7 }, // 03 - 0,1 unconfirmed. Digimon/Garou
+ { 0, 1, 2, 3, 4, 5, 6, 7 }, // 04 - NOT KNOWN YET
+ { 1, 2, 3, 4, 0, 5, 6, 7 }, // 05 - 0,1 unconfirmed. Harry
+ { 0, 1, 2, 3, 4, 5, 6, 7 }, // 06 - NOT KNOWN YET
+ { 0, 1, 2, 3, 4, 5, 6, 7 }, // 07 - NOT KNOWN YET
+};
+
+void _GBBBD(struct GB* gb, uint16_t address, uint8_t value) {
+ struct GBMemory* memory = &gb->memory;
+ switch (address & 0xF0FF) {
+ case 0x2000:
+ value = _reorderBits(value, _bbdBankReordering[memory->mbcState.bbd.bankSwapMode]);
+ break;
+ case 0x2001:
+ memory->mbcState.bbd.dataSwapMode = value & 0x07;
+ if (!(memory->mbcState.bbd.dataSwapMode == 0x07 || memory->mbcState.bbd.dataSwapMode == 0x05 || memory->mbcState.bbd.dataSwapMode == 0x04 || memory->mbcState.bbd.dataSwapMode == 0x00)) {
+ mLOG(GB_MBC, STUB, "Bitswap mode unsupported: %X", memory->mbcState.bbd.dataSwapMode);
+ }
+ break;
+ case 0x2080:
+ memory->mbcState.bbd.bankSwapMode = value & 0x07;
+ if (!(memory->mbcState.bbd.bankSwapMode == 0x03 || memory->mbcState.bbd.bankSwapMode == 0x05 || memory->mbcState.bbd.bankSwapMode == 0x00)) {
+ mLOG(GB_MBC, STUB, "Bankswap mode unsupported: %X", memory->mbcState.bbd.dataSwapMode);
+ }
+ break;
+ }
+ _GBMBC5(gb, address, value);
+}
+
+uint8_t _GBBBDRead(struct GBMemory* memory, uint16_t address) {
+ switch (address >> 14) {
+ case 0:
+ default:
+ return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
+ case 1:
+ return _reorderBits(memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)], _bbdDataReordering[memory->mbcState.bbd.dataSwapMode]);
+ }
+}
+
+static const uint8_t _hitekDataReordering[8][8] = {
+ { 0, 1, 2, 3, 4, 5, 6, 7 },
+ { 0, 6, 5, 3, 4, 1, 2, 7 },
+ { 0, 5, 6, 3, 4, 2, 1, 7 },
+ { 0, 6, 2, 3, 4, 5, 1, 7 },
+ { 0, 6, 1, 3, 4, 5, 2, 7 },
+ { 0, 1, 6, 3, 4, 5, 2, 7 },
+ { 0, 2, 6, 3, 4, 1, 5, 7 },
+ { 0, 6, 2, 3, 4, 1, 5, 7 },
+};
+
+static const uint8_t _hitekBankReordering[8][8] = {
+ { 0, 1, 2, 3, 4, 5, 6, 7 },
+ { 3, 2, 1, 0, 4, 5, 6, 7 },
+ { 2, 1, 0, 3, 4, 5, 6, 7 },
+ { 1, 0, 3, 2, 4, 5, 6, 7 },
+ { 0, 3, 2, 1, 4, 5, 6, 7 },
+ { 2, 3, 0, 1, 4, 5, 6, 7 },
+ { 3, 0, 1, 2, 4, 5, 6, 7 },
+ { 2, 0, 3, 1, 4, 5, 6, 7 },
+};
+
+void _GBHitek(struct GB* gb, uint16_t address, uint8_t value) {
+ struct GBMemory* memory = &gb->memory;
+ switch (address & 0xF0FF) {
+ case 0x2000:
+ value = _reorderBits(value, _hitekBankReordering[memory->mbcState.bbd.bankSwapMode]);
+ break;
+ case 0x2001:
+ memory->mbcState.bbd.dataSwapMode = value & 0x07;
+ break;
+ case 0x2080:
+ memory->mbcState.bbd.bankSwapMode = value & 0x07;
+ break;
+ case 0x300:
+ // See hhugboy src/memory/mbc/MbcUnlHitek.cpp for commentary on this return
+ return;
+ }
+ _GBMBC5(gb, address, value);
+}
+
+uint8_t _GBHitekRead(struct GBMemory* memory, uint16_t address) {
+ switch (address >> 14) {
+ case 0:
+ default:
+ return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
+ case 1:
+ return _reorderBits(memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)], _hitekDataReordering[memory->mbcState.bbd.dataSwapMode]);
+ }
+}
+
+void _GBLiCheng(struct GB* gb, uint16_t address, uint8_t value) {
+ if (address > 0x2100 && address < 0x3000) {
+ return;
+ }
+ _GBMBC5(gb, address, value);
+}
+
+void _GBSachen(struct GB* gb, uint16_t address, uint8_t value) {
+ struct GBSachenState* state = &gb->memory.mbcState.sachen;
+ uint8_t bank = value;
+ switch (address >> 13) {
+ case 0:
+ if ((state->unmaskedBank & 0x30) == 0x30) {
+ state->baseBank = bank;
+ GBMBCSwitchBank0(gb, state->baseBank & state->mask);
+ }
+ break;
+ case 1:
+ if (!bank) {
+ bank = 1;
+ }
+ state->unmaskedBank = bank;
+ bank = (bank & ~state->mask) | (state->baseBank & state->mask);
+ GBMBCSwitchBank(gb, bank);
+ break;
+ case 2:
+ if ((state->unmaskedBank & 0x30) == 0x30) {
+ state->mask = value;
+ bank = (state->unmaskedBank & ~state->mask) | (state->baseBank & state->mask);
+ GBMBCSwitchBank(gb, bank);
+ GBMBCSwitchBank0(gb, state->baseBank & state->mask);
+ }
+ break;
+ case 6:
+ if (gb->memory.mbcType == GB_UNL_SACHEN_MMC2 && state->locked == GB_SACHEN_LOCKED_DMG) {
+ state->locked = GB_SACHEN_LOCKED_CGB;
+ state->transition = 0;
+ }
+ break;
+ }
+}
+
+static uint16_t _unscrambleSachen(uint16_t address) {
+ uint16_t unscrambled = address & 0xFFAC;
+ unscrambled |= (address & 0x40) >> 6;
+ unscrambled |= (address & 0x10) >> 3;
+ unscrambled |= (address & 0x02) << 3;
+ unscrambled |= (address & 0x01) << 6;
+ return unscrambled;
+}
+
+uint8_t _GBSachenMMC1Read(struct GBMemory* memory, uint16_t address) {
+ struct GBSachenState* state = &memory->mbcState.sachen;
+ if (state->locked != GB_SACHEN_UNLOCKED && (address & 0xFF00) == 0x100) {
+ ++state->transition;
+ if (state->transition == 0x31) {
+ state->locked = GB_SACHEN_UNLOCKED;
+ } else {
+ address |= 0x80;
+ }
+ }
+
+ if ((address & 0xFF00) == 0x0100) {
+ address = _unscrambleSachen(address);
+ }
+
+ if (address < GB_BASE_CART_BANK1) {
+ return memory->romBase[address];
+ } else if (address < GB_BASE_VRAM) {
+ return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
+ } else {
+ return 0xFF;
+ }
+}
+
+uint8_t _GBSachenMMC2Read(struct GBMemory* memory, uint16_t address) {
+ struct GBSachenState* state = &memory->mbcState.sachen;
+ if (address >= 0xC000 && state->locked == GB_SACHEN_LOCKED_DMG) {
+ state->transition = 0;
+ state->locked = GB_SACHEN_LOCKED_CGB;
+ }
+
+ if (state->locked != GB_SACHEN_UNLOCKED && (address & 0x8700) == 0x0100) {
+ ++state->transition;
+ if (state->transition == 0x31) {
+ ++state->locked;
+ state->transition = 0;
+ }
+ }
+
+ if ((address & 0xFF00) == 0x0100) {
+ if (state->locked == GB_SACHEN_LOCKED_CGB) {
+ address |= 0x80;
+ }
+ address = _unscrambleSachen(address);
+ }
+
+ if (address < GB_BASE_CART_BANK1) {
+ return memory->romBase[address];
+ } else if (address < GB_BASE_VRAM) {
+ return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
+ } else {
+ return 0xFF;
+ }
+}
diff --git a/src/gb/memory.c b/src/gb/memory.c
index 0c11308fe..8ac15dbdc 100644
--- a/src/gb/memory.c
+++ b/src/gb/memory.c
@@ -755,6 +755,12 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) {
case GB_MBC3_RTC:
STORE_64LE(memory->rtcLastLatch, 0, &state->memory.rtc.lastLatch);
break;
+ case GB_MBC6:
+ state->memory.mbc6.flags = GBSerializedMBC6FlagsSetFlashBank0(0, memory->mbcState.mbc6.flashBank0);
+ state->memory.mbc6.flags = GBSerializedMBC6FlagsSetFlashBank1(state->memory.mbc6.flags, memory->mbcState.mbc6.flashBank1);
+ state->memory.mbc6.bank1 = memory->currentBank1;
+ state->memory.mbc6.sramBank1 = memory->currentSramBank1;
+ break;
case GB_MBC7:
state->memory.mbc7.state = memory->mbcState.mbc7.state;
state->memory.mbc7.eeprom = memory->mbcState.mbc7.eeprom;
@@ -797,6 +803,17 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) {
state->memory.mmm01.locked = memory->mbcState.mmm01.locked;
state->memory.mmm01.bank0 = memory->mbcState.mmm01.currentBank0;
break;
+ case GB_UNL_NT_OLD_1:
+ case GB_UNL_NT_OLD_2:
+ state->memory.ntOld.flags = GBSerializedNTOldFlagsSetSwapped(0, memory->mbcState.ntOld.swapped);
+ state->memory.ntOld.flags = GBSerializedNTOldFlagsSetRumble(state->memory.ntOld.flags, memory->mbcState.ntOld.rumble);
+ state->memory.ntOld.baseBank = memory->mbcState.ntOld.baseBank;
+ state->memory.ntOld.bankCount = memory->mbcState.ntOld.bankCount;
+ break;
+ case GB_UNL_NT_NEW:
+ state->memory.ntNew.splitMode = memory->mbcState.ntNew.splitMode;
+ state->memory.ntNew.bank1 = memory->currentBank1;
+ break;
case GB_UNL_BBD:
case GB_UNL_HITEK:
state->memory.bbd.dataSwapMode = memory->mbcState.bbd.dataSwapMode;
@@ -823,9 +840,11 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
memory->wramCurrentBank = state->memory.wramCurrentBank;
memory->sramCurrentBank = state->memory.sramCurrentBank;
- GBMBCSwitchBank(gb, memory->currentBank);
GBMemorySwitchWramBank(memory, memory->wramCurrentBank);
- GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
+ if (memory->mbcType != GB_MBC6 && memory->mbcType != GB_UNL_NT_NEW) {
+ GBMBCSwitchBank(gb, memory->currentBank);
+ GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
+ }
LOAD_16LE(memory->dmaSource, 0, &state->memory.dmaSource);
LOAD_16LE(memory->dmaDest, 0, &state->memory.dmaDest);
@@ -882,6 +901,16 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
case GB_MBC3_RTC:
LOAD_64LE(memory->rtcLastLatch, 0, &state->memory.rtc.lastLatch);
break;
+ case GB_MBC6:
+ memory->mbcState.mbc6.flashBank0 = GBSerializedMBC6FlagsGetFlashBank0(state->memory.mbc6.flags);
+ memory->mbcState.mbc6.flashBank1 = GBSerializedMBC6FlagsGetFlashBank1(state->memory.mbc6.flags);
+ memory->currentBank1 = state->memory.mbc6.bank1;
+ memory->currentSramBank1 = state->memory.mbc6.sramBank1;
+ GBMBCSwitchHalfBank(gb, 0, memory->currentBank);
+ GBMBCSwitchHalfBank(gb, 1, memory->currentBank1);
+ GBMBCSwitchSramHalfBank(gb, 0, memory->sramCurrentBank);
+ GBMBCSwitchSramHalfBank(gb, 1, memory->currentSramBank1);
+ break;
case GB_MBC7:
memory->mbcState.mbc7.state = state->memory.mbc7.state;
memory->mbcState.mbc7.eeprom = state->memory.mbc7.eeprom;
@@ -929,6 +958,24 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
GBMBCSwitchBank0(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 2);
}
break;
+ case GB_UNL_NT_OLD_1:
+ case GB_UNL_NT_OLD_2:
+ memory->mbcState.ntOld.swapped = GBSerializedNTOldFlagsGetSwapped(state->memory.ntOld.flags);
+ memory->mbcState.ntOld.rumble = GBSerializedNTOldFlagsGetRumble(state->memory.ntOld.flags);
+ memory->mbcState.ntOld.baseBank = state->memory.ntOld.baseBank;
+ memory->mbcState.ntOld.bankCount = state->memory.ntOld.bankCount;
+ GBMBCSwitchBank0(gb, memory->mbcState.ntOld.baseBank);
+ break;
+ case GB_UNL_NT_NEW:
+ memory->mbcState.ntNew.splitMode = state->memory.ntNew.splitMode;
+ memory->currentBank1 = state->memory.ntNew.bank1;
+ if (memory->mbcState.ntNew.splitMode) {
+ GBMBCSwitchHalfBank(gb, 0, memory->currentBank);
+ GBMBCSwitchHalfBank(gb, 1, memory->currentBank1);
+ } else {
+ GBMBCSwitchBank(gb, memory->currentBank);
+ }
+ break;
case GB_UNL_BBD:
case GB_UNL_HITEK:
memory->mbcState.bbd.dataSwapMode = state->memory.bbd.dataSwapMode & 0x7;
diff --git a/src/gb/overrides.c b/src/gb/overrides.c
index 5bfc4f3cf..ca1a8e9bf 100644
--- a/src/gb/overrides.c
+++ b/src/gb/overrides.c
@@ -672,7 +672,19 @@ static const struct GBCartridgeOverride _overrides[] = {
{ 0x630ED957, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (non-debug)
{ 0x5AFF0038, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (debug)
{ 0xA61856BD, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (non-debug)
+ // Unlicensed bootlegs
{ 0x30F8F86C, GB_MODEL_AUTODETECT, GB_UNL_PKJD, { 0 } }, // Pokemon Jade Version (Telefang Speed bootleg)
+ { 0xE1147E75, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_1, { 0 } }, // Rockman 8
+ { 0xEFF88FAA, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_1, { 0 } }, // True Color 25 in 1 (NT-9920)
+ { 0x811925D9, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // 23 in 1 (CR2011)
+ { 0x62A8016A, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // 29 in 1 (CR2020)
+ { 0x5758D6D9, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Caise Gedou 24 in 1 Diannao Huamian Xuan Game (CY2060)
+ { 0x62A8016A, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Caise Gedou 29 in 1 Diannao Huamian Xuan Game (CY2061)
+ { 0x80265A64, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Rockman X4 (Megaman X4)
+ { 0x805459DE, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Sonic Adventure 8
+ { 0x0B1B808A, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Super Donkey Kong 5
+ { 0x0B1B808A, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Super Donkey Kong 5 (Alt)
+ { 0x4650EB9A, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Super Mario Special 3
{ 0xB289D95A, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Capcom vs SNK - Millennium Fight 2001
{ 0x688D6713, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Digimon 02 4
{ 0x8931A272, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Digimon 2
diff --git a/src/gba/renderers/software-mode0.c b/src/gba/renderers/software-mode0.c
index 5303c1ce0..c42a4a71c 100644
--- a/src/gba/renderers/software-mode0.c
+++ b/src/gba/renderers/software-mode0.c
@@ -170,27 +170,21 @@
outX += 8; \
continue; \
} \
- LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
+ if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
+ LOAD_32(tileData, charBase & VRAM_BLOCK_MASK, vram); \
+ } else { \
+ LOAD_32BE(tileData, charBase & VRAM_BLOCK_MASK, vram); \
+ tileData = ((tileData & 0xF0F0F0F0) >> 4) | ((tileData & 0x0F0F0F0F) << 4); \
+ } \
if (tileData) { \
- if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
- BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \
- BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 1); \
- BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 2); \
- BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 3); \
- BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 4); \
- BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 5); \
- BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 6); \
- BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 7); \
- } else { \
- BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 7); \
- BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 6); \
- BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 5); \
- BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 4); \
- BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 3); \
- BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 2); \
- BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 1); \
- BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \
- } \
+ BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \
+ BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 1); \
+ BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 2); \
+ BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 3); \
+ BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 4); \
+ BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 5); \
+ BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 6); \
+ BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 7); \
} \
outX += 8; \
}
diff --git a/src/platform/qt/ApplicationUpdater.cpp b/src/platform/qt/ApplicationUpdater.cpp
index ce752ecce..4225bd7ee 100644
--- a/src/platform/qt/ApplicationUpdater.cpp
+++ b/src/platform/qt/ApplicationUpdater.cpp
@@ -73,7 +73,7 @@ QStringList ApplicationUpdater::listChannels() {
QString ApplicationUpdater::currentChannel() {
QLatin1String version(projectVersion);
QLatin1String branch(gitBranch);
- if (branch == QLatin1String("heads/") + version) {
+ if (branch == QLatin1String("heads/") + version || branch == version) {
return QLatin1String("stable");
} else {
return QLatin1String("dev");
@@ -137,7 +137,15 @@ QUrl ApplicationUpdater::parseManifest(const QByteArray& manifest) {
QString ApplicationUpdater::destination() const {
QFileInfo path(updateInfo().url.path());
QDir dir(ConfigController::configDir());
- return dir.filePath(QLatin1String("update.") + path.completeSuffix());
+ // QFileInfo::completeSuffix will eat all .'s in the filename...including
+ // ones in the version string, turning mGBA-1.0.0-win32.7z into
+ // 0.0-win32.7z instead of the intended .7z
+ // As a result, so we have to split out the complete suffix manually.
+ QString suffix(path.suffix());
+ if (path.completeBaseName().endsWith(".tar")) {
+ suffix = "tar." + suffix;
+ }
+ return dir.filePath(QLatin1String("update.") + suffix);
}
const char* ApplicationUpdater::platform() {
diff --git a/src/platform/qt/GameBoy.cpp b/src/platform/qt/GameBoy.cpp
index e0271d322..0c1ec8a9b 100644
--- a/src/platform/qt/GameBoy.cpp
+++ b/src/platform/qt/GameBoy.cpp
@@ -35,10 +35,14 @@ static const QList s_mbcList{
GB_HuC3,
GB_UNL_WISDOM_TREE,
GB_UNL_PKJD,
+ GB_UNL_NT_OLD_1,
+ GB_UNL_NT_OLD_2,
GB_UNL_NT_NEW,
GB_UNL_BBD,
GB_UNL_HITEK,
+ GB_UNL_LI_CHENG,
GB_UNL_SACHEN_MMC1,
+ GB_UNL_SACHEN_MMC2,
};
static QMap s_gbModelNames;
@@ -87,10 +91,13 @@ QString GameBoy::mbcName(GBMemoryBankControllerType mbc) {
s_mbcNames[GB_POCKETCAM] = tr("Pocket Cam");
s_mbcNames[GB_TAMA5] = tr("TAMA5");
s_mbcNames[GB_UNL_WISDOM_TREE] = tr("Wisdom Tree");
+ s_mbcNames[GB_UNL_NT_OLD_1] = tr("NT (old 1)");
+ s_mbcNames[GB_UNL_NT_OLD_2] = tr("NT (old 2)");
s_mbcNames[GB_UNL_NT_NEW] = tr("NT (new)");
s_mbcNames[GB_UNL_PKJD] = tr("Pokémon Jade/Diamond");
s_mbcNames[GB_UNL_BBD] = tr("BBD");
s_mbcNames[GB_UNL_HITEK] = tr("Hitek");
+ s_mbcNames[GB_UNL_LI_CHENG] = tr("Li Cheng");
s_mbcNames[GB_UNL_SACHEN_MMC1] = tr("Sachen (MMC1)");
s_mbcNames[GB_UNL_SACHEN_MMC2] = tr("Sachen (MMC2)");
}
diff --git a/src/sm83/debugger/memory-debugger.c b/src/sm83/debugger/memory-debugger.c
index e0a48553f..142ab92c7 100644
--- a/src/sm83/debugger/memory-debugger.c
+++ b/src/sm83/debugger/memory-debugger.c
@@ -47,7 +47,7 @@ static bool _checkWatchpoints(struct SM83Debugger* debugger, uint16_t address, s
size_t i;
for (i = 0; i < mWatchpointListSize(&debugger->watchpoints); ++i) {
watchpoint = mWatchpointListGetPointer(&debugger->watchpoints, i);
- if (watchpoint->address == address && (watchpoint->segment < 0 || watchpoint->segment == debugger->originalMemory.currentSegment(debugger->cpu, address)) && watchpoint->type & type) {
+ if (watchpoint->type & type && address >= watchpoint->minAddress && address < watchpoint->maxAddress && (watchpoint->segment < 0 || watchpoint->segment == debugger->originalMemory.currentSegment(debugger->cpu, address))) {
if (watchpoint->condition) {
int32_t value;
int segment;