all repos — mgba @ f8f75c4c913965cb656dbeac0c8ee85590082855

mGBA Game Boy Advance Emulator

src/gb/gb.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 "gb.h"
  7
  8#include "gb/io.h"
  9#include "gb/mbc.h"
 10
 11#include "core/core.h"
 12#include "core/cheats.h"
 13#include "util/crc32.h"
 14#include "util/memory.h"
 15#include "util/math.h"
 16#include "util/patch.h"
 17#include "util/vfs.h"
 18
 19#define CLEANUP_THRESHOLD 15
 20
 21const uint32_t CGB_LR35902_FREQUENCY = 0x800000;
 22const uint32_t SGB_LR35902_FREQUENCY = 0x418B1E;
 23
 24const uint32_t GB_COMPONENT_MAGIC = 0x400000;
 25
 26mLOG_DEFINE_CATEGORY(GB, "GB");
 27
 28static void GBInit(void* cpu, struct mCPUComponent* component);
 29static void GBInterruptHandlerInit(struct LR35902InterruptHandler* irqh);
 30static void GBProcessEvents(struct LR35902Core* cpu);
 31static void GBSetInterrupts(struct LR35902Core* cpu, bool enable);
 32static void GBIllegal(struct LR35902Core* cpu);
 33static void GBStop(struct LR35902Core* cpu);
 34
 35#ifdef _3DS
 36extern uint32_t* romBuffer;
 37extern size_t romBufferSize;
 38#endif
 39
 40void GBCreate(struct GB* gb) {
 41	gb->d.id = GB_COMPONENT_MAGIC;
 42	gb->d.init = GBInit;
 43	gb->d.deinit = 0;
 44}
 45
 46static void GBInit(void* cpu, struct mCPUComponent* component) {
 47	struct GB* gb = (struct GB*) component;
 48	gb->cpu = cpu;
 49	gb->sync = NULL;
 50
 51	GBInterruptHandlerInit(&gb->cpu->irqh);
 52	GBMemoryInit(gb);
 53
 54	gb->video.p = gb;
 55	GBVideoInit(&gb->video);
 56
 57	gb->audio.p = gb;
 58	GBAudioInit(&gb->audio, 2048, &gb->memory.io[REG_NR52], GB_AUDIO_DMG); // TODO: Remove magic constant
 59
 60	gb->sio.p = gb;
 61	GBSIOInit(&gb->sio);
 62
 63	gb->timer.p = gb;
 64
 65	gb->model = GB_MODEL_AUTODETECT;
 66
 67	gb->biosVf = 0;
 68	gb->romVf = 0;
 69	gb->sramVf = 0;
 70
 71	gb->pristineRom = 0;
 72	gb->pristineRomSize = 0;
 73	gb->yankedRomSize = 0;
 74
 75	gb->stream = NULL;
 76}
 77
 78bool GBLoadROM(struct GB* gb, struct VFile* vf) {
 79	GBUnloadROM(gb);
 80	gb->romVf = vf;
 81	gb->pristineRomSize = vf->size(vf);
 82	vf->seek(vf, 0, SEEK_SET);
 83#ifdef _3DS
 84	gb->pristineRom = 0;
 85	if (gb->pristineRomSize <= romBufferSize) {
 86		gb->pristineRom = romBuffer;
 87		vf->read(vf, romBuffer, gb->pristineRomSize);
 88	}
 89#else
 90	gb->pristineRom = vf->map(vf, gb->pristineRomSize, MAP_READ);
 91#endif
 92	if (!gb->pristineRom) {
 93		return false;
 94	}
 95	gb->yankedRomSize = 0;
 96	gb->memory.rom = gb->pristineRom;
 97	gb->memory.romBase = gb->memory.rom;
 98	gb->memory.romSize = gb->pristineRomSize;
 99	gb->romCrc32 = doCrc32(gb->memory.rom, gb->memory.romSize);
100
101	// TODO: error check
102	return true;
103}
104
105bool GBLoadSave(struct GB* gb, struct VFile* vf) {
106	gb->sramVf = vf;
107	gb->sramRealVf = vf;
108	return vf;
109}
110
111static void GBSramDeinit(struct GB* gb) {
112	if (gb->sramVf) {
113		gb->sramVf->unmap(gb->sramVf, gb->memory.sram, gb->sramSize);
114		if (gb->memory.mbcType == GB_MBC3_RTC) {
115			GBMBCRTCWrite(gb);
116		}
117		gb->sramVf->close(gb->sramVf);
118		gb->sramVf = 0;
119	} else if (gb->memory.sram) {
120		mappedMemoryFree(gb->memory.sram, gb->sramSize);
121	}
122	gb->memory.sram = 0;
123}
124
125void GBResizeSram(struct GB* gb, size_t size) {
126	if (gb->memory.sram && size <= gb->sramSize) {
127		return;
128	}
129	mLOG(GB, INFO, "Resizing SRAM to %"PRIz"u bytes", size);
130	struct VFile* vf = gb->sramVf;
131	if (vf) {
132		if (vf == gb->sramRealVf) {
133			ssize_t vfSize = vf->size(vf);
134			if (vfSize >= 0 && (size_t) vfSize < size) {
135				uint8_t extdataBuffer[0x100];
136				if (vfSize & 0xFF) {
137					vf->seek(vf, -(vfSize & 0xFF), SEEK_END);
138					vf->read(vf, extdataBuffer, vfSize & 0xFF);
139				}
140				if (gb->memory.sram) {
141					vf->unmap(vf, gb->memory.sram, gb->sramSize);
142				}
143				vf->truncate(vf, size + (vfSize & 0xFF));
144				if (vfSize & 0xFF) {
145					vf->seek(vf, size, SEEK_SET);
146					vf->write(vf, extdataBuffer, vfSize & 0xFF);
147				}
148				gb->memory.sram = vf->map(vf, size, MAP_WRITE);
149				memset(&gb->memory.sram[gb->sramSize], 0xFF, size - gb->sramSize);
150			} else if (size > gb->sramSize || !gb->memory.sram) {
151				if (gb->memory.sram) {
152					vf->unmap(vf, gb->memory.sram, gb->sramSize);
153				}
154				gb->memory.sram = vf->map(vf, size, MAP_WRITE);
155			}
156		} else {
157			if (gb->memory.sram) {
158				vf->unmap(vf, gb->memory.sram, gb->sramSize);
159			}
160			gb->memory.sram = vf->map(vf, size, MAP_READ);
161		}
162		if (gb->memory.sram == (void*) -1) {
163			gb->memory.sram = NULL;
164		}
165	} else {
166		uint8_t* newSram = anonymousMemoryMap(size);
167		if (gb->memory.sram) {
168			if (size > gb->sramSize) {
169				memcpy(newSram, gb->memory.sram, gb->sramSize);
170				memset(&newSram[gb->sramSize], 0xFF, size - gb->sramSize);
171			} else {
172				memcpy(newSram, gb->memory.sram, size);
173			}
174			mappedMemoryFree(gb->memory.sram, gb->sramSize);
175		} else {
176			memset(newSram, 0xFF, size);
177		}
178		gb->memory.sram = newSram;
179	}
180	if (gb->sramSize < size) {
181		gb->sramSize = size;
182	}
183}
184
185void GBSramClean(struct GB* gb, uint32_t frameCount) {
186	// TODO: Share with GBASavedataClean
187	if (!gb->sramVf) {
188		return;
189	}
190	if (gb->sramDirty & GB_SRAM_DIRT_NEW) {
191		gb->sramDirtAge = frameCount;
192		gb->sramDirty &= ~GB_SRAM_DIRT_NEW;
193		if (!(gb->sramDirty & GB_SRAM_DIRT_SEEN)) {
194			gb->sramDirty |= GB_SRAM_DIRT_SEEN;
195		}
196	} else if ((gb->sramDirty & GB_SRAM_DIRT_SEEN) && frameCount - gb->sramDirtAge > CLEANUP_THRESHOLD) {
197		if (gb->memory.mbcType == GB_MBC3_RTC) {
198			GBMBCRTCWrite(gb);
199		}
200		gb->sramDirty = 0;
201		if (gb->memory.sram && gb->sramVf->sync(gb->sramVf, gb->memory.sram, gb->sramSize)) {
202			mLOG(GB_MEM, INFO, "Savedata synced");
203		} else {
204			mLOG(GB_MEM, INFO, "Savedata failed to sync!");
205		}
206	}
207}
208
209void GBSavedataMask(struct GB* gb, struct VFile* vf) {
210	GBSramDeinit(gb);
211	gb->sramVf = vf;
212	gb->memory.sram = vf->map(vf, gb->sramSize, MAP_READ);
213}
214
215void GBSavedataUnmask(struct GB* gb) {
216	if (gb->sramVf == gb->sramRealVf) {
217		return;
218	}
219	GBSramDeinit(gb);
220	gb->sramVf = gb->sramRealVf;
221	gb->memory.sram = gb->sramVf->map(gb->sramVf, gb->sramSize, MAP_WRITE);
222}
223
224void GBUnloadROM(struct GB* gb) {
225	// TODO: Share with GBAUnloadROM
226	if (gb->memory.rom && gb->memory.romBase != gb->memory.rom) {
227		free(gb->memory.romBase);
228	}
229	if (gb->memory.rom && gb->pristineRom != gb->memory.rom) {
230		if (gb->yankedRomSize) {
231			gb->yankedRomSize = 0;
232		}
233		mappedMemoryFree(gb->memory.rom, GB_SIZE_CART_MAX);
234	}
235	gb->memory.rom = 0;
236
237	if (gb->romVf) {
238#ifndef _3DS
239		gb->romVf->unmap(gb->romVf, gb->pristineRom, gb->pristineRomSize);
240#endif
241		gb->romVf->close(gb->romVf);
242		gb->pristineRom = 0;
243		gb->romVf = 0;
244	}
245
246	GBSramDeinit(gb);
247}
248
249void GBLoadBIOS(struct GB* gb, struct VFile* vf) {
250	gb->biosVf = vf;
251}
252
253void GBApplyPatch(struct GB* gb, struct Patch* patch) {
254	size_t patchedSize = patch->outputSize(patch, gb->memory.romSize);
255	if (!patchedSize) {
256		return;
257	}
258	if (patchedSize > GB_SIZE_CART_MAX) {
259		patchedSize = GB_SIZE_CART_MAX;
260	}
261	gb->memory.rom = anonymousMemoryMap(GB_SIZE_CART_MAX);
262	if (!patch->applyPatch(patch, gb->pristineRom, gb->pristineRomSize, gb->memory.rom, patchedSize)) {
263		mappedMemoryFree(gb->memory.rom, patchedSize);
264		gb->memory.rom = gb->pristineRom;
265		return;
266	}
267	gb->memory.romSize = patchedSize;
268	gb->romCrc32 = doCrc32(gb->memory.rom, gb->memory.romSize);
269}
270
271void GBDestroy(struct GB* gb) {
272	GBUnloadROM(gb);
273
274	if (gb->biosVf) {
275		gb->biosVf->close(gb->biosVf);
276		gb->biosVf = 0;
277	}
278
279	GBMemoryDeinit(gb);
280	GBVideoDeinit(&gb->video);
281	GBSIODeinit(&gb->sio);
282}
283
284void GBInterruptHandlerInit(struct LR35902InterruptHandler* irqh) {
285	irqh->reset = GBReset;
286	irqh->processEvents = GBProcessEvents;
287	irqh->setInterrupts = GBSetInterrupts;
288	irqh->hitIllegal = GBIllegal;
289	irqh->stop = GBStop;
290	irqh->halt = GBHalt;
291}
292
293void GBReset(struct LR35902Core* cpu) {
294	struct GB* gb = (struct GB*) cpu->master;
295	GBDetectModel(gb);
296	if (gb->biosVf) {
297		gb->biosVf->seek(gb->biosVf, 0, SEEK_SET);
298		gb->memory.romBase = malloc(GB_SIZE_CART_BANK0);
299		ssize_t size = gb->biosVf->read(gb->biosVf, gb->memory.romBase, GB_SIZE_CART_BANK0);
300		uint32_t biosCrc = doCrc32(gb->memory.romBase, size);
301		switch (biosCrc) {
302		case 0x59C8598E:
303			break;
304		case 0x41884E46:
305			break;
306		default:
307			gb->biosVf->close(gb->biosVf);
308			gb->biosVf = NULL;
309			free(gb->memory.romBase);
310			gb->memory.romBase = gb->memory.rom;
311			break;
312		}
313
314		if (gb->biosVf) {
315			memcpy(&gb->memory.romBase[size], &gb->memory.rom[size], GB_SIZE_CART_BANK0 - size);
316			if (size > 0x100) {
317				memcpy(&gb->memory.romBase[0x100], &gb->memory.rom[0x100], sizeof(struct GBCartridge));
318			}
319
320			cpu->a = 0;
321			cpu->f.packed = 0;
322			cpu->c = 0;
323			cpu->e = 0;
324			cpu->h = 0;
325			cpu->l = 0;
326			cpu->sp = 0;
327			cpu->pc = 0;
328		}
329	}
330
331	cpu->b = 0;
332	cpu->d = 0;
333
334	if (!gb->biosVf) {
335		switch (gb->model) {
336		case GB_MODEL_DMG:
337			// TODO: SGB
338		case GB_MODEL_SGB:
339		case GB_MODEL_AUTODETECT: // Silence warnings
340			gb->model = GB_MODEL_DMG;
341			cpu->a = 1;
342			cpu->f.packed = 0xB0;
343			cpu->c = 0x13;
344			cpu->e = 0xD8;
345			cpu->h = 1;
346			cpu->l = 0x4D;
347			break;
348		case GB_MODEL_AGB:
349			cpu->b = 1;
350			// Fall through
351		case GB_MODEL_CGB:
352			cpu->a = 0x11;
353			cpu->f.packed = 0x80;
354			cpu->c = 0;
355			cpu->e = 0x08;
356			cpu->h = 0;
357			cpu->l = 0x7C;
358			break;
359		}
360
361		cpu->sp = 0xFFFE;
362		cpu->pc = 0x100;
363	}
364
365	gb->eiPending = INT_MAX;
366	gb->doubleSpeed = 0;
367
368	cpu->memory.setActiveRegion(cpu, cpu->pc);
369
370	if (gb->yankedRomSize) {
371		gb->memory.romSize = gb->yankedRomSize;
372		gb->yankedRomSize = 0;
373	}
374	GBMemoryReset(gb);
375	GBVideoReset(&gb->video);
376	GBTimerReset(&gb->timer);
377	GBIOReset(gb);
378	GBAudioReset(&gb->audio);
379	GBSIOReset(&gb->sio);
380
381	GBSavedataUnmask(gb);
382}
383
384void GBDetectModel(struct GB* gb) {
385	if (gb->model != GB_MODEL_AUTODETECT) {
386		return;
387	}
388	if (gb->biosVf) {
389		gb->biosVf->seek(gb->biosVf, 0, SEEK_SET);
390		void* bios = malloc(GB_SIZE_CART_BANK0);
391		ssize_t size = gb->biosVf->read(gb->biosVf, bios, GB_SIZE_CART_BANK0);
392		uint32_t biosCrc = doCrc32(gb->memory.romBase, size);
393		switch (biosCrc) {
394		case 0x59C8598E:
395			gb->model = GB_MODEL_DMG;
396			break;
397		case 0x41884E46:
398			gb->model = GB_MODEL_CGB;
399			break;
400		default:
401			gb->biosVf->close(gb->biosVf);
402			gb->biosVf = NULL;
403		}
404		free(bios);
405	}
406	if (gb->model == GB_MODEL_AUTODETECT && gb->memory.rom) {
407		const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
408		if (cart->cgb & 0x80) {
409			gb->model = GB_MODEL_CGB;
410		} else {
411			gb->model = GB_MODEL_DMG;
412		}
413	}
414
415	switch (gb->model) {
416	case GB_MODEL_DMG:
417	case GB_MODEL_SGB:
418	case GB_MODEL_AUTODETECT: //Silence warnings
419		gb->audio.style = GB_AUDIO_DMG;
420		break;
421	case GB_MODEL_AGB:
422	case GB_MODEL_CGB:
423		gb->audio.style = GB_AUDIO_CGB;
424		break;
425	}
426}
427
428void GBUpdateIRQs(struct GB* gb) {
429	int irqs = gb->memory.ie & gb->memory.io[REG_IF];
430	if (!irqs) {
431		return;
432	}
433	gb->cpu->halted = false;
434
435	if (!gb->memory.ime || gb->cpu->irqPending) {
436		return;
437	}
438
439	if (irqs & (1 << GB_IRQ_VBLANK)) {
440		LR35902RaiseIRQ(gb->cpu, GB_VECTOR_VBLANK);
441		gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_VBLANK);
442		return;
443	}
444	if (irqs & (1 << GB_IRQ_LCDSTAT)) {
445		LR35902RaiseIRQ(gb->cpu, GB_VECTOR_LCDSTAT);
446		gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_LCDSTAT);
447		return;
448	}
449	if (irqs & (1 << GB_IRQ_TIMER)) {
450		LR35902RaiseIRQ(gb->cpu, GB_VECTOR_TIMER);
451		gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_TIMER);
452		return;
453	}
454	if (irqs & (1 << GB_IRQ_SIO)) {
455		LR35902RaiseIRQ(gb->cpu, GB_VECTOR_SIO);
456		gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_SIO);
457		return;
458	}
459	if (irqs & (1 << GB_IRQ_KEYPAD)) {
460		LR35902RaiseIRQ(gb->cpu, GB_VECTOR_KEYPAD);
461		gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_KEYPAD);
462	}
463}
464
465void GBProcessEvents(struct LR35902Core* cpu) {
466	struct GB* gb = (struct GB*) cpu->master;
467	do {
468		int32_t cycles = cpu->nextEvent;
469		int32_t nextEvent = INT_MAX;
470		int32_t testEvent;
471
472		if (gb->eiPending != INT_MAX) {
473			gb->eiPending -= cycles;
474			if (gb->eiPending <= 0) {
475				gb->memory.ime = true;
476				GBUpdateIRQs(gb);
477				gb->eiPending = INT_MAX;
478			} else {
479				nextEvent = gb->eiPending;
480			}
481		}
482
483		testEvent = GBVideoProcessEvents(&gb->video, cycles >> gb->doubleSpeed);
484		if (testEvent != INT_MAX) {
485			testEvent <<= gb->doubleSpeed;
486			if (testEvent < nextEvent) {
487				nextEvent = testEvent;
488			}
489		}
490
491		testEvent = GBAudioProcessEvents(&gb->audio, cycles >> gb->doubleSpeed);
492		if (testEvent != INT_MAX) {
493			testEvent <<= gb->doubleSpeed;
494			if (testEvent < nextEvent) {
495				nextEvent = testEvent;
496			}
497		}
498
499		testEvent = GBTimerProcessEvents(&gb->timer, cycles);
500		if (testEvent < nextEvent) {
501			nextEvent = testEvent;
502		}
503
504		testEvent = GBSIOProcessEvents(&gb->sio, cycles);
505		if (testEvent < nextEvent) {
506			nextEvent = testEvent;
507		}
508
509		testEvent = GBMemoryProcessEvents(gb, cycles);
510		if (testEvent < nextEvent) {
511			nextEvent = testEvent;
512		}
513
514		cpu->cycles -= cycles;
515		cpu->nextEvent = nextEvent;
516
517		if (cpu->halted) {
518			cpu->cycles = cpu->nextEvent;
519		}
520	} while (cpu->cycles >= cpu->nextEvent);
521}
522
523void GBSetInterrupts(struct LR35902Core* cpu, bool enable) {
524	struct GB* gb = (struct GB*) cpu->master;
525	if (!enable) {
526		gb->memory.ime = enable;
527		gb->eiPending = INT_MAX;
528		GBUpdateIRQs(gb);
529	} else {
530		if (cpu->nextEvent > cpu->cycles + 4) {
531			cpu->nextEvent = cpu->cycles + 4;
532		}
533		gb->eiPending = cpu->cycles + 4;
534	}
535}
536
537void GBHalt(struct LR35902Core* cpu) {
538	if (!cpu->irqPending) {
539		cpu->cycles = cpu->nextEvent;
540		cpu->halted = true;
541	}
542}
543
544void GBStop(struct LR35902Core* cpu) {
545	struct GB* gb = (struct GB*) cpu->master;
546	if (cpu->bus) {
547		mLOG(GB, GAME_ERROR, "Hit illegal stop at address %04X:%02X\n", cpu->pc, cpu->bus);
548		if (cpu->components && cpu->components[CPU_COMPONENT_DEBUGGER]) {
549			struct mDebuggerEntryInfo info = {
550				.address = cpu->pc - 1,
551				.opcode = 0x1000 | cpu->bus
552			};
553			mDebuggerEnter((struct mDebugger*) cpu->components[CPU_COMPONENT_DEBUGGER], DEBUGGER_ENTER_ILLEGAL_OP, &info);
554		}
555		// Hang forever
556		gb->memory.ime = 0;
557		cpu->pc -= 2;
558	} else if (gb->memory.io[REG_KEY1] & 1) {
559		gb->doubleSpeed ^= 1;
560		gb->memory.io[REG_KEY1] &= 1;
561		gb->memory.io[REG_KEY1] |= gb->doubleSpeed << 7;
562	}
563	// TODO: Actually stop
564}
565
566void GBIllegal(struct LR35902Core* cpu) {
567	struct GB* gb = (struct GB*) cpu->master;
568	mLOG(GB, GAME_ERROR, "Hit illegal opcode at address %04X:%02X\n", cpu->pc, cpu->bus);
569	if (cpu->components && cpu->components[CPU_COMPONENT_DEBUGGER]) {
570		struct mDebuggerEntryInfo info = {
571			.address = cpu->pc,
572			.opcode = cpu->bus
573		};
574		mDebuggerEnter((struct mDebugger*) cpu->components[CPU_COMPONENT_DEBUGGER], DEBUGGER_ENTER_ILLEGAL_OP, &info);
575	}
576	// Hang forever
577	gb->memory.ime = 0;
578	--cpu->pc;
579}
580
581bool GBIsROM(struct VFile* vf) {
582	vf->seek(vf, 0x104, SEEK_SET);
583	uint8_t header[4];
584	static const uint8_t knownHeader[4] = { 0xCE, 0xED, 0x66, 0x66};
585
586	if (vf->read(vf, &header, sizeof(header)) < (ssize_t) sizeof(header)) {
587		return false;
588	}
589	if (memcmp(header, knownHeader, sizeof(header))) {
590		return false;
591	}
592	return true;
593}
594
595void GBGetGameTitle(struct GB* gb, char* out) {
596	const struct GBCartridge* cart = NULL;
597	if (gb->memory.rom) {
598		cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
599	}
600	if (gb->pristineRom) {
601		cart = (const struct GBCartridge*) &((uint8_t*) gb->pristineRom)[0x100];
602	}
603	if (!cart) {
604		return;
605	}
606	if (cart->oldLicensee != 0x33) {
607		memcpy(out, cart->titleLong, 16);
608	} else {
609		memcpy(out, cart->titleShort, 11);
610	}
611}
612
613void GBGetGameCode(struct GB* gb, char* out) {
614	memset(out, 0, 8);
615	const struct GBCartridge* cart = NULL;
616	if (gb->memory.rom) {
617		cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
618	}
619	if (gb->pristineRom) {
620		cart = (const struct GBCartridge*) &((uint8_t*) gb->pristineRom)[0x100];
621	}
622	if (!cart) {
623		return;
624	}
625	if (cart->cgb == 0xC0) {
626		memcpy(out, "CGB-????", 8);
627	} else {
628		memcpy(out, "DMG-????", 8);
629	}
630	if (cart->oldLicensee == 0x33) {
631		memcpy(&out[4], cart->maker, 4);
632	}
633}
634
635void GBFrameEnded(struct GB* gb) {
636	GBSramClean(gb, gb->video.frameCounter);
637
638	if (gb->cpu->components && gb->cpu->components[CPU_COMPONENT_CHEAT_DEVICE]) {
639		struct mCheatDevice* device = (struct mCheatDevice*) gb->cpu->components[CPU_COMPONENT_CHEAT_DEVICE];
640		size_t i;
641		for (i = 0; i < mCheatSetsSize(&device->cheats); ++i) {
642			struct mCheatSet* cheats = *mCheatSetsGetPointer(&device->cheats, i);
643			mCheatRefresh(device, cheats);
644		}
645	}
646}