-
Jun 24th, 2020, 02:38 PM
#1
Thread Starter
Fanatic Member
Fish VM
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.
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;
}
Last edited by BenJones; Jun 30th, 2020 at 02:24 PM.
Reason: Update 30/06/2020
Tags for this Thread
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|