rpix86 - Blog
May 24th, 2015 - rpix86 work continues!
Okay, after a break of about a year, I am back working on my emulators! For the past several weeks I have been working either on my Windows Phone version of pax86 or on rpix86. I returned to working on my emulators in the beginning of April, when I got an email describing problems running TradeWars 2002 on Raspberry Pi. I have received various bug reports during the past year, and have been adding them to my todo-list, but this bug report was slightly different as it looked like the game should actually already work on rpix86. So, I thought I would spend some time looking into what is going on with this game.
Work on supporting TradeWars 2002
TradeWars 2002 has two versions, one running under DPMI DOS extender and one running without it. The problem was that the DPMI one exited with "Cannot enable A20" and the non-DPMI version with "Numeric co-processor required". What bothered me was that rpix86 should already support enabling/disbling the A20 line, and it also has support for the numeric co-processor. So why does the game not detect them?
I started with the numeric co-processor problem, as there are several ways the game could attempt to enable the A20 line, and I thought it might be difficult to determine which method the game attempts to use. After some looking into the rpix86 code, I remembered that I had only coded complete FPU support for 32-bit protected mode, as all the games I have encountered so far have only needed FPU support in that mode. The real-mode FPU code simply silently skipped all operations. Perhaps the game tried to use FPU opcodes in real mode, and when the results were not correct, it exited?
I disabled the real mode FPU opcode skipping code, and indeed, the game encountered an FPU opcode in real mode:
-----------------------[TW2002]------------------------ TW2002: Unsupported opcode! INTVectors=0x42788000 CPU: REAL, 16, 0000FFFF EAX=00000000 EBX=00003D38 ECX=00007FBD EDX=00001E6E ESP=00009FFA EBP=00000000 ESI=00000264 EDI=00000042 ES=02D3 CS=1E6E SS=2494 DS=20BF FS=0000 GS=0000 NV UP EI PL ZR NA PO NC VM=0 IOPL=3 1E6E:09EB D93F fstcw [bx]That looks like the game attempts to store the FPU control word into memory in real mode. I checked the game code further, and noticed that it does indeed do some floating point calculations in real mode. I decided to spend my Easter vacation working on implementing the real-mode FPU opcodes, as now I had a game to use for testing them. This work mainly consisted of adding the real-mode opcode tables, as the main FPU operations are the same between the real and protected modes. It took several days, though.
After I had implemented the FPU opcodes, the game progressed further, but then exited with the following message:
File Error (1) - TWNODE.DAT : Invalid Function 05/24/27 00:00:08 AM Game Halted: SHARE.EXE not loaded or unresponsive. c:\games\2002v309>I have spent several days trying to debug the game and see how it determines that SHARE.EXE is not loaded (well, it isn't loaded, but I would like to fool the game into thinking it is), but have not yet been able to determine this. I recently tested the game in DOSBox, and it gets the exact same error, so I can not even use DOSBox to debug the game behaviour. Perhaps I will look into FreeDOS implementation of SHARE.EXE to determine the possible detection routines that the game uses. However, I got a bit bored with working on this game so I switched to working on my Windows Phone version of pax86 instead. I wrote a blog post about my work there a couple of weeks back.
Implementing support for Borland RTM DOS Extender
I recently got an email with a debug log from Tyrian 2000. The game crashed in the IRET opcode, which in principle should be supported in both real and protected modes. So, I decided to switch back to working on rpix86 for a while.
-----------------------[RTM]------------------------ RTM: Unsupported opcode! INTVectors=0x7387c000 CPU: PROT, 16, 0000FFFF EAX=000002A8 EBX=00000FFC ECX=000000B7 EDX=00003000 ESP=00000F94 EBP=00000F8C ESI=00000F9E EDI=00001000 ES=005B CS=0020 SS=0018 DS=0018 FS=0000 GS=0000 NV UP DI PL NZ NA PO CY VM=0 IOPL=3 0020:000015EC CF iretSince the game runs in protected mode, there are several possible reasons why rpix86 would exit at that point. I improved my error logging so that instead of the generic "Unsupported opcode" rpix86 would report the unsupported situation with IRET that it encountered. Running with the enhanced logging resulted in this log:
-----------------------[RTM]------------------------ RTM: Unsupported IRET to outer level! CPU: PROT, 16, 0000FFFF EAX=000002A8 EBX=00000FFC ECX=000000B7 EDX=00003000 ESP=00000F94 EBP=00000F8C ESI=00000F9E EDI=00001000 ES=005B CS=0020 SS=0018 DS=0018 FS=0000 GS=0000 NV UP DI PL NZ NA PO CY VM=0 IOPL=3 0020:000015EC CF iretOkay, now that is pretty clear, as I had not yet added support for interrupt return to outer (less privileged) protection level. The fact that the running program is called RTM made me remember that I actually worked on similar issues when I added support for Jazz Jackrabbit into my zerox86 emulator. Since zerox86 runs on MIPS architecture instead of ARM as in Raspberry Pi, the assembler code is very different and thus I had not added similar support to my ARM emulators at that time. However, I had written several blog posts about supporting Jazz Jackrabbit in both my DS2x86 and zerox86 blog posts, so I thought that using those blog posts and taking the ideas and algorithms from my MIPS emulator code would make it reasonably straight forward to implement the same support to my ARM emulators.
Interestingly, when I started up Jazz Jackrabbit, it exited with "Cannot Enable A20". The same problem that the DPMI -version of TradeWars 2002 had. At this point I thought it would help me quite a bit if I got my debug-version of DOSBox running again. I had switched to a new hard drive and installed Windows 8.1 since I last ran Visual C++ 2008 Express for DOSBox, but luckily I still had the install program for that old Visual C++ on my old hard drive. It installed fine, and pretty soon I was able to run Jazz in DOSBox. I noticed that it uses the keyboard controller method of setting the A20 line, and somewhat to my surprise I noticed that I did not have a proper handler for that yet in rpix86. I implemented that, and then got Jazz go drop into debugger in rpix86 at the same point as where Tyrian 2000 drops. Thus, I switched to working on Jazz Jackrabbit, so I could follow my old blog posts step by step.
The first step was adding that IRET return to outer priority level case. This was not terribly difficult, as I was able to simply look at my implementation for zerox86, mentally convert the MIPS asm to ARM asm code line by code line. I had to be careful to use the correct CPU registers in my new ASM code, by comparing this code to the normal IRET code I already had implemented, though.
After I had implemented the IRET opcode, the game not surprisingly encountered the priority level jump to the opposite direction. This is done using an INT opcode.
-----------------------[RTM]------------------------ RTM: Unsupported opcode! INTVectors=0x42840000 CPU: PROT, 16, 0000FFFF EAX=00000300 EBX=00000021 ECX=00000000 EDX=000002A8 ESP=00000F9E EBP=00000FD4 ESI=00000F9E EDI=00000FA2 ES=005B CS=00B7 SS=005B DS=0000 FS=0000 GS=0000 NV UP DI PL NZ NA PO CY VM=0 IOPL=3 00B7:000001F7 CD31 int 31Very similar approach worked here, I implemented the new code based on my zerox86 code.
Next, the game encountered a HLT (halt) opcode in protected mode. This opcode is not allowed in any other priority level except 0 (the most privileged), otherwise it will cause a General Protection Fault Exception.
-----------------------[RTM]------------------------ RTM: Unsupported opcode! INTVectors=0x428b0000 CPU: PROT, 16, 0000FFFF EAX=00000303 EBX=00000735 ECX=00000000 EDX=000000F4 ESP=00000FCE EBP=00000FFA ESI=00002258 EDI=00002195 ES=0000 CS=0113 SS=005B DS=00B7 FS=0000 GS=0000 NV UP DI PL NZ NA PO CY VM=0 IOPL=3 0113:0000000C F4 hltThe game was running in priority level 3 at that point, so it was actually supposed to cause a GPF. However, I had not implemented any exception handling in rpix86 yet, so that was the next step in my work on supporting the RTM DOS Extender.
At this point I had to spend some time hunting down a bug I had typed into the new INT handling code. Such bugs are pretty difficult to hunt down, even when I know the bug is very probably in the code I just added. When staring at the code you have written, you always see it as you meant it, not as you accidentally typed it in. :-) Anyways, after fixing that bug I was back on track (as I kept following my prior blog posts for those MIPS emulators), the next problem was the LES opcode that encountered a segment that was not present.
-----------------------[RTM]------------------------ RTM: Unsupported opcode! INTVectors=0x42830000 CPU: PROT, 16, 0000FFFF EAX=00000100 EBX=000003F4 ECX=000004AA EDX=00002000 ESP=000003D2 EBP=000003D4 ESI=0000396C EDI=00000000 ES=009F CS=00DF SS=009F DS=00E7 FS=0000 GS=0000 NV UP DI PL ZR NA PO CY VM=0 IOPL=3 00DF:0000C391 C47E06 les di,[bp+06]Here I added the Page Fault handling using my new exception handling mechanism, and again was able to progress further.
There had been several additional steps at this point that I had had to do for my MIPS emulators. However, since those enhancements were done into the C code, I had already implemented them also for my ARM emulators. The MIPS and ARM emulators share most of the C code, but the ASM code is obviously separate. Thus I was able to skip any new changes for reporting no extended memory for the Borland RTM, adding the "last fit" memory allocation strategy, and returning the proper return value from DOS call INT 21 AH=4D.
The next step was to handle opcode 0x8E, which loads a segment selector register. This also needed page fault handling, much like the LES opcode above. As you may notice from the debug log, at this point the actual game [JAZZ] is already running, while in the previous log entries rpix86 was still running the [RTM] loader program.
-----------------------[JAZZ]------------------------ JAZZ: Unsupported opcode! INTVectors=0x427d8000 CPU: PROT, 16, 0000FFFF EAX=00000000 EBX=000003EE ECX=00000540 EDX=00000000 ESP=000003EE EBP=000003F0 ESI=00002386 EDI=000023C7 ES=009F CS=00E7 SS=009F DS=00EF FS=0000 GS=0000 NV UP DI PL ZR NA PO CY VM=0 IOPL=3 00E7:00000593 8E46FE mov es,[bp+FE]
Then I had to implementation the Page Fault handling also to the RETF opcode handler...
-----------------------[JAZZ]------------------------ JAZZ: Unsupported opcode! INTVectors=0x427f0000 CPU: PROT, 16, 0000FFFF EAX=0000FB43 EBX=00000000 ECX=000001C7 EDX=00000000 ESP=00001FFC EBP=00000000 ESI=00000282 EDI=000023C6 ES=01AF CS=00E7 SS=028F DS=01AF FS=0000 GS=0000 NV UP EI PL ZR NA PO CY VM=0 IOPL=3 00E7:000003A6 CB retf
... and next the corresponding Page Fault in the CALL opcode.
-----------------------[JAZZ]------------------------ JAZZ: Unsupported opcode! INTVectors=0x42810000 CPU: PROT, 16, 0000FFFF EAX=00000000 EBX=00000277 ECX=0000078A EDX=00000277 ESP=00002000 EBP=00000000 ESI=0000028B EDI=0000EA32 ES=0287 CS=01D7 SS=028F DS=0287 FS=0000 GS=0000 NV UP EI PL ZR NA PO CY VM=0 IOPL=3 01D7:0000000C 9AA4395702 call 0257:39A4
At this point the game started up, but then dropped to the debugger with an "Unsupported SB DSP command FF!". As I mentioned in the DS2x86 blog post, the game seems to have a bug where it attempts to send the DMA length as parameters to SB DSP command 0x1C (start 8-bit autoinit DMA). Since this command does not actually take any parameters, those extra bytes are interpreted as additional (undocumented) commands.
0417:25DE cmp word [22ED],0200 jbe 25F8 Jump if SoundBlaster DSP version <= 2.00 mov al,1C call 22F5 Send DSP command 0x1C (start 8-bit autoinit DMA) mov ax,[22F1] ax = 0x0800 dec ax ax = 0x07FF call 22F5 Send DSP command 0xFF (undocumented!) mov al,ah call 22F5 Send DSP command 0x07 (undocumented!) 0417:25F7 retI had hacked these unsupported commands 0xFF and 0x07 to be silently ignored in my MIPS code, so I added a similar hack to the ARM code.
At this point the game started into graphics mode, but it kept crashing almost immediately with "Unhandled exception 0008 at 3042 0020" followed by differing ErrCodes. At times this happened even before the game went into graphics mode, but usually during the intro graphics. The game immediately dropped back to DOS at that point, and usually the whole rpix86 went unresponsive after a few key presses. To be sure I also tested Tyrian 2000, and it crashed similarly, so this was obviously caused by some bug in my code. This never happened in my MIPS emulators, so it looked like this was something specific to my ARM implementation.
I googled for that Exception 0008, and it seems to mean "Double fault", which occurs when the processor detects an exception while trying to invoke the handler for a prior exception. I never generate that exception in my code, so it looks like the Borland RTM generates that by itself when it encounters that situation. But how do I find out where and why that happens?
I assumed that RTM considers exceptions and hardware interrupts to be equivalent in that situation, so the most likely cause was that my hardware IRQ emulation interrupts an exception handler. I began looking at how I disable hardware interrupts when an exception occurs. The code seemed fine, the interrupts are disabled as soon as the exception handler starts. I added logging for every situation where the Disable Interrupts flag changes value, but that did not seem to show anything out of the ordinary. Finally I added also logging for whenever an interrupt or exception handler starts, and that produced this interesting code snippet:
0277:000001C0 EAX=00003F60 EBX=05AF0005 ECX=00000060 EDX=00000000 ESP=000016DA Enable interrupts EBP=000016E4 ESI=00001A0B EDI=00001B3E DS=05FF ES=028F SS=028F 1 1 = interrupts are now enabled FB9C sti 0020:00000C7A EAX=00000006 EBX=05AF05FF ECX=00000060 EDX=00000000 ESP=00000FEC GPF Exception Handler starts EBP=000016AA ESI=00001A0B EDI=00001B3E DS=00BF ES=028F SS=0018 0 0 = interrupts are now disabled 558B push bp 0020:00000B52 EAX=00000006 EBX=05AF05FF ECX=00000060 EDX=00000000 ESP=00000FE6 Timer IRQ Handler starts EBP=000016AA ESI=00001A0B EDI=00001B3E DS=00BF ES=028F SS=0018 0 0 = interrupts are now disabled 558B push bpIt looked like the GPF Exception handler had just started and turned the interrupts off, but then immediately the timer interrupt handler started, even though the interrupts should have been disabled!
I looked into my code that disables the interrupts, and found a potential race condition. If an interrupt happened while the exception was being generated, it was possible for the interrupt code to mark that the next opcode to run needs to be the interrupt handler. The exception handler did not reset that mark when it turned the interrupts off, only the hardware interrupt handler handled this situation correctly. I made the exception handler behave similarly to how the hardware interrupt handler behaves, and after that change the game no longer encountered that Exception 0008 problem.
The next step was to add code to read data from file directly to Mode-X VRAM memory. This reading from file directly to graphics RAM is only done in two games that I have encountered, Heimdall which uses this in the EGA graphics mode, and Jazz Jackrabbit in Mode-X. The C call for this routine already existed but was commented out, as I had not implemented the actuall handler in the ASM code yet. Without this code the Jazz character in the 3D intro game did not have any textures and was black, as in this screen copy:
Adding the code was pretty simple, as I had commented the MIPS code pretty well in my zerox86 source code. It took only a few minutes to add the same code to the ARM Mode-X ASM code.
My zerox86 blog post was also very useful in implementing the enhancements for the few Mode-X opcode handlers that Jazz Jackrabbit uses in the game menu selector drawing. These images from my zerox86 blog show the problem on the left and the fixed code on the right:
I had spent considerable time debugging the Jazz audio problems in zerox86, so I added the same code enhancements to my ARM SoundBlaster emulation code. Thus Jazz Jackrabbit sounded pretty good even the first time I ran it.
Okay, now Jazz Jackrabbit was running fine, so I switched back to checking Tyrian 2000. It too seemed to run otherwise fine, but it did not play any audio. I will need to look into this a bit further.
I have not yet had time to test the new rpix86 properly, so I did not want to release the new version today. However, if nothing major happens, I should be able to release version 0.18 next Sunday! I am still checking my todo-list for additional improvements I might have time to implement, but at least Jazz Jackrabbit will run in the next release. Thanks again for your interest in my emulators!
Apr 6th, 2014 - rpix86 version 0.17 released!
Here is the latest version of rpix86! Not a lot of changes, as I still have not had all that much time to work on rpix86, but there are a couple of changes that you might find useful.
- Changed the joystick event file handling to use /dev/input/js? files. Note that before this version you needed to give the event file number using the -j parameter, now you need to give the js file number (which I believe always start with zero, so you would normally use -j0) to have rpix86 use your joystick. Also note that prior to this version rpix86 attempted to automatically enable joystick support. In this version you need to give the -j0 parameter to enable joystick support. This change hopefully makes rpix86 more compatible with various joystick types.
- Added support for a secondary joystick, with -j1 command line parameter. If you give both -j0 and -j1, rpix86 will take the first two joystick axis from the first joystick and the other two axis from the second joystick. Both joysticks will also have two buttons allocated to them, since DOS joystick support has 4 analog channels and 4 buttons total.
- Added support for the freeware game StarGunner by Apogee. The setup program used some JPO and JPE opcodes, which always need game-specific handling. It also used a REP MOVSD operation from protected mode, to move the screen image to the VGA 640x480 16-color graphics mode. No other program has until now used that operation, so I had not yet implemented that. After fixing these issues, both the setup program and the game itself seemed to run fine, although rather slow. The system requirements mention a Pentium processor, which is quite a lot faster than what rpix86 can emulate.
Sorry I have not had time to work on more enhancements. I hope you enjoy this version, and let me know of any bugs you encounter!
Mar 9th, 2014 - rpix86 version 0.16 released!
Okay, after a bit of a hiatus on the rpix86 front, here finally is a new version of rpix86! I have ported a few features over from my other project, and also managed to spend a couple of days fixing some minor bugs for this version. Here are the most notable changes:
1. Implemented new experimental CD-ROM support
You can now give a new -c (as in CD-ROM) command line parameter to rpix86. This parameter gives the directory on your Raspberry Pi that should be used as the root directory of D: drive. This D: drive is emulated as a CD-ROM drive (when a DOS program queries the type of the drive). For example, if your game is located in /home/pi/rpix86/Games/MYGAME and you have a subdirectory CDROOT under that directory that contains all the files from the CD of the game, you can give a parameter -c/home/pi/rpix86/Games/MYGAME/CDROOT to have rpix86 use the contents of that directory as a D: drive.
Note that ISO images (or any other similar CD images) are not supported, you need to extract all the files from the image to a directory on your Raspberry Pi. I am looking into a possibility of adding ISO image support in the future.
2. Fixed the rpix86 screen copy feature
I had broken the screen copy feature in rpix86 several versions ago, but I had not had much need for that feature myself so I had not bothered to fix it. Now I finally decided to look into this problem. It turned out that I had accidentally moved the screen copy routine to a location in the code after the screen was cleared, which is why all the screen copies had just a black screen. In this version you can again take a screen shot by pressing the PrintScreen button on your keyboard. The screen copies are written into SCR??.BMP files, with the ?? being an incremental number.
3. Ignore the "drop-to-debugger" key press if debugger is not available
I added code to detect whether the current screen size allows running the inbuilt debugger, and if the debugger is not activated, the "drop-to-debugger" key (right Windows key) will not do anything. The built in debugger is meant for my own debugging use, so you can freely ignore this information completely. :-)
4. Improved previous fix for writing to unmapped EMS page
The fix I made in version 0.15 for the Aces Over Europe bug was not a very good one, so I made a better fix in this version. Now writing to an unmapped EMS page writes to normal low memory, just like it works on a real PC.
5. Improved mouse initialization (fixes crash in Fragile Alliance)
There was a minor problem in the mouse initialization routine which left some mouse scaling variables uninitialized. This could cause a division by zero problem in some games that tried to change the mouse scaling immediately after initializing the mouse. This problem is now fixed.
6. Added support for HLT opcode in protected mode (Fragile Allegiance)
Another minor problem was that the game "Fragile Allegiance" used the HLT (Halt) opcode from protected mode, and I had not yet implemented that yet. Implementing it was quite easy, and that fix allowed Fragile Allegiance to run.
7. Implemented a missing EGA opcode (Spacewrecked)
The game "Spacewrecked" failed to run because I had not implemented a rare EGA mode opcode it used. After implementing that the game began to run fine.
Thanks for your continued interest in rpix86, and let me know of any bugs you find in this version! My time to work on a new rpix86 version continues to be somewhat limited, but I will try to work on it at least every now and then.
Feb 23rd, 2014 - Lack of rpix86 progress
First off, sorry for my lack of blog updates for the recent weeks. I have not had time to work on rpix86 for a few weeks now, as I have been busy working on stuff relating to my license agreement with Retro Infinity. I am currently porting my x86 emulation core to a new platform, but this work falls under a NDA so I can not tell you more about it. This work has a deadline, and I prefer to get all stuff that has a deadline done sooner rather than later. Thus, rpix86 is on hold until this work is done.
I can however tell you about one enhancement that will be in the next version of rpix86. I will add a simple CD-ROM support to the next version. This means that you can give a second file folder parameter to rpix86, and this will be used as a D: drive and will show up as a CD-ROM to the DOS programs. This will allow you to run some programs that expect the CD to be present, and will also help installing some DOS programs distributed on a CD. This is also a small step towards ISO image support, which I hope to eventually have in rpix86.
In any case, thanks again for your continued interest in rpix86, and I hope to be able to get back to working on it within a few weeks or so.
Jan 5th, 2014 - rpix86 version 0.15 released!
Happy New Year! I decided to start a new blog page now that the year has changed, so that the single blog page does not grow too big. You can find the previous blog entries using the link at the bottom of this page.
This version has mostly game-specific fixes, but also a new somewhat experimental -f2 filtering option which may improve performance in some games. Here are the details of the fixes:
1. Fixed Crystal Caves hang on title screen
I got a test report asking if the Crystal Caves hang could be fixed, and I was somewhat surprised to hear that rpix86 had such a problem. This game has been working in DSx86 for ages, so it should work fine in rpix86 as well. I began to debug the problem, and noticed that indeed it hangs completely on the title screen. After some digging I found out that the reason it hangs is that the Interrupt Enable CPU flag is not set while the game waits for either timer interrupt or keyboard interrupt. So it never gets either of those interrupts and thus hangs.
The question was then to find out what code clears the Interrupt Enable flag. I added some debug logging to all the code that clears the flag, but that produced tons of output as every interrupt clears the flag for the duration of handling the interrupt. In the end I almost accidentally noticed that the game uses INT 03 (opcode 0xCC) which is a debugger breakpoint interrupt. This has some special handling in my code, and when looking at that code (and comparing it with older code from DSx86) I finally found the problem. In my rpix86 code I pushed the CPU flags with the interrupt flag already cleared, while I should have pushed the flags BEFORE clearing the interrupt flag. Fixing that bug allowed Crystal Caves to run normally. It uses similar code to Commander Keen 1 for horizontal smooth scrolling, though, so the scrolling may be somewhat jittery.
2. Removed unnecessary delay loop from default BIOS keyboard IRQ handler
While debugging Crystal Caves, I noticed that it overrides the BIOS INT 9 keyboard interrupt and checks for the pressed key using input from port 0x60 (the Keyboard Data Port). Input from that port clears the Output Buffer Full bit in the Keyboard Status Port 0x64. However, the game then calls the original BIOS INT 9 handler, which first loops up to 65536 times waiting for the Keyboard Status Port to report that the keyboard buffer is full, before proceding to read the key from port 0x60. This code is based on the actual BIOS of a PC clone, and I hadn't bothered to change that behaviour. (As a curious side note, one of my first PC clones (from around 1985 or so) contained the full printed BIOS source code as an appendix of its manual. That was quite helpful when I coded my own emulated BIOS routines originally for DSx86.)
This waiting for the keyboard status meant that when playing Crystal Caves (and possibly other similar games) there was an unnecessary delay loop of 65536 cycles that happened after every key press. Since I know that the keyboard interrupt in rpix86 only happens after a key is pressed, I removed that delay loop from my emulated BIOS routines.
3. Added experimental -f2 filtering option, for hardware dispmanx scaling
Next I looked into a strange problem in Lemmings where the game runs at more or less normal speed when I run it in a 640x480 window (which I usually do when debugging games in rpix86), but has bad stuttering when it is run full-screen (on my full-HD display). Since nothing in my code except the OpenGL ES drawing routines behave in any way differently depending on the screen size, this seemed quite strange. The OpenGL ES routines should handle all scaling in hardware (or so I thought), but just to be sure I added some timing code around the glTexSubImage2D() call I use to copy the emulated graphics memory to an OpenGL texture. This was the result:
|Screen size and filtering||Time||Max theoretical framerate|
|640x400 -f0||1.5 ms||667 fps|
|640x400 -f1||1.5 ms||667 fps|
|1920x1080 -f0||15 ms||66 fps|
|1920x1080 -f1||25 ms||40 fps|
To my big surprise, this single call took a lot more time when the screen size increased! With texture filtering on (-f1 parameter) it caused rpix86 to not even reach 60fps emulation speed, and even with no texture filtering (-f0) it only left very little time for the actual CPU emulation!
So, it occurred to me that perhaps I should check what happens if I do the size scaling using the dispmanx call instead of OpenGL ES, so that OpenGL can always draw into 640x480 (the maximum VGA resolution that rpix86 supports) screen size. This produced the following timings, so I decided to include this feature as a -f2 filtering mode. Feel free to experiment with this parameter, it may help performance in some situations. The scaling is not as smooth as with the -f1 parameter, though.
|Screen size and filtering||Time||Max theoretical framerate|
|640x400 -f0||1.5 ms||667 fps|
|640x400 -f1||1.5 ms||667 fps|
|640x400 -f2||1.6 ms||625 fps|
|1920x1080 -f0||15 ms||66 fps|
|1920x1080 -f1||25 ms||40 fps|
|1920x1080 -f2||1.6 ms||625 fps|
4. Implemented missing CD-ROM call INT2F AH=1501 (X-Wing INSTALL.EXE)
Next I checked why the INSTALL.EXE of X-Wing caused a soft crash in rpix86. I noticed that the game uses a CD-ROM detection method that I had not implemented yet, so I added that code (as a dummy routine, so that the game sees there is no CD-ROM device). This allowed the INSTALL.EXE to show the main selection screen.
5. Fixed DOS Get STDIN Status call to handle enhanced keyboard correctly
However, after getting the INSTALL.EXE main selection screen to show, I could not actually change the selection, as the cursor keys did not seem to work! This also needed some more debugging, until I found out that the game uses the DOS INT21 AH=0B GET STDIN STATUS call to check if a key is available. I had support for that call, but looking at that description I realized that I did not handle the enhanced keys properly. I originally emulated the old 83-key cursor keys in DSx86, and only later switched to enhanced 101/102-key DOS keyboard emulation. At that point I did not fix that DOS call to also handle enhanced key codes correctly. The fix was quite minor, and it made the INSTALL.EXE work properly.
6. Implemented several missing Mode-X opcodes (Aces Over Europe)
Next I tested Aces Over Europe, which had a soft crash when the actual flying should start. There were several VGA Mode-X opcodes that this game uses that have not been used by any other game. It looks like the game actually stores data (like pointers and such) into the VGA graphics memory, and then uses those later when drawing the screen. This is probably some sort of a speed trick, although it actually only slows things down in rpix86 as the graphics memory is slower to handle than normal RAM. In any case, after adding several such new Mode-X opcode handlers the game progressed to the actual flying quite fine.
7. Fixed writing to non-mapped EMS page (bug in Aces Over Europe)
However, after exiting the game back to 4DOS prompt, giving any command in 4DOS caused a soft crash. Obviously something in Aces Over Europe had corrupted memory that belonged to 4DOS.
I used my inbuilt memory watch routine to detect when the 4DOS memory gets corrupt, and noticed that the memory is already corrupt when 4DOS loads the code back from EMS memory. So, I changed the watch address to be inside the physical memory that 4DOS maps into EMS, and then caught the code in Aces Over Europe that writes to this memory. I confirmed in DOSBox that this seems to be a bug in the game, it writes into unmapped EMS memory page. I had been lazy in rpix86 and left unmapped EMS pages to point to the beginning of my EMS memory area, and in this case it meant that the game wrote to memory that belonged to 4DOS.
I did a quick fix to this problem by reserving one EMS page to be the "unmapped" page, so that any game that tries to write to an unmapped page writes safely to this reserved area. This fixed the crash in 4DOS after exiting Aces Over Europe.
Thanks again for your interest in rpix86, let me know of any new bugs you find in this version!
Previous blog entries
- See here for blog entries from 2013.