How does effective address calculation work on the PDP-10?

My understanding is that the instruction code contains an 18-bit address or offset, one bit for indirect, and a register code that's 4 bits. This probably means that the offset is added to the register, and the sum is the address (or the address of an address, depending on the indirect bit). And since you can't add an offset to R0, I guess that's somehow special cased to encode absolute addresses.

My confusion is about what happens next. It's gotta fetch that word from memory, right? Is that word actually the datum that's treated in whatever way by the instruction? Or is this word again treated in the same way as that 23-bit part of the instruction, adding the offset to a (possibly different) register, and maybe keep going like that?

  • I'm a bit puzzled as why you ask this question, as it is explained on like the very first pages (if not the first) of the PDP-10 machine language manual. Right with the instruction format you already cite. – Raffzahn Nov 7 at 13:04
  • 1
    @Raffzahn It's because I wanted to know the answer, of course! And I got confused because this scheme is so different from what I'm used to on the 6502. Plus it's not obvious why something should work this way, which makes it hard to learn. – Wilson Nov 7 at 13:53
  • By now I checked, except for the wording, my explanation is exactly what the manual says, and it's extreme clear. I fail to see any hidden point. And except for multiple levels of indirection its exactly like a 6502 works. The 6502 is just less regular coded. Or better everything cramped into variable length instructions ... Well, and missing the luxury of having 36 bit words to hold 18 bit addresses. – Raffzahn Nov 7 at 21:18
up vote 4 down vote accepted

TL;DR: Yes

A (calculated) address (from an instruction) is always used to fetch a word (36 Bit), which, if the I bit was set, is interpreted again the same way - thus allowing indefinite addressing chaining - otherwise used at face value for the operation.

The Long Read:

All instructions always an effective address, usually an operand in memory (*1)

As you already described, the effective address is calculated by using the three address fields of an instruction to calculate an 18 bit effective address (*2):

  • The indirect bit (I).
  • The 4 bit index field (X),
  • The basic 18 bit address field (Y)

If the X field is zero, the effective address is simply the contents of the address field (Y).

If the index field X is nonzero, then it names an accumulator to use as an index register. Any accumulator except 0 can be so used. The lower half of the contents of that register is added to the address field (Y) from the instruction.

A set I bit now specifies indirection. If set the first step is taken as before, but the resulting (18 bit, no overflow), is used to address a word (36 bit that is). Of this word the bits 13..35 (the lower 23 bits) are interpreted exactly as if taken from an instruction

*1 - For immediate instructions, it's not a memory address memory, but used directly, as a operand. Like ANDI, ADDI, MOVEI, etc.

*2 - The whole instruction format is

  • 9 bit OP
  • 4 bit Accumulator
  • 1 bit Indirect
  • 4 bit Index
  • 18 bit Address
  • This means 1) you can not point to 36 bit data 2) linked list traversal might require that specific value(s) is kept in specific register(s) – Wilson Nov 7 at 13:01
  • @Wilson 1) Why not? 2) Why? (and which value?) – Raffzahn Nov 7 at 13:05
  • 1) because the upper 18 bits of the word overlay the Indirect and Index fields. 2) because with each level of indirection, it's possibly adding an offset to a different register. So it's possible to create a data structure which behaves completely differently depending on register contents. – Wilson Nov 7 at 13:17
  • 1
    @Wilson That's why I did the footnote about immediate - the address generated is the value. No indirection used. The I-bit gets ignored when immediate is used. What else. And regarding the instruction being an address, that's how every CPU with memory addressing works. On a 6502 the second byte of LDA $10 addresses the byte to be loaded, with LDA #$10 it uses the second byte as data (immideate), and LDA ($10) uses the pointer at $10 to load a byte. Looking close you'll find it in the 6502s encoding. The PDP-10 is just way more regular than the 6502 and allows a chain of pointers. – Raffzahn Nov 7 at 21:15
  • 1
    @Wilson MOVEI A,3(C) is an immediate instruction. The number is treated as a data value, not an address. It falls under the answer's footnote 1. – JeremyP Nov 8 at 11:08

Execution of all PDP-10 instructions first involves the computation of an "effective address" E. This computation is identical for all instructions, regardless of how they will use the result, and regardless of whether it's actually going to be used as an address, or used at all. The instruction format contains 3 relevant fields: Y (an 18-bit value), X (a 4-bit index field), and I, the indirect bit.

The term "modified address" is useful here. If the X field is zero, then the modified address is just the value in the Y field. If the X field is non-zero, then it names an accumulator (1 to 15) whose low-half content is added to Y to give the modified address (truncated to 18 bits).

If the I bit is zero, then the effective address E is equal to the modified address. If the I bit is one, then the word of memory (an "indirect word") addressed by the modified address is fetched. This indirect word has Y, X, and I fields that are then subject to the same effective address calculation. The iteration continues until a word with I bit zero is fetched, at which point we exit the computation with an effective address E computed from the Y and X values in this, the last-fetched indirect word.

For instructions that actually reference memory (e.g., MOVE from memory, MOVEM to memory) then E is the address of the operand.

For instructions that don't reference memory (e.g., MOVEI immediate), E may be the actual operand, a mask, a shift count, etc., depending on the instruction. In these cases "effective address" is a misnomer, though note that what the PDP-10 calls "move immediate" is called "load address" on other architectures.

Here is a trivial example I coded up and ran on a simh-emulated PDP-10 to demonstrate indirection. It's an intentionally preposterous example (not realistic).

a: z @b
b: z @c
c: z @d
d: 43 ; '#'

start: movei 1,@a
outchr 1
end start

Note: 'z' is a pseudo-instruction that has opcode zero. It's useful for constructing address words such as this. '@' signifies setting the indirect bit.

The first instruction executed is "move immediate, indirect through location a, into accumulator 1". The I-bit is set in the instruction so we fetch the indirect word from 'a'. But that has a set I-bit so we fetch the indirect word from 'b'. But that has the I-bit set so we fetch the indirect word from 'c'. But that has the I-bit set so we fetch the indirect word from 'd'. That does not have the I-bit set so we're done, and the effective address E will be 43 octal.

Now, the instruction is move-immediate, so E is not really a memory address, it's the actual value. So accumulator 1, the target of the movei, is set to 43 octal.

From there we use a monitor call to output the content of acc 1 interpreted as an ASCII character: 43 octal is hash.

Here's the execution:

.exec foo
LINK: Loading
[LNKXCT FOO execution]

If I had coded the move instruction as 'move 1,@a' (normal move, not move immediate) then the effective address 43 octal would have been used as the address of the operand; we'd have output the low 7 bits of location 43 (whatever might be there) as a character.

In real life I'd probably have written the simple form 'move 1,d' and not have used any indirection.

It is clearly possible to set up infinite loops in effective address calculation. The simplest is:

Z @.

"Dot" is the current location, so the infinitude should be obvious. As far as I recall, Z is not a valid opcode, but that does not matter; the CPU does not get as far as figuring that out. This burns CPU and memory cycles and executes no instructions. However, the sequence is interruptible. When I tried this long ago, the monitor (TOPS-10 6.03) would abort the program after some number of seconds of CPU had been squandered.

Your Answer


By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.