• Please review our updated Terms and Rules here

8086 emulator tech discussion

Mike Chambers

Veteran Member
Joined
Sep 2, 2006
Messages
2,621
carried over from this post since the title of the thread no longer applied..

deathshadow, i'm linking my current source to this post for you. and for anybody else who would want to have a look at it. i know much of it might look really goofy, try not to cry.

here it is: http://rubbermallet.org/fake86-0.2.7.11.zip
a win32 .exe is in there too.

obviously, it's in FreeBASIC and i have also included my project file for the FBEdit IDE, which i HIGHLY reccommend using. You can download it from this site.

for anybody not familiar with the Fake86 project, here are a few screenshots of how it stands. it can't completely boot DOS yet.

fake86-bios.png


fake86-ohshit.png


fake86-rombasic7.png


fake86-mspacman.png


i'm still looking for programmers who might want to help on the project! :yell:

currently emulated:
-most basic CGA stuff
-interrupts 10h, 13h, 16h, and 19h (video, disk, keyboard, bootstrap) are intercepted and handled at a high-level rather than letting the BIOS
-all 8086/8088 instructions
-interrupt 8h (clock tick) is triggered about 18.2 times per second, but there is no proper PIC/PIT emulation yet

to do:
-many video functions
-proper keyboard I/O port emulation (when this is done, the BIOS can then handle int 16h on it's own)
-DMA controller emulation
-PIC/PIT emulation
-bughunting to see why DOS can't completely boot yet
-bughunting to see why ROM BASIC displays weird characters if you try PRINT HEX$(some_value)
-plenty more
 
Last edited:
I'm a little surprised how peppy your video refreshes are given they're done with pset... Though admittedly drawing to the screen ends up about the same as the 18 bytes per retrace of CGA 'slow' mode. It'll kick ass once you get some real bitblt routines in there.
 
Keyboard emulation should be easy to implement if you have added Intel 8255 PIO support. All you have to do is to detect keypresses/keydepresses, store make/break codes to the data port A of the emulated 8255, and fire off an IRQ1.

If you want to take it all the way, you should make sure the "reset" bit of port B of the 8255 are toggeled before accepting more keyboard input, and you should store the correct identifier in Port A if the Port B bit controlling the clock line is driven low for more than a certain time (wait till the clock goes high again before sending the identifier).
 
I'm a little surprised how peppy your video refreshes are given they're done with pset... Though admittedly drawing to the screen ends up about the same as the 18 bytes per retrace of CGA 'slow' mode. It'll kick ass once you get some real bitblt routines in there.

yeah, FreeBASIC is a good compiler. the plan was to actually just use a pointer to draw directly to the bitmap plane eventually. :)
 
Keyboard emulation should be easy to implement if you have added Intel 8255 PIO support. All you have to do is to detect keypresses/keydepresses, store make/break codes to the data port A of the emulated 8255, and fire off an IRQ1.

If you want to take it all the way, you should make sure the "reset" bit of port B of the 8255 are toggeled before accepting more keyboard input, and you should store the correct identifier in Port A if the Port B bit controlling the clock line is driven low for more than a certain time (wait till the clock goes high again before sending the identifier).

yeah should be pretty straightforward. no 8255 yet though. i'm working on the 8253 right now, and was going to get to that next. then the 8259.
 
so i've fixed the problem i had with GRP5 /5 (JMP m16:16), as well as a few other similar ops. i was doing the indirect absolute addressing wrong. but i've got it fixed.

here's that JMP emulation code now: (useseg is by default the value of DS, but it's changed if there's a segment prefix in front of an instruction)

Code:
    Case 5 'JMP Mp
        oper1 = readrm16(rm)
        ip = getmem16(useseg, oper1)
        cs = getmem16(useseg, oper1 + 2)

this is the ULTIMATE crash course in learning x86 assembly. ;)

so it's stopped jumping around to erroneous places when trying to boot DOS disks, but now it seems to wait in a loop for something or other after the "Starting MS-DOS..." part. i can see some action in my I/O port logging, and it looks like it's trying to talk to the 8253, so when i implement that i am hoping that i might be able to get to a command prompt. we'll see.
 
i can see some action in my I/O port logging, and it looks like it's trying to talk to the 8253, so when i implement that i am hoping that i might be able to get to a command prompt. we'll see.
DOS, particularly newer versions (like 5.x or 6.x) are really reliant upon a lot of hardware being present that isn't neccessarily there on a number of systems. I'm assuming that's 5.x or later with the "Starting MS-DOS" string displayed.

You might find that an older copy of DOS like 2.1, particularly a 'generic MS-DOS' like that meant for the Compaq or Tandy 2K may in fact be easier to get to boot than later versions, as Microsoft tended to make the non-IBM versions a bit more hardware generic. Even the Rainbow copy can surprise you if you adjust for the different video offset. (especially since that lacked video BIOS on the mono ones)

Though at the same time you never know what DEC or Tandy did with it after having it handed off to them... see the version of Dos 3.3 that came with my Tandy 1000SX that won't work on a normal PC.
 
so far, haven't been able to boot any version of DOS i've tried. MS or otherwise. i've probably tried 20 different types of DOS. also, i think i'm doing LEA wrong. i can't seem to find any good info on that opcode for some reason. what does it do different than just MOV?

i'd like to try the Rainbow DOS btw, but i don't have a copy and i don't know what the video base would be on the Rainbow.
 
Code:
        oper1 = readrm16(rm)
        ip = getmem16(useseg, oper1)
        cs = getmem16(useseg, oper1 + 2)

^ this code i posted before, i'm wondering... is this actually correct? with the JMP instruction of GRP5 /5 (and GRP5 /3 CALL), am i supposed to be using the value pointed to by rm as the index when calculating the address of the CS:IP pointer?

or am i supposed to be using the effective address of rm? like so:

Code:
        oper1 = getea(rm)
        ip = getmem16(useseg, oper1)
        cs = getmem16(useseg, oper1 + 2)



also wondering if my LDS/LES is right?

Code:
    Case &HC5 'C5 LDS Gv Mp
        modregrm
        oper1 = getmem16(cs, ip): StepIP 2
        putreg16 reg, getmem16(useseg, oper1)
        putreg16 ds, getmem16(useseg, oper1 + 2)
 
also, i think i'm doing LEA wrong. i can't seem to find any good info on that opcode for some reason. what does it do different than just MOV?
LEA is different in that it doesn't actually move anything -- it just sets up the pointer so you can move something. Some people even use it for faster optimized math... Abrash's graphics programming book mentions that.. google fu...

http://www.phatcode.net/res/224/files/html/ch06/06-02.html#Heading6

Using the effective address calculation to perform faster integer math... Helps explain why it's better than MOV for many tasks.

That's the part most people mess up though, even as a memory reference, LEA does NOT actually access memory, it just loads the target register with the address calculation.

mov ax,[bx+sp] gives you the contents of memory address [bx+sp]

lea ax,[bx+sp] is the same as

mov ax,bx
add ax,sp
 
Last edited:
thanks for clarifying. that's what i thought. so my code for it is correct.

Code:
    Case &H8D '8D LEA Gv M
        modregrm
        putreg16 reg, getea(rm)

Code:
Function getea(ByVal rmval as UInteger) as ULong
Select Case mode
    Case 0
        Select Case rmval
            Case 0: temp1 = (useseg * 16) + getreg16(bx) + si
            Case 1: temp1 = (useseg * 16) + getreg16(bx) + di
            Case 2: temp1 = (useseg * 16) + bp + si
            Case 3: temp1 = (useseg * 16) + bp + di
            Case 4: temp1 = (useseg * 16) + si
            Case 5: temp1 = (useseg * 16) + di
            Case 6: temp1 = (useseg * 16) + Disp
            Case 7: temp1 = (useseg * 16) + getreg16(bx)
        End Select
    Case 1, 2
        Select Case rmval
            Case 0: temp1 = (useseg * 16) + getreg16(bx) + si + Disp
            Case 1: temp1 = (useseg * 16) + getreg16(bx) + di + Disp
            Case 2: temp1 = (useseg * 16) + bp + si + Disp
            Case 3: temp1 = (useseg * 16) + bp + di + Disp
            Case 4: temp1 = (useseg * 16) + si + Disp
            Case 5: temp1 = (useseg * 16) + di + Disp
            Case 6: temp1 = (useseg * 16) + bp + Disp
            Case 7: temp1 = (useseg * 16) + getreg16(bx) + Disp
        End Select
	Case Else
        temp1 = getreg16(rmval)
End Select
getea = temp1
End Function

fixed another bug in the disk reading btw. for multiple sector read calls, it was reading the same sector over and over, n number of times. heh, i misplaced a file seek. it was inside the for:next sector count loop, not before it.
 
damn you, Tandy!

fake86-tandy.png


EDIT: i fixed that by forcing a return value of 0x21 on a memory read at FC00:0000. this disk won't boot to DOS all the way either though. :(
 
Last edited:
Thanks for very much attaching new 8086/8088 emulator.
I have any questions?
1. Is it possible to run this with IBM PC-5150/5160's BIOS?
2. Is it possible to emulator copy protected track / sector via INT13 when I give information of copy protected track / sector?
 
Thanks for very much attaching new 8086/8088 emulator.
I have any questions?
1. Is it possible to run this with IBM PC-5150/5160's BIOS?
2. Is it possible to emulator copy protected track / sector via INT13 when I give information of copy protected track / sector?

in it's current form, it can't run 5150/5160 BIOSes without doing a little bit of hex editing. i don't have a DMA controller (and a couple other things) emulated yet, which the original IBM BIOSes look for and test. if the test fails, it halts the PC. if you hex edit the BIOS ROM to bypass these or not care about the results, then it runs them and continues to boot, yes. atm, it's not worth the trouble. i just use a generic BIOS that doesn't run the tests.

i don't know much about floppies that are copy protected, so i can't answer that one.
 
alright, so i fixed the hex output bug in ROM BASIC. my XLAT had a typo in it, so it wasn't looking up the output in BASIC's hex conversion table properly.

however, now i also tried to purposely trip an invalid opcode (int 6h) during a DOSPLUS 2.1 floppy disk boot to see it's hex output because if int 6 is tripped during init, it will print out debug information... DOSPLUS' hex output looks wrong. there couldn't be too many different methods to convert decimal to hex in assembly, right?

here's a pic with screenshots of the new hex output in BASIC, and DOSPLUS' debug dump. any ideas how they might be going about the hex conversion, so that i know what to look for while i'm trying to figure this out?

fake86-hexweird.png
 
i get this interesting message trying to boot Wendin DOS..

fake86-wendin.png


interesting. the disk image is fine, it boots correctly in QEMU. my code is a bit messy anyway, so i'm considering calling this a practice run and starting over again using C.
 
last night i implemented support for the palette and intensity bits in CGA register 3D9h for 320x200 4-color mode. while i was at it, added i ditched PSETs in the rendering routines. PSET is pretty fast in FreeBASIC - but by using a pointer to framebuffer memory i am seeing about a 400-500% improvement in drawing time.

this is the current code for when using that video mode:

Code:
		usepal = (portram(&h3D9)\32) And 1
		intensity = ((portram(&h3D9)\16) And 1) * 8
		For y = 1 To 400
    		For x = 1 To 640
	    		charx = (x-1)\2
    			chary = (y-1)\2
  				vidptr = videobase + ((chary\2) * 80) + ((chary Mod 2) * 8192) + (charx \ 4) 'this crazy-ass formula is needed to resolve the right video memory location because of the CGA's interleaving. why, IBM? why?
    			curpixel = RAM(vidptr)
    			Select Case charx Mod 4
    				Case 3: curpixel = curpixel And 3
    				Case 2: curpixel = (curpixel\4) And 3
    				Case 1: curpixel = (curpixel\16) And 3
    				Case 0: curpixel = (curpixel\64) And 3
    			End Select
    			curpixel = curpixel * 2 + usepal + intensity
    			pixel = buffer + y * pitch + x * bypp
    			*pixel = PaletteCGA(curpixel) 
    		Next x
		Next y

results mean among other things, that the colors in ms. pacman are correct now..

fake86-mspacman2.png


:cool:

i might just keep this in FreeBASIC instead of re-doing it in C. i can go through and make a LOT of optimizations, probably doubling the speed and it's already running ~35-40% of DOSBox's speed.
 
Just checking in here--I've not been following this thread. That garbled hex display might be due to your treatment of the DAA instruction.

Alternatively, they might be separating the nibbles by using an AAM with a second byte of 10h instead of 0ah. Similarly, the AAD (D5h) instruction can be used with second bytes of values other than 0ah to combine nibbles in weird and wonderful ways.

I don't know if you've considered this.
 
Just checking in here--I've not been following this thread. That garbled hex display might be due to your treatment of the DAA instruction.

Alternatively, they might be separating the nibbles by using an AAM with a second byte of 10h instead of 0ah. Similarly, the AAD (D5h) instruction can be used with second bytes of values other than 0ah to combine nibbles in weird and wonderful ways.

I don't know if you've considered this.

wow, good call. my AAM and AAD are fine, but i had an IF statement slightly wrong in DAA.

fake86-dosplushex.png


unfortunately, when coding, slightly wrong is the same as completely wrong. thanks! :D
 
Last edited:
fixed a major bug today. for 83h GRP1 opcodes, where the destination operand is 16-bit, and the source operand is 8-bit - i wasn't sign extending! fixed a lot of issues. for example alley cat looks perfect now:

fake86-alleycat.png




it also partially fixed the bottom line on the ROM BASIC screen:

fake86-rombasic-new.png



plus now DOS 2.11 completely loads MSDOS.SYS, starts running the kernel, but then command.com craps out:

fake86-dos211.png




still not working completely, but i'm making good progress. :D
 
Back
Top