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