This is an HTML/Javascript CPU simulator and assembler for the CPU I designed. Originally, I created this CPU on paper many years ago for a homework assignment in college. More recently, I implemented my design in the Logisim (and Logisim Evolution) logic simulator, and eventually it ran on an FPGA. Refer to the Video Series section for links to my youtube series about this design.
(I purposely used an older browser for the screenshots because I prefer the 3d look over the flat button look of some modern browsers.)
OPTIONAL: If you want a gradual transition between Dark and Light modes, uncomment the line containing transition: color 300ms, background-color 300ms;
There are various ways of starting up this webpage. If your browser is in your search path you could simply do the following, for example, at a command prompt:
firefox sim.html
Or you could start your browser and use your browser's "Open File" (or
equivalent) menu option. Or you could use the file:///
URI/protocol and
specify the whole path.
BTW, if you prefer to have the assembler be in a separate tab, you could use
the provided assem.html
, but you would lose the automatic loading of the
program into the simulator.
For a live demo, go to https://mrmcsoftware.github.io/CPUsimulator but do try to use your own copy instead. Since the file loading function will only upload files from your computer (not a github repo), for the Live Demo, I've added the Examples: drop-down selection button at the top. You can select the example program you want to run, then click Start.
Note: Ignore
index.html
andprograms.js
. They are only there to make Github Pages work.
These are optional parameters you can use if you don't like the defaults.
Use these like this, for example (If specifying this on a terminal commandline,
you probably will need to escape the special characters, depending on your OS
(for example: sim.html?dark=false\&assembler=true
if using Linux,
"sim.html?dark=false&assembler=true"
if using Windows)):
sim.html?dark=false&assembler=true&wordwrap=off&midimode=off&image=2
- dark=false - Turn off dark mode
- assembler=true - Show the assembler at startup
- midimode=off - The assembly program will be providing frequencies rather than midi note numbers
- verbose=on - Provide some extra simulation information in the browser's console
- wordwrap=off - Don't use word wrap on TTY screen
- image={number} - Select which fancy "CPU Simulator" image to use (0, 1, 2, 3, 4, or -1 (-1 for no image))
All the buttons, input boxes, checkboxes, and file selectors have tool tips to better explain the function. Hover your pointer over the desired element to view the tool tip.
The I/O panel contains the Video screen (256x256, 128x128 (scaled to 256x256), and 320x240), Y and A register LED displays, a 12x7 LED Matrix display, a Strobe LED and a CPU status LED below it (green means program running, red means program (CPU) halted).
Below the I/O panel is the TTY screen. Your program can output text to this
TTY screen. After you click Start, keyboard focus will automatically
switch to the TTY screen and you will be able to press keys which will then be
given to your program via the DATAIN
instruction. If you click elsewhere
after clicking Start, you would need to first click the mouse pointer in
the TTY screen for any key presses to be registered. For any subsequent key
presses, you won't have to click the TTY screen again, unless you have clicked
somewhere else since the click in the TTY screen. Note: any key presses that
occur before a DATAIN
is executed will be buffered and can be seen in
the blue textbox (labeled KB) located above the checkboxes.
To the right of the I/O panel and TTY screen is the disassembly panel. You can click the Disassemble button to disassemble (convert your machine code (the running program) to assembly code) the machine code in the simulated computer's RAM. The image will be replaced with the disassembled code. Click Clear Disassembly to remove the disassembled code and return the image.
If you click the Instruction TTY checkbox on, an Instruction TTY will appear below the Disassembly panel. When your program runs, the currently running instruction and a few previously run instructions will be shown. Click the Instruction TTY checkbox off to remove the panel.
Note: These panels can be resized by clicking on and dragging the resize indicators at the bottom-right corner of the panels.
To assemble a program, click the Assembler checkbox on. The assembler
section will appear below the simulator. Type (or Paste (by clicking the right
mouse button in the panel)) your assembly code in the leftmost text box. You
can also click the file upload box (some browsers call it Browse) below the
assembly panel to select a file to load instead. Click the Assemble button
to assemble your program. The text box to the right will show error messages
and general information. You can control what information is shown by selecting
from the drop-down list under the text box. The Save As: and
Save Machine Code As: buttons will save your assembly source code and your
assembled machine code (once assembled) respectively. The filename will be
untitled.asm
and untitled
respectively if not specified in the
colored text boxes near the buttons. Any saved machine code can be loaded into
the simulator by choosing it with the file upload box at the top right corner
of the simulator section.
Note: Even though I use uppercase for all the CPU instructions and assembler directives in the sample programs and in the lists below, lowercase can also be used (even any combination of lowercase and uppercase).
Once assembled, your program will be in the simulated computer's RAM, and you can click the Start button on the simulator to run it. You can click the Go To Top button to conveniently go back to the simulator.
Usually, you would first click the Start button to start running your program. You can pause the running program by clicking the Pause button. Click on Resume when you want to continue running the program. Or click the Step button to execute a CPU instruction on each click (and then click Resume to return to normal execution if desired). You could also click Pause before clicking Start, and then Step to step from the start.
The CPU's HALT
instruction will stop the simulation of your program (and
so will the Reset button). As a safety measure, when loading in a
previously assembled program into the simulator, or when assembling using the
assembler, a HALT instruction is automatically added at the end of the program
to make sure the program will end instead of executing whatever might be in the
simulated computer's RAM after your program. Be aware of that when assigning
memory locations to your variables.
I use Javascript's setInterval
to execute a CPU instruction at most once
every millisecond (since setInterval is in milliseconds). Unfortunately, this
means the fastest the simulation could be is 1000Hz, which is fairly slow.
You can set this interval timer by entering a millisecond value in the yellow
text input box at the top.
You can check the Fast Mode (Be Careful) checkbox which will bypass
setInterval by running all the CPU instructions in your program in one shot.
Be careful though, NO simulator control or browser function will be available
until your program either finishes (with a HALT
instruction) or reaches
a DATAIN
instruction (unless you have FASTINTR
in various places
in your code. I'll cover FASTINTR
in the next paragraph). If neither
can happen in your program, an infinite loop will occur and you will likely
need to kill your browser (via whatever method is available in your OS).
SO BE CAREFUL when selecting this option. If a DATAIN
instruction is
reached, Fast Mode will be turned off and you would have to click it on again
(after a key press has satisfied the DATAIN
loop (if there is one in
your program)) if you want it back on. To avoid having to repeatedly click
the Fast Mode checkbox and the TTY screen back and forth, you can press
Ctrl-Shift
in the TTY screen (while the TTY screen has focus) to turn on
Fast Mode - that way the TTY screen won't lose focus. If the left
Ctrl-Shift
combination doesn't work for you, try the right
Ctrl-Shift
, OR hold the Ctrl
key down while pressing Shift
twice (probably a "browser thing"). Another caveat: if your program takes too
long to either reach HALT or DATAIN, I suspect the browser's "safety feature"
allowing you to stop the script will kick in, except the browser will perhaps
be too busy to put the dialog up, thus creating a worse situation than it was
supposed to solve. This is just a guess though.
There is a partial solution to this problem. FASTINTR
will interrupt the
continuous run loop temporarily allowing the browser to do whatever function
it needs to do, and then go back to Fast Mode until the next FASTINTR
,
a HALT
, or a DATAIN
. It might be challenging to find the best
places to put FASTINTR
. Refer to the example program fractal.asm
to see where I chose to put it - after each scanline of the generated fractal
image.
The DATAIN
CPU instruction will read from the simulated keyboard, the
clock, and the (hypothetical) external device. The external device's value can
be set (in hex) via the orange text input box at the top. For the most part,
you wouldn't likely ever need to set it to anything, but it's there in case you
want to. If the value is set to anything other than 0 (or nothing), any key
press or clock value won't be accurate.
The rest should be (I think) self-explanatory, so I won't cover the rest here.
I've provided sample programs in the examples directory that you can look
at and run on this simulator to better understand the instruction set, assembler
directives, and the simulated computer's I/O. Both assembly source code
(ending in .asm
) and assembled machine code (no extension) are provided.
For some of the programs you would need to click the checkboxes to set the
necessary option - the program will tell you what you need to check or uncheck.
In the case of io
and io.asm
, if you choose option 2 or 3 from the
menu, you will have to click Reset to stop the effect since there's no way
to exit via a key press. Same thing with clock
and clock.asm
.
readme
and readme.asm
don't really do anything, they just contain
all the example instructions listed in this README.md
file. They will,
however, show you how a breakpoint is handled. By the way, for option 2 and
3 of io
and io.asm
you would need to have the Show Y checkbox
checked to see the effect, and for option 1, you would need to have the
No Lag LED Matrix checkbox unchecked (this program won't tell you this
unlike the other programs). Also by the way, for the programs that have a menu,
press a key not in the menu to exit program.
OpCode | Instruction | Function | Examples |
---|---|---|---|
0 | LOADI | Y ⇦ Z | LOADI 128 |
1 | LOAD | Y ⇦ M(Z) | LOAD Xmin |
2 | STOREI | M(Z) ⇦ Y | STOREI X0 |
3 | STORE | A ⇦ M(Y), Z ⇦ M(A) | Not Implemented |
4 | ADDI | Y ⇦ Y + Z | ADDI 1 |
5 | ADD | Y ⇦ Y + M(Z) | ADD pixel |
6 | SUBI | Y ⇦ Y - Z | SUBI 1 |
7 | SUB | Y ⇦ Y - M(Z) | SUB Offset |
8 | JUMP | PC ⇦ Z | JUMP Menu |
9 | JUMPI (indirect) | PC ⇦ M(Z) | JUMPI CalcOff |
10 | JUMPII (ind.-ind.) | A ⇦ M(Z), PC ⇦ M(A) | Not Implemented |
11 | DATAIN | Y ⇦ external data | DATAIN |
12 | HALT | Stop running | HALT |
13 | SKIPNEG | If Y is negative, PC ⇦ PC + 1 | SKIPNEG |
14 | STROBE | Generate STROBE pulse | STROBE |
15 | NOOP | No Operation | NOOP |
16 | MULI | Y ⇦ Y * Z | MULI 10 |
17 | MUL | Y ⇦ Y * M(Z) | MUL val |
18 | DIVI | Y ⇦ Y / Z | DIVI 10 |
19 | DIV | Y ⇦ Y / M(Z) | DIV val |
20 | MODI | Y ⇦ Y % Z | MODI 6 |
21 | MOD | Y ⇦ Y % M(Z) | MOD num |
22 | LOADAI | A ⇦ Z | LOADAI 256 |
23 | LOADA | A ⇦ M(Z) | LOADA val |
24 | JUMPEQI | If Y = A, PC ⇦ Z | JUMPEQI done |
25 | JUMPGTI | If Y > A, PC ⇦ Z | JUMPGTI done |
26 | JUMPLTI | If Y < A, PC ⇦ Z | JUMPLTI done |
27 | JUMPNEQI | If Y ≠ A, PC ⇦ Z | JUMPNEQI done |
28 | JUMPGTEI | If Y ≥ A, PC ⇦ Z | JUMPGTEI done |
29 | JUMPLTEI | If Y ≤ A, PC ⇦ Z | JUMPLTEI done |
30 | NEGY | Y ⇦ -Y | NEGY |
31 | STOREAI | M(Z) ⇦ A | STOREAI t2 |
32 | LOADAIND | A ⇦ M(Y) | LOADAIND |
33 | ANDI | Y ⇦ Y & Z | ANDI 128 |
34 | AND | Y ⇦ Y & M(Z) | AND val |
35 | ORI | Y ⇦ Y | Z | ORI 128 |
36 | OR | Y ⇦ Y | M(Z) | OR val |
37 | NOT | Y ⇦ ~Y | NOT |
38 | XORI | Y ⇦ Y ^ Z | XORI 128 |
39 | XOR | Y ⇦ Y ^ M(Z) | XOR val |
40 | JUMPEQ (indirect) | If Y = A, PC ⇦ M(Z) | JUMPEQ val |
41 | JUMPGT (indirect) | If Y > A, PC ⇦ M(Z) | JUMPGT val |
42 | JUMPLT (indirect) | If Y < A, PC ⇦ M(Z) | JUMPLT val |
43 | JUMPNEQ (indirect) | If Y ≠ A, PC ⇦ M(Z) | JUMPNEQ val |
44 | JUMPGTE (indirect) | If Y ≥ A, PC ⇦ M(Z) | JUMPGTE val |
45 | JUMPLTE (indirect) | If Y ≤ A, PC ⇦ M(Z) | JUMPLTE val |
46 | STOREAIND | M(Y) ⇦ A | STOREAIND |
47 | FADDA | Y ⇦ Y + A | FADDA |
48 | FADD | Y ⇦ Y + M(Z) | FADD val |
49 | FSUBA | Y ⇦ Y - A | FSUBA |
50 | FSUB | Y ⇦ Y - M(Z) | FSUB val |
51 | FMULA | Y ⇦ Y * A | FMULA |
52 | FMUL | Y ⇦ Y * M(Z) | FMUL val |
53 | FDIVA | Y ⇦ Y / A | FDIVA |
54 | FDIV | Y ⇦ Y / M(Z) | FDIV val |
55 | FMODA | Y ⇦ Y % A | FMODA |
56 | FMOD | Y ⇦ Y % M(Z) | FMOD val |
57 | FNEGY | Y ⇦ -Y | FNEGY |
58 | FSQRTY | Y ⇦ sqrt(Y) | FSQRTY |
59 | FSQRT | Y ⇦ sqrt(M(Z)) | FSQRT val |
60 | FSINY | Y ⇦ sin(Y) | FSINY |
61 | FSIN | Y ⇦ sin(M(Z)) | FSIN val |
62 | FCOSY | Y ⇦ cos(Y) | FCOSY |
63 | FCOS | Y ⇦ cos(M(Z)) | FCOS val |
64 | FTANY | Y ⇦ tan(Y) | FTANY |
65 | FTAN | Y ⇦ tan(M(Z)) | FTAN val |
66 | FASINY | Y ⇦ asin(Y) | FASINY |
67 | FASIN | Y ⇦ asin(M(Z)) | FASIN val |
68 | FACOSY | Y ⇦ acos(Y) | FACOSY |
69 | FACOS | Y ⇦ acos(M(Z)) | FACOS val |
70 | FATANY | Y ⇦ atan(Y) | FATANY |
71 | FATAN | Y ⇦ atan(M(Z)) | FATAN val |
72 | FATAN2A | Y ⇦ atan2(Y,A) | FATAN2A |
73 | FATAN2 | Y ⇦ atan2(Y,M(Z)) | FATAN2 val |
74 | FPOWA | Y ⇦ pow(Y,A) | FPOWA |
75 | FPOW | Y ⇦ pow(Y,M(Z)) | FPOW val |
76 | FLNY | Y ⇦ ln(Y) | FLNY |
77 | FLN | Y ⇦ ln(M(Z)) | FLN val |
78 | FLOGY | Y ⇦ log(Y) | FLOGY |
79 | FLOG | Y ⇦ log(M(Z)) | FLOG val |
80 | FABSY | Y ⇦ abs(Y) | FABSY |
81 | FABS | Y ⇦ abs(M(Z)) | FABS val |
82 | FEXPY | Y ⇦ exp(Y) | FEXPY |
83 | FEXP | Y ⇦ exp(M(Z)) | FEXP val |
84 | FPTOINTY | Y ⇦ (int)(Y) | FPTOINTY |
85 | FPTOINT | Y ⇦ (int)(M(Z)) | FPTOINT val |
86 | INTTOFPY | Y ⇦ (float)(Y) | INTTOFPY |
87 | INTTOFP | Y ⇦ (float)(M(Z)) | INTTOFP val |
88 | FJUMPEQI | If Y = A, PC ⇦ Z | FJUMPEQI done |
89 | FJUMPGTI | If Y > A, PC ⇦ Z | FJUMPGTI done |
90 | FJUMPLTI | If Y < A, PC ⇦ Z | FJUMPLTI done |
91 | FJUMPNEQI | If Y ≠ A, PC ⇦ Z | FJUMPNEQI done |
92 | FJUMPGTEI | If Y ≥ A, PC ⇦ Z | FJUMPGTEI done |
93 | FJUMPLTEI | If Y ≤ A, PC ⇦ Z | FJUMPLTEI done |
94 | FJUMPEQ (indirect) | If Y = A, PC ⇦ M(Z) | FJUMPEQ val |
95 | FJUMPGT (indirect) | If Y > A, PC ⇦ M(Z) | FJUMPGT val |
96 | FJUMPLT (indirect) | If Y < A, PC ⇦ M(Z) | FJUMPLT val |
97 | FJUMPNEQ (indirect) | If Y ≠ A, PC ⇦ M(Z) | FJUMPNEQ val |
98 | FJUMPGTE (indirect) | If Y ≥ A, PC ⇦ M(Z) | FJUMPGTE val |
99 | FJUMPLTE (indirect) | If Y ≤ A, PC ⇦ M(Z) | FJUMPLTE val |
100 | CALL | Stack ⇦ PC, PC ⇦ Z | CALL print |
101 | RETURN | PC ⇦ Stack | RETURN |
102 | PUSH | UserStack ⇦ Y | PUSH |
103 | POP | Y ⇦ UserStack | POP |
104 | SREAD | Y ⇦ UserStack(Z) | SREAD 4 |
Pseudo Instructions Only Useful In Simulators (Not Part of CPU Design):
OpCode | Instruction | Function | Examples |
---|---|---|---|
253 | FASTINTR | Interrupt fast mode temporarily so browser can do things | FASTINTR |
254 | BREAKPOINTS | Pause execution to output a 0 (null)-terminated string | BREAKPOINTS Message |
255 | BREAKPOINT | Pause execution to output a register or memory value | BREAKPOINT aedge2 BREAKPOINT $fffffe /* output A */ |
Directive | Function | Examples |
---|---|---|
EQU | Equate label with a value | EQU Menu1 49 EQU Color1 $14ff14 EQU val1 ~12.75 EQU val2 %11101 |
: | Create an address label (space after ":") | : Loop STROBE NOOP JUMP Loop |
/* */ | Comment between the delimiters | /* This is a comment */ STROBE /* Another comment */ |
# | Comment to the end of the line | STROBE # This is a comment |
DC | Define a constant | : Xstart DC ~3.28125 |
DCS+[N,0,I,A] | Define a string (to the end of the line) N: add newline at end, 0: 0 (null)-terminated string, I: inline (generate code to output to TTY), A: use A register (with I) |
DCSI Enter n: |
DS | Define empty storage (number of bytes) | DS 1 DS 256 DS val2 |
OUTPUT | If 1, output generated machine code | OUTPUT 1 STROBE NOOP OUTPUT 0 |
This simulator could probably be improved to make it faster by using multithreading. This could possibly be achieved by using "web workers". There may also be other ways I'm not aware of.
Timing could also be better: I used setInterval
. Many say
setTimeout
would be better. Since this program really isn't showing
animations or video, I'm not sure the benefits of setTimeout would really
apply much. However, the midi sound at times is inconsistent perhaps because
of this.
Also, a better midi note player could be used. I purposely picked one that had the least amount of added files/dependencies. But there are ones that have more (and better) instruments, for example.
There is a chance that the Pause/Step/Run sequence could get messed up under
certain circumstances/sequences. This is because there are two variables
(interval
and pause
) that could get out of sync. I think it's
fixed, but I could be wrong.
And finally, I used various javascript graphics fill routines for the LEDs and
video screen. createImageData
/ putImageData
could also be used.
In various benchmarks people have performed in the past (non-related to this
project), some say fill routines are faster, some say putImageData is faster.
At any rate, for the video screen, a putImageData would/should be performed
after every pixel is set (to mimic the behavior of the design), so I'm not sure
if performance would be better with putImageData. I tried putImageData two
different ways: "blitting" the whole image each time, and blitting only the
desired pixel each time. There didn't seem to be any performance difference
between the putImageData method and the fill area method. Probably depends on
your hardware, though.
Midi sound javascript (audiosynth.js
) by Keith William Horwood (https://keithwhor.com/music/)
- Testing and Improving My CPU Design with Logisim (And Digital Logic Basics)
- Using My Even More Improved CPU in a Full-Fledged Computer Via Logisim
- My CPU With Improved Sound and MIDI Musical Keyboard Via Logisim
- My CPU / Computer: Conversion from Original Logisim to Logisim Evolution
- Converting My CPU to VHDL Via Logisim Evolution (for Eventual FPGA Board?)
- Converting My CPU to Verilog Via Logisim Evolution (for Eventual FPGA Board?)
- My CPU: Goodbye Gated Clocks! Hello Indirect Jumps (Now With More Programs)
- My CPU & Computer Simulator - A Very Useful Tool For My Logisim CPU
- My Logisim CPU / Computer - Now With Floating Point (FPU) (Fractals, Raytracer, Etc.)
- My CPU / Computer: Hardware Stack, Compilers, Java Simulator, and A Different Version of Logisim
- My Logisim CPU / Computer: Hardware User Stack for Easy Subroutine Parameter Passing and Recursion
- My CPU Design in an FPGA Via Collaboration with Dr. Kevin Walsh & His Improvements to Logisim
Here's some links to my other Github repos pertaining to this CPU design. The fractal assembly programs were spin-offs of the CPU project.
Mark Craig https://www.youtube.com/MrMcSoftware