The Kenbak-1 was marked for teaching students without any computer experience. So it's fairly easy to program.
Rather than a high-level computer language (like Fortran, Cobol, C, python, java, etc.) programs are written in "machine code" which includes just primitive operations on single bytes of data. The programmer has 256 bytes of memory (minus 8 special locations) which they can fill with instructions and data. Then the computer executes the instructions.
Only 8 input switches, 8 output lights, and 256 bytes of memory are significant limitations. Still, many complex and useful programs can be written with a little imagination.
To get started, let's make a simple program, and look at it in detail.
A common "test" program for many of the "blinking lights" computers of the 1960's and 1970's was a simple program to count upward from zero, on the front panel lights. This is easy to enter and verifies the computer is working,
Here's a simple flow-chart. We start by initializing the "A" register to zero. (This isn't really important as 8 bits will overflow quickly back to zero no matter which number A starts with.)
Second we display that A register value on the front panel lights. In the Kenbak-1, this is done by copying it to memory location 200. Third, we increment the A register value by adding "1". Last, we jump back to the beginning, so we can keep displaying and incrementing, over and over again.
In the original Kenbak-1, it will do this loop almost 256 times in a second, so the highest bit light flashes a little slower than once a second.
To the right is the program in a format we can call an "assembled listing" where the special "op-codes" for each instruction are written, along with description:
The first column, contains labels of any memory addresses which could help understand the program.
The second column has the address of each memory location.
The third column, is the coded data to be put into that address.
The last, 4th column, has a textual description.
For each instruction, there is a one-byte code denoting the instruction which we have to look up in the chart on the left. And most of the instructions are followed by a single byte operand, which may be a number, or a memory address where to find the constant, or even the address where a table is, that may hold our constant. Most of the instructions have a single additional byte following the instruction code, to specify this operand.
The chart on the left helps find the correct code for each of the instructions. The first column shows the instruction type, the second column shows the first octal digit (0, 1, or 2) and the next two columns show the second and third octal digits (0-7). We can use this chart to find out the 3-digit code for any instruction.
In our example above, to code the "Store A 200" instruction , we find the "load" instruction in the left most column (highlighted in yellow), see that the "A" register denotes "0' for the first digit, the instruction "Store" means the 2nd digit should be 3, and since our operand "200" will be a memory location that follows immediately after the code, the third digit is 4. So the code for "Store A 200" will be 034, followed by the constant "200" as the next byte.
To code the instruction "ADD A 1" (pink highlight), we see the first digit should again be "0" to denote the "A Register" and the second digit should be "0" to denote the "ADD" instruction, and since the "1" operate is a constant that follows, the third digit should be "3." This means the code will be "003" and the next byte will be the constant "001" to add "one" to the a register.
Lastly, for the Jump instruction, it's an "Unconditional" jump, not dependent on any other condition, so will be "344" (highlighted in blue)
Now that we've made the above program, we need to enter it into the Kenbak-1 computer. We must enter the "data" bytes for each of the memory "addresses" from 000 to 011.
First, since our data starts at 000, we need to store that as the starting address, but setting the front panel lights to 000, and press the "set address" button.
Next, we set the front panel lights to each of the data bytes, and press the "Store" button to enter that into the current memory location. The memory location will automatically increment after each "Store", to point to the next location after it. When entering the needed data, if a light is already on that doesn't need to be on, the entire byte must be cleared with the "Clear" button. Otherwise, touching each button turns on that light, without turning off any of the already lit lights. The animation shows exactly which buttons should be pressed.
(NOTE: The animation incorrectly shows the fifth byte as 033 when it should be 034 octal (thanks zuchodrig!) but changing this animation will take more time than I have at the moment. But you get the idea, right?)
Animation of entering a simple program into the Kenbak-1 computer.
On the internet is a whole lot of Kenbak-1 programs, such as simple games, like "Kill the Bit" or "Dice-Rolling" simulations.
John did make up some interesting programs back in 1971 which he showed to educators and students, to show that useful things could be done.
One story he has often retold is a program where a user would enter a date and year (just from 1900 to 1999) and the computer would tell which day of the week that date landed on by lighting up one of 7 lights that represented the day of the week, or an 8th "error" light, if the input date was not valid. How he coded this is lost to memory, and some complex examples have been made up, which cover a wider time line. We hope to recreate that here:
Here is a standard method suitable for mental computation of the day of the week:
Take the last two digits of the year.
Divide by 4, discarding any fraction.
Add the day of the month.
Add the month's "key value": Jan=1, Feb=4, Mar=4, Apr=0, May=2, Jun=5, Jul=0, Aug=3, Sep=6, Oct=1, Nov=4 Dec=6
Subtract 1 for January or February of a leap year.
Add 0 for 1900's, 6 for 2000's, 4 for 1700's, 2 for 1800's (if other centuries are needed.)
Add the last two digits of the year.
Divide by 7 and take the remainder.
Now 1 is Sunday, the first day of the week, 2 is Monday, and so on. Zero is Saturday.
An implementation of this is in the program below. Since John Blankenbaker would have to type each byte in by hand prior to a presentation, I suspect he aimed for the shortest code possible, and I think the below is the lowest number of entered bytes possible.
In this program you pre-load the year since 1900, the day of the month, and the month into locations 000, 001, and 002 respectively. This is an odd order, year, day, month, but since the indexed addressing mode in the above step 4 wants the month in the x-register, this saves some bytes to move data to the X register.
While the above implementation is nice and short, it does fail for some dates after about 1977 due to a byte overflow. If the (YEAR x 1.25 + Day_of_Month + month_table_value) > 127 or so, the tally after step 7 will likely overflow past +127, and be seen as a negative number. This gives a wrong answer. However, John used this program to demonstrate any date in the past, from 1900 to 1971 (the present, at that time) could be looked up (such as birthdays, anniversaries, and the like) so the program probably worked for any date in the 1900's, in the past (prior to 1973.)
A quick fix is to add two more instructions: If the tally after adding in the year is <0 (which means it overflowed, past 127) subtract 126 (which is a multiple of 7) and the day of week is the same, but the value is safely in positive range.
I must thank Jeff Jetton <jeff.jetton@gmail.com> in Nashville, TN for fixing a bug in the above program.
Unfortunately, Jeff Jeton also made me aware that instead of just doing the subtraction of 7, and checking if less than zero, it makes more sense to check the "borrow bit" after 7 is subtracted. This allows the sum byte to be interpreted more as an unsigned byte ranging from zero to 255 instead of -128 to 127 (a signed number) and with this new higher range, any date in the 1900's should now work. Blankenbaker discussed using this "borrow bit" with subtraction in his "Laboratory Manual" so I'm sure he would have made use of this.
But soon after I started to write the above program, a conversation with John Blankenbaker made it clear that this is not how his original program worked. In the original Blankenbaker program, the 2 digit year, month, and day of month were indeed stored in registers prior to running the program, but they were stored in binary coded decimal (BCD). ie for a year of 1972, the hexadecimal number 0x72 would be entered on the front panel, which is much easier for the user than having to convert to binary octal. Jeff Jetton made the following program which does this BCD to binary conversion.
Jeff Jetton <jeff.jetton@gmail.com> of Nashville, TN wrote the following program with a subroutine to convert an 8-bit binary coded decimal number to binary. But he also did a few extra tricks, a slightly different formula, possibly from the Wikipedia "Determination of the day of the week" entry. He has a different lookup table so the month doesn't have to be converted from BCD, there will be just some blank entries if the month is between 0x09 and 0x10, but those blank unused spots can be used for temporary storage.
Below is Jeff Jetton's Kenbak-1 Day of Week Program....
I have to admit I haven't had a chance to try out the above code, but it looks good.
Most recently I'm thinking of trying to recreate John Blankenbaker's algorithm as close as possible. I do suspect he was using the method published in the Farmer's Almanac for many years, not one of the methods in the Wikipedia Day_of_the_Week entries. And in his laboratory manual it discusses his method for converting BCD to Binary, as well as using the "Borrow Bit" for subtracting 7's from an unsigned 8-bit integer.
Below is an interesting short program from Wikipedia's Day of the Week entry, which covers a wide date range. But I really don't think this was commonly available to everday people in 1972. However, the Farmer's Almanac was ubiquitous in American homes in the 70's, and I'm sure Blankenbaker would have had access to it..
This is a great and simple, function. But the way John Blankenbaker complained about Math Teachers not knowing the method to calculating day of the week, I think this suggests the method was readilly available to everyone, and so I think he was talking about the Farmer's Almanac method.