all repos — mgba @ 245a13af63adec5b26e087950e1bb5f649f872fb

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);
 33
 34static void GBSetActiveRegion(struct LR35902Core* cpu, uint16_t address) {
 35	// TODO
 36}
 37
 38static void _GBMemoryDMAService(struct GB* gb);
 39
 40
 41void GBMemoryInit(struct GB* gb) {
 42	struct LR35902Core* cpu = gb->cpu;
 43	cpu->memory.cpuLoad8 = GBLoad8;
 44	cpu->memory.load8 = GBLoad8;
 45	cpu->memory.store8 = GBStore8;
 46	cpu->memory.setActiveRegion = GBSetActiveRegion;
 47
 48	gb->memory.wram = 0;
 49	gb->memory.wramBank = 0;
 50	gb->memory.rom = 0;
 51	gb->memory.romBank = 0;
 52	gb->memory.romSize = 0;
 53	gb->memory.sram = 0;
 54	gb->memory.mbcType = GB_MBC_NONE;
 55	gb->memory.mbc = 0;
 56
 57	gb->memory.dmaNext = INT_MAX;
 58	gb->memory.dmaRemaining = 0;
 59
 60	memset(gb->memory.hram, 0, sizeof(gb->memory.hram));
 61
 62	gb->memory.sramAccess = false;
 63	gb->memory.rtcAccess = false;
 64	gb->memory.rtcLatched = 0;
 65	gb->memory.rtc = NULL;
 66
 67	GBIOInit(gb);
 68}
 69
 70void GBMemoryDeinit(struct GB* gb) {
 71	mappedMemoryFree(gb->memory.wram, GB_SIZE_WORKING_RAM);
 72	if (gb->memory.rom) {
 73		mappedMemoryFree(gb->memory.rom, gb->memory.romSize);
 74	}
 75}
 76
 77void GBMemoryReset(struct GB* gb) {
 78	if (gb->memory.wram) {
 79		mappedMemoryFree(gb->memory.wram, GB_SIZE_WORKING_RAM);
 80	}
 81	gb->memory.wram = anonymousMemoryMap(GB_SIZE_WORKING_RAM);
 82	GBMemorySwitchWramBank(&gb->memory, 1);
 83	gb->memory.romBank = &gb->memory.rom[GB_SIZE_CART_BANK0];
 84	gb->memory.currentBank = 1;
 85	gb->memory.sramCurrentBank = 0;
 86	gb->memory.sramBank = gb->memory.sram;
 87
 88	memset(&gb->video.oam, 0, sizeof(gb->video.oam));
 89
 90	const struct GBCartridge* cart = &gb->memory.rom[0x100];
 91	switch (cart->type) {
 92	case 0:
 93	case 8:
 94	case 9:
 95		gb->memory.mbc = _GBMBCNone;
 96		gb->memory.mbcType = GB_MBC_NONE;
 97		break;
 98	case 1:
 99	case 2:
100	case 3:
101		gb->memory.mbc = _GBMBC1;
102		gb->memory.mbcType = GB_MBC1;
103		break;
104	case 5:
105	case 6:
106		gb->memory.mbc = _GBMBC2;
107		gb->memory.mbcType = GB_MBC2;
108		break;
109	case 0x0F:
110	case 0x10:
111	case 0x11:
112	case 0x12:
113	case 0x13:
114		gb->memory.mbc = _GBMBC3;
115		gb->memory.mbcType = GB_MBC3;
116		break;
117	default:
118		mLOG(GB_MBC, WARN, "Unknown MBC type: %02X", cart->type);
119	case 0x19:
120	case 0x1A:
121	case 0x1B:
122	case 0x1C:
123	case 0x1D:
124	case 0x1E:
125		gb->memory.mbc = _GBMBC5;
126		gb->memory.mbcType = GB_MBC5;
127		break;
128	case 0x20:
129		gb->memory.mbc = _GBMBC6;
130		gb->memory.mbcType = GB_MBC6;
131		break;
132	case 0x22:
133		gb->memory.mbc = _GBMBC7;
134		gb->memory.mbcType = GB_MBC7;
135		break;
136	}
137
138	if (!gb->memory.wram) {
139		GBMemoryDeinit(gb);
140	}
141}
142
143void GBMemorySwitchWramBank(struct GBMemory* memory, int bank) {
144	bank &= 7;
145	if (!bank) {
146		bank = 1;
147	}
148	memory->wramBank = &memory->wram[GB_SIZE_WORKING_RAM_BANK0 * bank];
149	memory->wramCurrentBank = bank;
150}
151
152uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address) {
153	struct GB* gb = (struct GB*) cpu->master;
154	struct GBMemory* memory = &gb->memory;
155	switch (address >> 12) {
156	case GB_REGION_CART_BANK0:
157	case GB_REGION_CART_BANK0 + 1:
158	case GB_REGION_CART_BANK0 + 2:
159	case GB_REGION_CART_BANK0 + 3:
160		return memory->rom[address & (GB_SIZE_CART_BANK0 - 1)];
161	case GB_REGION_CART_BANK1:
162	case GB_REGION_CART_BANK1 + 1:
163	case GB_REGION_CART_BANK1 + 2:
164	case GB_REGION_CART_BANK1 + 3:
165		return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
166	case GB_REGION_VRAM:
167	case GB_REGION_VRAM + 1:
168		return gb->video.vramBank[address & (GB_SIZE_VRAM_BANK0 - 1)];
169	case GB_REGION_EXTERNAL_RAM:
170	case GB_REGION_EXTERNAL_RAM + 1:
171		if (memory->rtcAccess) {
172			return gb->memory.rtcRegs[memory->activeRtcReg];
173		} else if (memory->sramAccess) {
174			return gb->memory.sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
175		}
176		return 0xFF;
177	case GB_REGION_WORKING_RAM_BANK0:
178	case GB_REGION_WORKING_RAM_BANK0 + 2:
179		return memory->wram[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
180	case GB_REGION_WORKING_RAM_BANK1:
181		return memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
182	default:
183		if (address < GB_BASE_OAM) {
184			return memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
185		}
186		if (address < GB_BASE_UNUSABLE) {
187			if (gb->video.mode < 2) {
188				return gb->video.oam.raw[address & 0xFF];
189			}
190			return 0xFF;
191		}
192		if (address < GB_BASE_IO) {
193			mLOG(GB_MEM, GAME_ERROR, "Attempt to read from unusable memory: %04X", address);
194			return 0xFF;
195		}
196		if (address < GB_BASE_HRAM) {
197			return GBIORead(gb, address & (GB_SIZE_IO - 1));
198		}
199		if (address < GB_BASE_IE) {
200			return memory->hram[address & GB_SIZE_HRAM];
201		}
202		return GBIORead(gb, REG_IE);
203	}
204}
205
206void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) {
207	struct GB* gb = (struct GB*) cpu->master;
208	struct GBMemory* memory = &gb->memory;
209	switch (address >> 12) {
210	case GB_REGION_CART_BANK0:
211	case GB_REGION_CART_BANK0 + 1:
212	case GB_REGION_CART_BANK0 + 2:
213	case GB_REGION_CART_BANK0 + 3:
214	case GB_REGION_CART_BANK1:
215	case GB_REGION_CART_BANK1 + 1:
216	case GB_REGION_CART_BANK1 + 2:
217	case GB_REGION_CART_BANK1 + 3:
218		memory->mbc(memory, address, value);
219		return;
220	case GB_REGION_VRAM:
221	case GB_REGION_VRAM + 1:
222		// TODO: Block access in wrong modes
223		gb->video.vramBank[address & (GB_SIZE_VRAM_BANK0 - 1)] = value;
224		return;
225	case GB_REGION_EXTERNAL_RAM:
226	case GB_REGION_EXTERNAL_RAM + 1:
227		if (memory->rtcAccess) {
228			gb->memory.rtcRegs[memory->activeRtcReg] = value;
229		} else if (memory->sramAccess) {
230			gb->memory.sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)] = value;
231		}
232		return;
233	case GB_REGION_WORKING_RAM_BANK0:
234	case GB_REGION_WORKING_RAM_BANK0 + 2:
235		memory->wram[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
236		return;
237	case GB_REGION_WORKING_RAM_BANK1:
238		memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
239		return;
240	default:
241		if (address < GB_BASE_OAM) {
242			memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
243		} else if (address < GB_BASE_UNUSABLE) {
244			if (gb->video.mode < 2) {
245				gb->video.oam.raw[address & 0xFF] = value;
246			}
247		} else if (address < GB_BASE_IO) {
248			mLOG(GB_MEM, GAME_ERROR, "Attempt to write to unusable memory: %04X:%02X", address, value);
249		} else if (address < GB_BASE_HRAM) {
250			GBIOWrite(gb, address & (GB_SIZE_IO - 1), value);
251		} else if (address < GB_BASE_IE) {
252			memory->hram[address & GB_SIZE_HRAM] = value;
253		} else {
254			GBIOWrite(gb, REG_IE, value);
255		}
256	}
257}
258
259int32_t GBMemoryProcessEvents(struct GB* gb, int32_t cycles) {
260	if (!gb->memory.dmaRemaining) {
261		return INT_MAX;
262	}
263	gb->memory.dmaNext -= cycles;
264	if (gb->memory.dmaNext <= 0) {
265		_GBMemoryDMAService(gb);
266	}
267	return gb->memory.dmaNext;
268}
269
270void GBMemoryDMA(struct GB* gb, uint16_t base) {
271	if (base > 0xF100) {
272		return;
273	}
274	gb->cpu->memory.store8 = GBDMAStore8;
275	gb->cpu->memory.load8 = GBDMALoad8;
276	gb->memory.dmaNext = gb->cpu->cycles + 8;
277	if (gb->memory.dmaNext < gb->cpu->nextEvent) {
278		gb->cpu->nextEvent = gb->memory.dmaNext;
279	}
280	gb->memory.dmaSource = base;
281	gb->memory.dmaDest = 0;
282	gb->memory.dmaRemaining = 0xA0;
283}
284
285void _GBMemoryDMAService(struct GB* gb) {
286	uint8_t b = GBLoad8(gb->cpu, gb->memory.dmaSource);
287	// TODO: Can DMA write OAM during modes 2-3?
288	gb->video.oam.raw[gb->memory.dmaDest] = b;
289	++gb->memory.dmaSource;
290	++gb->memory.dmaDest;
291	--gb->memory.dmaRemaining;
292	if (gb->memory.dmaRemaining) {
293		gb->memory.dmaNext += 4;
294	} else {
295		gb->memory.dmaNext = INT_MAX;
296		gb->cpu->memory.store8 = GBStore8;
297		gb->cpu->memory.load8 = GBLoad8;
298	}
299}
300
301uint8_t GBDMALoad8(struct LR35902Core* cpu, uint16_t address) {
302	struct GB* gb = (struct GB*) cpu->master;
303	struct GBMemory* memory = &gb->memory;
304	if (address < 0xFF80 || address == 0xFFFF) {
305		return 0xFF;
306	}
307	return memory->hram[address & GB_SIZE_HRAM];
308}
309
310void GBDMAStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) {
311	struct GB* gb = (struct GB*) cpu->master;
312	struct GBMemory* memory = &gb->memory;
313	if (address < 0xFF80 || address == 0xFFFF) {
314		return;
315	}
316	memory->hram[address & GB_SIZE_HRAM] = value;
317}
318
319uint8_t GBView8(struct LR35902Core* cpu, uint16_t address);
320
321void GBPatch8(struct LR35902Core* cpu, uint16_t address, int8_t value, int8_t* old);
322
323static void _switchBank(struct GBMemory* memory, int bank) {
324	size_t bankStart = bank * GB_SIZE_CART_BANK0;
325	if (bankStart + GB_SIZE_CART_BANK0 > memory->romSize) {
326		mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank);
327		bankStart &= (GB_SIZE_CART_BANK0 - 1);
328		bank /= GB_SIZE_CART_BANK0;
329	}
330	memory->romBank = &memory->rom[bankStart];
331	memory->currentBank = bank;
332}
333
334static void _switchSramBank(struct GBMemory* memory, int bank) {
335	size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM;
336	memory->sramBank = &memory->sram[bankStart];
337	memory->sramCurrentBank = bank;
338}
339
340static void _latchRtc(struct GBMemory* memory) {
341	time_t t;
342	struct mRTCSource* rtc = memory->rtc;
343	if (rtc) {
344		if (rtc->sample) {
345			rtc->sample(rtc);
346		}
347		t = rtc->unixTime(rtc);
348	} else {
349		t = time(0);
350	}
351	struct tm date;
352	localtime_r(&t, &date);
353	memory->rtcRegs[0] = date.tm_sec;
354	memory->rtcRegs[1] = date.tm_min;
355	memory->rtcRegs[2] = date.tm_hour;
356	memory->rtcRegs[3] = date.tm_yday; // TODO: Persist day counter
357	memory->rtcRegs[4] &= 0xF0;
358	memory->rtcRegs[4] |= date.tm_yday >> 8;
359}
360
361void _GBMBC1(struct GBMemory* memory, uint16_t address, uint8_t value) {
362	int bank = value & 0x1F;
363	switch (address >> 13) {
364	case 0x0:
365		switch (value) {
366		case 0:
367			memory->sramAccess = false;
368			break;
369		case 0xA:
370			memory->sramAccess = true;
371			_switchSramBank(memory, memory->sramCurrentBank);
372			break;
373		default:
374			// TODO
375			mLOG(GB_MBC, STUB, "MBC1 unknown value %02X", value);
376			break;
377		}
378		break;
379	case 0x1:
380		if (!bank) {
381			++bank;
382		}
383		_switchBank(memory, bank | (memory->currentBank & 0x60));
384		break;
385	default:
386		// TODO
387		mLOG(GB_MBC, STUB, "MBC1 unknown address: %04X:%02X", address, value);
388		break;
389	}
390}
391
392void _GBMBC2(struct GBMemory* memory, uint16_t address, uint8_t value) {
393	mLOG(GB_MBC, STUB, "MBC2 unimplemented");
394}
395
396void _GBMBC3(struct GBMemory* memory, uint16_t address, uint8_t value) {
397	int bank = value & 0x7F;
398	switch (address >> 13) {
399	case 0x0:
400		switch (value) {
401		case 0:
402			memory->sramAccess = false;
403			break;
404		case 0xA:
405			memory->sramAccess = true;
406			_switchSramBank(memory, memory->sramCurrentBank);
407			break;
408		default:
409			// TODO
410			mLOG(GB_MBC, STUB, "MBC3 unknown value %02X", value);
411			break;
412		}
413		break;
414	case 0x1:
415		if (!bank) {
416			++bank;
417		}
418		_switchBank(memory, bank);
419		break;
420	case 0x2:
421		if (value < 4) {
422			_switchSramBank(memory, value);
423			memory->rtcAccess = false;
424		} else if (value >= 8 && value <= 0xC) {
425			memory->activeRtcReg = value - 8;
426			memory->rtcAccess = true;
427		}
428		break;
429	case 0x3:
430		if (memory->rtcLatched && value == 0) {
431			memory->rtcLatched = value;
432		} else if (!memory->rtcLatched && value == 1) {
433			_latchRtc(memory);
434		}
435		break;
436	}
437}
438
439void _GBMBC5(struct GBMemory* memory, uint16_t address, uint8_t value) {
440	int bank = value & 0x7F;
441	switch (address >> 13) {
442	case 0x0:
443		switch (value) {
444		case 0:
445			memory->sramAccess = false;
446			break;
447		case 0xA:
448			memory->sramAccess = true;
449			_switchSramBank(memory, memory->sramCurrentBank);
450			break;
451		default:
452			// TODO
453			mLOG(GB_MBC, STUB, "MBC5 unknown value %02X", value);
454			break;
455		}
456		break;
457	case 0x1:
458		_switchBank(memory, bank);
459		break;
460	case 0x2:
461		if (value < 0x10) {
462			_switchSramBank(memory, value);
463		}
464		break;
465	default:
466		// TODO
467		mLOG(GB_MBC, STUB, "MBC5 unknown address: %04X:%02X", address, value);
468		break;
469	}
470}
471
472void _GBMBC6(struct GBMemory* memory, uint16_t address, uint8_t value) {
473	// TODO
474	mLOG(GB_MBC, STUB, "MBC6 unimplemented");
475}
476
477void _GBMBC7(struct GBMemory* memory, uint16_t address, uint8_t value) {
478	// TODO
479	mLOG(GB_MBC, STUB, "MBC7 unimplemented");
480}