c p u ville logo
Donn Stewart
13917 Deviar Dr
Centreville, VA 20120
dstew@cpuville.com

Designing, Building, and Selling Obsolete Computers -- for Educational Purposes -- since 2004

c p u ville logo menu icon

Programming and operation

The computer has a simple architecture. It has only one programmer-accessible register, the accumulator. Originally 12 bits wide, I added another 4 bits later to allow memory transfers. All load and store operations move data between memory and the accumulator, and the arithmetic-logical instructions use the accumulator as one operand, and data fetched from memory as the other. (The NOT operation, of course, only operates on the accumulator, and the operand in the instruction is ignored.) The results of arithmetic-logical operations are stored in the accumulator. Only the original 12 lower bits of the accumulator can be used for arithmetic-logical operations.

The computer has an instruction word size of 16 bits. The four leftmost bits of an instruction word are the operation code, and the 12 rightmost bits are the operand, like this:

instruction word format

In almost all cases, the operand is a memory address. In the case of the arithmetic-logical instructions the memory address holds the data that is to be operated on together with the data in the accumulator. For the load and store operations, the address is the source or target of the data to be moved. The one exception is the immediate load, LDI. In this case, the 12-bit operand value itself is placed in the accumulator. The various jump instructions use the operand as the target address, except the indirect jump JPI, in which the target address is held in the memory location pointed to by the operand. Here is the instruction set:

Hex OpcodeInstruction MnemonicOperation Performed
0ADDAdds contents of memory to accumulator
1ADCAdds contents of memory and carry to accumulator
2SUBSubtracts contents of memory from accumulator
3SBCSubtracts contents of memory and complemented carry from accumulator
4ANDBinary AND of memory with accumulator
5ORBinary OR of memory with accumulator
6XORBinary XOR of memory with accumulator
7NOTComplements accumulator (operand ignored)
8LDILoads 12-bit value of operand into accumulator (immediate load)
9LDMLoads contents of memory into accumulator
ASTMStores contents of accumulator to memory
BJMPJumps to memory location
CJPIJumps to contents of memory location (indirect jump)
DJPZIf accumulator = zero, jumps to memory location
EJPMIf accumulator is negative (minus), jumps to memory location
FJPCIf carry flag is set, jumps to memory location

The address space of the computer matches the size of the instruction word operand, 12 bits or 4 kilowords. The memory and input/output ports are addressed in the same way--there are no special input or output instructions. The address space in the computer is laid out this way:

000h to 7FFh (2048 words) Programmable, read-only memory (ROM)
800h to BFFh (1024 words) Static random-access memory (RAM)
C00h to FFFh (1024 ports) Input/output (I/O)

There are 5 input and 5 output ports in the computer, on the memory input/output board. The switch inputs, LED outputs and byte switcher ports are 16 bits wide. The UART ports are eight bits wide. Only the low-order eight bits of the 16 bit accumulator are transferred in UART writes. In UART reads, the upper eight bits of the accumulator are padded with zeros. Here are the computer input and output ports:

AddressInputOutput
C00hDIP switch bank 1LED bank 1
C01hDIP switch bank 2LED bank 2
C02hByte switcherByte switcher
C03hUART dataUART data
C04hUART statusUART control

The very first program I ran was a simple port reflector:

Code in ROM
LabelLocation (hex)Machine Code (hex)MnemonicOperandComment
Start0 009 C 00LDMPort_0Get number from switches
0 01A C 00STMPort_0Display number on LED's
0 02B 0 00JMPStartDo it again

When this program was running, I knew the basic structure of the computer was sound. To further test the machine I wrote the following program. This finds the largest factor of an integer, input from DIP switch bank 1. It is the same program as that first run by the first true computer, the Manchester Mark I "Baby" in 1948. Of course, my instruction set is different than the "Baby's" which had only 7 instructions!

Code in ROM
LabelLocation (hex)Machine Code (hex)MnemonicOperandComment
Start0 009 C 00LDMPort_0Get number to factor from input port
0 01A 8 00STMOriginal_numberStore number to factor in memory
0 02A 8 01STMFactorStarting factor = original number
Loop_10 039 8 01LDMFactorFactor loop
0 042 0 0FSUBOneNew factor = old factor - 1
0 05D 0 0CJPZQuitIf factor = 0, better quit (mistake)
0 06A 8 01STMFactorStore new factor
0 079 8 00LDMOriginal_numberTest factor by
Loop_20 082 8 01SUBFactorsubtracting repeatedly from original number
0 09D 0 0CJPZQuitFactor found-quit
0 0AE 0 03JPMLoop_1Went past zero, not a factor
0 0BB 0 08JMPLoop_2Still testing
Quit0 0C9 8 01LDMFactorGet the proven factor and
0 0DA C 00STMPort_0Display on the output
0 0EB 0 00JMPStartStart over
One0 0F0 0 01(constant)
Variables in RAM
LabelLocation (hex)
Original_number8 00
Factor8 01

This program tests an essential subset of the instructions, including the arithmetic instructions and two of the conditional jumps. It also tests the RAM. After this, I decided to write a program loader (see below).

Computer operation

The final configuration of the finished computer allows the computer to receive a program by way of the serial port. The clock speed switch on the logic board is set to 1.8 MHz (position number 7 on). This results in a serial port baud rate of 9600. The input port 1 switches (lower bank) are set to B08Eh, as seen in the picture of the Memory-I/O board. When the computer is started (taken out of reset), it begins execution in ROM at location 000h. There is an instruction there that jumps to the I/O port location and executes whatever instruction it finds there. This feature was put in during the troubleshooting phase of development to allow testing of the computer using a variety of routines stored in ROM. To run a particular test, a jump to that test is put on the port 1 switches. B08Eh is JMP 08Eh. Location 08Eh is the entry point for the program loader (see below).

The computer's serial port is connected to a dumb terminal (or PC running a terminal program) set for 9600 baud, one stop bit, no parity. Power is applied to the computer with the Reset/Run switch in the Reset position. A garbage character may appear on the terminal screen. The programmer may clear the terminal screen if the terminal has this ability. The computer is switched to Run. At this point the computer is ready to take instructions.

Program Loader

This program is the "operating system" of the computer. It takes hex character input from a terminal and converts it to 16-bit instruction words which are loaded into RAM starting at 0x80A. Upon receiving a ctrl-c, it jumps to 0x80A and begins to execute the loaded instructions. It needs upper case characters for A-F. Note that since the character input is 8-bit, and the path through the ALU is 12 bit, there is no easy way to get the highest order nybble of a 16-bit instruction word from the UART into memory. I solved this problem by making a table that contains the high order nybbles. A table entry is selected by the first character of each instruction word, and subsequent characters are converted to binary and placed into the word using the ALU. Later, I built a byte-switcher port which can more directly get low-order data into the high-order accumulator bits, and from there into memory. The instruction table had a gap in it due to the ASCII values of the characters, so I put some constants in the gap to save space.

The instruction set has does not have an instruction for indexed memory access, which is used to access a table. However, indexing can be done indirectly with the following procedure. Two words of RAM are set aside, the first word for the indexed instruction, the second for a return instruction (JPI or JMP). The index value is loaded into the accumulator, and the instruction to be indexed is added (using the ADD instruction). Even though the ALU is only 12 bits wide, the procedure works because the upper 4 bits of the accumulator will always contain the upper 4 bits of the most recently accessed value in memory. The indexed instruction is then placed in the RAM location set aside for it, then a JMP to that location is performed. After the instruction is finished, the return jump is performed. The program loader uses this technique. The base instructions are stored in the table gap in ROM at 0x07C and following, and there are RAM locations set aside for an indexed instruction and following return jump at 0x803 and 0x804.

There is a little patch of code at the end to allow the UART to divide its clock by 16. This helped character flow by improving start bit detection and bit framing. The program code can be entered at 0x027 or 0x08E. The program is in ROM at 0x027 because above it are some small test programs like the port reflector and factor routine. In its finished form, ROM has a JMP 0xC01 at the start. This instruction executes whatever instruction is on the lower DIP switches on the memory I/O board. In the picture, you can see the instruction word B08E there in the switches, which is JMP 08E.

Home-Built TTL CPU Program Loader

LabelLocationContentsMnemonicOperandComment
ROM:
027 907CLDM BRetInst Base Return Inst (JPI RetAdd1)
028 A804STM RetInst
029 804DLDI 04D UART mode with undivided clk
02A AC04STM UART Control
02B 8037LDI 037 UART command instruction
02C AC04STM UART Control
02D 8FFFLDI FFF Initial instruction index (-1)
02E A807STM InstIndex
MainLoop02F 8004LDI 004 Initial Nybble no. (loop 4 times)
030 A809STM NybbleNo
ReturnLoop0319809LDM NybbleNo
032 2079SUB One
033 E03FJPM Done Instruction complete; store in RAM
034 A809STM NybbleNo Instruction not complete;
035 007DADD BLDMMaskGet nybble mask by creating indexed
036 A803STM IndInst instruction
037 803ALDI 03A
038 A805STM RetAdd1
039 B803JMP IndInst Jump to indexed instruction
03A A808STM Mask Store mask retrieved from table
03B 803ELDI 03E
03C A806STM RetAdd2 Store return address
03D B04EJMP RxPoll_1 Get next char and process
03E B031JMP ReturnLoop
Done 03F 9807LDM InstIndex Increment index and store
040 0079ADD One
041 A807STM InstIndex
042 007EADD BSTMInst Create indexed store instruction
043 A803STM IndInst
044 8048LDI 048 Return address
045 A805STM RetAdd1
046 9802LDM Inst
047 B803JMP IndInst Store instruction in RAM
TxLoop_2048 9C04LDM UART Control
049 4079AND One Check TxRdy
04A D048JPZ TxLoop_2
04B 800DLDI ASCII CR Carriage return
04C AC03STM UART Data Send CR to display
04D B02FJMP MainLoop
RxPoll_104E 9C04LDM UART Control
04F 407AAND Two Check RxRdy
050 D04EJPZ RxPoll_1
051 9C03LDM UART Data Get Char
052 A800STM Char Store char
053 206BSUB CTRL-C End of input?
054 D80AJPZ ProgStart Yes-start program
TxPoll_1055 9C04LDM UART Control No- Echo Char and process
056 4079AND One Check TxRdy
057 D055JPZ TxPoll_1
058 9800LDM Char
059 AC03STM UART Data Echo Character
05A 207BSUB 30hex Make index by subtracting 30h
05B 007FADD BLDMInst Create indexed LDM instruction
05C A803STM IndInst Store in RAM
05D 8060LDI 060 Return address
05E A805STM RetAdd1
05F B803JMP IndInst Get partial inst from table
060 A801STM PartInst
061 9808LDM Mask
062 D067JPZ Next1 Special for highest nybble
063 4801AND PartInst Mask off partial inst
064 0802ADD Inst Combine with prior inst and store
065 A802STM Inst
066 C806JPI RetAdd2 Done
Next1 067 4801AND PartInst Mask off partial inst
068 A802STM Inst Store (high nybble)
069 C806JPI RetAdd2 Done
Zero 06A 0000
CTRL-C 06B 0003
MaskTable06C000F
06D 00F0
06E 0F00
InstTable06F0000
070 1111
071 2222
072 3333
073 4444
074 5555
075 6666
076 7777
077 8888
078 9999
One 079 0001
Two 07A 0002
30hex 07B 0030
BRetInst07C C805JPI RetAdd1
BLDMMask07D 906CLDM MaskTable
BSTMInst07E A80ASTM ProgStart
BLDMInst07F 906FLDM InstTable
080 AAAA
081 BBBB
082 CCCC
083 DDDD
084 EEEE
085 FFFF
08E 907CLDM BRetInst
08F A804STM RetInst
090 804ELDI 04E UART mode with Rx and Tx clk/16
091 AC04STM UART Control
092 B02BJMP 02B Entry with UART clk/16
LabelLocationContentsMnemonicOperandComment
RAM:
Char800
PartInst 801
Inst 802
IndInst 803
RetInst 804
RetAdd1 805
RetAdd2 806
InstIndex 807
Mask 808
NybbleNo 809
ProgStart 80A

Links to Original CPU pages:

menu icon