CISC8 - A new ISA for a 8/16 bit MCU
Philipp Klaus Krause (from the SDCC fame) is creating a new 8 bit CPU that would fit the gap that RISC-V has left when moving to 32/64 bit computing. It is the F8, and is heavily geared towards efficient code generation in C. I also wanted to explore that realm, mostly for learning purposes.
Introduction
F8 takes some heavy inspiration from the STM8 that is currently the most efficient architecture in SDCC. Therefore Philipp goes into a lenghty analysis of what makes the STM8 awesome.
I wanted to see if I could bring a novel approach to it, inspired by some older & newer architectures. The whole journey is done in order to learn the steps involved to create an ISA & develop a simple compiler for it.
I don’t expect any useful outcome from it, but I hope it will be fun & interesting experience.
Registers
- 4x16b general purpose registers (X,Y,Z,T)
-
A 16b “mask” register that restricts every instructions that work on general purpose registers. Setting the mask to 0x00FF, will pretend that the registers are only 8 bits and will only access their lower part.
- 4x16b stack register (S0, S1, S2, S3) that contains an address.
-
A register to select the active stack register
- 2 comparison registers that are always comparing, respectively, X to Y and Z to T. Yet taking the mask register into account. It is only & always updated after a write to any general register. 00 == equal, 01 == X > Y, 10 == Y < X, 11 == reserved Those are automatically used by conditional jumps.
- A
Instructions can directly acess memory via general purpose register indirection + offset, along with stack register + offset.
ISA
Instructions are of variable length from 1 to 5 bytes. A working instruction set fits inside the 2 byte instruction space, the spirit is to have an orthogonal ISA, but it’s not required as we aim for a CISC encoding efficiency.
The 1 byte instructions are a compressed version of the most useful 16 bit ones, in order to increase code density, similar to thumb. The 3 bytes instructions are an extension of the 16 bit ones in order to have a 16 bit immediate argument. The bigger bytes instructions are undefined for now.
The decoding on the various instruction length is done with the number of leading 1.
Length_in_byte = number_of_leading_one + 1
Therefore, if the instruction begins with
0 - 8 bit
10 - 16 bit
110 - 24 bit
1110 - 32 bit
... - ...
1111 1110 - 64 bit
1111 1111 - ERROR
16 bit
0000 0000 0000 0000 - trap (actually double trap)
Immediate
10 0xxx yy zzzzzzzz OP DST IMM
000 MOV 001 ADD 010 AND 011 OR 100 XOR 101 JR 110 111
Register based
10 1xxxx zzzz zzzz OP DST SRC
0000 MOV 0001 ADD 0010 AND 0011 OR 0100 XOR 0101 JR 0101 JR
1000 MOV 1001 ADD 1010 AND 1011 OR 1100 XOR 1101 1110 1111
DST 00 X 01 Y 10 @X 11 @Y
24 bit
110 xxxx
8 bit
abandoned
0000 0000 TRAP - detect unused
0000 xxxx SETFLAGS
0 xxx yy zz OP DST SRC
OP 1 ADD 2 SUB 3 AND 4 OR 5 NOT 6 NEG 7 MOV
DST/SRC 00 X 01 Y 10 @X 11 @Y
8 bit
abandoned
0000 0000 TRAP - detect unused
0000 xxxx SETFLAGS
0 xxx yy zz OP DST SRC
OP 1 ADD 2 SUB 3 AND 4 OR 5 NOT 6 NEG 7 MOV
DST/SRC 00 X 01 Y 10 @X 11 @Y