Compare commits
20
Commits
266f7a7900
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51dae3128b | ||
|
|
85a8172923 | ||
|
|
fafb440891 | ||
|
|
df105ea3cf | ||
|
|
831a307975 | ||
|
|
ea7b65994b | ||
|
|
ab2b107e7e | ||
|
|
da78d875c1 | ||
|
|
3ca4059c44 | ||
|
|
11ccfe78a9 | ||
|
|
948960e8f5 | ||
|
|
0e90e2c23c | ||
|
|
99fd7fabd7 | ||
|
|
d0f91f15f8 | ||
|
|
731e5bd45d | ||
|
|
5cf4768b80 | ||
|
|
cd5eada115 | ||
|
|
08874c4533 | ||
|
|
305ac557fa | ||
|
|
b511c6a620 |
No files matched your search
@@ -5,3 +5,4 @@
|
|||||||
/decoder8086
|
/decoder8086
|
||||||
/performance-aware
|
/performance-aware
|
||||||
/sim8086
|
/sim8086
|
||||||
|
/listing-*.data
|
||||||
+94
-6
@@ -1,8 +1,96 @@
|
|||||||
#+OPTIONS: toc:nil
|
#+OPTIONS: toc:nil
|
||||||
|
|
||||||
* 8086 CPU Emulator
|
* 8086 CPU Simulator
|
||||||
Following along with [[https://www.computerenhance.com/p/table-of-contents][Performance Aware by Computer Enhance]]
|
|
||||||
* License
|
A complete 8086 processor emulator written in Odin, featuring instruction decoding, execution, and cycle-accurate simulation. Implements a comprehensive subset of the Intel 8086 instruction set with proper flag handling, memory management, and register operations.
|
||||||
This project is licensed under the terms of the MIT license. For more
|
|
||||||
information, see the included LICENSE file. However, some of this is related to
|
** Features
|
||||||
a paid course so maybe pay for the course if you want to use these files?
|
|
||||||
|
- *Complete 8086 instruction decoding* - Supports MOV, arithmetic, jumps, loops, stack operations, and more
|
||||||
|
- *Cycle-accurate execution* - Proper CPU state management with registers and flags
|
||||||
|
- *Memory simulation* - 1MB addressable memory space with effective address calculation
|
||||||
|
- *Comprehensive testing* - 21 assembly test programs with automated validation
|
||||||
|
- *Reference validation* - Compares execution results against reference CPU states
|
||||||
|
|
||||||
|
** Architecture
|
||||||
|
|
||||||
|
The simulator is organized into several focused modules:
|
||||||
|
|
||||||
|
- =sim8086.odin= - Main entry point and program orchestration
|
||||||
|
- =types.odin= - Core data structures (CPU state, instructions, operands)
|
||||||
|
- =instructions.odin= - Complete 8086 instruction set definitions
|
||||||
|
- =decoding.odin= - Binary instruction decoding and parsing
|
||||||
|
- =execution.odin= - CPU execution engine with flag calculations
|
||||||
|
- =printing.odin= - Assembly output formatting
|
||||||
|
- =testing.odin= - Reference state parsing and validation
|
||||||
|
|
||||||
|
** Building and Running
|
||||||
|
|
||||||
|
#+BEGIN_SRC bash
|
||||||
|
# Build the simulator
|
||||||
|
make
|
||||||
|
|
||||||
|
# Decode and print assembly
|
||||||
|
./sim8086 program.bin instructions
|
||||||
|
|
||||||
|
# Execute with register dump
|
||||||
|
./sim8086 program.bin registers
|
||||||
|
|
||||||
|
# Full execution with memory dump
|
||||||
|
./sim8086 program.bin all dump
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
** Testing
|
||||||
|
|
||||||
|
Comprehensive test coverage included:
|
||||||
|
|
||||||
|
#+BEGIN_SRC bash
|
||||||
|
# Run all assembly tests
|
||||||
|
./test_asm.sh
|
||||||
|
|
||||||
|
# Run reference CPU validation tests
|
||||||
|
./test_ref_cpu.sh
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
Test files cover:
|
||||||
|
- Basic MOV operations and addressing modes
|
||||||
|
- Arithmetic and logical operations with proper flag setting
|
||||||
|
- Jump and loop instructions
|
||||||
|
- Stack operations (PUSH/POP)
|
||||||
|
- Complex programs with multiple instruction types
|
||||||
|
|
||||||
|
** Technical Implementation
|
||||||
|
|
||||||
|
*** Instruction Decoding
|
||||||
|
- Bit-level parsing of 8086 machine code
|
||||||
|
- Support for all addressing modes (register, immediate, memory, direct)
|
||||||
|
- Proper handling of prefixes and multi-byte instructions
|
||||||
|
|
||||||
|
*** Execution Engine
|
||||||
|
- Register file with proper 8/16-bit access patterns
|
||||||
|
- CPU flags (Zero, Carry, Sign, Overflow, etc.) with accurate calculations
|
||||||
|
- Effective address computation for memory operations
|
||||||
|
- Instruction pointer management and control flow
|
||||||
|
|
||||||
|
*** Memory Model
|
||||||
|
- 1MB linear address space
|
||||||
|
- Little-endian byte ordering
|
||||||
|
- Memory-mapped I/O simulation
|
||||||
|
|
||||||
|
** Supported Instructions
|
||||||
|
|
||||||
|
*Data Movement:* MOV, PUSH, POP, XCHG, LEA, LDS, LES
|
||||||
|
*Arithmetic:* ADD, SUB, MUL, DIV, INC, DEC, CMP, NEG
|
||||||
|
*Logical:* AND, OR, XOR, NOT, TEST
|
||||||
|
*Shifts/Rotates:* SHL, SHR, SAR, ROL, ROR, RCL, RCR
|
||||||
|
*Control Flow:* JMP, JE, JNE, JL, JG, LOOP, CALL, RET
|
||||||
|
*String Operations:* MOVS, CMPS, SCAS, LODS, STOS
|
||||||
|
*System:* INT, IRET, HLT, NOP
|
||||||
|
|
||||||
|
Built following the [[https://www.computerenhance.com/p/table-of-contents][Performance Aware Programming]] course by Casey Muratori.
|
||||||
|
|
||||||
|
** License
|
||||||
|
|
||||||
|
This project is licensed under the MIT License - see the LICENSE file for details.
|
||||||
|
|
||||||
|
*Note:* Some test files are derived from the Performance Aware Programming course materials. Consider supporting the course if you find this project useful.
|
||||||
@@ -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 47
|
||||||
|
; ========================================================================
|
||||||
|
|
||||||
|
bits 16
|
||||||
|
|
||||||
|
add bx, 30000
|
||||||
|
add bx, 10000
|
||||||
|
sub bx, 5000
|
||||||
|
sub bx, 5000
|
||||||
|
|
||||||
|
mov bx, 1
|
||||||
|
mov cx, 100
|
||||||
|
add bx, cx
|
||||||
|
|
||||||
|
mov dx, 10
|
||||||
|
sub cx, dx
|
||||||
|
|
||||||
|
add bx, 40000
|
||||||
|
add cx, -90
|
||||||
|
|
||||||
|
mov sp, 99
|
||||||
|
mov bp, 98
|
||||||
|
cmp bp, sp
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
--- test\listing_0047_challenge_flags execution ---
|
||||||
|
add bx, 30000 ; bx:0x0->0x7530 flags:->P
|
||||||
|
add bx, 10000 ; bx:0x7530->0x9c40 flags:P->SO
|
||||||
|
sub bx, 5000 ; bx:0x9c40->0x88b8 flags:SO->PAS
|
||||||
|
sub bx, 5000 ; bx:0x88b8->0x7530 flags:PAS->PO
|
||||||
|
mov bx, 1 ; bx:0x7530->0x1
|
||||||
|
mov cx, 100 ; cx:0x0->0x64
|
||||||
|
add bx, cx ; bx:0x1->0x65 flags:PO->P
|
||||||
|
mov dx, 10 ; dx:0x0->0xa
|
||||||
|
sub cx, dx ; cx:0x64->0x5a flags:P->PA
|
||||||
|
add bx, 40000 ; bx:0x65->0x9ca5 flags:PA->PS
|
||||||
|
add cx, -90 ; cx:0x5a->0x0 flags:PS->CPAZ
|
||||||
|
mov sp, 99 ; sp:0x0->0x63
|
||||||
|
mov bp, 98 ; bp:0x0->0x62
|
||||||
|
cmp bp, sp ; flags:CPAZ->CPAS
|
||||||
|
|
||||||
|
Final registers:
|
||||||
|
bx: 0x9ca5 (40101)
|
||||||
|
dx: 0x000a (10)
|
||||||
|
sp: 0x0063 (99)
|
||||||
|
bp: 0x0062 (98)
|
||||||
|
flags: CPAS
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
; ========================================================================
|
||||||
|
;
|
||||||
|
; (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 48
|
||||||
|
; ========================================================================
|
||||||
|
|
||||||
|
bits 16
|
||||||
|
|
||||||
|
mov cx, 200
|
||||||
|
mov bx, cx
|
||||||
|
add cx, 1000
|
||||||
|
mov bx, 2000
|
||||||
|
sub cx, bx
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
--- test\listing_0048_ip_register execution ---
|
||||||
|
mov cx, 200 ; cx:0x0->0xc8 ip:0x0->0x3
|
||||||
|
mov bx, cx ; bx:0x0->0xc8 ip:0x3->0x5
|
||||||
|
add cx, 1000 ; cx:0xc8->0x4b0 ip:0x5->0x9 flags:->A
|
||||||
|
mov bx, 2000 ; bx:0xc8->0x7d0 ip:0x9->0xc
|
||||||
|
sub cx, bx ; cx:0x4b0->0xfce0 ip:0xc->0xe flags:A->CS
|
||||||
|
|
||||||
|
Final registers:
|
||||||
|
bx: 0x07d0 (2000)
|
||||||
|
cx: 0xfce0 (64736)
|
||||||
|
ip: 0x000e (14)
|
||||||
|
flags: CS
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
; ========================================================================
|
||||||
|
;
|
||||||
|
; (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 49
|
||||||
|
; ========================================================================
|
||||||
|
|
||||||
|
bits 16
|
||||||
|
|
||||||
|
mov cx, 3
|
||||||
|
mov bx, 1000
|
||||||
|
loop_start:
|
||||||
|
add bx, 10
|
||||||
|
sub cx, 1
|
||||||
|
jnz loop_start
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
--- test\listing_0049_conditional_jumps execution ---
|
||||||
|
mov cx, 3 ; cx:0x0->0x3 ip:0x0->0x3
|
||||||
|
mov bx, 1000 ; bx:0x0->0x3e8 ip:0x3->0x6
|
||||||
|
add bx, 10 ; bx:0x3e8->0x3f2 ip:0x6->0x9 flags:->A
|
||||||
|
sub cx, 1 ; cx:0x3->0x2 ip:0x9->0xc flags:A->
|
||||||
|
jne $-6 ; ip:0xc->0x6
|
||||||
|
add bx, 10 ; bx:0x3f2->0x3fc ip:0x6->0x9 flags:->P
|
||||||
|
sub cx, 1 ; cx:0x2->0x1 ip:0x9->0xc flags:P->
|
||||||
|
jne $-6 ; ip:0xc->0x6
|
||||||
|
add bx, 10 ; bx:0x3fc->0x406 ip:0x6->0x9 flags:->PA
|
||||||
|
sub cx, 1 ; cx:0x1->0x0 ip:0x9->0xc flags:PA->PZ
|
||||||
|
jne $-6 ; ip:0xc->0xe
|
||||||
|
|
||||||
|
Final registers:
|
||||||
|
bx: 0x0406 (1030)
|
||||||
|
ip: 0x000e (14)
|
||||||
|
flags: PZ
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
; ========================================================================
|
||||||
|
;
|
||||||
|
; (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 50
|
||||||
|
; ========================================================================
|
||||||
|
|
||||||
|
bits 16
|
||||||
|
|
||||||
|
mov ax, 10
|
||||||
|
mov bx, 10
|
||||||
|
mov cx, 10
|
||||||
|
|
||||||
|
label_0:
|
||||||
|
cmp bx, cx
|
||||||
|
je label_1
|
||||||
|
|
||||||
|
add ax, 1
|
||||||
|
jp label_2
|
||||||
|
|
||||||
|
label_1:
|
||||||
|
sub bx, 5
|
||||||
|
jb label_3
|
||||||
|
|
||||||
|
label_2:
|
||||||
|
sub cx, 2
|
||||||
|
|
||||||
|
label_3:
|
||||||
|
loopnz label_0
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
--- test\listing_0050_challenge_jumps execution ---
|
||||||
|
mov ax, 10 ; ax:0x0->0xa ip:0x0->0x3
|
||||||
|
mov bx, 10 ; bx:0x0->0xa ip:0x3->0x6
|
||||||
|
mov cx, 10 ; cx:0x0->0xa ip:0x6->0x9
|
||||||
|
cmp bx, cx ; ip:0x9->0xb flags:->PZ
|
||||||
|
je $+7 ; ip:0xb->0x12
|
||||||
|
sub bx, 5 ; bx:0xa->0x5 ip:0x12->0x15 flags:PZ->P
|
||||||
|
jb $+5 ; ip:0x15->0x17
|
||||||
|
sub cx, 2 ; cx:0xa->0x8 ip:0x17->0x1a flags:P->
|
||||||
|
loopnz $-17 ; cx:0x8->0x7 ip:0x1a->0x9
|
||||||
|
cmp bx, cx ; ip:0x9->0xb flags:->CAS
|
||||||
|
je $+7 ; ip:0xb->0xd
|
||||||
|
add ax, 1 ; ax:0xa->0xb ip:0xd->0x10 flags:CAS->
|
||||||
|
jp $+7 ; ip:0x10->0x12
|
||||||
|
sub bx, 5 ; bx:0x5->0x0 ip:0x12->0x15 flags:->PZ
|
||||||
|
jb $+5 ; ip:0x15->0x17
|
||||||
|
sub cx, 2 ; cx:0x7->0x5 ip:0x17->0x1a flags:PZ->P
|
||||||
|
loopnz $-17 ; cx:0x5->0x4 ip:0x1a->0x9
|
||||||
|
cmp bx, cx ; ip:0x9->0xb flags:P->CPAS
|
||||||
|
je $+7 ; ip:0xb->0xd
|
||||||
|
add ax, 1 ; ax:0xb->0xc ip:0xd->0x10 flags:CPAS->P
|
||||||
|
jp $+7 ; ip:0x10->0x17
|
||||||
|
sub cx, 2 ; cx:0x4->0x2 ip:0x17->0x1a flags:P->
|
||||||
|
loopnz $-17 ; cx:0x2->0x1 ip:0x1a->0x9
|
||||||
|
cmp bx, cx ; ip:0x9->0xb flags:->CPAS
|
||||||
|
je $+7 ; ip:0xb->0xd
|
||||||
|
add ax, 1 ; ax:0xc->0xd ip:0xd->0x10 flags:CPAS->
|
||||||
|
jp $+7 ; ip:0x10->0x12
|
||||||
|
sub bx, 5 ; bx:0x0->0xfffb ip:0x12->0x15 flags:->CAS
|
||||||
|
jb $+5 ; ip:0x15->0x1a
|
||||||
|
loopnz $-17 ; cx:0x1->0x0 ip:0x1a->0x1c
|
||||||
|
|
||||||
|
Final registers:
|
||||||
|
ax: 0x000d (13)
|
||||||
|
bx: 0xfffb (65531)
|
||||||
|
ip: 0x001c (28)
|
||||||
|
flags: CAS
|
||||||
@@ -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]
|
||||||
@@ -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)
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,53 @@
|
|||||||
|
; ========================================================================
|
||||||
|
;
|
||||||
|
; (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 55
|
||||||
|
; ========================================================================
|
||||||
|
|
||||||
|
bits 16
|
||||||
|
|
||||||
|
; Start image after one row, to avoid overwriting our code!
|
||||||
|
mov bp, 64*4
|
||||||
|
|
||||||
|
; Draw the solid rectangle red/blue/alpha
|
||||||
|
mov dx, 64
|
||||||
|
y_loop_start:
|
||||||
|
|
||||||
|
mov cx, 64
|
||||||
|
x_loop_start:
|
||||||
|
mov byte [bp + 0], cl ; Red
|
||||||
|
mov byte [bp + 1], 0 ; Green
|
||||||
|
mov byte [bp + 2], dl ; Blue
|
||||||
|
mov byte [bp + 3], 255 ; Alpha
|
||||||
|
add bp, 4
|
||||||
|
|
||||||
|
loop x_loop_start
|
||||||
|
|
||||||
|
sub dx, 1
|
||||||
|
jnz y_loop_start
|
||||||
|
|
||||||
|
; Add the line rectangle green
|
||||||
|
mov bp, 64*4 + 4*64 + 4
|
||||||
|
mov bx, bp
|
||||||
|
mov cx, 62
|
||||||
|
outline_loop_start:
|
||||||
|
|
||||||
|
mov byte [bp + 1], 255 ; Top line
|
||||||
|
mov byte [bp + 61*64*4 + 1], 255 ; Bottom line
|
||||||
|
mov byte [bx + 1], 255 ; Left line
|
||||||
|
mov byte [bx + 61*4 + 1], 255 ; Right line
|
||||||
|
|
||||||
|
add bp, 4
|
||||||
|
add bx, 4*64
|
||||||
|
|
||||||
|
loop outline_loop_start
|
||||||
+25214
File diff suppressed because it is too large.
Load diff
@@ -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
|
||||||
@@ -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)
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
; ========================================================================
|
||||||
|
;
|
||||||
|
; (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 57
|
||||||
|
; ========================================================================
|
||||||
|
|
||||||
|
bits 16
|
||||||
|
|
||||||
|
mov bx, 1000
|
||||||
|
mov bp, 2000
|
||||||
|
mov si, 3000
|
||||||
|
mov di, 4000
|
||||||
|
|
||||||
|
mov cx, [bp + di]
|
||||||
|
mov [bx + si], cx
|
||||||
|
|
||||||
|
mov cx, [bp + si]
|
||||||
|
mov [bx + di], cx
|
||||||
|
|
||||||
|
mov cx, [bp + di + 1000]
|
||||||
|
mov [bx + si + 1000], cx
|
||||||
|
|
||||||
|
mov cx, [bp + si + 1000]
|
||||||
|
mov [bx + di + 1000], cx
|
||||||
|
|
||||||
|
add dx, [bp + si + 1000]
|
||||||
|
|
||||||
|
add word [bp + si], 76
|
||||||
|
|
||||||
|
add dx, [bp + si + 1001]
|
||||||
|
add [di + 999], dx
|
||||||
|
add word [bp + si], 75
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
**************
|
||||||
|
**** 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_0057_challenge_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, [bp+di] ; Clocks: +15 = 31 (8 + 7ea) | ip:0xc->0xe
|
||||||
|
mov word [bx+si], cx ; Clocks: +16 = 47 (9 + 7ea) | ip:0xe->0x10
|
||||||
|
mov cx, [bp+si] ; Clocks: +16 = 63 (8 + 8ea) | ip:0x10->0x12
|
||||||
|
mov word [bx+di], cx ; Clocks: +17 = 80 (9 + 8ea) | ip:0x12->0x14
|
||||||
|
mov cx, [bp+di+1000] ; Clocks: +19 = 99 (8 + 11ea) | ip:0x14->0x18
|
||||||
|
mov word [bx+si+1000], cx ; Clocks: +20 = 119 (9 + 11ea) | ip:0x18->0x1c
|
||||||
|
mov cx, [bp+si+1000] ; Clocks: +20 = 139 (8 + 12ea) | ip:0x1c->0x20
|
||||||
|
mov word [bx+di+1000], cx ; Clocks: +21 = 160 (9 + 12ea) | ip:0x20->0x24
|
||||||
|
add dx, [bp+si+1000] ; Clocks: +21 = 181 (9 + 12ea) | ip:0x24->0x28 flags:->PZ
|
||||||
|
add word [bp+si], 76 ; Clocks: +25 = 206 (17 + 8ea) | ip:0x28->0x2b flags:PZ->
|
||||||
|
add dx, [bp+si+1001] ; Clocks: +25 = 231 (9 + 12ea + 4p) | ip:0x2b->0x2f flags:->PZ
|
||||||
|
add word [di+999], dx ; Clocks: +33 = 264 (16 + 9ea + 8p) | ip:0x2f->0x33 flags:PZ->P
|
||||||
|
add word [bp+si], 75 ; Clocks: +25 = 289 (17 + 8ea) | ip:0x33->0x36 flags:P->A
|
||||||
|
|
||||||
|
Final registers:
|
||||||
|
bx: 0x03e8 (1000)
|
||||||
|
bp: 0x07d0 (2000)
|
||||||
|
si: 0x0bb8 (3000)
|
||||||
|
di: 0x0fa0 (4000)
|
||||||
|
ip: 0x0036 (54)
|
||||||
|
flags: A
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**************
|
||||||
|
**** 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_0057_challenge_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, [bp+di] ; Clocks: +19 = 35 (8 + 7ea + 4p) | ip:0xc->0xe
|
||||||
|
mov word [bx+si], cx ; Clocks: +20 = 55 (9 + 7ea + 4p) | ip:0xe->0x10
|
||||||
|
mov cx, [bp+si] ; Clocks: +20 = 75 (8 + 8ea + 4p) | ip:0x10->0x12
|
||||||
|
mov word [bx+di], cx ; Clocks: +21 = 96 (9 + 8ea + 4p) | ip:0x12->0x14
|
||||||
|
mov cx, [bp+di+1000] ; Clocks: +23 = 119 (8 + 11ea + 4p) | ip:0x14->0x18
|
||||||
|
mov word [bx+si+1000], cx ; Clocks: +24 = 143 (9 + 11ea + 4p) | ip:0x18->0x1c
|
||||||
|
mov cx, [bp+si+1000] ; Clocks: +24 = 167 (8 + 12ea + 4p) | ip:0x1c->0x20
|
||||||
|
mov word [bx+di+1000], cx ; Clocks: +25 = 192 (9 + 12ea + 4p) | ip:0x20->0x24
|
||||||
|
add dx, [bp+si+1000] ; Clocks: +25 = 217 (9 + 12ea + 4p) | ip:0x24->0x28 flags:->PZ
|
||||||
|
add word [bp+si], 76 ; Clocks: +33 = 250 (17 + 8ea + 8p) | ip:0x28->0x2b flags:PZ->
|
||||||
|
add dx, [bp+si+1001] ; Clocks: +25 = 275 (9 + 12ea + 4p) | ip:0x2b->0x2f flags:->PZ
|
||||||
|
add word [di+999], dx ; Clocks: +33 = 308 (16 + 9ea + 8p) | ip:0x2f->0x33 flags:PZ->P
|
||||||
|
add word [bp+si], 75 ; Clocks: +33 = 341 (17 + 8ea + 8p) | ip:0x33->0x36 flags:P->A
|
||||||
|
|
||||||
|
Final registers:
|
||||||
|
bx: 0x03e8 (1000)
|
||||||
|
bp: 0x07d0 (2000)
|
||||||
|
si: 0x0bb8 (3000)
|
||||||
|
di: 0x0fa0 (4000)
|
||||||
|
ip: 0x0036 (54)
|
||||||
|
flags: A
|
||||||
+61
-27
@@ -4,6 +4,12 @@ import "core:fmt"
|
|||||||
import "core:math"
|
import "core:math"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
|
SEGMENT_REGISTER_START :: 8
|
||||||
|
|
||||||
|
get_i16 :: proc(data: []u8) -> i16 {
|
||||||
|
return (i16)(data[1]) << 8 | (i16)(data[0])
|
||||||
|
}
|
||||||
|
|
||||||
get_op :: proc(inst: Instruction) -> (Op, bool) {
|
get_op :: proc(inst: Instruction) -> (Op, bool) {
|
||||||
op: Op
|
op: Op
|
||||||
interseg: bool
|
interseg: bool
|
||||||
@@ -12,8 +18,6 @@ get_op :: proc(inst: Instruction) -> (Op, bool) {
|
|||||||
case 0b000: op = .INC
|
case 0b000: op = .INC
|
||||||
case 0b001: op = .DEC
|
case 0b001: op = .DEC
|
||||||
case 0b010: op = .CALL
|
case 0b010: op = .CALL
|
||||||
// TODO: We really have to fix this because we shouldn't be figuring out if this
|
|
||||||
// is an intersegment here
|
|
||||||
case 0b011: op = .CALL; interseg = true
|
case 0b011: op = .CALL; interseg = true
|
||||||
case 0b100: op = .JMP
|
case 0b100: op = .JMP
|
||||||
case 0b101: op = .JMP; interseg = true
|
case 0b101: op = .JMP; interseg = true
|
||||||
@@ -62,7 +66,9 @@ get_op :: proc(inst: Instruction) -> (Op, bool) {
|
|||||||
}
|
}
|
||||||
return op, interseg
|
return op, interseg
|
||||||
}
|
}
|
||||||
parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, processed: ^int, word: bool, has_segreg: Maybe(Register)) -> Operand {
|
|
||||||
|
parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, processed: ^int,
|
||||||
|
total_bytes_processed: int, word: bool, has_segreg: Maybe(RegisterId)) -> Operand {
|
||||||
operand: Operand = None{}
|
operand: Operand = None{}
|
||||||
switch opinfo {
|
switch opinfo {
|
||||||
case .None:
|
case .None:
|
||||||
@@ -76,11 +82,11 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr
|
|||||||
case .SecondByteLast3: reg = data[1] & 0b111
|
case .SecondByteLast3: reg = data[1] & 0b111
|
||||||
}
|
}
|
||||||
if opinfo == .SegmentRegister {
|
if opinfo == .SegmentRegister {
|
||||||
operand = (RegisterId){id = SEGMENT_REGISTER_START + (int)(reg), access = .Full}
|
operand = (RegisterId){name = Register(SEGMENT_REGISTER_START + (int)(reg)), access = .Full}
|
||||||
} else if word {
|
} else if word {
|
||||||
operand = RegisterId { id = (int)(reg), access = .Full }
|
operand = RegisterId { name = Register(reg), access = .Full }
|
||||||
} else {
|
} else {
|
||||||
operand = RegisterId { id = (int)(reg % 4), access = reg < 4 ? .Low : .High }
|
operand = RegisterId { name = Register(reg % 4), access = reg < 4 ? .Low : .High }
|
||||||
}
|
}
|
||||||
case .RegisterMemory:
|
case .RegisterMemory:
|
||||||
mod := data[1] >> 6
|
mod := data[1] >> 6
|
||||||
@@ -93,19 +99,22 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr
|
|||||||
op = (DirectAddress)(get_i16(data[2:]))
|
op = (DirectAddress)(get_i16(data[2:]))
|
||||||
processed^ += 2
|
processed^ += 2
|
||||||
} else {
|
} else {
|
||||||
op = MemoryAddr{ addr_id = rm , displacement = None{} }
|
op = MemoryAddr{ addr_id = rm }
|
||||||
}
|
}
|
||||||
} else if mod == 1 {
|
} else if mod == 1 {
|
||||||
op = MemoryAddr{ addr_id = rm , displacement = (i8)(data[2]) }
|
// NOTE: Per the 8086 manual in order to detect signed displacement, you have
|
||||||
|
// to sign extend a byte to a word. In Odin like in other languages, first
|
||||||
|
// cast to an i8, before casting to an i16 and it'll sign extend
|
||||||
|
op = MemoryAddr{ addr_id = rm , displacement = i16(i8(data[2])) }
|
||||||
processed^ += 1
|
processed^ += 1
|
||||||
} else if mod == 2 {
|
} else if mod == 2 {
|
||||||
op = MemoryAddr{ addr_id = rm , displacement = get_i16(data[2:]) }
|
op = MemoryAddr{ addr_id = rm , displacement = get_i16(data[2:]) }
|
||||||
processed^ += 2
|
processed^ += 2
|
||||||
} else if mod == 3 {
|
} else if mod == 3 {
|
||||||
if word {
|
if word {
|
||||||
op = RegisterId { id = (int)(rm), access = .Full }
|
op = RegisterId { name = Register(rm), access = .Full }
|
||||||
} else {
|
} else {
|
||||||
op = RegisterId { id = (int)(rm % 4), access = rm < 4 ? .Low : .High }
|
op = RegisterId { name = Register(rm % 4), access = rm < 4 ? .Low : .High }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
operand = op
|
operand = op
|
||||||
@@ -115,14 +124,11 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr
|
|||||||
if inst.has_sign_extension {
|
if inst.has_sign_extension {
|
||||||
word_signed &&= data[0] & 0b0000_0010 == 0
|
word_signed &&= data[0] & 0b0000_0010 == 0
|
||||||
}
|
}
|
||||||
value: u16 = word_signed ? u16(get_i16(data[data_idx:])) : u16(data[data_idx])
|
value: i16 = word_signed ? get_i16(data[data_idx:]) : i16(i8(data[data_idx]))
|
||||||
operand = Immediate { value = value, size = word_signed ? .Signed16 : .Signed8 }
|
operand = Immediate { value = value, size = word_signed ? ._16 : ._8 }
|
||||||
processed^ += word_signed ? 2 : 1
|
processed^ += word_signed ? 2 : 1
|
||||||
case .ImmediateUnsigned:
|
|
||||||
operand = Immediate { value = u16(data[processed^]), size = .Unsigned8 }
|
|
||||||
processed^ += 1
|
|
||||||
case .Accumulator:
|
case .Accumulator:
|
||||||
operand = RegisterId { id = 0, access = word ? .Full : .Low }
|
operand = RegisterId { name = .ax, access = word ? .Full : .Low }
|
||||||
case .DirectAddress:
|
case .DirectAddress:
|
||||||
// operand = DirectAddress { value = get_i16(data[1:]) }
|
// operand = DirectAddress { value = get_i16(data[1:]) }
|
||||||
operand = (DirectAddress)(get_i16(data[1:]))
|
operand = (DirectAddress)(get_i16(data[1:]))
|
||||||
@@ -132,16 +138,33 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr
|
|||||||
// NOTE: In order to mimic the label offset, you have to take the value you got and add two
|
// NOTE: In order to mimic the label offset, you have to take the value you got and add two
|
||||||
operand = (Jump)((i8)(data[1]) + 2)
|
operand = (Jump)((i8)(data[1]) + 2)
|
||||||
case .VariablePort:
|
case .VariablePort:
|
||||||
operand = RegisterId { id = (int)(variable_port.code), access = .Full }
|
// NOTE: This isn't documented in the manual, but you can tell if we need to use
|
||||||
|
// the dx register or an immediate if the 4th LSB bit is set
|
||||||
|
if (data[0] & 0b1000) >> 3 == 1 {
|
||||||
|
operand = RegisterId { name = .dx, access = .Full }
|
||||||
|
} else {
|
||||||
|
operand = Immediate { value = i16(i8(data[1])), size = ._8 }
|
||||||
|
processed^ += 1
|
||||||
|
}
|
||||||
case .ShiftRotate:
|
case .ShiftRotate:
|
||||||
v_flag := data[0] & 0b10 != 0
|
v_flag := data[0] & 0b10 != 0
|
||||||
operand = v_flag ? RegisterId { id = 1, access = .Low } : Immediate { value = 1 }
|
operand = v_flag ? RegisterId { name = .cx, access = .Low } : Immediate { value = 1 }
|
||||||
case .Repeat:
|
case .Repeat:
|
||||||
operand = get_repeat_op(data[1])
|
bits := (data[1] & 0b1110) >> 1
|
||||||
|
w := (data[1] & 0b1) == 1 ? "w" : "b"
|
||||||
|
rep: string
|
||||||
|
switch bits {
|
||||||
|
case 0b010: rep = "movs"
|
||||||
|
case 0b011: rep = "cmps"
|
||||||
|
case 0b101: rep = "stos"
|
||||||
|
case 0b110: rep = "lods"
|
||||||
|
case 0b111: rep = "scas"
|
||||||
|
}
|
||||||
|
operand = Repeat(fmt.aprintf("%s%s", rep, w))
|
||||||
processed^ += 1
|
processed^ += 1
|
||||||
case .DirectWithinSegment:
|
case .DirectWithinSegment:
|
||||||
value := (int)(get_i16(data[1:])) + total_bytes_processed + 3
|
value := (int)(get_i16(data[1:])) + total_bytes_processed + 3
|
||||||
operand = Immediate { value = u16(value), size = .Signed16 }
|
operand = Immediate { value = i16(value), size = ._16 }
|
||||||
processed^ += 2
|
processed^ += 2
|
||||||
case .Intersegment:
|
case .Intersegment:
|
||||||
operand = Intersegment {
|
operand = Intersegment {
|
||||||
@@ -153,17 +176,26 @@ parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, pr
|
|||||||
return operand
|
return operand
|
||||||
}
|
}
|
||||||
|
|
||||||
decode_data :: proc(inst_list: ^[dynamic]Instruction, data: []u8, bytes_to_read: int) {
|
decode_data :: proc(inst_list: ^[dynamic]Instruction, data: []u8, bytes_to_read: int) -> int {
|
||||||
idx := 0
|
idx := 0
|
||||||
has_segment: Maybe(Register)
|
has_segment: Maybe(RegisterId)
|
||||||
has_lock: bool
|
has_lock: bool
|
||||||
|
total_bytes_processed: int
|
||||||
for idx < bytes_to_read {
|
for idx < bytes_to_read {
|
||||||
instruction: Instruction
|
instruction: Instruction
|
||||||
processed := 1
|
processed := 1
|
||||||
curr_byte := data[idx]
|
curr_byte := data[idx]
|
||||||
|
|
||||||
inst, ok := try_find_instruction(curr_byte)
|
found_inst: bool
|
||||||
if !ok {
|
inst: InstructionInfo
|
||||||
|
for i in instructions {
|
||||||
|
if i.encoding == (curr_byte & i.mask) {
|
||||||
|
found_inst = true
|
||||||
|
inst = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found_inst {
|
||||||
instruction = {
|
instruction = {
|
||||||
opname = .UNKNOWN,
|
opname = .UNKNOWN,
|
||||||
bytes_read = 1,
|
bytes_read = 1,
|
||||||
@@ -181,7 +213,7 @@ decode_data :: proc(inst_list: ^[dynamic]Instruction, data: []u8, bytes_to_read:
|
|||||||
continue
|
continue
|
||||||
} else if inst.opname == .SEGMENT {
|
} else if inst.opname == .SEGMENT {
|
||||||
reg := (curr_byte & 0b11000) >> 3
|
reg := (curr_byte & 0b11000) >> 3
|
||||||
has_segment = registers[SEGMENT_REGISTER_START+reg]
|
has_segment = RegisterId { name = Register(SEGMENT_REGISTER_START+reg) }
|
||||||
idx += 1
|
idx += 1
|
||||||
continue
|
continue
|
||||||
} else if inst.opname == .AAM {
|
} else if inst.opname == .AAM {
|
||||||
@@ -212,8 +244,8 @@ decode_data :: proc(inst_list: ^[dynamic]Instruction, data: []u8, bytes_to_read:
|
|||||||
case .Always16: word = true
|
case .Always16: word = true
|
||||||
}
|
}
|
||||||
|
|
||||||
dst_opr = parse_operand(inst, inst.dst, data[idx:], &processed, word, has_segment)
|
dst_opr = parse_operand(inst, inst.dst, data[idx:], &processed, total_bytes_processed, word, has_segment)
|
||||||
src_opr = parse_operand(inst, inst.src, data[idx:], &processed, word, has_segment)
|
src_opr = parse_operand(inst, inst.src, data[idx:], &processed, total_bytes_processed, word, has_segment)
|
||||||
|
|
||||||
if flip {
|
if flip {
|
||||||
src_opr, dst_opr = dst_opr, src_opr
|
src_opr, dst_opr = dst_opr, src_opr
|
||||||
@@ -230,6 +262,7 @@ decode_data :: proc(inst_list: ^[dynamic]Instruction, data: []u8, bytes_to_read:
|
|||||||
instruction.info = inst
|
instruction.info = inst
|
||||||
instruction.has_lock = has_lock
|
instruction.has_lock = has_lock
|
||||||
instruction.has_segment = has_segment
|
instruction.has_segment = has_segment
|
||||||
|
instruction.segment_offset += total_bytes_processed
|
||||||
instruction.opname = inst.opname
|
instruction.opname = inst.opname
|
||||||
instruction.opname,instruction.indirect_intersegment = get_op(instruction)
|
instruction.opname,instruction.indirect_intersegment = get_op(instruction)
|
||||||
|
|
||||||
@@ -242,4 +275,5 @@ decode_data :: proc(inst_list: ^[dynamic]Instruction, data: []u8, bytes_to_read:
|
|||||||
has_segment = nil
|
has_segment = nil
|
||||||
total_bytes_processed = idx
|
total_bytes_processed = idx
|
||||||
}
|
}
|
||||||
|
return total_bytes_processed
|
||||||
}
|
}
|
||||||
+143
-46
@@ -4,65 +4,162 @@ import "core:os"
|
|||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:math"
|
import "core:math"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
import "core:reflect"
|
||||||
|
import "core:strconv"
|
||||||
|
|
||||||
get_operand_value :: proc(operand: Operand) -> u16 {
|
get_ip :: proc(cpu: ^Cpu) -> int { return int(cpu.registers[.ip].full) }
|
||||||
|
|
||||||
|
get_effective_address_value :: proc(cpu: ^Cpu, id: u8) -> i16 {
|
||||||
|
switch id {
|
||||||
|
case 0: return cpu.registers[.bx].full + cpu.registers[.si].full
|
||||||
|
case 1: return cpu.registers[.bx].full + cpu.registers[.di].full
|
||||||
|
case 2: return cpu.registers[.bp].full + cpu.registers[.si].full
|
||||||
|
case 3: return cpu.registers[.bx].full + cpu.registers[.di].full
|
||||||
|
case 4: return cpu.registers[.si].full
|
||||||
|
case 5: return cpu.registers[.di].full
|
||||||
|
case 6: return cpu.registers[.bp].full
|
||||||
|
case 7: return cpu.registers[.bx].full
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
perform_load :: proc(cpu: ^Cpu, operand: Operand, is_word: bool) -> i16 {
|
||||||
#partial switch opr in operand {
|
#partial switch opr in operand {
|
||||||
case Immediate:
|
case Immediate:
|
||||||
return opr.value
|
return i16(opr.value)
|
||||||
case RegisterId:
|
case RegisterId:
|
||||||
|
reg_val := cpu.registers[opr.name]
|
||||||
switch opr.access {
|
switch opr.access {
|
||||||
case .Low, .High:
|
case .Low, .High:
|
||||||
val := opr.access == .Low ? registers[opr.id].value.low : registers[opr.id].value.high
|
return i16(opr.access == .Low ? reg_val.low : reg_val.high)
|
||||||
return u16(val)
|
|
||||||
case .Full:
|
case .Full:
|
||||||
return registers[opr.id].value.full
|
return i16(reg_val.full)
|
||||||
}
|
}
|
||||||
|
case DirectAddress:
|
||||||
|
value: i16
|
||||||
|
if is_word {
|
||||||
|
value = i16(u16(cpu.memory[opr+1] << 8) | u16(cpu.memory[opr]))
|
||||||
|
} else {
|
||||||
|
value = i16(cpu.memory[opr])
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
case MemoryAddr:
|
||||||
|
value: i16
|
||||||
|
idx := get_effective_address_value(cpu, opr.addr_id) + opr.displacement
|
||||||
|
if is_word {
|
||||||
|
value = i16(u16(cpu.memory[idx+1] << 8) | u16(cpu.memory[idx]))
|
||||||
|
} else {
|
||||||
|
value = i16(cpu.memory[idx])
|
||||||
|
}
|
||||||
|
return value
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
set_register_value :: proc(reg_id: RegisterId, value: u16) {
|
check_flags :: proc(cpu: ^Cpu, flags: bit_set[Flag], dst: i16, src: i16, result: i16, is_add: bool) {
|
||||||
switch reg_id.access {
|
if .ZF in flags do cpu.flags[.ZF] = result == 0
|
||||||
case .Low:
|
if .SF in flags do cpu.flags[.SF] = (result >> 15) & 0x1 == 1
|
||||||
registers[reg_id.id].value.low = u8(value)
|
if .CF in flags {
|
||||||
case .High:
|
cpu.flags[.CF] = is_add ? u32(dst) + u32(src) > 0xFFFF : src > dst
|
||||||
registers[reg_id.id].value.high = u8(value)
|
|
||||||
case .Full:
|
|
||||||
registers[reg_id.id].value.full = u16(value)
|
|
||||||
}
|
}
|
||||||
}
|
if .AF in flags {
|
||||||
|
lhs, rhs := dst & 0xF, src & 0xF
|
||||||
check_zero_flag :: proc(value: u16) {
|
cpu.flags[.AF] = is_add ? lhs + rhs > 15 : lhs < rhs
|
||||||
CPU.flags.ZF = value == 0
|
}
|
||||||
}
|
if .PF in flags {
|
||||||
|
val := result
|
||||||
check_sign_flag :: proc(value: u16) {
|
bit_count := 0
|
||||||
CPU.flags.SF = value >> 15 == 1
|
val &= 0x00FF
|
||||||
}
|
for val != 0 {
|
||||||
|
if val & 0x1 == 1 {
|
||||||
execute_instruction :: proc(inst: Instruction) {
|
bit_count += 1
|
||||||
if reg,ok := inst.dst.(RegisterId); ok {
|
}
|
||||||
#partial switch inst.opname {
|
val >>= 1
|
||||||
case .MOV:
|
|
||||||
src_val := get_operand_value(inst.src)
|
|
||||||
set_register_value(reg, src_val)
|
|
||||||
case .ADD:
|
|
||||||
src_val := get_operand_value(inst.src)
|
|
||||||
val := registers[reg.id].value.full + src_val
|
|
||||||
set_register_value(reg, val)
|
|
||||||
check_zero_flag(val)
|
|
||||||
check_sign_flag(val)
|
|
||||||
case .SUB:
|
|
||||||
src_val := get_operand_value(inst.src)
|
|
||||||
val := registers[reg.id].value.full - src_val
|
|
||||||
set_register_value(reg, val)
|
|
||||||
check_zero_flag(val)
|
|
||||||
check_sign_flag(val)
|
|
||||||
case .CMP:
|
|
||||||
src_val := get_operand_value(inst.src)
|
|
||||||
val := registers[reg.id].value.full - src_val
|
|
||||||
check_zero_flag(val)
|
|
||||||
check_sign_flag(val)
|
|
||||||
}
|
}
|
||||||
|
cpu.flags[.PF] = bit_count % 2 == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
perform_store :: proc(cpu: ^Cpu, value: i16, operand: Operand, is_word: bool) {
|
||||||
|
#partial switch opr in operand {
|
||||||
|
case RegisterId:
|
||||||
|
switch opr.access {
|
||||||
|
case .Low:
|
||||||
|
cpu.registers[opr.name].low = u8(value)
|
||||||
|
case .High:
|
||||||
|
cpu.registers[opr.name].high = u8(value)
|
||||||
|
case .Full:
|
||||||
|
cpu.registers[opr.name].full = i16(value)
|
||||||
|
}
|
||||||
|
case DirectAddress:
|
||||||
|
cpu.memory[opr] = u8(value)
|
||||||
|
if is_word do cpu.memory[opr+1] = u8((u16(value) & 0xFF00) >> 8)
|
||||||
|
case MemoryAddr:
|
||||||
|
effective_addr_val := get_effective_address_value(cpu, opr.addr_id)
|
||||||
|
addr := effective_addr_val + opr.displacement
|
||||||
|
cpu.memory[addr] = u8(value)
|
||||||
|
if is_word do cpu.memory[addr+1] = u8((u16(value) & 0xFF00) >> 8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
execute_instruction :: proc(cpu: ^Cpu, inst: Instruction) {
|
||||||
|
if jmp_offset,ok := inst.src.(Jump); ok {
|
||||||
|
jump: bool
|
||||||
|
#partial switch inst.opname {
|
||||||
|
case .JNZ, .JNE: jump = !cpu.flags[.ZF]
|
||||||
|
case .JE, .JZ: jump = cpu.flags[.ZF]
|
||||||
|
case .JP, .JPE: jump = cpu.flags[.PF]
|
||||||
|
case .JB, .JNAE: jump = cpu.flags[.CF]
|
||||||
|
|
||||||
|
case .LOOP:
|
||||||
|
cpu.registers[.cx].full -= 1
|
||||||
|
jump = cpu.registers[.cx].full != 0
|
||||||
|
case .LOOPNZ, .LOOPNE:
|
||||||
|
cpu.registers[.cx].full -= 1
|
||||||
|
// According to this resource, the loopnz inst does not change any flags
|
||||||
|
// https://yassinebridi.github.io/asm-docs/8086_instruction_set.html#LOOPNZ
|
||||||
|
// check_zero_flag(cpu, cpu.registers[.cx].full)
|
||||||
|
jump = cpu.registers[.cx].full != 0 && !cpu.flags[.ZF]
|
||||||
|
}
|
||||||
|
if jump {
|
||||||
|
cpu.registers[.ip].full += i16(jmp_offset)
|
||||||
|
cpu.registers[.ip].full -= i16(inst.bytes_read)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
status_flags := bit_set[Flag]{.OF, .SF, .ZF, .AF, .PF, .CF}
|
||||||
|
#partial switch inst.opname {
|
||||||
|
case .MOV:
|
||||||
|
src_val := perform_load(cpu, inst.src, inst.is_word)
|
||||||
|
perform_store(cpu, src_val, inst.dst, inst.is_word)
|
||||||
|
case .ADD:
|
||||||
|
src_val := perform_load(cpu, inst.src, inst.is_word)
|
||||||
|
dst_val := perform_load(cpu, inst.dst, inst.is_word)
|
||||||
|
val := dst_val + src_val
|
||||||
|
perform_store(cpu, val, inst.dst, inst.is_word)
|
||||||
|
check_flags(cpu, status_flags, dst_val, src_val, val, true)
|
||||||
|
case .SUB:
|
||||||
|
src_val := perform_load(cpu, inst.src, inst.is_word)
|
||||||
|
dst_val := perform_load(cpu, inst.dst, inst.is_word)
|
||||||
|
val := dst_val - src_val
|
||||||
|
perform_store(cpu, val, inst.dst, inst.is_word)
|
||||||
|
check_flags(cpu, status_flags, dst_val, src_val, val, false)
|
||||||
|
case .CMP:
|
||||||
|
src_val := perform_load(cpu, inst.src, inst.is_word)
|
||||||
|
dst_val := perform_load(cpu, inst.dst, inst.is_word)
|
||||||
|
val := dst_val - src_val
|
||||||
|
check_flags(cpu, status_flags, dst_val, src_val, val, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
execute_instructions :: proc(cpu: ^Cpu, instructions: DecodedInstructions) {
|
||||||
|
halt := false
|
||||||
|
for !halt && get_ip(cpu) < instructions.total_bytes_decoded {
|
||||||
|
inst,ok := instructions.inst_map[get_ip(cpu)]
|
||||||
|
if !ok {
|
||||||
|
panic("Something went wrong with a jump, most likely")
|
||||||
|
}
|
||||||
|
cpu.registers[.ip].full += i16(inst.bytes_read)
|
||||||
|
execute_instruction(cpu, inst)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+6
-8
@@ -76,6 +76,7 @@ Op :: enum {
|
|||||||
CALL,
|
CALL,
|
||||||
JMP,
|
JMP,
|
||||||
JNZ,
|
JNZ,
|
||||||
|
JNE,
|
||||||
JNGE,
|
JNGE,
|
||||||
JE,
|
JE,
|
||||||
JZ,
|
JZ,
|
||||||
@@ -90,7 +91,6 @@ Op :: enum {
|
|||||||
JBE,
|
JBE,
|
||||||
JO,
|
JO,
|
||||||
JS,
|
JS,
|
||||||
JNE,
|
|
||||||
JNL,
|
JNL,
|
||||||
JGE,
|
JGE,
|
||||||
JNLE,
|
JNLE,
|
||||||
@@ -106,6 +106,7 @@ Op :: enum {
|
|||||||
LOOP,
|
LOOP,
|
||||||
LOOPZ,
|
LOOPZ,
|
||||||
LOOPNZ,
|
LOOPNZ,
|
||||||
|
LOOPNE,
|
||||||
JCXZ,
|
JCXZ,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,16 +194,13 @@ instructions := [?]InstructionInfo {
|
|||||||
dst = .Accumulator, src = .Register,
|
dst = .Accumulator, src = .Register,
|
||||||
reg_info = .FirstByteLast3, has_flip = true, word_size = .Always16 },
|
reg_info = .FirstByteLast3, has_flip = true, word_size = .Always16 },
|
||||||
{ opname = .IN, desc = "", mask = 0b11111110, encoding = 0b11100100,
|
{ opname = .IN, desc = "", mask = 0b11111110, encoding = 0b11100100,
|
||||||
dst = .Accumulator, src = .ImmediateUnsigned },
|
dst = .Accumulator, src = .Immediate, word_size = .LastBit },
|
||||||
{ opname = .IN, desc = "", mask = 0b11111110, encoding = 0b11101100,
|
{ opname = .IN, desc = "", mask = 0b11111110, encoding = 0b11101100,
|
||||||
dst = .Accumulator, src = .VariablePort,
|
dst = .Accumulator, src = .VariablePort, word_size = .LastBit, },
|
||||||
word_size = .LastBit, },
|
|
||||||
{ opname = .OUT, desc = "", mask = 0b11111110, encoding = 0b11100110,
|
{ opname = .OUT, desc = "", mask = 0b11111110, encoding = 0b11100110,
|
||||||
dst = .ImmediateUnsigned, src = .Accumulator,
|
dst = .VariablePort, src = .Accumulator, word_size = .LastBit },
|
||||||
word_size = .LastBit, },
|
|
||||||
{ opname = .OUT, desc = "", mask = 0b11111110, encoding = 0b11101110,
|
{ opname = .OUT, desc = "", mask = 0b11111110, encoding = 0b11101110,
|
||||||
dst = .VariablePort, src = .Accumulator,
|
dst = .VariablePort, src = .Accumulator, word_size = .LastBit, },
|
||||||
word_size = .LastBit, },
|
|
||||||
{ opname = .TEST, desc = "", mask = 0b11111110, encoding = 0b10101000,
|
{ opname = .TEST, desc = "", mask = 0b11111110, encoding = 0b10101000,
|
||||||
dst = .Accumulator, src = .Immediate, word_size = .LastBit },
|
dst = .Accumulator, src = .Immediate, word_size = .LastBit },
|
||||||
{ opname = .XLAT, desc = "", mask = 0b11111111, encoding = 0b11010111,},
|
{ opname = .XLAT, desc = "", mask = 0b11111111, encoding = 0b11010111,},
|
||||||
|
|||||||
+22
-107
@@ -3,9 +3,17 @@ package sim_8086
|
|||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:math"
|
import "core:math"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
import "core:reflect"
|
||||||
|
|
||||||
instruction_builder := strings.builder_make()
|
instruction_builder := strings.builder_make()
|
||||||
|
|
||||||
|
RIGHT_ALIGN_AMOUNT := 35
|
||||||
|
|
||||||
|
operand_is :: proc($T: typeid, opr: Operand) -> bool {
|
||||||
|
_, ok := opr.(T)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
calculate_effective_address :: proc(r_m: u8) -> string {
|
calculate_effective_address :: proc(r_m: u8) -> string {
|
||||||
val: string
|
val: string
|
||||||
switch r_m {
|
switch r_m {
|
||||||
@@ -29,112 +37,32 @@ calculate_effective_address :: proc(r_m: u8) -> string {
|
|||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
get_memory_string :: proc(memoryAddr: MemoryAddr, has_segment: Maybe(Register)) -> string {
|
get_memory_string :: proc(memoryAddr: MemoryAddr, has_segment: Maybe(RegisterId)) -> string {
|
||||||
disp: string
|
disp: string
|
||||||
switch value in memoryAddr.displacement {
|
value := memoryAddr.displacement
|
||||||
case None:
|
if value != 0 {
|
||||||
disp = ""
|
disp = fmt.aprintf(" %s %d", value > 0 ? "+" : "-", math.abs(value))
|
||||||
case Disp8:
|
|
||||||
if value != 0 {
|
|
||||||
disp = fmt.aprintf(" %s %d", value > 0 ? "+" : "-", math.abs(value))
|
|
||||||
}
|
|
||||||
case Disp16:
|
|
||||||
if value != 0 {
|
|
||||||
disp = fmt.aprintf(" %s %d", value > 0 ? "+" : "-", math.abs(value))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
seg_string: string
|
seg_string: string
|
||||||
if segreg, ok := has_segment.?; ok {
|
if segreg, ok := has_segment.?; ok {
|
||||||
seg_string = fmt.aprintf("%s:", segreg.fullname)
|
seg_string = fmt.aprintf("%s:", get_register_name(segreg))
|
||||||
}
|
}
|
||||||
text := fmt.aprintf("%s[%s%s]", seg_string, calculate_effective_address(memoryAddr.addr_id), disp)
|
text := fmt.aprintf("%s[%s%s]", seg_string, calculate_effective_address(memoryAddr.addr_id), disp)
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
get_displacement_string :: proc(displacement: Displacement) -> string {
|
|
||||||
disp := ""
|
|
||||||
#partial switch value in displacement {
|
|
||||||
case i8:
|
|
||||||
if value != 0 {
|
|
||||||
disp = fmt.aprintf(" %s %d", value > 0 ? "+" : "-", math.abs(value))
|
|
||||||
}
|
|
||||||
case i16:
|
|
||||||
if value != 0 {
|
|
||||||
disp = fmt.aprintf(" %s %d", value > 0 ? "+" : "-", math.abs(value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return disp
|
|
||||||
}
|
|
||||||
|
|
||||||
get_opname :: proc(inst: Instruction) -> (string, bool) {
|
|
||||||
name: string
|
|
||||||
interseg: bool
|
|
||||||
if inst.opname == .TBD2 {
|
|
||||||
switch inst.raw_data[1] & 0b00111000 >> 3 {
|
|
||||||
case 0b000: name = "inc"
|
|
||||||
case 0b001: name = "dec"
|
|
||||||
case 0b010: name = "call"
|
|
||||||
// TODO: We really have to fix this because we shouldn't be figuring out if this
|
|
||||||
// is an intersegment here
|
|
||||||
case 0b011: name = "call"; interseg = true
|
|
||||||
case 0b100: name = "jmp"
|
|
||||||
case 0b101: name = "jmp"; interseg = true
|
|
||||||
case 0b110: name = "push"
|
|
||||||
}
|
|
||||||
} else if inst.opname == .TBD5 {
|
|
||||||
switch inst.raw_data[1] & 0b00111000 >> 3 {
|
|
||||||
case 0b000: name = "test"
|
|
||||||
case 0b001: name = "dec"
|
|
||||||
case 0b010: name = "not"
|
|
||||||
case 0b011: name = "neg"
|
|
||||||
case 0b100: name = "mul"
|
|
||||||
case 0b101: name = "imul"
|
|
||||||
case 0b110: name = "div"
|
|
||||||
case 0b111: name = "idiv"
|
|
||||||
}
|
|
||||||
} else if inst.opname == .TBD6 {
|
|
||||||
switch inst.raw_data[1] & 0b00111000 >> 3 {
|
|
||||||
case 0b000: name = "rol"
|
|
||||||
case 0b001: name = "ror"
|
|
||||||
case 0b010: name = "rcl"
|
|
||||||
case 0b011: name = "rcr"
|
|
||||||
case 0b100: name = "shl"
|
|
||||||
case 0b101: name = "shr"
|
|
||||||
case 0b111: name = "sar"
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bits: u8
|
|
||||||
if inst.opname == .TBD1 || inst.opname == .TBD3 {
|
|
||||||
bits = inst.raw_data[0] & 0b00111000 >> 3
|
|
||||||
} else {
|
|
||||||
bits = inst.raw_data[1] & 0b00111000 >> 3
|
|
||||||
}
|
|
||||||
switch bits {
|
|
||||||
case 0b000: name = "add"
|
|
||||||
case 0b001: name = "or"
|
|
||||||
case 0b010: name = "adc"
|
|
||||||
case 0b011: name = "sbb"
|
|
||||||
case 0b100: name = "and"
|
|
||||||
case 0b101: name = "sub"
|
|
||||||
case 0b110: name = "xor"
|
|
||||||
case 0b111: name = "cmp"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return name, interseg
|
|
||||||
}
|
|
||||||
|
|
||||||
get_register_name :: proc(reg_id: RegisterId) -> string {
|
get_register_name :: proc(reg_id: RegisterId) -> string {
|
||||||
low_names := [?]string{"al", "cl", "dl", "bl"}
|
low_names := [?]string{"al", "cl", "dl", "bl"}
|
||||||
high_names := [?]string{"ah", "ch", "dh", "bh"}
|
high_names := [?]string{"ah", "ch", "dh", "bh"}
|
||||||
switch reg_id.access {
|
switch reg_id.access {
|
||||||
case .Full: return registers[reg_id.id].fullname
|
case .Full: return reflect.enum_string(reg_id.name)
|
||||||
case .Low: return low_names[reg_id.id]
|
case .Low: return low_names[int(reg_id.name)]
|
||||||
case .High: return high_names[reg_id.id % 4]
|
case .High: return high_names[int(reg_id.name) % 4]
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
get_operand_string :: proc(operand: Operand, has_segment: Maybe(Register)) -> string {
|
get_operand_string :: proc(operand: Operand, has_segment: Maybe(RegisterId)) -> string {
|
||||||
string_val: string
|
string_val: string
|
||||||
switch val in operand {
|
switch val in operand {
|
||||||
case None:
|
case None:
|
||||||
@@ -142,20 +70,13 @@ get_operand_string :: proc(operand: Operand, has_segment: Maybe(Register)) -> st
|
|||||||
case RegisterId:
|
case RegisterId:
|
||||||
string_val = get_register_name(val)
|
string_val = get_register_name(val)
|
||||||
case Immediate:
|
case Immediate:
|
||||||
switch val.size {
|
string_val = fmt.aprintf("%d", val.value)
|
||||||
case .Signed8:
|
|
||||||
string_val = fmt.aprintf("%d", i8(val.value))
|
|
||||||
case .Unsigned8:
|
|
||||||
string_val = fmt.aprintf("%d", u8(val.value))
|
|
||||||
case .Signed16:
|
|
||||||
string_val = fmt.aprintf("%d", i16(val.value))
|
|
||||||
}
|
|
||||||
case MemoryAddr:
|
case MemoryAddr:
|
||||||
string_val = get_memory_string(val, has_segment)
|
string_val = get_memory_string(val, has_segment)
|
||||||
case DirectAddress:
|
case DirectAddress:
|
||||||
seg_string: string
|
seg_string: string
|
||||||
if segreg, ok := has_segment.?; ok {
|
if segreg, ok := has_segment.?; ok {
|
||||||
seg_string = fmt.aprintf("%s:", segreg.fullname)
|
seg_string = fmt.aprintf("%s:", get_register_name(segreg))
|
||||||
}
|
}
|
||||||
string_val = fmt.aprintf("%s[%d]", seg_string, val)
|
string_val = fmt.aprintf("%s[%d]", seg_string, val)
|
||||||
case Jump:
|
case Jump:
|
||||||
@@ -194,8 +115,8 @@ get_instruction_string :: proc(inst_info: InstructionInfo, instruction: Instruct
|
|||||||
src_str := get_operand_string(inst.src, inst.has_segment)
|
src_str := get_operand_string(inst.src, inst.has_segment)
|
||||||
opname: string
|
opname: string
|
||||||
is_interseg: bool
|
is_interseg: bool
|
||||||
// TODO: Do the RTTI thing here with reflection
|
|
||||||
opname = strings.to_lower(fmt.aprintf("%s", inst.opname))
|
opname = strings.to_lower(reflect.enum_string(inst.opname))
|
||||||
|
|
||||||
if dst_str == "" {
|
if dst_str == "" {
|
||||||
interseg_string: string
|
interseg_string: string
|
||||||
@@ -204,13 +125,7 @@ get_instruction_string :: proc(inst_info: InstructionInfo, instruction: Instruct
|
|||||||
}
|
}
|
||||||
fmt.sbprintf(&instruction_builder, "%s%s %s%s", opname, interseg_string, size_string, src_str)
|
fmt.sbprintf(&instruction_builder, "%s%s %s%s", opname, interseg_string, size_string, src_str)
|
||||||
} else {
|
} else {
|
||||||
// note: i don't know why this is the case, but only the move has the word/byte
|
fmt.sbprintf(&instruction_builder, "%s %s%s, %s", opname, size_string, dst_str, src_str)
|
||||||
// keyword next to the immediate, but other instructions have it on the memory address
|
|
||||||
if opname == "mov" {
|
|
||||||
fmt.sbprintf(&instruction_builder, "%s %s, %s%s", opname, dst_str, size_string, src_str)
|
|
||||||
} else {
|
|
||||||
fmt.sbprintf(&instruction_builder, "%s %s%s, %s", opname, size_string, dst_str, src_str)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare padding and comment to add debug info
|
// Prepare padding and comment to add debug info
|
||||||
|
|||||||
+67
-74
@@ -1,74 +1,12 @@
|
|||||||
package sim_8086
|
package sim_8086
|
||||||
|
|
||||||
import "core:os"
|
import "core:os"
|
||||||
|
import path "core:path/filepath"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:math"
|
import "core:math"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
import "core:reflect"
|
||||||
RIGHT_ALIGN_AMOUNT := 35
|
import "core:strconv"
|
||||||
|
|
||||||
// TODO: We completely botched this; ah and al refer to the ax register, they are not
|
|
||||||
// the low parts to sp, bp, si, di registers apparently you can't access those like that.
|
|
||||||
// We have to change how we coded this out. Likely we have to remove bytename as an option
|
|
||||||
// and then have some kind of flag when you're doing a dst or src operand specifying
|
|
||||||
// if it's the high or low part, not sure how to go about that
|
|
||||||
registers := [?]Register {
|
|
||||||
{fullname = "ax", code = 0b000},
|
|
||||||
{fullname = "cx", code = 0b001},
|
|
||||||
{fullname = "dx", code = 0b010},
|
|
||||||
{fullname = "bx", code = 0b011},
|
|
||||||
{fullname = "sp", code = 0b100},
|
|
||||||
{fullname = "bp", code = 0b101},
|
|
||||||
{fullname = "si", code = 0b110},
|
|
||||||
{fullname = "di", code = 0b111},
|
|
||||||
{fullname = "es", code = 0b000},
|
|
||||||
{fullname = "cs", code = 0b001},
|
|
||||||
{fullname = "ss", code = 0b010},
|
|
||||||
{fullname = "ds", code = 0b011},
|
|
||||||
}
|
|
||||||
|
|
||||||
variable_port := registers[2]
|
|
||||||
|
|
||||||
SEGMENT_REGISTER_START :: 8
|
|
||||||
|
|
||||||
CPU := Cpu {
|
|
||||||
memory = make([dynamic]u8, 65536)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: I don't like this here
|
|
||||||
total_bytes_processed := 0
|
|
||||||
|
|
||||||
get_i16 :: proc(data: []u8) -> i16 {
|
|
||||||
return (i16)(data[1]) << 8 | (i16)(data[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
operand_is :: proc($T: typeid, opr: Operand) -> bool {
|
|
||||||
_, ok := opr.(T)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
get_repeat_op :: proc(data: u8) -> Repeat {
|
|
||||||
bits := (data & 0b1110) >> 1
|
|
||||||
w := (data & 0b1) == 1 ? "w" : "b"
|
|
||||||
rep: string
|
|
||||||
switch bits {
|
|
||||||
case 0b010: rep = "movs"
|
|
||||||
case 0b011: rep = "cmps"
|
|
||||||
case 0b101: rep = "stos"
|
|
||||||
case 0b110: rep = "lods"
|
|
||||||
case 0b111: rep = "scas"
|
|
||||||
}
|
|
||||||
return Repeat(fmt.aprintf("%s%s", rep, w))
|
|
||||||
}
|
|
||||||
|
|
||||||
try_find_instruction :: proc(b: u8) -> (InstructionInfo, bool) {
|
|
||||||
for inst in instructions {
|
|
||||||
if inst.encoding == (b & inst.mask) {
|
|
||||||
return inst, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return InstructionInfo{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
f,err := os.open(os.args[1])
|
f,err := os.open(os.args[1])
|
||||||
@@ -95,25 +33,80 @@ main :: proc() {
|
|||||||
instruction_list := make([dynamic]string, 0, 512)
|
instruction_list := make([dynamic]string, 0, 512)
|
||||||
instructions_list := make([dynamic]Instruction, 0, 512)
|
instructions_list := make([dynamic]Instruction, 0, 512)
|
||||||
|
|
||||||
decode_data(&instructions_list, data[:], bytes_read)
|
total_bytes := decode_data(&instructions_list, data[:], bytes_read)
|
||||||
|
inst_map := make(map[int]Instruction)
|
||||||
for inst in instructions_list {
|
for inst in instructions_list {
|
||||||
execute_instruction(inst)
|
inst_map[inst.segment_offset] = inst
|
||||||
|
}
|
||||||
|
decoded_insts := DecodedInstructions {
|
||||||
|
inst_list = instructions_list,
|
||||||
|
inst_map = inst_map,
|
||||||
|
total_bytes_decoded = total_bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu := Cpu {
|
||||||
|
memory = make([dynamic]u8, 1024 * 1024), // 1,048,576
|
||||||
|
}
|
||||||
|
|
||||||
|
num_idx := strings.last_index(os.args[1], "list-") + 5
|
||||||
|
if listing_num,ok := strconv.parse_int(os.args[1][num_idx:num_idx+4]); ok {
|
||||||
|
if listing_num > 42 {
|
||||||
|
execute_instructions(&cpu, decoded_insts)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if what_to_print == "registers" || what_to_print == "all" {
|
if what_to_print == "registers" || what_to_print == "all" {
|
||||||
print_reg :: proc(reg: Register) {
|
fmt.println("Registers")
|
||||||
full := fmt.aprintf("%s: %d ", reg.fullname, reg.value.full)
|
for reg_val,name in cpu.registers {
|
||||||
hex := fmt.aprintf("0x%04x ", reg.value.full)
|
full := fmt.aprintf("%s: %d ", name, i16(reg_val.full))
|
||||||
|
hex := fmt.aprintf("0x%04x ", u16(reg_val.full))
|
||||||
fmt.printf("%s %*[1]s %s %*[4]s %08b %08b",
|
fmt.printf("%s %*[1]s %s %*[4]s %08b %08b",
|
||||||
full, 18 - len(full), "|", hex, 10 - len(hex), "|", reg.value.high, reg.value.low)
|
full, 18 - len(full), "|", hex, 10 - len(hex), "|", reg_val.high, reg_val.low)
|
||||||
fmt.println()
|
fmt.println()
|
||||||
}
|
}
|
||||||
for reg in registers {
|
fmt.println("\nFlags")
|
||||||
print_reg(reg)
|
for state,flag in cpu.flags {
|
||||||
|
fmt.printf("%c:%d ",reflect.enum_string(flag)[0], state ? 1 : 0)
|
||||||
|
}
|
||||||
|
fmt.println()
|
||||||
|
|
||||||
|
failed_ref: bool
|
||||||
|
path,ok := strings.replace(os.args[1], ".bin", ".txt", 1)
|
||||||
|
ref_cpu,_ := parse_reference_cpu_state(path)
|
||||||
|
for reg_val,name in ref_cpu.registers {
|
||||||
|
if name == .ip && reg_val.full == 0 do continue
|
||||||
|
if cpu.registers[name].full != reg_val.full {
|
||||||
|
msg := "%s register does not match reference - Expected 0x%04x | Actual 0x%04x"
|
||||||
|
fmt.printfln(msg, name, reg_val.full, cpu.registers[name].full)
|
||||||
|
failed_ref = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for f in Flag {
|
||||||
|
if ref_cpu.flags[f] != cpu.flags[f] {
|
||||||
|
msg := "%s flag does not match reference - Expected %d | Actual %d"
|
||||||
|
fmt.printfln(msg, f, ref_cpu.flags[f] ? 1 : 0, cpu.flags[f] ? 1 : 0)
|
||||||
|
failed_ref = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if failed_ref {
|
||||||
|
os.exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if what_to_print == "instructions" || what_to_print == "all" {
|
if what_to_print == "instructions" || what_to_print == "all" {
|
||||||
print_instructions_stdout(instructions_list[:])
|
print_instructions_stdout(instructions_list[:])
|
||||||
}
|
}
|
||||||
|
if len(os.args) > 3 && os.args[3] == "dump" {
|
||||||
|
f2,open_err := os.open("./listing-54.data", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644)
|
||||||
|
if open_err == nil {
|
||||||
|
bytes_written,write_err := os.write(f2, cpu.memory[:])
|
||||||
|
if write_err == nil {
|
||||||
|
fmt.println("Dumpped", bytes_written, "to disk: listing-54.data")
|
||||||
|
} else {
|
||||||
|
fmt.println("Error2:", write_err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.println("Error1:", open_err)
|
||||||
|
}
|
||||||
|
defer os.close(f)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -16,13 +16,14 @@ odin build . -out:sim8086
|
|||||||
for asm_txt in asm_files/*.txt;
|
for asm_txt in asm_files/*.txt;
|
||||||
do
|
do
|
||||||
asm_listing=$(basename $asm_txt .txt)
|
asm_listing=$(basename $asm_txt .txt)
|
||||||
./sim8086 asm_files/${asm_listing}.bin registers | awk '{print $1 $4}' | sort > temp1
|
output=$(./sim8086 asm_files/${asm_listing}.bin registers)
|
||||||
cat $asm_txt | awk '/Final registers/,0' | tail -n +2 | awk '{print $1 $2}' | sort > temp2
|
|
||||||
diff -U0 --color temp1 temp2
|
|
||||||
if [ $? -eq 1 ]; then
|
if [ $? -eq 1 ]; then
|
||||||
echo Listing $asm_listing Failed!
|
echo -e "Listing $asm_listing ${RED}Failed!${NC}"
|
||||||
|
echo "$output" | while IFS= read -r line; do
|
||||||
|
echo "$line"
|
||||||
|
done
|
||||||
|
echo -e ""
|
||||||
else
|
else
|
||||||
echo Listing $asm_listing Succeded!
|
echo -e "Listing $asm_listing: ${GREEN}Succeded!${NC}"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
rm temp1 temp2
|
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package sim_8086
|
||||||
|
|
||||||
|
import "core:os"
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:math"
|
||||||
|
import "core:strings"
|
||||||
|
import "core:strconv"
|
||||||
|
import "core:reflect"
|
||||||
|
|
||||||
|
get_cpu_register_by_name :: proc(cpu: ^Cpu, name: string) -> ^RegisterValue {
|
||||||
|
reg,_ := reflect.enum_from_name(Register, name)
|
||||||
|
return &cpu.registers[reg]
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_reference_cpu_state :: proc(filename: string) -> (Cpu, bool) {
|
||||||
|
cpu: Cpu
|
||||||
|
|
||||||
|
data,ok := os.read_entire_file(filename)
|
||||||
|
if !ok {
|
||||||
|
return cpu, false
|
||||||
|
}
|
||||||
|
defer delete(data)
|
||||||
|
|
||||||
|
content := string(data)
|
||||||
|
lines := strings.split(content, "\n")
|
||||||
|
defer delete(lines)
|
||||||
|
|
||||||
|
for line in lines {
|
||||||
|
space_count := 0
|
||||||
|
for c,i in line {
|
||||||
|
if space_count == 0 && c != ' ' {
|
||||||
|
break
|
||||||
|
} else if c == ' ' {
|
||||||
|
space_count += 1
|
||||||
|
} else {
|
||||||
|
if line[i:i+5] == "flags" {
|
||||||
|
idx := i + 7
|
||||||
|
for idx < len(line) {
|
||||||
|
flag_name := fmt.tprintf("%cF", line[idx])
|
||||||
|
if flag,ok := reflect.enum_from_name(Flag, flag_name); ok {
|
||||||
|
cpu.flags[flag] = true
|
||||||
|
} else {
|
||||||
|
fmt.eprintfln("Error parsing flag enum %s", flag_name)
|
||||||
|
}
|
||||||
|
idx += 1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reg_name := line[i:i+2]
|
||||||
|
reg_value := get_cpu_register_by_name(&cpu, reg_name)
|
||||||
|
hex_string := line[i+6:i+10]
|
||||||
|
if hex_num,ok := strconv.parse_int(hex_string, 16); ok {
|
||||||
|
reg_value^.full = i16(hex_num)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cpu, true
|
||||||
|
}
|
||||||
+51
-29
@@ -1,19 +1,40 @@
|
|||||||
package sim_8086
|
package sim_8086
|
||||||
|
|
||||||
Register :: struct {
|
Register :: enum {
|
||||||
fullname: string,
|
ax,
|
||||||
value: struct #raw_union {
|
cx,
|
||||||
using _: struct {
|
dx,
|
||||||
low, high: byte,
|
bx,
|
||||||
},
|
sp,
|
||||||
full: u16,
|
bp,
|
||||||
},
|
si,
|
||||||
code: u8,
|
di,
|
||||||
|
es,
|
||||||
|
cs,
|
||||||
|
ss,
|
||||||
|
ds,
|
||||||
|
ip,
|
||||||
}
|
}
|
||||||
|
|
||||||
Flags :: struct {
|
RegisterValue :: struct #raw_union {
|
||||||
ZF: bool,
|
using _: struct {
|
||||||
SF: bool,
|
low, high: byte,
|
||||||
|
},
|
||||||
|
full: i16,
|
||||||
|
}
|
||||||
|
|
||||||
|
Flag :: enum {
|
||||||
|
OF,
|
||||||
|
SF,
|
||||||
|
ZF,
|
||||||
|
AF,
|
||||||
|
PF,
|
||||||
|
CF,
|
||||||
|
// NOTE: These are the control flags, previous are status flags, justing noting in
|
||||||
|
// case we have to make that distinction in later modeling
|
||||||
|
TF,
|
||||||
|
DF,
|
||||||
|
IF,
|
||||||
}
|
}
|
||||||
|
|
||||||
WordSize :: enum {
|
WordSize :: enum {
|
||||||
@@ -25,36 +46,29 @@ WordSize :: enum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
None :: struct {}
|
None :: struct {}
|
||||||
Disp8 :: i8
|
|
||||||
Disp16 :: i16
|
|
||||||
Displacement :: union {
|
|
||||||
None,
|
|
||||||
Disp8,
|
|
||||||
Disp16
|
|
||||||
}
|
|
||||||
|
|
||||||
RegisterAccess :: enum {
|
RegisterAccess :: enum {
|
||||||
|
Full,
|
||||||
Low,
|
Low,
|
||||||
High,
|
High,
|
||||||
Full,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RegisterId :: struct {
|
RegisterId :: struct {
|
||||||
|
name: Register,
|
||||||
access: RegisterAccess,
|
access: RegisterAccess,
|
||||||
id: int,
|
|
||||||
}
|
}
|
||||||
ImmediateSize :: enum {
|
ImmediateSize :: enum {
|
||||||
Signed8,
|
_8,
|
||||||
Unsigned8,
|
_16,
|
||||||
Signed16,
|
|
||||||
}
|
}
|
||||||
Immediate :: struct {
|
Immediate :: struct {
|
||||||
value: u16,
|
value: i16,
|
||||||
size: ImmediateSize,
|
size: ImmediateSize,
|
||||||
}
|
}
|
||||||
MemoryAddr :: struct {
|
MemoryAddr :: struct {
|
||||||
addr_id: u8,
|
addr_id: u8,
|
||||||
displacement: Displacement,
|
displacement: i16,
|
||||||
|
disp_size: ImmediateSize,
|
||||||
}
|
}
|
||||||
DirectAddress :: distinct i16
|
DirectAddress :: distinct i16
|
||||||
Jump :: distinct i8
|
Jump :: distinct i8
|
||||||
@@ -81,7 +95,6 @@ OperandInfo :: enum {
|
|||||||
SegmentRegister,
|
SegmentRegister,
|
||||||
RegisterMemory,
|
RegisterMemory,
|
||||||
Immediate,
|
Immediate,
|
||||||
ImmediateUnsigned,
|
|
||||||
Accumulator,
|
Accumulator,
|
||||||
DirectAddress,
|
DirectAddress,
|
||||||
Jump,
|
Jump,
|
||||||
@@ -107,6 +120,7 @@ InstructionInfo :: struct {
|
|||||||
desc: string,
|
desc: string,
|
||||||
src: OperandInfo,
|
src: OperandInfo,
|
||||||
dst: OperandInfo,
|
dst: OperandInfo,
|
||||||
|
flags: bit_set[Flag],
|
||||||
word_size: WordSize,
|
word_size: WordSize,
|
||||||
reg_info: RegisterEncodingBits,
|
reg_info: RegisterEncodingBits,
|
||||||
has_flip: bool,
|
has_flip: bool,
|
||||||
@@ -125,14 +139,22 @@ Instruction :: struct {
|
|||||||
indirect_intersegment: bool,
|
indirect_intersegment: bool,
|
||||||
// TODO: This is trickier than I thought, it's more than just the one instruction
|
// TODO: This is trickier than I thought, it's more than just the one instruction
|
||||||
// that uses it
|
// that uses it
|
||||||
has_segment: Maybe(Register),
|
has_segment: Maybe(RegisterId),
|
||||||
has_lock: bool,
|
has_lock: bool,
|
||||||
bytes_read: int,
|
bytes_read: int,
|
||||||
|
segment_offset: int,
|
||||||
raw_data: []u8,
|
raw_data: []u8,
|
||||||
debug_msg: string,
|
debug_msg: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DecodedInstructions :: struct {
|
||||||
|
inst_list: [dynamic]Instruction,
|
||||||
|
inst_map: map[int]Instruction,
|
||||||
|
total_bytes_decoded: int
|
||||||
|
}
|
||||||
|
|
||||||
Cpu :: struct {
|
Cpu :: struct {
|
||||||
flags: Flags,
|
flags: [Flag]bool,
|
||||||
|
registers: [Register]RegisterValue,
|
||||||
memory: [dynamic]u8,
|
memory: [dynamic]u8,
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user