Timesharing on the MIT Weather Radar PDP-8/IX

Correction. The PL/1 terminology for heap storage was controlled, not based. Don’t know where I got based from. Maybe coursework, maybe I just misremembered from the earlier PL/1 machine problems I was assigned.

Some of the above details are from memory, some I just reconstructed looking at the source code.

I’m a little stunned that there is this much, or really any, interest in code that ran for a few years on a single oddly-modified PDP-8 at a lab that hasn’t existed for thirty years. It’s not like this is a historically significant system like ITS.

I’ve been doing a series of technical projects in my retirement, to keep busy and exercise my aging brain, and this little history is just the latest (here’s one of my favorite projects). I have very little experience posting to forums and don’t really know what people might like to read. My posts here have been all over the place—stormy days, ribbon cables, Rayleigh scattering, open-collector bus drivers, the Beach Boys, process scheduling. I can see that I have over 500 views, but I don’t know how many people that is or how much of what I’ve written is interesting for a retro computing audience.

There’s no need to bundle the messages—I write everything in a single Word document and then copy/paste to the forum.

As for an updated emulator: I’ve used SIMH on my PiDP-8 and PiDP-10 replicas, but I have no interest in updating its PDP-8 emulation. I do have a great PDP-8/I emulator that I wrote many years ago and that I might be interested in updating. It emulates a completely functional front console (lights and switches), a teletype or VT100, and RK05 disc drives, and has a built-in PAL8 assembler. It also runs the OS/8 package from the PiDP-8 group. It’s a desktop application, however, and only runs under Windows. It’s written in Visual Basic 6.0, and is about as non-portable as it gets.

5 posts were split to a new topic: Preservation: scanning and OCR tactics for old printouts

I would suggest bundling up all of @bill75’s messages here, along with the scanned listings, (and possibly machine readable files from OCR and/or typing (and possibly an updated emulator able to run it all)). All in due time, and with permission of course.

I have done similar things before, but this one here is special because of the personal narrative.

EDIT: Find similar things here: https://retrocomputingforum.com/t/software-time-capsules

2 Likes

I think it’s interesting perhaps not primarily for the technical aspects, but for the personal aspects. It’s a small window into someone’s view of computing in the 1970s. I have talked to many PDP-10 people, but no one has done a similar writeup about their experiences. Take ITS as an example; the technical aspects are now well preserved and are ready to spring back to life, but visiting them is kind of like walking the empty halls of Tech Square. The people are all gone.

Any OS that implements multiple processes (threads) must decide which one gets control of the CPU if more than one are ready to run. In a previous post I described the mechanisms; now let’s see how they were used.

Throughout the development of the IX modifications and RADAR timesharing, the big picture of why I did things was always a balance between doing something valuable for the goals of weather radar research, and a personal hunger to go beyond the conceptual level of coursework by building working systems. I wanted my own Little Deuce Coupe and I wanted the thrill that all engineers get watching their creations come to life. Keep this in mind in the following.

I had learned some of the key concepts of processor management in coursework, but not the implementation details or design tradeoffs in a real-time environment. I had learned about timeslices, and that a program might be I/O-bound or compute-bound. I had read and absorbed PDP-1 Multiprocessing.

In my case I knew that I’d be running a variety of applications with a variety of CPU needs. DECtape needed immediate service to switch from search mode to read/write mode. The digital sweep integrator needed some significant computation, on time, every 64 ms, to accurately capture radar data. The various character-oriented output devices needed only a tiny number of cycles every 100 ms (20 ms for the punch) to keep busy. The process listening for commands on the console TTY should be reasonably responsive to the human operator. The storage scope was always ready but writing text one dot at a time needed lots of cycles. Other stuff I’ve now forgotten needed cycles.

Another set of tradeoffs were imposed by the limited speed and memory size of the PDP-8, and the limitations of the development environment—line editor, TTY, no debugger. That’s really a 21st century perspective however—as a 21-year-old kid in 1975, 16,384 words and 300,000 instructions/sec seemed like a lot.

The need for a process priority system was obvious. Most of the details were obvious but some were less so, and some were determined empirically.

The most obvious detail is that the highest priority process that is ready to run should be the one running, and the highest priority process waiting on a semaphore queue should be taken first.

What to do with processes of equal priority was less obvious. As I noted in the earlier post, the process scheduler could place a process on a queue/list either before or after processes of equal priority. I implemented this choice because it was essentially free. It added no instructions to the scheduler or caller choosing after, and one instruction to callers choosing before. So how to use this capability?

There are three places in the kernel where the process scheduler (CHPCB) was called, which I’ll identify by octal address:

At 0464 in the traffic controller, when a process switch occurs because the process at the top of the ready list has higher priority than the running process, the soon-to-be-former running process is scheduled back on the ready list. Here the after choice is made. This is pretty obvious—it results round-robin scheduling of equal priority processes.

At 1222 in P, a process becoming blocked is scheduled after those of equal priority. Another obvious choice—this results in first-come first-served scheduling. In practice, however, I don’t think there was ever a case of multiple processes waiting on the same semaphore. But it was important to me to get the philosophy right and it didn’t cost anything to do so.

At 1246 in V, a process becoming ready was scheduled before those of equal priority. I think the idea was that when an event occurs that unblocks a process, it should get as close to immediate service as higher priority processes would allow. But looking back I’m not sure I like this choice. The newly unblocked process would be in the high-priority I/O-bound class, and therefore equal-priority processes must also be I/O bound and have found themselves on the ready list be being recently unblocked. This seems to violate the first-come first-served principal. It may be, however, that I made this choice after observing system behavior and concluded that it mostly works better in practice. We’ll never know for sure.

The I/O bit in process priority involved less obvious design choices. It certainly makes sense in general, and judging the status dynamically is better than fixing it at assembly time. Compute-bound processes must be OK with sharing the CPU and getting whatever cycles are available. Servicing I/O bound processes at high priority keeps the devices busy at generally low cost in cycles. But there are a lot of ways to do it. Should I/O priority be absolutely higher, or should it just be bumped a little? Should priority have one value for I/O and one for compute, determined by an argument to FORK? Should the time slice be adjusted in addition to priority? Are there better ways to judge I/O vs compute? Here perhaps is where simplicity ruled, given the limitations of the PDP-8 instruction set and the environment for writing code.

Coming posts, in addition to a complete scan of the PMIO code:

  • PDP-8 programming notes: the need for and use of self-modifying code; passing arguments on a single-accumulator machine; catch and throw in assembly language.

  • The supervisor call system

  • Heap storage

  • Character I/O—Dijkstra on a PDP-8

  • Reentrant code and the stack frame system

  • Interrupt driven DECtape device driver

  • Teaching PDP-8 timesharing at Bowdoin College—why on earth did I do that in the 21st century?

2 Likes

I’ve been thinking about the list of future posts I posted yesterday, and the comment Lars made about personal stories being more interesting. And indeed the word that comes to my mind after reviewing my list of future posts is: BORING. Who wants to read about yet another heap storage system, or DECtape driver, right? The character I/O stuff with ring buffers and P/V ops might be of some historical interest, and I think that my Bowdoin experience teaching PDP-8 timesharing would be a nice epilog to the whole topic, but otherwise I’ve got a new plan.

I’ll post the scanned source code of the rest of PMIO. I’ll discuss character I/O, and leave it at that. If anyone has questions or comments about it, great, I’ll answer. Then I’ll write up my grand 1974 adventures in the GATE project, with WR73 and the TI-980 on the Gillis. I’ll be in the Atlantic Ocean 800 miles off the coast of Africa writing code in hex machine language in the middle of the night, in secret. Among other adventures. That should be worth your time to read.

I won’t be able to post the scanned code, and discuss character I/O, until I get back to my desk on Wednesday. In the meantime I may start with some of the GATE story.

Since I’ve already written the post about PDP-8 programming notes, and hopefully it’s not too boring, here it is.

1 Like

Here are some PDP-8 programming techniques that PDP-8 programmers will know and ambitious readers examining my source code may notice.

Self-Modifying Code

Very early stored program machines often relied on self-modifying code for fundamental operations. Consider, for example, the early 1950s IBM 701, one of the first commercial computers, with a vacuum tube CPU and, at first, Williams tube memory. The 701 was an 18-bit machine with a 12-bit direct address in the instruction, and no other way to generate an address. Using an address computed at run time, however, is a necessary capability of stored program machines. For runtime addressing it had the STORE ADDRESS instruction, which would store the low 12 bits of the accumulator in the low 12 bits of an instruction, leaving alone the high 6 bits, which contained an op code. Then the modified instruction would be executed.

By the 1960s index registers and indirect addressing mostly did away with that trick, but the PDP-8 retained the need for self-modifying code for extended addressing and other purposes. To access memory in a field other than the current instruction field (CIF, the one containing the instructions being executed) you’d execute a CDF (change data field) instruction, which contained the 3-bit field address in the instruction itself. If you knew the desired field at assembly time you were all set. But if the field was computed at runtime you’d need to create a CDF instruction for subsequent execution.

A good example in my source code is the supervisor call (SVC) dispatcher at address 0326. The SVC instruction could be used in any field, and the SVC dispatcher, and the handler it dispatched to, would need to pick up arguments in that field. So the dispatcher modified the subroutine SVCDF at 0354 to do nothing more than just execute a CDF to the field that executed the SVC. The dispatcher (at 0336) and handler (e.g. FORK at 1255) could then call SVCDF as needed.

Another important use of self-modifying code involved extended arithmetic element (EAE) instructions. Multiply, divide, and shift were two-word instructions that got one of their operands from the second word. If that operand was an assembly-time constant, you were all set. If it was computed at runtime, you’d have to modify the instruction.

Argument Passing on a Single-Accumulator Machine

The PDP-8 was a triumph of simplicity and elegance. It was a single-accumulator machine with no instruction to load the accumulator from memory, and amazingly you didn’t really need one. Instead you used TAD (twos complement add, memory to AC) to load the accumulator, which worked because it was easy to keep AC at zero just before needing to load it. (It was called twos complement add because after years of ones-complement designs, people finally realized that von Neumann was right all along).

By convention, subroutines were called, and returned, with AC = 0 unless there was an argument or return value in AC. With one accumulator and no modern stack, a common convention for subroutines needing multiple arguments was to place them in memory just after the JMS that called the subroutine. This worked cleanly with JMS (jump subroutine), ISZ (increment memory and skip if zero), and indirect addressing.

This style can be seen in CHOINT at 0662, which takes only one argument but expects it in the word after the JMS, rather than the AC, because the argument is always an assembly-time constant. JMS stores the return address at 0662 and jumps to 0663. Since the return address is the address of the argument, it is fetched at 0663 with TAD I CHOINT. Then ISZ CHOINT at 0664 increments the address to skip the argument and point to the true return address. ISZ, also used for counting, never skips with this style because nobody puts a JMS at 7776. Finally CHOINT returns with JMP I CHOINT at 0636. If there were more arguments, they’d be fetched the same way. TAD, ISZ, and DCA (deposit and clear AC) work together to do much of the heavy lifting on the PDP-8. TAD and ISZ have many uses unrelated to adding and counting, part of the elegance of the instruction set.

Catch and Throw

Another common convention was that many subroutines would return to the address after the JMS if an error was detected, and skip over that address if successful. The non-skip return was in effect what we’d recognize as a throw, because the caller could choose to catch the error or just pass it along (rethrow) to its caller. To catch the error, the caller would put a JMP to an error handler following the JMS. To rethrow, it would simply put the standard JMP indirect through its return address there, causing another non-skip return. Then the caller would ISZ its return address to skip-return if successful. The non-skip returns would be passed up the call chain until someone wanted to catch the error.

A great example in the kernel starts at the SVC dispatcher at 0326. SVC skip-returns if successful, doing so with the ISZ at 0350. It calls a handler with a JMS at 0345, expecting a skip-return if the handler is successful. One such handler is FORK at 1252, which calls ALOC at 1271 to get storage for a new PCB. If ALOC fails, its non-skip return goes to 1272, which just passes the non-skip back up to SVC, which in turn passes it back to its caller.

In some circumstances the non-skip return should be logically impossible, i.e. a bug. In that case you’d put a JMS to a global fatal error handler, or maybe just a halt instruction (JMS rather than JMP so the global hander would know the address of the offending call).

2 Likes

I think there are people who definitely do, but another thing to consider is telling the same story, but making sure to give us the context! Presumably there’s a reason you wrote another heap storage system, or wrote your own DECtape driver, and that reason (coupled with information about how the implementation satisfied your particular needs) greatly magnifies the value of the post for those of us who are trying to understand the zeitgeist of computing environments of the past.

Don’t underestimate the value of even the most crunchy and “boring” details, though; not every post has to be for every audience. I’m loving this thread!

4 Likes

I want to know how to write file systems,so every OS book I have spends the first 97 chapters on time-sharing and the last one on a file system. Catch and throw and the Unix time sharing model with virtual memory makes things so complex and a C compiler is required is for that as well.Even real time OS’s are not that clean.

It is good to see a another way of time sharing, and that “boring” details makes it interesting. A index register is required but not local variables for time sharing. Nice to see the computing world before C,
and goto’s are not forbidden.

Thanks for the feedback. Really appreciated!

3 posts were split to a new topic: On the use of goto in C programming

(I have moved a few comments into a new topic. Please, commenters, if you are bringing new ideas into play, create a new topic for the new idea and quote freely from other topics to tie things together. We’ve seen it done - it could be done more often. Ideally, your new idea can be the start of an interesting discussion.)

This particular topic is an unusual one: it’s a series of long form posts from one person who is telling their (fascinating) personal history. It may well spark responses - I hope it does - but please consider the context each time: is a new topic a better idea than a response here?

1 Like

I have alluded to coursework in previous posts. Since I’m away from my desk and source material until Wednesday, I’m going to take this opportunity to write about what it was like for me as a 17-year-old freshman at MIT in 1971 encountering computers for the first time.

I was intrigued by computers throughout high school and desperate to learn what they were. My high school didn’t have one, and no one I knew—teachers, extended family—was able to help. I tried reading books on digital electronics, which had diagrams of logic gates made out of diodes. I knew what a diode was but couldn’t make sense of the circuits.

By senior year I had built a 4-bit binary counter of my own design. I “invented” a flip flop using a transistor, electromechanical relay, some resistors, and a battery. I wired four of them together to make the counter. They had to be electrically isolated by the relays, each with its own battery, because my understanding of circuits was limited to the middle-school model—make a complete circuit and current will flow, series and parallel wiring.

So I arrive in Cambridge in the fall of 1971 and met other freshmen who’d been programming for years. They knew this thing that they called Fortran, and that somehow a “compiler” was involved. I felt hopelessly behind, and I’d never be able to catch up.

In the electrical engineering department, course 6, you had a choice of concentrating on electronics (analog circuits, signal processing, and the like), course 6.1, or on computers (digital circuits, programming languages, Turing machines, etc.), course 6.3. I knew some electronics (or thought I did), felt outclassed in computers, so I was going to be 6.1. Still, my hunger to learn something about computers was insatiable, so I signed up for an introductory programming course that taught a subset of PL/1.

I loved the course and nailed the machine problems. But the only available next step would be to take 6.251, Systems Programming, spring term 1972. It had a reputation for being very challenging, and freshman were strongly advised to stay away. So instead I signed up for Thermodynamics in the chemistry department, which seemed like a fascinating topic. The first lecture, which was basically just going over the syllabus, was completely over my head. I immediately dropped the course and signed up for 6.251. Life-changing decision.

6.251 was taught by professor John Donovan, a dynamic, charismatic lecturer (and, much later, convicted felon, which you can read about in the link). He was famous for his anti-smoking crusade. At the time it was common for students and professors to smoke in class, but he stated up front that if you did you would fail the course. No one doubted him.

6.251 taught the basics of systems programming as it was understood in the IBM mainframe, batch processing era. Assemblers, linkers, loaders, compiler and OS basics. It introduced me to IBM 360 assembly language. I struggled with some of the concepts but again nailed the machine problems. I remember, for example, being completely confused about the difference between a macro and a subroutine. The concepts only became completely clear when I started writing real code on the PDP-8 at Weather Radar.

The final machine problem in 6.251 was to write an operator precedence parser. We were given the algorithm and had to write PL/1 code to implement it. At that point I had no prayer of coming up with the algorithm on my own, and didn’t fully understand how and why it worked, but I had no trouble writing the code. My solution was crisp, clean, and concise. I loved just looking at the listing. I remember the TA handing back my listing with a perfect grade, and giving me a dirty look as he did so, because my code had not a single comment. You weren’t marked off for that, but you should have been. I felt that comments would just clutter up my beautiful code, and that the grad student TAs surely should be able to understand it without comments. I learned just how wrong I was the hard way, when I couldn’t understand my own code after just a few months.

One of the more important lessons about comments that I learned the hard way is that it’s more important to say why the code is written that way than what it does. Several times in my posts about the RADAR code I had to admit that while I can see what I was doing, I couldn’t tell you why I did it. I hadn’t yet learned that lesson.

Eventually I did switch to course 6.3. I took a lot of straight electronics courses and loved them, but software engineering would become my professional life.

4 Likes

Travelling today so no writing, but here’s a party favor for you. It’s the PDP-8 Green Card I gave my Bowdoin students when I was teaching PDP-8 assembly language (named after the IBM 360 Green Card summarizing the instruction set). It’s designed to be printed double-sided and tri-folded. I printed it on actual green cardboard stock for the kids. I have the equivalent item from DEC (on white stock) but I think mine is better.

3 Likes

Nice card. Interesting to see a RF08 disc, and no DEC TAPE, and better interrupt service,

Bill was a senior when I was a freshman at MIT (1974-75). He got me hired as an “undergraduate researcher” at Weather Radar starting in the summer of 1975. That was my first technical job. He (and to some extent Speed) proceeded to mentor, teach and expose me to things that became fundamental to my career success in many of the same ways that he describes in his postings here. I will add some comments about my experiences at Weather Radar. The most important of those was when he let me know about a job running a PDP-8 based weather radar system at Palmer Station, Antarctica. I got the job, and that led in turn to the future opportunities that shaped my career.

Speed had a delightful sense of humor. Rather than “turn on the radar”, he’d say “Let’s turn on the Powerful Tool”, mocking the zillion scientific papers about the latest “powerful tool” for . He had two marvelous papers in his file cabinet. One was the Official NOAA List of Acronyms, which went on for 50+ pages of fine print. The other was a study of exactly how far the Green Building would bend backwards as the sun heated the front but not the back of the building, as measured by a very sensitive tilt-o-meter installed at the top of the building with Speed’s help.

Dr. Austin was not in my experience ever frivolous. I secretly wrote and installed a cookie program on the PDP-8, such that when the system was brought up it would type out “I WANT A COOKIE!”. Any input other than “COOKIE” would cause it to print “I WANT A FUCKING COOKIE!”. Sophomoric (literally; I was a soph) humor at its best. I figured Bill would enjoy this. Well, just my luck, it was Dr. Austin who powered up the machine, and she Was Not Amused. She had to call Bill who had to find me to explain. I expected to be fired, but I wasn’t; I expect but don’t know that Bill interceded on my behalf.

Hello Ed,
FYI, eight years ago I emailed you asking about CamExec. A while ago I went to Cambridge, met with Mike Speciner, and photographed all his listings. GitHub - pdp11/camexec: PDP-11 operating system modeled on ITS. · GitHub