| git.druid.rocks | index | druid520 | jury | src/ | disasm.c |
src/disasm.c
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "isa.h"
#define VAR_ROW 4 /* bytes per data line, same width as the longest op */
typedef struct {
char name[32];
unsigned long at;
unsigned long len;
} var;
var vars[VARS_MAX];
int nvars = 0;
void loadvars(const char* binfile, unsigned long binlen);
void printvar(const unsigned char* prog, const var* v);
int main(int argc, char** argv);
/* reads binfile + ".vars" if its there, keeps vars sorted by address.
* no file just means no variables, everything gets decoded like
* before. a line that doesnt make sense gets skipped with a warning. */
void
loadvars(const char* binfile, unsigned long binlen)
{
char* name;
char line[128];
FILE* f;
var v;
int lineno = 0;
int i;
char extra;
name = malloc(strlen(binfile) + strlen(VARS_EXT) + 1);
if(name == NULL)
{
return;
}
strcpy(name, binfile);
strcat(name, VARS_EXT);
f = fopen(name, "rb");
if(f == NULL)
{
free(name);
return;
}
while(fgets(line, sizeof(line), f) != NULL)
{
lineno = lineno + 1;
if(sscanf(line, "%31s %lx %lx %c", v.name, &v.at, &v.len, &extra) != 3 ||
v.len == 0 || v.at >= binlen || v.len > binlen - v.at)
{
fprintf(stderr, "warn: %s line %d: bad variable, ignored.\n", name, lineno);
continue;
}
if(nvars == VARS_MAX)
{
fprintf(stderr, "warn: %s: too many variables, rest ignored.\n", name);
break;
}
/* insert sorted, drop anything that overlaps a neighbour */
for(i = nvars; i > 0 && vars[i - 1].at > v.at; i = i - 1)
{
}
if((i > 0 && vars[i - 1].at + vars[i - 1].len > v.at) ||
(i < nvars && v.at + v.len > vars[i].at))
{
fprintf(stderr, "warn: %s line %d: '%s' overlaps another variable, ignored.\n", name, lineno, v.name);
continue;
}
memmove(&vars[i + 1], &vars[i], (nvars - i) * sizeof(var));
vars[i] = v;
nvars = nvars + 1;
}
fclose(f);
free(name);
}
/* "name;" then VAR_ROW bytes a line as "; 0x.. 0x..", which is valid
* asm that carries on the variable, so this still reassembles. the
* comment shows the bytes as characters, od -c style, lined up under
* the hex column's width. */
void
printvar(const unsigned char* prog, const var* v)
{
char hex[VAR_ROW * 3 + 1];
char src[VAR_ROW * 5 + 3];
unsigned long a;
int k;
int c;
printf("%04lx: %-12s %s; # %lu byte(s)\n", v->at, "", v->name, v->len);
for(a = v->at; a < v->at + v->len; a = a + VAR_ROW)
{
strcpy(src, ";");
for(k = 0; k < VAR_ROW && a + k < v->at + v->len; k = k + 1)
{
sprintf(hex + k * 3, "%02x ", prog[a + k]);
sprintf(src + strlen(src), " 0x%02x", prog[a + k]);
}
printf("%04lx: %-12s %-22s #", a, hex, src);
for(k = 0; k < VAR_ROW && a + k < v->at + v->len; k = k + 1)
{
c = prog[a + k];
switch(c)
{
case '\0': printf(" \\0"); break;
case '\t': printf(" \\t"); break;
case '\n': printf(" \\n"); break;
case '\r': printf(" \\r"); break;
default: printf(" %c", isprint(c) ? c : '.'); break;
}
}
printf("\n");
}
}
/* prints "addr: bytes asm". the asm column is valid asm source,
* a byte that doesnt start a valid instruction comes out as a raw byte
* with a comment saying why, so disasm output always assembles back to
* the exact same binary. variables from prog.bin.vars come out as data,
* and no instruction is decoded across the start of one. */
int
main(int argc, char** argv)
{
const char* infile = "prog.bin";
FILE* in;
static unsigned char prog[RAM_SIZE + 1];
unsigned long len;
unsigned long pc;
unsigned long end;
char hex[INS_MAX * 3 + 1];
char txt[INS_TXT];
ins i;
int st;
int k;
int vi = 0;
if(argc > 2)
{
fprintf(stderr, "err: bad usage.\n");
fprintf(stderr, "usage: %s [in.bin|-]\n", argv[0]);
return 1;
}
if(argc == 2)
{
infile = argv[1];
}
if(strcmp(infile, "-") == 0)
{
in = stdin;
}
else
{
in = fopen(infile, "rb");
if(in == NULL)
{
fprintf(stderr, "err: cant open %s.\n", infile);
return 1;
}
}
len = fread(prog, 1, RAM_SIZE + 1, in);
if(in != stdin)
{
fclose(in);
}
if(len > RAM_SIZE)
{
fprintf(stderr, "err: %s is bigger than ram (%d bytes).\n", infile, RAM_SIZE);
return 1;
}
if(in != stdin)
{
loadvars(infile, len);
}
pc = 0;
while(pc < len)
{
if(vi < nvars && vars[vi].at == pc)
{
printvar(prog, &vars[vi]);
pc = pc + vars[vi].len;
vi = vi + 1;
continue;
}
end = vi < nvars ? vars[vi].at : len;
st = isadecode(prog, end, pc, &i);
if(st != DEC_OK)
{
printf("%04lx: %02x %02x # %s\n", pc, prog[pc], prog[pc],
st == DEC_SHORT ? "cut off." : DECMSG[st]);
pc = pc + 1;
continue;
}
hex[0] = '\0';
for(k = 0; k < i.len; k = k + 1)
{
sprintf(hex + k * 3, "%02x ", prog[pc + k]);
}
isafmt(&i, txt);
printf("%04lx: %-12s %s\n", pc, hex, txt);
pc = pc + i.len;
}
fprintf(stderr, "ok: disassembled %lu bytes.\n", len);
return 0;
}