Skip to content

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 logic simulator, and eventually it ran on an FPGA.

Notifications You must be signed in to change notification settings

mrmcsoftware/CPUsimulator

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CPU Simulator And Assembler

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.)

Setup and Running

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 and programs.js. They are only there to make Github Pages work.

URL Parameters

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))

Using The Simulator

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.

Pausing and Stepping

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.

Halting

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.

Speed

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.

Getting Data Into the Simulated Computer

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

The rest should be (I think) self-explanatory, so I won't cover the rest here.

Sample Programs

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.

CPU Instruction Set

 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
 HALT
 : Message DCS0 Trace start 
255  BREAKPOINT   Pause execution to output a register or memory value  BREAKPOINT aedge2
 BREAKPOINT $ffffff /* output Y */ 

 BREAKPOINT $fffffe /* output A */ 

Assembler Directives

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
 : Values DC 'This_is_a_test','#',1234,%1101,val1,~1.26
 DC 123 
 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:
 DCSNIA This is a test
 DCSNI Hello World
 DCS0 Testing 123 
 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 

Possible Improvements

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.

Acknowledgements

Midi sound javascript (audiosynth.js) by Keith William Horwood (https://keithwhor.com/music/)

Video Series

Other Resources

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.

Author

Mark Craig https://www.youtube.com/MrMcSoftware

About

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 logic simulator, and eventually it ran on an FPGA.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published