all repos — mgba @ b27b7115a406763ddf1dfab8af186775ae0581de

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	gb->memory.wramBank = &gb->memory.wram[GB_SIZE_WORKING_RAM_BANK0];
 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
143uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address) {
144	struct GB* gb = (struct GB*) cpu->master;
145	struct GBMemory* memory = &gb->memory;
146	switch (address >> 12) {
147	case GB_REGION_CART_BANK0:
148	case GB_REGION_CART_BANK0 + 1:
149	case GB_REGION_CART_BANK0 + 2:
150	case GB_REGION_CART_BANK0 + 3:
151		return memory->rom[address & (GB_SIZE_CART_BANK0 - 1)];
152	case GB_REGION_CART_BANK1:
153	case GB_REGION_CART_BANK1 + 1:
154	case GB_REGION_CART_BANK1 + 2:
155	case GB_REGION_CART_BANK1 + 3:
156		return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
157	case GB_REGION_VRAM:
158	case GB_REGION_VRAM + 1:
159		return gb->video.vram[address & (GB_SIZE_VRAM - 1)];
160	case GB_REGION_EXTERNAL_RAM:
161	case GB_REGION_EXTERNAL_RAM + 1:
162		if (memory->rtcAccess) {
163			return gb->memory.rtcRegs[memory->activeRtcReg];
164		} else if (memory->sramAccess) {
165			return gb->memory.sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
166		}
167		return 0xFF;
168	case GB_REGION_WORKING_RAM_BANK0:
169	case GB_REGION_WORKING_RAM_BANK0 + 2:
170		return memory->wram[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
171	case GB_REGION_WORKING_RAM_BANK1:
172		return memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
173	default:
174		if (address < GB_BASE_OAM) {
175			return memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
176		}
177		if (address < GB_BASE_UNUSABLE) {
178			if (gb->video.mode < 2) {
179				return gb->video.oam.raw[address & 0xFF];
180			}
181			return 0xFF;
182		}
183		if (address < GB_BASE_IO) {
184			mLOG(GB_MEM, GAME_ERROR, "Attempt to read from unusable memory: %04X", address);
185			return 0xFF;
186		}
187		if (address < GB_BASE_HRAM) {
188			return GBIORead(gb, address & (GB_SIZE_IO - 1));
189		}
190		if (address < GB_BASE_IE) {
191			return memory->hram[address & GB_SIZE_HRAM];
192		}
193		return GBIORead(gb, REG_IE);
194	}
195}
196
197void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) {
198	struct GB* gb = (struct GB*) cpu->master;
199	struct GBMemory* memory = &gb->memory;
200	switch (address >> 12) {
201	case GB_REGION_CART_BANK0:
202	case GB_REGION_CART_BANK0 + 1:
203	case GB_REGION_CART_BANK0 + 2:
204	case GB_REGION_CART_BANK0 + 3:
205	case GB_REGION_CART_BANK1:
206	case GB_REGION_CART_BANK1 + 1:
207	case GB_REGION_CART_BANK1 + 2:
208	case GB_REGION_CART_BANK1 + 3:
209		memory->mbc(memory, address, value);
210		return;
211	case GB_REGION_VRAM:
212	case GB_REGION_VRAM + 1:
213		// TODO: Block access in wrong modes
214		gb->video.vram[address & (GB_SIZE_VRAM - 1)] = value;
215		return;
216	case GB_REGION_EXTERNAL_RAM:
217	case GB_REGION_EXTERNAL_RAM + 1:
218		if (memory->rtcAccess) {
219			gb->memory.rtcRegs[memory->activeRtcReg] = value;
220		} else if (memory->sramAccess) {
221			gb->memory.sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)] = value;
222		}
223		return;
224	case GB_REGION_WORKING_RAM_BANK0:
225	case GB_REGION_WORKING_RAM_BANK0 + 2:
226		memory->wram[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
227		return;
228	case GB_REGION_WORKING_RAM_BANK1:
229		memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
230		return;
231	default:
232		if (address < GB_BASE_OAM) {
233			memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
234		} else if (address < GB_BASE_UNUSABLE) {
235			if (gb->video.mode < 2) {
236				gb->video.oam.raw[address & 0xFF] = value;
237			}
238		} else if (address < GB_BASE_IO) {
239			mLOG(GB_MEM, GAME_ERROR, "Attempt to write to unusable memory: %04X:%02X", address, value);
240		} else if (address < GB_BASE_HRAM) {
241			GBIOWrite(gb, address & (GB_SIZE_IO - 1), value);
242		} else if (address < GB_BASE_IE) {
243			memory->hram[address & GB_SIZE_HRAM] = value;
244		} else {
245			GBIOWrite(gb, REG_IE, value);
246		}
247	}
248}
249
250int32_t GBMemoryProcessEvents(struct GB* gb, int32_t cycles) {
251	if (!gb->memory.dmaRemaining) {
252		return INT_MAX;
253	}
254	gb->memory.dmaNext -= cycles;
255	if (gb->memory.dmaNext <= 0) {
256		_GBMemoryDMAService(gb);
257	}
258	return gb->memory.dmaNext;
259}
260
261void GBMemoryDMA(struct GB* gb, uint16_t base) {
262	if (base > 0xF100) {
263		return;
264	}
265	gb->cpu->memory.store8 = GBDMAStore8;
266	gb->cpu->memory.load8 = GBDMALoad8;
267	gb->memory.dmaNext = gb->cpu->cycles + 8;
268	if (gb->memory.dmaNext < gb->cpu->nextEvent) {
269		gb->cpu->nextEvent = gb->memory.dmaNext;
270	}
271	gb->memory.dmaSource = base;
272	gb->memory.dmaDest = 0;
273	gb->memory.dmaRemaining = 0xA0;
274}
275
276void _GBMemoryDMAService(struct GB* gb) {
277	uint8_t b = GBLoad8(gb->cpu, gb->memory.dmaSource);
278	// TODO: Can DMA write OAM during modes 2-3?
279	gb->video.oam.raw[gb->memory.dmaDest] = b;
280	++gb->memory.dmaSource;
281	++gb->memory.dmaDest;
282	--gb->memory.dmaRemaining;
283	if (gb->memory.dmaRemaining) {
284		gb->memory.dmaNext += 4;
285	} else {
286		gb->memory.dmaNext = INT_MAX;
287		gb->cpu->memory.store8 = GBStore8;
288		gb->cpu->memory.load8 = GBLoad8;
289	}
290}
291
292uint8_t GBDMALoad8(struct LR35902Core* cpu, uint16_t address) {
293	struct GB* gb = (struct GB*) cpu->master;
294	struct GBMemory* memory = &gb->memory;
295	if (address < 0xFF80 || address == 0xFFFF) {
296		return 0xFF;
297	}
298	return memory->hram[address & GB_SIZE_HRAM];
299}
300
301void GBDMAStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) {
302	struct GB* gb = (struct GB*) cpu->master;
303	struct GBMemory* memory = &gb->memory;
304	if (address < 0xFF80 || address == 0xFFFF) {
305		return;
306	}
307	memory->hram[address & GB_SIZE_HRAM] = value;
308}
309
310uint8_t GBView8(struct LR35902Core* cpu, uint16_t address);
311
312void GBPatch8(struct LR35902Core* cpu, uint16_t address, int8_t value, int8_t* old);
313
314static void _switchBank(struct GBMemory* memory, int bank) {
315	size_t bankStart = bank * GB_SIZE_CART_BANK0;
316	if (bankStart + GB_SIZE_CART_BANK0 > memory->romSize) {
317		mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank);
318		bankStart &= (GB_SIZE_CART_BANK0 - 1);
319		bank /= GB_SIZE_CART_BANK0;
320	}
321	memory->romBank = &memory->rom[bankStart];
322	memory->currentBank = bank;
323}
324
325static void _switchSramBank(struct GBMemory* memory, int bank) {
326	size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM;
327	memory->sramBank = &memory->sram[bankStart];
328	memory->sramCurrentBank = bank;
329}
330
331static void _latchRtc(struct GBMemory* memory) {
332	time_t t;
333	struct mRTCSource* rtc = memory->rtc;
334	if (rtc) {
335		if (rtc->sample) {
336			rtc->sample(rtc);
337		}
338		t = rtc->unixTime(rtc);
339	} else {
340		t = time(0);
341	}
342	struct tm date;
343	localtime_r(&t, &date);
344	memory->rtcRegs[0] = date.tm_sec;
345	memory->rtcRegs[1] = date.tm_min;
346	memory->rtcRegs[2] = date.tm_hour;
347	memory->rtcRegs[3] = date.tm_yday; // TODO: Persist day counter
348	memory->rtcRegs[4] &= 0xF0;
349	memory->rtcRegs[4] |= date.tm_yday >> 8;
350}
351
352void _GBMBC1(struct GBMemory* memory, uint16_t address, uint8_t value) {
353	int bank = value & 0x1F;
354	switch (address >> 13) {
355	case 0x0:
356		switch (value) {
357		case 0:
358			memory->sramAccess = false;
359			break;
360		case 0xA:
361			memory->sramAccess = true;
362			_switchSramBank(memory, memory->sramCurrentBank);
363			break;
364		default:
365			// TODO
366			mLOG(GB_MBC, STUB, "MBC1 unknown value %02X", value);
367			break;
368		}
369		break;
370	case 0x1:
371		if (!bank) {
372			++bank;
373		}
374		_switchBank(memory, bank | (memory->currentBank & 0x60));
375		break;
376	default:
377		// TODO
378		mLOG(GB_MBC, STUB, "MBC1 unknown address: %04X:%02X", address, value);
379		break;
380	}
381}
382
383void _GBMBC2(struct GBMemory* memory, uint16_t address, uint8_t value) {
384	mLOG(GB_MBC, STUB, "MBC2 unimplemented");
385}
386
387void _GBMBC3(struct GBMemory* memory, uint16_t address, uint8_t value) {
388	int bank = value & 0x7F;
389	switch (address >> 13) {
390	case 0x0:
391		switch (value) {
392		case 0:
393			memory->sramAccess = false;
394			break;
395		case 0xA:
396			memory->sramAccess = true;
397			_switchSramBank(memory, memory->sramCurrentBank);
398			break;
399		default:
400			// TODO
401			mLOG(GB_MBC, STUB, "MBC3 unknown value %02X", value);
402			break;
403		}
404		break;
405	case 0x1:
406		if (!bank) {
407			++bank;
408		}
409		_switchBank(memory, bank);
410		break;
411	case 0x2:
412		if (value < 4) {
413			_switchSramBank(memory, value);
414			memory->rtcAccess = false;
415		} else if (value >= 8 && value <= 0xC) {
416			memory->activeRtcReg = value - 8;
417			memory->rtcAccess = true;
418		}
419		break;
420	case 0x3:
421		if (memory->rtcLatched && value == 0) {
422			memory->rtcLatched = value;
423		} else if (!memory->rtcLatched && value == 1) {
424			_latchRtc(memory);
425		}
426		break;
427	}
428}
429
430void _GBMBC5(struct GBMemory* memory, uint16_t address, uint8_t value) {
431	int bank = value & 0x7F;
432	switch (address >> 13) {
433	case 0x0:
434		switch (value) {
435		case 0:
436			memory->sramAccess = false;
437			break;
438		case 0xA:
439			memory->sramAccess = true;
440			_switchSramBank(memory, memory->sramCurrentBank);
441			break;
442		default:
443			// TODO
444			mLOG(GB_MBC, STUB, "MBC5 unknown value %02X", value);
445			break;
446		}
447		break;
448	case 0x1:
449		_switchBank(memory, bank);
450		break;
451	case 0x2:
452		if (value < 0x10) {
453			_switchSramBank(memory, value);
454		}
455		break;
456	default:
457		// TODO
458		mLOG(GB_MBC, STUB, "MBC5 unknown address: %04X:%02X", address, value);
459		break;
460	}
461}
462
463void _GBMBC6(struct GBMemory* memory, uint16_t address, uint8_t value) {
464	// TODO
465	mLOG(GB_MBC, STUB, "MBC6 unimplemented");
466}
467
468void _GBMBC7(struct GBMemory* memory, uint16_t address, uint8_t value) {
469	// TODO
470	mLOG(GB_MBC, STUB, "MBC7 unimplemented");
471}