This project is a stack-based virtual CPU. It has its own assembly language, assembler, binary executable format, CPU emulator and disassembler. There is also a simple VRAM model which can print ASCII pictures to the terminal.
Small factorial example:
factorial:
push ax
push 1
jbe basic_case
push ax
push bx
mul
pop bx
push ax
push 1
sub
pop ax
call factorial
ret
basic_case:
retMore examples can be found in examples/factorial/, examples/square_solver/
and examples/vram/.
acc- assembler, compiles.asmfiles to.mcmachine-code filesexec- CPU emulator, executes.mcfilesdisacc- disassembler, converts.mcfiles back to.asm- custom stack-based assembly language
- stack, registers, RAM and V-RAM
- image-to-VRAM generator script
- command/register definitions in
defines_and_setups/
Pipeline:
asm source -> assembler -> machine code -> CPU emulator
\
\-> disassembler -> asm source
Clone the repo and run make:
git clone https://github.com/DKay7/stack-cpu-emulator.git
cd stack-cpu-emulator
makeThis builds three executables: acc, exec and disacc.
Compile an assembly file:
./acc ./examples/square_solver/square_solver.asm ./examples/square_solver/square_solver.mc ./examples/square_solver/square_solver.lstRun it:
./exec ./examples/square_solver/square_solver.mcDisassemble it back:
./disacc ./examples/square_solver/square_solver.mc output.asmGenerated .mc, .lst and output.asm files are ignored by git.
There is a small script which converts an image to an assembly program that prints it through the emulator's VRAM:
python3 ./examples/vram/ascii_generator.pyEnter the path to an image and target width in ASCII symbols. Width around 150
usually works fine for most pictures. The script creates a .asm file which can
then be assembled and executed like other examples.
CPU includes:
- Stack data structure
- Registers
- Random Access Memory, RAM
- Video Random Access Memory, V-RAM
Stack is just LIFO data structure. In this CPU it stores data of double type. Stack has no fixed size, so it can increase and decrease depending on its capacity.
Registers are fast-access memory blocks. One register can hold just one value, also of double type. Total num of registers is 24.
RAM is Random Access Memory. In real CPUs it is much slower than registers, but in this realization speeds of access to registers and to the RAM are correlated.
VRAM is just a part of RAM, which data is interpreted by CPU as ASCII characters. You can set the place where VRAM starts at RAM, how long should it be and print out VRAM data to the screen by some of the commands below.
-
push value-- pushes value to stack.Available options:
1.1
push reg_name-- pushes value from register1.2
push [value]-- pushes value from ram element, which is pointed by value.1.3
push [reg_name]-- same as above, but pointer stores in register. -
pop-- pops value out of stackAvailable options:
1.1
pop reg_name-- pops value to register1.2
pop [value]-- pops value to ram element, which is pointed by value.1.3
pop [reg_name]-- same as above, but pointer stores in register. -
jmp label_name-- unconditional jump to label -
je label_name-- jump if two elements in stack are equal -
jne label_name-- jump if two elements in stack are not equal -
ja label_name-- jump if one element in stack are above than another -
jae label_name-- jump if one element in stack are above or equal than another -
jb label_name-- jump if one element in stack are below than another -
jbe label_name-- jump if one element in stack are below or equal than another -
call func_name-- calls a function. -
ret-- pops address from stack and returns from function. -
vrsetx value-- sets screen width. -
vrsety value-- sets screen height. -
scrupd-- updates screen (redraws it). -
scrclr-- clears screen buffer (not the screen). -
clrbuf value-- clears ram from 0 to given value. -
add-- adding two values from stack and pushes result back. -
sub-- subtracts two values from stack and pushes result back. -
mul-- multiplies two values from stack and pushes result back. -
div-- divides two values from stack and pushes result back. -
sqrt-- counts square root for value from stack and pushes result back. -
sin-- counts sine for value from stack and pushes result back. -
cos-- counts cosine for value from stack and pushes result back. -
rnd-- rounds value from stack and pushes result back. -
in-- takes value from the user and pushes it to the stack. -
out-- pops value from the stack and shows it to the user. -
dump-- dumps info about CPU. -
dumpstk-- dumps info about CPU's stack. -
validate-- validates CPU's stack. -
hlt-- terminates program.
All processor commands, registers and errors are written using a small DSL based
on definition headers. You can add a new command by writing a new command
definition to defines_and_setups/commands_defenitions.h. New error types and
registers can be added in the same style.