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