all repos — mgba @ 0ddac5e9614cefe166bee5920ad4455b78d92a35

mGBA Game Boy Advance Emulator

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}