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