all repos — mgba @ bcad1494541fd163edde5fd588f5399fdb43e0d3

mGBA Game Boy Advance Emulator

src/arm/decoder.c (view raw)

  1/* Copyright (c) 2013-2014 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/decoder.h>
  7
  8#include <mgba/internal/arm/decoder-inlines.h>
  9#include <mgba/internal/debugger/symbols.h>
 10#include <mgba-util/string.h>
 11
 12#ifdef USE_DEBUGGERS
 13#define ADVANCE(AMOUNT) \
 14	if (AMOUNT >= blen) { \
 15		buffer[blen - 1] = '\0'; \
 16		return total; \
 17	} \
 18	total += AMOUNT; \
 19	buffer += AMOUNT; \
 20	blen -= AMOUNT;
 21
 22static int _decodeRegister(int reg, char* buffer, int blen);
 23static int _decodeRegisterList(int list, char* buffer, int blen);
 24static int _decodePSR(int bits, char* buffer, int blen);
 25static int _decodePCRelative(uint32_t address, const struct mDebuggerSymbols* symbols, uint32_t pc, bool thumbBranch, char* buffer, int blen);
 26static int _decodeMemory(struct ARMMemoryAccess memory, struct ARMCore* cpu, const struct mDebuggerSymbols* symbols, int pc, char* buffer, int blen);
 27static int _decodeShift(union ARMOperand operand, bool reg, char* buffer, int blen);
 28
 29static const char* _armConditions[] = {
 30	"eq",
 31	"ne",
 32	"cs",
 33	"cc",
 34	"mi",
 35	"pl",
 36	"vs",
 37	"vc",
 38	"hi",
 39	"ls",
 40	"ge",
 41	"lt",
 42	"gt",
 43	"le",
 44	"al",
 45	"nv"
 46};
 47
 48static int _decodeRegister(int reg, char* buffer, int blen) {
 49	switch (reg) {
 50	case ARM_SP:
 51		strlcpy(buffer, "sp", blen);
 52		return 2;
 53	case ARM_LR:
 54		strlcpy(buffer, "lr", blen);
 55		return 2;
 56	case ARM_PC:
 57		strlcpy(buffer, "pc", blen);
 58		return 2;
 59	case ARM_CPSR:
 60		strlcpy(buffer, "cpsr", blen);
 61		return 4;
 62	case ARM_SPSR:
 63		strlcpy(buffer, "spsr", blen);
 64		return 4;
 65	default:
 66		return snprintf(buffer, blen, "r%i", reg);
 67	}
 68}
 69
 70static int _decodeRegisterList(int list, char* buffer, int blen) {
 71	if (blen <= 0) {
 72		return 0;
 73	}
 74	int total = 0;
 75	strlcpy(buffer, "{", blen);
 76	ADVANCE(1);
 77	int i;
 78	int start = -1;
 79	int end = -1;
 80	int written;
 81	for (i = 0; i <= ARM_PC; ++i) {
 82		if (list & 1) {
 83			if (start < 0) {
 84				start = i;
 85				end = i;
 86			} else if (end + 1 == i) {
 87				end = i;
 88			} else {
 89				if (end > start) {
 90					written = _decodeRegister(start, buffer, blen);
 91					ADVANCE(written);
 92					strlcpy(buffer, "-", blen);
 93					ADVANCE(1);
 94				}
 95				written = _decodeRegister(end, buffer, blen);
 96				ADVANCE(written);
 97				strlcpy(buffer, ",", blen);
 98				ADVANCE(1);
 99				start = i;
100				end = i;
101			}
102		}
103		list >>= 1;
104	}
105	if (start >= 0) {
106		if (end > start) {
107			written = _decodeRegister(start, buffer, blen);
108			ADVANCE(written);
109			strlcpy(buffer, "-", blen);
110			ADVANCE(1);
111		}
112		written = _decodeRegister(end, buffer, blen);
113		ADVANCE(written);
114	}
115	strlcpy(buffer, "}", blen);
116	ADVANCE(1);
117	return total;
118}
119
120static int _decodePSR(int psrBits, char* buffer, int blen) {
121	if (!psrBits) {
122		return 0;
123	}
124	int total = 0;
125	strlcpy(buffer, "_", blen);
126	ADVANCE(1);
127	if (psrBits & ARM_PSR_C) {
128		strlcpy(buffer, "c", blen);
129		ADVANCE(1);
130	}
131	if (psrBits & ARM_PSR_X) {
132		strlcpy(buffer, "x", blen);
133		ADVANCE(1);
134	}
135	if (psrBits & ARM_PSR_S) {
136		strlcpy(buffer, "s", blen);
137		ADVANCE(1);
138	}
139	if (psrBits & ARM_PSR_F) {
140		strlcpy(buffer, "f", blen);
141		ADVANCE(1);
142	}
143	return total;
144}
145
146static int _decodePCRelative(uint32_t address, const struct mDebuggerSymbols* symbols, uint32_t pc, bool thumbBranch, char* buffer, int blen) {
147	address += pc;
148	const char* label = NULL;
149	if (symbols) {
150		label = mDebuggerSymbolReverseLookup(symbols, address, -1);
151		if (!label && thumbBranch) {
152			label = mDebuggerSymbolReverseLookup(symbols, address | 1, -1);
153		}
154	}
155	if (label) {
156		return strlcpy(buffer, label, blen);
157	} else {
158		return snprintf(buffer, blen, "0x%08X", address);
159	}
160}
161
162static int _decodeMemory(struct ARMMemoryAccess memory, struct ARMCore* cpu, const struct mDebuggerSymbols* symbols, int pc, char* buffer, int blen) {
163	if (blen <= 1) {
164		return 0;
165	}
166	int total = 0;
167	bool elideClose = false;
168	char comment[64];
169	int written;
170	comment[0] = '\0';
171	if (memory.format & ARM_MEMORY_REGISTER_BASE) {
172		if (memory.baseReg == ARM_PC && memory.format & ARM_MEMORY_IMMEDIATE_OFFSET) {
173			uint32_t addrBase = memory.format & ARM_MEMORY_OFFSET_SUBTRACT ? -memory.offset.immediate : memory.offset.immediate;
174			if (!cpu || memory.format & ARM_MEMORY_STORE) {
175				strlcpy(buffer, "[", blen);
176				ADVANCE(1);
177				written = _decodePCRelative(addrBase, symbols, pc & 0xFFFFFFFC, false, buffer, blen);
178				ADVANCE(written);
179			} else {
180				uint32_t value;
181				_decodePCRelative(addrBase, symbols, pc & 0xFFFFFFFC, false, comment, sizeof(comment));
182				addrBase += pc & 0xFFFFFFFC; // Thumb does not have PC-relative LDRH/LDRB
183				switch (memory.width & 7) {
184				case 1:
185					value = cpu->memory.load8(cpu, addrBase, NULL);
186					break;
187				case 2:
188					value = cpu->memory.load16(cpu, addrBase, NULL);
189					break;
190				case 4:
191					value = cpu->memory.load32(cpu, addrBase, NULL);
192					break;
193				}
194				const char* label = NULL;
195				if (symbols) {
196					label = mDebuggerSymbolReverseLookup(symbols, value, -1);
197				}
198				if (label) {
199					written = snprintf(buffer, blen, "=%s", label);
200				} else {
201					written = snprintf(buffer, blen, "=0x%08X", value);
202				}
203				ADVANCE(written);
204				elideClose = true;
205			}
206		} else {
207			strlcpy(buffer, "[", blen);
208			ADVANCE(1);
209			written = _decodeRegister(memory.baseReg, buffer, blen);
210			ADVANCE(written);
211			if (memory.format & (ARM_MEMORY_REGISTER_OFFSET | ARM_MEMORY_IMMEDIATE_OFFSET) && !(memory.format & ARM_MEMORY_POST_INCREMENT)) {
212				strlcpy(buffer, ", ", blen);
213				ADVANCE(2);
214			}
215		}
216	} else {
217		strlcpy(buffer, "[", blen);
218		ADVANCE(1);
219	}
220	if (memory.format & ARM_MEMORY_POST_INCREMENT) {
221		strlcpy(buffer, "], ", blen);
222		ADVANCE(3);
223		elideClose = true;
224	}
225	if (memory.format & ARM_MEMORY_IMMEDIATE_OFFSET && memory.baseReg != ARM_PC) {
226		if (memory.format & ARM_MEMORY_OFFSET_SUBTRACT) {
227			written = snprintf(buffer, blen, "#-%i", memory.offset.immediate);
228			ADVANCE(written);
229		} else {
230			written = snprintf(buffer, blen, "#%i", memory.offset.immediate);
231			ADVANCE(written);
232		}
233	} else if (memory.format & ARM_MEMORY_REGISTER_OFFSET) {
234		if (memory.format & ARM_MEMORY_OFFSET_SUBTRACT) {
235			strlcpy(buffer, "-", blen);
236			ADVANCE(1);
237		}
238		written = _decodeRegister(memory.offset.reg, buffer, blen);
239		ADVANCE(written);
240	}
241	if (memory.format & ARM_MEMORY_SHIFTED_OFFSET) {
242		written = _decodeShift(memory.offset, false, buffer, blen);
243		ADVANCE(written);
244	}
245
246	if (!elideClose) {
247		strlcpy(buffer, "]", blen);
248		ADVANCE(1);
249	}
250	if ((memory.format & (ARM_MEMORY_PRE_INCREMENT | ARM_MEMORY_WRITEBACK)) == (ARM_MEMORY_PRE_INCREMENT | ARM_MEMORY_WRITEBACK)) {
251		strlcpy(buffer, "!", blen);
252		ADVANCE(1);
253	}
254	if (comment[0]) {
255		written = snprintf(buffer, blen, "  @ %s", comment);
256		ADVANCE(written);
257	}
258	return total;
259}
260
261static int _decodeShift(union ARMOperand op, bool reg, char* buffer, int blen) {
262	if (blen <= 1) {
263		return 0;
264	}
265	int total = 0;
266	strlcpy(buffer, ", ", blen);
267	ADVANCE(2);
268	int written;
269	switch (op.shifterOp) {
270	case ARM_SHIFT_LSL:
271		strlcpy(buffer, "lsl ", blen);
272		ADVANCE(4);
273		break;
274	case ARM_SHIFT_LSR:
275		strlcpy(buffer, "lsr ", blen);
276		ADVANCE(4);
277		break;
278	case ARM_SHIFT_ASR:
279		strlcpy(buffer, "asr ", blen);
280		ADVANCE(4);
281		break;
282	case ARM_SHIFT_ROR:
283		strlcpy(buffer, "ror ", blen);
284		ADVANCE(4);
285		break;
286	case ARM_SHIFT_RRX:
287		strlcpy(buffer, "rrx", blen);
288		ADVANCE(3);
289		return total;
290	}
291	if (!reg) {
292		written = snprintf(buffer, blen, "#%i", op.shifterImm);
293	} else {
294		written = _decodeRegister(op.shifterReg, buffer, blen);
295	}
296	ADVANCE(written);
297	return total;
298}
299
300static const char* _armMnemonicStrings[] = {
301	"ill",
302	"adc",
303	"add",
304	"and",
305	"asr",
306	"b",
307	"bic",
308	"bkpt",
309	"bl",
310	"bx",
311	"cmn",
312	"cmp",
313	"eor",
314	"ldm",
315	"ldr",
316	"lsl",
317	"lsr",
318	"mla",
319	"mov",
320	"mrs",
321	"msr",
322	"mul",
323	"mvn",
324	"neg",
325	"orr",
326	"ror",
327	"rsb",
328	"rsc",
329	"sbc",
330	"smlal",
331	"smull",
332	"stm",
333	"str",
334	"sub",
335	"swi",
336	"swp",
337	"teq",
338	"tst",
339	"umlal",
340	"umull",
341
342	"ill"
343};
344
345static const char* _armDirectionStrings[] = {
346	"da",
347	"ia",
348	"db",
349	"ib"
350};
351
352static const char* _armAccessTypeStrings[] = {
353	"",
354	"b",
355	"h",
356	"",
357	"",
358	"",
359	"",
360	"",
361
362	"",
363	"sb",
364	"sh",
365	"",
366	"",
367	"",
368	"",
369	"",
370
371	"",
372	"bt",
373	"",
374	"",
375	"t",
376	"",
377	"",
378	""
379};
380
381int ARMDisassemble(struct ARMInstructionInfo* info, struct ARMCore* cpu, const struct mDebuggerSymbols* symbols, uint32_t pc, char* buffer, int blen) {
382	const char* mnemonic = _armMnemonicStrings[info->mnemonic];
383	int written;
384	int total = 0;
385	const char* cond = "";
386	if (info->condition != ARM_CONDITION_AL && info->condition < ARM_CONDITION_NV) {
387		cond = _armConditions[info->condition];
388	}
389	const char* flags = "";
390	switch (info->mnemonic) {
391	case ARM_MN_LDM:
392	case ARM_MN_STM:
393		flags = _armDirectionStrings[MEMORY_FORMAT_TO_DIRECTION(info->memory.format)];
394		break;
395	case ARM_MN_LDR:
396	case ARM_MN_STR:
397	case ARM_MN_SWP:
398		flags = _armAccessTypeStrings[info->memory.width];
399		break;
400	case ARM_MN_ADD:
401	case ARM_MN_ADC:
402	case ARM_MN_AND:
403	case ARM_MN_ASR:
404	case ARM_MN_BIC:
405	case ARM_MN_EOR:
406	case ARM_MN_LSL:
407	case ARM_MN_LSR:
408	case ARM_MN_MLA:
409	case ARM_MN_MOV:
410	case ARM_MN_MUL:
411	case ARM_MN_MVN:
412	case ARM_MN_ORR:
413	case ARM_MN_ROR:
414	case ARM_MN_RSB:
415	case ARM_MN_RSC:
416	case ARM_MN_SBC:
417	case ARM_MN_SMLAL:
418	case ARM_MN_SMULL:
419	case ARM_MN_SUB:
420	case ARM_MN_UMLAL:
421	case ARM_MN_UMULL:
422		if (info->affectsCPSR && info->execMode == MODE_ARM) {
423			flags = "s";
424		}
425		break;
426	default:
427		break;
428	}
429	written = snprintf(buffer, blen, "%s%s%s ", mnemonic, cond, flags);
430	ADVANCE(written);
431
432	switch (info->mnemonic) {
433	case ARM_MN_LDM:
434	case ARM_MN_STM:
435		written = _decodeRegister(info->memory.baseReg, buffer, blen);
436		ADVANCE(written);
437		if (info->memory.format & ARM_MEMORY_WRITEBACK) {
438			strlcpy(buffer, "!", blen);
439			ADVANCE(1);
440		}
441		strlcpy(buffer, ", ", blen);
442		ADVANCE(2);
443		written = _decodeRegisterList(info->op1.immediate, buffer, blen);
444		ADVANCE(written);
445		if (info->memory.format & ARM_MEMORY_SPSR_SWAP) {
446			strlcpy(buffer, "^", blen);
447			ADVANCE(1);
448		}
449		break;
450	case ARM_MN_B:
451	case ARM_MN_BL:
452		if (info->operandFormat & ARM_OPERAND_IMMEDIATE_1) {
453			written = _decodePCRelative(info->op1.immediate, symbols, pc, true, buffer, blen);
454			ADVANCE(written);
455		}
456		break;
457	default:
458		if (info->operandFormat & ARM_OPERAND_IMMEDIATE_1) {
459			written = snprintf(buffer, blen, "#%i", info->op1.immediate);
460			ADVANCE(written);
461		} else if (info->operandFormat & ARM_OPERAND_MEMORY_1) {
462			written = _decodeMemory(info->memory, cpu, symbols, pc, buffer, blen);
463			ADVANCE(written);
464		} else if (info->operandFormat & ARM_OPERAND_REGISTER_1) {
465			written = _decodeRegister(info->op1.reg, buffer, blen);
466			ADVANCE(written);
467			if (info->op1.reg > ARM_PC) {
468				written = _decodePSR(info->op1.psrBits, buffer, blen);
469				ADVANCE(written);
470			}
471		}
472		if (info->operandFormat & ARM_OPERAND_SHIFT_REGISTER_1) {
473			written = _decodeShift(info->op1, true, buffer, blen);
474			ADVANCE(written);
475		} else if (info->operandFormat & ARM_OPERAND_SHIFT_IMMEDIATE_1) {
476			written = _decodeShift(info->op1, false, buffer, blen);
477			ADVANCE(written);
478		}
479		if (info->operandFormat & ARM_OPERAND_2) {
480			strlcpy(buffer, ", ", blen);
481			ADVANCE(2);
482		}
483		if (info->operandFormat & ARM_OPERAND_IMMEDIATE_2) {
484			written = snprintf(buffer, blen, "#%i", info->op2.immediate);
485			ADVANCE(written);
486		} else if (info->operandFormat & ARM_OPERAND_MEMORY_2) {
487			written = _decodeMemory(info->memory, cpu, symbols, pc, buffer, blen);
488			ADVANCE(written);
489		} else if (info->operandFormat & ARM_OPERAND_REGISTER_2) {
490			written = _decodeRegister(info->op2.reg, buffer, blen);
491			ADVANCE(written);
492		}
493		if (info->operandFormat & ARM_OPERAND_SHIFT_REGISTER_2) {
494			written = _decodeShift(info->op2, true, buffer, blen);
495			ADVANCE(written);
496		} else if (info->operandFormat & ARM_OPERAND_SHIFT_IMMEDIATE_2) {
497			written = _decodeShift(info->op2, false, buffer, blen);
498			ADVANCE(written);
499		}
500		if (info->operandFormat & ARM_OPERAND_3) {
501			strlcpy(buffer, ", ", blen);
502			ADVANCE(2);
503		}
504		if (info->operandFormat & ARM_OPERAND_IMMEDIATE_3) {
505			written = snprintf(buffer, blen, "#%i", info->op3.immediate);
506			ADVANCE(written);
507		} else if (info->operandFormat & ARM_OPERAND_MEMORY_3) {
508			written = _decodeMemory(info->memory, cpu, symbols, pc, buffer, blen);
509			ADVANCE(written);
510		} else if (info->operandFormat & ARM_OPERAND_REGISTER_3) {
511			written = _decodeRegister(info->op3.reg, buffer, blen);
512			ADVANCE(written);
513		}
514		if (info->operandFormat & ARM_OPERAND_SHIFT_REGISTER_3) {
515			written = _decodeShift(info->op3, true, buffer, blen);
516			ADVANCE(written);
517		} else if (info->operandFormat & ARM_OPERAND_SHIFT_IMMEDIATE_3) {
518			written = _decodeShift(info->op3, false, buffer, blen);
519			ADVANCE(written);
520		}
521		if (info->operandFormat & ARM_OPERAND_4) {
522			strlcpy(buffer, ", ", blen);
523			ADVANCE(2);
524		}
525		if (info->operandFormat & ARM_OPERAND_IMMEDIATE_4) {
526			written = snprintf(buffer, blen, "#%i", info->op4.immediate);
527			ADVANCE(written);
528		} else if (info->operandFormat & ARM_OPERAND_MEMORY_4) {
529			written = _decodeMemory(info->memory, cpu, symbols, pc, buffer, blen);
530			ADVANCE(written);
531		} else if (info->operandFormat & ARM_OPERAND_REGISTER_4) {
532			written = _decodeRegister(info->op4.reg, buffer, blen);
533			ADVANCE(written);
534		}
535		if (info->operandFormat & ARM_OPERAND_SHIFT_REGISTER_4) {
536			written = _decodeShift(info->op4, true, buffer, blen);
537			ADVANCE(written);
538		} else if (info->operandFormat & ARM_OPERAND_SHIFT_IMMEDIATE_4) {
539			written = _decodeShift(info->op4, false, buffer, blen);
540			ADVANCE(written);
541		}
542		break;
543	}
544	buffer[blen - 1] = '\0';
545	return total;
546}
547#endif
548
549uint32_t ARMResolveMemoryAccess(struct ARMInstructionInfo* info, struct ARMRegisterFile* regs, uint32_t pc) {
550	uint32_t address = 0;
551	int32_t offset = 0;
552	if (info->memory.format & ARM_MEMORY_REGISTER_BASE) {
553		if (info->memory.baseReg == ARM_PC && info->memory.format & ARM_MEMORY_IMMEDIATE_OFFSET) {
554			address = pc;
555		} else {
556			address = regs->gprs[info->memory.baseReg];
557		}
558	}
559	if (info->memory.format & ARM_MEMORY_POST_INCREMENT) {
560		return address;
561	}
562	if (info->memory.format & ARM_MEMORY_IMMEDIATE_OFFSET) {
563		offset = info->memory.offset.immediate;
564	} else if (info->memory.format & ARM_MEMORY_REGISTER_OFFSET) {
565		offset = info->memory.offset.reg == ARM_PC ? pc : regs->gprs[info->memory.offset.reg];
566	}
567	if (info->memory.format & ARM_MEMORY_SHIFTED_OFFSET) {
568		uint8_t shiftSize = info->memory.offset.shifterImm;
569		switch (info->memory.offset.shifterOp) {
570			case ARM_SHIFT_LSL:
571				offset <<= shiftSize;
572				break;
573			case ARM_SHIFT_LSR:
574				offset = ((uint32_t) offset) >> shiftSize;
575				break;
576			case ARM_SHIFT_ASR:
577				offset >>= shiftSize;
578				break;
579			case ARM_SHIFT_ROR:
580				offset = ROR(offset, shiftSize);
581				break;
582			case ARM_SHIFT_RRX:
583				offset = (regs->cpsr.c << 31) | ((uint32_t) offset >> 1);
584				break;
585			default:
586				break;
587		};
588	}
589	return address + (info->memory.format & ARM_MEMORY_OFFSET_SUBTRACT ? -offset : offset);
590}