• Please review our updated Terms and Rules here

8086 emulator tech discussion

Floating point makes heavy use of shifts and the status of the flag bits. So, that's where I'd start my checking. Then I'd move on to the arithmetic ops and check the flags.

Here's one using the arc tangent function:

Code:
? 4.0*atn(1.0)
 3.141593
Ok*
 
Mike,

Having evaluated the conditional jumps (as per Chuck's suggestion) and also checked these against my emulator, I propose the following corrections to your emulator:

Code:
    Case &H7C '7C JL Jb
        temp16 = signext(read86(cs * 16 + ip)): StepIP 1
        If (sf <> of) Then ip = ip + temp16
    
    Case &H7D '7D JGE Jb
          temp16 = signext(read86(cs * 16 + ip)): StepIP 1
        If (sf = of) Then ip = ip + temp16

Turns out the JL opcode test condition was actually incorrect in my own emulator (sorry!), though it didn't appear to cause any practical dramas. probably due to the test method that I utilised (please read on). Your implementation of JGE did cause problems in my emulator, however..


Chuck(G) said:
Mike, I'm surprised that you didn't set up a test suite for the various instructions and run it on the real hardware for verification. There are only 8 flags that really matter, so you could set up a table of 256 flag bytes and test every jump instruction, recording whether or not it actually took a
branch.

Writing verification software is a huge job when working with one of the hardware description languages (VHDL or Verliog). In fact, it's often a bigger job than writing the HDL for the implementation itself.

Good test writers are worth their weight in diamonds...

Another (simpler though less thorough) test method: one could also write a routine to create an output log of an application in progress using the trap interrupt on a real PC. Once this output log is obtained, one could then utilise it as a comparative reference to the same application running under one's own emulator.. i.e. if there's any discrepancy in the resultant states, one can identify and fix the problem(s) fairly quickly..

For the record, it was this somewhat indirect method that I used to get my own emulator to it's current state. Only difference is I didn't have a real IBM-PC available, therefore instruction logs generated by a mainstream emulator (DOSBox in this case) were used instead..

Of course, it isn't an ideal solution (i.e. merely feeding in some random programs does not yield 100% test coverage) and Chuck's simple example for JL provides clear evidence of this. Also, diagnosing asynchronous events (i.e. hardware interrupts) in this manner can also be fun too!

Ok, time for me to go back and re-acquaint myself with the seriously underused (until now) MS debug.com .. ;)


Cheers Valentin
 
Last edited:
yep i fixed those two jump condition checks. looking good.

and so is my math now:

fake86-goodmath.png


8)
 
sorry for another screen shot, but THIS one... is awesome...

fake86-dos622-boot.png


that's off a 20 MB hard disk image.

*struts*
 
i can't quite try that yet. there are still a few things to iron out. whenever i type a command, it responds as if i had issued a /? on the cmd line and they just spit out usage information. can't seem to load EXEs either. it says file not found. i'm getting there though. i oughta have it soon.

what i'd really enjoy is playing some ultima 6 on it.
 
..or for something simpler, I tried running VisiCalc

However, on Mike's latest software update all I got was a blank output screen..

i just loaded up vc.com in a hex editor, and it's riddled with int 21h calls so that explains the blank screen when loaded using /com on the command line. since there's no DOS kernel loaded up that way, int 21h vector just points to 0:0.
 
i just loaded up vc.com in a hex editor, and it's riddled with int 21h calls so that explains the blank screen when loaded using /com on the command line. since there's no DOS kernel loaded up that way, int 21h vector just points to 0:0.

Doh! Now I remember why I couldn't get that thing to load standalone in my own emulator (around two years ago..).

I do seem to recall that sierra's crossfire game does not use any DOS interrupts however..
 
Last edited:
LOL! Woohoo!!

How's the gameplay?

way too fast to not die immediately :p

i need to add some kind of support for throttling. on my 3.2 GHz Phenom II X4 (just one core of course) it emulates on average at somewhere between a 286 and a slow 386. i can live with that. maybe i can crank a bit more out of it later.

later tonight i'm going to start putting together a test suite for comparing my flag calcs against a real 8088 so i can squash these remaining bugs.
 
well, i've done comparisons against results on a real 286 chip. i tested ADD, SUB, ADC, SBB, MUL, IMUL, DIV, IDIV, AND, OR, XOR and every numerical result and flag matched 100%... so i don't think it's in the flags. must be something else goofed somewhere. i know there are other operations but they all share the same few flag calculation routines.
 
You know, I'm laughing at the number of games like crossfire or moonbugs that feel like rewrites of Big 5 TRS-80 classics like Attack Force or Defense Command.

Especially when the gameplay doesn't seem to be as good. You'd think a 16 bit processor and real graphics would make these games better, not worse :D
 
You know, I'm laughing at the number of games like crossfire or moonbugs that feel like rewrites of Big 5 TRS-80 classics like Attack Force or Defense Command.

Interesting - never thought about the history of these games..


You'd think a 16 bit processor and real graphics would make these games better, not worse :D

lol true, the IBM-PC was no arcade machine! :D even the Apple-II version of crossfire apparently have better on-screen graphics (if not gameplay) than the IBM-PC version!

Still, there were some (CGA-era) games that did at least attempt to make good use of the limited resources available..
 
it passes this monstrous conditional branching test program i wrote, as does real hardware, QEMU and DOSBox... i figure it's probably best to just write specific tests for various aspects of the emulation than it is to run various programs tracking down where a problem occurs.

Code:
; branch.asm - A program to test the Intel 8086 CPU's
; various conditional branching operations. Designed to
; verify corrent functionality of my 8086 PC emulator, Fake86.

org 100h

cli
push cs
pop ds

mov si, offset banner
call printmsg

testjc:
mov si, offset strjc
call printmsg
call blankflags
call setcf
jc testjc2
call fail
jmp testjnc
testjc2:
call blankflags
mov bx, offset testjnc
push bx
jc fail
pop bx ;not used, just cleaning up the stack
call pass


testjnc:
mov si, offset strjnc
call printmsg
call blankflags
jnc testjnc2
call fail
jmp testjz
testjnc2:
call blankflags
call setcf
mov bx, offset testjz
push bx
jnc fail
pop bx
call pass


testjz:
mov si, offset strjz
call printmsg
call blankflags
call setzf
jz testjz2
call fail
jmp testjnz
testjz2:
call blankflags
mov bx, offset testjnz
push bx
jz fail
pop bx
call pass


testjnz:
mov si, offset strjnz
call printmsg
call blankflags
jnz testjnz2
call fail
jmp testjs
testjnz2:
call blankflags
call setzf
mov bx, offset testjs
push bx
jnz fail
pop bx
call pass


testjs:
mov si, offset strjs
call printmsg
call blankflags
call setsf
js testjs2
call fail
jmp testjns
testjs2:
call blankflags
mov bx, offset testjns
push bx
js fail
pop bx
call pass


testjns:
mov si, offset strjns
call printmsg
call blankflags
jns testjns2
call fail
jmp testjo
testjns2:
call blankflags
call setsf
mov bx, offset testjo
push bx
jns fail
pop bx
call pass


testjo:
mov si, offset strjo
call printmsg
call blankflags
call setof
jo testjo2
call fail
jmp testjno
testjo2:
call blankflags
mov bx, offset testjno
push bx
jo fail
pop bx
call pass


testjno:
mov si, offset strjno
call printmsg
call blankflags
jno testjno2
call fail
jmp testjp
testjno2:
call blankflags
call setof
mov bx, offset testjp
push bx
jno fail
pop bx
call pass


testjp:
mov si, offset strjp
call printmsg
call blankflags
call setpf
jp testjp2
call fail
jmp testjnp
testjp2:
call blankflags
mov bx, offset testjnp
push bx
jp fail
pop bx
call pass


testjnp:
mov si, offset strjnp
call printmsg
call blankflags
jnp testjnp2
call fail
jmp testja
testjnp2:
call blankflags
call setpf
mov bx, offset testja
push bx
jnp fail
pop bx
call pass


testja:
mov si, offset strja
call printmsg
call blankflags ;case 1
ja testja2
call fail
jmp testjbe
testja2: ;case 2
call blankflags
call setcf
mov bx, offset testjbe
push bx
ja fail
pop bx
testja3:
call blankflags
call setzf
mov bx, offset testjbe
push bx
ja fail
pop bx
testja4:
call blankflags
call setcf
call setzf
mov bx, offset testjbe
push bx
ja fail
pop bx
call pass


testjbe:
mov si, offset strjbe
call printmsg
call blankflags ;case 1
call setcf
jbe testjbe2
call fail
jmp testjg
testjbe2:
call blankflags
call setzf
jbe testjbe3
call fail
jmp testjg
testjbe3:
call blankflags
call setcf
call setzf
jbe testjbe4
call fail
jmp testjg
testjbe4:
call blankflags
mov bx, offset testjg
push bx
jbe fail
pop bx
call pass


testjg:
mov si, offset strjg
call printmsg
call blankflags
jg testjg2:
call fail
jmp testjge
testjg2:
call blankflags
call setzf
mov bx, offset testjge
push bx
jg fail
pop bx
testjg3:
call blankflags
call setsf
call setzf
mov bx, offset testjge
push bx
jg fail
pop bx
testjg4:
call blankflags
call setof
call setzf
mov bx, offset testjge
push bx
jg fail
pop bx
testjg5:
call blankflags
call setsf
call setof
call setzf
mov bx, offset testjge
push bx
jg fail
pop bx
testjg6:
call blankflags
call setsf
call setof
mov bx, offset testjge
push bx
jg pass
pop bx
call fail


testjge:
mov si, offset strjge
call printmsg
call blankflags
jge testjge2
call fail
jmp testjl
testjge2:
call blankflags
call setsf
mov bx, offset testjl
push bx
jge fail
pop bx
testjge3:
call blankflags
call setof
mov bx, offset testjl
push bx
jge fail
pop bx
call pass


testjl:
mov si, offset strjl
call printmsg
call blankflags
call setsf
jl testjl2
call fail
jmp testjle
testjl2:
call blankflags
call setof
jl testjl3
call fail
jmp testjle
testjl3:
call blankflags
call setsf
call setof
mov bx, offset testjle
push bx
jl fail
pop bx
testjl4:
call blankflags
mov bx, offset testjle
push bx
jl fail
pop bx
call pass


testjle:
mov si, offset strjle
call printmsg
call blankflags
call setzf
jle testjle2
call fail
jmp finished
testjle2:
call blankflags
call setsf
jle testjle3
call fail
jmp finished
testjle3:
call blankflags
call setof
jle testjle4
call fail
jmp finished
testjle4:
call blankflags
call setsf
call setzf
jle testjle5
call fail
jmp finished
testjle5:
call blankflags
call setof
call setzf
jle testjle6
call fail
jmp finished
testjle6:
call blankflags
call setsf
call setof
call setzf
jle testjle7
call fail
jmp finished
testjle7:
call blankflags
mov bx, offset finished
push bx
jle fail
call pass


finished:
ret

pass:
mov si, offset strgood
call printmsg
ret

fail:
mov si, offset strfail
call printmsg
ret

blankflags:
xor ax, ax
push ax
popf
ret

setcf:
stc
ret

setof:
pushf
pop ax
or ah, 00001000b
push ax
popf
ret

setsf:
pushf
pop ax
or al, 10000000b
push ax
popf
ret

setzf:
pushf
pop ax
or al, 01000000b
push ax
popf
ret

setpf:
pushf
pop ax
or al, 00000100b
push ax
popf
ret

printmsg:
mov ah, 0Eh
cld
lodsb
cmp al, 0
jz done
int 10h
jmp printmsg
done:
ret

banner db '8086 CPU conditional branch test utility',13,10
       db 'Written on 3/4/2011 by Mike Chambers',13,10,13,10,0
strjc  db 'Testing JC/JB/JNAE (jump if CF=1)... ',0
strjnc db 'Testing JNC/JNB/JAE (jump if CF=0)... ',0
strjz  db 'Testing JZ/JE (jump if ZF=1)... ',0
strjnz db 'Testing JNZ/JNE (jump if ZF=0)... ',0
strjs  db 'Testing JS (jump if SF=1)... ',0
strjns db 'Testing JNS (jump if SF=0)... ',0
strjo  db 'Testing JO (jump if OF=1)... ',0
strjno db 'Testing JNO (jump if OF=0)... ',0
strjp  db 'Testing JP/JPE (jump if PF=1)... ',0
strjnp db 'Testing JNP/JPO (jump if PF=0)... ',0
strja  db 'Testing JA/JNBE (jump if CF=0 and ZF=0)... ',0
strjbe db 'Testing JBE/JNA (jump if CF=1 or ZF=1)... ',0
strjg  db 'Testing JG/JNLE (jump if SF=OF and ZF=0)... ',0
strjge db 'Testing JGE/JNL (jump if SF=OF)... ',0
strjl  db 'Testing JL/JNGE (jump if SF<>OF)... ',0
strjle db 'Testing JLE/JNG (jump if SF<>OF or ZF=1)... ',0

strgood db 'passed!',13,10,0
strfail db 'FAILED!',13,10,0

i'm writing a thorough utility to test all possible effective address calculations too. ~4.2 billion EAs to test for EACH addressing mode... that actually might take half a day to run! to test [BX+SI] for example, i would test every possible value combo of the registers, and make sure an LEA returns the same result as an ADD. i know my ADD/SUB routines are fine i tested those as well.
 
Last edited:
eh i decided i didn't need to test EVERY value and did increments by 10h, and i tested every single addressing mode's calculations, and it passes.
 
VisiCalc works perfectly under DOS 1.0, including loading/saving spreadsheets to/from a floppy disk image.

fake86-visicalc.png



still no go on getting EXEs to load properly or making DOS 6.22 work any better. my brain hurts, but i'm not giving up on this one.

btw, anybody know off-hand how to make VisiCalc to always show 2 decimal places? not terribly important, but just curious. that program is like 4 or 5 years older than me.
 
Back
Top