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