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;
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 = GB_BASE_OAM;
264 gb->memory.dmaRemaining = 0xA0;
265}
266
267void _GBMemoryDMAService(struct GB* gb) {
268 uint8_t b = GBLoad8(gb->cpu, gb->memory.dmaSource);
269 GBStore8(gb->cpu, gb->memory.dmaDest, b);
270 ++gb->memory.dmaSource;
271 ++gb->memory.dmaDest;
272 --gb->memory.dmaRemaining;
273 if (gb->memory.dmaRemaining) {
274 gb->memory.dmaNext += 4;
275 } else {
276 gb->memory.dmaNext = INT_MAX;
277 gb->cpu->memory.store8 = GBStore8;
278 gb->cpu->memory.load8 = GBLoad8;
279 }
280}
281
282uint8_t GBDMALoad8(struct LR35902Core* cpu, uint16_t address) {
283 struct GB* gb = (struct GB*) cpu->master;
284 struct GBMemory* memory = &gb->memory;
285 if (address < 0xFF80 || address == 0xFFFF) {
286 return 0xFF;
287 }
288 return memory->hram[address & GB_SIZE_HRAM];
289}
290
291void GBDMAStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) {
292 struct GB* gb = (struct GB*) cpu->master;
293 struct GBMemory* memory = &gb->memory;
294 if (address < 0xFF80 || address == 0xFFFF) {
295 return;
296 }
297 memory->hram[address & GB_SIZE_HRAM] = value;
298}
299
300uint8_t GBView8(struct LR35902Core* cpu, uint16_t address);
301
302void GBPatch8(struct LR35902Core* cpu, uint16_t address, int8_t value, int8_t* old);
303
304static void _switchBank(struct GBMemory* memory, int bank) {
305 size_t bankStart = bank * GB_SIZE_CART_BANK0;
306 if (bankStart + GB_SIZE_CART_BANK0 > memory->romSize) {
307 mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank);
308 return;
309 }
310 memory->romBank = &memory->rom[bankStart];
311 memory->currentBank = bank;
312}
313
314static void _switchSramBank(struct GBMemory* memory, int bank) {
315 size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM;
316 memory->sramBank = &memory->sram[bankStart];
317 memory->sramCurrentBank = bank;
318}
319
320void _GBMBC1(struct GBMemory* memory, uint16_t address, uint8_t value) {
321 int bank = value & 0x1F;
322 switch (address >> 13) {
323 case 0x0:
324 switch (value) {
325 case 0:
326 memory->sramAccess = false;
327 break;
328 case 0xA:
329 memory->sramAccess = true;
330 _switchSramBank(memory, memory->sramCurrentBank);
331 break;
332 default:
333 // TODO
334 mLOG(GB_MBC, STUB, "MBC1 unknown value %02X", value);
335 break;
336 }
337 break;
338 break;
339 case 0x1:
340 if (!bank) {
341 ++bank;
342 }
343 _switchBank(memory, bank | (memory->currentBank & 0x60));
344 break;
345 default:
346 // TODO
347 mLOG(GB_MBC, STUB, "MBC1 unknown address: %04X:%02X", address, value);
348 break;
349 }
350}
351
352void _GBMBC2(struct GBMemory* memory, uint16_t address, uint8_t value) {
353 mLOG(GB_MBC, STUB, "MBC2 unimplemented");
354}
355
356void _GBMBC3(struct GBMemory* memory, uint16_t address, uint8_t value) {
357 int bank = value & 0x7F;
358 switch (address >> 13) {
359 case 0x0:
360 switch (value) {
361 case 0:
362 memory->sramAccess = false;
363 break;
364 case 0xA:
365 memory->sramAccess = true;
366 _switchSramBank(memory, memory->sramCurrentBank);
367 break;
368 default:
369 // TODO
370 mLOG(GB_MBC, STUB, "MBC3 unknown value %02X", value);
371 break;
372 }
373 break;
374 case 0x1:
375 if (!bank) {
376 ++bank;
377 }
378 _switchBank(memory, bank);
379 break;
380 case 0x2:
381 if (value < 4) {
382 _switchSramBank(memory, value);
383 } else {
384 mLOG(GB_MBC, STUB, "MBC3 RTC unimplemented", value);
385 }
386 break;
387 default:
388 // TODO
389 mLOG(GB_MBC, STUB, "MBC3 unknown address: %04X:%02X", address, value);
390 break;
391 }
392}
393
394void _GBMBC4(struct GBMemory* memory, uint16_t address, uint8_t value) {
395 // TODO
396 mLOG(GB_MBC, STUB, "MBC4 unimplemented");
397}
398
399void _GBMBC5(struct GBMemory* memory, uint16_t address, uint8_t value) {
400 int bank = value & 0x7F;
401 switch (address >> 13) {
402 case 0x0:
403 switch (value) {
404 case 0:
405 memory->sramAccess = false;
406 break;
407 case 0xA:
408 memory->sramAccess = true;
409 _switchSramBank(memory, memory->sramCurrentBank);
410 break;
411 default:
412 // TODO
413 mLOG(GB_MBC, STUB, "MBC5 unknown value %02X", value);
414 break;
415 }
416 break;
417 case 0x1:
418 _switchBank(memory, bank);
419 break;
420 default:
421 // TODO
422 mLOG(GB_MBC, STUB, "MBC5 unknown address: %04X:%02X", address, value);
423 break;
424 }
425}
426
427void _GBMBC7(struct GBMemory* memory, uint16_t address, uint8_t value) {
428 // TODO
429 mLOG(GB_MBC, STUB, "MBC7 unimplemented");
430}