288 lines
6.7 KiB
C
288 lines
6.7 KiB
C
#if !defined(__gcc)
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
#endif
|
|
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
|
|
#include "exp.h"
|
|
#include "vm.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#if defined(_MSC_VER)
|
|
#include "getopt.h"
|
|
#else
|
|
#include <getopt.h>
|
|
#endif
|
|
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#define DEBUG_VAL(x) fprintf(stderr, #x " = %d\n", x)
|
|
|
|
#include "mnemonics.h"
|
|
|
|
const char *commandStrings[] =
|
|
{
|
|
// VM_CMD_COPY 0
|
|
"copy",
|
|
// VM_CMD_STORE 1
|
|
"store",
|
|
// VM_CMD_LOAD 2
|
|
"load",
|
|
// VM_CMD_GET 3
|
|
"get",
|
|
// VM_CMD_SET 4
|
|
"set",
|
|
// VM_CMD_BPGET 5
|
|
"bpget",
|
|
// VM_CMD_BPSET 6
|
|
"bpset",
|
|
// VM_CMD_CPGET 7
|
|
"cpget",
|
|
// VM_CMD_MATH 8
|
|
"math",
|
|
// VM_CMD_SPGET 9
|
|
"spget",
|
|
// VM_CMD_SPSET 10
|
|
"spset",
|
|
// VM_CMD_SYSCALL 11
|
|
"syscall",
|
|
// VM_CMD_HWIO 12
|
|
"hwio",
|
|
};
|
|
|
|
int disassembleVerbose = 0;
|
|
|
|
void disassemble(instruction_t *list, uint32_t count, uint32_t base, FILE *f)
|
|
{
|
|
int v = disassembleVerbose;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
instruction_t instr = list[i];
|
|
|
|
fprintf(f, "%8X: ", base + i);
|
|
|
|
const mnemonic_t *knownInstruction = NULL;
|
|
|
|
for (int j = 0; mnemonics[j].name != NULL; j++)
|
|
{
|
|
if (memcmp(&instr, &mnemonics[j].instr, sizeof(instruction_t) - sizeof(uint32_t)) == 0) {
|
|
knownInstruction = &mnemonics[j];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (knownInstruction != NULL)
|
|
{
|
|
fprintf(f, "%s", knownInstruction->name);
|
|
if (instr.argument != 0 || instr.input0 == VM_INPUT_ARG)
|
|
{
|
|
if(instr.output == VM_OUTPUT_JUMP || instr.output == VM_OUTPUT_JUMPR)
|
|
fprintf(f, " 0x%X", instr.argument);
|
|
else
|
|
fprintf(f, " %d", instr.argument);
|
|
}
|
|
fprintf(f, "\n");
|
|
continue;
|
|
}
|
|
|
|
switch (instr.execN)
|
|
{
|
|
case VM_EXEC_0: fprintf(f, "[ex(n)=0] "); break;
|
|
case VM_EXEC_1: fprintf(f, "[ex(n)=1] "); break;
|
|
case VM_EXEC_X: if (v) fprintf(f, "[ex(n)=x] "); break;
|
|
}
|
|
|
|
switch (instr.execZ)
|
|
{
|
|
case VM_EXEC_0: fprintf(f, "[ex(z)=0] "); break;
|
|
case VM_EXEC_1: fprintf(f, "[ex(z)=1] "); break;
|
|
case VM_EXEC_X: if (v) fprintf(f, "[ex(z)=x] "); break;
|
|
}
|
|
|
|
switch (instr.input0)
|
|
{
|
|
case VM_INPUT_ZERO: if (v) fprintf(f, "[i0:zero] "); break;
|
|
case VM_INPUT_POP: fprintf(f, "[i0:pop] "); break;
|
|
case VM_INPUT_PEEK: fprintf(f, "[i0:peek] "); break;
|
|
case VM_INPUT_ARG: fprintf(f, "[i0:arg] "); break;
|
|
}
|
|
|
|
switch (instr.input1)
|
|
{
|
|
case VM_INPUT_ZERO: if (v) fprintf(f, "[i1:zero] "); break;
|
|
case VM_INPUT_POP: fprintf(f, "[i1:pop] "); break;
|
|
// case VM_INPUT_PEEK: fprintf(f, "[i1:peek] "); break;
|
|
// case VM_INPUT_ARG: fprintf(f, "[i1:arg] "); break;
|
|
}
|
|
|
|
if (instr.command <= 12)
|
|
fprintf(f, "%s", commandStrings[instr.command]);
|
|
else
|
|
fprintf(f, "undefined [cmd:%d]", instr.command);
|
|
|
|
if (instr.cmdinfo != 0)
|
|
{
|
|
fprintf(f, " [ci:%d]", instr.cmdinfo);
|
|
}
|
|
|
|
if (instr.argument != 0 || instr.input0 == VM_INPUT_ARG)
|
|
{
|
|
if (instr.output == VM_OUTPUT_JUMP || instr.output == VM_OUTPUT_JUMPR)
|
|
fprintf(f, " 0x%X", instr.argument);
|
|
else
|
|
fprintf(f, " %d", instr.argument);
|
|
}
|
|
|
|
switch (instr.flags)
|
|
{
|
|
case VM_FLAG_NO: if (v) fprintf(f, " [f:no]"); break;
|
|
case VM_FLAG_YES: fprintf(f, " [f:yes]"); break;
|
|
}
|
|
|
|
switch (instr.output)
|
|
{
|
|
case VM_OUTPUT_DISCARD: if (v) fprintf(f, " [r:discard]"); break;
|
|
case VM_OUTPUT_JUMP: fprintf(f, " [r:jump]"); break;
|
|
case VM_OUTPUT_JUMPR: fprintf(f, " [r:jumpr]"); break;
|
|
case VM_OUTPUT_PUSH: fprintf(f, " [r:push]"); break;
|
|
}
|
|
|
|
fprintf(f, "\n");
|
|
}
|
|
}
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
opterr = 0;
|
|
|
|
int headers = 0;
|
|
int dumpSections = 0;
|
|
int dumpMetas = 0;
|
|
int disassembleSections = 0;
|
|
|
|
int c;
|
|
while ((c = getopt(argc, argv, "HsmdD")) != -1)
|
|
{
|
|
switch (c)
|
|
{
|
|
case 'H': headers = 1; break;
|
|
case 's': dumpSections = 1; break;
|
|
case 'm': dumpMetas = 1; break;
|
|
case 'D': disassembleVerbose = 1;
|
|
case 'd': disassembleSections = 1; break;
|
|
case '?':
|
|
if (optopt == 'o' || optopt == 'c' || optopt == 'd')
|
|
fprintf(stderr, "Option -%c requires an argument.\n", optopt);
|
|
else if (isprint(optopt))
|
|
fprintf(stderr, "Unknown option `-%c'.\n", optopt);
|
|
else
|
|
fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt);
|
|
return 1;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
|
|
if(!headers && !dumpSections && !dumpMetas && !disassembleSections) {
|
|
headers = 1;
|
|
}
|
|
|
|
|
|
for (int index = optind; index < argc; index++)
|
|
{
|
|
const char *fileName = argv[index];
|
|
FILE *f = fopen(fileName, "rb");
|
|
if (f == NULL)
|
|
{
|
|
fprintf(stderr, "Could not open file %s\n", fileName);
|
|
continue;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
expfile_t fileHeader;
|
|
|
|
if (fread(&fileHeader, 1, sizeof(expfile_t), f) != sizeof(expfile_t))
|
|
{
|
|
fprintf(stderr, "File %s does not contain a valid header.\n", fileName);
|
|
continue;
|
|
}
|
|
if (fileHeader.magicNumber != EXP_MAGIC)
|
|
{
|
|
fprintf(stderr, "Invalid magic in %s\n", fileName);
|
|
continue;
|
|
}
|
|
if (fileHeader.majorVersion != 1 && fileHeader.minorVersion == 0)
|
|
{
|
|
fprintf(
|
|
stderr, "Invalid version %s: %d.%d\n",
|
|
fileName,
|
|
fileHeader.majorVersion, fileHeader.minorVersion);
|
|
continue;
|
|
}
|
|
|
|
if(headers)
|
|
{
|
|
// We should be sane now...
|
|
fprintf(stdout, "EXP FILE %s\n", fileName);
|
|
fprintf(stdout, "Version: %d.%d\n", fileHeader.majorVersion, fileHeader.minorVersion);
|
|
fprintf(stdout, "Sections: %d\n", fileHeader.numSections);
|
|
fprintf(stdout, "Metas: %d\n", fileHeader.numMeta);
|
|
}
|
|
|
|
if (dumpSections || disassembleSections)
|
|
{
|
|
if(dumpSections) fprintf(stdout, "Sections:\n");
|
|
|
|
for (int i = 0; i < fileHeader.numSections; i++)
|
|
{
|
|
expsection_t section;
|
|
|
|
fseek(f, fileHeader.posSections + i * sizeof(expsection_t), SEEK_SET);
|
|
fread(§ion, 1, sizeof(expsection_t), f);
|
|
if(dumpSections)
|
|
{
|
|
fprintf(stdout, " Section #%d\n", i);
|
|
fprintf(stdout, " Name: %s\n", section.name);
|
|
fprintf(stdout, " Type: %s\n", (section.type ? "Data" : "Code"));
|
|
fprintf(stdout, " Base: 0x%X\n", section.base);
|
|
fprintf(stdout, " Start: %d\n", section.start);
|
|
fprintf(stdout, " Size: %d\n", section.length);
|
|
}
|
|
// Call disassembler
|
|
if (disassembleSections && section.type == 0)
|
|
{
|
|
if(dumpSections)
|
|
fprintf(stdout, " Disassembly:\n");
|
|
else
|
|
fprintf(stdout, "; Section '%s'@0x%08X\n", section.name, section.base);
|
|
|
|
instruction_t *buffer = malloc(section.length);
|
|
|
|
fseek(f, section.start, SEEK_SET);
|
|
int len = fread(buffer, 1, section.length, f);
|
|
if (len != section.length)
|
|
fprintf(stderr, "Read invalid size.\n");
|
|
|
|
disassemble(
|
|
buffer,
|
|
section.length / sizeof(instruction_t),
|
|
section.base,
|
|
stdout);
|
|
|
|
free(buffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
fclose(f);
|
|
}
|
|
return 0;
|
|
}
|