Browse Source

fxos: remove fxos from this repository

The tool is now hosted on its own repository Lephenixnoir/fxos.
pull/4/head
Lephenixnoir 5 months ago
parent
commit
eeeb0fa6b1
Signed by: Lephenixnoir <sebastien.michelland@protonmail.com> GPG Key ID: 1BBA026E13FC0495
28 changed files with 10 additions and 3479 deletions
  1. +3
    -24
      Makefile
  2. +6
    -14
      README.md
  3. +1
    -5
      configure
  4. +0
    -22
      fxos/analysis.c
  5. +0
    -220
      fxos/asm-sh3.txt
  6. +0
    -26
      fxos/asm-sh4.txt
  7. +0
    -74
      fxos/asm.c
  8. +0
    -268
      fxos/disassembly.c
  9. +0
    -33
      fxos/endianness.h
  10. +0
    -84
      fxos/errors.c
  11. +0
    -63
      fxos/errors.h
  12. +0
    -410
      fxos/fxos.h
  13. +0
    -120
      fxos/info.c
  14. +0
    -187
      fxos/lexer-asm.l
  15. +0
    -117
      fxos/lexer-reg.l
  16. +0
    -123
      fxos/lexer-sys.l
  17. +0
    -301
      fxos/main.c
  18. +0
    -38
      fxos/memory.c
  19. +0
    -91
      fxos/os.c
  20. +0
    -114
      fxos/reg-sh7305.txt
  21. +0
    -141
      fxos/reg-simlo.txt
  22. +0
    -58
      fxos/reg.c
  23. +0
    -7
      fxos/sys-lephe.txt
  24. +0
    -656
      fxos/sys-simlo.txt
  25. +0
    -59
      fxos/sys.c
  26. +0
    -82
      fxos/tables.c
  27. +0
    -98
      fxos/util.c
  28. +0
    -44
      fxos/util.h

+ 3
- 24
Makefile View File

@@ -24,15 +24,8 @@ bin = $(TARGETS:%=bin/%)

# fxconv has no sources files because it's written in Python, and fxsdk has no
# source either because it's written in Bash.
src = $(wildcard $1/*.c)
src-fxg1a := $(call src,fxg1a)
src-fxos := $(call src,fxos)
lex-fxos := $(wildcard fxos/*.l)

obj = $($1:%=build/%.o)
lex = $($1:%.l=build/%.yy.c.o)
obj-fxg1a := $(call obj,src-fxg1a)
obj-fxos := $(call obj,src-fxos) $(call lex,lex-fxos)
src-fxg1a := $(wildcard fxg1a/*.c)
obj-fxg1a := $(src-fxg1a:%=build/%.o)

# Sed command to copy install path to fxsdk.sh. On Mac OS, BSD sed is used so
# we need to do it a bit differently with a printf helper to insert a literal
@@ -48,7 +41,6 @@ all: $(bin)

all-fxsdk: bin/fxsdk.sh
all-fxg1a: bin/fxg1a
all-fxos: bin/fxos

# Explicit targets

@@ -56,8 +48,6 @@ bin/fxsdk.sh: fxsdk/fxsdk.sh | bin/
sed -e $(sed) $< > $@
bin/fxg1a: $(obj-fxg1a) | bin/
gcc $^ -o $@ $(lflags)
bin/fxos: $(obj-fxos) | bin/
gcc $^ -o $@ $(lflags)

bin/:
mkdir -p $@
@@ -70,12 +60,6 @@ build/%.c.o: %.c
@mkdir -p $(dir $@)
gcc -c $< -o $@ $(cflags) $(dflags)

# Flex lexers for the fxos database
build/fxos/lexer-%.yy.c: fxos/lexer-%.l
flex -o $@ -s $<
build/fxos/lexer-%.yy.c.o: build/fxos/lexer-%.yy.c
gcc -c $< -o $@ $(cflags) -Wno-unused-function $(dflags) -I fxos

#
# Dependency system, misc.
#
@@ -91,8 +75,6 @@ Makefile.cfg:

.PHONY: all clean distclean

.PRECIOUS: build/fxos/lexer-%.yy.c

#
# Installing
#
@@ -110,7 +92,6 @@ install: $(bin)
install -d $(PREFIX)/bin
install -d $(PREFIX)/share/fxsdk
install $(bin:bin/fxsdk.sh=) $(m755) $(PREFIX)/bin
install fxos/*.txt $(m644) $(PREFIX)/share/fxsdk
install -d $(PREFIX)/share/fxsdk/assets
install fxsdk/assets/* $(m644) $(PREFIX)/share/fxsdk/assets
install bin/fxsdk.sh $(m755) $(PREFIX)/bin/fxsdk
@@ -118,7 +99,7 @@ install: $(bin)
install fxconv/fxconv.py $(m644) $(PREFIX)/bin

uninstall:
rm -f $(PREFIX)/bin/{fxsdk,fxg1a,fxos,fxconv,fxconv.py}
rm -f $(PREFIX)/bin/{fxsdk,fxg1a,fxconv,fxconv.py}
rm -rf $(PREFIX)/share/fxsdk

#
@@ -127,8 +108,6 @@ uninstall:

clean-fxg1a:
@rm -rf build/fxg1a
clean-fxos:
@rm -rf build/fxos

clean:
@rm -rf build


+ 6
- 14
README.md View File

@@ -15,7 +15,10 @@ favorite operating system.

## Tool description

### Project management (_fxsdk_)
A tool called fxos used to live here and has now moved to [its own
repository](/Lephenixnoir/fxos).

**Project management (_fxsdk_)**

`fxsdk` lets you set up projects almost instantly with a default folder
structure and a working Makefile linking against gint for both fx-9860G and
@@ -28,7 +31,7 @@ with the role and description of each option.
`fxsdk` only writes files at project creation time, so you keep control over
your build system and configuration - it just helps you get started faster.

### G1A file generation (_fxg1a_)
**G1A file generation (_fxg1a_)**

`fxg1a` is a versatile g1a file editor that creates, edits and dumps the header
of fx-9860G add-ins files. It is used to build a g1a file out of a binary
@@ -37,22 +40,12 @@ program.
It supports PNG icons, checking the validity and checksums of the header,
repairing broken headers and dumping both the application data and icon.

### Data conversion (_fxconv_)
**Data conversion (_fxconv_)**

`fxconv` is a tool that interacts specifically with gint. It converts data
files such as images and fonts into gint-specific format and embeds the result
into object files that expose a single variable.

### OS disassembly and study (_fxos_)

`fxos` is a powerful disassembler and OS fiddling tool. It knows the structure
of OS files and can extract metadata, language information and syscalls through
a simple command-line interface.

Its key feature is the production of annotated assembler listings where
syscalls invocations and peripheral registers are identified and named. This
makes it a lot easier to climb back abstraction levels from the code.

## Build instructions

The fxSDK is platform-agnostic; a single install will cover any target
@@ -69,7 +62,6 @@ Broadly, you will need:
* `fxsdk` if you want to benefit from the automated project setup
* `fxg1a` if you want to compile add-ins for fx-9860G
* `fxconv` if you want to compile gint or develop add-ins with it
* `fxos` if you want to disassemble or study OS dumps

Each tool can be enabled or disabled with `--enable-<tool>` and
`--disable-<tool>`. For a default build you need no arguments:


+ 1
- 5
configure View File

@@ -10,7 +10,6 @@ PREFIX="$HOME/.local"
BUILD_fxsdk=1
BUILD_fxconv=1
BUILD_fxg1a=1
BUILD_fxos=1

#
# Tool name checking
@@ -20,8 +19,7 @@ check()
{
[[ $1 = "fxsdk" ]] ||
[[ $1 = "fxconv" ]] ||
[[ $1 = "fxg1a" ]] ||
[[ $1 = "fxos" ]]
[[ $1 = "fxg1a" ]]
}

#
@@ -38,7 +36,6 @@ Tool selection:
"fxsdk" Command-line options (you generally want this)
"fxconv" Asset conversion for gint (or any 4-aligned-VRAM system)
"fxg1a" G1A file wrapper, editor and analyzer
"fxos" OS fiddling tool, including syscall disassembly

--enable-<tool> Build and install the selected tool [default]
--disable-<tool> Do not build or install the selected tool
@@ -100,7 +97,6 @@ gen()
[[ $BUILD_fxsdk = 1 ]] && echo -n " fxsdk"
[[ $BUILD_fxconv = 1 ]] && echo -n " fxconv"
[[ $BUILD_fxg1a = 1 ]] && echo -n " fxg1a"
[[ $BUILD_fxos = 1 ]] && echo -n " fxos"

echo ""
}


+ 0
- 22
fxos/analysis.c View File

@@ -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);
}

+ 0
- 220
fxos/asm-sh3.txt View File

@@ -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

+ 0
- 26
fxos/asm-sh4.txt View File

@@ -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

+ 0
- 74
fxos/asm.c View File

@@ -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;
}

+ 0
- 268
fxos/disassembly.c View File

@@ -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;
}
}

+ 0
- 33
fxos/endianness.h View File

@@ -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 */

+ 0
- 84
fxos/errors.c View File

@@ -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);
}

+ 0
- 63
fxos/errors.h View File

@@ -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 */

+ 0
- 410
fxos/fxos.h View File

@@ -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 */

+ 0
- 120
fxos/info.c View File

@@ -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");
}

+ 0
- 187
fxos/lexer-asm.l View File

@@ -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)
{
err("%d: missing mnemonic", line);
}
else if(!insn->arg1)
{
insn->arg1 = t;
if(t == LITERAL) insn->literal1 = yylval;
}
else if(!insn->arg2)
{
insn->arg2 = t;
if(t == LITERAL) insn->literal2 = yylval;
}
}

yy_delete_buffer(buf);
if(count) *count = insn - table;
return table;
}

+ 0
- 117
fxos/lexer-reg.l View File

@@ -1,117 +0,0 @@
%{
#include <fxos.h>
#include <errors.h>
#include <util.h>

/* Values for tokens generated by the lexer */
static union {
char *text;
long long integer;
} yylval;

/* Token list */
#define NUMBER 0
#define SYMBOL 1

%}

%option prefix="reg"
%option noyywrap
%option nounput

decimal 0|[1-9][0-9]*
octal 0[0-7]+
hexa 0x[0-9a-fA-F]+
number {decimal}|{octal}|{hexa}

symbol [^0-9 \t\n][^\n]*

space [ \t]+

%%

^#[^\n]* ;
{space} ;
[\n] yylineno++;

{number} { yylval.integer = integer(yytext, NULL); return NUMBER; }
{symbol} { yylval.text = strdup(yytext); return SYMBOL; }

<<EOF>> return -1;

%%

#include <stdio.h>

/* lex_reg(): Peripheral register table lexer and parser */
struct reg_address *lex_reg(void *data, size_t len, int *count)
{
/* Count number of register descriptions (upper bound) */

YY_BUFFER_STATE buf = yy_scan_bytes(data, len);
yylineno = 1;

int total = 0, t;
while((t = yylex()) != -1)
{
total += (t == NUMBER);
if(t == SYMBOL) free(yylval.text);
}

yy_delete_buffer(buf);

/* Allocate a large enough register array */

struct reg_address *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, len);
yylineno = 1;

struct reg_address *reg = table - 1;
int line = -1;

while(1)
{
t = yylex();

if(t == NUMBER || t == -1)
{
/* Finalize current instruction */
if(reg >= table && !reg->name)
err("%d: unnamed register", line);
else reg++;
}
if(t == -1) break;

if(t == NUMBER)
{
reg->address = yylval.integer;
line = yylineno;
}
else if(t == SYMBOL && reg < table)
{
err("%d: expected register address", yylineno);
free(yylval.text);
}
else if(t == SYMBOL && !reg->name)
{
reg->name = yylval.text;
}
else if(t == SYMBOL)
{
err("%d: excess names for 0x%08x\n", reg->address);
free(yylval.text);
}
}

yy_delete_buffer(buf);
if(count) *count = reg - table;
return table;
}

+ 0
- 123
fxos/lexer-sys.l View File

@@ -1,123 +0,0 @@
%{
#include <fxos.h>
#include <errors.h>
#include <util.h>

/* Values for tokens generated by the lexer */
static union {
char *text;
long long integer;
} yylval;

/* Token list */
#define NUMBER 0
#define TEXT 1

%}

%option prefix="sys"
%option noyywrap
%option nounput

decimal 0|[1-9][0-9]*
octal 0[0-7]+
hexa 0x[0-9a-fA-F]+
number {decimal}|{octal}|{hexa}

space [ \t]+

%%

^#[^\n]* ;
{space} ;
[\n] yylineno++;

{number} { yylval.integer = integer(yytext, NULL); return NUMBER; }
[^ \t\n0-9].* { yylval.text = strdup(yytext); return TEXT; }

<<EOF>> return -1;

%%

#include <stdio.h>

/* lex_sys(): Syscall table lexer and parser */
struct sys_call *lex_sys(void *data, size_t len, int *count)
{
/* Count the number of syscalls */

YY_BUFFER_STATE buf = yy_scan_bytes(data, len);
yylineno = 1;

int total = 0, t;
while((t = yylex()) != -1)
{
total += (t == NUMBER);
if(t == TEXT) free(yylval.text);
}

yy_delete_buffer(buf);

/* Allocate a large enough syscall array */

struct sys_call *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, len);
yylineno = 1;

struct sys_call *call = table - 1;
int line = -1;
int named = 1;

while(1)
{
t = yylex();

if(t == NUMBER || t == -1)
{
/* Finalize current instruction */
if(!named) err("%d: unnamed syscall", line);
else call++;
}
if(t == -1) break;

if(t == NUMBER)
{
call->number = yylval.integer;
line = yylineno;
named = 0;
}
else if(t == TEXT && call < table)
{
err("%d: expected syscall id", yylineno);
free(yylval.text);
}
else if(t == TEXT && named == 0)
{
call->name = yylval.text;
named = 1;
}
else if(t == TEXT && named == 1)
{
call->descr = yylval.text;
named = 2;
}
else if(t == TEXT)
{
err("%d: excess qualifiers for syscall 0x%03x", line,
call->number);
free(yylval.text);
}
}

yy_delete_buffer(buf);
if(count) *count = call - table;
return table;
}

+ 0
- 301
fxos/main.c View File

@@ -1,301 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>

#include <fxos.h>
#include <errors.h>
#include <util.h>

static char const *help_string =
"usage: %1$s info (<os file> | -b <binary file>)\n"
" %1$s disasm <os file> (-a <address> | -s <syscall id>) [options...]\n"
" %1$s disasm -b <binary file> [options...]\n"
" %1$s analyze [-f] [-s] [-a] [-r] <number> <os file> [options...]\n"
"\n"
"fxos is a reverse-engineering tool to disassemble and analyze fx9860g-like\n"
"OS dumps, providing efficient annotations through an editable database.\n"
"\n"
"Commands:\n"
" info Identify an OS image: version, platform, date, checksums...\n"
" Identify the architecture of a binary file.\n"
" disasm Disassemble and annotate code with relative address targets,\n"
" syscall invocations and hints about memory structure.\n"
" analyze Dig an address or syscall number, finding syscall references,\n"
" 4-aligned occurrences, memory region and probable role.\n"
"\n"
"General options:\n"
" -b Work with an arbitrary binary file, not an OS\n"
"\n"
"Table extensions:\n"
" --table-asm <file> Use instruction codes and mnemonics from <file>\n"
" --table-sys <file> Use syscall prototypes and descriptions from <file>\n"
" --table-reg <file> Use peripheral register addresses from <file>\n"
"\n"
"Disassembly options:\n"
" -a <address> Start disassembling at this address\n"
" -s <syscall id> Start disassembling at this syscall's address\n"
" -l <length> Length of region\n"
" -3, --sh3 Assume SH3 OS and platform (default: guess)\n"
" -4, --sh4 Assume SH4 OS and platform (default: guess)\n"
"\n"
"Analysis modes:\n"
" -f, --full Run all analysis passes on <number> (same as -sar)\n"
" -s, --syscall Run syscall ID analysis\n"
" -a, --address Run code/data address analyis\n"
" -r, --register Run peripheral register analysis\n"
"\n"
"Analysis options:\n"
" --occurrences <num> Show at most <num> occurrences (integer or \"all\")\n"
"\n"
"All numbers support base prefixes '0' (octal) and '0x' (hexadecimal).\n";

#define OPT_ASM 1
#define OPT_SYS 2
#define OPT_REG 3
#define OPT_OCC 4

/*
** "info" command
*/

int main_info(int argc, char **argv)
{
int error=0, option=0, binary=0;

struct option const longs[] = {
{ "help", no_argument, NULL, 'h' },
};

while(option >= 0 && option != '?')
switch((option = getopt_long(argc, argv, "hb", longs, NULL)))
{
case 'h':
err(help_string, argv[0]);
break;
case 'b':
binary = 1;
break;
case '?':
error = 1;
}

if(error) return 1;
char const *path = argv[optind + 1];

if(!path)
{
err(help_string, argv[0]);
return 1;
}

err_context(path, NULL);

struct os os;
if(os_load(path, &os)) { err_pop(); return 1; }

if(binary)
{
err("TODO: Binary file info x_x");
}
else
{
info_os(&os);
}

os_free(&os);
err_pop();
return 0;
}

/*
** "disasm" command
*/

int main_disassembly(int argc, char **argv)
{
int error=0, option=0;

/* First load some of fxos' default resources */
asm_load(FXSDK_PREFIX "/share/fxsdk/asm-sh3.txt");
asm_load(FXSDK_PREFIX "/share/fxsdk/asm-sh4.txt");
sys_load(FXSDK_PREFIX "/share/fxsdk/sys-simlo.txt");
sys_load(FXSDK_PREFIX "/share/fxsdk/sys-lephe.txt");
reg_load(FXSDK_PREFIX "/share/fxsdk/reg-sh7305.txt");
reg_load(FXSDK_PREFIX "/share/fxsdk/reg-simlo.txt");

struct disassembly opt = { 0 };
opt.mpu = MPU_GUESS;
opt.len = 0x20;

struct option const longs[] = {
{ "help", no_argument, NULL, 'h' },
{ "sh3", no_argument, NULL, '3' },
{ "sh4", no_argument, NULL, '4' },
{ "table-asm", required_argument, NULL, OPT_ASM },
{ "table-sys", required_argument, NULL, OPT_SYS },
{ "table-reg", required_argument, NULL, OPT_REG },
};

while(option >= 0 && option != '?')
switch((option = getopt_long(argc, argv, "hb34a:s:l:", longs, NULL)))
{
case 'h':
err(help_string, argv[0]);
break;
case 'b':
opt.binary = 1;