• Please review our updated Terms and Rules here

8086 emulator programming

Mike Chambers

Veteran Member
Joined
Sep 2, 2006
Messages
2,621
i know this is quite an ambitious project, but i was wondering if anybody who knows what they're talking about can tell me if i'm headed in the right direction so far or if i've been wasting my time? (so i know to stop now)

programming it in VB6... here is a copy and paste of my core.bas module which is, obviously, the core subs and functions of the 8086 emulation.

yes, i know VB6 is crud but regardless... am i on the right track?:confused:

i've only written code to support 5 opcodes so far.

Code:
Public RAM(1048576) As Integer
Public PC As Long
Public AH, AL, BH, BL, CH, CL, DH, DL 'General purpose registers
Public CS, DS, SS, ES 'Segment registers
Public SI, DI, IP     'Pointer registers
Public SP, BP         'Stack registers
Public CF, PF, AF, ZF, SF, TF, fIF, DF, OF 'Flag registers

Public Sub LoadBIN(offset As Long, BINfile As String)
pos = offset
Open BINfile For Binary As #1
Do Until EOF(1)
    getbyte$ = Space$(1)
    Get #1, , getbyte$
    RAM(pos) = Asc(getbyte$)
    pos = pos + 1
Loop
MsgBox "Loaded " + BINfile + " into RAM." + Str$(LOF(1)) + " bytes."
Close #1
End Sub

Public Sub Exec()
opcode = RAM(PC)

Select Case opcode
    Case &H37 'AAA
        If ((AL And &HF) > 9) Or (AF = 1) Then
            AL = AL + 6
        Else
            CF = 0
            AF = 0
        End If
        IncPC 1
        
    Case &HD5 'AAD imm8
        val1 = RAM(PC + 1)
        AL = (AH * val1)
        AH = 0
        IncPC 2
        
    Case &HD4 'AAM imm8
        val1 = RAM(PC + 1)
        AH = AL / val1
        AL = Fix(AL / val1)
        IncPC 2
    
    Case &HD6 'SETALC - Set AL to carry flag
        AL = CF
        IncPC 1
        
    Case &H3F 'AAS - ASCII adjust after subtraction
        If LowNib(AL) > 9 Or AF = 1 Then
            AL = AL - 6
            AH = AH - 1
            AF = 1
            CF = 1
        Else
            AF = 0
            CF = 0
        End If
        IncPC 1
        
    Case Else
        MsgBox "UNKNOWN OPCODE!" + vbCrLf + "Decimal:" + Str$(opcode) + vbCrLf + "Hexadecimal: " + Hex$(opcode)
    
End Select

Form1.Label1 = "PC:" + Str$(PC)
End Sub

Public Sub IncPC(incval)
PC = PC + incval
End Sub

Public Function FullReg(RegHigh, RegLow)
'encode binary
bcode$ = ""
curcnt = 128
tmp2 = RegHigh
For tmp = 1 To 8
    If tmp2 - curcnt >= 0 Then
        bcode$ = bcode$ + "1"
        tmp2 = tmp2 - curcnt
    Else
        bcode$ = bcode$ + "0"
    End If
    curcnt = curcnt / 2
Next tmp

curcnt = 128
tmp2 = RegLow
For tmp = 1 To 8
    If tmp2 - curcnt >= 0 Then
        bcode$ = bcode$ + "1"
        tmp2 = tmp2 - curcnt
    Else
        bcode$ = bcode$ + "0"
    End If
    curcnt = curcnt / 2
Next tmp


'decode binary
curcnt = 32768
newnum = 0
For tmp = 1 To 16
    If Mid$(bcode$, tmp, 1) = "1" Then newnum = newnum + curcnt
    curcnt = curcnt / 2
Next tmp

FullReg = newnum
End Function

Public Function LowNib(NumVal)
'encode bianry
bcode$ = ""
curcnt = 128
tmp2 = NumVal
For tmp = 1 To 8
    If tmp2 - curcnt >= 0 Then
        bcode$ = bcode$ + "1"
        tmp2 = tmp2 - curcnt
    Else
        bcode$ = bcode$ + "0"
    End If
    curcnt = curcnt / 2
Next tmp

'decode binary
curcnt = 8
newnum = 0
For tmp = 1 To 4
    If Mid$(bcode$, tmp, 1) = "1" Then newnum = newnum + curcnt
    curcnt = curcnt / 2
Next tmp
End Function

Public Function HighNib(NumVal)
'encode bianry
bcode$ = ""
curcnt = 128
tmp2 = NumVal
For tmp = 1 To 8
    If tmp2 - curcnt >= 0 Then
        bcode$ = bcode$ + "1"
        tmp2 = tmp2 - curcnt
    Else
        bcode$ = bcode$ + "0"
    End If
    curcnt = curcnt / 2
Next tmp

'decode binary
curcnt = 8
newnum = 0
For tmp = 5 To 8
    If Mid$(bcode$, tmp, 1) = "1" Then newnum = newnum + curcnt
    curcnt = curcnt / 2
Next tmp
End Function
 
Last edited:
Hmm, looks ok. Just for speed purposes, most programmers setup a 256 position table with the address of each decode routine in a slot. Then just take the value of the opcode * "length of an address" and that's the index or displacement into the address table. Much, much faster than long chain of cases. An "invalid opcode" would just end up calling the "invalid opcode" decode routine, I don't have an 8088 opcode table in front of me.
 
We've been working on an emulator for an 8085, but I believe the instruction set of the 8086 is more complex (though I haven't investigated it).

Our source code is in C, but from what I remember from BASIC your code looks reasonable. I agree that the never-ending case statement is not the best way to go about a program like this. However, if this is your first time on this type of project, it would probably be the easiest to follow. You can always improve upon your code later.

Is this project for business or pleasure? I was just wondering, because unless you're trying to run your emulator on a non-intel platform, even the most recent PCs should run the 8086 code.

We decided we could not use any of the several available emulators for our project, due to a unique timing mechanism in the hardware we are emulating that triggers a regular interrupt. None of the emulators we tested could handle this requirement. Also, since we don't really use Windows (unless it is absolutely necessary), we often find ourselves building our own applications.
 
In the Altair32 Emulator, I used a call table -- basically an array of function pointers with each entry the name of the handler for that opcode. The "clock" routine grabs a byte from memory, decodes it, calls the opcode handler which increments the PC and accesses memory depending on how many bytes are required by the opcode. The clock routine also handles single-stepping and cycle counting, among other things. Take a look at my code (from i8080.c; lots of trimming done):

typedef void (*op_func)(byte); // Itable typedef
static void lxi(byte); // prototype for opcode handler

static op_funct Itable[] = {nop, lxi, ...} // opcode 0, 1, ...

The lxi function then has a switch/case state machine in it that does different things based on which T-state the CPU is in (fetch, read operand, write memory).

I have seen it done as a switch/case construct with inline macros (Marat Fayzullin's Z80 and 6502 emulators for example). I can't opine to the speed difference however.

The 8085 is more similar to the 8080 than the 8086, with the principal difference being the RIM instruction (IIRC). There are several 8086 emulators out there already -- I think PCE is one of them. I looked into it for a Tandy 2000 emulator (which used the 80186, and PCE had a 186 superset) but the project never got off the ground because the T2k hardware is so non-standard and I only had a partial BIOS listing.
 
Mike,

I encourage you to write this in C. You will be much more productive and happier than you will be with any form of BASIC.
 
thanks for the tips everybody. i've added a few instructions to the code since last night. would there really be a real difference in speed with a call table?
 
i have a quick question...

first of all, the emulator has come a long way from last night. it now emulates 24 instructions. i even added a rudimentary 80x25 text display window that dumps out every other(making it color-less) byte starting from B800h.

anyway, i compiled the micro-os kernel sample from emu8086 to a .BIN file and i have been running it step by step in that emu alongside mine.

my emu so far emulates the first 6 instructions of the micro-os binary perfectly, and all the registers and flags etc match up with emu8086 after each instruction....... UNTIL i get to the "CALL 002E8h" (its an immediate 16-bit call, opcode E8h) operation.

emu8086's IP register becomes 02E8h, but the IP reg in my emu turns into 8F01h. hopefully somebody can help me on this... it's like 3:30 am and i've been programming for hours.

here's the code for my CALL emulation:
Code:
    Case &HE8 'CALL imm
        newIP = FullReg(RAM(PC + 1), RAM(PC + 2))
        SP = SP - 2
        RAM(SP) = HighNib(IP)
        RAM(SP + 1) = LowNib(IP)
        IP = newIP

i have a feeling it's a problem with the FullReg function i wrote? here is that:
Code:
Public Function FullReg(RegHigh, RegLow)
FullReg = (RegHigh * 256) + RegLow
End Function

^ it is, obviously, supposed to combine two bytes to read them as a 16-bit value.

maybe it's too early in the morning and it's making me stupid... i think i'm going to go to sleep now so i can get up for work. maybe coming back to this fresh tomorrow night will help, but if any of you guys see a glaring problem please let me know. :p

thanks!
 
Last edited:
nevermind! fixed it... the 16-bit value following the CALL opcode is a relative offset from the instruction position after the CALL, not an absolute address :p
 
i am running into trouble with the whole mod/reg/rm byte encoding thing. :mad:

if anybody can help give me a more detailed explanation than what i've been able to find around the web, please let me know! coding this emu has not been too hard up until this point. :(
 
i am running into trouble with the whole mod/reg/rm byte encoding thing. :mad:

I'm not sure exactly what you're referring to. Again, our project was 8085 based, not 8086, and I know that the 8086 is more sophisticated, but I don't really have a clue what "mod/reg/rm byte encoding" means.

Of course, I know what a register is, and the RM command is a Return on Minus, if I recall correctly, but I don't think that's what you are referring to with "rm".

Is it bit manipulation that is troubling you? Activating specific bits in a byte? Logical .AND. and .OR.? I don't have the 8086 data sheet in front of me, but I'll check it out when I get home...
 
I'm not sure exactly what you're referring to. Again, our project was 8085 based, not 8086, and I know that the 8086 is more sophisticated, but I don't really have a clue what "mod/reg/rm byte encoding" means.

Of course, I know what a register is, and the RM command is a Return on Minus, if I recall correctly, but I don't think that's what you are referring to with "rm".

Is it bit manipulation that is troubling you? Activating specific bits in a byte? Logical .AND. and .OR.? I don't have the 8086 data sheet in front of me, but I'll check it out when I get home...

well it stands for mode/register/register-memory i think. it is used in a few of the MOV operations. i understand basically how it works.

its 1 byte, and the bits are separated like this:

the 2 left-most bits specify the addressing mode, the next 3 specify a destination register, and the last 3 specify a source register.

what makes it a bit more complicated is that the mode bits tell the CPU what addressing mode is to be used to determine the source location. direct, indirect, or just use the value in the register. theres one more mode that i'm forgetting i think.

i just have to do some more research to understand everything more specifically. then i have to write a bunch of code to parse the byte and properly work with it. it's more complicated than it sounds. :eek:
 
Ok, I get it...you're right, the two high order bits determine how the other six are interpretted. Without the book in front of me, I can't give a specific example, but I know that 10 in the high order bits specifies a differenct type of operation that a 01. I had to run to Kentucky last night rather unexpectedly so I didn't get a chance to look for my 8086 datasheet, but I think I should be able to find it soon, and see how it compared to the 8085--at that point I may be able to offer some more solid advice.
 
8086 emulator

8086 emulator

Currently, I am developing an 8086 emulator (in C) for the Symbian mobile platform (which I plan to release as open source, eventually). It is in early stages of development, but can already decode and execute some simple .COM files.
In the moment, I plan to implement graphics emulation, but don't know here to begin.
Let me explain: in the current version, the emulator is a console application. It can be compiled for both Win32 and Symbian (and maybe Linux as well, but I haven't tested yet). When thinking about emulating the graphics mode (I plan to support initially VGA mode 13h), I have some problems to face:
1) Which graphics library should I use? SDL? direct GDI of the target platform? Here I see the biggest problem, since SDL for Symbian is quite heavy!
2) should the emulator open a graphic window (separated from the console)?
3) How to map VGA mode 13h resolution to the mobile Symbian Series60 screen resolution?
4) and finally, but not less important, how to represent read/write access to video memory?

Any help would be appreciated. If you want to exchange ideas, here I am!
 
1) Which graphics library should I use? SDL? direct GDI of the target platform? Here I see the biggest problem, since SDL for Symbian is quite heavy!
2) should the emulator open a graphic window (separated from the console)?
3) How to map VGA mode 13h resolution to the mobile Symbian Series60 screen resolution?
4) and finally, but not less important, how to represent read/write access to video memory?

1. Anything that is as light as possible so you can have a single bitmap to manipulate that will get mapped directly to the screen. An 8086 .COM is not going to say "draw a line from a,b-x,y", it's going to manipulate memory directly. You need a way to translate that memory to screen memory.

2. No, have everything in the same window. Look at how DOSBOX operates.

3. No idea, what is the resolution of the mobile Symbian Series60?

4. You keep the host program's screen memory in a memory buffer that has nothing to do with the actual screen of the symbian, as previously mentioned.
 
Back
Top