src/arm/debugger/cli-debugger.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 <mgba/internal/arm/debugger/cli-debugger.h>
7
8#include <mgba/core/core.h>
9#include <mgba/internal/arm/debugger/debugger.h>
10#include <mgba/internal/arm/debugger/memory-debugger.h>
11#include <mgba/internal/arm/decoder.h>
12#include <mgba/internal/debugger/cli-debugger.h>
13
14static void _printStatus(struct CLIDebuggerSystem*);
15
16static void _disassembleArm(struct CLIDebugger*, struct CLIDebugVector*);
17static void _disassembleThumb(struct CLIDebugger*, struct CLIDebugVector*);
18static void _setBreakpointARM(struct CLIDebugger*, struct CLIDebugVector*);
19static void _setBreakpointThumb(struct CLIDebugger*, struct CLIDebugVector*);
20static void _writeRegister(struct CLIDebugger*, struct CLIDebugVector*);
21
22static void _disassembleMode(struct CLIDebugger*, struct CLIDebugVector*, enum ExecutionMode mode);
23static uint32_t _printLine(struct CLIDebugger* debugger, uint32_t address, enum ExecutionMode mode);
24
25static struct CLIDebuggerCommandSummary _armCommands[] = {
26 { "b/a", _setBreakpointARM, CLIDVParse, "Set a software breakpoint as ARM" },
27 { "b/t", _setBreakpointThumb, CLIDVParse, "Set a software breakpoint as Thumb" },
28 { "break/a", _setBreakpointARM, CLIDVParse, "Set a software breakpoint as ARM" },
29 { "break/t", _setBreakpointThumb, CLIDVParse, "Set a software breakpoint as Thumb" },
30 { "dis/a", _disassembleArm, CLIDVParse, "Disassemble instructions as ARM" },
31 { "dis/t", _disassembleThumb, CLIDVParse, "Disassemble instructions as Thumb" },
32 { "disasm/a", _disassembleArm, CLIDVParse, "Disassemble instructions as ARM" },
33 { "disasm/t", _disassembleThumb, CLIDVParse, "Disassemble instructions as Thumb" },
34 { "disassemble/a", _disassembleArm, CLIDVParse, "Disassemble instructions as ARM" },
35 { "disassemble/t", _disassembleThumb, CLIDVParse, "Disassemble instructions as Thumb" },
36 { "w/r", _writeRegister, CLIDVParse, "Write a register" },
37 { 0, 0, 0, 0 }
38};
39
40static inline void _printPSR(struct CLIDebuggerBackend* be, ARMPSR psr) {
41 be->printf(be, "%08X [%c%c%c%c%c%c%c]\n", psr,
42 ARMPSRIsN(psr) ? 'N' : '-',
43 ARMPSRIsZ(psr) ? 'Z' : '-',
44 ARMPSRIsC(psr) ? 'C' : '-',
45 ARMPSRIsV(psr) ? 'V' : '-',
46 ARMPSRIsI(psr) ? 'I' : '-',
47 ARMPSRIsF(psr) ? 'F' : '-',
48 ARMPSRIsT(psr) ? 'T' : '-');
49}
50
51static void _disassemble(struct CLIDebuggerSystem* debugger, struct CLIDebugVector* dv) {
52 struct ARMCore* cpu = debugger->p->d.core->cpu;
53 _disassembleMode(debugger->p, dv, cpu->executionMode);
54}
55
56static void _disassembleArm(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
57 _disassembleMode(debugger, dv, MODE_ARM);
58}
59
60static void _disassembleThumb(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
61 _disassembleMode(debugger, dv, MODE_THUMB);
62}
63
64static void _disassembleMode(struct CLIDebugger* debugger, struct CLIDebugVector* dv, enum ExecutionMode mode) {
65 struct ARMCore* cpu = debugger->d.core->cpu;
66 uint32_t address;
67 int size;
68 int wordSize;
69
70 if (mode == MODE_ARM) {
71 wordSize = WORD_SIZE_ARM;
72 } else {
73 wordSize = WORD_SIZE_THUMB;
74 }
75
76 if (!dv || dv->type != CLIDV_INT_TYPE) {
77 address = cpu->gprs[ARM_PC] - wordSize;
78 } else {
79 address = dv->intValue;
80 dv = dv->next;
81 }
82
83 if (!dv || dv->type != CLIDV_INT_TYPE) {
84 size = 1;
85 } else {
86 size = dv->intValue;
87 dv = dv->next; // TODO: Check for excess args
88 }
89
90 int i;
91 for (i = 0; i < size; ++i) {
92 address += _printLine(debugger, address, mode);
93 }
94}
95
96static inline uint32_t _printLine(struct CLIDebugger* debugger, uint32_t address, enum ExecutionMode mode) {
97 struct CLIDebuggerBackend* be = debugger->backend;
98 char disassembly[48];
99 struct ARMInstructionInfo info;
100 be->printf(be, "%08X: ", address);
101 if (mode == MODE_ARM) {
102 uint32_t instruction = debugger->d.core->busRead32(debugger->d.core, address);
103 ARMDecodeARM(instruction, &info);
104 ARMDisassemble(&info, address + WORD_SIZE_ARM * 2, disassembly, sizeof(disassembly));
105 be->printf(be, "%08X\t%s\n", instruction, disassembly);
106 return WORD_SIZE_ARM;
107 } else {
108 struct ARMInstructionInfo info2;
109 struct ARMInstructionInfo combined;
110 uint16_t instruction = debugger->d.core->busRead16(debugger->d.core, address);
111 uint16_t instruction2 = debugger->d.core->busRead16(debugger->d.core, address + WORD_SIZE_THUMB);
112 ARMDecodeThumb(instruction, &info);
113 ARMDecodeThumb(instruction2, &info2);
114 if (ARMDecodeThumbCombine(&info, &info2, &combined)) {
115 ARMDisassemble(&combined, address + WORD_SIZE_THUMB * 2, disassembly, sizeof(disassembly));
116 be->printf(be, "%04X %04X\t%s\n", instruction, instruction2, disassembly);
117 return WORD_SIZE_THUMB * 2;
118 } else {
119 ARMDisassemble(&info, address + WORD_SIZE_THUMB * 2, disassembly, sizeof(disassembly));
120 be->printf(be, "%04X \t%s\n", instruction, disassembly);
121 return WORD_SIZE_THUMB;
122 }
123 }
124}
125
126static void _printStatus(struct CLIDebuggerSystem* debugger) {
127 struct CLIDebuggerBackend* be = debugger->p->backend;
128 struct ARMCore* cpu = debugger->p->d.core->cpu;
129 int r;
130 for (r = 0; r < 4; ++r) {
131 be->printf(be, "%08X %08X %08X %08X\n",
132 cpu->gprs[r << 2],
133 cpu->gprs[(r << 2) + 1],
134 cpu->gprs[(r << 2) + 2],
135 cpu->gprs[(r << 2) + 3]);
136 }
137 _printPSR(be, cpu->cpsr);
138 int instructionLength;
139 enum ExecutionMode mode = ARMPSRIsT(cpu->cpsr);
140 if (mode == MODE_ARM) {
141 instructionLength = WORD_SIZE_ARM;
142 } else {
143 instructionLength = WORD_SIZE_THUMB;
144 }
145 _printLine(debugger->p, cpu->gprs[ARM_PC] - instructionLength, mode);
146}
147
148static void _writeRegister(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
149 struct CLIDebuggerBackend* be = debugger->backend;
150 struct ARMCore* cpu = debugger->d.core->cpu;
151 if (!dv || dv->type != CLIDV_INT_TYPE) {
152 be->printf(be, "%s\n", ERROR_MISSING_ARGS);
153 return;
154 }
155 if (!dv->next || dv->next->type != CLIDV_INT_TYPE) {
156 be->printf(be, "%s\n", ERROR_MISSING_ARGS);
157 return;
158 }
159 uint32_t regid = dv->intValue;
160 uint32_t value = dv->next->intValue;
161 if (regid >= ARM_PC) {
162 return;
163 }
164 cpu->gprs[regid] = value;
165}
166
167static void _setBreakpointARM(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
168 struct CLIDebuggerBackend* be = debugger->backend;
169 if (!dv || dv->type != CLIDV_INT_TYPE) {
170 be->printf(be, "%s\n", ERROR_MISSING_ARGS);
171 return;
172 }
173 uint32_t address = dv->intValue;
174 ARMDebuggerSetSoftwareBreakpoint(debugger->d.platform, address, MODE_ARM);
175}
176
177static void _setBreakpointThumb(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
178 struct CLIDebuggerBackend* be = debugger->backend;
179 if (!dv || dv->type != CLIDV_INT_TYPE) {
180 be->printf(be, "%s\n", ERROR_MISSING_ARGS);
181 return;
182 }
183 uint32_t address = dv->intValue;
184 ARMDebuggerSetSoftwareBreakpoint(debugger->d.platform, address, MODE_THUMB);
185}
186
187static uint32_t _lookupPlatformIdentifier(struct CLIDebuggerSystem* debugger, const char* name, struct CLIDebugVector* dv) {
188 struct ARMCore* cpu = debugger->p->d.core->cpu;
189 if (strcmp(name, "sp") == 0) {
190 return cpu->gprs[ARM_SP];
191 }
192 if (strcmp(name, "lr") == 0) {
193 return cpu->gprs[ARM_LR];
194 }
195 if (strcmp(name, "pc") == 0) {
196 return cpu->gprs[ARM_PC];
197 }
198 if (strcmp(name, "cpsr") == 0) {
199 return cpu->cpsr;
200 }
201 // TODO: test if mode has SPSR
202 if (strcmp(name, "spsr") == 0) {
203 return cpu->spsr;
204 }
205 if (name[0] == 'r' && name[1] >= '0' && name[1] <= '9') {
206 int reg = atoi(&name[1]);
207 if (reg < 16) {
208 return cpu->gprs[reg];
209 }
210 }
211 dv->type = CLIDV_ERROR_TYPE;
212 return 0;
213}
214
215void ARMCLIDebuggerCreate(struct CLIDebuggerSystem* debugger) {
216 debugger->printStatus = _printStatus;
217 debugger->disassemble = _disassemble;
218 debugger->lookupPlatformIdentifier = _lookupPlatformIdentifier;
219 debugger->platformName = "ARM";
220 debugger->platformCommands = _armCommands;
221}