A small virtual machine written in C++, complete with an assembler and a debugger.
This is a virtual machine that simulates execution of a nonexistent 16-bit CPU. It includes tools for assembling machine code from assembly code, a dissasembler, and a graphical debugger.
micro_vm uses SCons to build. Use the scons command to build, and scons -c to clean up. This is also wrapped up in a Makefile, but you need to have SCons installed in order to build.
You will also need to install the ncurses development headers; on Debian-based Linux distros this can be found in the ncurses-dev package.
Once you have built the VM, you can execute the test program by running ./cpu out.bin. It will present a blank prompt. Enter 2 numbers and the VM will add them together:
./cpu out.bin
2
4
6
More test programs can be found in the progs/ directory
The CPU is a 16-bit little-endian RISC processor with a few high-level instructions for interacting with console input and output.
A(8 bit)X= I[High]Y= I[Low]FP(16-bit)SP(8-bit offset from FP)IP(16-bit)F(flags)- Zero
- Carry
- Sign
SP points to next available element in stack (uninitialized). Therefore, it should never be accessed directly by program code.
The instruction set is pretty standard and should look familiar to most assembly programmers. The specification listed beside the instruction is C syntax, with a * indicating a memory address dereference (so for example, *I refers to the memory location at the address stored in register I).
A numeric suffix indicates the byte size an operand literal.
- Operations
BAD: InvalidNOP: NothingADD:A = A + *X:YADD1:A = A + LiteralAND:A = A & *X:YAND1:A = A & LiteralOR:A = A | *X:YORL:A = A | LiteralCMPL:A = ~AINCA:++AINCX:++XINCY:++YINCI:++IDECA:--ADECX:--XDECY:--YDECI:--IDIV:I = I / A; A = I % AMUL:I = A * Y(8-bit operands, 16-bit result)
- Compare
CMP:Flags=ACompared to*X:YCMP1:Flags=ACompared toLiteral
- Memory
LDA:A = *X:YLDA1:A = LiteralLDX1:X = LiteralLDY1:Y = LiteralDREF:I = *ILAS1:A = SP[Literal]LIS1:I = SP[Literal]STOR:*I = ATXA:A = XTYA:A = YTAX:X = ATAY:Y = ATSPI:I = FP + SPLDI2:I = Literal
- Stack manipulation
PSHA:*SP = A; ++SPPSHX:*SP = X; ++SPPSHY:*SP = Y; ++SPPSH1:*SP = Lit8; ++SPPSH2:*SP = Lit16; SP += 2POPA:--SP; A = *(FP + SP)POPX:--SP; X = *(FP + SP)POPY:--SP; Y = *(FP + SP)POP1:SP -= LitPOP:--SPLAL1:A = FP[Literal](LoadAfrom local)LAP1:A = FP[-5 - Literal](LoadAfrom parameter)LIP1:I = FP -5 - Literal(LoadIfrom parameter address)LIL1:I = FP + Literal(LoadIfrom local address)STL1:FP[Literal] = A
- Flow Control
JMP2:IP = LiteralJGE2:If F[Zero] || !F[Sign], IP = LiteralJG2:If !F[Sign], IP = LiteralJL2:If F[Sign], IP = LiteralJLE2:If F[Zero] || F[Sign], IP = LiteralJE2:If F[Zero], IP = LiteralJNE2:If !F[Zero], IP = LiteralJC2:If F[Carry], IP = LiteralCAL2:*(FP+SP)=FP; *(FP+SP+2)=SP; *(FP+SP+3)=IP; FP+=SP+5; SP = 0; IP = Literal(Call subroutine)RET:IP = FP[-2]; SP = FP[-3]; FP = FP[-5](Return from subroutine)
- I/O
PRNT: PrintAas ASCII byteKEYB: Read key intoA. \0 is end of input
- Flags
CC: Clear Carry
- Debug
BRK: Debug break (NOPif no debugger)
Assembles input assembly code into a machine code file (out.bin) and a debug metadata file (out.dbg)
Usage: ./asm input.asm
Disassembles machine code into human-readable assembly code and outputs it to stdout.
Usage: ./disasm out.bin
Runs a program interactively on a debugger GUI (written in ncurses).
Usage: ./debug out (Notice no '.bin')
The enter key steps the program forward, and the 'r' steps the program backwards.
main:
PSH1 13
CAL2 fib
POP
PSHA
CAL2 printint8
POP
PSH2 endl
CAL2 puts
POP1 2
RET
fib:
LAP1 1
CMP1 1
JG2 .recur
RET
.recur:
CC
DECA
PSHA
CAL2 fib
POPX
PSHA
TXA
DECA
PSHA
CAL2 fib
POP
TSPI
DECI
ADD
POP1 1
RET
%file lib.asm