all repos — mgba @ 799a436acdc4316fa42d288100d79c11dc5c949e

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