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}