Your scientists were so preoccupied with whether or not they could, they didn’t stop to think if they should.
-Ian Malcolm
Ever since I got my first 286 board way back in the early 90’s (1990? 1991?) I have been intrigued by the whole protected mode of operation. Unfortunately, in the era the required tools were way out of my reach, and of course were not available at retail. But now I live in the future where I have all the parts! Let’s look at the needed parts
- IBM PS/2 model 60
- Microsoft C 6.00a
- Phar Lap 286|DOS-Extender
- Space Quest II
I have to once more again thank my patrons, and people tolerating the google ads as it made all the difference in being able to buy all this stuff. And now is as good a time as any other to put it all together.
I stumbled upon this repo on sourceforge, Sarien. It included a Turbo C++ port, which is pretty exciting! So, this became my goal, get Sarien running on Phar Lap 286.
Installing Microsoft C requires you to pick and choose both hosting, targeting environments, along with what the preferred libraries are. In the business we call this foreshadowing as this can be such a giant PITA. At least virtual machines are fast, plentiful, and cheap. In addition I had been using MS-DOS player to host the tools on Windows 11. This of course proved weird later.
The first step was getting it running on MS-DOS using Microsoft C 6.0a. This was actually pretty easy, the hard part was working out the makefile, as some files don’t compile with optimisations. And overall, the project doesn’t seem to work with /Ox at all. I haven’t spent enough time mixing and matching settings to find what actually doesn’t work, but I’m in a hurry, and /Os seems to work just fine.
In no time I had both the CGA & VGA drivers up and running and verified working on my PS/2. Great!
Now comes the fun, getting it ready to run on Pharlap.
Phar Lap’s 286|DOS-Extender is pure magic. A DOS extender is a special program that can load a protected mode program into memory on a 286 or better computer and run it. At it’s heart, it can proxy MS-DOS functionality from protected mode to real mode, allowing you to use a lot of methodology and code from traditional real mode code. Phar Lap, goes beyond that by providing a pseudo OS/2 1.2 environment on MS-DOS, including advanced features like DLL’s, and being able to use ALL the RAM in your computer. Of course on the 286 there is a massive caveat:
The 286 has no built-in function for switching from protected mode to real mode. This makes programs that rely a LOT on MS-DOS potentially very slow. You can absolutely feel the difference between the real mode and the protected mode version of Sarien.
Phar Lap does include a test program, swtest which can benchmark the switching methods, so let’s run it and get some scores.
Switch code version = 1.14
BIOS signature: BA66CC86
BIOS date: 02/13/87
Machine ID = 0, A20 method = PS2, Reset method = Standard
Starting test for Switch Mode 3 (SLOW) ... Test complete.
Avg switch time (usecs): To prot = 34, To real = 101, Total = 135
Min switch time (usecs): To prot = 32, To real = 98, Total = 130
Max switch time (usecs): To prot = 35, To real = 103, Total = 138
Machine ID = 0, A20 method = PS2, Reset method = Standard
Starting test for Switch Mode 2 (AT) ... Test complete.
Avg switch time (usecs): To prot = 34, To real = 86, Total = 120
Min switch time (usecs): To prot = 33, To real = 83, Total = 116
Max switch time (usecs): To prot = 36, To real = 88, Total = 124
Machine ID = 0, A20 method = PS2, Reset method = Standard
Starting test for Switch Mode 1 (SURE) ... Test complete.
Avg switch time (usecs): To prot = 34, To real = 70, Total = 104
Min switch time (usecs): To prot = 32, To real = 68, Total = 100
Max switch time (usecs): To prot = 35, To real = 72, Total = 107
For those of you wondering what the timing is like on a 386, here is my 16Mhz PS/2 Model 80 board (now with fully 32bit memory)
Switch code version = 1.14
BIOS signature: 039D2DB4
BIOS date: 03/30/87
Machine ID = 0, A20 method = PS2, Reset method = Standard
Starting test for Switch Mode 5 (386) ... Test complete.
Avg switch time (usecs): To prot = 31, To real = 22, Total = 53
Min switch time (usecs): To prot = 30, To real = 20, Total = 50
Max switch time (usecs): To prot = 32, To real = 23, Total = 55
I’m honestly surprised the 286 switches from protected back to real so quickly! Although as you can see from the 386 timings it’s significantly faster than the 286.
Here is a quick video, real mode on the left, protected mode on the right. Yes I need to get a VGA capture card. Sorry.
Being a DOS extender it does have built in functions for things like hooking interrupts like this:
/* install our new timer tick routine */
DosSetPassToProtVec(IRQ0, (PIHANDLER)new_prot_timer_tick,
&old_prot_timer_tick, &old_real_timer_tick);
Unlike some kind of OS/2 method which would involve creating a thread and or timers.
Setting video modes via the video BIOS is also supported, using the built in int86 style calls:
memset(&r,0x0,sizeof(r));
r.h.ah = 0;
r.h.al = 3;
int86(0x10, &r, &r);
Using pointers into things like video ram do require ‘asking for permission’ but it’s not too involved:
int rseg;
/* Get PM pointer to text screen */
DosMapRealSeg(0xb800,4000,&rseg);
textptr=MAKEP(rseg,0);
with the segment mapped, and a pointer to the segment, and now I can read/write directly into video RAM!
/* save text screen */
memcpy(textbuf,textptr,4000);
Just like that!
I’m not sure what I screwed up on the VGA graphics, as it doesn’t work correctly, but oddly enough CGA does work.
And now this is where everything goes off the rails.
It ran fine on emulation. So all excited I fired up the PS/2 and….
This lead me to more fun in how on earth to debug this. Of course Phar Lap 286 version 2.5 requires me to have Microsoft C/C++ 7.0. I shamelessly downloaded a disk set from pcjs.org. You actually need to install it, to copy out the files required:
- 28/05/1991 05:37 pm 47,216 CFIG286.EXE
- 26/11/1991 11:19 am 13,531 GORUN286.EXE
- 19/03/1992 04:00 am 42,720 shw0.dll
- 19/03/1992 04:00 am 105,039 eew0cxx.dll
- 19/03/1992 04:00 am 410,112 cvw4.exe
- 19/03/1992 04:00 am 91,118 eew0can.dll
- 19/03/1992 04:00 am 74,400 emw0w0.dll
- 03/08/1992 09:34 pm 2,649 INT33.DLL
- 03/08/1992 09:40 pm 2,718 MSG.DLL
- 03/08/1992 09:40 pm 1,702 NAMPIPES.DLL
- 03/08/1992 09:42 pm 2,073 NLS.DLL
- 03/08/1992 09:43 pm 5,184 PTRACE.DLL
- 03/08/1992 09:45 pm 2,320 SESMGR.DLL
- 03/08/1992 09:50 pm 1,508 WIN87EM.DLL
- 05/08/1992 12:04 am 3,100 KEYBOARD.DLL
- 05/08/1992 06:33 pm 270 TOOLHELP.DLL
- 14/08/1992 07:38 pm 7,891 KERNEL.DLL
- 14/08/1992 09:40 pm 14,545 USER.DLL
- 09/09/1992 10:59 pm 209,922 RUN286.EXE
- 09/09/1992 10:59 pm 229,046 RUN286D.EXE
- 14/09/1992 11:01 pm 14,024 TLW0LOC.DLL
- 17/09/1992 07:26 pm 34,152 CVP7.EXE
While CVP7 does come with Phar Lap, you have to run it via run286. As you may have noticed there is a mixture of OS/2 and Windows DLL’s in here, as at this point CodeView was a Windows protected mode debugger. The divorce was in full swing, and Microsoft C/C++ 7.0 had amputated the majority of OS/2 support. I’m sure all this is in the manuals, however all I have is disk images. There was no C 6.0a supported hosted debugger. Maybe it’s in the v1/v2 of Phar Lap 286, but I only have 2.5. There is version 3 files on the internet but I wanted to stick to 2.5.
And this is all I got. Yes, I did recompile with ‘/Od /Zi’ along with using cvpack on the executable. Yes, after copying the source to the PS/2 I was able to see the source line mapping, but it immediately jumps to assembly and GP Faults. All this is fine, but IT RUNS UNDER EMULATION.
What is going on?!
I asked around on discord, and found someone willing to test on their 286. It also crashed. I tried VMware and .. it crashed too! So did 86box! Ok now we’re going somewhere!
Since I had been using MS-DOS player to run the tools, I had an issue with the linker in C 6.0a crashing, so I tried the one from C/C++ 7. It also didn’t work. I tried the one from Visual C++ 1.5. It also failed. Almost giving up on the entire thing, since I had copied the source code to the PS/2, I tried something really silly, I compiled it using the /qc or QuickC flag. I wasn’t too worried about sizes as again I’m going to run in protected mode. It took some 20-30 minutes to compile, as 10Mhz machines are not the best for building software in this modern age. Much to my surprise it actually ran.
This was kind of shocking as I’m not sure what I screwed up to not get this to work, but it worked! I went ahead and changed the build to not use QuickC, but rebuild with /Os (Optimize for space). It took about an hour. And it too worked.
Shockingly it runs! I’m not sure what on earth is up with the linking. I did find it easier to just rebuild on Qemu since it can easily map into my source directory and copy everything over and re-build very quickly.
Later I did try copying over compiled objects built using the MS-DOS Player, and linked them natively, and they ran fine.
What is it with the LINK?!
One weird thing on 86box is my pre-built machine I was using has a 5 1/4″ 1.2Mb floppy for the A: drive. It’s too small to fit MS-DOS, the game data and Phar Lap 286 all on there. Although Stacker to the rescue and it fits!
I removed a lot of Unix quality of life, to make it more MS-DOS dumping everything in the same directory so you can save & load games.
Assuming anyone is interested in this at all, I have the source up on github. I’ll follow up with some performance videos showing how much slower real vs protected mode is, along with some binaries/demos. A 5 1/4″ floppy disk image can be downloaded here for any suitable emulator.