1. Simple one-module programs that can be entered directly into the program memory and executed, usually on data entered into the registers.
Addition:
Register A: Summand 1
Register B: Summand 2
010 001 add A+B->C
111 110 halt
Adds the summands, writes the sum to register C, then halts. Very trivial, since addition is one of the core operations. A halt must be offered, or the program will cycle without end.
Subtraction:
Buffer: Minuend
Register A: 1111 111 111
Register B: Subtrahend
010 001 add A+B->C
010 110 add AXC->B
001 001 store Bf in A
010 001 add A+B->C
111 110 halt
/* 1. subtracts one from the subtrahend and stores the result in C.
2. XOR-compares every bit in C with 1. The result is the bitwise inversion of the contents of C. This value is then stored back into B.
1. and 2. put together calculate the binary complement of the subtrahend - this value can be generated either by inverting the bit values, then adding one, or by subtracting one, then inverting.
3. all three main data registers are needed to calculate the complement; the minuend must be stored elsewhere and copied in later.
The rest is just an addition.*/
Multiplication:
Buffer: smaller factor
Register A: larger factor
001 100 store Bf in D
010 100 add A->B
011 101 cot D-z
010 000 add A+B->B
011 101 cot D-z
101 011 jmp 4
111 110 halt
/*
cot is mnemonic for "count" - it does either incrementation or decrementation, depending on the operand, and i didn't want to abbreviate it "cnt".
1. the first factor (smaller than 63) must be stored in the counting register. It can also be entered manually to D, i just wanted to verify the "store to counter" command.
2. the adder receives only the value of A as input, thus produces the same value as output. This operation allows single-instruction copies of A's values either to B or C, instead of the two-operation standard of going through the buffer. Copying the value instead of starting with additions means there's no need to set register B to zero beforehand.
The actual multiplication happens by sequential addition - add A to the running sum in B, decrement counter D and repeat if the latter value has not reached zero yet.
Longer programs are required if the factor in reg. D can be zero or one.
*/
Division
Register A: divisor
Register B: zero
Register C: dividend
Register D: zero
010 000 add A+B->B
100 000 cmp B>C
111 110 halt
011 100 cot D+z
101 000 jmp 1
/* the division is done by repeatedly adding the divisor to a running sum and comparing that sum with the dividend. When that sum is no bigger than the dividend, the counter is incremented and operation jumps back to the start. When the running sum is bigger than the dividend, the program halts. The quotient stands in the counter register D. */
Fibonacci sequence
Register B: one
Register C: one
Register D: loop number, should be a number from one to seven.
010 010 add B+C->B
010 011 add B+C->C
011 101 cot D-z
101 000 jmp 1
111 110 halt
/* since the location where an addition result is stored can be chosen freely between registers B and C, two registers that can also be added to each other, the fibonacci numbers pretty much calculate themselves. It's only necessary to add a counting loop.*/
2. Program actually installed in the CRAMPS:
a) "boot" program in Module 1
N.B. write-to status bit must be set.
111 000 read input (from lever array)
001 011 store at Program adress 4
111 101 toggle write-to status bit
001 000 NOP
110 001 load Module 2
110 100 load Module 5
111 100 toggle Module bank status bit
110 000 load Module 9
/* 1. the read input command simply stops operation and opens access to the lever array. The room also contains another operation lever, by which the machine can be restarted once a value has been entered via the levers to the buffer. The value of the six lowest bits should be 101 100, 101 101 or 101 110.
2. Due to the set status bit, the value in the buffer is not written to data register C, but rather to the adressed program position.
3. resets the status bit, further writes go to data registers again.
4. is actually overwritten by the value entered into the buffer. It should be a jump command now, either to position 5,6 or 7. Each of the three starts up one of the three main programs. In the last case, module banks must be switched first.*/
Program 1: sort three numbers by ascending order. Modules 2-4
Module 2
111 010 read datum from tape
001 001 store Bf to A
111 010 read datum from tape
001 010 store Bf to B
111 010 read datum from tape
001 011 store Bf to C
110 010 load Module 3
unused
/* Preparation. Fetches three data from the SHROOM and sticks them into the main data registers.*/
Module 3
100 011 comp A>B
101 101 jmp 6
100 000 cmp B>C
110 011 load Module 4
111 110 halt
000 010 load Bf from B
010 100 add A->B (direct copy)
001 001 store Bf to A
/* compares the stored numbers. If the "lower" number is actually bigger, the next instruction is read and executed, otherwise - when the number pair is alread properly sorted - it gets skipped. If A is larger than B, operation jumps to 6. A and B are exchanged by moving B to the Buffer, directly copying A to B via the adder and storing the value taken from B at A. Operation then wraps around and starts over at position 1.
If B is larger than C, the final module is loaded to exchange those values.*/
Module 4
000 011 load Bf from C
001 110 store Bf at F
000 010 load Bf from B
001 011 store Bf at C
000 110 load Bf from F
001 010 store Bf at B
110 010 load Module 3
unused
/* exchanges B and C, using F as additional storage. Module 3 is then loaded into active memory and operation again compares A>B.*/
Program 2: square root calculation, Modules 5-8
Module 5
111 010 load Bf from tape
001 011 store Bf at C
111 111 toggle I/O status bit
111 001 write to output
111 000 read input
001 001 store Bf to A
000 000 load zero into Bf
110 101 load Module 6
/* Primes the operation by fetching a source number from tape and storing it at C, dividing it by two via right-shifting and storing that number as divisor/first guess at A. Finally, a zero is fetched because the "running sum" and "Quotient" counters must be set to zero before division can take place. */
Module 6
001 010 store Bf at B
001 100 store Bf at D
010 000 add A+B->B
100 000 cmp. B>C
110 110 load Module 7
011 100 cot D+z
101 010 jmp 3
111 110 halt
/* the division loop takes place from positions 3 to 7. The program will spend most of its time here. Division is performed like in the above division example - by adding the divisor into the running sum and comparing that to the dividend. If the data we're using don't work out and the calculation goes nowhere (e.g. when dividing by zero), the counter will eventually wrap around to zero, disabling the jump. At this point, operation will stop. Normally, running sum will grow to be bigger than the Dividend at some point, the correct quotient will stand in the counter and operation proceeds to the next module. */
Module 7
000 100 load Bf from D
001 010 store Bf at B
100 011 cmp. A>B
101 101 jmp 6
111 110 halt
010 000 add A+B->B
000 010 load Bf from B
110 111 load Module 8
/* The calculated quotient is compared with the divisor. If the divisor is bigger, the result has not been reached. If the divisor is not bigger, the calculation is finished, the square root is the divisor, found in Register A.
If the result is not reached, approximation is done by averaging divisor and quotient. This is done by adding them up and dividing the result by two. Due to the small size of modules, this operation is spread over two modules. Only the summation takes place here, the division by two must be done in Module 8. */
Module 8
111 001 write output
111 000 read input
001 001 store Bf at A
000 000 load zero to Bf
110 101 load Module 6
unused
unused
unused
/* The sum of Quotient and Divisor is halved by bitshifting it to the right, done through the "control latch" input/output. The result is stored as new divisor in register A and a new zero is brought to the buffer, so that registers B and D can be cleared once we return to Module 6 to perform the next division. */
Program 3: Hello world. Modules 9-14
Module 9
111 001 write output
110 001 load Module 10
101 011 x
101 010 x
111 011 x
101 010 x
101 011 x
100 011 x
Module 10
111 001 write output
110 010 load Module 11
100 010 x
100 010 x
100 010 x
100 010 x
111 011 x
000 011 x
Module 11
111 001 write output
111 011 load Module 12
010 000 x
101 000 x
101 000 x
101 000 x
010 000 x
000 000 x
Module 12
111 001 write output
111 100 load Module 13
101 011 x
101 010 x
101 011 x
101 011 x
011 010 x
010 011 x
Module 13
111 001 write output
111 101 load Module 14
111 001 x
010 010 x
010 001 x
010 000 x
111 011 x
100 101 x
Module 14
111 001 write output
111 110 halt
111 001 x
010 001 x
010 001 x
010 000 x
010 001 x
000 000 x
/*In each module, program positions 3-8 are never read and executed, they are entirely converted into pseudo-graphics for display on the 5x7 "pixel" display. I marked them with "x", because the actual instructions they decode to are irrelevant. In module 14, for example, the program code would once again be sent to output, contents of A and B would be added together four times, writing the results three times to C (essentially pointless to repeat) and once to B, then zero would be loaded into the buffer and the program would start over.
The command words map to the output matrix like this:
3333338
4444448
555o558
6666668
7777778
3-7 - command words, read left-to-right. 8 - command word #8, read top-to-bottom (except for its sixth bit, which is dropped).
o - oops. I noticed too late that my reading loops were only 34 bits long. Instead of linking up another bit-reader just to accomodate the last dot
while the middle column was going to stay unused in the "text" displays anyway, i just left the central hatch unlinked to the input. It gets opened by the "screen saver", though.
The program outputs "HE" "LL" "O " "UR" "IS" "T!" on the display. It takes about four days to execute, and the graphics are very crude and wonky:
The "X" shapes are the "screen saver" displayed when no output is sent. The display has only that one significant input, which could just as well be active constantly, but i wanted to demonstrate that the computer can send dedicated output; a different data source _could_ be linked up to the display.
*/