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 } else {
370 gb->memory.io[REG_HDMA5] |= 0x80;
371 }
372 }
373}
374
375uint8_t GBDMALoad8(struct LR35902Core* cpu, uint16_t address) {
376 struct GB* gb = (struct GB*) cpu->master;
377 struct GBMemory* memory = &gb->memory;
378 if (address < 0xFF80 || address == 0xFFFF) {
379 return 0xFF;
380 }
381 return memory->hram[address & GB_SIZE_HRAM];
382}
383
384void GBDMAStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) {
385 struct GB* gb = (struct GB*) cpu->master;
386 struct GBMemory* memory = &gb->memory;
387 if (address < 0xFF80 || address == 0xFFFF) {
388 return;
389 }
390 memory->hram[address & GB_SIZE_HRAM] = value;
391}
392
393uint8_t GBView8(struct LR35902Core* cpu, uint16_t address);
394
395void GBPatch8(struct LR35902Core* cpu, uint16_t address, int8_t value, int8_t* old);
396
397static void _switchBank(struct GBMemory* memory, int bank) {
398 size_t bankStart = bank * GB_SIZE_CART_BANK0;
399 if (bankStart + GB_SIZE_CART_BANK0 > memory->romSize) {
400 mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank);
401 bankStart &= (GB_SIZE_CART_BANK0 - 1);
402 bank /= GB_SIZE_CART_BANK0;
403 }
404 memory->romBank = &memory->rom[bankStart];
405 memory->currentBank = bank;
406}
407
408static void _switchSramBank(struct GBMemory* memory, int bank) {
409 size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM;
410 memory->sramBank = &memory->sram[bankStart];
411 memory->sramCurrentBank = bank;
412}
413
414static void _latchRtc(struct GBMemory* memory) {
415 time_t t;
416 struct mRTCSource* rtc = memory->rtc;
417 if (rtc) {
418 if (rtc->sample) {
419 rtc->sample(rtc);
420 }
421 t = rtc->unixTime(rtc);
422 } else {
423 t = time(0);
424 }
425 struct tm date;
426 localtime_r(&t, &date);
427 memory->rtcRegs[0] = date.tm_sec;
428 memory->rtcRegs[1] = date.tm_min;
429 memory->rtcRegs[2] = date.tm_hour;
430 memory->rtcRegs[3] = date.tm_yday; // TODO: Persist day counter
431 memory->rtcRegs[4] &= 0xF0;
432 memory->rtcRegs[4] |= date.tm_yday >> 8;
433}
434
435void _GBMBC1(struct GBMemory* memory, uint16_t address, uint8_t value) {
436 int bank = value & 0x1F;
437 switch (address >> 13) {
438 case 0x0:
439 switch (value) {
440 case 0:
441 memory->sramAccess = false;
442 break;
443 case 0xA:
444 memory->sramAccess = true;
445 _switchSramBank(memory, memory->sramCurrentBank);
446 break;
447 default:
448 // TODO
449 mLOG(GB_MBC, STUB, "MBC1 unknown value %02X", value);
450 break;
451 }
452 break;
453 case 0x1:
454 if (!bank) {
455 ++bank;
456 }
457 _switchBank(memory, bank | (memory->currentBank & 0x60));
458 break;
459 default:
460 // TODO
461 mLOG(GB_MBC, STUB, "MBC1 unknown address: %04X:%02X", address, value);
462 break;
463 }
464}
465
466void _GBMBC2(struct GBMemory* memory, uint16_t address, uint8_t value) {
467 mLOG(GB_MBC, STUB, "MBC2 unimplemented");
468}
469
470void _GBMBC3(struct GBMemory* memory, uint16_t address, uint8_t value) {
471 int bank = value & 0x7F;
472 switch (address >> 13) {
473 case 0x0:
474 switch (value) {
475 case 0:
476 memory->sramAccess = false;
477 break;
478 case 0xA:
479 memory->sramAccess = true;
480 _switchSramBank(memory, memory->sramCurrentBank);
481 break;
482 default:
483 // TODO
484 mLOG(GB_MBC, STUB, "MBC3 unknown value %02X", value);
485 break;
486 }
487 break;
488 case 0x1:
489 if (!bank) {
490 ++bank;
491 }
492 _switchBank(memory, bank);
493 break;
494 case 0x2:
495 if (value < 4) {
496 _switchSramBank(memory, value);
497 memory->rtcAccess = false;
498 } else if (value >= 8 && value <= 0xC) {
499 memory->activeRtcReg = value - 8;
500 memory->rtcAccess = true;
501 }
502 break;
503 case 0x3:
504 if (memory->rtcLatched && value == 0) {
505 memory->rtcLatched = value;
506 } else if (!memory->rtcLatched && value == 1) {
507 _latchRtc(memory);
508 }
509 break;
510 }
511}
512
513void _GBMBC5(struct GBMemory* memory, uint16_t address, uint8_t value) {
514 int bank = value;
515 switch (address >> 13) {
516 case 0x0:
517 switch (value) {
518 case 0:
519 memory->sramAccess = false;
520 break;
521 case 0xA:
522 memory->sramAccess = true;
523 _switchSramBank(memory, memory->sramCurrentBank);
524 break;
525 default:
526 // TODO
527 mLOG(GB_MBC, STUB, "MBC5 unknown value %02X", value);
528 break;
529 }
530 break;
531 case 0x1:
532 _switchBank(memory, bank);
533 break;
534 case 0x2:
535 if (memory->mbcType == GB_MBC5_RUMBLE) {
536 memory->rumble->setRumble(memory->rumble, (value >> 3) & 1);
537 value &= ~8;
538 }
539 _switchSramBank(memory, value & 0xF);
540 break;
541 default:
542 // TODO
543 mLOG(GB_MBC, STUB, "MBC5 unknown address: %04X:%02X", address, value);
544 break;
545 }
546}
547
548void _GBMBC6(struct GBMemory* memory, uint16_t address, uint8_t value) {
549 // TODO
550 mLOG(GB_MBC, STUB, "MBC6 unimplemented");
551}
552
553void _GBMBC7(struct GBMemory* memory, uint16_t address, uint8_t value) {
554 int bank = value & 0x7F;
555 switch (address >> 13) {
556 case 0x1:
557 _switchBank(memory, bank);
558 break;
559 case 0x2:
560 if (value < 0x10) {
561 _switchSramBank(memory, value);
562 }
563 break;
564 default:
565 // TODO
566 mLOG(GB_MBC, STUB, "MBC7 unknown address: %04X:%02X", address, value);
567 break;
568 }
569}
570
571uint8_t _GBMBC7Read(struct GBMemory* memory, uint16_t address) {
572 struct GBMBC7State* mbc7 = &memory->mbcState.mbc7;
573 switch (address & 0xF0) {
574 case 0x00:
575 case 0x10:
576 case 0x60:
577 case 0x70:
578 return 0;
579 case 0x20:
580 if (memory->rotation && memory->rotation->readTiltX) {
581 int32_t x = -memory->rotation->readTiltX(memory->rotation);
582 x >>= 21;
583 x += 2047;
584 return x;
585 }
586 return 0xFF;
587 case 0x30:
588 if (memory->rotation && memory->rotation->readTiltX) {
589 int32_t x = -memory->rotation->readTiltX(memory->rotation);
590 x >>= 21;
591 x += 2047;
592 return x >> 8;
593 }
594 return 7;
595 case 0x40:
596 if (memory->rotation && memory->rotation->readTiltY) {
597 int32_t y = -memory->rotation->readTiltY(memory->rotation);
598 y >>= 21;
599 y += 2047;
600 return y;
601 }
602 return 0xFF;
603 case 0x50:
604 if (memory->rotation && memory->rotation->readTiltY) {
605 int32_t y = -memory->rotation->readTiltY(memory->rotation);
606 y >>= 21;
607 y += 2047;
608 return y >> 8;
609 }
610 return 7;
611 case 0x80:
612 return (mbc7->sr >> 16) & 1;
613 default:
614 return 0xFF;
615 }
616}
617
618void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value) {
619 if ((address & 0xF0) != 0x80) {
620 return;
621 }
622 struct GBMBC7State* mbc7 = &memory->mbcState.mbc7;
623 GBMBC7Field old = memory->mbcState.mbc7.field;
624 mbc7->field = GBMBC7FieldClearIO(value);
625 if (!GBMBC7FieldIsCS(old) && GBMBC7FieldIsCS(value)) {
626 if (mbc7->state == GBMBC7_STATE_WRITE) {
627 if (mbc7->writable) {
628 memory->sramBank[mbc7->address * 2] = mbc7->sr >> 8;
629 memory->sramBank[mbc7->address * 2 + 1] = mbc7->sr;
630 }
631 mbc7->sr = 0x1FFFF;
632 mbc7->state = GBMBC7_STATE_NULL;
633 } else {
634 mbc7->state = GBMBC7_STATE_IDLE;
635 }
636 }
637 if (!GBMBC7FieldIsSK(old) && GBMBC7FieldIsSK(value)) {
638 if (mbc7->state > GBMBC7_STATE_IDLE && mbc7->state != GBMBC7_STATE_READ) {
639 mbc7->sr <<= 1;
640 mbc7->sr |= GBMBC7FieldGetIO(value);
641 ++mbc7->srBits;
642 }
643 switch (mbc7->state) {
644 case GBMBC7_STATE_IDLE:
645 if (GBMBC7FieldIsIO(value)) {
646 mbc7->state = GBMBC7_STATE_READ_COMMAND;
647 mbc7->srBits = 0;
648 mbc7->sr = 0;
649 }
650 break;
651 case GBMBC7_STATE_READ_COMMAND:
652 if (mbc7->srBits == 2) {
653 mbc7->state = GBMBC7_STATE_READ_ADDRESS;
654 mbc7->srBits = 0;
655 mbc7->command = mbc7->sr;
656 }
657 break;
658 case GBMBC7_STATE_READ_ADDRESS:
659 if (mbc7->srBits == 8) {
660 mbc7->state = GBMBC7_STATE_COMMAND_0 + mbc7->command;
661 mbc7->srBits = 0;
662 mbc7->address = mbc7->sr;
663 if (mbc7->state == GBMBC7_STATE_COMMAND_0) {
664 switch (mbc7->address >> 6) {
665 case 0:
666 mbc7->writable = false;
667 mbc7->state = GBMBC7_STATE_NULL;
668 break;
669 case 3:
670 mbc7->writable = true;
671 mbc7->state = GBMBC7_STATE_NULL;
672 break;
673 }
674 }
675 }
676 break;
677 case GBMBC7_STATE_COMMAND_0:
678 if (mbc7->srBits == 16) {
679 switch (mbc7->address >> 6) {
680 case 0:
681 mbc7->writable = false;
682 mbc7->state = GBMBC7_STATE_NULL;
683 break;
684 case 1:
685 mbc7->state = GBMBC7_STATE_WRITE;
686 if (mbc7->writable) {
687 int i;
688 for (i = 0; i < 256; ++i) {
689 memory->sramBank[i * 2] = mbc7->sr >> 8;
690 memory->sramBank[i * 2 + 1] = mbc7->sr;
691 }
692 }
693 break;
694 case 2:
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] = 0xFF;
700 memory->sramBank[i * 2 + 1] = 0xFF;
701 }
702 }
703 break;
704 case 3:
705 mbc7->writable = true;
706 mbc7->state = GBMBC7_STATE_NULL;
707 break;
708 }
709 }
710 break;
711 case GBMBC7_STATE_COMMAND_SR_WRITE:
712 if (mbc7->srBits == 16) {
713 mbc7->srBits = 0;
714 mbc7->state = GBMBC7_STATE_WRITE;
715 }
716 break;
717 case GBMBC7_STATE_COMMAND_SR_READ:
718 if (mbc7->srBits == 1) {
719 mbc7->sr = memory->sramBank[mbc7->address * 2] << 8;
720 mbc7->sr |= memory->sramBank[mbc7->address * 2 + 1];
721 mbc7->srBits = 0;
722 mbc7->state = GBMBC7_STATE_READ;
723 }
724 break;
725 case GBMBC7_STATE_COMMAND_SR_FILL:
726 if (mbc7->srBits == 16) {
727 mbc7->sr = 0xFFFF;
728 mbc7->srBits = 0;
729 mbc7->state = GBMBC7_STATE_WRITE;
730 }
731 break;
732 default:
733 break;
734 }
735 } else if (GBMBC7FieldIsSK(old) && !GBMBC7FieldIsSK(value)) {
736 if (mbc7->state == GBMBC7_STATE_READ) {
737 mbc7->sr <<= 1;
738 ++mbc7->srBits;
739 if (mbc7->srBits == 16) {
740 mbc7->srBits = 0;
741 mbc7->state = GBMBC7_STATE_NULL;
742 }
743 }
744 }
745}