KENBAK-1 · Volume 7

KENBAK-1 — Volume 7 — Programming the KENBAK-1 — From Counting Lights to Subroutines

How Kenbak Corporation's workbook taught programming to people who had never seen a computer, and the same road walked again: counting in the lamps, writing a program on a coding sheet, slowing a loop down, waiting for a button, adding fifty numbers two ways, and calling a subroutine with Jump and Mark — every listing in octal as keyed, every result run in the simulator

Figure 1 — Store, under the Memory lamp: the button that put every KENBAK-1 program into memory, one byte at a time. Beside it are Read and Start, and the LOCK/UNL toggle that protects the memory from the panel.
Figure 1 — Store, under the Memory lamp: the button that put every KENBAK-1 program into memory, one byte at a time. Beside it are Read and Start, and the LOCK/UNL toggle that protects the memory from the panel. — Photo: kenbak.com, "Design of the Kenbak-1", CC BY-SA 4.0, https://www.kenbak.com/design

7.1 About This Volume

The KENBAK-1 was sold to schools and individuals as a way to learn programming, and it came with the means to do it: the Programming Reference Manual summarised in Volume 6 and, for beginners, a workbook of Laboratory Exercises that starts from “Every computer has a memory in which numbers are stored” and ends, thirty exercises later, with sorting, subroutines and a date-entry program. This volume follows the same road. It begins with the workbook’s first programs, shows how a KENBAK-1 program was written on paper and then keyed in, and builds up through delays, input, loops over data, decisions and subroutines.

Every program here is given in octal as keyed — address, contents, then the symbolic form — and every one was assembled and run in the series’ simulator, 03-outputs/tools/kenbak_sim.py. The programs that are new to this volume are in kenbak_programs.py beside it, whose output file programs_output.txt holds the listings and runs quoted below. The workbook’s own programs were checked in the simulator’s self-test against the answers the workbook gives. Instruction names and notation follow Volume 6; lamps are drawn as ● (lit) and ○ (dark), grouped 2–3–3 as on the panel.

7.2 The Workbook

The Laboratory Exercises (Kenbak Corporation, 1971; scan at Adwater & Stir) is a workbook of about 150 pages, “intended for independent work”. Its preface states the method: “Ideas and concepts are introduced in small increments and are interspersed with actual experience on the computer. Emphasis is placed on the principles of self-discovery, immediate feedback, and rewards.” It asks questions and leaves blanks for the answers; it gives the reader a program to load before it explains what any of the numbers mean; and it holds back features until the reader has felt the need for them. Immediate (constant) addressing does not appear until Exercise 19, after the reader has written many programs that keep their constants in separate bytes; indexed addressing waits for Exercise 21, and subroutines for Exercise 25. Each new feature arrives with the reminder that it “did not allow us to do anything that we couldn’t do before, but [it] made it easier”.

The first exercise is about nothing but memory: storing a byte, reading it back, and noticing that reading does not destroy it. One experiment asks the reader to store numbers, switch the power off and on, and read them again, and asks “Has the data changed? Do you know why?” — the shift-register memory of Volume 3 forgets everything without power. Another asks the reader to store 000 at location 377 and read it back, and explains only that “This is a special location and we will learn more about it later.”

7.3 A First Program: Counting in the Lamps

In Exercise 2 the reader stores seven bytes, starting at address 003, and is told to press Start. “The output lights should have started blinking.” Only in Exercise 3 does the workbook reveal that “The numbers you stored in the memory were instructions or commands to the computer”, and it gives a variant that halts after each pass, so that the reader can watch it count:

Loc  Contents  Symbolic
003  004       P: start at 004
004  023       LOAD A C=000      A = 0
005  000
006  034       STORE A OUTPUT    show A in the lamps
007  200
010  000       HALT              wait for Start
011  003       ADD A C=001       A = A + 1
012  001
013  344       JPD UNC 006       back to the STORE
014  006

The workbook lists these as binary, since it has not yet introduced octal: the first byte is “00 000 100”. The reader presses Start repeatedly: “The output lights should all be out”, then “00 000 00●”, then “00 000 0●0”. In the simulator, four presses of Start show:

Start 1   ○○ ○○○ ○○○   000
Start 2   ○○ ○○○ ○○●   001
Start 3   ○○ ○○○ ○●○   002
Start 4   ○○ ○○○ ○●●   003

The exercise’s real lesson is binary counting, which the reader is asked to discover as a rule about the lamps: “Find the first light at or from the right hand end that is Off. This light will change to On. All of the lights from the right hand end up to this light but not including this light will change to Off.”

The continuous version — Exercise 2’s program, and the one the Programming Reference Manual uses as its example of console use (Volume 6) — runs too fast to follow: in the timing model of Volume 3, the lowest lamp changes about 160 times a second.

7.4 Writing a Program on Paper

By Exercise 12 the workbook has the reader writing programs, and it teaches a method that would be recognised by any assembly-language programmer of the period. A program is first written symbolically, with names for locations, on a coding sheet whose columns are Loc, Data, Symbolic Address, Contents and Comments. Kenbak printed pads of these sheets, with the whole instruction code table down the left margin.

Figure 2 — A Kenbak programming worksheet. The left margin is the instruction code table — register digits, operation digits, modes, jump conditions, bit and shift codes — so that a program can be hand-assemb…
Figure 2 — A Kenbak programming worksheet. The left margin is the instruction code table — register digits, operation digits, modes, jump conditions, bit and shift codes — so that a program can be hand-assembled on the sheet itself; the columns are location, data (the octal byte), symbolic address, contents and comments. — Scan: Kenbak Corporation, Programming Worksheets (1971), via Adwater & Stir, https://adwaterandstir.com/operation-kenbak/

Then locations are assigned. The workbook’s advice is concrete: “Except for the reserved locations 000, 001, 002, 003 and 200, 201, 202, 203 and 377, we can make most any choice for the first instruction. Location 004 is a very good choice.” It suggests putting constants well away from the code (“We’ll assign ONE beginning at 204. Then if we add more instructions to the program, we won’t have to move ONE”). Finally the octal codes are filled in from the table, the addresses from the assignments, and the program is keyed in with Clear, the data buttons and Store, one byte at a time. The series’ simulator includes a small assembler that does exactly this bookkeeping, using the manual’s notation; every listing below is its output.

7.5 Slowing Down: A Delay Loop

A program that shows something in the lamps must usually waste time on purpose. The simplest way is a loop that counts a register round from 0 back to 0:

Loc  Contents  Label   Symbolic
004  103       LOOP    ADD B C=001      count in B
005  001
006  134               STORE B OUTPUT   show it
007  200
010  223               LOAD X C=000     then waste time:
011  000
012  203       WAIT    ADD X C=001      X runs 1, 2 ... 377, 0
013  001
014  243               JPD X!=0 WAIT    256 passes
015  012
016  344               JPD UNC LOOP
017  004
003  004               (P: start at 004)

The inner loop is two instructions, run 256 times. The simulator shows the lamps stepping through 001, 002, 003, 004 … with 430 instructions per count; the timing model of Volume 3 puts that at about 1.3 seconds per count on an original machine. On a replica the figure depends on its firmware’s speed setting (Volume 9), and the length of a delay loop is the one thing that will usually need adjusting when a program moves from one KENBAK to another.

7.6 Listening to the Buttons

The data buttons write into location 377 even while a program runs, so a program can read them at any time. The difficulty is knowing when the operator has finished keying a number. The Programming Reference Manual gives the answer: “If the input data is not to be used until the formation or entry is completed, one of the bits should be used as an interlock signal.” The program below keeps a running total. The operator keys a number from 0 to 127 on buttons 0–6, and then presses button 7 to say “enter”:

Loc  Contents  Label   Symbolic
004  123       START   LOAD B C=000     running total
005  000
006  134               STORE B OUTPUT
007  200
010  134               STORE B INPUT    clear the input register
011  377
012  372       WAIT    SKP1 7 INPUT     button 7 pressed? then skip the jump
013  377
014  344               JPD UNC WAIT
015  012
016  024               LOAD A INPUT
017  377
020  323               AND A C=177      drop the interlock bit
021  177
022  104               ADD B A          total = total + entry
023  000
024  134               STORE B OUTPUT
025  200
026  023               LOAD A C=000
027  000
030  034               STORE A INPUT    clear for the next entry
031  377
032  344               JPD UNC WAIT
033  012

Four things in it recur in every interactive KENBAK-1 program. The wait is a skip on a bit (372 377) followed by a jump back (344 012): two instructions that loop until bit 7 of the input is a 1. The interlock bit is removed with AND A C=177. “ADD B A” is a register-to-register addition: memory addressing with address 000, which is A. And the program clears 377 itself by storing zero there, so the operator does not have to press Clear.

In the simulator, entering 005, then 014, then 144 (5, 12 and 100 decimal), each followed by button 7, gives totals:

after 005 + 7    ○○ ○○○ ●○●   005   (5)
after 014 + 7    ○○ ○●○ ○○●   021   (17)
after 144 + 7    ○● ●●○ ●○●   165   (117)

and between the number keys and button 7 the lamps do not change — the program is still waiting.

7.7 Loops Over Data: Fifty Numbers, Two Ways

7.7.1 The workbook’s way: a program that rewrites itself

Exercise 14 sets a problem: fifty small numbers — “the number of runs that a baseball team scored in 50 games” — are stored at locations 204 to 265 octal. Add them up. Before it has introduced indirect or indexed addressing, the workbook solves it with self-modifying code: an ADD instruction whose address byte the program increments on each pass. “After the computer has done the second instruction above, do you think it could add 1 to the address in this instruction?” Assembled from the workbook’s symbolic listing (EX 14-6), with the codes of EX 14-2:

Loc  Contents  Label   Symbolic
004  024       BEGIN   LOAD A K204
005  043
006  034               STORE A ADDR     reset the address in the ADD
007  013
010  024               LOAD A ZERO
011  044
012  004       LOOP    ADD A 204        ADDR is the byte at 013
013  204
014  034               STORE A SAVESUM
015  045
016  024               LOAD A ADDR      form next address
017  013
020  004               ADD A ONE
021  046
022  034               STORE A ADDR
023  013
024  014               SUB A K266       last address?
025  047
026  044               JPD A=0 END
027  034
030  024               LOAD A SAVESUM   restore A
031  045
032  344               JPD UNC LOOP
033  012
034  024       END     LOAD A SAVESUM
035  045
036  034               STORE A OUTPUT
037  200
040  000               HALT
041  344               JPD UNC BEGIN
042  004
043  204       K204
044  000       ZERO
045  000       SAVESUM
046  001       ONE
047  266       K266
Figure 3 — A page of Exercise 14 in the Laboratory Exercises: the first, numeric version of the self-modifying loop, placed at 100–117 with its constants at 300–302, followed by the true-or-false questions th…
Figure 3 — A page of Exercise 14 in the Laboratory Exercises: the first, numeric version of the self-modifying loop, placed at 100–117 with its constants at 300–302, followed by the true-or-false questions the reader is asked about it. — Scan: Kenbak Corporation, Laboratory Exercises, p. EX 14-2 (1971), via Adwater & Stir, https://adwaterandstir.com/operation-kenbak/

The workbook supplies test data — “00000 11111 22222 33333 44444 55555 66666 77777 88888 99999” — and the answer: “The correct answer is 225 (= 341 octal).” In the simulator the program halts with the lamps showing ●● ●○○ ○○●, 341. The workbook then asks the reader to change the five 0s to 9s: “The correct answer is now 270 which is equal to octal 416. What answer does the computer give you? Can you show the octal number 416 at one time in the lights?” The simulator shows ○○ ○○● ●●○, 016 — 270 less 256, the carry lost — which is the point of the question.

7.7.2 With the index register

Exercise 21 introduces indexed addressing, and the same job becomes a loop that does not touch its own instructions. X counts from 0 to 50 decimal (062 octal); ADD A TABLE,X adds the byte at 204 + X:

Loc  Contents  Label   Symbolic
004  023       BEGIN   LOAD A C=000
005  000
006  223               LOAD X C=000
007  000
010  006       LOOP    ADD A TABLE,X    indexed: address = 204 + X
011  204
012  203               ADD X C=001
013  001
014  124               LOAD B X         B = X
015  002
016  113               SUB B C=062      062 octal = 50
017  062
020  143               JPD B!=0 LOOP
021  010
022  034               STORE A OUTPUT
023  200
024  000               HALT
025  344               JPD UNC BEGIN
026  004
Figure 4 — The indexed version as a flowchart. Four boxes and a test replace the workbook's address arithmetic; the program never alters its own instructions, so it can be restarted without being reset.
Figure 4 — The indexed version as a flowchart. Four boxes and a test replace the workbook's address arithmetic; the program never alters its own instructions, so it can be restarted without being reset. — Diagram drawn for this series (03-outputs/diagrams/diagrams_B.py)

On the same data it halts showing 341. The comparison, from the simulator, is a fair summary of what the addressing modes are for:

Table 1 — On the same data it halts showing 341. The comparison, from the simulator, is a fair summary of what the addressing modes are for

VersionBytesInstructions executedModel time on an original
Self-modifying (EX 14-6)36454about 0.7 s
Indexed19254about 0.5 s

Self-modifying code was not a trick on the KENBAK-1 but a normal technique, and the workbook teaches it deliberately: a program that writes its own address bytes is the plainest possible demonstration that instructions are numbers in memory.

7.8 Decisions: Jumps, Skips and the Flags

A KENBAK-1 program decides by testing a register (the conditional jumps), a single bit (the skips) or the result of arithmetic (the overflow and carry bytes at 201–203). The last is the one that needs care, because the jump conditions read a register as signed. To ask whether one unsigned number is smaller than another — whether an address has passed 377, for instance — subtract and look at the carry, which on the KENBAK-1 is the borrow: “SKP0 1 201” skips if there was no borrow. Volume 10’s prime-number program uses exactly this to compare a number with the square of a trial divisor.

The workbook’s Exercise 27 makes the point with a puzzle. It asks for a subroutine that adds a 16-bit number in memory (pointed to by X) to a 16-bit number held in B and A, and prints “one person’s solution which has an error in it”:

ADD DP   (mark)
         ADD A (X)
         ADD X C=1
         ADD B (X)
         SKIP 0 b1 (201)
         SUB B C=1
         JPI UNC ADD DP

The error is the fifth line. If the low halves produced a carry, the carry must be added to the high half, so “SUB B C=1” should be “ADD B C=1”. With that correction, the simulator adds the workbook’s own example operands, 10 011 101 11 110 100 and 01 000 100 01 101 001, to get 342 135 octal in B and A, which is their 16-bit sum.

7.9 Subroutines with Jump and Mark

A subroutine on the KENBAK-1 begins with one spare byte. The call, JMD UNC name, writes the caller’s return address (the address just after the call) into that byte and continues at the byte after it; the subroutine ends with JPI UNC name, an indirect jump through the stored address (Volume 6). The program below multiplies by the shift-and-add method — the KENBAK-1 has no multiply instruction — and calls the subroutine twice, halting to show each product:

Loc  Contents  Label   Symbolic
004  023       MAIN    LOAD A C=006
005  006
006  123               LOAD B C=007
007  007
010  364               JMD UNC MPY      A = 6 x 7
011  030
012  034               STORE A OUTPUT
013  200
014  000               HALT
015  023               LOAD A C=015
016  015
017  123               LOAD B C=013
020  013
021  364               JMD UNC MPY      A = 13 x 11
022  030
023  034               STORE A OUTPUT
024  200
025  000               HALT
026  344               JPD UNC MAIN
027  004
030  000       MPY     (mark)           return address is written here
031  034               STORE A MCAND
032  073
033  023               LOAD A C=000
034  000
035  034               STORE A PROD
036  074
037  144       MLOOP   JPD B=0 MDONE    no multiplier bits left
040  067
041  202               SKP0 0 B         low bit of B a zero? skip the add
042  001
043  344               JPD UNC MADD
044  057
045  024       MSHIFT  LOAD A MCAND     multiplicand x 2
046  073
047  211               SFTL A 1
050  034               STORE A MCAND
051  073
052  051               SFTR B 1         multiplier / 2 ...
053  072               SET0 7 B         ... and clear the copied sign bit
054  001
055  344               JPD UNC MLOOP
056  037
057  024       MADD    LOAD A PROD
060  074
061  004               ADD A MCAND
062  073
063  034               STORE A PROD
064  074
065  344               JPD UNC MSHIFT
066  045
067  024       MDONE   LOAD A PROD
070  074
071  354               JPI UNC MPY      return through the mark
072  030
073  000       MCAND
074  000       PROD
Figure 5 — The call and return. JMD UNC MPY at 010 writes 012 into MPY (030) and continues at 031; at the end, JPI UNC MPY reads 012 from 030 and jumps there. The addresses are those of the assembled listing.
Figure 5 — The call and return. JMD UNC MPY at 010 writes 012 into MPY (030) and continues at 031; at the end, JPI UNC MPY reads 012 from 030 and jumps there. The addresses are those of the assembled listing. — Diagram drawn for this series (03-outputs/diagrams/diagrams_B.py)

The first Start shows ○○ ●○● ○●○, 052 — 42, which is 6 × 7. The second shows ●○ ○○● ●●●, 217 — 143, which is 13 × 11, correct as an unsigned number although the jump conditions would read that byte as negative. After the second call, location 030 holds 023, the address after the second JMD: the mark is simply overwritten by each call.

Three details of the listing are worth noticing. “SKP0 0 B” (202 001) tests bit 0 of register B by its address, 001 — the bit instructions work on registers because registers are memory. “SFTR B 1” (051) is the sign-preserving shift of Volume 6, so it copies bit 7; for a multiplier above 127 that would leave a 1 at the top for ever and the loop would never end, so “SET0 7 B” (072 001) clears it. And because there is only one mark byte per subroutine, MPY cannot call itself, and a subroutine that calls another must not be called by it.

7.10 Debugging With Twelve Lamps

The KENBAK-1 has no screen and no printer, and the tools for finding a mistake are the ones on the panel. The manual’s first advice is to read back what was keyed — “To check the entry of the data, which is a good habit to get into…” — by setting the address and pressing Read once per byte. The second is the single step: hold Stop, press and release Start, and exactly one instruction runs. The P register (location 003) then shows “the address of the next instruction to be executed”, and A, B and X can be read at 000–002. The workbook’s Exercise 9 has the reader single-step a program and record P after each instruction, predicting the values first.

The third tool is HALT. A program under test can be peppered with HALT instructions that stop it with an intermediate result in the lamps; since HALT leaves P pointing at the next instruction, Start carries on from where it stopped. Many of the programs in this volume and in Volume 10 use HALT the same way in normal operation, to show one result at a time.

Figure 6 — "Example of Console Use" from the Programming Reference Manual (PR-23): the counting program, the 39 button presses that load it, and the read-back that checks it. The series' simulator presses the…
Figure 6 — "Example of Console Use" from the Programming Reference Manual (PR-23): the counting program, the 39 button presses that load it, and the read-back that checks it. The series' simulator presses the same 39 buttons in its self-test and gets the read-back the page predicts. — Scan: Kenbak Corporation, Programming Reference Manual, p. PR-23 (1971), via Adwater & Stir, https://adwaterandstir.com/operation-kenbak/

What none of these tools can do is show the program and its data at once. The KENBAK-1 programmer works with a coding sheet on the desk and the machine beside it, keeping the two in step by hand. That is slow, and it is also exactly the discipline the workbook set out to teach: to know, before pressing Start, what every byte should be.


Next — Volume 8: The Replica Kits — Re-creating KENBAK-1 Fifty Years On. From Grant Stockly’s TTL “Series 2” of 2011 and Mark Wilson’s Arduino-based Kenbak-uino to the µKENBAK-1 and nanoKENBAK-1 on sale today and the new chip-for-chip reproductions — what each one is, what is inside it, and how faithfully it behaves.

Comments (0)

  1. Loading…

Comments are held for moderation — nothing appears until approved.