by kragen | 2025-08-07 03:05:27+00:00
I'm glad you liked it!

Yeah, with (PC)+ (27), you didn't need a separate immediate addressing mode where you tried to stuff an operand such as 2 into the leftover bits in the instruction word; you could just put your full-word-sized immediate operands directly in the instruction stream, the way you did with subroutine parameters on the PDP-8. And there was a similar trick for @(PC)+ (37) where you could include the 16-bit address of the data you wanted to access instead of the literal data itself. But that kind of thing, plus the similarly powerful indexed addressing modes (6x and 7x), also meant that even the instruction decoder in a fast pipelined implementation of the PDP-11 instruction set would have been a lot more difficult, because it has to decode all the addressing modes—so, AFAIK, nobody ever tried to build one.

And different kinds of PC-relative addressing is basically the only benefit of making the PC a general-purpose register; it's really rare to want to XOR the PC, multiply it, or compare it to another register. And it cost you one of the only eight registers.

And you still can't do ARM things like

@ if (≥) r2 := mem[r0 + 4*r1] ldrge r2, [r0, r1, lsl 2] @ if (≤) { r2 := mem[r0]; r0 += 4*r1; } ldrle r2, [r0], r1, lsl 2 @ store four words at r3 and increment it by 16 stmia r3!, {r0, r1, r7, r9} @ load the first and third fields of the three-word @ object at r3, incrementing r3 to point to the next object ldr r1, [r3] ldr r2, [r3, #8]!

A lot of the hairier combinations have been removed from Thumb and ARM64, including most of conditional execution and, in ARM64, ldm and stm. Those probably made sense as instructions when you didn't have an instruction cache to execute instructions out of, because a single stm can store theoretically 16 registers in 17 cycles, so you can get almost your full memory bandwidth for copying and in particular for procedure prologues and epilogues, instead of wasting half of it on instruction fetch. And they're very convenient, as you saw above. But nowadays you could call a millicode subroutine if you want the convenience.

All these shenanigans (both PDP-11 and ARM) also make it tricky to restart instructions after a page fault, so AFAIK the only paged PDP-11 anyone ever built was the VAX. A single instruction can perform up to four memory accesses or modify up to two registers, which may be PC (with autoincrement and decrement), as well as modifying a memory location, which could have been one of the values you read from memory—or one of the pointers that told you where to read from memory, or where to write. Backing out all those state changes successfully to handle a fault seems like a dramatic amount of complexity and therefore slowness.

I'm aware that I'm talking about things I don't know very much about, though, because I've:

- never programmed a PDP-11;

- never programmed a PDP-8;

- never programmed in VAX assembly;

- never designed a pipelined CPU;

- never designed a CPU that could handle page faults.

So I could be wrong about even the objective factors—and of course no argument could ever take away your pleasure of programming in PDP-11 assembly.