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