all repos — mgba @ fc095ffd63259166d9a20cf23133b25efd966aea

mGBA Game Boy Advance Emulator

src/gb/mbc.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 "mbc.h"
  7
  8#include "gb/gb.h"
  9#include "gb/memory.h"
 10
 11#include <time.h>
 12
 13mLOG_DEFINE_CATEGORY(GB_MBC, "GB MBC");
 14
 15static void _GBMBCNone(struct GB* gb, uint16_t address, uint8_t value) {
 16	UNUSED(gb);
 17	UNUSED(address);
 18	UNUSED(value);
 19
 20	mLOG(GB_MBC, GAME_ERROR, "Wrote to invalid MBC");
 21}
 22
 23static void _GBMBC1(struct GB*, uint16_t address, uint8_t value);
 24static void _GBMBC2(struct GB*, uint16_t address, uint8_t value);
 25static void _GBMBC3(struct GB*, uint16_t address, uint8_t value);
 26static void _GBMBC5(struct GB*, uint16_t address, uint8_t value);
 27static void _GBMBC6(struct GB*, uint16_t address, uint8_t value);
 28static void _GBMBC7(struct GB*, uint16_t address, uint8_t value);
 29static void _GBHuC3(struct GB*, uint16_t address, uint8_t value);
 30
 31void GBMBCSwitchBank(struct GBMemory* memory, int bank) {
 32	size_t bankStart = bank * GB_SIZE_CART_BANK0;
 33	if (bankStart + GB_SIZE_CART_BANK0 > memory->romSize) {
 34		mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank);
 35		bankStart &= (memory->romSize - 1);
 36		bank = bankStart / GB_SIZE_CART_BANK0;
 37	}
 38	memory->romBank = &memory->rom[bankStart];
 39	memory->currentBank = bank;
 40}
 41
 42void GBMBCSwitchSramBank(struct GB* gb, int bank) {
 43	size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM;
 44	GBResizeSram(gb, (bank + 1) * GB_SIZE_EXTERNAL_RAM + (gb->sramSize & 0xFF));
 45	gb->memory.sramBank = &gb->memory.sram[bankStart];
 46	gb->memory.sramCurrentBank = bank;
 47}
 48
 49void GBMBCInit(struct GB* gb) {
 50	const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
 51	switch (cart->type) {
 52	case 0:
 53	case 8:
 54	case 9:
 55		gb->memory.mbc = _GBMBCNone;
 56		gb->memory.mbcType = GB_MBC_NONE;
 57		gb->sramSize = 0;
 58		return;
 59	case 1:
 60	case 2:
 61	case 3:
 62		gb->memory.mbc = _GBMBC1;
 63		gb->memory.mbcType = GB_MBC1;
 64		gb->sramSize = 0x2000;
 65		break;
 66	case 5:
 67	case 6:
 68		gb->memory.mbc = _GBMBC2;
 69		gb->memory.mbcType = GB_MBC2;
 70		gb->sramSize = 0x200;
 71		break;
 72	case 0x0F:
 73	case 0x10:
 74	case 0x11:
 75	case 0x12:
 76	case 0x13:
 77		gb->memory.mbc = _GBMBC3;
 78		gb->memory.mbcType = GB_MBC3;
 79		gb->sramSize = 0x2048;
 80		break;
 81	default:
 82		mLOG(GB_MBC, WARN, "Unknown MBC type: %02X", cart->type);
 83		// Fall through
 84	case 0x19:
 85	case 0x1A:
 86	case 0x1B:
 87		gb->memory.mbc = _GBMBC5;
 88		gb->memory.mbcType = GB_MBC5;
 89		gb->sramSize = 0x2000;
 90		break;
 91	case 0x1C:
 92	case 0x1D:
 93	case 0x1E:
 94		gb->memory.mbc = _GBMBC5;
 95		gb->memory.mbcType = GB_MBC5_RUMBLE;
 96		gb->sramSize = 0x2000;
 97		break;
 98	case 0x20:
 99		gb->memory.mbc = _GBMBC6;
100		gb->memory.mbcType = GB_MBC6;
101		gb->sramSize = 0; // TODO
102		break;
103	case 0x22:
104		gb->memory.mbc = _GBMBC7;
105		gb->memory.mbcType = GB_MBC7;
106		gb->sramSize = 0x2000;
107		break;
108	case 0xFE:
109		gb->memory.mbc = _GBHuC3;
110		gb->memory.mbcType = GB_HuC3;
111		gb->sramSize = 0x2000;
112		break;
113	}
114
115	GBResizeSram(gb, gb->sramSize);
116}
117
118static void _latchRtc(struct GBMemory* memory) {
119	time_t t;
120	struct mRTCSource* rtc = memory->rtc;
121	if (rtc) {
122		if (rtc->sample) {
123			rtc->sample(rtc);
124		}
125		t = rtc->unixTime(rtc);
126	} else {
127		t = time(0);
128	}
129	struct tm date;
130	localtime_r(&t, &date);
131	memory->rtcRegs[0] = date.tm_sec;
132	memory->rtcRegs[1] = date.tm_min;
133	memory->rtcRegs[2] = date.tm_hour;
134	memory->rtcRegs[3] = date.tm_yday; // TODO: Persist day counter
135	memory->rtcRegs[4] &= 0xF0;
136	memory->rtcRegs[4] |= date.tm_yday >> 8;
137}
138
139void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) {
140	struct GBMemory* memory = &gb->memory;
141	int bank = value & 0x1F;
142	switch (address >> 13) {
143	case 0x0:
144		switch (value) {
145		case 0:
146			memory->sramAccess = false;
147			break;
148		case 0xA:
149			memory->sramAccess = true;
150			GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
151			break;
152		default:
153			// TODO
154			mLOG(GB_MBC, STUB, "MBC1 unknown value %02X", value);
155			break;
156		}
157		break;
158	case 0x1:
159		if (!bank) {
160			++bank;
161		}
162		GBMBCSwitchBank(memory, bank | (memory->currentBank & 0x60));
163		break;
164	case 0x2:
165		bank &= 3;
166		if (!memory->mbcState.mbc1.mode) {
167			GBMBCSwitchBank(memory, (bank << 5) | (memory->currentBank & 0x1F));
168		} else {
169			GBMBCSwitchSramBank(gb, bank);
170		}
171		break;
172	case 0x3:
173		memory->mbcState.mbc1.mode = value & 1;
174		if (memory->mbcState.mbc1.mode) {
175			GBMBCSwitchBank(memory, memory->currentBank & 0x1F);
176		} else {
177			GBMBCSwitchSramBank(gb, 0);
178		}
179		break;
180	default:
181		// TODO
182		mLOG(GB_MBC, STUB, "MBC1 unknown address: %04X:%02X", address, value);
183		break;
184	}
185}
186
187void _GBMBC2(struct GB* gb, uint16_t address, uint8_t value) {
188	struct GBMemory* memory = &gb->memory;
189	int bank = value & 0xF;
190	switch (address >> 13) {
191	case 0x0:
192		switch (value) {
193		case 0:
194			memory->sramAccess = false;
195			break;
196		case 0xA:
197			memory->sramAccess = true;
198			GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
199			break;
200		default:
201			// TODO
202			mLOG(GB_MBC, STUB, "MBC1 unknown value %02X", value);
203			break;
204		}
205		break;
206	case 0x1:
207		if (!bank) {
208			++bank;
209		}
210		GBMBCSwitchBank(memory, bank);
211		break;
212	default:
213		// TODO
214		mLOG(GB_MBC, STUB, "MBC2 unknown address: %04X:%02X", address, value);
215		break;
216	}}
217
218void _GBMBC3(struct GB* gb, uint16_t address, uint8_t value) {
219	struct GBMemory* memory = &gb->memory;
220	int bank = value & 0x7F;
221	switch (address >> 13) {
222	case 0x0:
223		switch (value) {
224		case 0:
225			memory->sramAccess = false;
226			break;
227		case 0xA:
228			memory->sramAccess = true;
229			GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
230			break;
231		default:
232			// TODO
233			mLOG(GB_MBC, STUB, "MBC3 unknown value %02X", value);
234			break;
235		}
236		break;
237	case 0x1:
238		if (!bank) {
239			++bank;
240		}
241		GBMBCSwitchBank(memory, bank);
242		break;
243	case 0x2:
244		if (value < 4) {
245			GBMBCSwitchSramBank(gb, value);
246			memory->rtcAccess = false;
247		} else if (value >= 8 && value <= 0xC) {
248			memory->activeRtcReg = value - 8;
249			memory->rtcAccess = true;
250		}
251		break;
252	case 0x3:
253		if (memory->rtcLatched && value == 0) {
254			memory->rtcLatched = false;
255		} else if (!memory->rtcLatched && value == 1) {
256			_latchRtc(memory);
257			memory->rtcLatched = true;
258		}
259		break;
260	}
261}
262
263void _GBMBC5(struct GB* gb, uint16_t address, uint8_t value) {
264	struct GBMemory* memory = &gb->memory;
265	int bank;
266	switch (address >> 12) {
267	case 0x0:
268	case 0x1:
269		switch (value) {
270		case 0:
271			memory->sramAccess = false;
272			break;
273		case 0xA:
274			memory->sramAccess = true;
275			GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
276			break;
277		default:
278			// TODO
279			mLOG(GB_MBC, STUB, "MBC5 unknown value %02X", value);
280			break;
281		}
282		break;
283	case 0x2:
284		bank = (memory->currentBank & 0x100) | value;
285		GBMBCSwitchBank(memory, bank);
286		break;
287	case 0x3:
288		bank = (memory->currentBank & 0xFF) | ((value & 1) << 8);
289		GBMBCSwitchBank(memory, bank);
290		break;
291	case 0x4:
292	case 0x5:
293		if (memory->mbcType == GB_MBC5_RUMBLE && memory->rumble) {
294			memory->rumble->setRumble(memory->rumble, (value >> 3) & 1);
295			value &= ~8;
296		}
297		GBMBCSwitchSramBank(gb, value & 0xF);
298		break;
299	default:
300		// TODO
301		mLOG(GB_MBC, STUB, "MBC5 unknown address: %04X:%02X", address, value);
302		break;
303	}
304}
305
306void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) {
307	// TODO
308	mLOG(GB_MBC, STUB, "MBC6 unimplemented");
309	UNUSED(gb);
310	UNUSED(address);
311	UNUSED(value);
312}
313
314void _GBMBC7(struct GB* gb, uint16_t address, uint8_t value) {
315	struct GBMemory* memory = &gb->memory;
316	int bank = value & 0x7F;
317	switch (address >> 13) {
318	case 0x1:
319		GBMBCSwitchBank(memory, bank);
320		break;
321	case 0x2:
322		if (value < 0x10) {
323			GBMBCSwitchSramBank(gb, value);
324		}
325		break;
326	default:
327		// TODO
328		mLOG(GB_MBC, STUB, "MBC7 unknown address: %04X:%02X", address, value);
329		break;
330	}
331}
332
333uint8_t GBMBC7Read(struct GBMemory* memory, uint16_t address) {
334	struct GBMBC7State* mbc7 = &memory->mbcState.mbc7;
335	switch (address & 0xF0) {
336	case 0x00:
337	case 0x10:
338	case 0x60:
339	case 0x70:
340		return 0;
341	case 0x20:
342		if (memory->rotation && memory->rotation->readTiltX) {
343			int32_t x = -memory->rotation->readTiltX(memory->rotation);
344			x >>= 21;
345			x += 2047;
346			return x;
347		}
348		return 0xFF;
349	case 0x30:
350		if (memory->rotation && memory->rotation->readTiltX) {
351			int32_t x = -memory->rotation->readTiltX(memory->rotation);
352			x >>= 21;
353			x += 2047;
354			return x >> 8;
355		}
356		return 7;
357	case 0x40:
358		if (memory->rotation && memory->rotation->readTiltY) {
359			int32_t y = -memory->rotation->readTiltY(memory->rotation);
360			y >>= 21;
361			y += 2047;
362			return y;
363		}
364		return 0xFF;
365	case 0x50:
366		if (memory->rotation && memory->rotation->readTiltY) {
367			int32_t y = -memory->rotation->readTiltY(memory->rotation);
368			y >>= 21;
369			y += 2047;
370			return y >> 8;
371		}
372		return 7;
373	case 0x80:
374		return (mbc7->sr >> 16) & 1;
375	default:
376		return 0xFF;
377	}
378}
379
380void GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value) {
381	if ((address & 0xF0) != 0x80) {
382		return;
383	}
384	struct GBMBC7State* mbc7 = &memory->mbcState.mbc7;
385	GBMBC7Field old = memory->mbcState.mbc7.field;
386	mbc7->field = GBMBC7FieldClearIO(value);
387	if (!GBMBC7FieldIsCS(old) && GBMBC7FieldIsCS(value)) {
388		if (mbc7->state == GBMBC7_STATE_WRITE) {
389			if (mbc7->writable) {
390				memory->sramBank[mbc7->address * 2] = mbc7->sr >> 8;
391				memory->sramBank[mbc7->address * 2 + 1] = mbc7->sr;
392			}
393			mbc7->sr = 0x1FFFF;
394			mbc7->state = GBMBC7_STATE_NULL;
395		} else {
396			mbc7->state = GBMBC7_STATE_IDLE;
397		}
398	}
399	if (!GBMBC7FieldIsSK(old) && GBMBC7FieldIsSK(value)) {
400		if (mbc7->state > GBMBC7_STATE_IDLE && mbc7->state != GBMBC7_STATE_READ) {
401			mbc7->sr <<= 1;
402			mbc7->sr |= GBMBC7FieldGetIO(value);
403			++mbc7->srBits;
404		}
405		switch (mbc7->state) {
406		case GBMBC7_STATE_IDLE:
407			if (GBMBC7FieldIsIO(value)) {
408				mbc7->state = GBMBC7_STATE_READ_COMMAND;
409				mbc7->srBits = 0;
410				mbc7->sr = 0;
411			}
412			break;
413		case GBMBC7_STATE_READ_COMMAND:
414			if (mbc7->srBits == 2) {
415				mbc7->state = GBMBC7_STATE_READ_ADDRESS;
416				mbc7->srBits = 0;
417				mbc7->command = mbc7->sr;
418			}
419			break;
420		case GBMBC7_STATE_READ_ADDRESS:
421			if (mbc7->srBits == 8) {
422				mbc7->state = GBMBC7_STATE_COMMAND_0 + mbc7->command;
423				mbc7->srBits = 0;
424				mbc7->address = mbc7->sr;
425				if (mbc7->state == GBMBC7_STATE_COMMAND_0) {
426					switch (mbc7->address >> 6) {
427					case 0:
428						mbc7->writable = false;
429						mbc7->state = GBMBC7_STATE_NULL;
430						break;
431					case 3:
432						mbc7->writable = true;
433						mbc7->state = GBMBC7_STATE_NULL;
434						break;
435					}
436				}
437			}
438			break;
439		case GBMBC7_STATE_COMMAND_0:
440			if (mbc7->srBits == 16) {
441				switch (mbc7->address >> 6) {
442				case 0:
443					mbc7->writable = false;
444					mbc7->state = GBMBC7_STATE_NULL;
445					break;
446				case 1:
447					mbc7->state = GBMBC7_STATE_WRITE;
448					if (mbc7->writable) {
449						int i;
450						for (i = 0; i < 256; ++i) {
451							memory->sramBank[i * 2] = mbc7->sr >> 8;
452							memory->sramBank[i * 2 + 1] = mbc7->sr;
453						}
454					}
455					break;
456				case 2:
457					mbc7->state = GBMBC7_STATE_WRITE;
458					if (mbc7->writable) {
459						int i;
460						for (i = 0; i < 256; ++i) {
461							memory->sramBank[i * 2] = 0xFF;
462							memory->sramBank[i * 2 + 1] = 0xFF;
463						}
464					}
465					break;
466				case 3:
467					mbc7->writable = true;
468					mbc7->state = GBMBC7_STATE_NULL;
469					break;
470				}
471			}
472			break;
473		case GBMBC7_STATE_COMMAND_SR_WRITE:
474			if (mbc7->srBits == 16) {
475				mbc7->srBits = 0;
476				mbc7->state = GBMBC7_STATE_WRITE;
477			}
478			break;
479		case GBMBC7_STATE_COMMAND_SR_READ:
480			if (mbc7->srBits == 1) {
481				mbc7->sr = memory->sramBank[mbc7->address * 2] << 8;
482				mbc7->sr |= memory->sramBank[mbc7->address * 2 + 1];
483				mbc7->srBits = 0;
484				mbc7->state = GBMBC7_STATE_READ;
485			}
486			break;
487		case GBMBC7_STATE_COMMAND_SR_FILL:
488			if (mbc7->srBits == 16) {
489				mbc7->sr = 0xFFFF;
490				mbc7->srBits = 0;
491				mbc7->state = GBMBC7_STATE_WRITE;
492			}
493			break;
494		default:
495			break;
496		}
497	} else if (GBMBC7FieldIsSK(old) && !GBMBC7FieldIsSK(value)) {
498		if (mbc7->state == GBMBC7_STATE_READ) {
499			mbc7->sr <<= 1;
500			++mbc7->srBits;
501			if (mbc7->srBits == 16) {
502				mbc7->srBits = 0;
503				mbc7->state = GBMBC7_STATE_NULL;
504			}
505		}
506	}
507}
508
509void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) {
510	struct GBMemory* memory = &gb->memory;
511	int bank = value & 0x3F;
512	if (address & 0x1FFF) {
513		mLOG(GB_MBC, STUB, "HuC-3 unknown value %04X:%02X", address, value);
514	}
515
516	switch (address >> 13) {
517	case 0x0:
518		switch (value) {
519		case 0xA:
520			memory->sramAccess = true;
521			GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
522			break;
523		default:
524			memory->sramAccess = false;
525			break;
526		}
527		break;
528	case 0x1:
529		GBMBCSwitchBank(memory, bank);
530		break;
531	case 0x2:
532		GBMBCSwitchSramBank(gb, bank);
533		break;
534	default:
535		// TODO
536		mLOG(GB_MBC, STUB, "HuC-3 unknown address: %04X:%02X", address, value);
537		break;
538	}
539}