all repos — mgba @ cf982746c214c56cb51a96558a1e40e1ebb524e2

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