Silicon observatory / No. 005

Inside SCHEME-79.

A Lisp interpreter etched into one chip in 1979. It has no instruction set: the program is list structure in memory, the type bits are the opcodes, and the garbage collector is microcode.

1979INTRODUCED
32 bitWORD
≈1 MHzCLOCK
not publishedTRANSISTORS
READY

02 / The chip

One bus, ten registers
MICRO PLA · STATE776boot-load— NANO PLA · OPERATIONNOPcycle 1 of 1 32-BIT BUS00000000— PADS MEMORYADDRESS0HALF—
CYCLES0
CHIP TIME0 µs
MICRO-WORDS0
COLLECTIONS0

03 / Pads

Last 48 cycles · newest right

A memory reference is two cycles: ALE latches the node address off the bus, then READ or WRITE moves a word, with CDR choosing the half (Figs. 8 and 9). Tick slow memory to see FREEZE hold each access for a cycle.

04 / Heap

symbol celllist dataS-codeclosure · control pointnumberbroken heartmarked in usefreeNEWCELLMEMTOP

07 / The microcode

AI Memo 559's appendix, as printed. Highlight = the line behind the current micro-word. Click a routine's first line to set a breakpoint on it; the octal number is the MICRO state this page's compiler gave it.

Scheme workbench

Ready.
Scheme quick reference

The host translates this dialect into S-code. Numbers are 24-bit and non-negative; characters are #\a. nil and () are the all-zero word. t is NIL retyped as an immediate, which makes it the same word as 0 — and every non-NIL value counts as true.

(define (f x . rest) …)
(define x 42)
(lambda (a b) …)
(lambda args …)
(let ((a 1)) …)
(let loop ((i 0)) …)
(if p c a) (cond …)
(and …) (or …) (not x)
(begin …) (set! x v)
(catch k … (k v) …)
(apply f list)
car cdr cons rplaca rplacd
eq pointer? type? type!
zerop 1+ -1+ (or 1-)
(gc)
(write-char #\a)
(print-number 42)

'sym '(1 (2) . 3)
null?

Primitives are opcodes, not values: (map car l) needs (lambda (x) (car x)). cons with more than two arguments is list*, because of how the chip collects operands. pointer? is the chip's not-atom test, and it is true of symbols too: a symbol is a pointer to its value cell. or returns T, not the value that was true.

Define *interrupt-handler* with a rest parameter to receive the interrupt point; call it to resume. *gc-handler* is (lambda k (gc) (k)) unless you redefine it.

Model & limitations

The microcode is the memo's. Every routine in the appendix of AI Memo 559 is transcribed as printed and runs — the evaluator, the storage allocator, the Deutsch–Schorr–Waite marker, the two-finger compacting sweep and the broken-heart relocation. primitive-type! is printed as (assign *exp* *val*) without a fetch; the compiler reads a bare register as a fetch. primitive-displacement-add1 saves NEWCELL in ARGS and never puts it back, as printed; the translator never emits it.

The compilation is this page's. The memo describes how micro-LISP became vertical micro-words and horizontal nano-words (p.12–15) and prints a few of each, but not the PLA contents. This page compiles the listing the same way — nested expressions through INTERMEDIATE-ARGUMENT, even/odd branch pairs with copied micro-words, return addresses at pointer-type codes — into 343 micro-words. Cycle counts are therefore close to the real chip's, not equal to them. The FRAME/DISPLACEMENT split of 12 + 12 bits is also this page's choice; the memo only requires displacement to be the high field.

Checked two ways. A second machine runs the micro-LISP directly, statement by statement, as the memo's authors did in Lisp (p.12). The test suite requires the compiled chip to reach the same registers at every dispatch and make the same memory writes in the same order.

Cycle level, not gate level. One step of the clock is one bus transfer; the two clock phases, the precharge and the electrical behaviour are not modelled. The two silicon bugs the memo reports — a rare one in the GC microcode it does not describe, and a race on the GC-needed pad — are not reproduced.

The host is invented. The real chip was loaded and driven by an MIT Lisp Machine through Howard Cannon's interface board (p.22). Here the page translates Scheme to S-code (the memo does not publish its translator), loads memory, answers READ-INTERRUPT with the address of *boot*, *gc-handler* or *interrupt-handler*, and turns GC-NEEDED into an interrupt. It holds user interrupts back while a GC interrupt is in progress, which the memo leaves to "external I/O locations". The console is the page's own. A dispatch on a type with no microcode stops the page with a message; the real chip would run whatever micro-word sat at that state.

Keyboard: Space run/pause, S step, B back, R reset, I interrupt. Back undoes up to 512 manual steps; running clears that history.

08 / The S-code

Types are opcodes · octal

09 / What it got right, and what it got wrong

In the memo's own words, pp.22–24

What it got right

What it got wrong