8-BIT BREADBOARD COMPUTER

8bitcpu

Overview

This is my modified build of Ben Eater’s 8-bit CPU project, which is an incredible introduction to digital electronics and computer architecture! I won’t explain the background to the project in detail here, so do check out Ben’s comprehensive introduction.

In brief, the idea is to use basic 7400-series logic chips wired together on breadboards to build the simplest possible programmable computer from scratch.

The architecture and layout of my build follows Ben’s fairly closely, with a few enhancements:

  • 74HC series chips
  • 16-bit address bus, to enable addressing of 32KB SRAM
  • 16-bit memory address register
  • 16-bit instruction register, to give an 8-bit instruction opcode and 8-bit operand
  • 2 x 16-bit general purpose registers C and D, although arithmetic remains 8-bit
  • 8-bit segment register to extend the 8-bit program counter (allowing execution beyond the bottom 256-byte memory segment)
  • Arduino Nano to bootstrap the SRAM, and allow direct SRAM writing via USB

Code

Code for the various components described below is available on my GitHub.

Architecture

The layout of the CPU modules is as follows, with the 16-bit bus running through the centre.

8bitcpu board layout

Control Lines & Instruction Set

The following is a snippet of the declarative specification for the control lines and instruction set (opcodes and microcode), which is converted and written to the control EEPROMs.

8bitcpu control logic

We have 24 control lines (hence 3 control EEPROMs with 8 outputs each), each of which enable the function of one thing. For example, the ‘AIN’ line instructs the A register to load from the bus, the ‘PCEN’ line instructs the Program Counter to increment, etc.

Each instruction has 8 microcode steps, each step being a combination of enabled control lines.

The instruction set consists of:

  • NOP
  • HALT
  • Load Reg from Memory Address
  • Load Reg from Immediate
  • Store Reg to Memory Address Direct/Indirect
  • Jump to Memory Address Direct/Indirect
  • Jump if Carry Flag
  • Jump if Zero Flag
  • ALU Add
  • ALU Subtract
  • Output DL to Display

Assembly Language

I implemented a simple assembler to convert an assembly language into machine code for the computer. The following is an example of an assembly program to brute-force prime numbers below 255 (with a rough Python translation at the top):

# Equivalent Python:
#
# for a in range(2, 256):
#     d = 0
#     for b in range(2, a):
#         c = a
#         while c > 0:
#             c = c - b
#             if c == 0:
#                 d += 1
#     if d == 0:
#         print(a, 'prime')

reset:
    LD 0x01 RA   # Intel-like syntax: load immediate 0x01 into A
    ST RA *0xff  # Store value in A into memory location 0xff

next_candidate:
    LD *0xff RA
    LD 0x01 RB
    ADD
    JC reset
    ST RA *0xff
    LD 0x01 RB
    ST RB *0xfe
    LD 0x00 RD

next_trial:
    LD *0xfe RA
    LD 0x1 RB
    ADD
    ST RA *0xfe

    LD *0xff RA
    LD *0xfe RB
    SUB
    JZ found_prime

    LD *0xff RA
    LD 0x00 RC

test_loop:
    SUB
    JZ next_candidate
    JC test_loop # SUB A B => CF set if A >= B, not A < B
    JMP next_trial

found_prime:
    LD *0xff RA
    OUT
    jmp next_candidate

8bitcpu assembler

It simply tokenises an input assembly language file, and converts each mneumonic and (optional) operand into opcode byte and parameter byte.

Emulator

I also wrote an emulator in C++ to ease program development - far quicker to test locally than storing, resetting and running on the actual board!

8bitcpu emulator

This is a snippet of the main loop with opcode switch and two ALU instruction handlers.

Assembling & Emulating

8bitcpu emulator

Here we’ve assembled the prime-test program above, and piped it through into the emulator.

We can see that the emulated computer is executing the instruction at address 0x25, with opcode 0x42, corresponding to a SUB.

In total, the emulated computer has executed 0x190 (400) instructions.

The output register currently displays 7.