all repos — mgba @ 8561c80a1c43392523ca3be614f2ae8d72b3a469

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