I now have pForth 2.0.1, Phil Burk’s portable Forth, running on my VAXstations under
OpenVMS VAX 7.3. This is an ANS-style Forth with 32-bit cells, double-cell words, local
variables, floating point, file words and a SYSTEM word that runs a DCL
command. It is built with DEC C, installed for every user with an installer and
an uninstaller, and runs as an ordinary unprivileged user. Alongside it is the
VAX-11 fig-Forth of 1985, assembled from its MACRO-32 source without a change.
Claude Code assisted, which is to say, did much of the hard work. (This seems to be my tagline these days.)
This one needed small source changes, so the download folder has the whole story: the upstream archive with its hash, five patches of a few lines each (kept apart, with the reason for each), and a recipe to recreate the kit from nothing. No existing Forth word was changed (three new ones were added); the patches are about the C compiler and C library of a VAX.
Downloads, with SHA-256 sums in SHA256SUMS.TXT:
- FORTH_VAX_201.BCK (968 KB): the kit
to install (a
BACKUPsaveset). - FORTH_INSTALL.COM, FORTH_UNINSTALL.COM, FORTH_SETUP.COM (each user runs it) and FORTH_STMLF.COM (a small helper, see “One thing to know”).
- README.TXT, the licence PFORTH_LICENSE.TXT and the provenance SOURCES.TXT.
- Source kit:
the upstream archive, the patches, the build and kit procedures, the lifecycle
test, the fig-Forth sources and
RECREATE.md. - Examples: five programs with their expected output and the test procedure.
- The file list of the saveset: FORTH_VAX_201_LIST.LIS.
Licence
pForth is under the zero-clause BSD licence (“Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted”), so the kit can be passed on freely. The installer, uninstaller, setup procedure, patches and examples are mine, under the same terms. The 1985 VAX-11 fig-Forth was released into the public domain by the Central Iowa Forth Interest Group; its notices are kept in the source.
Which Forth
I went looking for a Forth that builds on a VAX under VMS 7.3 with DEC C 6.4 (there is no VAX C, and no GCC here), is reasonably standard, and is still maintained. All checked 14-OCT-2026:
| Version | Where | What I did |
|---|---|---|
| pForth 2.0.1 (Jan 2023, “ANS-like”, ANSI C, 8,400 lines) | github.com/philburk/pforth | built: the only candidate that needs neither GCC nor Unix |
| fig-Forth 2.0 for VAX-11 (1985, MACRO-32) | DECUS VAX85 collection | built unchanged, installed as FIGFORTH |
| pfe 0.30 / 0.32 (Portable Forth Environment) | DECUS CDs | not built: no VMS port, autoconf and libtool, dlopen, GCC-isms |
| Gforth 0.2 - 0.4 | DECUS CDs | not built: needs GCC; its own to-do list names VMS as unported |
| TILE Forth 2.1 (1990, Forth-83) | DECUS CDs | not built: Forth-83 only, Unix-oriented |
| C-Forth | DECUS CDs | not built: no DOES> |
I found no Forth with a VMS build anywhere on the web.
What was built
- Source:
pforth-2.0.1.tar.gzfrom thev2.0.1tag (the newest release when I looked, 15-OCT-2026), 139,439 bytes, SHA-256f4c417d7d1f2c187716263484bdc534d3224b6d159e049d00828a89fa5d6894d. - Compiler: DEC C V6.4,
CC/NAMES=AS_IS/FLOAT=G_FLOAT/OPTIMIZE, a short DCL procedure instead of the Unix makefile. - Measured on the VAXstation 4000/90 (71 MHz KA49), in a batch job: 15
modules, 0 compile failures, 1 minute 6 seconds; link 1.4 seconds;
PFORTH -i system.fthbuilds the 125-block dictionary in 7 seconds.PFORTH.EXEis 106 blocks. The whole thing: 1 minute 13 seconds. Compiling without optimisation took one minute: the build is not the bottleneck. - The kit: 157 files, 1,552 blocks installed, the
BACKUPsaveset 1,890 blocks; the lifecycle test (install, refuse a second install, uninstall with a changed file kept, reinstall, replace twice: 21 checks) takes 2 minutes 16 seconds and the install itself 19 seconds on the 4000/90; 3 minutes 47 seconds and 33 seconds on the 4000/60.
The source changes
Five patches, all small, all guarded so that the Unix build is unchanged:
- No
<stdint.h>(17 lines,pforth.h). DEC C for the VAX has no such header and no 64-bit integer type.PF_NO_STDINTsupplies the types; pForth testssizeofwhere it matters, souint64_tbeing 32 bits is handled. - Three new words (38 lines,
pfcustom.c):SYSTEM ( c-addr u -- n )runs a DCL command in a subprocess and returns its status;CLOCK ( -- ticks )is the process CPU time in 10 ms ticks andCLOCKS/SECis 100. The portable platform has no shell or timer word, and a Forth on VMS wants both. sdSleepMillis(20 lines). The “stdio” platform I/O file of pForth 2.0.1 does not define it, so that platform does not link; on VMS it isLIB$WAIT.- No
round()(5 lines). The VAX C library lacks the C99 function thatFROUNDuses; half away from zero, likeround(). RESIZE-FILEcannot shrink a file (8 lines). Making a file shorter in ANSI C is done withfreopen(NULL, ...), which crashes the DEC C run-time library with an access violation. Extending a file works; shrinking reports an error.
Two things in the build that were not code: DEC C resolves #include "../x.h"
against the current default directory rather than the including file’s
directory (the two files in csrc/stdio are compiled from their own
directory), and /NAMES=AS_IS means the VMS routine must be written LIB$WAIT
in capitals.
pForth’s own tests
pForth ships test files, and I ran them on the VAX under the same procedure as on a Linux PC:
| Test | VAX 7.3 | Linux (same patched sources) |
|---|---|---|
T_COREX.FTH | 124 passed, 0 failed | 124 / 0 |
T_STRINGS.FTH | 58 / 0 | 58 / 0 |
T_LOCALS.FTH | 9 / 0 | 9 / 0 |
T_ALLOC.FTH | 1 / 0 | 1 / 0 |
T_FLOATS.FTH | 59 / 1 | 60 / 0 |
T_FILE.FTH | 88 / 6 | 94 / 0 |
- The float failure is
0.0 -0.0 0.0 F~. VAX floating point has no negative zero;-0.0is just0.0. A difference of the machine, not a bug. - The six file failures are
RESIZE-FILEto a smaller size (patch 5) and the checks that depend on the shrunk file.
One thing to know
SAVE-INPUT and RESTORE-INPUT need Stream_LF files. I found this because
T_COREX.FTH stopped silently halfway on the VAX. pForth’s RESTORE-INPUT works
out the position of the start of a line by subtracting the line length from the
file position, which is right when a position is a byte offset (a Stream_LF
file) and wrong for the variable-length record files that UNZIP, EDT and
TYPE redirection produce. Plain INCLUDE and INCLUDED of such a file work:
I tested a record file with definitions, blank lines and both kinds of comment,
and everything loaded.
So: ordinary Forth source in any VMS format is fine; a program that uses
SAVE-INPUT/RESTORE-INPUT (the Hayes test suite does) needs its source in
Stream_LF. The kit’s own .FTH files are Stream_LF already, and
FORTH_STMLF.COM converts yours:
$ @SYS$COMMON:[SYSLIB.FORTH]FORTH_STMLF MYPROG.FTH
I did not change pForth’s file-position logic.
Installing and using
You need OpenVMS VAX 7.3 and SYSTEM (or SYSPRV). About 3,500 free blocks.
- Put the saveset and the two
.COMfiles in one directory on the VAX (.BCKin binary,.COMin ASCII; the installer puts back the saveset’s record attributes if your transfer lost them). $ @FORTH_INSTALLinstalls intoSYS$COMMON:[SYSLIB.FORTH], writes a manifest and runs a smoke test in a subprocess.@FORTH_INSTALL "" REPLACEreplaces an installation. Nothing outside that directory is touched: noINSTALL, no rights identifier, no startup file, no system logical name.- Each user:
$ @SYS$COMMON:[SYSLIB.FORTH]FORTH_SETUP(or put it inLOGIN.COM), then$ PFORTH. Quote switches, because DCL upper-cases them:PFORTH "-q" MYFILE.FTH. $ @FORTH_EXAMPLES:RUN_EXAMPLESruns the five examples and compares them with their expected output.- To remove it:
$ @SYS$COMMON:[SYSLIB.FORTH]FORTH_UNINSTALLdeletes only the files its manifest recorded, and keeps any that you changed.
$ PFORTH
PForth V2.0.0, LE/32, built Oct 14 2026 20:26:43
pForth loading dictionary from file PFORTH_DIC:PFORTH.DIC
Cell Size = 4
: SQUARE DUP * ; 7 SQUARE . CR 49
ok
Stack<10> s\" WRITE SYS$OUTPUT F$GETSYI(\"VERSION\")" system . cr
V7.3 65537
ok
Stack<10> bye
(The banner says V2.0.0 although this is the v2.0.1 tag, commit 3956a7fd: upstream
left PFORTH_VERSION_NAME at “2.0.0” in csrc/pf_guts.h, so this is not the wrong source.)
The fig-Forth is FIGFORTH after the same setup. It is the real thing: upper
case, no recursion by name inside a definition, loops only inside definitions,
and a HELP word command that reads its own help library.
Examples
The five programs in examples/ are a tour of what is different on a 32-bit
VAX with VMS file names:
STACK.FTH: factorial, Fibonacci and GCD, the cell size (4 bytes, 32 bits,-1 U.is 4294967295) and double-cell arithmetic: 20! = 2,432,902,008,176,640,000 in two cells, withUM*.DEFWORDS.FTH:CREATE ... DOES>: an array that checks its index and throws, a table-defining word and a traffic-light state machine.SIEVE.FTH: the Byte magazine sieve, 8,190 flags, 10 passes. Measured on the 4000/90: 417 ticks = 4.17 seconds of CPU; on the 4000/60: 680 ticks = 6.80 seconds, 1,899 primes per pass.FILEIO.FTH: writes a file inSYS$SCRATCH:, reads it back withREAD-LINE, counts 5 lines, 20 words and 107 characters, deletes it.VMSDCL.FTH: DCL from Forth:F$GETSYI("VERSION"),TYPE,SEARCH,DELETE, each with its status.
All five pass as USER1 (a test account with only standard user privileges)
on the 4000/90 and the 4000/60, from the installed
kit (10 seconds for all five on the 4000/90, 17 seconds on the 4000/60).
Findings from writing them: an interpreted S" in pForth is a transient
buffer, so a 2CONSTANT of one holds garbage (I got CREATE-FILE to make a
file called .; use a colon definition); S" does not understand \", S\"
does; and BYE inside an included file prints an INCLUDE error banner, so the
examples simply end and let the end of input exit.
What I did not do
No test on a simulator or an Alpha; no VAX C build; no saved user dictionary
(SAVE-FORTH) on VMS; the Hayes coretest.fth suite was not run (pForth’s own
tests were).
