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