add estimation of clock cycles
This commit is contained in:
parent
e61281fc89
commit
d38cb3c328
@ -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
|
41
examples/56_estimating_cycles.asm
Normal file
41
examples/56_estimating_cycles.asm
Normal 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
|
72
examples/56_estimating_cycles.txt
Normal file
72
examples/56_estimating_cycles.txt
Normal 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)
|
66
src/main.c
66
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 <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;
|
||||
|
@ -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));
|
||||
}
|
Loading…
Reference in New Issue
Block a user