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);
17mLOG_DEFINE_CATEGORY(GB_MEM);
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 return;
318 }
319 memory->romBank = &memory->rom[bankStart];
320 memory->currentBank = bank;
321}
322
323static void _switchSramBank(struct GBMemory* memory, int bank) {
324 size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM;
325 memory->sramBank = &memory->sram[bankStart];
326 memory->sramCurrentBank = bank;
327}
328
329static void _latchRtc(struct GBMemory* memory) {
330 time_t t;
331 struct mRTCSource* rtc = memory->rtc;
332 if (rtc) {
333 if (rtc->sample) {
334 rtc->sample(rtc);
335 }
336 t = rtc->unixTime(rtc);
337 } else {
338 t = time(0);
339 }
340 struct tm date;
341 localtime_r(&t, &date);
342 memory->rtcRegs[0] = date.tm_sec;
343 memory->rtcRegs[1] = date.tm_min;
344 memory->rtcRegs[2] = date.tm_hour;
345 memory->rtcRegs[3] = date.tm_yday; // TODO: Persist day counter
346 memory->rtcRegs[4] &= 0xF0;
347 memory->rtcRegs[4] |= date.tm_yday >> 8;
348}
349
350void _GBMBC1(struct GBMemory* memory, uint16_t address, uint8_t value) {
351 int bank = value & 0x1F;
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, "MBC1 unknown value %02X", value);
365 break;
366 }
367 break;
368 case 0x1:
369 if (!bank) {
370 ++bank;
371 }
372 _switchBank(memory, bank | (memory->currentBank & 0x60));
373 break;
374 default:
375 // TODO
376 mLOG(GB_MBC, STUB, "MBC1 unknown address: %04X:%02X", address, value);
377 break;
378 }
379}
380
381void _GBMBC2(struct GBMemory* memory, uint16_t address, uint8_t value) {
382 mLOG(GB_MBC, STUB, "MBC2 unimplemented");
383}
384
385void _GBMBC3(struct GBMemory* memory, uint16_t address, uint8_t value) {
386 int bank = value & 0x7F;
387 switch (address >> 13) {
388 case 0x0:
389 switch (value) {
390 case 0:
391 memory->sramAccess = false;
392 break;
393 case 0xA:
394 memory->sramAccess = true;
395 _switchSramBank(memory, memory->sramCurrentBank);
396 break;
397 default:
398 // TODO
399 mLOG(GB_MBC, STUB, "MBC3 unknown value %02X", value);
400 break;
401 }
402 break;
403 case 0x1:
404 if (!bank) {
405 ++bank;
406 }
407 _switchBank(memory, bank);
408 break;
409 case 0x2:
410 if (value < 4) {
411 _switchSramBank(memory, value);
412 memory->rtcAccess = false;
413 } else if (value >= 8 && value <= 0xC) {
414 memory->activeRtcReg = value - 8;
415 memory->rtcAccess = true;
416 }
417 break;
418 case 0x3:
419 if (memory->rtcLatched && value == 0) {
420 memory->rtcLatched = value;
421 } else if (!memory->rtcLatched && value == 1) {
422 _latchRtc(memory);
423 }
424 break;
425 }
426}
427
428void _GBMBC5(struct GBMemory* memory, uint16_t address, uint8_t value) {
429 int bank = value & 0x7F;
430 switch (address >> 13) {
431 case 0x0:
432 switch (value) {
433 case 0:
434 memory->sramAccess = false;
435 break;
436 case 0xA:
437 memory->sramAccess = true;
438 _switchSramBank(memory, memory->sramCurrentBank);
439 break;
440 default:
441 // TODO
442 mLOG(GB_MBC, STUB, "MBC5 unknown value %02X", value);
443 break;
444 }
445 break;
446 case 0x1:
447 _switchBank(memory, bank);
448 break;
449 case 0x2:
450 if (value < 0x10) {
451 _switchSramBank(memory, value);
452 }
453 break;
454 default:
455 // TODO
456 mLOG(GB_MBC, STUB, "MBC5 unknown address: %04X:%02X", address, value);
457 break;
458 }
459}
460
461void _GBMBC6(struct GBMemory* memory, uint16_t address, uint8_t value) {
462 // TODO
463 mLOG(GB_MBC, STUB, "MBC6 unimplemented");
464}
465
466void _GBMBC7(struct GBMemory* memory, uint16_t address, uint8_t value) {
467 // TODO
468 mLOG(GB_MBC, STUB, "MBC7 unimplemented");
469}