view arm7dis/armdis.c @ 91:daf69d5edb3f

armdis: ldr/str decoding implemented (but not PC-relative ldr yet)
author Michael Spacefalcon <msokolov@ivan.Harhan.ORG>
date Sun, 30 Mar 2014 00:27:25 +0000
parents f68d8e7a904f
children 708f2452d1ae
line wrap: on
line source

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>

extern char *binfilename;
extern u_char *filemap;
extern unsigned disasm_len, base_vma;

extern unsigned get_u16(), get_u32();

extern char *regnames[16], *condition_decode[16];

static char *dataproc_ops[16] = {"and", "eor", "sub", "rsb",
				 "add", "adc", "sbc", "rsc",
				 "tst", "teq", "cmp", "cmn",
				 "orr", "mov", "bic", "mvn"};
static char *shift_types[4] = {"lsl", "lsr", "asr", "ror"};

static void
arm_branch(off, word)
	unsigned off, word;
{
	unsigned dest;

	dest = (word & 0x00FFFFFF) << 2;
	if (dest & 0x02000000)
		dest |= 0xFC000000;
	dest += base_vma + off + 8;
	printf("b%s%s\t0x%x\n", word&0x1000000 ? "l" : "",
		condition_decode[word>>28], dest);
}

static void
op2_immed(word)
	unsigned word;
{
	unsigned low8, rot, val;

	low8 = word & 0xFF;
	rot = (word & 0xF00) >> 7;
	val = (low8 << (32 - rot)) | (low8 >> rot);
	if (val <= 9)
		printf("#%u\n", val);
	else
		printf("#%u\t; 0x%x\n", val, val);
}

static void
op2_regbyconst(word)
	unsigned word;
{
	unsigned c, t;

	c = (word >> 7) & 0x1F;
	t = (word >> 5) & 3;
	if (!c) {
		switch (t) {
		case 0:
			printf("%s", regnames[word&0xF]);
			return;
		case 3:
			printf("%s, rrx", regnames[word&0xF]);
			return;
		default:
			c = 32;
		}
	}
	printf("%s, %s #%u", regnames[word&0xF], shift_types[t], c);
}

static void
op2_regbyreg(word)
	unsigned word;
{
	printf("%s, %s %s", regnames[word&0xF], shift_types[(word>>5)&3],
		regnames[(word>>8)&0xF]);
}

static void
op2_regshift(word)
	unsigned word;
{
	if (word & 0x10)
		op2_regbyreg(word);
	else
		op2_regbyconst(word);
	putchar('\n');
}

static void
dataproc_op2(word)
	unsigned word;
{
	if (word & 0x02000000)
		op2_immed(word);
	else
		op2_regshift(word);
}

static void
dataproc_tstcmp_overlay(word)
	unsigned word;
{
	char msrmask[5], *cp;

	if ((word & 0x0FFFFFF0) == 0x012FFF10) {
		printf("bx%s\t%s\n", condition_decode[word>>28],
			regnames[word&0xF]);
		return;
	} else if ((word & 0x0FBF0FFF) == 0x010F0000) {
		printf("mrs%s\t%s, %cPSR\n", condition_decode[word>>28],
			regnames[(word>>12)&0xF], word&0x400000 ? 'S' : 'C');
		return;
	} else if ((word & 0x0DB0F000) == 0x0120F000) {
		if (!(word & 0x02000000) && (word & 0xFF0)) {
			printf("<invalid MSR>\n");
			return;
		}
		if (word & 0xF0000) {
			cp = msrmask;
			if (word & 0x80000)
				*cp++ = 'f';
			if (word & 0x40000)
				*cp++ = 's';
			if (word & 0x20000)
				*cp++ = 'x';
			if (word & 0x10000)
				*cp++ = 'c';
			*cp = '\0';
		} else
			strcpy(msrmask, "null");
		printf("msr%s\t%cPSR_%s, ", condition_decode[word>>28],
			word&0x400000 ? 'S' : 'C', msrmask);
		dataproc_op2(word);
		return;
	}
	printf("<invalid BX/MRS/MSR>\n");
}

static void
dataproc(word)
	unsigned word;
{
	unsigned opc;

	opc = (word >> 21) & 0xF;
	switch (opc) {
	case 0:
	case 1:
	case 2:
	case 3:
	case 4:
	case 5:
	case 6:
	case 7:
	case 0xC:
	case 0xE:
		printf("%s%s%s\t%s, %s, ", dataproc_ops[opc],
			condition_decode[word>>28], word&0x100000 ? "s" : "",
			regnames[(word>>12)&0xF], regnames[(word>>16)&0xF]);
		dataproc_op2(word);
		return;
	case 0xD:
	case 0xF:
		printf("%s%s%s\t%s, ", dataproc_ops[opc],
			condition_decode[word>>28], word&0x100000 ? "s" : "",
			regnames[(word>>12)&0xF]);
		dataproc_op2(word);
		return;
	case 8:
	case 9:
	case 0xA:
	case 0xB:
		if (word & 0x100000) {
			printf("%s%s\t%s, ", dataproc_ops[opc],
				condition_decode[word>>28],
				regnames[(word>>16)&0xF]);
			dataproc_op2(word);
		} else
			dataproc_tstcmp_overlay(word);
		return;
	}
}

static void
multiply(word)
	unsigned word;
{
	if ((word & 0x0FE000F0) == 0x90)
		printf("mul%s%s\t%s, %s, %s\n", condition_decode[word>>28],
			word&0x100000 ? "s" : "", regnames[(word>>16)&0xF],
			regnames[word&0xF], regnames[(word>>8)&0xF]);
	else if ((word & 0x0FE000F0) == 0x00200090)
		printf("mla%s%s\t%s, %s, %s, %s\n", condition_decode[word>>28],
			word&0x100000 ? "s" : "", regnames[(word>>16)&0xF],
			regnames[word&0xF], regnames[(word>>8)&0xF],
			regnames[(word>>12)&0xF]);
	else if ((word & 0x0F8000F0) == 0x00800090)
		printf("%c%sl%s%s\t%s, %s, %s, %s\n",
			word&0x400000 ? 's' : 'u',
			word&0x200000 ? "mla" : "mul",
			condition_decode[word>>28],
			word&0x100000 ? "s" : "",
			regnames[(word>>12)&0xF], regnames[(word>>16)&0xF],
			regnames[word&0xF], regnames[(word>>8)&0xF]);
	else
		printf("<invalid multiply>\n");
}

static void
ldr_str_imm_pre(off, word)
	unsigned off, word;
{
	unsigned loff = word & 0xFFF;

	/* check for literal pool fetches will go here */
	printf("%s%s%s\t%s, [%s", word&0x100000 ? "ldr" : "str",
		condition_decode[word>>28], word&0x400000 ? "b" : "",
		regnames[(word>>12)&0xF], regnames[(word>>16)&0xF]);
	if (loff || word&0x200000)
		printf(", #%s%u", word&0x800000 ? "" : "-", loff);
	putchar(']');
	if (word & 0x200000)
		putchar('!');
	if (loff >= 10)
		printf("\t; 0x%x", loff);
	putchar('\n');
}

static void
ldr_str_imm_post(word)
	unsigned word;
{
	unsigned loff = word & 0xFFF;

	printf("%s%s%s%s\t%s, [%s], #%s%u", word&0x100000 ? "ldr" : "str",
		condition_decode[word>>28], word&0x400000 ? "b" : "",
		word&0x200000 ? "t" : "",
		regnames[(word>>12)&0xF], regnames[(word>>16)&0xF],
		word&0x800000 ? "" : "-", loff);
	if (loff >= 10)
		printf("\t; 0x%x", loff);
	putchar('\n');
}

static void
ldr_str_reg_pre(word)
	unsigned word;
{
	if (word & 0x10) {
		printf("<invalid ldr/str: offset reg shift by reg>\n");
		return;
	}
	printf("%s%s%s\t%s, [%s, ", word&0x100000 ? "ldr" : "str",
		condition_decode[word>>28], word&0x400000 ? "b" : "",
		regnames[(word>>12)&0xF], regnames[(word>>16)&0xF]);
	if (!(word & 0x800000))
		putchar('-');
	op2_regbyconst(word);
	putchar(']');
	if (word & 0x200000)
		putchar('!');
	putchar('\n');
}

static void
ldr_str_reg_post(word)
	unsigned word;
{
	if (word & 0x10) {
		printf("<invalid ldr/str: offset reg shift by reg>\n");
		return;
	}
	printf("%s%s%s%s\t%s, [%s], ", word&0x100000 ? "ldr" : "str",
		condition_decode[word>>28], word&0x400000 ? "b" : "",
		word&0x200000 ? "t" : "",
		regnames[(word>>12)&0xF], regnames[(word>>16)&0xF]);
	if (!(word & 0x800000))
		putchar('-');
	op2_regbyconst(word);
	putchar('\n');
}

static void
ldr_str_ext(off, word)
	unsigned off, word;
{
	printf("<extended ldr/str>\n");
}

static void
dataproc_74_overlay(off, word)
	unsigned off, word;
{
	if (word & 0x60)
		ldr_str_ext(off, word);
	else
		multiply(word);
}

void
arm_disasm_line(off)
	unsigned off;
{
	unsigned word;

	word = get_u32(filemap + off);
	printf("%8x:\t%08x\t", base_vma + off, word);
	if ((word >> 28) == 0xF) {
		printf("<invalid-F>\n");
		return;
	}
	switch ((word >> 24) & 0xF) {
	case 0:
	case 1:
		if ((word & 0x90) == 0x90)
			dataproc_74_overlay(off, word);
		else
			dataproc(word);
		return;
	case 2:
	case 3:
		dataproc(word);
		return;
	case 4:
		ldr_str_imm_post(word);
		return;
	case 5:
		ldr_str_imm_pre(off, word);
		return;
	case 6:
		ldr_str_reg_post(word);
		return;
	case 7:
		ldr_str_reg_pre(word);
		return;
	case 8:
	case 9:
		printf("<ldm/stm>\n");
		return;
	case 0xA:
	case 0xB:
		arm_branch(off, word);
		return;
	case 0xC:
	case 0xD:
	case 0xE:
		printf("<COPROCESSOR>\n");
		return;
	case 0xF:
		printf("swi%s\t0x%x\n", condition_decode[word>>28],
			word & 0xFFFFFF);
		return;
	}
}

main(argc, argv)
	char **argv;
{
	unsigned off;

	common_init(argc, argv, 4);
	for (off = 0; off < disasm_len; off += 4)
		arm_disasm_line(off);
	exit(0);
}