src/gba/supervisor/cli.c (view raw)
1/* Copyright (c) 2013-2015 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 "cli.h"
7
8#include "gba/io.h"
9#include "gba/serialize.h"
10#include "gba/supervisor/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_MANUAL, 0);
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