# VBForums CodeBank > CodeBank - C++ >  Fish VM

## BenJones

hi,
The last few weeks I been reading about virtual machines and how they work as I always wanted to write my own little language. I have read many books on the subject but never really sat down and tried to do something. 

So I decided the other day and try and make my own little VM one day I  hope to build something I can use to make programs, but for now I am just starting off small and see how things process.

anyway here what I made so far, this VM supports a small instruction set, you can play around with numbers it supports global only variables and that about it. its got some examples with this code at the moment they are hardcoded into integer arrays. 

In the next updates I am going to introduce an assembler to convert a more human readable  form of language into binary code or also known as byte code Then have the VM execute it.

 anyway I hope you like what I done and hope it will help someone. Comments are more than welcome or if you can think of improvements I can make drop me a line.

You will need GCC to compile the code, but I think it should also work in Visual Studio since it just basic C code.



```
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#define MAX_STACK 100
#define MAX_GLOBAL 1000
#define MAX_CODE 65536

//Program counter
int pc = 0;
//Stack pointer always points to the top of the stack.
int sp =-1;

int Stack[MAX_STACK] = {0};

int Global[MAX_GLOBAL] = {0};

//Main program vars
int ProgData[MAX_CODE] = {0};
int ProgCount = 0;

//Instructions
enum TOprands{
    NOP = 0,
    IADD = 1,
    ISUB = 2,
    IMUL = 3,
    IDIV = 4,
    IAND = 5,
    IOR = 6,
    IXOR = 7,
    IMOD = 8,
    INC = 9,
    DEC = 10,
    NEG = 11,
    NOT = 12,
    SHL = 13,
    SHR = 14,
    DUP = 15,
    ISGT = 16,
    ISLT = 17,
    ISEQ = 18,
    JMP = 19,
    JT = 20,
    JF = 21,
    ICONST = 22,
    GSTORE = 23,
    GLOAD = 24,
    PRINTI = 25,
    PRINTC = 26,
    HALT = 27
}Operands;

//Testing programs
//Add two numbers and show the result
int testAdd[] = {
    ICONST,20,
    ICONST,5,
    IADD,
    PRINTI,
    HALT
};
//Print ABC and make a beep sound
int testChar[] = {
    ICONST,65,
    PRINTC,
    ICONST,66,
    PRINTC,
    ICONST,67,
    PRINTC,
    ICONST,7,
    PRINTC,
    HALT
};
//Test Dup
int testDup[] = {
    ICONST,50,
    DUP,
    IADD,
    PRINTI,
    HALT
};

//Test global variables
int varTest1[] = {
    ICONST,25,
    GSTORE,1,
    ICONST,15,
    GLOAD,1,
    IADD,
    PRINTI,
    HALT
};

int varTest2[] = {
    //x = 5
    ICONST,20,
    GSTORE,0,
    // y = 0
    ICONST,0,
    GSTORE,1,
    //y = x + 20
    GLOAD,0,
    ICONST,20,
    IADD,
    //y now contains 40
    GSTORE,1,
    GLOAD,1,
    PRINTI,
    HALT
};

//Jump test
int jumpTest[] = {
    JMP,6,
    ICONST,10,
    PRINTI,
    HALT,
    ICONST,20,
    //Print newline
    PRINTI,
    ICONST,10,
    PRINTC,
    //Make a beep sound
    ICONST, 7,
    PRINTC,
    JMP,2
};

int testIfElse[] = {
    //set x to 8
    ICONST,8,
    GSTORE,0,
    //set y to 4
    ICONST,4,
    GSTORE,1,
    //load x and t onto the stack
    GLOAD,0,
    GLOAD,1,
    //test if x > y
    ISGT,
    //jump to label 22 if x > y
    JT,22,
    //Else
    //load y
    GLOAD,1,
    //set z to y
    GSTORE,2,
    //load z onto the stack
    GLOAD,2,
    //print z
    PRINTI,
    HALT,
    //Address 22
    //load x
    GLOAD,0,
    //set z to x
    GSTORE,2,
    //load z onto the stack
    GLOAD,2,
    //print the value of z
    PRINTI,
    HALT
};

int testINC [] = {
    //Push 10 on the stack
    ICONST,10,
    //Inc stack value by 1
    INC,
    //Print stack value now 11
    PRINTI,
    HALT
};

int testDEC [] = {
    //Push 10 on the stack
    ICONST,10,
    //Dec stack value by 1
    DEC,
    //Print stack value now 9
    PRINTI,
    HALT
};

int testNeg [] = {
    ICONST,5,
    NEG,
    DUP,
    PRINTI,
    NEG,
    PRINTI,
    HALT
};

int testNot [] = {
    ICONST,0,
    NOT,
    PRINTI,
    HALT
};

int testSHL[] = {
    ICONST,4,
    GSTORE,0,
    GLOAD,0,
    ICONST,4,
    SHL,
    GSTORE,1,
    GLOAD,1,
    PRINTI,
    HALT
};
int testSHR[] = {
    //set a = 512
    ICONST,512,
    GSTORE,0,
    //load a
    GLOAD,0,
    //push 8 on the stack
    ICONST,8,
    //Shift right
    SHR,
    //set c = (a >> 8)
    GSTORE,1,
    GLOAD,1,
    PRINTI,
    HALT
};

int testHello[] = {
    ICONST, 'H',
    PRINTC,
    ICONST, 'e',
    PRINTC,
    ICONST, 'l',
    PRINTC,
    ICONST, 'l',
    PRINTC,
    ICONST, 'o',
    PRINTC,
    HALT
};

int testSwap[] = {
    //Set a = 100
    ICONST,100,
    GSTORE,0,
    //Set b = 200
    ICONST,200,
    GSTORE,1,
    //swap numbers
    //set t to a
    GLOAD,1,
    GSTORE,2,
    //store a into b
    GLOAD,0,
    GSTORE,1,
    //set a to t
    GLOAD,2,
    GSTORE,0,
    //end swap
    //display value of a
    GLOAD,0,
    PRINTI,
    //This just separates the numbers eg 200:100
    ICONST,':',
    PRINTC,
    //display value of b
    GLOAD,1,
    PRINTI,
    HALT
};

//End of testing programs

void Push(int v){
    Stack[++sp] = v;
}

int Pop(){
    return Stack[sp--];
}

int Peek(){
    return Stack[sp];
}

void vm_addCode(int code[], int size){
int x = 0;
    while(x < size){
        ProgData[x] = code[x];
        x++;
    }
    ProgCount = x;
}

void vm_free(){
    pc = 0;
    sp =-1;
    ProgCount = 0;
    memset(ProgData,0,sizeof ProgData);
    memset(Stack,0,sizeof Stack);
    memset(Global,0,sizeof Global);
}

int vm_BinaryOp(int op, int a, int b){
    switch(op){
    case IADD:
        return a + b;
    case ISUB:
        return b - a;
    case IMUL:
        return a * b;
    case IDIV:
        return b / a;
    case IAND:
        return a && b;
    case IOR:
        return a || b;
    case IXOR:
        return a ^ b;
    case IMOD:
        return b % a;
    case ISGT:
        return (b > a) ? 1 : 0;
    case ISLT:
        return (b < a) ? 1 : 0;
    case ISEQ:
        return (a == b) ? 1 : 0;
    default:
        return 0;
    }
}

void vm_execute(){
int a  = 0;
int b = 0;
int addr = 0;

    while(pc < ProgCount){
        //Fetch op-codes
        int op = ProgData[pc];
        //Execute the op-codes
        switch(op){
        case JMP:
            //Jump to address
            pc++;
            //Get address from pcode
            addr = ProgData[pc];
            //Set program counter to addr
            pc = addr;
            break;
        case JT:
            //Jump if stack contains 1
            pc++;
            //Get jump address from pcode
            addr = ProgData[pc];
            //Test for true or 1
            if(Pop()){
                //Jump to the address
                pc = addr;
            }
            break;
        case JF:
            //Jump if stack contains 0
            pc++;
            //Get jump address from pcode
            addr = ProgData[pc];
            //Test for false or 0
            if(!Pop()){
                //Jump to the address
                pc = addr;
            }
            break;
        case ICONST:
            //INC program counter to get next index
            pc++;
            //Push the value on the stack top
            Push(ProgData[pc]);
            break;
        case GSTORE:
            pc++;
            //Get variable address
            addr = ProgData[pc];
            //Store the top of the stack in global[address]
            Global[addr] = Pop();
            break;
        case GLOAD:
            pc++;
            //Get variable address
            addr = ProgData[pc];
            //Push the value from global onto the stack
            Push(Global[addr]);
            break;
        case IADD:
        case ISUB:
        case IMUL:
        case IDIV:
        case IAND:
        case IOR:
        case IXOR:
        case IMOD:
        case ISGT:
        case ISLT:
            //Pop of the two values on top of the stack
            a = Pop();
            b = Pop();
            //Preform the binary op and push back the result on the stack.
            Push(vm_BinaryOp(op,a,b));
            break;
        case INC:
            a = Pop();
            Push(a+1);
            break;
        case DEC:
            a = Pop();
            Push(a-1);
            break;
        case NEG:
            a = Pop();
            Push(-a);
            break;
        case NOT:
            a = Pop();
            Push(!a);
            break;
        case SHL:
            a = Pop();
            b = Pop();
            Push(b << a);
            break;
        case SHR:
            a = Pop();
            b = Pop();
            Push(b >> a);
            break;
        case DUP:
            //Duplicate what is on the stack and push the value on the stack.
            Push(Peek());
            break;
        case PRINTI:
            //Pop of the top of the stack value and display to user.
            printf("%d",Pop());
            break;
        case PRINTC:
            //Same as PRINTI but this converts and prints as a char
            printf("%c",Pop());
            break;
        case HALT:
            //Stop the program
            pc = ProgCount;
            break;
        case NOP:
            //Do nothing
            break;
        default:
            break;
        }
        //INC Program counter
        pc++;
    }
}

int main()
{
    //Get the size of the program to execute
    int n = sizeof(testSwap) / sizeof(int);
    //Add test code make sure you also include the size
    vm_addCode(testSwap,n);

    vm_execute();
    //Clean up VM
    vm_free();

    return 0;
}
```

----------


## BenJones

I have updated my Virtual Machine added some more examples and op-codes you can now do simple logic if's and jumps, I am now currently working on an assembler that I will upload as soon as I have finished it.

----------


## BenJones

I have updated my Virtual machine to include an assembler I also added a few examples to show how the VM works no more hard coded examples. anyway hope you like the update.

fish.zip

To assemble and test the examples use this when in the bin folder



```
./build loop.txt loop.fish
```

to run the above assembled example on the VM use



```
./fish loop.fish
```

Next update I will hope to add function calls to the VM and also look at building a compiler frontend so you can compile a more human like high level language.

----------

