prepare project structure for running simulations
This commit is contained in:
parent
bdd21c3373
commit
0b57a4fed2
3
.vscode/c_cpp_properties.json
vendored
3
.vscode/c_cpp_properties.json
vendored
@ -4,7 +4,8 @@
|
||||
"name": "Win32",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"C:/MinGW/include/**"
|
||||
"C:/MinGW/include/**",
|
||||
"src/**"
|
||||
],
|
||||
"defines": [
|
||||
"_DEBUG",
|
||||
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -6,6 +6,7 @@
|
||||
"fcntl.h": "c",
|
||||
"io.h": "c",
|
||||
"errno.h": "c",
|
||||
"stdbool.h": "c"
|
||||
"stdbool.h": "c",
|
||||
"sim8086.h": "c"
|
||||
}
|
||||
}
|
3
Makefile
3
Makefile
@ -1,11 +1,10 @@
|
||||
CC=gcc
|
||||
CFLAGS=-g -Wall
|
||||
INCLUDES=./include
|
||||
|
||||
.DEFAULT_GOAL := main
|
||||
|
||||
%: src/%.c
|
||||
$(CC) -o $@ $< $(CFLAGS) -I$(INCLUDES)
|
||||
$(CC) -o $@ $< $(CFLAGS)
|
||||
|
||||
clean:
|
||||
rm main.exe
|
227
src/main.c
227
src/main.c
@ -2,9 +2,14 @@
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include "os.h"
|
||||
#include <errno.h>
|
||||
#include "asm.c"
|
||||
|
||||
#include "os.h"
|
||||
#include "sim8086.h"
|
||||
|
||||
#include "sim8086.c"
|
||||
#include "sim8086_decoder.c"
|
||||
#include "sim8086_simulator.c"
|
||||
|
||||
#define strequal(a, b) strcmp(a, b) == 0
|
||||
|
||||
@ -45,10 +50,6 @@ void get_tmp_file(char *filename, const char *prefix) {
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void print_usage(const char *program) {
|
||||
fprintf(stderr, "Usage: %s <test|dump> <file>\n", program);
|
||||
}
|
||||
|
||||
int compile_asm(const char *src, const char *dst) {
|
||||
char command[512] = { 0 };
|
||||
snprintf(command, sizeof(command), "nasm \"%s\" -O0 -o \"%s\"", src, dst);
|
||||
@ -72,7 +73,6 @@ int compare_files(const char *expected, const char *gotten) {
|
||||
}
|
||||
|
||||
if (!feof(f1) || !feof(f2)) {
|
||||
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -83,87 +83,162 @@ err:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int dissassemble(FILE *src, FILE *dst) {
|
||||
fprintf(dst, "bits 16\n\n");
|
||||
|
||||
char buff[256];
|
||||
struct instruction inst;
|
||||
int counter = 1;
|
||||
while (true) {
|
||||
enum decode_error err = decode_instruction(src, &inst);
|
||||
if (err == DECODE_ERR_EOF) break;
|
||||
if (err != DECODE_OK) {
|
||||
fprintf(stderr, "ERROR: Failed to decode %d instruction: %s\n", counter, decode_error_to_str(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
instruction_to_str(buff, sizeof(buff), &inst);
|
||||
fprintf(dst, buff);
|
||||
fprintf(dst, "\n");
|
||||
counter += 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int simulate(FILE *src) {
|
||||
todo("simulate");
|
||||
}
|
||||
|
||||
void print_usage(const char *program) {
|
||||
fprintf(stderr, "Usage: %s <test-dump|dump|run> <file>\n", program);
|
||||
}
|
||||
|
||||
int test_decoder(const char *asm_file) {
|
||||
if (!strendswith(asm_file, ".asm")) {
|
||||
printf("ERROR: Expected *.asm file, gotten '%s'", asm_file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *bin_filename = "test-input.o";
|
||||
if (compile_asm(asm_file, bin_filename)) {
|
||||
printf("ERROR: Failed to compile '%s'", asm_file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE *bin_test = fopen(bin_filename, "rb");
|
||||
if (bin_test == NULL) {
|
||||
printf("ERROR: Opening file '%s': %d\n", bin_filename, errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *dissassembly_filename = "test-dump.asm";
|
||||
FILE *dissassembly = fopen(dissassembly_filename, "wb+");
|
||||
if (dissassembly == NULL) {
|
||||
printf("ERROR: Opening file '%s': %d\n", dissassembly_filename, errno);
|
||||
return -1;
|
||||
}
|
||||
dissassemble(bin_test, dissassembly);
|
||||
fclose(dissassembly);
|
||||
|
||||
char *dissassembly_dump_filename = "test-dump-asm.o";
|
||||
if (compile_asm(dissassembly_filename, dissassembly_dump_filename)) {
|
||||
printf("ERROR: Failed to compile '%s'", dissassembly_filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!compare_files(bin_filename, dissassembly_dump_filename)) {
|
||||
printf("Test success\n");
|
||||
} else {
|
||||
printf("Test failed\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dump_decompilation(const char *input) {
|
||||
if (strendswith(input, ".asm")) {
|
||||
char bin_filename[MAX_PATH_SIZE];
|
||||
get_tmp_file(bin_filename, "nasm_output");
|
||||
|
||||
if (compile_asm(input, bin_filename)) {
|
||||
printf("ERROR: Failed to compile '%s'", input);
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE *assembly = fopen(bin_filename, "rb");
|
||||
if (assembly == NULL) {
|
||||
printf("ERROR: Opening file '%s': %d\n", bin_filename, errno);
|
||||
remove(bin_filename);
|
||||
return -1;
|
||||
}
|
||||
dissassemble(assembly, stdout);
|
||||
fclose(assembly);
|
||||
|
||||
remove(bin_filename);
|
||||
} else {
|
||||
FILE *assembly = fopen(input, "r");
|
||||
if (assembly == NULL) {
|
||||
printf("ERROR: Opening file '%s': %d\n", input, errno);
|
||||
return -1;
|
||||
}
|
||||
dissassemble(assembly, stdout);
|
||||
fclose(assembly);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int run_simulation(const char *input) {
|
||||
if (strendswith(input, ".asm")) {
|
||||
char bin_filename[MAX_PATH_SIZE];
|
||||
get_tmp_file(bin_filename, "nasm_output");
|
||||
|
||||
if (compile_asm(input, bin_filename)) {
|
||||
printf("ERROR: Failed to compile '%s'", input);
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE *assembly = fopen(bin_filename, "rb");
|
||||
if (assembly == NULL) {
|
||||
printf("ERROR: Opening file '%s': %d\n", bin_filename, errno);
|
||||
remove(bin_filename);
|
||||
return -1;
|
||||
}
|
||||
simulate(assembly);
|
||||
fclose(assembly);
|
||||
|
||||
remove(bin_filename);
|
||||
} else {
|
||||
FILE *assembly = fopen(input, "r");
|
||||
if (assembly == NULL) {
|
||||
printf("ERROR: Opening file '%s': %d\n", input, errno);
|
||||
return -1;
|
||||
}
|
||||
simulate(assembly);
|
||||
fclose(assembly);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc != 3) {
|
||||
print_usage(argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strequal(argv[1], "test")) {
|
||||
char *asm_file = argv[2];
|
||||
if (!strendswith(asm_file, ".asm")) {
|
||||
printf("ERROR: Expected *.asm file, gotten '%s'", asm_file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *bin_filename = "test-input.o";
|
||||
if (compile_asm(asm_file, bin_filename)) {
|
||||
printf("ERROR: Failed to compile '%s'", asm_file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE *bin_test = fopen(bin_filename, "rb");
|
||||
if (bin_test == NULL) {
|
||||
printf("ERROR: Opening file '%s': %d\n", bin_filename, errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *dissassembly_filename = "test-dump.asm";
|
||||
FILE *dissassembly = fopen(dissassembly_filename, "wb+");
|
||||
if (dissassembly == NULL) {
|
||||
printf("ERROR: Opening file '%s': %d\n", dissassembly_filename, errno);
|
||||
return -1;
|
||||
}
|
||||
dissassemble(bin_test, dissassembly);
|
||||
fclose(dissassembly);
|
||||
|
||||
char *dissassembly_dump_filename = "test-dump-asm.o";
|
||||
if (compile_asm(dissassembly_filename, dissassembly_dump_filename)) {
|
||||
printf("ERROR: Failed to compile '%s'", dissassembly_filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!compare_files(bin_filename, dissassembly_dump_filename)) {
|
||||
printf("Test success\n");
|
||||
} else {
|
||||
printf("Test failed\n");
|
||||
}
|
||||
if (strequal(argv[1], "test-dump")) {
|
||||
return test_decoder(argv[2]);
|
||||
|
||||
} else if (strequal(argv[1], "dump")) {
|
||||
char *input_file = argv[2];
|
||||
return dump_decompilation(argv[2]);
|
||||
|
||||
if (strendswith(input_file, ".asm")) {
|
||||
char bin_filename[MAX_PATH_SIZE];
|
||||
get_tmp_file(bin_filename, "nasm_output");
|
||||
|
||||
if (compile_asm(input_file, bin_filename)) {
|
||||
printf("ERROR: Failed to compile '%s'", input_file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE *assembly = fopen(bin_filename, "rb");
|
||||
if (assembly == NULL) {
|
||||
printf("ERROR: Opening file '%s': %d\n", bin_filename, errno);
|
||||
return -1;
|
||||
}
|
||||
dissassemble(assembly, stdout);
|
||||
fclose(assembly);
|
||||
|
||||
remove(bin_filename);
|
||||
} else {
|
||||
FILE *assembly = fopen(input_file, "r");
|
||||
if (assembly == NULL) {
|
||||
printf("ERROR: Opening file '%s': %d\n", input_file, errno);
|
||||
return -1;
|
||||
}
|
||||
dissassemble(assembly, stdout);
|
||||
fclose(assembly);
|
||||
}
|
||||
} else if (strequal(argv[1], "run")) {
|
||||
return run_simulation(argv[2]);
|
||||
|
||||
} else {
|
||||
print_usage(argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
128
src/sim8086.c
Normal file
128
src/sim8086.c
Normal file
@ -0,0 +1,128 @@
|
||||
|
||||
static const char *reg_to_str(enum reg_value reg) {
|
||||
const char *reg_value_str[__REG_COUNT] = {
|
||||
"al", "cl", "dl", "bl", "ah", "ch", "dh", "bh",
|
||||
"ax", "cx", "dx", "bx", "sp", "bp", "si", "di"
|
||||
};
|
||||
|
||||
assert(0 <= reg && reg <= __REG_COUNT);
|
||||
return reg_value_str[reg];
|
||||
}
|
||||
|
||||
static void mem_to_str(char *buff, size_t max_size, struct mem_value *mem) {
|
||||
const char *mem_base_str[8] = {
|
||||
"bx + si",
|
||||
"bx + di",
|
||||
"bp + si",
|
||||
"bp + di",
|
||||
"si",
|
||||
"di",
|
||||
"bp",
|
||||
"bx"
|
||||
};
|
||||
|
||||
assert(0 <= mem->base && mem->base <= __MEM_BASE_COUNT);
|
||||
if (mem->base == MEM_BASE_DIRECT_ADDRESS) {
|
||||
snprintf(buff, max_size, "[%d]", (u16)mem->disp);
|
||||
} else if (mem->disp > 0) {
|
||||
snprintf(buff, max_size, "[%s + %d]", mem_base_str[mem->base], mem->disp);
|
||||
} else if (mem->disp < 0) {
|
||||
snprintf(buff, max_size, "[%s - %d]", mem_base_str[mem->base], -mem->disp);
|
||||
} else {
|
||||
snprintf(buff, max_size, "[%s]", mem_base_str[mem->base]);
|
||||
}
|
||||
}
|
||||
|
||||
static void reg_or_mem_to_str(char *buff, size_t max_size, struct reg_or_mem_value *value) {
|
||||
if (value->is_reg) {
|
||||
strncpy(buff, reg_to_str(value->reg), max_size);
|
||||
} else {
|
||||
mem_to_str(buff, max_size, &value->mem);
|
||||
}
|
||||
}
|
||||
|
||||
static void src_to_str(char *buff, size_t max_size, struct src_value *value) {
|
||||
switch (value->variant)
|
||||
{
|
||||
case SRC_VALUE_REG:
|
||||
strncpy(buff, reg_to_str(value->reg), max_size);
|
||||
break;
|
||||
case SRC_VALUE_MEM:
|
||||
mem_to_str(buff, max_size, &value->mem);
|
||||
break;
|
||||
case SRC_VALUE_IMMEDIATE16:
|
||||
snprintf(buff, max_size, "%d", value->immediate);
|
||||
break;
|
||||
case SRC_VALUE_IMMEDIATE8:
|
||||
snprintf(buff, max_size, "%d", (u8)value->immediate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *operation_to_str(enum operation op) {
|
||||
const char *operation_str[__OP_COUNT] = {
|
||||
"mov", "add", "sub", "cmp", "je", "jl", "jle", "jb", "jbe", "jp", "jo",
|
||||
"js", "jne", "jnl","jnle", "jnb", "jnbe", "jnp", "jno", "jns", "loop",
|
||||
"loopz", "loopnz", "jcxz"
|
||||
};
|
||||
|
||||
assert(0 <= op && op <= __OP_COUNT);
|
||||
return operation_str[op];
|
||||
}
|
||||
|
||||
static void instruction_to_str(char *buff, size_t max_size, struct instruction *inst) {
|
||||
switch (inst->op)
|
||||
{
|
||||
case OP_MOV:
|
||||
case OP_CMP:
|
||||
case OP_SUB:
|
||||
case OP_ADD: {
|
||||
char dest[32];
|
||||
char src[32];
|
||||
const char *opcode = operation_to_str(inst->op);
|
||||
reg_or_mem_to_str(dest, sizeof(dest), &inst->dest);
|
||||
src_to_str(src, sizeof(src), &inst->src);
|
||||
|
||||
bool is_dest_mem = !inst->dest.is_reg;
|
||||
if (is_dest_mem && inst->src.variant == SRC_VALUE_IMMEDIATE16) {
|
||||
snprintf(buff, max_size, "%s %s, word %s", opcode, dest, src);
|
||||
} else if (is_dest_mem && inst->src.variant == SRC_VALUE_IMMEDIATE8) {
|
||||
snprintf(buff, max_size, "%s %s, byte %s", opcode, dest, src);
|
||||
} else {
|
||||
snprintf(buff, max_size, "%s %s, %s", opcode, dest, src);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_JE:
|
||||
case OP_JL:
|
||||
case OP_JLE:
|
||||
case OP_JB:
|
||||
case OP_JBE:
|
||||
case OP_JP:
|
||||
case OP_JO:
|
||||
case OP_JS:
|
||||
case OP_JNE:
|
||||
case OP_JNL:
|
||||
case OP_JNLE:
|
||||
case OP_JNB:
|
||||
case OP_JNBE:
|
||||
case OP_JNP:
|
||||
case OP_JNO:
|
||||
case OP_LOOP:
|
||||
case OP_LOOPZ:
|
||||
case OP_LOOPNZ:
|
||||
case OP_JCXZ:
|
||||
case OP_JNS: {
|
||||
const char *opcode = operation_to_str(inst->op);
|
||||
i8 offset = inst->jmp_offset+2;
|
||||
if (offset >= 0) {
|
||||
snprintf(buff, max_size, "%s $+%d", opcode, offset);
|
||||
} else {
|
||||
snprintf(buff, max_size, "%s $%d", opcode, offset);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
panic("Invalid instruction opcode %d\n", inst->op);
|
||||
}
|
||||
}
|
102
src/sim8086.h
Normal file
102
src/sim8086.h
Normal file
@ -0,0 +1,102 @@
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define u32 uint32_t
|
||||
#define i32 int32_t
|
||||
#define u16 uint16_t
|
||||
#define i16 int16_t
|
||||
#define u8 uint8_t
|
||||
#define i8 int8_t
|
||||
|
||||
#define panic(...) fprintf(stderr, "PANIC(%s:%d): ", __FILE__, __LINE__); fprintf(stderr, __VA_ARGS__); abort()
|
||||
#define todo(...) fprintf(stderr, "TODO(%s:%d): ", __FILE__, __LINE__); fprintf(stderr, __VA_ARGS__); abort()
|
||||
#define ARRAY_LEN(arr) sizeof(arr) / sizeof(arr[0])
|
||||
|
||||
enum operation {
|
||||
OP_MOV,
|
||||
OP_ADD,
|
||||
OP_SUB,
|
||||
OP_CMP,
|
||||
OP_JE,
|
||||
OP_JL,
|
||||
OP_JLE,
|
||||
OP_JB,
|
||||
OP_JBE,
|
||||
OP_JP,
|
||||
OP_JO,
|
||||
OP_JS,
|
||||
OP_JNE,
|
||||
OP_JNL,
|
||||
OP_JNLE,
|
||||
OP_JNB,
|
||||
OP_JNBE,
|
||||
OP_JNP,
|
||||
OP_JNO,
|
||||
OP_JNS,
|
||||
OP_LOOP,
|
||||
OP_LOOPZ,
|
||||
OP_LOOPNZ,
|
||||
OP_JCXZ,
|
||||
__OP_COUNT
|
||||
};
|
||||
|
||||
// Order and place of these `enum reg_value` enums is IMPORTANT! Don't rearrange!
|
||||
enum reg_value {
|
||||
REG_AL, REG_CL, REG_DL, REG_BL, REG_AH, REG_CH, REG_DH, REG_BH,
|
||||
REG_AX, REG_CX, REG_DX, REG_BX, REG_SP, REG_BP, REG_SI, REG_DI,
|
||||
__REG_COUNT
|
||||
};
|
||||
|
||||
// Order and place of these `enum mem_base` enums is IMPORTANT! Don't rearrange!
|
||||
enum mem_base {
|
||||
MEM_BASE_BX_SI,
|
||||
MEM_BASE_BX_DI,
|
||||
MEM_BASE_BP_SI,
|
||||
MEM_BASE_BP_DI,
|
||||
MEM_BASE_SI,
|
||||
MEM_BASE_DI,
|
||||
MEM_BASE_BP,
|
||||
MEM_BASE_BX,
|
||||
MEM_BASE_DIRECT_ADDRESS,
|
||||
__MEM_BASE_COUNT
|
||||
};
|
||||
|
||||
struct mem_value {
|
||||
enum mem_base base;
|
||||
union {
|
||||
i16 disp;
|
||||
u16 direct_address;
|
||||
};
|
||||
};
|
||||
|
||||
struct reg_or_mem_value {
|
||||
bool is_reg;
|
||||
union {
|
||||
enum reg_value reg;
|
||||
struct mem_value mem;
|
||||
};
|
||||
};
|
||||
|
||||
enum src_value_variant {
|
||||
SRC_VALUE_REG,
|
||||
SRC_VALUE_MEM,
|
||||
SRC_VALUE_IMMEDIATE8,
|
||||
SRC_VALUE_IMMEDIATE16
|
||||
};
|
||||
|
||||
struct src_value {
|
||||
enum src_value_variant variant;
|
||||
union {
|
||||
enum reg_value reg;
|
||||
struct mem_value mem;
|
||||
u16 immediate;
|
||||
};
|
||||
};
|
||||
|
||||
struct instruction {
|
||||
enum operation op;
|
||||
struct reg_or_mem_value dest;
|
||||
struct src_value src;
|
||||
i8 jmp_offset;
|
||||
};
|
@ -1,19 +1,6 @@
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define u32 uint32_t
|
||||
#define i32 int32_t
|
||||
#define u16 uint16_t
|
||||
#define i16 int16_t
|
||||
#define u8 uint8_t
|
||||
#define i8 int8_t
|
||||
|
||||
#define panic(...) fprintf(stderr, "ABORT(%s:%d): ", __FILE__, __LINE__); fprintf(stderr, __VA_ARGS__); abort()
|
||||
#define todo(...) fprintf(stderr, "TODO(%s:%d): ", __FILE__, __LINE__); fprintf(stderr, __VA_ARGS__); abort()
|
||||
#define dbg(...) printf("; "); printf(__VA_ARGS__); printf("\n")
|
||||
#define ARRAY_LEN(arr) sizeof(arr) / sizeof(arr[0])
|
||||
|
||||
// TODO: find a way to merge "to/from register" with "to/from accumulator" branches into a single code path
|
||||
|
||||
@ -24,30 +11,6 @@ enum decode_error {
|
||||
DECODE_ERR_UNKNOWN_OP,
|
||||
};
|
||||
|
||||
enum operation {
|
||||
OP_MOV,
|
||||
OP_ADD,
|
||||
OP_SUB,
|
||||
OP_CMP,
|
||||
OP_JE, OP_JL, OP_JLE, OP_JB, OP_JBE,
|
||||
OP_JP,
|
||||
OP_JO,
|
||||
OP_JS,
|
||||
OP_JNE, OP_JNL, OP_JNLE, OP_JNB, OP_JNBE,
|
||||
OP_JNP,
|
||||
OP_JNO,
|
||||
OP_JNS,
|
||||
OP_LOOP,
|
||||
OP_LOOPZ,
|
||||
OP_LOOPNZ,
|
||||
OP_JCXZ,
|
||||
__OP_COUNT
|
||||
};
|
||||
const char *operation_str[__OP_COUNT] = {
|
||||
"mov", "add", "sub", "cmp", "je", "jl", "jle", "jb", "jbe", "jp", "jo",
|
||||
"js", "jne", "jnl","jnle", "jnb", "jnbe", "jnp", "jno", "jns", "loop",
|
||||
"loopz", "loopnz", "jcxz"
|
||||
};
|
||||
const enum operation cond_jmp_lookup[16] = {
|
||||
[0b0100] = OP_JE,
|
||||
[0b1100] = OP_JL,
|
||||
@ -73,184 +36,6 @@ const enum operation cond_loop_jmp_lookup[4] = {
|
||||
[0b11] = OP_JCXZ
|
||||
};
|
||||
|
||||
// Order and place of these `enum reg_value` enums is IMPORTANT! Don't rearrange!
|
||||
enum reg_value {
|
||||
REG_AL, REG_CL, REG_DL, REG_BL, REG_AH, REG_CH, REG_DH, REG_BH,
|
||||
REG_AX, REG_CX, REG_DX, REG_BX, REG_SP, REG_BP, REG_SI, REG_DI,
|
||||
__REG_COUNT
|
||||
};
|
||||
const char *reg_value_str[__REG_COUNT] = {
|
||||
"al", "cl", "dl", "bl", "ah", "ch", "dh", "bh",
|
||||
"ax", "cx", "dx", "bx", "sp", "bp", "si", "di"
|
||||
};
|
||||
|
||||
// Order and place of these `enum mem_base` enums is IMPORTANT! Don't rearrange!
|
||||
enum mem_base {
|
||||
MEM_BASE_BX_SI,
|
||||
MEM_BASE_BX_DI,
|
||||
MEM_BASE_BP_SI,
|
||||
MEM_BASE_BP_DI,
|
||||
MEM_BASE_SI,
|
||||
MEM_BASE_DI,
|
||||
MEM_BASE_BP,
|
||||
MEM_BASE_BX,
|
||||
MEM_BASE_DIRECT_ADDRESS,
|
||||
__MEM_BASE_COUNT
|
||||
};
|
||||
const char *mem_base_str[8] = {
|
||||
"bx + si",
|
||||
"bx + di",
|
||||
"bp + si",
|
||||
"bp + di",
|
||||
"si",
|
||||
"di",
|
||||
"bp",
|
||||
"bx"
|
||||
};
|
||||
|
||||
struct mem_value {
|
||||
enum mem_base base;
|
||||
i16 disp;
|
||||
// IMPORTANT! Keep in mind that `disp` should be interpreted as `u16`, if `base == MEM_BASE_DIRECT_ADDRESS`
|
||||
};
|
||||
|
||||
struct reg_or_mem_value {
|
||||
bool is_reg;
|
||||
union {
|
||||
enum reg_value reg;
|
||||
struct mem_value mem;
|
||||
};
|
||||
};
|
||||
|
||||
enum src_value_variant {
|
||||
SRC_VALUE_REG,
|
||||
SRC_VALUE_MEM,
|
||||
SRC_VALUE_immediate8,
|
||||
SRC_VALUE_immediate16
|
||||
};
|
||||
|
||||
struct src_value {
|
||||
enum src_value_variant variant;
|
||||
union {
|
||||
enum reg_value reg;
|
||||
struct mem_value mem;
|
||||
u16 immediate;
|
||||
};
|
||||
};
|
||||
|
||||
struct instruction {
|
||||
enum operation op;
|
||||
struct reg_or_mem_value dest;
|
||||
struct src_value src;
|
||||
i8 jmp_offset;
|
||||
};
|
||||
|
||||
static const char *reg_to_str(enum reg_value reg) {
|
||||
assert(0 <= reg && reg <= __REG_COUNT);
|
||||
return reg_value_str[reg];
|
||||
}
|
||||
|
||||
static void mem_to_str(char *buff, size_t max_size, struct mem_value *mem) {
|
||||
assert(0 <= mem->base && mem->base <= __MEM_BASE_COUNT);
|
||||
if (mem->base == MEM_BASE_DIRECT_ADDRESS) {
|
||||
snprintf(buff, max_size, "[%d]", (u16)mem->disp);
|
||||
} else if (mem->disp > 0) {
|
||||
snprintf(buff, max_size, "[%s + %d]", mem_base_str[mem->base], mem->disp);
|
||||
} else if (mem->disp < 0) {
|
||||
snprintf(buff, max_size, "[%s - %d]", mem_base_str[mem->base], -mem->disp);
|
||||
} else {
|
||||
snprintf(buff, max_size, "[%s]", mem_base_str[mem->base]);
|
||||
}
|
||||
}
|
||||
|
||||
static void reg_or_mem_to_str(char *buff, size_t max_size, struct reg_or_mem_value *value) {
|
||||
if (value->is_reg) {
|
||||
strncpy(buff, reg_to_str(value->reg), max_size);
|
||||
} else {
|
||||
mem_to_str(buff, max_size, &value->mem);
|
||||
}
|
||||
}
|
||||
|
||||
static void src_to_str(char *buff, size_t max_size, struct src_value *value) {
|
||||
switch (value->variant)
|
||||
{
|
||||
case SRC_VALUE_REG:
|
||||
strncpy(buff, reg_to_str(value->reg), max_size);
|
||||
break;
|
||||
case SRC_VALUE_MEM:
|
||||
mem_to_str(buff, max_size, &value->mem);
|
||||
break;
|
||||
case SRC_VALUE_immediate16:
|
||||
snprintf(buff, max_size, "%d", value->immediate);
|
||||
break;
|
||||
case SRC_VALUE_immediate8:
|
||||
snprintf(buff, max_size, "%d", (u8)value->immediate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *operation_to_str(enum operation op) {
|
||||
assert(0 <= op && op <= __OP_COUNT);
|
||||
return operation_str[op];
|
||||
}
|
||||
|
||||
static void instruction_to_str(char *buff, size_t max_size, struct instruction *inst) {
|
||||
switch (inst->op)
|
||||
{
|
||||
case OP_MOV:
|
||||
case OP_CMP:
|
||||
case OP_SUB:
|
||||
case OP_ADD: {
|
||||
char dest[32];
|
||||
char src[32];
|
||||
const char *opcode = operation_to_str(inst->op);
|
||||
reg_or_mem_to_str(dest, sizeof(dest), &inst->dest);
|
||||
src_to_str(src, sizeof(src), &inst->src);
|
||||
|
||||
bool is_dest_mem = !inst->dest.is_reg;
|
||||
if (is_dest_mem && inst->src.variant == SRC_VALUE_immediate16) {
|
||||
snprintf(buff, max_size, "%s %s, word %s", opcode, dest, src);
|
||||
} else if (is_dest_mem && inst->src.variant == SRC_VALUE_immediate8) {
|
||||
snprintf(buff, max_size, "%s %s, byte %s", opcode, dest, src);
|
||||
} else {
|
||||
snprintf(buff, max_size, "%s %s, %s", opcode, dest, src);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_JE:
|
||||
case OP_JL:
|
||||
case OP_JLE:
|
||||
case OP_JB:
|
||||
case OP_JBE:
|
||||
case OP_JP:
|
||||
case OP_JO:
|
||||
case OP_JS:
|
||||
case OP_JNE:
|
||||
case OP_JNL:
|
||||
case OP_JNLE:
|
||||
case OP_JNB:
|
||||
case OP_JNBE:
|
||||
case OP_JNP:
|
||||
case OP_JNO:
|
||||
case OP_LOOP:
|
||||
case OP_LOOPZ:
|
||||
case OP_LOOPNZ:
|
||||
case OP_JCXZ:
|
||||
case OP_JNS: {
|
||||
const char *opcode = operation_to_str(inst->op);
|
||||
i8 offset = inst->jmp_offset+2;
|
||||
if (offset >= 0) {
|
||||
snprintf(buff, max_size, "%s $+%d", opcode, offset);
|
||||
} else {
|
||||
snprintf(buff, max_size, "%s $%d", opcode, offset);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
panic("Invalid instruction opcode %d\n", inst->op);
|
||||
}
|
||||
}
|
||||
|
||||
static i16 extend_sign_bit(i8 number) {
|
||||
if (number & 0b10000000) {
|
||||
return number | (0b11111111 << 8);
|
||||
@ -366,10 +151,10 @@ enum decode_error decode_instruction(FILE *src, struct instruction *output) {
|
||||
output->dest.reg = decode_reg(reg, wide);
|
||||
|
||||
if (wide) {
|
||||
output->src.variant = SRC_VALUE_immediate16;
|
||||
output->src.variant = SRC_VALUE_IMMEDIATE16;
|
||||
output->src.immediate = fgetc(src) | (fgetc(src) << 8);
|
||||
} else {
|
||||
output->src.variant = SRC_VALUE_immediate8;
|
||||
output->src.variant = SRC_VALUE_IMMEDIATE8;
|
||||
output->src.immediate = fgetc(src);
|
||||
}
|
||||
|
||||
@ -385,10 +170,10 @@ enum decode_error decode_instruction(FILE *src, struct instruction *output) {
|
||||
decode_reg_or_mem(&output->dest, src, rm, mod, wide);
|
||||
|
||||
if (wide) {
|
||||
output->src.variant = SRC_VALUE_immediate16;
|
||||
output->src.variant = SRC_VALUE_IMMEDIATE16;
|
||||
output->src.immediate = fgetc(src) | (fgetc(src) << 8);
|
||||
} else {
|
||||
output->src.variant = SRC_VALUE_immediate8;
|
||||
output->src.variant = SRC_VALUE_IMMEDIATE8;
|
||||
output->src.immediate = fgetc(src);
|
||||
}
|
||||
|
||||
@ -473,7 +258,7 @@ enum decode_error decode_instruction(FILE *src, struct instruction *output) {
|
||||
decode_reg_or_mem(&output->dest, src, rm, mod, wide);
|
||||
|
||||
if (wide) {
|
||||
output->src.variant = SRC_VALUE_immediate16;
|
||||
output->src.variant = SRC_VALUE_IMMEDIATE16;
|
||||
if (sign_extend) {
|
||||
output->src.immediate = fgetc(src);
|
||||
output->src.immediate = extend_sign_bit(output->src.immediate);
|
||||
@ -481,7 +266,7 @@ enum decode_error decode_instruction(FILE *src, struct instruction *output) {
|
||||
output->src.immediate = fgetc(src) | (fgetc(src) << 8);
|
||||
}
|
||||
} else {
|
||||
output->src.variant = SRC_VALUE_immediate8;
|
||||
output->src.variant = SRC_VALUE_IMMEDIATE8;
|
||||
output->src.immediate = fgetc(src);
|
||||
}
|
||||
|
||||
@ -502,10 +287,10 @@ enum decode_error decode_instruction(FILE *src, struct instruction *output) {
|
||||
}
|
||||
|
||||
if (wide) {
|
||||
output->src.variant = SRC_VALUE_immediate16;
|
||||
output->src.variant = SRC_VALUE_IMMEDIATE16;
|
||||
output->src.immediate = fgetc(src) | (fgetc(src) << 8);
|
||||
} else {
|
||||
output->src.variant = SRC_VALUE_immediate8;
|
||||
output->src.variant = SRC_VALUE_IMMEDIATE8;
|
||||
output->src.immediate = fgetc(src);
|
||||
}
|
||||
|
||||
@ -516,7 +301,7 @@ enum decode_error decode_instruction(FILE *src, struct instruction *output) {
|
||||
output->op = cond_jmp_lookup[opcode];
|
||||
output->jmp_offset = jmp_offset;
|
||||
|
||||
// Conditional jumps
|
||||
// Conditional loop jumps
|
||||
} else if ((byte1 & 0b11111100) == 0b11100000) {
|
||||
i8 jmp_offset = fgetc(src);
|
||||
u8 opcode = byte1 & 0b00000011;
|
||||
@ -529,26 +314,3 @@ enum decode_error decode_instruction(FILE *src, struct instruction *output) {
|
||||
|
||||
return DECODE_OK;
|
||||
}
|
||||
|
||||
int dissassemble(FILE *src, FILE *dst) {
|
||||
fprintf(dst, "bits 16\n\n");
|
||||
|
||||
char buff[256];
|
||||
struct instruction inst;
|
||||
int counter = 1;
|
||||
while (true) {
|
||||
enum decode_error err = decode_instruction(src, &inst);
|
||||
if (err == DECODE_ERR_EOF) break;
|
||||
if (err != DECODE_OK) {
|
||||
fprintf(stderr, "ERROR: Failed to decode %d instruction: %s\n", counter, decode_error_to_str(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
instruction_to_str(buff, sizeof(buff), &inst);
|
||||
fprintf(dst, buff);
|
||||
fprintf(dst, "\n");
|
||||
counter += 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
0
src/sim8086_simulator.c
Normal file
0
src/sim8086_simulator.c
Normal file
Loading…
Reference in New Issue
Block a user