• Please review our updated Terms and Rules here

8086 emulator tech discussion

The right way to do this would be to use a union:

Code:
 union _bytewordregs_
{
   uint16_t wordregs[8];
   uint8_t byteregs[16];
} regs;

  regs.wordregs[0] =1;   // assign value of 1 to a word register
  regs.byteregs[2] = 2;   // assign value of 2 to a byte register

On the portability issue, you may run into problems if you try to run your emulator on a big endian CPU, such as a 68000, since the ordering of high- and low-order bytes in a word is the reverse of the x86 family.
 
The right way to do this would be to use a union:

Code:
 union _bytewordregs_
{
   uint16_t wordregs[8];
   uint8_t byteregs[16];
} regs;

  regs.wordregs[0] =1;   // assign value of 1 to a word register
  regs.byteregs[2] = 2;   // assign value of 2 to a byte register

On the portability issue, you may run into problems if you try to run your emulator on a big endian CPU, such as a 68000, since the ordering of high- and low-order bytes in a word is the reverse of the x86 family.

from the research i've done, there isn't really anything reliable as far as detecting endianness regarding compile time defines. i guess i could maybe use a lookup table for the byteregs that i generate upon execution? one little lookup table as an array shouldnt cause TOO much slowdown. probably not even noticeable at all.

btw, this new C version of the CPU core is WICKED fast! :D

the BIOS runs POSTs and hits int 19h before you can ever see the screen count/test the RAM!!!!!! maybe if you look really closely, for maybe around 30-40 milliseconds you can see it. this is on my Phenom II X4 955 @ 3.6 GHz. it's damn fast on an 800 MHz P3 too!

i have the video rendering forked to it's own thread to keep it all very fast. most of the frequently used routines are macros now, like getreg/putreg/getmem/putmem and the routine to parse addresing mode bytes, as well as some others but there is still more optimizing i can do. i think this will end up killing DOSBox in speed, but granted DOSBox has a much more complex CPU core.
 
Last edited:
While it's possible to detect endian-ness of a platform at execution time, it's not a good idea because your code will be filled with run-time tests.

Better is to define a preprocessor macro whose treatment of bytes mapping to words is dependent upon a predefined preprocessor variable. That way, you compile and execute only the code version needed.

Like this:

#ifdef LITTLE_ENDIAN
#define BYTE_TO_WORD...
#define UPPER_BYTE....
#define LOWER_BYTE...
#else // if big-endian
#define BYTE_TO_WORD...
#define UPPER_BYTE...
#define LOWER_BYTE...
#endif
 
While it's possible to detect endian-ness of a platform at execution time, it's not a good idea because your code will be filled with run-time tests.

Better is to define a preprocessor macro whose treatment of bytes mapping to words is dependent upon a predefined preprocessor variable. That way, you compile and execute only the code version needed.

Like this:

#ifdef LITTLE_ENDIAN
#define BYTE_TO_WORD...
#define UPPER_BYTE....
#define LOWER_BYTE...
#else // if big-endian
#define BYTE_TO_WORD...
#define UPPER_BYTE...
#define LOWER_BYTE...
#endif

oh, maybe the info i was looking up was outdated. (or just wrong?) is LITTLE_ENDIAN always going to be defined on recent gcc versions if its a little endian system? if not, i can just do that and require the user to configure it before compile. or i guess i could make some sort of shell script that can detect and define as needed. yeah i would definitely rather not have it detect at execution time.
 
Either way, that should work.

I've sometimes wondered how well "C" constructs translate on architectures that don't follow the same trodden ditch. For example, those which do not have a word length that is a multiple of 8 bits, or do not have anything other than full word (e.g. 64 bit) addressing or don't have twos complement arithmetic (or even binary arithmetic). FORTRAN can handle those, but I doubt that C could do a decent job of things.

(I've programmed machines with all of those characteristics and the language wasn't C--but every one of them hosted FORTRAN)
 
Last edited:
Either way, that should work.

I've sometimes wondered how well "C" constructs translate on architectures that don't follow the same trodden ditch. For example, those which do not have a word length that is a multiple of 8 bits, or do not have anything other than full word (e.g. 64 bit) addressing or don't have twos complement arithmetic (or even binary arithmetic). FORTRAN can handle those, but I doubt that C could do a decent job of things.

(I've programmed machines with all of those characteristics and the language wasn't C--but every one of them hosted FORTRAN)

that is a good question. fortunately, a machine like that would be rare enough that i wouldn't even worry about it.

and something interesting i've just found, the C version of this program as it stands so far can run QuickBASIC. for some reason i was never able to get it to run in the FreeBASIC version. i wonder why that is.

fake86-quickbasic.png
 
I know that I'm going to get blasted for this, but I find that "C" seems to be better about organizing data structures than Quick BASIC.

Is there such a thing as a portable QB program? Is there a version of QB that runs on a MIPS R4000, say?
 
I know that I'm going to get blasted for this, but I find that "C" seems to be better about organizing data structures than Quick BASIC.
That's not an unreasonable statement -- Both C has some concepts that are pretty much absent in basic -- the big two being pointers and complex data types (struct in C, record in Pascal).

The difference in memory limits is also nice -- qbasic was limited to single 16 bit segments (64k) for code, variables and itself respectively; on anything less than a 286 most C compilers will let you use all free ram for variables as a heap.

Turbo Pascal is similarly blessed having the 'record' type, pointers, and the concept of a heap instead of limiting you to just a stack.

Though he was working on the basic one in freebasic, which I believe introduces a few of those concepts to the language; in any case it's a 32 bit compiler so all those 16 bit limitatations go bye-bye...

Though question: what flavor of C compiler are you using?
 
Me? Depends on the platform, but if it's x86 of any flavor and Windoze, Microsoft. Not that it's the best, just a nasty habit when working with Microsoft SDKs and DDKs--it was easier than trying to figure out how to get everything to another vendor's compiler. I've been with Microsoft on x86 C since it was Lattice.

In *nix, generally whatever the default host compiler is (usually gcc).

In µC work, whatever's available. I do miss decent assemblers in the µC world, however.
 
Actually that question was directed at Mike... Though I'd take it easy on M$ compilers -- while horribly platform tied they do tend to perform many times better than that fat bloated train wreck GCC...

Though with GCC being over-the-top on being cross-platform it should come as little surprise it does none of them particularly well... good thing we can just throw hardware at the problem :(

I've been looking at working in C again, but it really is the LAST programming language I'd look at -- it's always bothered me how needlessly cryptic, useless it is "out of box" without includes, loose it is on the typecasting; I often wonder if the joke about "Unix and C are a hoax" is actaully a joke. HOW it ended up the default choice for most programmers and why most other languages after it are pretty much just retreads (java and php for example) is really hard for me to fathom.
 
actually, QuickBASIC did support complex data types.. it's called "Type".. for example:

Code:
Type mytypename
    somevariable As Integer
    anotherone As Long
End Type

Dim customstuff As mytypename
customstuff.somevariable = 12345
customstuff.anotherone = 6789

and i'm actually using gcc. i don't any real complaints about it. i've never noticed anything i compiled in MS Visual C++ perform much better if at all, but as you mentioned it might be just because modern machines are so powerful to begin with. i will stick with gcc because i love the portability.

and yes, FreeBASIC has all sorts of great features. it does pointers, inline assembly, and even supports unions.
 
now this is cool.. i took my NES emulator, tweaked it to compile in DOS, compiled and ran it inside Fake86. homebrew emulator running inside homebrew emulator. madness i tell ya!

fake86-moarnes2.png


:p
 
actually, QuickBASIC did support complex data types.. it's called "Type"..
Quickbasic, yes... but I thought it was pulled from qbasic as part of the way to get people to pay for it... along with stripping out the compiler. qBasic basically being the closest thing M$ has ever done to a shareware release in the hopes people would pay for Quickbasic.

Since they aren't the same thing...
 
Quickbasic, yes... but I thought it was pulled from qbasic as part of the way to get people to pay for it... along with stripping out the compiler. qBasic basically being the closest thing M$ has ever done to a shareware release in the hopes people would pay for Quickbasic.

Since they aren't the same thing...

i just checked it, QBASIC does allow type structures too.
 
Quickbasic, yes... but I thought it was pulled from qbasic as part of the way to get people to pay for it... along with stripping out the compiler. qBasic basically being the closest thing M$ has ever done to a shareware release in the hopes people would pay for Quickbasic.

Since they aren't the same thing...

I had always heard that Qbasic's reason for existance was Bill Gates insisted that DOS include a version of BASIC because that's what gave him his early success, and they didn't want to give away full QuickBASIC, and basica/gwbasic were getting a bit long in the tooth.

That being said, that story seems to contradict/conflict with various other things said about Gates, so who knows...
__
Trevor
 
big endian/little endian detection

big endian/little endian detection

Re. detecting endianity in C at compile time: It's not entirely straight forward, so this is what I use. It works for Linux, IRIX, AIX, SUN, TRU64 and more. NB: What I have not tested is any incarnation of Windows (nor cygwin etc. either). I write all my code (e.g. emulators) so that it works on both little endian and big endian.
Code:
#if defined(__sgi)
# include <sys/endian.h>
#else
# if defined(__osf__)
#  include <machine/endian.h>
# else
#  if defined(__sun)
#   include <arpa/nameser_compat.h>
#  else
#   if defined(_AIX)
#    include <sys/machine.h>
#   else
#    include <endian.h>
#   endif
#  endif
# endif
#endif

/*
 * These macros may be left undefined if certain flags like _XOPEN_SOURCE
 * or _ISOC99_SOURCE are defined. Fix that if so:
 */
#ifndef BYTE_ORDER
# define BYTE_ORDER __BYTE_ORDER
#endif
#ifndef LITTLE_ENDIAN
# define LITTLE_ENDIAN __LITTLE_ENDIAN
#endif
With the above (which I usually have in a header file which I include where needed) I have reliable BYTE_ORDER and LITTLE_ENDIAN definitions, and that's all I need. I use them like so, usually to define macros which I use in the actual code:
Code:
#if BYTE_ORDER==LITTLE_ENDIAN
...
#else
...
#endif
 
Mike,

I am just amazed on the massive amount of progress you've made on this emulator. How much time do you devote to it? The IBM PC/8086 is a complex system/chip to emulate (compared to something like Apple II/6502). When did you start working on it?
 
Re. detecting endianity in C at compile time: It's not entirely straight forward, so this is what I use. It works for Linux, IRIX, AIX, SUN, TRU64 and more. NB: What I have not tested is any incarnation of Windows (nor cygwin etc. either). I write all my code (e.g. emulators) so that it works on both little endian and big endian.
Code:
#if defined(__sgi)
# include <sys/endian.h>
#else
# if defined(__osf__)
#  include <machine/endian.h>
# else
#  if defined(__sun)
#   include <arpa/nameser_compat.h>
#  else
#   if defined(_AIX)
#    include <sys/machine.h>
#   else
#    include <endian.h>
#   endif
#  endif
# endif
#endif

/*
 * These macros may be left undefined if certain flags like _XOPEN_SOURCE
 * or _ISOC99_SOURCE are defined. Fix that if so:
 */
#ifndef BYTE_ORDER
# define BYTE_ORDER __BYTE_ORDER
#endif
#ifndef LITTLE_ENDIAN
# define LITTLE_ENDIAN __LITTLE_ENDIAN
#endif
With the above (which I usually have in a header file which I include where needed) I have reliable BYTE_ORDER and LITTLE_ENDIAN definitions, and that's all I need. I use them like so, usually to define macros which I use in the actual code:
Code:
#if BYTE_ORDER==LITTLE_ENDIAN
...
#else
...
#endif

great little snippet! thanks, yeah that looks about perfect.
 
Mike,

I am just amazed on the massive amount of progress you've made on this emulator. How much time do you devote to it? The IBM PC/8086 is a complex system/chip to emulate (compared to something like Apple II/6502). When did you start working on it?

it's a labor of love. :p

i started like last november or december. for a while i was just not understanding certain things, and much hair was pulled out but eventually everything clicked. basman74 helped me out with some details here and there as well, since he's written his own emulator. (in return, i showed him how i handled the basics of unchained VGA mode so he could play wolf3d on his)

and yeah, i do have quite a bit of time available to work on it. i run a PC repair shop, so when i don't have any actual work to do i get to sit down and code. :)
 
Back
Top