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