GNU Emacs 19.28 and 21.2 for VAX OpenVMS 7.3
I have been running GNU Emacs on my VAXstations under OpenVMS VAX 7.3, and today there is a kit other people can install: Emacs 19.28 and Emacs 21.2, terminal-only, side by side, with an installer and a regression suite. Both were built on a VAXstation 4000/96, installed from the kit on a VAXstation 4000/60 and on a simulated MicroVAX 3900, and tested on all three.
Claude Code assisted, which is to say, did much of the hard work.
Downloads, with SHA-256 sums in SHA256SUMS.TXT:
- Binary kit
(32 MB): two BACKUP savesets, the installer, the test suite, and a
README.TXTthat says everything below in more detail. - Source kit (67 MB): the complete source of both versions as built, plus the two unmodified upstream archives they came from.
- README.TXT on its own, and SHA256SUMS.TXT.
Emacs is GPL; the source kit is the corresponding source.
Where the two versions come from
- 19.28 is Richard Levitte’s 1996 VMS kit. It is the version that has been on VMS the longest and it still runs well on a small VAX.
- 21.2 is Thien-Thi Nguyen’s VMS port of Emacs 21, in the form Eric Brown brought up on OpenVMS x86-64 in 2025-2026. Eric’s tree is the base; the VAX work below is on top of it. 21.2 has font-lock, a text-mode menu bar, and twenty-five years of Lisp the older version lacks.
Nothing in either tree was rewritten. The changes are patches, each one a separate commit with the symptom, the cause and the fix in its message.
Installing
You need OpenVMS VAX 7.3 with TCP/IP Services (Emacs links against it), about 200,000 free blocks on the system disk, and a VT-series terminal or an emulator of one. No compiler is needed for the binary kit.
Get the kit’s files into one directory on the VAX. Transfer modes matter: the two
.BCKsavesets in binary, every.COM,.ELand.TXTin ASCII. (UnZip for VMS gets both right by itself if you unzip on the VAX.) A procedure sent in binary arrives as 512-byte fixed records and DCL refuses it with%RMS-W-RTB; the kit is not damaged, send the text files again in ASCII.As
SYSTEM:$ SET DEFAULT dev:[the.kit.directory] $ @EMACS_KIT_INSTALL CHECK ! says what it would do $ @EMACS_KIT_INSTALLThe installer repairs the savesets’ record attributes (they always arrive wrong), restores both versions under
SYS$SYSDEVICE:[GNU...], copies the shared procedures, offers to add the two lines yourSYLOGIN.COMneeds (it asks first; it never editsSYSTARTUP_VMS.COM, it prints the one line to add there yourself), and then runs the regression suite. Its exit status is the suite’s.Log in again.
EMACS19andEMACS21start a kept editor: a subprocess that survivesC-z, soEMACS21a second time re-attaches with your buffers intact.RUNEMACS19andRUNEMACS21run Emacs directly. PlainEMACSis 19.28 unless the logical nameGNU_EMACS_DEFAULTis21.
Installing the kit twice, or over an older copy of it, is safe: every file ends up as exactly one version. (The first version of the installer got that wrong; see below.)
Testing it
@SYS$SYSDEVICE:[GNU]EMACSTEST ALL from your own login runs the batch
suite against both versions: 254 checks each, in twenty groups – VMS
file versions and names, logical names, record formats, completion,
dired, access checks that depend on your privileges, synchronous and
asynchronous subprocesses, timers, GC, byte compilation, the editing
primitives – and reports PASS/FAIL per check and a summary line.
Every check was shown to fail at least once before it was trusted: the
suite has a switch that negates any named check, and the checks that
guard a specific fix were run against the build that lacked it.
There is also a second tier of scripted full-screen tests (a kept editor,
M-!, M-x shell, completion, function keys, two kept editors at once)
that drive a terminal from a host over telnet and assert on the rendered
screen. Those are not in the kit; they are in the project repository.
What was changed, and why
Emacs 21.2 – ten changes. Eight are VAX-only and leave Alpha and x86-64 alone:
- The compile switch
/OPTIMIZE=(LEVEL=5,TUNE=HOST)is Alpha’s; the VAX compiler rejects it. <libicb.h>does not exist on VAX and nothing used it.- The VAX compiler has no 64-bit integer type. The port used
unsigned __int64for a timer request id andunsigned long longfor the privilege mask; both became longwords (the mask as two of them, as 19.28 did it). - The same in
vmsproc.c(long longdelta time, done withLIB$EMUL), plusXPROCESS()applied to something that was already a struct pointer – a type error every compiler should have caught. - The dump step aborted with “unhandled overlap”: the VAX linker puts the BSS markers beside DATA, so the BSS range duplicated DATA.
- A function name from VSI C on x86-64 had been typed into the source;
on VAX it is plain
sys_access. make-docfilecommands were too long for DCL when the build directory was deep, and one batch of docstrings was silently left out ofDOC.- All platforms: the last record a subprocess wrote before exiting
was dropped.
M-! SHOW TIMEprinted nothing. - All platforms: under
-batch, file access was checked as the account that built Emacs, because the UIC is cached in a static that the dump file preserves. - The one that took a day: Compaq C for VAX inlines a function that
calls the
allocabuiltin, then keeps using SP-relative slots after SP has moved. Every file-name completion –C-x C-f TAB,RETin dired – jumped into data and killed Emacs. That buffer now comes from the heap. 19.28 is immune because it uses Emacs’s ownalloca.c.
Plus VMS terminal names in termcap, and a vt500.el so that on an
LK-style keyboard without an Escape key, F11 acts as ESC.
A traditional C preprocessor (tradcpp, shipped in the 21.2 kit) is used
instead of Compaq C’s, which puts a space between every token and wrecks
the generated MMS files.
Emacs 19.28 needed build-system repairs, the same dropped-record fix as
number 8, and one more: close() was being called on an index into
Emacs’s own mailbox table, so emacs -batch -l file.el closed the file it
was loading and died at the next read.
Since the first kit (which I never published), the subprocess path got a proper going-over, with a trace compiled into a test build to see what it was doing rather than guessing:
M-x shellin 21.2 was slow – a 600-fileDIRECTORYtook eleven seconds whereM-!of the same command took one. The trace showed no waiting at all: each read returned one DCL record and Emacs spent about fifty milliseconds of its own CPU per record on filters and redisplay. A read now drains the records that follow. 1.8 seconds, faster than 19.28.- A one-shot asynchronous subprocess never exited: DCL, given both a command and an input device, kept reading for another command, so no sentinel ever ran and the process quota filled up. Both versions.
- Behind that, three more in 21.2: output lost at exit,
-batchpty records truncated to ten characters (the batch “frame” was ten columns wide), anddelete-processhanging because the 21.2 port had regressed SIGKILL from$DELPRCto$FORCEX, which cannot stop a DCL sitting at its prompt. - In 19.28, a shell’s exit sentinel ran only at the next keystroke: after the exit AST, a drain read waited on a dead pty until any other event.
- 21.2 reported a failing command’s VMS completion code as a Unix signal
(“user defined signal 1”); it now shows the code and its message text.
In 19.28,
RETin dired said “Buffer is read-only” (the keymap never bound it).start-processwith no argument crashed the image.
Rebuilding it
The source kit’s emacs-21.2-vax-source-*.zip is the whole tree as built.
Unzip it under your own account on a VAX with Compaq C and MMS, then
$ SUBMIT/NOPRINT/LOG_FILE=SYS$LOGIN:E21.LOG -
/PARAMETERS=(CONFIGURE) dev:[dir]BUILD_EMACS21.COM
configures and builds it (about 18 minutes on a 4000/96, 5 on a
simulator), and /PARAMETERS=(INSTALL) as SYSTEM installs it. The
install prefix is compiled in, so a kit built with one prefix runs only
from that prefix. Building 19.28 is documented in the repository; its
generated makefiles need a repair script before MMS will accept them.
The builds are reproducible in the sense that matters here: the same commit built on the simulator and on real hardware gives the same warnings in the same numbers and an image and dump of the same size, and this was checked every time anything changed.
Known limitations
- No X or DECwindows: terminal only, by design.
- Neither version notices a terminal window resize;
M-x set-screen-widthorC-lafter resizing. - 21.2, unprivileged accounts:
file-writable-pon your own home directory says nil although writing there works. - 19.28:
run-at-timenever fires in batch mode – it needs a helper program the VMS build never included. - Output captured from a subprocess ends with one extra blank line.
- The link reports
%LINK-W-MULDEFfor the allocator and directory routines: Emacs’s own versions replacing the C library’s, by design.
How this was done
Two VAXstations, one simulator, and a small team of Claude sessions: one that plans and reviews, and one fresh session per run with a written brief. The rule that made it work was that nothing counts until a script fails without the fix and passes with it, on the simulator and on real hardware, with the transcripts kept. Several of the fixes above were found by tests that were written to fail first; the last one before release – the kit’s own self-test failing for every account except the one that installed it – was caught only by logging in as an ordinary user and running exactly what the README says, with nothing else staged. The repository with the tooling, the test suites and every review will be public in due course.
Credits: Richard Levitte for the 19.28 VMS port, Thien-Thi Nguyen for the 21.2 VMS port, and Eric Brown for the OpenVMS x86-64 work the 21.2 base comes from.
