Viktor's amazing 4-bit processor

From Esolang
Jump to navigation Jump to search

Viktor's amazing 4-bit processor is an esoteric computer hardware designed and soldered by the physicist Viktor T. Toth in 1999. The goal of the computer was for the creator to learn about electronics, specifically creating circuits from TTL logic chips. The computer executes a very simple custom machine language.

Machine language

The machine language is somewhat similar to that of other early processors, except that the address space is very small.

The computer has a main memory consisting of 256 individually addressable nybbles (a nybble means 4 bits), used for both data and code, a one byte long accumulator register, a one byte long program counter, and three status flags (C - carry, for overflow during operations, Z - zero, for checking if something equals 0, and D - data, which reads from a microswitch).

Instructions are read from the main memory sequentially, unless a jump happens. Each instruction is either one nybble long or three nybble long: the first nybble gives the opcode, the next two are used as an 8 bit argument in little-endian. The instructions that access data in the memory always access two adjacent nybbles together, with the address being the first one, interpreted as a byte stored as two nybbles in little-endian. Addresses are measured in nybbles, so data doesn't have to be aligned. The instructions are the following:

Instructions
Opcode Mnemonic Size Description
0 HLT 1 Halts the processor.
1 LDA nn 3 Load 8-bit value from address nn into accumulator.
2 STA nn 3 Store accumulator into 8-bit address nn.
3 JMP nn 3 Jump to (set program counter to) nn.
4 SPC nn 3 Store value of program counter to address nn.
5 AND nn 3 Bitwise AND the value of the accumulator with nn. Update Z (zero) flag.
6 OR nn 3 Bitwise OR the value of the accumulator with nn. Update Z (zero) flag.
7 ADD nn 3 Add nn to the accumulator. Update C (carry) and Z (zero) flags.
8 SUB nn 3 Subtract nn from the accumulator. Update C (carry) and Z (zero) flags.
9 JNZ nn 3 Jump to nn, but only if the Z (zero) flag is not set.
A CMP nn 3 Subtract nn from the accumulator, but instead of storing the result in the accumulator, discard the result. Update C (carry) and Z (zero) flags. (C will be set if nn is greater, Z will be set if they're the same, no flags if accumulator is greater)
B JND nn 3 Jump to nn, but only if the D (data) flag is not set. (D flag is controlled by user)
C JNC nn 3 Jump to nn, but only if the C (carry) flag is not set.
D ROL 1 Rotate the accumulator left. Move C to the least significant bit, move the most significant bit to C.
E ROR 1 Rotate the accumulator right. Move C to the most significant bit, move the least significant bit to C.
F CLF 1 Clear the flags.

The machine language is completely specified without space for extension, since all opcodes have a defined meaning, and all addresses correspond to memory that exists. This should not be surprising, given that the goal was a single hardware implementation. This also means all programs are valid.

The arithmetic operations take an immediate operand, so you have to use self-modifying code with the STA instruction to use other operands. Similarly, you would have to use self-modifying code to load or store from an indexed (non-constant) address, or to jump indirectly. Thus, the machine language favors self-modifying code even more than the 6502 processor does.

The machine language itself is described on the creator's webpage.

Example programs

Add two 8 bit numbers

00   1 A F   LDA FA   // Load input 2
03   2 A 0   STA 0A   // Store input 2 in ADD argument
06   1 8 F   LDA F8   // Load input 1
09   7 0 0   ADD --   // Add input 2 [argument gets overwritten]
0C   2 C F   STA FC   // Store in output
0F   0       HLT      // Halt

F8   3 0     03       // Input 1
FA   5 0     05       // Input 2
FC   0 0     --       // Output

Both an assembly listing and the machine language nybbles are shown. This program showcases self-modifying code.

Multiply two 4 bit numbers

00    LDA FC    1 C F
03    AND 0F    5 F 0
06    STA FA    2 A F    // Store input 1 in FA.
09    LDA FD    1 D F
0C    AND 0F    5 F 0
0F    STA F8    2 8 F    // Store input 2 in A and F8.
12 :B ROR       E        // Get a bit from A.
13    JNC :A    C 9 2    // Skip this next part if the bit was not 1
16    LDA FA    1 A F
19    STA >X    2 0 2
1C    LDA FE    1 E F
1F    ADD ?X    7 0 0
22    STA FE    2 E F    // Add FA to FE.
25    LDA F8    1 8 F
28    ROR       E        // Get a bit from F8.
29 :A CLF       F        // Discard it.
2A    STA F8    2 8 F    // Store in F8.
2D    LDA FA    1 A F
30    ROL       D
31    STA FA    2 A F
34    CLF       F        // Double FA.
35    LDA F8    1 8 F
38    ADD 00    7 0 0
3B    JNZ :B    9 2 1    // Repeat if F8 is not 0.
3E    HLT       0        // Halt.

F8-F9 = working mem 2
FA-FB = working mem
FC = input 1
FD = input 2
FE-FF = output

The main page that gives a summary of the Processor also gives two example programs.

Implementations

Original hardware implementation

The Processor has a hardware implementation built from TTL logic chips. These include an SRAM chip for the main memory, and a flash ROM storing microcode.

Apart from the processor implementing the actual machine language, the hardware also has peripherials to directly read and write the main memory, which allows to load programs and inputs and read results.

Viktor T. Toth describes the hardware implementation on his webpage with circuit diagrams and other details given in such detail that you could probably reproduce it to build another working model.

According to private correspondance, the machine runs on a very slow clock cycle, but the exact speed is unknown.

Other implementations

User:B_jonas once had an unoptimized software implementation of the processor, plus an assembler (supporting only numeric operands, not symbols). This worked well enough that it executed the two example programs correctly, but since those programs don't use all the instructions of the machine, the implementation of the remaining instructions (SPC, CMP, OR, JND) is not verified. This is not at hand anymore. It might still exist somewhere on his old backups, but it would probably be easier to write a new implementations than to find it.

User:TheBigH has an implementation in PixelWalker.