# simple-computer: a Go simulation of the scott CPU with 16-bit registers and no stack

> djhworld/simple-computer is a Go simulator of the scott CPU that builds every layer of the machine from NAND gates up to a 65K RAM system with a 240x160-pixel GLFW display, following J. Clark Scott's design from his book 'But How Do It Know?' The go.mod file requires go 1.25 to build, while the README still states go 1.12 as the minimum.

**djhworld/simple-computer** — the scott CPU from "But How Do It Know?" by J. Clark Scott

- Repository: https://github.com/djhworld/simple-computer
- Stars: 2,004 · Forks: 159
- Language: Go
- License: not declared
- Published: 2026-10-11 · Updated: 2026-10-11 · Language: en
- Canonical page: https://hysenlabs.com/projects/djhworld-simple-computer

## The scott CPU and who this simulator is for

J. Clark Scott's book 'But How Do It Know?' walks through computer construction starting from a NAND gate and assembling layers upward: registers, RAM, the ALU, a control unit and I/O devices. djhworld/simple-computer is a Go implementation of every step in that chain, written so the author could see all the parts of the machine interact rather than trust a diagram.

This is not a general-purpose emulator or a production tool. It does not implement any existing CPU architecture, has no documented compatibility with other simulators, and makes no performance claims beyond a clock speed measured on one machine. Its value lies in the direct correspondence between the Go source and the book's schematic: separate packages for alu/, circuit/, components/, cpu/, io/ and memory/ each map to a layer the book describes.

The author wrote it as a learning exercise, noting in the README that the goal was to understand computers at a lower level without studying the physics of digital electronics. A blog post at djharper.dev describes the construction in more detail.

## A single data path connects every component, with no memory protection anywhere

Every transfer between registers, the ALU, RAM and the I/O devices travels through one shared bus, and nothing in the design allows two transfers to occur at the same time. The control unit sequences every operation in turn, which means memory reads, register writes and I/O all contend for the same channel. The top-level directory reflects this structure: alu/, arch/, circuit/, components/, computer/, cpu/, io/ and memory/ each hold a separate layer, with computer/ acting as the integration point above them.

Protection at the memory level is absent. Code loads at offset 0x0500, but that convention is enforced by the assembler and simulator, not by hardware. Any correctly addressed write instruction can reach any location in the 65K address space, including the region below 0x0500, so a misaddressed pointer corrupts silently. For a learning simulator this makes the machine simpler to reason about; for anything that handles data that matters, the absence of memory protection is a hard stop.

The two I/O devices sit at fixed addresses: the keyboard at 0x000F and the display at 0x0007. Both communicate through the IN and OUT machine instructions.

## Why 8-bit became 16-bit: one bus, more RAM

Scott's book uses an 8-bit CPU for clarity, which gives it a narrow address space. This implementation uses 16-bit registers and a 16-bit bus instead. The author's reason is stated directly: more RAM was wanted, and because there is only one system bus, widening the bus was the path to a larger address space. The result is 65K of RAM and four 16-bit registers: R0, R1, R2 and R3.

Clock speed is an observation, not a specification. The README gives it as approximately 0.006 MHz on the author's machine, which is the only machine mentioned. Screen resolution is 240 by 160 pixels, rendered through a GLFW window. Neither figure is a design target or a guaranteed minimum on another host.

This widening also means programs written for the book's 8-bit design do not transfer directly. Anyone following the book alongside the code will find that register widths, address sizes and instruction operands all reflect the 16-bit decision rather than the book's 8-bit original.

## Building needs GLFW, and the README's go 1.12 figure conflicts with go.mod

Two external dependencies are required: Go and GLFW 3.2 or newer. Running make from the repository root compiles three binaries:

```bash
make
```

This builds bin/simulator, bin/assembler and bin/generator. The simulator depends on github.com/go-gl/glfw for the display window and keyboard input, and on github.com/go-gl/gl for rendering. Tests run separately:

```bash
make test
```

Running make test exercises the full component stack and finishes in 30 to 45 seconds.

The README's build requirements section lists go 1.12 or newer, but the go.mod file at the repository root declares go 1.25, and those figures are not compatible with each other. Go's module system will reject a toolchain below the version named in go.mod, so a developer who follows the README's figure will get an error before compilation begins. The go.mod file is the authoritative requirement, and go 1.25 is the minimum that will actually build the project.

## Machine instructions, a pseudo instruction, and three gaps that matter for arithmetic

The instruction table in the README lists 18 rows across two types. The machine instructions are LOAD, STORE, DATA, JR, JMP, conditional JMP[CAEZ]+, CLF, IN, OUT, ADD, SHR, SHL, NOT, AND, OR, XOR and CMP. One row, CALL, is labeled as a pseudo instruction because it relies on the programmer to arrange the return jump rather than relying on a hardware mechanism for saving the return address.

Conditional jumps read from the flags register, which CMP populates. Flags are C, A, E and Z, and multiple flags can appear in a single condition: JMPEZ jumps when the equal or zero flag is set. CLF clears the entire flags register.

Three gaps in the set have the largest impact on general programming. ADD is the only arithmetic instruction provided, so subtraction must be constructed by applying NOT to one operand and then using ADD, with no macro or shorthand in the assembler to compress that into one line. Moving a value between registers requires a store to memory followed by a load, because no MOV instruction exists in the set. Floating point arithmetic has no hardware support at all.

## CALL has no stack beneath it, and keyboard response requires a polling loop

CALL is a pseudo instruction because nothing in hardware saves state when it runs. Jumping to a subroutine works, but returning from it depends on the programmer placing a jump instruction back at the subroutine's end. Register state is not preserved either: anything the subroutine modifies stays modified after the return. A subroutine that calls another subroutine will overwrite register state in ways that depend on what the inner routine does, and recovery is up to the programmer.

Interrupt support is also absent. To handle keyboard input, a program must check address 0x000F on every iteration of its main loop. A tight polling loop blocks all other computation; loosening the loop to allow computation between polls risks missing keystrokes. Neither approach is clean, and both are direct consequences of the absent interrupt mechanism. Scott's book has a section on extending the CPU with interrupt hardware, but the simulation does not implement it: the README describes the required additions as a large amount of further wiring.

## An LLVM compiler from a contributor, and assembly programs the author calls bad

A developer with the GitHub handle @realkompot built an LLVM-based compiler for the scott CPU architecture, hosted separately at github.com/realkompot/llvm-project-scott-cpu, which produces binaries the djhworld simulator can run. A snake game built with that compiler is linked from the README. Neither the compiler nor the game is part of this repository.

A working LLVM backend for a machine that lacks subtract and a stack says something about the instruction set's coverage. Even with those constraints, the set is sufficient for a compiler to generate programs that run.

The repository includes example programs under _programs/ that the author wrote in assembly. The README describes these as very bad code, adding that writing them affected the author's sanity, which is an accurate account of hand-written assembly for a minimal instruction set without shorthand. Running one shows the simulator in action:

```bash
./bin/simulator -bin _programs/brush.bin
```

The assembler, built by make as bin/assembler, processes the text assembly format described in cmd/assembler/ for anyone writing new programs.

## Conclusion

simple-computer is worth building if you have read 'But How Do It Know?' and want to step through the CPU it describes. It is not useful as a general emulator: no floating point, no stack, no subtract instruction, and no interrupts mean that writing anything non-trivial requires working around fundamental gaps. Before you build, confirm your Go installation is 1.25 or newer, because go.mod sets 1.25 as the minimum despite what the README states. Install GLFW 3.2 or newer for the display window, then run the example programs under _programs/ to see the simulator in action.

## FAQ

### What is djhworld/simple-computer?

It is a Go simulation of the scott CPU from J. Clark Scott's book 'But How Do It Know?' The project builds the machine from NAND gates up through registers, RAM, the ALU, a control unit and I/O devices, all sharing a single system bus.

### What Go version does simple-computer require to build?

The go.mod file declares go 1.25. The README states go 1.12 as the minimum, but the module file is authoritative, and a Go installation below 1.25 will fail before compilation begins.

### How do I build and run simple-computer?

Install Go 1.25 or newer and GLFW 3.2 or newer, then run make from the repository root to build bin/simulator, bin/assembler and bin/generator. Start the simulator with a binary program using ./bin/simulator -bin followed by the path to the .bin file.

### Does simple-computer support interrupts or a call stack?

Neither. Programs that respond to keyboard input must poll address 0x000F in a loop, and CALL instructions cannot be nested because no stack saves the return address or preserves register state.

### Can simple-computer run programs compiled from a higher-level language?

A contributor named @realkompot built an LLVM-based compiler for the scott CPU instruction set, hosted at github.com/realkompot/llvm-project-scott-cpu. It produces binaries the simulator can execute, and the README links to a snake game example built with it.

## Sources

- [djhworld/simple-computer on GitHub](https://github.com/djhworld/simple-computer)
- [Issues](https://github.com/djhworld/simple-computer/issues)
- [README](https://github.com/djhworld/simple-computer/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/djhworld-simple-computer
