From 2ed8500c3f2bda3fefa47893fd2ec2b77dfb258a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=20Rodr=C3=ADguez?= Date: Sat, 30 Apr 2016 19:42:01 +0200 Subject: [PATCH 1/7] Add initial prototype for ROM dumper Will read the opcodes from a ROM and dump all the opcodes into stdout. Still pending to process every opcode that is read. --- src/Makefile.am | 7 ++++++- src/mdisasm.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 src/mdisasm.c diff --git a/src/Makefile.am b/src/Makefile.am index a67fe3e..79cb9b6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,7 +2,12 @@ noinst_LIBRARIES = lib8.a lib8_a_SOURCES = cpu.c cpu.h lib8_a_CFLAGS = -std=c99 -Wall -bin_PROGRAMS = chip8 +bin_PROGRAMS = chip8 mdisasm + chip8_SOURCES = chip8.c libsdl.c libsdl.h chip8_CFLAGS = @SDL_CFLAGS@ -std=c99 -Wall chip8_LDADD = lib8.a @SDL_LIBS@ + +mdisasm_SOURCES = mdisasm.c +mdisasm_FLAGS = -std=c99 -Wall + diff --git a/src/mdisasm.c b/src/mdisasm.c new file mode 100644 index 0000000..14a2546 --- /dev/null +++ b/src/mdisasm.c @@ -0,0 +1,48 @@ +#include +#include + +static int +load_rom_file(const char* filename, unsigned char** buf) +{ + // Try to open the file. + FILE* fp = fopen(filename, "rb"); + if (fp == NULL) { + fprintf(stderr, "File not found: %s\n", filename); + return -1; + } + + // How big is this file? + fseek(fp, 0, SEEK_END); + int length = ftell(fp); + fseek(fp, 0, SEEK_SET); + + // Try to load the ROM. + *buf = malloc(length); + if (*buf == NULL) { + return -1; + } + fread(*buf, length, 1, fp); + fclose(fp); + + // Return how many bytes we read (is the size of the array). + return length; +} + +int +main(int argc, char** argv) +{ + if (argc != 2) { + fprintf(stderr, "Usage: %s [file name]\n", argv[0]); + return 1; + } + + unsigned char* buffer; + int length = load_rom_file(argv[1], &buffer); + + for (int byte = 0; byte < length; byte += 2) { + printf("%02x%02x\n", buffer[byte], buffer[byte + 1]); + } + + free(buffer); + return 0; +} From 18fe60772bd5cb405330d26147a2adfe7f89d44b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=20Rodr=C3=ADguez?= Date: Sat, 30 Apr 2016 19:45:11 +0200 Subject: [PATCH 2/7] Ignore binary disassembler file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6462991..4f956bb 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ **/*.gcda src/chip8 +src/mdisasm From 2525e35efd10d593c057815ccabe4bb3c4b0ac22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=20Rodr=C3=ADguez?= Date: Sat, 30 Apr 2016 21:03:13 +0200 Subject: [PATCH 3/7] Implement opcode decompiling So WIP, missing a few things. Not well tested. Not tested at all. --- src/mdisasm.c | 194 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 193 insertions(+), 1 deletion(-) diff --git a/src/mdisasm.c b/src/mdisasm.c index 14a2546..debc9b3 100644 --- a/src/mdisasm.c +++ b/src/mdisasm.c @@ -1,6 +1,195 @@ #include #include +#define OPCODE_P(opcode) (opcode >> 12) +#define OPCODE_X(opcode) ((opcode & 0xF00) >> 8) +#define OPCODE_Y(opcode) ((opcode & 0xF0) >> 4) +#define OPCODE_N(opcode) (opcode & 0xF) +#define OPCODE_NNN(opcode) (opcode & 0xFFF) +#define OPCODE_KK(opcode) (opcode & 0xFF) + +static void +to_instruction_0(unsigned short opcode, char* out) +{ + if (opcode == 0x00E0) { + snprintf(out, 20, "EXIT"); + } else if (opcode == 0x00EE) { + snprintf(out, 20, "RET"); + } else { + snprintf(out, 20, "SYS %x", OPCODE_NNN(opcode)); + } +} + +static void +to_instruction_1(unsigned short opcode, char* out) +{ + snprintf(out, 20, "JP %x", OPCODE_NNN(opcode)); +} + +static void +to_instruction_2(unsigned short opcode, char* out) +{ + snprintf(out, 20, "CALL %x", OPCODE_NNN(opcode)); +} + +static void +to_instruction_3(unsigned short opcode, char* out) +{ + snprintf(out, 20, "SE V[%x], %x", OPCODE_X(opcode), OPCODE_KK(opcode)); +} + +static void +to_instruction_4(unsigned short opcode, char* out) +{ + snprintf(out, 20, "SNE V[%x], %x", OPCODE_X(opcode), OPCODE_KK(opcode)); +} + +static void +to_instruction_5(unsigned short opcode, char* out) +{ + snprintf(out, 20, "SE V[%x], V[%x]", OPCODE_X(opcode), OPCODE_Y(opcode)); +} + +static void +to_instruction_6(unsigned short opcode, char* out) +{ + snprintf(out, 20, "LD V[%x], %x", OPCODE_X(opcode), OPCODE_KK(opcode)); +} + +static void +to_instruction_7(unsigned short opcode, char* out) +{ + snprintf(out, 20, "AND V[%x], %x", OPCODE_X(opcode), OPCODE_KK(opcode)); +} + +static void +to_instruction_8(unsigned short opcode, char* out) +{ + int x = OPCODE_X(opcode), y = OPCODE_Y(opcode), n = OPCODE_N(opcode); + switch (n) { + case 0: + snprintf(out, 20, "LD V[%x], V[%x]", x, y); + break; + case 1: + snprintf(out, 20, "OR V[%x], V[%x]", x, y); + break; + case 2: + snprintf(out, 20, "AND V[%x], V[%x]", x, y); + break; + case 3: + snprintf(out, 20, "XOR V[%x], V[%x]", x, y); + break; + case 4: + snprintf(out, 20, "ADD V[%x], V[%x]", x, y); + break; + case 5: + snprintf(out, 20, "SUB V[%x], V[%x]", x, y); + break; + case 6: + snprintf(out, 20, "SHR V[%x]", x); + break; + case 7: + snprintf(out, 20, "SUBN V[%x], V[%x]", x, y); + break; + case 0xE: + snprintf(out, 20, "SHL V[%x]", x); + break; + } +} + +static void +to_instruction_9(unsigned short opcode, char* out) +{ + snprintf(out, 20, "SNE V[%x], V[%x]", OPCODE_X(opcode), OPCODE_Y(opcode)); +} + +static void +to_instruction_A(unsigned short opcode, char* out) +{ + snprintf(out, 20, "LD I, %x", OPCODE_NNN(opcode)); +} + +static void +to_instruction_B(unsigned short opcode, char* out) +{ + snprintf(out, 20, "JP V[0], %x", OPCODE_NNN(opcode)); +} + +static void +to_instruction_C(unsigned short opcode, char* out) +{ + snprintf(out, 20, "RND V[%x], %x", OPCODE_X(opcode), OPCODE_KK(opcode)); +} + +static void +to_instruction_D(unsigned short opcode, char* out) +{ + int x = OPCODE_X(opcode), y = OPCODE_Y(opcode), n = OPCODE_N(opcode); + snprintf(out, 20, "DRW %x, %x, %x", x, y, n); +} + +static void +to_instruction_E(unsigned short opcode, char* out) +{ + if (OPCODE_KK(opcode) == 0x9E) { + snprintf(out, 20, "SKP V[%x]", OPCODE_X(opcode)); + } else if (OPCODE_KK(opcode) == 0xA1) { + snprintf(out, 20, "SKNP V[%x]", OPCODE_X(opcode)); + } +} + +static void +to_instruction_F(unsigned short opcode, char* out) +{ + switch (OPCODE_KK(opcode)) { + case 0x07: + snprintf(out, 20, "LD V[%x], DT", OPCODE_X(opcode)); + break; + case 0x0A: + snprintf(out, 20, "LD V[%x], K", OPCODE_X(opcode)); + break; + case 0x15: + snprintf(out, 20, "LD DT, V[%x]", OPCODE_X(opcode)); + break; + case 0x18: + snprintf(out, 20, "LD ST, V[%x]", OPCODE_X(opcode)); + break; + case 0x1E: + snprintf(out, 20, "ADD I, V[%x]", OPCODE_X(opcode)); + break; + case 0x29: + snprintf(out, 20, "LD F, V[%x]", OPCODE_X(opcode)); + break; + case 0x33: + snprintf(out, 20, "LD B, V[%x]", OPCODE_X(opcode)); + break; + case 0x55: + snprintf(out, 20, "LD [I], V[%x]", OPCODE_X(opcode)); + break; + case 0x65: + snprintf(out, 20, "LD V[%x], [I]", OPCODE_X(opcode)); + break; + } +} + +typedef void (*to_instr) (unsigned short opcode, char* out); + +static to_instr instructions[] = { + &to_instruction_0, &to_instruction_1, &to_instruction_2, &to_instruction_3, + &to_instruction_4, &to_instruction_5, &to_instruction_6, &to_instruction_7, + &to_instruction_8, &to_instruction_9, &to_instruction_A, &to_instruction_B, + &to_instruction_C, &to_instruction_D, &to_instruction_E, &to_instruction_F +}; + +static void +to_instruction(unsigned short opcode, char* out) +{ + int p = OPCODE_P(opcode); + // Fallback. In case instructions array fail to print for some reason. + snprintf(out, 20, "%04x", opcode); + instructions[p](opcode, out); +} + static int load_rom_file(const char* filename, unsigned char** buf) { @@ -38,9 +227,12 @@ main(int argc, char** argv) unsigned char* buffer; int length = load_rom_file(argv[1], &buffer); + char output[20]; for (int byte = 0; byte < length; byte += 2) { - printf("%02x%02x\n", buffer[byte], buffer[byte + 1]); + unsigned short opcode = buffer[byte] << 8 | buffer[byte + 1]; + to_instruction(opcode, output); + printf("%03x %04x %s\n", (0x200 + byte), opcode, output); } free(buffer); From 7b5fb61fe4ccb211570a729ee71be6ad288bfdb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=20Rodr=C3=ADguez?= Date: Sat, 30 Apr 2016 21:07:29 +0200 Subject: [PATCH 4/7] Fix Makefile.am --- src/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile.am b/src/Makefile.am index 79cb9b6..71d14e1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,5 +9,5 @@ chip8_CFLAGS = @SDL_CFLAGS@ -std=c99 -Wall chip8_LDADD = lib8.a @SDL_LIBS@ mdisasm_SOURCES = mdisasm.c -mdisasm_FLAGS = -std=c99 -Wall +mdisasm_CFLAGS = -std=c99 -Wall From c30ce24a3d377ac62c3a2cc25ca5c8cf9b44e0d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Mart=C3=ADn?= Date: Sat, 30 Apr 2016 21:10:46 -0300 Subject: [PATCH 5/7] Minimal instruction fixes Corrects 7xkk (ADD) and Dxyn (DRW). --- src/mdisasm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mdisasm.c b/src/mdisasm.c index debc9b3..4361443 100644 --- a/src/mdisasm.c +++ b/src/mdisasm.c @@ -38,7 +38,7 @@ to_instruction_3(unsigned short opcode, char* out) snprintf(out, 20, "SE V[%x], %x", OPCODE_X(opcode), OPCODE_KK(opcode)); } -static void +static void to_instruction_4(unsigned short opcode, char* out) { snprintf(out, 20, "SNE V[%x], %x", OPCODE_X(opcode), OPCODE_KK(opcode)); @@ -59,7 +59,7 @@ to_instruction_6(unsigned short opcode, char* out) static void to_instruction_7(unsigned short opcode, char* out) { - snprintf(out, 20, "AND V[%x], %x", OPCODE_X(opcode), OPCODE_KK(opcode)); + snprintf(out, 20, "ADD V[%x], %x", OPCODE_X(opcode), OPCODE_KK(opcode)); } static void @@ -125,7 +125,7 @@ static void to_instruction_D(unsigned short opcode, char* out) { int x = OPCODE_X(opcode), y = OPCODE_Y(opcode), n = OPCODE_N(opcode); - snprintf(out, 20, "DRW %x, %x, %x", x, y, n); + snprintf(out, 20, "DRW V[%x], V[%x], %x", x, y, n); } static void From 421c48b54a6d5f999ddf0113b39da336b832766b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=20Rodr=C3=ADguez?= Date: Sun, 1 May 2016 20:33:47 +0200 Subject: [PATCH 6/7] Use getopt for input parameters Much much better --- src/mdisasm.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/src/mdisasm.c b/src/mdisasm.c index 4361443..dd9e14f 100644 --- a/src/mdisasm.c +++ b/src/mdisasm.c @@ -1,5 +1,10 @@ #include #include +#include +#include +#include "../config.h" + +#define APP_NAME "mdisasm" #define OPCODE_P(opcode) (opcode >> 12) #define OPCODE_X(opcode) ((opcode & 0xF00) >> 8) @@ -8,6 +13,15 @@ #define OPCODE_NNN(opcode) (opcode & 0xFFF) #define OPCODE_KK(opcode) (opcode & 0xFF) +static char* output_mode = "full"; + +static struct option long_options[] = { + { "help", no_argument, 0, 'h' }, + { "version", no_argument, 0, 'v' }, + { "output", optional_argument, 0, 0 }, + { 0, 0, 0, 0 } +}; + static void to_instruction_0(unsigned short opcode, char* out) { @@ -217,16 +231,56 @@ load_rom_file(const char* filename, unsigned char** buf) return length; } +static void +usage(const char *app_name) +{ + printf("Usage: %s [-h] [-v] [--output=full|minimal] \n", app_name); +} + int main(int argc, char** argv) { - if (argc != 2) { - fprintf(stderr, "Usage: %s [file name]\n", argv[0]); - return 1; + int indexpt, c; + while ((c = getopt_long(argc, argv, "hv", long_options, &indexpt)) != -1) { + switch (c) { + case 'h': + usage(argv[0]); + exit(0); + case 'v': + printf("%s %s\n", APP_NAME, PACKAGE_VERSION); + exit(0); + break; + case 0: + if (!strncmp(long_options[indexpt].name, "output", 7)) { + output_mode = optarg; + if (output_mode == NULL) { + printf("--output: no option given.\n"); + exit(1); + } + if (strncmp(output_mode, "full", 5) && + strncmp(output_mode, "minimal", 8)) { + printf("Unknown value %s. Use %s -h\n", + output_mode, argv[0]); + exit(1); + } + break; + } + printf("Unknown option: %s. Use %s -h\n", + long_options[indexpt].name, argv[0]); + break; + default: + exit(1); + } + } + + /* If this is true, no file has been given. */ + if (optind >= argc) { + fprintf(stderr, "%1$s: no file given. '%1$s -h' for help.\n", argv[0]); + exit(1); } unsigned char* buffer; - int length = load_rom_file(argv[1], &buffer); + int length = load_rom_file(argv[optind], &buffer); char output[20]; for (int byte = 0; byte < length; byte += 2) { From 995f353d951d69475d9ba83f8fece96db57b8aad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=20Rodr=C3=ADguez?= Date: Sun, 1 May 2016 20:39:37 +0200 Subject: [PATCH 7/7] Use different output modes Using --output=full will render for each instruction, memory address, compiled opcode and decompiled instruction. Using --output=minimal will just render the decompiled instruction. --- src/mdisasm.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/mdisasm.c b/src/mdisasm.c index dd9e14f..77b9b92 100644 --- a/src/mdisasm.c +++ b/src/mdisasm.c @@ -286,7 +286,11 @@ main(int argc, char** argv) for (int byte = 0; byte < length; byte += 2) { unsigned short opcode = buffer[byte] << 8 | buffer[byte + 1]; to_instruction(opcode, output); - printf("%03x %04x %s\n", (0x200 + byte), opcode, output); + if (!strncmp(output_mode, "full", 5)) { + printf("%03x\t%04x\t%s\n", (0x200 + byte), opcode, output); + } else if (!strncmp(output_mode, "minimal", 8)) { + printf("%s\n", output); + } } free(buffer);