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 return;
207 case GB_REGION_EXTERNAL_RAM:
208 case GB_REGION_EXTERNAL_RAM + 1:
209 if (memory->sramAccess) {
210 gb->memory.sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)] = value;
211 }
212 return;
213 case GB_REGION_WORKING_RAM_BANK0:
214 case GB_REGION_WORKING_RAM_BANK0 + 2:
215 memory->wram[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
216 return;
217 case GB_REGION_WORKING_RAM_BANK1:
218 memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
219 return;
220 default:
221 if (address < GB_BASE_OAM) {
222 memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
223 } else if (address < GB_BASE_UNUSABLE) {
224 if (gb->video.mode < 2) {
225 gb->video.oam.raw[address & 0xFF] = value;
226 }
227 } else if (address < GB_BASE_IO) {
228 mLOG(GB_MEM, GAME_ERROR, "Attempt to write to unusable memory: %04X:%02X", address, value);
229 } else if (address < GB_BASE_HRAM) {
230 GBIOWrite(gb, address & (GB_SIZE_IO - 1), value);
231 } else if (address < GB_BASE_IE) {
232 memory->hram[address & GB_SIZE_HRAM] = value;
233 } else {
234 GBIOWrite(gb, REG_IE, value);
235 }
236 }
237}
238
239int32_t GBMemoryProcessEvents(struct GB* gb, int32_t cycles) {
240 if (!gb->memory.dmaRemaining) {
241 return INT_MAX;
242 }
243 gb->memory.dmaNext -= cycles;
244 if (gb->memory.dmaNext <= 0) {
245 _GBMemoryDMAService(gb);
246 }
247 return gb->memory.dmaNext;
248}
249
250void GBMemoryDMA(struct GB* gb, uint16_t base) {
251 if (base > 0xF100) {
252 return;
253 }
254 gb->cpu->memory.store8 = GBDMAStore8;
255 gb->cpu->memory.load8 = GBDMALoad8;
256 gb->memory.dmaNext = gb->cpu->cycles + 8;
257 if (gb->memory.dmaNext < gb->cpu->nextEvent) {
258 gb->cpu->nextEvent = gb->memory.dmaNext;
259 }
260 gb->memory.dmaSource = base;
261 gb->memory.dmaDest = 0;
262 gb->memory.dmaRemaining = 0xA0;
263}
264
265void _GBMemoryDMAService(struct GB* gb) {
266 uint8_t b = GBLoad8(gb->cpu, gb->memory.dmaSource);
267 // TODO: Can DMA write OAM during modes 2-3?
268 gb->video.oam.raw[gb->memory.dmaDest] = b;
269 ++gb->memory.dmaSource;
270 ++gb->memory.dmaDest;
271 --gb->memory.dmaRemaining;
272 if (gb->memory.dmaRemaining) {
273 gb->memory.dmaNext += 4;
274 } else {
275 gb->memory.dmaNext = INT_MAX;
276 gb->cpu->memory.store8 = GBStore8;
277 gb->cpu->memory.load8 = GBLoad8;
278 }
279}
280
281uint8_t GBDMALoad8(struct LR35902Core* cpu, uint16_t address) {
282 struct GB* gb = (struct GB*) cpu->master;
283 struct GBMemory* memory = &gb->memory;
284 if (address < 0xFF80 || address == 0xFFFF) {
285 return 0xFF;
286 }
287 return memory->hram[address & GB_SIZE_HRAM];
288}
289
290void GBDMAStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) {
291 struct GB* gb = (struct GB*) cpu->master;
292 struct GBMemory* memory = &gb->memory;
293 if (address < 0xFF80 || address == 0xFFFF) {
294 return;
295 }
296 memory->hram[address & GB_SIZE_HRAM] = value;
297}
298
299uint8_t GBView8(struct LR35902Core* cpu, uint16_t address);
300
301void GBPatch8(struct LR35902Core* cpu, uint16_t address, int8_t value, int8_t* old);
302
303static void _switchBank(struct GBMemory* memory, int bank) {
304 size_t bankStart = bank * GB_SIZE_CART_BANK0;
305 if (bankStart + GB_SIZE_CART_BANK0 > memory->romSize) {
306 mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank);
307 return;
308 }
309 memory->romBank = &memory->rom[bankStart];
310 memory->currentBank = bank;
311}
312
313static void _switchSramBank(struct GBMemory* memory, int bank) {
314 size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM;
315 memory->sramBank = &memory->sram[bankStart];
316 memory->sramCurrentBank = bank;
317}
318
319void _GBMBC1(struct GBMemory* memory, uint16_t address, uint8_t value) {
320 int bank = value & 0x1F;
321 switch (address >> 13) {
322 case 0x0:
323 switch (value) {
324 case 0:
325 memory->sramAccess = false;
326 break;
327 case 0xA:
328 memory->sramAccess = true;
329 _switchSramBank(memory, memory->sramCurrentBank);
330 break;
331 default:
332 // TODO
333 mLOG(GB_MBC, STUB, "MBC1 unknown value %02X", value);
334 break;
335 }
336 break;
337 break;
338 case 0x1:
339 if (!bank) {
340 ++bank;
341 }
342 _switchBank(memory, bank | (memory->currentBank & 0x60));
343 break;
344 default:
345 // TODO
346 mLOG(GB_MBC, STUB, "MBC1 unknown address: %04X:%02X", address, value);
347 break;
348 }
349}
350
351void _GBMBC2(struct GBMemory* memory, uint16_t address, uint8_t value) {
352 mLOG(GB_MBC, STUB, "MBC2 unimplemented");
353}
354
355void _GBMBC3(struct GBMemory* memory, uint16_t address, uint8_t value) {
356 int bank = value & 0x7F;
357 switch (address >> 13) {
358 case 0x0:
359 switch (value) {
360 case 0:
361 memory->sramAccess = false;
362 break;
363 case 0xA:
364 memory->sramAccess = true;
365 _switchSramBank(memory, memory->sramCurrentBank);
366 break;
367 default:
368 // TODO
369 mLOG(GB_MBC, STUB, "MBC3 unknown value %02X", value);
370 break;
371 }
372 break;
373 case 0x1:
374 if (!bank) {
375 ++bank;
376 }
377 _switchBank(memory, bank);
378 break;
379 case 0x2:
380 if (value < 4) {
381 _switchSramBank(memory, value);
382 } else {
383 mLOG(GB_MBC, STUB, "MBC3 RTC unimplemented", value);
384 }
385 break;
386 default:
387 // TODO
388 mLOG(GB_MBC, STUB, "MBC3 unknown address: %04X:%02X", address, value);
389 break;
390 }
391}
392
393void _GBMBC4(struct GBMemory* memory, uint16_t address, uint8_t value) {
394 // TODO
395 mLOG(GB_MBC, STUB, "MBC4 unimplemented");
396}
397
398void _GBMBC5(struct GBMemory* memory, uint16_t address, uint8_t value) {
399 int bank = value & 0x7F;
400 switch (address >> 13) {
401 case 0x0:
402 switch (value) {
403 case 0:
404 memory->sramAccess = false;
405 break;
406 case 0xA:
407 memory->sramAccess = true;
408 _switchSramBank(memory, memory->sramCurrentBank);
409 break;
410 default:
411 // TODO
412 mLOG(GB_MBC, STUB, "MBC5 unknown value %02X", value);
413 break;
414 }
415 break;
416 case 0x1:
417 _switchBank(memory, bank);
418 break;
419 case 0x2:
420 if (value < 0x10) {
421 _switchSramBank(memory, value);
422 }
423 break;
424 default:
425 // TODO
426 mLOG(GB_MBC, STUB, "MBC5 unknown address: %04X:%02X", address, value);
427 break;
428 }
429}
430
431void _GBMBC7(struct GBMemory* memory, uint16_t address, uint8_t value) {
432 // TODO
433 mLOG(GB_MBC, STUB, "MBC7 unimplemented");
434}