Compare commits
41
Commits
6365bd73a0
...
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 |
No files matched your search
@@ -3,3 +3,6 @@
|
|||||||
/asm_files/*.bin
|
/asm_files/*.bin
|
||||||
/8086_family_Users_Manual_1_.pdf
|
/8086_family_Users_Manual_1_.pdf
|
||||||
/decoder8086
|
/decoder8086
|
||||||
|
/performance-aware
|
||||||
|
/sim8086
|
||||||
|
/listing-*.data
|
||||||
+94
-6
@@ -1,8 +1,96 @@
|
|||||||
#+OPTIONS: toc:nil
|
#+OPTIONS: toc:nil
|
||||||
|
|
||||||
* 8086 CPU Emulator
|
* 8086 CPU Simulator
|
||||||
Following along with [[https://www.computerenhance.com/p/table-of-contents][Performance Aware by Computer Enhance]]
|
|
||||||
* License
|
A complete 8086 processor emulator written in Odin, featuring instruction decoding, execution, and cycle-accurate simulation. Implements a comprehensive subset of the Intel 8086 instruction set with proper flag handling, memory management, and register operations.
|
||||||
This project is licensed under the terms of the MIT license. For more
|
|
||||||
information, see the included LICENSE file. However, some of this is related to
|
** Features
|
||||||
a paid course so maybe pay for the course if you want to use these files?
|
|
||||||
|
- *Complete 8086 instruction decoding* - Supports MOV, arithmetic, jumps, loops, stack operations, and more
|
||||||
|
- *Cycle-accurate execution* - Proper CPU state management with registers and flags
|
||||||
|
- *Memory simulation* - 1MB addressable memory space with effective address calculation
|
||||||
|
- *Comprehensive testing* - 21 assembly test programs with automated validation
|
||||||
|
- *Reference validation* - Compares execution results against reference CPU states
|
||||||
|
|
||||||
|
** Architecture
|
||||||
|
|
||||||
|
The simulator is organized into several focused modules:
|
||||||
|
|
||||||
|
- =sim8086.odin= - Main entry point and program orchestration
|
||||||
|
- =types.odin= - Core data structures (CPU state, instructions, operands)
|
||||||
|
- =instructions.odin= - Complete 8086 instruction set definitions
|
||||||
|
- =decoding.odin= - Binary instruction decoding and parsing
|
||||||
|
- =execution.odin= - CPU execution engine with flag calculations
|
||||||
|
- =printing.odin= - Assembly output formatting
|
||||||
|
- =testing.odin= - Reference state parsing and validation
|
||||||
|
|
||||||
|
** Building and Running
|
||||||
|
|
||||||
|
#+BEGIN_SRC bash
|
||||||
|
# Build the simulator
|
||||||
|
make
|
||||||
|
|
||||||
|
# Decode and print assembly
|
||||||
|
./sim8086 program.bin instructions
|
||||||
|
|
||||||
|
# Execute with register dump
|
||||||
|
./sim8086 program.bin registers
|
||||||
|
|
||||||
|
# Full execution with memory dump
|
||||||
|
./sim8086 program.bin all dump
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
** Testing
|
||||||
|
|
||||||
|
Comprehensive test coverage included:
|
||||||
|
|
||||||
|
#+BEGIN_SRC bash
|
||||||
|
# Run all assembly tests
|
||||||
|
./test_asm.sh
|
||||||
|
|
||||||
|
# Run reference CPU validation tests
|
||||||
|
./test_ref_cpu.sh
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
Test files cover:
|
||||||
|
- Basic MOV operations and addressing modes
|
||||||
|
- Arithmetic and logical operations with proper flag setting
|
||||||
|
- Jump and loop instructions
|
||||||
|
- Stack operations (PUSH/POP)
|
||||||
|
- Complex programs with multiple instruction types
|
||||||
|
|
||||||
|
** Technical Implementation
|
||||||
|
|
||||||
|
*** Instruction Decoding
|
||||||
|
- Bit-level parsing of 8086 machine code
|
||||||
|
- Support for all addressing modes (register, immediate, memory, direct)
|
||||||
|
- Proper handling of prefixes and multi-byte instructions
|
||||||
|
|
||||||
|
*** Execution Engine
|
||||||
|
- Register file with proper 8/16-bit access patterns
|
||||||
|
- CPU flags (Zero, Carry, Sign, Overflow, etc.) with accurate calculations
|
||||||
|
- Effective address computation for memory operations
|
||||||
|
- Instruction pointer management and control flow
|
||||||
|
|
||||||
|
*** Memory Model
|
||||||
|
- 1MB linear address space
|
||||||
|
- Little-endian byte ordering
|
||||||
|
- Memory-mapped I/O simulation
|
||||||
|
|
||||||
|
** Supported Instructions
|
||||||
|
|
||||||
|
*Data Movement:* MOV, PUSH, POP, XCHG, LEA, LDS, LES
|
||||||
|
*Arithmetic:* ADD, SUB, MUL, DIV, INC, DEC, CMP, NEG
|
||||||
|
*Logical:* AND, OR, XOR, NOT, TEST
|
||||||
|
*Shifts/Rotates:* SHL, SHR, SAR, ROL, ROR, RCL, RCR
|
||||||
|
*Control Flow:* JMP, JE, JNE, JL, JG, LOOP, CALL, RET
|
||||||
|
*String Operations:* MOVS, CMPS, SCAS, LODS, STOS
|
||||||
|
*System:* INT, IRET, HLT, NOP
|
||||||
|
|
||||||
|
Built following the [[https://www.computerenhance.com/p/table-of-contents][Performance Aware Programming]] course by Casey Muratori.
|
||||||
|
|
||||||
|
** License
|
||||||
|
|
||||||
|
This project is licensed under the MIT License - see the LICENSE file for details.
|
||||||
|
|
||||||
|
*Note:* Some test files are derived from the Performance Aware Programming course materials. Consider supporting the course if you find this project useful.
|
||||||
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,431 +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_ids(u8* buf)
|
|
||||||
{
|
|
||||||
u8 inst = buf[0];
|
|
||||||
InstFormat fmt = {0};
|
|
||||||
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_funcs) / (sizeof(inst_parser_f)*6*4); i++)
|
|
||||||
for (int j = 0; j < 6; j++)
|
|
||||||
for (int k = 0; k < 4 && inst_funcs[i][j][k] != NULL; k++)
|
|
||||||
{
|
|
||||||
printf("%p\n", (void*)(uptr)inst_funcs[i][j][k]);
|
|
||||||
// Apply inst_func_t
|
|
||||||
}
|
|
||||||
// for (int j = 0; j < 4 || ;)
|
|
||||||
return (ParsedInstruction){0};
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
ParsedInstruction parse_instruction(u8* buf)
|
|
||||||
{
|
|
||||||
u8 inst = buf[0];
|
|
||||||
InstFormat fmt = {0};
|
|
||||||
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 _ = parse_instruction_ids(buffer + bytes_processed);
|
|
||||||
(void)_;
|
|
||||||
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,223 +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;
|
|
||||||
|
|
||||||
enum InstructionIdentifier
|
|
||||||
{
|
|
||||||
_PREFIX_2,
|
|
||||||
_PREFIX_3,
|
|
||||||
_PREFIX_6,
|
|
||||||
_NAME,
|
|
||||||
_D,
|
|
||||||
_W,
|
|
||||||
_S,
|
|
||||||
_MOD,
|
|
||||||
_REGISTER,
|
|
||||||
_ACC,
|
|
||||||
_RM,
|
|
||||||
_DISP_LO,
|
|
||||||
_DISP_HI,
|
|
||||||
_DATA_W0,
|
|
||||||
_DATA_W1,
|
|
||||||
} InstructionIdentifier;
|
|
||||||
|
|
||||||
typedef struct ParsedInst
|
|
||||||
{
|
|
||||||
u64 progress;
|
|
||||||
u8 something;
|
|
||||||
} ParsedInst;
|
|
||||||
|
|
||||||
typedef ParsedInst (*inst_parser_f)(ParsedInst);
|
|
||||||
|
|
||||||
ParsedInst pre_2(ParsedInst pi) {return pi;}
|
|
||||||
ParsedInst pre_3(ParsedInst pi) {return pi;}
|
|
||||||
ParsedInst pre_6(ParsedInst pi) {return pi;}
|
|
||||||
ParsedInst name(ParsedInst pi) {return pi;}
|
|
||||||
ParsedInst reg(ParsedInst pi) {return pi;}
|
|
||||||
ParsedInst w(ParsedInst pi) {return pi;}
|
|
||||||
ParsedInst d(ParsedInst pi) {return pi;}
|
|
||||||
ParsedInst s(ParsedInst pi) {return pi;}
|
|
||||||
|
|
||||||
ParsedInst mod(ParsedInst pi) {return pi;}
|
|
||||||
ParsedInst inst(ParsedInst pi) {return pi;}
|
|
||||||
ParsedInst rm(ParsedInst pi) {return pi;}
|
|
||||||
|
|
||||||
ParsedInst disp_lo(ParsedInst pi) {return pi;}
|
|
||||||
ParsedInst disp_hi(ParsedInst pi) {return pi;}
|
|
||||||
|
|
||||||
ParsedInst data_w0(ParsedInst pi) {return pi;}
|
|
||||||
ParsedInst data_w1(ParsedInst pi) {return pi;}
|
|
||||||
|
|
||||||
inst_parser_f inst_funcs[][6][4] =
|
|
||||||
{
|
|
||||||
{{pre_2, name, d, w}, {mod, reg, rm}, {disp_lo}, {disp_hi}},
|
|
||||||
{{pre_6, s, w}, {mod, name, rm}, {disp_lo}, {disp_hi}, {data_w0}, {data_w1}},
|
|
||||||
{{pre_6, w}, {data_w0}, {data_w1}},
|
|
||||||
};
|
|
||||||
|
|
||||||
enum InstructionIdentifier inst_ids[][6][4] =
|
|
||||||
{
|
|
||||||
{{_PREFIX_2, _NAME, _D, _W}, {_MOD, _REGISTER, _RM}, {_DISP_LO}, {_DISP_HI}},
|
|
||||||
{{_PREFIX_6, _S, _W}, {_MOD, _NAME, _RM}, {_DISP_LO}, {_DISP_HI}, {_DATA_W0}, {_DATA_W1}},
|
|
||||||
{{_PREFIX_6, _W}, {_DATA_W0}, {_DATA_W1}},
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct InstructionParser
|
|
||||||
{
|
|
||||||
enum InstructionIdentifier inst_ids[6][4];
|
|
||||||
} InstructionParser;
|
|
||||||
|
|
||||||
// InstructionParser inst_formats[] =
|
|
||||||
// {
|
|
||||||
// {{{_PREFIX_2, _NAME, _D, _W}, {_MOD, _REGISTER, _RM}, {_DISP_LO}, {_DISP_HI}}},
|
|
||||||
// {{{_PREFIX_6, _S, _W}, {_MOD, _NAME, _RM}, {_DISP_LO}, {_DISP_HI}, {_DATA_W0}, {_DATA_W1}}},
|
|
||||||
// };
|
|
||||||
|
|
||||||
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,510 +0,0 @@
|
|||||||
package decoder_8086
|
|
||||||
|
|
||||||
import "core:os"
|
|
||||||
import "core:fmt"
|
|
||||||
import "core:math"
|
|
||||||
import "core:strings"
|
|
||||||
|
|
||||||
Register :: struct {
|
|
||||||
fullname: string,
|
|
||||||
bytename: string,
|
|
||||||
value: struct #raw_union {
|
|
||||||
using _: struct {
|
|
||||||
low, high: byte,
|
|
||||||
},
|
|
||||||
full: u16,
|
|
||||||
},
|
|
||||||
code: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
OpName :: enum {
|
|
||||||
TBD,
|
|
||||||
MOV,
|
|
||||||
ADD,
|
|
||||||
SUB,
|
|
||||||
CMP,
|
|
||||||
JMP,
|
|
||||||
JNZ,
|
|
||||||
JNGE,
|
|
||||||
JE,
|
|
||||||
JZ,
|
|
||||||
JL,
|
|
||||||
JLE,
|
|
||||||
JNG,
|
|
||||||
JB,
|
|
||||||
JNAE,
|
|
||||||
JP,
|
|
||||||
JPE,
|
|
||||||
JNA,
|
|
||||||
JBE,
|
|
||||||
JO,
|
|
||||||
JS,
|
|
||||||
JNE,
|
|
||||||
JNL,
|
|
||||||
JGE,
|
|
||||||
JNLE,
|
|
||||||
JG,
|
|
||||||
JNB,
|
|
||||||
JAE,
|
|
||||||
JNBE,
|
|
||||||
JA,
|
|
||||||
JNP,
|
|
||||||
JPO,
|
|
||||||
JNO,
|
|
||||||
JNS,
|
|
||||||
LOOP,
|
|
||||||
LOOPZ,
|
|
||||||
LOOPNZ,
|
|
||||||
JCXZ,
|
|
||||||
}
|
|
||||||
|
|
||||||
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},
|
|
||||||
}
|
|
||||||
|
|
||||||
RegInfo :: struct {
|
|
||||||
in_first_byte: bool,
|
|
||||||
shift_offset: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
OpCodeId :: enum {
|
|
||||||
None,
|
|
||||||
First,
|
|
||||||
Second,
|
|
||||||
}
|
|
||||||
|
|
||||||
LastBit :: struct{}
|
|
||||||
FourthBit :: struct{}
|
|
||||||
|
|
||||||
WordSize :: union {
|
|
||||||
None,
|
|
||||||
LastBit,
|
|
||||||
FourthBit,
|
|
||||||
}
|
|
||||||
|
|
||||||
InstructionInfo :: struct {
|
|
||||||
mask: u8,
|
|
||||||
encoding: u8,
|
|
||||||
opname: OpName,
|
|
||||||
desc: string,
|
|
||||||
opcode_id: OpCodeId,
|
|
||||||
word_size: WordSize,
|
|
||||||
reg_info: Maybe(RegInfo),
|
|
||||||
has_data: bool,
|
|
||||||
has_address: bool,
|
|
||||||
has_accumulator: bool,
|
|
||||||
has_segreg: bool,
|
|
||||||
has_flip: bool,
|
|
||||||
has_sign_extension: bool,
|
|
||||||
is_jump: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Maybe we can get rid of it since I don't have to specify the shift_offset,
|
|
||||||
// not like it changes a lot
|
|
||||||
reg_first_last := RegInfo{ in_first_byte = true, shift_offset = 0 }
|
|
||||||
reg_second_middle := RegInfo{ in_first_byte = false, shift_offset = 3 }
|
|
||||||
|
|
||||||
instructions := [?]InstructionInfo {
|
|
||||||
{ opname = .MOV, desc = "Register/memory to/from register", mask = 0b11111100, encoding = 0b10001000,
|
|
||||||
reg_info = reg_second_middle, has_address = true, word_size = LastBit{}, has_flip = true },
|
|
||||||
{ opname = .MOV, desc = "Immediate to register/memory", mask = 0b11111110, encoding = 0b11000110,
|
|
||||||
has_data = true, has_address = true, word_size = LastBit{}, },
|
|
||||||
{ opname = .MOV, desc = "Immediate to register", mask = 0b11110000, encoding = 0b10110000,
|
|
||||||
reg_info = reg_first_last, has_data = true, word_size = FourthBit{} },
|
|
||||||
{ opname = .MOV, desc = "Memory to accumulator", mask = 0b11111110, encoding = 0b10100000,
|
|
||||||
has_flip = true, word_size = LastBit{}, has_accumulator = true },
|
|
||||||
{ opname = .MOV, desc = "Accumulator to memory", mask = 0b11111110, encoding = 0b10100010,
|
|
||||||
has_flip = true, word_size = LastBit{}, has_accumulator = true },
|
|
||||||
{ opname = .MOV, desc = "Register/memory to segment register", mask = 0b11111111, encoding = 0b10001110,
|
|
||||||
has_segreg = true, has_address = true, word_size = None{} },
|
|
||||||
{ opname = .MOV, desc = "Segment register to register/memory", mask = 0b11111111, encoding = 0b10001100,
|
|
||||||
has_segreg = true, has_address = true, word_size = None{} },
|
|
||||||
{ opname = .TBD, desc = "Reg/memory with register to either", mask = 0b11000100, encoding = 0b00000000,
|
|
||||||
opcode_id = .First, reg_info = reg_second_middle, has_address = true, word_size = LastBit{}, has_flip = true },
|
|
||||||
{ opname = .TBD, desc = "Immediate to register/memory", mask = 0b11111100, encoding = 0b10000000,
|
|
||||||
opcode_id = .Second, has_data = true, has_address = true,
|
|
||||||
word_size = LastBit{}, has_sign_extension = true },
|
|
||||||
{ opname = .TBD, desc = "Immediate to accumulator", mask = 0b11000100, encoding = 0b00000100,
|
|
||||||
word_size = LastBit{}, has_data = true },
|
|
||||||
|
|
||||||
{ opname = .JE, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01110100, is_jump = true},
|
|
||||||
{ opname = .JZ, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01110100, is_jump = true},
|
|
||||||
|
|
||||||
{ opname = .JL, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01111100, is_jump = true},
|
|
||||||
{ opname = .JNGE, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01111100, is_jump = true},
|
|
||||||
|
|
||||||
{ opname = .JLE, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01111110, is_jump = true},
|
|
||||||
{ opname = .JNG, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01111110, is_jump = true},
|
|
||||||
|
|
||||||
{ opname = .JB, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01110010, is_jump = true},
|
|
||||||
{ opname = .JNAE, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01110010, is_jump = true},
|
|
||||||
|
|
||||||
{ opname = .JBE, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01110110, is_jump = true},
|
|
||||||
{ opname = .JNA, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01110110, is_jump = true},
|
|
||||||
|
|
||||||
{ opname = .JP, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01111010, is_jump = true},
|
|
||||||
{ opname = .JPE, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01111010, is_jump = true},
|
|
||||||
|
|
||||||
{ opname = .JO, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01110000, is_jump = true},
|
|
||||||
{ opname = .JS, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01111000, is_jump = true},
|
|
||||||
|
|
||||||
{ opname = .JNE, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01110101, is_jump = true},
|
|
||||||
{ opname = .JNZ, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01110101, is_jump = true},
|
|
||||||
|
|
||||||
{ opname = .JNL, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01111101, is_jump = true},
|
|
||||||
{ opname = .JGE, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01111101, is_jump = true},
|
|
||||||
|
|
||||||
{ opname = .JNLE, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01111111, is_jump = true},
|
|
||||||
{ opname = .JG, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01111111, is_jump = true},
|
|
||||||
|
|
||||||
{ opname = .JNB, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01110011, is_jump = true},
|
|
||||||
{ opname = .JAE, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01110011, is_jump = true},
|
|
||||||
|
|
||||||
{ opname = .JNBE, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01110111, is_jump = true},
|
|
||||||
{ opname = .JA, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01110111, is_jump = true},
|
|
||||||
|
|
||||||
{ opname = .JNP, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01111011, is_jump = true},
|
|
||||||
{ opname = .JPO, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01111011, is_jump = true},
|
|
||||||
|
|
||||||
{ opname = .JNO, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01110001, is_jump = true},
|
|
||||||
|
|
||||||
{ opname = .JNS, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b01111001, is_jump = true},
|
|
||||||
{ opname = .LOOP, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b11100010, is_jump = true},
|
|
||||||
{ opname = .LOOPZ, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b11100001, is_jump = true},
|
|
||||||
{ opname = .LOOPNZ, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b11100000, is_jump = true},
|
|
||||||
{ opname = .JCXZ, desc = "Jump on not zero", mask = 0b11111111, encoding = 0b11100011, is_jump = true},
|
|
||||||
}
|
|
||||||
|
|
||||||
None :: struct {}
|
|
||||||
|
|
||||||
Disp8 :: i8
|
|
||||||
Disp16 :: i16
|
|
||||||
Displacement :: union {
|
|
||||||
None,
|
|
||||||
Disp8,
|
|
||||||
Disp16
|
|
||||||
}
|
|
||||||
|
|
||||||
Value8 :: i8
|
|
||||||
Value16 :: i16
|
|
||||||
Data :: union {
|
|
||||||
None,
|
|
||||||
Value8,
|
|
||||||
Value16
|
|
||||||
}
|
|
||||||
|
|
||||||
ModMemory :: struct {}
|
|
||||||
Mod8BitDisp :: i8
|
|
||||||
Mod16BitDisp :: i16
|
|
||||||
ModRegister :: struct {}
|
|
||||||
|
|
||||||
ModMode :: union {
|
|
||||||
ModMemory,
|
|
||||||
Mod8BitDisp,
|
|
||||||
Mod16BitDisp,
|
|
||||||
ModRegister,
|
|
||||||
}
|
|
||||||
|
|
||||||
RegisterId :: distinct u8
|
|
||||||
Immediate8 :: distinct i8
|
|
||||||
Immediate16 :: distinct i16
|
|
||||||
MemoryAddr :: struct {
|
|
||||||
addr_id: u8,
|
|
||||||
displacement: Displacement
|
|
||||||
}
|
|
||||||
Accumulator :: distinct i16
|
|
||||||
OperandType :: union {
|
|
||||||
RegisterId,
|
|
||||||
Immediate8,
|
|
||||||
Immediate16,
|
|
||||||
MemoryAddr,
|
|
||||||
Accumulator,
|
|
||||||
}
|
|
||||||
|
|
||||||
inst_map := make(map[u8]InstructionInfo)
|
|
||||||
RIGHT_ALIGN_AMOUNT := 35
|
|
||||||
|
|
||||||
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) -> string {
|
|
||||||
disp: string
|
|
||||||
switch value in memoryAddr.displacement {
|
|
||||||
case None:
|
|
||||||
disp = ""
|
|
||||||
case Disp8:
|
|
||||||
if value != 0 {
|
|
||||||
disp = fmt.aprintf(" %s %d", value > 0 ? "+" : "-", math.abs(value))
|
|
||||||
}
|
|
||||||
case Disp16:
|
|
||||||
if value != 0 {
|
|
||||||
disp = fmt.aprintf(" %s %d", value > 0 ? "+" : "-", math.abs(value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
text := fmt.aprintf("[%s%s]", calculate_effective_address(memoryAddr.addr_id), disp)
|
|
||||||
return text
|
|
||||||
}
|
|
||||||
|
|
||||||
get_memory_type_string :: proc(mem_type: OperandType, is_word: bool) -> string {
|
|
||||||
string_val: string
|
|
||||||
switch val in mem_type {
|
|
||||||
case RegisterId:
|
|
||||||
string_val = is_word ? registers[val].fullname : registers[val].bytename
|
|
||||||
case Immediate8:
|
|
||||||
string_val = fmt.aprintf("%d", val)
|
|
||||||
case Immediate16:
|
|
||||||
string_val = fmt.aprintf("%d", val)
|
|
||||||
case MemoryAddr:
|
|
||||||
string_val = get_memory_string(val)
|
|
||||||
case Accumulator:
|
|
||||||
string_val = fmt.aprintf("[%d]", val)
|
|
||||||
}
|
|
||||||
return string_val
|
|
||||||
}
|
|
||||||
|
|
||||||
get_i16 :: proc(data: []u8) -> i16 {
|
|
||||||
return (i16)(data[1]) << 8 | (i16)(data[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
parse_displacement :: proc(data: []u8) -> (displacement: Displacement, disp_amount: int) {
|
|
||||||
mod := (data[0] & 0b11000000) >> 6
|
|
||||||
disp: Displacement = None{}
|
|
||||||
amount: int
|
|
||||||
switch mod {
|
|
||||||
case 1:
|
|
||||||
disp = (i8)(data[1])
|
|
||||||
amount = 1
|
|
||||||
case 2:
|
|
||||||
disp = get_i16(data[1:])
|
|
||||||
amount = 2
|
|
||||||
}
|
|
||||||
return disp, amount
|
|
||||||
}
|
|
||||||
|
|
||||||
get_displacement_string :: proc(displacement: Displacement) -> string {
|
|
||||||
disp := ""
|
|
||||||
#partial switch value in displacement {
|
|
||||||
case i8:
|
|
||||||
if value != 0 {
|
|
||||||
disp = fmt.aprintf(" %s %d", value > 0 ? "+" : "-", math.abs(value))
|
|
||||||
}
|
|
||||||
case i16:
|
|
||||||
if value != 0 {
|
|
||||||
disp = fmt.aprintf(" %s %d", value > 0 ? "+" : "-", math.abs(value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return disp
|
|
||||||
}
|
|
||||||
|
|
||||||
try_find_instruction :: proc(b: u8) -> (InstructionInfo, bool) {
|
|
||||||
for inst in instructions {
|
|
||||||
// fmt.print(inst.encoding, ",")
|
|
||||||
if inst.encoding == (b & inst.mask) {
|
|
||||||
return inst, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return InstructionInfo{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
get_opname :: proc(b: u8) -> string {
|
|
||||||
name: string
|
|
||||||
switch b & 0b00111000 >> 3 {
|
|
||||||
case 0b000: name = "add"
|
|
||||||
case 0b101: name = "sub"
|
|
||||||
case 0b111: name = "cmp"
|
|
||||||
}
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
|
||||||
main :: proc() {
|
|
||||||
// f,err := os.open(len(os.args) > 1 ? os.args[1] : "./asm_files/01-02-39.bin")
|
|
||||||
f,err := os.open(os.args[1])
|
|
||||||
if err != os.ERROR_NONE {
|
|
||||||
fmt.eprintln("ERROR:", err)
|
|
||||||
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 false {
|
|
||||||
os.exit(0)
|
|
||||||
}
|
|
||||||
// asdf :u16 = 0b00000011_11101000
|
|
||||||
// asdf2 :i16 = (i16)(asdf)
|
|
||||||
// fmt.printfln("%d", asdf2)
|
|
||||||
read_next := false
|
|
||||||
src_dst := true
|
|
||||||
idx := 0
|
|
||||||
added_label := false
|
|
||||||
line_count := 0
|
|
||||||
instruction_builder := strings.builder_make()
|
|
||||||
instruction_list := make([dynamic]string, 128)
|
|
||||||
for idx < bytes_read {
|
|
||||||
processed := 1
|
|
||||||
curr_byte := data[idx]
|
|
||||||
|
|
||||||
instruction, ok := try_find_instruction(curr_byte)
|
|
||||||
if !ok {
|
|
||||||
txt := "unknown instruction"
|
|
||||||
line := fmt.aprintf("%s %*[1]s %8b", txt, RIGHT_ALIGN_AMOUNT - len(txt), ";;", curr_byte)
|
|
||||||
instruction_list[line_count] = line
|
|
||||||
line_count += 1
|
|
||||||
idx += 1
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
lhs2: OperandType
|
|
||||||
rhs2: OperandType
|
|
||||||
is_word: bool
|
|
||||||
is_immediate := false
|
|
||||||
flip_dst := false
|
|
||||||
has_memory_addr := false
|
|
||||||
has_immediate := false
|
|
||||||
rm: u8
|
|
||||||
mod: u8
|
|
||||||
reg: u8
|
|
||||||
|
|
||||||
if instruction.has_flip {
|
|
||||||
flip_dst = curr_byte & 2 != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
switch val in instruction.word_size {
|
|
||||||
case LastBit: is_word = curr_byte & 1 == 1
|
|
||||||
case FourthBit: is_word = curr_byte & 0b0000_1000 != 0
|
|
||||||
case None:
|
|
||||||
}
|
|
||||||
|
|
||||||
if reg_info, ok := instruction.reg_info.(RegInfo); ok {
|
|
||||||
b := reg_info.in_first_byte ? data[idx] : data[idx+1]
|
|
||||||
reg = (b >> reg_info.shift_offset) & 0b111
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
data_idx := idx + 1
|
|
||||||
|
|
||||||
if instruction.has_address {
|
|
||||||
mod = data[idx+1] >> 6
|
|
||||||
rm = data[idx+1] & 0b00000111
|
|
||||||
|
|
||||||
data_idx += 1 + ((int)(mod) % 3)
|
|
||||||
processed += 1 + ((int)(mod) % 3)
|
|
||||||
|
|
||||||
if mod == 0 {
|
|
||||||
if rm == 0b110 {
|
|
||||||
lhs2 = (Accumulator)(get_i16(data[idx+2:]))
|
|
||||||
processed += 2
|
|
||||||
data_idx += 2
|
|
||||||
} else {
|
|
||||||
lhs2 = MemoryAddr{ addr_id = rm , displacement = None{} }
|
|
||||||
}
|
|
||||||
// NOTE: This also works when it's an Accumulator apparently
|
|
||||||
has_memory_addr = true
|
|
||||||
} else if mod == 1 {
|
|
||||||
lhs2 = MemoryAddr{ addr_id = rm , displacement = (i8)(data[idx+2]) }
|
|
||||||
has_memory_addr = true
|
|
||||||
} else if mod == 2 {
|
|
||||||
lhs2 = MemoryAddr{ addr_id = rm , displacement = get_i16(data[idx+2:]) }
|
|
||||||
has_memory_addr = true
|
|
||||||
} else if mod == 3 {
|
|
||||||
lhs2 = (RegisterId)(registers[rm].code)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
lhs2 = (RegisterId)(registers[reg].code)
|
|
||||||
}
|
|
||||||
if instruction.has_data {
|
|
||||||
word_signed := is_word
|
|
||||||
if instruction.has_sign_extension {
|
|
||||||
word_signed = is_word && curr_byte & 0b0000_0010 == 0
|
|
||||||
}
|
|
||||||
processed += word_signed ? 2 : 1
|
|
||||||
rhs2 = (OperandType)(word_signed ? (Immediate16)(get_i16(data[data_idx:])) : (Immediate8)(data[data_idx]))
|
|
||||||
has_immediate = true
|
|
||||||
} else if instruction.has_accumulator {
|
|
||||||
processed += is_word ? 2 : 1
|
|
||||||
rhs2 = (OperandType)(is_word ? (Accumulator)(get_i16(data[data_idx:])) : (Accumulator)(data[data_idx]))
|
|
||||||
} else {
|
|
||||||
rhs2 = (RegisterId)(reg)
|
|
||||||
}
|
|
||||||
|
|
||||||
if flip_dst {
|
|
||||||
lhs2, rhs2 = rhs2, lhs2
|
|
||||||
}
|
|
||||||
|
|
||||||
lhs := get_memory_type_string(lhs2, is_word)
|
|
||||||
rhs := get_memory_type_string(rhs2, is_word)
|
|
||||||
size_string := has_immediate && has_memory_addr ? is_word ? "word " : "byte " : ""
|
|
||||||
full_inst: string
|
|
||||||
opname: string
|
|
||||||
if instruction.opname == .TBD {
|
|
||||||
if instruction.opcode_id == .Second {
|
|
||||||
opname = strings.to_lower(fmt.aprintf("%s", get_opname(data[idx+1])))
|
|
||||||
} else {
|
|
||||||
opname = strings.to_lower(fmt.aprintf("%s", get_opname(curr_byte)))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
opname = strings.to_lower(fmt.aprintf("%s", instruction.opname))
|
|
||||||
}
|
|
||||||
if instruction.is_jump {
|
|
||||||
// NOTE: In order to mimic the label offset, you have to take the value you got and add two
|
|
||||||
value := (i8)(data[idx+1]) + 2
|
|
||||||
full_inst = fmt.aprintf("%s $%s%d ; %d", strings.to_lower(opname), value >= 0 ? "+" : "", value, value - 2)
|
|
||||||
processed += 1
|
|
||||||
} else {
|
|
||||||
opname = strings.to_lower(opname)
|
|
||||||
if opname == "mov" {
|
|
||||||
full_inst = fmt.aprintf("%s %s, %s%s", opname, lhs, size_string, rhs)
|
|
||||||
} else {
|
|
||||||
full_inst = fmt.aprintf("%s %s%s, %s", opname, size_string, lhs, rhs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.sbprintf(&instruction_builder, "%s %*[1]s", full_inst, RIGHT_ALIGN_AMOUNT - len(full_inst), ";;")
|
|
||||||
for i in 0..<processed {
|
|
||||||
fmt.sbprintf(&instruction_builder, " %08b", data[idx + i])
|
|
||||||
}
|
|
||||||
instruction_list[line_count] = strings.clone(strings.to_string(instruction_builder))
|
|
||||||
idx += processed
|
|
||||||
line_count += 1
|
|
||||||
strings.builder_reset(&instruction_builder)
|
|
||||||
}
|
|
||||||
fmt.println("bits 16\n")
|
|
||||||
for i in 0..<line_count {
|
|
||||||
fmt.println(instruction_list[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+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
|
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;
|
for ASM_BIN in asm_files/*.bin;
|
||||||
do
|
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
|
nasm output.asm -o output.bin 2> /dev/null
|
||||||
ASM_FILE=${ASM_BIN%.*}.asm
|
ASM_FILE=${ASM_BIN%.*}.asm
|
||||||
if [ ! -e output.bin ]; then
|
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