FreeCalypso > hg > freecalypso-reveng
changeset 111:0f94d17899b3
tiobjd: disassembly integrated, no relocs or hints yet
author | Michael Spacefalcon <msokolov@ivan.Harhan.ORG> |
---|---|
date | Thu, 03 Apr 2014 05:14:15 +0000 |
parents | e650fdc743fe |
children | 61a58677dc68 |
files | ticoff/Makefile ticoff/armdis.c ticoff/atcommon.c ticoff/disasm.c ticoff/main.c ticoff/thumbdis.c |
diffstat | 6 files changed, 1052 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/ticoff/Makefile Thu Apr 03 03:03:41 2014 +0000 +++ b/ticoff/Makefile Thu Apr 03 05:14:15 2014 +0000 @@ -1,7 +1,8 @@ CC= gcc CFLAGS= -O2 PROG= tiobjd -OBJS= basics.o globals.o lowlevel.o main.o reloc.o symtab.o tables.o +OBJS= armdis.o atcommon.o basics.o disasm.o globals.o lowlevel.o main.o \ + reloc.o symtab.o tables.o thumbdis.o HDRS= coffconst.h filestruct.h globals.h intstruct.h all: ${PROG}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ticoff/armdis.c Thu Apr 03 05:14:15 2014 +0000 @@ -0,0 +1,491 @@ +/* + * ARM state disassembly + */ + +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include "intstruct.h" +#include "globals.h" + +extern unsigned get_u16(), get_u32(); + +extern char *regnames[16], *condition_decode[16], *shift_types[4]; + +static char *dataproc_ops[16] = {"and", "eor", "sub", "rsb", + "add", "adc", "sbc", "rsc", + "tst", "teq", "cmp", "cmn", + "orr", "mov", "bic", "mvn"}; + +static void +arm_branch(off, word) + unsigned off, word; +{ + unsigned dest; + + dest = (word & 0x00FFFFFF) << 2; + if (dest & 0x02000000) + dest |= 0xFC000000; + dest += 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 if ((word & 0x0FB00FF0) == 0x01000090) + printf("swp%s%s\t%s, %s, [%s]\n", condition_decode[word>>28], + word&0x400000, "b", "", regnames[(word>>12)&0xF], + regnames[word&0xF], regnames[(word>>16)&0xF]); + else + printf("<invalid multiply>\n"); +} + +static int +check_ldr_litpool(sec, off, word, loff, size) + struct internal_scnhdr *sec; + unsigned off, word, loff; +{ + unsigned litoff, datum; + + /* base reg must be 15 */ + if (((word >> 16) & 0xF) != 15) + return(0); + /* must be a load */ + if (!(word & 0x100000)) + return(0); + /* no writeback allowed */ + if (word & 0x200000) + return(0); + /* alignment */ + if (loff & (size - 1)) + return(0); + /* range */ + off += 8; + if (word & 0x800000) + litoff = off + loff; + else { + if (loff > off) + return(0); + litoff = off - loff; + } + if (litoff >= sec->size) + return(0); + /* all checks passed, proceed */ + switch (size) { + case 1: + datum = filemap[sec->data_offset + litoff]; + break; + case 2: + datum = get_u16(filemap + sec->data_offset + litoff); + break; + case 4: + datum = get_u32(filemap + sec->data_offset + litoff); + break; + } + printf("=0x%x\t; via 0x%x\n", datum, litoff); + return(1); +} + +static void +ldr_str_imm_pre(sec, off, word) + struct internal_scnhdr *sec; + unsigned off, word; +{ + unsigned loff = word & 0xFFF; + + printf("%s%s%s\t%s, ", word&0x100000 ? "ldr" : "str", + condition_decode[word>>28], word&0x400000 ? "b" : "", + regnames[(word>>12)&0xF]); + if (check_ldr_litpool(sec, off, word, loff, word&0x400000 ? 1 : 4)) + return; + printf("[%s", 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(sec, off, word) + struct internal_scnhdr *sec; + unsigned off, word; +{ + unsigned loff; + + if (!(word&0x01000000) && word&0x200000) { + printf("<invalid ldrh/strh: P=0, W=1>\n"); + return; + } + if (!(word&0x400000) && word&0xF00) { + printf("<invalid ldrh/strh: SBZ!=0>\n"); + return; + } + printf("%s%s%s%c\t%s, ", word&0x100000 ? "ldr" : "str", + condition_decode[word>>28], + word&0x40 ? "s" : "", + word&0x20 ? 'h' : 'b', + regnames[(word>>12)&0xF]); + if (word & 0x400000) + loff = ((word & 0xF00) >> 4) | (word & 0xF); + switch (word & 0x01400000) { + case 0: + /* reg post */ + printf("[%s], %s%s", regnames[(word>>16)&0xF], + word&0x800000 ? "" : "-", regnames[word&0xF]); + break; + case 0x400000: + /* imm post */ + printf("[%s], #%s%u", regnames[(word>>16)&0xF], + word&0x800000 ? "" : "-", loff); + if (loff >= 10) + printf("\t; 0x%x", loff); + break; + case 0x01000000: + /* reg pre */ + printf("[%s, %s%s]%s", regnames[(word>>16)&0xF], + word&0x800000 ? "" : "-", regnames[word&0xF], + word&0x200000 ? "!" : ""); + break; + case 0x01400000: + /* imm pre */ + if (check_ldr_litpool(sec, off, word, loff, word&0x20 ? 2 : 1)) + return; + printf("[%s", 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); + break; + } + putchar('\n'); +} + +static void +dataproc_74_overlay(sec, off, word) + struct internal_scnhdr *sec; + unsigned off, word; +{ + if (word & 0x60) + ldr_str_ext(sec, off, word); + else + multiply(word); +} + +static void +ldm_stm(word) + unsigned word; +{ + int r, flag; + + printf("%s%s%c%c\t%s", word&0x100000 ? "ldm" : "stm", + condition_decode[word>>28], + word&0x800000 ? 'i' : 'd', word&0x01000000 ? 'b' : 'a', + regnames[(word>>16)&0xF]); + if (word & 0x200000) + putchar('!'); + fputs(", {", stdout); + flag = 0; + for (r = 0; r < 16; r++) + if (word & (1 << r)) { + if (flag) + fputs(", ", stdout); + fputs(regnames[r], stdout); + flag = 1; + } + putchar('}'); + if (word & 0x400000) + putchar('^'); + putchar('\n'); +} + +void +arm_disasm_line(sec, off) + struct internal_scnhdr *sec; + unsigned off; +{ + unsigned word; + + word = get_u32(filemap + sec->data_offset + off); + printf("%08x\t", 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(sec, 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(sec, off, word); + return; + case 6: + ldr_str_reg_post(word); + return; + case 7: + ldr_str_reg_pre(word); + return; + case 8: + case 9: + ldm_stm(word); + 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; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ticoff/atcommon.c Thu Apr 03 05:14:15 2014 +0000 @@ -0,0 +1,14 @@ +/* + * Lean and mean ARM7TDMI disassembler + * Written by Spacefalcon the Outlaw + */ + +/* a few disassembly bits common between ARM and Thumb */ + +char *regnames[16] = {"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc"}; + +char *condition_decode[16] = {"eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", + "hi", "ls", "ge", "lt", "gt", "le", "", "INV"}; + +char *shift_types[4] = {"lsl", "lsr", "asr", "ror"};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ticoff/disasm.c Thu Apr 03 05:14:15 2014 +0000 @@ -0,0 +1,144 @@ +/* + * Putting it all together: section-, symbol- and reloc-aware disassembly + */ + +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include "intstruct.h" +#include "coffconst.h" +#include "globals.h" + +extern unsigned get_u16(), get_u32(); + +void +disasm_text_section(sec) + struct internal_scnhdr *sec; +{ + unsigned symnum, relnum; + unsigned pos, incr, headroom; + int state = -1, linebrk = 0; + struct internal_syment *sym; + struct internal_reloc *rel; + char *sym_comment; + + printf("Disassembling code section:\n"); + if (sec->nsymbols) + sort_symbols_of_sec(sec); + if (sec->nreloc) + get_relocs_of_sec(sec); + symnum = relnum = 0; + for (pos = 0; pos < sec->size; pos += incr) { + headroom = sec->size - pos; + while (symnum < sec->nsymbols) { + sym = sec->sorted_symbols[symnum]; + if (sym->value > pos) { + if (sym->value - pos < headroom) + headroom = sym->value - pos; + break; + } + /* hit symbol */ + if (!linebrk) { + putchar('\n'); + linebrk = 1; + } + switch (sym->class) { + case C_EXT: + sym_comment = "Global"; + break; + case C_STAT: + sym_comment = "static"; + break; + case C_LABEL: + sym_comment = "label"; + if (!strcmp(sym->name, "$CODE16")) + state = 1; + else if (!strcmp(sym->name, "$CODE32")) + state = 0; + break; + default: + sym_comment = "unexpected class!"; + } + printf("%s:\t; %s\n", sym->name, sym_comment); + symnum++; + } + if (relnum < sec->nreloc) { + rel = sec->int_relocs + relnum; + if (rel->location == pos) + relnum++; /* it's ours */ + else { + if (rel->location - pos < headroom) + headroom = rel->location - pos; + rel = 0; /* no reloc for current pos */ + } + } else + rel = 0; + printf("%8x:\t", pos); + switch (state) { + case 0: /* ARM */ + if (pos & 3) { + printf("MISALIGNED pos in CODE32 state, aborting\n"); + return; + } + /* reloc handling to be added */ + arm_disasm_line(sec, pos); + incr = 4; + break; + case 1: /* Thumb */ + if (pos & 1) { + printf("MISALIGNED pos in CODE16 state, aborting\n"); + return; + } + /* reloc handling to be added */ + if (headroom >= 4 && thumb_check_bl(sec, pos)) + incr = 4; + else { + thumb_disasm_line(sec, pos); + incr = 2; + } + break; + default: + printf("UNKNOWN T state, aborting\n"); + return; + } + linebrk = 0; + if (incr > headroom) { + printf("error: increment %u > headroom %u, aborting\n", + incr, headroom); + return; + } + } +} + +void +disasm_sectype_by_name(sec) + struct internal_scnhdr *sec; +{ + if (!strncmp(sec->name, ".text", 5)) + disasm_text_section(sec); + /* other section types to be added */ + else + printf("Unrecognized section type, skipped\n"); +} + +cmd_disasm() +{ + struct internal_scnhdr *sec; + unsigned secnum; + + printf("%s:\n", objfilename); + dump_filehdr_info(); + putchar('\n'); + get_int_section_table(); + get_int_symbol_table(); + extern_profile_report("Module"); + for (secnum = 0; secnum < nsections; secnum++) { + sec = sections + secnum; + printf("=== %s ===\n", sec->name); + disasm_sectype_by_name(sec); + putchar('\n'); + } + exit(0); +}
--- a/ticoff/main.c Thu Apr 03 03:03:41 2014 +0000 +++ b/ticoff/main.c Thu Apr 03 05:14:15 2014 +0000 @@ -10,6 +10,7 @@ #include "globals.h" extern int cmd_basics(); +extern int cmd_disasm(); extern int cmd_nm(); extern int cmd_profile(); extern int cmd_rawrel(); @@ -23,6 +24,7 @@ int (*func)(); } cmdtab[] = { {"basics", cmd_basics}, + {"disasm", cmd_disasm}, {"dumpsym", cmd_symtab}, /* backward compat */ {"hdr", dump_filehdr_info}, {"nm", cmd_nm},
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ticoff/thumbdis.c Thu Apr 03 05:14:15 2014 +0000 @@ -0,0 +1,399 @@ +/* + * Thumb state disassembly + */ + +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include "intstruct.h" +#include "globals.h" + +extern unsigned get_u16(), get_u32(); + +extern char *regnames[16], *condition_decode[16], *shift_types[4]; + +static void +format_1_2(word) + unsigned word; +{ + unsigned op, imm; + + op = (word >> 11) & 3; + if (op != 3) { + /* format 1 */ + imm = (word >> 6) & 0x1F; + if (op != 0 && imm == 0) + imm = 32; + printf("%s\t%s, %s, #%u\n", shift_types[op], regnames[word&7], + regnames[(word>>3)&7], imm); + return; + } + /* format 2 */ + printf("%s\t%s, %s, ", word&0x200 ? "sub" : "add", regnames[word&7], + regnames[(word>>3)&7]); + if (word & 0x400) + printf("#%u\n", (word >> 6) & 7); + else + printf("%s\n", regnames[(word >> 6) & 7]); +} + +static void +format_3(word) + unsigned word; +{ + static char *opctab[4] = {"mov", "cmp", "add", "sub"}; + unsigned imm; + + imm = word & 0xFF; + printf("%s\t%s, #%u", opctab[(word>>11)&3], regnames[(word>>8)&7], imm); + if (imm > 9) + printf("\t; 0x%x", imm); + putchar('\n'); +} + +static void +format_4(word) + unsigned word; +{ + static char *opc[16] = {"and", "eor", "lsl", "lsr", + "asr", "adc", "sbc", "ror", + "tst", "neg", "cmp", "cmn", + "orr", "mul", "bic", "mvn"}; + + printf("%s\t%s, %s\n", opc[(word>>6)&0xF], regnames[word&7], + regnames[(word>>3)&7]); +} + +static void +format_5_bx(word) + unsigned word; +{ + if (word & 0x80) + printf("<invalid: blx instead of bx>\n"); + else + printf("bx\t%s\n", regnames[(word>>3)&0xF]); +} + +static void +format_5_hiops(word) + unsigned word; +{ + static char *opctab[3] = {"add", "cmp", "mov"}; + int reg1, reg2, op; + + if (word & 0xC0) { + reg1 = word & 7; + if (word & 0x80) + reg1 += 8; + reg2 = (word >> 3) & 0xF; + op = (word >> 8) & 3; + if (op == 2 && reg1 == reg2 && reg1 != 15) + printf("nop\t\t\t(mov %s, %s)\n", + regnames[reg1], regnames[reg2]); + else + printf("%s\t%s, %s\n", opctab[op], + regnames[reg1], regnames[reg2]); + } else + printf("<invalid: hi-reg format with both low regs>\n"); +} + +static void +format_5(word) + unsigned word; +{ + if ((word & 0x300) == 0x300) + format_5_bx(word); + else + format_5_hiops(word); +} + +static void +format_6(sec, off, word) + struct internal_scnhdr *sec; + unsigned off, word; +{ + unsigned loff, litoff; + + loff = (word & 0xFF) << 2; + off &= ~3; + off += 4; + litoff = off + loff; + if (litoff+4 <= sec->size) + printf("ldr\t%s, =0x%x\t; via 0x%x\n", regnames[(word>>8)&7], + get_u32(filemap + sec->data_offset + litoff), litoff); + else + printf("ldr\t%s, [pc, #%u]\t(0x%x)\n", regnames[(word>>8)&7], + loff, litoff); +} + +static void +format_7(word) + unsigned word; +{ + printf("%s%s\t%s, [%s, %s]\n", word&0x800 ? "ldr" : "str", + word&0x400 ? "b" : "", regnames[word&7], + regnames[(word>>3)&7], regnames[(word>>6)&7]); +} + +static void +format_8(word) + unsigned word; +{ + static char *opc[4] = {"strh", "ldrsb", "ldrh", "ldrsh"}; + + printf("%s\t%s, [%s, %s]\n", opc[(word>>10)&3], regnames[word&7], + regnames[(word>>3)&7], regnames[(word>>6)&7]); +} + +static void +format_9(word) + unsigned word; +{ + unsigned loff; + + loff = (word >> 6) & 0x1F; + if (!(word & 0x1000)) + loff <<= 2; + printf("%s%s\t%s, [%s, #%u]", word&0x800 ? "ldr" : "str", + word&0x1000 ? "b" : "", regnames[word&7], + regnames[(word>>3)&7], loff); + if (loff >= 10) + printf("\t; 0x%x", loff); + putchar('\n'); +} + +static void +format_10(word) + unsigned word; +{ + unsigned loff; + + loff = (word >> 6) & 0x1F; + loff <<= 1; + printf("%sh\t%s, [%s, #%u]", word&0x800 ? "ldr" : "str", + regnames[word&7], regnames[(word>>3)&7], loff); + if (loff >= 10) + printf("\t; 0x%x", loff); + putchar('\n'); +} + +static void +format_11(word) + unsigned word; +{ + unsigned loff; + + loff = (word & 0xFF) << 2; + printf("%s\t%s, [sp, #%u]", word&0x800 ? "ldr" : "str", + regnames[(word>>8)&7], loff); + if (loff >= 10) + printf("\t; 0x%x", loff); + putchar('\n'); +} + +static void +format_12(off, word) + unsigned off, word; +{ + unsigned loff; + + loff = (word & 0xFF) << 2; + printf("add\t%s, %s, #%u", regnames[(word>>8)&7], + word&0x800 ? "sp" : "pc", loff); + if (loff >= 10) + printf("\t; 0x%x", loff); + putchar('\n'); +} + +static void +format_13(word) + unsigned word; +{ + unsigned loff; + + if ((word & 0xFF00) != 0xB000) { + printf("<invalid format 13>\n"); + return; + } + loff = (word & 0x7F) << 2; + printf("%s\tsp, #%u", word&0x80 ? "sub" : "add", loff); + if (loff >= 10) + printf("\t; 0x%x", loff); + putchar('\n'); +} + +static void +format_14(word) + unsigned word; +{ + int r, flag; + + if ((word & 0xF600) != 0xB400) { + printf("<invalid format 14>\n"); + return; + } + printf("%s\t{", word&0x800 ? "pop" : "push"); + flag = 0; + for (r = 0; r < 9; r++) + if (word & (1 << r)) { + if (flag) + fputs(", ", stdout); + if (r == 8) + fputs(word&0x800 ? "pc" : "lr", stdout); + else + fputs(regnames[r], stdout); + flag = 1; + } + putchar('}'); + putchar('\n'); +} + +static void +format_15(word) + unsigned word; +{ + int r, flag; + + printf("%sia\t%s!, {", word&0x800 ? "ldm" : "stm", + regnames[(word>>8)&7]); + flag = 0; + for (r = 0; r < 8; r++) + if (word & (1 << r)) { + if (flag) + fputs(", ", stdout); + fputs(regnames[r], stdout); + flag = 1; + } + putchar('}'); + putchar('\n'); +} + +static void +format_16_17(off, word) + unsigned off, word; +{ + unsigned cond; + unsigned dest; + + cond = (word >> 8) & 0xF; + switch (cond) { + case 0xE: + printf("<invalid: bal>\n"); + return; + case 0xF: + printf("swi\t0x%x\n", word & 0xFF); + return; + } + dest = (word & 0xFF) << 1; + if (dest & 0x00000100) + dest |= 0xFFFFFE00; + dest += off + 4; + printf("b%s\t0x%x\n", condition_decode[cond], dest); +} + +static void +format_18(off, word) + unsigned off, word; +{ + unsigned dest; + + if (word & 0x800) { + printf("<invalid format 18>\n"); + return; + } + dest = (word & 0x7FF) << 1; + if (dest & 0x00000800) + dest |= 0xFFFFF000; + dest += off + 4; + printf("b\t0x%x\n", dest); +} + +void +thumb_disasm_line(sec, off) + struct internal_scnhdr *sec; + unsigned off; +{ + unsigned word; + + word = get_u16(filemap + sec->data_offset + off); + printf("%04x\t\t", word); + switch (word >> 12) { + case 0: + case 1: + format_1_2(word); + return; + case 2: + case 3: + format_3(word); + return; + case 4: + if (word & 0x800) + format_6(sec, off, word); + else if (word & 0x400) + format_5(word); + else + format_4(word); + return; + case 5: + if (word & 0x200) + format_8(word); + else + format_7(word); + return; + case 6: + case 7: + format_9(word); + return; + case 8: + format_10(word); + return; + case 9: + format_11(word); + return; + case 0xA: + format_12(off, word); + return; + case 0xB: + if (word & 0x400) + format_14(word); + else + format_13(word); + return; + case 0xC: + format_15(word); + return; + case 0xD: + format_16_17(off, word); + return; + case 0xE: + format_18(off, word); + return; + case 0xF: + printf("<half-bl>\n"); + return; + } +} + +thumb_check_bl(sec, off) + struct internal_scnhdr *sec; + unsigned off; +{ + unsigned ins1, ins2; + unsigned dest; + + ins1 = get_u16(filemap + sec->data_offset + off); + if ((ins1 & 0xF800) != 0xF000) + return(0); + ins2 = get_u16(filemap + sec->data_offset + off + 2); + if ((ins2 & 0xF800) != 0xF800) + return(0); + /* match */ + dest = ((ins1 & 0x7FF) << 12) | ((ins2 & 0x7FF) << 1); + if (dest & 0x00400000) + dest |= 0xFF800000; + dest += off + 4; + printf("%04x %04x\tbl\t0x%x\n", ins1, ins2, dest); + return(1); +}