Hi All,
Today I have looked at the five simulations for the 16-bit (and 12-bit) cpus:
Blue 16 instructions, 16-bit wordsize, direct addressing only. 4K words memory
PDP-8 8 main instructions, plus OPR extensions. 12-bit wordsize. Direct and indirect addressing modes. 4K words memory
J1 Stack machine, 16-bit, 16 primitive Forth instructions, 8K words memory, 8K words I/O
Suite-16 16-bit, accumulator plus 15 gen purpose registers. 31 instructions. 64K words memory
SIMPL Stack based virtual machine, 16-bit wordsize, 64K word address space
The 8-bit machine simulations for Gigatron, 6502, 8080 and Z80 are sufficiently different - they will be investigated at a later date.
Each simulation follows the general Fetch-Decode-Execute sequence contained within a loop. This is normally implemented in C using a switch-case structure contained within a while(1) loop.
Blue and PDP-8 - these are very similar machines based on a single accumulator and memory providing the other operand. Blue provides 16 instructions, whilst the PDP-8 has 8 basic instructions plus several others that are provided as a subset of the OPR instruction. Apart from their wordsize they are fairly similar. Blue only offers a direct addressing mode, whilst the PDP-8 offers indirect as well.
The J1 by James Bowman is a stack machine intended for Forth execution. It has 16 Forth primitive instructions decoded using a switch-case structure.
Suite-16 is my own creation. Based on Steve Wozniak's SWEET16 virtual machine for the 6502, it has an accumulator and 15 general purpose registers. It has 31 instructions, 6 of which are conditional branch instructions.
SIMPL is not so much a cpu simulation, but a tiny interpreted language with some similarities to Forth. At it's core is a simple virtual machine, capable of executing about 30 primitive instructions. It has routines for text and number entry, and output for both decimal and strings. I have included it in the list for comparison, because it offers a different approach than simulating a "conventional" cpu.
ResultsThe first results relate to the codesize in bytes as reported on a standard Arduino UNO.
It also gives an indication of the number of single lines of code SLOC.
The code includes UART routines for getchar() and putchar() to eliminate the usual Arduino serial.xxx function which was found to be particularly verbose. For the same reason the Arduino setup() and loop() functions have been replaced with a main() loop and while(1) for the cpu execution loop. In each case the cpu simulation has an instruction for both serial input and serial output. This ensures that the getchar() and putchar() routines are included in the bytecount total.
The getchar(), putchar() and UART initialisation routines account for 18 lines of code and add about 200 bytes of AVR code.
Code:
Blue 1482 bytes 75 SLOC
PDP-8 1526 bytes 220 SLOC
J1 1506 bytes 225 SLOC
SUITE-16 1958 bytes 100 SLOC
SIMPL 1312 bytes 229 SLOC
The results are interesting, in that for Blue, PDP-8 and J1 are all very close to 1500 bytes, but represent 3 very different processors.
Blue is somewhat hobbled and difficult to program because it only offers direct addressing. However it does have a fairly complete set of ALU instructions, ADD, AND, OR, XOR and INVert.
The PDP-8 only offers ADD and AND, but has the means to synthesise the other operations using short macros and the direct manipulation of the accumulator in the OPR instruction subset.
The J1 as a stack machine requires additional logic to control the stack pointers. It has a full complement of ALU operations ADD, SUB, AND, OR, XOR, INVert and DEC.
SUITE-16 has a total of 31 instructions, more than any of the preceding cpus. They are decoded as two, 16 entry switch statements. As it has sixteen 16-bit registers, plus a PC and an addressing register for coding convenience, all these 16 bit registers will present a coding challenge for the 8-bit AVR used on the Arduino. This is likely the cause for the additional 450 bytes of codesize.
SIMPL was included in the list mainly for comparative reasons. It is also based around a switch-case statement, which is used to implement a 16-bit virtual machine. Instead of numerical instructions, which require decoding as a series of instruction fields, it uses single ascii characters which are used to select blocks of code that execute the functions.
ConclusionsSimulating a simple cpu using C is an interesting educational exercise, and can be done fairly quickly in relatively few lines of code.
Lines of code does not directly correspond to the codesize in bytes. Getting an 8-bit AVR to simulate a 16-bit cpu will require two, bytewide operations for all of the ALU instructions, plus a fair amount of stack work.
A conventional cpu can be simulated using an array in memory to hold the "program". Running within a tight loop will be a routine to fetch the next instruction from memory, an instruction decoder and a switch-case structure to select the instruction.
The process could be further simplified using a "template" for a generic cpu. It would then just be a case of choosing the instruction set and how they are encoded into various fields in the instruction word.
16 instructions appear to provide a "comfortable" minimum for efficient working. Blue, despite it's 16 instructions is heavily compromised by lack of an indirect addressing mode. Conversely, the PDP-8 simulation could be modified to use a 16-bit wordsize, adding extra instructions and a greater addressing range.
The approach taken in SIMPL provides an alternative to coding a conventional cpu. It's instruction set is more of a shorthand assembly language for a virtual cpu. The operations are chosen for their mnemonic value, and are selected from the 116 non-numeric ascii codes. More about SIMPL on my Hackaday project pages
https://hackaday.io/project/176794-simpl