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