__ __ /\ \ /\ \ 000 | LDA | Z | A = [X] / \ \ / \ \ 001 | NOR | Z | A = A NOR [X] / /\ \ \ / /\ \ \ OOO D Z XXXXXXX 010 | ADD | C | A = A + [X] / / /\ \ \\/_/\ \ \ \ \ \ \____ Operand 011 | SHR | C | A = [X] >> 1 / / / \ \_\ / / / \ \ \________ Zero Page 100 | LEA | - | A = X / / / __/ / / / / / \ \_________ Dereference 101 | STA | - | [X] = A / / / /\ \/ / / / / __ \___________ Opcode 110 | JMP | - | P = X / / /__\ \ \/ / / /_/\_\ 111 | JFC | - | if !F: P = X / / /____\ \ \ / /_____/ / \/________\_\/ \________/ joewing.net
The Q2 is a single-board, discrete transistor computer. All transistors in the Q2 are n-channel MOSFETs. The Q2 is implemented on a single 314mm x 240mm PCB using surface mount components, allowing the board to be produced and assembled almost entirely by machine.
The video above is a quick demonstration of the Q2. First the Q2 is programmed via a Raspberry Pi Zero and then the program is run at 80 Hz (the slow clock). The program is available here.
The Q2 is, in many ways, a 1960s-style minicomputer on a board. Inspired by the DEC PDP-8 (the PDP-8/S in particular), it is a 12-bit accumulator-based architecture with a small instruction set and a bit-serial ALU.
The Q2 was designed to keep the transistor count down while keeping the instruction set versatile enough to be usable. Unfortunately, this means the Q2 is fairly slow. All instructions take either 8 or 32 cycles to complete. Further, to keep the power consumption down without increasing transistor count, the maximum clock frequency is 80 kHz. Higher frequencies should be possible at the expense of more power (lower valued resistor pull-ups) or more transistors (active pull-up). While slow even for a 1960s computer, the Q2 is still fast enough to run many interesting programs including those that require interaction, such as games.
The Q2 has three user-visible registers:
- A - 12-bit accumulator
- F - 1-bit flag
- P - 12-bit program counter
In addition to these, there is a 12-bit X register that is used to hold the operand, a 4-bit state counter, 1-bit clock divider, and a 4-bit opcode register. All 46 registers are positive edge-triggered D flip-flops.
All instructions are one 12-bit word and have the following bit pattern:
OOO D Z XXXXXXX \ \ \ \____ Operand \ \ \________ Zero Page \ \_________ Dereference \___________ Opcode
Here OOO is the opcode which determines the operation to perform, D is the dereference bit, Z is the zero-page bit, and XXXXXXX gives the low 7 bits of the operand. The high 5 bits of the operand are set to either the high bits of the program counter (if Z=0) or 0s (if Z=1). If the dereference flag is set, the operand is then loaded from memory (X = [X]).
The zero-page bit allows easy access to the first 128 words of memory. These locations can be used much like additional registers (similar to the PDP-8 or 6502). The prefix "=" is used to indicate a zero-page operand.
The dereference bit is used to allow indirect access to a memory location. This can be used to access memory on other pages or for array accesses. The prefix "@" is used to indicate indirect access. The dereference bit and zero-page bit can be used together to perform an indirect access through the zero page (prefix "@="). This is useful for storing a stack pointer in the zero page, for example.
Combined, the dereference and zero-page bits provide 4 address modes:
|00||x||Current page relative|
|01||=x||Zero page relative|
|10||@x||Indirect through current page|
|11||@=x||Indirect through zero page|
The following instructions are supported:
|000||lda||Z||Load: A = [X]|
|001||nor||Z||Not OR: A = A NOR [X]|
|010||add||C||Add: A = A + [X]|
|011||shr||C||Shift right: A = [X] >> 1|
|100||lea||-||Load effective address: A = X|
|101||sta||-||Store: [X] = A|
|110||jmp||-||Jump: P = X|
|111||jfc||-||Jump flag clear: if !F: P = X|
The flag is set on carry (C), zero (Z), or left unchanged (-), depending on the instruction.
For programming, the Q2 has a simple front-panel interface. It has 12 LEDs to show the current address (the address bus, which is the P register after reset) and 12 LEDs to show the data bus. There are also 12 switches to serve as input and the following buttons:
- Deposit - Store the value in the switches to the current address.
- Next - Increment the P register (the current address).
- Reset - Set the P register to the value in the switches and reset the Q2.
- Halt - Stop the clock.
- Run - Start the clock.
The front panel allows one to enter a program and run it. However, the process is quite time-consuming and error-prone, so the front panel switches are also exposed through a 40-pin header. This header just happens to have a pinout that lines up with the GPIO pins of a Raspberry Pi, making it easy to use a Raspberry Pi to program and backup the Q2 via the q2prog utility. Note that Raspberry Pis with the 26-pin header will only be able to write to the Q2, but Raspberry Pis with a 40-pin header can both read and write.
LCD and Buttons
To allow one to interact with a running program, the Q2 includes a 16x2 LCD and 12 push buttons. These are exposed via address 0xFFF. Reading from 0xFFF reads the current state of the buttons, where a 1 means the button is not pressed and a 0 means the button is pressed. The LCD uses the common HD44780 interface where the low eight bits are data/commands to the LCD and bit 8 controls if data (0) or a command (1) is being written.