x8086NetEmu, an 8086 emulator written in VB.NET

I remember the time I used to know the 8088/8086, inside out!
Heck! I new the 80286 inside out!

The first program (if you wanna call it that) I wrote was in a Casio Pocket Computer; I then moved to a TRS-80 and then a Commodore 64; later the 128, and then an XT, and even on an HP 28 and on a 48 and… well, you get the picture: I’m old.

The important thing here is that the Casio Pocket Computer had to be programmed using a minimalist version of Basic and, for the most part, it did the job. But not for me. I simply wanted to do things that its Basic implementation couldn’t do.

Then, I learned about a programming language known as “assembler” or “assembly“.

This coincided with me having the Commodore 64.
One of the first programs I wrote for the C64 was an “assembler interpreter” that substituted its default Basic-based one.
It was cool and it was a nice way to familiarize myself with all the “hidden secrets” (thanks to the Transactor Book of Bits and Pieces #1) the C64 had, without having to rely on a bunch of PEEKs and POKEs.

Several years later (in 1990, to be specific) I got my first “real” computer: an x286 /w 1MB of RAM and a 20MB hard disk.
One of a kind, by those days standards.

At that very same time I was taking a class in the university that required you to learn (on your own) x86 assembly language, so I did… and I loved it, and one of the things I loved the most is that the more assembly language you learn, the more you understand the inner-workings of the computer you’re working on.

<irrelevant to the article>

Show

One day, in a laboratory for that class, the professor (who had a crush on my, then, girlfriend, today, wife) gave us the following assignment: to write a program (in assembly) that could multiply two numbers.
As he dismisses the class, calls me and tells me that, since I have some (apparent) experience I should do a program that calculates the factorial of any whole/positive number… up to ten.
Just to mess with him I wrote a program that could calculate the factorial of any whole/positive number as long as the machine had enough RAM to perform the computation.

Since that day, I started coding many programs in assembly language, most of them useless, others quite interesting and complex.


</irrelevant to the article>

I remember understanding everything about the 8086 — everything!
The CPU itself, the way memory worked, the latches used to transfer information, the PIC, the PIT, the hard disk controller… everything!

Fast forward a couple of decades…

And so I thought, about 4 years ago, that I could write an 8086 emulator.
This is a project I work on when I’m, well, bored… but honestly, I never thought it’d turn to be one of my greatest nightmares!

One of the most important motivations for this project was to:

  1. Satisfy my ego (knowing that I can code an emulator)
  2. Hopefully, help others (specially youngsters) better understand how computers work by providing some visual feedback representing the way all the components in a (8086) computer work

So, highly motivated (and even more naively) I started coding away…

The Emulator

One of the first things I did was to create a machine code decoder. That is, a program that could read byte-code machine data and translate it into assembly language.

For example, given this byte-code sequence:

The decoder produces the following assembly code:

Remember the assembler interpreter for the C64 that I mentioned earlier? Well, that was extremely easy to do because the C64 has one byte-code per instruction.
LDA, STX, BRK, etc… they all have their unique byte-code representation.
Intel’s processors, however, use an encoding mechanism to represent all the available mnemonics, and their addressing modes, in just one or two bytes making its decoding incredibly complex.

Anyway, once I was able to properly decode (interpret byte-code into actual assembly code) I started coding the “emulator”.
The emulator is nothing more than a series of routines that mimic what the actual 8086 processor would do.

For example, this is the emulated code for the CWD mnemonic:

Then, I implemented the necessary code to also emulate the registers, the flags, the memory and the stack.

Registers

The registers were quite simple to emulate and I think I did a quite good job at doing so, as it is not a trivial thing.
All the registers in the 8086 are 16bits (2bytes) but not all of them can be split.
For example, AX (the accumulator) is a 16bit register that can be split into two additional registers: AH (for the high byte) and AL (for the low byte). But, the DS (data segment) register cannot be split so there’s no direct way of addressing its upper or lower bytes.

This poses a challenge: how do you create a unique object that can represent both splittable and non splittable registers and that, at the same time, is fast enough to work under such conditions?

Flags

Flags support is really easy to implement as long as you keep in mind that the 10 flags supported by the 8086 leave inside a 16 bit memory area.

Memory

I think I’ve done a pretty good job at emulating the memory.
It is done in such a way that it is very easy (and quite fast) for the emulator itself to easily read/write to/from it.
Nearly every single operation in the emulation process requires a memory access so this implementation is one of the most important aspects of the whole emulator.
Also, the implementation also uses some shortcuts (helper functions) to simplify the code so that it is easy to read and understand — remember that one of the purposes of this emulator is to serve as an educational tool, not to be the fastest.

Stack

The stack was a little trickier due the way it affects the SS (stack segment) and SP (stack pointer) registers.
Internally, the emulator provides two methods to either push or pop bytes from the stack with ease, and lets the memory manager handle the the way the SS and SP registers change their values based on the type of access performed on the stack.

PAGE: 1 2 3 4

  • Patrick VDW

    Hello,

    Me again… sorry for that, but perhaps now I’m in the right blog.

    Is it possible to send me the x8086NetEmu source please? I’m trying to “understand” a minimal PC (e.g. BIOS, CPU, Memory, some devices and then booting do DOS 1) so your code would help me enormously…

    You can send it to: pat_vdw@yahoo.co.uk

    Many many thanks in advance!

    Patrick VDW

    • Hi Patrick,

      I will be posting the sources for the emulator in a couple of days.
      I’m trying to fix a couple of bugs before I post it.

      • Patrick VDW

        Hello,

        What a quick answer: you just made my day!

        Many thanks!!

        Patrick

        • Patrick, I’ve just posted a link to the source code and binaries.
          You’ll find it under the “August 20, 2013” update heading.

          • Patrick VDW

            Hello,

            I have them. Again: many many thanks…

            If you ever want to visit Belgium, give me a note and I’ll buy you some good Belgian beer!!

            Kind regards
            Patrick

          • That sounds awesome!
            I’d love to visit Belgium!!! ๐Ÿ˜‰

  • Mike Chambers

    This looks interesting, but I’m having a problem running it. All I can get out of it is SlimDX errors. I’ve got the runtime installed, but the program refuses to run. I tried it on both my main desktop, and on my netbook. Same issue. (BTW, I’m the writer of fake86)

    I’m interested in seeing how an 8086 emulator written in VB.NET actually performs. I wrote one in VB6 just for fun about a year ago. I’ve never seen one in classic VB6, and thought it would be kind of funny. It runs really badly. (of course)

    • Hi Mike.
      First of all, congratulations on your fake86 — it is simply amazing. I’ve used some of your own code (from an old release) to develop several of the low level CPU emulation in x8086NetEmu.

      Now, as for the problem you’re having.
      I’m not sure what could be causing that… and, honestly, I thought I had included everything in the ZIP file so that anyone could re-compile it.

      I will now re-build the ZIP archive and make sure everything is included.

      I’ll post back when the new ZIP archive is available.

    • Mike, me again…
      I have just updated the ZIP file and I can confirm that it now includes all the necessary files to compile.

      Please (please!) let me know of any bugs you find ๐Ÿ˜‰

      • Mike Chambers

        Excellent! Thanks.

        I believe I know why you can’t boot newer versions of DOS. You haven’t implemented group 5 opcodes. Looks like you’re sending ops FEh and FFh to the group 4 handler, but FFh is group 5. It handles mostly indirect calling and jumping.

        When I first started writing my emulator, I had bugs in group 5. Once I fixed it, it booted DOS 6.22! ๐Ÿ™‚

        It’s probably the source of a lot of your problems.

        • Unfortunately, that wasn’t the problem as the Group5 is already implemented inside the Group4 function…

          What I did find, debugging my code vs yours is that at some point the code that is loaded from the boot disk is offset by one byte in my code.

          So the whole set of instructions are not at the correct location (IP).

          I honestly don’t even know where to start looking for this one!

          • Mike Chambers

            Are you taking into account that the INC/DEC in group 4 are byte operations, but in group 5 they are word ops?

          • Yes.
            Anyway, I think I have found the problem…or at least, one of them.

            Just so you know, it is related to your “signext” function…

            I’ll let you know if implementing it solves the problem(s).

          • Mike Chambers

            Good luck! It will be fun to play around in it if it can boot a newer DOS. Maybe even run some games…

          • That’s the idea ๐Ÿ˜‰

            One question through.
            Since type casting in VB.NET doesn’t really work as in c++, would you say that my interpretation of your signext function is correct?

            v = v And &HFF
            If (v And &H80) 0 Then v = &HFF00 Or v
            Return v

            Basically, I first convert the value into a byte and then, if the byte is signed I OR it with FF00

            It appears to work on some cases, but not all…

          • Holy cr…
            It just booted 6.22!!!

            Unfortunately, it crashes right away.
            Guess I’m still missing a couple of “tweaks”.

            (Sorry for the multiple posts — I got a bit carried away)

          • Mike Chambers

            Yeah, that should work. Good job getting 6.22 kind of working! Does the program hard crash or do you mean DOS just goes nuts or stops responding or something?

          • After the command prompt appears, if you try to type any commands it either shows a “bad command or argument error”, crashes (cpu enters loop cycle), or halts the CPU.

          • FINALLY FOUND THE DREADED BUG!!!!

            I’ll post a detailed description of the problem later… but for now, here’s the x8086NetEmu running QBasic from DOS 6

          • Mike Chambers

            NICE job!! Haha, this looks cool. Will download and play with it.

          • Mike Chambers

            Alright, yeah it works pretty nicely now for the most part. Great! I played Space Commander a bit, which was perfect. The only problem I had was that often the keys would repeat when I pressed them. Also, I can’t compile the source again because it’s looking for the Binary and ConsoleCrayon stuff in a hardcoded path on C: like before. Good work though.

  • Mike Chambers

    I see you say you can’t run MINIX 2.0. Fake86 won’t boot those floppies past the boot monitor either. Download the “DOSMINIX” package of version 2.0.2. and try booting the MINIX.MNX hard drive image directly. That boots in Fake86. Hit escape when it gets to the monitor and type this:

    hd=bios
    save
    boot

    …and see how that works for you.

    • No. It doesn’t boot from that image either.

      I know I still have a serious bug since most of DOS 6.22 utilities such as edit.exe and qbasic.exe, for example, won’t work on x8086NetEmu, yet they work perfectly fine under fake86.

      The funny thing is that the DOS 6.22 image I’m using boots just fine with x8086NetEmu but freezes with fake86…

      Anyway, thanks for pointing me to DOSMINIX.
      Hopefully, that bug that prevents x8086NetEmu from booting it is also related to the issue I’m having with most of the DOS 6.22 programs.

      • Mike Chambers

        Did you make install DOS on that image that fake86 locks up on from inside your emulator? If so, maybe you’re not using the same head and SPT values that I am for a hard drive image. That could cause it. That may even cause that MINIX image to be reading in the wrong sectors. It *may* explain your divide error crash. It’s a possibility anyway. Does your DOS image work in QEMU?

    • With the image you suggested, the emulator doesn’t do anything… CS:IP jump to some invalid address and it freezes.

      However, I have a Minix 2.0.2 image, which x8086NetEmu has never been able to boot, but it does display a boot prompt.

      So, after the prompt, I followed your instructions and this is what I’m getting:

      • Mike Chambers

        Well it’s getting there at least, lots of progress. It’s so hard to debug this stuff! Could be the divide code, but could also have nothing to do with it whatsoever lol. So much fun.

        • ha haha ha…

          The fact that the program is reporting a divide error could just come from a poorly implemented DAA opcode. Who knows!

          Anyway, the fact that fake86 can boot that image (although I haven’t tried it yet), might help me figure out what’s going on.

          Oh… and I’m “pretty sure” that all routines in Group 3 (including DIV) are properly implemented; otherwise, the emulator wouldn’t be able to even boot DOS 2.x

          That said… I could be just plain wrong. Just as you said: this is so freaking hard to debug!

          …but it’s fun…;)

    • Mike, when you have the time, could you please check why fake86 is not booting the “MS-DOS 6.22.IMA” image I’m using?

      To obtain it, just download the latest version of the x8086NetEmu.

      It baffles me that fake86 is so MUCH MORE complete than x8086NetEmu and yet, it fails to pass executing cd1.sys from the image’s config.sys.

      I mean, at this point, I’m just looking at the way we have implemented some of the most complex opcodes (such as those from Group 2).
      I don’t know… maybe if you can find some bug in your code, might help me find a dozen in mine ๐Ÿ˜‰

      • Mike Chambers

        I didn’t notice this message before. I just saw your next message. I’ll have a look and see if I can figure that out. Too bad you don’t have a forum here, it would be easier to talk than these messages.

        • Yes, this messaging system is really annoying.
          I do have a forum, but it’s on the “commercial” web site — I’ll do some research and see if there’s a simple forum system for WordPress.

        • The forums are now available!

          Here’s a direct link to the x8086NetEmu’s forum:
          http://whenimbored.xfx.net/forums/forum/software-discussion/x8086netemu/

          • Mike Chambers

            Great! It won’t let me start any threads though, it says I need to be logged in but I don’t see any special log in or register links for the forum part. Maybe I’m overlooking it.

          • Click the “Log In” link on the right hand side (bluish section) and select “Register”.

            Registering on the blog is the same thing as registering on the forums.

            I wish the forums could support OpenID… but unfortunately, it doesn’t.

      • Mike Chambers

        Okay, figured it out. CD1.SYS first of all is trying to execute some 386 opcodes. It’s trying to use op 66h which is operand-size prefix. It’s trying to execute some 32-bit operations, but that’s not exactly why it’s hanging.

        If I change my port read function to return 00h instead of FFh on a port that is not connected to anything, it continues to the DOS prompt as your emu does.

        However, I verified on my real IBM XT that the correct behavior is to hang at “BANANA”. FFh is actually what should be returned for a port not connected to anything (open-bus). This is verified further by the fact that when I return 00h, the BIOS reports a bunch of hardware that I’m not actually emulating. Joystick, extra serial ports, etc.

        If you fix that open-bus read value, yours should also hang in the same place. The proper solution is to just take that device driver out of config.sys.

        • Mike, do you know the port number that is being queried?
          As far as I know, I always return 0xFF for ports that the emulator does not support.

          • Mike Chambers

            It’s reading CFCh and CFDh. Not sure what else it might be.

          • That’s weird…
            I can confirm that the emulator is returning 0xFF for all unsupported port addresses, including 0xCFC and 0xCFD.

  • Pingback: x8086NetEmu for Linux, Mac OS X and Raspberry Pi | When I'm BoredWhen I'm Bored()

  • Pingback: VB.NET and its broken data types | When I'm BoredWhen I'm Bored()

  • Pingback: Factorial calculation algorithms - When I'm Bored()