all repos — mgba @ 19758d711528b15a255b353d1ef740d516b8685e

mGBA Game Boy Advance Emulator

src/gba/gba-cli.c (view raw)

  1/* Copyright (c) 2013-2014 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 "gba-cli.h"
  7
  8#include "gba-io.h"
  9#include "gba-serialize.h"
 10#include "gba-thread.h"
 11
 12#ifdef USE_CLI_DEBUGGER
 13
 14static const char* ERROR_MISSING_ARGS = "Arguments missing"; // TODO: share
 15
 16static void _GBACLIDebuggerInit(struct CLIDebuggerSystem*);
 17static void _GBACLIDebuggerDeinit(struct CLIDebuggerSystem*);
 18static bool _GBACLIDebuggerCustom(struct CLIDebuggerSystem*);
 19static uint32_t _GBACLIDebuggerLookupIdentifier(struct CLIDebuggerSystem*, const char* name, struct CLIDebugVector* dv);
 20
 21static void _frame(struct CLIDebugger*, struct CLIDebugVector*);
 22static void _load(struct CLIDebugger*, struct CLIDebugVector*);
 23static void _rewind(struct CLIDebugger*, struct CLIDebugVector*);
 24static void _save(struct CLIDebugger*, struct CLIDebugVector*);
 25
 26struct CLIDebuggerCommandSummary _GBACLIDebuggerCommands[] = {
 27	{ "frame", _frame, 0, "Frame advance" },
 28	{ "load", _load, CLIDVParse, "Load a savestate" },
 29	{ "rewind", _rewind, CLIDVParse, "Rewind the emulation a number of recorded intervals" },
 30	{ "save", _save, CLIDVParse, "Save a savestate" },
 31	{ 0, 0, 0, 0 }
 32};
 33#endif
 34
 35struct GBACLIDebugger* GBACLIDebuggerCreate(struct GBAThread* context) {
 36	struct GBACLIDebugger* debugger = malloc(sizeof(struct GBACLIDebugger));
 37#ifdef USE_CLI_DEBUGGER
 38	debugger->d.init = _GBACLIDebuggerInit;
 39	debugger->d.deinit = _GBACLIDebuggerDeinit;
 40	debugger->d.custom = _GBACLIDebuggerCustom;
 41	debugger->d.lookupIdentifier = _GBACLIDebuggerLookupIdentifier;
 42
 43	debugger->d.name = "Game Boy Advance";
 44	debugger->d.commands = _GBACLIDebuggerCommands;
 45
 46	debugger->context = context;
 47#else
 48	UNUSED(context);
 49#endif
 50
 51	return debugger;
 52}
 53
 54#ifdef USE_CLI_DEBUGGER
 55static void _GBACLIDebuggerInit(struct CLIDebuggerSystem* debugger) {
 56	struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger;
 57
 58	gbaDebugger->frameAdvance = false;
 59}
 60
 61static void _GBACLIDebuggerDeinit(struct CLIDebuggerSystem* debugger) {
 62	UNUSED(debugger);
 63}
 64
 65static bool _GBACLIDebuggerCustom(struct CLIDebuggerSystem* debugger) {
 66	struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger;
 67
 68	if (gbaDebugger->frameAdvance) {
 69		if (!gbaDebugger->inVblank && GBARegisterDISPSTATIsInVblank(gbaDebugger->context->gba->memory.io[REG_DISPSTAT >> 1])) {
 70			ARMDebuggerEnter(&gbaDebugger->d.p->d, DEBUGGER_ENTER_BREAKPOINT);
 71			gbaDebugger->frameAdvance = false;
 72			return false;
 73		}
 74		gbaDebugger->inVblank = GBARegisterDISPSTATGetInVblank(gbaDebugger->context->gba->memory.io[REG_DISPSTAT >> 1]);
 75		return true;
 76	}
 77	return false;
 78}
 79
 80static uint32_t _GBACLIDebuggerLookupIdentifier(struct CLIDebuggerSystem* debugger, const char* name, struct CLIDebugVector* dv) {
 81	struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger;
 82	int i;
 83	for (i = 0; i < REG_MAX; i += 2) {
 84		const char* reg = GBAIORegisterNames[i >> 1];
 85		if (reg && strcasecmp(reg, name) == 0) {
 86			return GBALoad16(gbaDebugger->context->gba->cpu, BASE_IO | i, 0);
 87		}
 88	}
 89	dv->type = CLIDV_ERROR_TYPE;
 90	return 0;
 91}
 92
 93static void _frame(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
 94	UNUSED(dv);
 95	debugger->d.state = DEBUGGER_CUSTOM;
 96
 97	struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system;
 98	gbaDebugger->frameAdvance = true;
 99	gbaDebugger->inVblank = GBARegisterDISPSTATGetInVblank(gbaDebugger->context->gba->memory.io[REG_DISPSTAT >> 1]);
100}
101
102static void _load(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
103	if (!dv || dv->type != CLIDV_INT_TYPE) {
104		printf("%s\n", ERROR_MISSING_ARGS);
105		return;
106	}
107
108	int state = dv->intValue;
109	if (state < 1 || state > 9) {
110		printf("State %u out of range", state);
111	}
112
113	struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system;
114
115	GBALoadState(gbaDebugger->context, gbaDebugger->context->stateDir, dv->intValue);
116}
117
118static void _rewind(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
119	struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system;
120	if (!dv) {
121		GBARewindAll(gbaDebugger->context);
122	} else if (dv->type != CLIDV_INT_TYPE) {
123		printf("%s\n", ERROR_MISSING_ARGS);
124	} else {
125		GBARewind(gbaDebugger->context, dv->intValue);
126	}
127}
128
129static void _save(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
130	if (!dv || dv->type != CLIDV_INT_TYPE) {
131		printf("%s\n", ERROR_MISSING_ARGS);
132		return;
133	}
134
135	int state = dv->intValue;
136	if (state < 1 || state > 9) {
137		printf("State %u out of range", state);
138	}
139
140	struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system;
141
142	GBASaveState(gbaDebugger->context, gbaDebugger->context->stateDir, dv->intValue, true);
143}
144#endif