Compare commits
49
Commits
7c48c5ed90
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51dae3128b | ||
|
|
85a8172923 | ||
|
|
fafb440891 | ||
|
|
df105ea3cf | ||
|
|
831a307975 | ||
|
|
ea7b65994b | ||
|
|
ab2b107e7e | ||
|
|
da78d875c1 | ||
|
|
3ca4059c44 | ||
|
|
11ccfe78a9 | ||
|
|
948960e8f5 | ||
|
|
0e90e2c23c | ||
|
|
99fd7fabd7 | ||
|
|
d0f91f15f8 | ||
|
|
731e5bd45d | ||
|
|
5cf4768b80 | ||
|
|
cd5eada115 | ||
|
|
08874c4533 | ||
|
|
305ac557fa | ||
|
|
b511c6a620 | ||
|
|
266f7a7900 | ||
|
|
863ccfc583 | ||
|
|
fd8f696627 | ||
|
|
a91c3d9ba9 | ||
|
|
c312b7fd57 | ||
|
|
b78d043b6c | ||
|
|
118ed482c1 | ||
|
|
6e7783c623 | ||
|
|
7e74d1e9a2 | ||
|
|
32ddb518e9 | ||
|
|
6909f75b35 | ||
|
|
d4216d793a | ||
|
|
3dca2254e1 | ||
|
|
460e1a8700 | ||
|
|
fc02debf65 | ||
|
|
babb07cb43 | ||
|
|
8967be44a0 | ||
|
|
08ef7d901b | ||
|
|
782ff93ace | ||
|
|
390fedc848 | ||
|
|
c7ee622847 | ||
|
|
6365bd73a0 | ||
|
|
1f9837b256 | ||
|
|
c1799fcac2 | ||
|
|
446848dffb | ||
|
|
bc0a8b65eb | ||
|
|
f8f5744cd3 | ||
|
|
fbedf7cf67 | ||
|
|
c5e9cfac44 |
No files matched your search
@@ -3,3 +3,6 @@
|
||||
/asm_files/*.bin
|
||||
/8086_family_Users_Manual_1_.pdf
|
||||
/decoder8086
|
||||
/performance-aware
|
||||
/sim8086
|
||||
/listing-*.data
|
||||
+94
-6
@@ -1,8 +1,96 @@
|
||||
#+OPTIONS: toc:nil
|
||||
|
||||
* 8086 CPU Emulator
|
||||
Following along with [[https://www.computerenhance.com/p/table-of-contents][Performance Aware by Computer Enhance]]
|
||||
* License
|
||||
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
|
||||
a paid course so maybe pay for the course if you want to use these files?
|
||||
* 8086 CPU Simulator
|
||||
|
||||
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.
|
||||
|
||||
** Features
|
||||
|
||||
- *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.
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,451 @@
|
||||
; ========================================================================
|
||||
;
|
||||
; (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 42
|
||||
; ========================================================================
|
||||
|
||||
;
|
||||
; NOTE(casey): This is not meant to be a real compliance test for 8086
|
||||
; disassemblers. It's just a reasonable selection of opcodes and patterns
|
||||
; to use as a first pass in making sure a disassembler handles a large
|
||||
; cross-section of the encoding. To be absolutely certain you haven't
|
||||
; missed something, you would need a more exhaustive listing!
|
||||
;
|
||||
|
||||
bits 16
|
||||
|
||||
mov si, bx
|
||||
mov dh, al
|
||||
mov cl, 12
|
||||
mov ch, -12
|
||||
mov cx, 12
|
||||
mov cx, -12
|
||||
mov dx, 3948
|
||||
mov dx, -3948
|
||||
mov al, [bx + si]
|
||||
mov bx, [bp + di]
|
||||
mov dx, [bp]
|
||||
mov ah, [bx + si + 4]
|
||||
mov al, [bx + si + 4999]
|
||||
mov [bx + di], cx
|
||||
mov [bp + si], cl
|
||||
mov [bp], ch
|
||||
mov ax, [bx + di - 37]
|
||||
mov [si - 300], cx
|
||||
mov dx, [bx - 32]
|
||||
mov [bp + di], byte 7
|
||||
mov [di + 901], word 347
|
||||
mov bp, [5]
|
||||
mov bx, [3458]
|
||||
mov ax, [2555]
|
||||
mov ax, [16]
|
||||
mov [2554], ax
|
||||
mov [15], ax
|
||||
|
||||
push word [bp + si]
|
||||
push word [3000]
|
||||
push word [bx + di - 30]
|
||||
push cx
|
||||
push ax
|
||||
push dx
|
||||
push cs
|
||||
|
||||
pop word [bp + si]
|
||||
pop word [3]
|
||||
pop word [bx + di - 3000]
|
||||
pop sp
|
||||
pop di
|
||||
pop si
|
||||
pop ds
|
||||
|
||||
xchg ax, [bp - 1000]
|
||||
xchg [bx + 50], bp
|
||||
|
||||
xchg ax, ax
|
||||
xchg ax, dx
|
||||
xchg ax, sp
|
||||
xchg ax, si
|
||||
xchg ax, di
|
||||
|
||||
xchg cx, dx
|
||||
xchg si, cx
|
||||
xchg cl, ah
|
||||
|
||||
in al, 200
|
||||
in al, dx
|
||||
in ax, dx
|
||||
|
||||
out 44, ax
|
||||
out dx, al
|
||||
|
||||
xlat
|
||||
lea ax, [bx + di + 1420]
|
||||
lea bx, [bp - 50]
|
||||
lea sp, [bp - 1003]
|
||||
lea di, [bx + si - 7]
|
||||
|
||||
lds ax, [bx + di + 1420]
|
||||
lds bx, [bp - 50]
|
||||
lds sp, [bp - 1003]
|
||||
lds di, [bx + si - 7]
|
||||
|
||||
les ax, [bx + di + 1420]
|
||||
les bx, [bp - 50]
|
||||
les sp, [bp - 1003]
|
||||
les di, [bx + si - 7]
|
||||
|
||||
lahf
|
||||
sahf
|
||||
pushf
|
||||
popf
|
||||
|
||||
add cx, [bp]
|
||||
add dx, [bx + si]
|
||||
add [bp + di + 5000], ah
|
||||
add [bx], al
|
||||
add sp, 392
|
||||
add si, 5
|
||||
add ax, 1000
|
||||
add ah, 30
|
||||
add al, 9
|
||||
add cx, bx
|
||||
add ch, al
|
||||
|
||||
adc cx, [bp]
|
||||
adc dx, [bx + si]
|
||||
adc [bp + di + 5000], ah
|
||||
adc [bx], al
|
||||
adc sp, 392
|
||||
adc si, 5
|
||||
adc ax, 1000
|
||||
adc ah, 30
|
||||
adc al, 9
|
||||
adc cx, bx
|
||||
adc ch, al
|
||||
|
||||
inc ax
|
||||
inc cx
|
||||
inc dh
|
||||
inc al
|
||||
inc ah
|
||||
inc sp
|
||||
inc di
|
||||
inc byte [bp + 1002]
|
||||
inc word [bx + 39]
|
||||
inc byte [bx + si + 5]
|
||||
inc word [bp + di - 10044]
|
||||
inc word [9349]
|
||||
inc byte [bp]
|
||||
|
||||
aaa
|
||||
daa
|
||||
|
||||
sub cx, [bp]
|
||||
sub dx, [bx + si]
|
||||
sub [bp + di + 5000], ah
|
||||
sub [bx], al
|
||||
sub sp, 392
|
||||
sub si, 5
|
||||
sub ax, 1000
|
||||
sub ah, 30
|
||||
sub al, 9
|
||||
sub cx, bx
|
||||
sub ch, al
|
||||
|
||||
sbb cx, [bp]
|
||||
sbb dx, [bx + si]
|
||||
sbb [bp + di + 5000], ah
|
||||
sbb [bx], al
|
||||
sbb sp, 392
|
||||
sbb si, 5
|
||||
sbb ax, 1000
|
||||
sbb ah, 30
|
||||
sbb al, 9
|
||||
sbb cx, bx
|
||||
sbb ch, al
|
||||
|
||||
dec ax
|
||||
dec cx
|
||||
dec dh
|
||||
dec al
|
||||
dec ah
|
||||
dec sp
|
||||
dec di
|
||||
dec byte [bp + 1002]
|
||||
dec word [bx + 39]
|
||||
dec byte [bx + si + 5]
|
||||
dec word [bp + di - 10044]
|
||||
dec word [9349]
|
||||
dec byte [bp]
|
||||
|
||||
neg ax
|
||||
neg cx
|
||||
neg dh
|
||||
neg al
|
||||
neg ah
|
||||
neg sp
|
||||
neg di
|
||||
neg byte [bp + 1002]
|
||||
neg word [bx + 39]
|
||||
neg byte [bx + si + 5]
|
||||
neg word [bp + di - 10044]
|
||||
neg word [9349]
|
||||
neg byte [bp]
|
||||
|
||||
cmp bx, cx
|
||||
cmp dh, [bp + 390]
|
||||
cmp [bp + 2], si
|
||||
cmp bl, 20
|
||||
cmp byte [bx], 34
|
||||
cmp ax, 23909
|
||||
|
||||
aas
|
||||
das
|
||||
|
||||
mul al
|
||||
mul cx
|
||||
mul word [bp]
|
||||
mul byte [bx + di + 500]
|
||||
|
||||
imul ch
|
||||
imul dx
|
||||
imul byte [bx]
|
||||
imul word [9483]
|
||||
|
||||
aam
|
||||
|
||||
div bl
|
||||
div sp
|
||||
div byte [bx + si + 2990]
|
||||
div word [bp + di + 1000]
|
||||
|
||||
idiv ax
|
||||
idiv si
|
||||
idiv byte [bp + si]
|
||||
idiv word [bx + 493]
|
||||
|
||||
aad
|
||||
cbw
|
||||
cwd
|
||||
|
||||
not ah
|
||||
not bl
|
||||
not sp
|
||||
not si
|
||||
not word [bp]
|
||||
not byte [bp + 9905]
|
||||
|
||||
shl ah, 1
|
||||
shr ax, 1
|
||||
sar bx, 1
|
||||
rol cx, 1
|
||||
ror dh, 1
|
||||
rcl sp, 1
|
||||
rcr bp, 1
|
||||
|
||||
shl word [bp + 5], 1
|
||||
shr byte [bx + si - 199], 1
|
||||
sar byte [bx + di - 300], 1
|
||||
rol word [bp], 1
|
||||
ror word [4938], 1
|
||||
rcl byte [3], 1
|
||||
rcr word [bx], 1
|
||||
|
||||
shl ah, cl
|
||||
shr ax, cl
|
||||
sar bx, cl
|
||||
rol cx, cl
|
||||
ror dh, cl
|
||||
rcl sp, cl
|
||||
rcr bp, cl
|
||||
|
||||
shl word [bp + 5], cl
|
||||
shr word [bx + si - 199], cl
|
||||
sar byte [bx + di - 300], cl
|
||||
rol byte [bp], cl
|
||||
ror byte [4938], cl
|
||||
rcl byte [3], cl
|
||||
rcr word [bx], cl
|
||||
|
||||
and al, ah
|
||||
and ch, cl
|
||||
and bp, si
|
||||
and di, sp
|
||||
and al, 93
|
||||
and ax, 20392
|
||||
and [bp + si + 10], ch
|
||||
and [bx + di + 1000], dx
|
||||
and bx, [bp]
|
||||
and cx, [4384]
|
||||
and byte [bp - 39], 239
|
||||
and word [bx + si - 4332], 10328
|
||||
|
||||
test bx, cx
|
||||
test dh, [bp + 390]
|
||||
test [bp + 2], si
|
||||
test bl, 20
|
||||
test byte [bx], 34
|
||||
test ax, 23909
|
||||
|
||||
or al, ah
|
||||
or ch, cl
|
||||
or bp, si
|
||||
or di, sp
|
||||
or al, 93
|
||||
or ax, 20392
|
||||
or [bp + si + 10], ch
|
||||
or [bx + di + 1000], dx
|
||||
or bx, [bp]
|
||||
or cx, [4384]
|
||||
or byte [bp - 39], 239
|
||||
or word [bx + si - 4332], 10328
|
||||
|
||||
xor al, ah
|
||||
xor ch, cl
|
||||
xor bp, si
|
||||
xor di, sp
|
||||
xor al, 93
|
||||
xor ax, 20392
|
||||
xor [bp + si + 10], ch
|
||||
xor [bx + di + 1000], dx
|
||||
xor bx, [bp]
|
||||
xor cx, [4384]
|
||||
xor byte [bp - 39], 239
|
||||
xor word [bx + si - 4332], 10328
|
||||
|
||||
rep movsb
|
||||
rep cmpsb
|
||||
rep scasb
|
||||
rep lodsb
|
||||
rep movsw
|
||||
rep cmpsw
|
||||
rep scasw
|
||||
rep lodsw
|
||||
|
||||
; NOTE(casey): Special thanks (as always!) to Mārtiņš Možeiko for figuring out why NASM
|
||||
; wouldn't compile "rep stds" instructions. It was because it was a misprint in the 8086
|
||||
; manual! It was really just "rep stos", which of course is still in x64, and NASM
|
||||
; assembles it just fine.
|
||||
rep stosb
|
||||
rep stosw
|
||||
|
||||
call [39201]
|
||||
call [bp - 100]
|
||||
call sp
|
||||
call ax
|
||||
|
||||
jmp ax
|
||||
jmp di
|
||||
jmp [12]
|
||||
jmp [4395]
|
||||
|
||||
ret -7
|
||||
ret 500
|
||||
ret
|
||||
|
||||
label:
|
||||
je label
|
||||
jl label
|
||||
jle label
|
||||
jb label
|
||||
jbe label
|
||||
jp label
|
||||
jo label
|
||||
js label
|
||||
jne label
|
||||
jnl label
|
||||
jg label
|
||||
jnb label
|
||||
ja label
|
||||
jnp label
|
||||
jno label
|
||||
jns label
|
||||
loop label
|
||||
loopz label
|
||||
loopnz label
|
||||
jcxz label
|
||||
|
||||
int 13
|
||||
int3
|
||||
|
||||
into
|
||||
iret
|
||||
|
||||
clc
|
||||
cmc
|
||||
stc
|
||||
cld
|
||||
std
|
||||
cli
|
||||
sti
|
||||
hlt
|
||||
wait
|
||||
|
||||
lock not byte [bp + 9905]
|
||||
lock xchg [100], al
|
||||
|
||||
mov al, cs:[bx + si]
|
||||
mov bx, ds:[bp + di]
|
||||
mov dx, es:[bp]
|
||||
mov ah, ss:[bx + si + 4]
|
||||
|
||||
and ss:[bp + si + 10], ch
|
||||
or ds:[bx + di + 1000], dx
|
||||
xor bx, es:[bp]
|
||||
cmp cx, es:[4384]
|
||||
test byte cs:[bp - 39], 239
|
||||
sbb word cs:[bx + si - 4332], 10328
|
||||
|
||||
lock not byte CS:[bp + 9905]
|
||||
|
||||
;
|
||||
; NOTE(casey): These were not in the original homework set, but have been added since, as people
|
||||
; found instruction encodings that were not previously covered. Thank you to everyone who
|
||||
; submitted test cases!
|
||||
;
|
||||
|
||||
call 123:456
|
||||
jmp 789:34
|
||||
|
||||
mov [bx+si+59],es
|
||||
|
||||
jmp 2620
|
||||
call 11804
|
||||
|
||||
retf 17556
|
||||
ret 17560
|
||||
retf
|
||||
ret
|
||||
|
||||
call [bp+si-0x3a]
|
||||
call far [bp+si-0x3a]
|
||||
jmp [di]
|
||||
jmp far [di]
|
||||
|
||||
jmp 21862:30600
|
||||
|
||||
;
|
||||
; TODO(casey): I would like to uncomment this, but as far as I can tell, NASM doesn't recognize the ESC instruction :(
|
||||
; so even if I just force the assembler to output the bits here, our disasm will fail to assemble because it will (correctly!)
|
||||
; print the esc instruction and NASM will error because it doesn't know what that is.
|
||||
;
|
||||
; esc 938,ax
|
||||
;
|
||||
|
||||
;
|
||||
; TODO(casey): According to NASM, "rep movsb" is "not lockable". However the 8086 manual seems to think it is, and
|
||||
; even describes what happens when you you lock a rep: the lock is held for the duration of the rep operation. So...
|
||||
; yeah. Not sure why this doesn't work in NASM:
|
||||
;
|
||||
; lock rep movsb
|
||||
;
|
||||
@@ -0,0 +1,27 @@
|
||||
; ========================================================================
|
||||
;
|
||||
; (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 43
|
||||
; ========================================================================
|
||||
|
||||
bits 16
|
||||
|
||||
mov ax, 1
|
||||
mov bx, 2
|
||||
mov cx, 3
|
||||
mov dx, 4
|
||||
|
||||
mov sp, 5
|
||||
mov bp, 6
|
||||
mov si, 7
|
||||
mov di, 8
|
||||
@@ -0,0 +1,23 @@
|
||||
--- test\listing_0043_immediate_movs execution ---
|
||||
mov ax, 1 ; ax:0x0->0x1
|
||||
mov bx, 2 ; bx:0x0->0x2
|
||||
mov cx, 3 ; cx:0x0->0x3
|
||||
mov dx, 4 ; dx:0x0->0x4
|
||||
mov sp, 5 ; sp:0x0->0x5
|
||||
mov bp, 6 ; bp:0x0->0x6
|
||||
mov si, 7 ; si:0x0->0x7
|
||||
mov di, 8 ; di:0x0->0x8
|
||||
|
||||
Final registers:
|
||||
ax: 0x0001 (1)
|
||||
bx: 0x0002 (2)
|
||||
cx: 0x0003 (3)
|
||||
dx: 0x0004 (4)
|
||||
sp: 0x0005 (5)
|
||||
bp: 0x0006 (6)
|
||||
si: 0x0007 (7)
|
||||
di: 0x0008 (8)
|
||||
es: 0x0000 (0)
|
||||
ss: 0x0000 (0)
|
||||
cs: 0x0000 (0)
|
||||
ds: 0x0000 (0)
|
||||
@@ -0,0 +1,32 @@
|
||||
; ========================================================================
|
||||
;
|
||||
; (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 44
|
||||
; ========================================================================
|
||||
|
||||
bits 16
|
||||
|
||||
mov ax, 1
|
||||
mov bx, 2
|
||||
mov cx, 3
|
||||
mov dx, 4
|
||||
|
||||
mov sp, ax
|
||||
mov bp, bx
|
||||
mov si, cx
|
||||
mov di, dx
|
||||
|
||||
mov dx, sp
|
||||
mov cx, bp
|
||||
mov bx, si
|
||||
mov ax, di
|
||||
@@ -0,0 +1,27 @@
|
||||
--- test\listing_0044_register_movs execution ---
|
||||
mov ax, 1 ; ax:0x0->0x1
|
||||
mov bx, 2 ; bx:0x0->0x2
|
||||
mov cx, 3 ; cx:0x0->0x3
|
||||
mov dx, 4 ; dx:0x0->0x4
|
||||
mov sp, ax ; sp:0x0->0x1
|
||||
mov bp, bx ; bp:0x0->0x2
|
||||
mov si, cx ; si:0x0->0x3
|
||||
mov di, dx ; di:0x0->0x4
|
||||
mov dx, sp ; dx:0x4->0x1
|
||||
mov cx, bp ; cx:0x3->0x2
|
||||
mov bx, si ; bx:0x2->0x3
|
||||
mov ax, di ; ax:0x1->0x4
|
||||
|
||||
Final registers:
|
||||
ax: 0x0004 (4)
|
||||
bx: 0x0003 (3)
|
||||
cx: 0x0002 (2)
|
||||
dx: 0x0001 (1)
|
||||
sp: 0x0001 (1)
|
||||
bp: 0x0002 (2)
|
||||
si: 0x0003 (3)
|
||||
di: 0x0004 (4)
|
||||
es: 0x0000 (0)
|
||||
ss: 0x0000 (0)
|
||||
cs: 0x0000 (0)
|
||||
ds: 0x0000 (0)
|
||||
@@ -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 45
|
||||
; ========================================================================
|
||||
|
||||
bits 16
|
||||
|
||||
mov ax, 0x2222
|
||||
mov bx, 0x4444
|
||||
mov cx, 0x6666
|
||||
mov dx, 0x8888
|
||||
|
||||
mov ss, ax
|
||||
mov ds, bx
|
||||
mov es, cx
|
||||
|
||||
mov al, 0x11
|
||||
mov bh, 0x33
|
||||
mov cl, 0x55
|
||||
mov dh, 0x77
|
||||
|
||||
mov ah, bl
|
||||
mov cl, dh
|
||||
|
||||
mov ss, ax
|
||||
mov ds, bx
|
||||
mov es, cx
|
||||
|
||||
mov sp, ss
|
||||
mov bp, ds
|
||||
mov si, es
|
||||
mov di, dx
|
||||
@@ -0,0 +1,35 @@
|
||||
--- test\listing_0045_challenge_register_movs execution ---
|
||||
mov ax, 8738 ; ax:0x0->0x2222
|
||||
mov bx, 17476 ; bx:0x0->0x4444
|
||||
mov cx, 26214 ; cx:0x0->0x6666
|
||||
mov dx, 34952 ; dx:0x0->0x8888
|
||||
mov ss, ax ; ss:0x0->0x2222
|
||||
mov ds, bx ; ds:0x0->0x4444
|
||||
mov es, cx ; es:0x0->0x6666
|
||||
mov al, 17 ; ax:0x2222->0x2211
|
||||
mov bh, 51 ; bx:0x4444->0x3344
|
||||
mov cl, 85 ; cx:0x6666->0x6655
|
||||
mov dh, 119 ; dx:0x8888->0x7788
|
||||
mov ah, bl ; ax:0x2211->0x4411
|
||||
mov cl, dh ; cx:0x6655->0x6677
|
||||
mov ss, ax ; ss:0x2222->0x4411
|
||||
mov ds, bx ; ds:0x4444->0x3344
|
||||
mov es, cx ; es:0x6666->0x6677
|
||||
mov sp, ss ; sp:0x0->0x4411
|
||||
mov bp, ds ; bp:0x0->0x3344
|
||||
mov si, es ; si:0x0->0x6677
|
||||
mov di, dx ; di:0x0->0x7788
|
||||
|
||||
Final registers:
|
||||
ax: 0x4411 (17425)
|
||||
bx: 0x3344 (13124)
|
||||
cx: 0x6677 (26231)
|
||||
dx: 0x7788 (30600)
|
||||
sp: 0x4411 (17425)
|
||||
bp: 0x3344 (13124)
|
||||
si: 0x6677 (26231)
|
||||
di: 0x7788 (30600)
|
||||
es: 0x6677 (26231)
|
||||
ss: 0x4411 (17425)
|
||||
cs: 0x0000 (0)
|
||||
ds: 0x3344 (13124)
|
||||
@@ -0,0 +1,28 @@
|
||||
; ========================================================================
|
||||
;
|
||||
; (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 46
|
||||
; ========================================================================
|
||||
|
||||
bits 16
|
||||
|
||||
mov bx, -4093
|
||||
mov cx, 3841
|
||||
sub bx, cx
|
||||
|
||||
mov sp, 998
|
||||
mov bp, 999
|
||||
cmp bp, sp
|
||||
|
||||
add bp, 1027
|
||||
sub bp, 2026
|
||||
@@ -0,0 +1,24 @@
|
||||
--- test\listing_0046_add_sub_cmp execution ---
|
||||
mov bx, 61443 ; bx:0x0->0xf003
|
||||
mov cx, 3841 ; cx:0x0->0xf01
|
||||
sub bx, cx ; bx:0xf003->0xe102 flags:->S
|
||||
mov sp, 998 ; sp:0x0->0x3e6
|
||||
mov bp, 999 ; bp:0x0->0x3e7
|
||||
cmp bp, sp ; flags:S->
|
||||
add bp, 1027 ; bp:0x3e7->0x7ea
|
||||
sub bp, 2026 ; bp:0x7ea->0x0 flags:->PZ
|
||||
|
||||
Final registers:
|
||||
bx: 0xe102 (57602)
|
||||
cx: 0x0f01 (3841)
|
||||
sp: 0x03e6 (998)
|
||||
ax: 0x0000 (0)
|
||||
bp: 0x0000 (0)
|
||||
dx: 0x0000 (0)
|
||||
si: 0x0000 (0)
|
||||
di: 0x0000 (0)
|
||||
es: 0x0000 (0)
|
||||
ss: 0x0000 (0)
|
||||
cs: 0x0000 (0)
|
||||
ds: 0x0000 (0)
|
||||
flags: PZ
|
||||
@@ -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
|
||||
@@ -1,335 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "lib.h"
|
||||
#include "decode.h"
|
||||
|
||||
/// Get Effective Address Calculation Registers
|
||||
char* get_eac_register(char rm)
|
||||
{
|
||||
char* reg_name;
|
||||
switch (rm)
|
||||
{
|
||||
case 0b000: reg_name = "bx + si"; break;
|
||||
case 0b001: reg_name = "bx + di"; break;
|
||||
case 0b010: reg_name = "bp + si"; break;
|
||||
case 0b011: reg_name = "bp + di"; break;
|
||||
case 0b100: reg_name = "si"; break;
|
||||
case 0b101: reg_name = "di"; break;
|
||||
case 0b110: reg_name = "bp"; break;
|
||||
case 0b111: reg_name = "bx"; break;
|
||||
default: perror("Invalid R/M value"); exit(1);
|
||||
}
|
||||
return reg_name;
|
||||
}
|
||||
|
||||
static char* reg_name(Register reg, char wide)
|
||||
{
|
||||
return wide == 1 ? reg.fullname : reg.bytename;
|
||||
}
|
||||
|
||||
static u8 mask_and_shift(u8 value, u8 mask)
|
||||
{
|
||||
value &= mask;
|
||||
int count = 0;
|
||||
while ((mask & 0x1) == 0 && count < 8)
|
||||
{
|
||||
value >>= 1;
|
||||
mask >>= 1;
|
||||
count++;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
ParsedInstruction parse_instruction(u8* buf)
|
||||
{
|
||||
u8 inst = buf[0];
|
||||
InstFormat fmt;
|
||||
bool matched_inst = false;
|
||||
// TODO: This might be a good time to learn how to make a hashtable in C
|
||||
for (u16 i = 0; i < sizeof(inst_formats) / sizeof(InstFormat); i++)
|
||||
{
|
||||
if ((inst & ~inst_formats[i].mask_inst) == inst_formats[i].inst_enc)
|
||||
{
|
||||
fmt = inst_formats[i];
|
||||
matched_inst = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matched_inst)
|
||||
return (ParsedInstruction){.bytes_read = 0};
|
||||
u8_opt d_opt = none_u8();
|
||||
u8_opt s_opt = none_u8();
|
||||
u8_opt w_opt = none_u8();
|
||||
u8_opt reg_opt = none_u8();
|
||||
u8_opt mod_opt = none_u8();
|
||||
u8_opt rm_opt = none_u8();
|
||||
u16_opt data_opt = none_u16();
|
||||
u16_opt displacement_opt = none_u16();
|
||||
u8 is_data_addr = false;
|
||||
|
||||
u16 bytes_read = 1;
|
||||
bytes_read += fmt.has_operands ? 1 : 0;
|
||||
|
||||
if (fmt.has_d) d_opt = some_u8((inst & 0x2) >> 1);
|
||||
if (fmt.has_s) s_opt = some_u8((inst & 0x2) >> 1);
|
||||
if (fmt.has_rm) rm_opt = some_u8(buf[1] & 0x7);
|
||||
if (fmt.has_mod) mod_opt = some_u8((buf[1] & 0b11000000) >> 6);
|
||||
if (fmt.has_w) w_opt = some_u8(mask_and_shift(buf[0], fmt.mask_w));
|
||||
if (fmt.parse_reg.tag == P_REG_MASK)
|
||||
{
|
||||
u8 reg = fmt.has_operands ? buf[1] : buf[0];
|
||||
reg_opt = some_u8(mask_and_shift(reg, fmt.parse_reg.mask));
|
||||
}
|
||||
else if (fmt.parse_reg.tag == P_REG_FIXED)
|
||||
{
|
||||
reg_opt = some_u8(fmt.parse_reg.fixed);
|
||||
is_data_addr = true;
|
||||
}
|
||||
if (fmt.has_data)
|
||||
{
|
||||
u8 idx = 1;
|
||||
if (fmt.has_operands) idx += 1;
|
||||
// This is a trick because mod == 1 and mod == 2 will displace one and two bytes
|
||||
// respectively but mod == 3 wraps to 0 since it doesn't displace
|
||||
if (fmt.has_displacement) idx += mod_opt.value % 3;
|
||||
u16 data;
|
||||
if (fmt.has_s && s_opt.value == 1)
|
||||
{
|
||||
data = (sbyte)buf[idx];
|
||||
bytes_read += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
data = w_opt.value != 0 ? (i16)buf[idx+1] << 8 | buf[idx] : (sbyte)buf[idx];
|
||||
bytes_read += w_opt.value == 0 ? 1 : 2;
|
||||
}
|
||||
data_opt = some_u16(data);
|
||||
}
|
||||
if (fmt.has_displacement && mod_opt.value % 3 > 0)
|
||||
{
|
||||
u16 disp = mod_opt.value == MODE_MEM_DIS_16
|
||||
? (i16)buf[3] << 8 | buf[2]
|
||||
: (sbyte)buf[2];
|
||||
displacement_opt = some_u16(disp);
|
||||
bytes_read += mod_opt.value % 3;
|
||||
}
|
||||
else if (fmt.has_displacement && mod_opt.value == MODE_MEM_NO_DIS && rm_opt.value == 0x6)
|
||||
{
|
||||
displacement_opt = some_u16((i16)buf[3] << 8 | buf[2]);
|
||||
bytes_read += 2;
|
||||
}
|
||||
|
||||
return (ParsedInstruction) {
|
||||
.id = fmt.id,
|
||||
.name = fmt.name,
|
||||
.data = data_opt,
|
||||
.displacement = displacement_opt,
|
||||
.w = w_opt,
|
||||
.d = d_opt,
|
||||
.s = s_opt,
|
||||
.mod = mod_opt,
|
||||
.reg = reg_opt,
|
||||
.rm = rm_opt,
|
||||
.is_data_addr = is_data_addr,
|
||||
.bytes_read = bytes_read,
|
||||
};
|
||||
}
|
||||
|
||||
Instruction decode_instruction(ParsedInstruction inst)
|
||||
{
|
||||
Operand opr1 , opr2 = {0};
|
||||
i16 payload = 0;
|
||||
|
||||
IF_LET_SOME(u8, mod, inst.mod)
|
||||
{
|
||||
IF_LET_SOME(u8, reg, inst.reg)
|
||||
{
|
||||
opr1.tag = OPR_T_REGISTER;
|
||||
opr1.reg.value = registers[(size_t)reg];
|
||||
opr1.reg.wide = inst.w.value;
|
||||
}
|
||||
else
|
||||
{
|
||||
opr1.tag = OPR_T_IMMEDIATE;
|
||||
opr1.imm.value = inst.data.value;
|
||||
// TODO: This is dumb, we shouldn't do it this way
|
||||
if (inst.s.value == 1) opr1.imm.direct = 0;
|
||||
else opr1.imm.direct = inst.w.value + 1;
|
||||
}
|
||||
if (mod == MODE_RGSTR_MODE)
|
||||
{
|
||||
opr2.tag = OPR_T_REGISTER;
|
||||
opr2.reg.value = registers[(size_t)inst.rm.value];
|
||||
opr2.reg.wide = inst.w.value;
|
||||
}
|
||||
else if (mod == MODE_MEM_NO_DIS && inst.rm.value == 0x6)
|
||||
{
|
||||
opr2.tag = OPR_T_DIRADDR;
|
||||
opr2.dir_addr.value = inst.displacement.value;
|
||||
}
|
||||
else
|
||||
{
|
||||
opr2.tag = OPR_T_MEMORY;
|
||||
opr2.mem.eac_name = get_eac_register(inst.rm.value);
|
||||
opr2.mem.mode = mod;
|
||||
opr2.mem.displacement = (i16)inst.displacement.value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IF_LET_SOME(u16, data, inst.data)
|
||||
{
|
||||
if (inst.is_data_addr)
|
||||
{
|
||||
opr1.tag = OPR_T_DIRADDR;
|
||||
opr1.dir_addr.value = (i16)data;
|
||||
}
|
||||
else
|
||||
{
|
||||
opr1.tag = OPR_T_IMMEDIATE;
|
||||
opr1.imm.value = (i16)data;
|
||||
opr1.imm.direct = 0;
|
||||
}
|
||||
}
|
||||
IF_LET_SOME(u8, reg, inst.reg)
|
||||
{
|
||||
opr2.tag = OPR_T_REGISTER;
|
||||
opr2.reg.value = registers[(size_t)reg];
|
||||
opr2.reg.wide = inst.w.value;
|
||||
}
|
||||
}
|
||||
if (inst.d.tag == SOME && inst.d.value == 1)
|
||||
{
|
||||
Operand temp = opr1;
|
||||
opr1 = opr2;
|
||||
opr2 = temp;
|
||||
}
|
||||
return (Instruction) {
|
||||
.id = inst.id,
|
||||
.data = payload,
|
||||
.operation = inst.name,
|
||||
.src_opr = opr1,
|
||||
.dst_opr = opr2,
|
||||
};
|
||||
}
|
||||
|
||||
void get_operand_string(char* str_buf, Operand oprnd)
|
||||
{
|
||||
if (oprnd.tag == OPR_T_REGISTER)
|
||||
{
|
||||
strcpy(str_buf, reg_name(oprnd.reg.value, oprnd.reg.wide));
|
||||
}
|
||||
else if (oprnd.tag == OPR_T_MEMORY)
|
||||
{
|
||||
char disp_str[16] = {'\0'};
|
||||
i16 disp = oprnd.mem.displacement;
|
||||
if (disp != 0)
|
||||
sprintf(disp_str, " %s %d", (disp > 0 ? "+" : "-"), abs(disp));
|
||||
sprintf(str_buf, "[%s%s]", oprnd.mem.eac_name, disp_str);
|
||||
}
|
||||
else if (oprnd.tag == OPR_T_IMMEDIATE)
|
||||
{
|
||||
char *size = "";
|
||||
if (oprnd.imm.direct > 0)
|
||||
size = oprnd.imm.direct == 1 ? "byte " : "word ";
|
||||
sprintf(str_buf, "%s%d", size, oprnd.imm.value);
|
||||
}
|
||||
else if (oprnd.tag == OPR_T_DIRADDR)
|
||||
{
|
||||
sprintf(str_buf, "[%d]", oprnd.dir_addr.value);
|
||||
}
|
||||
}
|
||||
|
||||
void get_instr_string(char* str_buf, Instruction inst)
|
||||
{
|
||||
char src_str[32], dst_str[32];
|
||||
get_operand_string(src_str, inst.src_opr);
|
||||
get_operand_string(dst_str, inst.dst_opr);
|
||||
sprintf(str_buf, "%s %s, %s", inst.operation, dst_str, src_str);
|
||||
}
|
||||
|
||||
char *memory[65536];
|
||||
// Keep this global for debugging purposes
|
||||
u16 inst_count = 1;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc < 2)
|
||||
{
|
||||
printf("Usage: Please provide assembled instructions as input\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (stat(argv[1], &st) == -1)
|
||||
{
|
||||
perror("Unable to get file size\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
unsigned char* buffer = malloc(st.st_size);
|
||||
if (!buffer)
|
||||
{
|
||||
perror("Unable to allocate memory for binary file");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
FILE *f = fopen(argv[1], "r");
|
||||
if (!f)
|
||||
{
|
||||
perror("fopen\n");
|
||||
free(buffer);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
size_t bytes_read = fread(buffer, sizeof(unsigned char), st.st_size, f);
|
||||
if (bytes_read != (size_t)st.st_size)
|
||||
{
|
||||
fprintf(stderr, "Read of binary file to memory incomplete.\n");
|
||||
free(buffer);
|
||||
fclose(f);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
printf("; Decoded 8086 Assembly Instructions\n\n");
|
||||
printf("bits 16\n\n");
|
||||
|
||||
char *inst_str_buf = malloc(sizeof(char) * 256);
|
||||
u32 bytes_processed = 0;
|
||||
while (bytes_processed < bytes_read)
|
||||
{
|
||||
ParsedInstruction parsed = parse_instruction(buffer + bytes_processed);
|
||||
|
||||
if (parsed.bytes_read > 0)
|
||||
{
|
||||
Instruction inst = decode_instruction(parsed);
|
||||
get_instr_string(inst_str_buf, inst);
|
||||
bytes_processed += parsed.bytes_read;
|
||||
// printf("%s %d/%ld", inst_str_buf, bytes_processed, bytes_read);
|
||||
printf("%s", inst_str_buf);
|
||||
int len = strlen(inst_str_buf);
|
||||
for (int i = 0; i < 32 - len; i++)
|
||||
printf(" ");
|
||||
printf("; %d, %d", inst_count++, inst.id);
|
||||
}
|
||||
else
|
||||
{
|
||||
bytes_processed += 1;
|
||||
fprintf(stderr, "___Unrecognized Instruction___");
|
||||
}
|
||||
// char inst = buffer[0];
|
||||
// if (mov_inst(f, buffer, inst)) goto handled;
|
||||
// if (add_inst(f, buffer, inst)) goto handled;
|
||||
// handled:
|
||||
printf("\n");
|
||||
}
|
||||
free(inst_str_buf);
|
||||
free(buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
#include "lib.h"
|
||||
|
||||
enum Mode
|
||||
{
|
||||
MODE_MEM_NO_DIS = 0b00,
|
||||
MODE_MEM_DIS_08 = 0b01,
|
||||
MODE_MEM_DIS_16 = 0b10,
|
||||
MODE_RGSTR_MODE = 0b11,
|
||||
};
|
||||
|
||||
typedef struct Register
|
||||
{
|
||||
char* fullname;
|
||||
char* bytename;
|
||||
union {
|
||||
struct {
|
||||
char low;
|
||||
char high;
|
||||
};
|
||||
u16 full;
|
||||
} value;
|
||||
u8 code;
|
||||
} Register;
|
||||
|
||||
Register registers[8] = {
|
||||
{.code = 0b000, .fullname = "ax", .bytename = "al"},
|
||||
{.code = 0b001, .fullname = "cx", .bytename = "cl"},
|
||||
{.code = 0b010, .fullname = "dx", .bytename = "dl"},
|
||||
{.code = 0b011, .fullname = "bx", .bytename = "bl"},
|
||||
{.code = 0b100, .fullname = "sp", .bytename = "ah"},
|
||||
{.code = 0b101, .fullname = "bp", .bytename = "ch"},
|
||||
{.code = 0b110, .fullname = "si", .bytename = "dh"},
|
||||
{.code = 0b111, .fullname = "di", .bytename = "bh"},
|
||||
};
|
||||
|
||||
enum OperandType {OPR_T_MEMORY, OPR_T_REGISTER, OPR_T_IMMEDIATE, OPR_T_DIRADDR};
|
||||
|
||||
typedef struct Operand
|
||||
{
|
||||
enum OperandType tag;
|
||||
union {
|
||||
struct Mem {
|
||||
char *eac_name;
|
||||
i16 displacement;
|
||||
u8 mode;
|
||||
} mem;
|
||||
struct Reg {
|
||||
Register value;
|
||||
bool wide;
|
||||
} reg;
|
||||
struct Imm {
|
||||
i16 value;
|
||||
u8 direct;
|
||||
} imm;
|
||||
struct DirAddr {
|
||||
i16 value;
|
||||
} dir_addr;
|
||||
};
|
||||
} Operand;
|
||||
|
||||
enum ParseRegType { P_REG_NONE, P_REG_MASK, P_REG_FIXED };
|
||||
typedef struct ParseReg
|
||||
{
|
||||
enum ParseRegType tag;
|
||||
union {
|
||||
u8 none;
|
||||
u8 mask;
|
||||
u8 fixed;
|
||||
};
|
||||
} ParseReg;
|
||||
|
||||
typedef struct InstFormat
|
||||
{
|
||||
u16 id;
|
||||
char *name;
|
||||
ParseReg parse_reg;
|
||||
u8 inst_enc;
|
||||
u8 mask_inst;
|
||||
u8 mask_w;
|
||||
bool has_operands;
|
||||
bool has_displacement;
|
||||
bool has_data;
|
||||
bool has_d;
|
||||
bool has_w;
|
||||
bool has_mod;
|
||||
bool has_rm;
|
||||
bool has_s;
|
||||
bool has_SR;
|
||||
} InstFormat;
|
||||
|
||||
typedef struct ParsedInstruction
|
||||
{
|
||||
u16 id;
|
||||
char *name;
|
||||
u16_opt data;
|
||||
u16_opt displacement;
|
||||
u8_opt w;
|
||||
u8_opt d;
|
||||
u8_opt s;
|
||||
u8_opt mod;
|
||||
u8_opt reg;
|
||||
u8_opt rm;
|
||||
u8_opt SR;
|
||||
u8 is_data_addr;
|
||||
u8 bytes_read;
|
||||
} ParsedInstruction;
|
||||
|
||||
typedef struct Instruction
|
||||
{
|
||||
Operand src_opr;
|
||||
Operand dst_opr;
|
||||
i16 data;
|
||||
char *operation;
|
||||
u16 id;
|
||||
} Instruction;
|
||||
|
||||
InstFormat inst_formats[] =
|
||||
{
|
||||
////////
|
||||
// MOV
|
||||
////////
|
||||
// Register/memory to/from register
|
||||
{.id=1, .name="mov", .inst_enc=0b10001000, .mask_inst=0x3, .mask_w=0x1,
|
||||
.has_operands=true, .has_displacement=true, .has_d=true, .has_w=true,
|
||||
.has_mod=true, .has_rm=true, .parse_reg={.tag = P_REG_MASK, .mask=0b00111000} },
|
||||
// Immediate to register/memory
|
||||
{.id=2, .name="mov", .inst_enc=0b11000110, .mask_inst=0x1, .mask_w=0x1, .has_operands=true,
|
||||
.has_displacement=true, .has_data=true, .has_w=true, .has_mod=true, .has_rm=true},
|
||||
// Immediate to register
|
||||
{.id=3, .name="mov", .inst_enc=0b10110000, .mask_inst=0xF, .mask_w=0x8,
|
||||
.parse_reg={.tag = P_REG_MASK, .mask=0b00000111}, .has_data=true, .has_w=true},
|
||||
// Memory to accumulator | Accumulator to memory using the `d` bit
|
||||
// even though the manual doesn't specify it
|
||||
{.id=4, .name="mov", .inst_enc=0b10100000, .mask_inst=0x3, .mask_w=0x1, .has_data=true,
|
||||
.has_w=true, .has_d=true, .parse_reg={.tag = P_REG_FIXED, .fixed=0x0}},
|
||||
// Register/memory to segment register and inverse using the `d` bit
|
||||
{.id=5, .name="mov", .inst_enc=0b10001100, .mask_inst=0x3, .has_SR=true, .has_d=true,
|
||||
.has_displacement=true, .has_mod=true, .has_rm=true},
|
||||
////////
|
||||
// ADD
|
||||
////////
|
||||
// Reg/memory with register or either
|
||||
{.id=6, .name="add", .inst_enc=0b00000000, .mask_inst=0x3, .has_displacement=true,
|
||||
.mask_w=0x1, .has_operands=true, .has_w=true, .has_d=true, .has_mod=true,
|
||||
.has_rm=true, .parse_reg={.tag = P_REG_MASK, .mask=0b00111000}},
|
||||
// Immediate to register/memory
|
||||
{.id=7, .name="add", .inst_enc=0b10000000, .mask_inst=0x3, .mask_w=0x1, .has_w=true,
|
||||
.has_s=true, .has_operands=true, .has_displacement=true,
|
||||
.has_data=true, .has_mod=true, .has_rm=true},
|
||||
{.id=8, .name="add", .inst_enc=0b00000100, .mask_inst=0x1, .mask_w=0x1,
|
||||
.has_data=true, .has_w=true, .parse_reg={.tag = P_REG_FIXED, .fixed=0x0}},
|
||||
};
|
||||
@@ -1,119 +0,0 @@
|
||||
package decoder_8086
|
||||
|
||||
import "core:os"
|
||||
import "core:fmt"
|
||||
|
||||
Register :: struct {
|
||||
fullname: string,
|
||||
bytename: string,
|
||||
value: struct #raw_union {
|
||||
using _: struct {
|
||||
low, high: byte,
|
||||
},
|
||||
full: u16,
|
||||
},
|
||||
code: u8,
|
||||
}
|
||||
|
||||
RegMemMode :: enum {
|
||||
Memory00 = 0b00,
|
||||
Memory08 = 0b01,
|
||||
Memory16 = 0b10,
|
||||
Register = 0b11,
|
||||
};
|
||||
|
||||
registers := [8]Register {
|
||||
{fullname = "ax", bytename = "al", code = 0b000},
|
||||
{fullname = "cx", bytename = "cl", code = 0b001},
|
||||
{fullname = "dx", bytename = "dl", code = 0b010},
|
||||
{fullname = "bx", bytename = "bl", code = 0b011},
|
||||
{fullname = "sp", bytename = "ah", code = 0b100},
|
||||
{fullname = "bp", bytename = "ch", code = 0b101},
|
||||
{fullname = "si", bytename = "dh", code = 0b110},
|
||||
{fullname = "di", bytename = "bh", code = 0b111},
|
||||
}
|
||||
|
||||
Instruction :: struct {
|
||||
mask: u8,
|
||||
encoding: u8,
|
||||
name: string,
|
||||
desc: string,
|
||||
}
|
||||
|
||||
instructions := [?]Instruction {
|
||||
{ mask = 0b11111100, encoding = 0b10001000, name = "mov", desc = "Register/memory to/from register" },
|
||||
{ mask = 0b11111110, encoding = 0b11000110, name = "mov", desc = "Immediate to register/memory" },
|
||||
{ mask = 0b11110000, encoding = 0b10110000, name = "mov", desc = "Immediate to register" },
|
||||
{ mask = 0b11111110, encoding = 0b10100000, name = "mov", desc = "Memory to accumulator" },
|
||||
{ mask = 0b11111110, encoding = 0b10100010, name = "mov", desc = "Accumulator to memory" },
|
||||
{ mask = 0b11111111, encoding = 0b10001110, name = "mov", desc = "Register/memory to segment register" },
|
||||
{ mask = 0b11111111, encoding = 0b10001100, name = "mov", desc = "Segment register to register/memory" },
|
||||
}
|
||||
|
||||
inst_map := make(map[u8]Instruction)
|
||||
|
||||
main :: proc() {
|
||||
ax := registers[0]
|
||||
ax.value.full = 52428
|
||||
f,err := os.open(len(os.args) > 1 ? os.args[1] : "./asm_files/01-02-39.bin")
|
||||
if err != os.ERROR_NONE {
|
||||
os.exit(1)
|
||||
}
|
||||
defer os.close(f)
|
||||
|
||||
data := make([]u8, 512)
|
||||
bytes_read, err2 := os.read(f, data)
|
||||
if err2 != nil {
|
||||
// ...
|
||||
os.exit(1)
|
||||
}
|
||||
|
||||
for inst in instructions {
|
||||
inst_map[inst.encoding] = inst
|
||||
}
|
||||
|
||||
if true {
|
||||
for i in 0..<bytes_read {
|
||||
fmt.printfln("%b", data[i])
|
||||
mask: u8 = 0b11111111
|
||||
for i in 0..=4 {
|
||||
encoding := data[i] & mask
|
||||
if inst, ok := inst_map[encoding]; ok {
|
||||
fmt.printfln("%b, %v", encoding, inst.desc)
|
||||
// fmt.println(inst.desc)
|
||||
break
|
||||
}
|
||||
mask <<= 1
|
||||
}
|
||||
// fmt.println("=============")
|
||||
}
|
||||
os.exit(0)
|
||||
}
|
||||
|
||||
read_next := false
|
||||
src_dst := true
|
||||
is_word := false
|
||||
prev :u8 = 0
|
||||
fmt.println("bits 16\n")
|
||||
for i in 0..<bytes_read {
|
||||
b := data[i]
|
||||
if read_next {
|
||||
mod := (b & 0b11000000) >> 6
|
||||
reg := (b & 0b00111000) >> 3
|
||||
rm := b & 0b00000111
|
||||
src_name := is_word ? registers[reg].fullname : registers[reg].bytename
|
||||
dst_name := is_word ? registers[rm].fullname : registers[rm].bytename
|
||||
fmt.printfln("mov %s, %s ;; %b %b", dst_name, src_name, prev, b)
|
||||
read_next = false
|
||||
continue
|
||||
}
|
||||
if b == 0b10001001 {
|
||||
read_next = true
|
||||
is_word = true
|
||||
} else if b == 0b10001000 {
|
||||
read_next = true
|
||||
is_word = false
|
||||
}
|
||||
prev = b
|
||||
}
|
||||
}
|
||||
+279
@@ -0,0 +1,279 @@
|
||||
package sim_8086
|
||||
|
||||
import "core:fmt"
|
||||
import "core:math"
|
||||
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) {
|
||||
op: Op
|
||||
interseg: bool
|
||||
if inst.opname == .TBD2 {
|
||||
switch inst.raw_data[1] & 0b00111000 >> 3 {
|
||||
case 0b000: op = .INC
|
||||
case 0b001: op = .DEC
|
||||
case 0b010: op = .CALL
|
||||
case 0b011: op = .CALL; interseg = true
|
||||
case 0b100: op = .JMP
|
||||
case 0b101: op = .JMP; interseg = true
|
||||
case 0b110: op = .PUSH
|
||||
}
|
||||
} else if inst.opname == .TBD5 {
|
||||
switch inst.raw_data[1] & 0b00111000 >> 3 {
|
||||
case 0b000: op = .TEST
|
||||
case 0b001: op = .DEC
|
||||
case 0b010: op = .NOT
|
||||
case 0b011: op = .NEG
|
||||
case 0b100: op = .MUL
|
||||
case 0b101: op = .IMUL
|
||||
case 0b110: op = .DIV
|
||||
case 0b111: op = .IDIV
|
||||
}
|
||||
} else if inst.opname == .TBD6 {
|
||||
switch inst.raw_data[1] & 0b00111000 >> 3 {
|
||||
case 0b000: op = .ROL
|
||||
case 0b001: op = .ROR
|
||||
case 0b010: op = .RCL
|
||||
case 0b011: op = .RCR
|
||||
case 0b100: op = .SHL
|
||||
case 0b101: op = .SHR
|
||||
case 0b111: op = .SAR
|
||||
}
|
||||
} else if inst.opname == .TBD1 || inst.opname == .TBD3 || inst.opname == .TBD4 {
|
||||
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: op = .ADD
|
||||
case 0b001: op = .OR
|
||||
case 0b010: op = .ADC
|
||||
case 0b011: op = .SBB
|
||||
case 0b100: op = .AND
|
||||
case 0b101: op = .SUB
|
||||
case 0b110: op = .XOR
|
||||
case 0b111: op = .CMP
|
||||
}
|
||||
} else {
|
||||
op = inst.opname
|
||||
}
|
||||
return op, interseg
|
||||
}
|
||||
|
||||
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{}
|
||||
switch opinfo {
|
||||
case .None:
|
||||
case .Register, .SegmentRegister:
|
||||
reg: u8
|
||||
switch inst.reg_info {
|
||||
case .None: panic("Register is required but the encoded location is not provided")
|
||||
case .FirstByteLast3: reg = data[0] & 0b111
|
||||
case .FirstByteMiddle3: reg = (data[0] >> 3) & 0b111
|
||||
case .SecondByteMiddle3: reg = (data[1] >> 3) & 0b111
|
||||
case .SecondByteLast3: reg = data[1] & 0b111
|
||||
}
|
||||
if opinfo == .SegmentRegister {
|
||||
operand = (RegisterId){name = Register(SEGMENT_REGISTER_START + (int)(reg)), access = .Full}
|
||||
} else if word {
|
||||
operand = RegisterId { name = Register(reg), access = .Full }
|
||||
} else {
|
||||
operand = RegisterId { name = Register(reg % 4), access = reg < 4 ? .Low : .High }
|
||||
}
|
||||
case .RegisterMemory:
|
||||
mod := data[1] >> 6
|
||||
rm := data[1] & 0b111
|
||||
processed^ += 1
|
||||
op: Operand
|
||||
if mod == 0 {
|
||||
if rm == 0b110 {
|
||||
// op = DirectAddress { value = get_i16(data[2:]) }
|
||||
op = (DirectAddress)(get_i16(data[2:]))
|
||||
processed^ += 2
|
||||
} else {
|
||||
op = MemoryAddr{ addr_id = rm }
|
||||
}
|
||||
} else if mod == 1 {
|
||||
// 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
|
||||
} else if mod == 2 {
|
||||
op = MemoryAddr{ addr_id = rm , displacement = get_i16(data[2:]) }
|
||||
processed^ += 2
|
||||
} else if mod == 3 {
|
||||
if word {
|
||||
op = RegisterId { name = Register(rm), access = .Full }
|
||||
} else {
|
||||
op = RegisterId { name = Register(rm % 4), access = rm < 4 ? .Low : .High }
|
||||
}
|
||||
}
|
||||
operand = op
|
||||
case .Immediate:
|
||||
data_idx := processed^
|
||||
word_signed := word
|
||||
if inst.has_sign_extension {
|
||||
word_signed &&= data[0] & 0b0000_0010 == 0
|
||||
}
|
||||
value: i16 = word_signed ? get_i16(data[data_idx:]) : i16(i8(data[data_idx]))
|
||||
operand = Immediate { value = value, size = word_signed ? ._16 : ._8 }
|
||||
processed^ += word_signed ? 2 : 1
|
||||
case .Accumulator:
|
||||
operand = RegisterId { name = .ax, access = word ? .Full : .Low }
|
||||
case .DirectAddress:
|
||||
// operand = DirectAddress { value = get_i16(data[1:]) }
|
||||
operand = (DirectAddress)(get_i16(data[1:]))
|
||||
processed^ += 2
|
||||
case .Jump:
|
||||
processed^ += 1
|
||||
// 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)
|
||||
case .VariablePort:
|
||||
// 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:
|
||||
v_flag := data[0] & 0b10 != 0
|
||||
operand = v_flag ? RegisterId { name = .cx, access = .Low } : Immediate { value = 1 }
|
||||
case .Repeat:
|
||||
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
|
||||
case .DirectWithinSegment:
|
||||
value := (int)(get_i16(data[1:])) + total_bytes_processed + 3
|
||||
operand = Immediate { value = i16(value), size = ._16 }
|
||||
processed^ += 2
|
||||
case .Intersegment:
|
||||
operand = Intersegment {
|
||||
ip = get_i16(data[1:]),
|
||||
cs = get_i16(data[3:]),
|
||||
}
|
||||
processed^ += 4
|
||||
}
|
||||
return operand
|
||||
}
|
||||
|
||||
decode_data :: proc(inst_list: ^[dynamic]Instruction, data: []u8, bytes_to_read: int) -> int {
|
||||
idx := 0
|
||||
has_segment: Maybe(RegisterId)
|
||||
has_lock: bool
|
||||
total_bytes_processed: int
|
||||
for idx < bytes_to_read {
|
||||
instruction: Instruction
|
||||
processed := 1
|
||||
curr_byte := data[idx]
|
||||
|
||||
found_inst: bool
|
||||
inst: InstructionInfo
|
||||
for i in instructions {
|
||||
if i.encoding == (curr_byte & i.mask) {
|
||||
found_inst = true
|
||||
inst = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found_inst {
|
||||
instruction = {
|
||||
opname = .UNKNOWN,
|
||||
bytes_read = 1,
|
||||
raw_data = data[idx:idx+1],
|
||||
}
|
||||
append(inst_list, instruction)
|
||||
idx += 1
|
||||
continue
|
||||
}
|
||||
|
||||
// Here we check if the instruction affects the next instruction
|
||||
if inst.opname == .LOCK {
|
||||
has_lock = true
|
||||
idx += 1
|
||||
continue
|
||||
} else if inst.opname == .SEGMENT {
|
||||
reg := (curr_byte & 0b11000) >> 3
|
||||
has_segment = RegisterId { name = Register(SEGMENT_REGISTER_START+reg) }
|
||||
idx += 1
|
||||
continue
|
||||
} else if inst.opname == .AAM {
|
||||
processed += 1
|
||||
}
|
||||
|
||||
debug_str: string
|
||||
// NOTE: This is a special case because it matches the bit pattern of .TBD5,
|
||||
// but the instruction itself is different
|
||||
if inst.opname == .TBD5 && (data[idx] & 0xFF) == 0b11110110 && (data[idx+1] & 0b00111000) == 0 {
|
||||
inst = test_inst
|
||||
}
|
||||
|
||||
src_opr: Operand
|
||||
dst_opr: Operand
|
||||
|
||||
word: bool
|
||||
flip: bool
|
||||
op: Operand
|
||||
|
||||
if inst.has_flip {
|
||||
flip = curr_byte & 2 != 0
|
||||
}
|
||||
|
||||
#partial switch inst.word_size {
|
||||
case .LastBit: word = curr_byte & 1 == 1
|
||||
case .FourthBit: word = curr_byte & 0b0000_1000 != 0
|
||||
case .Always16: word = true
|
||||
}
|
||||
|
||||
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, total_bytes_processed, word, has_segment)
|
||||
|
||||
if flip {
|
||||
src_opr, dst_opr = dst_opr, src_opr
|
||||
}
|
||||
|
||||
processed += inst.consume_extra_bytes
|
||||
|
||||
instruction.src = src_opr
|
||||
instruction.dst = dst_opr
|
||||
instruction.is_word = word
|
||||
instruction.bytes_read = processed
|
||||
instruction.raw_data = data[idx:idx+processed]
|
||||
instruction.debug_msg = debug_str
|
||||
instruction.info = inst
|
||||
instruction.has_lock = has_lock
|
||||
instruction.has_segment = has_segment
|
||||
instruction.segment_offset += total_bytes_processed
|
||||
instruction.opname = inst.opname
|
||||
instruction.opname,instruction.indirect_intersegment = get_op(instruction)
|
||||
|
||||
// fmt.println(parsed_inst)
|
||||
append(inst_list, instruction)
|
||||
|
||||
idx += processed
|
||||
|
||||
has_lock = false
|
||||
has_segment = nil
|
||||
total_bytes_processed = idx
|
||||
}
|
||||
return total_bytes_processed
|
||||
}
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
package sim_8086
|
||||
|
||||
import "core:os"
|
||||
import "core:fmt"
|
||||
import "core:math"
|
||||
import "core:strings"
|
||||
import "core:reflect"
|
||||
import "core:strconv"
|
||||
|
||||
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 {
|
||||
case Immediate:
|
||||
return i16(opr.value)
|
||||
case RegisterId:
|
||||
reg_val := cpu.registers[opr.name]
|
||||
switch opr.access {
|
||||
case .Low, .High:
|
||||
return i16(opr.access == .Low ? reg_val.low : reg_val.high)
|
||||
case .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
|
||||
}
|
||||
|
||||
check_flags :: proc(cpu: ^Cpu, flags: bit_set[Flag], dst: i16, src: i16, result: i16, is_add: bool) {
|
||||
if .ZF in flags do cpu.flags[.ZF] = result == 0
|
||||
if .SF in flags do cpu.flags[.SF] = (result >> 15) & 0x1 == 1
|
||||
if .CF in flags {
|
||||
cpu.flags[.CF] = is_add ? u32(dst) + u32(src) > 0xFFFF : src > dst
|
||||
}
|
||||
if .AF in flags {
|
||||
lhs, rhs := dst & 0xF, src & 0xF
|
||||
cpu.flags[.AF] = is_add ? lhs + rhs > 15 : lhs < rhs
|
||||
}
|
||||
if .PF in flags {
|
||||
val := result
|
||||
bit_count := 0
|
||||
val &= 0x00FF
|
||||
for val != 0 {
|
||||
if val & 0x1 == 1 {
|
||||
bit_count += 1
|
||||
}
|
||||
val >>= 1
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
package sim_8086
|
||||
|
||||
Op :: enum {
|
||||
UNKNOWN,
|
||||
TBD1,
|
||||
TBD2,
|
||||
TBD3,
|
||||
TBD4,
|
||||
TBD5,
|
||||
TBD6,
|
||||
MOV,
|
||||
PUSH,
|
||||
POP,
|
||||
XCHG,
|
||||
IN,
|
||||
OUT,
|
||||
XLAT,
|
||||
LEA,
|
||||
LDS,
|
||||
LES,
|
||||
LAHF,
|
||||
SAHF,
|
||||
PUSHF,
|
||||
POPF,
|
||||
ADD,
|
||||
ADC,
|
||||
INC,
|
||||
AAA,
|
||||
DAA,
|
||||
SUB,
|
||||
SBB,
|
||||
DEC,
|
||||
NEG,
|
||||
MUL,
|
||||
IMUL,
|
||||
CMP,
|
||||
AAS,
|
||||
DAS,
|
||||
AAM,
|
||||
DIV,
|
||||
IDIV,
|
||||
AAD,
|
||||
CBW,
|
||||
CWD,
|
||||
ROL,
|
||||
ROR,
|
||||
RCL,
|
||||
RCR,
|
||||
SHL,
|
||||
SHR,
|
||||
SAR,
|
||||
NOT,
|
||||
TEST,
|
||||
OR,
|
||||
AND,
|
||||
XOR,
|
||||
REP,
|
||||
RET,
|
||||
RETF,
|
||||
INT,
|
||||
INT3,
|
||||
INTO,
|
||||
IRET,
|
||||
CLC,
|
||||
CMC,
|
||||
STC,
|
||||
CLD,
|
||||
STD,
|
||||
CLI,
|
||||
STI,
|
||||
HLT,
|
||||
WAIT,
|
||||
ESC,
|
||||
LOCK,
|
||||
SEGMENT,
|
||||
CALL,
|
||||
JMP,
|
||||
JNZ,
|
||||
JNE,
|
||||
JNGE,
|
||||
JE,
|
||||
JZ,
|
||||
JL,
|
||||
JLE,
|
||||
JNG,
|
||||
JB,
|
||||
JNAE,
|
||||
JP,
|
||||
JPE,
|
||||
JNA,
|
||||
JBE,
|
||||
JO,
|
||||
JS,
|
||||
JNL,
|
||||
JGE,
|
||||
JNLE,
|
||||
JG,
|
||||
JNB,
|
||||
JAE,
|
||||
JNBE,
|
||||
JA,
|
||||
JNP,
|
||||
JPO,
|
||||
JNO,
|
||||
JNS,
|
||||
LOOP,
|
||||
LOOPZ,
|
||||
LOOPNZ,
|
||||
LOOPNE,
|
||||
JCXZ,
|
||||
}
|
||||
|
||||
// TODO: We should figure out another way of handling this case. The way we're doing it
|
||||
// isn't that great; we return a string with the instruction name, but ideally we have all
|
||||
// the instructions accounted for, because eventually we will need the final parsed
|
||||
// instruction to contain all the information related to it
|
||||
// test_inst := InstructionInfo {
|
||||
// opname = .NOT, desc = "", mask = 0b11111110, encoding = 0b11110110,
|
||||
// dst = .RegisterMemory, src = .Immediate, word_size = .LastBit
|
||||
// }
|
||||
test_inst := InstructionInfo {
|
||||
opname = .TEST, desc = "", mask = 0b11111110, encoding = 0b11110110,
|
||||
dst = .RegisterMemory, src = .Immediate, word_size = .LastBit
|
||||
}
|
||||
|
||||
instructions := [?]InstructionInfo {
|
||||
{ opname = .TBD1, desc = "Immediate to accumulator",
|
||||
mask = 0b11000110, encoding = 0b00000100, check_second_encoding = true,
|
||||
dst = .Accumulator, src = .Immediate,
|
||||
word_size = .LastBit, },
|
||||
{ opname = .TBD2, desc = "", check_second_encoding = true,
|
||||
mask = 0b11111110, encoding = 0b11111110,
|
||||
src = .RegisterMemory,
|
||||
word_size = .LastBit, },
|
||||
{ opname = .TBD3, desc = "", check_second_encoding = true,
|
||||
mask = 0b11000100, encoding = 0b00000000,
|
||||
dst = .RegisterMemory, src = .Register,
|
||||
word_size = .LastBit, reg_info = .SecondByteMiddle3, has_flip = true },
|
||||
{ opname = .TBD4, desc = "", check_second_encoding = true,
|
||||
mask = 0b11111100, encoding = 0b10000000,
|
||||
dst = .RegisterMemory, src = .Immediate,
|
||||
word_size = .LastBit, has_sign_extension = true },
|
||||
{ opname = .TBD5, desc = "", check_second_encoding = true,
|
||||
mask = 0b11111110, encoding = 0b11110110,
|
||||
// dst = .Immediate, src = .RegisterMemory, word_size = .LastBit, },
|
||||
src = .RegisterMemory, word_size = .LastBit, },
|
||||
{ opname = .TBD6, desc = "", check_second_encoding = true,
|
||||
mask = 0b11111100, encoding = 0b11010000,
|
||||
dst = .RegisterMemory, src = .ShiftRotate, word_size = .LastBit, },
|
||||
{ opname = .MOV, desc = "Register/memory to/from register",
|
||||
mask = 0b11111100, encoding = 0b10001000,
|
||||
dst = .RegisterMemory, src = .Register,
|
||||
word_size = .LastBit, reg_info = .SecondByteMiddle3, has_flip = true },
|
||||
{ opname = .MOV, desc = "Immediate to register/memory",
|
||||
mask = 0b11111110, encoding = 0b11000110,
|
||||
dst = .RegisterMemory, src = .Immediate,
|
||||
word_size = .LastBit, },
|
||||
{ opname = .MOV, desc = "Immediate to register",
|
||||
mask = 0b11110000, encoding = 0b10110000,
|
||||
dst = .Register, src = .Immediate,
|
||||
word_size = .FourthBit, reg_info = .FirstByteLast3 },
|
||||
{ opname = .MOV, desc = "Memory to accumulator",
|
||||
mask = 0b11111110, encoding = 0b10100000,
|
||||
dst = .Accumulator, src = .DirectAddress,
|
||||
word_size = .LastBit, },
|
||||
{ opname = .MOV, desc = "Accumulator to memory",
|
||||
mask = 0b11111110, encoding = 0b10100010,
|
||||
dst = .DirectAddress, src = .Accumulator,
|
||||
word_size = .LastBit, },
|
||||
// NOTE: There's another instruction but it seems like it just flips, and while not specified
|
||||
// it seems like it has an undocumented flip bit just like the others
|
||||
{ opname = .MOV, desc = "Register/memory to segment register",
|
||||
mask = 0b11111101, encoding = 0b10001100,
|
||||
dst = .RegisterMemory, src = .SegmentRegister,
|
||||
reg_info = .SecondByteMiddle3, word_size = .Always16, has_flip = true },
|
||||
{ opname = .PUSH, desc = "", mask = 0b11111000, encoding = 0b01010000,
|
||||
src = .Register, reg_info = .FirstByteLast3,
|
||||
word_size = .Always16, },
|
||||
{ opname = .PUSH, desc = "", mask = 0b11100111, encoding = 0b00000110,
|
||||
src = .SegmentRegister, reg_info = .FirstByteMiddle3,
|
||||
word_size = .Always16, },
|
||||
{ opname = .POP, desc = "", mask = 0b11111111, encoding = 0b10001111,
|
||||
src = .RegisterMemory, word_size = .Always16 },
|
||||
{ opname = .POP, desc = "", mask = 0b11111000, encoding = 0b01011000,
|
||||
src = .Register, reg_info = .FirstByteLast3,
|
||||
word_size = .Always16, },
|
||||
{ opname = .POP, desc = "", mask = 0b11100111, encoding = 0b00000111,
|
||||
src = .SegmentRegister, reg_info = .FirstByteMiddle3,
|
||||
word_size = .Always16, },
|
||||
{ opname = .XCHG, desc = "", mask = 0b11111110, encoding = 0b10000110,
|
||||
dst = .RegisterMemory, src = .Register,
|
||||
reg_info = .SecondByteMiddle3, word_size = .LastBit, has_flip = true },
|
||||
{ opname = .XCHG, desc = "", mask = 0b11111000, encoding = 0b10010000,
|
||||
dst = .Accumulator, src = .Register,
|
||||
reg_info = .FirstByteLast3, has_flip = true, word_size = .Always16 },
|
||||
{ opname = .IN, desc = "", mask = 0b11111110, encoding = 0b11100100,
|
||||
dst = .Accumulator, src = .Immediate, word_size = .LastBit },
|
||||
{ opname = .IN, desc = "", mask = 0b11111110, encoding = 0b11101100,
|
||||
dst = .Accumulator, src = .VariablePort, word_size = .LastBit, },
|
||||
{ opname = .OUT, desc = "", mask = 0b11111110, encoding = 0b11100110,
|
||||
dst = .VariablePort, src = .Accumulator, word_size = .LastBit },
|
||||
{ opname = .OUT, desc = "", mask = 0b11111110, encoding = 0b11101110,
|
||||
dst = .VariablePort, src = .Accumulator, word_size = .LastBit, },
|
||||
{ opname = .TEST, desc = "", mask = 0b11111110, encoding = 0b10101000,
|
||||
dst = .Accumulator, src = .Immediate, word_size = .LastBit },
|
||||
{ opname = .XLAT, desc = "", mask = 0b11111111, encoding = 0b11010111,},
|
||||
{ opname = .LEA, desc = "", mask = 0b11111111, encoding = 0b10001101,
|
||||
dst = .Register, src = .RegisterMemory,
|
||||
reg_info = .SecondByteMiddle3, word_size = .Always16 },
|
||||
{ opname = .LDS, desc = "", mask = 0b11111111, encoding = 0b11000101,
|
||||
dst = .Register, src = .RegisterMemory,
|
||||
reg_info = .SecondByteMiddle3, word_size = .Always16 },
|
||||
{ opname = .LES, desc = "", mask = 0b11111111, encoding = 0b11000100,
|
||||
dst = .Register, src = .RegisterMemory,
|
||||
reg_info = .SecondByteMiddle3, word_size = .Always16 },
|
||||
{ opname = .LAHF, desc = "", mask = 0b11111111, encoding = 0b10011111,},
|
||||
{ opname = .SAHF, desc = "", mask = 0b11111111, encoding = 0b10011110,},
|
||||
{ opname = .PUSHF, desc = "", mask = 0b11111111, encoding = 0b10011100,},
|
||||
{ opname = .POPF, desc = "", mask = 0b11111111, encoding = 0b10011101,},
|
||||
{ opname = .INC, desc = "", mask = 0b11111000, encoding = 0b01000000,
|
||||
src = .Register, reg_info = .FirstByteLast3, word_size = .Always16 },
|
||||
{ opname = .AAA, desc = "", mask = 0b11111111, encoding = 0b00110111,},
|
||||
{ opname = .DAA, desc = "", mask = 0b11111111, encoding = 0b00100111,},
|
||||
{ opname = .DEC, desc = "", mask = 0b11111000, encoding = 0b01001000,
|
||||
src = .Register, reg_info = .FirstByteLast3, word_size = .Always16 },
|
||||
{ opname = .AAS, desc = "", mask = 0b11111111, encoding = 0b00111111,},
|
||||
{ opname = .DAS, desc = "", mask = 0b11111111, encoding = 0b00101111,},
|
||||
{ opname = .AAM, desc = "", mask = 0b11111111, encoding = 0b11010100,},
|
||||
{ opname = .AAD, desc = "", mask = 0b11111111, encoding = 0b11010101, consume_extra_bytes = 1 },
|
||||
{ opname = .CBW, desc = "", mask = 0b11111111, encoding = 0b10011000,},
|
||||
{ opname = .CWD, desc = "", mask = 0b11111111, encoding = 0b10011001,},
|
||||
{ opname = .TEST, desc = "", mask = 0b11111100, encoding = 0b10000100,
|
||||
dst = .RegisterMemory, src = .Register,
|
||||
word_size = .LastBit, reg_info = .SecondByteMiddle3, has_flip = true },
|
||||
{ opname = .REP, desc = "", mask = 0b11111110, encoding = 0b11110010, src = .Repeat },
|
||||
{ opname = .RET, desc = "", mask = 0b11111111, encoding = 0b11000011,},
|
||||
{ opname = .RET, src = .Immediate, word_size = .Always16,
|
||||
desc = "", mask = 0b11111111, encoding = 0b11000010,},
|
||||
{ opname = .RETF, desc = "", mask = 0b11111111, encoding = 0b11001011,},
|
||||
{ opname = .RETF, desc = "", mask = 0b11111111, encoding = 0b11001010,
|
||||
src = .Immediate, word_size = .Always16 },
|
||||
{ opname = .INT, src = .Immediate, desc = "", mask = 0b11111111, encoding = 0b11001101,},
|
||||
{ opname = .INT3, desc = "", mask = 0b11111111, encoding = 0b11001100,},
|
||||
{ opname = .INTO, desc = "", mask = 0b11111111, encoding = 0b11001110,},
|
||||
{ opname = .IRET, desc = "", mask = 0b11111111, encoding = 0b11001111,},
|
||||
{ opname = .CLC, desc = "", mask = 0b11111111, encoding = 0b11111000,},
|
||||
{ opname = .CMC, desc = "", mask = 0b11111111, encoding = 0b11110101,},
|
||||
{ opname = .STC, desc = "", mask = 0b11111111, encoding = 0b11111001,},
|
||||
{ opname = .CLD, desc = "", mask = 0b11111111, encoding = 0b11111100,},
|
||||
{ opname = .STD, desc = "", mask = 0b11111111, encoding = 0b11111101,},
|
||||
{ opname = .CLI, desc = "", mask = 0b11111111, encoding = 0b11111010,},
|
||||
{ opname = .STI, desc = "", mask = 0b11111111, encoding = 0b11111011,},
|
||||
{ opname = .HLT, desc = "", mask = 0b11111111, encoding = 0b11110100,},
|
||||
{ opname = .WAIT, desc = "", mask = 0b11111111, encoding = 0b10011011,},
|
||||
{ opname = .LOCK, desc = "", mask = 0b11111111, encoding = 0b11110000,},
|
||||
{ opname = .SEGMENT, desc = "", mask = 0b11100111, encoding = 0b00100110,},
|
||||
{ opname = .CALL, desc = "", mask = 0b11111111, encoding = 0b10011010, src = .Intersegment },
|
||||
{ opname = .JMP, desc = "", mask = 0b11111111, encoding = 0b11101010, src = .Intersegment },
|
||||
{ opname = .JMP, desc = "", mask = 0b11111111, encoding = 0b11101001, src = .DirectWithinSegment },
|
||||
{ opname = .CALL, desc = "", mask = 0b11111111, encoding = 0b11101000, src = .DirectWithinSegment },
|
||||
{ opname = .JE, mask = 0b11111111, encoding = 0b01110100, src = .Jump, desc = "Jump on not zero", },
|
||||
{ opname = .JZ, mask = 0b11111111, encoding = 0b01110100, src = .Jump, desc = "Jump on not zero", },
|
||||
|
||||
{ opname = .JL, mask = 0b11111111, encoding = 0b01111100, src = .Jump, desc = "Jump on not zero", },
|
||||
{ opname = .JNGE, mask = 0b11111111, encoding = 0b01111100, src = .Jump, desc = "Jump on not zero", },
|
||||
|
||||
{ opname = .JLE, mask = 0b11111111, encoding = 0b01111110, src = .Jump, desc = "Jump on not zero", },
|
||||
{ opname = .JNG, mask = 0b11111111, encoding = 0b01111110, src = .Jump, desc = "Jump on not zero", },
|
||||
|
||||
{ opname = .JB, mask = 0b11111111, encoding = 0b01110010, src = .Jump, desc = "Jump on not zero", },
|
||||
{ opname = .JNAE, mask = 0b11111111, encoding = 0b01110010, src = .Jump, desc = "Jump on not zero", },
|
||||
|
||||
{ opname = .JBE, mask = 0b11111111, encoding = 0b01110110, src = .Jump, desc = "Jump on not zero", },
|
||||
{ opname = .JNA, mask = 0b11111111, encoding = 0b01110110, src = .Jump, desc = "Jump on not zero", },
|
||||
|
||||
{ opname = .JP, mask = 0b11111111, encoding = 0b01111010, src = .Jump, desc = "Jump on not zero", },
|
||||
{ opname = .JPE, mask = 0b11111111, encoding = 0b01111010, src = .Jump, desc = "Jump on not zero", },
|
||||
|
||||
{ opname = .JO, mask = 0b11111111, encoding = 0b01110000, src = .Jump, desc = "Jump on not zero", },
|
||||
{ opname = .JS, mask = 0b11111111, encoding = 0b01111000, src = .Jump, desc = "Jump on not zero", },
|
||||
|
||||
{ opname = .JNE, mask = 0b11111111, encoding = 0b01110101, src = .Jump, desc = "Jump on not zero", },
|
||||
{ opname = .JNZ, mask = 0b11111111, encoding = 0b01110101, src = .Jump, desc = "Jump on not zero", },
|
||||
|
||||
{ opname = .JNL, mask = 0b11111111, encoding = 0b01111101, src = .Jump, desc = "Jump on not zero", },
|
||||
{ opname = .JGE, mask = 0b11111111, encoding = 0b01111101, src = .Jump, desc = "Jump on not zero", },
|
||||
|
||||
{ opname = .JNLE, mask = 0b11111111, encoding = 0b01111111, src = .Jump, desc = "Jump on not zero", },
|
||||
{ opname = .JG, mask = 0b11111111, encoding = 0b01111111, src = .Jump, desc = "Jump on not zero", },
|
||||
|
||||
{ opname = .JNB, mask = 0b11111111, encoding = 0b01110011, src = .Jump, desc = "Jump on not zero", },
|
||||
{ opname = .JAE, mask = 0b11111111, encoding = 0b01110011, src = .Jump, desc = "Jump on not zero", },
|
||||
|
||||
{ opname = .JNBE, mask = 0b11111111, encoding = 0b01110111, src = .Jump, desc = "Jump on not zero", },
|
||||
{ opname = .JA, mask = 0b11111111, encoding = 0b01110111, src = .Jump, desc = "Jump on not zero", },
|
||||
|
||||
{ opname = .JNP, mask = 0b11111111, encoding = 0b01111011, src = .Jump, desc = "Jump on not zero", },
|
||||
{ opname = .JPO, mask = 0b11111111, encoding = 0b01111011, src = .Jump, desc = "Jump on not zero", },
|
||||
|
||||
{ opname = .JNO, mask = 0b11111111, encoding = 0b01110001, src = .Jump, desc = "Jump on not zero", },
|
||||
|
||||
{ opname = .JNS, mask = 0b11111111, encoding = 0b01111001, src = .Jump, desc = "Jump on not zero", },
|
||||
{ opname = .LOOP, mask = 0b11111111, encoding = 0b11100010, src = .Jump, desc = "Jump on not zero", },
|
||||
{ opname = .LOOPZ, mask = 0b11111111, encoding = 0b11100001, src = .Jump, desc = "Jump on not zero", },
|
||||
{ opname = .LOOPNZ, mask = 0b11111111, encoding = 0b11100000, src = .Jump, desc = "Jump on not zero", },
|
||||
{ opname = .JCXZ, mask = 0b11111111, encoding = 0b11100011, src = .Jump, desc = "Jump on not zero", },
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef int16_t i16;
|
||||
typedef int32_t i32;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
typedef float f32;
|
||||
typedef double f64;
|
||||
typedef uintptr_t uptr;
|
||||
typedef char sbyte;
|
||||
typedef ptrdiff_t size;
|
||||
typedef size_t usize;
|
||||
|
||||
enum OptionTag {NONE, SOME};
|
||||
|
||||
#define OPTION(type) \
|
||||
typedef struct \
|
||||
{ \
|
||||
enum OptionTag tag; \
|
||||
union { \
|
||||
char none; \
|
||||
type value; \
|
||||
}; \
|
||||
} type##_opt; \
|
||||
\
|
||||
static inline type##_opt none_##type(void) \
|
||||
{ \
|
||||
return (type##_opt){ .tag = NONE, .none = 0 }; \
|
||||
} \
|
||||
\
|
||||
static inline type##_opt some_##type(type value) \
|
||||
{ \
|
||||
return (type##_opt){ .tag = SOME, .value = value }; \
|
||||
} \
|
||||
\
|
||||
static inline int get_some_##type(type##_opt opt, type* out_value) \
|
||||
{ \
|
||||
if (opt.tag != SOME) return 0; \
|
||||
*out_value = opt.value; \
|
||||
return 1; \
|
||||
}
|
||||
|
||||
#define IF_LET_SOME(type, var, opt) \
|
||||
type var; \
|
||||
if (get_some_##type(opt, &var))
|
||||
|
||||
|
||||
OPTION(u8)
|
||||
OPTION(u16)
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
package sim_8086
|
||||
|
||||
import "core:fmt"
|
||||
import "core:math"
|
||||
import "core:strings"
|
||||
import "core:reflect"
|
||||
|
||||
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 {
|
||||
val: string
|
||||
switch r_m {
|
||||
case 0b000:
|
||||
val = "bx + si"
|
||||
case 0b001:
|
||||
val = "bx + di"
|
||||
case 0b010:
|
||||
val = "bp + si"
|
||||
case 0b011:
|
||||
val = "bp + di"
|
||||
case 0b100:
|
||||
val = "si"
|
||||
case 0b101:
|
||||
val = "di"
|
||||
case 0b110:
|
||||
val = "bp"
|
||||
case 0b111:
|
||||
val = "bx"
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
get_memory_string :: proc(memoryAddr: MemoryAddr, has_segment: Maybe(RegisterId)) -> string {
|
||||
disp: string
|
||||
value := memoryAddr.displacement
|
||||
if value != 0 {
|
||||
disp = fmt.aprintf(" %s %d", value > 0 ? "+" : "-", math.abs(value))
|
||||
}
|
||||
seg_string: string
|
||||
if segreg, ok := has_segment.?; ok {
|
||||
seg_string = fmt.aprintf("%s:", get_register_name(segreg))
|
||||
}
|
||||
text := fmt.aprintf("%s[%s%s]", seg_string, calculate_effective_address(memoryAddr.addr_id), disp)
|
||||
return text
|
||||
}
|
||||
|
||||
get_register_name :: proc(reg_id: RegisterId) -> string {
|
||||
low_names := [?]string{"al", "cl", "dl", "bl"}
|
||||
high_names := [?]string{"ah", "ch", "dh", "bh"}
|
||||
switch reg_id.access {
|
||||
case .Full: return reflect.enum_string(reg_id.name)
|
||||
case .Low: return low_names[int(reg_id.name)]
|
||||
case .High: return high_names[int(reg_id.name) % 4]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
get_operand_string :: proc(operand: Operand, has_segment: Maybe(RegisterId)) -> string {
|
||||
string_val: string
|
||||
switch val in operand {
|
||||
case None:
|
||||
string_val = ""
|
||||
case RegisterId:
|
||||
string_val = get_register_name(val)
|
||||
case Immediate:
|
||||
string_val = fmt.aprintf("%d", val.value)
|
||||
case MemoryAddr:
|
||||
string_val = get_memory_string(val, has_segment)
|
||||
case DirectAddress:
|
||||
seg_string: string
|
||||
if segreg, ok := has_segment.?; ok {
|
||||
seg_string = fmt.aprintf("%s:", get_register_name(segreg))
|
||||
}
|
||||
string_val = fmt.aprintf("%s[%d]", seg_string, val)
|
||||
case Jump:
|
||||
string_val = fmt.aprintf("$%s%d", val >= 0 ? "+" : "", val)
|
||||
case Repeat:
|
||||
string_val = (string)(val)
|
||||
case Intersegment:
|
||||
string_val = fmt.aprintf("%d:%d", val.cs, val.ip)
|
||||
}
|
||||
return string_val
|
||||
}
|
||||
|
||||
get_unknown_inst_string :: proc(inst: Instruction) -> string {
|
||||
print_at_end := false
|
||||
txt := "unknown instruction"
|
||||
line := fmt.aprintf("%s %*[1]s %8b", txt, RIGHT_ALIGN_AMOUNT - len(txt), ";;", inst.raw_data[0])
|
||||
return line
|
||||
}
|
||||
|
||||
get_instruction_string :: proc(inst_info: InstructionInfo, instruction: Instruction) {
|
||||
inst := instruction
|
||||
src_is_imm := operand_is(Immediate, inst.src)
|
||||
dst_is_bracketed := operand_is(MemoryAddr, inst.dst) || operand_is(DirectAddress, inst.dst)
|
||||
src_is_bracketed := operand_is(MemoryAddr, inst.src) || operand_is(DirectAddress, inst.src)
|
||||
shiftrot := inst_info.opname == .TBD6
|
||||
size_string := ""
|
||||
if ((src_is_imm && dst_is_bracketed) || (dst_is_bracketed && shiftrot)) || (src_is_bracketed && operand_is(None, inst.dst)) {
|
||||
size_string = inst.is_word ? "word " : "byte "
|
||||
}
|
||||
|
||||
if inst.has_lock {
|
||||
fmt.sbprint(&instruction_builder, "lock ")
|
||||
}
|
||||
|
||||
dst_str := get_operand_string(inst.dst, inst.has_segment)
|
||||
src_str := get_operand_string(inst.src, inst.has_segment)
|
||||
opname: string
|
||||
is_interseg: bool
|
||||
|
||||
opname = strings.to_lower(reflect.enum_string(inst.opname))
|
||||
|
||||
if dst_str == "" {
|
||||
interseg_string: string
|
||||
if instruction.indirect_intersegment {
|
||||
interseg_string = " far"
|
||||
}
|
||||
fmt.sbprintf(&instruction_builder, "%s%s %s%s", opname, interseg_string, 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
|
||||
b_len := strings.builder_len(instruction_builder)
|
||||
fmt.sbprintf(&instruction_builder, "%*[0]s", RIGHT_ALIGN_AMOUNT - b_len, ";;")
|
||||
|
||||
if inst.has_lock {
|
||||
fmt.sbprintf(&instruction_builder, " lock")
|
||||
}
|
||||
if _,ok := inst.has_segment.?; ok {
|
||||
fmt.sbprintf(&instruction_builder, " segment")
|
||||
}
|
||||
for i in 0..<inst.bytes_read {
|
||||
fmt.sbprintf(&instruction_builder, " %08b", inst.raw_data[i])
|
||||
}
|
||||
}
|
||||
|
||||
print_instructions_stdout :: proc(instructions: []Instruction) {
|
||||
last_opname: [3]byte
|
||||
repeating_op_count := 0
|
||||
fmt.println("bits 16\n")
|
||||
for inst in instructions {
|
||||
strings.builder_reset(&instruction_builder)
|
||||
get_instruction_string(inst.info, inst)
|
||||
op2 := strings.to_string(instruction_builder)
|
||||
if op2[0:3] != string(last_opname[:]) {
|
||||
if repeating_op_count > 0 {
|
||||
fmt.println()
|
||||
}
|
||||
repeating_op_count = 0
|
||||
} else {
|
||||
repeating_op_count += 1
|
||||
}
|
||||
copy(last_opname[:], op2[0:3])
|
||||
fmt.println(op2)
|
||||
}
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
package sim_8086
|
||||
|
||||
import "core:os"
|
||||
import path "core:path/filepath"
|
||||
import "core:fmt"
|
||||
import "core:math"
|
||||
import "core:strings"
|
||||
import "core:reflect"
|
||||
import "core:strconv"
|
||||
|
||||
main :: proc() {
|
||||
f,err := os.open(os.args[1])
|
||||
if err != os.ERROR_NONE {
|
||||
fmt.eprintln("ERROR:", err)
|
||||
os.exit(1)
|
||||
}
|
||||
defer os.close(f)
|
||||
|
||||
what_to_print := len(os.args) >= 3 ? os.args[2] : ""
|
||||
|
||||
data := make([]u8, 1024)
|
||||
bytes_read, err2 := os.read(f, data)
|
||||
if err2 != nil {
|
||||
// ...
|
||||
os.exit(1)
|
||||
}
|
||||
|
||||
// asdf :u16 = 0b00000110_11011101
|
||||
// asdf2 :i16 = (i16)(asdf)
|
||||
// fmt.printfln("%d", asdf2)
|
||||
print_at_end := false
|
||||
line_count := 0
|
||||
instruction_list := make([dynamic]string, 0, 512)
|
||||
instructions_list := make([dynamic]Instruction, 0, 512)
|
||||
|
||||
total_bytes := decode_data(&instructions_list, data[:], bytes_read)
|
||||
inst_map := make(map[int]Instruction)
|
||||
for inst in instructions_list {
|
||||
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" {
|
||||
fmt.println("Registers")
|
||||
for reg_val,name in cpu.registers {
|
||||
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",
|
||||
full, 18 - len(full), "|", hex, 10 - len(hex), "|", reg_val.high, reg_val.low)
|
||||
fmt.println()
|
||||
}
|
||||
fmt.println("\nFlags")
|
||||
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" {
|
||||
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)
|
||||
}
|
||||
}
|
||||
+6
-1
@@ -6,9 +6,14 @@ NC='\033[0m'
|
||||
|
||||
make asm_files > /dev/null
|
||||
|
||||
if [ ! "$(command -v ./sim8086)" ]; then
|
||||
echo -e "\nError: 'sim8086' executable not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for ASM_BIN in asm_files/*.bin;
|
||||
do
|
||||
./decoder8086 "$ASM_BIN" > output.asm 2> /dev/null
|
||||
./sim8086 "$ASM_BIN" instructions > output.asm 2> /dev/null
|
||||
nasm output.asm -o output.bin 2> /dev/null
|
||||
ASM_FILE=${ASM_BIN%.*}.asm
|
||||
if [ ! -e output.bin ]; then
|
||||
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
make asm_files > /dev/null
|
||||
|
||||
if [ ! "$(command -v ./sim8086)" ]; then
|
||||
echo -e "\nError: 'sim8086' executable not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
odin build . -out:sim8086
|
||||
|
||||
for asm_txt in asm_files/*.txt;
|
||||
do
|
||||
asm_listing=$(basename $asm_txt .txt)
|
||||
output=$(./sim8086 asm_files/${asm_listing}.bin registers)
|
||||
if [ $? -eq 1 ]; then
|
||||
echo -e "Listing $asm_listing ${RED}Failed!${NC}"
|
||||
echo "$output" | while IFS= read -r line; do
|
||||
echo "$line"
|
||||
done
|
||||
echo -e ""
|
||||
else
|
||||
echo -e "Listing $asm_listing: ${GREEN}Succeded!${NC}"
|
||||
fi
|
||||
done
|
||||
@@ -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
|
||||
}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
package sim_8086
|
||||
|
||||
Register :: enum {
|
||||
ax,
|
||||
cx,
|
||||
dx,
|
||||
bx,
|
||||
sp,
|
||||
bp,
|
||||
si,
|
||||
di,
|
||||
es,
|
||||
cs,
|
||||
ss,
|
||||
ds,
|
||||
ip,
|
||||
}
|
||||
|
||||
RegisterValue :: struct #raw_union {
|
||||
using _: struct {
|
||||
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 {
|
||||
None,
|
||||
LastBit,
|
||||
FourthBit,
|
||||
Always8,
|
||||
Always16,
|
||||
}
|
||||
|
||||
None :: struct {}
|
||||
|
||||
RegisterAccess :: enum {
|
||||
Full,
|
||||
Low,
|
||||
High,
|
||||
}
|
||||
|
||||
RegisterId :: struct {
|
||||
name: Register,
|
||||
access: RegisterAccess,
|
||||
}
|
||||
ImmediateSize :: enum {
|
||||
_8,
|
||||
_16,
|
||||
}
|
||||
Immediate :: struct {
|
||||
value: i16,
|
||||
size: ImmediateSize,
|
||||
}
|
||||
MemoryAddr :: struct {
|
||||
addr_id: u8,
|
||||
displacement: i16,
|
||||
disp_size: ImmediateSize,
|
||||
}
|
||||
DirectAddress :: distinct i16
|
||||
Jump :: distinct i8
|
||||
Repeat :: string
|
||||
Intersegment :: struct {
|
||||
ip: i16,
|
||||
cs: i16,
|
||||
}
|
||||
|
||||
Operand :: union {
|
||||
None,
|
||||
RegisterId,
|
||||
Immediate,
|
||||
MemoryAddr,
|
||||
DirectAddress,
|
||||
Jump,
|
||||
Repeat,
|
||||
Intersegment,
|
||||
}
|
||||
|
||||
OperandInfo :: enum {
|
||||
None,
|
||||
Register,
|
||||
SegmentRegister,
|
||||
RegisterMemory,
|
||||
Immediate,
|
||||
Accumulator,
|
||||
DirectAddress,
|
||||
Jump,
|
||||
VariablePort,
|
||||
ShiftRotate,
|
||||
Repeat,
|
||||
DirectWithinSegment,
|
||||
Intersegment,
|
||||
}
|
||||
|
||||
RegisterEncodingBits :: enum {
|
||||
None,
|
||||
FirstByteLast3,
|
||||
SecondByteMiddle3,
|
||||
SecondByteLast3,
|
||||
FirstByteMiddle3,
|
||||
}
|
||||
|
||||
InstructionInfo :: struct {
|
||||
mask: u8,
|
||||
encoding: u8,
|
||||
opname: Op,
|
||||
desc: string,
|
||||
src: OperandInfo,
|
||||
dst: OperandInfo,
|
||||
flags: bit_set[Flag],
|
||||
word_size: WordSize,
|
||||
reg_info: RegisterEncodingBits,
|
||||
has_flip: bool,
|
||||
has_sign_extension: bool,
|
||||
check_second_encoding: bool,
|
||||
consume_extra_bytes: int,
|
||||
shift_rotate_flag: bool,
|
||||
}
|
||||
|
||||
Instruction :: struct {
|
||||
opname: Op,
|
||||
src: Operand,
|
||||
dst: Operand,
|
||||
info: InstructionInfo,
|
||||
is_word: bool,
|
||||
indirect_intersegment: bool,
|
||||
// TODO: This is trickier than I thought, it's more than just the one instruction
|
||||
// that uses it
|
||||
has_segment: Maybe(RegisterId),
|
||||
has_lock: bool,
|
||||
bytes_read: int,
|
||||
segment_offset: int,
|
||||
raw_data: []u8,
|
||||
debug_msg: string,
|
||||
}
|
||||
|
||||
DecodedInstructions :: struct {
|
||||
inst_list: [dynamic]Instruction,
|
||||
inst_map: map[int]Instruction,
|
||||
total_bytes_decoded: int
|
||||
}
|
||||
|
||||
Cpu :: struct {
|
||||
flags: [Flag]bool,
|
||||
registers: [Register]RegisterValue,
|
||||
memory: [dynamic]u8,
|
||||
}
|
||||
Reference in new issue
Block a user