Virtualization Challenge IV – Act II – QNX 1.2 HDD Boot ($2000 prize)

(This is a guest post by Antoni Sawicki aka Tenox)

A couple of months ago we hosted VIrtualization Challenge for QNX v1.2. I expected that the hard part would be to circumvent the copy protection and the rest would be easy. It turned out to be quite the opposite! The copy protection was worked around in no time by several people independently. What turned out to be impossible is to install the OS on a hard disk.

QNX 1.2 does have several drivers for different hdd controllers including BIOS mode. It has fdisk, can create partitions, install MBR, format fs, mount hard disk volumes… but it cannot install boot code. Apparently this functionality has been added only in QNX 2.x. After a long debate we settled for a solution where you boot kernel from a floppy disk and use the rest of the os from a hard disk. This was implemented by Forty who won the challenge which was outlined in this post.

In a rather unexpected turn of events Dan Dodge, co-creator and CEO of QNX Software Systems himself reached out to us and offered to extend the contest to finish the process properly. Dan is offering $2000 prize for making QNX 1.2 boot from hard disk without use of the boot floppy disk. I have confirmed the details in an email exchange.

Rules: As always the winner will be the first person who provides a working image in the comments. Any emulator/hypervisor is allowed. You can use boot loader from QNX 2.x, or write your own or anything else you come up with. There are some tips in Dan’s comment. Ask away for more details. The QNX repository is here. Good luck! πŸ™‚

Update: The challenge has been completed! The winner is Mihai Gaitos and this is the winning entry. I will work with Mihai to get a more detailed blog post of what has been done and Dan to hand out the $2000 prize. Congratulations!!!

92 thoughts on “Virtualization Challenge IV – Act II – QNX 1.2 HDD Boot ($2000 prize)

  1. QNX 2.1 brings back many fond memories. One day I need to restore the computer I wire-wrapped up to develop the first version of QNX on. Gordon Bell and I each built our own computers while staying in residence at the University of Waterloo (2nd year). This was well before the IBM PC.

    When you build your own computer you quickly realize that nothing will run on it. So I decided to write my own operating system. Inspired by Unix and some early work on message passing happening at the University of Waterloo I chose to write a microkernel OS which was Unix like.

    The project did not start with the OS but the tools needed to write it. So I started by teaching myself how to write a C compiler. The C compiler was broken into four components (parser, code-gen, assembler and linker). These were written on a Honeywell 6660 in the language B (there was no C on the mainframe). With this in place Gord and I started working on the operating system producing images which we downloaded to our home made computers via a modem link.

    When we graduated we would loose access to the Honeywell so we focused on self hosting the tool chain. This required us to port the tools written in B to C and fit them into 64K of memory when they ran. Our home build computers actually only had 48K of program memory as 16K was reserved for the display.

    As part of the self hosting I wrote the full screen editor Ed (which I used for the next 20 years).

    From very early on we knew we were on a path to building a fully distributed operation system base upon a microkernel architecture and message passing. That appeared in QNX 2.0 and perfected in QNX 4.0.

    So what does QNX stand for? To explain its origin you need to go back to the early days when the company was called Quantum Software System and we called the OS Qunix. One day we got a letter from AT&T asking if our OS was based upon Unix source code. We said, “not a single line of code” and they said then you can’t have Unix as part of its name. So, we dropped the u and i which resulted in QNX which is pronounced Q-nix. Simple as that.

  2. Dan! You should open source some of the early, low level bits. A lot of people working with dual core microcontrollers like the ESP32 and K210 are facing tough issues that could use simple, lightweight message passing solutions could help with.

    • Hold on. I think that I just copied all the userspace from QNX 1.2 but the Kernel resides out-of-File System, cause it still reports as 2.15C. So it is possible that I was too overly excited since it is more than the Boot Loader πŸ™

  3. All right, full procedure of what I did, which took me around 8 hours.

    To be honest, I thought it wasn’t even worth to try participating in the contest because the prize is a lot of big, so competence should be even bigger in such context. Bsides, after several years stalking this blog and OS/2 Museum I was expecting that the contest would be finished before I even dare to move a finger, as all the guys that usually posts here seems to have light years more hands-on experience with vintage Hardware/Software that I do, plus programming and debugging, areas where I’m an absolute zero. I also fail with hexadecimal editors. But I was tempted to give it a try since it sounded perhaps too easy for such a big prize…

    First I checked the QNX 2.2 Manual, QNX22_OS_BOOK.pdf. At page QNX 18/19 it mentioned that it could use as HD Boot Loader either DOS or QNX own one. As thing stands, I though that either QNX 1.2 wasn’t either capable of creating a MBR, or a VBR in its own Partition, so it was worth a try to attempt to partition the disk and install a MBR with another tool then copy files to that partition to see what happens. If that didn’t worked, then it means that the QNX Partition still needed a VBR, going further than the mentioned standard DOS Boot Loader.
    Second, I tried downloading latest 86Box and the already packaged from the previous contest winner. With latest 86Box v3.0 build 2723 I never got to the QNX splash screen, it appears that it crashed with a blinking cursor in the top left corner. I got it working with 86Box v2.07 build 2008, the latest stable one from the project website, which also required to create the machine from scratch cause the newer CFG files uses a different syntax. Even after 8 more hours of experience configuring 86Box than when I first downloaded it, I still can’t boot that image with build 2723, so I pretty much confirm that there is something broken there (Albeit the HD image I uploaded curiously works).

    The MBR solution was the easiest to try. I was particularly enthusiastic cause I found out that QNX fdisk reported that on the HD image, the QNX Partition began at Cylinder 0, whereas in QNX 2 Manual at Page QNX 14 the first Partition began at Cylinder 1, so I thought that the HD was formatted as a Superfloppy with VBR but no MBR (Due to my lack of knowledge about old HD geometry it may be possible than it doesn’t overlap at all, since maybe the Partition starts at another Heads/Sector count). I wasn’t expecting QNX 1.2 to be able to partition the HD and flag its own partition as booteable, albeit it isn’t enough to get it booting.
    So, I grabbed a PC DOS 2.00 Diskette copy from PCjs, create a blank HD from scratch matching the 10 MB with standard 306 Cylinders, 4 Heads and 17 Sectors geometry, and used PC DOS fdisk to format the HD creating a DOS partition, then switched to QNX to create its own one. I picked the IMG files from the qnx12-kryoflux-raw.rar package to copy with the backup command from the diskses. Sadly, reformatting and copying the disk contents to the new partition failed, pointing out that the QNX Partition was still missing the VBR (The partition with PC DOS booted flawlessly when active), so the QNX 2 Manual didn’t really applied here. I thought about some Frankenstein type operation like copying the VBR from a booteable Diskette could save the day, but I suck at hexadecimal editors to edit the HD image. So I decided to take the long way of trying to install QNX 2 Boot Loader.

    For QNX 2.15C I used the qnx2-1-dist.tar.gz disks. I spend quite a bit of time figuring out why it got stuck while booting until I discovered that it wanted an IBM PC/XT with 512 KiB RAM (1986 model) and not 256 KiB (1982 model).
    First I tried to manually install QNX 2.15C to the HD following the Manual instructions, but I failed somewhere since the boot command always complained that I was trying to use a Hard Disk Driver for a Floppy Drive. I spend a lot of time figuring out how to workaround that, then I noticed that there was an install command, so I instead used it and voila, managed to install QNX 2.15C to a booteable HD, finally.
    Next step was to systematically strip QNX 2.15C of all its files until figuring out what was the bare minimum for it to reach near the end of the boot process. According to the QNX 2 Manual at Page QNX 20, there are at least two critical files, one is the HD Driver and another the Netboot:


    (Note that os.2.15Catp should be erasable since it goes unused, it is for Protected Mode)
    From then, I just copied qnx12_boot_patched.img and qnx12_utils.img contents to the HD, then tried to get it booting, which was successful and is the image I uploaded.
    Unless you go more low level by actually editing out-of-File System stuff, that is as much as I can go with my skill level. However, I noticed that what I copied is mostly user space as the entire Kernel remains in place, and that is far more than the allowed Boot Loader, so it may not actually meet the contest goal…

  4. Before I forget everything I did, it is better to write it down so that it can serve as a QNX crash course for other people. This will be my last Post here cause I capped out at what I can do. The next steps require reverse engineering and debugging gurus, and I’m not even at a begineer level there.

    I’m writing this for people that already have some experience in MS-DOS or Linux command line interface (I don’t come from a Unix background and my Linux experience is from this decade only), as it is easier if I explain what are the differences and quirks that you have to get used to before getting confortable with QNX. And also to note a few important differences between QNX 1.2 and 2.15C (Since the 2.2 User Reference is the closest that is available).

    I used two QNX versions, 1.2 and 2.15C.

    QNX 1.2 is found here:

    You want since it includes the copy protected patched qnx12_boot_patched.img bootable floppy, albeit you don’t need the other two IMG since they are for the preconfigured HD install from the previous contest.
    You also want qnx12-dd.tar.gz for the rest of the floppy diskses, which is far smaller than the qnx12-kryoflux-raw.rar which also contains them. You have to rename the files extension from .dd to .img (qnx12-kryoflux-raw.rar already has them renamed, but weights 60 MB or so. Less download time if you rename them). Note that the qnx12_boot.dd file is 356 KiB instead of 360 KiB in size when compared to qnx12_boot_patched.img and doesn’t boot, which is why you need the other ZIP with the patched one. The most important disk other than the boot one is the qnx12_utils.

    You can use in A drive qnx12_boot_patched.img and in B qnx12_utils.img
    It works in an IBM PC/XT (1982) with 256 KiB RAM. If adding a HD, use the [ISA] [MFM] IBM Fixed Disk Adapter and the classic 10 MB HD with standard 306 Cylinders, 4 Heads and 17 Sectors geometry. Remember that I’m using 86Box v2.07 build 2008.

    QNX 2.15C is found here:

    You want either qnx2-1-dist.tar.gz or, both seems to contain the same files.

    You can use boot.img in A drive and boot_util.img in B drive. Same logic than with QNX 1.2.
    This one requires an IBM PC/XT (1986) with 512 KiB RAM. In the 1982 XT that caps out at 256 KiB, it stops somewhere after loading the Boot Loader. QNX 1.2 also works with the newer machine in case you don’t want to switch between them.

    First, is basic movement between drives and directories. Commands are found in the /cmds directory, and each diskette has its own set of commands available, which may be reelevant at a later point (See much below about QNX 1.2 vs 2.15C differences).

    cd: I suppose that anyone here knows how to use cd, so no need to explain that one. Note that there is no analogous to cd .. that I found to return to the previous upper directory, I had to use cd / to get to the root then start again navigating down from it. You can also do cd directly to a directory, like cd /cmds or whatever else.

    ls: Standard file and directory display is the ls command. For MS-DOS users, ls is like a dir -w. Things that have a + previous to them are directories. Note that I didn’t found out how to display file size.

    zap: Deletes files and directories. A very powerful rm.

    ed: Text editor like vi. Note that since I come from a MS-DOS background and I’m used to its EDIT, or Linux nano, I simply can’t get used to ed interface. May have to bother to read the manual for this one…
    I just used ed to read files but not to actually edit them. You can quit with Ctrl + Z.

    cp: Your copy command. You know what it does.

    backup: An enchanced copy command for full disk copies. I didn’t really bothered to compare what cp and backup can do, if in doubt just use cp with full routes. There are not many files so you may actually prefer to copy or delete on an one-by-one basis, which is what I do as I’m not good using wildcards.

    The first major difference is on how drives are called. Whereas MS-DOS could call the first two diskette drives and the HD as A, B and C, here you have 1, 2 and 3. Thus, if you have two floppies and want to get to the other drive (Hard Disk will have to wait until you learn to mount it), you do cd 2:/
    Note that using cd / moves you to the root, but not of the drive that you’re currently in but the one that QNX takes as main. Thus if you do cd 2:/ then cd /, you will are actually returning to cd 1:/. If in doubt, always be specific.

    The diskses have a subset of commands on them, and QNX 1.2 and 2.15C boot and util diskses have a different arrangement, so commands that may work out-of-the-box in one may not in the other, and this applies even to rather basic commands!
    For example, ls and ed are NOT found on QNX 2.15C boot disk. By default, QNX 2.15C just checks for commands in the main disk. QNX 1.2 does actually looks for contents in the /cmds directory of the second disk, so ed, which sits in the util disks in the secondary drive is actually invoked properly, but QNX 2.15C does not do that.

    QNX 2.15C from the 360 KiB floppy diskses pretty much requires to remotely execute commands. Whereas an ls or ed in QNX 1.2 would suffice, in QNX 2.15C to use ls you have either to be at the command folder ( cd 2:/cmds ) and use ls to see the contents of other directories ( ls 1:/ or ls 1:/cmds you get the point), or remotely execute it ( 2:/cmds/ls ) to do an ls in the current directory.
    Or you can do what I just learned: Use the search command to tell it to look for commands in the /cmds directories of both diskettes! This may simplify commands I typed in later steps that had the full route.

    search 1 2

    Now we get at the point where you know enough to manually mount the Hard Disk. Note that instructions are different in both QNX 1.2 and 2.15C not only due to the previous command searching difference, but because the HDC/Disk Driver is also in different locations.
    For example, a Driver you would need for the PC/XT stock HDC in QNX 1.2 is the /config/xt file (It is available on both diskettes), whereas in QNX 2.15C it is in the utils diskette, 2:/drivers/disk.xt

    Adittionally, QNX 2.15C can execute the 2:/cmds/install command which is an automatic installation that also adds the Boot Loader and is what I used previously to install it to a booteable HD, but you may want to manually use a few commands before going this way to get confortable with QNX. I however found that install had a problematic step, since it seems to want to copy stuff only from the floppy 1 drive. You may as well boot with only the boot disk inserted, replace it with boot_utils on the same drive, then execute install and manually replace disks again when it ask to do so during installation to copy their contents to the HD.

    Accounting for the QNX versions differences, manually beginning the mount process involves loading first the HDC Driver. It would look like this:

    QNX 1.2: mount disk 3 /config/xt
    QNX 2.15C: mount disk 3 /drivers/disk.xt

    Check with mount command and you will have a new Drive 3: Hard, 10404K, which previously wasn’t present. QNX 2.15C will also display partition=non-Qnx. This applies EVEN if the HD is partitioned, since you mounted the HD Driver but still have to mount the partition.
    At this point, if the HD image is blank, you have to partition it with QNX fdisk:

    QNX 1.2: fdisk 3
    QNX 2.15C: 2:/cmds/fdisk 3

    I suggest you to use the QNX 2.2 Reference Guide cause at this point you should be able to follow the mounting instructions at the beginning. Basically, QNX partition type is 7, and if using the standard 10 MB HD, the partition starts at Cylinder 1 (0 may work but the User Reference uses 1, I don’t know if 0 overlaps with the standard DOS MBR) and ends at Cylinder 305. fdisk will compute the other two values automatically. You may also mark the partition as Bootable (Use Left and Right Arrow Keys, see that in the upper bar you have selected Boot and Enter. It will show a * on the Boot column), but note that it is useless without a VBR Boot Loader.
    Note that fdisk in 1.2 and 2.15C gives opposite results: In QNX 1.2, I see the qnx partition as last in the fourth place, whereas in 2.15C it appears as the first partition. Not sure why.

    After partitioning, you have to mount the actual partition from the HD instead of the full HD:

    mount disk 3 d=3 pa=qnx

    You can afterwards use mount and notice that it added an offset value to the Drive 3 HD. I didn’t checked whenever if having a Dual Boot with a QNX and DOS Partitions QNX can also boot the DOS one, and whenever you can map it to another disk or something (Sort of disk 4 d=3 pa=dos or whatever).

    If the HD was already formatted with a File System, you can do cd 3:/ and use ls to see its contents. Otherwise, you have to create a File System on the QNX Partition:

    QNX 1.2: dinit 3 +hard
    QNX 2.15C: 2:/dinit 3 +hard

    At this point the HD should be ready to use, albeit you have to manually mount it (First HD, then Partition) on every reboot.
    In the case of QNX 2.15c, you can also manually install the VBR Boot Loader using the boot command, but it always errored out saying it doesn’t support floppies just hard diskses. I think I had to copy the Kernel image and HD Driver to the HD then execute boot from there. Since I didn’t managed to get it working using boot command, I instead use the install command and end up with a booteable HD just following its rather easy instructions.

    The QNX User Reference also mentions a lot the backup command to make full copies of a floppy to the HD. It works like this:

    backup 1:/ 3:/ +all s=c

    Replace 1:/ with 2:/ when appropiate.

    By this point you should have enough experience to move around either QNX version confortably, or try to use install to get a QNX 2 booteable HD by yourself.

    About the contest, the only other thing I managed to do is to use zap to blow up the supposedly untouchable /config/hdisk.cfg (HD Driver) and /netboot/os.2.15Cpcat (Kernel Image). The HD image keeps booting after blasting them. This seems to be cause the Boot Loader directly addresses these files physical location instead of looking at the File System table, so it will keep booting until their locations are overwritten or zero-filled. The User Reference does mention that you can overwrite them but not delete them and replace from scratch, so that they maintain their physical location. The purpose of the PC DOS SYS command should be conceptually identical to this.

    Also, the User Reference claims that the advantage than the QNX Boot Loader (Which sits in the MBR) has over DOS one is that it pauses a few seconds to give you a boot menu in case there are other partitions, whereas DOS always boots the active Partition without asking anything unless you have a Boot Manager in the VBR. The QNX Boot Loader is not however the VBR one that would be required to boot from the HD QNX Partition cause it is MBR.

    So I suppose that at this point, what someone has to do is figure out how to load the HD Driver then the QNX 1.2 Kernel (Or viceversa?), which should live at fixed locations.
    QNX 1.2 seems to have the Kernel image as something out-of-File System, perhaps fully contained in the VBR, but I don’t have the knowledge to validate that claim. Requires some work beyond what I did, so I think that I’m stopping here.

    What a shame, I already even decided what I was going to buy with those tasty dollars then noticed that I epic failed the contest. Made sense, there was no way I was going to beat any of the reverse engineering gurus on the first place, heh.

  5. Obsessive compulsive disorder strikes back. Can someone claim the prize already so I can move on with something else? PLEASEEEEEEEE

    Of interest for reverse engineering, here is a small rundown of the QNX File System, at Page QNX 23:

    And, perhaps the most interesing find, is THIS file, hidding in plain view on the repository:
    This file mentions three commands which I failed to find in all the diskses I checked.

    First is HBOOT. HBOOT was intended to install a QNX Boot Loader in the HD MBR (Which is done by BOOT command), with the twist that it could move the copy protection from the diskette to the HD and viceversa as a “fingerprint” of sorts, so the same license exist in just one place at once.
    Second is SYS. SYS installs the VBR Boot Loader, which is what we want. However, it seems that its functionality was fused into BOOT.
    The most interesing one is READOS. It specifically says that non-networked versions of QNX do not have the OS Kernel available as a file, so this command creates such file from a diskette drive source. And it uses as example os.1.25pc !!! So basically, the hunt is on for this specific utility. The rest can be accoplished by the standard BOOT command.

    I also found the TXT in the QNX FTP dump available in the repository, at\usr\free2\technotes\hard_disk_boot, but I failed to find other mentions of HBOOT/SYS/READOS yet.

    BTW, I posted like 12 hours ago a reply with instructions about how to mount in both QNX 1.2 and 2.15C, was it filtered out?

    • Im not sure why you trigger ansimet, but all your comments have. I have been downsizing offices and running around so I can’t check as often as I’d like, and with the time difference I might have been asleep.

      Also the blogs emails are being tagged as spam by all the big companies so I tried setting it up to relay through my Exchange server then off to Microsoft Office 365…

      Hopefully this all helps.

  6. This is the largest virtual machine enthusiast organization in China. I hope overseas colleagues can join in. Welcome to your coming! 点击链ζŽ₯加ε…₯ηΎ€θŠγ€ι•œθ”γ€‘οΌš

  7. The next goal was to figure out how to dump raw blocks into files, as I needed a way to recover the non-file QNX 1.2 Kernel image and make a file out of it. Ironically, I later found out that the guy that won the previous contest, was there and done that back in October:

    I tried to find a way to locate the Kernel image with QNX available tools but failed. ddump, which shows a drive raw block contents in hexadecimal format, properly displays the contents of the blocks, but I didn’t figure out how to save them.

    QNX has a *nix pipe system implemented, so I could use > to write command output to a file. Using dump, which shows a file contents in hexadecimal format (Different from ddump), works with the pipe system. For example, I picked a text file, and used the > pipe:

    dump /config/sys.init > /test

    This works. You can see the test file contents using ed or p (Prints file contents, like MS-DOS type command). It is however stupid to use dump this way cause it was just a straight file copy, but it served for experimentation purposes, heh. The one that I was interesed in, ddump, which is for raw blocks, does not work with piping. Doing this:

    ddump 1 0 > /test

    …silently fails. 86Box does NOT report HD activity, albeit it does every time I press Enter. You can abort with Ctrl + Z, and it creates the test file but it is either empty or can’t be readed (The p command reports “file busy”. So, you can’t see its contents with p, or edit it with ed, nor delete it with rm, but zap works).

    At this point, I figured out that it was easier to use an Hex Editor, HxD, from outside 86Box to directly edit the disk images.

    The first task was to figure out how to locate something that seems like a Kernel. The closest thing I had as reference were os.2.15Catp and os.2.15Cpcat, so I used dump on them to see their contents and take note of the beginning and ending patterns. Doing this from start to finish was SLOW, in the order of 30-45 mins, so I actually launched another emulator instance to do it on each file simultaneously. I also used the size command, which shows how much characters (Bytes) they measure.

    os.2.15Cpcat Size: 109632 Bytes
    70 00 01 00 00 40 50 START SEQUENCE
    E9 67 01 00 C8 90 01 00 55 B8 01 80 51 01 9D 0A 9D 01 END SEQUENCE

    os.2.15Catp Size: 112353 Bytes
    70 00 01 00 00 40 DE START SEQUENCE
    E9 67 01 00 C8 90 01 00 55 B8 01 80 51 01 9D 0A 9D 01 END SEQUENCE

    I manage to easily locate with HxD the hex patterns in the HD image with QNX 2 installed. They perfectly match, and the files themselves are entirely continuous on the HD image, so they end where you expect them to, at the end of their exact Byte file size. Even for a newbie like me this was very easy, because it instantly makes sense.

    Looking for them in the floppy image was not straightforward. While there are two start patterns, one at 11E10h for os.2.15Cpcat and another at 33E10h for os.2.15Catp, as expected, there are three end patterns. Besides, os.2.15Catp starts even before os.2.15Cpcat ends, as if the first file was truncated, so at first it greatly confused me.
    It took me a while to figure out after a manual comparison (I isolated the two HD image files then scrolled down matching lines) that it may be due to fragmentation and than that should be handled at the File System level, so the two files are not entirely continuous, which may make raw block copy an incorrect solution. The other end pattern may actually be data remants from a raw development image, the so-called slack space, similar to the PC-DOS 0.90 case:
    This means that manually making a Kernel file from raw blocks may require knowledge of the QNX File System and how to read the bitmap file to know how to build a proper contiguous piece. Ouch.

    Inmediately after, I looked for the Kernel start pattern in the QNX 1.2 floppies. It is found only on the booteable ones, namely qnx12_boot_patched.img and qnx12_demo.img, so it confirms that 70 00 01 00 00 40 is the correct start sequence for the beginning of the Kernel image. qnx12_boot_patched.img starts at 41400h, and in qnx12_demo.img is at 48000h. The question is… where it ends? It is fragmented? I can’t fix those myself, but I can experiment via brute force

    The easiest way to brute force this was to use HxD to do a raw copy of the hex data from the floppy Kernel to its final location in the HD image. At first glance, the Kernel image may be from 41400h to 57BF0h, which is where you star to have zeros/padding. That is a total of 92144 Bytes. I can raw copy this to overwrite the current os.2.15Cpcat Kernel image, which is bigger thus shouldn’t require File System level modifications (The QNX 2.2 User Reference said that you can overwrite it without configuring boot again. However, I installed the VBR Boot Loader with the QNX 2.15C /driver/disk.xt driver, not 1.2 /config/xt one!), and see what happens…

    The QNX MBR Boot Loader works, as usual. When it tries to boot from Partition 1, there is a spinning arrow, then it clears the screen and I get a blinking cursor in the top left corner. Which is actually better than some other non-recorded experiments when I was getting used to do this raw copy, since in another attempt (Doing the same but with qnx12_demo.img Kernel) I wasn’t able to get beyond the spinning arrow (It stops when it hangs), so I think it progresses further.
    Actually, I noticed that AFTER getting to the blinking cursor, it attempts to read from both floppy drives. I recalled that the last contest winner Forty mentioned that he could somewhat boot the Kernel from the HD but it then tries to transfer control to the floppy, and finishes loading from there. This works in mine, too, if I insert the qnx12_boot_patched.img right after the QNX MBR Boot Loader (So that BIOS boots from HD and not floppy). So I’m basically at the same point he was back in October…

    What I did notice, is that the qnx12_boot.img, which is the non-patched copy protected image, WORKS if inserted the same way. If instead I try to boot from it directly from floppy, it goes with the blinking cursor for a while then the emulated IBM PC/XT reboots. Not sure what it is going on here…

    I suppose than that floppy imag eactually working may be related to the tip that Don said that the copy protection could be transfered to the HD, so maybe that is why it works if there is a valid license there… Or not? I suppose that the next step is reading old comments about how the copy protection was dealed with in the previous contest and see if I can get somewhere with a dissambler like IDA.

  8. HI,
    Reading this blog is very heart warming to me. I’ve used every version of QNX since 0.47. My original serial number was 1040. I have a website at I wrote the LBA version of the IDE driver, which I still give out. The last QNX 2 project I did was maybe 7 years ago. QNX 2 is still dear to my heart. I’m afraid that my memory about some very early details, like how I got my first hard drive, a Davong 5 Meg drive to boot off the hard disk are probably too vague to remember, but if there are questions about programming in QNX 1.x or 2 I will be happy to try and answer them. My email is maschoen AT gmail DOT com.

  9. These are pretty much my hexadecimal adventures notes.


    qnx12_boot.img vs qnx12_boot_patched.img
    Almost identical, except:
    4A5A9h Byte 74 (JZ instruction) changed to EB (Unconditional JMP)

    Copy protection expected to be in Track 39 Side 1 (58E00h – 5A000h / 4608 Bytes)

    qnx12_demo.img Kernel starts @ 48000h
    Possible endings (Assuming continuous Kernel image):
    58200h 1020Eh / 66062 Bytes (Last useful Byte)
    58BF0h 10C00h / 68608 Bytes (Padding with zeros)
    59FFFh 12000h / 73728 Bytes (To end of disk)

    qnx12_boot.img AND qnx12_boot_patched.img Kernel starts @ 41400h
    Possible endings (Assuming continuous Kernel image):
    57BD4h 167D5h / 92117 Bytes (Last useful Byte)
    57BFFh 16800h / 92160 Bytes (Padding with zeros)
    59FFFh 18C00h / 101376 Bytes (To end of disk, including copy protection Track)

    HD LOCATIONS (Only reelevant to my images)


    A minimal bootable QNX 2.15C with only the HD Driver, the Real Mode os.2.15Cpcat Kernel, and sh and login from QNX 1.2. Is intended to be as untainted as possible. The other HD image is based on this one after hex editing.
    Note that the QNX 2.2 boot command refuses to install the QNX 1.2 /config/xt Driver as incompatible or not correct version, don’t remember the exact error. Not sure if I had copied that file to the HD then deleted it or not.

    os.2.15Cpcat Kernel starts from 9A10h to 2464Fh 1AC3Fh / 109631 Bytes

    Copy protection ignore 74 / EB Byte at 12BB9h


    This one has currently qnx12_boot_patched.img Kernel slapped to it (From 41400h to end of disk).

    With qnx12_demo.img Kernel
    Possible endings:
    9A10h + 1020Eh / 66062 Bytes = 19C1Eh
    9A10h + 10C00h / 68608 Bytes = 1A610h
    9A10h + 12000h / 73728 Bytes = 1BA10h

    All them hangs after QNX MBR Boot Loader with the spinning arrow pointing down. No floppy activity.

    With qnx12_boot.img OR qnx12_boot_patched.img Kernel
    Possible endings:
    9A10h + 167D5h / 92117 Bytes = 201E5h
    9A10h + 16800h / 92160 Bytes = 20210h
    9A10h + 18C00h / 101376 Bytes = 22610h

    With 74 @ 12BB9h on the HD image, they pass the QNX MBR Boot Loader spinning arrow, then the screen clears with a blinking cursor on the top left corner for a second or so. Afterwards, the the computer resets. They also reset even if inserting qnx12_boot.img or qnx12_boot_patched.img after the MBR Boot Loader.
    With EB @ 12BB9h, it remains with the blinking cursor without resetting. Inserting qnx12_boot.img right at the QNX MBR Boot Loader (So that the BIOS doesn’t boot from floppy) does pass that point. However, it transfer control from the HD to the floppy and finishes loading from there, ignoring the HD afterwards. This is what Forty found out in October. Yet still, it seems that at least the HD Driver and a part of the QNX 1.2 Kernel works from HD.

    Worthy of mention, crazyc say that his image (Which is identical to qnx12_boot.img after using libdsk dskconv to convert from IMD to IMG) works… on MAME. I didn’t manage to get it working on 86Box, it resets. And MAME is an emulator that strives for accuracy, so… Maybe is worth to try also on another emulator.

    Still didn’t tested IDA, but I’m not exactly interesed either cause due to my knowledge of reverse engineering and x86 ASM chances are it gets me nowhere. Nor I had success with the testing the PCjs debugger… Someone else will have to try that. Actual reverse engineering is pretty much an unbreakable wall for me.

  10. From Peterv who was one of the early QNX developers.

    Wow, that was over 30 years ago. Did you or Gord so the copy protection? My memory is foggy but I can try:
    – when faster machines came in some drivers stopped working because they used while loops for delays and the counter was 16-bit and wrapped in early versions of QNX. Later it was maxed, but the CPU could still be to fast. I seem to remember this problem with the floppy driver, but that might have been just QNX4.
    – To install on a hard drive, it would read the copy protected license from the floppy, undo the copy protection, then write it to the hard disk. You could reverse that if you wanted to use it with a different machine. A backup license floppy was provided incase something crashed during this process.
    – On the floppy it used soft sectors, so an invalid sector number higher than on any cylinder was written to the real sector for the license to be stored.
    – On the hard disk it stored the license on a cylinder after the reported last cylinder. The old hard drives that used CHS addressing this worked as they always had a spare cylinder, but when the new integrated hard drives came out they translated CHS into LBA and didn’t allow this any more.

  11. More from Peterv. This is all based upon a foggy memory from a long time ago!

    – I think the OS image had to be in the first extent as the OS boot loader didn’t know how to handle the QNX2 linked extents (which was stored in the data). I seem to remember a letter showing up where the spinning arrow was during boot if it wasn’t continuous.

    – Also the boot loader was patched when the OS was installed. With the BIOS drive letter as well as the location of the OS. In QNX4 it used β€œ.boot”, but not QNX2.

  12. I’m still trying…

    I tried to migrate from 86Box to BOSCH, because it is held on high regard for its debugging capabilities and thought my chances to be able t odebug were higher there. It didn’t go well.
    While QNX 2 boots from floppy and seems to work properly, installing it to the HD and trying to boot from there, or trying to boot from the QNX 1.2 patched floppy image, makes BOSCH to simply unexpectely close as soon as I launch the simulation. After trying a multitude of different options, I found no way to get it working.
    Besides, BOSCH isn’t exactly a cycle accurate emulator or anything like that, and support for IBM PC/XT era Devices is pretty much null (I think just the PC/XT Keyboard is supported). For reference, I have been using the /drivers/disk.bios as HD Driver since I’m not expecting support for any early XT/AT HDCs. Booting with QNX 2 and using that HD Driver, I can get the HD detected and initialized, and I can copy files to it and use the boot command, but I simply didn’t managed to get booting working due to what I previously stated.
    I thought about trying to get a PC DOS 2.00 install working first, then install QNX 2 on another partition relying on the PC DOS MBR Boot Loader instead of the QNX just to discard it a possible cause, but I desisted on the BOSCH route cause I found MAME more viable.

    Next step was MAME. After fiddling looking for the ibm5160 ROMs and figuring out the command line parameters, I got working crazyc QNX 1.2 IMD image, after reading his comment regarding disabling the default Serial Mouse (I simply disabled the COM Port at ISA2 Slot). Seems that IMD format has a bit more metadata than the raw IMG files I have been working with, due to the higher-than-physically-possible Sector number trick. Command line is this:

    mame64 ibm5160 -isa2 “” -flop1 boot.img -flop2 boot_util.img -hard1 MAME_HD.chs

    Worthy of note is that HD images aren’t directly interchangeable between 86Box and MAME without converting them with chdman first:

    chdman createhd –input 86BOX_HD.img –output MAME_HD.chs –chs 306,4,17 –compression none

    chdman extracthd –input MAME_HD.chs –output 86BOX_HD.img

    Giving the last Dan comment about the copy protection being written a Cylinder above the max, I tried doing so in the 86Box HD image. I did some LBA-to-CHS math to try to add a license copy on every 512 Byte sector belonging to the 307 Cylinder. It looks like this:


    307C 1H 1S to 307C 4H 17S

    LBA 20893 ((307 * 4) + 1) * 17 + (1 – 1)
    LBA 20909 ((307 * 4) + 1) * 17 + (17 – 1)

    LBA 20910 ((307 * 4) + 2) * 17 + (1 – 1)
    LBA 20926 ((307 * 4) + 2) * 17 + (17 – 1)

    LBA 20927 ((307 * 4) + 3) * 17 + (1 – 1)
    LBA 20943 ((307 * 4) + 3) * 17 + (17 – 1)

    LBA 20944 ((307 * 4) + 4) * 17 + (1 – 1)
    LBA 20960 ((307 * 4) + 4) * 17 + (17 – 1)

    Sadly, I’m not 100% sure whenever it is correct cause I need a way to verify that data is inside the HD image from emulator side where it looks from me with the Hex tools.

    The thing is, since this is very emulator sensitive, I don’t know if 86Box actually tries to retrieve data beyond the defined CHS. Maybe manually adding like 80 512 Bytes sectors each with a copy of the diskette copy protection track didn’t worked precisely because 86Box ignores them.
    I have more hope than MAME is capable of making the above-highest-Cylinder possible than 86Box, giving than MAME goes for as close accuracy with real Hardware as possible thus it should be able to deal with that. However, I need a way to verify if I can actually read/write in MAME with a HD defined as 306 Cylinders something in the 307 one. I thought about using spatch to attempt to write then check how it appears on the MAME CHS image (Which isn’t flat thus harder to directly edit than 86Box raw ones, since I may need to add uncompressed data that I can track its position, then replace it with an Hex editor with the copy protection). This means that I will have to go deep in LBA math.

    And I also don’t know whenever the copy protection track is copied straight from its 512 Bytes diskette sector to the HD one or it is at a different offset. I also have to experiment with that…

  13. I wanted to test a bit more 86Box.

    I did a copy of the HD image to try to mount it as a second HD. It works like this:

    mount disk 4 /drivers/disk.xt p=2

    Not adding p=2 will default to first HD, so it will load the same image and you may be unaware that you’re overwritting contents. It will look different cause the Offset matters when using the spatch command. When mounted directly without a partition (pa=qnx), spatch matches the location of the HD image file since it is at Offset 0.

    Locations HD raw IMG with HxD (Decimal) vs ddump in 86Box (Hex) vs CHS-to-LBA vs spatch

    0h / Sector 0 Block 1 0,0,1 spatch disk 4 0 MBR
    2000h / Sector 16 Block 11 0,0,17
    2200h / Sector 17 Block 12 0,1,1
    4200h / Sector 33 Block 21 0,1,17
    4400h / Sector 34 Block 22 0,2,1
    6400h / Sector 50 Block 32 0,2,17
    6600h / Sector 51 Block 33 0,3,1
    8600h / Sector 67 Block 44 0,3,17 spatch disk 4 43
    8800h / Sector 68 Block 45 1,0,1 spatch disk 4 44 VBR

    A20600h / Sector 20739 Block 5103 304,3,17 spatch disk 4 5102
    A20800h / Sector 20740 Block 5104 305,0,0 spatch disk 4 5103
    A28C00h / Sector 20806 Block 5147 305,3,16 spatch disk 4 5146
    A28E00h / Sector 20807 Block 5148 305,3,17 spatch disk 4 5147
    A29000h / Sector 20808 Block 5149 306,0,1 spatch disk 4 5148
    A31600h / Sector 20875 Block 518B 306,3,17 spatch disk 4 518A

    If letting ddump run automatically, Block 5149 shows a repeat of Block 5148, and so on. Using ddump 4 5149 insteads reads a default sector somewhere. Same with spatch.

    spatch has an Addr functions that display in either Block, LBA (That matches HxD!) and some form of Segment:Offset.

    I also noticed than my geometry math was a bit off cause it is 0-3 Heads with 4 Total Heads and not 1-4 Heads. Cylinders are also 1 less than the Total Cylinders when using the geometry conversion formula. That is why I never managed to translate why LBAs always appear to start at 17. So I was a few LBAs wrong the entire time…

    Now the question is whenever it is possible to play with the CHS geometry values than the emulator sees and whenever QNX can access more. 86Box sees it as a CHS 306,4,17 image. However, I manually added to the HD image a full 512 Bytes sector and an incomplete 16 Bytes following it.

    mount disk 4 /drivers/disk.xt p=2 t=306 h=4 n=17 (Default)
    spatch disk 4 5147 (305,3,17) reads well
    spatch disk 4 5148 (306,0,1) reads garbage
    mount disk 4 /drivers/disk.xt p=2 t=305 h=4 n=17 (1 less)
    spatch disk 4 5104 (305,0,0) reads well
    spatch disk 4 5105 (305,0,1) reads garbage
    mount disk 4 /drivers/disk.xt p=2 t=307 h=4 n=17 (1 more)
    spatch disk 4 5148 (306,0,1) reads well
    spatch disk 4 5149 (306,0,2) reads the first 16 Bytes then garbage if invoking spatch directly, if using 5148 then letting the next sector load, it just repeats.

    The 2000 U$D question is, does the boot command saves CHS data? Cause it SEEMS that if removing a Cylinder I can get 86Box to see a default 306,4,17 sized image but get QNX 1.2 to finish at 305,0,1, which is that famous extra Cylinder. I’m going to try that…

  14. A lot of this old stuff is still rattling around in my head from decades past.
    Yes, p=X means this is the 2nd device. xt and at (ide, eide) hardware supported 2 devices 1 and 2, 1 being the default. This parameter was used for scsi 0-7. There is only room in memory for one copy of the code, so a driver has to be able to handle requests to either physical device. If one had two hard drives the procedure was as follows:

    $ mount disk 3 /drivers/disk.xt pa=qnx
    $ mount disk 4 d=3 p=2 pa=qnx

    The d= parameter refers to the “disk 3” in the first mount. The pa= is optional. pa= could also refer to a partition number, as in pa=7.

    It was possible to mount two different drivers if you had two types of hardare, eg.

    $ mount disk 3 /drivers/disk.xt
    $ mount disk 4 /drivers/disk.corvus

    As far as the CHS numbers, be aware the the boot code look at a BIOS entry that was loaded into memeory by the BIOS from the CMOS. The CHS numbers in CMOS had to match the CHS of the disk, especially when the driver is first mounted and patched. At times we used the c= h= n= parameters when mounting to force the right values. This became a bit trickier when LBA drives appeared. LBA drives have default CHS numbers as well as being setable. I remember slaving over trying to get LBA drives to boot off the hard drive. Later when I wrote the LBA eide driver I still had to worry about this.

    • Thanks for the tips. Do note than everything I did until now has been based on the IBM PC/XT 5160, which does NOT have RTC SRAM for backuping settings like the HD Type like the PC/AT does.
      Actually, it may be a good idea to reproduce everything emulating a PC/AT just to see whenever I get different results.

  15. Dang! Yeah I forgot about that. Every time I started DOS I had to enter the date/time. I guess my comments about the BIOS entry are relevant only after the IBM AT appeared. Later versions of QNX have a program called boot, but for the IBM PC there were special procedures, maybe hboot?

  16. I just started looking into this, kind of disappointed of missing out πŸ™‚

    A few comments:
    – last working 86box build is 2639 (and 2640 change is clearly related, the change log reads: Implemented PIC IRQ latch and delay (per the datasheet), IBM PCjr now works without a workaround delay in cpu/808x.c which was therefore removed; also redid memory and I/O accesses in cpu/808x.c to fix word writes on 8086)

    – As I understand, the kernel is using its own hardware access drivers and has the root device “hardcoded” in it. So, not only would one need to find a way to load the kernel from the HDD but it would also need to include the relevant hardware driver (“config/xt”)

    Side-note: I started examining the (floppy) loader and it seems to me (the loader is somewhat convoluted) that it tries to read from track 29 up to track 40 (41st!), both heads, except CHS 41,1,9. I don’t know what 86Box answers to that πŸ™‚

    I have not yet looked at the kernel.

  17. It took me a while to get IBM PC/AT 5170 (ibm5170) working on MAME. Here is a rundown of the procedure…
    You need MAME, obviously, and a few ROMs that you have to get elsewhere. If you are experimenting, adding different expansion cards may ask for different ROMs (Quite a few I found in repositories).
    You also need the Diagnostics for the IBM Personal Computer AT floppy disk, since the PC/AT does NOT have a traditional BIOS Setup builtin in the Motherboard Firmware ROM but instead modifies the values stored in the RTC non volatile SRAM from that floppy, which effectively serves the role of a floppy based BIOS Setup. The file you need that matches the CRC32 expected by MAME is the RAW disk IMG found here:

    I had issues with MAME because setting it the first time isn’t very straightforward. For one, the standard Tab menu UI when inside MAME apparently comes disabled by default when emulating certain things. You first have to press the Scroll Lock Key, which then enables using Tab. This is required since otherwise you will not be able to change floppies on the fly. Do note that while you’re in Tab enabled mode, there are certain keys that seems to overlap, since p seems to both press the P Key AND pause the emulator.
    The command line to actually start MAME was also overly complicated, until I found this site, which can generate it:

    Do note that MAME PC/AT emulation is far better in some things than 86Box, yet much worse in others. For example, the default HDC for the ibm5170 MAME machine is called ide, and is supposed to provide an IDE interface, whereas the original IBM PC/AT used a special FDC + MFM HDC combo card that 86Box seems to emulate but MAME does not. This means that configuring the PC/AT BIOS HD Type serves nothing cause the PC/AT BIOS can’t drive the other HDC cards, but you still have to configure it with the BIOS Setup floppy since otherwise the BIOS complains at POST of wrong or missing Hardware. Ironically, using the BIOS Setup floppy in 86Box for Setup causes a fatal “System Board Error”, whereas it passes properly on MAME. On 86Box, PC/AT BIOS configuration hass to be done from within IBM Cassette BASIC, at least I found no other reliable way:

    The problem with MAME is that it is completely sucking on the HDC emulation front. Without the proper HDC, you are left with the PC/XT hdc, which is the only one I managed to get working. While MAME emulates a multitude of SCSI Controllers, I have NOT managed to get even a single one of those I tested actually working, whereas I did manage to get to the HD MBR Boot Loader with similar SCSI Controllers (Or I think the same, as both support the Adaptec AHA1542B) in 86Box (Note that in the VBR boot step I get Missing Operating System, for sure because SCSI is either nor supported or I didn’t used the proper QNX HD Driver). Typically, when you install a SCSI Controller with a booteable Option ROM you get an extra screen after POST that shows that it has loaded something, whereas in MAME I never got anything extra. So I suppose that it is not properly loading the Option ROMs and that is why no SCSI Controller appears to work.
    Amusingly, MAME ibm5160 machine seems to emulate the famous XTIDE (86Box also does), whereas ibm5170 doesn’t offer such option even when the real Hardware appears to work in it. I wanted to test it cause it had configurable CHS geometry, which could give me another tool against the copy protection. So far, at least on MAME, if using a CHS HD image with 307 cylinders, the last cylinder can be properly accessed if remounting it with an extra cylinder, which seems to be what the copy protection did according to the tips from the QNX developer.

    I also had issues with the Keyboard emulation. The standard pcat Keyboard seems to have wrong mappings or something, cause it writes near garbage on screen. The pcat101, which is the IBM Model M, also does. I got it working with the ms_naturl one.

    So, here it is a working command line parameter:

    IBM PC/AT with two 5.25” DD FDDs and XT MFM HDC

    mame64 -w ibm5170 -bios rev1 -ramsize 640K -isa1 vga -isa2 fdc -isa2:fdc:fdc:0 525dd -isa2:fdc:fdc:1 525dd -isa3 hdc -isa4 “” -kbd ms_naturl

    The very first boot, activate the MAME ui by pressing Scroll Lock, then Tab. Go to File Manager, and add ATDG207.IMG to flop1, then Reset the machine and allow it to boot from the floppy. Afterwards, use common sense to achieve the following configuration:

    4 – SETUP
    Diskette Drive A – Double Sided (360 KB)
    Diskette Drive B – Double Sided (360 KB)
    Fixed Disk Drive C – Not Installed
    Fixed Disk Drive D – Not Installed
    Base Memory Size – 640 KB
    Expansion Memory Size – 0 KB
    Primary display is: – Color Display (80 columns)

    Reset again, it should not give anymore POST errors unless you change the Hardware. Afterwards, you can use QNX2 boot.img and boot_utils.img floppies with and a blank HD image to install QNX2 as usual (No progress on QNX 1.2 HD boot challenge, sorry). Note than due to the fact that I’m using the hdc card, it uses the /drivers/disk.xt Driver. disk.bios does NOT work cause it seems to rely on the PC/AT BIOS HD Type values. I’m still trying to figure out the relationship with SCSI HDs, cause as far that I know, SCSI Controllers with Option ROMs usually hook INT 13h and should be usable via BIOS Interrupts, but I don’t know how these deals with the HD Type. Since QNX 2 disk.bios HD Driver can be manually configured with CHS geometry, I suppose that on a working SCSI setup, it had to be mounted with explicit values (That I was intending to do on 86Box after figuring out I can’t get SCSI Controllers working on MAME. SCSI and XTIDE should be the last things I have left to try).

    Do note that there seems to be an emulation error affecting both 86Box and MAME if using the IBM PC/AT or anything above it for QNX 2.2, as all them include a RTC. If you do a full QNX 2.2 install (boot.img, boot_utils.img, util1.img and util2.img) to HD, you also copy the /config/sys.init file. This file by default makes a calls to the rtc command that does NOT always work. When it fail, you get something like this right after QNX logo:

    QNX Version 2.15C/cmds/login 0206 – Terminated at 22e0:1d3f. DIVIDE exception 8000.

    It doesn’t hangs QNX but you get no working shell to input commands.

    This seems a Y2K type issue. Both 86Box and MAME seems to use realtime for the RTC clock, but at least on MAME you can easily change it with the BIOS Setup floppy whereas it is much harder on 86Box. And it is also temporal. Solution is to boot from floppy, edit the offending file, and comment out or delete the IF sequence that calls the rtc command.

    I’m going to delete all my QNX HD images then create them one last time from scratch with proper documenting procedures cause even I easily lost track of what each contains.

  18. I don’t know if this helps much, but QNX 1.2 does not have a single root. The hierarchical name space starting with / only refers to files. Devices start with $. So you have $con0 the main console and $mdm the first serial port.

    The / can be preceed with a number, 1: for the first floppy, 3: usually the first hard drive. This is the equivalent of A: and C: in DOS. This number is optional. If you just refer from the slash, the file system can search multiple disks using a search order which can be set. The default search order is just the single disk number that is booted off of, so if you boot off a floppy, the search order 1 and off of a hard disk at 3, 3.

    I don’t know what pre-QNX 2 versions worked, but they used a program called boot that loaded a previously saved image of the driver code/data segments that was saved in /config. This had to be loaded using the BIOS of course. I suspect that QNX 1.2 did something similar but without a working version it would be hard guess. Dan Dodge or Gorden Bell might be enlisted to answer this quesition. Dan already posted here. Gordon might answer a question at [email protected]

    • Sorry for not being clear.

      What I meant to say was that the kernel somehow gets to the point of mounting a disk and reading 1:/config/sys.init. I assume that the “1:” part might be hardcoded somewhere in the kernel.

      However, the main problem that I see is that mounting a harddisk requires a separate “driver” (config/xt) that does not seem to be included in the (floppy) kernel.

      As to booting, at least for the floppy image, the loader simply loads into memory everything starting at track 29 (that would be absolute sector 522). The BITMAP (in filesystem) only covers 528 sectors (extent size is 66 bytes, that suggests 528 blocks/sectors).

  19. “What I meant to say was that the kernel somehow gets to the point of mounting a disk and reading 1:/config/sys.init. I assume that the “1:” part might be hardcoded somewhere in the kernel.”

    The 1: part is not hardcoded. If setup properly, the kernel will be loaded from the hard drive 3: and the startup shell script 3:/config/sys.init will be executed.

    “However, the main problem that I see is that mounting a harddisk requires a separate β€œdriver” (config/xt) that does not seem to be included in the (floppy) kernel.”

    You are correct that floppy driver is build into fsys, and the hard drive driver is not. In version 2.0 a special image of the driver, saved in /config by the “boot” command is loaded from the hard drive using the BIOS. I suspect something similar happened with 1.2. In the case of an xt hard drive, the BIOS driver should work, although it would not be interrupt driven.

    The reason that track 29 is being loaded is probably related to where the boot file is on the floppy. It could be moved and then another track would be loaded. It’s not something hard coded into QNX 1.2.

  20. In my experience, /config/sys.init is not even that important, is just an AUTOEXEC.BAT. QNX will complain if it isn’t there but it will boot to an usable state regardless.

    The bare minimum files (At least for QNX 2.2) are:
    /netboot/os.2.15Cpcat (The Kernel image)
    /config/hdisk.cfg (Seems to be an installed version of the HD Driver)
    I usually also have in /drivers/ the Driver that I’m using, but didn’t checked whenever it is actually required for it to be there after installation via the boot command since hdisk.cfg seems to do that job. Do note that hdisk.cfg seems to be customized for that specific Partition, as I have tried the same installation procedure mounting the HD as 305C 4H 17N and another with 306C 4H 17N, both with partitions beginning at Cylinder 1 and ending at either 304 or 305, and the hdisk.cfg started at the same location yet very early on the file, everything was 1 Byte off.
    These two files reside at an specific location in the HD that should be referenced as an absolute address in the Partition VBR. I would believe than the HD Driver gets loaded before the Kernel image. There is also an optional /config/sys.cfg that the boot command may install and also requires to be at a specific location to work, but I never used it.
    Check instructions on the BOOT command here, since most of what I know comes from the manual:

    You also need the following…
    To be able to login and get an usable shell, as otherwise you have no way to type in commands. These seems to be called by the Kernel image itself automatically, as /config/sys.init, but they’re a bit more critical to get an usable system.

    One thing I figured out, is that QNX 2.2 boot command doesn’t allows you to install one of the early QNX 1.2 Drivers located at /config/ , and I have also been unsuccessful in trying to mount in QNX 1.2 an HD using the QNX 2.2 Drivers at /drivers/ . All my hex editing involved reusing the hdisk.cfg from QNX 2.2 to load a copypasted in QNX 1.2 image, so it may be worthless considering that maybe the HD Driver was never compatible with the older Kernel image on the first place!

    The HBOOT command that was mentioned was intended to use old QNX 1.x HD Drivers, it did basically the same than BOOT for QNX 1.x, but also copied the copy protection. However, this tool is nowhere to be found. Same with READOS, which could make a file from an out-of-File System Kernel image. There was a tech doc inside the repo QNX FTP RAR that covered them that should give you enough tips.

  21. You are mostly correct about hdisk.cfg. It is created at installation time by booting off of a floppy, mounting the appropriate driver and then using the boot command, which among other things creates hdisk.cfg as an image of the configured in memory driver. This allows it to be loaded at boot time using the BIOS in place where it can start working.

    As far as I can tell, the attempt here is to boot QNX 1.2 so neither boot nor hdisk.cfg are part of the OS yet.

  22. Not that it helps directly with the given problem but it might be useful: I’m in the process of writing a simple tool to read files from qnx disk images. I’m thinking of adding write support as well, to ease files transfer.
    It’s mostly based on the technical notes mentioned above, as well as a peak in fsys.h

    The program is plain C and should be usable on any Linux machine.

    Side-note: And only after dumping the first file I realized that QNX is separating lines int ext files using ASCII code 0x1e (RS = Record Separator) instead of the more usual 0x0a (LF)

    In case anyone is interested I’ll upload the sources to my site and post a link here.

  23. Ouch, I guess I’m too late to the party. I wrote the read only version of the program you describe, to read QNX 2 files, for QNX 4. The only issue to port to Linux would be in opening the disk and reading it sector by sector.

    There are other possible options. There was an open source archiver on QNX 2. Once added to an archive file, it could be layed directly onto a floppy from QNX 2. One might reverse engineer the “vol” format QNX 2 used, There also was the program that would allow writing to a DOS floppy, although it might not work on QNX 1.2.

  24. I have started to disassemble (and try to understand πŸ˜‰ ) what mount does, but there are a few unknowns.

    First of all, is the executable format of qnx documented anywhere? I managed to find the entry point (and from that, start of main) but I did this by a signature more than by analyzing the header (not to mention that mount is somewhat different from what test executables I built)

    Second: is there any documentation about qnx interrupt services? (ABI… sort of). E.g. I found out (in a rather roundabout way that deserves its own story) that int 70 seems related to task/message passing (send, vc_send_reply, vc_create* etc) and int 72 more to memory/filesystem functions (fopen,fclose,malloc,free…). I do not yet know all of them, and of course I don’t know what the qnx-specific ones *do*, what arguments they take and so on.

    Somewhat unrelated:
    The qnx_access program (read-only version, so far) is here:
    (it seems to work with QNX 2 disk images as well; at least with the one here )
    For HDD image access, -o must be used (see readme)

    • WRT my first question, it seems I have just found the executable file format in the ftp archive usr/free2/technotes/qnx_load

      (sorry for replying to myself, but I don’t think comments are editable)

    • I think I can help out a little. The software interrupts are involved with having the kernel do message passing. You could disassemble send()/receive()/reply() to find out how this works. That’s most of what the kernel does. Access to devices and files is by a separate system task, not the kernel. Functions such as fopen() fread() fwrite() fseek() and fclose() are cover functions that send messages to one of the system tasks. When opening a device, fopen() sees the name starts with ‘$’ so it sends the open message to the device admin, which has a fixed task-id. Likewise, when opening a file, there is no $. It may be a little more complicated as you can open a: instead of 1: and the message makes its way to the dosfsys task, which has to have been started. It doesn’t have a fixed task-id so there must be a little more involved.

      Mount is a utility that interacts with the fsys or dev task, but the details weren’t disclosed as far as I remember. I no doubt sends these tasks messages, but I couldn’t tell you whether mount or fsys reserves memory and loads a driver into it.

      • I see. Thank you for the reply and information, it is certainly helping me understand the OS structure. The way OS calls distinguish devices ($-prefix) will certainly be useful. (Bear in mind that until some days ago I didn’t know anything about QNX).

        Side-note: first thing to do is writing a(nother) small tool, to extract contiguous CODE/DATA segments from executables, now that I have found the specs. The code being fragmentend inside the file and some (un)lucky coincidences have caused me to misidentify some calls. I have only noticed this late last night, when I saw a call that would have landed in the middle of an instruction (although the code around target looked valid). Upon further examination I noticed that (at least for mount) the first fragment of code is precisely 4000 bytes long. By the way, I found it somewhat quaint how this version of QNX seems to use decimal-rounded constants and 1-based indices (default stack size is 1000, now this segment is precisely 4000, interrupt services start with 1, same for disk blocks)

        Of course, I will also have to make sense of the kernel header (apart from the fact that it is 128 bytes long and apparently composed of 32 bytes entries, first word of each entry being the target segment).

        Quite a research project (and now the holiday is over..)

  25. I’m not sure what you are working with. The 1.2 version project will be quite a challenge. If you are interested in QNX 1 or 2, it might make sense to install the last QNX version 2.21 on a VM. From there you have a native compiler and you can look into the details. Somethings of course improved by 2.21 over 1.2 which could cause confusing looking backwards, but IMHO 2.21 would be a good place to work backwards from.

    If you need help getting going with 2.21, I can assist. I have a 2.21 VM running along with a couple of version on old cpus.

    • Again, thank you for your assistance, I’ll ask sooner next time I hit an unknown πŸ™‚

      Mostly (for reason of convenience, i.e. having everything under Linux means using PCem) I am working with the floppy(!) version of QNX 1.2. It seems that the C compiler works pretty well even under these circumstances.
      By the way, one of my first attempts (that might still prove useful later) was to write a program to dump the entire kernel memory, in order to look for differences between kernel image from disk, running kernel, and running kernel with hdd driver installed. Since I don’t know how to dump memory from the emulator (be it 86box or PCem), I had to write a small QNX program to dump memory to file and then retrieve that file. When all you have is a hammer…

      I have not managed to get the HDD version of 1.2 to run under PCem (not for lack of effort; that’s yet another story, I have tried using the bios driver, not only xt but it still doesn’t work) so it’s off to 86box when I want to check something in relation to that one.

      I also have downloaded the QNX 2.21 HDD image from Tenox and got that to run under PCem (so it’s sort of handy) but, as you said, the differences between versions could be confusing. Not to mention its mount is around 15k vs 9K for 1.2. On the other hand, that 2.21 HDD image proved a handy test-case for my filesystem tool.

      • I managed to get working in PCem v17 on Windows 10. Not sure if it may be a problem with the Linux port or what. Config file:

        gameblaster = 0
        gus = 0
        ssi2001 = 0
        voodoo = 0
        model = ibmxt
        cpu_manufacturer = 0
        cpu = 0
        fpu = none
        cpu_use_dynarec = 0
        cpu_waitstates = 0
        gfxcard = cga
        video_speed = -1
        sndcard = none
        cpu_speed = 0
        disc_a = qnx12-hd-boot-floppy.img
        disc_b =
        hdd_controller = mfm_xebec
        mem_size = 512
        cdrom_drive = 0
        cdrom_channel = 2
        cdrom_path =
        zip_channel = -1
        hdc_sectors = 17
        hdc_heads = 4
        hdc_cylinders = 306
        hdc_fn = qnx12-hd-xt-harddisk.img
        hdd_sectors = 0
        hdd_heads = 0
        hdd_cylinders = 0
        hdd_fn =
        hde_sectors = 0
        hde_heads = 0
        hde_cylinders = 0
        hde_fn =
        hdf_sectors = 0
        hdf_heads = 0
        hdf_cylinders = 0
        hdf_fn =
        hdg_sectors = 0
        hdg_heads = 0
        hdg_cylinders = 0
        hdg_fn =
        hdh_sectors = 0
        hdh_heads = 0
        hdh_cylinders = 0
        hdh_fn =
        hdi_sectors = 0
        hdi_heads = 0
        hdi_cylinders = 0
        hdi_fn =
        drive_a_type = 1
        drive_b_type = 1
        bpb_disable = 0
        cd_speed = 24
        cd_model = pcemcd
        joystick_type = 0
        mouse_type = 0
        enable_sync = 0
        netcard =
        lpt1_device = none
        vid_resize = 0
        video_fullscreen_scale = 0
        video_fullscreen_first = 1

        joystick_0_nr = 0
        joystick_1_nr = 0

        screenshot_format = png
        screenshot_flash = 1
        custom_width = 640
        custom_height = 480
        fullscreen = 0
        fullscreen_mode = 0
        scale = 1
        scale_mode = 1
        vsync = 0
        focus_dim = 0
        alternative_update_lock = 0
        render_driver = auto

        input_scale = 1.000000
        input_stretch = 0
        shader_refresh_rate = 0.000000

        [GL3 Shaders]
        shaders = 0

        Note that there was a guy that reported that QNX has issues if using a Microsoft Serial Mouse, which comes enabled by default in both MAME and PCem.

        • I see. Thank you, that might be it (spurious interrupts from the serial controller). Nevertheless, at the moment I’m pressed for other matters (e.g QNX message types πŸ™‚ and things like that)

          By the way, congratulations for what you’ve done so far!

  26. I didn’t realize that you had a working version of 1.2, so my comments are mute. I think you can do everything you need to, programming with 1.2 that you need. If you have already written a program to dump memory, you’ve probably already come across the weird ‘C’ ‘@’ convention that was used to access memory off the ES register.

    There’s been a lot of discussion here about getting 1.2 to boot from the hard disk. One of the hurdles may have to do with the fact that the arrangement to configure everything was short lived. From a user point of view, it felt like a hack on the part of Dan and Gordon. Originally there were only two hard drives supported, Davong and Corvus. Later the XT came along. I don’t know when the BIOS driver arrived. It was rarely used because it was not interrupt driven,…, of course.

    Somewhere between 1.2 and 2.21 the process was refined with the “boot” command. Even there was a bit of black art involved. The Head/Track/Cylinder configuration in the BIOS had to match the H/T/C configuration that the mounted driver saw when the boot command was issued. Otherwise, things just didn’t work. This was even true for LBA drives.

    • Regarding the weird @ notation: Surprisingly, no πŸ™‚
      my memory transfer function: (assume destination buffer is always based in DS)
      asm(“push ds”);
      asm(“push es”);
      asm(“mov ax,ds”);
      asm(“mov es,ax”);
      asm(“mov di,14[bp]”);
      asm(“mov ax,16[bp]”);
      asm(“mov ds,ax”);
      asm(“mov si,18[bp]”);
      asm(“mov cx,20[bp]”);
      asm(“rep movw”);
      asm(“pop es”);
      asm(“pop ds”);

      I’ll upload the entire program later; the program takes filename, segment and number of paragraphs (bytes*16) as arguments and writes the corresponding memory data to file (rather crude, but effective)

      My plan (should I get that far) would be to write my own loader, set everything in memory “just so” and start the kernel. I haven’t even started on that since the loader seems like the easy part (if the kernel is contiguous in the filesystem or just separated at the end of disk/partition). The main issue is integrating the xt driver in the kernel (it seems to me that for this version there is not even BIOS HDD support included) *and* getting the kernel to initialize it at start (that means patching the kernel)

      Speaking of boot, at least for the floppy version there is a small table inside the (floppy) boot sector that instructs the loader what to load. It has 4 rows composed of track, head, start_sector, sector_count, but the funny thing is that for each track it first loads 8 sectors, then the 9th, like so:
      ; data (C,H,S,cnt for int 13 reading)
      ; 1d 00 01 08
      ; 1d 00 09 01
      ; 1d 01 01 08
      ; 1d 01 09 01
      ; 00 00 00 00
      The loader code passes through this table and increases track number on each pass, eventually stopping at track 40.
      Maybe there was a previous version that was using 8-sector tracks?

      I am almost certain something similar was happening with the HDD boot routine as well, but as I was saying, that is the simple part. Patching the kernel… not so much.

      I finally finished understanding (well, more or less) the command line parsing that mount does and what variables it sets, now I’m well into the driver loading code (first hurdle was a message that took me a while to realize was ta_alloc_seg).

      • There is something that I disagree with, and is that is impossible that 1983-1984 QNX 1.2 relied on BIOS HD Type info for anything cause that didn’t existed in pre-IBM PC/AT computers. On the PC/AT, you could configure the HD Type that is wired to the standard IBM provided HDC card, and that info got saved in the RTC SRAM, but there was nothing similar pre-PC/AT. So for QNX 1.2, which should slighty predate the PC/AT, and I’m sure that also for QNX 2 if used in an IBM PC or PC/XT, it should have been done in some other way…

        For reference, here is the original IBM/Xebec HDC card that was used in the IBM PC/XT:
        The last 1986 revision had default settings for four different HD types, but I’m not sure how they were exposed to Software. Regardless, anything predating the last revision may only care about the single 306C/4H/17S 10 MiB default HD. So I doubt that QNX 1.2 xt Driver relied on directly reading that Jumper block, either.
        Note that if you select the hdc card in MAME, it does allow you to configure the HD Type based on those four supported XT types. Also, these HD Types from the XT HDC do NOT directly match the order of the PC/AT HD Types. You can compare the previous table with this one:

        QNX xt HDC Driver defaults to the standard 10 MiB HD CHS values, but it is also flexible since it allows you to manually input non standard sizes, and there seems that there were third party HDs intended to be used with the XT HDC that had to be manually configured. However, I didn’t experimented with that too much to see how much you can do with it, besides that it works if using either 305C or 307C (To read the extra cylinder, which is where the copy protection was supposed to be as the previous developer comments), which I did tested.
        My guess based on some of the previous comments is that since it was stated that all HDs of that era always had one more cylinder than its specifications said, the xt Driver may blindly mount the HD with +1 cylinders from specified, check for the copy protection, unmount, then mount with the specified values. I suppose that you will eventually prove my guess right or wrong.
        Could it also be possible that copy protection depended also on the HD Driver being used?

        • I disagree somewhat. Indeed, there is no HDD Type Info, however, BIOS int 13h,8 is documented as “XT & newer” and it returns geometry info for any drive, including HDD. (My first PC was an XT clone…)

          HDD BIOS was separated from mainboard BIOS (a separate ROM on the HDC) and it overloaded int 13h with its own routines.

          Also QNX documentation (found on floppy disk QNX_12_utils) says:

          BIOS DRIVER

          This driver uses the INT 13 bios calls to perfom hard disk IO. It will work on any controler card or machine which supports INT 13 hard disk IO. These cards usually contain a ROM which the bios finds and executes when power is turned on. This allows us to provide immediate support for the many new drives on the market. The BIOS driver reads the disk configuration table set up by the bios to determine the physical characteristics of the hard disk. This can be over-riden by specifying tracks, heads and sectors
          on the mount command.

          On the other hand, on another page it also says it supports AT, so there’s that πŸ™‚

          (on the HDD version you can “cd /expl” and then “expl drivers”)

          • I’m aware of the HDCs Option ROMs. However, where does INT 13h,8 source its CHS values from? Given the fact that back then you were supposed to configure that manually, the XT Xebec HDC should be always hardwired to 306/4/17, except on the last revision that has the Jumper block. If the copy protection relies on that, it would be unusable in any other HD except the standard geometry 10 MB ones.
            This should be testeable on MAME, which supports changing the Xebec HDC Jumper block (86Box doesn’t make it configurable as far that I could see).

            BTW, found this in Ralf Brown’s Interrupt List about Int 13/AH=08h:

            The maximum cylinder number reported in CX is usually two less than the total cylinder count reported in the fixed disk parameter table (see INT 41h,INT 46h) because early hard disks used the last cylinder for testing purposes; however, on some Zenith machines, the maximum cylinder number reportedly is three less than the count in the fixed disk parameter table.. For BIOSes which reserve the last cylinder for testing purposes, the cylinder count is automatically decremented.

            • I assume that if the card is fixed on a certain HDD type, it will only report that, no matter the reality, but it will report it. It is not inconceivable that a ROM change might be needed along to use the card with another HDD type πŸ™‚ For the newer cards (with jumpers) it’s safe to assume that jumper configuration gave the interface information about HDD geometry.

              “BTW, found this in Ralf Brown’s Interrupt List about Int 13/AH=08h:
              Thanks for the reference! I remembered reading somewhere about that geometry difference and couldn’t remember where! (in relation to my other story )

  27. I see, so if you write in ASM you don’t need the @ stuff. It’s for use in ‘C’. You can embed ASM in their C compiler, but there was also load_es_register() routine. Once loaded you can use @ instead of * to indicate a pointer that uses the ES register. It’s nice because you can stay in ‘C’ but cheat your way out of the 64K data region. For the real memory mode QNX that means you can look anywhere.

    I agree with you about the Microsoft interrupts. QNX 2 did not need a mouse generally unless you were using their first GUI, called QNX Windows. I’m quite sure that was not supported in QNX 1.2.

    As far as those CHS numbers being used, the boot loader needs to know where the boot file. I suspect it was in a fixed location for QNX 1.2. For booting off the hard drive, this information came from the partition.

  28. A little status update (teaser)
    It seems my approach worked, I now have a patched kernel that does not require mount or xt and that initializes the HDD before starting.
    I still have to write the HDD loader itself (for starters I’ll keep everything hardcoded in order to see it working)
    And I also have to patch some more so that the OS starts on drive 3: instead of 1:

    It shouldn’t be much longer now (hopefully the remaining 10% of the task won’t take 90% of the time).

    I have also learned a lot about QNX internals (at least WRT to version 1.2 πŸ™‚ )
    It would not have been possible without the header files included with the C compiler and the qnx_load technote from the ftp archive was also of great help.

    • The osimage only needs to be 79360 bytes. My original dump just read what the loader was reading.

      You can extend the section used for shared by modifying the word at 0000:0685. You can make the shared section a bit longer. A bit longer than the loaded file itself. I changed the value from 0x1A3 to 225.

      The QNX 2.1 loader will happily load 79360 bytes of the shorter image. Then it will load the second item in the load table at 13dd:0000. Which if the second item is short enough will be nicely protected in the segment for shared..

  29. Thanks!
    I have thought in terms of addresses related to fsys (that is segment 05E4 or absolute address 05E40). This is because I disassembled and analyzed a part of fsys in order to understand what is happening there πŸ™‚

    What is more, instead of adding to shared (last kernel section), I extended fsys by 2k (and pushed everything below it downwards) since at the time I thought I might need to run code inside of it. I did need that but not too much, so in the end everything could have been at end of shared (of course with shared enlarged by the respective size just to be sure)

    I’m off to write the boot loader πŸ™‚

  30. There are 2 main improvements I want to add:
    First, now I use an ugly hack to set drive to 3:/ – I patched the initial ta_create message inside task* to load “3:/xi/sh 3:/xi/sys.init” (Obviously I also created the relevant directory, copied sh and wrote a sys.init file inside it)
    It would be much better to just set the relevant info before that, only I still have to dig for that (that’s what I’m working on now)

    Second, as the image is built now, the kernel image lies beyond the end of the partition (I created a partition smaller than the HDD in order to have some spare spece). It would be better to load the file directly from the filesystem but that would add complexity (and I spent around an hour because I forgot that 8088 doesn’t have certain instructions that I took for granted, such as shl/shr reg,count; for 8088 count can’t be immediate).

    *task: To ease analysis I broke the kernel into its relevant parts: header, task, fsys, dev, shared. Since these are separate tasks, all data references inside each one is relative to their own segments. Fortunately at least task and fsys use DS = CS πŸ™‚

    Header is an array of 5 struct code_entry (see task.h) with fixed size 32 bytes (code_entry is of at least 20 bytes+length(name))

  31. @forty:
    “putting search 3 2 1 into 3:/xi/sys.init seems to work too.”

    As opposed to search 3, you mean?

    Nevertheless, I added code to the init routine so that it would set the proper value. (Also I removed some debug messages from the loader, that happened right before jump into kernel)

    Will update the machine on my site and post another hdd image shortly.

      • Great progress. I did note that the mount command shows 1 and 2 as floppies but not 3. Likewise a dcheck / chkfsys on any drive gives an error.

            • Confirmed working on 86Box v2.07 build 2008 emulating an IBM PC with 256 KiB RAM, CGA and the XT HDC with the following configuration:

              machine = ibmpc82
              cpu_use_dynarec = 0
              time_sync = disabled
              mem_size = 256

              gfxcard = cga

              [Input devices]
              mouse_type = none

              [Other peripherals]
              hdc = st506_xt

              rctrl_is_lalt = 1

              [Hard disks]
              hdd_01_parameters = 17, 4, 306, 0, mfm
              hdd_01_fn = qnx12_hddboot_v005.img
              hdd_01_mfm_channel = 0

              [Ports (COM & LPT)]
              serial1_enabled = 0
              serial2_enabled = 0
              lpt1_enabled = 0

              Congratulations on winning! The moment that programming skills had to get involved, I knew I was out of that league, but I’m still proud of what I achieve.
              Will wait for you to post the full history in your blog.

  32. I just realized I forgot to take into account the partition size. Will update shortly.

    That is why it’s better to do things right (i.e. actually read the partition table when building the in-memory disk structure)

  33. ..and done. chkfsys no longer complains.
    New image at and updated.
    I’m beginning to think I should streamline the process.

    By the way, the only method I could find of loading a custom HDD image in a PCjs machine is to run the entire repository, are there any simpler ways that I didn’t notice?

    @Zir Blazer thanks for the confirmation! As an aside, the problem with my Linux PCem was that I forgot to copy the XEBEC ROM 😐

    • Thank you! For me it is all new πŸ™‚

      WRT ed, it seems to be working fine, why do you say it is missing?

      On the other hand, one of my comments seems to have disappeared, maybe too many comments in a short while? Reposting here:
      @Zir Blazer – thank you for your confirmation and also for appreciation. Of course I will post the whole story (with sources and notes). As an aside: QNX wasn’t seeing the HDD in my PCem because I forgot to copy the Xebec ROM.

      The latest (corrected) image version is 006 (use/modify 005 link above, I won’t post another lik since maybe that was triggering some anti-spam measures)

  34. My mistake. After QNX 2, the name was changed to qed. I’ve gotten so used to qed that I changed the name on my QNX 2 machines. ed is there and works fine. I would need to change the keyboard mappings on my mac laptop to get it to work as there is no Big + on the right.

  35. One of the goals was to compile Hello World!. I was partially successful, The headers are missing as is the assembler (asm) and the linker (link). The parser (cc1) and code generator (cc2) are there. So I typed in

    $ cc1 | cc2
    main() {
    printf(“Hello world!\n”);

    which produced
    seg 3
    seg 1
    call prologue
    sub sp,#$$1
    jb stk_overflow
    cmp sp,stack_base
    jb stk_underflow
    seg 2
    b data “Hello world!\n”,0
    seg 1

    mov ax,#$2
    push ax
    add sp,#2
    $$1 equ 0
    jmp epilogue
    seg 3

  36. Argh. It treats as a tag I guess. So here it is again without around symbols.

    seg 3
    seg 1
    call prologue
    sub sp,#$$1
    jb stk_overflow
    cmp sp,stack_base
    jb stk_underflow
    seg 2
    b data β€œHello world!\n”,0
    seg 1

    mov ax,#$2
    push ax
    call printf
    add sp,#2
    $$1 equ 0
    jmp epilogue
    seg 3
    export main

  37. @Dan: Dodge: It seems that at some point when building the image I forgot to copy everything off diskettes. I’ll re(-re-re-re) build the image now, sorry.
    (Didn’t get much sleep these last few days)

    @Mitchell Schoenbrun: It happens. I just tried it on a whim, thinking it was a typical “menu” key back in the day (didn’t bother checking the manual) and was glad to see it working. I know, muscle memory can be a pain. It’s funny though, despite using mostly vi(m), I adapted somewhat easier to wordstar (childhood memories) than ed πŸ™‚

  38. Done updating. The disk should now include everything that was diskettes.
    I also added (to user/hawk) my memory copy program.
    Image now at v007 (link above, etc), qnx_pcjs updated

    cc now works πŸ™‚

  39. A minor historical note here. While I was scratching my head over why /lib/stdio.h, /cmds/cc and /cmds/link were missing, I create a simple hello-world .o file using cc1 and cc2 on both 1.2 and my QNX 2.21 system. Dumping the small files they appeared to be identical. This suggests strongly that no changes were made to the compiler in the intervening versions.

  40. It might well be that the differences were only in library files. Another thing I’ve noticed in this adventure was that there are a few stub functions that – at least in 1.2 – don’t do anything except return 0 or the value passed to them as argument. I assume that those functions were placeholders for functionality that was not yet implemented.
    And while writing that it occured to me that I have both 1.2 and 2.21 on my computer and could perform a simple check. Most libraries (that is object files in lib) are different, except for:

    And I’ve just noticed that, even in 1.2, asm already knows about 186/286 processors. (I assume +186 and +286 are related to that)

    As to the reason they were missing, it might be that when using keyboard to switch from pcem to another window/desktop, the keypress would be passed to pcem as well. And backup (as well as other QNX programs) can be “paused” with a keypress (and resumed with ENTER). So I might have paused it without noticing and then seeing the output stopped I assumed it copied everything.

  41. I removed the ugly xi/sh hack, now system starts with proper search 3 default.
    search is modified at a later point (I assume when reconfiguring for dual FDD), so that it ends up being search 3 2 but config/sys.init takes care of that. Just in case, I also added a cd 3:/ command, but it doesn’t seem necessary.

    I’d rather only have search 3 because, in case of a mistyped command it will take a while to search everywhere, especially if no diskette is inserted.

    As usual, image is at and the js has been updated as well.

  42. The contest has been won. I just did a
    $ cd /user/quantum
    $ cc hanoi.c
    and ran it. It brought back very fond memories. So, how do I send the funds?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.