From f187fba4fbee89d8fab91b445ed43d4ed0c0bc45 Mon Sep 17 00:00:00 2001
From: Vicki Pfau <vi@endrift.com>
Date: Mon, 24 Jul 2017 13:48:59 -0700
Subject: [PATCH] GB MBC: More detailed TAMA5 implementation, working saves

---
 include/mgba/internal/gb/memory.h | 14 ++++--
 src/gb/mbc.c                      | 78 ++++++++++++++++++++++---------
 2 files changed, 66 insertions(+), 26 deletions(-)

diff --git a/include/mgba/internal/gb/memory.h b/include/mgba/internal/gb/memory.h
index c2d5e92fa..44eb2a541 100644
--- a/include/mgba/internal/gb/memory.h
+++ b/include/mgba/internal/gb/memory.h
@@ -87,8 +87,16 @@ enum GBMBC7MachineState {
 };
 
 enum GBTAMA5Register {
-	GBTAMA5_BANK = 0x0,
-	GBTAMA5_MAX
+	GBTAMA5_BANK_LO = 0x0,
+	GBTAMA5_BANK_HI = 0x1,
+	GBTAMA5_WRITE_LO = 0x4,
+	GBTAMA5_WRITE_HI = 0x5,
+	GBTAMA5_CS = 0x6,
+	GBTAMA5_ADDR_LO = 0x7,
+	GBTAMA5_MAX = 0x8,
+	GBTAMA5_ACTIVE = 0xA,
+	GBTAMA5_READ_LO = 0xC,
+	GBTAMA5_READ_HI = 0xD,
 };
 
 struct GBMBC1State {
@@ -112,9 +120,7 @@ struct GBPocketCamState {
 };
 
 struct GBTAMA5State {
-	bool unlocked;
 	uint8_t reg;
-	uint8_t value;
 	uint8_t registers[GBTAMA5_MAX];
 };
 
diff --git a/src/gb/mbc.c b/src/gb/mbc.c
index 9b628c322..496289a06 100644
--- a/src/gb/mbc.c
+++ b/src/gb/mbc.c
@@ -224,6 +224,7 @@ void GBMBCInit(struct GB* gb) {
 		memset(gb->memory.rtcRegs, 0, sizeof(gb->memory.rtcRegs));
 		gb->memory.mbcWrite = _GBTAMA5;
 		gb->memory.mbcRead = _GBTAMA5Read;
+		gb->sramSize = 0x20;
 		break;
 	case GB_MBC3_RTC:
 		memset(gb->memory.rtcRegs, 0, sizeof(gb->memory.rtcRegs));
@@ -772,31 +773,39 @@ void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value) {
 	switch (address >> 13) {
 	case 0x5:
 		if (address & 1) {
-			if (tama5->unlocked) {
-				tama5->reg = value;
-			} else if (value == 0xA) {
-				tama5->unlocked = true;
-			}
+			tama5->reg = value;
 		} else {
-			uint8_t reg = tama5->reg >> 1;
-			if (reg < GBTAMA5_MAX) {
-				uint8_t mask = 0xF << (4 * (tama5->reg & 1));
-				value <<= (4 * (tama5->reg & 1));
-				value |= tama5->registers[reg] & ~mask;
-				tama5->registers[reg] = value;
-				if (tama5->reg & 1) {
-					switch (reg) {
-					case GBTAMA5_BANK:
-						GBMBCSwitchBank(gb, value & 0x1F);
-						// Fall through
-					default:
-						mLOG(GB_MBC, STUB, "TAMA5 unknown register: %02X:%02X", reg, value);
+			value &= 0xF;
+			if (tama5->reg < GBTAMA5_MAX) {
+				tama5->registers[tama5->reg] = value;
+				uint8_t address = ((tama5->registers[GBTAMA5_CS] << 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_CS:
+					break;
+				case GBTAMA5_ADDR_LO:
+					switch (tama5->registers[GBTAMA5_CS] >> 1) {
+					case 0x0: // RAM write
+						memory->sram[address] = out;
 						break;
+					case 0x1: // RAM read
+						break;
+					default:
+						mLOG(GB_MBC, STUB, "TAMA5 unknown address: %X-%02X:%02X", tama5->registers[GBTAMA5_CS] >> 1, address, out);
 					}
-					tama5->unlocked = false;
+					break;
+				default:
+					mLOG(GB_MBC, STUB, "TAMA5 unknown write: %02X:%X", tama5->reg, value);
+					break;
 				}
 			} else {
-				mLOG(GB_MBC, STUB, "TAMA5 unknown register: %02X", reg);
+				mLOG(GB_MBC, STUB, "TAMA5 unknown write: %02X", tama5->reg);
 			}
 		}
 		break;
@@ -807,11 +816,36 @@ void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value) {
 
 uint8_t _GBTAMA5Read(struct GBMemory* memory, uint16_t address) {
 	struct GBTAMA5State* tama5 = &memory->mbcState.tama5;
-	mLOG(GB_MBC, STUB, "TAMA5 unknown address: %04X", address);
+	if ((address & 0x1FFF) > 1) {
+		mLOG(GB_MBC, STUB, "TAMA5 unknown address: %04X", address);
+	}
 	if (address & 1) {
 		return 0xFF;
 	} else {
-		return 0xF0 | tama5->unlocked;
+		uint8_t value = 0xF0;
+		uint8_t address = ((tama5->registers[GBTAMA5_CS] << 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_CS] >> 1) {
+			case 1:
+				value = memory->sram[address];
+				break;
+			default:
+				mLOG(GB_MBC, STUB, "TAMA5 unknown read: %02X", tama5->reg);
+				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;
+		}
 	}
 }