Timesharing on the MIT Weather Radar PDP-8/IX

No rush with the rest of the listings. I’m just about done with the kernel. I put the relevant files here for now:

I will verify the listing by assembling it. I already have PAL III (and LAP6) compatible assemblers written in Python which can be updated as fit. Ultimately, I would like to add the missing devices and addressing extensions to SIMH and see the system run.

I had forgotten about that, but yeah that was not a great day in the radar room. I have no memory of whether I helped keep your job. Nice hack though.

Amazing work Lars! Sorry, but here’s the rest of PMIO, all the code in field 0. It’s 68 listing pages, with many blank spaces. I paid more careful attention to the scanner settings this time. For the kernel I carelessly just accepted the default settings, which were 200 dpi color. For the new scan it’s 600 dpi grey scale.

I’m going to try the same thing on a parallel track, but first I have a lot more writing I want to do. I can run PAL8 under OS/8, and my simulator (not SIMH) has its own built-in assembler (all written, don’t think ill of me, in Visual Basic 6.0). I can modify it (keeping VB6 running under Windows 11 required help from Copilot).

Adding address extensions will be easy. The real problem with simulation of the Weather Radar PDP-8/IX is not the CPU but rather the odd collection of I/O devices, whose only documentation would come from reverse-engineering the source code. Tradeoffs will present themselves.

If you really want to see this run, then working together but on parallel tracks would I think be a productive redundancy. I can’t promise any particular time commitment, but in principle I’m game to try.

Here is a catalog of the PMIO code just posted.


You can see another PDP-8 programming quirk—packages are broken up and spread around to make good used of 0200-word memory pages.

You can see that the majority of the code is devoted to input parsing and formatted output. The Parse/Format package is shared by multiple processes, and makes heavy use of the stack package. I’ll have more to say about these in later posts.

The 611 Scope package includes a 5x7 dot matrix font. It took me a little while to reverse-engineer the format. Can you?

The debugger is called every 125 ms by the clock interrupt handler. It can display and modify any address in any field as commanded by Air Force-provided switches, while RADAR is running. I could often diagnose and sometimes fix trouble without bringing down the system, important on a stormy day.

Here are two pages from On the Handling of Digital Data, Silver, W.M. & Geotis, S.G., 17th Conference on Radar Meteorology, Seattle, 1976, American Meteorological Society.

The first page shows an example of one of the kinds of displays that the meteorologists liked. Displayed are dbZ values and iso-dbZ contours. This display is of GATE data recorded by the TI-980 system and printed on a Gould 100 dpi printer. The PDP-8 RADAR system could make a similar but less sophisticated display on the two-color ASR-38.

The second page shows an ASR-33 console session with RADAR, along with explanations. There’s a simple command language that’s parsed and executed.

I gave the paper at the conference. I’m 22, hair down to my ass, speaking to an audience of real-life scientists in coats and ties. I was terrified and no doubt it showed in my voice. Over the course of many years I grew to enjoy public speaking, and did major presentations for customers and investors at Cognex.

Thanks for sharing that! For convenience, here are a couple of screenshots:

(There’s more text in the document itself.)

Thanks, Lars! Someday I’ll see if I can make Camexec/DDT/Teco run, and write a history of Camex for this forum.

That would be awesome! Especially the latter.

Here’s a question. What is the purpose of your division between kernel and pmio? It seems to me they are both part of the same listing and program, and they are both needed to assemble the whole system. Right?

Bill mentions the disdrometer, to measure the distribution of the sizes of raindrops. This was essentially a microphone with a plastic diaphragm, facing upward, with a slight conical shape so the water would drip off the sides. The rain would “tap” on the microphone, producing electrical pulses which were stored in the associated box of electronics, about which I know nothing. My job was to press a button on that box, which would output the stored distribution numbers onto paper tape (and presumably reset the counters for the next storm). I’d then take the paper tape to the computer center, where they had a paper-tape-to-punched-card transducer. Can you imagine the racket? The data on punched cards was appended to a program also on punched cards, and fed into the IBM mainframe. A day later I retrieved the cards and the corresponding printout, which went off to Dr. Austin and her grad students for analysis.

2 Likes

Yes. “Kernel” is a name I came up with last week to distinguish the portion of the listing that I had the energy to scan at first. It included basic process management stuff and seemed like a good place to stop. It needed a name so “kernel” is what I chose. It still might be a good name for that portion.

Here is the complete scanned PMIO (all of field 0), 600 dpi grey, including what I have called the kernel, rescanned at 600 dpi grey.

Here is the full catalog

1 Like

I’m working on a discussion of the Stack package, including why it behaves like a stack but is allocated on the heap. Hope to post later today (east coast USA time).

The package that I’ve called Stack solves one specific and important problem—how multiple processes can simultaneously share a subroutine, each with its own internal state.

An example of such a subroutine is READ (3054), that can read and parse input from a TTY. You tell it which TTY keyboard to use for input and which printer to use for echoing characters. You tell it how you want the input to be parsed. It handles input editing conventions for a TTY (i.e. paper).


Another example is WRITE (3122) for formatted output

These were shared simultaneously by two processes using two TTYs (and in principle as many as memory will hold). Each call to READ or WRITE needs lots of internal state to function—indices, pointers, buffers, flags, modes, return address, whatever, and that state must be separate and private for each process.

On contemporary multi-thread systems, each thread has its own stack, and most state (e.g. automatic variables) is allocated on the stack without the programmer having to think much about it. If there is shared read/write data not on the stack, it can be protected by a mutex. Each call to a function gets its own stack frame to hold its state.

Most PDP-8 code is written in a style suited to its instruction set and memory size. JMS puts the return address in the called subroutine. Arguments are often passed in memory after the JMS. State variables are static. All of these are unsuitable for shared code.

The Stack package provides the mechanism for process-private stack frames, called Working Registers (WR) in the code, with the routines CALL (2600) and RETURN (2655).

A call to READ looks like this:

The supervisor call dispatcher (0326) transfers to CALL, following the SVC pointer argument to the callee’s location and arguments. The location can be in any field of memory. CALL fetches the first word of the callee to get the size of the working registers (i.e. stack frame) needed. A call to ALOC gets the WR (failure causes the SVC to non-skip return), which are added to the front of a linked list that is process private (PWREGS at 0003). CALL saves the caller’s state (PC, AC, MQ, Link, etc.) in the WR. CALL hacks SVC to cause it to return to READ, not the SVC caller. SVC returns through the traffic controller.

SVC RETURN pops and frees WR, restores the callers state, and hacks SVC to return to caller. This hacking of the SVC return would be bad programming practice on a machine with more memory and a richer instruction set, but this is the PDP-8. The lack of good comments, however, is bad practice on any machine.

Here is how working registers are declared. There is a WR header (listing page 2)

Then each stack-called subroutine adds whatever internal state it needs to this. Listing page 34 is the definition for the WRs used by both READ and WRITE. Here is the portion of it that is common to both READ and WRITE. Notice how it is placed just after the header with the *WR.

Following this are additions specific to READ and specific to WRITE. READ’s includes a 72-character buffer, for a total WR size of 66. Stack-called routines are entered with X pointing to their WR.

The PDP-8 version of mutex is simply to turn interrupts off (IOF) for the section of code you want to protect. Just don’t keep them off too long. In a stack-called subroutine, you must do this with JMS, for example
IOF JMS

If the called routine is short enough (and can’t block) to keep interrupts off, it turns them on just before the return instruction (a JMP indirect). ION delays one instruction for this purpose. If the routine is not short enough (or might block) it must save the return address in the WR and turn them back on.

Why use chains of stack frames allocated on the heap instead of just stacks? It’s really hard to know how big to make those stacks, and each individual stack must be as big as might be needed. On machines with lots of memory that’s not a big problem, but RADAR has no memory to waste. With heap allocation the total amount in use at any point is just what’s needed at that point, as long as fragmentation doesn’t kill you.

Of course this is like 1976. I haven’t seen the PDP-11 or C yet. My experience with stacks and multi-thread programming was very limited. I used the term working registers, not stack frame. Who knows what I was actually thinking at the time, something no comments record.

Weather Radar ran datasets on the main IBM 370 for big calculations in Fortran. It was slow and expensive, but that’s what you did. Imagine the surprise when we showed we could do the same thing in minutes for free on the 980, our 16-bit mini.

I know what you were thinking but the SYSOP will not let me post it, :slight_smile:
Would a subroutine call using a real stack have made for a simpler program?

Good question, but I think not. The PDP-8 instruction set, and the clumsy address space broken into fields, were not at all friendly to stack frame programming in a multi-thread system. Essentially all of the mechanism needed to do heap-allocated stack frames would also be needed for stack-allocated frames. The only real change would be to replace a call to ALOC and FREE with some code to push and pop a stack frame. ALOC and FREE are of course more complex than push and pop, but I had them for other reasons and the calls to ALOC and FREE are simple.

The GATE project has been in the background throughout this history, for a variety of reasons. The PDP-8 is a historically significant machine, while the TI-980 is so obscure it doesn’t even have a Wikipedia page. The IX modifications and RADAR timesharing are noteworthy, and I have a complete source code listing to work from. But it’s time now to take a little break from the tedium of PDP-8 assembly language and join me on the Gillis in the summer of 1974. We can get back to RADAR later.

In an earlier post I wrote that I was “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.” Let’s see how and why I got there.

GATE was a very big deal. This description from Nature is worth reading in full:

“The Atlantic Tropical Experiment (GATE), the first major observational experiment of the Global Atmospheric Research Programme (GARP) and designed to explore the tropical atmosphere, was successfully completed in September 1974. In what was probably the largest and most complex international scientific experiment ever undertaken, ten nations—Brazil, Canada, France, Federal Republic of Germany, German Democratic Republic, Mexico, Netherlands, USA, UK and USSR—working in close collaboration, contributed 39 specially equipped ships, 13 large research aircraft, several meteorological satellites and some 5,000 personnel to an intensive three-month study of weather systems in the tropical eastern Atlantic Ocean; in addition, some 50 countries in Africa and South America participated by making additional surface and upper-air observations.” –Mason, B. The GARP Atlantic tropical experiment. Nature 255, 17–20 (1975).

The MIT Weather Radar Research Project was awarded a contract by the National Science Foundation to provide radar coverage on one of those 39 ships, with Polly as principal investigator, Speed as chief engineer, Ken as digital architect, and Steve and me as technicians. This was going to be an expensive undertaking. In addition to salary and MIT’s overhead, the contract covered buying WR73, and the TI-980 system with its two mag tape drives, two disc drives, and various other peripherals. All of this hardware needed to be hauled up to the 18th floor, antenna installed on the roof, for development. Then hauled back down, shipped to Maimi for installation on the Gillis, and sailed to Dakar, Senegal, before heading out to the observing station in the Atlantic. Then hauled back and reinstalled on the 18th floor and roof.

We didn’t have the budget of the big government contributions to GATE, like NOAA’s radar installation on the Oceanographer. So we made two key decisions to save money that would have significant and unfortunate consequences. We managed to overcome the troubles, but it was a close call, and the Gillis had troubles of its own.

The first decision involved stabilizing the antenna as the ship rolled and pitched. We needed to know and control where the dish was pointing relative to earth coordinates, not relative to the ship on which it sat. The conventional way to do this, what NOAA did for example, was to use a stable platform. Imagine a very sturdy table that could rotate along two axes aligned to the roll and pitch axes of the ship. With a vertical reference gyro, powerful servo motors, and basic servo control loops, the table can be kept parallel to the earth’s surface as the ship moved. The radar antenna, with its separate azimuth and elevation servos, would be mounted on the table and could be controlled relative to earth coordinates just like it would be on the ground instead of the ocean.

But who needs a stable platform, right? The gyros and ship’s compass would give ship coordinates relative to vertical and North. We knew where we wanted the dish to point relative to the earth. We could just do the 3D coordinate rotation to transform earth coordinates to instantaneous ship coordinates, and servo control the antenna’s ship-relative azimuth and elevation to produce the desired earth-relative azimuth and elevation. Just a little linear algebra, digital control theory, and some assembly language to run the transform and control loops at 50 Hz. We were MIT guys—what could go wrong?

Ken wrote the code for the TI-980. He was a fine programmer, and the code worked as intended. That he wrote it in hex machine language, not assembly language, is another story that we’ll get to later. The code was fine; the antenna was not.

The WR73 servo system was not designed for the large torques that would be required to scan the dish in stable earth coordinates while the relatively small Gillis is rolling and pitching on the Atlantic. The torques were magnified by the dish whipping around at the end of a long lever arm. Remember this photo I posted earlier:

We were great EEs, but the mechanical stuff didn’t occur to us until it was too late. Once on the ocean we stripped the gears in short order, rendering the antenna useless. Rendering the entire project useless. Panic ensued.

We managed to secure replacement gears from the manufacturer, along with someone who knew how to install them and was willing to travel to Dakar. Wire stays were added to the tower on which the antenna sat to keep it from whipping around so much. I think we lowered the stiffness of the digital servo loop (the proportional term in a proportional/derivative control loop) to try to be a little more gentle on the gears. If that somewhat reduced our ability to point in the desired earth coordinates, at least with Ken’s 3D rotation code we still knew where it was pointing at all times. Researchers could compensate for some pointing wobble in the data sets as long as the true earth coordinates were known and recorded.

So we survived that little misadventure, and it was all patched up before I arrived in Dakar. It was the second consequential decision that led to all the hex machine language programming, and my secret midnight hacking.

I’ll continue the story in the next post, after I arrive in Dakar to find the Gillis itself disabled.

1 Like

One little footnote on the 3D coordinate transforms. Like many minicomputers of that era, the TI-980 did not have floating point hardware—if you wanted it, say for your Fortran program, it was emulated and slow. Perhaps one could buy a floating-point add-on from TI, but we were saving money, and besides, software engineers of that era well knew how to write fixed point code and looked askance at people who needed floats.

Floats are convenient for dynamic range and precision. We could manage dynamic range by careful and manual placement of the implicit binary point throughout a calculation. Some would consider this tedious; I liked it and needed it throughout my career in industrial machine vision.

Precision wasn’t really an issue, because everything we could measure would produce no more than twelve bits on a good day. dbZ values were eight bits (on a log scale), and the low-order one or two bits were mostly noise. Gyro, compass, azimuth, and elevation angles were 16-bit binary angles, but only the high order ten or twelve bits could actually be measured.

We needed to do the full forward and reverse transform at 50 Hz on an early 1970s CPU. A full 3D rotation is trivial to code with floating point, but quite manageable in fixed point if one was careful, and using emulated floats was not going to work even if we wanted to.

One serious problem, however, was the need for lots of sines and cosines throughout the transform. Ken used a great chip set for this purpose that was available back then—a hardware (i.e. ROM) sine/cosine lookup table. A full table for a 12-bit binary angle input would be unnecessarily large for a 1973-vintage ROM, so there were two small tables, one to look up sine/cosine from high-order angle bits and one to look up interpolation coefficients from low-order bits. The 980 could easily handle the interpolation.

The 980 was pretty good at fixed-point multiplication and division, but of course this was long before parallel, one-cycle multipliers became common on machines that weren’t supercomputers. Throughout the 1980s, on for example the PDP-11 and MC68000 families, the same serial shift/add methods used on the PDP-8 and TI-980 were still in use. We were well aware of how many clocks a multiply needed, and planned accordingly.

2 Likes