all repos — mgba @ bf8cc66a8533d15495a32d4fb1ec8feee9ac7731

mGBA Game Boy Advance Emulator

src/gb/mbc.c (view raw)

  1/* Copyright (c) 2013-2016 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include <mgba/internal/gb/mbc.h>
  7
  8#include <mgba/core/interface.h>
  9#include <mgba/internal/lr35902/lr35902.h>
 10#include <mgba/internal/gb/gb.h>
 11#include <mgba/internal/gb/memory.h>
 12#include <mgba-util/vfs.h>
 13
 14mLOG_DEFINE_CATEGORY(GB_MBC, "GB MBC", "gb.mbc");
 15
 16static void _GBMBCNone(struct GB* gb, uint16_t address, uint8_t value) {
 17	UNUSED(gb);
 18	UNUSED(address);
 19	UNUSED(value);
 20
 21	mLOG(GB_MBC, GAME_ERROR, "Wrote to invalid MBC");
 22}
 23
 24static void _GBMBC1(struct GB*, uint16_t address, uint8_t value);
 25static void _GBMBC2(struct GB*, uint16_t address, uint8_t value);
 26static void _GBMBC3(struct GB*, uint16_t address, uint8_t value);
 27static void _GBMBC5(struct GB*, uint16_t address, uint8_t value);
 28static void _GBMBC6(struct GB*, uint16_t address, uint8_t value);
 29static void _GBMBC7(struct GB*, uint16_t address, uint8_t value);
 30static void _GBHuC3(struct GB*, uint16_t address, uint8_t value);
 31static void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value);
 32static void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value);
 33
 34static uint8_t _GBMBC7Read(struct GBMemory*, uint16_t address);
 35static void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value);
 36
 37static uint8_t _GBTAMA5Read(struct GBMemory*, uint16_t address);
 38
 39static uint8_t _GBPocketCamRead(struct GBMemory*, uint16_t address);
 40
 41void GBMBCSwitchBank(struct GB* gb, int bank) {
 42	size_t bankStart = bank * GB_SIZE_CART_BANK0;
 43	if (bankStart + GB_SIZE_CART_BANK0 > gb->memory.romSize) {
 44		mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank);
 45		bankStart &= (gb->memory.romSize - 1);
 46		bank = bankStart / GB_SIZE_CART_BANK0;
 47		if (!bank) {
 48			++bank;
 49		}
 50	}
 51	gb->memory.romBank = &gb->memory.rom[bankStart];
 52	gb->memory.currentBank = bank;
 53	if (gb->cpu->pc < GB_BASE_VRAM) {
 54		gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc);
 55	}
 56}
 57
 58void GBMBCSwitchBank0(struct GB* gb, int bank) {
 59	size_t bankStart = bank * GB_SIZE_CART_BANK0 << gb->memory.mbcState.mbc1.multicartStride;
 60	if (bankStart + GB_SIZE_CART_BANK0 > gb->memory.romSize) {
 61		mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank);
 62		bankStart &= (gb->memory.romSize - 1);
 63	}
 64	gb->memory.romBase = &gb->memory.rom[bankStart];
 65	if (gb->cpu->pc < GB_SIZE_CART_BANK0) {
 66		gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc);
 67	}
 68}
 69
 70static bool _isMulticart(const uint8_t* mem) {
 71	bool success = true;
 72	struct VFile* vf;
 73
 74	vf = VFileFromConstMemory(&mem[GB_SIZE_CART_BANK0 * 0x10], 1024);
 75	success = success && GBIsROM(vf);
 76	vf->close(vf);
 77
 78	vf = VFileFromConstMemory(&mem[GB_SIZE_CART_BANK0 * 0x20], 1024);
 79	success = success && GBIsROM(vf);
 80	vf->close(vf);
 81
 82	return success;
 83}
 84
 85void GBMBCSwitchSramBank(struct GB* gb, int bank) {
 86	size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM;
 87	if (bankStart + GB_SIZE_EXTERNAL_RAM > gb->sramSize) {
 88		mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid RAM bank: %0X", bank);
 89		bankStart &= (gb->sramSize - 1);
 90		bank = bankStart / GB_SIZE_EXTERNAL_RAM;
 91	}
 92	gb->memory.sramBank = &gb->memory.sram[bankStart];
 93	gb->memory.sramCurrentBank = bank;
 94}
 95
 96void GBMBCInit(struct GB* gb) {
 97	const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
 98	if (gb->memory.rom) {
 99		switch (cart->ramSize) {
100		case 0:
101			gb->sramSize = 0;
102			break;
103		case 1:
104			gb->sramSize = 0x800;
105			break;
106		default:
107		case 2:
108			gb->sramSize = 0x2000;
109			break;
110		case 3:
111			gb->sramSize = 0x8000;
112			break;
113		}
114
115		if (gb->memory.mbcType == GB_MBC_AUTODETECT) {
116			switch (cart->type) {
117			case 0:
118			case 8:
119			case 9:
120				gb->memory.mbcType = GB_MBC_NONE;
121				break;
122			case 1:
123			case 2:
124			case 3:
125				gb->memory.mbcType = GB_MBC1;
126				if (gb->memory.romSize >= GB_SIZE_CART_BANK0 * 0x31 && _isMulticart(gb->memory.rom)) {
127					gb->memory.mbcState.mbc1.multicartStride = 4;
128				} else {
129					gb->memory.mbcState.mbc1.multicartStride = 5;
130				}
131				break;
132			case 5:
133			case 6:
134				gb->memory.mbcType = GB_MBC2;
135				break;
136			case 0x0F:
137			case 0x10:
138				gb->memory.mbcType = GB_MBC3_RTC;
139				break;
140			case 0x11:
141			case 0x12:
142			case 0x13:
143				gb->memory.mbcType = GB_MBC3;
144				break;
145			default:
146				mLOG(GB_MBC, WARN, "Unknown MBC type: %02X", cart->type);
147				// Fall through
148			case 0x19:
149			case 0x1A:
150			case 0x1B:
151				gb->memory.mbcType = GB_MBC5;
152				break;
153			case 0x1C:
154			case 0x1D:
155			case 0x1E:
156				gb->memory.mbcType = GB_MBC5_RUMBLE;
157				break;
158			case 0x20:
159				gb->memory.mbcType = GB_MBC6;
160				break;
161			case 0x22:
162				gb->memory.mbcType = GB_MBC7;
163				break;
164			case 0xFC:
165				gb->memory.mbcType = GB_POCKETCAM;
166				break;
167			case 0xFD:
168				gb->memory.mbcType = GB_TAMA5;
169				break;
170			case 0xFE:
171				gb->memory.mbcType = GB_HuC3;
172				break;
173			case 0xFF:
174				gb->memory.mbcType = GB_HuC1;
175				break;
176			}
177		}
178	} else {
179		gb->memory.mbcType = GB_MBC_NONE;
180	}
181	gb->memory.mbcRead = NULL;
182	switch (gb->memory.mbcType) {
183	case GB_MBC_NONE:
184		gb->memory.mbcWrite = _GBMBCNone;
185		break;
186	case GB_MBC1:
187		gb->memory.mbcWrite = _GBMBC1;
188		break;
189	case GB_MBC2:
190		gb->memory.mbcWrite = _GBMBC2;
191		gb->sramSize = 0x200;
192		break;
193	case GB_MBC3:
194		gb->memory.mbcWrite = _GBMBC3;
195		break;
196	default:
197		mLOG(GB_MBC, WARN, "Unknown MBC type: %02X", cart->type);
198		// Fall through
199	case GB_MBC5:
200		gb->memory.mbcWrite = _GBMBC5;
201		break;
202	case GB_MBC6:
203		mLOG(GB_MBC, WARN, "unimplemented MBC: MBC6");
204		gb->memory.mbcWrite = _GBMBC6;
205		break;
206	case GB_MBC7:
207		gb->memory.mbcWrite = _GBMBC7;
208		gb->memory.mbcRead = _GBMBC7Read;
209		gb->sramSize = 0x100;
210		break;
211	case GB_MMM01:
212		mLOG(GB_MBC, WARN, "unimplemented MBC: MMM01");
213		gb->memory.mbcWrite = _GBMBC1;
214		break;
215	case GB_HuC1:
216		mLOG(GB_MBC, WARN, "unimplemented MBC: HuC-1");
217		gb->memory.mbcWrite = _GBMBC1;
218		break;
219	case GB_HuC3:
220		gb->memory.mbcWrite = _GBHuC3;
221		break;
222	case GB_TAMA5:
223		mLOG(GB_MBC, WARN, "unimplemented MBC: TAMA5");
224		memset(gb->memory.rtcRegs, 0, sizeof(gb->memory.rtcRegs));
225		gb->memory.mbcWrite = _GBTAMA5;
226		gb->memory.mbcRead = _GBTAMA5Read;
227		gb->sramSize = 0x20;
228		break;
229	case GB_MBC3_RTC:
230		memset(gb->memory.rtcRegs, 0, sizeof(gb->memory.rtcRegs));
231		gb->memory.mbcWrite = _GBMBC3;
232		break;
233	case GB_MBC5_RUMBLE:
234		gb->memory.mbcWrite = _GBMBC5;
235		break;
236	case GB_POCKETCAM:
237		gb->memory.mbcWrite = _GBPocketCam;
238		gb->memory.mbcRead = _GBPocketCamRead;
239		break;
240	}
241
242	gb->memory.currentBank = 1;
243	gb->memory.sramCurrentBank = 0;
244	gb->memory.sramAccess = false;
245	gb->memory.rtcAccess = false;
246	gb->memory.activeRtcReg = 0;
247	gb->memory.rtcLatched = false;
248	memset(&gb->memory.rtcRegs, 0, sizeof(gb->memory.rtcRegs));
249
250	GBResizeSram(gb, gb->sramSize);
251
252	if (gb->memory.mbcType == GB_MBC3_RTC) {
253		GBMBCRTCRead(gb);
254	}
255}
256
257static void _latchRtc(struct mRTCSource* rtc, uint8_t* rtcRegs, time_t* rtcLastLatch) {
258	time_t t;
259	if (rtc) {
260		if (rtc->sample) {
261			rtc->sample(rtc);
262		}
263		t = rtc->unixTime(rtc);
264	} else {
265		t = time(0);
266	}
267	time_t currentLatch = t;
268	t -= *rtcLastLatch;
269	*rtcLastLatch = currentLatch;
270
271	int64_t diff;
272	diff = rtcRegs[0] + t % 60;
273	if (diff < 0) {
274		diff += 60;
275		t -= 60;
276	}
277	rtcRegs[0] = diff % 60;
278	t /= 60;
279	t += diff / 60;
280
281	diff = rtcRegs[1] + t % 60;
282	if (diff < 0) {
283		diff += 60;
284		t -= 60;
285	}
286	rtcRegs[1] = diff % 60;
287	t /= 60;
288	t += diff / 60;
289
290	diff = rtcRegs[2] + t % 24;
291	if (diff < 0) {
292		diff += 24;
293		t -= 24;
294	}
295	rtcRegs[2] = diff % 24;
296	t /= 24;
297	t += diff / 24;
298
299	diff = rtcRegs[3] + ((rtcRegs[4] & 1) << 8) + (t & 0x1FF);
300	rtcRegs[3] = diff;
301	rtcRegs[4] &= 0xFE;
302	rtcRegs[4] |= (diff >> 8) & 1;
303	if (diff & 0x200) {
304		rtcRegs[4] |= 0x80;
305	}
306}
307
308void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) {
309	struct GBMemory* memory = &gb->memory;
310	int bank = value & 0x1F;
311	int stride = 1 << memory->mbcState.mbc1.multicartStride;
312	switch (address >> 13) {
313	case 0x0:
314		switch (value) {
315		case 0:
316			memory->sramAccess = false;
317			break;
318		case 0xA:
319			memory->sramAccess = true;
320			GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
321			break;
322		default:
323			// TODO
324			mLOG(GB_MBC, STUB, "MBC1 unknown value %02X", value);
325			break;
326		}
327		break;
328	case 0x1:
329		if (!bank) {
330			++bank;
331		}
332		bank &= stride - 1;
333		GBMBCSwitchBank(gb, bank | (memory->currentBank & (3 * stride)));
334		break;
335	case 0x2:
336		bank &= 3;
337		if (memory->mbcState.mbc1.mode) {
338			GBMBCSwitchBank0(gb, bank);
339			GBMBCSwitchSramBank(gb, bank);
340		}
341		GBMBCSwitchBank(gb, (bank << memory->mbcState.mbc1.multicartStride) | (memory->currentBank & (stride - 1)));
342		break;
343	case 0x3:
344		memory->mbcState.mbc1.mode = value & 1;
345		if (memory->mbcState.mbc1.mode) {
346			GBMBCSwitchBank0(gb, memory->currentBank >> memory->mbcState.mbc1.multicartStride);
347		} else {
348			GBMBCSwitchBank0(gb, 0);
349			GBMBCSwitchSramBank(gb, 0);
350		}
351		break;
352	default:
353		// TODO
354		mLOG(GB_MBC, STUB, "MBC1 unknown address: %04X:%02X", address, value);
355		break;
356	}
357}
358
359void _GBMBC2(struct GB* gb, uint16_t address, uint8_t value) {
360	struct GBMemory* memory = &gb->memory;
361	int bank = value & 0xF;
362	switch (address >> 13) {
363	case 0x0:
364		switch (value) {
365		case 0:
366			memory->sramAccess = false;
367			break;
368		case 0xA:
369			memory->sramAccess = true;
370			GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
371			break;
372		default:
373			// TODO
374			mLOG(GB_MBC, STUB, "MBC1 unknown value %02X", value);
375			break;
376		}
377		break;
378	case 0x1:
379		if (!bank) {
380			++bank;
381		}
382		GBMBCSwitchBank(gb, bank);
383		break;
384	default:
385		// TODO
386		mLOG(GB_MBC, STUB, "MBC2 unknown address: %04X:%02X", address, value);
387		break;
388	}
389}
390
391void _GBMBC3(struct GB* gb, uint16_t address, uint8_t value) {
392	struct GBMemory* memory = &gb->memory;
393	int bank = value & 0x7F;
394	switch (address >> 13) {
395	case 0x0:
396		switch (value) {
397		case 0:
398			memory->sramAccess = false;
399			break;
400		case 0xA:
401			memory->sramAccess = true;
402			GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
403			break;
404		default:
405			// TODO
406			mLOG(GB_MBC, STUB, "MBC3 unknown value %02X", value);
407			break;
408		}
409		break;
410	case 0x1:
411		if (!bank) {
412			++bank;
413		}
414		GBMBCSwitchBank(gb, bank);
415		break;
416	case 0x2:
417		if (value < 4) {
418			GBMBCSwitchSramBank(gb, value);
419			memory->rtcAccess = false;
420		} else if (value >= 8 && value <= 0xC) {
421			memory->activeRtcReg = value - 8;
422			memory->rtcAccess = true;
423		}
424		break;
425	case 0x3:
426		if (memory->rtcLatched && value == 0) {
427			memory->rtcLatched = false;
428		} else if (!memory->rtcLatched && value == 1) {
429			_latchRtc(gb->memory.rtc, gb->memory.rtcRegs, &gb->memory.rtcLastLatch);
430			memory->rtcLatched = true;
431		}
432		break;
433	}
434}
435
436void _GBMBC5(struct GB* gb, uint16_t address, uint8_t value) {
437	struct GBMemory* memory = &gb->memory;
438	int bank;
439	switch (address >> 12) {
440	case 0x0:
441	case 0x1:
442		switch (value) {
443		case 0:
444			memory->sramAccess = false;
445			break;
446		case 0xA:
447			memory->sramAccess = true;
448			GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
449			break;
450		default:
451			// TODO
452			mLOG(GB_MBC, STUB, "MBC5 unknown value %02X", value);
453			break;
454		}
455		break;
456	case 0x2:
457		bank = (memory->currentBank & 0x100) | value;
458		GBMBCSwitchBank(gb, bank);
459		break;
460	case 0x3:
461		bank = (memory->currentBank & 0xFF) | ((value & 1) << 8);
462		GBMBCSwitchBank(gb, bank);
463		break;
464	case 0x4:
465	case 0x5:
466		if (memory->mbcType == GB_MBC5_RUMBLE && memory->rumble) {
467			memory->rumble->setRumble(memory->rumble, (value >> 3) & 1);
468			value &= ~8;
469		}
470		GBMBCSwitchSramBank(gb, value & 0xF);
471		break;
472	default:
473		// TODO
474		mLOG(GB_MBC, STUB, "MBC5 unknown address: %04X:%02X", address, value);
475		break;
476	}
477}
478
479void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) {
480	// TODO
481	mLOG(GB_MBC, STUB, "MBC6 unimplemented");
482	UNUSED(gb);
483	UNUSED(address);
484	UNUSED(value);
485}
486
487void _GBMBC7(struct GB* gb, uint16_t address, uint8_t value) {
488	int bank = value & 0x7F;
489	switch (address >> 13) {
490	case 0x0:
491		switch (value) {
492		default:
493		case 0:
494			gb->memory.mbcState.mbc7.access = 0;
495			break;
496		case 0xA:
497			gb->memory.mbcState.mbc7.access |= 1;
498			break;
499		}
500		break;
501	case 0x1:
502		GBMBCSwitchBank(gb, bank);
503		break;
504	case 0x2:
505		if (value == 0x40) {
506			gb->memory.mbcState.mbc7.access |= 2;
507		} else {
508			gb->memory.mbcState.mbc7.access &= ~2;
509		}
510		break;
511	case 0x5:
512		_GBMBC7Write(&gb->memory, address, value);
513	default:
514		// TODO
515		mLOG(GB_MBC, STUB, "MBC7 unknown address: %04X:%02X", address, value);
516		break;
517	}
518}
519
520uint8_t _GBMBC7Read(struct GBMemory* memory, uint16_t address) {
521	struct GBMBC7State* mbc7 = &memory->mbcState.mbc7;
522	if (mbc7->access != 3) {
523		return 0xFF;
524	}
525	switch (address & 0xF0) {
526	case 0x20:
527		if (memory->rotation && memory->rotation->readTiltX) {
528			int32_t x = -memory->rotation->readTiltX(memory->rotation);
529			x >>= 21;
530			x += 0x81D0;
531			return x;
532		}
533		return 0xFF;
534	case 0x30:
535		if (memory->rotation && memory->rotation->readTiltX) {
536			int32_t x = -memory->rotation->readTiltX(memory->rotation);
537			x >>= 21;
538			x += 0x81D0;
539			return x >> 8;
540		}
541		return 7;
542	case 0x40:
543		if (memory->rotation && memory->rotation->readTiltY) {
544			int32_t y = -memory->rotation->readTiltY(memory->rotation);
545			y >>= 21;
546			y += 0x81D0;
547			return y;
548		}
549		return 0xFF;
550	case 0x50:
551		if (memory->rotation && memory->rotation->readTiltY) {
552			int32_t y = -memory->rotation->readTiltY(memory->rotation);
553			y >>= 21;
554			y += 0x81D0;
555			return y >> 8;
556		}
557		return 7;
558	case 0x60:
559		return 0;
560	case 0x80:
561		return mbc7->eeprom;
562	default:
563		return 0xFF;
564	}
565}
566
567static void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value) {
568	struct GBMBC7State* mbc7 = &memory->mbcState.mbc7;
569	if (mbc7->access != 3) {
570		return;
571	}
572	switch (address & 0xF0) {
573	case 0x00:
574		mbc7->latch = (value & 0x55) == 0x55;
575		return;
576	case 0x10:
577		mbc7->latch |= (value & 0xAA);
578		if (mbc7->latch == 0xAB && memory->rotation && memory->rotation->sample) {
579			memory->rotation->sample(memory->rotation);
580		}
581		mbc7->latch = 0;
582		return;
583	default:
584		mLOG(GB_MBC, STUB, "MBC7 unknown register: %04X:%02X", address, value);
585		return;
586	case 0x80:
587		break;
588	}
589	GBMBC7Field old = memory->mbcState.mbc7.eeprom;
590	value = GBMBC7FieldFillDO(value); // Hi-Z
591	if (!GBMBC7FieldIsCS(old) && GBMBC7FieldIsCS(value)) {
592		mbc7->state = GBMBC7_STATE_IDLE;
593	}
594	if (!GBMBC7FieldIsCLK(old) && GBMBC7FieldIsCLK(value)) {
595		if (mbc7->state == GBMBC7_STATE_READ_COMMAND || mbc7->state == GBMBC7_STATE_EEPROM_WRITE || mbc7->state == GBMBC7_STATE_EEPROM_WRAL) {
596			mbc7->sr <<= 1;
597			mbc7->sr |= GBMBC7FieldGetDI(value);
598			++mbc7->srBits;
599		}
600		switch (mbc7->state) {
601		case GBMBC7_STATE_IDLE:
602			if (GBMBC7FieldIsDI(value)) {
603				mbc7->state = GBMBC7_STATE_READ_COMMAND;
604				mbc7->srBits = 0;
605				mbc7->sr = 0;
606			}
607			break;
608		case GBMBC7_STATE_READ_COMMAND:
609			if (mbc7->srBits == 10) {
610				mbc7->state = 0x10 | (mbc7->sr >> 6);
611				if (mbc7->state & 0xC) {
612					mbc7->state &= ~0x3;
613				}
614				mbc7->srBits = 0;
615				mbc7->address = mbc7->sr & 0x7F;
616			}
617			break;
618		case GBMBC7_STATE_DO:
619			value = GBMBC7FieldSetDO(value, mbc7->sr >> 15);
620			mbc7->sr <<= 1;
621			--mbc7->srBits;
622			if (!mbc7->srBits) {
623				mbc7->state = GBMBC7_STATE_IDLE;
624			}
625			break;
626		default:
627			break;
628		}
629		switch (mbc7->state) {
630		case GBMBC7_STATE_EEPROM_EWEN:
631			mbc7->writable = true;
632			mbc7->state = GBMBC7_STATE_IDLE;
633			break;
634		case GBMBC7_STATE_EEPROM_EWDS:
635			mbc7->writable = false;
636			mbc7->state = GBMBC7_STATE_IDLE;
637			break;
638		case GBMBC7_STATE_EEPROM_WRITE:
639			if (mbc7->srBits == 16) {
640				if (mbc7->writable) {
641					memory->sram[mbc7->address * 2] = mbc7->sr >> 8;
642					memory->sram[mbc7->address * 2 + 1] = mbc7->sr;
643				}
644				mbc7->state = GBMBC7_STATE_IDLE;
645			}
646			break;
647		case GBMBC7_STATE_EEPROM_ERASE:
648			if (mbc7->writable) {
649				memory->sram[mbc7->address * 2] = 0xFF;
650				memory->sram[mbc7->address * 2 + 1] = 0xFF;
651			}
652			mbc7->state = GBMBC7_STATE_IDLE;
653			break;
654		case GBMBC7_STATE_EEPROM_READ:
655			mbc7->srBits = 16;
656			mbc7->sr = memory->sram[mbc7->address * 2] << 8;
657			mbc7->sr |= memory->sram[mbc7->address * 2 + 1];
658			mbc7->state = GBMBC7_STATE_DO;
659			value = GBMBC7FieldClearDO(value);
660			break;
661		case GBMBC7_STATE_EEPROM_WRAL:
662			if (mbc7->srBits == 16) {
663				if (mbc7->writable) {
664					int i;
665					for (i = 0; i < 128; ++i) {
666						memory->sram[i * 2] = mbc7->sr >> 8;
667						memory->sram[i * 2 + 1] = mbc7->sr;
668					}
669				}
670				mbc7->state = GBMBC7_STATE_IDLE;
671			}
672			break;
673		case GBMBC7_STATE_EEPROM_ERAL:
674			if (mbc7->writable) {
675				int i;
676				for (i = 0; i < 128; ++i) {
677					memory->sram[i * 2] = 0xFF;
678					memory->sram[i * 2 + 1] = 0xFF;
679				}
680			}
681			mbc7->state = GBMBC7_STATE_IDLE;
682			break;
683		default:
684			break;
685		}
686	} else if (GBMBC7FieldIsCS(value) && GBMBC7FieldIsCLK(old) && !GBMBC7FieldIsCLK(value)) {
687		value = GBMBC7FieldSetDO(value, GBMBC7FieldGetDO(old));
688	}
689	mbc7->eeprom = value;
690}
691
692void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) {
693	struct GBMemory* memory = &gb->memory;
694	int bank = value & 0x3F;
695	if (address & 0x1FFF) {
696		mLOG(GB_MBC, STUB, "HuC-3 unknown value %04X:%02X", address, value);
697	}
698
699	switch (address >> 13) {
700	case 0x0:
701		switch (value) {
702		case 0xA:
703			memory->sramAccess = true;
704			GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
705			break;
706		default:
707			memory->sramAccess = false;
708			break;
709		}
710		break;
711	case 0x1:
712		GBMBCSwitchBank(gb, bank);
713		break;
714	case 0x2:
715		GBMBCSwitchSramBank(gb, bank);
716		break;
717	default:
718		// TODO
719		mLOG(GB_MBC, STUB, "HuC-3 unknown address: %04X:%02X", address, value);
720		break;
721	}
722}
723
724void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value) {
725	struct GBMemory* memory = &gb->memory;
726	int bank = value & 0x3F;
727	switch (address >> 13) {
728	case 0x0:
729		switch (value) {
730		case 0:
731			memory->sramAccess = false;
732			break;
733		case 0xA:
734			memory->sramAccess = true;
735			GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
736			break;
737		default:
738			// TODO
739			mLOG(GB_MBC, STUB, "Pocket Cam unknown value %02X", value);
740			break;
741		}
742		break;
743	case 0x1:
744		GBMBCSwitchBank(gb, bank);
745		break;
746	case 0x2:
747		if (value < 0x10) {
748			GBMBCSwitchSramBank(gb, value);
749			memory->mbcState.pocketCam.registersActive = false;
750		} else {
751			memory->mbcState.pocketCam.registersActive = true;
752		}
753		break;
754	default:
755		mLOG(GB_MBC, STUB, "Pocket Cam unknown address: %04X:%02X", address, value);
756		break;
757	}
758}
759
760uint8_t _GBPocketCamRead(struct GBMemory* memory, uint16_t address) {
761	if (memory->mbcState.pocketCam.registersActive) {
762		return 0;
763	}
764	return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
765}
766
767void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value) {
768	struct GBMemory* memory = &gb->memory;
769	struct GBTAMA5State* tama5 = &memory->mbcState.tama5;
770	switch (address >> 13) {
771	case 0x5:
772		if (address & 1) {
773			tama5->reg = value;
774		} else {
775			value &= 0xF;
776			if (tama5->reg < GBTAMA5_MAX) {
777				tama5->registers[tama5->reg] = value;
778				uint8_t address = ((tama5->registers[GBTAMA5_CS] << 4) & 0x10) | tama5->registers[GBTAMA5_ADDR_LO];
779				uint8_t out = (tama5->registers[GBTAMA5_WRITE_HI] << 4) | tama5->registers[GBTAMA5_WRITE_LO];
780				switch (tama5->reg) {
781				case GBTAMA5_BANK_LO:
782				case GBTAMA5_BANK_HI:
783					GBMBCSwitchBank(gb, tama5->registers[GBTAMA5_BANK_LO] | (tama5->registers[GBTAMA5_BANK_HI] << 4));
784					break;
785				case GBTAMA5_WRITE_LO:
786				case GBTAMA5_WRITE_HI:
787				case GBTAMA5_CS:
788					break;
789				case GBTAMA5_ADDR_LO:
790					switch (tama5->registers[GBTAMA5_CS] >> 1) {
791					case 0x0: // RAM write
792						memory->sram[address] = out;
793						break;
794					case 0x1: // RAM read
795						break;
796					default:
797						mLOG(GB_MBC, STUB, "TAMA5 unknown address: %X-%02X:%02X", tama5->registers[GBTAMA5_CS] >> 1, address, out);
798					}
799					break;
800				default:
801					mLOG(GB_MBC, STUB, "TAMA5 unknown write: %02X:%X", tama5->reg, value);
802					break;
803				}
804			} else {
805				mLOG(GB_MBC, STUB, "TAMA5 unknown write: %02X", tama5->reg);
806			}
807		}
808		break;
809	default:
810		mLOG(GB_MBC, STUB, "TAMA5 unknown address: %04X:%02X", address, value);
811	}
812}
813
814uint8_t _GBTAMA5Read(struct GBMemory* memory, uint16_t address) {
815	struct GBTAMA5State* tama5 = &memory->mbcState.tama5;
816	if ((address & 0x1FFF) > 1) {
817		mLOG(GB_MBC, STUB, "TAMA5 unknown address: %04X", address);
818	}
819	if (address & 1) {
820		return 0xFF;
821	} else {
822		uint8_t value = 0xF0;
823		uint8_t address = ((tama5->registers[GBTAMA5_CS] << 4) & 0x10) | tama5->registers[GBTAMA5_ADDR_LO];
824		switch (tama5->reg) {
825		case GBTAMA5_ACTIVE:
826			return 0xF1;
827		case GBTAMA5_READ_LO:
828		case GBTAMA5_READ_HI:
829			switch (tama5->registers[GBTAMA5_CS] >> 1) {
830			case 1:
831				value = memory->sram[address];
832				break;
833			default:
834				mLOG(GB_MBC, STUB, "TAMA5 unknown read: %02X", tama5->reg);
835				break;
836			}
837			if (tama5->reg == GBTAMA5_READ_HI) {
838				value >>= 4;
839			}
840			value |= 0xF0;
841			return value;
842		default:
843			mLOG(GB_MBC, STUB, "TAMA5 unknown read: %02X", tama5->reg);
844			return 0xF1;
845		}
846	}
847}
848
849void GBMBCRTCRead(struct GB* gb) {
850	struct GBMBCRTCSaveBuffer rtcBuffer;
851	struct VFile* vf = gb->sramVf;
852	if (!vf) {
853		return;
854	}
855	ssize_t end = vf->seek(vf, -sizeof(rtcBuffer), SEEK_END);
856	switch (end & 0x1FFF) {
857	case 0:
858		break;
859	case 0x1FFC:
860		vf->seek(vf, -sizeof(rtcBuffer) - 4, SEEK_END);
861		break;
862	default:
863		return;
864	}
865	vf->read(vf, &rtcBuffer, sizeof(rtcBuffer));
866
867	LOAD_32LE(gb->memory.rtcRegs[0], 0, &rtcBuffer.latchedSec);
868	LOAD_32LE(gb->memory.rtcRegs[1], 0, &rtcBuffer.latchedMin);
869	LOAD_32LE(gb->memory.rtcRegs[2], 0, &rtcBuffer.latchedHour);
870	LOAD_32LE(gb->memory.rtcRegs[3], 0, &rtcBuffer.latchedDays);
871	LOAD_32LE(gb->memory.rtcRegs[4], 0, &rtcBuffer.latchedDaysHi);
872	LOAD_64LE(gb->memory.rtcLastLatch, 0, &rtcBuffer.unixTime);
873}
874
875void GBMBCRTCWrite(struct GB* gb) {
876	struct VFile* vf = gb->sramVf;
877	if (!vf) {
878		return;
879	}
880
881	uint8_t rtcRegs[5];
882	memcpy(rtcRegs, gb->memory.rtcRegs, sizeof(rtcRegs));
883	time_t rtcLastLatch = gb->memory.rtcLastLatch;
884	_latchRtc(gb->memory.rtc, rtcRegs, &rtcLastLatch);
885
886	struct GBMBCRTCSaveBuffer rtcBuffer;
887	STORE_32LE(rtcRegs[0], 0, &rtcBuffer.sec);
888	STORE_32LE(rtcRegs[1], 0, &rtcBuffer.min);
889	STORE_32LE(rtcRegs[2], 0, &rtcBuffer.hour);
890	STORE_32LE(rtcRegs[3], 0, &rtcBuffer.days);
891	STORE_32LE(rtcRegs[4], 0, &rtcBuffer.daysHi);
892	STORE_32LE(gb->memory.rtcRegs[0], 0, &rtcBuffer.latchedSec);
893	STORE_32LE(gb->memory.rtcRegs[1], 0, &rtcBuffer.latchedMin);
894	STORE_32LE(gb->memory.rtcRegs[2], 0, &rtcBuffer.latchedHour);
895	STORE_32LE(gb->memory.rtcRegs[3], 0, &rtcBuffer.latchedDays);
896	STORE_32LE(gb->memory.rtcRegs[4], 0, &rtcBuffer.latchedDaysHi);
897	STORE_64LE(rtcLastLatch, 0, &rtcBuffer.unixTime);
898
899	if (vf->size(vf) == gb->sramSize) {
900		// Writing past the end of the file can invalidate the file mapping
901		vf->unmap(vf, gb->memory.sram, gb->sramSize);
902		gb->memory.sram = NULL;
903	}
904	vf->seek(vf, gb->sramSize, SEEK_SET);
905	vf->write(vf, &rtcBuffer, sizeof(rtcBuffer));
906	if (!gb->memory.sram) {
907		gb->memory.sram = vf->map(vf, gb->sramSize, MAP_WRITE);
908		GBMBCSwitchSramBank(gb, gb->memory.sramCurrentBank);
909	}
910}