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