| @@ -1,7 +1,82 @@ | |||||
| /* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
| /* Copyright (C) 2014-2016 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
| Released under the terms of the MIT License. See LICENSE for details. */ | Released under the terms of the MIT License. See LICENSE for details. */ | ||||
| #include "disassembler.h" | #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. | Disassemble the binary file at the input path into z80 source code. | ||||
| @@ -1,14 +1,26 @@ | |||||
| /* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
| /* Copyright (C) 2014-2016 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
| Released under the terms of the MIT License. See LICENSE for details. */ | Released under the terms of the MIT License. See LICENSE for details. */ | ||||
| #pragma once | #pragma once | ||||
| #include <stdbool.h> | #include <stdbool.h> | ||||
| #include <stddef.h> | |||||
| #include <stdint.h> | |||||
| /* Structs */ | /* Structs */ | ||||
| // ... | |||||
| typedef struct { | |||||
| size_t size; | |||||
| char *bytestr; | |||||
| char *line; | |||||
| } DisasInstr; | |||||
| // typedef struct { ... } Disassembly; | |||||
| /* Functions */ | /* 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*); | bool disassemble_file(const char*, const char*); | ||||
| @@ -0,0 +1,49 @@ | |||||
| /* Copyright (C) 2014-2016 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
| 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... | |||||
| } | |||||
| @@ -0,0 +1,10 @@ | |||||
| /* Copyright (C) 2014-2016 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
| Released under the terms of the MIT License. See LICENSE for details. */ | |||||
| #pragma once | |||||
| #include <stdint.h> | |||||
| /* Functions */ | |||||
| char* decode_mnemonic(const uint8_t*); | |||||
| @@ -0,0 +1,118 @@ | |||||
| /* Copyright (C) 2014-2016 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
| 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]; | |||||
| } | |||||
| @@ -0,0 +1,11 @@ | |||||
| /* Copyright (C) 2014-2016 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
| Released under the terms of the MIT License. See LICENSE for details. */ | |||||
| #pragma once | |||||
| #include <stddef.h> | |||||
| #include <stdint.h> | |||||
| /* Functions */ | |||||
| size_t get_instr_size(const uint8_t*); | |||||
| @@ -161,3 +161,15 @@ bool mmu_write_byte(MMU *mmu, uint16_t addr, uint8_t value) | |||||
| return true; | 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)); | |||||
| } | |||||
| @@ -28,3 +28,4 @@ void mmu_load_rom(MMU*, const uint8_t*, size_t); | |||||
| void mmu_power(MMU*); | void mmu_power(MMU*); | ||||
| uint8_t mmu_read_byte(MMU*, uint16_t); | uint8_t mmu_read_byte(MMU*, uint16_t); | ||||
| bool mmu_write_byte(MMU*, uint16_t, uint8_t); | bool mmu_write_byte(MMU*, uint16_t, uint8_t); | ||||
| uint32_t mmu_read_quad(MMU*, uint16_t); | |||||
| @@ -169,8 +169,12 @@ static inline void increment_refresh_counter(Z80 *z80) | |||||
| */ | */ | ||||
| static inline void trace_instruction(const 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; | cycles -= z80->pending_cycles; | ||||
| while (cycles > 0 && !z80->except) { | while (cycles > 0 && !z80->except) { | ||||
| uint8_t opcode = mmu_read_byte(z80->mmu, z80->regfile.pc); | uint8_t opcode = mmu_read_byte(z80->mmu, z80->regfile.pc); | ||||
| increment_refresh_counter(z80); | |||||
| if (TRACE_LEVEL) | if (TRACE_LEVEL) | ||||
| trace_instruction(z80); | trace_instruction(z80); | ||||
| increment_refresh_counter(z80); | |||||
| cycles -= (*instruction_lookup_table[opcode])(z80, opcode); | cycles -= (*instruction_lookup_table[opcode])(z80, opcode); | ||||
| } | } | ||||