After getting UNIX 6th Edition running on an 11/23 with an MSCP disk controller, I was looking for ways to get a complete system with a newer tool chain and full source code running -- and I started looking at the Programmer's Work Bench. As it turns out, there are complete source and binary distributions of PWB 1.0 available on the TUHS site, and with a working 6th Edition already in place, the transition was rather simple.

The steps from v6 to PWB

For starters, I installed the PWB tool chain, comprising as, C, ld, lex, yacc, and make, and some new libraries (liba, libc, libS, libPW).

This is when I first got side tracked. I have nothing against ed(1), but I do find it more comfortable to use an editor that can work visually in two dimensions, so I looked around for something that could do this. Initially, I was hoping that maybe there was an early vi that I could backport, or something, but then I stumbled across the ded screen oriented editor. It's a rather different editor, but quite interesting. The version I found had a number of debilitating bugs, and was not adapted to ANSI terminals at all, but these things weren't hard to fix. Having been an Emacs user for forty years, I also changed ded to use Emacs control keys for basic cursor movement instead of the ones its original author set it up with. I'm quite happy with the result.

Anyway, with a working screen editor, I set upon the PWB kernel, and got that working on my 11/23+. This involved adapting both the low level code I'd been using on the /23, the changes to ken/main.c (which is now os/main.c), and, of course, the MSCP driver. I also moved over various kernel fixes that I'd been using under v6, especially the Y2k fix and the mount point traversal fix from Wolfgang Helbig, available on TUHS.

I've changed a couple of values in /sys/hd/param.h. Being in Europe, I set HZ to 50 (and I've also changed the time zone in /usr/src/libc/ctime.c to be CET). I've set MAXMEM, which is the maximum allowable amount of core to allocate to a process, to be 32 KW instead of 64, because of the lack of separate I and D. Perhaps not necessary is the increase of USIZE from 12 to 16. I did that when I suspected that a process' kernel mode stack was hitting the u struct (which it wasn't), so I bumped USIZE to 16, which was the value in plain 6th Edition. It made sense for USG to lower this for PWB, as they needed to squeeze a large number of concurrent user processes onto their machines. I'm just me, and I have plenty of RAM in the /23. Anyway, this value needs to match that in the low level code in /sys/ml/mch.s.

With the kernel working, I rearranged the rest of the source code, unashamedly changing the structure: I just can't get used to the willy-nilly s1, s2, s3, s4... naming. I also created a lot of Makefiles, and ended up with a comfortable source hierarchy that I can use. Having added Wolfgang Helbig's patches to userland, I rebuilt and installed all of it, except for the RJE stuff, which I really don't see myself ever doing anything with. The binaries and data files are on the simh disk image (see below), but I don't have that stuff installed on my actual 11/23. I did originally learn to program using punched cards on a UNIVAC, but I don't think I'll ever have one in my basement hobby room...

With that exception, I have a complete PWB 1.0 running.

A few more tweaks

By and by, I got so fed up with not having umask that I added the system call, making it v7 compatible, and made a built-in for it in the shell. It defaults to 022.

I also reinstated the v6 smdate system call, after noticing that it is actually used (well, at least attempts are made) by a couple of applications in PWB/UNIX, most notably tp(1).

I modified the interrupt character in hd/tty.h: it's now ctrl-c instead of rubout.

I added a few useful files under /usr/include, to make it easier to backport software. For instance, stat.h is incompatible with anything newer than v6, so I put in a mostly v7 compatible sys/stat.h, with the expected stuff in it, except for the st_size field, which needs to be computed from st_size0 and st_size1. Compiles will fail if st_size is referenced, but that's just a single little thing to fix in the client code, and often not needed.

I added popen(3) and fdopen(3) to libS, again because I needed them for an application I backported. (Specifically, UUCP from v7.)

PWB/UNIX has the notion of a host name, but I thought defining that by editing a header file was inelegant, so I modified the config(8) program to put the name of the configuration file for the kernel into that variable.

I also modified config to accept a new "-p" flag, with a parameter that's the model number of the PDP-11 the kernel is to run on. All this really does is to add the ".data" directive to low.s if, and only if, the specified model number is greater than 40, implying that the target host has separate I and D capability. Of course, this has to match the ../ml/mch.s being used, anyway, so it needs to be done right, but this was an easy hack, and saves me calling ed from make to remove the directive from the generated file.

The mount command has a nice feature where it checks whether the file system has a name, set using /etc/labelit, that matches the directory you're mounting it on. That check assumed that you'd only ever mount a file system on a node in the root directory, though. I fixed that, so it looks at the last element in the mount path; my ra0d file system mounts on /usr/local, and is named "local".

Oh, and speaking of file systems: icheck has a nice feature, where it can organize the free list for smart allocation order on disks. As far as I can tell, "-s126:5" is what one wants for the RD52.

Some supporting applications

As mentioned above, I ported ded and UUCP, and they're both working well for me. With UUCP in place, and hooked up to one of my more modern UNIX installations over a serial port, I'm not limited to floppy disks for the exchange of data with the system. I can even send and receive email on the 11/23.

For the above mentioned exchange of data, I find myself preferring to use cpio. PWB/UNIX does not have tar, but it does have cpio, which should not be surprising, given that find and cpio where originally written for PWB/UNIX. The binary header format used by the PWB cpio differs slightly from the one that became more widely used when cpio was incorporated into System III. This makes it awkward to exchange data with modern systems. I use NetBSD, which uses libarchive for its tar and cpio commands, so I extended libarchive with support for the two binary formats, and made a pull request that has been accepted.

When using RX50 floppies for data exchange, it's nice to be able to easily chop up a large cpio archive into 800 block pieces, and transport them on floppies. After doing this with dd a couple of times, I wrote fdio, a small application to automate the floppy handling. It's used in the same fashion as cpio, so to move a file hierarchy from one host to another via floppies, I can say

  find . -print | cpio -o | fdio -o
on one host, and
  fdio -i | cpio -idm
on the other, and then just swap floppies when told to, which is very comfortable.

An RDBMS on an 11/23? Sure, why not?

Relational database systems are among my special interests, and once upon a time I ported Ingres 6.3 to 2.11BSD. Digging out the old 6.3 distribution, and getting it running under PWB/UNIX, was a natural next step once PWB was stable.

By version 6.3, the Ingres project had pretty much left the idea of running on a non-split I/D system behind. They had left all the old stuff in the source tree, though, so with a bit of rearranging in the Makefiles, it wasn't hard to once again get it ready for smaller hosts. Just a few little tweaks here and there were needed to get it running, and after adding the Ingres lock driver to the kernel, it happily works in a multi user environment, as well.

There's a lot of good documentation in the Ingres distribution, including a tutorial with a demo database. Note that your user has to be set up as an Ingres user for anything to work. (The "demo" database is installed, and my user, "tih", is correctly set up in the snapshot I'm making available (see below).) The project's paper The Design and Implementation of INGRES from 1976 is a very readable introduction to the RDBMS, even though it, like the tutorial, predates 6.3.

Ingres works by starting a set of processes that communicate via pipes, and uses quite a bit of memory, of course. Running two instances at once, to test the lock driver, revealed that my MSCP driver, as I'd been using it, had errors that kept the system from swapping. Fixing the immediate problem caused it to clobber the partition following the swap partition. Calculations and checks are working correctly now, but I've established that the C compiler has weaknesses in the type conversion logic, making it impossible to fully make it treat signed integers as unsigned.

RD52 disk image snapshot

In addition to the physical PDP-11/23+ I'm running PWB/UNIX 1.0 on, I maintain an RD52 image for simh, that I keep in the same state. This makes it easy to test experimental kernel changes there, where it takes mere seconds to create a backup copy to roll back to if something fails. Once tested and working, I make the same changes on the 11/23, or transfer the changed files using UUCP, if the change is more than just a quick edit.

A copy of the snapshot may be downloaded from my home system. The accompanying startup script for simh lets it start a few gettys on terminals that you can connect to with telnet to localhost, port 8023. (I have DLV11-J cards in my 11/23 machines, and this setup matches that nicely.)

The root password is "ken". The password for user "ken" you can find on the Interwebs. :) Under my user, "tih", there are some sample dot files that you may find useful as starting points. There's a working 'man' command, but it doesn't invoke any pager by itself, so the normal invocation pattern would be like

man 5 cpio | pg
The PWB system documentation is under /usr/man/op, and the full Ingres documentation is in its directory tree, under /usr/local/ingres.

There are two special device nodes on the image: /dev/fdio and /dev/tapx. The former is for my fdio utility, the latter for tp, and those names are the default device names for the applications, and are character and block device nodes, respectively, for ra3h. This means that they both represent the entire ra3 disk (rq3 in simh), and make the applications access that drive by default. To move files in and out of the simh image using tp, I just escape to simh, attach a tp file to rq3, continue the emulator, use tp, which accesses that file, and, finally, escape into simh again and detach the file.

While the userland source code is under /usr/src, the kernel source is under /sys. This was done to even out the disk usage a bit. (As previously mentioned, I'm using the partitioning scheme that became standard in BSD later on.)

When I need to build a new kernel, whether because I've changed something in the os or io directories (and run make there), or because I've changed the "sirius" (the /23 is named after our cat) configuration file in the cf directory, I just run make in the cf directory to get a new kernel. Do note: nothing keeps you from building a kernel that's too big! Every make run ends by running 'size' on the generated kernel, and you have to visually check that it's less than 0140000 (octal) bytes total size.