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