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}