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