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