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