Programming the Kenbak-1
Programming the Kenbak-1 is Easy...
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.
A First look at a Simple Program:
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)
How to Put our Program into the Computer.
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.
But what about "Useful" Programs?
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": JFM AMJ JAS OND: 144 025 036 146
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 0 is Sunday, the first day of the week, 2 is Monday, and so on.
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 2 entered bytes.
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) > 120 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 would work within the limited time range.
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.
That being said, the above program is having difficulties actually giving the correct answer, so debugging is planned for a later date. But still, even a non-working program can give a good idea of how to program, and how a program should look, right? Work with me here.
After a recent conversation with John Blankenbaker, it became clear that the original 1971 program was slightly different. 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 ended on the front panel, which is much easier for the "user" than having to convert to binary octal. The conversion from this BCD to binary is not hard, but the above program will need to be modified to closer reflect the historical program.
If anyone is able to make a good working version of this program, please CONTACT US here, and we'll thank you with either a genuine piece from an original Kenbak-1 computer (not an important part) or a Kenbak-1 keychain (you may pick.)