all repos — mgba @ ccf584238b8518a8dfa52a45dcc4d0caac5a2ec6

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