all repos — mgba @ 7f64f8cf3bee4b8bff476cda46c84a5008fb33f4

mGBA Game Boy Advance Emulator

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	char disassembly[64];
102	struct ARMInstructionInfo info;
103	be->printf(be, "%08X:  ", address);
104	if (mode == MODE_ARM) {
105		uint32_t instruction = debugger->d.core->busRead32(debugger->d.core, address);
106		ARMDecodeARM(instruction, &info);
107		ARMDisassemble(&info, address + WORD_SIZE_ARM * 2, disassembly, sizeof(disassembly));
108		be->printf(be, "%08X\t%s\n", instruction, disassembly);
109		return WORD_SIZE_ARM;
110	} else {
111		struct ARMInstructionInfo info2;
112		struct ARMInstructionInfo combined;
113		uint16_t instruction = debugger->d.core->busRead16(debugger->d.core, address);
114		uint16_t instruction2 = debugger->d.core->busRead16(debugger->d.core, address + WORD_SIZE_THUMB);
115		ARMDecodeThumb(instruction, &info);
116		ARMDecodeThumb(instruction2, &info2);
117		if (ARMDecodeThumbCombine(&info, &info2, &combined)) {
118			ARMDisassemble(&combined, address + WORD_SIZE_THUMB * 2, disassembly, sizeof(disassembly));
119			be->printf(be, "%04X %04X\t%s\n", instruction, instruction2, disassembly);
120			return WORD_SIZE_THUMB * 2;
121		} else {
122			ARMDisassemble(&info, address + WORD_SIZE_THUMB * 2, disassembly, sizeof(disassembly));
123			be->printf(be, "%04X     \t%s\n", instruction, disassembly);
124			return WORD_SIZE_THUMB;
125		}
126	}
127}
128
129static void _printStatus(struct CLIDebuggerSystem* debugger) {
130	struct CLIDebuggerBackend* be = debugger->p->backend;
131	struct ARMCore* cpu = debugger->p->d.core->cpu;
132	int r;
133	for (r = 0; r < 16; r += 4) {
134		be->printf(be, "%sr%i: %08X  %sr%i: %08X  %sr%i: %08X  %sr%i: %08X\n",
135		    r < 10 ? " " : "", r, cpu->gprs[r],
136		    r < 9 ? " " : "", r + 1, cpu->gprs[r + 1],
137		    r < 8 ? " " : "", r + 2, cpu->gprs[r + 2],
138		    r < 7 ? " " : "", r + 3, cpu->gprs[r + 3]);
139	}
140	be->printf(be, "cpsr: ");
141	_printPSR(be, cpu->cpsr);
142	be->printf(be, "Cycle: %" PRIu64 "\n", mTimingGlobalTime(debugger->p->d.core->timing));
143	int instructionLength;
144	enum ExecutionMode mode = cpu->cpsr.t;
145	if (mode == MODE_ARM) {
146		instructionLength = WORD_SIZE_ARM;
147	} else {
148		instructionLength = WORD_SIZE_THUMB;
149	}
150	_printLine(debugger->p, cpu->gprs[ARM_PC] - instructionLength, mode);
151}
152
153static void _setBreakpointARM(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
154	struct CLIDebuggerBackend* be = debugger->backend;
155	if (!dv || dv->type != CLIDV_INT_TYPE) {
156		be->printf(be, "%s", ERROR_MISSING_ARGS);
157		return;
158	}
159	uint32_t address = dv->intValue;
160	ssize_t id = ARMDebuggerSetSoftwareBreakpoint(debugger->d.platform, address, MODE_ARM);
161	if (id > 0) {
162		debugger->backend->printf(debugger->backend, INFO_BREAKPOINT_ADDED, id);
163	}
164}
165
166static void _setBreakpointThumb(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
167	struct CLIDebuggerBackend* be = debugger->backend;
168	if (!dv || dv->type != CLIDV_INT_TYPE) {
169		be->printf(be, "%s", ERROR_MISSING_ARGS);
170		return;
171	}
172	uint32_t address = dv->intValue;
173	ssize_t id = ARMDebuggerSetSoftwareBreakpoint(debugger->d.platform, address, MODE_THUMB);
174	if (id > 0) {
175		debugger->backend->printf(debugger->backend, INFO_BREAKPOINT_ADDED, id);
176	}
177}
178
179void ARMCLIDebuggerCreate(struct CLIDebuggerSystem* debugger) {
180	debugger->printStatus = _printStatus;
181	debugger->disassemble = _disassemble;
182	debugger->platformName = "ARM";
183	debugger->platformCommands = _armCommands;
184	debugger->platformCommandAliases = _armCommandAliases;
185}