An 8086 PC emulator in 4043 bytes
ioccc.org
ioccc.org
"CPU supports the full 8086/186 instruction set. Due to the complexities of the 8086’s arbitrary-length instruction decoding and flags, 8086 instructions are first converted to a simpler intermediate format before being executed. This conversion, along with instruction lengths and how each instruction modifies the flags, is assisted by some lookup tables which form part of the BIOS binary."
With that in mind, the BIOS is _hand-written_ and about 12kB (gzips to <4kB), so that's also a fantastic achievement. All-in-all, this is a wonderful entry.
I/**/n
The code up to that point (2100 bytes total) is roughly as long as the listed output of the size tool (1979). The difference is probably due to the fact that I just counted raw bytes rather than accounting for whitespace and such. My guess, then, is that /* */ without a space hits a bug in the size tool where it probably counts that as the beginning of a comment but misses the end, causing it to count the entire remainder of the program as a comment. The compiler, meanwhile, will see that as an empty comment and continue reading code.This was a fairly predictable consequence of providing an official size tool. Used to be, they just laid out the rules for figuring out sizes, and you had to either count manually, or build your own tool to count. Now, bugs in the size tool end up being bugs in the rules, and fun like this ensues. Whether this is good or bad is a matter of opinion. I would say it certainly makes things interesting!
Edit: HN formatting makes the inline form of that comment look like a C++/C99-style // comment. Fun!
I still haven't puzzled out exactly what all the variables are, but I think that this makes the structure much clearer.
Or after running through preprocessing (not a huge help I found, sadly): https://gist.github.com/peterc/8259713
I think the reason for doing it this way rather than putting the type declaration alongside the parameter is then it would also demand also adding an int declaration for the BX parameter which would then cause the code to be longer. That is to say with:
main(BX,char**nE){
.. it would throw an expected identifier error on the char. So you'd be putting the original: main(BX,nE)n**nE;{
Against this error-free alternative: main(int BX,n**nE){
.. but that's one byte longer.If I got any of this wrong, I hope someone will correct me though :)
It's only complex and irregular if you look at it wrong. ;)
The x86 (like the 808{5,6} and the Z80) instructions look organised in an octal format: ftp://mipt.cc/Opcode.txt
I haven't inspected the code in any detail to see if it takes advantage of this.
Then I saw AutoCAD, Flight Simulator, and Windows.
Mind = blown.
Just add the bios and image files elsewhere.
Just a small reminder how far we have come - and what Moore's law (RIP) has done for us. Yes the original PC had few enough transistors one could actually count them. NowadAys ...
An awesome piece of work
I agree regarding the BIOS, but this sounds like the BIOS image contains "microcode" necessary for the emulator to run, which would seem to be cheating just a little.
Also, if someone can tell me how to make Windows not make the emulator segfault on my machine, that'd be great.
EDIT: Oh, it works fine in a graphical terminal. I'm guessing SDL failing to init on a text terminal broke everything.
EDIT 2: ALL THIS, PLUS REVERSI! You can even play Reversi!
edit: P.S. working deobfuscation, i.e. you can build the executable. Including some minor corrections to the orgininal (e.g. 1MB of RAM allocation instead 2MB, BIOS file size limited to 65536-256 bytes, etc.). It a nice code, minimalistic, e.g. the "rep" prefixed instructions are actually repeated, not unrolled. Really optimized for code size. Even deobfuscated it takes 12-13KB and 22KB of executable (x86-64 Linux, stripped binary). Very cool.