all repos — mgba @ 3f2454a85a4df1ea4f70abe1aaba19b6280788dd

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/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}