all repos — mgba @ af77e5ab62a920000b8e33544ba2f2efa0cb42e9

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