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