Compare commits
4 Commits
0d7a1beedd
...
d38cb3c328
Author | SHA1 | Date | |
---|---|---|---|
d38cb3c328 | |||
e61281fc89 | |||
8ca6a10eff | |||
91d8bbcc3d |
@ -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
|
30
examples/51_memory_mov.asm
Normal file
30
examples/51_memory_mov.asm
Normal 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]
|
18
examples/51_memory_mov.txt
Normal file
18
examples/51_memory_mov.txt
Normal 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)
|
36
examples/52_memory_add_loop.asm
Normal file
36
examples/52_memory_add_loop.asm
Normal 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
|
42
examples/52_memory_add_loop.txt
Normal file
42
examples/52_memory_add_loop.txt
Normal 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
|
35
examples/53_add_loop_challenge.asm
Normal file
35
examples/53_add_loop_challenge.asm
Normal 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
|
35
examples/53_add_loop_challenge.txt
Normal file
35
examples/53_add_loop_challenge.txt
Normal 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
|
43
examples/54_draw_rectangle.asm
Normal file
43
examples/54_draw_rectangle.asm
Normal 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
28938
examples/54_draw_rectangle.txt
Normal file
File diff suppressed because it is too large
Load Diff
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)
|
121
src/main.c
121
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() {
|
||||
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
@ -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, ®_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, ®_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));
|
||||
}
|
Loading…
Reference in New Issue
Block a user