UNIVAC 1004 · programmed by wiring.
The 1004 has no software, no instruction set, and no memory in which to hold either. To program it, you wire it. This manual is a complete guide to the emulator's plug-board, its primitives, and the half-dozen idioms that 1962-vintage operators used to make a machine without code add up a column, count a deck, multiply by repeated addition, and print a header report.
The machine.
The UNIVAC 1004 was introduced by Sperry-Rand in 1962 and stayed in service through the early 1970s. It is sometimes described as a small computer, but that is not quite right. The 1004 has no addressable memory, no opcodes, no program counter, and nothing resembling a CPU register file. What it has is an electromechanical card path, a 132-column drum printer, four BCD counters, four selector flip-flops, and a removable plug-board: a panel of perhaps four hundred patch-cord jacks at which the operator physically wires together the machine's data paths.
A program for the 1004 was a particular pattern of patch cords. To install a program was to remove one plug-board and slot in another. A library of programs was a library of physical objects — payroll on Tuesday's board, billing on Wednesday's, inventory on Thursday's. Programmers were called wiremen.
The 1004 has no software. It has wiring. The two are not the same thing.
Despite this, the 1004 was an industrial workhorse. Through most of the 1960s it computed payroll for the United States Army, processed inventory for Westinghouse, and printed insurance statements for Mutual of Omaha. What it could not do — multiply two numbers directly, sort more than the deck the operator had pre-sorted, branch on arbitrary conditions — it accomplished by clever wiring of the four counters, four selectors, and thirty-one program steps that the plug-board makes available.
The emulator preserves this conceptual model deliberately. There is no source-code editor, because there was no source code. There is no instruction trace, because there were no instructions. There is, instead, a thirty-one-row table representing the plug-board, a field-definition table representing the named-bus assignments, and a live overlay that draws the active patch cord on every machine cycle in a glowing curve so that you can see what your wiring actually does.
The card cycle.
Everything the 1004 does happens inside a card cycle. A card cycle begins when a
fresh card is read from the hopper into the read buffer, runs the program steps top to
bottom (one by one, fast), and ends when one of the steps fires a NEW-CARD
sequence action. At that point the print buffer is ejected as a printed line, the punch
buffer (if used) is punched as an output card, and the next card is read in. The step
counter resets to step 1 and the cycle begins again.
A program is therefore a recipe for one card cycle, run repeatedly. There is no outer loop and no main function. The repetition is built into the machine.
Fields are named buses
The 1004 reads cards as raw 80-column strings of 6-bit BCD characters. To do anything
useful with that data you have to tell the machine which columns mean what. That is the
job of the field-definition table: it lets you declare up to eight named fields
(FLD-A through FLD-H) by their column ranges on the card, the
print drum, and the punch deck.
A field definition has five slots:
| Slot | Meaning |
|---|---|
| Length | Number of card columns the field occupies, 1 to 60-something. Required. |
| Card Col | First card column to read. 1-based. Set to 0 if the field is not sourced from the card (a counter-display field, for instance). |
| Print Col | First print column to write to when the field is used as a print destination (Print A, etc.). 1-based. |
| Punch Col | First punch column to write to when the field is used as a punch destination (Pu A, etc.). 1-based. |
| Literal Text | A constant string. If Card Col is 0 and this is non-empty, the field's source reads this literal — useful for printable labels like TOTAL = . |
A field is a name for a piece of data, plus instructions for where on the printer
or punch its contents should land. Field A might be a 3-character HOURS field that reads
card columns 1–3 and prints at print column 5. Field B might be a 5-character TOTAL
field that doesn't read from the card (Card Col = 0) but writes to print column 35 with
the literal text TOTAL = . The same field name may be used as both a source
and a destination in different program steps.
The program step.
The plug-board has thirty-one program steps. Each step is a single row of the program table. A step has seven slots, and they answer seven questions in order:
Reading a step is a left-to-right exercise in answering questions. Cond: when
should this step fire? Src: what data hub do we read from? Op: what
do we do with that data? Dst: where do we send the result? Sel:
(only for compares) which selector should I set? Seq: what happens after this
step finishes? Arg: the argument to the sequence action — the target step
number for a JUMP, ignored otherwise.
The condition gates the whole step
If the step's condition is false, the entire step is skipped — no transfer, no sequence
action — and execution falls through to the next step. This is important: a row whose
sequence is NEW-CARD but whose condition is false will not fire
the new-card action. The condition gates everything.
The default sequence is NEXT, which simply advances to the following step.
Most rows in a typical program use NEXT. The interesting sequences are the
ones that change the flow: JUMP, NEW-CARD, PRT-LINE,
HALT.
cond=NEOF, src=Field A, op=MOV, dst=Print A, seq=PRT-LINE reads as: "While
there's a card in the hopper, copy Field A to its print column, then eject the line."
Get used to this voice; it's how the rest of this manual writes them.
Reference tables.
These five tables enumerate every primitive available in the emulator. Bookmark this section. You will scroll back to it constantly while writing your first few patch-boards.
Sources — what a step reads from
| Code | Name | Provides |
|---|---|---|
| OFF | No source | Use when the step exists only for its sequence action (e.g. an unconditional JUMP or a trailer HALT). No data is transferred. |
| FA – FH | Field A — H | The field's contents. If the field has a non-zero Card Col, the data comes from that range of columns of the current card. If Card Col = 0 and the field has Literal Text, the literal is provided. Otherwise, blanks of the field's length. |
| C1 – C4 | Counter 1 — 4 | The current 5-digit BCD value of the counter, sign and all. |
| ZERO | Zeros | The 5-character string "00000". Useful for resetting a counter (with MOV) or zero-padding a print field. |
| BLANK | Blanks | Five spaces. Useful for blanking a region of the print buffer between lines. |
| E1 – E9 | Digit emitter | A 5-digit BCD constant equal to the digit suffix: E1 emits "00001", E5 emits "00005", and so on. The standard idiom for incrementing a counter by one is src=E1, op=ADD, dst=Cn. |
Operations — what a step does
| Code | Name | Behavior |
|---|---|---|
| MOV | Move | Copy the source bytes into the destination, replacing whatever was there. The classic way to load a counter, write a print field, or copy data between fields. |
| ADD | Add | BCD-add the source value to the destination's current value. Only meaningful when the destination is a counter; using it on a print field treats the print buffer as a numeric region, which is rarely what you want. |
| SUB | Subtract | BCD-subtract the source from the destination. Underflow wraps without flagging an error. |
| CMP | Compare | Compare the source value to the destination's current value (the destination is read, not written). Sets the EQ / LT / GT flags so that subsequent rows can branch on them. If the Sel slot is set to S1–S4, also latches that selector. Does not modify the destination. |
Destinations — where the result goes
| Code | Name | Effect |
|---|---|---|
| OFF | No destination | Use for sequence-only rows or for compares that don't need a particular destination. The transfer is a no-op. |
| PA – PH | Print A — H | Write to the print buffer beginning at the field's Print Col. The print buffer is 132 columns wide and is ejected by a PRT-LINE or implicitly by NEW-CARD. |
| PuA – PuH | Punch A — H | Write to the punch buffer beginning at the field's Punch Col. The punch buffer is ejected by PCH-CARD. |
| C1 – C4 | Counter 1 — 4 | Target the corresponding 5-digit BCD counter. MOV sets it; ADD/SUB accumulate into it; CMP reads it. |
Conditions — when a step fires
| Code | Tests |
|---|---|
| Always | Step always fires. |
| S1 ON / S1 OFF … S4 ON / S4 OFF | Tests the corresponding selector flip-flop (set by a previous CMP or by an explicit selector set). |
| EQ / NE | The most recent CMP resulted in equality (or did not). |
| LT / GT | The most recent CMP resulted in source < destination (or >). |
| EOF | A read past the last card has occurred — we are now on the trailer cycle. |
| no EOF | A card was successfully read for this cycle. |
| C1 = 0 … C4 = 0 | The counter currently holds zero. |
| C1 ≠ 0 … C4 ≠ 0 | The counter currently holds a non-zero value. |
Sequences — what happens after the step
| Code | Name | Effect |
|---|---|---|
| NEXT | Next step | Advance to step n + 1. The default for any row that doesn't say otherwise. |
| JUMP n | Jump | Jump to step n in the same card cycle. The Arg column holds the target step number. Cycles can contain many jumps; the cycle ends only on NEW-CARD or HALT. |
| PRT-LINE | Print line | Eject the current print buffer as one printed line, then advance to the next step. The print buffer is cleared after ejection. |
| PCH-CARD | Punch card | Punch one card from the current punch buffer, then advance. The punch buffer is cleared. |
| NEW-CARD | Next card | Eject any pending print buffer, punch any pending punch buffer, read the next card from the hopper, and restart at step 1. If the hopper is empty, latch EOF and run the trailer cycle. |
| HALT | Stop | Stop the processor. The COMPLETE light comes on. There is no "go again" — to run the program a second time you reload the hopper and press RUN. |
Counters & selectors.
The 1004 has eight pieces of stateful hardware that programs can read and write: four counters and four selectors. Everything else — the card buffer, the print buffer, the punch buffer, the EOF latch, the comparison flags — is either transient or driven by the machine itself. The counters and selectors are the entire user-accessible state of a 1004 program.
Counters
A counter is a 5-digit signed BCD register. There are four of them, addressed
C1 through C4. They are initialized to +00000 when
the program starts and are not cleared between card cycles. Anything you accumulate into
them stays there.
Operations on a counter are exactly what you would expect:
MOV ZERO → C1 resets it. MOV C1 → Print A places the counter's
current value into the print buffer at field A's print column.
ADD FA → C1 adds field A's value to the counter — this is the standard
accumulator pattern. SUB E1 → C1 decrements the counter by one.
CMP C1 → C2 compares two counters and sets the comparison flags.
The most useful conditions on a counter are C1 = 0 and C1 ≠ 0.
These let you implement loops without setting up a comparison every iteration. The
repeated-add multiplication pattern (§ VI) uses a counter as its loop
variable: load HOURS into C2, then loop while
C2 ≠ 0, adding RATE to C1 and subtracting E1
from C2 on each pass.
Selectors
A selector is a single-bit latching flip-flop. There are four of them, named
S1 through S4. Each is either ON or OFF. They start OFF.
Selectors are latching: once set, they keep their value until something
explicitly changes them.
Selectors are set by the Sel slot of a CMP step. The slot
chooses which selector to set, and the comparison result determines whether to set it
ON or OFF, by these conventions:
| Sel | Sets ON when |
|---|---|
| S1 | source < destination ("low") |
| S2 | source = destination ("equal") |
| S3 | source > destination ("high") |
| S4 | source ≠ destination ("unequal") |
Subsequent rows test selectors via the Cond slot: S1 ON,
S2 OFF, and so on. The classic use is a group break: compare the
current group code (a card field) to the saved one (a counter holding the previous
cycle's group code). Set S2 on equal. On the next row, run the
detail-print logic only if S2 ON; otherwise run the
subtotal-line logic and reset.
Selectors are also used for once-only logic — a header line printed only on the
first card, for instance. The trick is to use a counter as a poor man's flag: a
C3 = 0 condition gates the header rows on the first cycle, and at the end
of the header you bump C3 with E1 ADD C3. From the second
cycle onward, C3 is non-zero and the header rows are silently skipped.
This idiom appears in the FORMAT sample (§ VII).
Common patterns.
Six idioms cover most of what 1004 operators wrote. Internalize these and the rest of plug-board programming is composition.
The accumulator
Sum a numeric field across all cards into a counter. After EOF, print the total.
ROW COND SRC OP DST SEQ NOTES 1 NEOF FA ADD C1 NEXT accumulate the field 2 NEOF FA MOV PA PRT-LINE echo the detail line 3 NEOF — — — NEW-CARD advance 4 EOF C1 MOV PB PRT-LINE print total at field B 5 EOF — — — HALT
The counter
Count the number of cards processed using a digit emitter as the increment.
ROW COND SRC OP DST SEQ 1 NEOF E1 ADD C2 NEXT count++ 2 NEOF — — — NEW-CARD 3 EOF C2 MOV PA PRT-LINE print count 4 EOF — — — HALT
Multiplication via repeated add
The 1004 has no MUL instruction. To multiply HOURS × RATE,
initialize a result counter to zero, load HOURS into a loop counter, then
loop: add RATE to the result, decrement the loop counter, and exit when
the loop counter reaches zero.
ROW COND SRC OP DST SEQ ARG NOTES 1 NEOF ZERO MOV C1 NEXT gross = 0 2 NEOF FA MOV C2 NEXT loop = HOURS 3 C2=0 — — — JUMP 7 exit when done 4 Always FB ADD C1 NEXT gross += RATE 5 Always E1 SUB C2 NEXT loop-- 6 Always — — — JUMP 3 retest 7 NEOF C1 MOV PC PRT-LINE print gross 8 NEOF — — — NEW-CARD
This is the canonical 1962 form. Operators were paid by the hour and the 1004 paid them one addition at a time.
Once-only logic (the header trick)
Print a header line only on the first card. Use a counter as a flag: gate the header
rows on C3 = 0, then bump C3 at the end of the header.
ROW COND SRC OP DST SEQ NOTES 1 C3=0 FH MOV PH PRT-LINE FH = literal "REPORT" 2 C3=0 E1 ADD C3 NEXT mark header-printed 3 NEOF FA MOV PA NEXT detail line begins ...
Group break (control break)
When a sorted deck contains group codes, you often want a subtotal line each time the group changes. Save the previous group's code in a counter, compare each new card's group code against it, and use a selector to detect change.
ROW COND SRC OP DST SEL SEQ ARG NOTES 1 NEOF FA CMP C4 S2 NEXT S2 = (group == prev) 2 S2 OFF C1 MOV PE — PRT-LINE break: print subtotal 3 S2 OFF ZERO MOV C1 — NEXT reset subtotal 4 NEOF FA MOV C4 — NEXT save current group 5 NEOF FB ADD C1 — NEXT accumulate detail ...
The trailer (post-EOF processing)
The trailer is whatever runs after the last card has been read. The EOF latch is set
just before the trailer cycle, so any rows with cond = EOF become the
trailer routine. Always finish with an EOF-conditioned HALT;
otherwise the program may loop on the trailer indefinitely.
Worked examples.
Six sample plug-boards ship with the emulator. Each is short enough to read in full and each demonstrates one or two patterns from § VI. Load any of them with the Load Sample dropdown.
ECHO — print every card
The minimal program. Defines one 60-character field, prints it, advances. Three rows total. The "hello world" of plug-board programming.
Field A: len=60, cardCol=1, prtCol=5 ROW COND SRC OP DST SEQ 1 NEOF FA MOV PA PRT-LINE 2 NEOF — — — NEW-CARD 3 EOF — — — HALT
TOTAL — sum a numeric column
Reads a 5-digit amount from cols 1–5, prints each detail, accumulates a grand total
into C1, and prints TOTAL = nnnnn after EOF using a literal-text
field for the label. With the shipped card data (125, 250, 75, 1000, 500) the trailer
reads TOTAL = 01950.
MULTI — multi-field invoice listing
Three fields per card: a 4-character ID, a 20-character NAME, and a 5-digit AMOUNT. Each detail line prints all three. The trailer prints a grand total. Demonstrates the common case where one card produces one printed line.
GROUPS — card count and grand total
Each card has a 2-digit GROUP code and a 5-digit AMOUNT. Counts cards into C2
using E1 ADD C2, accumulates totals into C1, and prints
CARDS = nnnnn and TOTAL = nnnnn in the trailer. The simplest
use of the digit-emitter primitive — E1 as a +1 source is the canonical
way to count.
PAYROLL — multiplication via repeated add
The headline sample. For each card, multiplies HOURS × RATE by repeated
addition (Pattern 3) and prints
NAME GROSS = nnnnn. With the shipped cards
(40 × $25, 35 × $30, 20 × $15) the gross-pay column reads
01000, 01050, 00300. This is real 1962 math: forty additions to compute one
multiplication.
FORMAT — header, detail, trailer
The most elaborate sample. Demonstrates Pattern 4 (header-once via C3 = 0),
a card count via E1 ADD C2, a grand total via FB ADD C1, and
a two-line trailer that prints both COUNT = nnnnn and
TOTAL = nnnnn from literal-text fields. Six fields, fourteen program steps,
and produces a recognizable 1968-style report.
Limits & gotchas.
A few things to know before they bite you.
The 31-step limit is real
A real 1004 plug-board has thirty-one program steps and there is no software upgrade
that will give you a thirty-second. If a program won't fit, you must factor it: write
the deck out via PCH-CARD with intermediate results punched in unused
columns, then re-run the deck through a second plug-board that completes the work. This
is how the 1962 operator got around the limit, and it's also why card-handling speed
mattered as much as logic speed.
BCD decimal, not binary
All arithmetic is BCD on 5-digit signed quantities. There is no binary, no
floating-point, and no bit-manipulation. Counter overflow at ±99999 wraps
silently. You cannot store a flag in a counter as bit 0 — counters are decimal-coded
five characters wide.
One source, one destination, one operation per step
Each row of the plug-board is a single transfer. You cannot, in one step, simultaneously add field A to counter 1 and field B to counter 2. That requires two rows. This sounds obvious but the temptation to write a "fat" row is real, especially when you're tight on the 31-step budget.
Conditions gate the entire step
A row whose condition is false skips both its data transfer and its sequence action.
This is different from the historical machine, where the sequence wires fired regardless
of the condition wires. The emulator chooses the more conventional flow because it
makes loops and trailers easier to reason about. If a step needs to fire its sequence
unconditionally, set its condition to Always and use a no-op
OFF → OFF transfer.
The trailer must HALT
The card cycle does not naturally stop after EOF. If your trailer rows do not end with
a row that fires HALT under cond = EOF, the program will
continue running the EOF cycle forever. (The emulator caps it at a reasonable
instruction count to avoid hangs, but the right discipline is always to provide an
explicit halt.)
Differences from the historical machine
The emulator simplifies a few things that the real 1004 did differently. The historical
plug-board had hundreds of named hubs, not eight fields; there were dedicated digit
emitters at every counter input, not a generic E1–E9 source pool; and the
real machine could read, compute, print, and punch in parallel within one cycle. The
emulator's logic is sequential within a card cycle, which is a fair simplification given
that most 1004 programs never relied on the parallelism. The conceptual model — fields,
sources, destinations, conditions, sequences, counters, selectors — is faithful.