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}