1
0

add estimation of clock cycles

This commit is contained in:
Rokas Puzonas 2023-05-07 18:27:22 +03:00
parent e61281fc89
commit d38cb3c328
5 changed files with 259 additions and 2 deletions

View File

@ -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
* Memory addressing - 83
* Clocks per instruction - 66

View File

@ -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

View File

@ -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)

View File

@ -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 <test-dump|dump|sim|sim-dump> ...\n", 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) {
@ -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;

View File

@ -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));
}