A brief tour of the PDP-11, the most influential minicomputer of all time
Pangram verdict · v3.3
We believe that this document is fully human-written
AI likelihood · overall
HumanArticle text · 1,738 words · 5 segments analyzed
The history of computing could arguably be divided into three eras: that of mainframes, minicomputers, and microcomputers. Minicomputers provided an important bridge between the first mainframes and the ubiquitous micros of today. This is the story of the PDP-11, the most influential and successful minicomputer ever. In their moment, minicomputers were used in a variety of applications. They served as communications controllers, instrument controllers, large system pre-processors, desk calculators, and real-time data acquisition handlers. But they also laid the foundation for significant hardware architecture advances and contributed greatly to modern operating systems, programming languages, and interactive computing as we know them today. In today’s world of computing, in which every computer runs some variant of Windows, Mac, or Linux, it’s hard to distinguish between the CPUs underneath the operating system. But there was a time when differences in CPU architecture were a big deal. The PDP-11 helps explain why that was the case. The PDP-11 was introduced in 1970, a time when most computing was done on expensive GE, CDC, and IBM mainframes that few people had access to. There were no laptops, desktops, or personal computers. Programming was done by only a few companies, mostly in assembly, COBOL, and FORTRAN. Input was done on punched cards, and programs ran in non-interactive batch runs. Although the first PDP-11 was modest, it laid the groundwork for an invasion of minicomputers that would make a new generation of computers more readily available, essentially creating a revolution in computing. The PDP-11 helped birth the UNIX operating system and the C programming language. It would also greatly influence the next generation of computer architectures. During the 22-year lifespan of the PDP-11—a tenure unheard of by today’s standards—more than 600,000 PDP-11s were sold. Early PDP-11 models were not overly impressive. The first PDP-11 11/20 cost $20,000, but it shipped with only about 4KB of RAM. It used paper tape as storage and had an ASR-33 teletype printer console that printed 10 characters per second.
But it also had an amazing orthogonal 16-bit architecture, eight registers, 65KB of address space, a 1.25 MHz cycle time, and a flexible UNIBUS hardware bus that would support future hardware peripherals. This was a winning combination for its creator, Digital Equipment Corporation. The initial application for the PDP-11 included real-time hardware control, factory automation, and data processing. As the PDP-11 gained a reputation for flexibility, programmability, and affordability, it saw use in traffic light control systems, the Nike missile defense system, air traffic control, nuclear power plants, Navy pilot training systems, and telecommunications. It also pioneered the word processing and data processing that we now take for granted. And the PDP-11’s influence is most strikingly evident in the device’s assembly programming. Assembler programming basics Before high-level languages such as Python, Java, and Fortran were invented, programming was done in assembly language. Assembly language programming can be done with very little RAM and storage—perfect for the environment of the early days of computing. Assembly language is a low-level intermediary format that is converted to machine language that can then be run directly by the computer. It is low-level because you are directly manipulating aspects of the computer’s architecture. Simply put, assembly programming moves your data byte by byte through hardware registers and memory. What made programming the PDP-11 different was that the minicomputer’s design was elegant. Every instruction had its place, and every instruction made sense. A 16-bit address space meant that each register could directly address up to 64KB of RAM, with the top 4K reserved for memory-mapped input and output. Early PDP-11s could address a total of 128KB of RAM using register segments (more on this in a bit). So despite PDP-11 systems coming with only 4KB of RAM, they were still productive through the clever use of early programming techniques. An assembly language program It’s easiest to grasp this concept through an example of a simple PDP-11 assembly language program, which we’ll go through below. Keywords that start with a “.” are directives to the assembler. .globl exports a label as a symbol to the linker for use by the operating system. .text defines the start of the code segment.
.data defines the start of a separate data segment. Keywords ending with a “:” are labels. Assembly programming uses labels to symbolically address memory. (Note: With the PDP-11 jargon and coding to come, any text after / is a comment.) Keywords Translation .globl _main Export label _main as an entry point for the operating system to use .text Start of instruction segment where read-only code lives _main: MOV VAL1, R0 Copy the word value at memory location VAL1 into register 0 ADD $10, R0 Add 10 to the value in register 0 MOV R0, VAL1 Copy the value in register 0 to the memory location VAL1 .data Start of data segment where read/writable data lives VAL1: .word $100 Reserve 2 bytes of storage to hold Val1, initialized to 100 Although numeric values can be used for memory addresses, the use of labels instead of hard-coded addresses makes it easier to program and makes the code relocatable in memory. This gives the operating system flexibility when running the code, ensuring each program is fast and efficient. The .data assembler directive puts the data in a memory segment that’s both readable and writable. The memory segment for code is read-only to prevent programming errors from corrupting the program and causing crashes. This separation of instructions from data on the PDP-11 is called “split instruction and data.” In addition to adding stability, this feature also doubles the address space by enabling 64KB for code and 64KB for data—this was considered quite an innovation at the time. Accordingly, Intel’s X86 microcomputers later made extensive use of segments. An original front panel of a PDP-11/20. Credit: Rama & Musée Bolo PDP-11 architecture What about the PDP-11 made it better to program with? Its simple but powerful architecture. Eight 16-bit registers were the heart of the instruction set architecture, or ISA. Six registers were general-purpose, one was the stack pointer, and one was the program counter. The registers could access any other register—as well as memory or direct data—using one of the eight different addressing modes.
Each register could perform logic, math, or test operations on a 16-bit word of data, an 8-bit byte of data, or access memory. A register could also read or write a 16-bit word of memory or an 8-bit byte of memory. Later versions of PDP-11s added registers to directly process floating-point numbers. Here’s a look at some of those addressing modes: / Direct register address MOV $1, R0 / Move the number 1 into register 0 / Register indirect MOV $1, (r0) / Move the number 1 into the memory pointed to by register 0 / Auto-increment MOV $1,(r0)+ / Move the number 1 into the memory pointed to by register 0, and increase the address in register 0 by 1 word / Auto-decrement MOV $1,-(r0) / Decrement the address in register 1, then move the number 1 into the memory it points to / Index register MOV $1, START(r0) / Move the number 1 into the address created by adding the contents of register 1 to the address of START Stack pointer and program counter As mentioned, the PDP-11 keeps instructions and code in separate memory segments. The stack pointer, or SP, helps you manage data memory, and the program counter, or PC, helps you manage the execution order of your code. In the early days of computing, CPUs had a limited number of registers, so most operations were done in memory. Stacks and heaps were the ways that programmers managed data in memory. The Heap was generally used for global variables, data, and constants. The stack was used for the dynamic variables and data used in subroutines. Having a dedicated register for the stack was a real luxury for programmers and contributed to faster program execution. Programming today uses the modern technique of garbage collection to automate memory management. But in assembly language programming, if you need to use memory, you have to manage every aspect of it manually. Stack pointer instructions help you do that. Here are stack pointer addressing modes. /
Deferred MOV (SP), R0 / Move the value of the memory pointed to by the stack pointer to register 0 / Auto increment MOV $1, (SP)+ / Move the value 1 into the memory pointed to by the stack pointer and increment the value of the stack pointer / Indexed MOV ARRAYSTART(SP), R0 / Create an effective address by adding the value of ARRAYSTART to the value of the stack pointer, move the contents of that value into register 0 / Index deferred MOV @VAL1(SP), R1 Use of the program counter The program counter can be accessed like any of the other registers, but doing so is not a good idea. On the PDP-11, the program counter stays busy keeping track of the next instruction in memory. The program counter is intended to support jump, branching, and other control flow instructions. The effect of the instruction JMP START has the same effect as MOV START,PC: to start executing code at location START. The difference here is that using a JMP or branch instruction clearly indicates a change in your flow control, making it easier to read a program and trace the execution. Here is an example of unconditional jumps and branches: JMP START / Jump to the address of START, which can be anywhere in the 16-bit memory address space. This is also called a “long jump” because the effective address of “START” uses all 16 bits. A long jump is also an example of a direct address. BR CALC / Branch to the offset address of CALC, forward up to 127 words or backward up to 128 words This has the same effect as ADD CALC, PC, where CALC is an 8-bit value that can be negative. The branch instruction is also called a “short jump” because you can’t jump as far. In this case, CALC is an indirect address that is relative to the current address as an offset. So, for instance, if CALC was a location 32 words later in memory, the equivalent instruction would be to add 32 words to the Program counter. Why the need for a long and a short jump? One word: memory. The 16-bit address in the long jump uses an extra byte.