src/gb/memory.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 "memory.h"
7
8#include "core/interface.h"
9#include "gb/gb.h"
10#include "gb/io.h"
11
12#include "util/memory.h"
13
14#include <time.h>
15
16mLOG_DEFINE_CATEGORY(GB_MBC, "GB MBC");
17mLOG_DEFINE_CATEGORY(GB_MEM, "GB Memory");
18
19static void _GBMBCNone(struct GBMemory* memory, uint16_t address, uint8_t value) {
20 UNUSED(memory);
21 UNUSED(address);
22 UNUSED(value);
23
24 mLOG(GB_MBC, GAME_ERROR, "Wrote to invalid MBC");
25}
26
27static void _GBMBC1(struct GBMemory*, uint16_t address, uint8_t value);
28static void _GBMBC2(struct GBMemory*, uint16_t address, uint8_t value);
29static void _GBMBC3(struct GBMemory*, uint16_t address, uint8_t value);
30static void _GBMBC5(struct GBMemory*, uint16_t address, uint8_t value);
31static void _GBMBC6(struct GBMemory*, uint16_t address, uint8_t value);
32static void _GBMBC7(struct GBMemory*, uint16_t address, uint8_t value);
33static uint8_t _GBMBC7Read(struct GBMemory*, uint16_t address);
34static void _GBMBC7Write(struct GBMemory*, uint16_t address, uint8_t value);
35
36static void GBSetActiveRegion(struct LR35902Core* cpu, uint16_t address) {
37 // TODO
38}
39
40static void _GBMemoryDMAService(struct GB* gb);
41static void _GBMemoryHDMAService(struct GB* gb);
42
43void GBMemoryInit(struct GB* gb) {
44 struct LR35902Core* cpu = gb->cpu;
45 cpu->memory.cpuLoad8 = GBLoad8;
46 cpu->memory.load8 = GBLoad8;
47 cpu->memory.store8 = GBStore8;
48 cpu->memory.setActiveRegion = GBSetActiveRegion;
49
50 gb->memory.wram = 0;
51 gb->memory.wramBank = 0;
52 gb->memory.rom = 0;
53 gb->memory.romBank = 0;
54 gb->memory.romSize = 0;
55 gb->memory.sram = 0;
56 gb->memory.mbcType = GB_MBC_NONE;
57 gb->memory.mbc = 0;
58
59 gb->memory.rtc = NULL;
60
61 GBIOInit(gb);
62}
63
64void GBMemoryDeinit(struct GB* gb) {
65 mappedMemoryFree(gb->memory.wram, GB_SIZE_WORKING_RAM);
66 if (gb->memory.rom) {
67 mappedMemoryFree(gb->memory.rom, gb->memory.romSize);
68 }
69}
70
71void GBMemoryReset(struct GB* gb) {
72 if (gb->memory.wram) {
73 mappedMemoryFree(gb->memory.wram, GB_SIZE_WORKING_RAM);
74 }
75 gb->memory.wram = anonymousMemoryMap(GB_SIZE_WORKING_RAM);
76 GBMemorySwitchWramBank(&gb->memory, 1);
77 gb->memory.romBank = &gb->memory.rom[GB_SIZE_CART_BANK0];
78 gb->memory.currentBank = 1;
79 gb->memory.sramCurrentBank = 0;
80 gb->memory.sramBank = gb->memory.sram;
81
82 gb->memory.ime = false;
83 gb->memory.ie = 0;
84
85 gb->memory.dmaNext = INT_MAX;
86 gb->memory.dmaRemaining = 0;
87 gb->memory.dmaSource = 0;
88 gb->memory.dmaDest = 0;
89 gb->memory.hdmaNext = INT_MAX;
90 gb->memory.hdmaRemaining = 0;
91 gb->memory.hdmaSource = 0;
92 gb->memory.hdmaDest = 0;
93 gb->memory.isHdma = false;
94
95 gb->memory.sramAccess = false;
96 gb->memory.rtcAccess = false;
97 gb->memory.activeRtcReg = 0;
98 gb->memory.rtcLatched = 0;
99 memset(&gb->memory.rtcRegs, 0, sizeof(gb->memory.rtcRegs));
100
101 memset(&gb->memory.hram, 0, sizeof(gb->memory.hram));
102
103 const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
104 switch (cart->type) {
105 case 0:
106 case 8:
107 case 9:
108 gb->memory.mbc = _GBMBCNone;
109 gb->memory.mbcType = GB_MBC_NONE;
110 break;
111 case 1:
112 case 2:
113 case 3:
114 gb->memory.mbc = _GBMBC1;
115 gb->memory.mbcType = GB_MBC1;
116 break;
117 case 5:
118 case 6:
119 gb->memory.mbc = _GBMBC2;
120 gb->memory.mbcType = GB_MBC2;
121 break;
122 case 0x0F:
123 case 0x10:
124 case 0x11:
125 case 0x12:
126 case 0x13:
127 gb->memory.mbc = _GBMBC3;
128 gb->memory.mbcType = GB_MBC3;
129 break;
130 default:
131 mLOG(GB_MBC, WARN, "Unknown MBC type: %02X", cart->type);
132 case 0x19:
133 case 0x1A:
134 case 0x1B:
135 gb->memory.mbc = _GBMBC5;
136 gb->memory.mbcType = GB_MBC5;
137 break;
138 case 0x1C:
139 case 0x1D:
140 case 0x1E:
141 gb->memory.mbc = _GBMBC5;
142 gb->memory.mbcType = GB_MBC5_RUMBLE;
143 break;
144 case 0x20:
145 gb->memory.mbc = _GBMBC6;
146 gb->memory.mbcType = GB_MBC6;
147 break;
148 case 0x22:
149 gb->memory.mbc = _GBMBC7;
150 gb->memory.mbcType = GB_MBC7;
151 memset(&gb->memory.mbcState.mbc7, 0, sizeof(gb->memory.mbcState.mbc7));
152 break;
153 }
154
155 if (!gb->memory.wram) {
156 GBMemoryDeinit(gb);
157 }
158}
159
160void GBMemorySwitchWramBank(struct GBMemory* memory, int bank) {
161 bank &= 7;
162 if (!bank) {
163 bank = 1;
164 }
165 memory->wramBank = &memory->wram[GB_SIZE_WORKING_RAM_BANK0 * bank];
166 memory->wramCurrentBank = bank;
167}
168
169uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address) {
170 struct GB* gb = (struct GB*) cpu->master;
171 struct GBMemory* memory = &gb->memory;
172 switch (address >> 12) {
173 case GB_REGION_CART_BANK0:
174 case GB_REGION_CART_BANK0 + 1:
175 case GB_REGION_CART_BANK0 + 2:
176 case GB_REGION_CART_BANK0 + 3:
177 return memory->rom[address & (GB_SIZE_CART_BANK0 - 1)];
178 case GB_REGION_CART_BANK1:
179 case GB_REGION_CART_BANK1 + 1:
180 case GB_REGION_CART_BANK1 + 2:
181 case GB_REGION_CART_BANK1 + 3:
182 return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
183 case GB_REGION_VRAM:
184 case GB_REGION_VRAM + 1:
185 return gb->video.vramBank[address & (GB_SIZE_VRAM_BANK0 - 1)];
186 case GB_REGION_EXTERNAL_RAM:
187 case GB_REGION_EXTERNAL_RAM + 1:
188 if (memory->rtcAccess) {
189 return gb->memory.rtcRegs[memory->activeRtcReg];
190 } else if (memory->sramAccess) {
191 return gb->memory.sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
192 } else if (memory->mbcType == GB_MBC7) {
193 return _GBMBC7Read(memory, address);
194 }
195 return 0xFF;
196 case GB_REGION_WORKING_RAM_BANK0:
197 case GB_REGION_WORKING_RAM_BANK0 + 2:
198 return memory->wram[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
199 case GB_REGION_WORKING_RAM_BANK1:
200 return memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
201 default:
202 if (address < GB_BASE_OAM) {
203 return memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
204 }
205 if (address < GB_BASE_UNUSABLE) {
206 if (gb->video.mode < 2) {
207 return gb->video.oam.raw[address & 0xFF];
208 }
209 return 0xFF;
210 }
211 if (address < GB_BASE_IO) {
212 mLOG(GB_MEM, GAME_ERROR, "Attempt to read from unusable memory: %04X", address);
213 return 0xFF;
214 }
215 if (address < GB_BASE_HRAM) {
216 return GBIORead(gb, address & (GB_SIZE_IO - 1));
217 }
218 if (address < GB_BASE_IE) {
219 return memory->hram[address & GB_SIZE_HRAM];
220 }
221 return GBIORead(gb, REG_IE);
222 }
223}
224
225void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) {
226 struct GB* gb = (struct GB*) cpu->master;
227 struct GBMemory* memory = &gb->memory;
228 switch (address >> 12) {
229 case GB_REGION_CART_BANK0:
230 case GB_REGION_CART_BANK0 + 1:
231 case GB_REGION_CART_BANK0 + 2:
232 case GB_REGION_CART_BANK0 + 3:
233 case GB_REGION_CART_BANK1:
234 case GB_REGION_CART_BANK1 + 1:
235 case GB_REGION_CART_BANK1 + 2:
236 case GB_REGION_CART_BANK1 + 3:
237 memory->mbc(memory, address, value);
238 return;
239 case GB_REGION_VRAM:
240 case GB_REGION_VRAM + 1:
241 // TODO: Block access in wrong modes
242 gb->video.vramBank[address & (GB_SIZE_VRAM_BANK0 - 1)] = value;
243 return;
244 case GB_REGION_EXTERNAL_RAM:
245 case GB_REGION_EXTERNAL_RAM + 1:
246 if (memory->rtcAccess) {
247 gb->memory.rtcRegs[memory->activeRtcReg] = value;
248 } else if (memory->sramAccess) {
249 gb->memory.sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)] = value;
250 } else if (gb->memory.mbcType == GB_MBC7) {
251 _GBMBC7Write(&gb->memory, address, value);
252 }
253 return;
254 case GB_REGION_WORKING_RAM_BANK0:
255 case GB_REGION_WORKING_RAM_BANK0 + 2:
256 memory->wram[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
257 return;
258 case GB_REGION_WORKING_RAM_BANK1:
259 memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
260 return;
261 default:
262 if (address < GB_BASE_OAM) {
263 memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
264 } else if (address < GB_BASE_UNUSABLE) {
265 if (gb->video.mode < 2) {
266 gb->video.oam.raw[address & 0xFF] = value;
267 }
268 } else if (address < GB_BASE_IO) {
269 mLOG(GB_MEM, GAME_ERROR, "Attempt to write to unusable memory: %04X:%02X", address, value);
270 } else if (address < GB_BASE_HRAM) {
271 GBIOWrite(gb, address & (GB_SIZE_IO - 1), value);
272 } else if (address < GB_BASE_IE) {
273 memory->hram[address & GB_SIZE_HRAM] = value;
274 } else {
275 GBIOWrite(gb, REG_IE, value);
276 }
277 }
278}
279
280int32_t GBMemoryProcessEvents(struct GB* gb, int32_t cycles) {
281 int nextEvent = INT_MAX;
282 if (gb->memory.dmaRemaining) {
283 gb->memory.dmaNext -= cycles;
284 if (gb->memory.dmaNext <= 0) {
285 _GBMemoryDMAService(gb);
286 }
287 nextEvent = gb->memory.dmaNext;
288 }
289 if (gb->memory.hdmaRemaining) {
290 gb->memory.hdmaNext -= cycles;
291 if (gb->memory.hdmaNext <= 0) {
292 _GBMemoryHDMAService(gb);
293 }
294 if (gb->memory.hdmaNext < nextEvent) {
295 nextEvent = gb->memory.hdmaNext;
296 }
297 }
298 return nextEvent;
299}
300
301void GBMemoryDMA(struct GB* gb, uint16_t base) {
302 if (base > 0xF100) {
303 return;
304 }
305 gb->cpu->memory.store8 = GBDMAStore8;
306 gb->cpu->memory.load8 = GBDMALoad8;
307 gb->memory.dmaNext = gb->cpu->cycles + 8;
308 if (gb->memory.dmaNext < gb->cpu->nextEvent) {
309 gb->cpu->nextEvent = gb->memory.dmaNext;
310 }
311 gb->memory.dmaSource = base;
312 gb->memory.dmaDest = 0;
313 gb->memory.dmaRemaining = 0xA0;
314}
315
316void GBMemoryWriteHDMA5(struct GB* gb, uint8_t value) {
317 gb->memory.hdmaSource = gb->memory.io[REG_HDMA1] << 8;
318 gb->memory.hdmaSource |= gb->memory.io[REG_HDMA2];
319 gb->memory.hdmaDest = gb->memory.io[REG_HDMA3] << 8;
320 gb->memory.hdmaDest |= gb->memory.io[REG_HDMA4];
321 gb->memory.hdmaSource &= 0xFFF0;
322 if (gb->memory.hdmaSource >= 0x8000 && gb->memory.hdmaSource < 0xA000) {
323 mLOG(GB_MEM, GAME_ERROR, "Invalid HDMA source: %04X", gb->memory.hdmaSource);
324 return;
325 }
326 gb->memory.hdmaDest &= 0x1FF0;
327 gb->memory.hdmaDest |= 0x8000;
328 bool wasHdma = gb->memory.isHdma;
329 gb->memory.isHdma = value & 0x80;
330 if (!wasHdma && !gb->memory.isHdma) {
331 gb->memory.hdmaRemaining = ((value & 0x7F) + 1) * 0x10;
332 gb->memory.hdmaNext = gb->cpu->cycles;
333 gb->cpu->nextEvent = gb->cpu->cycles;
334 }
335}
336
337void _GBMemoryDMAService(struct GB* gb) {
338 uint8_t b = GBLoad8(gb->cpu, gb->memory.dmaSource);
339 // TODO: Can DMA write OAM during modes 2-3?
340 gb->video.oam.raw[gb->memory.dmaDest] = b;
341 ++gb->memory.dmaSource;
342 ++gb->memory.dmaDest;
343 --gb->memory.dmaRemaining;
344 if (gb->memory.dmaRemaining) {
345 gb->memory.dmaNext += 4;
346 } else {
347 gb->memory.dmaNext = INT_MAX;
348 gb->cpu->memory.store8 = GBStore8;
349 gb->cpu->memory.load8 = GBLoad8;
350 }
351}
352
353void _GBMemoryHDMAService(struct GB* gb) {
354 uint8_t b = gb->cpu->memory.load8(gb->cpu, gb->memory.hdmaSource);
355 gb->cpu->memory.store8(gb->cpu, gb->memory.hdmaDest, b);
356 ++gb->memory.hdmaSource;
357 ++gb->memory.hdmaDest;
358 --gb->memory.hdmaRemaining;
359 gb->cpu->cycles += 2;
360 if (gb->memory.hdmaRemaining) {
361 gb->memory.hdmaNext += 2;
362 } else {
363 gb->memory.io[REG_HDMA1] = gb->memory.hdmaSource >> 8;
364 gb->memory.io[REG_HDMA2] = gb->memory.hdmaSource;
365 gb->memory.io[REG_HDMA3] = gb->memory.hdmaDest >> 8;
366 gb->memory.io[REG_HDMA4] = gb->memory.hdmaDest;
367 if (gb->memory.isHdma) {
368 --gb->memory.io[REG_HDMA5];
369 if (gb->memory.io[REG_HDMA5] == 0xFF) {
370 gb->memory.isHdma = false;
371 }
372 } else {
373 gb->memory.io[REG_HDMA5] |= 0x80;
374 }
375 }
376}
377
378uint8_t GBDMALoad8(struct LR35902Core* cpu, uint16_t address) {
379 struct GB* gb = (struct GB*) cpu->master;
380 struct GBMemory* memory = &gb->memory;
381 if (address < 0xFF80 || address == 0xFFFF) {
382 return 0xFF;
383 }
384 return memory->hram[address & GB_SIZE_HRAM];
385}
386
387void GBDMAStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) {
388 struct GB* gb = (struct GB*) cpu->master;
389 struct GBMemory* memory = &gb->memory;
390 if (address < 0xFF80 || address == 0xFFFF) {
391 return;
392 }
393 memory->hram[address & GB_SIZE_HRAM] = value;
394}
395
396uint8_t GBView8(struct LR35902Core* cpu, uint16_t address);
397
398void GBPatch8(struct LR35902Core* cpu, uint16_t address, int8_t value, int8_t* old);
399
400static void _switchBank(struct GBMemory* memory, int bank) {
401 size_t bankStart = bank * GB_SIZE_CART_BANK0;
402 if (bankStart + GB_SIZE_CART_BANK0 > memory->romSize) {
403 mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank);
404 bankStart &= (GB_SIZE_CART_BANK0 - 1);
405 bank /= GB_SIZE_CART_BANK0;
406 }
407 memory->romBank = &memory->rom[bankStart];
408 memory->currentBank = bank;
409}
410
411static void _switchSramBank(struct GBMemory* memory, int bank) {
412 size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM;
413 memory->sramBank = &memory->sram[bankStart];
414 memory->sramCurrentBank = bank;
415}
416
417static void _latchRtc(struct GBMemory* memory) {
418 time_t t;
419 struct mRTCSource* rtc = memory->rtc;
420 if (rtc) {
421 if (rtc->sample) {
422 rtc->sample(rtc);
423 }
424 t = rtc->unixTime(rtc);
425 } else {
426 t = time(0);
427 }
428 struct tm date;
429 localtime_r(&t, &date);
430 memory->rtcRegs[0] = date.tm_sec;
431 memory->rtcRegs[1] = date.tm_min;
432 memory->rtcRegs[2] = date.tm_hour;
433 memory->rtcRegs[3] = date.tm_yday; // TODO: Persist day counter
434 memory->rtcRegs[4] &= 0xF0;
435 memory->rtcRegs[4] |= date.tm_yday >> 8;
436}
437
438void _GBMBC1(struct GBMemory* memory, uint16_t address, uint8_t value) {
439 int bank = value & 0x1F;
440 switch (address >> 13) {
441 case 0x0:
442 switch (value) {
443 case 0:
444 memory->sramAccess = false;
445 break;
446 case 0xA:
447 memory->sramAccess = true;
448 _switchSramBank(memory, memory->sramCurrentBank);
449 break;
450 default:
451 // TODO
452 mLOG(GB_MBC, STUB, "MBC1 unknown value %02X", value);
453 break;
454 }
455 break;
456 case 0x1:
457 if (!bank) {
458 ++bank;
459 }
460 _switchBank(memory, bank | (memory->currentBank & 0x60));
461 break;
462 default:
463 // TODO
464 mLOG(GB_MBC, STUB, "MBC1 unknown address: %04X:%02X", address, value);
465 break;
466 }
467}
468
469void _GBMBC2(struct GBMemory* memory, uint16_t address, uint8_t value) {
470 mLOG(GB_MBC, STUB, "MBC2 unimplemented");
471}
472
473void _GBMBC3(struct GBMemory* memory, uint16_t address, uint8_t value) {
474 int bank = value & 0x7F;
475 switch (address >> 13) {
476 case 0x0:
477 switch (value) {
478 case 0:
479 memory->sramAccess = false;
480 break;
481 case 0xA:
482 memory->sramAccess = true;
483 _switchSramBank(memory, memory->sramCurrentBank);
484 break;
485 default:
486 // TODO
487 mLOG(GB_MBC, STUB, "MBC3 unknown value %02X", value);
488 break;
489 }
490 break;
491 case 0x1:
492 if (!bank) {
493 ++bank;
494 }
495 _switchBank(memory, bank);
496 break;
497 case 0x2:
498 if (value < 4) {
499 _switchSramBank(memory, value);
500 memory->rtcAccess = false;
501 } else if (value >= 8 && value <= 0xC) {
502 memory->activeRtcReg = value - 8;
503 memory->rtcAccess = true;
504 }
505 break;
506 case 0x3:
507 if (memory->rtcLatched && value == 0) {
508 memory->rtcLatched = value;
509 } else if (!memory->rtcLatched && value == 1) {
510 _latchRtc(memory);
511 }
512 break;
513 }
514}
515
516void _GBMBC5(struct GBMemory* memory, uint16_t address, uint8_t value) {
517 int bank;
518 switch (address >> 12) {
519 case 0x0:
520 case 0x1:
521 switch (value) {
522 case 0:
523 memory->sramAccess = false;
524 break;
525 case 0xA:
526 memory->sramAccess = true;
527 _switchSramBank(memory, memory->sramCurrentBank);
528 break;
529 default:
530 // TODO
531 mLOG(GB_MBC, STUB, "MBC5 unknown value %02X", value);
532 break;
533 }
534 break;
535 case 0x2:
536 bank = (memory->currentBank & 0x100) | value;
537 _switchBank(memory, bank);
538 break;
539 case 0x3:
540 bank = (memory->currentBank & 0xFF) | ((value & 1) << 8);
541 _switchBank(memory, bank);
542 break;
543 case 0x4:
544 case 0x5:
545 if (memory->mbcType == GB_MBC5_RUMBLE) {
546 memory->rumble->setRumble(memory->rumble, (value >> 3) & 1);
547 value &= ~8;
548 }
549 _switchSramBank(memory, value & 0xF);
550 break;
551 default:
552 // TODO
553 mLOG(GB_MBC, STUB, "MBC5 unknown address: %04X:%02X", address, value);
554 break;
555 }
556}
557
558void _GBMBC6(struct GBMemory* memory, uint16_t address, uint8_t value) {
559 // TODO
560 mLOG(GB_MBC, STUB, "MBC6 unimplemented");
561}
562
563void _GBMBC7(struct GBMemory* memory, uint16_t address, uint8_t value) {
564 int bank = value & 0x7F;
565 switch (address >> 13) {
566 case 0x1:
567 _switchBank(memory, bank);
568 break;
569 case 0x2:
570 if (value < 0x10) {
571 _switchSramBank(memory, value);
572 }
573 break;
574 default:
575 // TODO
576 mLOG(GB_MBC, STUB, "MBC7 unknown address: %04X:%02X", address, value);
577 break;
578 }
579}
580
581uint8_t _GBMBC7Read(struct GBMemory* memory, uint16_t address) {
582 struct GBMBC7State* mbc7 = &memory->mbcState.mbc7;
583 switch (address & 0xF0) {
584 case 0x00:
585 case 0x10:
586 case 0x60:
587 case 0x70:
588 return 0;
589 case 0x20:
590 if (memory->rotation && memory->rotation->readTiltX) {
591 int32_t x = -memory->rotation->readTiltX(memory->rotation);
592 x >>= 21;
593 x += 2047;
594 return x;
595 }
596 return 0xFF;
597 case 0x30:
598 if (memory->rotation && memory->rotation->readTiltX) {
599 int32_t x = -memory->rotation->readTiltX(memory->rotation);
600 x >>= 21;
601 x += 2047;
602 return x >> 8;
603 }
604 return 7;
605 case 0x40:
606 if (memory->rotation && memory->rotation->readTiltY) {
607 int32_t y = -memory->rotation->readTiltY(memory->rotation);
608 y >>= 21;
609 y += 2047;
610 return y;
611 }
612 return 0xFF;
613 case 0x50:
614 if (memory->rotation && memory->rotation->readTiltY) {
615 int32_t y = -memory->rotation->readTiltY(memory->rotation);
616 y >>= 21;
617 y += 2047;
618 return y >> 8;
619 }
620 return 7;
621 case 0x80:
622 return (mbc7->sr >> 16) & 1;
623 default:
624 return 0xFF;
625 }
626}
627
628void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value) {
629 if ((address & 0xF0) != 0x80) {
630 return;
631 }
632 struct GBMBC7State* mbc7 = &memory->mbcState.mbc7;
633 GBMBC7Field old = memory->mbcState.mbc7.field;
634 mbc7->field = GBMBC7FieldClearIO(value);
635 if (!GBMBC7FieldIsCS(old) && GBMBC7FieldIsCS(value)) {
636 if (mbc7->state == GBMBC7_STATE_WRITE) {
637 if (mbc7->writable) {
638 memory->sramBank[mbc7->address * 2] = mbc7->sr >> 8;
639 memory->sramBank[mbc7->address * 2 + 1] = mbc7->sr;
640 }
641 mbc7->sr = 0x1FFFF;
642 mbc7->state = GBMBC7_STATE_NULL;
643 } else {
644 mbc7->state = GBMBC7_STATE_IDLE;
645 }
646 }
647 if (!GBMBC7FieldIsSK(old) && GBMBC7FieldIsSK(value)) {
648 if (mbc7->state > GBMBC7_STATE_IDLE && mbc7->state != GBMBC7_STATE_READ) {
649 mbc7->sr <<= 1;
650 mbc7->sr |= GBMBC7FieldGetIO(value);
651 ++mbc7->srBits;
652 }
653 switch (mbc7->state) {
654 case GBMBC7_STATE_IDLE:
655 if (GBMBC7FieldIsIO(value)) {
656 mbc7->state = GBMBC7_STATE_READ_COMMAND;
657 mbc7->srBits = 0;
658 mbc7->sr = 0;
659 }
660 break;
661 case GBMBC7_STATE_READ_COMMAND:
662 if (mbc7->srBits == 2) {
663 mbc7->state = GBMBC7_STATE_READ_ADDRESS;
664 mbc7->srBits = 0;
665 mbc7->command = mbc7->sr;
666 }
667 break;
668 case GBMBC7_STATE_READ_ADDRESS:
669 if (mbc7->srBits == 8) {
670 mbc7->state = GBMBC7_STATE_COMMAND_0 + mbc7->command;
671 mbc7->srBits = 0;
672 mbc7->address = mbc7->sr;
673 if (mbc7->state == GBMBC7_STATE_COMMAND_0) {
674 switch (mbc7->address >> 6) {
675 case 0:
676 mbc7->writable = false;
677 mbc7->state = GBMBC7_STATE_NULL;
678 break;
679 case 3:
680 mbc7->writable = true;
681 mbc7->state = GBMBC7_STATE_NULL;
682 break;
683 }
684 }
685 }
686 break;
687 case GBMBC7_STATE_COMMAND_0:
688 if (mbc7->srBits == 16) {
689 switch (mbc7->address >> 6) {
690 case 0:
691 mbc7->writable = false;
692 mbc7->state = GBMBC7_STATE_NULL;
693 break;
694 case 1:
695 mbc7->state = GBMBC7_STATE_WRITE;
696 if (mbc7->writable) {
697 int i;
698 for (i = 0; i < 256; ++i) {
699 memory->sramBank[i * 2] = mbc7->sr >> 8;
700 memory->sramBank[i * 2 + 1] = mbc7->sr;
701 }
702 }
703 break;
704 case 2:
705 mbc7->state = GBMBC7_STATE_WRITE;
706 if (mbc7->writable) {
707 int i;
708 for (i = 0; i < 256; ++i) {
709 memory->sramBank[i * 2] = 0xFF;
710 memory->sramBank[i * 2 + 1] = 0xFF;
711 }
712 }
713 break;
714 case 3:
715 mbc7->writable = true;
716 mbc7->state = GBMBC7_STATE_NULL;
717 break;
718 }
719 }
720 break;
721 case GBMBC7_STATE_COMMAND_SR_WRITE:
722 if (mbc7->srBits == 16) {
723 mbc7->srBits = 0;
724 mbc7->state = GBMBC7_STATE_WRITE;
725 }
726 break;
727 case GBMBC7_STATE_COMMAND_SR_READ:
728 if (mbc7->srBits == 1) {
729 mbc7->sr = memory->sramBank[mbc7->address * 2] << 8;
730 mbc7->sr |= memory->sramBank[mbc7->address * 2 + 1];
731 mbc7->srBits = 0;
732 mbc7->state = GBMBC7_STATE_READ;
733 }
734 break;
735 case GBMBC7_STATE_COMMAND_SR_FILL:
736 if (mbc7->srBits == 16) {
737 mbc7->sr = 0xFFFF;
738 mbc7->srBits = 0;
739 mbc7->state = GBMBC7_STATE_WRITE;
740 }
741 break;
742 default:
743 break;
744 }
745 } else if (GBMBC7FieldIsSK(old) && !GBMBC7FieldIsSK(value)) {
746 if (mbc7->state == GBMBC7_STATE_READ) {
747 mbc7->sr <<= 1;
748 ++mbc7->srBits;
749 if (mbc7->srBits == 16) {
750 mbc7->srBits = 0;
751 mbc7->state = GBMBC7_STATE_NULL;
752 }
753 }
754 }
755}