Originally with all the buildup of compilers & GCC ports to OS/2, I had a small goal of getting Sarien running on OS/2. I did have it running on both a 286 & 386 DOS Extender, so the code should work fine, right?
To recap, years ago I had done a QuakeWorld port to OS/2 using the full screen VIO mode, a legacy hangover from 16bit OS/2. It works GREAT on the released 2.00 GA version. I went through the motion of getting the thunking from 32bit mode to 16bit mode, to find out that it doesn’t exist in the betas!
So that meant I was going to have to break down and do something with Presentation Manager.
So the first thing I needed was a program I could basically uplift into what I needed, and I found it through FastGPI.
While it was originally built with GCC, I had rebuilt it using Visual C++ 2003 for the math, and the Windows NT 1991 compiler for the front-end. As you can see it works just fine. While I’m not a graphical programmer by any stretch, the source did have some promise in that it creates a bitmap in memory, alters it a runtime, and blits (fast binary copy) it to the Display window. Just what I need!
for (y = 0; y < NUM_MASSES_Y; y++)
{
for (x = 0; x < NUM_MASSES_X; x++)
{
disp_val = ((int) current[x][y] + 16);
if (disp_val > 32) disp_val = 32;
else if (disp_val < 0) disp_val = 0;
Bitmap[y*NUM_MASSES_X+x] = RGBmap[disp_val];
}
}
It goes through the X/Y coordinate plane of the calculated values, and stores them as an RGB mapping into the bitmap. Seems simple enough right?
DosRequestMutexSem(hmtxLock, SEM_INDEFINITE_WAIT);
/* This is the key to the speed. Instead of doing a GPI call to set the
color and a GPI call to set the pixel for EACH pixel, we get by
with only two GPI calls. */
GpiSetBitmapBits(hpsMemory, 0L, (LONG) (NUM_MASSES_Y-2), &Bitmap[0], pbmi);
GpiBitBlt(hps, hpsMemory, 3L, aptl, ROP_SRCCOPY, BBO_AND);
DosReleaseMutexSem(hmtxLock);
It then locks the screen memory, and then sets up the copy & uses the magical GpiBitBlt to copy it to the video memory, then releases the lock. This all looks like something I can totally use!
I then have it call the old ‘main’ procedure form Sarien as a thread, and have it source the image from the Sarien temporary screen buffer
disp_val = ((int) screen_buffer[y*NUM_MASSES_X+x] );
Which all looks simple enough!
And WOW it did something! I of course, have no keyboard, so can’t hit enter, and I screwed up the coordinates. I turned off the keyboard read, flipped the X/Y and was greeted with this!
And it’s backwards. And upside down. But it clearly is rendering into FastGPI’s gray palette! I have to admit I was really shocked it was running! At this point there is no timer, so it runs at full speed (I’m using Qemu 0.80 which is very fast) and even if there was keyboard input it’d be totally unplayable in this reversed/reversed state.
The first thing to do is to flip the display. I tried messing with how the bitmap was stored, but it had no effect. Instead, I had to think about how to draw it backwards in RAM.
{
for (x = 0; x < NUM_MASSES_X; x++)
{
disp_val = ((int) screen_buffer[y*NUM_MASSES_X+x] ); //+ 16);
if (disp_val > 32) disp_val = 32;
else if (disp_val < 0) disp_val = 0;
Bitmap[((NUM_MASSES_Y-y)*(NUM_MASSES_X))-(NUM_MASSES_X-x)] = RGBmap[disp_val];
}
}
Now comes the next fun part, colour.
I had made the decision that since I want to target as many of the OS/2 2.0 betas as possible they will be running at best in 16 colour mode, so I’ll stick to the CGA 4 colour modes. So the first thing I need is to find out what the RGB values CGA can display.
This handy image is from the The 8-Bit Guy’s video “CGA Graphics – Not as bad as you thought!” but here are the four possible sets:
And of course I got super lucky with finding this image:
So now I could just manually populate the OS/2 palette with the appropriate CGA mapping, just like how it worked in MS-DOS:
First define the colours:
#define CGA_00 0x000000
#define CGA_01 0x0000AA
#define CGA_02 0x00AA00
#define CGA_03 0x00AAAA
#define CGA_04 0xAA0000
#define CGA_05 0xAA00AA
#define CGA_06 0xAA5500
#define CGA_07 0xAAAAAA
#define CGA_08 0x555555
#define CGA_09 0x5555FF
#define CGA_10 0x55FF55
#define CGA_11 0x55FFFF
#define CGA_12 0xFF5555
#define CGA_13 0xFF55FF
#define CGA_14 0xFFFF55
#define CGA_15 0xFFFFFF
Then map the 16 colours onto the CGA 4 colours:
OS2palette[0]=CGA_00;
OS2palette[1]=CGA_11;
OS2palette[2]=CGA_11;
OS2palette[3]=CGA_11;
OS2palette[4]=CGA_13;
OS2palette[5]=CGA_13;
OS2palette[6]=CGA_13;
OS2palette[7]=CGA_15;
OS2palette[8]=CGA_00;
OS2palette[9]=CGA_11;
OS2palette[10]=CGA_11;
OS2palette[11]=CGA_11;
OS2palette[12]=CGA_13;
OS2palette[13]=CGA_13;
OS2palette[14]=CGA_13;
OS2palette[15]=CGA_15;
So now it’s looking right but there is no timer so on modern machines via emulation it runs at warp speed. And that’s where OS/2 shows its origins is that it’s timer ticks about every 32ms, so having a high resolution timer is basically out of the question. There may have been options later one, but those most definitively will not be an option for early betas. I thought I could do a simple thread that counts and sleeps, as hooking events and alarms again suffer from the 32ms tick resolution problem so maybe a sleeping thread is good enough.
static void Timer(){
for(;;){
DosSleep(20);
clock_ticks++;
}
}
And it crashed. Turns out that I wasn’t doing the threads correctly and was blowing their stack. And somehow the linker definition file from FastGPI kept sneaking back in, lowering the stack as well.
Eventually I got it sorted out.
The next big challenge came of course from the keyboard. And I really struggled here as finding solid documentation on how to do this is not easy to come by. Both Bing/google want to suggest articles about OS/2 and why it failed (hint it’s the PS/2 model 60), but nothing much on actually being useful about it.
Eventually through a lot of trial and error, well a lot of errors I had worked uppon this:
case WM_CHAR:
if (SHORT1FROMMP(parm1) & KC_KEYUP)
break;
pm_keypress=1;
switch (SHORT1FROMMP(parm1))
{
case VK_LEFT:
key_from=KEY_LEFT;
break;
case VK_RIGHT:
key_from=KEY_RIGHT;
break;
case VK_UP:
key_from=KEY_UP;
break;
case VK_DOWN:
key_from=KEY_DOWN;
break;
case KC_VIRTUALKEY:
default:
key_from=SHORT1FROMMP(parm2);
break;
}
I had cheated and just introduced 2 new variables, key_from, pm_keypress to signal a key had been pressed and which key it was. I had issues mapping certain keys so it was easier to just manually map the VK_ mapping from OS/2 into the KEY_ for Sarien. So it triggers only on single key down events, and handles only one at a time. So for fast typers this sucks, but I didn’t want to introduce more mutexes, more locking and queues or DIY circular buffers. I’m at the KISS stage still.
I’m not sure why it was dropping letters, I would hit ‘d’ all I wanted and it never showed up. I then recompiled the entire thing and with the arrow keys now mapped I could actually move!
And just like that, Roger Wilco now walks.
From there I added the savegame fixes I did for the 286/386 versions, along with trying to not paint every frame with a simple frame skip and…
And it’s basically unplayable on my PS/2 model 80. Even with the 32bit XGA-2 video card.
I had to give it a shot under 86Box, to try the CGA/EGA versions:
It’s weird how the image distorts! Although the black and white mapping seems to work fine.
I should also point out that the CGA/EGA versions are running on OS/2 2.0 Beta 6.123, which currently is the oldest beta I can get a-hold of. So at the least I did reach my goal of having a 32bit version for early OS/2.
I would imagine it running okay on any type of Pentium system, however. So, what would the advantage of this, vs just running the original game in a dos box? Well, it is a native 32bit app. This is the future that was being sold to us back in 1990. I’m sure the native assembly that Sierra used was far more efficient and would have made more sense to just be a full screened 16bit VIO application.
So how long did it take to get from there to here? Shockingly not that much time, — 02/20/2024 6:02 PM for running FastGPI, to — 02/20/2024 10:56 PM For the first image being displayed in Presentation Manager, and finally — 02/21/2024 10:39 PM to when I was first able to walk. As you can see, that is NOT a lot of time. Granted I have a substantially faster machine today than what I’d have in 1990 (I didn’t get a 286 until late 91? early 92?), compiling Sarien on the PS/2 takes 30-40 minutes and that’s with the ultra-fast BlueSCSI, compared to even using MS-DOS Player I can get a build in about a minute without even compiling in parallel.
I’ve put the source over on github: neozeed/sarienPM: Sarien for OS/2 (github.com)
I think the best way to distribute this is in object form, so I’ve created both a zip & disk image containing the source & objects, so you can link natively on your machine, just copy the contents of the floppy somewhere and just run ‘build.cmd’ which will invoke the system linker, LINK386 to do it’s job. I have put both the libc & os2386 libraries on the disk so it should just work about everywhere. Or it did for me!
So that’s my quick story over the last few days working on & off on this simple port of Sarien to OS/2 Presentation Manager. As always, I want to give thanks to my Patrons!