The tool is now hosted on its own repository Lephenixnoir/fxos.pull/4/head
@ -1,22 +0,0 @@ | |||
#include <fxos.h> | |||
#include <stdio.h> | |||
/* analysis_short(): Print a one-line summary for an address */ | |||
void analysis_short(struct os const *os, uint32_t value) | |||
{ | |||
/* Find out whether it is a syscall address */ | |||
int syscall = os_syscall_find(os, value); | |||
if(syscall != -1) | |||
{ | |||
printf(" %%%03x", syscall); | |||
/* Also find out the syscall's name! */ | |||
struct sys_call const *call = sys_find(syscall); | |||
if(call) printf(" %s", call->name); | |||
} | |||
/* Find out any peripheral register address */ | |||
struct reg_address const *reg = reg_find(value); | |||
if(reg) printf(" %s", reg->name); | |||
} |
@ -1,220 +0,0 @@ | |||
# Instruction decoding table: 'sh3' | |||
# Format: [01nmdi]{16}, followed by the mnemonic and the list of arguments. | |||
# There should be at most one series of each argument letter in the opcode. | |||
# | |||
# Possible argument strings are predefined and include: | |||
# rn rm #imm | |||
# jump8 jump12 disp pcdisp | |||
# @rn @rm @rn+ @rm+ @-rn | |||
# @(disp,rn) @(disp,rm) @(r0,rn) @(r0,rm) @(disp,gbr) | |||
# | |||
# The disassembler substitutes some elements as follows: | |||
# rn -> value of the "n"-sequence | |||
# rm -> value of the "m"-sequence | |||
# #imm -> value of the "i"-sequence | |||
# disp -> value of the "d"-sequence | |||
# jump8 -> value of the 8-bit "d"-sequence plus value of PC | |||
# jump12 -> value of the 12-bit "d"-sequence plus value of PC | |||
# pcdisp -> same as "jump8", but also prints pointed value (for data) | |||
0000000001001000 clrs | |||
0000000000001000 clrt | |||
0000000000101000 clrmac | |||
0000000000011001 div0u | |||
0000000000111000 ldtlb | |||
0000000000001001 nop | |||
0000000000101011 rte | |||
0000000000001011 rts | |||
0000000001011000 sets | |||
0000000000011000 sett | |||
0000000000011011 sleep | |||
0100nnnn00010101 cmp/pl rn | |||
0100nnnn00010001 cmp/pz rn | |||
0100nnnn00010000 dt rn | |||
0000nnnn00101001 movt rn | |||
0100nnnn00000100 rotl rn | |||
0100nnnn00000101 rotr rn | |||
0100nnnn00100100 rotcl rn | |||
0100nnnn00100101 rotcr rn | |||
0100nnnn00100000 shal rn | |||
0100nnnn00100001 shar rn | |||
0100nnnn00000000 shll rn | |||
0100nnnn00000001 shlr rn | |||
0100nnnn00001000 shll2 rn | |||
0100nnnn00001001 shlr2 rn | |||
0100nnnn00011000 shll8 rn | |||
0100nnnn00011001 shlr8 rn | |||
0100nnnn00101000 shll16 rn | |||
0100nnnn00101001 shlr16 rn | |||
0011nnnnmmmm1100 add rm, rn | |||
0011nnnnmmmm1110 addc rm, rn | |||
0011nnnnmmmm1111 addv rm, rn | |||
0010nnnnmmmm1001 and rm, rn | |||
0011nnnnmmmm0000 cmp/eq rm, rn | |||
0011nnnnmmmm0010 cmp/hs rm, rn | |||
0011nnnnmmmm0011 cmp/ge rm, rn | |||
0011nnnnmmmm0110 cmp/hi rm, rn | |||
0011nnnnmmmm0111 cmp/gt rm, rn | |||
0010nnnnmmmm1100 cmp/str rm, rn | |||
0011nnnnmmmm0100 div1 rm, rn | |||
0010nnnnmmmm0111 div0s rm, rn | |||
0011nnnnmmmm1101 dmuls.l rm, rn | |||
0011nnnnmmmm0101 dmulu.l rm, rn | |||
0110nnnnmmmm1110 exts.b rm, rn | |||
0110nnnnmmmm1111 exts.w rm, rn | |||
0110nnnnmmmm1100 extu.b rm, rn | |||
0110nnnnmmmm1101 extu.w rm, rn | |||
0110nnnnmmmm0011 mov rm, rn | |||
0000nnnnmmmm0111 mul.l rm, rn | |||
0010nnnnmmmm1111 muls.w rm, rn | |||
0010nnnnmmmm1110 mulu.w rm, rn | |||
0110nnnnmmmm1011 neg rm, rn | |||
0110nnnnmmmm1010 negc rm, rn | |||
0110nnnnmmmm0111 not rm, rn | |||
0010nnnnmmmm1011 or rm, rn | |||
0100nnnnmmmm1100 shad rm, rn | |||
0100nnnnmmmm1101 shld rm, rn | |||
0011nnnnmmmm1000 sub rm, rn | |||
0011nnnnmmmm1010 subc rm, rn | |||
0011nnnnmmmm1011 subv rm, rn | |||
0110nnnnmmmm1000 swap.b rm, rn | |||
0110nnnnmmmm1001 swap.w rm, rn | |||
0010nnnnmmmm1000 tst rm, rn | |||
0010nnnnmmmm1010 xor rm, rn | |||
0010nnnnmmmm1101 xtrct rm, rn | |||
0100mmmm00001110 ldc rm, sr | |||
0100mmmm00011110 ldc rm, gbr | |||
0100mmmm00101110 ldc rm, vbr | |||
0100mmmm00111110 ldc rm, ssr | |||
0100mmmm01001110 ldc rm, spc | |||
0100mmmm10001110 ldc rm, r0_bank | |||
0100mmmm10011110 ldc rm, r1_bank | |||
0100mmmm10101110 ldc rm, r2_bank | |||
0100mmmm10111110 ldc rm, r3_bank | |||
0100mmmm11001110 ldc rm, r4_bank | |||
0100mmmm11011110 ldc rm, r5_bank | |||
0100mmmm11101110 ldc rm, r6_bank | |||
0100mmmm11111110 ldc rm, r7_bank | |||
0100mmmm00001010 lds rm, mach | |||
0100mmmm00011010 lds rm, macl | |||
0100mmmm00101010 lds rm, pr | |||
0000nnnn00000010 stc sr, rn | |||
0000nnnn00010010 stc gbr, rn | |||
0000nnnn00100010 stc vbr, rn | |||
0000nnnn00110010 stc ssr, rn | |||
0000nnnn01000010 stc spc, rn | |||
0000nnnn10000010 stc r0_bank, rn | |||
0000nnnn10010010 stc r1_bank, rn | |||
0000nnnn10100010 stc r2_bank, rn | |||
0000nnnn10110010 stc r3_bank, rn | |||
0000nnnn11000010 stc r4_bank, rn | |||
0000nnnn11010010 stc r5_bank, rn | |||
0000nnnn11100010 stc r6_bank, rn | |||
0000nnnn11110010 stc r7_bank, rn | |||
0000nnnn00001010 sts mach, rn | |||
0000nnnn00011010 sts macl, rn | |||
0000nnnn00101010 sts pr, rn | |||
0100nnnn00101011 jmp @rn | |||
0100nnnn00001011 jsr @rn | |||
0000nnnn10000011 pref @rn | |||
0100nnnn00011011 tas.b @rn | |||
0010nnnnmmmm0000 mov.b rm, @rn | |||
0010nnnnmmmm0001 mov.w rm, @rn | |||
0010nnnnmmmm0010 mov.l rm, @rn | |||
0110nnnnmmmm0000 mov.b @rm, rn | |||
0110nnnnmmmm0001 mov.w @rm, rn | |||
0110nnnnmmmm0010 mov.l @rm, rn | |||
0000nnnnmmmm1111 mac.l @rm+, @rn+ | |||
0100nnnnmmmm1111 mac.w @rm+, @rn+ | |||
0110nnnnmmmm0100 mov.b @rm+, rn | |||
0110nnnnmmmm0101 mov.w @rm+, rn | |||
0110nnnnmmmm0110 mov.l @rm+, rn | |||
0100mmmm00000111 ldc.l @rm+, sr | |||
0100mmmm00010111 ldc.l @rm+, gbr | |||
0100mmmm00100111 ldc.l @rm+, vbr | |||
0100mmmm00110111 ldc.l @rm+, ssr | |||
0100mmmm01000111 ldc.l @rm+, spc | |||
0100mmmm10000111 ldc.l @rm+, r0_bank | |||
0100mmmm10010111 ldc.l @rm+, r1_bank | |||
0100mmmm10100111 ldc.l @rm+, r2_bank | |||
0100mmmm10110111 ldc.l @rm+, r3_bank | |||
0100mmmm11000111 ldc.l @rm+, r4_bank | |||
0100mmmm11010111 ldc.l @rm+, r5_bank | |||
0100mmmm11100111 ldc.l @rm+, r6_bank | |||
0100mmmm11110111 ldc.l @rm+, r7_bank | |||
0100mmmm00000110 lds.l @rm+, mach | |||
0100mmmm00010110 lds.l @rm+, macl | |||
0100mmmm00100110 lds.l @rm+, pr | |||
0010nnnnmmmm0100 mov.b rm, @-rn | |||
0010nnnnmmmm0101 mov.w rm, @-rn | |||
0010nnnnmmmm0110 mov.l rm, @-rn | |||
0100nnnn00000011 stc.l sr, @-rn | |||
0100nnnn00010011 stc.l gbr, @-rn | |||
0100nnnn00100011 stc.l vbr, @-rn | |||
0100nnnn00110011 stc.l ssr, @-rn | |||
0100nnnn01000011 stc.l spc, @-rn | |||
0100nnnn10000011 stc.l r0_bank, @-rn | |||
0100nnnn10010011 stc.l r1_bank, @-rn | |||
0100nnnn10100011 stc.l r2_bank, @-rn | |||
0100nnnn10110011 stc.l r3_bank, @-rn | |||
0100nnnn11000011 stc.l r4_bank, @-rn | |||
0100nnnn11010011 stc.l r5_bank, @-rn | |||
0100nnnn11100011 stc.l r6_bank, @-rn | |||
0100nnnn11110011 stc.l r7_bank, @-rn | |||
0100nnnn00000010 sts.l mach, @-rn | |||
0100nnnn00010010 sts.l macl, @-rn | |||
0100nnnn00100010 sts.l pr, @-rn | |||
10000000nnnndddd mov.b r0, @(disp, rn) | |||
10000001nnnndddd mov.w r0, @(disp, rn) | |||
0001nnnnmmmmdddd mov.l rm, @(disp, rn) | |||
10000100mmmmdddd mov.b @(disp, rm), r0 | |||
10000101mmmmdddd mov.w @(disp, rm), r0 | |||
0101nnnnmmmmdddd mov.l @(disp, rm), rn | |||
0000nnnnmmmm0100 mov.b rm, @(r0, rn) | |||
0000nnnnmmmm0101 mov.w rm, @(r0, rn) | |||
0000nnnnmmmm0110 mov.l rm, @(r0, rn) | |||
0000nnnnmmmm1100 mov.b @(r0, rm), rn | |||
0000nnnnmmmm1101 mov.w @(r0, rm), rn | |||
0000nnnnmmmm1110 mov.l @(r0, rm), rn | |||
11000000dddddddd mov.b r0, @(disp, gbr) | |||
11000001dddddddd mov.w r0, @(disp, gbr) | |||
11000010dddddddd mov.l r0, @(disp, gbr) | |||
11000100dddddddd mov.b @(disp, gbr), r0 | |||
11000101dddddddd mov.w @(disp, gbr), r0 | |||
11000110dddddddd mov.l @(disp, gbr), r0 | |||
11001101iiiiiiii and.b #imm, @(r0, gbr) | |||
11001111iiiiiiii or.b #imm, @(r0, gbr) | |||
11001100iiiiiiii tst.b #imm, @(r0, gbr) | |||
11001110iiiiiiii xor.b #imm, @(r0, gbr) | |||
1001nnnndddddddd mov.w pcdisp, rn | |||
1101nnnndddddddd mov.l pcdisp, rn | |||
11000111dddddddd mova pcdisp, r0 | |||
0000mmmm00100011 braf rm | |||
0000mmmm00000011 bsrf rm | |||
10001011dddddddd bf jump8 | |||
10001111dddddddd bf/s jump8 | |||
10001001dddddddd bt jump8 | |||
10001101dddddddd bt/s jump8 | |||
1010dddddddddddd bra jump12 | |||
1011dddddddddddd bsr jump12 | |||
0111nnnniiiiiiii add #imm, rn | |||
11001001iiiiiiii and #imm, r0 | |||
10001000iiiiiiii cmp/eq #imm, r0 | |||
1110nnnniiiiiiii mov #imm, rn | |||
11001011iiiiiiii or #imm, r0 | |||
11001000iiiiiiii tst #imm, r0 | |||
11001010iiiiiiii xor #imm, r0 | |||
11000011iiiiiiii trapa #imm |
@ -1,26 +0,0 @@ | |||
# Instruction decoding table: 'sh4' | |||
# This table is an extension for SH4 processors. See 'asm-sh3.txt' for details | |||
# on the format. | |||
0000nnnn01110011 movco.l r0, @rn | |||
0000mmmm01100011 movli.l @rm, r0 | |||
0100mmmm10101001 movua.l @rm, r0 | |||
0100mmmm11101001 movua.l @rm+, r0 | |||
0000nnnn11000011 movca.l r0, @rn | |||
0000nnnn11100011 icbi @rn | |||
0000nnnn10010011 ocbi @rn | |||
0000nnnn10100011 ocbp @rn | |||
0000nnnn10110011 ocbwb @rn | |||
0000nnnn11010011 prefi @rn | |||
0000000010101011 synco | |||
0100mmmm00111010 ldc rm, sgr | |||
0100mmmm11111010 ldc rm, dbr | |||
0100mmmm00110110 ldc.l @rm+, sgr | |||
0100mmmm11110110 ldc.l @rm+, dbr | |||
0000nnnn00111010 stc sgr, rn | |||
0000nnnn11111010 stc dbr, rn | |||
0100nnnn00110010 stc.l sgr, @-rn | |||
0100nnnn11110010 stc.l dbr, @-rn |
@ -1,74 +0,0 @@ | |||
#include <fxos.h> | |||
#include <errors.h> | |||
#include <util.h> | |||
#include <string.h> | |||
/* asm_free_item(): Free an assembly instruction details */ | |||
static void asm_free_item(void *item) | |||
{ | |||
struct asm_insn *insn = item; | |||
free(insn->mnemonic); | |||
free(insn->literal1); | |||
free(insn->literal2); | |||
} | |||
/* asm_load(): Load an assembly table */ | |||
void asm_load(char const *file) | |||
{ | |||
err_context("assembly", file, NULL); | |||
if(!table_available()) | |||
{ | |||
err("too many tables, skipping"); | |||
err_pop(); | |||
return; | |||
} | |||
/* Map the file to memory */ | |||
int fd; | |||
size_t size; | |||
void *data = map(file, &fd, &size); | |||
if(!data) { err_pop(); return; } | |||
/* If the file is named "asm-x.txt", use "x" as the table name */ | |||
char *name = match_table_name(file, "asm", ".txt"); | |||
/* Parse the contents; the lexer in is [lexer-asm.l] and the parser is | |||
implemented as an automaton with basic error correction. */ | |||
int count; | |||
struct asm_insn *ins = lex_asm(data, size, &count); | |||
table_create("asm", name, asm_free_item, count, sizeof *ins, ins); | |||
unmap(data, fd, size); | |||
err_pop(); | |||
} | |||
/* Global storage for asm_match() */ | |||
static uint16_t asm_match_opcode; | |||
/* asm_match(): Check if an instruction matches a pattern */ | |||
int asm_match(void *item) | |||
{ | |||
struct asm_insn *insn = item; | |||
return ((insn->bits ^ asm_match_opcode) & insn->arg_mask) == 0; | |||
} | |||
/* asm_decode(): Match a 16-bit opcode against the assembly database */ | |||
int asm_decode(uint16_t opcode, struct asm_match *match, int next) | |||
{ | |||
asm_match_opcode = opcode; | |||
char const *table; | |||
struct asm_insn *insn = table_find("asm", asm_match, &table, next); | |||
if(!insn) return 1; | |||
/* Set match details */ | |||
match->insn = insn; | |||
match->table = table; | |||
match->n = (opcode >> insn->n_sh) & insn->n_mask; | |||
match->m = (opcode >> insn->m_sh) & insn->m_mask; | |||
match->d = (opcode >> insn->d_sh) & insn->d_mask; | |||
match->i = (opcode >> insn->i_sh) & insn->i_mask; | |||
return 0; | |||
} |
@ -1,268 +0,0 @@ | |||
#include <fxos.h> | |||
#include <errors.h> | |||
#include <stdio.h> | |||
#include <string.h> | |||
/* Architecture we are disassembling for */ | |||
static enum mpu mpu = MPU_GUESS; | |||
/* Current program counter */ | |||
static uint32_t pc = 0; | |||
/* Data chunk under disassembly (whole file) */ | |||
static uint8_t *data; | |||
static size_t len; | |||
/* Non-NULL when disassembling an OS */ | |||
static struct os const *os = NULL; | |||
/* pcdisp(): Compute the address of a PC-relative displacement | |||
@pc Current PC value | |||
@disp Displacement value (from opcode) | |||
@unit Number of bytes per step (1, 2 or 4), depends on instruction | |||
Returns the pointed location. */ | |||
static uint32_t pcdisp(uint32_t pc, uint32_t disp, int unit) | |||
{ | |||
pc += 4; | |||
if(unit == 4) pc &= ~3; | |||
return pc + disp * unit; | |||
} | |||
/* pcread(): Read data pointed through a PC-relative displacement | |||
@pc Current PC value | |||
@disp Displacement value (from opcode) | |||
@unit Number of bytes per set (also number of bytes read) | |||
@out If non-NULL, set to 1 if pointed address is out of the file | |||
Returns the value pointed at, an undefined value if out of bounds. */ | |||
static uint32_t pcread(uint32_t pc, uint32_t disp, int unit, int *out) | |||
{ | |||
/* Take physical addresses */ | |||
uint32_t addr = pcdisp(pc, disp, unit) & 0x1fffffff; | |||
if(out) *out = (addr + unit > len); | |||
uint32_t value = 0; | |||
while(unit--) value = (value << 8) | data[addr++]; | |||
return value; | |||
} | |||
/* matches(): Count number of matches for a single instruction | |||
@opcode 16-bit opcode value | |||
Returns the number of matching instructions in the database. */ | |||
static int matches(uint16_t opcode) | |||
{ | |||
struct asm_match match; | |||
int count = 0; | |||
while(!asm_decode(opcode, &match, count)) | |||
{ | |||
count++; | |||
} | |||
return count; | |||
} | |||
static void arg_output(int type, char const *literal, | |||
struct asm_match const *match, int opsize) | |||
{ | |||
int n = match->n; | |||
int m = match->m; | |||
int d = match->d; | |||
int i = match->i; | |||
/* Sign extensions of d to 8 and 12 bits */ | |||
int32_t d8 = (int8_t)d; | |||
int32_t d12 = (d & 0x800) ? (int32_t)(d | 0xfffff000) : (d); | |||
/* Sign extension of i to 8 bits */ | |||
int32_t i8 = (int8_t)i; | |||
int out_of_bounds; | |||
uint32_t addr; | |||
uint32_t value; | |||
switch(type) | |||
{ | |||
case LITERAL: | |||
printf("%s", literal); | |||
break; | |||
case IMM: | |||
printf("#%d", i8); | |||
break; | |||
case RN: | |||
printf("r%d", n); | |||
break; | |||
case RM: | |||
printf("r%d", m); | |||
break; | |||
case JUMP8: | |||
value = pcdisp(pc, d8, 2); | |||
printf("<%x", value); | |||
if(os) analysis_short(os, value | 0x80000000); | |||
printf(">"); | |||
break; | |||
case JUMP12: | |||
value = pcdisp(pc, d12, 2); | |||
printf("<%x", value); | |||
if(os) analysis_short(os, value | 0x80000000); | |||
printf(">"); | |||
break; | |||
case PCDISP: | |||
addr = pcdisp(pc, d, opsize); | |||
value = pcread(pc, d, opsize, &out_of_bounds); | |||
printf("<%x>", addr); | |||
if(out_of_bounds) printf("(out of bounds)"); | |||
else | |||
{ | |||
printf("(#0x%0*x", opsize * 2, value); | |||
if(os) analysis_short(os, value); | |||
printf(")"); | |||
} | |||
break; | |||
case AT_RN: | |||
printf("@r%d", n); | |||
break; | |||
case AT_RM: | |||
printf("@r%d", m); | |||
break; | |||
case AT_RMP: | |||
printf("@r%d+", m); | |||
break; | |||
case AT_RNP: | |||
printf("@r%d+", n); | |||
break; | |||
case AT_MRN: | |||
printf("@-r%d", n); | |||
break; | |||
case AT_DRN: | |||
printf("@(%d, r%d)", d * opsize, n); | |||
break; | |||
case AT_DRM: | |||
printf("@(%d, r%d)", d * opsize, m); | |||
break; | |||
case AT_R0RN: | |||
printf("@(r0, r%d)", n); | |||
break; | |||
case AT_R0RM: | |||
printf("@(r0, r%d)", m); | |||
break; | |||
case AT_DGBR: | |||
printf("@(%d, gbr)", d * opsize); | |||
break; | |||
} | |||
} | |||
static void instruction_output(uint16_t opcode, struct asm_match const *match) | |||
{ | |||
char const *mnemonic = match->insn->mnemonic; | |||
printf(" %5x: %04x %s", pc, opcode, mnemonic); | |||
/* Find out operation size */ | |||
size_t n = strlen(mnemonic); | |||
int opsize = 0; | |||
if(n >= 3 && mnemonic[n-2] == '.') | |||
{ | |||
int c = mnemonic[n-1]; | |||
opsize = (c == 'b') + 2 * (c == 'w') + 4 * (c == 'l'); | |||
} | |||
/* Output arguments */ | |||
if(!match->insn->arg1) return; | |||
printf("%*s", (int)(8-n), ""); | |||
arg_output(match->insn->arg1, match->insn->literal1, match, opsize); | |||
if(!match->insn->arg2) return; | |||
printf(", "); | |||
arg_output(match->insn->arg2, match->insn->literal2, match, opsize); | |||
} | |||
static void instruction_single(uint16_t opcode) | |||
{ | |||
struct asm_match match; | |||
asm_decode(opcode, &match, 0); | |||
instruction_output(opcode, &match); | |||
printf("\n"); | |||
} | |||
static void instruction_conflicts(uint16_t opcode, int count) | |||
{ | |||
struct asm_match match; | |||
printf("\n # conflicts[%d] on <%x>(%04x)\n", count, pc, opcode); | |||
for(int i = 0; i < count; i++) | |||
{ | |||
asm_decode(opcode, &match, i); | |||
instruction_output(opcode, &match); | |||
printf(" # table '%s'\n", match.table); | |||
} | |||
printf("\n"); | |||
} | |||
/* disassembly_os(): Disassemble an address or a syscall */ | |||
void disassembly_os(struct os const *src, struct disassembly const *opt) | |||
{ | |||
/* Initialize this file's global state */ | |||
mpu = src->mpu; | |||
pc = opt->start; | |||
data = src->data; | |||
len = src->len; | |||
os = src; | |||
/* Override MPU guesses if user requested a specific platform */ | |||
if(opt->mpu != MPU_GUESS) mpu = opt->mpu; | |||
/* Handle situations where the start address is a syscall */ | |||
if(opt->syscall) | |||
{ | |||
pc = os_syscall(os, opt->start); | |||
if(pc == (uint32_t)-1) | |||
{ | |||
err("syscall 0x%04x does not exist", opt->start); | |||
return; | |||
} | |||
} | |||
/* Take physical addresses */ | |||
pc &= 0x1fffffff; | |||
/* Cut the length if it reaches past the end of the file */ | |||
uint32_t limit = (pc + opt->len) & ~1; | |||
if(limit > os->len) limit = os->len; | |||
/* Enforce alignment of PC */ | |||
if(pc & 1) | |||
{ | |||
err("address is not 2-aligned, skipping 1 byte"); | |||
pc += 1; | |||
} | |||
uint8_t *data = os->data; | |||
while(pc < limit) | |||
{ | |||
if(os) | |||
{ | |||
int syscall = os_syscall_find(os, pc | 0x80000000); | |||
if(syscall == -1) | |||
syscall = os_syscall_find(os, pc | 0xa0000000); | |||
if(syscall != -1) | |||
{ | |||
struct sys_call const *s = sys_find(syscall); | |||
printf("\n<%x %%%03x", pc, syscall); | |||
if(s) printf(" %s", s->name); | |||
printf(">\n"); | |||
} | |||
} | |||
uint16_t opcode = (data[pc] << 8) | data[pc + 1]; | |||
int count = matches(opcode); | |||
if(count == 0) printf(" %5x: %04x\n", pc, opcode); | |||
else if(count == 1) instruction_single(opcode); | |||
else instruction_conflicts(opcode, count); | |||
pc += 2; | |||
} | |||
} |
@ -1,33 +0,0 @@ | |||
//--- | |||
// Cross-platform endianness conversion. (seriously?) | |||
//--- | |||
#ifndef FX_ENDIANNESS | |||
#define FX_ENDIANNESS | |||
#if defined(__APPLE__) | |||
#include <libkern/OSByteOrder.h> | |||
#define htobe16(x) OSSwapHostToBigInt16(x) | |||
#define htole16(x) OSSwapHostToLittleInt16(x) | |||
#define be16toh(x) OSSwapBigToHostInt16(x) | |||
#define le16toh(x) OSSwapLittleToHostInt16(x) | |||
#define htobe32(x) OSSwapHostToBigInt32(x) | |||
#define htole32(x) OSSwapHostToLittleInt32(x) | |||
#define be32toh(x) OSSwapBigToHostInt32(x) | |||
#define le32toh(x) OSSwapLittleToHostInt32(x) | |||
#define htobe64(x) OSSwapHostToBigInt64(x) | |||
#define htole64(x) OSSwapHostToLittleInt64(x) | |||
#define be64toh(x) OSSwapBigToHostInt64(x) | |||
#define le64toh(x) OSSwapLittleToHostInt64(x) | |||
#elif defined(__linux__) | |||
#include <sys/types.h> | |||
#endif | |||
#endif /* FX_ENDIANNESS */ |
@ -1,84 +0,0 @@ | |||
#include <errors.h> | |||
#include <stdarg.h> | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <errno.h> | |||
#define MAX 32 | |||
/* Array of context strings. */ | |||
static char const *context[MAX] = { NULL }; | |||
/* Number of active elements. */ | |||
static int elements = 0; | |||
/* push(): Push an element in the context buffer | |||
@el New element | |||
Returns non-zero if the buffer is full. */ | |||
static int push(char const *el) | |||
{ | |||
if(elements >= MAX - 2) return 1; | |||
context[elements++] = el; | |||
return 0; | |||
} | |||
/* err_context(): Push one or more context elements */ | |||
int err_context(char const *context, ...) | |||
{ | |||
va_list args; | |||
va_start(args, context); | |||
int x = 0; | |||
/* Delimiter between calls to err_context(), for err_pop() */ | |||
x |= push(NULL); | |||
x |= push(context); | |||
while(1) | |||
{ | |||
char const *el = va_arg(args, char const *); | |||
if(!el) break; | |||
x |= push(el); | |||
} | |||
va_end(args); | |||
return x; | |||
} | |||
/* err_pop(): Pop back context elements */ | |||
void err_pop(void) | |||
{ | |||
/* Clear everything until the last NULL delimiter */ | |||
while(--elements >= 0 && context[elements]) | |||
{ | |||
context[elements] = NULL; | |||
} | |||
} | |||
void err_debug(void) | |||
{ | |||
for(int i = 0; i < MAX; i++) fprintf(stderr, "%s: ", context[i]); | |||
fprintf(stderr, "\n"); | |||
} | |||
/* errf(): Emit an error message */ | |||
void errf(int flags, char const *str, ...) | |||
{ | |||
int saved_errno = errno; | |||
va_list args; | |||
va_start(args, str); | |||
for(int i = 0; i <= MAX-2 && (context[i] || context[i+1]); i++) | |||
{ | |||
if(context[i]) fprintf(stderr, "%s: ", context[i]); | |||
} | |||
vfprintf(stderr, str, args); | |||
if(flags & ERR_ERRNO) | |||
{ | |||
fprintf(stderr, ": %s", strerror(saved_errno)); | |||
} | |||
fprintf(stderr, "\n"); | |||
va_end(args); | |||
} |
@ -1,63 +0,0 @@ | |||
//--- | |||
// errors: Error message management | |||
// | |||
// An attempt at producing elaborate error messages providing context from | |||
// multiple function calls, with a small amount of code. | |||
// | |||
// Basically, caller functions define context and message parameters | |||
// before doing anything risky. When an error occurs in a sub-function, | |||
// the context is used to prepend relevant information and fill in the | |||
// error message where relevant. | |||
// | |||
// This means that the caller functions must know it advance what kind of | |||
// messages can be emitted to accurately set the context. Although it | |||
// would work through library calls, it is not very suitable. It's rather | |||
// meant for the logic of applications. | |||
//--- | |||
#ifndef FXOS_ERRORS | |||
#define FXOS_ERRORS | |||
/* Error levels */ | |||
#define ERR_CRIT 0x01 /* Aborts part or whole of the program's task */ | |||
#define ERR_ERR 0x02 /* Unexpected behavior */ | |||
#define ERR_WARN 0x03 /* Internal consistency issues */ | |||
#define ERR_LOG 0x04 /* Internal logging */ | |||
/* Error flags */ | |||
#define ERR_ERRNO 0x10 | |||
/* err_context(): Push one or more context elements | |||
@context Context string, must live until it is popped by err_pop() | |||
@... More contexts, same requirements as the first (NULL-terminated) | |||
Returns non-zero if the deepest context level (at least 15) is reached. */ | |||
int err_context(char const *context, ...); | |||
/* err_pop(): Pop back context elements | |||
Pops back all context elements that were added by the previous call to | |||
err_context(), no matter their number. */ | |||
void err_pop(void); | |||
/* errf(): Emit an error message | |||
Error message is printed via fprintf() and supports the same format. All the | |||
arguments are passed directly. | |||
Available flags are any combination of at most one error level: | |||
ERR_CRIT -- Unrecoverable error | |||
ERR_ERR -- Normal error | |||
ERR_WARN -- Warning | |||
ERR_LOG -- Logging | |||
and any of these boolean flags: | |||
ERR_ERRNO -- Also print strerror(errno) as a suffix (as perror()). | |||
Error levels might be used later on for filtering. Messages without level | |||
will always be displayed. | |||
@flags An OR-combination of the previous flags, or 0 | |||
@str Error messages | |||
@... Arguments to format [str] through fprintf() */ | |||
void errf(int flags, char const *str, ...); | |||
/* err(): Short error function */ | |||
#define err(str, ...) errf(0, str, ##__VA_ARGS__) | |||
#endif /* FXOS_ERRORS */ |
@ -1,410 +0,0 @@ | |||
//--- | |||
// fxos:fxos - Main interfaces | |||
//--- | |||
#ifndef FX_FXOS | |||
#define FX_FXOS | |||
#include <stdint.h> | |||
#include <stdlib.h> | |||
/* Microprocessor platforms */ | |||
enum mpu { | |||
MPU_GUESS = 0, | |||
MPU_SH7705 = 1, | |||
MPU_SH7305 = 2, | |||
}; | |||
/* | |||
** Memory (memory.c) | |||
*/ | |||
/* struct region: A valid memory region for at least one platform */ | |||
struct region | |||
{ | |||
uint32_t start; /* Start address */ | |||
uint32_t end; /* End address */ | |||
char const *name; /* Name, used to identify RAM dump files */ | |||
enum mpu platform; /* Platform hint or MPU_GUESS */ | |||
}; | |||
/* memory_region(): Find the region where an address is located | |||
Returns NULL if the address points to no valid memory. This function rejects | |||
addresses of peripheral registers in P3 or P4 space and only heeds for | |||
contiguous RAM or ROM areas. | |||
@address 32-bit virtual memory address | |||
Returns a region description matching the address, NULL if none is known.*/ | |||
struct region const *memory_region(uint32_t address); | |||
/* | |||
** General table storage (tables.c) | |||
*/ | |||
/* struct table: Parametric data table */ | |||
struct table { | |||
char const *type; /* Table type, set by user */ | |||
char *name; /* Table name, also set by user */ | |||
void (*free_item)(void *); /* Function to free individual items */ | |||
int count; /* Number of items in the table */ | |||
size_t size; /* Size of each item */ | |||
void *items; /* Table data */ | |||
}; | |||
/* table_available(): Whether a table can be allocated | |||
Returns non-zero if there is space left for a new table, zero if not. */ | |||
int table_available(void); | |||
/* table_create(): Create a new table | |||
Allocates a new table inside the global storage. The created table can be | |||
searched immediately with table_find(). | |||
@type Table type, expectedly a string constant | |||
@name Name string, this module takes ownership and will free() it | |||
@free_item Function to use to destroy items in the future | |||
@count Number of items | |||
@size Size of each item | |||
@items Full data array */ | |||
void table_create(char const *type, char *name, void (*free_item)(void *), | |||
int count, size_t size, void *items); | |||
/* table_find(): Find matching entries in the database tables | |||
This function traverses all the tables of type @type and returns all the | |||
elements [e] such that [match(e)] is non-zero. | |||
The search starts with [next=0] and returns the first match; further calls | |||
with [next!=0] will return more matching elements until no more are found | |||
(in which case this function returns NULL) or this function is called again | |||
with [next=0] to start a new search. | |||
@type Table filter by type | |||
@match Match function | |||
@name Set to matching table name, if not NULL | |||
@next Set it to 0 on the first call, and non-zero after that | |||
Returns a match if one is found, NULL otherwise. */ | |||
void *table_find(char const *type, int (*match)(void *), char const **name, | |||
int next); | |||
/* | |||
** Assembly tables (asm.c, lexer-asm.l) | |||
** These tables reference all assembler instructions used by fxos to | |||
** disassemble code. In case of conflict, fxos will disassemble the same | |||
** opcode several times. | |||
*/ | |||
/* struct asm_insn: Entry of an instruction table */ | |||
struct asm_insn { | |||
uint16_t bits; /* Opcode; arbitrary values for arguments */ | |||
uint16_t arg_mask; /* 1 for constant bits, 0 for arguments */ | |||
/* Position of the arguments */ | |||
uint8_t n_sh, m_sh, d_sh, i_sh; | |||
/* Masks indicating the length of arguments */ | |||
uint16_t n_mask, m_mask, d_mask, i_mask; | |||
char *mnemonic; /* NUL-terminated mnemonic */ | |||
int arg1; /* asm_arg member */ | |||
int arg2; /* asm_arg member */ | |||
char *literal1; /* When arg1 == LITERAL, argument string */ | |||
char *literal2; /* When arg2 == LITERAL, argument string */ | |||
}; | |||
/* enum asm_arg: Argument variants */ | |||
enum asm_arg { | |||
LITERAL=1, /* Literal string, eg. "vbr" or "@(r0, gbr)" */ | |||
IMM, RN, RM, /* "#imm", "rn", "rm" */ | |||
JUMP8, JUMP12, /* Jump from displacement (PC + disp) */ | |||
PCDISP, /* PC-displacement with data */ | |||
AT_RN, AT_RM, /* "@rn", "@rm" */ | |||
AT_RMP, AT_RNP, AT_MRN, /* Post-increment and pre-decrement */ | |||
AT_DRN, AT_DRM, /* Displacement structure addressing */ | |||
AT_R0RN, AT_R0RM, /* r0 structure addressing */ | |||
AT_DGBR, /* GBR addressing */ | |||
}; | |||
/* struct asm_match: Matching of a 16-bit code against an instruction | |||
Specifies the source instruction and the value of the parameters. The value | |||
of [m], [n], [d] or [i] is unspecified for parameters that are not used in | |||
the matched instruction's opcode. */ | |||
struct asm_match { | |||
/* Matching instruction */ | |||
struct asm_insn const *insn; | |||
char const *table; /* Table name */ | |||
int m, n, d, i; /* Parameter assignment */ | |||
}; | |||
/* asm_load(): Load an assembly table | |||
Loads all instructions described by @file into a table named "x" is @file's | |||
basename is on the form "asm-x.txt" and the whole basename otherwise. The | |||
resulting table is available immediately to use with asm_match(). | |||
Skips every row that does not conform to the file's syntax after printing a | |||
message to stderr. | |||
@file Input file path */ | |||
void asm_load(char const *file); | |||
/* asm_decode(): Match a 16-bit opcode against the assembly database | |||
This function searches matches of a 16-bit instruction code inside the | |||
instruction database. Depending on the database files currently loaded, | |||
there can be several matches; this function uses static variables to | |||
maintain state information through several calls. | |||
First call this function with [next] set to 0. If there is no match, the | |||
call will return non-zero and [*match] will be left unchanged. Otherwise, | |||
the first matching instruction will be described in [*match], the call will | |||
return 0 and internal static variables will be reset. | |||
Repeatedly call this function with [next != 0] to get further matches. The | |||
search ends when there are no more matches, in which case this function | |||
returns non-zero and [*match] is left unchanged. Any non-zero value for | |||
[next] is suitable. | |||
The name of the table providing the match is set in [match->table]. Please | |||
bear in mind, though, that table names do not uniquely identify tables. | |||
@opcode 16-bit opcode to be matched against the database | |||
@match Set to a description of the matching instruction (must not be NULL) | |||
@next Set it to 0 on the first call, and non-zero after that | |||
Returns 0 if a match is found, non-zero otherwise. */ | |||
int asm_decode(uint16_t opcode, struct asm_match *match, int next); | |||
/* asm_quit(): Unload all assembly tables | |||
Releases all memory held by the assembly table database. */ | |||
void asm_quit(void); | |||
/* lex_asm(): Assembly table lexer and parser | |||
Lexes and parses string @data of length @length, allocating and filling an | |||
instruction array whose size is stored in [*count] if @count is non-NULL. | |||
Prints messages to stderr for every syntax error in the file. | |||
@data Input memory, not NUL-terminated (typically a memory-mapped file) | |||
@length Length of input string | |||
@count Set to number of successfully decoded instructions if non-NULL | |||
Returns a free()able array of decoded instructions. */ | |||
struct asm_insn *lex_asm(void *data, size_t length, int *count); | |||
/* | |||
** Syscall tables (sys.c) | |||
*/ | |||
/* struct sys_call: Entry of a syscall table */ | |||
struct sys_call { | |||
uint32_t number; /* Syscall number */ | |||
char *name; /* Symbol or function name */ | |||
char *descr; /* Prototype or description */ | |||
}; | |||
/* sys_load(): Load a syscall table | |||
Loads a syscall description table. If @file is named "sys-x.txt" then the | |||
table name is set to "x", otherwise @file's basename. | |||
Prints syntax errors and skips invalid lines. | |||
@file Input file path */ | |||
void sys_load(char const *path); | |||
/* sys_find(): Find information on a given syscall number | |||
Traverse the syscall tables currently loaded and returns the first entry | |||
matching the provided syscall number, if any is found. | |||
@number Syscall number | |||
Returns a description of the syscall, NULL if none was found. */ | |||
struct sys_call const *sys_find(uint32_t number); | |||
/* sys_quit(): Release memory held by the syscall tables */ | |||
void sys_quit(void); | |||
/* lex_sys(): Syscall table lexer and parser | |||
Lexes and parses string @data of length @len, allocating and filling a | |||
syscall array whose size is stored in [*count] if @count is not NULL. | |||
Prints syntax errors on stderr. | |||
@data Input memory (typically memory-mapped file) | |||
@len Length of input | |||
@count Set to number of decoded entries if not NULL | |||
Returns a free()able table of decoded syscall entries. */ | |||
struct sys_call *lex_sys(void *data, size_t len, int *count); | |||
/* | |||
** Peripheral register tables (reg.c) | |||
*/ | |||
/* struct reg_address: Entry of a peripheral register table */ | |||
struct reg_address { | |||
uint32_t address; /* Register address */ | |||
char *name; /* Typically an upper-case dotted specifier */ | |||
}; | |||
/* reg_load(): Load a peripheral register table | |||
Loads a peripheral register listing. If @file is named "reg-x.txt" then the | |||
table name is set to "x", otherwise @file's basename. | |||
Prints syntax errors and skips invalid lines. Loaded data is available | |||
immediately through reg_find(). | |||
@file Input file path */ | |||
void reg_load(char const *path); | |||
/* reg_find(): Find information on a given peripheral register address | |||
Looks up the loaded tables and returns the first entry matching the given | |||
address (if any). | |||
@address Any input address | |||
Returns a pointer to the matching register, NULL if none was found. */ | |||
struct reg_address const *reg_find(uint32_t address); | |||
/* reg_quit(): Release memory held by the peripheral register tables */ | |||
void reg_quit(void); | |||
/* lex_reg(): Peripheral register table lexer and parser | |||
Lexes and parses @data (of length @length). Allocates and fills a register | |||
description array and stores its size in [*count] if @count is not NULL. | |||
Prints messages on stderr if there are syntax errors. | |||
@data Input string (needs not be NUL-terminated) | |||
@len Length of input | |||
@count Set to the number of decoded register addresses, if not NULL | |||
Returns a free()able table with the decoded data. */ | |||
struct reg_address *lex_reg(void *data, size_t len, int *count); | |||
/* | |||
** General OS operations (os.c) | |||
*/ | |||
/* struct os: Basic OS information */ | |||
struct os { | |||
void *data; /* Operating system dump */ | |||
size_t len; /* File length */ | |||
int fd; /* Underlying file descriptor */ | |||
char version[15]; /* NUL-terminated OS version string */ | |||
enum mpu mpu; /* User-provided or guessed MPU type */ | |||
uint32_t syscall_table; /* Syscall table address */ | |||
int syscalls; /* Number of valid syscalls found */ | |||
uint32_t footer; /* Footer address (-1 if not found) */ | |||
}; | |||
/* os_load(): Load an OS file and find out basic properties | |||
Guesses the MPU type, finds the syscall table and its size, finds the footer | |||
address. | |||
@path File path | |||
@os Will be filled with loaded data and information | |||
Returns non-zero in case of loading error or file format error. */ | |||
int os_load(char const *path, struct os *os); | |||
/* os_syscall(): Get the address of a syscall entry | |||
Does not check bounds, only returns (uint32_t)-1 if the requested entry of | |||
the table is past the end of the file. | |||
@os Source OS | |||
@syscall Syscall entry number | |||
Returns the syscall address. */ | |||
uint32_t os_syscall(struct os const *os, int syscall); | |||
/* os_syscall_find(): Find a syscall which points to an address | |||
This function looks for a syscall entry (among the ones that point to valid | |||
memory) whose value is @entry. | |||
@os Loaded OS structure | |||
@entry Researched value | |||
Returns a syscall ID if some is found, -1 otherwise. */ | |||
int os_syscall_find(struct os const *os, uint32_t entry); | |||
/* os_free(): Free an OS file opened with os_load() | |||
@os Loaded OS structure */ | |||
void os_free(struct os const *os); | |||
/* | |||
** File identification (info.c) | |||
*/ | |||
/* info_os(): Print general information on an OS file | |||
This function prints the OS metadata, traverses the syscall table, and | |||
shows a few details of known binary regions such as the footer. | |||
@os Input OS file */ | |||
void info_os(struct os const *os); | |||
/* info_binary(): Print general information on a binary file | |||
This function tries to determine the platform by looking for SH4-only | |||
instructions or SH7705 and SH7305-specific registers addresses. The analysis | |||
results are printed on stdout. | |||
@data Input file data (memory-mapped) | |||
@len Length of input */ | |||
void info_binary(void *data, size_t len); | |||
/* | |||
** Disassembling (disassembly.c) | |||
*/ | |||
/* struct disassembly: Disassembly options */ | |||
struct disassembly | |||
{ | |||
int binary; /* OS file (0) or binary file (1) */ | |||
enum mpu mpu; /* Force architecture (or MPU_GUESS) */ | |||
uint32_t start; /* Start address or syscall ID */ | |||
int syscall; /* Non-zero if [start] is a syscall ID */ | |||
uint32_t len; /* Length of disassembled region */ | |||
}; | |||
/* disassembly_os(): Disassemble an address or a syscall | |||
Produces a disassembly listing of the program on stdout, annotated with | |||
every piece of information that can be extracted from the OS. | |||
@os Operating system image to disassemble | |||
@opt Disassembly region and options */ | |||
void disassembly_os(struct os const *os, struct disassembly const *opt); | |||
/* | |||
** Blind analysis (analysis.c) | |||
*/ | |||
/* analysis_short(): Print a one-line summary for an address | |||
Prints a list of space-separated elements summarizing the information that | |||
can be found about the provided value (typically an address). This summary | |||
is often inserted in disassembled code as annotation. | |||
@os Source OS | |||
@value Analyzed value, often an address */ | |||
void analysis_short(struct os const *os, uint32_t value); | |||
/* struct analysis: In-depth analysis options */ | |||
struct analysis | |||
{ | |||
/* Force underlying architecture */ | |||
enum mpu mpu; | |||
/* Analysis mode */ | |||
enum { | |||
ANALYSIS_SYSCALL = 0x01, | |||
ANALYSIS_ADDRESS = 0x02, | |||
ANALYSIS_REGISTER = 0x04, | |||
ANALYSIS_FULL = 0x07, | |||
} type; | |||
/* Max number of printed occurrences */ | |||
int occurrences; | |||
}; | |||
#endif /* FX_FXOS */ |
@ -1,120 +0,0 @@ | |||
#include <fxos.h> | |||
#include <errors.h> | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <endianness.h> | |||
char const *info_str = | |||
"Header information:\n" | |||
" Bootcode timestamp (DateA) (0x8000ffb0) : %s\n" | |||
" Serial number (0x8000ffd0) : %s\n" | |||
" Bootcode checksum (0x8000fffc) : 0x%s\n" | |||
" OS version (0x80010020) : %s\n"; | |||
char const *footer_str = | |||
"\nFooter information:\n" | |||
" Detected footer address : 0x8%07x\n" | |||
" Langdata entries found : %d\n" | |||
" OS date (DateO) (0x8%07x)" " : %s\n" | |||
" OS checksum (0x8%07x)" " : 0x%s\n"; | |||
char const *syscall_str = | |||
"\nSyscall information:\n" | |||
" Syscall table address (0x8001007c) : 0x%08x\n" | |||
" Entries that point to valid memory : 0x%x\n" | |||
" First seemingly invalid entry : 0x%08x\n" | |||
" Syscall entries outside ROM:\n"; | |||
char const *syscall_nonrom_str = | |||
" %%%04x -> 0x%08x (%s memory)\n"; | |||
/* extract_str(): Extract a string from the OS file | |||
This function checks offsets and bounds and writes "(eof)" if the provided | |||
file is too small for the tested offset. */ | |||
static void extract_str(struct os const * os, uint32_t address, size_t size, | |||
char *dst) | |||
{ | |||
if(address + size > os->len) | |||
{ | |||
strcpy(dst, "(eof)"); | |||
return; | |||
} | |||
memcpy(dst, os->data + address, size); | |||
dst[size] = 0; | |||
} | |||
/* extract_int(): Extract a string from the OS file */ | |||
static void extract_int(struct os const *os, uint32_t address, char *dst) | |||
{ | |||
if(address + 4 > os->len) | |||
{ | |||
strcpy(dst, "(eof)"); | |||
return; | |||
} | |||
uint32_t x; | |||
memcpy(&x, os->data + address, 4); | |||
x = be32toh(x); | |||
sprintf(dst, "%08x", x); | |||
} | |||
/* info_os(): Print general information on an OS file */ | |||
void info_os(struct os const *os) | |||
{ | |||
void *data = os->data; | |||
size_t len = os->len; | |||
char bios_timestamp[15]; | |||
extract_str(os, 0xffb0, 14, bios_timestamp); | |||
char serial[9]; | |||
extract_str(os, 0xffd0, 8, serial); | |||
char bios_checksum[9]; | |||
extract_int(os, 0xfffc, bios_checksum); | |||
printf(info_str, bios_timestamp, serial, bios_checksum, os->version); | |||
if(os->footer == (uint32_t)-1) | |||
{ | |||
printf("\nFooter could not be found.\n"); | |||
} | |||
else | |||
{ | |||
uint32_t addr = os->footer + 8; | |||
int langdata = 0; | |||
while(addr + 8 < len && !memcmp(data + addr, "Langdata", 8)) | |||
{ | |||
langdata++; | |||
addr += 0x30; | |||
} | |||
char os_timestamp[15]; | |||
extract_str(os, addr, 14, os_timestamp); | |||
char os_checksum[9]; | |||
extract_int(os, addr + 0x18, os_checksum); | |||
printf(footer_str, os->footer, langdata, addr, os_timestamp, | |||
addr + 0x18, os_checksum); | |||
} | |||
printf(syscall_str, os->syscall_table, os->syscalls, | |||
os_syscall(os, os->syscalls)); | |||
int total = 0; | |||
for(int i = 0; i < os->syscalls; i++) | |||
{ | |||
uint32_t e = os_syscall(os, i); | |||
struct region const *r = memory_region(e); | |||
if(!r || !strcmp(r->name, "ROM")) continue; | |||
printf(syscall_nonrom_str, i, e, r->name); | |||
total++; | |||
} | |||
if(!total) printf(" (none)\n"); | |||
} |
@ -1,187 +0,0 @@ | |||
%{ | |||
#include <fxos.h> | |||
#include <errors.h> | |||
#include <util.h> | |||
/* Text value for parser */ | |||
static char *yylval; | |||
%} | |||
%option prefix="asm" | |||
%option noyywrap | |||
%option nounput | |||
code ^[01nmdi]{16} | |||
literal [^ ,\t\n]+|[^ ,\t\n(]*"("[^")"\n]*")"[^ ,\t\n]* | |||
space [ \t]+ | |||
%% | |||
^#[^\n]* ; | |||
{space} ; | |||
, ; | |||
[\n] yylineno++; | |||
{code} { yylval = strdup(yytext); return 0; } | |||
^.{0,16} { err("%d: invalid opcode at start of line", yylineno); } | |||
"#imm" { return IMM; } | |||
"rn" { return RN; } | |||
"rm" { return RM; } | |||
"jump8" { return JUMP8; } | |||
"jump12" { return JUMP12; } | |||
"pcdisp" { return PCDISP; } | |||
"@rn" { return AT_RN; } | |||
"@rm" { return AT_RM; } | |||
"@rm+" { return AT_RMP; } | |||
"@rn+" { return AT_RNP; } | |||
"@-rn" { return AT_MRN; } | |||
"@(disp,"[ ]*"rn)" { return AT_DRN; } | |||
"@(disp,"[ ]*"rm)" { return AT_DRM; } | |||
"@(r0,"[ ]*"rn)" { return AT_R0RN; } | |||
"@(r0,"[ ]*"rm)" { return AT_R0RM; } | |||
"@(disp",[ ]*"gbr)" { return AT_DGBR; } | |||
{literal} { yylval = strdup(yytext); return LITERAL; } | |||
<<EOF>> { return -1; } | |||
%% | |||
#include <stdio.h> | |||
/* set_code(): Build an efficient representation of an opcode | |||
Takes a 16-byte string as argument, representing the parameterized opcode, | |||
and computes a bit-based representation inside the assembly structure. | |||
@code 16-bit opcode made of '0', '1', 'm', 'n', 'd' and 'i' | |||
@insn Instruction object */ | |||
void set_code(char const *code, struct asm_insn *insn) | |||
{ | |||
insn->bits = insn->arg_mask = 0; | |||
insn->n_sh = insn->n_mask = 0; | |||
insn->m_sh = insn->m_mask = 0; | |||
insn->d_sh = insn->d_mask = 0; | |||
insn->i_sh = insn->i_mask = 0; | |||
for(int i = 0; i < 16; i++) | |||
{ | |||
int c = code[i]; | |||
/* Constant bits */ | |||
if(c == '0' || c == '1') | |||
{ | |||
insn->bits = (insn->bits << 1) | (c - '0'); | |||
insn->arg_mask <<= 1; | |||
continue; | |||
} | |||
/* Argument bits */ | |||
insn->bits <<= 1; | |||
insn->arg_mask = (insn->arg_mask << 1) | 1; | |||
if(c == 'n') | |||
{ | |||
insn->n_sh = 15 - i; | |||
insn->n_mask = (insn->n_mask << 1) | 1; | |||
} | |||
if(c == 'm') | |||
{ | |||
insn->m_sh = 15 - i; | |||
insn->m_mask = (insn->m_mask << 1) | 1; | |||
} | |||
if(c == 'd') | |||
{ | |||
insn->d_sh = 15 - i; | |||
insn->d_mask = (insn->d_mask << 1) | 1; | |||
} | |||
if(c == 'i') | |||
{ | |||
insn->i_sh = 15 - i; | |||
insn->i_mask = (insn->i_mask << 1) | 1; | |||
} | |||
} | |||
insn->arg_mask = ~insn->arg_mask; | |||
} | |||
/* lex_asm(): Assembly table lexer and parser */ | |||
struct asm_insn *lex_asm(void *data, size_t length, int *count) | |||
{ | |||
/* First count the number of instruction codes */ | |||
YY_BUFFER_STATE buf = yy_scan_bytes(data, length); | |||
yylineno = 1; | |||
int total = 0, t; | |||
while((t = yylex()) != -1) | |||
{ | |||
total += (t == 0); | |||
if(t == 0 || t == LITERAL) free(yylval); | |||
} | |||
yy_delete_buffer(buf); | |||
/* Allocate a large enough instruction array */ | |||
struct asm_insn *table = calloc(total, sizeof *table); | |||
if(!table) | |||
{ | |||
errf(ERR_ERRNO, "cannot allocate memory for database"); | |||
return 0; | |||
} | |||
/* Lex all instructions and fill in the array */ | |||
buf = yy_scan_bytes(data, length); | |||
yylineno = 1; | |||
struct asm_insn *insn = table - 1; | |||
int line = -1; | |||
int named = 1; | |||
while(1) | |||
{ | |||
t = yylex(); | |||
if(yylineno != line || t == 0 || t == -1) | |||
{ | |||
/* Finalize current instruction */ | |||
if(!named) err("%d: unnamed instruction", line); | |||
insn++; | |||
} | |||
if(t == -1) break; | |||
if(t == 0) | |||
{ | |||
set_code(yylval, insn); | |||
free(yylval); | |||
line = yylineno; | |||
named = 0; | |||
} | |||
else if(t == LITERAL && !named) | |||
{ | |||
insn->mnemonic = yylval; | |||
named = 1; | |||
} | |||
else if(!named) | |||
{ |