Aeon Emulator Blog

November 24, 2009

Aeon Version 0.51

Filed under: Aeon, New Version — Greg Divis @ 3:41 pm

It’s been a while since I’ve posted a new build. A lot has changed in the code, but the single biggest visible change is the addition of protected mode emulation. It’s very far from complete. Right now, I’ve only gotten two protected mode games to work: Doom and Rise of the Triad – even so, those only work correctly if you disable Sound Blaster sound effects. Why those two games? I just happened to have them on my hard drive. Here’s the complete change list:

  • 16/32-bit protected mode support added (no actual paging or protection works yet though)
  • Fixed consistency issues with 16 vs. 32 bit stack and instruction pointers
  • Fixed 32-bit addressing modes
  • Small Sound Blaster DSP fixes, still lots of playback issues though
  • Added support for a few FPU instructions
  • Added extended memory support (XMS)
  • Most instructions now work with 16 or 32-bit operand sizes
  • Fixed a few issues that caused hardware interrupts to get disabled when they shouldn’t
  • Eliminated generation of multiple identical operand decoders (reduces memory usage, may increase performance somewhat)
  • Fixed an issue with unchained 256-color (mode X) display modes sometimes getting rendered incorrectly

If you’re expecting your favorite 32-bit DOS game to work now, you’ll probably be disappointed (unless, of course, you wanted to play Doom with no sound). Also, performance is not all that great for 32-bit programs, as I haven’t really optimized it for speed yet. I’m going to focus on improving protected mode compatibility up through version 0.6; expect all of these things to gradually improve from here.

Download here.

November 20, 2009

Success!

Filed under: Aeon, Protected Mode — Greg Divis @ 9:06 pm

“…the worst part of this implementation is over” – Me, one post ago

I regretted typing that almost immediately, because right after that I found an embarrassing number of bugs in my code and faced hours of very tedious troubleshooting figuring out what was wrong. The result, though, was worth it:

DMP

What, a 32-bit protected mode MOD player is not cool enough? Fine:

Doom

Ok, so two 32-bit programs working does not mean I’ve got good compatibility yet. I’ll probably be posting a build 0.51 or so after I’ve had a chance to do some regression testing to make sure I haven’t broken anything. Right now, my plan is for version 0.6 to have decent protected mode game compatibility, so you should be able to see some continuous improvement as I approach it. That should also mean more frequent builds (I hope).

But right now, it’s Friday night. I’m taking a break. :)

November 15, 2009

Protected Mode – Not quite there yet

Filed under: Aeon, Protected Mode — Greg Divis @ 6:20 pm

After doing my homework on x86 protected mode, I’ve been working on actually implementing it. As expected, it’s not easy and I’ve encountered lots of gotchas. Before I talk any more about this, here’s where I was at when I wrote this post:

image

Unfortunately it crashes right after displaying that message, but believe me, it’s better than it was. I certainly have a greater appreciation for the kind of nightmare the Intel engineers that designed the 386 had to deal with – grafting an entirely different mode of operation while maintaining compatibility with 16-bit code. DOS extenders really took advantage of this to allow a 32/16-bit protected mode application to run on top of 16-bit real-mode DOS – and DOS/4GW was possibly the most commonly used of these extenders.

(As an aside, many of the protected-mode-related instructions begin with the byte 0F, so if you’ve seen something like “Opcode blahblah0F not implemented.” from trying to run something in Aeon, that’s probably why.)

At this point, I’ve gotten a DOS/4GW game to at least get its protected mode environment set up. The good news it that I think the worst part of this implementation is over – the crash I’m getting now is due to a feature I haven’t implemented yet, rather that one that I’ve implemented wrong some time in the past. Way easier to debug.

Also, to properly support DOS extenders I had to get an extended memory manager working (among other things). All of that is done, so I’m hoping that soon I’ll have more to show for all of this work than a screenshot of DOS/4GW starting up.

November 10, 2009

Decoding and Decoupling

Filed under: Aeon, Performance — Greg Divis @ 2:16 am

In optimizing code for performance, it’s crucial to know the critical path. Once you know which part of your code is most impacting performance, you know where to spend most of your effort optimizing. In Aeon, this means focusing on the instruction decoding, since every instruction must be decoded before it can be emulated. The x86 instruction set is a CISC architecture, so this means there’s a lot of instructions and a lot of data is packed into a small number of bits. For example, most of the x86 instructions accept multiple operands, with the operands consisting of any combination of CPU register, memory address, or immediate value. Add to this the fact that memory addressing has implicit register-based offsets encoded in the operand, with two completely different meanings depending on whether the instruction uses 16 or 32-bit addressing, and you have a lot to decode on potentially every instruction.

The more I learned about the x86 machine code, the more I wanted to decouple it from the actual instruction implementation. In other words, I wanted to be able to write code that would emulate the function of an instruction without worrying about the form its operands are in, and I wanted this to be fast. Eventually, I did succeed in this…mostly. Here’s what my 16-bit MOV instruction emulator looks like:

[Opcode("89/r rmw,rw|8B/r rw,rmw|8C/r rmw,sreg|8E/r sreg,rmw|A1 ax,moffsw|A3 moffsw,ax|B8+ rw,iw|C7/0 rmw,iw", AddressSize = 16 | 32)]
public static void MoveWord(VirtualMachine vm, out ushort dest, ushort src)
{
    dest = src;
}

Before I get into that giant Opcode attribute on the method, let’s dissect the method itself. The first parameter is present on every instruction, and it contains a reference to the current emulated system – this is mainly used by instructions that need to cause certain side-effects, such as updating processor flags or accessing memory in a specific way; for the MOV instruction, it isn’t used, but is still required due to the way this method gets invoked. Following the VirtualMachine are the operands for the instruction; in this case, the first one is marked with the out keyword, meaning that the operand is only written-to by the method. The second operand is only read from, so it is passed by value. Both operands are 16-bit values, implying that this emulates the 16-bit operand-size version of MOV. Finally, the body of the method simply assigns the value of the second operand to the first operand. The dest and src parameters could be registers, values in emulated RAM, or immediates, but to MoveWord they are all the same.

One of the things I like about C# is that you can add custom metadata to things like methods and then inspect these at runtime using reflection. In this case, the custom OpcodeAttribute class specifies all of the opcodes and operand formats for each instruction implementation. The | symbol inside the long opcode string above basically means “or,” so all of the substrings between them specify the different MOV opcodes and their operands. Note that all of them take exactly two operands and all of them have a writable first operand (immediate values only ever show up as the second operand). I’ll break down the first form of MOV:

89 Specifies a 1-byte hexadecimal opcode
/r The byte following the opcode specifies at least one register
rmw The first operand is either a register or a value in memory of the current processor word-size (16-bit, in this case)
rw The second operand is a register of the current processor word-size (16-bit)

This information, along with the out keyword in the method signature, allows Aeon to generate code for decoding these operands, fetching initial values, and then writing any out or ref values back. Basically, when emulation begins for the first time, all of the opcode methods are enumerated and the OpcodeAttributes on them are parsed into more manageable objects. From this information, a small IL method is generated to decode, call the implementation method, and then write back values if necessary. Because the resulting generated method gets compiled by the JIT like everything else, simple instructions like MOV end up getting automatically inlined, despite the runtime binding.

Besides the speed, another benefit to this is that it’s really easy for me to add new instructions (provided they don’t encode some type of operand I haven’t dealt with yet). All I need to do is add a static method with the appropriate attribute and the runtime binding handles the rest. The downside is that writing this IL generator was a big pain, and it was very hard to test and nearly impossible to debug. Hmm… I guess it was worth it…

Create a free website or blog at WordPress.com.