An actual look at Microsoft OS/2 2.0

Years ago, feeling nostalgic, I had written a quick throw away thing about the old article from PC Magazine, 29th of May, 1990, detailing the coming future of Operating Systems, Microsoft OS/2 2.0. 14 year old me was blown away!

614KB Free! And Flight Simulator 4 in a window

This release marks the last time that Microsoft would release an OS/2 beta to developers, instead with the runaway success of Windows 3.0, Microsoft would remove resources from the constrained OS/2, and refocus both on Windows 3.1, and Windows NT.

Thanks to one of my Patrons – Brian Ledbetter, the much-sought Microsoft OS/2 2.0 Pre-Release 2 is now available! So obviously the first thing to do was to re-create the original magical screenshot.

One thing of note is that thanks to some of the metadata left at the end of the disks we know when this disk set was made:

$FTIME = Wed Jun 22 09:00:56 1988
$DTIME = Tue Aug 15 07:48:10 1989
$PCTIME = Thu Apr 27 11:51:54 1989
$FREV = Wed May 31 09:17:24 1989
$DUP TIME = Fri Jul 13 16:02:27 1990

Given that the PC Magazine issue was published in May of 1990, it showcased version 6.43, shipped to devs early January of 1990, and timestamped 20/12/1989. I would speculate that it’s the Pre-Release 1.

The OS/2 Software Development Kit

This Pre-Release 2 set was made in July with the files being timestamped 01/06/1990, so physical releases every 6 months? I guess that makes sense when you look at how many disks have to be duplicated, boxed up and sent out. It’s one big win for digital downloads, or how Windows 10/11 can background download updates, or even entire OS updates (that are more like new installs) today. Mailing big heavy boxes must have sucked. And dealing with lost/damaged disks. Also as you can see on the box, it only contains 5 1/4″ disks, they didn’t even ship dual media.

Maybe the large box is a reaction to the first Pre-Release’s 16 disks & a few photocopied sheets mere 10oz. Maybe people were displeased that they didn’t get much printed ‘bang’ for their $2,600 – the price of the 2.0 SDK.

The disks were dumped with Kyroflux, and on several of the disks there is extra trailing information on the disks revealing data from when they were manufactured:

$TRACEBACK (tm) Ver 1.04
$OPER = operator
$STATION = 1
$STN LIST = 1 3 5 7 11 13 15 17 19
$WORKORDER = -1
$SYSTEM = 7
$PRODUCT = z:/product/110098449.p
$PTIME = ???
$FORMAT = /format/fmt12.f
$FTIME = Wed Jun 22 09:00:56 1988
$DRIVE = /drive/3xhd96sdsmt.d
$DTIME = Tue Aug 15 07:48:10 1989
$MODULE = /config/3xhdSDSmt.s
$MTIME = ???
$WINDOW MARGIN = 87.5%
$RETRY SETTINGS = 3,2,25
$FUNCTION = write/verify
$PRECOMP = /precomp/3xhdSDS.t
$PCTIME = Thu Apr 27 11:51:54 1989
$FREV = Wed May 31 09:17:24 1989
$DRIVESN =
$LOADERSN = 525-100-70
$DFCSN = 3402 SDSFC
$USTRING =
$TSTRING = TraceBack (tm)   (c) 1989 Trace Products All Rights Reserved.
$DUP TIME = Fri Jul 13 16:02:27 1990
$DISK NUMBER =  22 of 45
$SN = NONE

That’s right, this disk (Install 1) was manufactured on Friday the 13th!

Running strings against the DOSKRNL reveals this string:

MS DOS Version 4.00 (C)Copyright 1988 Microsoft CorpLicensed Material - Property of Microsoft

Not only is it a new OS/2, but a new MS-DOS! Neat!

 APPEND.EXE
 ASSIGN.COM
 COMMAND.COM
 EDLIN.COM
 GRAFTABL.COM
 JOIN.EXE
 SETCOM40.EXE
 SUBST.EXE

This is *NOT* a comprehensive version of MS-DOS 4.0. Then again anything to do with disks, you should be using OS/2. It’s more so commands needed for working inside the virtual MS-DOS environment. And yes, it doesn’t include any basic.

There is built in support for EMS, and there is an XMS driver, but it’s not activated by default. I didn’t bother trying Windows 3.0, but I did install Word for Windows, which has a runtime version of Windows 2.11

Windows 2.11 Runtime on OS/2

Trying to run the windows executable directly gives a weird error:

SYS0195: The operating system cannot run %1.

I went ahead and installed WLO, or the Windows Libraries for OS/2, and some things do run, and many more hang the system. I think its an issue in PM, and CAD (Control+Alt+Delete) does reboot the system.

WLO on Microsoft OS/2

It’s a shame Microsoft couldn’t get Windows as the UI to OS/2, it certainly would have had a far more viable lifetime. I’ve ranted about it before but IBM’s insistance of supporting the 286, releasing a $6,000 286 in 1987 basically ensured early OS/2 was facing the wrong direction technically, and by ignoring the existing Windows stack, it just delayed OS/2 being useful from the get-go.

Anyways…..

And running strings against the OS2KRNL reveals:

Copyright 1986 IBM Corp.
Internal revision 6.78, 90/05/09

And to further muddle the waters:

@(#)ldrste.c    13.125 90/05/09
@(#)ldrfixup.c  13.43 90/05/09
@(#)ldrsubr.c   13.116 90/05/09
@(#)pgset.c     13.64 90/05/01
@(#)selmgrc.c 13.76 90/05/01
@(#)vmalias.c   13.48  90/05/01
@(#)vmalloc.c   13.71 90/05/01
@(#)vmapi.c          13.50 90/05/07
@(#)vmfree.c    13.89 90/05/01
@(#)vminfo.c         13.33 90/05/01
@(#)vminit.c    13.60 90/05/01
@(#)vmshared.c 13.41 90/05/01
@(#)selkh.c 13.14 90/05/01
@(#)tklibi.c 13.42 90/05/09
@(#)inidin2.asm 1.86 90/04/27
@(#)selinit.asm 13.51 90/05/09
@(#)ldrinita.asm        13.47 90/05/09
@(#)selwrk.asm 13.51 90/05/07
@(#)vdmaa.asm         13.54 90/04/27
@(#)trap.asm    1.82 90/05/04
@(#)trap286.asm 13.43 90/05/04

If you were ever wondering what the names of the source was, here is the .c & assembly:

dbgee.c
dbger.c
dbgwp.c
dem.c
dyndto.c
dyndtr.c
em86.c
except.c
ldrgc.c
ldrinit.c
ldrmte.c
pgage.c
pgalloc.c
pgfault.c
pgframe.c
pginit.c
pgmap.c
selini.c
semalloc.c
seminout.c
semtools.c
semverif.c
semwrkr.c
smalloc.c
smsft.c
tkinit.c
tksleep.c
tkthrd.c
vdmapi.c
vdmctrl.c
vdmm.c
vdmmasrt.c
vdmmcrt.c
vdmmem.c
vdmmheap.c
vdmmisc.c
vdmpage.c
vdmprop.c
vdmtime.c
vmbmp32.c
vmdevhlp.c
vmkrh.c
vmksh.c
vmlocate.c
vmlock.c
vmob.c
----
demfile2.asm
demioc2.asm
demnls.asm
dhrouter.asm
dynci.asm
dyndtra.asm
em86io.asm
em86misc.asm
fsinfo.asm
gshare.asm
int.asm
kmodea.asm
ldrrsrc.asm
ldrrun.asm
monhigh.asm
npx.asm
pageio.asm
pga.asm
selmap.asm
selmgra.asm
selseg.asm
sftio.asm
sicg.asm
sig.asm
tkexit1.asm
tkmisc.asm
tkwksem.asm
vdhserv.asm
vmmisc.asm
vmname.asm

Neat!

I would imagine a lot of the v86 mode virtual device drivers came from Windows/386. But it looks like these were all written in C? Running strings against all the virtual device drivers reveals all C? I got zero hits for .asm

emm.c
emm1.c
emm2.c
emmctrl.c
vbios.c
vcmos.c
vcmosio.c
vdma.c
vdsk.c
vdskint.c
vdskio.c
vkbd.c
vkbdint.c
vkbdio.c
vkbdreq.c
vmbound.c
vmdevrq.c
vmevent.c
vmhook.c
vminit.c
vmlpen.c
vmmisc.c
vmpos.c
vmptr.c
vmrate.c
vmreset.c
vmstate.c
vmuser.c
vpic.c
vtd.c
vtdint.c
vtdio.c
vtdreq.c
xmm.c
xmmctrl.c

It’s not like any of this is ever going to see the light of day, but it’s all interesting, at least to me.

Let’s install!

Installing Microsoft OS/2 2.0 Beta on VMware

I’ll have to update where I had found this, if it was the electronic documentation, or the one time I got to go through the printed documentation that the Microsoft version was adding True Type Fonts. Sadly I can’t find any evidence in the binaries.

OS/2 System Monospace Fonts
OS/2 Courier Fonts, (c) Copyright 1988 Microsoft Corp., Portions Copyright 1985 Bitstream, Inc.
OS/2 Helvetica Fonts, (c) Copyright 1988 Microsoft Corp., Portions Copyright 1985 Bitstream, Inc.
OS/2 Times Roman Fonts, (c) Copyright 1988 Microsoft Corp., Portions Copyright 1985 Bitstream, Inc.

  "PM_Fonts"           "SYSMONO"            "C:\OS2\DLL\SYSMONO.FON"
  "PM_Fonts"           "COURIER"            "C:\OS2\DLL\COURIER.FON"
  "PM_Fonts"           "HELV"               "C:\OS2\DLL\HELV.FON"
  "PM_Fonts"           "TIMES"              "C:\OS2\DLL\TIMES.FON"

Well, that’s sad.

No VIO for you!
No VIO access from 32bit

As I had touched on earlier, These early OS/2’s have no user accessible way to call the legacy 16bit OS/2 API’s. At best you have simple text mode stuff, anything graphical, you need to port to Presentation Manager.

I was able to take the GCC port I did to OS/2 and re-link the objects ,and I was correct, it ran without any changes! I had compiled it using the December 1991 Windows NT Pre-Release’s CL386 compiler. So far so good.

I tried to use the 1991 compiler to build Sarien, and it instantly crashes. I also tried building everything but the Presentation Manager with GCC, and again instant crash. I was able to use the OS/2 2.1 v1.2 DDK, and get a working EXE, even using /Ox (maximum optimisations!). Clearly there is something fundamentally missing or I’m missing something fundamental.

While there is a HPFS installable filesystem, there is no CD-ROM IFS. Running strings against HPFS reveals this much when looking for C. There was no hits for .asm

execioh.c: calling strat2 with PIOH=%p
execioh.c: returned from strat2 with PIOH=%p
qdiskop.c: entered, ioh=%p lsnStrt=%lu csec=%u pbData=%p
unlckio.c:entered, pioh=%p
unlckio.c:retheaping lock handle %p

Even more strange, is that HPFS for OS/2 was still 16bit. I had hoped that even though this is a beta, that there would be a 32bit version of the filesystem. Sadly that kind of feature was reserved for Lan Server installs.

HPFS.IFS: MS-DOS executable, NE for OS/2 1.x (DLL or font)

Could you imagine shipping a 32bit filesystem to home/low/middle tier users today? Speaking of, let’s check the rest of the C:\OS2 binaries:

ANSI.EXE:     MS-DOS executable, NE for OS/2 1.x (EXE)
ATTRIB.EXE:   MS-DOS executable, NE for OS/2 1.x (EXE)
BACKUP.EXE:   MS-DOS executable, NE for OS/2 1.x (EXE)
CACHE.EXE:    MS-DOS executable, NE for OS/2 1.x (EXE)
CMD.EXE:      MS-DOS executable, NE for OS/2 1.x (EXE)
CREATEDD.EXE: MS-DOS executable, NE for OS/2 1.x (EXE)
E.EXE:        MS-DOS executable, NE for OS/2 1.x (EXE)
EAUTIL.EXE:   MS-DOS executable, NE for OS/2 1.x (EXE)
FDISKPM.EXE:  MS-DOS executable, NE for OS/2 1.x (EXE)
FIND.EXE:     MS-DOS executable, NE for OS/2 1.x (EXE)
HELPMSG.EXE:  MS-DOS executable, NE for OS/2 1.x (EXE)
MAKEINI.EXE:  MS-DOS executable, NE for OS/2 1.x (EXE)
MOVESPL.EXE:  MS-DOS executable, NE for OS/2 1.x (EXE)
PATCH.EXE:    MS-DOS executable, NE for OS/2 1.x (EXE)
PICICHG.EXE:  MS-DOS executable, NE for OS/2 1.x (EXE)
PICPRINT.EXE: MS-DOS executable, NE for OS/2 1.x (EXE)
PICSHOW.EXE:  MS-DOS executable, NE for OS/2 1.x (EXE)
PMCPL.EXE:    MS-DOS executable, LE executable
PMEXEC.EXE:   MS-DOS executable, LE executable
PMFILE.EXE:   MS-DOS executable, LE executable
PMSHELL.EXE:  MS-DOS executable, NE for OS/2 1.x (EXE)
PMSPOOL.EXE:  MS-DOS executable, LE executable
PSTAT.EXE:    MS-DOS executable, NE for OS/2 1.x (EXE)
REPLACE.EXE:  MS-DOS executable, NE for OS/2 1.x (EXE)
RESTORE.EXE:  MS-DOS executable, NE for OS/2 1.x (EXE)
SORT.EXE:     MS-DOS executable, NE for OS/2 1.x (EXE)
SPOOL.EXE:    MS-DOS executable, NE for OS/2 1.x (EXE)
SYSLOG.EXE:   MS-DOS executable, NE for OS/2 1.x (EXE)
TRACE.EXE:    MS-DOS executable, LE executable
TRACEFMT.EXE: MS-DOS executable, NE for OS/2 1.x (EXE)
UNPACK.EXE:   MS-DOS executable, NE for OS/2 1.x (EXE)
VIEW.EXE:     MS-DOS executable, NE for OS/2 1.x (EXE)
VIEWDOC.EXE:  MS-DOS executable, NE for OS/2 1.x (EXE)
XCOPY.EXE:    MS-DOS executable, NE for OS/2 1.x (EXE)

The vast majority of them are 16bit. The 32bit stuff is in the defunct LE format. This is why nothing from the GA (General Availability) versions will ever run on these betas, and why I was trying to make stuff available as linkable objects. And yes, my Sarien port is LE:

SARIEN.EXE: MS-DOS executable, LE executable

There is a LAN client disk set, so I guess you were expected to just go across the LAN.

The SDK & Toolkit for this beta have been around for a substantial time. The big difference is that we now have binary compatibility so we can run ALL the examples, namely the Open Dialog demo.

Open Dialog

We take this kind of thing for granted today, common controls, but back in the early 1990’s this was a surprisingly lacking from many UI’s of the time. Since these beta DLL’s use a different format, this won’t run on the later betas, let alone the release. So, this is for those looking for secret hidden stuff.

Is it usable though? Well if your workload is OS/2 applications, ABSOLUTELY. If you want to do cute stuff on MS-DOS.. It’s all YMMV, but traditional apps seem to behave pretty well.

It’s a shame for some reason that overall, these early OS/2 2.0 betas were not all that wide spread, as they are just so interesting! And compared to the GA version of OS/2 2.0 these ancient versions with the 1.x Presentation Manager do feel a lot faster. Sometimes I miss the Workplace Shell, Other times I miss the old terrible Desktop Manager with its incredible simplification.

For those just wanting to mess with the Operating System, it really is a developer’s release so it’s pretty spartan. Sadly there is no XGA drivers in these early betas, there is 8514/a support but I had no luck with it. I suspect it’s probably something I had done wrong. I should also point out, if you are using 86Box, and are using a 486DX/Pentium be sure to enable the softfloat option.

FPU passthru gives major issues with MS-DOS under OS/2

Maybe a math co-processor really was a hidden requirement of these early OS/2 betas? So, perhaps more confirmation that buying an 80387 was NOT a waste of money. I should also add that when trying to compile PHOON, I did have to use /FPi87 or inline 8087 instructions, otherwise instant crash. I had thought it was a mixed FPU mode crashing the linking of GCC+MSC code but I tried a few combinations, none of them worked.

Once more again, I want to thank my Patrons for making this possible, and a big thanks to Brian Ledbetter for being so kind to preserve this incredibly rare, and historically significant software kit.

I’ve made my VMware image available, and 86box.

Totally unfair comparison of Microsoft C

Because I hate myself, I tried to get the Microsoft OS/2 Beta 2 SDK’s C compiler building simple stuff for text mode NT. Because, why not?!

Since the object files won’t link, we have to go in with assembly. And that of course doesn’t directly assemble, but it just needs a little hand holding:

Microsoft (R) Program Maintenance Utility   Version 1.40
Copyright (c) Microsoft Corp 1988-93. All rights reserved.

        cl386 /Ih /Ox /Zi /c /Fadhyrst.a dhyrst.c
Microsoft (R) Microsoft 386 C Compiler. Version 1.00.075
Copyright (c) Microsoft Corp 1984-1989. All rights reserved.

dhyrst.c
        wsl sed -e 's/FLAT://g' dhyrst.a > dhyrst.a1
        wsl sed -e "s/DQ\t[0-9a-f]*r/&XMMMMMMX/g" dhyrst.a1  | wsl sed -e "s/rXMMMMMMX/H/g" > dhyrst.asm
        ml /c dhyrst.asm
Microsoft (R) Macro Assembler Version 6.11
Copyright (C) Microsoft Corp 1981-1993.  All rights reserved.

 Assembling: dhyrst.asm
        del dhyrst.a dhyrst.a1 dhyrst.asm
        link -debug:full -out:dhyrst.exe dhyrst.obj libc.lib
Microsoft (R) 32-Bit Executable Linker Version 1.00
Copyright (C) Microsoft Corp 1992-93. All rights reserved.

I use sed to remove the FLAT: directives which makes everything upset. Also there is some weird confusion on how to pad float constants and encode them.

CONST   SEGMENT  DWORD USE32 PUBLIC 'CONST'
$T20001         DQ      0040f51800r    ;        86400.00000000000
CONST      ENDS

MASM 6.11 is very update with this. I just padded it with more zeros, but it just hung. I suspect DQ isn’t the right size? I’m not 386 MASM junkie. I’m at least getting the assembler to shut-up but it doesn’t work right. I’ll have to look more into it.

Xenix 386 also includes an earlier version of Microsoft C / 386, and it formats the float like this:

CONST   SEGMENT  DWORD USE32 PUBLIC 'CONST'
$T20000         DQ      0040f51800H    ;        86400.00000000000
CONST      ENDS

So I had thought maybe if I replace the ‘r’ with a ‘H’ that might be enough? The only annoying thing about the Xenix compiler is that it was K&R so I spent a few minutes porting phoon to K&R, dumped the assembly and came up with this sed string to find the pattern, mark it, and replace it (Im not that good at this stuff)

wsl sed -e "s/DQ\t[0-9a-f]r/&XMMMMMMX/g" $.a1 \
| wsl sed -e "s/rXMMMMMMX/H/g" > $*.asm

While it compiles with no issues, and runs, it just hangs. I tried the transplanted Xenix assembly and it just hangs as well. Clearly there is something to do with how to use floats.

I then looked at whetstone, and after building it noticed this is the output compiling with Visual C++ 8.0

      0       0       0  1.0000e+000 -1.0000e+000 -1.0000e+000 -1.0000e+000
  12000   14000   12000 -1.3190e-001 -1.8218e-001 -4.3145e-001 -4.8173e-001
  14000   12000   12000  2.2103e-002 -2.7271e-002 -3.7914e-002 -8.7290e-002
 345000       1       1  1.0000e+000 -1.0000e+000 -1.0000e+000 -1.0000e+000
 210000       1       2  6.0000e+000  6.0000e+000 -3.7914e-002 -8.7290e-002
  32000       1       2  5.0000e-001  5.0000e-001  5.0000e-001  5.0000e-001
 899000       1       2  1.0000e+000  1.0000e+000  9.9994e-001  9.9994e-001
 616000       1       2  3.0000e+000  2.0000e+000  3.0000e+000 -8.7290e-002
      0       2       3  1.0000e+000 -1.0000e+000 -1.0000e+000 -1.0000e+000
  93000       2       3  7.5000e-001  7.5000e-001  7.5000e-001  7.5000e-001

However this is the output from C/386:

      0       0       0  5.2998e-315  1.5910e-314  1.5910e-314  1.5910e-314
  12000   14000   12000  0.0000e+000  0.0000e+000  0.0000e+000  0.0000e+000
  14000   12000   12000  0.0000e+000  0.0000e+000  0.0000e+000  0.0000e+000
 345000       1       1  5.2998e-315  1.5910e-314  1.5910e-314  1.5910e-314
 210000       1       2  6.0000e+000  6.0000e+000  0.0000e+000  0.0000e+000
  32000       1       2  5.2946e-315  5.2946e-315  5.2946e-315  5.2946e-315
 899000       1       2  5.2998e-315  5.2998e-315  0.0000e+000  0.0000e+000
 616000       1       2  5.3076e-315  5.3050e-315  5.3076e-315  0.0000e+000
      0       2       3  5.2998e-315  1.5910e-314  1.5910e-314  1.5910e-314
  93000       2       3  5.2972e-315  5.2972e-315  5.2972e-315  5.2972e-315

Great they look nothing alike. So something it totally broken. I guess the real question is, does it even work on OS/2?

Since I should post the NMAKE Makefile so I can remember how it can do custom steps so I can edit the intermediary files. Isn’t C fun?!

INC = /Ih
OPT = /Ox
DEBUG = /Zi
CC = cl386

OBJ = dhyrst.obj

.c.obj:
	$(CC) $(INC) $(OPT) $(DEBUG) /c /Fa$*.a $*.c
	wsl sed -e 's/FLAT://g' $*.a > $*.a1
	wsl sed -e "s/DQ\t[0-9a-f]*r/&XMMMMMMX/g" $*.a1 \
	| wsl sed -e "s/rXMMMMMMX/H/g" > $*.asm
	ml /c $*.asm
	del $*.a $*.a1 $*.asm

dhyrst.exe: $(OBJ)
        link -debug:full -out:dhyrst.exe $(OBJ) libc.lib

clean:
        del $(OBJ)
        del dhyrst.exe
        del *.asm *.a *.a1

As you can see, I’m using /Ox or maximum speed! So how does it compare?

Dhrystone(1.1) time for 180000000 passes = 20
This machine benchmarks at 9000000 dhrystones/second

And for the heck of it, how does Visual C++ 1.0’s performance compare?

Dhrystone(1.1) time for 180000000 passes = 7
This machine benchmarks at 25714285 dhrystones/second

That’s right the 1989 compiler is 35% the speed of the 1993 compiler. wow. Also it turns out that MASM 6.11 actually can (mostly) assemble the output of this ancient compiler. It’s nice when something kind of work. I can also add that the Infocom ’87 interpreter works as well.

YAY!

So it turns out GCC could have been available on Windows NT the entire time!

This is going to be a bit convoluted but here goes.. GCC isn’t a monolithic compiler, instead it’s various parts are separate programs. This lets us tackle it one part at a time. And/Or bypass a lot of it until I want to tackle it.

Flow of GCC

I’m sure many people have explained this far better than I ever could but in C you write source files (obviously), the pre-processor reads those and ‘header’ files that describe interfaces to libraries, other objects, various macros and definitions (magical numbers) and the pre-processor will read those files, and do simple macro expansion and test insert/replacements to generate a single .i file at the end of it’s run.

The C compiler (cc1) now reads that single .i file and translates it into native assembly. This allows for ‘mid/high level’ aspects of C to be machine independent (portable) but now will be written into a very system dependant assembly file, the single .S file. One thing of note is that so far everything is text files. You can edit the assembly file as you would any document, or even further ‘process’ it if needed/wanted.

The assembler ax386 (GAS) will then read the single assembly file and write a a binary object file hi.OBJ. There typically isn’t all that much to be said about assemblers although fancier ones allow for really strong Macro capabilities like Microsoft MASM.

From here on, it’s all binary objects!

The linker then takes your object files, and links them together with other system objects and system libraries into an executable, in this case. Linkers can build all kinds of other things, but for now we’re just pretending its static C compilation like it’s the 1970’s.

At it’s heart GCC processes text files.

The first part in this insane experiment, is to build GCC 1.40 with Microsoft Visual C++ 1.0. Surprisingly it didn’t take an insane amount of messing with stuff, and I got an executable! But everything it compiled failed to assemble. Looking at this fragment, even if you don’t know i386 assembly you might spot the error:

main:
        pushl %ebp
        a b,c
        pushl %esi
        pushl %ebx

Yeah, it’s the “a b,c” part. Those are NOT valid i386 opcodes!

Just because it compiled didn’t mean it actually worked.

I used MinGW to build the same source, same Makefile, and I got a working executable. Annoyed I started compiling random files with Microsoft C, and finally found the file that broke it all, it turned out to be insn-output.c needing to be compiled with the “/D__STDC__” flags. A quick modification of the Makefile and now I have a working CC1!

Okay, great, it’s well known back in the early dangerous ages of the 1980’s/1990’s that everyone wasn’t running Linux, nor were binary distributions of GCC that far spread, rather I think to re-enforce the source was available it was expected that you’d use your system compiler. Systems like DJGPP/EMX take the path of binding a.out object files into something that MS-DOS can run via a dos extender, or the bind utility to allow you to run the a.out on OS/2. What I wan’t to do is verify that in fact Windows NT was a viable host for GCC back in the public pre-releases of 1991.

I’m sticking with the December build 239 version as it has working floating point. Something that GCC has intrinsic support of, and I don’t feel like trying to work out emulation.

The next step is to try to build it with the family mode-OS/2 version of the C compiler, which of course lead to the real issue of this 16bit hosted cross compiler:

        cl386 /u /Od /Ic:\MSVC32S\C386\INCLUDE /I. /Iconfig /c combine.c
Microsoft (R) Microsoft 386 C Compiler. Version 1.00.075
Copyright (c) Microsoft Corp 1984-1989. All rights reserved.

combine.c
combine.c(1734) : fatal error C1002: compiler is out of heap space in Pass 2
NMAKE : fatal error U1077: 'C:\WINDOWS\system32\cmd.exe' : return code '0x2'
Stop.

Very frustrating. I tried mixing and matching from Visual C++ 1.0 & this old compiler, and while it did compile, it doesn’t run. does it mean anything?!

GCC 1.40 compiled by Microsoft 386 C Compiler. Version 1.00.075

I should point out that this should be an expected working configuration as GCC does build on Xenix using the 32bit Microsoft C 5.1/386 compiler. Furthered again that Xenix and these 1991 versions of NT use the same 32bit OMF object format. And expanding on the Xenixnt experiment using the Xenix’ified GAS assembler with old Visual C++ includes & libraries to produce a possible retro-early port of GCC to NT, the next move is to bulid GAS on NT.

Xenix GAS 1.38 compiled by by Microsoft 386 C Compiler. Version 1.00.075

GAS gave me some weird issues with ctype.h where it runs fine with the one from Visual C++ 1.0 but the OS/2 & NT pre-release both fail. However the old Pre-release compiler cannot deal with the much newer ctype include file. So after much hammering I amputated whatever was bothering it, and it’s just enough to build & run. Great!

Going back to the phases, I used a simple hello world program:

void main() {
 printf("Hello World!\n");
}

While not being a good program, it doesn’t include stdio.h, nor does it return anything. It’s terrible. But in this case it allows me to be lazy and sidestep the pre-processor cpp.exe. This way I can just directly run it through cc1 and get my assembler file hi.S

Next I pass it to ax386 (GAS) and get the resulting object file hi.OBJ

And finally link it with link.exe in this case.

Hello World from GCC 1.40 on NT!

And with all the drama I’ve now compiled a simple hello world program on Windows NT.

If it were 1991, I would hollow out gcc.c so it doesn’t use signals or forks to invoke the needed phases, and of course build the pre-processor. In addition, libgcc needs to be compiled to allow for floating point operations to work correctly. None of which is impossible, although I’m not sure it’s all that needed as it isn’t 1991.

phoon

With a little bit more work, I got the floating point support to compile, which relies on both a working ‘native’ compiler, and a working GCC to compile the 2nd half. I usually use phoon, or Phases of the Moon, to test floating point, and as you can see, it’s working!

I’m not sure if there was a 32bit version of Microsoft C/386 available for Microsoft OS/2 2.00 betas. Also, I don’t know if the Microsoft link386 for OS/2 can also link Xenix 386 object files? Would it have been possible to bootstrap GCC/GAS on Microsoft OS/2 2.00? I really don’t know, and as of this writing no versions of the old Microsoft OS/2 2.00 betas have surfaced.

** update from the future, turns out that I found a way to convince the cl386 compilers from the NT Pre-Releases in 1991 to re-build an existing GCC that was built for NT. The catch is the linker, LINK386 of course, as the format was constantly changing. However the object files are fine, and I was able to just copy them over on diskette and re-link the compiler. It even ran. It’s not tested at all, so it turns out the 1989 compiler wasn’t good enough, but the 1991 was.

GCC 1.40 on OS/2 2.00 beta 6.123

It’s interesting to me to see that even before GCC 2.6, that vintage versions from 1991 would compile and run directly on Windows NT.

I uploaded the source on github, along with some binaries.

Building OS/2 apps from QuickC for Windows

Hello from QuickC for Windows

Back in the olden days of when Microsoft had pivoted out of OS/2 in a hurry, I’ve always felt that the common ‘OMF’ objects ought to link for OS/2. But for some reason I never tried. But for some reason I thought I’d try it today.

I first installed Microsoft C 6.0, and set it up for a native OS/2 to OS/2 1.2 setup. This way I get a pure OS/2 include/library directory set. In retrospect, I don’t know why I didn’t just use 2 include / library directory sets to far easier target stuff, without dealing with changing the default names, and making linking an all around living hell.

So the first thing to do is to tell QuickC for Windows to default to the OS/2 include directory (turns out it wont link anyways). Compiling is nothing special. When setting up the project you’ll need a DEF file, I use this simple one:

NAME        QCO2 WINDOWAPI
PROTMODE
CODE        PRELOAD

Nothing to it!

I tried to fight the Windows linker, but it figures out what you are doing and won’t do it. But can you manually link? Well QuickC for Windows does include a DOS linker, and it’s oddly enough newer than the one for Microsoft C 6.0!

C:\proj\o2>msdos \WIN16APP\QCWIN\bin\link hi.obj

Microsoft (R) Segmented-Executable Linker  Version 5.15
Copyright (C) Microsoft Corp 1984-1991.  All rights reserved.

Run File [hi.exe]:
List File [NUL.MAP]:
Libraries [.LIB]: doscalls SLIBCE
Definitions File [NUL.DEF]: qco2.def

C:\proj\o2>msdos hi.exe
This program cannot be run in DOS mode.

Manually invoking the linker wasn’t all too hard, just answer the 30 questions. I did set the LIB environment variable so it picked up the libraries just fine. And yes, it created my OS/2 binary no problem!

And as you saw from above, yes it does run!

I do suppose the graphical editors would have been nice some 30 years ago, but in today’s era, sadly it doesn’t matter. QuickC for Windows won’t run under WLO, so this prevents it being a backdoor GUI/Protected mode compiler for OS/2. It’s a shame too as at least running under Windows 3.0, QuickC for Windows is WAY faster than using Microsoft C 6.00 in either read mode, protected mode with smartdrive.. I’m not sure what the deal is. Even with the advanced caching SCSI controller.

Not sure if anyone cares, but I think it’s cool.

Inside Windows 2000

part 1

A talk from Dave Probert on the design of the NTOS kernel. Shame Microsoft didn’t put this anywhere people could have found this 20+ years ago, just as a shame they never opened up NTOS like they did that even tepid Windows Research Kernel. It goes without saying this is the ‘Linux is a cancer’ generation, with the crazed idea that looking at Linux would contaminate Windows.

PART 2: https://www.youtube.com/watch?v=OrohgqgzhgM

PART 3: https://www.youtube.com/watch?v=SUK_OfEMQos

PART 4: https://www.youtube.com/watch?v=JqnvmedwkYc

Too little, too late I guess. It would have been an interesting foundation a long time ago.

And of course the ongoing work behind the scenes making Windows fully 64bit compatible on the Alpha at the time.

Bill Nye, the Microsoft C 6 guy

So, a while back I had found this up on eBay. As much as I’m trying not to buy old stuff I just couldn’t resist. And the price was just too good, I’d just have to forego going out to dinner for a week.

While looking around for something on Microsoft C, I stumbled upon this promo video for Microsoft C 6. Naturally I had to share it!

I had been using it to mess around with a poorly ported Hack 1.03, although I haven’t done much with that in a while.

One thing is for sure, that the old MS-DOS memory limits were becoming more and more of an issue. Sadly, they didn’t include the QuickC for Windows product which had the benefit of building in protected mode for access to far more memory, nor did they include any DOS Extender to even allow larger runtime access. Obviously you were expected to run this under MSOS/2 1.2 in this era. Although targeting OS/2 protected mode allowed easier integration with PharLap’s 286 based DOS Extender.

Since this was the OS/2 era, the Windows 3.0 SDK was a separate product.

There was another release, the 6.00ax version which included a DOS Extender, allowing the compiler to access 16MB of ram, as reported in this leaflet in a combined Microsoft C & Windows 3.0 SDK package.

The followup Microsoft C/C++ 7.0 addressed many of these shortcomings, but of course famously removed targeting OS/2. There was a later update that at least provided OS/2 compiled version of the binaries allowing you to run it under OS/2. I never tried to see if it could be paired with the OS/2 SDK, and manually made to generate OS/2 executables. I suspect not.

The larger thing is that Microsoft C 386 remained a ‘hidden’ product on Xenix, and the 32bit OS/2 and NTOS/2 betas.

Fun with Windows ePDK NT 3.1 build 196 & Some Xenix fun!

I’m not sure if I covered the Windows NT 3.1 build 196 before. First the most obvious is that as of this moment it’s the earliest version of Windows NT available.

So let’s do some obligatory scratch of the surface. Like all the other 1991 pre-releases there really is just a text mode setup install script. Choosing the lesser amount of pain, I went with a MS-DOS hosted install. However, using MS-DOS 6.22 resulted in a broken dual boot system. But we live in the era of virtual machines, so it really doesn’t matter. I’m using Qemu 0.14-rc2, something that is ‘era correct’ for when the first avalanche broke on early Windows NT pre-releases. I’ve had issues with more modern versions of Qemu, and I felt that if we’re using vintage software may as well go in all the way.

The boot loader identifies itself as being 1990 vintage. Pretty sure it doesn’t mean anything, but we haven’t been blessed with the “blue screen” of ARC yet.

The login screen and desktop have a very strong Windows 3.1 beta feel to them. And that would land this where it was, Windows was such a big seller that Maritz had been trying to convince Gates & Balmer to ‘switch to the Windows horse’ in the spring/summer of 1990, culminating in Gate’s July decision to walk away from NT/OS2 and rebrand the new OS as Windows NT. Oddly enough it was Balmer who was in favour of OS/2 & IBM. (Showstopper 89-90)

At first glance the opening window isn’t all that interesting. It’s just very. Windows.

Rest assured Reversi, of course made the this Win32 cut. And it’s full of weird easter eggs, oddly enough in the OS/2 surviving bits.

type OS2LDR.DOS
OS2LDR 01.00.01
by KeithMo 01/08/91

On the Discord there had been a big discussion about early NT executable formats, and the whole COFF vs ECOFF vs PE/PEI. I had tried to hunt down the specific version of GCC I used ages to to build a so called Dec Alpha GCC cross compiler, but the short version is that it didn’t work as we don’t have any assembler/linker for anything GNU targets. There had been a cygwin port and an OpenNT on Alpha, but all that is lost to the winds, minus what few scraps I had saved. I did try building some cross tools to elf hoping to just objcopy out the data and get linkable objects, but that didn’t work either.

So I though this was a perfect opportunity to take a look at this early pre-release version of NT, and although I do know that you have to convert your objects into something the ‘COFF’ linker will accept:

And I never really paid that much attention to the object files. I do know that you can link them with Link386 for OS/2, but the NT object files themselves report:

wsl file SIMPLE.OBJ
SIMPLE.OBJ: Intel 80386 COFF object file, not stripped, 4 sections, symbol offset=0x102, 20 symbols, created Thu Jun  1 14:13:50 2023, 1st section name ".text"

Well, now that is interesting. And of course the COFF thing reminded me of Xenix! And sure enough ages ago I had found the source to a modified version of GNU GAS that outputs COFF. Once more again this is an indication that all 386 roads in Microsoft really did originate with Xenix. It’s too bad there never was a Windows/386 on Xenix/386. What an incredible OS that would have been! There must be some incredible stories from the tool teams that worked on Microsoft C/386 along with other projects. Oddly enough they never get anywhere near as much exposure as Office or OS.

Now this is fun, but nothing takes in these ancient COFF objects, do they? I tried to run LINK 1.0 from Win32s SDK and surprisingly it didn’t complain about the object, rather, it auto converted it in memory:

Microsoft (R) 32-Bit Executable Linker Version 1.00
Copyright (C) Microsoft Corp 1992-93. All rights reserved.

SIMPLE.OBJ : warning LNK4016: unresolved external symbol "__chkstk"
LINK : warning LNK4016: unresolved external symbol "_mainCRTStartup"
SIMPLE.OBJ : warning LNK4016: unresolved external symbol "_printf"
SIMPLE.exe : error LNK1120: 3 unresolved externals

Does this mean that if I give it some libraries it will actually link?

LINK.EXE SIMPLE.OBJ /SUBSYSTEM:CONSOLE /MACHINE:i386 -entry:mainCRTStartup -out:simple.exe libc.lib kernel32.lib
Microsoft (R) 32-Bit Executable Linker Version 1.00
Copyright (C) Microsoft Corp 1992-93. All rights reserved.


C:\temp\nt196\x\dec\x>simple
Win32, it's happenin'!

This was. VERY unexpected.

So I had this crazy idea, what if the Xenix assembler could in fact build objects that are also compaible in this manner? I used the a.out GCC / Linux porting tools I had built so I could compile Linux on Windows NT using the vintage tools as a starting point. I guess I should also add that when people always say ‘use newer version of THING’ this is how you miss out on old stuff like this. If I had been obsessed with using modern tools and modern operating systems, I’d have missed out on this Xenix filled window.

I took the gcc driver & the cc1 compiler from 1.40 and the c pre-processor from 2.5.8 as it can understand C++ comments. First I manually compiled the ‘simple’ example to assembly:

gcc -v -nostdinc -I/xenixnt/h -S SIMPLE.c -O simple.S
gcc version 1.40
 cpp -nostdinc -v -I/xenixnt/h -undef -D__GNUC__ -Dunix -Di386 -D__unix__ -D__i386__ -D__OPTIMIZE__ SIMPLE.c C:/Users/jsteve/AppData/Local/Temp/cca2_048.cpp
GNU CPP version 2.5.8 (80386, BSD syntax)
#include "..." search starts here:
#include <...> search starts here:
 /xenixnt/h
End of search list.
 cc1 C:/Users/jsteve/AppData/Local/Temp/cca2_048.cpp -quiet -dumpbase SIMPLE.c -O -version -o SIMPLE.s
GNU C version 1.40 (80386, BSD syntax) compiled by GNU C version 5.1.0.
default target switches: -m80387
 cpp -nostdinc -v -I/xenixnt/h -undef -D__GNUC__ -$ -Dunix -Di386 -D__unix__ -D__i386__ -D__OPTIMIZE__ simple.S C:/Users/jsteve/AppData/Local/Temp/cca2_048.s
GNU CPP version 2.5.8 (80386, BSD syntax)
#include "..." search starts here:
#include <...> search starts here:
 /xenixnt/h
End of search list.

Which gave me the following assembly:

        .file   "SIMPLE.c"
gcc_compiled.:
.text
LC0:
        .ascii "Win32, it's happenin'!\0"
        .align 2
.globl _main
_main:
        pushl %ebp
        movl %esp,%ebp
        pushl $LC0
        call _printf
        leave
        ret

Now to assemble with the GAS Xenix assembler

C:\temp\nt196\files\MSTOOLS\SAMPLES\SIMPLE>ax386 SIMPLE.s -o SIMPLE.obj

C:\temp\nt196\files\MSTOOLS\SAMPLES\SIMPLE>wsl file SIMPLE.obj
SIMPLE.obj: 8086 relocatable (Microsoft), "SIMPLE.c", 1st record data length 10, 2nd record type 0x88, 2nd record data length 11

Not quite the same. But it does closer resemble the output from the OS/2 bound versions of the Pre-Rease compilers:

file EMPTY.OBJ
EMPTY.OBJ: 8086 relocatable (Microsoft), "empty.c", 1st record data length 9, 2nd record type 0x88, 2nd record data length 7

So will it link?!

C:\temp\nt196\files\MSTOOLS\SAMPLES\SIMPLE>LINK.EXE SIMPLE.OBJ /SUBSYSTEM:CONSOLE /MACHINE:i386 -entry:mainCRTStartup -out:simple.exe libc.lib kernel32.lib
Microsoft (R) 32-Bit Incremental Linker Version 2.50
Copyright (C) Microsoft Corp 1992-94. All rights reserved.

SIMPLE.OBJ : warning LNK4033: converting object format from OMF to COFF

C:\temp\nt196\files\MSTOOLS\SAMPLES\SIMPLE>simple
Win32, it's happenin'!

Well now this is interesting! LONG before MinGW, or the GCC port to Windows NT, it turns out that in fact GCC could target Windows NT the entire time!

So the next thing to do is something not as trivial, like phoon.

I setup some quick script to pre-process, compile, assemble and then try to link, but as this one uses floating point, disaster struck:

phoon.obj : error LNK2001: unresolved external symbol "___fixdfsi"
astro.obj : error LNK2001: unresolved external symbol "___fixdfsi"
phoon.exe : error LNK1120: 1 unresolved externals

Now ages ago while messgin with old GCC & DooM I also had weird math calls not working. In the end I ended up extracting them from libgcc builds, so I thought I’d try the libgcc built during the GCC 2.6.3 on NT adventure.

link /NODEFAULTLIB:libc.lib /NODEFAULTLIB:OLDNAMES.LIB -out:phoon.exe astro.obj date_p.obj phoon.obj -entry:mainCRTStartup libgcc1.lib LIBC.LIB KERNEL32.LIB
Microsoft (R) 32-Bit Incremental Linker Version 2.50
Copyright (C) Microsoft Corp 1992-94. All rights reserved.

And of course:

To try to make the steps make a little more sense, and to allow for some higher level of automation I made a Makefile:

CC=gcc
CC1=cc1
AS=ax386
CPP=cpp


CFLAGS= -O
CPPFLAGS= -lang-c-c++-comments -nostdinc -I/xenixnt/h

OBJ =   astro.obj \
date_p.obj \
phoon.obj

LIBS = libgcc1.lib LIBC.LIB KERNEL32.LIB

phoon.exe: $(OBJ)
        link -out:phoon.exe $(OBJ) -entry:mainCRTStartup $(LIBS)

%.obj: %.c
        $(CPP) $(CPPFLAGS) $< $*.i
        $(CC1) $*.i -quiet $(CFLAGS) -version -o $*.S
        $(AS) $*.S -o $*.obj


clean:
        del $(OBJ) *.i *.S phoon.exe

I’m sure there is better ways to do this, but it breaks the compile up to it’s individual parts:

Run the pre-processor to allow // in the comments, C++ hadn’t been the default thing back when GCC 1.40 was a thing. Also path it to the headers, in this case I’m using the ones from NT 196. Trying to link with the 196 libraries gave me this:

C:\xenixnt\demos\phoon>link /NODEFAULTLIB:LIBC.LIB /NODEFAULTLIB:OLDNAMES.LIB -out:phoon.exe astro.obj date_p.obj phoon.obj -entry:mainCRTStartup base.lib wincrt.lib ntdll.lib \xenixnt\lib\libgcc1.lib
Microsoft (R) 32-Bit Incremental Linker Version 2.50
Copyright (C) Microsoft Corp 1992-94. All rights reserved.

wincrt.lib(maincrt0.obj) : warning LNK4078: multiple ".data" sections found with different attributes (40000040)
astro.obj : error LNK2001: unresolved external symbol "_asin"
astro.obj : error LNK2001: unresolved external symbol "_atan"
astro.obj : error LNK2001: unresolved external symbol "_atan2"
phoon.obj : error LNK2001: unresolved external symbol "_cos"
astro.obj : error LNK2001: unresolved external symbol "_cos"
astro.obj : error LNK2001: unresolved external symbol "_floor"
astro.obj : error LNK2001: unresolved external symbol "_sin"
phoon.obj : error LNK2001: unresolved external symbol "_sqrt"
astro.obj : error LNK2001: unresolved external symbol "_sqrt"
astro.obj : error LNK2001: unresolved external symbol "_tan"
phoon.exe : error LNK1120: 8 unresolved externals

Which is not surprising as there is no FPU/Floating point math support in 196. I tried the December 1991 Pre-Release, but it failed for other reasons:

I did copy over BASE.DLL BASERTL.DLL CSR.DLL DBGDLL.DLL as it wanted, but despite the symbol being in the DLL it didn’t load.

So that’s why I’m using the libraries from the Win32s SDK.

Okay, so far now we have GCC 1.40 compiling to an old Xenix GAS assembler, and linking with Microsoft link from Visual C++ 1.0/2.0 era. The next step is to see if we can just link the objects under 196, and get a running EXE!

I have this tiny fibonacci example program, so with it compiled & assembled by GCC & GAS, I did the final link under 196, and YES it runs!

I then built the ’87 InfoTaskForce, Infocom interpreter, and it was just a simple link, and it’s running!

Possible things to do? GCC should be able to build itself, so it should be possible to build GCC and link that on 196 or December 1991, and get a native version of GCC on NT. The other possibility is to get newer versions of GCC (cc1 drop in replacements) to build for Xenix and / or OS/2. Obviously this Xenix linker is the gateway to older 386 Microsoft based products!

For those interested in such things, I’ve uploaded all of this to archive.org here: windows-nt-196-linking-and-running-gcc

Everyone seems to be losing their minds over the Windows XP Professional Key

algorithm being cracked.

But of course, how does that help me?

Unironically, I had purchased this for a whopping £4.68

No, really here’s the receipt. What a bargain!

Of course this is a legit copy with a legit key. But the online activation servers are all gone, and it looks like I’d have to call someone asking about my 22 year old copy of Windows, that I’ll load up and quickly forget.

Since I’m going to use QEMU, 0.90 with pcap support I thought I’d share the startup options:

set loopback=\Device\NPF_{3DF0EC5D-7FBE-46DF-ACF8-EF5D8679A473}
set vmnet1=\Device\NPF_{3BC364F4-5A15-405D-926C-C594383F0323}
qemu -m 512 -L pc-bios ^
-hda xphome.vmdk ^
-soundhw es1370 ^
-net nic,model=pcnet,macaddr=52:24:00:33:00:01 ^
-net pcap,devicename=%loopback% ^
%1 %2 %3 %4 %5 %6

I had high hopes for this thing. Clearly misplaced ambitions.

First up, it’s an upgrade version. So that means instead of installing XP I had to waste my time installing NT Workstation 3.51, then installing XP. Yuck. And of course it just want small FAT disks of the 2/4 gigabyte boundary type as it’s 1994. Not the bright future of 2002’s Windows XP.

I don’t know why Qemu 0.90 has issues with XP detecting the CD-ROM drive, but yeah that sucked. I wanted to load up some more insane SNA experiments, but there is no DLC / 802.2 driver for XP Home. wow.

At least once it’s satisfied, we can format the disk as one big happy partition, and we can get on with our lives.

Installation is rather uneventful, however we are instantly reminded that we have only 30 days to go. Since we have that nasty CD-ROM issue that means shutting down, and booting back up, but with this fun program on an ISO image, xp_activate.

I did try to make a call, to activate my Windows, but the connection was terrible and I’m not even sure if these numbers were right. No I mean I know they didn’t work.

So I did what all legit users end up doing, using the crack for my 21 year old copy of Windows.

And just a few clicks later, it was done.

Windows XP Home is activated.

I don’t know if it’s even really going to last, I didn’t try anything else, actually I already deleted it. And the XP folio is back on the bookshelf.

Not only is there no DLC, did you know you can’t uninstall TCP/IP? At least you can unbind it from your NIC. While it does have IPX/SPX there is no built in Netware client. When they said HOME they meant it!

Looking back at MS-DOS 4.00M, or in the beginning before there was OS/2

With the pre-christmas release of the Microsoft OS/2 betas 1.00, 1.01, 1.02, 1.03 & 1.05 on archive.org, and helping Ncommander with an upcoming video, it seemed like a good place to start, not with OS/2 but rather with MS-DOS 4.0.

From the book INSIDE OS/2 ( ISBN 1-55615-117-9 )

Microsoft started work on a multitasking version of MS-DOS in January 1983.  At the time, it was internally called MS-DOS version 3.0. When a new version of the single-tasking MS-DOS was shipped under the name MS-DOS version 3.0, the multitasking version was renamed, internally, to MS-DOS version 4.0. A version of this product–a multitasking, real-mode only MS-DOS–was shipped as MS-DOS version 4.0. Because MS-DOS version 4.0 runs only in real mode, it can run on 8088 and 8086 machines as well as on 80286 machines. The limitations of the real mode environment make MS-DOS version 4.0 a specialized product. Although MS-DOS version 4.0 supports full preemptive multitasking, system memory is limited to the 640 KB available in real mode, with no swapping.2 This means that all processes have to fit into the single 640 KB memory area. Only one MS-DOS version 3.x compatible real mode application can be run; the other processes must be special MS-DOS version 4.0 processes that understand their environment and cooperate with the operating system to coexist peacefully with the single MS-DOS version 3.x real mode application.     

Because of these restrictions, MS-DOS version 4.0 was not intended for general release, but as a platform for specific OEMs to support extended PC architectures. For example, a powerful telephone management system could be built into a PC by using special MS-DOS version 4.0 background processes to control the telephone equipment. The resulting machine could then be marketed as a “compatible MS-DOS 3 PC with a built-in superphone.” Although MS-DOS version 4.0 was released as a special OEM product, the project–now called MS-DOS version 5.0–continued. The goal was to take advantage of the protected mode of the 80286 to provide full general purpose multitasking without the limitations–as seen in MS-DOS version 4.0–of a real-mode only environment. Soon, Microsoft and IBM signed a Joint Development Agreement that provided for the design and development of MS-DOS version 5.0 (now called CP/DOS). The agreement is complex, but it basically provides for joint development and then subsequent joint ownership, with both companies holding full rights to the resulting product.

As the project neared completion, the marketing staffs looked at CP/DOS, nee DOS 5, nee DOS 4, nee DOS 3, and decided that it needed…you guessed it…a name change. As a result, the remainder of this book will discuss the design and function of an operating system called OS/2.

– Inside OS/2.

Although MS-DOS 4.00M disk images have been floating around for quite some time, either a 2 360k disk set, or a single 720k disk image, I don’t think anyone (including me) really tore into it that much. It does have the ability to freeze DOS 3 programs, giving the illusion of running more than one. The session manager is pretty sparse but hitting left alt twice will pop it up giving you the ability to toggle through programs with ease.

MS-DOS 4.00M

There is a FDISK, FORMAT & SYS command making it straight forward to setup a hard disk, and copy the files over, I didn’t see any installer.

there is a PS command to show running processes. Also there is a DOSSIZE to show the memory partitioning and how much is available. Although there is a SWAPPER program I’ve been unable to get it to actually fun.

multitasking!

Another interesting thing if you run the unix ‘strings’ command against all the EXE’s you’ll find the string:

C Library - (C)Copyright Microsoft Corp 1985

Implying that not only was DOS 4.00M a ‘new’ DOS, but it was also written in C. No doubt this contributed to a larger file size than DOS 3, however it would also give that holy grail of portability, at least to new CPU modes. Also many files have the name of the source files baked in such as:

@(#)append.c    1.1 85/10/09
@(#)assign.c    6.1 85/10/23
@(#)attrib.c    6.1 85/10/24
@(#)fdisk.c     1.1 85/10/09
@(#)fddata.c    1.1 85/10/09
@(#)fdlow.c     1.1 85/10/09
@(#)fdsub.c     1.1 85/10/09
@(#)joinsbst.c  6.3 85/11/08
@(#)sysvar.c    6.2 85/11/08
@(#)cds.c       6.2 85/11/08
@(#)dpb.c       6.1 85/11/08
@(#)label.c     6.1 85/10/24
@(#)newdef.y    6.2 85/10/14
@(#)ms4bnr.c    1.1 85/10/15
@(#)mode.c      6.2 85/10/24
@(#)getkey.c    6.1 85/10/25
@(#)pifmes.c    6.1 85/10/25
@(#)advpscrn.c  6.1 85/10/25
@(#)advescrn.c  6.1 85/10/25
@(#)usrscrn.c   6.1 85/10/25
@(#)rangers.c   6.1 85/10/25

Okay so far, so good. But we’ve all seen this before, and scratched this OS about this far, because what else can you do? It’s not like there is any dev tools to do anything fun!

Well the tool hidden in plain sight is LINK4, which in retrospect is specific for MS-DOS 4.00M.

Microsoft (R) 8086 Object Linker  Version 4.01
Copyright (C) Microsoft Corp 1984, 1985.  All rights reserved.

Object Modules [.OBJ]:

There is no SDK for MS-DOS 4.00M, but they were kind enough to leave the linker in place. A quick check of the Windows 1.01 SDK shows that it also includes LINK4:

Microsoft 8086 Object Linker
Version 4.00  (C) Copyright Microsoft Corp 1984, 1985

Object Modules [.OBJ]:

It appears that if the dates and versions are to be trusted they are of the same vintage, but the Windows linker is older, and that they both output to a NE or New Executable. So to start the experiment I created a simple hello world exe from a simple:

void main(){
  printf("Hello from MSC 3\n");
}

To compile this I used Microsoft C 3.0 (more on why later), and used LINK4 to create an EXE:

C:\dos\msc3>cl /c hello.c
Microsoft C Compiler  Version 3.00
(C)Copyright Microsoft Corp 1984 1985
hello.c

C:\dos\msc3>msdos dos4m\link4 hello.OBJ

Microsoft (R) 8086 Object Linker  Version 4.01
Copyright (C) Microsoft Corp 1984, 1985.  All rights reserved.

Run File [HELLO.EXE]:
List File [NUL.MAP]:
Libraries [.LIB]:
Definitions File [NUL.DEF]

Okay, everything looks fine so far. Attempting to run this under MS-DOS just results in the error:

Program too big to fit into memory

Well now that’s odd. Checking the EXE with the Linux ‘file’ command reveals:

file HELLO.EXE
HELLO.EXE: MS-DOS executable, NE (unknown OS 0) (EXE)

So obviously it’s a NE, but it is an older/unknown version to the file map database. There is no stub so I suppose that is why MS-DOS is getting confused.

Now let’s try MS-DOS 4.00M

Hello!

Well now isn’t that interesting?!

Excited with the ability to create special MS-DOS 4.00M programs, I get my favorite vintage ’87 Infocom interpreter, InfoTaskForce 87, and get it building on MSC 3.0. However instead of using the MS-DOS 4.00M linker, I thought I should try to use the Windows 1.01 linker and libraries for the exe:

C:\dos\msc3\infocom>msdos ..\win101sdk\bin\LINK4.EXE @infocom.win.lnk

Microsoft 8086 Object Linker
Version 4.00  (C) Copyright Microsoft Corp 1984, 1985

Object Modules [.OBJ]: FILE.OBJ FUNCS.OBJ INFOCOM.OBJ INIT.OBJ INPUT.OBJ +
Object Modules [.OBJ]: INTERP.OBJ IO.OBJ JUMP.OBJ OBJECT.OBJ OPTIONS.OBJ PAGE.OBJ +
Object Modules [.OBJ]: PRINT.OBJ PROPERTY.OBJ SUPPORT.OBJ VARIABLE.OBJ TERM.OBJ
Run File [FILE.EXE]: INFOCOM.EXE/ALIGN:16
List File [NUL.MAP]: INFOCOM.MAP
Libraries [.LIB]: MWLIBFP MWLIBC/NOD
Definitions File [NUL.DEF] INFOW.DEF;

And for those interested this is my .DEF file:

NAME    Infocom

DESCRIPTION 'Infocom 87 interpreter for Planetfall(83)'

DATA    MULTIPLE


HEAPSIZE    1024        ; Must be non-zero to use Local memory manager
STACKSIZE   4096        ; Must be non-zero for SS == DS
                        ; suggest 4k as minimum stacksize

SEGMENTS
    _INIT   PRELOAD MOVEABLE DISCARDABLE

One thing to save you the horror is that between MS-DOS 2 & 3 the way command line arguments changed. I forget the details but no matter what I tried I was unable to parse the CLI or the environment in this setup. I suppose if I had documentation of the product there would be some hint as to what tools or setup to use. Instead, I took the easy way and hard coded to load Planetfall.

InfoTaskForce compiled with MSC 3.0, using Windows 1.01 libc / LINK4

Unfortunately, this success would prove to be the exception to the rule. I took trek, converted it to K&R C, as Microsoft C 3.00 from 1985 is well. old, and sadly it just won’t run. Likewise, I took Hack 1.03 and although it runs on MS-DOS it will not run on MS-DOS 4.00M. I am sure there is some fundamental reason why it’s not working, and probably tied to creating a proper DEF file. I’m sure it was all written down somewhere but I don’t know. And yes I tried specifying either floating point emulation via library or inline, and it made no difference.

Looking at OS/2 1.00

Loading up the infamous $3,000 OS/2 1.00 beta, and hitting ctrl+escape you are greeted with session manager!

Session Manager for OS/2

Notice the R for real-mode. With the obvious implication that everything else is protected mode. Going one step further on the excellent site pcjs.org there is OS/2 betas SIZZLE and although there is no OS/2 development bits on there, the directory DOS3TOOL reveals that the C compiler for this era for at least MS-DOS is MSC 3.0. Also included is our friend LINK4.

I create a simple def file that contains the single word ‘PROTMODE’ which should give me my OS/2 binary.

So let’s run that through hello world:

msdos sizzle\DOS3TOOL\link4  hello.OBJ,hello,,,hello.def;

Microsoft (R) Segmented-Executable Linker  Version 5.00.21
Copyright (C) Microsoft Corp 1984, 1985, 1986.  All rights reserved.


C:\dos\msc3>

However attempting to run this just crashes amazingly.

Real mode LIBC in Protected mode:

No doubt it’s because the real-mode libc is using interrupt 21 calls, which OS/2 sure wouldn’t like. I’m pretty sure it requires an OS/2 libc that uses DOSCALLS.DLL to function, which I just don’t have any pre-release versions, nor any libc source code to really make it possible. And attempting to port one to OS/2 pre-releases just doesn’t seem so worth the time.

So for the heck of it I point the LIB variable to the OS/2 1.00 SDK’s libs and re-run the link:

C:\dos\msc3>msdos sizzle\DOS3TOOL\link4  hello.OBJ,hello.exe,hello.map,C:\86box\100\x\MSC\LIB\slibc5.lib \86box\100\x\LIB\DOSCALLS.LIB,hello.def;

Microsoft (R) Segmented-Executable Linker  Version 5.00.21
Copyright (C) Microsoft Corp 1984, 1985, 1986.  All rights reserved.

By default it’s trying to link in EM.LIB, SLIBFP.LIB, SLIBC.LIB. Trying to add them all in the command line link just hangs LINK4 maybe a response file is better suited. Anyways:

Hello from MSC 3.0 in protected mode.

It does run on OS/2 1.00, which I guess isn’t surprising as the LINK4 & libraries are from/for this version.

As an interesting note, OS/2 links against doscalls library/DLL to interface to the OS. While MS-DOS 4.00M doesn’t have a seperate DLL, rather it’s baked into IBMDOS.COM

DOSCALLS
ALLOCSEG
REALLOCSEG
FREESEG
LOCKSEG
UNLOCKSEG
GETSEGSIZE
GETDSHANDLE
CRITENTER
CRITLEAVE
FCRITENTER
FCRITLEAVE
PBLOCK
PRUN
SUBSCREEN
GETPIDS
DOSDISCARDCODE
DOSGETHANDLE
DOSHANDLEJUMP

Noticeably absent is file I/O, No doubt allowing programs to use the standard int21 interface to the kernel for file I/O. No doubt this is in its primordial state, as the OS was going to evolve a bit more until it became OS/2. Unfortunately I have no idea how to link or call into this. Without any SDK it’s impossible to say. And even then is developing for a real mode OS worth the effort?

So what have we learned? LINK4, aka the MS-DOS 4.00M Linker, probably should have been called LINKNE for the NE format. Also there is references to it having it’s own virtual memory paging system, and being able to link larger EXE’s than the traditional link command. Sadly I was unable to get any non trivial programs running. I don’t think it was a memory model thing, although the C compiler has issues with InfoTaskForce and the large memory model for some reason, but small & medium work fine. I’d like to think that DOS 4.00M could support massive EXE’s much like Windows 1.01, however despite being from the same company and using the same tools, the memory manager for DOS 4.00M & Windows is fundamentally different.

With all these exiting OS/2 betas now available I’ll have to take some more time to explore them in more detail.

But until then I thought this genesis of DOS 4.00M was worth the look.