July 6th, 2014, 20:42 Posted By: wraggster
This version has one major change and a few smaller fixes. Here is some additional info about the changes in this version.
[h=4]No more high-resolution timer support needed in the kernel![/h]The major change in this version affects the timer interrupt handling. My original timer IRQ code in zerox86 was based on the code I use on Android and Raspberry Pi, where the Linux kernel has support for high-resolution timers. In those platforms I let the kernel interrupt my code at the correct timer interval, thus making my emulator timer interrupts happen in real time. This is a simple and easy system, but since the GCW-Zero kernel only runs timers at 250Hz (4ms interval), this is not sufficient to emulate games that need higher frequency timer interrupts.
I had already coded a different timer interrupt emulation system for my Windows Phone 8 version of pax86. The Windows Phone 8 task switch frequency was even less than that of GCW-Zero, so on that platform I had to use an internal counter and then periodically check the elapsed time using performance counters. This has the drawback of needing extra memory accesses for the counter in the main opcode interpreter loop, so it slows down all the emulation. However, luckily on zerox86 I had one MIPS register still unallocated, so instead of using a counter in memory, I was able to use this register for the counter. This meant that instead of loading a value from memory, decrementing it, saving it, and testing it for zero (4 opcodes), I was able to simply decrement the register and test it for zero (2 opcodes) on zerox86. This still causes a slowdown, but running Doom timedemo it looks like it only slows zerox86 down about 3% (meaning the new version runs at 97% of the speed of the previous version). I find this an acceptable trade-off for running games at their proper speed.
When the counter register reaches zero, I then call the C routine clock_gettime(CLOCK_MONOTONIC, ×truct) to get the current time (with around a microsecond actual precission), and check whether it is time to start an emulated timer IRQ. I then reset the counter register to a suitable value (depending on the actual wanted IRQ frequency) for the next timer interval. Since I don't do cycle counting, this counter is rather approximate, which is why I try to have the counter reach zero several times during one timer IRQ period, and use the clock_gettime() call to determine the more accurate IRQ time.
I tested how much time the clock_gettime() call takes on GCW-Zero, and noticed that two adjacent calls return a time difference of about 1666 nanoseconds, and that I can call the routine over a million times in a loop that lasts for a single second. So the call is pretty fast, and it should not be a problem calling it at up to 100.000 times per second for a game that sets the timer IRQ to some very high value (like 33kHz, as in Wizardry 6). At those speeds this will of course still slow down the emulation quite a bit, but the normal timer IRQ speeds of less than 1000Hz should run quite fine, and even the games that play digitized audio using Direct DAC system (one sample of audio generated at every timer IRQ) should run reasonably OK. The actual routine that starts the timer IRQ handling in zerox86 is still not very fast, and I plan to still improve that in the future versions.
Here is a list of some of the games that will now run much better than before. Any game that used a higher timer speed than 250Hz used to run slower than normal.
[h=4]Fixed screen corruption in Ishar: Legend of the Fortress logo screen[/h]After I got the new timer system working, I wanted to take a look at the screen corruption problems in various games. I first started with Grand Prix 2, but since the corruption in that game happens only in the actual game and it takes a LONG time to get to that point in my debug-enabled DOSBox (which I use for comparisons), I decided to start with a different game. Ishar: Legend of the Fortress was a suitable test game, as in it the screen corruptions happens in the title screen as soon as the game starts. I spent a full day debugging the game in both zerox86 and DOSBox, but was not able to find the problem. On the second day of debugging I was able to get closer to the problem location, and noticed that something strange happens after the game has read the logo image from disk to EMS memory, and before it is copied from EMS memory to the VGA screen VRAM.
- Commander Keen 4 (sets timer to 560Hz)
- Ishar: Legend of the Fortress (sets timer to 13240Hz for Direct DAC intro music)
- Jill of the Jungle (sets timer to 6080Hz for Direct DAC sound effects)
- LineWars II (sets timer to 768Hz)
- Sid Meier's Colonization (sets timer to 607Hz)
I added full logging to this code, but I could find no differences between the code running on zerox86 and it running on DOSBox. However, when I instead added a memory watch to a pixel on the screen, and compared the operations that are performed on that pixel, I found that on zerox86 some operations were not performed at all! After some head scratching it finally occured to me that my full logging code recalculates the CPU flags after every opcode, which does not happen when running the code without logging. I then decided to test what happens if I forcibly recalculate the CPU flags after every opcode, and that fixed the screen corruption! So, finally I found out how the screen gets corrupt, now I just had to find out which opcode uses my lazy flags incorrectly. I changed the full logging code to optionally not recalculate the flags, ran both versions, and then compared the logs. There I finally saw the problem: I had forgotten to adjust the zero flag testing algorithm for LOOPNE and LOOPE opcodes when I made a small performance fix to the zerox86 version of my code! After fixing that problem Ishar logo began to draw correctly. Here are some screen captures, first the problem screen and then the fixed screen in the new version of zerox86.
For more information and downloads, click here!
There are 0 comments - Join In and Discuss Here