1
0
8086/src/main.c

376 lines
9.6 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include "os.h"
#include "sim8086.h"
#include "sim8086.c"
#include "sim8086_memory.c"
#include "sim8086_decoder.c"
#include "sim8086_simulator.c"
// TODO: refactor cli commands, there is a lot of repeating code for reading assemblies and compiling them.
#define strequal(a, b) strcmp(a, b) == 0
const char *get_tmp_dir() {
#ifdef IS_WINDOWS
char *dir;
if ((dir = getenv("TMPDIR")) != NULL) return dir;
if ((dir = getenv("TEMP")) != NULL) return dir;
if ((dir = getenv("TMP")) != NULL) return dir;
return NULL;
#elif IS_LINUX
return "/tmp";
#endif
}
int strendswith(const char *str, const char *suffix)
{
if (!str || !suffix)
return 0;
size_t lenstr = strlen(str);
size_t lensuffix = strlen(suffix);
if (lensuffix > lenstr)
return 0;
return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0;
}
char *strdup(const char *src) {
char *dst = malloc(strlen (src) + 1);
if (dst == NULL) return NULL;
strcpy(dst, src);
return dst;
}
void get_tmp_file(char *filename, const char *prefix) {
const char *dir = get_tmp_dir();
sprintf(filename, "%s\\%sXXXXXX", dir, prefix);
int fd = mkstemp(filename);
close(fd);
}
int compile_asm(const char *src, const char *dst) {
char command[512] = { 0 };
snprintf(command, sizeof(command), "nasm \"%s\" -O0 -o \"%s\"", src, dst);
return system(command);
}
int compare_files(const char *expected, const char *gotten) {
int rc = -1;
FILE *f1 = fopen(expected, "r");
FILE *f2 = fopen(gotten, "r");
int i = 0;
while (!feof(f1) && !feof(f2)) {
int byte1 = fgetc(f1);
int byte2 = fgetc(f2);
if (byte1 != byte2) {
printf("Mismatch byte at %d, expected %d got %d\n", i, byte1, byte2);
goto err;
}
i++;
}
if (!feof(f1) || !feof(f2)) {
goto err;
}
rc = 0;
err:
fclose(f1);
fclose(f2);
return rc;
}
int dissassemble(FILE *src, FILE *dst) {
fprintf(dst, "bits 16\n\n");
struct memory mem = { 0 };
int byte_count = load_mem_from_stream(&mem, src, 0);
if (byte_count == -1) {
fprintf(stderr, "ERROR: Failed to load file to memory\n");
return -1;
}
char buff[256];
struct instruction inst;
u16 inst_address = 0;
while (inst_address < byte_count) {
enum decode_error err = decode_instruction(&mem, &inst_address, &inst);
if (err == DECODE_ERR_EOF) break;
if (err != DECODE_OK) {
fprintf(stderr, "ERROR: Failed to decode instruction at 0x%08x: %s\n", inst_address, decode_error_to_str(err));
return -1;
}
instruction_to_str(buff, sizeof(buff), &inst);
fprintf(dst, buff);
fprintf(dst, "\n");
}
return 0;
}
int simulate(FILE *src, struct memory *mem) {
int byte_count = load_mem_from_stream(mem, src, 0);
if (byte_count == -1) {
fprintf(stderr, "ERROR: Failed to load file to memory\n");
return -1;
}
struct cpu_state state = { 0 };
struct instruction inst;
while (state.ip < byte_count) {
enum decode_error err = decode_instruction(mem, &state.ip, &inst);
if (err == DECODE_ERR_EOF) break;
if (err != DECODE_OK) {
fprintf(stderr, "ERROR: Failed to decode instruction at 0x%08x: %s\n", state.ip, decode_error_to_str(err));
return -1;
}
execute_instruction(mem, &state, &inst);
}
printf("Final registers:\n");
printf(" ax: 0x%04x (%d)\n", state.ax, state.ax);
printf(" bx: 0x%04x (%d)\n", state.bx, state.bx);
printf(" cx: 0x%04x (%d)\n", state.cx, state.cx);
printf(" dx: 0x%04x (%d)\n", state.dx, state.dx);
printf(" sp: 0x%04x (%d)\n", state.sp, state.sp);
printf(" bp: 0x%04x (%d)\n", state.bp, state.bp);
printf(" si: 0x%04x (%d)\n", state.si, state.si);
printf(" di: 0x%04x (%d)\n", state.di, state.di);
printf(" ip: 0x%04x (%d)\n", state.ip, state.ip);
printf(" flags: %s%s\n", state.flags.sign ? "S" : "", state.flags.zero ? "Z" : "");
return 0;
}
int estimate_clocks(FILE *src) {
struct memory mem = { 0 };
int byte_count = load_mem_from_stream(&mem, src, 0);
if (byte_count == -1) {
fprintf(stderr, "ERROR: Failed to load file to memory\n");
return -1;
}
u32 total_clocks = 0;
char buff[256];
struct cpu_state state = { 0 };
struct instruction inst;
while (state.ip < byte_count) {
enum decode_error err = decode_instruction(&mem, &state.ip, &inst);
if (err == DECODE_ERR_EOF) break;
if (err != DECODE_OK) {
fprintf(stderr, "ERROR: Failed to decode instruction at 0x%08x: %s\n", state.ip, decode_error_to_str(err));
return -1;
}
execute_instruction(&mem, &state, &inst);
u32 clocks = estimate_instruction_clocks(&inst);
total_clocks += clocks;
instruction_to_str(buff, sizeof(buff), &inst);
printf("%s ; Clocks = %d (+%d)\n", buff, total_clocks, clocks);
}
return 0;
}
void print_usage(const char *program) {
fprintf(stderr, "Usage: %s <command> ...\n", program);
fprintf(stderr, "\ttest-dump <file.asm> - disassemble and test output\n");
fprintf(stderr, "\tdump <file> - disassemble\n");
fprintf(stderr, "\tsim <file> - simulate program\n");
fprintf(stderr, "\tsim-dump <file> <output> - simulate program and dump memory to file\n");
fprintf(stderr, "\tclocks <file> - output estimation of clocks\n");
}
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_with_memory(const char *input, struct memory *mem) {
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, mem);
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, mem);
fclose(assembly);
}
return 0;
}
int run_simulation(const char *input) {
struct memory mem = { 0 };
return run_simulation_with_memory(input, &mem);
}
int run_simulation_and_dump(const char *input, char const *output) {
struct memory mem = { 0 };
int rc = run_simulation_with_memory(input, &mem);
if (rc) return rc;
FILE *output_file = fopen(output, "wb");
if (output_file == NULL) {
return -1;
}
int written = fwrite(mem.mem, sizeof(u8), MEMORY_SIZE, output_file);
if (written != MEMORY_SIZE) {
fclose(output_file);
return -1;
}
return fclose(output_file);
}
int run_estimate_clocks(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;
}
estimate_clocks(assembly);
fclose(assembly);
remove(bin_filename);
} else {
return -1;
}
return 0;
}
int main(int argc, char **argv) {
if (argc <= 2) {
print_usage(argv[0]);
return -1;
}
if (strequal(argv[1], "test-dump") && argc == 3) {
return test_decoder(argv[2]);
} else if (strequal(argv[1], "dump") && argc == 3) {
return dump_decompilation(argv[2]);
} else if (strequal(argv[1], "sim") && argc == 3) {
return run_simulation(argv[2]);
} else if (strequal(argv[1], "sim-dump") && argc == 4) {
return run_simulation_and_dump(argv[2], argv[3]);
} else if (strequal(argv[1], "clocks") && argc == 3) {
return run_estimate_clocks(argv[2]);
} else {
print_usage(argv[0]);
return -1;
}
}