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