all repos — mgba @ a3d0b85d5651abde4317f5ba6692d060b52230d4

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