1
0

Compare commits

...

4 Commits

17 changed files with 29655 additions and 132 deletions

View File

@ -4,4 +4,9 @@ For [Computer, Enhance!](https://www.computerenhance.com/)
Examples gotten from: https://github.com/cmuratori/computer_enhance/tree/main/perfaware/part1
Reference: https://edge.edx.org/c4x/BITSPilani/EEE231/asset/8086_family_Users_Manual_1_.pdf
8086 reference manual: https://edge.edx.org/c4x/BITSPilani/EEE231/asset/8086_family_Users_Manual_1_.pdf
Important pages in manual:
* Registers - 24
* Instruction structures - 163
* Memory addressing - 83
* Clocks per instruction - 66

View File

@ -0,0 +1,30 @@
; ========================================================================
;
; (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 51
; ========================================================================
bits 16
mov word [1000], 1
mov word [1002], 2
mov word [1004], 3
mov word [1006], 4
mov bx, 1000
mov word [bx + 4], 10
mov bx, word [1000]
mov cx, word [1002]
mov dx, word [1004]
mov bp, word [1006]

View File

@ -0,0 +1,18 @@
--- test\listing_0051_memory_mov execution ---
mov word [+1000], 1 ; ip:0x0->0x6
mov word [+1002], 2 ; ip:0x6->0xc
mov word [+1004], 3 ; ip:0xc->0x12
mov word [+1006], 4 ; ip:0x12->0x18
mov bx, 1000 ; bx:0x0->0x3e8 ip:0x18->0x1b
mov word [bx+4], 10 ; ip:0x1b->0x20
mov bx, [+1000] ; bx:0x3e8->0x1 ip:0x20->0x24
mov cx, [+1002] ; cx:0x0->0x2 ip:0x24->0x28
mov dx, [+1004] ; dx:0x0->0xa ip:0x28->0x2c
mov bp, [+1006] ; bp:0x0->0x4 ip:0x2c->0x30
Final registers:
bx: 0x0001 (1)
cx: 0x0002 (2)
dx: 0x000a (10)
bp: 0x0004 (4)
ip: 0x0030 (48)

View File

@ -0,0 +1,36 @@
; ========================================================================
;
; (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 52
; ========================================================================
bits 16
mov dx, 6
mov bp, 1000
mov si, 0
init_loop_start:
mov word [bp + si], si
add si, 2
cmp si, dx
jnz init_loop_start
mov bx, 0
mov si, 0
add_loop_start:
mov cx, word [bp + si]
add bx, cx
add si, 2
cmp si, dx
jnz add_loop_start

View File

@ -0,0 +1,42 @@
--- test\listing_0052_memory_add_loop execution ---
mov dx, 6 ; dx:0x0->0x6 ip:0x0->0x3
mov bp, 1000 ; bp:0x0->0x3e8 ip:0x3->0x6
mov si, 0 ; ip:0x6->0x9
mov word [bp+si], si ; ip:0x9->0xb
add si, 2 ; si:0x0->0x2 ip:0xb->0xe
cmp si, dx ; ip:0xe->0x10 flags:->CPAS
jne $-7 ; ip:0x10->0x9
mov word [bp+si], si ; ip:0x9->0xb
add si, 2 ; si:0x2->0x4 ip:0xb->0xe flags:CPAS->
cmp si, dx ; ip:0xe->0x10 flags:->CAS
jne $-7 ; ip:0x10->0x9
mov word [bp+si], si ; ip:0x9->0xb
add si, 2 ; si:0x4->0x6 ip:0xb->0xe flags:CAS->P
cmp si, dx ; ip:0xe->0x10 flags:P->PZ
jne $-7 ; ip:0x10->0x12
mov bx, 0 ; ip:0x12->0x15
mov si, 0 ; si:0x6->0x0 ip:0x15->0x18
mov cx, [bp+si] ; ip:0x18->0x1a
add bx, cx ; ip:0x1a->0x1c
add si, 2 ; si:0x0->0x2 ip:0x1c->0x1f flags:PZ->
cmp si, dx ; ip:0x1f->0x21 flags:->CPAS
jne $-9 ; ip:0x21->0x18
mov cx, [bp+si] ; cx:0x0->0x2 ip:0x18->0x1a
add bx, cx ; bx:0x0->0x2 ip:0x1a->0x1c flags:CPAS->
add si, 2 ; si:0x2->0x4 ip:0x1c->0x1f
cmp si, dx ; ip:0x1f->0x21 flags:->CAS
jne $-9 ; ip:0x21->0x18
mov cx, [bp+si] ; cx:0x2->0x4 ip:0x18->0x1a
add bx, cx ; bx:0x2->0x6 ip:0x1a->0x1c flags:CAS->P
add si, 2 ; si:0x4->0x6 ip:0x1c->0x1f
cmp si, dx ; ip:0x1f->0x21 flags:P->PZ
jne $-9 ; ip:0x21->0x23
Final registers:
bx: 0x0006 (6)
cx: 0x0004 (4)
dx: 0x0006 (6)
bp: 0x03e8 (1000)
si: 0x0006 (6)
ip: 0x0023 (35)
flags: PZ

View File

@ -0,0 +1,35 @@
; ========================================================================
;
; (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 53
; ========================================================================
bits 16
mov dx, 6
mov bp, 1000
mov si, 0
init_loop_start:
mov word [bp + si], si
add si, 2
cmp si, dx
jnz init_loop_start
mov bx, 0
mov si, dx
sub bp, 2
add_loop_start:
add bx, word [bp + si]
sub si, 2
jnz add_loop_start

View File

@ -0,0 +1,35 @@
--- test\listing_0053_add_loop_challenge execution ---
mov dx, 6 ; dx:0x0->0x6 ip:0x0->0x3
mov bp, 1000 ; bp:0x0->0x3e8 ip:0x3->0x6
mov si, 0 ; ip:0x6->0x9
mov word [bp+si], si ; ip:0x9->0xb
add si, 2 ; si:0x0->0x2 ip:0xb->0xe
cmp si, dx ; ip:0xe->0x10 flags:->CPAS
jne $-7 ; ip:0x10->0x9
mov word [bp+si], si ; ip:0x9->0xb
add si, 2 ; si:0x2->0x4 ip:0xb->0xe flags:CPAS->
cmp si, dx ; ip:0xe->0x10 flags:->CAS
jne $-7 ; ip:0x10->0x9
mov word [bp+si], si ; ip:0x9->0xb
add si, 2 ; si:0x4->0x6 ip:0xb->0xe flags:CAS->P
cmp si, dx ; ip:0xe->0x10 flags:P->PZ
jne $-7 ; ip:0x10->0x12
mov bx, 0 ; ip:0x12->0x15
mov si, dx ; ip:0x15->0x17
sub bp, 2 ; bp:0x3e8->0x3e6 ip:0x17->0x1a flags:PZ->
add bx, [bp+si] ; bx:0x0->0x4 ip:0x1a->0x1c
sub si, 2 ; si:0x6->0x4 ip:0x1c->0x1f
jne $-5 ; ip:0x1f->0x1a
add bx, [bp+si] ; bx:0x4->0x6 ip:0x1a->0x1c flags:->P
sub si, 2 ; si:0x4->0x2 ip:0x1c->0x1f flags:P->
jne $-5 ; ip:0x1f->0x1a
add bx, [bp+si] ; ip:0x1a->0x1c flags:->P
sub si, 2 ; si:0x2->0x0 ip:0x1c->0x1f flags:P->PZ
jne $-5 ; ip:0x1f->0x21
Final registers:
bx: 0x0006 (6)
dx: 0x0006 (6)
bp: 0x03e6 (998)
ip: 0x0021 (33)
flags: PZ

View File

@ -0,0 +1,43 @@
; ========================================================================
;
; (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 54
; ========================================================================
bits 16
; Start image after one row, to avoid overwriting our code!
mov bp, 64*4
mov dx, 0
y_loop_start:
mov cx, 0
x_loop_start:
; Fill pixel
mov word [bp + 0], cx ; Red
mov word [bp + 2], dx ; Blue
mov byte [bp + 3], 255 ; Alpha
; Advance pixel location
add bp, 4
; Advance X coordinate and loop
add cx, 1
cmp cx, 64
jnz x_loop_start
; Advance Y coordinate and loop
add dx, 1
cmp dx, 64
jnz y_loop_start

28938
examples/54_draw_rectangle.txt Normal file

File diff suppressed because it is too large Load Diff

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() {
@ -113,9 +115,8 @@ int dissassemble(FILE *src, FILE *dst) {
return 0;
}
int simulate(FILE *src) {
struct memory mem = { 0 };
int byte_count = load_mem_from_stream(&mem, src, 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;
@ -124,19 +125,20 @@ int simulate(FILE *src) {
struct cpu_state state = { 0 };
struct instruction inst;
while (state.ip < byte_count) {
enum decode_error err = decode_instruction(&mem, &state.ip, &inst);
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(&state, &inst);
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);
@ -146,8 +148,44 @@ int simulate(FILE *src) {
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> <file>\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) {
@ -225,7 +263,7 @@ int dump_decompilation(const char *input) {
return 0;
}
int run_simulation(const char *input) {
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");
@ -241,7 +279,7 @@ int run_simulation(const char *input) {
remove(bin_filename);
return -1;
}
simulate(assembly);
simulate(assembly, mem);
fclose(assembly);
remove(bin_filename);
@ -251,28 +289,85 @@ int run_simulation(const char *input) {
printf("ERROR: Opening file '%s': %d\n", input, errno);
return -1;
}
simulate(assembly);
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 != 3) {
if (argc <= 2) {
print_usage(argv[0]);
return -1;
}
if (strequal(argv[1], "test-dump")) {
if (strequal(argv[1], "test-dump") && argc == 3) {
return test_decoder(argv[2]);
} else if (strequal(argv[1], "dump")) {
} else if (strequal(argv[1], "dump") && argc == 3) {
return dump_decompilation(argv[2]);
} else if (strequal(argv[1], "sim")) {
} 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;

View File

@ -42,8 +42,7 @@ static void reg_or_mem_to_str(char *buff, size_t max_size, struct reg_or_mem_val
}
static void src_to_str(char *buff, size_t max_size, struct src_value *value) {
switch (value->variant)
{
switch (value->variant) {
case SRC_VALUE_REG:
strncpy(buff, reg_to_str(value->reg), max_size);
break;
@ -71,8 +70,7 @@ static const char *operation_to_str(enum operation op) {
}
static void instruction_to_str(char *buff, size_t max_size, struct instruction *inst) {
switch (inst->op)
{
switch (inst->op) {
case OP_MOV:
case OP_CMP:
case OP_SUB:

View File

@ -95,6 +95,7 @@ struct src_value {
};
};
// TODO: Store "wide" flag on instruction, it is useful to know when doing most operations
struct instruction {
enum operation op;
struct reg_or_mem_value dest;

View File

@ -43,8 +43,7 @@ static i16 extend_sign_bit(i8 number) {
}
const char *decode_error_to_str(enum decode_error err) {
switch (err)
{
switch (err) {
case DECODE_OK:
return "ok";
case DECODE_ERR_EOF:
@ -83,19 +82,19 @@ static void decode_reg_or_mem(
value->is_reg = true;
value->reg = decode_reg(rm, wide);
} else if (mod == 0b10) { // Mod = 0b10, memory with i16 displacement
i16 displacement = pull_byte_at(mem, addr) | (pull_byte_at(mem, addr) << 8);
i16 displacement = pull_u16_at(mem, addr);
value->is_reg = false;
value->mem.base = decode_mem_base(rm);
value->mem.disp = displacement;
} else if (mod == 0b01) { // Mod = 0b01, memory with i8 displacement
i8 displacement = pull_byte_at(mem, addr);
i8 displacement = pull_u8_at(mem, addr);
value->is_reg = false;
value->mem.base = decode_mem_base(rm);
value->mem.disp = extend_sign_bit(displacement);
} else if (mod == 0b00) { // Mod = 0b00, memory no displacement (most of the time)
value->is_reg = false;
if (rm == 0b110) { // Direct address
u16 address = pull_byte_at(mem, addr) | (pull_byte_at(mem, addr) << 8);
u16 address = pull_u16_at(mem, addr);
value->mem.base = MEM_BASE_DIRECT_ADDRESS;
value->mem.disp = address;
} else {
@ -130,11 +129,11 @@ static void deocde_reg_or_mem_to_src(
// TODO: add handling for 'DECODE_ERR_MISSING_BYTES'
// Handy reference: Table 4-12. 8086 Instruction Encoding
enum decode_error decode_instruction(struct memory *mem, u16 *addr, struct instruction *output) {
u8 byte1 = pull_byte_at(mem, addr);
u8 byte1 = pull_u8_at(mem, addr);
// MOVE: Register memory to/from register
if ((byte1 & 0b11111100) == 0b10001000) {
u8 byte2 = pull_byte_at(mem, addr);
u8 byte2 = pull_u8_at(mem, addr);
bool wide = byte1 & 0b1;
bool direction = (byte1 & 0b10) >> 1;
@ -164,16 +163,16 @@ enum decode_error decode_instruction(struct memory *mem, u16 *addr, struct instr
if (wide) {
output->src.variant = SRC_VALUE_IMMEDIATE16;
output->src.immediate = pull_byte_at(mem, addr) | (pull_byte_at(mem, addr) << 8);
output->src.immediate = pull_u16_at(mem, addr);
} else {
output->src.variant = SRC_VALUE_IMMEDIATE8;
output->src.immediate = pull_byte_at(mem, addr);
output->src.immediate = pull_u8_at(mem, addr);
}
// MOVE: Immediate to register/memory
} else if ((byte1 & 0b11111110) == 0b11000110) {
u8 byte2 = pull_byte_at(mem, addr);
u8 byte2 = pull_u8_at(mem, addr);
bool wide = byte1 & 0b1;
u8 mod = (byte2 & 0b11000000) >> 6;
@ -184,10 +183,10 @@ enum decode_error decode_instruction(struct memory *mem, u16 *addr, struct instr
if (wide) {
output->src.variant = SRC_VALUE_IMMEDIATE16;
output->src.immediate = pull_byte_at(mem, addr) | (pull_byte_at(mem, addr) << 8);
output->src.immediate = pull_u16_at(mem, addr);
} else {
output->src.variant = SRC_VALUE_IMMEDIATE8;
output->src.immediate = pull_byte_at(mem, addr);
output->src.immediate = pull_u8_at(mem, addr);
}
// MOVE: Memory to accumulator
@ -200,9 +199,9 @@ enum decode_error decode_instruction(struct memory *mem, u16 *addr, struct instr
bool wide = byte1 & 0b1;
if (wide) {
output->src.mem.disp = pull_byte_at(mem, addr) | (pull_byte_at(mem, addr) << 8);
output->src.mem.disp = pull_u16_at(mem, addr);
} else {
output->src.mem.disp = pull_byte_at(mem, addr);
output->src.mem.disp = pull_u8_at(mem, addr);
}
// MOVE: Accumulator to memory
@ -216,9 +215,9 @@ enum decode_error decode_instruction(struct memory *mem, u16 *addr, struct instr
output->dest.mem.base = MEM_BASE_DIRECT_ADDRESS;
if (wide) {
output->dest.mem.disp = pull_byte_at(mem, addr) | (pull_byte_at(mem, addr) << 8);
output->dest.mem.disp = pull_u16_at(mem, addr);
} else {
output->dest.mem.disp = pull_byte_at(mem, addr);
output->dest.mem.disp = pull_u8_at(mem, addr);
}
// ADD/SUB/CMP: Reg/memory with register to either
@ -235,7 +234,7 @@ enum decode_error decode_instruction(struct memory *mem, u16 *addr, struct instr
bool wide = byte1 & 0b01;
bool direction = (byte1 & 0b10) >> 1;
u8 byte2 = pull_byte_at(mem, addr);
u8 byte2 = pull_u8_at(mem, addr);
u8 mod = (byte2 & 0b11000000) >> 6;
u8 reg = (byte2 & 0b00111000) >> 3;
u8 rm = byte2 & 0b00000111;
@ -252,7 +251,7 @@ enum decode_error decode_instruction(struct memory *mem, u16 *addr, struct instr
// ADD/SUB/CMP: immediate with register/memory
} else if ((byte1 & 0b11111100) == 0b10000000) {
u8 byte2 = pull_byte_at(mem, addr);
u8 byte2 = pull_u8_at(mem, addr);
u8 variant = (byte2 & 0b00111000) >> 3;
if (variant == 0b000) {
@ -273,14 +272,14 @@ enum decode_error decode_instruction(struct memory *mem, u16 *addr, struct instr
if (wide) {
output->src.variant = SRC_VALUE_IMMEDIATE16;
if (sign_extend) {
output->src.immediate = pull_byte_at(mem, addr);
output->src.immediate = pull_u8_at(mem, addr);
output->src.immediate = extend_sign_bit(output->src.immediate);
} else {
output->src.immediate = pull_byte_at(mem, addr) | (pull_byte_at(mem, addr) << 8);
output->src.immediate = pull_u16_at(mem, addr);
}
} else {
output->src.variant = SRC_VALUE_IMMEDIATE8;
output->src.immediate = pull_byte_at(mem, addr);
output->src.immediate = pull_u8_at(mem, addr);
}
// ADD/SUB/CMP: immediate with accumulator
@ -301,22 +300,22 @@ enum decode_error decode_instruction(struct memory *mem, u16 *addr, struct instr
if (wide) {
output->src.variant = SRC_VALUE_IMMEDIATE16;
output->src.immediate = pull_byte_at(mem, addr) | (pull_byte_at(mem, addr) << 8);
output->src.immediate = pull_u16_at(mem, addr);
} else {
output->src.variant = SRC_VALUE_IMMEDIATE8;
output->src.immediate = pull_byte_at(mem, addr);
output->src.immediate = pull_u8_at(mem, addr);
}
// Conditional jumps
} else if ((byte1 & 0b11110000) == 0b01110000) {
i8 jmp_offset = pull_byte_at(mem, addr);
i8 jmp_offset = pull_u8_at(mem, addr);
u8 opcode = byte1 & 0b00001111;
output->op = cond_jmp_lookup[opcode];
output->jmp_offset = jmp_offset;
// Conditional loop jumps
} else if ((byte1 & 0b11111100) == 0b11100000) {
i8 jmp_offset = pull_byte_at(mem, addr);
i8 jmp_offset = pull_u8_at(mem, addr);
u8 opcode = byte1 & 0b00000011;
output->op = cond_loop_jmp_lookup[opcode];
output->jmp_offset = jmp_offset;

View File

@ -25,12 +25,31 @@ int load_mem_from_file(struct memory *mem, const char *filename, u32 start) {
}
// TODO: Make this error some kind of error, when reading past end
u8 read_byte_at(struct memory *mem, u16 address) {
u8 read_u8_at(struct memory *mem, u16 address) {
return mem->mem[address % MEMORY_SIZE];
}
u8 pull_byte_at(struct memory *mem, u16 *address) {
u8 byte = read_byte_at(mem, *address);
u16 read_u16_at(struct memory *mem, u16 address) {
return read_u8_at(mem, address) | (read_u8_at(mem, address+1) << 8);
}
void write_u8_at(struct memory *mem, u16 address, u8 value) {
mem->mem[address % MEMORY_SIZE] = value;
}
void write_u16_at(struct memory *mem, u16 address, u16 value) {
write_u8_at(mem, address+0, (value >> 0) & 0xFF);
write_u8_at(mem, address+1, (value >> 8) & 0xFF);
}
u16 pull_u16_at(struct memory *mem, u16 *address) {
u16 value = read_u16_at(mem, *address);
(*address) += 2;
return value;
}
u8 pull_u8_at(struct memory *mem, u16 *address) {
u8 byte = read_u8_at(mem, *address);
(*address)++;
return byte;
}

View File

@ -1,118 +1,153 @@
u16 read_reg_value(struct cpu_state *state, enum reg_value reg)
u16 read_reg_value(struct cpu_state *cpu, enum reg_value reg)
{
switch (reg)
{
case REG_AL: return state->ax & 0xFF;
case REG_CL: return state->cx & 0xFF;
case REG_DL: return state->dx & 0xFF;
case REG_BL: return state->bx & 0xFF;
case REG_AH: return (state->ax >> 8) & 0xFF;
case REG_CH: return (state->cx >> 8) & 0xFF;
case REG_DH: return (state->dx >> 8) & 0xFF;
case REG_BH: return (state->bx >> 8) & 0xFF;
case REG_AX: return state->ax;
case REG_CX: return state->cx;
case REG_DX: return state->dx;
case REG_BX: return state->bx;
case REG_SP: return state->sp;
case REG_BP: return state->bp;
case REG_SI: return state->si;
case REG_DI: return state->di;
switch (reg) {
case REG_AL: return cpu->ax & 0xFF;
case REG_CL: return cpu->cx & 0xFF;
case REG_DL: return cpu->dx & 0xFF;
case REG_BL: return cpu->bx & 0xFF;
case REG_AH: return (cpu->ax >> 8) & 0xFF;
case REG_CH: return (cpu->cx >> 8) & 0xFF;
case REG_DH: return (cpu->dx >> 8) & 0xFF;
case REG_BH: return (cpu->bx >> 8) & 0xFF;
case REG_AX: return cpu->ax;
case REG_CX: return cpu->cx;
case REG_DX: return cpu->dx;
case REG_BX: return cpu->bx;
case REG_SP: return cpu->sp;
case REG_BP: return cpu->bp;
case REG_SI: return cpu->si;
case REG_DI: return cpu->di;
default: panic("Unhandled register '%s'", reg_to_str(reg));
}
}
void write_reg_value(struct cpu_state *state, enum reg_value reg, u16 value)
void write_reg_value(struct cpu_state *cpu, enum reg_value reg, u16 value)
{
switch (reg)
{
switch (reg) {
case REG_AL:
state->ax = (state->ax & 0xFF00) & value;
cpu->ax = (cpu->ax & 0xFF00) & value;
break;
case REG_CL:
state->cx = (state->cx & 0xFF00) & value;
cpu->cx = (cpu->cx & 0xFF00) & value;
break;
case REG_DL:
state->dx = (state->dx & 0xFF00) & value;
cpu->dx = (cpu->dx & 0xFF00) & value;
break;
case REG_BL:
state->bx = (state->bx & 0xFF00) & value;
cpu->bx = (cpu->bx & 0xFF00) & value;
break;
case REG_AH:
state->ax = (state->ax & 0x00FF) & (value << 8);
cpu->ax = (cpu->ax & 0x00FF) & (value << 8);
break;
case REG_CH:
state->cx = (state->cx & 0x00FF) & (value << 8);
cpu->cx = (cpu->cx & 0x00FF) & (value << 8);
break;
case REG_DH:
state->dx = (state->dx & 0x00FF) & (value << 8);
cpu->dx = (cpu->dx & 0x00FF) & (value << 8);
break;
case REG_BH:
state->bx = (state->bx & 0x00FF) & (value << 8);
cpu->bx = (cpu->bx & 0x00FF) & (value << 8);
break;
case REG_AX:
state->ax = value;
cpu->ax = value;
break;
case REG_CX:
state->cx = value;
cpu->cx = value;
break;
case REG_DX:
state->dx = value;
cpu->dx = value;
break;
case REG_BX:
state->bx = value;
cpu->bx = value;
break;
case REG_SP:
state->sp = value;
cpu->sp = value;
break;
case REG_BP:
state->bp = value;
cpu->bp = value;
break;
case REG_SI:
state->si = value;
cpu->si = value;
break;
case REG_DI:
state->di = value;
cpu->di = value;
break;
default:
panic("Unhandled register '%s'", reg_to_str(reg));
}
}
u16 read_src_value(struct cpu_state *state, struct src_value *src) {
switch (src->variant)
{
u16 read_mem_base_value(struct cpu_state *cpu, enum mem_base base) {
switch (base) {
case MEM_BASE_BX_SI: return read_reg_value(cpu, REG_BX) + read_reg_value(cpu, REG_SI);
case MEM_BASE_BX_DI: return read_reg_value(cpu, REG_BX) + read_reg_value(cpu, REG_DI);
case MEM_BASE_BP_SI: return read_reg_value(cpu, REG_BP) + read_reg_value(cpu, REG_SI);
case MEM_BASE_BP_DI: return read_reg_value(cpu, REG_BP) + read_reg_value(cpu, REG_DI);
case MEM_BASE_SI: return read_reg_value(cpu, REG_SI);
case MEM_BASE_DI: return read_reg_value(cpu, REG_DI);
case MEM_BASE_BP: return read_reg_value(cpu, REG_BP);
case MEM_BASE_BX: return read_reg_value(cpu, REG_BX);
default: return 0;
}
}
u16 calculate_mem_address(struct cpu_state *cpu, struct mem_value *addr) {
if (addr->base == MEM_BASE_DIRECT_ADDRESS) {
return addr->direct_address;
} else {
return read_mem_base_value(cpu, addr->base) + addr->disp;
}
}
u16 read_mem_value(struct memory *mem, struct cpu_state *cpu, struct mem_value *value, bool wide) {
u16 addr = calculate_mem_address(cpu, value);
return wide ? read_u16_at(mem, addr) : read_u8_at(mem, addr);
}
void write_mem_value(struct memory *mem, struct cpu_state *cpu, struct mem_value *location, u16 value, bool wide) {
u16 addr = calculate_mem_address(cpu, location);
if (wide) {
write_u16_at(mem, addr, value);
} else {
write_u8_at(mem, addr, value);
}
}
u16 read_src_value(struct memory *mem, struct cpu_state *cpu, struct src_value *src, bool wide) {
switch (src->variant) {
case SRC_VALUE_REG:
return read_reg_value(state, src->reg);
return read_reg_value(cpu, src->reg);
case SRC_VALUE_IMMEDIATE8:
case SRC_VALUE_IMMEDIATE16:
return src->immediate;
case SRC_VALUE_MEM:
todo("Handle read from memory");
return read_mem_value(mem, cpu, &src->mem, wide);
default:
panic("Unhandled src variant %d\n", src->variant);
}
}
u16 read_src_or_mem_value(struct cpu_state *state, struct reg_or_mem_value *reg_or_mem) {
u16 read_reg_or_mem_value(struct memory *mem, struct cpu_state *cpu, struct reg_or_mem_value *reg_or_mem, bool wide) {
if (reg_or_mem->is_reg) {
return read_reg_value(state, reg_or_mem->reg);
return read_reg_value(cpu, reg_or_mem->reg);
} else {
todo("Handle read from memory");
return read_mem_value(mem, cpu, &reg_or_mem->mem, wide);
}
}
void write_src_or_mem_value(struct cpu_state *state, struct reg_or_mem_value *reg_or_mem, u16 value) {
void write_reg_or_mem_value(struct memory *mem, struct cpu_state *cpu, struct reg_or_mem_value *reg_or_mem, u16 value, bool wide) {
if (reg_or_mem->is_reg) {
write_reg_value(state, reg_or_mem->reg, value);
write_reg_value(cpu, reg_or_mem->reg, value);
} else {
todo("Handle write to memory");
write_mem_value(mem, cpu, &reg_or_mem->mem, value, wide);
}
}
bool is_reg_16bit(enum reg_value reg) {
switch (reg)
{
switch (reg) {
case REG_AL:
case REG_CL:
case REG_DL:
@ -140,70 +175,151 @@ bool are_instruction_operands_16bit(struct instruction *inst) {
return is_reg_16bit(inst->dest.reg);
} else if (inst->src.variant == SRC_VALUE_REG) {
return is_reg_16bit(inst->src.reg);
} else if (inst->src.variant == SRC_VALUE_IMMEDIATE8) {
return false;
} else if (inst->src.variant == SRC_VALUE_IMMEDIATE16) {
return true;
} else {
return inst->src.variant == SRC_VALUE_IMMEDIATE16;
panic("Failed to determine instruction width\n");
}
}
void execute_instruction(struct cpu_state *state, struct instruction *inst) {
switch (inst->op)
{
void update_sign_flag(struct cpu_state *cpu, struct instruction *inst, u16 result) {
if (are_instruction_operands_16bit(inst)) {
cpu->flags.sign = (result >> 15) & 0b1;
} else {
cpu->flags.sign = (result >> 7) & 0b1;
}
}
void execute_instruction(struct memory *mem, struct cpu_state *cpu, struct instruction *inst) {
switch (inst->op) {
case OP_MOV: {
u16 src_value = read_src_value(state, &inst->src);
write_src_or_mem_value(state, &inst->dest, src_value);
bool wide = are_instruction_operands_16bit(inst);
u16 src_value = read_src_value(mem, cpu, &inst->src, wide);
write_reg_or_mem_value(mem, cpu, &inst->dest, src_value, wide);
break;
}
case OP_ADD: {
u16 dest_value = read_src_or_mem_value(state, &inst->dest);
u16 src_value = read_src_value(state, &inst->src);
bool wide = are_instruction_operands_16bit(inst);
u16 dest_value = read_reg_or_mem_value(mem, cpu, &inst->dest, wide);
u16 src_value = read_src_value(mem, cpu, &inst->src, wide);
u16 result = dest_value + src_value;
state->flags.zero = result == 0;
if (are_instruction_operands_16bit(inst)) {
state->flags.sign = (result >> 15) & 0b1;
} else {
state->flags.sign = (result >> 7) & 0b1;
}
cpu->flags.zero = result == 0;
update_sign_flag(cpu, inst, result);
write_src_or_mem_value(state, &inst->dest, result);
write_reg_or_mem_value(mem, cpu, &inst->dest, result, wide);
break;
}
case OP_SUB: {
u16 dest_value = read_src_or_mem_value(state, &inst->dest);
u16 src_value = read_src_value(state, &inst->src);
bool wide = are_instruction_operands_16bit(inst);
u16 dest_value = read_reg_or_mem_value(mem, cpu, &inst->dest, wide);
u16 src_value = read_src_value(mem, cpu, &inst->src, wide);
u16 result = dest_value - src_value;
state->flags.zero = result == 0;
if (are_instruction_operands_16bit(inst)) {
state->flags.sign = (result >> 15) & 0b1;
} else {
state->flags.sign = (result >> 7) & 0b1;
}
cpu->flags.zero = result == 0;
update_sign_flag(cpu, inst, result);
write_src_or_mem_value(state, &inst->dest, result);
write_reg_or_mem_value(mem, cpu, &inst->dest, result, wide);
break;
}
case OP_CMP: {
u16 dest_value = read_src_or_mem_value(state, &inst->dest);
u16 src_value = read_src_value(state, &inst->src);
bool wide = are_instruction_operands_16bit(inst);
u16 dest_value = read_reg_or_mem_value(mem, cpu, &inst->dest, wide);
u16 src_value = read_src_value(mem, cpu, &inst->src, wide);
u16 result = dest_value - src_value;
state->flags.zero = result == 0;
if (are_instruction_operands_16bit(inst)) {
state->flags.sign = (result >> 15) & 0b1;
} else {
state->flags.sign = (result >> 7) & 0b1;
}
cpu->flags.zero = result == 0;
update_sign_flag(cpu, inst, result);
break;
}
case OP_JNE: {
if (!state->flags.zero) {
i8 jmp_offset = inst->jmp_offset;
state->ip += jmp_offset;
if (!cpu->flags.zero) {
cpu->ip += inst->jmp_offset;
}
break;
} 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));
}