The DynamicSystems™ CoolCPU is the future of computing.

Advanced features like "addition" and "number comparisons" make anything else seem...uncool.

Try it now

Description

The CoolCPU is a brand new CPU architecture, inspired by processors like the Zilog Z80 and the MOS Technology 6502.

The CoolCPU can access up to 256 bytes of external memory. The first 128 bytes (0x00 to 0x7F) are read-only memory, or ROM, and it's what you set in the "Try it now" box above. Then, the memory from 0x80 to 0xF0 is random-access memory, or RAM. Your code can store and load values from RAM however it wants.

Finally, 0xF1 is the output register. Anything you write to the output register will be printed to the screen. In addition, consult the DynamicBlast! Engine section to learn about the remaining registers.

Registers

The CoolCPU has four main registers. The first is the Program Counter, or PC, which keeps track of the next instruction to run. The PC always starts at 0, and the CPU keeps track of it for you.

The remaining three are registers A, B, and C, which are general-purpose 8-bit registers. You can use them any way you want; however, note that some instructions only work on register A.

Timing

Different CoolCPU instructions take different amounts of time to run. We measure each instruction in clock cycles. The Instructions table below specifies the number of cycles each instruction requires.

DynamicBlast! Engine

The DynamicBlast! Engine lets you read data from an external hard drive connected to the CoolCPU. It works by copying data from the external hard drive to RAM. You must set the parameters for the copy (for example, the source and destination) and then start the DynamicBlast! Engine.

The hard drive is broken up into 256 different sectors, numbered from 0 to 255. Each sector is 32 bytes long. The DynamicBlast! Engine works at a sector level. It will always copy exactly one sector—no more and no less.

To perform a copy, do the following:

Poking the DynamicBlast! Engine

As mentioned before, poking the DynamicBlast! Engine is crucial. To poke the DynamicBlast! Engine, write the poke index to DBEPKE (0xF5). The poke index starts at 5 and decreases by one for each poke. So, for the first poke you would write a 5, then the next poke a 4, the next poke a 3, and so on.

The timing of the first poke does not matter. However, for every poke after the first one, you must poke precisely 36 cycles after the previous poke—no more, no less. To be even more precise: there should be exactly 36 cycles from right before the store instruction of the first poke up to right before the store instruction of the next poke. Incorrect poke timing will destroy the attached hard drive.

You will need to poke the hard drive a total of 5 times. (so the last poke index you write to DBEPKE will be a 1) After the fifth poke, the data will be available in memory, and the copy will be complete.

Instruction set

Name Hexadecimal Cycles Description
NOP 00 1
No Operation
Does nothing.
LDA nn 01 nn 1
Load Register A
Reads the data at address nn and stores it in register A.
LDB nn 02 nn 1
Load Register B
Reads the data at address nn and stores it in register B.
LDC nn 03 nn 1
Load Register C
Reads the data at address nn and stores it in register C.
LDA [A] 04 2
Load Register A
Reads the data located at the address in register A and stores it in register A.
LDB [A] 05 2
Load Register B
Reads the data located at the address in register A and stores it in register B.
LDC [A] 06 2
Load Register C
Reads the data located at the address in register A and stores it in register C.
STA nn 11 nn 1
Store Register A
Stores the value of register A into address nn.
STB nn 12 nn 1
Store Register B
Stores the value of register B into address nn.
STC nn 13 nn 1
Store Register C
Stores the value of register C into address nn.
STA [A] 14 2
Store Register A
Stores the value of register A into the memory address in register A.
STB [A] 15 2
Store Register B
Stores the value of register B into the memory address in register A.
STC [A] 16 2
Store Register C
Stores the value of register C into the memory address in register A.
INC 20 2
Increment
Add 1 to register A.
DEC 21 2
Decrement
Subtract 1 from register A.
CON nn 22 nn 1
Constant
Sets register A to the literal value nn.
JP nn 30 nn 2
Jump
Moves the Program Counter to address nn.
JZ nn 31 nn 3
Jump If Zero
If register A is 0, moves the Program Counter to address nn. Otherwise does nothing.
JNZ nn 32 nn 3
Jump If Not Zero
If register A is not 0, moves the Program Counter to address nn. Otherwise does nothing.
SWB 40 1
Swap Register B
Swaps the contents of registers A and B.
SWC 41 1
Swap Register C
Swaps the contents of registers A and C.
HCF FF N/A
Halt And Catch Fire
Lights the CPU on fire, which terminates your program. Due to technological restrictions, the CoolCPU simulator cannot accurately simulate this instruction, and will only halt the CPU.

Sample program 1

Let's say you want to print out "cool!". This means you'd have to write each character in that string to the output register, which we know from the "Description" section above is 0xF1.

By looking up the characters in an ASCII table, you can figure out what values to write. The instructions to do this might look something like:

; print out c
CON 0x63
STA 0xF1

; print out o
CON 0x6F
STA 0xF1

; print out o
CON 0x6F
STA 0xF1

; print out l
CON 0x6C
STA 0xF1

; print out !
CON 0x21
STA 0xF1

; stop CPU
HCF

We use the CON instruction to load a value into register A, and then use the STA instruction to store that value into the output register. Finally, we use HCF to set the CPU on fire, which ends the program.

Now, using the table above, we can determine the hexadecimal values that correspond to these instructions:

22 63
11 F1
22 6F
11 F1
22 6F
11 F1
22 6C
11 F1
22 21
11 F1
FF

(they're spaced out on multiple lines for clarity—the spacing has no effect on their location)

And that's our program! You can try it in the simulator above.

Sample program 2

Let's say you now want to repeat the earlier code five times. You could just write the whole thing out over and over; however, it's better to do this in a loop. That might look something like this:

	CON 5
loop:
	SWB

	; print out c
	CON 0x63
	STA 0xF1

	; print out o
	CON 0x6F
	STA 0xF1

	; print out o
	CON 0x6F
	STA 0xF1

	; print out l
	CON 0x6C
	STA 0xF1

	; print out !
	CON 0x21
	STA 0xF1

	SWB

	DEC
	JNZ loop

	; stop CPU
	HCF

The main thing to notice here is that we're using the A register to track how many iterations are left in the loop. At the beginning of the program, the CON 5 sets it to 5. Then, at the end of each loop iteration, the DEC instruction subtracts 1, and the JNZ continues the loop—unless A is 0, in which case HCF is run.

However, you might have noticed an issue with this: we're using the A register for both the loop count and the character values! That's what the SWB instruction is for: it swaps registers A and B. We swap at the start of the loop to save the loop count, and then do a swap at the end to restore the count so that we can check it. Since most operations occur on register A, you'll find the swap instructions to be very helpful.

Like before, we can use the instruction table to find the hexadecimal representation of these instructions:

22 05
40
22 63
11 F1
22 6F
11 F1
22 6F
11 F1
22 6C
11 F1
22 21
11 F1
40
21
32 02
FF

Notice how we had to calculate where the loop label is in the code. Based on the size of the instruction before it, and knowing that the first byte is address 0x0, we can determine that loop is 0x02. So, JNZ loop becomes JNZ 0x02, which becomes 32 02.