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 "core/interface.h"
9#include "gb/gb.h"
10#include "gb/io.h"
11#include "gb/mbc.h"
12#include "gb/serialize.h"
13
14#include "util/memory.h"
15
16mLOG_DEFINE_CATEGORY(GB_MEM, "GB Memory");
17
18static void _pristineCow(struct GB* gba);
19
20static uint8_t GBFastLoad8(struct LR35902Core* cpu, uint16_t address) {
21 if (UNLIKELY(address > cpu->memory.activeRegionEnd)) {
22 cpu->memory.setActiveRegion(cpu, address);
23 return cpu->memory.cpuLoad8(cpu, address);
24 }
25 return cpu->memory.activeRegion[address & cpu->memory.activeMask];
26}
27
28static void GBSetActiveRegion(struct LR35902Core* cpu, uint16_t address) {
29 struct GB* gb = (struct GB*) cpu->master;
30 struct GBMemory* memory = &gb->memory;
31 switch (address >> 12) {
32 case GB_REGION_CART_BANK0:
33 case GB_REGION_CART_BANK0 + 1:
34 case GB_REGION_CART_BANK0 + 2:
35 case GB_REGION_CART_BANK0 + 3:
36 cpu->memory.cpuLoad8 = GBFastLoad8;
37 cpu->memory.activeRegion = memory->romBase;
38 cpu->memory.activeRegionEnd = GB_BASE_CART_BANK1;
39 cpu->memory.activeMask = GB_SIZE_CART_BANK0 - 1;
40 break;
41 case GB_REGION_CART_BANK1:
42 case GB_REGION_CART_BANK1 + 1:
43 case GB_REGION_CART_BANK1 + 2:
44 case GB_REGION_CART_BANK1 + 3:
45 cpu->memory.cpuLoad8 = GBFastLoad8;
46 cpu->memory.activeRegion = memory->romBank;
47 cpu->memory.activeRegionEnd = GB_BASE_VRAM;
48 cpu->memory.activeMask = GB_SIZE_CART_BANK0 - 1;
49 break;
50 default:
51 cpu->memory.cpuLoad8 = GBLoad8;
52 break;
53 }
54}
55
56static void _GBMemoryDMAService(struct GB* gb);
57static void _GBMemoryHDMAService(struct GB* gb);
58
59void GBMemoryInit(struct GB* gb) {
60 struct LR35902Core* cpu = gb->cpu;
61 cpu->memory.cpuLoad8 = GBLoad8;
62 cpu->memory.load8 = GBLoad8;
63 cpu->memory.store8 = GBStore8;
64 cpu->memory.setActiveRegion = GBSetActiveRegion;
65
66 gb->memory.wram = 0;
67 gb->memory.wramBank = 0;
68 gb->memory.rom = 0;
69 gb->memory.romBank = 0;
70 gb->memory.romSize = 0;
71 gb->memory.sram = 0;
72 gb->memory.mbcType = GB_MBC_AUTODETECT;
73 gb->memory.mbc = 0;
74
75 gb->memory.rtc = NULL;
76
77 GBIOInit(gb);
78}
79
80void GBMemoryDeinit(struct GB* gb) {
81 mappedMemoryFree(gb->memory.wram, GB_SIZE_WORKING_RAM);
82 if (gb->memory.rom) {
83 mappedMemoryFree(gb->memory.rom, gb->memory.romSize);
84 }
85}
86
87void GBMemoryReset(struct GB* gb) {
88 if (gb->memory.wram) {
89 mappedMemoryFree(gb->memory.wram, GB_SIZE_WORKING_RAM);
90 }
91 gb->memory.wram = anonymousMemoryMap(GB_SIZE_WORKING_RAM);
92 GBMemorySwitchWramBank(&gb->memory, 1);
93 gb->memory.romBank = &gb->memory.rom[GB_SIZE_CART_BANK0];
94 gb->memory.currentBank = 1;
95 gb->memory.sramCurrentBank = 0;
96
97 gb->memory.ime = false;
98 gb->memory.ie = 0;
99
100 gb->memory.dmaNext = INT_MAX;
101 gb->memory.dmaRemaining = 0;
102 gb->memory.dmaSource = 0;
103 gb->memory.dmaDest = 0;
104 gb->memory.hdmaNext = INT_MAX;
105 gb->memory.hdmaRemaining = 0;
106 gb->memory.hdmaSource = 0;
107 gb->memory.hdmaDest = 0;
108 gb->memory.isHdma = false;
109
110 gb->memory.sramAccess = false;
111 gb->memory.rtcAccess = false;
112 gb->memory.activeRtcReg = 0;
113 gb->memory.rtcLatched = false;
114 memset(&gb->memory.rtcRegs, 0, sizeof(gb->memory.rtcRegs));
115
116 memset(&gb->memory.hram, 0, sizeof(gb->memory.hram));
117 memset(&gb->memory.mbcState, 0, sizeof(gb->memory.mbcState));
118
119 GBMBCInit(gb);
120 gb->memory.sramBank = gb->memory.sram;
121
122 if (!gb->memory.wram) {
123 GBMemoryDeinit(gb);
124 }
125}
126
127void GBMemorySwitchWramBank(struct GBMemory* memory, int bank) {
128 bank &= 7;
129 if (!bank) {
130 bank = 1;
131 }
132 memory->wramBank = &memory->wram[GB_SIZE_WORKING_RAM_BANK0 * bank];
133 memory->wramCurrentBank = bank;
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->romBase[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.vramBank[address & (GB_SIZE_VRAM_BANK0 - 1)];
153 case GB_REGION_EXTERNAL_RAM:
154 case GB_REGION_EXTERNAL_RAM + 1:
155 if (memory->rtcAccess) {
156 return memory->rtcRegs[memory->activeRtcReg];
157 } else if (memory->sramAccess) {
158 return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
159 } else if (memory->mbcType == GB_MBC7) {
160 return GBMBC7Read(memory, address);
161 } else if (memory->mbcType == GB_HuC3) {
162 return 0x01; // TODO: Is this supposed to be the current SRAM bank?
163 }
164 return 0xFF;
165 case GB_REGION_WORKING_RAM_BANK0:
166 case GB_REGION_WORKING_RAM_BANK0 + 2:
167 return memory->wram[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
168 case GB_REGION_WORKING_RAM_BANK1:
169 return memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
170 default:
171 if (address < GB_BASE_OAM) {
172 return memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
173 }
174 if (address < GB_BASE_UNUSABLE) {
175 if (gb->video.mode < 2) {
176 return gb->video.oam.raw[address & 0xFF];
177 }
178 return 0xFF;
179 }
180 if (address < GB_BASE_IO) {
181 mLOG(GB_MEM, GAME_ERROR, "Attempt to read from unusable memory: %04X", address);
182 return 0xFF;
183 }
184 if (address < GB_BASE_HRAM) {
185 return GBIORead(gb, address & (GB_SIZE_IO - 1));
186 }
187 if (address < GB_BASE_IE) {
188 return memory->hram[address & GB_SIZE_HRAM];
189 }
190 return GBIORead(gb, REG_IE);
191 }
192}
193
194void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) {
195 struct GB* gb = (struct GB*) cpu->master;
196 struct GBMemory* memory = &gb->memory;
197 switch (address >> 12) {
198 case GB_REGION_CART_BANK0:
199 case GB_REGION_CART_BANK0 + 1:
200 case GB_REGION_CART_BANK0 + 2:
201 case GB_REGION_CART_BANK0 + 3:
202 case GB_REGION_CART_BANK1:
203 case GB_REGION_CART_BANK1 + 1:
204 case GB_REGION_CART_BANK1 + 2:
205 case GB_REGION_CART_BANK1 + 3:
206 memory->mbc(gb, address, value);
207 cpu->memory.setActiveRegion(cpu, cpu->pc);
208 return;
209 case GB_REGION_VRAM:
210 case GB_REGION_VRAM + 1:
211 // TODO: Block access in wrong modes
212 gb->video.vramBank[address & (GB_SIZE_VRAM_BANK0 - 1)] = value;
213 return;
214 case GB_REGION_EXTERNAL_RAM:
215 case GB_REGION_EXTERNAL_RAM + 1:
216 if (memory->rtcAccess) {
217 memory->rtcRegs[memory->activeRtcReg] = value;
218 } else if (memory->sramAccess) {
219 memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)] = value;
220 } else if (memory->mbcType == GB_MBC7) {
221 GBMBC7Write(memory, address, value);
222 }
223 gb->sramDirty |= GB_SRAM_DIRT_NEW;
224 return;
225 case GB_REGION_WORKING_RAM_BANK0:
226 case GB_REGION_WORKING_RAM_BANK0 + 2:
227 memory->wram[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
228 return;
229 case GB_REGION_WORKING_RAM_BANK1:
230 memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
231 return;
232 default:
233 if (address < GB_BASE_OAM) {
234 memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
235 } else if (address < GB_BASE_UNUSABLE) {
236 if (gb->video.mode < 2) {
237 gb->video.oam.raw[address & 0xFF] = value;
238 }
239 } else if (address < GB_BASE_IO) {
240 mLOG(GB_MEM, GAME_ERROR, "Attempt to write to unusable memory: %04X:%02X", address, value);
241 } else if (address < GB_BASE_HRAM) {
242 GBIOWrite(gb, address & (GB_SIZE_IO - 1), value);
243 } else if (address < GB_BASE_IE) {
244 memory->hram[address & GB_SIZE_HRAM] = value;
245 } else {
246 GBIOWrite(gb, REG_IE, value);
247 }
248 }
249}
250uint8_t GBView8(struct LR35902Core* cpu, uint16_t address, int segment) {
251 struct GB* gb = (struct GB*) cpu->master;
252 struct GBMemory* memory = &gb->memory;
253 switch (address >> 12) {
254 case GB_REGION_CART_BANK0:
255 case GB_REGION_CART_BANK0 + 1:
256 case GB_REGION_CART_BANK0 + 2:
257 case GB_REGION_CART_BANK0 + 3:
258 return memory->romBase[address & (GB_SIZE_CART_BANK0 - 1)];
259 case GB_REGION_CART_BANK1:
260 case GB_REGION_CART_BANK1 + 1:
261 case GB_REGION_CART_BANK1 + 2:
262 case GB_REGION_CART_BANK1 + 3:
263 if (segment < 0) {
264 return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
265 } else {
266 if ((size_t) segment * GB_SIZE_CART_BANK0 > memory->romSize) {
267 return 0xFF;
268 }
269 return memory->rom[(address & (GB_SIZE_CART_BANK0 - 1)) + segment * GB_SIZE_CART_BANK0];
270 }
271 case GB_REGION_VRAM:
272 case GB_REGION_VRAM + 1:
273 if (segment < 0) {
274 return gb->video.vramBank[address & (GB_SIZE_VRAM_BANK0 - 1)];
275 } else {
276 return gb->video.vram[(address & (GB_SIZE_VRAM_BANK0 - 1)) + segment *GB_SIZE_VRAM_BANK0];
277 }
278 case GB_REGION_EXTERNAL_RAM:
279 case GB_REGION_EXTERNAL_RAM + 1:
280 if (memory->rtcAccess) {
281 return memory->rtcRegs[memory->activeRtcReg];
282 } else if (memory->sramAccess) {
283 if (segment < 0) {
284 return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
285 } else {
286 return memory->sram[(address & (GB_SIZE_EXTERNAL_RAM - 1)) + segment *GB_SIZE_EXTERNAL_RAM];
287 }
288 } else if (memory->mbcType == GB_MBC7) {
289 return GBMBC7Read(memory, address);
290 } else if (memory->mbcType == GB_HuC3) {
291 return 0x01; // TODO: Is this supposed to be the current SRAM bank?
292 }
293 return 0xFF;
294 case GB_REGION_WORKING_RAM_BANK0:
295 case GB_REGION_WORKING_RAM_BANK0 + 2:
296 return memory->wram[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
297 case GB_REGION_WORKING_RAM_BANK1:
298 if (segment < 0) {
299 return memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
300 } else {
301 return memory->wram[(address & (GB_SIZE_WORKING_RAM_BANK0 - 1)) + segment *GB_SIZE_WORKING_RAM_BANK0];
302 }
303 default:
304 if (address < GB_BASE_OAM) {
305 return memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
306 }
307 if (address < GB_BASE_UNUSABLE) {
308 if (gb->video.mode < 2) {
309 return gb->video.oam.raw[address & 0xFF];
310 }
311 return 0xFF;
312 }
313 if (address < GB_BASE_IO) {
314 mLOG(GB_MEM, GAME_ERROR, "Attempt to read from unusable memory: %04X", address);
315 return 0xFF;
316 }
317 if (address < GB_BASE_HRAM) {
318 return GBIORead(gb, address & (GB_SIZE_IO - 1));
319 }
320 if (address < GB_BASE_IE) {
321 return memory->hram[address & GB_SIZE_HRAM];
322 }
323 return GBIORead(gb, REG_IE);
324 }
325}
326
327int32_t GBMemoryProcessEvents(struct GB* gb, int32_t cycles) {
328 int nextEvent = INT_MAX;
329 if (gb->memory.dmaRemaining) {
330 gb->memory.dmaNext -= cycles;
331 if (gb->memory.dmaNext <= 0) {
332 _GBMemoryDMAService(gb);
333 }
334 nextEvent = gb->memory.dmaNext;
335 }
336 if (gb->memory.hdmaRemaining) {
337 gb->memory.hdmaNext -= cycles;
338 if (gb->memory.hdmaNext <= 0) {
339 _GBMemoryHDMAService(gb);
340 }
341 if (gb->memory.hdmaNext < nextEvent) {
342 nextEvent = gb->memory.hdmaNext;
343 }
344 }
345 return nextEvent;
346}
347
348void GBMemoryDMA(struct GB* gb, uint16_t base) {
349 if (base > 0xF100) {
350 return;
351 }
352 gb->cpu->memory.store8 = GBDMAStore8;
353 gb->cpu->memory.load8 = GBDMALoad8;
354 gb->cpu->memory.cpuLoad8 = GBDMALoad8;
355 gb->memory.dmaNext = gb->cpu->cycles + 8;
356 if (gb->memory.dmaNext < gb->cpu->nextEvent) {
357 gb->cpu->nextEvent = gb->memory.dmaNext;
358 }
359 gb->memory.dmaSource = base;
360 gb->memory.dmaDest = 0;
361 gb->memory.dmaRemaining = 0xA0;
362}
363
364void GBMemoryWriteHDMA5(struct GB* gb, uint8_t value) {
365 gb->memory.hdmaSource = gb->memory.io[REG_HDMA1] << 8;
366 gb->memory.hdmaSource |= gb->memory.io[REG_HDMA2];
367 gb->memory.hdmaDest = gb->memory.io[REG_HDMA3] << 8;
368 gb->memory.hdmaDest |= gb->memory.io[REG_HDMA4];
369 gb->memory.hdmaSource &= 0xFFF0;
370 if (gb->memory.hdmaSource >= 0x8000 && gb->memory.hdmaSource < 0xA000) {
371 mLOG(GB_MEM, GAME_ERROR, "Invalid HDMA source: %04X", gb->memory.hdmaSource);
372 return;
373 }
374 gb->memory.hdmaDest &= 0x1FF0;
375 gb->memory.hdmaDest |= 0x8000;
376 bool wasHdma = gb->memory.isHdma;
377 gb->memory.isHdma = value & 0x80;
378 if (!wasHdma && !gb->memory.isHdma) {
379 gb->memory.hdmaRemaining = ((value & 0x7F) + 1) * 0x10;
380 gb->memory.hdmaNext = gb->cpu->cycles;
381 gb->cpu->nextEvent = gb->cpu->cycles;
382 }
383}
384
385void _GBMemoryDMAService(struct GB* gb) {
386 uint8_t b = GBLoad8(gb->cpu, gb->memory.dmaSource);
387 // TODO: Can DMA write OAM during modes 2-3?
388 gb->video.oam.raw[gb->memory.dmaDest] = b;
389 ++gb->memory.dmaSource;
390 ++gb->memory.dmaDest;
391 --gb->memory.dmaRemaining;
392 if (gb->memory.dmaRemaining) {
393 gb->memory.dmaNext += 4;
394 } else {
395 gb->memory.dmaNext = INT_MAX;
396 gb->cpu->memory.store8 = GBStore8;
397 gb->cpu->memory.load8 = GBLoad8;
398 }
399}
400
401void _GBMemoryHDMAService(struct GB* gb) {
402 uint8_t b = gb->cpu->memory.load8(gb->cpu, gb->memory.hdmaSource);
403 gb->cpu->memory.store8(gb->cpu, gb->memory.hdmaDest, b);
404 ++gb->memory.hdmaSource;
405 ++gb->memory.hdmaDest;
406 --gb->memory.hdmaRemaining;
407 gb->cpu->cycles += 2;
408 if (gb->memory.hdmaRemaining) {
409 gb->memory.hdmaNext += 2;
410 } else {
411 gb->memory.io[REG_HDMA1] = gb->memory.hdmaSource >> 8;
412 gb->memory.io[REG_HDMA2] = gb->memory.hdmaSource;
413 gb->memory.io[REG_HDMA3] = gb->memory.hdmaDest >> 8;
414 gb->memory.io[REG_HDMA4] = gb->memory.hdmaDest;
415 if (gb->memory.isHdma) {
416 --gb->memory.io[REG_HDMA5];
417 if (gb->memory.io[REG_HDMA5] == 0xFF) {
418 gb->memory.isHdma = false;
419 }
420 } else {
421 gb->memory.io[REG_HDMA5] |= 0x80;
422 }
423 }
424}
425
426struct OAMBlock {
427 uint16_t low;
428 uint16_t high;
429};
430
431static const struct OAMBlock _oamBlockDMG[] = {
432 { 0xA000, 0xFE00 },
433 { 0xA000, 0xFE00 },
434 { 0xA000, 0xFE00 },
435 { 0xA000, 0xFE00 },
436 { 0x8000, 0xA000 },
437 { 0xA000, 0xFE00 },
438 { 0xA000, 0xFE00 },
439 { 0xA000, 0xFE00 },
440};
441
442static const struct OAMBlock _oamBlockCGB[] = {
443 { 0xA000, 0xC000 },
444 { 0xA000, 0xC000 },
445 { 0xA000, 0xC000 },
446 { 0xA000, 0xC000 },
447 { 0x8000, 0xA000 },
448 { 0xA000, 0xC000 },
449 { 0xC000, 0xFE00 },
450 { 0xA000, 0xC000 },
451};
452
453uint8_t GBDMALoad8(struct LR35902Core* cpu, uint16_t address) {
454 struct GB* gb = (struct GB*) cpu->master;
455 struct GBMemory* memory = &gb->memory;
456 const struct OAMBlock* block = gb->model < GB_MODEL_CGB ? _oamBlockDMG : _oamBlockCGB;
457 block = &block[memory->dmaSource >> 13];
458 if (address >= block->low && address < block->high) {
459 return 0xFF;
460 }
461 if (address >= GB_BASE_OAM && address < GB_BASE_UNUSABLE) {
462 return 0xFF;
463 }
464 return GBLoad8(cpu, address);
465}
466
467void GBDMAStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) {
468 struct GB* gb = (struct GB*) cpu->master;
469 struct GBMemory* memory = &gb->memory;
470 const struct OAMBlock* block = gb->model < GB_MODEL_CGB ? _oamBlockDMG : _oamBlockCGB;
471 block = &block[memory->dmaSource >> 13];
472 if (address >= block->low && address < block->high) {
473 return;
474 }
475 if (address >= GB_BASE_OAM && address < GB_BASE_UNUSABLE) {
476 return;
477 }
478 GBStore8(cpu, address, value);
479}
480
481void GBPatch8(struct LR35902Core* cpu, uint16_t address, int8_t value, int8_t* old, int segment) {
482 struct GB* gb = (struct GB*) cpu->master;
483 struct GBMemory* memory = &gb->memory;
484 int8_t oldValue = -1;
485
486 switch (address >> 12) {
487 case GB_REGION_CART_BANK0:
488 case GB_REGION_CART_BANK0 + 1:
489 case GB_REGION_CART_BANK0 + 2:
490 case GB_REGION_CART_BANK0 + 3:
491 _pristineCow(gb);
492 oldValue = memory->rom[address & (GB_SIZE_CART_BANK0 - 1)];
493 memory->rom[address & (GB_SIZE_CART_BANK0 - 1)] = value;
494 break;
495 case GB_REGION_CART_BANK1:
496 case GB_REGION_CART_BANK1 + 1:
497 case GB_REGION_CART_BANK1 + 2:
498 case GB_REGION_CART_BANK1 + 3:
499 _pristineCow(gb);
500 if (segment < 0) {
501 oldValue = memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
502 memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)] = value;
503 } else {
504 if ((size_t) segment * GB_SIZE_CART_BANK0 > memory->romSize) {
505 return;
506 }
507 oldValue = memory->rom[(address & (GB_SIZE_CART_BANK0 - 1)) + segment * GB_SIZE_CART_BANK0];
508 memory->rom[(address & (GB_SIZE_CART_BANK0 - 1)) + segment * GB_SIZE_CART_BANK0] = value;
509 }
510 break;
511 case GB_REGION_VRAM:
512 case GB_REGION_VRAM + 1:
513 if (segment < 0) {
514 oldValue = gb->video.vramBank[address & (GB_SIZE_VRAM_BANK0 - 1)];
515 gb->video.vramBank[address & (GB_SIZE_VRAM_BANK0 - 1)] = value;
516 } else {
517 oldValue = gb->video.vram[(address & (GB_SIZE_VRAM_BANK0 - 1)) + segment * GB_SIZE_VRAM_BANK0];
518 gb->video.vramBank[(address & (GB_SIZE_VRAM_BANK0 - 1)) + segment * GB_SIZE_VRAM_BANK0] = value;
519 }
520 break;
521 case GB_REGION_EXTERNAL_RAM:
522 case GB_REGION_EXTERNAL_RAM + 1:
523 mLOG(GB_MEM, STUB, "Unimplemented memory Patch8: 0x%08X", address);
524 return;
525 case GB_REGION_WORKING_RAM_BANK0:
526 case GB_REGION_WORKING_RAM_BANK0 + 2:
527 oldValue = memory->wram[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
528 memory->wram[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
529 break;
530 case GB_REGION_WORKING_RAM_BANK1:
531 if (segment < 0) {
532 oldValue = memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
533 memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
534 } else {
535 oldValue = memory->wram[(address & (GB_SIZE_WORKING_RAM_BANK0 - 1)) + segment * GB_SIZE_WORKING_RAM_BANK0];
536 memory->wram[(address & (GB_SIZE_WORKING_RAM_BANK0 - 1)) + segment * GB_SIZE_WORKING_RAM_BANK0] = value;
537 }
538 break;
539 default:
540 if (address < GB_BASE_OAM) {
541 oldValue = memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
542 memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
543 } else if (address < GB_BASE_UNUSABLE) {
544 oldValue = gb->video.oam.raw[address & 0xFF];
545 gb->video.oam.raw[address & 0xFF] = value;
546 } else if (address < GB_BASE_HRAM) {
547 mLOG(GB_MEM, STUB, "Unimplemented memory Patch8: 0x%08X", address);
548 return;
549 } else if (address < GB_BASE_IE) {
550 oldValue = memory->hram[address & GB_SIZE_HRAM];
551 memory->hram[address & GB_SIZE_HRAM] = value;
552 } else {
553 mLOG(GB_MEM, STUB, "Unimplemented memory Patch8: 0x%08X", address);
554 return;
555 }
556 }
557 if (old) {
558 *old = oldValue;
559 }
560}
561
562void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) {
563 const struct GBMemory* memory = &gb->memory;
564 memcpy(state->wram, memory->wram, GB_SIZE_WORKING_RAM);
565 memcpy(state->hram, memory->hram, GB_SIZE_HRAM);
566 STORE_16LE(memory->currentBank, 0, &state->memory.currentBank);
567 state->memory.wramCurrentBank = memory->wramCurrentBank;
568 state->memory.sramCurrentBank = memory->sramCurrentBank;
569
570 STORE_32LE(memory->dmaNext, 0, &state->memory.dmaNext);
571 STORE_16LE(memory->dmaSource, 0, &state->memory.dmaSource);
572 STORE_16LE(memory->dmaDest, 0, &state->memory.dmaDest);
573
574 STORE_32LE(memory->hdmaNext, 0, &state->memory.hdmaNext);
575 STORE_16LE(memory->hdmaSource, 0, &state->memory.hdmaSource);
576 STORE_16LE(memory->hdmaDest, 0, &state->memory.hdmaDest);
577
578 STORE_16LE(memory->hdmaRemaining, 0, &state->memory.hdmaRemaining);
579 state->memory.dmaRemaining = memory->dmaRemaining;
580 memcpy(state->memory.rtcRegs, memory->rtcRegs, sizeof(state->memory.rtcRegs));
581
582 GBSerializedMemoryFlags flags = 0;
583 flags = GBSerializedMemoryFlagsSetSramAccess(flags, memory->sramAccess);
584 flags = GBSerializedMemoryFlagsSetRtcAccess(flags, memory->rtcAccess);
585 flags = GBSerializedMemoryFlagsSetRtcLatched(flags, memory->rtcLatched);
586 flags = GBSerializedMemoryFlagsSetIme(flags, memory->ime);
587 flags = GBSerializedMemoryFlagsSetIsHdma(flags, memory->isHdma);
588 flags = GBSerializedMemoryFlagsSetActiveRtcReg(flags, memory->activeRtcReg);
589 STORE_16LE(flags, 0, &state->memory.flags);
590}
591
592void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
593 struct GBMemory* memory = &gb->memory;
594 memcpy(memory->wram, state->wram, GB_SIZE_WORKING_RAM);
595 memcpy(memory->hram, state->hram, GB_SIZE_HRAM);
596 LOAD_16LE(memory->currentBank, 0, &state->memory.currentBank);
597 memory->wramCurrentBank = state->memory.wramCurrentBank;
598 memory->sramCurrentBank = state->memory.sramCurrentBank;
599
600 GBMBCSwitchBank(memory, memory->currentBank);
601 GBMemorySwitchWramBank(memory, memory->wramCurrentBank);
602 GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
603
604 LOAD_32LE(memory->dmaNext, 0, &state->memory.dmaNext);
605 LOAD_16LE(memory->dmaSource, 0, &state->memory.dmaSource);
606 LOAD_16LE(memory->dmaDest, 0, &state->memory.dmaDest);
607
608 LOAD_32LE(memory->hdmaNext, 0, &state->memory.hdmaNext);
609 LOAD_16LE(memory->hdmaSource, 0, &state->memory.hdmaSource);
610 LOAD_16LE(memory->hdmaDest, 0, &state->memory.hdmaDest);
611
612 LOAD_16LE(memory->hdmaRemaining, 0, &state->memory.hdmaRemaining);
613 memory->dmaRemaining = state->memory.dmaRemaining;
614 memcpy(memory->rtcRegs, state->memory.rtcRegs, sizeof(state->memory.rtcRegs));
615
616 GBSerializedMemoryFlags flags;
617 LOAD_16LE(flags, 0, &state->memory.flags);
618 memory->sramAccess = GBSerializedMemoryFlagsGetSramAccess(flags);
619 memory->rtcAccess = GBSerializedMemoryFlagsGetRtcAccess(flags);
620 memory->rtcLatched = GBSerializedMemoryFlagsGetRtcLatched(flags);
621 memory->ime = GBSerializedMemoryFlagsGetIme(flags);
622 memory->isHdma = GBSerializedMemoryFlagsGetIsHdma(flags);
623 memory->activeRtcReg = GBSerializedMemoryFlagsGetActiveRtcReg(flags);
624}
625
626void _pristineCow(struct GB* gb) {
627 if (gb->memory.rom != gb->pristineRom) {
628 return;
629 }
630 gb->memory.rom = anonymousMemoryMap(GB_SIZE_CART_MAX);
631 memcpy(gb->memory.rom, gb->pristineRom, gb->memory.romSize);
632 memset(((uint8_t*) gb->memory.rom) + gb->memory.romSize, 0xFF, GB_SIZE_CART_MAX - gb->memory.romSize);
633 GBMBCSwitchBank(&gb->memory, gb->memory.currentBank);
634}