Viktor's amazing 4-bit processor
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.
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 nibbles (a nibble means 4 bits), used for both data and code. There is also a one byte long accumulator register, a one byte long program counter, two status flags (carry and zero). There's also a so-called data flag that reads user input from a microswitch if I understand correctly.
Instructions are read from the main memory, normally sequentially, but altered by jumps. Each instruction is either one nibble long or three nibble long: the first nibble gives the opcode, the next two are interpreted in various ways depending on the opcode. The instructions that access data in the memory always access two adjacent nibbles together, interpreted as a byte stored as two nibbles in big-endian. Addresses are measured in nibbles, so data need not be aligned. The instructions are the following (opcode in parenthesis).
- CLF (0xF)
- clear the flags.
- AND (0x5), OR (0x6)
- compute bitwise operation of the accumulator with immediate byte operand, put result to accumulator, update zero flag.
- ADD (0x7), SUB (0x8)
- add the accumulator with the immediate operand or its negative, put result to accumulator, update carry and zero flag.
- CMP (0xA)
- subtract immediate operand from accumulator, discard result but update the carry and zero flags.
- ROL (0xD), ROR (0xE)
- rotate accumulator concatenated with carry flag one bit left or right, there's no operand.
- LDA (0x1)
- load a byte from memory to accumulator, argument is absolute data address to load from.
- STA (0x2)
- store byte to memory to accumulator, argument is absolute data address.
- SPC (0x4)
- store value of program counter to memory, argument is absolute data address to store to.
- JMP (0x3)
- jump to immediate address given in operand
- JNZ (0x9), JNC (0xC), JND (0xB)
- branch to immediate address given in operand if zero flag is clear, carry flag is clear, or data flag is clear respectively.
- HLT (0x0)
- halts the processor until restarted externally by the operator
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.
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 main page that gives a summary of the Processor also gives two example programs. These are the only programs written for the Processor that I know of. Both an assembly listing and the machine language nibbles are shown.
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.
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.