I'm using The Ultimate Nerd Game 0.2.6, a 3D digital logic simulator with only inverters and OR gates, to try to make a CPU. I've never made a CPU of any kind before, so the results won't be great, but it's still fun to try and learn about CPUs along the way.
TUNG 0.2.6 is used because it's the newest working version of the game (0.2.7 is completely broken), but some time within the next few months, a new version by the name of Logic World is going to be released, which will allow for an even more compact/fast design.
Due to TUNG being a buggy game, I will not be able to add anything to my CPU until a few months from now (when Logic World is released). The game frequently neglects to save my progress, which can cause a whole day of progress to be lost. When Logic World is released, I'll start a new CPU in it. Until then, I'm going to have to stop working on this project.
Yesterday, I added a zero flag, a carry flag, a negative flag, and the compliments of all three. Before today, I didn't have anything that used these flags. Today, I added instructions to jump if certain flags were set and instructions to compare numbers to the A register.
In order to test these instructions, I made a small program to count to four and then halt.
In assembly, this program looks something like this:
LDA 0 ;Reset A
Loop: ;Counting loop
STA 0xFF ;Write to display
ADD 0x01 ;Adds 1 to A
CMP 0x05 ;Checks if A=5
JMP NZ Loop ;Loops if A!=5
HLT ;Stops if A=5
Because it never displays after A=5, the program only counts to four.
The instruction STA, which is used in the demo, writes to a location in memory. Although I haven't added the instructions for locations pointed to by registers, they will be implemented soon. I will also be adding instructions for reading from memory, which will allow for a program to take input and use RAM.
I also fixed a bug in the hardware that caused the ADD instruction to act extremely weirdly. Instead of adding the numbers it was supposed to, it would often add the wrong numbers, or the right numbers at the wrong time. In fixing this, I was also able to fix some problems I was having with the flags.
Finally, I added one new instruction that serves no purpose whatsoever. HCF, or Halt and Catch Fire, permanently disconnects the clock from the CPU. There is no way outside of the CPU to recover from an HCF. The only way to fix the CPU after an HCF is to go into the CPU and press a button to reconnect the clock.
Here's a current list of all opcodes and instructions:
0x00 - NOP
0x01 - LDA (immediate)
0x02 - LDB (immediate)
0x03 - LDC (immediate)
0x04 - LDD (immediate)
0x05 - LDE (immediate)
0x06 - JMP (immediate)
0x08 - ADD B
0x09 - ADD C
0x0A - ADD D
0x0B - ADD E
0x0C - ADD (immediate)
0x10 - CMP (immediate)
0x11 - CMP A
0x12 - CMP B
0x13 - CMP C
0x14 - CMP D
0x15 - CMP E
0x16 - JMP Z (immediate)
0x17 - JMP NZ (immediate)
0x20 - STA (immediate)
0x26 - JMP C (immediate)
0x27 - JMP NC (immediate)
0x36 - JMP P (immediate)
0x37 - JMP N (immediate)
0xFE - HCF
0xFF - HLT
And a current image:
Sadly, I may not be able to work on the CPU tomorrow or the next day, but I will come back to it as soon as I can and continue posting.
Today, I've added a few more instructions, and tested them in a simple program. The new instruction set looks like this:
0x00 - NOP
0x01 - LD A (immediate)
0x02 - LD B (immediate)
0x03 - LD C (immediate)
0x04 - LD D (immediate)
0x05 - LD E (immediate)
0x06 - JMP (immediate)
0x07 - NOP
0x08 - ADD B
0x08 - NOP
...
0xFE - NOP
0xFF - HALT
In order to test the functionality of each instruction and the hardware, I created a simple looping program to repeatedly increment the A register.
LD A, 0 ;resets the A register
LD B, 1 ;Loads 1 into B for incrementing (no inc instruction yet)
Loop:
ADD B ;adds A and B, then loads the result into A
JMP Loop ;Loops to increment A
I did end up coming across a few bugs in the hardware and my instruction decoding, so I'm glad I tested before the CPU became more complicated/difficult to debug.
I'll continue adding more instructions for now, and I'll give another update at some point tomorrow.
Currently, the CPU has an ALU, an accumulator, a program counter, 4 general purpose registers, and circuitry for reading from specific addresses in memory. The CPU lacks a flags register, though this will be added later. I've began work on the instruction decoding, and have managed to get a few instructions to work.
The ALU is identical to the one in the NAND Game, only instead of the default logic operation being NAND, it's OR. This means it can only add, OR, invert and/or zero A/B, and invert the output. The zeroing and inverting of the inputs and outputs does allow this ALU to do any necessary operation for basic programs. It does not yet support flags/comparisons.
The program counter, accumulator, and general purpose registers are all 8 bit. Maybe later I will make the program counter 16 bit, but for now it will remain at 8.
Each instruction is given 4 clock cycles to run. The first is used to load the opcode, and the others can be used to actually execute instructions. This does decrease the speed of the CPU by a bit, so I may make a way to skip the unused clock cycles later.
That's all for now, but I will be working on it as often as I can, so expect to see an update within a week.