Contents > General-purpose Computer
This section presents the MC programs that implement internal control sequences and machine code instructions. The programs represent a layer of abstraction analogous to microcode in traditional processor design.
As revealed in the image below, the state register consists of twenty-one 8-bit registers. Most of them are internal registers, but a few are exposed to machine code instructions.
The three bytes immediately left of the state register and the byte immediately right of the state register are called I, J, K, and Q, respectively, regardless of the state register’s location. The MC programs execute relative to byte I.
Several 8-bit registers jointly function as 16-bit or 24-bit registers:
The 16-bit address register, a, stores I’s address:
Since the state register starts out two bytes right of the machine code program, a process initializes a to L−1.
The MC program that slides the state register left decrements a to keep it pointed at I:
SLIDE_STATE_REG_LEFT.mc DEC_16 13 ; --a; SWAP 2 ; swap K right SWAP 3 ; swap K right SWAP 4 ; swap K right SWAP 5 ; swap K right SWAP 6 ; swap K right SWAP 7 ; swap K right SWAP 8 ; swap K right SWAP 9 ; swap K right SWAP 10 ; swap K right SWAP 11 ; swap K right SWAP 12 ; swap K right SWAP 13 ; swap K right SWAP 14 ; swap K right SWAP 15 ; swap K right SWAP 16 ; swap K right SWAP 17 ; swap K right SWAP 18 ; swap K right SWAP 19 ; swap K right SWAP 20 ; swap K right SWAP 21 ; swap K right SWAP 22 ; swap K right
Similarly, the MC program that slides the state register right increments a:
SLIDE_STATE_REG_RIGHT.mc INC_16 13 ; ++a; SWAP 23 ; swap Q left SWAP 22 ; swap Q left SWAP 21 ; swap Q left SWAP 20 ; swap Q left SWAP 19 ; swap Q left SWAP 18 ; swap Q left SWAP 17 ; swap Q left SWAP 16 ; swap Q left SWAP 15 ; swap Q left SWAP 14 ; swap Q left SWAP 13 ; swap Q left SWAP 12 ; swap Q left SWAP 11 ; swap Q left SWAP 10 ; swap Q left SWAP 9 ; swap Q left SWAP 8 ; swap Q left SWAP 7 ; swap Q left SWAP 6 ; swap Q left SWAP 5 ; swap Q left SWAP 4 ; swap Q left SWAP 3 ; swap Q left
The 16-bit program counter, P, stores the address of the instruction to be fetched. As the computer slides the state register across RAM, when P equals a, it copies IJK into the instruction register, ijk:
Machine code instructions vary in length from one to three bytes. The first byte is the opcode and the rest, if any, is an 8-bit or a 16-bit operand. At twenty-four bits wide, ijk can store any instruction. And at decode time, if the instruction does not take up the full width of ijk, the computer discards the remainder.
The implementation follows. To highlight the most pertinent code, the listings omit SWAPs from this point forward.
FETCH.mc CMP_C 9 ; s1 = (P1 == a1); CMP_AND_C 10 ; s1 &= (P0 == a0); COPY_A_B_C 2 ; if (s1) i = I; COPY_A_B_C 3 ; if (s1) j = J; COPY_A_B_C 4 ; if (s1) k = K;
Since there is no 16-bit comparator, the program separately compares the high bytes, P1 and a1, and the low bytes, P0 and a0. It puts the result in s1, one of two 8-bit shuttle registers that transport intermediate values between production sites and consumption sites. Because there is no 24-bit copy function, the program individually and conditionally copies the bytes of IJK to ijk if s1 is set.
After the computer slides the state register across RAM, it increments P by the fetched instruction length in preparation of the next fetch. The implementation uses match functions that check if the opcode in i fits the pattern of a 2- or 3-byte instruction:
INCREMENT_P.mc INC_16 8 ; ++P; SEX_C 12 ; s1 = (i is a 2-byte instruction); INC_16_C 8 ; if (s1) ++P; THREE_C 12 ; s1 = (i is a 3-byte instruction); INC_16_C 8 ; if (s1) ++P; INC_16_C 8 ; if (s1) ++P;
After the computer increments P, it decodes and executes the fetched instruction. The process begins with the transfer instructions, each of which copies an 8-bit source register to an 8-bit destination register, among those listed below.
Register | Name | Description |
---|---|---|
A | Accumulator | Input and lone output of all arithmetic and logic instructions. |
B | Data Register | Input of some arithmetic and logic instructions. |
M | High Memory Register | High byte of the 16-bit Memory Register, MN, which contains the source address of load instructions and the destination address of store instructions. |
N | Low Memory Register | Low byte of MN. |
There are twelve ways to transfer data between those registers:
The following MC program decodes and executes the transfer instructions.
DECODE_EXECUTE_TRANSFER.mc TMX_C 12 ; s1 = (i matches TM*); C_COPY_A_B 13 ; if (s1) s0 = M; TNX_C 13 ; s1 = (i matches TN*); C_COPY_A_B 14 ; if (s1) s0 = N; TAX_C 17 ; s1 = (i matches TA*); C_COPY_A_B 18 ; if (s1) s0 = A; TBX_C 18 ; s1 = (i matches TB*); C_COPY_A_B 19 ; if (s1) s0 = B; TXB_C 18 ; s1 = (i matches T*B); C_COPY_B_A 19 ; if (s1) B = s0; TXA_C 17 ; s1 = (i matches T*A); C_COPY_B_A 18 ; if (s1) A = s0; TXN_C 13 ; s1 = (i matches T*N); C_COPY_B_A 14 ; if (s1) N = s0; TXM_C 12 ; s1 = (i matches T*M); C_COPY_B_A 13 ; if (s1) M = s0;
If the opcode in i matches the pattern of a transfer instruction, then the program copies the source register to s0 (lines 1–8). Subsequently, the program copies s0 to the destination register, again conditioned on i (lines 10–17). Since transfer instructions are 1-byte instructions, the program ignores j and k.
Next, the computer handles arithmetic and logic instructions. Each computes an 8-bit function of A or of A and B, and it puts the result in A. If the result is negative (if bit-7 of A is 1), the instruction sets the negative flag, n, to 1; otherwise, it resets n to 0. If the result is zero, the instruction sets the zero flag, z, to 1; otherwise, it resets z to 0.
The MC program below decodes and executes the twelve arithmetic and logic instructions, all of which are 1-byte instructions. Each implementation follows the same pattern. First, the program compares i and an opcode, storing the result in s1. Then, the program sets s0 to a function of A or of A and B. Finally, the program conditionally copies s0 to A based on s1.
DECODE_EXECUTE_ARITHMETIC_LOGIC.mc CLEAR 14 ; d = 0; ADD_C 12 ; s1 = (i is ADD); OR_AB_FB 13 ; d |= s1; COPY_B_A 15 ; s0 = A; ADD_AB_FB 16 ; s0 += B; C_COPY_A_B 14 ; if (s1) A = s0; AND_C 12 ; s1 = (i is AND); OR_AB_FB 13 ; d |= s1; COPY_B_A 15 ; s0 = A; AND_AB_FB 16 ; s0 &= B; C_COPY_A_B 14 ; if (s1) A = s0; DEC_C 12 ; s1 = (i is DEC); OR_AB_FB 13 ; d |= s1; COPY_B_A 15 ; s0 = A; DEC 15 ; --s0; C_COPY_A_B 14 ; if (s1) A = s0; INC_C 12 ; s1 = (i is INC); OR_AB_FB 13 ; d |= s1; COPY_B_A 15 ; s0 = A; INC 15 ; ++s0; C_COPY_A_B 14 ; if (s1) A = s0; LS2_C 12 ; s1 = (i is LS2); OR_AB_FB 13 ; d |= s1; COPY_B_A 15 ; s0 = A; LS2 15 ; s0 <<= 2; C_COPY_A_B 14 ; if (s1) A = s0; LS3_C 12 ; s1 = (i is LS3); OR_AB_FB 13 ; d |= s1; COPY_B_A 15 ; s0 = A; LS3 15 ; s0 <<= 3; C_COPY_A_B 14 ; if (s1) A = s0; LS4_C 12 ; s1 = (i is LS4); OR_AB_FB 13 ; d |= s1; COPY_B_A 15 ; s0 = A; LS4 15 ; s0 <<= 4; C_COPY_A_B 14 ; if (s1) A = s0; OR_C 12 ; s1 = (i is OR); OR_AB_FB 13 ; d |= s1; COPY_B_A 15 ; s0 = A; OR_AB_FB 16 ; s0 |= B; C_COPY_A_B 14 ; if (s1) A = s0; RS1_C 12 ; s1 = (i is RS1); OR_AB_FB 13 ; d |= s1; COPY_B_A 15 ; s0 = A; RS1 15 ; s0 >>= 1; C_COPY_A_B 14 ; if (s1) A = s0; RS5_C 12 ; s1 = (i is RS5); OR_AB_FB 13 ; d |= s1; COPY_B_A 15 ; s0 = A; RS5 15 ; s0 >>= 5; C_COPY_A_B 14 ; if (s1) A = s0; SUB_C 12 ; s1 = (i is SUB); OR_AB_FB 13 ; d |= s1; COPY_B_A 15 ; s0 = A; SUB_AB_FB 16 ; s0 -= B; C_COPY_A_B 14 ; if (s1) A = s0; XOR_C 12 ; s1 = (i is XOR); OR_AB_FB 13 ; d |= s1; COPY_B_A 15 ; s0 = A; XOR_AB_FB 16 ; s0 ^= B; C_COPY_A_B 14 ; if (s1) A = s0; CLEAR 15 ; s0 = 0; // required by C_MINUS C_MINUS 15 ; s0 = (A < 0); C_COPY_A_B 14 ; if (d) n = s0; C_ZERO 15 ; s0 = (A == 0); C_COPY_A_B 14 ; if (d) z = s0;
The program resets the destination flag, d, to 0 (false) on line 1. If i contains any of the twelve arithmetic and logic instruction opcodes, then the program sets d to 1 (true). On lines 77 and 79, the program updates n and z, respectively, subject to d.
Next, the computer handles the set instructions. There are three of them and they vary in length. The first is a 2-byte instruction that assigns A to an 8-bit immediate, a constant operand in j. The second is the same except B is the target:
The third is a 3-byte instruction that assigns MN to a 16-bit immediate contained in jk:
The following MC program decodes and executes the set instructions.
DECODE_EXECUTE_SET.mc SEA_C 12 ; s1 = (i is SEA); C_COPY_A_B 12 ; if (s1) A = j; SEB_C 11 ; s1 = (i is SEB); C_COPY_A_B 12 ; if (s1) B = j; SMN_C 11 ; s1 = (i is SMN); C_COPY_A_B 12 ; if (s1) M = j; C_COPY_A_B 11 ; if (s1) N = k;
Next, the computer handles the branch instructions. There are six of them. Each consists of an opcode in i followed by a 16-bit target address in jk. The first unconditionally jumps to the target simply by copying jk to P:
The next four conditionally jump to the target iff a specified flag, n or z, equals a given value, 0 or 1:
The last is the jump-to-subroutine instruction. It copies P to the 16-bit return register, R:
Since the copy occurs after P is incremented, R points to the instruction immediately following the jump-to-subroutine instruction. That is, R contains the return address, hence its name.
Following the copy, it jumps:
Afterwards, a return-from-subroutine instruction restores P from R:
This link register scheme enables subroutine calls one level deep (all subroutines are leaf subroutines). The computer does not provide a call stack.
Here is the MC program for the branch instructions:
DECODE_EXECUTE_BRANCH.mc ; Jump: JMP_C 9 ; s1 = (i is JMP); C_COPY_A_B 10 ; if (s1) P0 = k; C_COPY_A_B 9 ; if (s1) P1 = j; ; Conditional branches: BEQ_C 8 ; s1 = (i is BEQ); AND_AB_FB 9 ; s1 &= z; C_COPY_A_B 9 ; if (s1) P1 = j; C_COPY_A_B 12 ; if (s1) P0 = k; BNE_C 8 ; s1 = (i is BNE); AND_NOT_AB_FB 9 ; s1 &= !z; C_COPY_A_B 9 ; if (s1) P1 = j; C_COPY_A_B 12 ; if (s1) P0 = k; BMI_C 8 ; s1 = (i is BMI); AND_AB_FB 9 ; s1 &= n; C_COPY_A_B 9 ; if (s1) P1 = j; C_COPY_A_B 12 ; if (s1) P0 = k; BPL_C 8 ; s1 = (i is BPL); AND_NOT_AB_FB 9 ; s1 &= !n; C_COPY_A_B 9 ; if (s1) P1 = j; C_COPY_A_B 12 ; if (s1) P0 = k; ; Jump-to-subroutine: JSR_C 8 ; s1 = (i is JSR); C_COPY_A_B 13 ; if (s1) R0 = P0; C_COPY_A_B 12 ; if (s1) P0 = k; C_COPY_A_B 10 ; if (s1) R1 = P1; C_COPY_A_B 9 ; if (s1) P1 = j; ; Return-from-subroutine: RTS_C 8 ; s1 = (i is RTS); C_COPY_B_A 10 ; if (s1) P1 = R1; C_COPY_B_A 13 ; if (s1) P0 = R0;
Last, the computer handles the store and load instructions. A store instruction writes a source register, A or B, to memory at the address in MN. While decoding, if the instruction is not a store instruction, the computer resets the write flag, w, to 0. Otherwise, it sets w to 1, and it copies the source register to the 8-bit memory value register, m:
The computer executes a store instruction as it slides the state register slides across RAM. For each address, if w is 1 and a equals MN, then the computer copies m to I:
A load instruction reads a byte from memory at the address in MN, and it puts the byte in a destination register, A or B. While decoding, if the instruction is not a load instruction, the computer resets the read flag, r, to 0. Otherwise, it sets r to 1, and it assigns the aforementioned destination flag, d, to 0 for A or 1 for B.
The computer executes a load instruction as it slides the state register slides across RAM. For each address, if r is 1 and a equals MN, then the computer copies I to m:
After the computer slides the state register across RAM and before it decodes the fetched instruction, if r is 1, then the computer copies the read byte in m to A or B based on the value in d, and it assigns n and z as if the read byte were the result of an arithmetic or logic instruction:
Note: d, n, r, w, and z each occupy a full byte of the state register despite being 1-bit flags. And there is neither a carry flag nor an overflow flag.
The following MC program decodes load and store instructions. As explained, load assigns d and r, while store assigns m and w.
DECODE_LOAD_STORE.mc STB_C 11 ; s1 = (i is STB); C_COPY_B_A 12 ; if (s1) m = B; STA_C 11 ; s1 = (i is STA); C_COPY_B_A 12 ; if (s1) m = A; LDB_C 12 ; d = (i is LDB); LDX_C 7 ; r = (i matches LD*); STX_C 6 ; w = (i matches ST*);
Since load and store instructions are single-byte instructions, the program ignores j and k.
The MC program below executes load and store instructions. As the computer slides the state register across RAM, the program compares MN and a. When they match, the program copies I to m if r indicates read, or it copies m to I if w indicates write.
EXECUTE_LOAD_STORE.mc CMP_C 12 ; s0 = (M == a1); CMP_AND_C 14 ; s0 &= (N == a0); COPY_B_A 11 ; s1 = s0; AND_AB_AF 8 ; s1 &= r; COPY_A_B_C 5 ; if (s1) m = I; COPY_B_A 11 ; s1 = s0; AND_AB_AF 7 ; s1 &= w; COPY_B_A_C 5 ; if (s1) I = m;
The following MC program completes the execution of load instructions. If r indicates read, then the program assigns n and z based on m, and the program copies m to A or B based on d.
ASSIGN_LOADED.mc MINUS_C 11 ; s0 = (m < 0); C_COPY_A_B 16 ; if (r) n = s0; ZERO_C 16 ; s0 = (m == 0); C_COPY_A_B 17 ; if (r) z = s0; C_AND_A_NOT_B 16 ; s0 = r & !d; C_COPY_A_B 18 ; if (s0) A = m; AND_A_B_C 15 ; s0 = r & d; C_COPY_A_B 19 ; if (s0) B = m;
Putting it all together, the MC program below slides the state register leftward across RAM. Along the way (lines 1–24), it fetches an instruction, and it potentially loads or stores a byte. Afterward (lines 26–32), the program assigns A or B to the loaded byte (if there is one), it increments P, and it decodes and executes the transfer instructions, the arithmetic and logic instructions, the set instructions, and the branch instructions. Finally, it decodes the load and store instructions, deferring their execution to the rightward slide.
CYCLE_LEFT.mc FETCH L-1 EXECUTE_LOAD_STORE L-1 SLIDE_STATE_REG_LEFT L-1 FETCH L-2 EXECUTE_LOAD_STORE L-2 SLIDE_STATE_REG_LEFT L-2 FETCH L-3 EXECUTE_LOAD_STORE L-3 SLIDE_STATE_REG_LEFT L-3 ... FETCH 2 EXECUTE_LOAD_STORE 2 SLIDE_STATE_REG_LEFT 2 FETCH 1 EXECUTE_LOAD_STORE 1 SLIDE_STATE_REG_LEFT 1 FETCH 0 EXECUTE_LOAD_STORE 0 ASSIGN_LOADED 0 INCREMENT_P 0 DECODE_EXECUTE_TRANSFER 0 DECODE_EXECUTE_ARITHMETIC_LOGIC 0 DECODE_EXECUTE_SET 0 DECODE_EXECUTE_BRANCH 0 DECODE_LOAD_STORE 0
The rightward slide follows.
CYCLE_RIGHT.mc FETCH 0 EXECUTE_LOAD_STORE 0 SLIDE_STATE_REG_RIGHT 0 FETCH 1 EXECUTE_LOAD_STORE 1 SLIDE_STATE_REG_RIGHT 1 FETCH 2 EXECUTE_LOAD_STORE 2 SLIDE_STATE_REG_RIGHT 2 ... FETCH L-3 EXECUTE_LOAD_STORE L-3 SLIDE_STATE_REG_RIGHT L-3 FETCH L-2 EXECUTE_LOAD_STORE L-2 SLIDE_STATE_REG_RIGHT L-2 FETCH L-1 EXECUTE_LOAD_STORE L-1 ASSIGN_LOADED L-1 INCREMENT_P L-1 DECODE_EXECUTE_TRANSFER L-1 DECODE_EXECUTE_ARITHMETIC_LOGIC L-1 DECODE_EXECUTE_SET L-1 DECODE_EXECUTE_BRANCH L-1 DECODE_LOAD_STORE L-1
Unlike all the other MC programs, which were coded by hand, a process generated CYCLE_LEFT and CYCLE_RIGHT due to their dependency on the machine code program length. In the real code, the placeholders, L-x, are actual array index values.
The computer alternates between CYCLE_LEFT and CYCLE_RIGHT from startup to shutdown.
© 2023 meatfighter.com |