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