DEFCON19 lanyard and PDP-8 assembly, part 1

2011-08-15

So. Apparently the ones and zeroes on the DC19 lanyard constitute valid PDP-8 code.

Hmm.

I first thought (not sure if I heard this or inferred it) that only the part unnecessary to that incredible puzzle was PDP-8 code.

Here’s the text on the lanyard, with linebreaks instead of colons and asterisks instead of DEFCON logos (the entries with the logos are what’s required to complete the puzzle):

1110110000*01
0*11010010100
0110*10010011
1111000*00100
11*1101101000
*101010010000
111*000100001
01101001010*1
001010010*011
0010100*10100
011010*010011
11100*0000101
001010010100*
111010010*100
01001001*0101
1010100010001
111010000000o
0010100100115
1111000000107
000000000000
000000000000
000000000000

That leaves us with the following instructions to translate to PDP-8. The “o”, “5”, and “7” don’t obviously fit here, and if you also strip the trailing “1” at the end of the first entry, we get “1o57” - the creator of the puzzle. OK. But that’s still just 7 instructions (3 of which are just zeroes):

1010100010001
111010000000o
0010100100115
1111000000107
000000000000
000000000000
000000000000

However, if we take the first set of numbers and strip out the DEFCON logos, we get 15 more 12 bit entries, which yields a more interesting 22 instructions (again, 3 of which are zeroes):

111011000001
011010010100
011010010011
111100000100
111101101000
101010010000
111000100001
011010010101
001010010011
001010010100
011010010011
111000000101
001010010100
111010010100
010010010101
101010001000
111010000000
001010010011
111100000010
000000000000
000000000000
000000000000

To go further than that, I had to read more about the PDP-8. Brian Shelburne’s PDP-8 emulator not only is an emulator, but contains some really great and easy to read PDFs (only 85KB of the download is emulator; the rest is documentation).

Instructions with annotation

To understand what was going on, I went through and annotated these binary numbers with their octal equivalents and what Shelburne’s PDFs say their instructions mean. Some opening notes:

  • “direct” means the indirect addressing bit was disabled, it’s not doing anything complicated. You can ignore this.
  • “current” means the memory page is expected to be the current page. This program is small and will never use more than one page so I THINK you can ignore this, and treat it like we’re using zero page addressing where you get the Effective Address by prepending 00000 to the offset
  • The instruction reference is found in the appendix and is really nice. I also found this list of PDP-8 instructions to be an additional help in deciphering instructions like 7550 and 7005… Shelburne’s descriptions are a bit less terse.
  • The ACC is the “accumulator” register, a 12-bit wide register (the same width as a normal memory address). The L is the “link” register, a 1-bit wide register.
  • The SR is the “switch register”, which is also 12 bits wide and can be set by physical switches on the PDP-8’s front panel.

Below I have the instructions in monospace font in both binary and octal, followed by their assembly names. My notes are in bullet points beneath each one.

111011000001=7301o	cla, cll, iac
  • Clear ACC
  • clear Link
  • increment ACC
011010010100=3224o	dca, direct, current, 0010100=0024o
  • deposit contents of ACC to the memory address 0024 and clear ACC
011010010011=3223o	dca, direct, current, 0010011=0023o
  • deposit contents of ACC to memory address 0023 and clear ACC
111100000100=7404o	osr
  • “or SR with ACC”. I think this puts the contents of the SR onto ACC
111101101000=7550o	sma, sza, 0/1
  • This instruction is kinda confusing.
  • The “sma” and “sza” instructions mean skip the next instruction if ACC is negative or zero.
  • But the “0/1” bit reverses this, so it will skip the next instruction if ACC is positive or non-zero.
101010010000=5220o	jmp, direct, current, 0010000=0020o
  • Jump to memory address 0020
111000100001=7041o	cma, iac
  • complement ACC,
  • increment the ACC.
  • Doing these instructions in this order is the way to switch an integer between a negative and positive number
011010010101=3225o	dca, direct, current, 0010101=0025o
  • deposit contents of ACC to meory address 0025 and clear ACC
001010010011=1223o	tad, direct, current, 0010011=0023o
  • Add the contents of memory address 0023 to ACC
001010010100=1224o	tad, direct, current, 0010100=0024o
  • Add the contents of memory address 0024 to ACC
011010010011=3223o	dca, direct, current, 0010011=0023o
  • deposit contents of ACC to memory address 0023
  • this appears to clear ACC also?
111000000101=7005o	ral, iac
  • Rotate ACC and L left
  • “RAL rotates (circularly shifts) the 13 bit LINK/AC register one place left, so that the most significant bit of AC becomes the new link, and the old link becomes the least significant bit of AC.”
  • Then ACC is incremented
  • WAIT. this the order I thought it happened in but apparently the incrementing happens FIRST!
001010010100=1224o	tad, direct, current, 0010100=0024o
  • Add the contents of memory address 0024 to ACC
111010010100=7224o	cla, cml, ral
  • Clear ACC
  • complement L (set all zero bits to one and all one bits to zero)
  • rotate ACC and L left.
  • (double checked this and it does indeed happen in this order)
010010010101=2225o	isz, direct, current, 0010101=0025o
  • increment the contents of 0025 and skip the next instruction if the result is zero
101010001000=5210o	jmp, direct, current, 0001000=0010o
  • jump to memory address 0100
111010000000=7200o	cla
  • clear accumulator
001010010011=1223o	tad, direct, current, 0010011=0023o
  • Add the contents of memory address 0023 to ACC
111100000010=7402o	hlt
  • Halt / stop executing
000000000000=0000o
000000000000=0000o
000000000000=0000o
  • The documentation says to zero-out addresses you intend to use for storage later. Maybe that’s what this is?

Running the code

So now you’ve got a list of instructions, some of which reference memory addresses (the ones with the 7 digit binary numbers that i converted into 4 digit octal numbers are memory addresses). What belongs at those addresses, though? On the PDP-8 you are responsible for deciding the address of each instruction, and I have no idea what 1o57 had in mind when he wrote this. I started out just putting all these instructions in to memory one after another starting with address 0000 and ending on 0025, and that has worked OK so far.

You can load the code like so:

  • Shelburne’s emulator accepts code in the format mmmm/nnnn, where mmmm is an octal memory address and nnnn is an octal instruction. Save the following as lanyard.obj; it’s easiest to do this in the same directory as the emulator and in DOS-compatible (8.3) naming scheme due to the emulator’s limitations:

      0000/7301
      0001/3224
      0002/3223
      0003/7404
      0004/7550
      0005/5220
      0006/7041
      0007/3225
      0010/1223
      0011/1224
      0012/3223
      0013/7005
      0014/1224
      0015/7224
      0016/2225
      0017/5210
      0020/7200
      0021/1223
      0022/7402
      0023/0000
      0024/0000
      0025/0000
    
  • run the emulator

  • enter debug mode

  • type L lanyard.obj and press enter

  • You can run the whole code by pressing G then enter. It doesn’t seem to do much besides change the contents of 0024 from 0000 to 0001.

  • You can reset (R) and then re-load the code, and then step through the code instruction-by-instruction using the spacebar

When you step through the code like that, you see that there’s a JMP instruction, which I entered as 0005/5220, that just jumps over most of the code to memory address 0220, and nothing much interesting happens after that.

At first, I wasn’t sure if the code is doing all it is designed to do (“be valid PDP-8 code and also be part of the DEFCON19 puzzle”) or if there’s a way I was supposed to arrange it in memory so that running it does something more obviously interesting.

SR - the Switch Register

Also at first, I didn’t understand the osr instruction (0003/7404). Its description is “Or Switch Register with ACC”, but I didn’t realize that there is an actual register called the switch register (SR), which is a 12-bit (1-word) wide bank of physical switches on the PDP-8’s front panel. On the original device you programmed it by flipping 12 physical binary switches. In the emulator, you can program it in octal. It’s to the left of the main memory area on the debug screen, below the ACC. When we ran the code the first time, the SR was all zeroes.

  • that JMP instruction I mentioned earlier is problematic, but the instruction that precedes it will skip it if the ACC is positive or nonzero, and the instruction that precedes THAT puts the contents of the SR on the ACC. Maybe I should try to manipulate the SR so that it will skip that JMP instruction?
  • I first tried setting the SR to ‘1057’ and pressing start, just as a shot in the dark. This does bypass the first problematic JMP instruction, but if it’s the right answer, I can’t make sense of the output. (Need to spend more time on this.)
  • The SR gets copied to the ACC, and then copied to address 0025, and then that address gets complemented. Later, that gets incremented by one and if the result is zero ANOTHER jmp instruction (0017/5210) is skipped. Maybe I need to manipulate the SR such that this instruction gets skipped also?

This is where I’m at now. I haven’t really had time to work on this for a couple of days so hopefully tomorrow I can do something with it.

Music: Jóhann Jóhannsson - IBM 1401, A User’s Manual

Update: I’ve got a part 2 over here, where I finish the program but still have unanswered questions.