Tuesday, November 27, 2012

Data processing... 70's style

noSQL is the new trend... Seriously?

It is really amazing to see that in a purposedly scientific/engineering discipline as is (or should be) data processing we find some trends that we could name perfectly as "fashion" phenomena. Things that come in full force and dissapear whithout a trace. What is the hot development platform today will be forgotten (and badmouthed) tomorrow. I don't think I need to name an example of any of those. Specially in the field of "modern" java frameworks and environments. And meanwhile, in datacenters split al over the world, we can find lots and lots of lovely handcrafted COBOL and PLI code doing its job silently and faithfully, while a big part of the world economy relies in software build without all this modern fuss about patterns, architectures and... fancy trends...

Well, I guess the previous rant puts me definitely in the Grumpy Old Fart team. Not that I'm really so old (I am "just" 48 years old), but, to say it shortly, I'm really p*ss*d off about the younglings that try to teach me my job, day after day. Damn! I was crunching files when they were wearing diapers! :) Most of those guys are unable to code a master-transaction process program without using 2 gigabytes of framework code, a lot of XML descriptors, a fancy GUI and a Nespresso machine! Well, forget about the Nespresso, I use one of those myself :). And, to be honest, I amb not against the modern frameworks which save a lot of time and a lot of boilerplate code... I can write in java and I consider myself quite good at it, and I do know about some of those modern frameworks and I actually find some of those really briliant and smart. But, some times, the old fart in me has to rebel and yell out. Enter noSql, the last trend in database management.

Aparently some smart guys have noticed that the SQL layer adds a (sometimes) unnecessary overhead to a data processing application. And that the integrity safeguards imposed by the SQL databases (not a good choice of words... relational databases would be more precise) impair the scalability when an application reaches the multi-terabyte level. Then those smart guys have the really briliant thought of getting rid not only of the SQL layer, of but the relational model itself, and go to a pure, non-constrained key-value pair model...

At that time my semi-obsolete neurons start to send signals to my visual cortex, and I almost can see, in green over black letters the words ORGANIZATION IS INDEXED, ACCESS IS RANDOM. It looks like the smart java guys have rediscovered the indexed files! Of course, they are not going to call their amazing discovey "indexed file". It sounds too mainframish. They will use fancy words and fancy names to call that old, purposedly concept of tying a record... Ooops... I mean... an OBJECT to a key.

Now I will try a prescience exercise. I predict some of those brilliant guys will realize sometime in the next years that all those nice objects described by key-value parts have... oh, I'm feeling myself smart now... RELATIONSHIPS between them. And that those relationships can be modeled and integrated into the data storage. Now that is a revolutionary concept :) The only problem is that this revolution happened 40 years ago. Before the Codd model went mainstream, there were at least another two database models. And, what is even more revolutionary, those database models are still widely used today...

Pre-relational database models: hyerarchies and networks

You could build any information system just using plain old sequential files... but you don't want to do it. You could also do it using pure indexed files, and for very simple data models perhaps it could be a good choice. But if your data model gets complex (let's say... three or more files) your data management code will explode in a burst of tedious file and record management routines. The Database Management Sytems (DBMS) were designed to help with this complexity. At a very basic level, a DBMS will help the programer to:

  • Coordinate the changes between several data sets (record types), ensuring the consitence between them.
  • Isolate the programmer from the physical design of the data storage. 
  • Protect the information, providing utilities to back up and restore the information to a consistent state.
  • Providing the developers with views of the data restricted to their needs.
The first available DBMS models responded to two models:
  • A hyerarchical model, in which the different record types are related via "parent-child" relationships (for instance, a machine to its parts).
  • A network model, in which the different record types can be related via arbitrary relationships, not necessarily hyerarchical.
The network model can be seen as a generalization of the hyerarchical model, and the hyerarchical model can be seen as a restriction of the network one.

Take my data to the moon

The most known implementation of the hyerarchical model is  IBM IMS/DB. IMS is not just a database system, is also a transaction processing monitor. It runs in IBM zSeries mainframes, and it is widely used today in the banking and insurance industries, as well as in government related information management. It is insanely powerful, and eats heavy transactions like a kid devours candy. I would love to introduce my readers to IMS, but unfortunately there is no legal way to run IMS at home unless you have an indecent amount of cash. And, of course, IMS design and programming is quite ugly... That does not mean it is not fun. Actually, the restrictions of the hyerarchical model forces the designer to think twice (and trice!) about the design, and forces the programmer to get a deep" knowledge about the problem domain he is working in. Oh, and it is noSQL :). The basic method to access IMS data is to issue a 'GU' call, which returns the record ("segment" in IMS tongue) which satisfies a "SSA" (Segment Search Argument), which is basically a key search expression. So IMS gives you values associated to keys. Key-value parts... with the add-on of chains of "child" segments physically attached to the main (or "root") segments. Oh, by the way, the origin of IMS was the need to keep track of the parts of one of the most complex machines built by the mankind: the Apollo spacecraft. So, each time you use an ATM to get some cash you are probably using a piece of technology born to help to put the astronauts in the moon!

Untangle the network

The network model allows more flexibility than the hyerarchical one. Actually, it could look similar to the more used and known relational model. The main difference is in the relational mode there are (mostly) no physical relationships between record types (relations or tables); the relationships are built at run time using joins based on foreign keys. In a network database, the relationships do exist in the database, usually as physical pointers which relate the different record types. 

The network model databases were standarized at the late sixties and the first seventies by the CODASYL comitee. The network databases are also known as CODASYL databases, and that is the name we are going to use from now.

A CODASYL database is described by a SCHEMA definition. The schema definition contains:
  • The physical characteristics of the database, like the devices it uses, the areas (or parts) it has, the size of those areas in pages, and the size of the pages in bytes or words.
  • The different record types present in the database. For each record type the designer specifies the details about its physical placement, the retrieval and location methods (direct, by physical address, or using hashed keys) and the record structure (the composition of each record in fields). In contrast to the more formal relational model, a CODASYL record can contain arrays and "unstructured" byte bags.
  • The relationships between records, named "sets" in the CODASYL nomenclature. For each set, the designer specifies the "owner" and the "members". The owner and the members are joined using pointers, and the designer has some degree of control about which pointers to use. The most basic structure has a pointer in the "owner" to the first "member", and pointers relating each member to the one following it in a linked list. That linked list can be enhanced using backwards pointers (making it a double linked list) and pointers to the owner.
  • One or more "subschemas", which are subsets of the whole schema that can be used by the application programs. The designer/administrator has some (weak) tools to restrict the subschemas visible to the programmers; this allows him to hide the salary data of a personnel database to the guys doing work not related to payroll. I think you get the idea.
The best known CODASYL database is probably IDMS, owned by Computer Associates. It is still used nowadays. We cannot use IDMS legally (as far as I know), but we can use one of its derivatives. DEC licensed IDMS for use in its PDP-10 mainframes, and sold it as DBMS-10 and DBMS-20. And., guess what? We can run those under SIMH!

A little bit of time travel

We will need some material to set up our retro-database management experiment:

If you are like me and decided to do the full install by yourself you will find how to do it here. You will not find how to install COBOL and DBMS there, but you can look at the BASIC and FORTRAN instructions. The TOPS-10 installations are basically manual: you restore a save set to a working directory and then move the files by hand to the SYS: and HLP: directories. Piece of cake! Oh, I recommend to install EDT unless you want to add the need to learn an editor to your pdp10 adventure. You will find EDT and other goodies in this tape.

Once you have the TOPS-10 system up and running with a working COBOL compiler and an installed DBMS-10 database you will need to do some magic to add DBMS support to COBOL. Just follow the docs here and here. It is not really complex. Basically, restore the full DBMS tape into a working directory, copy C74LIB.REL and C74SHR.REL to this directory and submit a file to rebuild DBMS. When it is done, copy back both files AND C74O12.EXE to SYS: and you will be ready. If you got stuck, feel free to post a comment and I will try to help.

The real stuff: managing your LP (not CDs please) 70's style.

I have written some code as an example of what could be a classic data management application. You can find and download the code from this github repositotry. The files you will find are:
  • The schema file, RECRDS.DDL, which defines a database with two record types and one set:
    • LP-RECORD holds information about LPs (yes, those big circular black pieces of vinyl)
    • TRACK-RECORD holds information about the tracks of a record.
    • LP-SET relates a set of tracks to a record
  • REC001.CBL, a COBOL program to load the database from a flat, sequential file
  • REC002.CBL, a COBOL program to mantain the database using transactions from a sequential file
  • REC003.CBL, a COBOL program to empty the database.
  • LP.CBL, TRACK.CBL and TRANS.CBL: "copybook" files with the input records.
  • COMP.MIC, a comand procedure to compile and link the above programs.
  • RECA01.DAT, a sample input file for REC001.
  • RECA02.DAT, a sample input file for REC002.
By the way, the user executing these code must have the ENQ-DEQ privilege. You must use REACT to add it, or just use the OPERATOR account (which, in a real production system would be anathema, of course).

Oh, remember this is COBOL-74. That means there are no scoped statements. No END-IF. No END-PERFORM. No inline PERFORM. You get the idea...

Last details

If you want to try to build the programs you will need to create a COBOL text library using LP.CBL, TRACK.CBL and TRANS.CBL. This is the recipe:


You will probably want to read the docs:
And this is all by now. This is a different post, leaving the system level discussion for a while. Enjoy data processing, oldies style!

Wednesday, November 21, 2012

Status report

I have experienced a close relative loss in the last days, after some weeks of hospitalization, so I have not worked a lot in my pet projects. So this is going to be a very short post, just to keep this blog alive. I don't like to make online promises, but I plan to restart my work in MUXX and perhaps to post an entry about layered products installation under RSX-11M.

As for MUXX, I have stopped the work after achieving milestone 2. I plan to begin working in milestone 3 in the next days/weeks and the commits to github will resume their usual pace. So no new news about this.

On the simulators front, I have moved one of the emulated VAXen out of the Raspberry Pi. It is the uVAX-3900 simulation running OpenVMS 7.3. The Pi is too cramped to move that simulated machine with an acceptable speed, so it lives again in my old MacBook. Right now I have 5 simulations running in the Raspberry:

  • pdp11 running RSX-11M
  • pdp11 running RSX-11M+
  • pdp11 running RSTS/E
  • pdp10 running TOPS-10
  • vax780 running VMS 4.7 (and acting as level 2 router for my HECnet area)

There are two simulations running in the macbook:

  • pdp10 running TOPS-20 (Panda distribution)
  • vax running OpenVMS 7.3

The TOPS-20 system does not run directly inside the macbook (which, in turn, runs Ubuntu 10.04 LTS). I run it inside a VirtualBOX VM which runs a super-light Linux distro. The reason is I was not able to share the ethernet adapter between the host system and the pdp10. Adding a thin layer of virtualization fixed that. Now I have both TCP/IP and DECNET in the pdp10, so life is good.

Oh, by the way. The forthcoming version of simh will come with some nice surprises for us DEC-nostalgics. The current repository adds some "new" VAX machines:

  • uVAX I
  • uVAX II
  • VAX 730
  • VAX 750
  • Industrial uVAX

This, and a revamped I/O subsystem which will allow the simulators to use asynchronous I/O. Mark and the rest of contributors are doing, as always, a very good job. Thanks a lot, guys!

Saturday, October 20, 2012

MUXX: Milestone 2 reached

My toy kernel slowly evolves...

This morning I have considered my little toy PDP-11 kernel has reached the second milestone. This means that right now my creature has the following super-advanced features:

  • It can open and close channels to devices (currently just the paper tape puncher and reader and the line printer).
  • It can read and write using those channels, in synchronous mode (no context switch when a task blocks for a read... it spins faithfully until the I/O operation is complete).
  • It can load tasks from a channel and execute those tasks in a separate, memory protected address space. 
The next goal is to pull some functionality out of the kernel space and move it to different processes. At that point I will be able to say without being ashamed of myself that MUXX has a microkernel architecture... :) And I really must do it, because the current version is just at the limit of the 24KB I defined as kernel code space. So, the next steps will be:

  • Define a framework of inter-processes message passing and replying (I will probably inspire myself on minix to do this).
  • Decide which kernel functionality I will move to the auxiliary processes without breaking it all. It will be probably the memory management and the error/message handling.
I have swapped milestones 3 and 4. My original plan was to get interrupt-driven asynchronous I/O first, but right now I can't stuff anything else in the kernel space. On the other hand, the asynchcronous I/O implementation could and will use separated processes and inter-task messaging, so it makes much more sense to do it in this order.

About process creation and task loading

As I wrote when I presented this project, my goal is to learn about operating system implementation as well as to known better the PDP-11 architecture. I have to say I am progressing in both goals. A lot.

Lets put an example. The load() function. This funcion loads a task image from a channel and puts it into memory. When I started I planned it to be a syscall. It IS a syscall in some operating systems (in particular, MVS or z/OS, as you want to call that thing). In UNIX there is not a load() function, but a family of exec() functions which do (aproximately) the same function.

The UNIX operating system uses a somehow curious way to create a new task. To create a new task (or process) you must use the fork() system call, which basically duplicates the executing task. Then this function returns a different result for the "parent" task (in this case it returns the ID of the created process) and the "child" or "new" task (it returns NULL). Then the parent task can go on with its own business, while the "new" one can call the exec() function to load a new executable and transfer control to it. 

In contrast, MUXX has a different model, inspired in VMS. In that wonderful operating system the parent task uses the CREPRC syscall to build a new address space and load an image into it, just in one step. There is no "forking": the new process does not begin its existance as a copy of the parent. In MUXX the CREPRC system call does create a new address space and does set up its corresponding memory pages, but it does not load an image into it. It assumes the new process will execute some code already present in the kernel image (so the new task is hard-linked into the kernel). The reason to do it this way is really simple: during the development of MUXX I needed to be able to create new tasks before I could think about loading different, separated images. So CREPRC just creates and branches, but does not load.

So here comes LOADPRC, a second syscall which does load a new image. And now comes the problem: the MUXX kernel is not preemptable at this moment. That means that the kernel code (so the syscalls) executes with the interrupts disabled. And to do a potentially long (in terms of elapsed time) operation like an  image load with interrupts (and hence task switching) disabled is really an ugly solution. That means, basically, that the actual loading of the new image has to be performed in user mode, out of the kernel space.

This brings us into another problem. We are loading an image into a new task, what means different memory configuration (different MMU setup)... from user mode code and from (probably) a non-privileged task. I could probably have written some kludge to do the MMU config switching during the LOAD, but I felt a little bit lazy about it, so I decided to use another aproximation. Enters rshell.

rshell comes from resident shell. It will evolve to be the "resident" part of the system shell (the part which will be present in memory at all the time), and is hard-linked with the kernel code, so it has to live in the low 24KB of memory. Right now rshell is really simple. It does just one thing: it looks at the task memory base (at 060000 octal) for a device name (in the future it will be a file specification) and calls load() to read that task into memory (beginning at 060000!). load() is a library function, not a syscall, and executes mostly in user mode (except for the actual I/O). When/if the load is completed, rshell jumps to the address 060000, where the linker has located the crt0() function, which in turn initializes the runtime and calls the main() function for the task we are loading. 

The nice part of this is rshell executes in the new task address space. So when a program invokes LOADPRC it does the following:
  • It prepares a new task address space, just like CREPRC does.
  • It copies the name of the image to load at the position 060000 of the new address space
  • It prepares the new task to begin its execution at the rshell() entry point
And that's it. When the scheduler selects the new task to execute it will run rshell, which will load the new image and transfer control to it... Of course, right now MUXX can use just the paper tape reader, so there are concurrency problems (if we try to run 3 tasks they will try to load at the same time...), but those are workable.

About the MMU setup and MUXX memory model

When I started loading tasks at their own address (beginning at 060000) I begun experiencing weird and hard to explain crashes. They came in all flavours: Illegal instructions, odd memory accesses, MMU exceptions... I really had no idea about what was happening, but they just occurred when I started the tasks using LOADPRC (if I linked the same tasks into the kernel and ran them using CREPRC they did fine). After some investigation, I realized the code resident at the page 6 (060000-080000) was being overwritten, so when the execution got transferred to the corrupted position all kinds of Bad Things happened. Observing the overwritten areas they seemed to be clobbered by printf()... so I spent a pair of days trying to find some bug there.

I obtained printf() from the 2.11BSD libc, with some minor changes to adapt the BSD code to MUXX ABI. I had previously found some stupid bugs in my minor changes, but I thought it was now correct. After some hours of work, I decided printf() was doing fine, so the problem had to be somewhere else. And that somewhere else happened to be precisely here. Let's take a closer look at that.

MUXX task memory map

The muxx_memsvc_svc.c module contains the basic memory management code. If you look at the linked version you'll see it is incomplete (right now the memory deallocation is not implemented yet).One of the things that module does is to set up the address space for a new task. That is what muxx_setup_taskmem() does.

A MUXX task address space has three (or four) different memory regions:
  • 000000 to 057777 is the kernel space, shared by all the tasks. It is write-protected from user mode code (except for the tasks with operprv privilege and the system tasks). 
  • 060000 to 137777 is the user space. CREPRC and LOADPRC allows to select between three task sizes (SMALL, MED and LARGE), which correspond to real user spaces up to 077777, 117777 or 137777 respectively.
  • 0150000 to 157777 is the stack space, furtherly divided at 156000 between user mode stack and kernel mode stack
The space between 137777 and 150000 is not allocated at this point. It will be used when I implement different stack sizes.

The fourth region, between 160000 and 177777 is the mapped IO area, and it is not mapped into the user mode address space unless the task has the ioprv privilege.

PDP-11 memory management

The PDP-11 program-accessible address space is, then, a 65566 position array of bytes, or a 32768 array of words. The physical memory addressing capability of the PDP11 depends on the model and goes up to 2048 Kbytes. The model I am targeting, the 11-60, has a 256KB addressable space. The MMU allows the PDP to map those 256KB so they cant be seen through the 64KB address space. When the MMU is enabled, the addresses the programs work with are virtual addresses which must be relocated to real, physical addresses. Using the MMU we can provide different relocations for different tasks, so each task gets its own 64KB address space, with or without sharing memory with other tasks.

A 16-bit virtual address is formed by two parts:
  • The highest 3 bits form a number from 0 to 7 which tells us what "page register" will we use to relocate the address.
  • The lowest 13 bits form a displacement inside a 8192 bytes "page".
The MMU has a bunch of registers to configure the memory relocation. These registers are grouped into two or three sets corresponding to the processor execution modes (user, kernel and, in some models, supervisor). The processor models with separate I and D address spaces double the number of registers. Our PDP-11/60 has just two sets for user mode and kernel mode. Each set contains eigth pairs of registers, each of those pairs corresponding to one "page" between 0 and 7. And each pair is composed by a page address register (PAR) and a page descriptor register (PDR). Each PAR contains a number of physical memory block, being each block formed by 64 bytes. The PDR contain control information, like the access permissions for the page they describe, the size of the block and some other information, particulary the growth direction, which tells the MMU if the accesses will be done upwards (like in normal code or data arrays) or downwards (like in a stack).

Lets say our program refers to the virtual address 060042. That address breaks into:
  • A page number in bits 13-15, which is 03.
  • A displacement in bits 0-12, which is 42.
That means the MMU will use the PAR and PDR for the page 03 of the current mode. We will ignore the PDR at this moment. Let's say the PAR for the page 03 contains the value 01200. Then, the physical direction will be formed this way:
  • Physical address base: 01200 * 0100 = 0120000 (remember we are using octal figures)
  • Displacement: 042
  • Physical address: 0120042
That will be the real address the CPU will reference. Changing the values of the PAR we can assign the same virtual address to different physical addresses, efectively isolating the tasks from each other. Manipulating the PDR we can establish protection for the memory pages, so the user mode code can't write into the kernel space or can't even see the IO mapped addresses.

The MMU can also detect when we try to access a memory address which is out of the mapped range. The PDR contains a field with the length (in blocks) of the current page. So, if  we confugure it with a 4KB memory page and we reference a position at that page with a displacement grearter than 4KB, the MMU forces a memory trap and invokes a handler using the usual PDP-11 trap sequence; this allows an operating system to do something about that, being it just killing the offending task, invoking some paging mechanism or panicking if the trap has occurred in kernel space.

Now you can tell me a liar. I've omitted that pesky "growth direction" bit in the PDR. If that bit is set then we are telling the MMU that the page grows downward, and then the size check works just the opposite way. That means in our exemple of a 4KB page it would trigger a trap if we referred to a virtual memory with a displacement less than 4KB! In MUXX the stack resides in a 4KB page, so if we grow the stack below that 4KB line, it will trigger the trap and will abort the task (currently, it panics the system, since MUXX does not know how to kill a task yet)

Misunderstanding the MMU and learning it the hard way

Let's look at the stack preparing code in muxx_setup_taskmem:

  ** Task stack space
  ** The stack size options should be evaluated and applied here
  ** Since the stack grows downward, the PAR contains Addr - Size 
  ** so the physical addresses are properly calculated without
  ** overlapping.
  ** Example: Base virtual address: 0140000
  **          Top of stack:         0157777 (0120000 + 020000 - 1)
  **          Bottom of stack:      0150000 (0120000 + 010000 )
  **          PAR value:            0500
  **          Size:                 0100 (4K)
  **          Physical range:       060000:067777   
  mcb = muxx_mem_getblock(task, 0100, MMCB_FLG_STK, 6);
  if (mcb != NULL) {
    task->mmuState.upar[6] = mcb->blockAddr-0100;
    task->mmuState.updr[6] = PDR_ACC_RW | PDR_SIZ_4K | PDR_DIR_DN;
    task->mmuState.kpar[6] = mcb->blockAddr-0100;
    task->mmuState.kpdr[6] = PDR_ACC_RW | PDR_SIZ_4K | PDR_DIR_DN;
  } else {
    return (ENOMEM);

Don't you find anything unusual? Let's check the previous, bugged code:
  ** Task stack space
  ** The stack size options should be evaluated and applied here
  mcb = muxx_mem_getblock(task, 0100, MMCB_FLG_STK, 6);
  if (mcb != NULL) {
    task->mmuState.upar[6] = mcb->blockAddr;
    task->mmuState.updr[6] = PDR_ACC_RW | PDR_SIZ_4K | PDR_DIR_DN;
    task->mmuState.kpar[6] = mcb->blockAddr;
    task->mmuState.kpdr[6] = PDR_ACC_RW | PDR_SIZ_4K | PDR_DIR_DN;
  } else {
    return (ENOMEM);

Do you see the difference? In the corrected code I'm substracting 0100 from the PAR value for the stack page. So if I'm allocating the addresses 140000-150000 to the stack, I'm not writing 1400 in the PAR, but 1300. Why? 

The top of the stack for the kernel mode code is at 157777. That is, page 6, displacement 17777. If I put 1400 in the PAR this translates to 140000 + 17777 = 157777. Since the stack PDR is configure with the "grow downwards" bit, the MMU will not complain about this relocation and when MUXX writes something in the stack, it will clobber the 157777 physical memory address.

Of course, our stack is 4KB big, so we have just allocated 0100 blocks for it. And we have allocated the next available blocks to the next created task (since the stack is the last page we set up). So the next page will have probably the blocks 1500 to 1700 (for a 8K so 200 blocks page). Now we can see the problem. The stack of this task is sharing the same memory blocks of some page of the next created task. And that is precisely what we see... the 03 page of the next task (the one which will be allocated first!) is clobbed by our current task writing into its stack! 

The solution is to pull back the content we write in the PAR by 0100, corresponding to the 4KB size we are not allocating to the stack. Redoing the numbers, the 157777 virtual address would relocate into block 1300 plus displacement 17777 = 147777, which is just in the 1400-1477 range. If we go down 140000 the MMU will detect it and will trigger a trap, so we are safe. The weird thing is that in our memory management tables we will register the stack uses the pages  1400-1477, while the PAR will contain 1300. Weird, but correct.

Going on, and small machines...

And that is all for today. As unrelated stuff, my raspberry holds now 6 simulations: 2 VAXen, 3 PDP-11s (RSXM, RSXM+ and RSTS) and PDP-10... without a lot of load, but they work. For those of you who are part of HECNET, remember those machines are in area 7 and most of them have GUEST accounts, You will be welcome at my humble simulated, rapsberryzed digital home.

Monday, September 17, 2012

New toy...

This is going to be a short post... I've just got a little nice toy, and I've moved 3 of my SIMH machines to it. 

Yes, it is a Raspberry PI. And right now it is hosting three simulated machines: a VAX 780 running VMS 4.7 (and working as area router for my HecNet link), a microVAX 3900 running openVMS 7.3 and a PDP-11 running RSX11-MPlus 4.6. Of course, the simulated machines are idle most of the time, otherwise I guess they would overwhelm the poor raspberry...

To make it run, I have cloned the SIMH git repository from http://github.com/simh/simh, and before compiling it I've installed the packages for libpcap and vde2 (if you are going to do this, don't forget you have to install the -dev packages). SIMH compiled without any complaint, and once I got the simulated machines copied to the USB pendrive I've plugged to the thingy, they booted without a hassle.

After that, I just edited /etc/networking/interfaces so the vde magic gets configured at boot time:

auto lo

iface lo inet loopback

#iface eth0 inet dhcp

auto eth0
     iface eth0 inet static

auto tap0
     iface tap0 inet manual
     vde2-switch -t tap0 -n 16 -s /tmp/vde.ctl -M /tmp/vde.mgmt -m 666 --mgmtmode 666 

auto br0
     iface br0 inet static
     bridge_ports eth0 tap0             

And that is basically all (I'm using a static IP address bound to br0... the default configuration for the debian-based distribution I'm using is to use DHCP).

Not bad for a 35€ little computer...

Thursday, September 6, 2012

Writing a kernel: I'm falling in love with the PDP-11 architecture

You are doing WHAT?

So the crazyness goes on. I have decided to learn to program in PDP-11 assembly language, and the way I'm doing it is writing a sort of operating system for that platform. I did some OS practices in my college time, but they were based on x86, and were limited to create a real mode (no memory protection!) multitasker. At work I have no reason to do kernel level programming (although I've done some systems level stuff for the IBM mainframe), so I was somehow lacking on OS knowledge.

First of all, my work is completely public. I'm publishing my code at github. The public repository is https://github.com/jguillaumes/muxx MUXX is the name of the toy operating system (Mostly Useless eXperimental eXecutive). I have got to build some tools to manage the SIMH paper tape loading format, which can be found at https://github.com/jguillaumes/retroutils. Feel free to take a look at the code and partake anything you find useful. 

The big picture

MUXX is basically a vehicle for my enjoyement. So it will be probably full of design flaws, underoptimal implementations and failed decisions. Lets see what it IS and what it IS NOT:
  • It will NOT be a UNIX clone.
  • It will have a microkernel structure. Basically, that means that some of the low-level functions will reside in separated tasks with separated address spaces; some of those tasks will be the memory management and some of the device drivers, including the console, multiplexer, paper tape and flopply drivers.
  • It will be written in assembly code and C (not because I like it, but because there is no option that I know of building a cross-compiler for the PDP-11 capable of generating systems level code).
  • The target, emulated machine will be a PDP 11/60 with 256 KW of memory. The only reason for that is that was the only PDP11 I worked with, something like  25 years ago...
MUXX will use memory management (it will be a mapped system), and will run in kernel and user modes. It will not use supervisor mode (at least at the beginning), neither I/D space separation, although I will try to code the necessary hooks to implement those features. 

Development milestones.

There is a doc subdirectory in the MUXX public git repository, which contains some documents about what I am doing. The milestones.odt document lists my development plan (of course subject to changes... that's the good part of being project manager/analyst/system programmer/technical writer all at the same time). I have set up a series of milestones I'm planning to follow, which are (copied straight from the document):

  • Milestone 0: Console I/O, MMU enablement and mapping, system calls using TRAP or EMT, switching to/from user mode via RTS and traps. Already achieved.
  • Milestone 1: Clock interrupt. “AAA/BBB” task switching, with full context management. Human-readable trap/abort messages. Basic memory management (embedded in kernel). Tasks “A” and “B” still linked into kernel. Achieved.
  • Milestone 2: Device Driver framework. Programmed mode paper tape device driver (read only and synchronous). Tasks “A” and “B” loaded from LDA image on paper tape. Tasks loading will be hardcoded at kernel startup, but the tasks will not be linked into the kernel.
  • Milestone 3: Interrupt-driven paper tape device driver. Tasks “A” and “B” loaded from paper tape. The task loading will be still hardcoded.
  • Milestone 4: Memory management out of kernel, in privileged task. Message switching between tasks
  • Milestone 5: Interrupt-driven console device driver. Basic command interpreter (“TASKS”, “LOADA”, “LOADB”, “STOPA” and “STOPB” commands. Loading of tasks “A” and “B” moved out of the startup code.
  • Milestone 6: Interrupt-driven multiplexer device driver (probably DZ11). Tasks “A” and “B” running in different terminals. Several instances of the tasks (up to number of terminals) will be started from the console.
At this time I've achieved milestone 1, so I have a working multitasker, with full memory protection and (sort of) meaningful abort/panic messages. The only device MUXX knows off at this time is the console (DL11), and just for output; that output is done in synchronous mode (so the kernel loops until the I/O operation is done). I have also written a basic "read character" rountine, which is also synchronous, so it can't be used seriously in a multitasking kernel... but I'll overcome this limitation when I reach milestone 5... some day.

Sources of "inspiration"

I don't want to write just another UNIX clone. We have perhaps too many of those. My plan is to learn about OS design and implementantion, and to have fun. Having said that, there is a lot of useful information around. My main sources for... inspiration are:
  • Andy Tannenbaum's book. Yeah, the blue brick. Both the text and the minix sources are really good sources of information. And it is written to be easy to learn from. I'm taking the whole microkernel and message passing ideas from minix.
  • The PDP-11 Handbook. The basic reference book about the PDP-11 architecture. It covers mostly all you need to program the '11: instruction set, interrupt handling, memory managing and basic programming techiques.
  • The PDP-11 peripherals handbook. It contains the information needed to write device drivers: I/O mappings, interrupt vector numbers and control register description. The version I've found in the net is not the last one, and several devices available in SIMH are not covered, but it will be enough to begin.
  • The 2.11BSD system source code.The best way to solve a problem is to look how someone else did solve it before you. Of course you can also set up your own 2.11BSD simulated machine if you want to, but it is probably easier to browse the source code from the linked site.


The development is being done in a linux machine, so we need some cross-development tools. My previous entry talked about building a PDP-11 cross-assembler. I progressed a little bit since I wrote that. Most of the information I wrote is valid. I've built the tools from the git repositories. The GCC git repository can be found here. The binutils repository is this one.
If you have read the previous entry perhaps you remember I had a quite weird problem with the linker, which failed to create pdp11 binaries. It complained about a syntax error in the default linking script. Although this is not a showstopper (I use a customized link script to build the kernel), it is annoying. The solutuion was ridiculously easy: you MUST use a "clean" environment to build binutils and gcc. By "clean" I mean you must erase some environment variables which interfere in the linking process: LIBPATH, SHLIB_PATH were the culprits in my case. If you plan to build the cross-building tools, check your environment for related variables.
I also found a bug in the gas assembler. I submited it to the binutils bugzilla.The problem is gas assembles this instruction:

         jsr    pc,@(R0)
        jsr    pc,(R0)
Instead of
        jsr    pc,@0(R0)

Unfortunately, the gcc compiler generates this code if the program uses a function pointer table... for instance, in the typical syscall routing code. Provisionally, until the bug gets fixed (I could try to do it myself...) I'm using a long switch-case block to do the syscall routing. Ugly, but works.
Having a C compiler and an assembler is not enough. The C language is just a little bit over the assembly code. It needs a library (libc) to work. I have tried to port the newlib C library to the PDP-11 target, with mixed results. The real problem is the 64K address space. The code for a simple sprintf() is larger than those 64K! I'm still working on that, but it seems the problem relies in the float/double support. Right now my "kernel" does not support floating point in any way, so I will try to get rid of the float code and see if I can put newlib on diet...

Current status

Right now my "kernel" does the following things:

  • Enables and configures memory management, so each "task" gets its 64K protected address space.
  • Creates and runs tasks in user mode, using TRAP to call the system services.
  • Multitasks, using a simple round-robbin algorithm. I plan to add priorities later.
  • Writes to console (synchronously).
  • Can provide somehow inteligible panic information when it crashes (and it does it a lot).
Right now, the "kernel" and the "tasks" are linked together. My next step will be to write a paper tape device driver, so I can move the "user tasks" out of the kernel and load them separately. Once this is done, I will work on removing the memory management to the kernel and putting it to its own task. 

I'm having a lot of fun :).

Tuesday, July 24, 2012

Writing PDP11 assembly code from Linux (and running it on bare -simulated- metal!)

Let's pretend for a moment you are as crazy as myself about computing and, specially, about classic computers like the PDP-11. Let's say your crazyness gets to the point you start to consider seriously writing your own operating system for the PDP-11. Or, at least, doing the first steps to write something similat to an operating system. Sounds scary, doesn't it? No way! It sounds fun!

Still reading? Fine, because we are going to do those very first steps towards this goal. And the very first step to build an operating system is to have the hability to write, debug and run standalone software. That is, to run programs in a PDP-11 without any operating system loaded.

To do that, we can use two different approaches:
  • We can use a running PDP11 system with an existen operating system to write and assemble the software, moving it to our "empty" system.
  • We can use a cross-assembler and cross-compiler to write the software under another operating system, and feed it to our PDP11.
At this point, it is important to remember we are talking about a simulated PDP-11. At least in my case, I don't have access to a real machine. Then, it makes sense to use the host operating environment to write and compile the software we will run in the simulator. The plan is to be able to build files loadable into the SIMH simulator using the "load" console command. Then we can use simh to run the software or to single step it. No operating system needed for that.

Load file format

Simh can load into memory files representing a paper tape image. Yup, you have read it well. Punched paper tape. Now we are talking about classic computing! 

We can find the format of those images reading the pdp11_sys.c source file of the simh distribution. The code is in a function called sim_load. The comments block of that function describes the file format, which is not very complicated. The file is composed by byte blocks, each one of them preceded by a header and followed by a checksum. The last block is en empty one (just header and checksum). The structure of the header is as follows:

Offset Length Datatype Content
0 1 char Fixed value: 1
1 1 char Fixed value: 0
2 2 word Size of the data block, in little endian format
4 2 word Load address for that block, in little endian format

The checksum is computed adding every byte value of the block including the header and taking the 2's complement of the low order byte of the result. In other words, the negative value of the low order byte taken as an unsigned character.

The last (empty) block also contains a "load address", but in this case the content of that field will be loaded into the PC register of the machine after the file has been loaded. So it has to contain the entry point for the loaded program. 

We will refer to this format as "load format".

Generating a load format file

From RT-11

The easiest way to generate a load file is to use a running RT-11 system. The RT-11 linker has the option of generating directly load files instead of native executables. We have just to add the "/LDA" switch to the LINK command and we'll get a file with a LDA extension instead of the usual SAV one. The linker will set up the file to load at the octal address 01000 by default, just over the interrupt vector area, so we will be mostly fine with the default. We can change it using the /BOTTOM switch if we really need to load our code in any other place.

So, we can edit our source code in our RT-11 system, assemble/compile it using the native MACRO-11 compiler or whatever HL language we want, and LINK the resulting object into a LDA file. Now we have to transfer that file to our host environment. We have several options to do that. As examples:
  • We can use KERMIT to move the file to our host system. For some reason, I've not been able to do this. When I launch KERMIT in SERVER mode in the RT-SYSTEM it ignores my download requests. 
  • We could use a TCP/IP stack. I haven't done that, so I can't really help about that option.
  • We can use the paper tape emulation in SIMH. To do so, we have to SYSGEN our RT-11 adding the PC device, and then we can simply COPY from or to PC: to transfer the files.
This procedure works, but you have to use the RT-11 environment and specifically the KED editor, which I've found difficult to use with my terminal emulators. You can edit in your host environment and use the paper tape device to upload your sources to RT-11, but its quite cumbersome (and you must be sure you are using the DOS convention for the line terminators...), so I wanted to find an alternative.

Cross-compiling and cross-assembling

The obvious solution is to use a cross-assembler and a cross-compiler to generate the PDP-11 code directly in your host environment. In my case, that host environment is a laptop running Ubuntu Linux. After asking in the simh mailing list, I found myself with several opti (ons:
  • The simh distribution contains a port of the "native" assembler, MACRO-11, to unix. It compiles without problems and generates PDP-11 object files. It also uses the DEC source format (being a port of the native assembler), so it seams to be the best approach to the problem. Unfortunately, an OBJ file is not loadable to SIMH, neither is enough to do medium complex things. I wanted to be able to write modular code, and to be able to link together assembly and C code. Without a linker, I couldn't do that. So I had to discard macro11. Bob Armstrong sent me an utility to extract the contents of the OBJ file to build files in EPROM HEX format; it could be extended to generate LDA files, but nevertheless the linker would still be missing.
  • The GNU toolchain. That means the gas assembler, part of the binutils package, and the very known gcc C compiler, as well as ld and the rest of utilities. After a wrong start that is the option I have choosen. Right now I have been able to write, compile and execute a "Hello world" program writing directly to the emulated PDP-11 console. So I guess I'm in the right path... Let's elaborate a little bit about how to achieve this.

Building the cross-assembler and cross-compiler

First, the bill of materials. I used these GNU packages:

That is obviously not the last gcc version, but I was not able to build the cross-compiler using the last release, so I downgraded back to 3.4. Anyway, we don't need the bleeding edge features of the last version, so we will be fine with 3.4.6. If you are going to build the cross-compiler, download just the "core" archive. Unless you want c++, fortran, ada and the rest of languages the core is all you need.

The recommended procedure to build the tools is as follows:
  • Unpackage the binutils tarball
  • Create a new directory for your binutils build, and cd to it
  • Execute the follwing command (from the new, empty directory):
<binutils_dir>/configure --target=pdp11-aout

Substitute <binutils_dir> for the directory where the tarball was expanded. This command will configure the build for a /usr/localprefix installation. You can change it if you want, using the corresponding configure options.

  • After the completion of configure, just type make to build the software and make install to move the executables to the /usr/local/bin directory (you will probably need to run that command as root using sudo or similar). You will end with a series of executables named pdp11-aout-as, pdp11-aout-ld and so on. Those executables are your cross-building tools.
  • After building binutils, you can build the cross-compiler. The procedure is basically the same, substituting binutils by gcc. It's also recommended to use a separate directory to do the build.

Using the cross-building tools

If you try to compile and link a "helloworld" program you will find the linker does not work. It will complain about a syntax error in its default linking script... 

I really had no idea about that "linker script". Well, we learn everyday. With some inspiration from this nice site, I wrote my own linking script, adapted to generate loadable PDP11 code. The script itself is this:

phys = 00001000;
  .text phys : AT(phys) {
    code = .;
    . = ALIGN(0100);
  .data : AT(phys + (data - code))
    data = .;
    . = ALIGN(0100);
  .bss : AT(phys + (bss - code))
    bss = .;
    . = ALIGN(0100);
  end = .;
ldaout.cmd (END)                    

We are basically telling the linker we want an output format known as "a.out", and that we will be defining the three classical "C" sections (text, data and bss). The executable entry point is start, so we will have to create a global empty point in our code with that name. We are telling the linker to configure the executable to load at the address 01000 octal (0x200), and to align each section at a 0100 (octal) byte boundary.

Obviously, we are missing a detail. SIMH can't load a.out executables. The ld linker supports a binary output format, which could be easily converted to LDA just adding the header and computing the checksum, but unfortunately ld relocates the code to be loaded at the 0 memory position, and that is not useful for a PDP-11 system. So I had to write a program to convert from a.out to lda format... If you are interested you can get the sources from the git repository. It should build without any problems in any unix-like system. To use the utility to generate a LDA file from an unstructured binary type:
bin2load -f input_file -o output_file -b load_address (in octal)

To generate an LDA file from an a.out executable:
bin2load -a -f input_file -o output_file

Don't specify a load address when you convert an a.out file; that information is in the executable itself... And please take into account the utility can be improved a lot. For instance, there is no boundary checking so you could generate something which would load over the PDP-11 64K barrier. Just treat it as a toy :)

Let's play a little bit

So we have all the tools we need and it is time to give them a try. I have written a pair of very simple assembly sources. The first one is a subroutine to write a single character on the console device. It will loop until the console is ready to receive a byte, will write it and will wait again to ensure it has already been sent. Notice this source is in BSD syntax (it uses the dollar sign to specify immediate arguments).

        .TITLE putconch: send a byte to the system console
        .IDENT "V01.00"

        .GLOBAL _putconch

        XCSR    = 0177564
        XBUF    = 0177566
        TXRDY   = 0x0080
        NRETRY  = 5000


        mov     r1,-(sp)
        mov     r2,-(sp)
        mov     $NRETRY, r1
        mov     XCSR,r2
        bit     r2, $TXRDY
        bne     20$
        dec     r1
        bne     10$
        mov     $2,r0
        jmp     999$

20$:    movb    r0,XBUF
        mov     $NRETRY, r1
30$:    mov     XCSR,r2
        bit     r2, $TXRDY
        bne     40$
        dec     r1
        bne     30$
        mov     $2, r0
        jmp     999$

40$:    mov     $0, r0
        mov (sp)+, r2
        mov (sp)+, r1
        rts     pc

        .end _putconch                        

The second one uses this routine to write a string to the console. This one has the "start" symbol and hence is the entry point for the program:

     .TITLE Say hello on console
        .IDENT "V00.00"

        .GLOBAL start
        .GLOBAL _putconch

        STACK = 0x1000

        mov     $STACK, sp
        mov     $hellom, r1
        mov     $helloc, r2
10$:    movb    (r1), r0
        jsr     pc, _putconch
        dec     r2
        beq     99$
        inc     r1
        jmp     10$

99$:    nop

hellom: .ascii  "Hello world!"
        helloc = . - hellom


To assemble the routines we will use our cross-assembler:

pdp11-aout-as putconch.s -o putconch.o
pdp11-aout-as hellopdp.s -o hellopdp.o

After that we can link the a.out executable and generate the LDA file:

pdp11-aout-ld -T ldaout.cmd hellopdp.o putconch.o -o hellopdp.out
bin2load -a -f hellopdp.out -o hellopdp.lda

And now it is the real moment:

sim> load hellopdp.lda
sim> e pc
PC:     001000
sim> e -m 001000:01100
1000:   MOV #10000,SP
1004:   MOV #1200,R1
1010:   MOV #14,R2
1014:   MOVB (R1),R0
1016:   JSR PC,1040
1022:   DEC R2
1024:   BEQ 1034
1026:   INC R1
1030:   JMP 1014
1034:   NOP
1036:   HALT
1040:   MOV R1,-(SP)
1042:   MOV R2,-(SP)
1044:   MOV #11610,R1
1050:   MOV 177564,R2
1054:   BIT R2,#200
1060:   BNE 1076
1062:   DEC R1
1064:   BNE 1050
1066:   MOV #2,R0
1072:   JMP 1140
1076:   MOVB R0,177566
sim> g
Hello world!
HALT instruction, PC: 001040 (MOV R1,-(SP))

It worked! Not bad for such a patch work!

In the next post we will try to add some C code to the mix...

Sunday, July 15, 2012

Installing RSX-11M 4.6 from scratch - Part 3: Post-installation tasks

This is the last entry of the series depicting a full installation of the RSX-11M operating system from scratch in a simulated PDP-11 created using simh. In the previous two entries we created a baseline system from the distribution tape image and proceeded to generate the operating system itself. In this last entry we will finish the installation by:
  • Tailoring the system image.
  • Providing HELP support.
  • Creating user accounts.
  • Customizing the system startup procedure.
We begin with a pristine system, as we left it in the part 2 of this series, and we will end with a fully configured system ready to be used. However, we won't cover the installation of layered products like language processors (compilers) or decnet support (network).

Tailoring the system image

First thing we can do is to modify the system image to our convenience. In this example we will just change the terminal type for the console (TT0:) so we get a more friendly environment during the boot process. We could configure ALL the system terminals in this moment, but it is not really necessary, since we can do it in the system startup procedure.  This is a really easy thing to do, as seen in the next console log, which begins with the initial bootup of our system.

sim> b rp

  RSX-11M V4.6 BL56   124.K MAPPED

>TIM 13:15 15-JUL-92
>SET /BUF=TI:132.
>ACS SY:/BLKS=1024.
>; This system startup command file (LB:[1,2]STARTUP.CMD) contains a
>; template of commands to initialize the queue print spooler and queue
>; LP0:, initialize the error logger, initialize the DCL CLI, and install
>; the RMS Library and Utilities.  As is these commands are commented out
>; and are not executed.  To include these commands as part of the
>; startup procedure, edit the file to remove the period and semi-colon
>; (.;) comment delimiter from the beginning of each line.  These
>; commands may be useful for initializing the various facilities for
>; your installation or else they may provide a model with which to
>; tailor initialization commands for your particular installation. 

>@ <eof>
>SET /UIC=[1,54]
Enter filename: RSX11M

At the next boot, TT0: will be treated as a VT100 so we will get proper line-editing capabilities when we enter the time and line size. It's a small improvement, but useful anyway...

Providing HELP

The system install process copies the HELP content into [1,2] in the form of a Universal Library, HELP.ULB. To get proper HELP we need to extract the contents of that library. We will be asked if we want complete (FULL) or simplified (BRIEF) help, and if we want to extract also the "introductory" files used to support the basic RSX user manual. In this installation we will ask for FULL help text and we will also get the introductory files. Back in the 70-80s, when the storage space was expensive the system managers would have analyzed the real needs of their user base and the cost of the media used to hold the files. Since we don't have those worries, we will go for the full set...

>SET /UIC=[1,2]
>; BRIEF HELP support for MCR and/or DCL gives the command,
>; function, and syntax without extensive comments.
>; FULL HELP support for MCR and/or DCL gives a full description
>; of each command element rather than just listing them.
>; There is no difference between FULL and BRIEF HELP support
>; for the RSX-11M UTILITIES.
>; Answer YES if you want FULL HELP support for MCR and all
>* Do you want FULL HELP support for MCR? [Y/N]: Y
>; Answer YES if you want FULL HELP support for DCL and all
>; the UTILITIES. 
>* Do you want FULL HELP support for DCL? [Y/N]: Y
>; Answer YES if you want the INTRODUCTORY files used with the manual 
>; 'INTRODuction to RSX-11M'.  This manual includes a full interactive
>; terminal session.  These files are used with that session.  See the
>; section on help files in the Post-System-Generation Guidelines
>; Chapter of the RSX-11M System Generation and Installation Guide for
>; more information.
>* Do you want the INTRODUCTORY FILES? [Y/N]: Y
>; The following options have been selected:
>; The appropriate files will now be extracted from the
>; HELP Universal Library.
>; This file extracts all the files that are necessary when full MCR
>; help is chosen.
>; This file extracts all the files that are necessary when full DCL
>;  help is chosen.
>; This file contains commands to extract all the utility files
>; from HELP.ULB.  These files are extracted if any form of HELP
>; is requested (i.e. full MCR or DCL, or brief MCR or DCL).
>; This file extracts all the files required when either MCR brief
>; or full help was chosen.
>; This is a command file to extract any files that go with either
>; full or brief DCL help.

Right now we have full HELP support, both for MCR and DCL.

Creating user accounts

If we want to use our RSX system in multiuse mode must create the accounts file and define some user accounts. We will define a SYSTEM account, bound to the [1,2] UIC, and a USER account, bound to [200,1]. Additionally, we will provide the USER account with the introductory files extracted from the help universal library. We will create those accounts with no password... Of course this is not a good idea if you want to share your system with someone else...

Creating the accounts file

To create and manage the accounts file we must use the ACNT program, located in the [1,54] UIC. The first step is to create the accounts file itself using the "C" command. This is the corresponding console log:

>SET /UIC=[1,54]


Options are: A - ADD, C - CREATE FILE, D - DELETE, E - EXAMINE,

Enter option: C

Enter maximum number of accounts: 25

Options are: A - ADD, C - CREATE FILE, D - DELETE, E - EXAMINE,

Enter option:

Creating the SYSTEM and USER accounts

Now we can create our two initial accounts.

Enter option: A
Enter account or  for options ( N,N ): 1,2
Password ( <=6 chars. ): 
Default system device ( DDU ): DB0
First name ( <=12 chars. ): 
Last name ( <=14 chars. ): SYSTEM
Enter user CLI (default=MCR): 
Slave terminal? [Y/N]: 
UFD DB00:[001,002]
UFD -- Directory already exists


Enter account or  for options ( N,N ): 200,1
Password ( <=6 chars. ): 
Default system device ( DDU ): DB0
First name ( <=12 chars. ): 
Last name ( <=14 chars. ): USER
Enter user CLI (default=MCR): 
Slave terminal? [Y/N]: 
UFD DB00:[200,001]

UFD -- Directory already exists

Enter account or  for options ( N,N ): <ESC>

Options are: A - ADD, C - CREATE FILE, D - DELETE, E - EXAMINE,

Enter option: ^Z

Operation complete

Copying the introductory files to the USER account

Once we have created the accounts and exited the ACNT program, we can provide the introductory files to the user account.

>SET /UIC=[200,1]


And that's it. We have now two accounts, a privileged one and a user, nonprivileged one.

Customizing the system startup procedure
The system startup procedure, STARTUP.CMD, is located in the [1,2] directory. The installation provides us with a template we can modify to our preference. This is the one I'm using as a starting point. The lines I added or modified are highlighted in bold.

.SETS   RK05    "DK"
.SETS   RL01    "DL"
.IF RK05 EQ "'<sydisk>'" .GOTO END
.IF RL01 NE "'<sydisk>'" .GOTO BIG
.IFT <baslin> .GOTO 50
; This system startup command file (LB:[1,2]STARTUP.CMD) contains a
; template of commands to initialize the queue print spooler and queue
; LP0:, initialize the error logger, initialize the DCL CLI, and install
; the RMS Library and Utilities.  As is these commands are commented out
; and are not executed.  To include these commands as part of the
; startup procedure, edit the file to remove the period and semi-colon
; (.;) comment delimiter from the beginning of each line.  These
; commands may be useful for initializing the various facilities for
; your installation or else they may provide a model with which to
; tailor initialization commands for your particular installation.
.;      ;
.;      ;
        QUE /START:QMG
.;      ; QUEUE LP0:
        QUE LP0:/CR/NM
        QUE LP0:/SPOOL/FLAG:1
.;      ;
        ELI /LOG
.;      ;
.;      ;
.;      ;
.;      ;
.;      ;
.;      ; NOTE - These examples use the top of the GEN partition to create an
.;      ; appropriate sized partition.  You must replace "base" in each of the
.;      ; examples below with the octal address of the base of the new
.;      ; partition.
.;      ;
.;      ; If you wish to use the full-function RMS resident library,
.;      ; use the following commands:
.;      ;
.;              SET /TOP=GEN:-1300
.;              SET /MAIN=RMSRES:36531:1300:COM
.;              INS LB:[1,1]RMSRES.TSK
.;      ;
.;      ; If you wish to use the subset RMS resident library, use the
.;      ; following commands:
.;              SET /TOP=GEN:-600
.;              SET /MAIN=RMSRES:base:600:COM
.;              INS LB:[1,1]RMSRESSUB.TSK
.;      ;
.;      ; If you wish to use the RMSDAP resident library to access RMS files
.;      ;on remote nodes, create a partition and install the following library:
.;              SET /TOP=GEN:-500
.;              SET /MAIN=DAPRES:base:500:COM
.;              INS LB:[1,1]DAPRES.TSK
.;      ;
.;      ;The following RMS utilities may be individually installed as needed:
.;      ;
.;      INS LB:[1,54]RMSDES.TSK
.;      INS LB:[1,54]RMSDEF.TSK
.;      INS LB:[1,54]RMSBCK.TSK
.;      INS LB:[1,54]RMSRST.TSK
.;      INS LB:[1,54]RMSCNV.TSK
.;      INS LB:[1,54]RMSIFL.TSK
.;      INS LB:[1,54]RMSDSP.TSK

        INS LB:[1,54]PIP.TSK
        INS LB:[1,54]EDT.TSK
        INS LB:[1,54]TKB.TSK
        SET /VT100=TT1:
        SET /VT100=TT2:
        SET /VT100=TT3:
        SET /VT100=TT4:
        SET /VT100=TT5:
        SET /VT100=TT6:
        SET /VT100=TT7:
        SET /VT100=TT10:

As you can see, the changes I made were:

  • Enabling the printer spooled.
  • Enablig the error logger.
  • Installing and enabling the DCL command processor.
  • Installing some images I guess I would use: the EDT editor, the PIP utility and the task builder.
  • Configuring the DZ11 lines for a VT100 emulated terminal.

 And that's all! At this moment you should take another backup of your system disk image, and then you could begin installing layered products. If you search the internet, remember most of the tapes in trailing-edge.com are corruptes (not everyone, but most of them). I've found that BP2 2.5 is usable. With a little bit of luck you can find other kits in other sites (to be honest, I can't even remember where did I get mine...). I plan to make more posts about the PDP11 and RSX, and I'll try to publish the installation logs for the layereds I've been able to get. But this will be another post...

Thursday, July 12, 2012

Installing RSX-11M 4.6 from scratch - Part 2: System generation (SYSGEN)

SYSGEN process

We ended the first post of this series with a bootable baseline system. This is a generic system, which has to be tailored to our desired configuration. This process is named SYSGEN (system generation) and consists of three steps or phases. The phases 1 and 2 can be chained together, while the phase 3 has to be executed manually. At the end of SYSGEN we will have a RSX11M system tailored to our hardware configuration and ready to boot.

SYSGEN can also be used to customize the executive ("kernel") characteristics, and to select which features we want to have generated. In this example we will NOT do that kind of advanced configuration. We will choose the "standard function" configuration, which will build a quite complete system. We will also select the option to support DECNET-11 just in case we want/can install it in the future.

SYSGEN phase 1 and 2

The console log that follows corresponds to the preparation and execution of the first two phases of SYSGEN. As before, the user input is highlighted in bold and the log has been minimally edited to improve readability.

As you will see, this SYSGEN run used the autoconfig facility to find out the CSR and interrupt vectors for the configured devices. Take into account that ACF is not always available. For instance, if you SYSGEN from an online system you will have to enter that information by hand. The SIMH show config command will tell you the necessary information... if you are using a simulated system!

Notice: Please take into account the warning note about questions 53 and 54. I found that mistake after publishing this log. I plan to do a new log and replace this one for a corrected one, but in the interim, please, please, don't use DB0: as your CDA device!

sim> boot rp

  RSX-11M V4.6 BL56   124.K MAPPED (BASELINE)

>* PLEASE ENTER TIME AND DATE (HR:MN DD-MMM-YY) [S]: 10:27 12-jul-92
>TIM 10:27 12-jul-92

>SET /BUF=TI:132.
>ACS SY:/BLKS=1024.
>@ <eof>
>set /uic=[200,200]
>; RSX-11M V4.6 BL56   System Generation PHASE I -- Version 04.17
>; 12-JUL-92 10:27:16
>; Big disk distribution kit

>*  1. Autoconfigure the host system hardware? [Y/N]: y
Processor Type:  11/45          Memory Size:  124. Kw


        Floating Point Processor (FP11)
        Extended Instruction Set (EIS)
        Switch Register (SWR)
        Display Register
        Parity Memory

Name    Vector     CSR      Unit    Type      Remark
DLA      160      174400                     
                             0      RL02     
                             1      RL02     
                             2      RL01     
                             3      RL01     
RHA      254      176700                      Mixed Massbus devices
                             0      RP06     
                             1      RP06     
                             2      RM03     
                             3      RM03     
                             4      RM03     
                             5      RM03     
                             6      RM03     
                             7      RM03     
MSA      224      172522                     
LPA      200      177514                     
PRA      070      177550                     
PPA      074      177554                     
YLA      060      177560                     
YZA      300      160100                     

>*  2. Do you want to override Autoconfigure results? [Y/N]: 
>*  3. Do you want to inhibit execution of MCR commands (PREPGEN)? [Y/N]: 
>*  4. Have you made a copy of the distribution kit? [Y/N]: y
>*  5. Are you generating an unmapped system? [Y/N]: n
>*  6. Use an input saved answer file? [Y/N]: n
>*  8. Do you want a Standard Function System? [Y/N]: y
>; Standard Function System - Phase I SYSGEN
>*  9. Name of output saved answer file [D: SYSSAVED.CMD] [S]: 
>; Phase I output saved answers created in file DB0:[200,200]SYSSAVED.CMD;1
>* 14. Clean up files from previous GENs? [Y/N]: y
>* 15. Chain to Phase II after Phase I completes? [Y/N]: y
>SET /UIC=[1,1]
>PIP [1,50]SYSVMR.CMD;*/DE/NM,[1,54]SYSVMR;*
>PIP [11,20]*.OBJ;*/DE/NM,*.UDC;*,*.ICR;*,*.IDS;*,*.PCS;*
>PIP [11,24]*.OBJ;*/DE/NM,*.UDC;*,*.ICR;*,*.IDS;*,*.TTY;*,*.PCS;*
>PIP [11,30]*.LST;*/DE/NM,[11,34]*.LST;*
>PIP [11,10]RSXMC.MAC;*/DE/NM,ICTAB;*,[200,200]SGNPARM.CMD;*

DB0: has 300552. blocks free, 40118. blocks used out of 340670.
Largest contiguous space = 164956. blocks
9459. file headers are free, 1108. headers used out of 10567.

>SET /UIC=[11,10]
>; Target configuration
>* 10. Line frequency:   A- 60 Hz    B- 50 Hz   [D: A] [S]: 
>; The response to the following question specifies the highest interrupt
>; vector.  If you respond with a value less than or equal to 400, SYSGEN
>; will assign the value associated with the highest interrupt vector
>; specified during the Peripheral Section.  Therefore, if your system
>; will include devices that are not specified during the Peripheral
>; Section and which have vectors above 400 (devices such as K-series and
>; certain communication devices), specify that value in the next question.
>* 14. Highest interrupt vector [O R:0-774 D:0]: 
>;     For device configuration: "*" Prints device table, "." Terminates inquiry
>;                               "?" Prints current configuration
>;     Enter devices and number of controllers for devices which require drivers
>;     Current system configuration:
>;     DB=1, DL=1, LP=1, MS=1, NL=1, PP=1, PR=1, CO=1
>;     YL=1, YZ=1
>* 15. Devices [S]: .
>; Processor:11/45   Memory Size:124K,Mapped   System:RSX-11M
>;           Switch Register
>;           Floating Point Processor
>;           Extended Instruction Set
>;           Parity Memory
>; Host configuration
>*  1. Is a line printer available? [Y/N]: n
>*  3. Does the listing/map device have at least 120 columns? [Y/N]: 
>*  4. Assembly listings device (ddu:) [D: "NL:"] [S]: 
>*  5. Map device for Executive and device drivers (ddu:) [D: SY0:] [S]: 
>; Executive Options
>;     Cancel selective marktime support will be included
>;     Answer Y(ES) if the following support is desired
>* 30. Executive Debugging Tool (XDT)? [Y/N]: 
>* 32. Include support for communications products (such as DECnet)? [Y/N]: y
>;  If you will be generating DECnet into this system, be sure you have
>;  read the section on SYSGEN requirements in the  "RSX DECnet Network
>;  Generation and Installation Guide." 
>;  Several  DECnet  features  (eg. remote terminal support,  11S  task 
>;  loading/upline dumping, etc.) require special  consideration during
>* 32A. Include Network Command Terminal support? [Y/N]: y
>;     Checkpointing support will be included

WARNING: The answers to the questions 53 and 54 are WRONG. Terribly wrong. DON'T USE YOUR SYSTEM DISK AS THE CDA DEVICE! If the system crashes the dump will render yout disk unbootable! Use whatever else device you have configured. A tape device like MS0: can be a good option. Answer MS0: to the question number 53, and you will get reasonable defaults for the CSR in question 54.

sim> boot rp
>* 53. Enter CDA memory dump device mnemonic (ddu:) [S R:3-4]: db0:
>* 54. Enter CDA memory dump device CSR [O R:160000-177700 D:176700]: 
>* 56. RT-11 emulation support? [Y/N]: 
>; Terminal driver options
>;      The Full Duplex Terminal Driver will be included.
>; System Options
>;     Answer Y(ES) if the following support is desired
>* 2A. Include support for the IP11 Industrial I/O Subsystem? [Y/N]: n
>* 11. What name would you like to give your system [D: RSX11M] [S R:0-6]: 
>* 12. Do you want SPM-11 support? [Y/N]: 
>;     Thinking ...
>; End of Executive option generation at 10:28:24 on 12-JUL-92
>; Peripheral configuration
>;     Parameters  appearing  in  square  brackets  "[...]"  can  only be
>;     specified  for  the  first  controller  of  a  particular  device.
>;     Parameters appearing in parentheses "(...)" only need be specified
>;     if   the  indicated  option  is  present  on  the  target  system.
>;     The   default  for  loadable  drivers  has  been  set  to  *TRUE*.
>;     To override this setting enter R (resident) as the first parameter
>;     for    the    first    controller   of   the   specified   device.
>; A/D and Laboratory devices: None specified
>; Interprocessor communication devices: None specified
>; Unit record devices: LP, PR, PP
>; Disks: DB, DL
>; Tapes: MS
>; Non-physical (pseudo) devices: CO, NL, TI, CL, LB, SY
>; Terminal interface devices: YL, YZ
>SET /UIC=[1,24] ! Creating TTDRVBLD.CMD
>SET /UIC=[11,10]
>; End of interrupt vector area has been set to 400
>; Create Executive build files
>; Start of Executive assembly at 10:28:31 on 12-JUL-92
>SET /UIC=[11,24]
>; End of Executive assembly at 10:29:26 on 12-JUL-92
>; Start of MCR (subset) assembly at 10:29:26 on 12-JUL-92
>SET /UIC=[12,24]
>; End of MCR (subset) assembly at 10:29:43 on 12-JUL-92
>; Start of device drivers assembly at 10:29:43 on 12-JUL-92
>SET /UIC=[11,24]
>; End of device drivers assembly at 10:30:34 on 12-JUL-92
>; Prepare for task building
>SET /UIC=[1,24]
>PIP RSX11M.OBS=[11,24]*.OBJ
>LBR RSX11M/CR:100.:1010.:128./-EP=RSX11M.OBS
>LBR TTDRV/CR:30.:380.:64.=TTDRV
>PIP [1,24]MCR.OBJ/NV/NM=[12,24]*.OBJ
>PIP [12,24]*.OBJ;*/DE/NM
>; Clean-up extraneous object files
>PIP RSX11M.OBS;*/DE/NM,TTDRV.OBJ;*,[11,24]*.OBJ;*,*.TTY;*
>;  End of SYSGEN phase I at 10:30:37 on 12-JUL-92
>;      -- Chaining to [200,200]SYSGEN2.CMD
>SET /UIC=[200,200]
>; RSX-11M V4.6 BL56  System Generation PHASE II -- Version 3.02
>; 12-JUL-92 10:30:37
>; Big disk distribution kit SYSGEN version 04.17 for RSX-11M BL56  
>; Continuation from SYSGEN PHASE I done on 12-JUL-92 at 10:30:37
>; SYSGEN assumes that the map disk is already mounted in SY0: with
>; UFD [1,34].
>; Standard Function System - Phase II SYSGEN
>; Phase II output saved answers created in file DB0:[200,200]SYSSAVED.CMD;1

DB0: has 300182. blocks free, 40488. blocks used out of 340670.
Largest contiguous space = 164956. blocks
9448. file headers are free, 1119. headers used out of 10567.

>; Start of library build at 10:30:38 on 12-JUL-92
>SET /UIC=[1,1]
Module "ANSPAD" replaced
Module "ASSLUN" replaced
Module "BIGBUF" replaced
Module "CLOSE " replaced
Module "CONTRL" replaced
Module "CREATE" replaced
Module "DIRECT" replaced
Module "FCSTYP" replaced
Module "GET   " replaced
Module "GETSQ " replaced
Module "OPEN  " replaced
Module "OPENR " replaced
Module "OPFID " replaced
Module "OPFNB " replaced
Module "PARSFN" replaced
Module "POINT " replaced
Module "PUT   " replaced
Module "PUTSQ " replaced
Module "RDWAIT" replaced
Module "RETADR" replaced
Module "RSTFDB" replaced
Module "RWBLK " replaced
Module "RWLONG" replaced
Module "WATSET" replaced
Module "WTWAIT" replaced
Module "WTWATD" replaced

>SET /UIC=[1,24]
Module "DV2OV " replaced
Module "SPSOV " replaced
Module "STSUB " replaced
Module "SX1OV " replaced
Module "SX2OV " replaced
Module "SX3OV " replaced
Module "SX4OV " replaced
Module "ST5OV " replaced
>; End of library build at 10:30:43 on 12-JUL-92
>; Start of Executive task build at 10:30:43 on 12-JUL-92
>PIP [1,34]*.*;*/DE/NM
>; End of Executive task build at 10:30:47 on 12-JUL-92
>; Start of system image creation at 10:30:47 on 12-JUL-92
>SET /UIC=[1,54]
>; End of system image creation at 10:30:47 on 12-JUL-92
>; Start of full duplex terminal driver task build at 10:30:47 on 12-JUL-92
>SET /UIC=[1,24]
>; End of full duplex terminal driver task build at 10:30:49 on 12-JUL-92
>; Start of loadable driver task build at 10:30:49 on 12-JUL-92
>; End of loadable driver task build at 10:30:51 on 12-JUL-92
>; Start of common block task build at 10:30:51 on 12-JUL-92
>; Build the ANSLIB flavor of the FCS resident library common
>; SYSGEN assumes that the map disk is mounted in SY0: 
>; and that UFD [1,34] exists on it.
>; Creating the task build .CMD and .ODL files in SY:[1,24]
>; End of common block task build at 10:30:53 on 12-JUL-92
>; Start of privileged task build at 10:30:53 on 12-JUL-92
>;      Note that the following tasks will be built for your system:
>;       BOO    DMO     COT     FCPMDL  ICP     INI     INS     
>;       PMT    MCR     DCL     MOU     QMG     QMGCLI  QMGPRT  
>;       LPP    F11MSG  MTAACP  SAV     TKTN    UFD     LOA     
>;       UNL    PMD     RMD     SHF     ACNT    BYE     HEL     
>;       BRO    SHUTUP  ACS     ERRLOG  ELI     
>; It is assumed that [1,1]FCSRES.STB is an ANSLIB version of the FCS
>; resident library
>; Creating the task build .CMD and .ODL files in SY:[1,24]
>; End of privileged task build at 10:32:10 on 12-JUL-92
>; Start of system VMR at 10:32:10 on 12-JUL-92
>SET /UIC=[1,54]
>INS $BOO;-1
>INS $VMR;-1
VMR -- *DIAG*-Partition reduced to executive common size
VMR -- *DIAG*-Partition reduced to executive common size
VMR -- *DIAG*-Loadable driver larger than 4K
VMR -- *DIAG*-Installed tasks may no longer fit in partition
EXCOM1 117734 120000 014700 MAIN COM
EXCOM2 117670 134700 010200 MAIN COM
LDRPAR 117624 145100 002600 MAIN TASK
TTPAR  117260 147700 040000 MAIN TASK
DRVPAR 116734 207700 013300 MAIN SYS 
       116670 207700 002100 SUB  DRIVER - DB:
       116570 212000 002100 SUB  DRIVER - DL:
       116470 214100 001100 SUB  DRIVER - LP:
       116370 215200 004500 SUB  DRIVER - MS:
       116270 221700 000500 SUB  DRIVER - PP:
       116170 222400 000300 SUB  DRIVER - PR:
       116070 222700 000300 SUB  DRIVER - CO:
SYSPAR 116024 223200 011700 MAIN TASK
FCSRES 115760 235100 040000 MAIN COM
FCPPAR 115714 275100 024200 MAIN SYS 
GEN    115650 321300 436500 MAIN SYS 
LDR... 13.02  117510 LDRPAR 248. 002600 LB0:-00104414 FIXED
TKTN   05.00  111164 SYSPAR 248. 011700 LB0:-00110230
...RMD 03.00  114510 GEN    225. 027200 LB0:-00112200
F11MSG 13.00  113600 GEN    200. 005700 LB0:-00110247
MTAACP 15.01  113464 GEN    200. 014700 LB0:-00111734
...DMO 04.00  114144 GEN    160. 014600 LB0:-00107317
MCR... 07.00  112440 SYSPAR 160. 011700 LB0:-00110133
...DCL 5.04   112324 GEN    160. 051500 LB0:-00110610
...MOU 27.01  111644 GEN    160. 037700 LB0:-00110465
...MCR 07.00  111300 GEN    160. 020000 LB0:-00110274
F11ACP 06.01  115534 FCPPAR 149. 024200 LB0:-00107413
ERRLOG 2.00   114030 GEN    148. 040000 LB0:-00112575
PMT... 2.00   113004 GEN    148. 006300 LB0:-00107573
COT... 2.0    115420 GEN    145. 013600 LB0:-00107336
PMD... 08.01  112670 GEN    140. 016200 LB0:-00111625
SHF... 6.00   111414 SYSPAR 105. 011700 LB0:-00111665
...INS 9.01   113120 GEN    100. 034600 LB0:-00110062
...SAV 05.00  111530 GEN    100. 033300 LB0:-00112116
...UFD 05.00  111050 GEN    100. 005700 LB0:-00110261
QMG... 03.04  115304 GEN     75. 031700 LB0:-00110543
PRT... 2.0    114740 GEN     70. 001100 LB0:-00110243
LP0    06.00  114624 GEN     70. 014500 LB0:-00111522
...ACS 3.00   114374 GEN     70. 005000 LB0:-00111722
...AT. 9.0    113234 GEN     64. 060000 LB0:-00107665
...QUE 05.01  115170 GEN     50. 020100 LB0:-00111327
...PRI 05.01  115054 GEN     50. 020100 LB0:-00111327
...BOO 06.02  114260 GEN     50. 022000 LB0:-00107251
...ELI 1.00   113714 GEN     50. 017300 LB0:-00112641
...MAG 03.00  113350 GEN     50. 031500 LB0:-00110162
...LOA 04.02  112554 GEN     50. 032600 LB0:-00111570
...HEL 04.00  112210 GEN     50. 024100 LB0:-00112451
...BYE 07.00  112074 GEN     50. 012700 LB0:-00111705
...BRO 07.00  111760 GEN     50. 030400 LB0:-00112516
...UNL 4.02   110734 GEN     50. 024500 LB0:-00111471
LP0:    Loaded
PP0:    Loaded
PR0:    Loaded
DB0:    Loaded
DB1:    Loaded
DL0:    Loaded
DL1:    Loaded
DL2:    Loaded
DL3:    Loaded
MS0:    Loaded
CO0:  TT0:
TT0:    Loaded
TT1:    Loaded
TT2:    Loaded
TT3:    Loaded
TT4:    Loaded
TT5:    Loaded
TT6:    Loaded
TT7:    Loaded
TT10:    Loaded
CL0:  TT0:
LB0:  DB0:
SY0:  DB0:
>; End of system VMR at 10:32:12 on 12-JUL-92
>; An alternate version of SYSLIB.OLB, the system object library, was
>; created to provide support for ANSI magtape/FCS big-buffering (which
>; you selected in Phase I).  You may want to rebuild DMP, FLX, PIP, and 
>; VFY in Phase III to use ANSLIB.OLB.  Note, however, building tasks with
>; ANSLIB.OLB causes an increase in the size of tasks as compared to the
>; SYSLIB.OLB version of the tasks.
>; A memory resident library of FCS routines (which you selected in Phase I)
>; was built to help reduce task memory requirements.  You may want to rebuild
>; tasks in Phase III to link to the FCS resident library.
>; When SYSGEN finishes, boot in your target system, and save the system with a
>; bootstrap.  For example:
>;     >BOO [1,54]RSX11M
>;      RSX11M V4.6 BL56 
>;      >TIM 12:00 14-JUN-85
>;     >SAV /WB
>;      RSX11M V4.6 BL56        124.K   MAPPED
>;     >RED DB:=SY:
>;     >RED DB:=LB:
>;     >MOU DB:RSXM56
>;     >@DB:[1,2]STARTUP
>;     .
>;     .
>;     .
>; End of SYSGEN phase II at 10:32:12 on 12-JUL-92
>SET /UIC=[200,200]
>@ <eof>
>boot [1,54]rsx11m
RSX11M V4.6 BL56  


  RSX-11M V4.6 BL56   124.K MAPPED

>TIM 10:35 12-JUL-92
>SET /BUF=TI:132.
>ACS SY:/BLKS=1024.
>; This system startup command file (LB:[1,2]STARTUP.CMD) contains a
>; template of commands to initialize the queue print spooler and queue
>; LP0:, initialize the error logger, initialize the DCL CLI, and install
>; the RMS Library and Utilities.  As is these commands are commented out
>; and are not executed.  To include these commands as part of the
>; startup procedure, edit the file to remove the period and semi-colon
>; (.;) comment delimiter from the beginning of each line.  These
>; commands may be useful for initializing the various facilities for
>; your installation or else they may provide a model with which to
>; tailor initialization commands for your particular installation. 
>@ <eof>

Note about VMR

As seen in the log, the SYSGEN procedure executes automatically VMR to generate the bootable system image. This automatic execution happens only when you are SYSGENing a baseline system. If you have to SYSGEN again a running, online system, SYSGEN does not execute VMR for you. This is an important difference between 11M and 11M+. In that case, you have to add some magic to get a correct VMR execution:

>SET /UIC=[1,54]

Then you can soft-boot the new system and save the image to disk:


SYSGEN Phase 3

During phase 3 we can build/rebuild the system tasks, both privileged and non-privileged. This will allow us to patch or modify the tasks, or add tasks which are not built by default. You will probably want to build at least EDT (unless you like the default editor, EDI). In this example we will be building ALL the non-privileged tasks. If you are using real hardware you will not want to do this, since it will take a lot of time; using emulated hardware that is a non-issue, so I'll built all the stuff...

>SET /UIC=[200,200]
>; RSX-11M V4.6 System Generation PHASE III -- Version 04.06
>; 12-JUL-92 10:35:22
>*  1. In what UIC is SGNPARM.CMD if not in [200,200] [S]: 
>; Big disk distribution kit SYSGEN version 04.17 for RSX-11M BL56  
>; Continuation from SYSGEN PHASE I done on 12-JUL-92 at 10:30:37
>*  2. Are you building nonprivileged tasks? [Y/N]: Y
>*  4. Enter map device (ddu:) [D: NL:] [S]: 
>;      For nonprivileged tasks:  * -- Prints the table of tasks
>;                                % -- Builds all tasks
>;                                . -- Terminates inquiry
>;      Enter responses separated by commas.  All responses need
>;      not fit on one line.
>; Example:  BAD,LBR,PIP,.
>* 5. Enter task name(s) [S]: *
>;      Nonprivileged tasks:
>;              BAD     BRU     CDA     CFL     CMP
>;              CRF     DMP     DSC     EDI     EDT
>;              FLX     FMT     FTB     IOX     LBR
>;              MAC     PAT     PIP     RPT     SLP
>;              TKB     VFY     VMR     ZAP
>* 5. Enter task name(s) [S]: %
>; [1,1]FCSRES.STB can be used to build many tasks
>; so that those tasks link to the FCS resident library
>*  6. Use [1,1]FCSRES.STB when building those tasks? [Y/N]: 
>; LB:[1,1]ANSLIB.OLB can be used to build certain tasks
>; so that those tasks have ANSI/big-buffered support
>*  7. Use LB:[1,1]ANSLIB.OLB when building those tasks? [Y/N]: 
>* 11. Pause to edit any task build .CMD or .ODL files? [Y/N]: 
>* 12. Delete task build .CMD and .ODL files after task building? [Y/N]: 
>SET /UIC=[1,24]
>; Creating the task build .CMD and .ODL files in SY:[1,24]
>SET /UIC=[200,200]
>@ <eof>


Enter minutes to wait before shutdown: 0
OK to shutdown? [Y/N]: Y
 All further logins are disabled 
 12-JUL-92 10:37 System is now shutting down -- RSX11M


ACS -- Checkpoint file now inactive

DMO -- System disk being dismounted
DMO -- SYSTEM  dismounted from DB0:    *** Final dismount initiated ***
10:37:40  *** DB0: -- Dismount complete

SHUTUP operation complete

HALT instruction, PC: 124122 (CLRB @#177776)
sim> quit
And this is it! Now we have a bootable, complete RSX11M system. The only missing things are the HELP support, the configuration of the STARTUP.CMD procedure and the creation and management of the accounts file, which we will leave for the third and last post of the series. Meanwhile, I suggest to make another copy of the RP06.000 file so we can revert to a pristine system just in case we break it badly during our experimentation...


I've added a pair of improvements and corrected a quite big error as suggested by Oleg Safiullin. Thanks, Oleg for your help.