From time to time I like to experiment with various computing machines and architectures. Since a computer is just a very complicated state machine, it should be possible to make one that uses a simpler instruction set. After all, single instruction set computers and languages like Brainf*ck exist and work.
The size of an instruction set is a tradeoff between code size, execution speed and how complicated the underlying hardware is. Reduced Instruction Set Computer (RISC) and Complex Instruction Set Computer (CISC) was the first division of instruction set architectures as more and more CPUs were designed.
RISC CPUs were historically faster (in instructions per second) than CISC because they had less instructions, so the hardware could be made simpler and clock speeds faster. The performance was however variable as what RISC had to do in several instructions, a CISC could do in one. That distioction is very symbolic these days with ARM, a typycal RISC, implementing more and more instructions, and x86, a typical CISC (8086, the "Mainframe on a chip"), gaining more RISC-like features with the introduction of amd64 over 20 years ago.
For a few projects I am doing I need a small instruction set, so I am trying co come up with a round number of instructions. Currently 8 is a nice round number as each instruction can be coded in just 3 bits. The ISA is targeted to be ran as a virtual machine, but there is nothing to prevent implementing it in some HDL. Why 3 bytes? It's a secret :)
There are 5 registers
a Accumulator
d Data pointer/second register
tmp hidden temporary register for SWAP
sp Stack pointer
ip Instruction pointer
Only the accumulator and stack pointer can be directly modified. These are the 8 instructions:
opcode instruction
-------------------------
0 ZERO a <- 0;
1 INC a++;
2 ROL rotate a left
3 SWAP tmp <- a; a <- d; d <- tmp;
4 PUSH sp++; stack[sp] <- a;
5 POP a <- stack[sp]; sp--;
6 JZ iff a == 0 then ip <- d - 1;
7 JP ip <- d - 1;
There are a few groups. You may notice there is no constant loadin instruction, there is simply no way to encode a sensible constant and an instruction in 3 bits. Instead, loading a constant into a register is done using the ZERO
, INC
and ROL
instructions:
Example: Load 13 (0b1101) into register a:
ZERO INC ROL INC ROL ROL INC
The ROL
instruction is there so that constants can be formed easily. Otherwise loading a constant would need a lot INC
instructions. This way constants are formed bit-by-bit. SWAP
is used to access the d
register.
PUSH
and POP
are the usual instructions for running the stack.
The VM can interface to the outside world by a SYSCALL
. This is not a real instruction, but rather a call gate. When the program jumps to address zero, the VM will execute a system call using the stack for arguments. The system call can be anything, it can even be mapped to the underlying OS (eg. Linux) syscall. the SYSCALL
is an idiom for ZERO SWAP JP
. You have to save the accumulator yourself at the right moment.
Jumps and calls are hard. I only wrote a few programs in this ISA, so this part is not well thought out. Two instructions are provided, a JZ
(jump if zero) and JP
(unconditional jump). There is no call instruction, that is, a jump with saving the return address. In this version of the ISA there is no way to save the instruction pointer, so I might replace the unconditional jump with a CALL
instruction that saves the ip
to stack and jumps to address in d
. In that case, an uncoditional jump would be PUSH ZERO JZ
. The function return RET
would become POP SWAP ZERO JZ
. The stack would be used to return a value, however it would need some more instructions to swap the place of the return address and the return value on the stack. This could become a part of the RET
idiom.
Arithmetic operations are still to be done. Some programs simply don't need them, but they should be possible. This is still work in progress.
Back to homepage...