all repos — mgba @ 92c6b90b03f8c04b53312bf2273d86a9783ee073

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