Want to do some binary machine coding using switches and lights? First, you need to build a PDP-11. This is because a PDP-11 has switches and lights, which is awesome. If you just want to learn a bit without building a PDP-11, scroll down to programming binary machine code.
Disclaimer: I've never used a real PDP-11, so I don't really know what I'm talking about, and there's probably a bunch of errors and mistakes here.
To build my dodgy simulated version of a PDP-11. You'll need (among other things):
The idea is that it's A4 sized, and one sheet is to bolt the switches to, and the other is to go in front.
There's a super awesome service called RazorLAB, and they will very quickly and reasonably cheaply laser cut stuff for you.
I made an inkscape file that looks something like this:
After a few failures - preparations A through G, I came up with preparation H. Previous preparations involved 3mm acrylic, which wasn't thick enough, 5mm was the way to go. The 5mm acrylic wasn't just for the strength, it was also for the LEDs. I started out with 5mm LEDs, which are too long. 3mm LEDs in 5mm acrylic mean they don't poke out.
I learned that inkscape is hopeless. So I used LibreCAD instead. Which isn't quite as bad. But you can type in the commands to draw circles and rectangles, which is all I need. So I wrote a python script to send key presses to LibreCAD. I switch between windows, linux and OSX a lot, so the python script contains commands for both windows and linux. Figuring out what to uncomment/comment is an exercise for the reader.
I ended up with two DXF files, something like this for the the top and bottom.
I ended up using the fantastic London Hackspace's laser cutter, because even RazorLab can get expensive if you (or inkscape [which is s#*t]) keep messing up the design.
Making a PCB would make it easier than using veroboard like I did [that'll make your eyes bleed], but the schematic should be something like this:
What I did, but you shouldn't do, is use male to female jumper wires and solder the male to the board. Pin headers (eg) would work better.
If you look at the Raspberry Pi pinout (source), you'll see you can grab power and I2C from the top few pins:
The maximum permitted current draw from the 3.3V pins is 50 mA. GPIO voltage levels are 3.3V and are not 5V tolerant.
To power your board, you can't use the 3.3V, as it doesn't supply enough current. Using I2C at 5V might work, but I didn't risk it, so I used a 4-channel I2C-safe Bi-directional Logic Level Converter - BSS138.
Once you've made your board, hooked up your LEDs, bolted the switches, you'll want it to look nice. (Photoshop PSD). The contrast between the colours on it isn't enough, so you'd probably want to increase that if you make your own.
I printed the above onto a Sticker, cut it out, and stuck it on. Now you've made something that resembles a PDP-11, you'll want it to work like a PDP-11. You'll now want to enable I2C on your raspberry-pi. Install WiringPi and WiringPi2 while you're at it.
I assume you've written an slightly-working and badly written PDP-11 emulator that uses wiringpi. It's probably the first and only C project I've done, so it's not a fine example of my programming.
If not, download mine here: https://github.com/jonatron/pdp-11/tree/master/newsrc
When I say it's mine, it's actually a final year Computer Science group project, which I was part of [a few years ago], and I happened to decide to make a physical version years later. The newsrc folder is my hacked version, and the src folder is how it was left from the uni project originally. A big shout out to Bob Eager for the project idea and all the help he gave the group.
The code has a load of compiler warnings, is incomplete, and doesn't run anything other than very simple machine code programs. But it's a lot easier to understand than something like SimH.
If someone is interested, it would be amazing if they could rewrite the project, or help clean it up at least. There's some python scripts in the git repo to help map the switches and LEDs pin numbers to the correct numbers in the C code.
Lets look at some code!
005000 clr r0
005200 inc r0
000775 br .-4 back to inc r0
PC = 01000;
What I'm doing is setting the memory addresses 01000, 01002, 01004, and 01006, using the function setWord. In C, a number starting with 0 means octal. But why would you use it? Here's why:
A PDP-11 is [simplifying here] a 16-bit machine, so it can support up to 2^16 = 64k 8-bit bytes. Every byte of memory has an address, which is just a number. Some memory addresses are special, and are used for Input/Output (IO). For example, to write a character to a teletype terminal, you set the memory address 0777566.
The PDP-11/40 processor handbook has a handy list of instructions.
The first instruction, at address 01000, is 005000. If you look for an op code starting with 0050, you find that the instruction is CLR. CLR clears given address.
CLR is a single operand instruction. In this case, the mode part is 0, which means register mode. The final part (bits 0-2), is 0, so 005000 means clear register 0.
To quote wikipedia:
In computer architecture, a processor register is a small amount of storage available as part of a CPU or other digital processor. Such registers are (typically) addressed by mechanisms other than main memory and can be accessed more quickly. Almost all computers, load-store architecture or not, load data from a larger memory into registers where it is used for arithmetic, manipulated, or tested, by some machine instruction. Manipulated data is then often stored back in main memory, either by the same instruction or a subsequent one.As you can see below, register 0 is available to use.
R7 is also known as the Program Counter (PC), which contains the memory address of the current instruction.
005200 inc r0
000775 br .-4 back to inc r0
The branch instruction is actually quite complicated.
000775 is 111 111 101 in binary. This means the offset portion is 11 111 101. This is 8 bits - a byte. But it's two's complement , which means it's -2 as a normal (decimal) number. In the documentation above, the new PC is PC + (2 * offset). 2*offset is -4.
So when it hits that instruction, it goes back 4 instructions, to the start, creating a loop. Cool!
You may have noticed I explained a slightly different program to the one I programmed in the video. They both display light patterns on the DATA register.
In the video, I use the DEP switch after entering each instruction in the switch register. It transfers the contents of the switch register to the address in the address register, and increments the address register. After getting to the end, I set the switch register back to 0 (all down), and flick the load address switch. This sets the address register back to the start, so we're starting from the right place. After that, I flick the enable/halt switch, which is probably fairly inaccurate to what you'd do on the real thing.
You may have also noticed the switches on the right don't work the same as the real thing, that's because it's hard to find switches like that. The RUN/BUS/FETCH etc lights aren't connected, this is because the amount of soldering required was excessive even without those.
If you read this far, thanks. That's all for now. My next post will probably be ranting about AWS, which is something completely different.