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