all repos — mgba @ 362c572009ac74f34eda03b4b4b7b5f93faa1daf

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