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/core/timing.h>
10#include <mgba/internal/arm/debugger/debugger.h>
11#include <mgba/internal/arm/debugger/memory-debugger.h>
12#include <mgba/internal/arm/decoder.h>
13#include <mgba/internal/debugger/cli-debugger.h>
14
15static void _printStatus(struct CLIDebuggerSystem*);
16
17static void _disassembleArm(struct CLIDebugger*, struct CLIDebugVector*);
18static void _disassembleThumb(struct CLIDebugger*, struct CLIDebugVector*);
19static void _setBreakpointARM(struct CLIDebugger*, struct CLIDebugVector*);
20static void _setBreakpointThumb(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 { "break/a", _setBreakpointARM, "I", "Set a software breakpoint as ARM" },
27 { "break/t", _setBreakpointThumb, "I", "Set a software breakpoint as Thumb" },
28 { "disassemble/a", _disassembleArm, "Ii", "Disassemble instructions as ARM" },
29 { "disassemble/t", _disassembleThumb, "Ii", "Disassemble instructions as Thumb" },
30 { 0, 0, 0, 0 }
31};
32
33static struct CLIDebuggerCommandAlias _armCommandAliases[] = {
34 { "b/a", "break/a" },
35 { "b/t", "break/t" },
36 { "dis/a", "disassemble/a" },
37 { "dis/t", "disassemble/t" },
38 { "disasm/a", "disassemble/a" },
39 { "disasm/t", "disassemble/t" },
40 { 0, 0 }
41};
42
43static inline void _printPSR(struct CLIDebuggerBackend* be, union PSR psr) {
44 be->printf(be, "%08X [%c%c%c%c%c%c%c]\n", psr.packed,
45 psr.n ? 'N' : '-',
46 psr.z ? 'Z' : '-',
47 psr.c ? 'C' : '-',
48 psr.v ? 'V' : '-',
49 psr.i ? 'I' : '-',
50 psr.f ? 'F' : '-',
51 psr.t ? 'T' : '-');
52}
53
54static void _disassemble(struct CLIDebuggerSystem* debugger, struct CLIDebugVector* dv) {
55 struct ARMCore* cpu = debugger->p->d.core->cpu;
56 _disassembleMode(debugger->p, dv, cpu->executionMode);
57}
58
59static void _disassembleArm(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
60 _disassembleMode(debugger, dv, MODE_ARM);
61}
62
63static void _disassembleThumb(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
64 _disassembleMode(debugger, dv, MODE_THUMB);
65}
66
67static void _disassembleMode(struct CLIDebugger* debugger, struct CLIDebugVector* dv, enum ExecutionMode mode) {
68 struct ARMCore* cpu = debugger->d.core->cpu;
69 uint32_t address;
70 int size;
71 int wordSize;
72
73 if (mode == MODE_ARM) {
74 wordSize = WORD_SIZE_ARM;
75 } else {
76 wordSize = WORD_SIZE_THUMB;
77 }
78
79 if (!dv || dv->type != CLIDV_INT_TYPE) {
80 address = cpu->gprs[ARM_PC] - wordSize;
81 } else {
82 address = dv->intValue;
83 dv = dv->next;
84 }
85
86 if (!dv || dv->type != CLIDV_INT_TYPE) {
87 size = 1;
88 } else {
89 size = dv->intValue;
90 // TODO: Check for excess args
91 }
92
93 int i;
94 for (i = 0; i < size; ++i) {
95 address += _printLine(debugger, address, mode);
96 }
97}
98
99static inline uint32_t _printLine(struct CLIDebugger* debugger, uint32_t address, enum ExecutionMode mode) {
100 struct CLIDebuggerBackend* be = debugger->backend;
101 struct mCore* core = debugger->d.core;
102 char disassembly[64];
103 struct ARMInstructionInfo info;
104 be->printf(be, "%08X: ", address);
105 if (mode == MODE_ARM) {
106 uint32_t instruction = core->busRead32(core, address);
107 ARMDecodeARM(instruction, &info);
108 ARMDisassemble(&info, core->cpu, core->symbolTable, address + WORD_SIZE_ARM * 2, disassembly, sizeof(disassembly));
109 be->printf(be, "%08X\t%s\n", instruction, disassembly);
110 return WORD_SIZE_ARM;
111 } else {
112 struct ARMInstructionInfo info2;
113 struct ARMInstructionInfo combined;
114 uint16_t instruction = core->busRead16(core, address);
115 uint16_t instruction2 = core->busRead16(core, address + WORD_SIZE_THUMB);
116 ARMDecodeThumb(instruction, &info);
117 ARMDecodeThumb(instruction2, &info2);
118 if (ARMDecodeThumbCombine(&info, &info2, &combined)) {
119 ARMDisassemble(&combined, core->cpu, core->symbolTable, address + WORD_SIZE_THUMB * 2, disassembly, sizeof(disassembly));
120 be->printf(be, "%04X %04X\t%s\n", instruction, instruction2, disassembly);
121 return WORD_SIZE_THUMB * 2;
122 } else {
123 ARMDisassemble(&info, core->cpu, core->symbolTable, address + WORD_SIZE_THUMB * 2, disassembly, sizeof(disassembly));
124 be->printf(be, "%04X \t%s\n", instruction, disassembly);
125 return WORD_SIZE_THUMB;
126 }
127 }
128}
129
130static void _printStatus(struct CLIDebuggerSystem* debugger) {
131 struct CLIDebuggerBackend* be = debugger->p->backend;
132 struct ARMCore* cpu = debugger->p->d.core->cpu;
133 int r;
134 for (r = 0; r < 16; r += 4) {
135 be->printf(be, "%sr%i: %08X %sr%i: %08X %sr%i: %08X %sr%i: %08X\n",
136 r < 10 ? " " : "", r, cpu->gprs[r],
137 r < 9 ? " " : "", r + 1, cpu->gprs[r + 1],
138 r < 8 ? " " : "", r + 2, cpu->gprs[r + 2],
139 r < 7 ? " " : "", r + 3, cpu->gprs[r + 3]);
140 }
141 be->printf(be, "cpsr: ");
142 _printPSR(be, cpu->cpsr);
143 be->printf(be, "Cycle: %" PRIu64 "\n", mTimingGlobalTime(debugger->p->d.core->timing));
144 int instructionLength;
145 enum ExecutionMode mode = cpu->cpsr.t;
146 if (mode == MODE_ARM) {
147 instructionLength = WORD_SIZE_ARM;
148 } else {
149 instructionLength = WORD_SIZE_THUMB;
150 }
151 _printLine(debugger->p, cpu->gprs[ARM_PC] - instructionLength, mode);
152}
153
154static void _setBreakpointARM(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
155 struct CLIDebuggerBackend* be = debugger->backend;
156 if (!dv || dv->type != CLIDV_INT_TYPE) {
157 be->printf(be, "%s", ERROR_MISSING_ARGS);
158 return;
159 }
160 uint32_t address = dv->intValue;
161 ssize_t id = ARMDebuggerSetSoftwareBreakpoint(debugger->d.platform, address, MODE_ARM);
162 if (id > 0) {
163 debugger->backend->printf(debugger->backend, INFO_BREAKPOINT_ADDED, id);
164 }
165}
166
167static void _setBreakpointThumb(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", ERROR_MISSING_ARGS);
171 return;
172 }
173 uint32_t address = dv->intValue;
174 ssize_t id = ARMDebuggerSetSoftwareBreakpoint(debugger->d.platform, address, MODE_THUMB);
175 if (id > 0) {
176 debugger->backend->printf(debugger->backend, INFO_BREAKPOINT_ADDED, id);
177 }
178}
179
180void ARMCLIDebuggerCreate(struct CLIDebuggerSystem* debugger) {
181 debugger->printStatus = _printStatus;
182 debugger->disassemble = _disassemble;
183 debugger->platformName = "ARM";
184 debugger->platformCommands = _armCommands;
185 debugger->platformCommandAliases = _armCommandAliases;
186}