Emacs: History, Lisp, and the Editor-as-Program
Why Emacs Was Created
Section titled “Why Emacs Was Created”Emacs grew out of a particular pain at the MIT AI Lab in the mid-1970s. The PDP-10s there ran ITS, and the default editor was TECO — Text Editor and Corrector — a character-oriented command language so terse it was nearly unreadable. Users extended TECO by writing macros, and by 1976 there were two rival macro collections floating around the lab: TMACS (Guy Steele, Dave Moon, and others) and TECMAC (John Kulp, Richard Bryan). Someone — it appears to have been Guy Steele — started a project to merge and clean them up, and within a month or two Richard Stallman had taken over as the primary coder. The name “Emacs” (short for “Editor MACroS”) was Stallman’s suggestion.
The original EMACS was still a pile of TECO macros. The real lesson, as Stallman later put it at the International Lisp Conference in 2002, was that a macro language bolted onto an editor as an afterthought is always going to be the wrong shape; the extension language needs to be a real programming language from the start. Bernie Greenberg proved that around 1978 with Multics Emacs, written entirely in MacLisp, where extending the editor was just writing ordinary Lisp. Greenberg reported that even the secretaries in his office picked it up without realizing they were programming.
That’s the crucial insight: Emacs’s real innovation wasn’t the keybindings or the buffer model. It was the idea that an editor should be a live programming environment whose extension language is the same language the editor is written in, all the way down.
From there: Dan Weinreb’s EINE (1976, first Lisp-based Emacs, on the MIT Lisp Machine), ZWEI (1978), ZMACS (running on Symbolics and LMI hardware), then James Gosling’s Gosling Emacs in C for Unix around 1981, with a half-Lisp called Mocklisp. Stallman started GNU Emacs in 1984, initially borrowing some Gosling code, but he ripped out Mocklisp and replaced it with a real Lisp interpreter — which required rewriting most of the codebase. Version 13 shipped on March 20, 1985, as the first program released by the nascent GNU Project. It remains under active development — version 30.2 shipped in August 2025 — making it one of the longest continuously-developed software projects in existence.
Emacs’s Place in the Lisp Ecosystem
Section titled “Emacs’s Place in the Lisp Ecosystem”Emacs is not formally part of any Lisp implementation, but it’s probably the single most important reason Lisp is still a living language used by working programmers rather than a historical curiosity. Emacs Lisp (elisp) is its own dialect — dynamically scoped by default until relatively recently, distinct from Common Lisp and Scheme, with heavy editor-specific primitives (buffers, points, markers, overlays, hooks). It’s also the de facto development environment for Common Lisp (via SLIME), Scheme (via Geiser), Clojure (via CIDER), and Racket. So Emacs occupies two seats at the Lisp table: as a Lisp program in its own right with millions of users, and as the IDE through which most other Lisps are driven.
Is Emacs Part of the REPL, or Run From It?
Section titled “Is Emacs Part of the REPL, or Run From It?”This is the sharp question, and the answer depends on which Emacs and which direction you look.
In Multics Emacs (1978), the relationship was straightforward: Emacs was a MacLisp program running inside the MacLisp REPL. You launched Lisp, loaded Emacs, and the editor was just a large set of Lisp functions. You could drop back to the REPL at any time.
In GNU Emacs, the architecture inverted. The core — redisplay, buffer primitives, I/O, the garbage collector — is written in C. The Lisp interpreter is a subsystem of Emacs, not the other way around. So GNU Emacs contains a REPL rather than running within one. You can open it with M-x ielm (Inferior Emacs Lisp Mode), or evaluate expressions directly in the *scratch* buffer, or eval any sexp in any buffer with C-x C-e.
But here’s the conceptually important part, and it’s directly relevant to your KN-86 work: every command invocation in Emacs is a read-eval-print cycle. When you press C-x C-f, Emacs reads the keystrokes, looks them up in the current keymap, finds the symbol find-file, and calls eval on that function. The editor is a REPL whose input is keystrokes rather than typed sexps, and whose keymap is a reader that turns key sequences into Lisp function calls. That’s exactly the architecture your 30-key Lisp-primitive keyboard points at — the keymap-as-reader is the thing Emacs made load-bearing.
Most Common Functions and How Configuration Works
Section titled “Most Common Functions and How Configuration Works”The everyday elisp vocabulary is smaller than people expect. Buffer manipulation runs through insert, delete-region, point, goto-char, buffer-substring, save-excursion. Searching goes through search-forward and re-search-forward. Commands are defined with defun and marked user-callable with the (interactive ...) declaration. Keybindings are installed with global-set-key, local-set-key, and define-key, using kbd to parse human-readable keystrokes. Event wiring runs through add-hook and run-hooks. Modes are defined with define-derived-mode and define-minor-mode.
Configuration lives in ~/.emacs.d/init.el (or the older ~/.emacs), which is just a Lisp file evaluated at startup. You set variables with setq, declare user-facing options with defcustom, and the graphical Customize interface writes back into the init file via custom-set-variables. Modern configurations almost universally use the use-package macro, which wraps package installation, autoloading, keybindings, and hook setup into one declarative form. The config is a program — there’s no separate config language, which is the Multics Emacs insight playing out forty-five years later.
Implementing a Micro-Emacs in Fe
Section titled “Implementing a Micro-Emacs in Fe”Fe gives you a tight working set: fn, mac, =, let, if, while, do, cons/car/cdr, list, is, atom, print, arithmetic, and the cfunc bridge for calling into C. No file I/O, no terminal control, no display — you wire those in yourself. That’s actually the right shape for the KN-86, because the firmware layer (nOSh) already owns the screen and keyboard, and you’d expose primitives rather than give scripts raw hardware.
A minimal Emacs-shaped editor in Fe has five moving parts:
The buffer. Simplest representation is a pair of lists: characters before point and characters after point (the classic “gap buffer as two stacks” trick). Insert pushes onto the before-list; forward-char moves one cell from after to before; backward-char does the reverse. Everything else is expressible in those terms.
The point. Implicit — it’s the boundary between the two lists. point returns the length of the before-list.
The C bridge. You expose roughly six cfuncs: read-key (blocks for a keystroke, returns a symbol), clear-screen, move-cursor, put-char, get-size, and flush. Everything else — line wrapping, scrolling, status line — is pure Fe.
The keymap. An association list from key symbols to Fe functions, which is literally how GNU Emacs stores keymaps under the hood. (define-key map 'C-f 'forward-char) becomes a cons onto the alist.
The command loop. Three lines of Fe conceptually:
(while running (let ((k (read-key))) (eval (cdr (assoc k keymap)))) (redisplay))That’s the whole editor. Redisplay walks the buffer list, emits characters through put-char, positions the cursor at point, and flushes. A mode is a keymap plus a hook function run on buffer creation. A “cartridge” in your KN-86 sense could ship as a Fe source file that extends the keymap and adds commands — which is exactly the Emacs extension model, scaled down to a handheld.
The thing worth stealing from Emacs is not the codebase — it’s the doctrine. The editor and the extension language are the same system. Every keystroke is a function call. The config is a program. The primitives are the same primitives the user writes in. For a 30-key Lisp keyboard driving a capability-based firmware, that lineage fits almost suspiciously well.