------------------------------
Primeval Forth on the Web
------------------------------
Brad Nelson
April 25, 2026
A tremendous community works to keep IBM 1130 history alive.
Thank you Bob Flanders, Carl Claunch, Charles Anthony, Paul Hardy!
And the many archivists out there!
------------------------------------------------------------
https://bitsavers.org/pdf/ibm/punchedCard
/Keypunch/029/A24-3332-3_29_Reference_Man.pdf
https://bitsavers.org/pdf/ibm/punchedCard
/Keypunch/029/225-3357-3_29_FE_Maint_Man_Nov70.pdf
https://bitsavers.org/pdf/ibm/1130
/A26-5717-0_1130_Operating_Procedures_1968.pdf
https://www.tnmoc.org/ibm1130
https://www.forth.org/svfig/kk/02-2018-Claunch.pdf
https://www.forth.com/resources/forth-programming-language/
https://github.com/monsonite/1968-FORTH
https://gitlab.com/unused0/protoforth
https://groups.google.com/g/ibm1130/c/U46_pE5-iQk
http://media.ibm1130.org/E0024.pdf
http://media.ibm1130.org/E0010.pdf
https://www.columbia.edu/cu/computinghistory/1130.html
https://www.columbia.edu/cu/computinghistory/2250.html
IBM 1130 Forth - The first "FORTH"
----------------------------------
* Evolution of Chuck's earlier interpreters
* First Forth called "FORTH" - 1968
- Due to the limitations of the DMS file system
* Chuck kept a listing!
- Bob Flanders corresponded with Chuck in 2011
- Received 11 pages of assembler and 5 pages of Forth
- Charles Anthony got it working in 2024 in simh
- Paul Hardy made me aware of it in 2024
@forth_page1.png
@forth_page2.png
Chuck's IBM 1130
----------------
* Mohasco Industries - 1968
* IBM 1130 minicomputer
- 16-bit CPU, 8k RAM, disk, keyboard, printer,
card reader/punch, Fortran
- 2250 graphic display
@ibm1130_room.png
@ibm1130_whole.png
@ibm2250_unit.png
@ibm2250.png
My Goals
--------
* Emulate 1130 Forth on the Web
- Capture the experience of 1130 Forth
* Understand Forth at this stage
- What survived into modern Forth
- What survived into colorForth
Emulator
--------
* HTML, CSS, and JavaScript
* Emulate lights, buttons, switches, keyboard
* Emulate the CPU and I/O system
- Focus on Chuck's Forth
- Simplify I/O devices
- Avoid DMS dependency
IBM 1130 CPU
------------
* Registers:
* 15-bit IAR
* 16-bit ACC + 16-bit EXT
* Carry and Overflow flags
* 3 in-core index registers XR1, XR2, XR3
* Twos-complement math
* Double instructions mostly assume 32-bit alignment
* Interrupt levels 0-5
* Dedicated IO Instruction (XIO)
* Mostly common "Effective Address" calculation
- instruction relative, index relative,
short or long address, optional indirect
* BSI stores return address for subroutines
IBM 1130 INSTRUCTION SET
------------------------
XIO SLA SRA LDS STS WAIT
BSI BSC LDX STX MDX
A AD S SD M D
LD LDD STO STD AND OR EOR
IBM 1130 INSTRUCTION FORMAT
---------------------------
Short:
----------------------------------------------------
| 0 1 2 3 4 5 6 7 || 8 9 10 11 12 13 14 15 |
|--------------|--|------||------------------------|
OP OP OP OP OP F Tag Displacement
Long:
----------------------------------------------------
| 0 1 2 3 4 5 6 7 || 8 9 10 11 12 13 14 15 |
|--------------|--|------||--|---------------------|
OP OP OP OP OP F Tag IA Modifier
| 0 1 2 3 4 5 6 7 || 8 9 10 11 12 13 14 15 |
|--------------------------------------------------|
ADDRESS
case 0b11010: // STO
EffectiveAddress();
m[sar & ADDR_MASK] = acc;
Timing(7.6, 11.2, 10.8, 14.8);
break;
case 0b11011: // STD
EffectiveAddress();
m[(sar | 1) & ADDR_MASK] = ext;
m[sar & ADDR_MASK] = acc;
Timing(11.2, 14.9, 14.4, 18.0);
break;
case 0b11100: // AND
EffectiveAddress();
afr = m[sar & ADDR_MASK];
acc &= afr;
Timing(7.6, 11.2, 10.8, 14.8);
break;
case 0b11101: // OR
EffectiveAddress();
afr = m[sar & ADDR_MASK];
acc |= afr;
Timing(7.6, 11.2, 10.8, 14.8);
break;
0A8D 0 0000 00288 OR DC OR TOP OF STACK
0A8E 0 C200 00289 LD 2
0A8F 0 72FF 00290 MDX 2 -1
0A90 0 EA00 00291 OR 2
0A91 0 D200 00292 STO 2
0A92 00 4C800A8D 00293 B I OR
IBM 1130 BOOT
-------------
* Boots from 1442 card reader/punch
* A special load card format
- Covers a subset of instruction format
* As I had only one program to load
- Did not implement boot card mode
- Loaded that assembler program directly
@regular_card_load.png
@boot_card_load.png
DISK
----
* 1130 Forth's kernel loads from disk sectors
* Depends on IBM's "Disk Monitor System"
* Uses LIBF calling convention
- Compact one word entrypoint
- Arguments in adjacent memory
- Saves space / complexity
- Linker/loader stores names in 5 x 6-bit characters
- Calls DISK1 (used), and PRNT1 (unused)
- LINK BALO?
* Emulate by converting to a fake pseudoinstruction
- only used in initial startup
* Weird reverse order data
- Would not have figured out without
Charles Anthony's loader as reference
@disk.png
0B3C 00 678009F9 00374 LDX I3 X3
0B3E 20 042624B1 00375 LIBF DISK1
0B3F 0 1000 00376 DC /1000
0B40 0 0C0A 00377 DC BUF
0B41 0 0B2A 00378 DC ERROR
0B42 20 042624B1 00379 BL1 LIBF DISK1
0B43 0 0000 00380 DC /0000
0B44 0 0C0A 00381 DC BUF
0B45 0 70FC 00382 B BL1
0B94 00 678009F9 00433 PR3 LDX I3 X3
0B96 20 176558F1 00434 LIBF PRNT1
0B97 0 2000 00435 DC /2000
0B98 0 0BA5 00436 DC PRN
0B99 0 0B2A 00437 DC ERROR
0B9A 20 176558F1 00438 PR1 LIBF PRNT1
0B9B 0 0000 00439 DC /0000
0B9C 0 70FD 00440 B PR1
09FB 00 66000AF7 00200 START LDX L2 STACK+1 RESTART LOCATION
09FD 00 65000ADF 00201 LDX L1 A
09FF 00 67000B07 00202 LDX L3 INTST
0A01 00 6F000B06 00203 STX L3 R
0A03 00 67000DC0 00204 LDX L3 E2
0A05 00 6F000AD7 00205 STX L3 E1
0A07 0 7304 00206 MDX 3 4
0A08 00 6F000AD8 00207 STX L3 E
0A0A 00 67001860 00208 LDX L3 2*SECT+74
0A0C 00 6F000AF6 00209 STX L3 STACK
0A0E 00 6F000B28 00210 STX L3 C2
0A10 00 670000B3 00211 LDX L3 /B3
0A12 00 6F00000A 00212 STX L3 /A
0A14 00 67000A31 00213 LDX L3 FORTH
0A16 00 6F000AF7 00214 STX L3 STACK+1
0A18 00 67000912 00215 LDX L3 ACCEP
0A1A 00 6F000ADD 00216 STX L3 A-2
0A1C 00 67000C09 00217 LDX L3 BCD+63
0A1E 00 6F000ADE 00218 STX L3 A-1
0A20 00 67000969 00219 LDX L3 SAVE0
0A22 00 6F000ADB 00220 STX L3 A-4
0A24 00 670019FF 00221 LDX L3 /19FF
0A26 00 6F000D83 00222 STX L3 IC
0A28 00 670010EE 00223 LDX L3 /10EE
0A2A 00 6F000C0B 00224 STX L3 SECT
0A2C 0 400E 00225 BSI RECUR
0A2D 00 02053580 00226 LINK BALO
IBM 1130 CONSOLE
----------------
* Repurposed Selectric + IBM 029 Keypunch
- Black and red ribbon under program control
* Keyboard
- Interrupts for keypress
- No enter key!
- Special EOF, ERASE FIELD, BACKSPACE, INT REQ unused
* Switches and Buttons
- Buttons for raw debugging, single stepping, entry
- Switches for power, keyboard/bits, alarms
- 4 or 5 rows of switches
* Numeric and Alpha lights/keys seem to have varied!
- Very confusing, tried both at various points
@ibm1130_whole.png
@ibm1130_console.png
@console_4row.png
@console_5row.png
@alpha_numeric_keys.png
@keyboard_noalpha.png
@hold_numeric.png
@mode_vs_hold.png
IBM 029 KEYPUNCH
----------------
* Tool for punching and copying 80 column cards
* "programmable" cylinder card to specify card format
- field layout: alpha vs numeric
- optional zero prefix
- automatic column skips
* Some models are numeric only
@keypunch029.png
@keypunch029_keys.png
@program_drum.png
@keypunch029_numeric_note.png
@keypunch029_alpha_note.png
@keypunch029_models.png
@keypunch029_numeric.png
@program_card.png
CONSOLE + LIGHTS EMULATION
--------------------------
* Set most of the lights as state changes
- Do not have all the sub-instruction states
- Single step, but not sub-step
* Emulate push buttons directly
* Lots of character sets:
- Card code
- 1443 printer (EBCDIC)
- Linker 6-bit (packed card code)
- Chuck has his own!
CHUCK
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ ¢#<(+|&!$*);¬-/,%_>?:.@'="
EBCDIC
0 1 2 3 4 5 6 7 8 9 A B C D E F
4 ¢ . < ( + |
5 & ! $ * ) ; ¬
6 - / ¦ , % _ > ?
7 ` : # @ ' = "
8 a b c d e f g h i ±
9 j k l m n o p q r
A ~ s t u v w x y z
B ^ [ ]
C { A B C D E F G H I
D } J K L M N O P Q R
E \ S T U V W X Y Z
F 0 1 2 3 4 5 6 7 8 9
1130 FORTH
----------
* Amazingly dense 230 lines of Forth code.
* Two classes of words:
- OPERATION / ¢ - like a code word
- Regular words - string to interpret
: NAME def.. ; OR . NAME def... ,
(bug in protoforth loader prevents ;)
* Space OR special character separated
- but only from disk
- Explains 1+ 2*, etc.
* Boots with HELLO THERE instead of OK
- But has $ -> OK inside
1130 FORTH
----------
* ( ) for comments
* 'message string' for strings
* @ is VALUE
* ! is = ( e.g. 123 val= )
* IF..ELSE..THEN but only in code words
* Self modifying words: INTEGER, VECTOR
* Several collisions:
- INTEGER replaced by INTEPRETER
- POSITIVE replaced by POSITION
FORTH REGISTERS
---------------
ACC / EXT - temporary
XR1 - Other quick variables
XR2 - Data stack pointer
XR3 - Current word entry (transient)
0AD7 0 0000 00342 E1 DC TOP OR SYMBOL TABLE
0AD8 0 0000 00343 E DC THE PLACE TO START SEARCHES
0AD9 0 15C4 00344 W1 DC 2*WORD -6
0ADA 0 0000 00345 DC D - 5 SAVE CHARACTER
0ADB 0 0000 00346 DC -4 SAVE OPERATION
0ADC 0 0000 00347 DC C -3 CURRENT CHARACTER
0ADD 0 0000 00348 DC -2 ACCEPT
0ADE 0 0000 00349 DC -1 CHARACTER TABLE
0ADF 0 0000 00350 A DC A XR1 CURRENT CHARACTER
0AE0 0 0000 00351 N DC 1
0AE1 0 0000 00352 DC W 2 WORD CHARACTER
0AE2 0014 00353 WORD BSS 20 3
0AF6 0010 00354 STACK BSS 16
0B06 0 0000 00355 R DC
0B07 0020 00356 INTST BSS 32
0B27 0 1AE0 00357 C1 DC 2*SECT+642+72 RESET CHARACTER
0B28 0 0000 00358 C2 DC CHARACTER BEYOND RECORD
0B29 0 1860 00359 C3 DC 2*SECT+74 VHARACTER BEYOND SECTOR
0B2A 0 0000 00360 ERROR DC 0
0B2B 00 4C800B2A 00361 B I ERROR
DICTIONARY ENTRY
----------------
NAME[0..1]
NAME[1..2]
Code address
0 / x2 string code address / variable
BASIC ENTRY
-----------
09C1 0 0000 00157 ENTRY DC INITIAL ENTRY
09C2 00 44000922 00158 BSI L NEXT
09C4 00 74040AD7 00159 MDM L E1,4
09C6 00 67800AD7 00160 LDX I3 E1
09C8 00 6F000AD8 00161 STX L3 E
09CA 00 74040AD8 00162 MDM L E,4
09CC 0 C103 00163 LD 1 3 WORD
09CD 0 D300 00164 STO 3
09CE 0 C104 00165 LD 1 4 WORD+1
09CF 0 D301 00166 STO 3 1
09D0 00 4C8009C1 00167 B I ENTRY
OPERATION
---------
0AB7 0 0000 00319 OPER DC OPERATION
0AB8 00 440009C1 00320 BSI L ENTRY
0ABA 00 74010D83 00321 MDM L IC,1
0ABC 00 C4000D83 00322 LD L IC
0ABE 0 7201 00323 MDX 2 1
0ABF 0 D200 00324 STO 2
0AC0 0 D302 00325 STO 3 2
0AC1 00 4C800AB7 00326 B I OPER
REGULAR WORD
------------
09D2 0 0000 00168 ENTER DC ADD TO SYMBOL TABLE
09D3 0 40ED 00169 BSI ENTRY
09D4 00 C4000A55 00170 LD L INTER
09D6 0 D302 00171 STO 3 2
09D7 00 C4000D83 00172 LD L IC
09D9 0 1001 00173 SLA 1
09DA 0 8091 00174 A ONE
09DB 0 D1FB 00175 STO 1 -5 D
09DC 0 808F 00176 A ONE
09DD 0 D303 00177 STO 3 3
09DE 0 C016 00178 LD ASAVE SKIP OVER DEFINITION
09DF 0 D1FC 00179 STO 1 -4 SAVE
09E0 00 44000922 00180 EN1 BSI L NEXT
09E2 0 C015 00181 LD COMMA
09E3 00 94000AE2 00182 S L WORD
09E5 00 4C2009EF 00183 BNZ EN2
09E7 0 C1FB 00184 EN3 LD 1 -5 D
09E8 0 1801 00185 SRA 1
09E9 00 D4000D83 00186 STO L IC
09EB 0 C00A 00187 LD ASAV0
09EC 0 D1FC 00188 STO 1 -4 SAVE
09ED 00 4C8009D2 00189 B I ENTER
09EF 0 C007 00190 EN2 LD DOT
09F0 00 94000AE2 00191 S L WORD
09F2 00 4C2009E0 00192 BNZ EN1
09F4 0 70F2 00193 B EN3
09F5 0 0962 00194 ASAVE DC SAVE
09F6 0 0969 00195 ASAV0 DC SAVE0
09F7 0 3024 00196 DOT DC /3024 SEMICOLON
09F8 0 3424 00197 COMMA DC /3424 ,
CALLING
-------
* Return stack, PUSH + PULL but for something else
* Each call stores Entry,
Character in Entry, ACCEPT function
INTERPRET
---------
0A55 0 0A56 00250 INTER DC *
0A56 0 0000 00251 DC INTERPRET
0A57 00 C4000AD8 00252 LD L E
0A59 00 D4800B06 00253 STO I R
0A5B 00 6F000AD8 00254 STX L3 E
0A5D 00 74010B06 00255 MDM L R,1
0A5F 0 C1FD 00256 LD 1 -3 C
0A60 00 D4800B06 00257 STO I R
0A62 00 74010B06 00258 MDM L R,1
0A64 0 C303 00259 LD 3 3
0A65 0 D1FD 00260 STO 1 -3 C
0A66 0 C1FE 00261 LD 1 -2 ACCEPT
0A67 00 D4800B06 00262 STO I R
0A69 00 74010B06 00263 MDM L R,1
0A6B 0 C003 00264 LD ARETR
0A6C 0 D1FE 00265 STO 1 -2 ACCEP
0A6D 00 4C800A56 00266 B I INTER+1
SEMICOLON (COMMA)
-----------------
0A70 0 0000 00268 COM DC END INTERPRET
0A71 00 74FD0B06 00269 MDM L R,-3
0A73 00 67800B06 00270 LDX I3 R
0A75 0 C301 00271 LD 3 1
0A76 0 D1FD 00272 STO 1 -3 C
0A77 0 C302 00273 LD 3 2
0A78 0 D1FE 00274 STO 1 -2 ACCEPT
0A79 0 C300 00275 LD 3
0A7A 00 94000AD8 00276 S L E
0A7C 00 4C880A70 00277 BNP I COM
0A7E 0 C300 00278 LD 3 0
0A7F 00 D4000AD8 00279 STO L E
0A81 00 4C800A70 00280 B I COM
Converse of Modern Forth
------------------------
* Parsing of "normal" words is deferred to runtime
- No indirect-threaded virtual instructions
- Conditionals instead manipulate the parse stream
( n -- ) NONZERO
( n -- ) FALSE
( n -- ) POSITIVE
* But still Hyper Static Global Envirornment
- You can't use a word before it is defined
* How would loops have worked?
- Loops and conditionals in OPERATIONs are more conventional
- Main loop is done with callbacks + OPERATIONs
Other Words
-----------
NEXT - Parse next word
DO - Lookup word (from next)
LOC - Get code location for a word
LOC
---
0A83 0 0000 00281 LOC DC LOCATION OF CODE
0A84 00 44000922 00282 BSI L NEXT
0A86 00 44000993 00283 BSI L DO
0A88 0 C302 00284 LD 3 2
0A89 0 7201 00285 MDX 2 1
0A8A 0 D200 00286 STO 2
0A8B 00 4C800A83 00287 B I LOC
DO (Called Find in FORTH)
-------------------------
0993 0 0000 00120 DO DC INTERPRET WORD
0994 00 67800AD8 00121 LDX I3 E
0996 0 73FC 00122 DO1 MDX 3 -4
0997 0 C300 00123 LD 3
0998 00 4C980993 00124 BZ I DO
099A 0 9103 00125 S 1 3 WORD
099B 00 4C200996 00126 BNZ DO1
099D 0 C301 00127 LD 3 1
099E 0 9104 00128 S 1 4 WORD+1
099F 00 4C200996 00129 BNZ DO1
09A1 00 4C800993 00130 B I DO
BLOCK
-----
0B3B 0 0000 00373 BLOCK DC 0 NEXT BLOCK
0B3C 00 678009F9 00374 LDX I3 X3
0B3E 20 042624B1 00375 LIBF DISK1
0B3F 0 1000 00376 DC /1000
0B40 0 0C0A 00377 DC BUF
0B41 0 0B2A 00378 DC ERROR
0B42 20 042624B1 00379 BL1 LIBF DISK1
0B43 0 0000 00380 DC /0000
0B44 0 0C0A 00381 DC BUF
0B45 0 70FC 00382 B BL1
0B46 00 74010C0B 00383 MDM L SECT,1
0B48 00 C4000B27 00384 LD L C1
0B4A 0 D1FD 00385 STO 1 -3 C
0B4B 00 D4000B28 00386 STO L C2
0B4D 00 4C800B3B 00387 B I BLOCK
VARIABLES
---------
0ACF 0 0000 00336 INTEG DC DECLARE INTEGER
0AD0 00 67800AD8 00337 LDX I3 E
0AD2 0 C0D1 00338 LD ADDR
0AD3 0 D302 00339 STO 3 2
0AD4 0 40D0 00340 BSI ADDR+1
0AD5 00 4C800ACF 00341 B I INTEG
....
0AA4 0 0AA5 00305 ADDR DC *
0AA5 0 0000 00306 DC PLACE ADDRESS ON STACK
0AA6 0 7201 00307 MDX 2 1
0AA7 00 6F00096D 00308 STX L3 TEMP
0AA9 00 7403096D 00309 MDM L TEMP,3
0AAB 00 C400096D 00310 LD L TEMP
0AAD 0 D200 00311 STO 2
0AAE 00 4C800AA5 00312 B I ADDR+1
( NOT ORIGINAL! - ADDED FOR TESTING! )
: NUMBUF 20 VECTOR ; NUMBUF
: EMIT 100 * NUMBUF 1 + = NUMBUF WTC 1 TYPE ;
OPERATION ERAW 0 LS FF MS LOC TYP CALL RETURN
: CR 8100 ERAW ; : RRR RED ERAW ; : BBB BLACK ERAW ;
: DIGIT 0F AND EMIT ; : SIXF 10 / SWAP ;
: NN BBB SIXF SIXF SIXF DIGIT DIGIT DIGIT DIGIT RRR CR ;
OPERATION FND LOC NEXT CALL LOC FIND CALL 3 LOAD 1 MS 0 SS RETURN
OPERATION EXE 0 LS FF MS 3 STORE 2 BSI X3 I RETURN
OPERATION OVER FF LS 1 MS 0 SS RETURN : 2DUP OVER OVER ;
OPERATION ISNEG NEGATIVE CONDITION RETURN
: MIN 2DUP - ISNEG SWAP SWAP DROP ; : +VAL + VALUE ;
: SEE DUP 3 +VAL SWAP DUP 7 +VAL SWAP 3 +VAL - FFF AND 50 MIN MESSAGE ;
: SP 2 VALUE ; : SP0 1 VECTOR ; SP0 SP SP0 =
: DEPTH SP SP0 VALUE - ; : NAME 2 * 4 TYPE ;
( END TESTING CODE )
DEMO
----
https://flagxor.github.io/forth1130/
QUESTIONS❓
🙏
Thank you!