all repos — mgba @ 49ccb3d080ded5e3628921475aa35d7b2692c2b8

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