From d38cb3c32840908744262179e29a55537072484e Mon Sep 17 00:00:00 2001 From: Rokas Puzonas Date: Sun, 7 May 2023 18:27:22 +0300 Subject: [PATCH] add estimation of clock cycles --- README.md | 3 +- examples/56_estimating_cycles.asm | 41 ++++++++++++++++ examples/56_estimating_cycles.txt | 72 ++++++++++++++++++++++++++++ src/main.c | 66 +++++++++++++++++++++++++- src/sim8086_simulator.c | 79 +++++++++++++++++++++++++++++++ 5 files changed, 259 insertions(+), 2 deletions(-) create mode 100644 examples/56_estimating_cycles.asm create mode 100644 examples/56_estimating_cycles.txt diff --git a/README.md b/README.md index cbd804d..79e6428 100644 --- a/README.md +++ b/README.md @@ -8,4 +8,5 @@ Examples gotten from: https://github.com/cmuratori/computer_enhance/tree/main/pe Important pages in manual: * Registers - 24 * Instruction structures - 163 -* Memory addressing - 83 \ No newline at end of file +* Memory addressing - 83 +* Clocks per instruction - 66 \ No newline at end of file diff --git a/examples/56_estimating_cycles.asm b/examples/56_estimating_cycles.asm new file mode 100644 index 0000000..bf44d46 --- /dev/null +++ b/examples/56_estimating_cycles.asm @@ -0,0 +1,41 @@ +; ======================================================================== +; +; (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved. +; +; This software is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any damages +; arising from the use of this software. +; +; Please see https://computerenhance.com for further information +; +; ======================================================================== + +; ======================================================================== +; LISTING 56 +; ======================================================================== + +bits 16 + +mov bx, 1000 +mov bp, 2000 +mov si, 3000 +mov di, 4000 + +mov cx, bx +mov dx, 12 + +mov dx, [1000] + +mov cx, [bx] +mov cx, [bp] +mov [si], cx +mov [di], cx + +mov cx, [bx + 1000] +mov cx, [bp + 1000] +mov [si + 1000], cx +mov [di + 1000], cx + +add cx, dx +add [di + 1000], cx +add dx, 50 \ No newline at end of file diff --git a/examples/56_estimating_cycles.txt b/examples/56_estimating_cycles.txt new file mode 100644 index 0000000..8b92b35 --- /dev/null +++ b/examples/56_estimating_cycles.txt @@ -0,0 +1,72 @@ +************** +**** 8086 **** +************** + +WARNING: Clocks reported by this utility are strictly from the 8086 manual. +They will be inaccurate, both because the manual clocks are estimates, and because +some of the entries in the manual look highly suspicious and are probably typos. + +--- test\listing_0056_estimating_cycles execution --- +mov bx, 1000 ; Clocks: +4 = 4 | bx:0x0->0x3e8 ip:0x0->0x3 +mov bp, 2000 ; Clocks: +4 = 8 | bp:0x0->0x7d0 ip:0x3->0x6 +mov si, 3000 ; Clocks: +4 = 12 | si:0x0->0xbb8 ip:0x6->0x9 +mov di, 4000 ; Clocks: +4 = 16 | di:0x0->0xfa0 ip:0x9->0xc +mov cx, bx ; Clocks: +2 = 18 | cx:0x0->0x3e8 ip:0xc->0xe +mov dx, 12 ; Clocks: +4 = 22 | dx:0x0->0xc ip:0xe->0x11 +mov dx, [+1000] ; Clocks: +14 = 36 (8 + 6ea) | dx:0xc->0x0 ip:0x11->0x15 +mov cx, [bx] ; Clocks: +13 = 49 (8 + 5ea) | cx:0x3e8->0x0 ip:0x15->0x17 +mov cx, [bp] ; Clocks: +13 = 62 (8 + 5ea) | ip:0x17->0x1a +mov word [si], cx ; Clocks: +14 = 76 (9 + 5ea) | ip:0x1a->0x1c +mov word [di], cx ; Clocks: +14 = 90 (9 + 5ea) | ip:0x1c->0x1e +mov cx, [bx+1000] ; Clocks: +17 = 107 (8 + 9ea) | ip:0x1e->0x22 +mov cx, [bp+1000] ; Clocks: +17 = 124 (8 + 9ea) | ip:0x22->0x26 +mov word [si+1000], cx ; Clocks: +18 = 142 (9 + 9ea) | ip:0x26->0x2a +mov word [di+1000], cx ; Clocks: +18 = 160 (9 + 9ea) | ip:0x2a->0x2e +add cx, dx ; Clocks: +3 = 163 | ip:0x2e->0x30 flags:->PZ +add word [di+1000], cx ; Clocks: +25 = 188 (16 + 9ea) | ip:0x30->0x34 +add dx, 50 ; Clocks: +4 = 192 | dx:0x0->0x32 ip:0x34->0x37 flags:PZ-> + +Final registers: + bx: 0x03e8 (1000) + dx: 0x0032 (50) + bp: 0x07d0 (2000) + si: 0x0bb8 (3000) + di: 0x0fa0 (4000) + ip: 0x0037 (55) + + +************** +**** 8088 **** +************** + +WARNING: Clocks reported by this utility are strictly from the 8086 manual. +They will be inaccurate, both because the manual clocks are estimates, and because +some of the entries in the manual look highly suspicious and are probably typos. + +--- test\listing_0056_estimating_cycles execution --- +mov bx, 1000 ; Clocks: +4 = 4 | bx:0x0->0x3e8 ip:0x0->0x3 +mov bp, 2000 ; Clocks: +4 = 8 | bp:0x0->0x7d0 ip:0x3->0x6 +mov si, 3000 ; Clocks: +4 = 12 | si:0x0->0xbb8 ip:0x6->0x9 +mov di, 4000 ; Clocks: +4 = 16 | di:0x0->0xfa0 ip:0x9->0xc +mov cx, bx ; Clocks: +2 = 18 | cx:0x0->0x3e8 ip:0xc->0xe +mov dx, 12 ; Clocks: +4 = 22 | dx:0x0->0xc ip:0xe->0x11 +mov dx, [+1000] ; Clocks: +18 = 40 (8 + 6ea + 4p) | dx:0xc->0x0 ip:0x11->0x15 +mov cx, [bx] ; Clocks: +17 = 57 (8 + 5ea + 4p) | cx:0x3e8->0x0 ip:0x15->0x17 +mov cx, [bp] ; Clocks: +17 = 74 (8 + 5ea + 4p) | ip:0x17->0x1a +mov word [si], cx ; Clocks: +18 = 92 (9 + 5ea + 4p) | ip:0x1a->0x1c +mov word [di], cx ; Clocks: +18 = 110 (9 + 5ea + 4p) | ip:0x1c->0x1e +mov cx, [bx+1000] ; Clocks: +21 = 131 (8 + 9ea + 4p) | ip:0x1e->0x22 +mov cx, [bp+1000] ; Clocks: +21 = 152 (8 + 9ea + 4p) | ip:0x22->0x26 +mov word [si+1000], cx ; Clocks: +22 = 174 (9 + 9ea + 4p) | ip:0x26->0x2a +mov word [di+1000], cx ; Clocks: +22 = 196 (9 + 9ea + 4p) | ip:0x2a->0x2e +add cx, dx ; Clocks: +3 = 199 | ip:0x2e->0x30 flags:->PZ +add word [di+1000], cx ; Clocks: +33 = 232 (16 + 9ea + 8p) | ip:0x30->0x34 +add dx, 50 ; Clocks: +4 = 236 | dx:0x0->0x32 ip:0x34->0x37 flags:PZ-> + +Final registers: + bx: 0x03e8 (1000) + dx: 0x0032 (50) + bp: 0x07d0 (2000) + si: 0x0bb8 (3000) + di: 0x0fa0 (4000) + ip: 0x0037 (55) \ No newline at end of file diff --git a/src/main.c b/src/main.c index 2f50437..8d6aaf7 100644 --- a/src/main.c +++ b/src/main.c @@ -12,6 +12,8 @@ #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() { @@ -146,12 +148,44 @@ int simulate(FILE *src, struct memory *mem) { 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 ...\n", program); + fprintf(stderr, "Usage: %s ...\n", program); fprintf(stderr, "\ttest-dump - disassemble and test output\n"); fprintf(stderr, "\tdump - disassemble\n"); fprintf(stderr, "\tsim - simulate program\n"); fprintf(stderr, "\tsim-dump - simulate program and dump memory to file\n"); + fprintf(stderr, "\tclocks - output estimation of clocks\n"); } int test_decoder(const char *asm_file) { @@ -286,6 +320,33 @@ int run_simulation_and_dump(const char *input, char const *output) { 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]); @@ -304,6 +365,9 @@ int main(int argc, char **argv) { } 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; diff --git a/src/sim8086_simulator.c b/src/sim8086_simulator.c index 3ef9eda..3be6396 100644 --- a/src/sim8086_simulator.c +++ b/src/sim8086_simulator.c @@ -243,4 +243,83 @@ void execute_instruction(struct memory *mem, struct cpu_state *cpu, struct instr } default: todo("Unhandled instruction execution '%s'\n", operation_to_str(inst->op)); } +} + +int estimate_ea_clocks(struct mem_value *value) { + bool has_disp = value->disp != 0; + switch (value->base) + { + case MEM_BASE_DIRECT_ADDRESS: + return 6; + case MEM_BASE_SI: + case MEM_BASE_DI: + case MEM_BASE_BP: + case MEM_BASE_BX: + return 5 + (has_disp ? 4 : 0); + case MEM_BASE_BP_DI: + case MEM_BASE_BX_SI: + return 7 + (has_disp ? 4 : 0); + case MEM_BASE_BX_DI: + case MEM_BASE_BP_SI: + return 8 + (has_disp ? 4 : 0); + default: + panic("Unhandled EA clocks estimation case '%d'\n", value->base); + } +} + +u32 estimate_instruction_clocks(struct instruction *inst) { + switch (inst->op) { + case OP_MOV: { + bool is_src_memory = inst->src.variant == SRC_VALUE_MEM; + bool is_dest_memory = !inst->dest.is_reg; + bool is_src_accumulator = inst->src.variant == SRC_VALUE_REG && inst->src.reg == REG_AX; + bool is_dest_accumulator = inst->dest.is_reg && inst->dest.reg == REG_AX; + bool is_src_reg = inst->src.variant == SRC_VALUE_REG; + bool is_dest_reg = inst->dest.is_reg; + bool is_src_immediate = inst->src.variant == SRC_VALUE_IMMEDIATE8 || inst->src.variant == SRC_VALUE_IMMEDIATE16; + + if ((is_src_accumulator && is_dest_memory) || (is_dest_accumulator && is_src_memory)) { + return 10; + } else if (is_src_reg && is_dest_reg) { + return 2; + } else if (is_dest_reg && is_src_memory) { + return 8 + estimate_ea_clocks(&inst->src.mem); + } else if (is_dest_memory && is_src_reg) { + return 9 + estimate_ea_clocks(&inst->dest.mem); + } else if (is_dest_reg && is_src_immediate) { + return 4; + } else if (is_dest_memory && is_src_immediate) { + return 10 + estimate_ea_clocks(&inst->dest.mem); + } + + break; + } + case OP_ADD: { + bool is_src_memory = inst->src.variant == SRC_VALUE_MEM; + bool is_dest_memory = !inst->dest.is_reg; + bool is_src_reg = inst->src.variant == SRC_VALUE_REG; + bool is_dest_reg = inst->dest.is_reg; + bool is_dest_accumulator = inst->dest.is_reg && inst->dest.reg == REG_AX; + bool is_src_immediate = inst->src.variant == SRC_VALUE_IMMEDIATE8 || inst->src.variant == SRC_VALUE_IMMEDIATE16; + + if (is_src_reg && is_dest_reg) { + return 3; + } else if (is_dest_reg && is_src_memory) { + return 9 + estimate_ea_clocks(&inst->src.mem); + } else if (is_dest_memory && is_src_reg) { + return 16 + estimate_ea_clocks(&inst->dest.mem); + } else if (is_dest_reg && is_src_immediate) { + return 4; + } else if (is_dest_memory && is_src_immediate) { + return 17 + estimate_ea_clocks(&inst->dest.mem); + } else if (is_dest_accumulator && is_src_immediate) { + return 4; + } + + break; + } default: + todo("Unhandled instruction estimation '%s'\n", operation_to_str(inst->op)); + } + + todo("Unhandled estimation variant '%s'\n", operation_to_str(inst->op)); } \ No newline at end of file