• Stars
    star
    157
  • Rank 238,399 (Top 5 %)
  • Language
    Python
  • License
    MIT License
  • Created over 9 years ago
  • Updated over 9 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

eXtremely small virtual machine -- for educational purposes :)

xs-vm Build Status

eXtremely Simple Virtual Machine

The purpose of this project is to implement a simple virtual machine, capable of executing assembly code similar to ARM. I will keep it simple (so probably no operation modes, interrupt handling, etc) because I built it for educational purposes. Yet, it's powerful enough to do things like recursion (see the Fibonacci example ). Why Python? Development speed, as opposed to performance, is the key priority for this project so Python fits in perfectly.

It is distributed under MIT license (see license.txt for the details).

All Python versions from 2.7 up to 3.4 are supported.

Installing and running

Executing code

$ cat demos/function_call.s
mov r1, #1
mov r2, #3
bl func1
swi #0

func1    push lr
         add r0, r1, r2
         pop pc
         
$ python run.py demos/function_call.s
Instructions executed: 7
Instructions executed by type:
╒═══════════════╀═════════╀═══════════════╀═════════╕
β”‚ Instruction   β”‚   Count β”‚ Instruction   β”‚   Count β”‚
β•žβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•ͺ═════════β•ͺ═══════════════β•ͺ═════════║
β”‚ swi           β”‚       1 β”‚ bl            β”‚       1 β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ mov           β”‚       2 β”‚ pop           β”‚       1 β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ add           β”‚       1 β”‚ push          β”‚       1 β”‚
β•˜β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•›
Register bank after halting:
╒═════╀═══╀═════╀══════════╕
β”‚ r0  β”‚ 4 β”‚ r1  β”‚        1 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ r2  β”‚ 3 β”‚ r3  β”‚        0 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ r4  β”‚ 0 β”‚ r5  β”‚        0 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ r6  β”‚ 0 β”‚ r7  β”‚        0 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ r8  β”‚ 0 β”‚ r9  β”‚        0 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ r10 β”‚ 0 β”‚ r11 β”‚        0 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ r12 β”‚ 0 β”‚ r13 β”‚ 16777215 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ r14 β”‚ 3 β”‚ r15 β”‚        4 β”‚
β•˜β•β•β•β•β•β•§β•β•β•β•§β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•›

Using -d or --debug will enable the debugging mode, in which the values of the register bank will be dumped after executing every instruction:

$ python run.py --debug demos/function_call.s
Debug mode: True
Executing mov r1, #1 from 0
Register bank after executing the instruction:
╒═════╀═══╀═════╀══════════╕
β”‚ r0  β”‚ 0 β”‚ r1  β”‚        1 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ r2  β”‚ 0 β”‚ r3  β”‚        0 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ r4  β”‚ 0 β”‚ r5  β”‚        0 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ r6  β”‚ 0 β”‚ r7  β”‚        0 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ r8  β”‚ 0 β”‚ r9  β”‚        0 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ r10 β”‚ 0 β”‚ r11 β”‚        0 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ r12 β”‚ 0 β”‚ r13 β”‚ 16777215 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ r14 β”‚ 0 β”‚ r15 β”‚        1 β”‚
β•˜β•β•β•β•β•β•§β•β•β•β•§β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•›
Executing mov r2, #3 from 1
Register bank after executing the instruction:
╒═════╀═══╀═════╀══════════╕
β”‚ r0  β”‚ 0 β”‚ r1  β”‚        1 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ r2  β”‚ 3 β”‚ r3  β”‚        0 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ r4  β”‚ 0 β”‚ r5  β”‚        0 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ r6  β”‚ 0 β”‚ r7  β”‚        0 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ r8  β”‚ 0 β”‚ r9  β”‚        0 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ r10 β”‚ 0 β”‚ r11 β”‚        0 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ r12 β”‚ 0 β”‚ r13 β”‚ 16777215 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ r14 β”‚ 0 β”‚ r15 β”‚        2 β”‚
β•˜β•β•β•β•β•β•§β•β•β•β•§β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•›
Executing bl func1 from 2
.......................

Installing and running tests

Just clone this repository and install the dependencies (pip install -r requirements.txt).

You can run the tests simply by running nosetests from the project root directory.

Architecture

The VM has 16 registers (R0-R15). Most of the are general purpose, with a few special ones:

  • SP (R13). Stack pointer. Points to the last element pushed to the stack (or 0xFFFFFF if nothing has been pushed yet).

  • LR (R14). Link register. Holds a return address of the function call.

  • PC (R15). Program counter. Holds the address of the instruction in memory which will be executed next.

For function calls, the result is stored in R0, and R1-R3 are normally used to pass the parameters.

Supported instructions

Instruction Example Description
mov mov r1, #5 Move some value (either other register or a constant) to the register.
mov r1, r2
add add r0, r1, #3 r0 = r1 + 3
sub sub r0, r1, #3 r0 = r1 - 3
mul mul r0, r1, #4 r0 = r1 * 4
mla mla r0, r1, #3, #5 r0 = r1 * 3 + 5
cmp cmp r0, r1 Compare 2 numerical values and store the difference in comparison register (comp_reg = r0 - r1). The value is used later for conditional branching.
b b main Always branch. Set PC to the instruction to which the given label is pointing to.
b 0x001 Instead of label, memory location can be also passed.
beq beq main Branch if equal, the result of cmp instruction is used.
bne bne main Branch if not equal.
blt blt foo Branch if less than (comp_reg < 0).
bgt bgt foo Branch if greater than (comp_reg > 0).
bl bl printf Branch and link. Stores PC in LR, equivalent of a function call.
nop nop No OPeration. Do nothing.
push push r0 Push the value of r0 to the stack.
pop pop r1 Pop the element from the stack and store the value in r1.
swi swi #0 Software interrupt. See below.

Software interrupts

In xs-vm, software interrupts is a method of communication between the application being executed and the virtual machine executing it.

The list of supported software interrupts:

Interrupt Description
swi #0 Halt. Stop executing the application and quit