all repos — mgba @ 5d7a43639b444c14d98c92ea5e8a06f9059cd847

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