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