Created
June 6, 2020 06:36
-
-
Save shepgoba/479db311b8ee6a68d5c3436eeba9bd45 to your computer and use it in GitHub Desktop.
A Simple CHIP-8 Disassembler
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdint.h> | |
#include <stdlib.h> | |
#include <string.h> | |
char *get_instruction_string(uint16_t opcode) { | |
char *result = malloc(sizeof(char) * 32); | |
uint8_t add_newline = 0; | |
uint8_t arg1 = (opcode & 0x0f00) >> 8; | |
uint8_t arg2 = (opcode & 0x00f0) >> 4; | |
uint8_t byte = opcode & 0xff; | |
switch (opcode & 0xf000) { | |
case 0x0000: { | |
if (opcode & 0x000E) { | |
strcpy(result, "ret"); | |
add_newline = 1; | |
} else { | |
strcpy(result, "cls"); | |
} | |
break; | |
} | |
case 0x1000: { | |
snprintf(result, 32, "jp $%x", opcode & 0x0fff); | |
add_newline = 1; | |
break; | |
} | |
case 0x2000: { | |
snprintf(result, 32, "call $%x", opcode & 0x0fff); | |
break; | |
} | |
case 0x3000: { | |
snprintf(result, 32, "se V%x, $%x", arg1, byte); | |
break; | |
} | |
case 0x4000: { | |
snprintf(result, 32, "sne V%x, $%x", arg1, byte); | |
break; | |
} | |
case 0x5000: { | |
snprintf(result, 32, "se V%x, V%x", arg1, arg2); | |
break; | |
} | |
case 0x6000: { | |
snprintf(result, 32, "ld V%x, $%x", arg1, byte); | |
break; | |
} | |
case 0x7000: { | |
snprintf(result, 32, "add V%x, $%x", arg1, byte); | |
break; | |
} | |
case 0x8000: { | |
switch (opcode & 0x000f) { | |
case 0x0: { | |
snprintf(result, 32, "ld V%x, V%x", arg1, arg2); | |
break; | |
} | |
case 0x1: { | |
snprintf(result, 32, "or V%x, V%x", arg1, arg2); | |
break; | |
} | |
case 0x2: { | |
snprintf(result, 32, "and V%x, V%x", arg1, arg2); | |
break; | |
} | |
case 0x3: { | |
snprintf(result, 32, "xor V%x, V%x", arg1, arg2); | |
break; | |
} | |
case 0x4: { | |
snprintf(result, 32, "add V%x, V%x", arg1, arg2); | |
break; | |
} | |
case 0x5: { | |
snprintf(result, 32, "sub V%x, V%x", arg1, arg2); | |
break; | |
} | |
case 0x6: { | |
snprintf(result, 32, "shr V%x {, V%x}", arg1, arg2); | |
break; | |
} | |
case 0x7: { | |
snprintf(result, 32, "subn V%x, V%x", arg1, arg2); | |
break; | |
} | |
case 0xE: { | |
snprintf(result, 32, "shl V%x {, V%x}", arg1, arg2); | |
break; | |
} | |
} | |
break; | |
} | |
case 0x9000: { | |
snprintf(result, 32, "sne V%x, V%x", arg1, arg2); | |
break; | |
} | |
case 0xA000: { | |
snprintf(result, 32, "ld I, $%x", opcode & 0x0fff); | |
break; | |
} | |
case 0xB000: { | |
snprintf(result, 32, "jp V0, $%x", opcode & 0x0fff); | |
break; | |
} | |
case 0xC000: { | |
snprintf(result, 32, "rnd V%x, $%x", opcode & 0x0fff); | |
break; | |
} | |
case 0xD000: { | |
snprintf(result, 32, "drw V%x, V%x, $%x", arg1, arg2, opcode & 0x000f); | |
break; | |
} | |
case 0xE000: { | |
if ((opcode & 0x000f) == 0xE) { | |
snprintf(result, 32, "skp V%x", arg1); | |
} else { | |
snprintf(result, 32, "sknp V%x", arg1); | |
} | |
break; | |
} | |
case 0xF000: { | |
switch (opcode & 0x00ff) { | |
case 0x07: { | |
snprintf(result, 32, "ld V%x, dt", arg1); | |
break; | |
} | |
case 0x0A: { | |
snprintf(result, 32, "ld V%x, {keypress}", arg1); | |
break; | |
} | |
case 0x15: { | |
snprintf(result, 32, "ld dt, V%x", arg1); | |
break; | |
} | |
case 0x18: { | |
snprintf(result, 32, "ld st, V%x", arg1); | |
break; | |
} | |
case 0x1E: { | |
snprintf(result, 32, "add I, V%x", arg1); | |
break; | |
} | |
case 0x29: { | |
snprintf(result, 32, "ld F, V%x", arg1); | |
break; | |
} | |
case 0x33: { | |
snprintf(result, 32, "ld B, V%x", arg1); | |
break; | |
} | |
case 0x55: { | |
snprintf(result, 32, "ld [I], V%x", arg1); | |
break; | |
} | |
case 0x65: { | |
snprintf(result, 32, "ld V%x, [I]", arg1); | |
break; | |
} | |
} | |
break; | |
} | |
default: | |
strcpy(result, ""); | |
} | |
if (add_newline) { | |
uint8_t offset = strlen(result); | |
result[offset] = '\n'; | |
result[offset + 1] = 0; | |
} | |
return result; | |
} | |
int main(int argc, char **argv) { | |
if (argc < 2) { | |
printf("Enter a file to disassemble\n"); | |
return -1; | |
} | |
FILE *file = fopen(argv[1], "rb"); | |
if (!file) { | |
printf("could not open file"); | |
return -1; | |
} | |
uint16_t rom_addr = 0x200; | |
uint16_t num = 0; | |
printf("rom_start:\n"); | |
while (fread(&num, sizeof(uint16_t), 1, file) != 0) { | |
uint16_t opcode = ((num & 0xff00) >> 8) | ((num & 0xff) << 8); | |
char *instruction_string = get_instruction_string(opcode); | |
printf("0x%04x: %s\n", rom_addr, instruction_string); | |
free(instruction_string); | |
rom_addr += 2; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment