diff --git a/src/disassembler.c b/src/disassembler.c index 0575be1..5be17e6 100644 --- a/src/disassembler.c +++ b/src/disassembler.c @@ -1,7 +1,82 @@ -/* Copyright (C) 2014-2015 Ben Kurtovic +/* Copyright (C) 2014-2016 Ben Kurtovic Released under the terms of the MIT License. See LICENSE for details. */ #include "disassembler.h" +#include "disassembler/mnemonics.h" +#include "disassembler/sizes.h" +#include "util.h" + +/* + Format a sequence of bytes of a certain length as a pretty string. + + The result must be freed by the caller. +*/ +static char* format_bytestring(const uint8_t *bytes, size_t size) +{ + if (!size) + return NULL; + + char *str = cr_malloc(sizeof(char) * (3 * size)); + size_t i; + + for (i = 0; i < size; i++) { + snprintf(&str[3 * i], 3, "%02X", bytes[i]); + str[3 * i + 2] = ' '; + } + str[3 * size - 1] = '\0'; + return str; +} + +/* + Extract the arguments for the given instruction. + + The return value must be free()d. +*/ +static char* decode_argument(const uint8_t *bytes) +{ + // TODO + (void) bytes; + return NULL; +} + +/* + Free the given DisasInstr struct. +*/ +void disas_instr_free(DisasInstr *instr) +{ + free(instr->bytestr); + free(instr->line); + free(instr); +} + +/* + Disassemble a single instruction starting at the given address. + + Return a dynamically allocated structure containing various interesting + fields. This must be freed by the user with disas_instr_free(). +*/ +DisasInstr* disassemble_instruction(const uint8_t *bytes) +{ + size_t size = get_instr_size(bytes); + char *bytestr = format_bytestring(bytes, size); + char *mnemonic = decode_mnemonic(bytes); + char *arg = decode_argument(bytes); + char *line; + + if (arg) { + line = cr_malloc(strlen(mnemonic) + strlen(arg) + 2); + sprintf(line, "%s\t%s", mnemonic, arg); + free(arg); + } else { + line = cr_strdup(mnemonic); + } + + DisasInstr *instr = cr_malloc(sizeof(DisasInstr)); + instr->size = size; + instr->bytestr = bytestr; + instr->line = line; + return instr; +} /* Disassemble the binary file at the input path into z80 source code. diff --git a/src/disassembler.h b/src/disassembler.h index d56db3e..a4715d8 100644 --- a/src/disassembler.h +++ b/src/disassembler.h @@ -1,14 +1,26 @@ -/* Copyright (C) 2014-2015 Ben Kurtovic +/* Copyright (C) 2014-2016 Ben Kurtovic Released under the terms of the MIT License. See LICENSE for details. */ #pragma once #include +#include +#include /* Structs */ -// ... +typedef struct { + size_t size; + char *bytestr; + char *line; +} DisasInstr; + +// typedef struct { ... } Disassembly; /* Functions */ +void disas_instr_free(DisasInstr*); + +DisasInstr* disassemble_instruction(const uint8_t*); +// Disassembly* disassemble(const uint8_t*); // TODO bool disassemble_file(const char*, const char*); diff --git a/src/disassembler/mnemonics.c b/src/disassembler/mnemonics.c new file mode 100644 index 0000000..ff5cf44 --- /dev/null +++ b/src/disassembler/mnemonics.c @@ -0,0 +1,49 @@ +/* Copyright (C) 2014-2016 Ben Kurtovic + Released under the terms of the MIT License. See LICENSE for details. */ + +#include "mnemonics.h" + +static char* instr_mnemonics[256] = { + /* 00 */ "nop", "ld", "ld", "inc", "inc", "dec", "ld", "rlca", + /* 08 */ "ex", "add", "ld", "dec", "inc", "dec", "ld", "rrca", + /* 10 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* 18 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* 20 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* 28 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* 30 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* 38 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* 40 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* 48 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* 50 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* 58 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* 60 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* 68 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* 70 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* 78 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* 80 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* 88 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* 90 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* 98 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* A0 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* A8 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* B0 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* B8 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* C0 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* C8 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* D0 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* D8 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* E0 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* E8 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* F0 */ "????", "????", "????", "????", "????", "????", "????", "????", + /* F8 */ "????", "????", "????", "????", "????", "????", "????", "????" +}; + +/* + Extract the assembly mnemonic for the given opcode. + + The return value is a string literal and should not be freed. +*/ +char* decode_mnemonic(const uint8_t *bytes) +{ + return instr_mnemonics[bytes[0]]; // TODO: extended... +} diff --git a/src/disassembler/mnemonics.h b/src/disassembler/mnemonics.h new file mode 100644 index 0000000..82d8084 --- /dev/null +++ b/src/disassembler/mnemonics.h @@ -0,0 +1,10 @@ +/* Copyright (C) 2014-2016 Ben Kurtovic + Released under the terms of the MIT License. See LICENSE for details. */ + +#pragma once + +#include + +/* Functions */ + +char* decode_mnemonic(const uint8_t*); diff --git a/src/disassembler/sizes.c b/src/disassembler/sizes.c new file mode 100644 index 0000000..1941ae2 --- /dev/null +++ b/src/disassembler/sizes.c @@ -0,0 +1,118 @@ +/* Copyright (C) 2014-2016 Ben Kurtovic + Released under the terms of the MIT License. See LICENSE for details. */ + +#include "sizes.h" + +static size_t instr_sizes[256] = { + 1, 3, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, + 2, 3, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 2, 1, + 2, 3, 3, 1, 1, 1, 2, 1, 2, 1, 3, 1, 1, 1, 2, 1, + 2, 3, 3, 1, 1, 1, 2, 1, 2, 1, 3, 1, 1, 1, 2, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3, 3, 3, 1, 2, 1, 1, 1, 3, 0, 3, 3, 2, 1, + 1, 1, 3, 2, 3, 1, 2, 1, 1, 1, 3, 2, 3, 0, 2, 1, + 1, 1, 3, 1, 3, 1, 2, 1, 1, 1, 3, 1, 3, 0, 2, 1, + 1, 1, 3, 1, 3, 1, 2, 1, 1, 1, 3, 1, 3, 0, 2, 1 +}; + +static size_t instr_sizes_extended[256] = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, + 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, + 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, + 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 +}; + +static size_t instr_sizes_bits[256] = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 +}; + +static size_t instr_sizes_index[256] = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 4, 4, 2, 2, 2, 3, 2, 2, 2, 4, 2, 2, 2, 3, 2, + 2, 2, 2, 2, 3, 3, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, + 3, 3, 3, 3, 3, 3, 2, 3, 2, 2, 2, 2, 2, 2, 3, 2, + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, + 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 +}; + +static size_t instr_sizes_index_bits[256] = { + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 +}; + +/* + Return the byte length of the instruction starting at the given address. +*/ +size_t get_instr_size(const uint8_t *bytes) +{ + uint8_t b = bytes[0]; + + if (b == 0xED) + return instr_sizes_extended[bytes[1]]; + if (b == 0xCB) + return instr_sizes_bits[bytes[1]]; + if (b == 0xDD || b == 0xFD) { + if (bytes[1] == 0xCB) + return instr_sizes_index_bits[bytes[2]]; + return instr_sizes_index[bytes[1]]; + } + return instr_sizes[b]; +} diff --git a/src/disassembler/sizes.h b/src/disassembler/sizes.h new file mode 100644 index 0000000..01949e3 --- /dev/null +++ b/src/disassembler/sizes.h @@ -0,0 +1,11 @@ +/* Copyright (C) 2014-2016 Ben Kurtovic + Released under the terms of the MIT License. See LICENSE for details. */ + +#pragma once + +#include +#include + +/* Functions */ + +size_t get_instr_size(const uint8_t*); diff --git a/src/mmu.c b/src/mmu.c index bb9e2a5..2aa491f 100644 --- a/src/mmu.c +++ b/src/mmu.c @@ -161,3 +161,15 @@ bool mmu_write_byte(MMU *mmu, uint16_t addr, uint8_t value) return true; } } + +/* + Read four bytes of memory from the given address. +*/ +uint32_t mmu_read_quad(MMU *mmu, uint16_t addr) +{ + return ( + mmu_read_byte(mmu, addr) + + (mmu_read_byte(mmu, addr + 1) << 8) + + (mmu_read_byte(mmu, addr + 2) << 16) + + (mmu_read_byte(mmu, addr + 3) << 24)); +} diff --git a/src/mmu.h b/src/mmu.h index 3ee4088..a91669a 100644 --- a/src/mmu.h +++ b/src/mmu.h @@ -28,3 +28,4 @@ void mmu_load_rom(MMU*, const uint8_t*, size_t); void mmu_power(MMU*); uint8_t mmu_read_byte(MMU*, uint16_t); bool mmu_write_byte(MMU*, uint16_t, uint8_t); +uint32_t mmu_read_quad(MMU*, uint16_t); diff --git a/src/z80.c b/src/z80.c index b3a45bd..f951ca8 100644 --- a/src/z80.c +++ b/src/z80.c @@ -169,8 +169,12 @@ static inline void increment_refresh_counter(Z80 *z80) */ static inline void trace_instruction(const Z80 *z80) { - TRACE("PC @ 0x%04X", z80->regfile.pc) - // TODO + uint32_t quad = mmu_read_quad(z80->mmu, z80->regfile.pc); + uint8_t bytes[4] = {quad, quad >> 8, quad >> 16, quad >> 24}; + DisasInstr *instr = disassemble_instruction(bytes); + + TRACE("0x%04X: %11s %s", z80->regfile.pc, instr->bytestr, instr->line) + disas_instr_free(instr); } /* @@ -185,9 +189,9 @@ bool z80_do_cycles(Z80 *z80, double cycles) cycles -= z80->pending_cycles; while (cycles > 0 && !z80->except) { uint8_t opcode = mmu_read_byte(z80->mmu, z80->regfile.pc); + increment_refresh_counter(z80); if (TRACE_LEVEL) trace_instruction(z80); - increment_refresh_counter(z80); cycles -= (*instruction_lookup_table[opcode])(z80, opcode); }