Compare commits

..
53 Commits
Author SHA1 Message Date
JosephFerano 51dae3128b Listings 0056, 0057. Improve README 2025-06-14 10:31:35 +07:00
JosephFerano 85a8172923 Add .LOOP instruction, dump memory to disk with cli arg 2025-03-23 08:58:29 +07:00
JosephFerano fafb440891 Listings 54 and 55 2025-03-23 08:58:12 +07:00
JosephFerano df105ea3cf Generalize with perform_load and perform_store, use bit_set for cpu flags 2025-03-22 18:54:36 +07:00
JosephFerano 831a307975 Fix and simplify (delete) all the signage stuff 2025-03-22 15:21:47 +07:00
JosephFerano ea7b65994b Fix IN and OUT instructions decoding 2025-03-22 15:20:48 +07:00
JosephFerano ab2b107e7e Listing 53 2025-03-22 15:09:00 +07:00
JosephFerano da78d875c1 Effective address movs work, but at what cost? 2025-03-21 20:47:42 +07:00
JosephFerano 3ca4059c44 Listing 52 2025-03-21 20:47:34 +07:00
JosephFerano 11ccfe78a9 Handle Memory movs, change displacement from union to enum 2025-03-21 20:11:39 +07:00
JosephFerano 948960e8f5 Listing 51 2025-03-21 20:11:20 +07:00
JosephFerano 0e90e2c23c Handle additional jumps for listing 50 2025-03-21 17:19:48 +07:00
JosephFerano 99fd7fabd7 Add inst decoded type for better modeling, handle jumps correctly with IP register 2025-03-21 16:33:58 +07:00
JosephFerano d0f91f15f8 Listings 48 and 49 2025-03-21 16:33:24 +07:00
JosephFerano 731e5bd45d CF, OF, and PF flags, keep values as i16, update ref test script 2025-03-20 20:22:12 +07:00
JosephFerano 5cf4768b80 Decode then check reference flags, change registers and flags to use enumerated arrays 2025-03-20 16:13:37 +07:00
JosephFerano cd5eada115 Listing 47 2025-03-20 16:09:44 +07:00
JosephFerano 08874c4533 Use an enum to represent registers, parse txt files, check CPU state against expected 2025-03-20 13:06:33 +07:00
JosephFerano 305ac557fa Move registers to CPU struct, start working on testing code 2025-03-20 09:44:41 +07:00
JosephFerano b511c6a620 Clean up/inline functions and clear out TODOs 2025-03-20 08:19:20 +07:00
JosephFerano 266f7a7900 Factor out get/set actions to reduce duplication, execute add,sub,cmp and check flags 2025-03-19 21:10:22 +07:00
JosephFerano 863ccfc583 Convert TBD instruction op names to the actual enum variants, rather than strings 2025-03-19 21:09:42 +07:00
JosephFerano fd8f696627 Listing 46 2025-03-19 21:07:11 +07:00
JosephFerano a91c3d9ba9 Clean up operand code by removing unnecessary variants 2025-03-19 18:57:34 +07:00
JosephFerano c312b7fd57 Fix register logic since we can access high and low parts, it's really ugly 2025-03-18 21:16:08 +07:00
JosephFerano b78d043b6c Add new listings, supply cs register, fix segreg mov instruction 2025-03-17 21:51:52 +07:00
JosephFerano 118ed482c1 Add listing 0044.txt, rm test files at the end of script 2025-03-16 23:55:26 +07:00
JosephFerano 6e7783c623 Bash script to compare expected output, print values in a way that's easy to test 2025-03-16 23:45:37 +07:00
JosephFerano 7e74d1e9a2 Print registers and execute basic mov instructions 2025-03-15 22:28:54 +07:00
JosephFerano 32ddb518e9 Break code up into files, delete C code, organize things better 2025-03-15 17:28:51 +07:00
JosephFerano 6909f75b35 Little bit more clean up 2025-03-13 21:59:37 +07:00
JosephFerano d4216d793a Listing 43/44 asm files 2025-03-13 17:50:58 +07:00
JosephFerano 3dca2254e1 Add helper proc to clean up the word/byte keyword 2025-03-13 17:50:32 +07:00
JosephFerano 460e1a8700 Little bit of clean up and organization 2025-03-13 12:46:25 +07:00
JosephFerano fc02debf65 Last bunch of instructions to get listing 42 fully read 2025-03-12 23:46:47 +07:00
JosephFerano babb07cb43 Segment register for memory addresses and lock string 2025-03-12 11:31:14 +07:00
JosephFerano 8967be44a0 Major refactor to define both operands so either can be empty 2025-03-12 11:07:40 +07:00
JosephFerano 08ef7d901b IN, OUT, XLAT, LEA, LDS, LES, ADC 2025-03-07 19:27:01 +07:00
JosephFerano 782ff93ace Handle accumulator differently, handle segment registers, handle unary, add instructions 2025-03-07 10:14:36 +07:00
JosephFerano 390fedc848 Add more instructions, add segment registers, move types up, read more data 2025-03-07 10:13:49 +07:00
JosephFerano c7ee622847 Rename listing files 2025-03-03 20:40:47 +07:00
JosephFerano 6365bd73a0 Encode jump offsets, collect instruction lines as strings, print at end 2025-02-24 20:03:18 +07:00
JosephFerano 1f9837b256 Change how we detect explicit word sizes, fix data_idx 2025-02-23 12:05:56 +07:00
JosephFerano c1799fcac2 Jump instructions and read opname from 3-bit pattern 2025-02-22 22:03:14 +07:00
JosephFerano 446848dffb Generalizing data and operand bracketing, to make add instrunction work 2025-02-21 23:35:24 +07:00
JosephFerano bc0a8b65eb New declarative style instruction definition, generalized parsing 2025-02-21 21:53:11 +07:00
JosephFerano f8f5744cd3 WIP: Separate parsing from printing, the easy way 2025-02-16 17:10:29 +07:00
JosephFerano fbedf7cf67 Process mov instruction properly, 01-02-39.asm now decoded properly 2025-02-14 23:08:45 +07:00
JosephFerano c5e9cfac44 Parse instruction function 2025-02-14 13:09:18 +07:00
JosephFerano 7c48c5ed90 Debug print encoding map 2025-01-21 22:46:31 +07:00
JosephFerano 444510cca2 RegMemMode enum, Instruction struct with 'mov' encodings, plus a map, read bytes 2025-01-21 22:45:49 +07:00
JosephFerano 86428f0263 Test w flag and print with the bytes in comments 2025-01-21 14:50:30 +07:00
JosephFerano f67f6de0b6 Update building and testing for new odin code 2025-01-21 14:50:03 +07:00
53 changed files with 56955 additions and 613 deletions

No files matched your search

+4
View File
@@ -2,3 +2,7 @@
/.idea/ /.idea/
/asm_files/*.bin /asm_files/*.bin
/8086_family_Users_Manual_1_.pdf /8086_family_Users_Manual_1_.pdf
/decoder8086
/performance-aware
/sim8086
/listing-*.data
+4 -4
View File
@@ -9,16 +9,16 @@ bin_files = $(patsubst %.asm,%.bin,$(asm_files))
all: decode asm_files all: decode asm_files
decode: decode.c decode.h lib.h decode: decoder8086.odin
$(CC) $(CFLAGS) $< -o $@ odin build decoder8086.odin -file
asm_files: $(bin_files) asm_files: $(bin_files)
%.bin: %.asm %.bin: %.asm
nasm $< -o $@ nasm $< -o $@
run: all run: all
./decode ./decode8086
clean: clean:
rm -vf *.so *.o decode $(bin_files) rm -vf *.so *.o decoder8086 $(bin_files)
+94 -6
View File
@@ -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.
View File
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+451
View File
@@ -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
;
+27
View File
@@ -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
+23
View File
@@ -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)
+32
View File
@@ -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
+27
View File
@@ -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)
+43
View File
@@ -0,0 +1,43 @@
; ========================================================================
;
; (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
;
; This software is provided 'as-is', without any express or implied
; warranty. In no event will the authors be held liable for any damages
; arising from the use of this software.
;
; Please see https://computerenhance.com for further information
;
; ========================================================================
; ========================================================================
; LISTING 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
+35
View File
@@ -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)
+28
View File
@@ -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
+24
View File
@@ -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
+36
View File
@@ -0,0 +1,36 @@
; ========================================================================
;
; (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
;
; This software is provided 'as-is', without any express or implied
; warranty. In no event will the authors be held liable for any damages
; arising from the use of this software.
;
; Please see https://computerenhance.com for further information
;
; ========================================================================
; ========================================================================
; LISTING 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
+22
View File
@@ -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
+23
View File
@@ -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
+12
View File
@@ -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
+24
View File
@@ -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
+17
View File
@@ -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
+38
View File
@@ -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
+37
View File
@@ -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
+30
View File
@@ -0,0 +1,30 @@
; ========================================================================
;
; (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
;
; This software is provided 'as-is', without any express or implied
; warranty. In no event will the authors be held liable for any damages
; arising from the use of this software.
;
; Please see https://computerenhance.com for further information
;
; ========================================================================
; ========================================================================
; LISTING 51
; ========================================================================
bits 16
mov word [1000], 1
mov word [1002], 2
mov word [1004], 3
mov word [1006], 4
mov bx, 1000
mov word [bx + 4], 10
mov bx, word [1000]
mov cx, word [1002]
mov dx, word [1004]
mov bp, word [1006]
+18
View File
@@ -0,0 +1,18 @@
--- test\listing_0051_memory_mov execution ---
mov word [+1000], 1 ; ip:0x0->0x6
mov word [+1002], 2 ; ip:0x6->0xc
mov word [+1004], 3 ; ip:0xc->0x12
mov word [+1006], 4 ; ip:0x12->0x18
mov bx, 1000 ; bx:0x0->0x3e8 ip:0x18->0x1b
mov word [bx+4], 10 ; ip:0x1b->0x20
mov bx, [+1000] ; bx:0x3e8->0x1 ip:0x20->0x24
mov cx, [+1002] ; cx:0x0->0x2 ip:0x24->0x28
mov dx, [+1004] ; dx:0x0->0xa ip:0x28->0x2c
mov bp, [+1006] ; bp:0x0->0x4 ip:0x2c->0x30
Final registers:
bx: 0x0001 (1)
cx: 0x0002 (2)
dx: 0x000a (10)
bp: 0x0004 (4)
ip: 0x0030 (48)
+36
View File
@@ -0,0 +1,36 @@
; ========================================================================
;
; (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
;
; This software is provided 'as-is', without any express or implied
; warranty. In no event will the authors be held liable for any damages
; arising from the use of this software.
;
; Please see https://computerenhance.com for further information
;
; ========================================================================
; ========================================================================
; LISTING 52
; ========================================================================
bits 16
mov dx, 6
mov bp, 1000
mov si, 0
init_loop_start:
mov word [bp + si], si
add si, 2
cmp si, dx
jnz init_loop_start
mov bx, 0
mov si, 0
add_loop_start:
mov cx, word [bp + si]
add bx, cx
add si, 2
cmp si, dx
jnz add_loop_start
+42
View File
@@ -0,0 +1,42 @@
--- test\listing_0052_memory_add_loop execution ---
mov dx, 6 ; dx:0x0->0x6 ip:0x0->0x3
mov bp, 1000 ; bp:0x0->0x3e8 ip:0x3->0x6
mov si, 0 ; ip:0x6->0x9
mov word [bp+si], si ; ip:0x9->0xb
add si, 2 ; si:0x0->0x2 ip:0xb->0xe
cmp si, dx ; ip:0xe->0x10 flags:->CPAS
jne $-7 ; ip:0x10->0x9
mov word [bp+si], si ; ip:0x9->0xb
add si, 2 ; si:0x2->0x4 ip:0xb->0xe flags:CPAS->
cmp si, dx ; ip:0xe->0x10 flags:->CAS
jne $-7 ; ip:0x10->0x9
mov word [bp+si], si ; ip:0x9->0xb
add si, 2 ; si:0x4->0x6 ip:0xb->0xe flags:CAS->P
cmp si, dx ; ip:0xe->0x10 flags:P->PZ
jne $-7 ; ip:0x10->0x12
mov bx, 0 ; ip:0x12->0x15
mov si, 0 ; si:0x6->0x0 ip:0x15->0x18
mov cx, [bp+si] ; ip:0x18->0x1a
add bx, cx ; ip:0x1a->0x1c
add si, 2 ; si:0x0->0x2 ip:0x1c->0x1f flags:PZ->
cmp si, dx ; ip:0x1f->0x21 flags:->CPAS
jne $-9 ; ip:0x21->0x18
mov cx, [bp+si] ; cx:0x0->0x2 ip:0x18->0x1a
add bx, cx ; bx:0x0->0x2 ip:0x1a->0x1c flags:CPAS->
add si, 2 ; si:0x2->0x4 ip:0x1c->0x1f
cmp si, dx ; ip:0x1f->0x21 flags:->CAS
jne $-9 ; ip:0x21->0x18
mov cx, [bp+si] ; cx:0x2->0x4 ip:0x18->0x1a
add bx, cx ; bx:0x2->0x6 ip:0x1a->0x1c flags:CAS->P
add si, 2 ; si:0x4->0x6 ip:0x1c->0x1f
cmp si, dx ; ip:0x1f->0x21 flags:P->PZ
jne $-9 ; ip:0x21->0x23
Final registers:
bx: 0x0006 (6)
cx: 0x0004 (4)
dx: 0x0006 (6)
bp: 0x03e8 (1000)
si: 0x0006 (6)
ip: 0x0023 (35)
flags: PZ
+35
View File
@@ -0,0 +1,35 @@
; ========================================================================
;
; (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
;
; This software is provided 'as-is', without any express or implied
; warranty. In no event will the authors be held liable for any damages
; arising from the use of this software.
;
; Please see https://computerenhance.com for further information
;
; ========================================================================
; ========================================================================
; LISTING 53
; ========================================================================
bits 16
mov dx, 6
mov bp, 1000
mov si, 0
init_loop_start:
mov word [bp + si], si
add si, 2
cmp si, dx
jnz init_loop_start
mov bx, 0
mov si, dx
sub bp, 2
add_loop_start:
add bx, word [bp + si]
sub si, 2
jnz add_loop_start
+35
View File
@@ -0,0 +1,35 @@
--- test\listing_0053_add_loop_challenge execution ---
mov dx, 6 ; dx:0x0->0x6 ip:0x0->0x3
mov bp, 1000 ; bp:0x0->0x3e8 ip:0x3->0x6
mov si, 0 ; ip:0x6->0x9
mov word [bp+si], si ; ip:0x9->0xb
add si, 2 ; si:0x0->0x2 ip:0xb->0xe
cmp si, dx ; ip:0xe->0x10 flags:->CPAS
jne $-7 ; ip:0x10->0x9
mov word [bp+si], si ; ip:0x9->0xb
add si, 2 ; si:0x2->0x4 ip:0xb->0xe flags:CPAS->
cmp si, dx ; ip:0xe->0x10 flags:->CAS
jne $-7 ; ip:0x10->0x9
mov word [bp+si], si ; ip:0x9->0xb
add si, 2 ; si:0x4->0x6 ip:0xb->0xe flags:CAS->P
cmp si, dx ; ip:0xe->0x10 flags:P->PZ
jne $-7 ; ip:0x10->0x12
mov bx, 0 ; ip:0x12->0x15
mov si, dx ; ip:0x15->0x17
sub bp, 2 ; bp:0x3e8->0x3e6 ip:0x17->0x1a flags:PZ->
add bx, [bp+si] ; bx:0x0->0x4 ip:0x1a->0x1c
sub si, 2 ; si:0x6->0x4 ip:0x1c->0x1f
jne $-5 ; ip:0x1f->0x1a
add bx, [bp+si] ; bx:0x4->0x6 ip:0x1a->0x1c flags:->P
sub si, 2 ; si:0x4->0x2 ip:0x1c->0x1f flags:P->
jne $-5 ; ip:0x1f->0x1a
add bx, [bp+si] ; ip:0x1a->0x1c flags:->P
sub si, 2 ; si:0x2->0x0 ip:0x1c->0x1f flags:P->PZ
jne $-5 ; ip:0x1f->0x21
Final registers:
bx: 0x0006 (6)
dx: 0x0006 (6)
bp: 0x03e6 (998)
ip: 0x0021 (33)
flags: PZ
+43
View File
@@ -0,0 +1,43 @@
; ========================================================================
;
; (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
;
; This software is provided 'as-is', without any express or implied
; warranty. In no event will the authors be held liable for any damages
; arising from the use of this software.
;
; Please see https://computerenhance.com for further information
;
; ========================================================================
; ========================================================================
; LISTING 54
; ========================================================================
bits 16
; Start image after one row, to avoid overwriting our code!
mov bp, 64*4
mov dx, 0
y_loop_start:
mov cx, 0
x_loop_start:
; Fill pixel
mov word [bp + 0], cx ; Red
mov word [bp + 2], dx ; Blue
mov byte [bp + 3], 255 ; Alpha
; Advance pixel location
add bp, 4
; Advance X coordinate and loop
add cx, 1
cmp cx, 64
jnz x_loop_start
; Advance Y coordinate and loop
add dx, 1
cmp dx, 64
jnz y_loop_start
File diff suppressed because it is too large. Load diff
+53
View File
@@ -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
File diff suppressed because it is too large. Load diff
+41
View File
@@ -0,0 +1,41 @@
; ========================================================================
;
; (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
;
; This software is provided 'as-is', without any express or implied
; warranty. In no event will the authors be held liable for any damages
; arising from the use of this software.
;
; Please see https://computerenhance.com for further information
;
; ========================================================================
; ========================================================================
; LISTING 56
; ========================================================================
bits 16
mov bx, 1000
mov bp, 2000
mov si, 3000
mov di, 4000
mov cx, bx
mov dx, 12
mov dx, [1000]
mov cx, [bx]
mov cx, [bp]
mov [si], cx
mov [di], cx
mov cx, [bx + 1000]
mov cx, [bp + 1000]
mov [si + 1000], cx
mov [di + 1000], cx
add cx, dx
add [di + 1000], cx
add dx, 50
+72
View File
@@ -0,0 +1,72 @@
**************
**** 8086 ****
**************
WARNING: Clocks reported by this utility are strictly from the 8086 manual.
They will be inaccurate, both because the manual clocks are estimates, and because
some of the entries in the manual look highly suspicious and are probably typos.
--- test\listing_0056_estimating_cycles execution ---
mov bx, 1000 ; Clocks: +4 = 4 | bx:0x0->0x3e8 ip:0x0->0x3
mov bp, 2000 ; Clocks: +4 = 8 | bp:0x0->0x7d0 ip:0x3->0x6
mov si, 3000 ; Clocks: +4 = 12 | si:0x0->0xbb8 ip:0x6->0x9
mov di, 4000 ; Clocks: +4 = 16 | di:0x0->0xfa0 ip:0x9->0xc
mov cx, bx ; Clocks: +2 = 18 | cx:0x0->0x3e8 ip:0xc->0xe
mov dx, 12 ; Clocks: +4 = 22 | dx:0x0->0xc ip:0xe->0x11
mov dx, [+1000] ; Clocks: +14 = 36 (8 + 6ea) | dx:0xc->0x0 ip:0x11->0x15
mov cx, [bx] ; Clocks: +13 = 49 (8 + 5ea) | cx:0x3e8->0x0 ip:0x15->0x17
mov cx, [bp] ; Clocks: +13 = 62 (8 + 5ea) | ip:0x17->0x1a
mov word [si], cx ; Clocks: +14 = 76 (9 + 5ea) | ip:0x1a->0x1c
mov word [di], cx ; Clocks: +14 = 90 (9 + 5ea) | ip:0x1c->0x1e
mov cx, [bx+1000] ; Clocks: +17 = 107 (8 + 9ea) | ip:0x1e->0x22
mov cx, [bp+1000] ; Clocks: +17 = 124 (8 + 9ea) | ip:0x22->0x26
mov word [si+1000], cx ; Clocks: +18 = 142 (9 + 9ea) | ip:0x26->0x2a
mov word [di+1000], cx ; Clocks: +18 = 160 (9 + 9ea) | ip:0x2a->0x2e
add cx, dx ; Clocks: +3 = 163 | ip:0x2e->0x30 flags:->PZ
add word [di+1000], cx ; Clocks: +25 = 188 (16 + 9ea) | ip:0x30->0x34
add dx, 50 ; Clocks: +4 = 192 | dx:0x0->0x32 ip:0x34->0x37 flags:PZ->
Final registers:
bx: 0x03e8 (1000)
dx: 0x0032 (50)
bp: 0x07d0 (2000)
si: 0x0bb8 (3000)
di: 0x0fa0 (4000)
ip: 0x0037 (55)
**************
**** 8088 ****
**************
WARNING: Clocks reported by this utility are strictly from the 8086 manual.
They will be inaccurate, both because the manual clocks are estimates, and because
some of the entries in the manual look highly suspicious and are probably typos.
--- test\listing_0056_estimating_cycles execution ---
mov bx, 1000 ; Clocks: +4 = 4 | bx:0x0->0x3e8 ip:0x0->0x3
mov bp, 2000 ; Clocks: +4 = 8 | bp:0x0->0x7d0 ip:0x3->0x6
mov si, 3000 ; Clocks: +4 = 12 | si:0x0->0xbb8 ip:0x6->0x9
mov di, 4000 ; Clocks: +4 = 16 | di:0x0->0xfa0 ip:0x9->0xc
mov cx, bx ; Clocks: +2 = 18 | cx:0x0->0x3e8 ip:0xc->0xe
mov dx, 12 ; Clocks: +4 = 22 | dx:0x0->0xc ip:0xe->0x11
mov dx, [+1000] ; Clocks: +18 = 40 (8 + 6ea + 4p) | dx:0xc->0x0 ip:0x11->0x15
mov cx, [bx] ; Clocks: +17 = 57 (8 + 5ea + 4p) | cx:0x3e8->0x0 ip:0x15->0x17
mov cx, [bp] ; Clocks: +17 = 74 (8 + 5ea + 4p) | ip:0x17->0x1a
mov word [si], cx ; Clocks: +18 = 92 (9 + 5ea + 4p) | ip:0x1a->0x1c
mov word [di], cx ; Clocks: +18 = 110 (9 + 5ea + 4p) | ip:0x1c->0x1e
mov cx, [bx+1000] ; Clocks: +21 = 131 (8 + 9ea + 4p) | ip:0x1e->0x22
mov cx, [bp+1000] ; Clocks: +21 = 152 (8 + 9ea + 4p) | ip:0x22->0x26
mov word [si+1000], cx ; Clocks: +22 = 174 (9 + 9ea + 4p) | ip:0x26->0x2a
mov word [di+1000], cx ; Clocks: +22 = 196 (9 + 9ea + 4p) | ip:0x2a->0x2e
add cx, dx ; Clocks: +3 = 199 | ip:0x2e->0x30 flags:->PZ
add word [di+1000], cx ; Clocks: +33 = 232 (16 + 9ea + 8p) | ip:0x30->0x34
add dx, 50 ; Clocks: +4 = 236 | dx:0x0->0x32 ip:0x34->0x37 flags:PZ->
Final registers:
bx: 0x03e8 (1000)
dx: 0x0032 (50)
bp: 0x07d0 (2000)
si: 0x0bb8 (3000)
di: 0x0fa0 (4000)
ip: 0x0037 (55)
+42
View File
@@ -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
+71
View File
@@ -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
-335
View File
@@ -1,335 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <assert.h>
#include "lib.h"
#include "decode.h"
/// Get Effective Address Calculation Registers
char* get_eac_register(char rm)
{
char* reg_name;
switch (rm)
{
case 0b000: reg_name = "bx + si"; break;
case 0b001: reg_name = "bx + di"; break;
case 0b010: reg_name = "bp + si"; break;
case 0b011: reg_name = "bp + di"; break;
case 0b100: reg_name = "si"; break;
case 0b101: reg_name = "di"; break;
case 0b110: reg_name = "bp"; break;
case 0b111: reg_name = "bx"; break;
default: perror("Invalid R/M value"); exit(1);
}
return reg_name;
}
static char* reg_name(Register reg, char wide)
{
return wide == 1 ? reg.fullname : reg.bytename;
}
static u8 mask_and_shift(u8 value, u8 mask)
{
value &= mask;
int count = 0;
while ((mask & 0x1) == 0 && count < 8)
{
value >>= 1;
mask >>= 1;
count++;
}
return value;
}
ParsedInstruction parse_instruction(u8* buf)
{
u8 inst = buf[0];
InstFormat fmt;
bool matched_inst = false;
// TODO: This might be a good time to learn how to make a hashtable in C
for (u16 i = 0; i < sizeof(inst_formats) / sizeof(InstFormat); i++)
{
if ((inst & ~inst_formats[i].mask_inst) == inst_formats[i].inst_enc)
{
fmt = inst_formats[i];
matched_inst = true;
break;
}
}
if (!matched_inst)
return (ParsedInstruction){.bytes_read = 0};
u8_opt d_opt = none_u8();
u8_opt s_opt = none_u8();
u8_opt w_opt = none_u8();
u8_opt reg_opt = none_u8();
u8_opt mod_opt = none_u8();
u8_opt rm_opt = none_u8();
u16_opt data_opt = none_u16();
u16_opt displacement_opt = none_u16();
u8 is_data_addr = false;
u16 bytes_read = 1;
bytes_read += fmt.has_operands ? 1 : 0;
if (fmt.has_d) d_opt = some_u8((inst & 0x2) >> 1);
if (fmt.has_s) s_opt = some_u8((inst & 0x2) >> 1);
if (fmt.has_rm) rm_opt = some_u8(buf[1] & 0x7);
if (fmt.has_mod) mod_opt = some_u8((buf[1] & 0b11000000) >> 6);
if (fmt.has_w) w_opt = some_u8(mask_and_shift(buf[0], fmt.mask_w));
if (fmt.parse_reg.tag == P_REG_MASK)
{
u8 reg = fmt.has_operands ? buf[1] : buf[0];
reg_opt = some_u8(mask_and_shift(reg, fmt.parse_reg.mask));
}
else if (fmt.parse_reg.tag == P_REG_FIXED)
{
reg_opt = some_u8(fmt.parse_reg.fixed);
is_data_addr = true;
}
if (fmt.has_data)
{
u8 idx = 1;
if (fmt.has_operands) idx += 1;
// This is a trick because mod == 1 and mod == 2 will displace one and two bytes
// respectively but mod == 3 wraps to 0 since it doesn't displace
if (fmt.has_displacement) idx += mod_opt.value % 3;
u16 data;
if (fmt.has_s && s_opt.value == 1)
{
data = (sbyte)buf[idx];
bytes_read += 1;
}
else
{
data = w_opt.value != 0 ? (i16)buf[idx+1] << 8 | buf[idx] : (sbyte)buf[idx];
bytes_read += w_opt.value == 0 ? 1 : 2;
}
data_opt = some_u16(data);
}
if (fmt.has_displacement && mod_opt.value % 3 > 0)
{
u16 disp = mod_opt.value == MODE_MEM_DIS_16
? (i16)buf[3] << 8 | buf[2]
: (sbyte)buf[2];
displacement_opt = some_u16(disp);
bytes_read += mod_opt.value % 3;
}
else if (fmt.has_displacement && mod_opt.value == MODE_MEM_NO_DIS && rm_opt.value == 0x6)
{
displacement_opt = some_u16((i16)buf[3] << 8 | buf[2]);
bytes_read += 2;
}
return (ParsedInstruction) {
.id = fmt.id,
.name = fmt.name,
.data = data_opt,
.displacement = displacement_opt,
.w = w_opt,
.d = d_opt,
.s = s_opt,
.mod = mod_opt,
.reg = reg_opt,
.rm = rm_opt,
.is_data_addr = is_data_addr,
.bytes_read = bytes_read,
};
}
Instruction decode_instruction(ParsedInstruction inst)
{
Operand opr1 , opr2 = {0};
i16 payload = 0;
IF_LET_SOME(u8, mod, inst.mod)
{
IF_LET_SOME(u8, reg, inst.reg)
{
opr1.tag = OPR_T_REGISTER;
opr1.reg.value = registers[(size_t)reg];
opr1.reg.wide = inst.w.value;
}
else
{
opr1.tag = OPR_T_IMMEDIATE;
opr1.imm.value = inst.data.value;
// TODO: This is dumb, we shouldn't do it this way
if (inst.s.value == 1) opr1.imm.direct = 0;
else opr1.imm.direct = inst.w.value + 1;
}
if (mod == MODE_RGSTR_MODE)
{
opr2.tag = OPR_T_REGISTER;
opr2.reg.value = registers[(size_t)inst.rm.value];
opr2.reg.wide = inst.w.value;
}
else if (mod == MODE_MEM_NO_DIS && inst.rm.value == 0x6)
{
opr2.tag = OPR_T_DIRADDR;
opr2.dir_addr.value = inst.displacement.value;
}
else
{
opr2.tag = OPR_T_MEMORY;
opr2.mem.eac_name = get_eac_register(inst.rm.value);
opr2.mem.mode = mod;
opr2.mem.displacement = (i16)inst.displacement.value;
}
}
else
{
IF_LET_SOME(u16, data, inst.data)
{
if (inst.is_data_addr)
{
opr1.tag = OPR_T_DIRADDR;
opr1.dir_addr.value = (i16)data;
}
else
{
opr1.tag = OPR_T_IMMEDIATE;
opr1.imm.value = (i16)data;
opr1.imm.direct = 0;
}
}
IF_LET_SOME(u8, reg, inst.reg)
{
opr2.tag = OPR_T_REGISTER;
opr2.reg.value = registers[(size_t)reg];
opr2.reg.wide = inst.w.value;
}
}
if (inst.d.tag == SOME && inst.d.value == 1)
{
Operand temp = opr1;
opr1 = opr2;
opr2 = temp;
}
return (Instruction) {
.id = inst.id,
.data = payload,
.operation = inst.name,
.src_opr = opr1,
.dst_opr = opr2,
};
}
void get_operand_string(char* str_buf, Operand oprnd)
{
if (oprnd.tag == OPR_T_REGISTER)
{
strcpy(str_buf, reg_name(oprnd.reg.value, oprnd.reg.wide));
}
else if (oprnd.tag == OPR_T_MEMORY)
{
char disp_str[16] = {'\0'};
i16 disp = oprnd.mem.displacement;
if (disp != 0)
sprintf(disp_str, " %s %d", (disp > 0 ? "+" : "-"), abs(disp));
sprintf(str_buf, "[%s%s]", oprnd.mem.eac_name, disp_str);
}
else if (oprnd.tag == OPR_T_IMMEDIATE)
{
char *size = "";
if (oprnd.imm.direct > 0)
size = oprnd.imm.direct == 1 ? "byte " : "word ";
sprintf(str_buf, "%s%d", size, oprnd.imm.value);
}
else if (oprnd.tag == OPR_T_DIRADDR)
{
sprintf(str_buf, "[%d]", oprnd.dir_addr.value);
}
}
void get_instr_string(char* str_buf, Instruction inst)
{
char src_str[32], dst_str[32];
get_operand_string(src_str, inst.src_opr);
get_operand_string(dst_str, inst.dst_opr);
sprintf(str_buf, "%s %s, %s", inst.operation, dst_str, src_str);
}
char *memory[65536];
// Keep this global for debugging purposes
u16 inst_count = 1;
int main(int argc, char** argv)
{
if (argc < 2)
{
printf("Usage: Please provide assembled instructions as input\n");
exit(0);
}
struct stat st;
if (stat(argv[1], &st) == -1)
{
perror("Unable to get file size\n");
return EXIT_FAILURE;
}
unsigned char* buffer = malloc(st.st_size);
if (!buffer)
{
perror("Unable to allocate memory for binary file");
return EXIT_FAILURE;
}
FILE *f = fopen(argv[1], "r");
if (!f)
{
perror("fopen\n");
free(buffer);
return EXIT_FAILURE;
}
size_t bytes_read = fread(buffer, sizeof(unsigned char), st.st_size, f);
if (bytes_read != (size_t)st.st_size)
{
fprintf(stderr, "Read of binary file to memory incomplete.\n");
free(buffer);
fclose(f);
return EXIT_FAILURE;
}
fclose(f);
printf("; Decoded 8086 Assembly Instructions\n\n");
printf("bits 16\n\n");
char *inst_str_buf = malloc(sizeof(char) * 256);
u32 bytes_processed = 0;
while (bytes_processed < bytes_read)
{
ParsedInstruction parsed = parse_instruction(buffer + bytes_processed);
if (parsed.bytes_read > 0)
{
Instruction inst = decode_instruction(parsed);
get_instr_string(inst_str_buf, inst);
bytes_processed += parsed.bytes_read;
// printf("%s %d/%ld", inst_str_buf, bytes_processed, bytes_read);
printf("%s", inst_str_buf);
int len = strlen(inst_str_buf);
for (int i = 0; i < 32 - len; i++)
printf(" ");
printf("; %d, %d", inst_count++, inst.id);
}
else
{
bytes_processed += 1;
fprintf(stderr, "___Unrecognized Instruction___");
}
// char inst = buffer[0];
// if (mov_inst(f, buffer, inst)) goto handled;
// if (add_inst(f, buffer, inst)) goto handled;
// handled:
printf("\n");
}
free(inst_str_buf);
free(buffer);
return 0;
}
-152
View File
@@ -1,152 +0,0 @@
#include "lib.h"
enum Mode
{
MODE_MEM_NO_DIS = 0b00,
MODE_MEM_DIS_08 = 0b01,
MODE_MEM_DIS_16 = 0b10,
MODE_RGSTR_MODE = 0b11,
};
typedef struct Register
{
char* fullname;
char* bytename;
union {
struct {
char low;
char high;
};
u16 full;
} value;
u8 code;
} Register;
Register registers[8] = {
{.code = 0b000, .fullname = "ax", .bytename = "al"},
{.code = 0b001, .fullname = "cx", .bytename = "cl"},
{.code = 0b010, .fullname = "dx", .bytename = "dl"},
{.code = 0b011, .fullname = "bx", .bytename = "bl"},
{.code = 0b100, .fullname = "sp", .bytename = "ah"},
{.code = 0b101, .fullname = "bp", .bytename = "ch"},
{.code = 0b110, .fullname = "si", .bytename = "dh"},
{.code = 0b111, .fullname = "di", .bytename = "bh"},
};
enum OperandType {OPR_T_MEMORY, OPR_T_REGISTER, OPR_T_IMMEDIATE, OPR_T_DIRADDR};
typedef struct Operand
{
enum OperandType tag;
union {
struct Mem {
char *eac_name;
i16 displacement;
u8 mode;
} mem;
struct Reg {
Register value;
bool wide;
} reg;
struct Imm {
i16 value;
u8 direct;
} imm;
struct DirAddr {
i16 value;
} dir_addr;
};
} Operand;
enum ParseRegType { P_REG_NONE, P_REG_MASK, P_REG_FIXED };
typedef struct ParseReg
{
enum ParseRegType tag;
union {
u8 none;
u8 mask;
u8 fixed;
};
} ParseReg;
typedef struct InstFormat
{
u16 id;
char *name;
ParseReg parse_reg;
u8 inst_enc;
u8 mask_inst;
u8 mask_w;
bool has_operands;
bool has_displacement;
bool has_data;
bool has_d;
bool has_w;
bool has_mod;
bool has_rm;
bool has_s;
bool has_SR;
} InstFormat;
typedef struct ParsedInstruction
{
u16 id;
char *name;
u16_opt data;
u16_opt displacement;
u8_opt w;
u8_opt d;
u8_opt s;
u8_opt mod;
u8_opt reg;
u8_opt rm;
u8_opt SR;
u8 is_data_addr;
u8 bytes_read;
} ParsedInstruction;
typedef struct Instruction
{
Operand src_opr;
Operand dst_opr;
i16 data;
char *operation;
u16 id;
} Instruction;
InstFormat inst_formats[] =
{
////////
// MOV
////////
// Register/memory to/from register
{.id=1, .name="mov", .inst_enc=0b10001000, .mask_inst=0x3, .mask_w=0x1,
.has_operands=true, .has_displacement=true, .has_d=true, .has_w=true,
.has_mod=true, .has_rm=true, .parse_reg={.tag = P_REG_MASK, .mask=0b00111000} },
// Immediate to register/memory
{.id=2, .name="mov", .inst_enc=0b11000110, .mask_inst=0x1, .mask_w=0x1, .has_operands=true,
.has_displacement=true, .has_data=true, .has_w=true, .has_mod=true, .has_rm=true},
// Immediate to register
{.id=3, .name="mov", .inst_enc=0b10110000, .mask_inst=0xF, .mask_w=0x8,
.parse_reg={.tag = P_REG_MASK, .mask=0b00000111}, .has_data=true, .has_w=true},
// Memory to accumulator | Accumulator to memory using the `d` bit
// even though the manual doesn't specify it
{.id=4, .name="mov", .inst_enc=0b10100000, .mask_inst=0x3, .mask_w=0x1, .has_data=true,
.has_w=true, .has_d=true, .parse_reg={.tag = P_REG_FIXED, .fixed=0x0}},
// Register/memory to segment register and inverse using the `d` bit
{.id=5, .name="mov", .inst_enc=0b10001100, .mask_inst=0x3, .has_SR=true, .has_d=true,
.has_displacement=true, .has_mod=true, .has_rm=true},
////////
// ADD
////////
// Reg/memory with register or either
{.id=6, .name="add", .inst_enc=0b00000000, .mask_inst=0x3, .has_displacement=true,
.mask_w=0x1, .has_operands=true, .has_w=true, .has_d=true, .has_mod=true,
.has_rm=true, .parse_reg={.tag = P_REG_MASK, .mask=0b00111000}},
// Immediate to register/memory
{.id=7, .name="add", .inst_enc=0b10000000, .mask_inst=0x3, .mask_w=0x1, .has_w=true,
.has_s=true, .has_operands=true, .has_displacement=true,
.has_data=true, .has_mod=true, .has_rm=true},
{.id=8, .name="add", .inst_enc=0b00000100, .mask_inst=0x1, .mask_w=0x1,
.has_data=true, .has_w=true, .parse_reg={.tag = P_REG_FIXED, .fixed=0x0}},
};
-62
View File
@@ -1,62 +0,0 @@
package decoder_8086
import "core:os"
import "core:fmt"
Register :: struct {
fullname: string,
bytename: string,
value: struct #raw_union {
using _: struct {
low, high: byte,
},
full: u16,
},
code: u8,
}
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},
}
main :: proc() {
ax := registers[0]
ax.value.full = 52428
f,err := os.open("./asm_files/01-01-38.bin")
if err != os.ERROR_NONE {
os.exit(1)
}
defer os.close(f)
data := make([]u8, 256)
bytes_read, err2 := os.read(f, data)
if err2 != nil {
// ...
os.exit(1)
}
// mov cx, bx ; mov 001, 011
// mov dx, bx
read_next := false
for b in data {
if read_next {
mod := (b & 0b11000000) >> 6
reg := (b & 0b00111000) >> 3
rm := b & 0b00000111
fmt.printfln("mov %s, %s", registers[rm].fullname, registers[reg].fullname)
read_next = false
continue
}
if b == 0b10001001 {
read_next = true
continue
}
}
}
+279
View File
@@ -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
View File
@@ -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)
}
}
+307
View File
@@ -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", },
}
-53
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
./decode "$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
+29
View File
@@ -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
+61
View File
@@ -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
View File
@@ -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,
}