It's relatively easy to bring up a V6 PDP-11 Unix under the Ersatz-11 PDP-11 emulator, as covered in the Bringing up V6 Unix on the Ersatz-11 PDP-11 Emulator page. This is an addendum/ancillary page, for those who wish to go further.
It covers a range of topics, including how to get the Standard I/O Library, and the 'tar' command (which, alas, needs system mods to really be supported 'right'), means to keep file write timestamps when copying/moving them, how to have the system default to more reasonable input editing characters, etc.
Most importantly, it has a newer C compiler, one with support for longs, unsigneds, and a bunch of other stuff. (You could hack unsigneds in the 'vanilla' V6 C compiler, by using a char *, but that didn't always work.) In addition to /bin/ncc, you need the entire /xlib directory - don't rename it, or move the files to /lib; just copy it over as is.
Warning: for return of long data items from procedures to work (long returns use R1 as well as R0), you need a new csv.s, since the one from vanilla V6 bashes R1 on a procedure return. (Hey, R1 isn't used on return in that version, so bashing it is fine.) It goes in the C library, /lib/libc.a, once you've assembled it.
To get access to the contents of that disk pack image, you need to add either the RL or RP driver to your Unix (see here and here for how to do that).
In addition to the new C compiler, it has a ton of new commands. Alas, all we have are the binaries... :-( Too bad a source disk didn't get saved, along with all those useless packs full of biology stuff!
I haven't fully explored what's there, but one thing that is there is a new cdb, with an extended command set. Note: Some of the commands use some new Unix system calls which aren't in 'vanilla' V6, so they may blow out when you try and use them.
There's also complete kernel code, including code to bring it up (mostly) to v7 compatability, something else I haven't completely explored.
I haven't actually used it much, since there's a copy of the compiled library on the Shoppa disk, in /lib/libS.a. One hitch is that that library is in the so-called 'new archive' format, which the vanilla V6 tools don't grok. That disk has the new archive tool ('nar'), and you can either unpack the library with that, and then repack it with the old archive tool, or use a copy of the library which I did that to already, here.
(I think I got the files in there in the right order... but maybe not!
So look out for unresolved references. If you see some, the hack fix is to
specify that library twice in the command line [xxx -lS -lS], and that
should kludge it for the moment.)
Another hitch is that some of the calls require capabilities the Unix V6
kernel doesn't have (and can't be simulated). The lseek() system call is one
example; one can simulate the call to it in the C library routine fseek(), by
using block and byte seek() calls (and I did, see the code
but the problem is that the lseek() system call also returns the file offset
pointer (which ftell(), among others, uses), and there's no way to get that
info in V6 (that I knew of).
So fully supporting the Standard I/O Library requires a system mod (below).
System mods for Stdio and others; additional library things
lseek() and smdate()
Adding lseek() is pretty easy. I have the code in a new file sys5.c,
it also needs an entry in sysent.c, an updated copy of which is
Another hitch is that some of the calls require capabilities the Unix V6 kernel doesn't have (and can't be simulated). The lseek() system call is one example; one can simulate the call to it in the C library routine fseek(), by using block and byte seek() calls (and I did, see the code here), but the problem is that the lseek() system call also returns the file offset pointer (which ftell(), among others, uses), and there's no way to get that info in V6 (that I knew of).
So fully supporting the Standard I/O Library requires a system mod (below).
Note: That copy of sysent.c also has the entry for the smdate() system call un-commented-out. That's because 'tar' (below) kinda-sorta requires the ability to change file modified dates to work 'properly' (as in, how it usually works on other systems), and I needed smdate() for that (see the 'tar' entry for the details). The fixes to keep file write data (below) also use smdate().
So you'll also have to un-comment-out the code for that call, in sys4.c; or if you don't feel like wrestling with it in 'ed' (or extracting it and editing it on your host machine), you can just download a fixed copy of it here.
Compile them all (don't forget the "-O" flag!), and add them to lib1, viz.:
ar r ../lib1 sys*.oand then build a new system (see here for how to do that).
While doing 'tar', at one point I thought I needed the utime() system call, so I prepared a copy of it for V6 (available here if you're curious, including a V7 version of the iupdat() internal system routine), but since it turned out I didn't need it, I never bothered adding it.
I have yet to add the access() system call, but for the moment you can fake it with this library routine. (This only works for code you're compiling, of course; when trying to use an existing binary off the Shoppa pack, this won't help you.)
Note that the original 'mv' has to be installed as SUID root (for reasons I don't recall off the top of my head - 'Use the Source, Luke'); this one has to be, too. To the best of my knowledge it is safe to use in a multi-user system; as in, when adding the changes, I tried to make sure they would work for normal users, etc, but... they haven't been carefully audited to make sure there aren't any security issues.
You can change the editing characters with 'stty'. You can't, however, change the interrupt characters. That includes the use of DELETE for 'interrupt process', which is, ah, non-ideal by modern standards. So since we have to recompile things to change them, we might as well change the input editing characters too.
The interrupt characters are specified in sys/tty.h; change that, and then re-compile tty.c, which is where they are used. Remember to install it in the device library (../lib2) before you build a new Unix.
cc -c -O tty.c ar r ../lib2/tty.o rm tty.o
The input editing characters are used in kl.c and dz.c, but I'm not sure how long their setting of them lasts; I think those setting are over-ridden pretty quickly by getty, etc. To change the input editing characters, you need to change getty.c and login.c. The ones I use are here and here; getty goes in /etc, and login in /bin.
Note that I have added two entries to the terminal type table in getty; entry '3' is for pseudo-ttys (console, etc) which send DELETE from the BACKSPACE character on the standard Windoze keyboard, and '4' is for those (TELNET, etc) which send a BACKSPACE character from that key.
So in my /etc/ttys, tty8 (which is the Ersatz-11 console, a pseudo-VT100), is "183" (the '1' turns the line on); and the DZ lines (used for TELNET logins) are "1[a-h]4".
The code does use fseek(), which in the 'vanilla' Standard I/O Library uses the lseek() call (which also isn't in 'vanilla' V6 Unix), but you could use the alternative fseek() (here, above) instead of adding the lseek() system call.
Once you have them in, getting tar itself to work is pretty easy; mostly dealing with the fact that some system calls return different data in V6. The most problematic one is stat(), which returns the file size in a paired byte and shortword. chown() also takes a different number of arguments in V7; I hacked up a V7-compatible chown (available here - add it to the C library once compiled) to deal with that.
The other issue with tar() is that it uses the utime() system call - but it doesn't set the access time, just the modified time. So although I prepared a copy of the utime() system call for V6 (available here if you're curious, including a V7 version of the iupdat() internal system routine), I didn't need it: I just changed the code in tar to use the mdate() system call (the user form of smdate()).
The source to the V6ified 'ar' is here. You will also need the header files stdio.h, types.h, nstat.h, dir.h, and signal.h (available through the links).
Having done that, V6 compatible source is here.
It uses 'ncheck' for inode number -> file name mappings; it keeps the mappings in a file ("filenms") in the root directory of each disk pack, and recomputes them automagically whenever it looks like they are out of date. (The logic here is still not entirely complete.)
The source is available here, but as currently written it requires some minor hacks to the kernel (to get the system uptime, and also the current values in param.h - I got tired of having to recompile 'si' whenever I changed a parameter). The latter also includes a tweak to allow the running system's size to be found, to make sure that the symbol table in /unix applies to the running system.
So, you will also need:
ld -x l.o m40.o c.o param.o ../lib1 ../lib2 mv a.out /nunix
This command is now too large to be compiled with the 'vanilla' V6 C compiler (gets symbol table overflows), so you have to either i) break it into two pieces (which I was too lazy to do), ii) compile it with the new C compiler (above), or iii) increase the size of the symbol table in the V6 compiler (which is not as hard as it sounds). To do the latter, edit c0h.c to change 'hshsiz' from 200 to (say) 400. Then re-compile and install. Or you can just download it here.
Also it needs some things I moved out to a private library (I called it libL.a, for 'Local') while I was still trying to make it fit the old compiler (before I just gave up :-):
si mfitV 5in one TELNET window while you're working in another can be most interested. I was particularly amazed to find out (via the 'b' flag) that even with a large buffer cache, the cache is almost always entirely filled with blocks from the root device. I'm guessing this is because /bin/ is there; it would be interesting to move that to another device, and see what happens.
Note: First, it only works on systems that have had the smdate() system call added. Second, it wasn't written for use on a real time-sharing system; i.e. you have to be super-user to use it. And if you 'set-UID' it, anyone will be able to change the write dates on anyone else's files. Yes it would be easy to code so that it checks to see if the file owner is the same as the real UID, but... until someone really needs it, I have more interesting things to do! ;-)
Back to JNC's home page
© Copyright 2014, 2018, 2019-2020 by J. Noel Chiappa
Last updated: 18/September/2020