Remembering the CDC 6600

Ah, nostalgia, nostalgia! El Reg just published a piece about a classic:

Control Data Corporation 6600
Released: 1964
Price: ~$6m-$10m
OS: COS, SCOPE, MACE, KRONOS
Processor: One 60-bit CPU, ten shared-logic 12-bit peripheral I/O processors
Memory: 128K 60-bit words
Display: Printer, plotter and dual video display console
Storage: 2MB extended core storage, magnetic disk, magnetic drum

I was a systems programmer on one of these beautiful beasts for a year, from July ’72 until August ’73. It was my first job out of school, at the University of London Computer Centre. We had a 6600 and a 6400, and while I was there we took delivery of a 7600. The 6600 had daily scheduled maintenance from 1 PM to 2 PM, and although we usually needed all of it (OS patches, replacing hardware modules, etc.) there were times when there was nothing to do… so we had the most powerful computer in the country ((Well, maybe. We kept hearing about this rather non-standard IBM mainframe up at Daresbury…)) at our disposal, for whatever we wanted! I had this simulation framework that I’d written to explore the behaviour of different paging algorithms, and it ran really nicely on a dedicated 6600!
One of the more idiosyncratic features of the CDC 6600 was the way in which applications issued system calls. In the early versions of the SCOPE OS, all of the operating system functions ran in the PPUs ((Peripheral processing units.)). ((Later on, they introduced some CPU-resident OS services, which I always thought was a real hack.)) So how does a CPU-resident application issue a system call? There were no dedicated instructions for the purpose; no traps, or gates, software interrupts, or anything like that. The technique was simple:

  • Construct a request block in memory, including the function code, buffer pointers, etc.
  • Clear a flag bit in the request block.
  • Store the address of the request block in location 1 of the application’s address space. (This location was referred to as the RA, or Reference Address, plus 1.) [Thanks to Peter Schow for the correction.]
  • Poll the flag bit until it is set.

In practice, the busy waiting didn’t last long; as soon as the OS noticed the presence of a (non-zero) address in RA+1, it would switch the CPU to another task.
All this leaves only one question: how do you access memory location RA+1 from a Fortran program? We used a little library function, IADDR(), which returned the address of any variable. And then we wrote code like this:

DIMENSION MEM(0)
INTEGER RA
DIMENSION IORB(16)
RA = -IADDR(MEM)

MEM(RA+1) = IADDR(IORB)

All of this looks rather primitive now. However, it’s worth pointing out that it looked quite primitive then. I came to the 6600 from the PDP-10, which was a powerful CISC design with a very rich instruction set ((The most expressive programming language was assembler – at least, until LISP and POP-2 arrived.)), and Seymour Cray’s minimalist design took me by surprise. In many ways the 6600 was the first RISC machine: a clean memory model, specialized register files, and simple instructions that got the most out of the technology of the day. It used ones-complement arithmetic, which meant that we had to cope with two zeros – positive (all 0’s) and negative (all 1’s).
What a gorgeous piece of machinery.