Skip to content

Instantly share code, notes, and snippets.

@SouhailHammou
Created September 22, 2015 17:00
Show Gist options
  • Star 22 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save SouhailHammou/6b4069b07e538a48bb70 to your computer and use it in GitHub Desktop.
Save SouhailHammou/6b4069b07e538a48bb70 to your computer and use it in GitHub Desktop.
Virtual machine with a custom instruction set.
/*
VM by Souhail Hammou : custom instruction set
data space and stack space are customizable.
Important : In calculations the VM is using unsigned values.
*/
#include <stdio.h>
#include <stdint.h>
#include <conio.h>
#define TRUE 1
#define FALSE 0
typedef unsigned char boolean;
typedef uint8_t BYTE;
typedef uint16_t WORD;
typedef uint32_t DWORD;
typedef struct
{
/*data has also the code*/
BYTE data[4096];
/*stack space , size of one element is WORD in order to be able to push addresses*/
WORD stack[256];
}ADDRESS_SPACE,*PADDRESS_SPACE;
typedef struct
{
/*General Purpose Registers R0 -> R3*/
WORD GPRs[4];
union
{
unsigned char Flags;
struct
{
unsigned char ZF:1;
unsigned char CF:1;
unsigned char Unused:6;
};
};
WORD IP;
WORD SP;
}REGS,*PREGS;
void VmLoop(PADDRESS_SPACE AS,PREGS Regs)
{
int i;
boolean exit = FALSE;
BYTE opcode,byte_val,byte_val2,byte_val3;
WORD word_val,word_val2;
while(!exit)
{
/*read byte (opcode)*/
//printf("[+] IP : %.4X => ",Regs->IP);
opcode = AS->data[Regs->IP++];
/*opcodes switch*/
switch(opcode)
{
case 0x90 :
//printf("NOP\n");
break;
/*
Each nibble of the operand represents a General purpose register (GPR)
the highest nibble is the destination , the lowest one is the source.
Example:
10 12 => MOV R1,R2
10 11 => MOV R1,R1
10 01 => MOV R0,R1
*/
case 0x10 :
byte_val = AS->data[Regs->IP++];
if((byte_val & 0xF0) <= 0x30 && (byte_val & 0x0F) <= 3)
{
Regs->GPRs[(byte_val & 0xF0)>>4] = Regs->GPRs[byte_val & 0x0F];
//printf("MOV R%d,R%d\n",(byte_val & 0xF0)>>4,byte_val & 0x0F);
}
else
goto exception;
break;
/*
Move and extend byte from memory to register
Example:
12 03 50 00 => MOVX R3,BYTE [0050]
12 00 00 01 => MOVX R0,BYTE [0100]
*/
case 0x12 :
byte_val = AS->data[Regs->IP++];
if(byte_val > 3)
goto exception;
word_val = *(WORD*)&AS->data[Regs->IP];
Regs->IP += 2;
if(word_val >= sizeof(AS->data))
goto exception;
Regs->GPRs[byte_val] = 0;
*(BYTE*)&Regs->GPRs[byte_val] = AS->data[word_val];
//printf("MOVX R%d, BYTE [%.4X]\n",byte_val,word_val);
break;
/*
Move word from memory to register
14 03 50 00 => MOV R3,WORD [0050]
14 00 00 01 => MOV R0,WORD [0100]
*/
case 0x14 :
byte_val = AS->data[Regs->IP++];
if(byte_val > 3)
goto exception;
word_val = *(WORD*)&AS->data[Regs->IP];
Regs->IP += 2;
if(word_val >= sizeof(AS->data))
goto exception;
Regs->GPRs[byte_val] = *(WORD*)&AS->data[word_val];
//printf("MOV R%d, WORD [%.4X]\n",byte_val,word_val);
break;
/*
Move and extend byte to register
16 01 15 => MOVX R1,15h
*/
case 0x16 :
byte_val = AS->data[Regs->IP++];
if(byte_val > 3)
goto exception;
Regs->GPRs[byte_val] = 0;
*(BYTE*)&Regs->GPRs[byte_val] = AS->data[Regs->IP++];
//printf("MOVX R%d,%.2Xh\n",byte_val,AS->data[Regs->IP - 1]);
break;
/*
Move word to register
18 01 15 28 => MOV R1,2815h
*/
case 0x18 :
byte_val = AS->data[Regs->IP++];
if(byte_val > 3)
goto exception;
Regs->GPRs[byte_val] = 0;
Regs->GPRs[byte_val] = *(WORD*)&AS->data[Regs->IP];
//printf("MOV R%d,%.4Xh\n",byte_val,*(WORD*)&AS->data[Regs->IP]);
Regs->IP += sizeof(WORD);
break;
/*
Move byte from register to memory location
ex :
1C 01 20 01 => MOV BYTE [0120],R1
1C 03 50 03 => MOV BYTE [0350],R3
*/
case 0x1c :
byte_val = AS->data[Regs->IP++];
if(byte_val > 3)
goto exception;
word_val = *(WORD*)&AS->data[Regs->IP];
Regs->IP += 2;
if(word_val >= sizeof(AS->data))
goto exception;
AS->data[word_val] = *(BYTE*)&Regs->GPRs[byte_val];
//printf("MOV BYTE [%.4X],R%d\n",word_val,byte_val);
break;
/*
Move word from register to memory location
ex :
1F 01 20 01 => MOV WORD [0120],R1
1F 03 50 03 => MOV WORD [0350],R3
*/
case 0x1f :
byte_val = AS->data[Regs->IP++];
if(byte_val > 3)
goto exception;
word_val = *(WORD*)&AS->data[Regs->IP];
Regs->IP += 2;
if(word_val >= sizeof(AS->data))
goto exception;
*(WORD*)&AS->data[word_val] = Regs->GPRs[byte_val];
//printf("MOV WORD [%.4X],R%d\n",word_val,byte_val);
break;
/*
Unconditional Jump
example :
E0 10 00 => JMP 0010
E0 54 02 => JMP 0254
*/
case 0xE0 :
word_val = *(WORD*)&AS->data[Regs->IP];
Regs->IP += 2;
if(word_val > sizeof(AS->data))
goto exception;
Regs->IP = word_val;
//printf("JMP %.4X\n",word_val);
break;
/*
JZ : Jump if equal
E2 54 01 =>JNZ 0154
*/
case 0xE2 :
word_val = *(WORD*)&AS->data[Regs->IP];
Regs->IP += 2;
if(word_val > sizeof(AS->data))
goto exception;
/*Jump if ZF is set*/
if(Regs->ZF)
Regs->IP = word_val;
//printf("JZ %.4X\n",word_val);
break;
/*
JNZ : Jump if not equal
E3 54 01 => JNZ 0154
*/
case 0xE3 :
word_val = *(WORD*)&AS->data[Regs->IP];
Regs->IP += 2;
if(word_val > sizeof(AS->data))
goto exception;
if(! Regs->ZF)
Regs->IP = word_val;
//printf("JNZ %.4X\n",word_val);
break;
/*
JAE : Jump if above or equal
E4 54 01 : JAE 0154
*/
case 0xE4 :
word_val = *(WORD*)&AS->data[Regs->IP];
Regs->IP += 2;
if(word_val > sizeof(AS->data))
goto exception;
if(Regs->ZF || ! Regs->CF)
Regs->IP = word_val;
//printf("JAE %.4X\n",word_val);
break;
/*
JBE : Jump if below or equal
E6 54 01 : JBE 0154
*/
case 0xE6 :
word_val = *(WORD*)&AS->data[Regs->IP];
Regs->IP += 2;
if(word_val > sizeof(AS->data))
goto exception;
if(Regs->ZF || Regs->CF)
Regs->IP = word_val;
//printf("JBE %.4X\n",word_val);
break;
/*
JB : Jump if below
E8 54 01 : JB 0154
*/
case 0xE8 :
word_val = *(WORD*)&AS->data[Regs->IP];
Regs->IP += 2;
if(word_val > sizeof(AS->data))
goto exception;
if(Regs->CF && ! Regs->ZF)
Regs->IP = word_val;
//printf("JB %.4X\n",word_val);
break;
/*
JA : Jump if above
EC 54 01 => JA 0154
*/
case 0xEC :
word_val = *(WORD*)&AS->data[Regs->IP];
Regs->IP += 2;
if(word_val > sizeof(AS->data))
goto exception;
if( ! Regs->CF && ! Regs->ZF)
Regs->IP = word_val;
//printf("JA %.4X\n",word_val);
break;
/*=======================================================*/
/*ARITHMETIC OPERATIONS ON THE WHOLE REGISTER (WORD)*/
/*
ADD : Add value to register
AD 01 15 00 : ADD R1,15h
AD 01 01 50 : ADD R1,5001h
Updated flags :
ZF and CF
*/
case 0xAD :
byte_val = AS->data[Regs->IP++];
if(byte_val > 3)
goto exception;
word_val = *(WORD*)&AS->data[Regs->IP];
Regs->IP += 2;
word_val2 = Regs->GPRs[byte_val] + word_val;
if(word_val2 == 0)
Regs->ZF = 1;
else
Regs->ZF = 0;
if(word_val2 < Regs->GPRs[byte_val])
Regs->CF = 1;
else
Regs->CF = 0;
Regs->GPRs[byte_val] = word_val2;
//printf("ADD R%d,%.4X\n",byte_val,word_val);
break;
/*
ADD : Add 2 registers
A5 12 : ADD R1,R2
A5 30 : ADD R3,R0
*/
case 0xA5 :
byte_val = AS->data[Regs->IP++];
if((byte_val & 0xF0) <= 0x30 && (byte_val & 0x0F) <= 3)
{
word_val = Regs->GPRs[(byte_val & 0xF0)>>4];
word_val2 = Regs->GPRs[(byte_val & 0xF0)>>4] += Regs->GPRs[byte_val & 0x0F];
if(word_val2 == 0)
Regs->ZF = 1;
else
Regs->ZF = 0;
if(word_val2 < word_val)
Regs->CF = 1;
else
Regs->CF = 0;
}
else
goto exception;
//printf("ADD R%d,R%d\n",(byte_val & 0xF0)>>4,byte_val & 0x0F);
break;
/*
ADDL : Add 2 registers (low byte)
A2 12 => ADDL R1,R2
*/
case 0xA2 :
byte_val = AS->data[Regs->IP++];
if((byte_val & 0xF0) <= 0x30 && (byte_val & 0x0F) <= 3)
{
byte_val2 = *(BYTE*)&Regs->GPRs[(byte_val & 0xF0)>>4];
byte_val3 = *(BYTE*)&Regs->GPRs[(byte_val & 0xF0)>>4] += *(BYTE*)&Regs->GPRs[byte_val & 0x0F];
if(byte_val3 == 0)
Regs->ZF = 1;
else
Regs->ZF = 0;
if(byte_val3 < byte_val2)
Regs->CF = 1;
else
Regs->CF = 0;
}
else
goto exception;
//printf("ADDL R%d,R%d\n",(byte_val & 0xF0)>>4,byte_val & 0x0F);
break;
/*
SUB : substract value from register
5B 01 15 00 : SUB R1,15h
5B 01 01 50 : SUB R1,5001h
Updated flags :
ZF and CF
*/
case 0x5B :
byte_val = AS->data[Regs->IP++];
if(byte_val > 3)
goto exception;
word_val = *(WORD*)&AS->data[Regs->IP];
Regs->IP += 2;
word_val2 = Regs->GPRs[byte_val] - word_val;
if(word_val2 == 0)
Regs->ZF = 1;
else
Regs->ZF = 0;
if(word_val2 > Regs->GPRs[byte_val])
Regs->CF = 1;
else
Regs->CF = 0;
Regs->GPRs[byte_val] = word_val2;
//printf("SUB R%d,%.4X\n",byte_val,word_val);
break;
/*
SUB : substract registers (word)
5C 01 => SUB R0,R1
*/
case 0x5C :
byte_val = AS->data[Regs->IP++];
if((byte_val & 0xF0) <= 0x30 && (byte_val & 0x0F) <= 3)
{
word_val = Regs->GPRs[(byte_val & 0xF0)>>4];
word_val2 = Regs->GPRs[(byte_val & 0xF0)>>4] -= Regs->GPRs[byte_val & 0x0F];
if(word_val2 == 0)
Regs->ZF = 1;
else
Regs->ZF = 0;
if(word_val2 > word_val)
Regs->CF = 1;
else
Regs->CF = 0;
}
else
goto exception;
//printf("SUB R%d,R%d",(byte_val & 0xF0)>>4,byte_val & 0x0F);
break;
/*
SUBL : Substract 2 registers (low part)
5D 12 => SUBL R1,R2
*/
case 0x5D :
byte_val = AS->data[Regs->IP++];
if((byte_val & 0xF0) <= 0x30 && (byte_val & 0x0F) <= 3)
{
byte_val2 = *(BYTE*)&Regs->GPRs[(byte_val & 0xF0)>>4];
byte_val3 = *(BYTE*)&Regs->GPRs[(byte_val & 0xF0)>>4] -= *(BYTE*)&Regs->GPRs[byte_val & 0x0F];
if(byte_val3 == 0)
Regs->ZF = 1;
else
Regs->ZF = 0;
if(byte_val3 > byte_val2)
Regs->CF = 1;
else
Regs->CF = 0;
}
else
goto exception;
//printf("SUBL R%d,R%d",(byte_val & 0xF0)>>4,byte_val & 0x0F);
break;
/*
XOR : Xor 2 registers
(operand uses nibbles : high = dest , low = source)
F0 12 => XOR R1,R2
F0 01 => XOR R0,R1
*/
case 0xF0 :
byte_val = AS->data[Regs->IP++];
if((byte_val & 0xF0) <= 0x30 && (byte_val & 0x0F) <= 3)
{
word_val = Regs->GPRs[(byte_val & 0xF0)>>4] ^= Regs->GPRs[byte_val & 0x0F];
if(word_val == 0)
Regs->ZF = 1;
else
Regs->ZF = 0;
Regs->CF = 0;
}
else
goto exception;
//printf("XOR R%d,R%d\n",(byte_val & 0xF0)>>4,byte_val & 0x0F);
break;
/*===============================================================*/
/*ARITHMETIC OPERATIONS ON THE LOWER BYTE OF THE REGISTER*/
/*
XORL : Xor the lower bytes of 2 registers
F1 12 : XORL R1,R2
F1 01 : XORL R0,R1
*/
case 0xF1 :
byte_val = AS->data[Regs->IP++];
if((byte_val & 0xF0) <= 0x30 && (byte_val & 0x0F) <= 3)
{
byte_val2 = *(BYTE*)&Regs->GPRs[(byte_val & 0xF0)>>4] ^= *(BYTE*)&Regs->GPRs[byte_val & 0x0F];
if(byte_val2 == 0)
Regs->ZF = 1;
else
Regs->ZF = 0;
Regs->CF = 0;
}
else
goto exception;
//printf("XORL R%d,R%d\n",(byte_val & 0xF0)>>4,byte_val & 0x0F);
break;
/*
ADDL : add only to the lower of the register
A1 03 20 => ADDL R3,20h
*/
case 0xA1:
byte_val = AS->data[Regs->IP++];
if(byte_val > 3)
goto exception;
byte_val2 = AS->data[Regs->IP++];
byte_val3 = *(BYTE*)&Regs->GPRs[byte_val] + byte_val2;
if(byte_val3 == 0)
Regs->ZF = 1;
else
Regs->ZF = 0;
if(byte_val3 < *(BYTE*)&Regs->GPRs[byte_val])
Regs->CF = 1;
else
Regs->CF = 0;
*(BYTE*)&Regs->GPRs[byte_val] = byte_val3;
//printf("ADDL R%d,%.2X\n",byte_val,byte_val2);
break;
/*
SUBL : Substract only from the lower byte of the register
51 03 20 => SUBL R3,20h
*/
case 0x51:
byte_val = AS->data[Regs->IP++];
if(byte_val > 3)
goto exception;
byte_val2 = AS->data[Regs->IP++];
byte_val3 = *(BYTE*)&Regs->GPRs[byte_val] - byte_val2;
if(byte_val3 == 0)
Regs->ZF = 1;
else
Regs->ZF = 0;
if(byte_val3 > *(BYTE*)&Regs->GPRs[byte_val])
Regs->CF = 1;
else
Regs->CF = 0;
*(BYTE*)&Regs->GPRs[byte_val] = byte_val3;
//printf("SUBL R%d,%.2X\n",byte_val,byte_val2);
break;
/*===============================================================*/
/*
Store register (low byte) at [Rx].
55 21 => MOV BYTE [R2],R1
*/
case 0x55 :
byte_val = AS->data[Regs->IP++];
if((byte_val & 0xF0) <= 0x30 && (byte_val & 0x0F) <= 3 && Regs->GPRs[(byte_val & 0xF0)>>4] < sizeof(AS->data))
AS->data[Regs->GPRs[(byte_val & 0xF0)>>4]] = *(BYTE*)&Regs->GPRs[byte_val & 0x0F];
else
goto exception;
//printf("MOV BYTE [R%d],R%d\n",(byte_val & 0xF0)>>4,byte_val & 0x0F);
break;
/*
Load and extend low byte of register from memory pointed by a register
56 21 => MOV R2,BYTE [R1]
*/
case 0x56 :
byte_val = AS->data[Regs->IP++];
if((byte_val & 0xF0) <= 0x30 && (byte_val & 0x0F) <= 3 && Regs->GPRs[(byte_val & 0x0F)] < sizeof(AS->data))
{
*(BYTE*)&Regs->GPRs[(byte_val & 0xF0)>>4] = AS->data[Regs->GPRs[byte_val & 0x0F]];
Regs->GPRs[(byte_val & 0xF0)>>4] &= 0xFF;
}
else
goto exception;
//printf("MOVX R%d, BYTE [R%d]\n",(byte_val & 0xF0)>>4,byte_val & 0x0F);
break;
/*
CMP : Compare 2 registers (word)
70 12 : CMP R1,R2
*/
case 0x70 :
byte_val = AS->data[Regs->IP++];
if((byte_val & 0xF0) <= 0x30 && (byte_val & 0x0F) <= 3 && Regs->GPRs[(byte_val & 0x0F)] < sizeof(AS->data))
{
word_val = Regs->GPRs[(byte_val & 0xF0)>>4];
word_val2 = Regs->GPRs[byte_val & 0x0F];
if(word_val2 == word_val)
Regs->ZF = 1;
else
Regs->ZF = 0;
if(word_val2 > word_val)
Regs->CF = 1;
else
Regs->CF = 0;
}
else
goto exception;
//printf("CMP R%d,R%d\n",(byte_val & 0xF0)>>4,byte_val & 0x0F);
break;
/*
CMPL : Compare 2 registers (lower byte)
71 12 : CMPL R1,R2
*/
case 0x71 :
byte_val = AS->data[Regs->IP++];
if((byte_val & 0xF0) <= 0x30 && (byte_val & 0x0F) <= 3 && Regs->GPRs[(byte_val & 0x0F)] < sizeof(AS->data))
{
byte_val2 = *(BYTE*)&Regs->GPRs[(byte_val & 0xF0)>>4];
byte_val3 = *(BYTE*)&Regs->GPRs[byte_val & 0x0F];
if(byte_val3 == byte_val2)
Regs->ZF = 1;
else
Regs->ZF = 0;
if(byte_val3 > byte_val2)
Regs->CF = 1;
else
Regs->CF = 0;
}
else
goto exception;
//printf("CMP R%d,R%d\n",(byte_val & 0xF0)>>4,byte_val & 0x0F);
break;
/*
Push register
example : AF 01 => PUSH R1
*/
case 0xAF :
byte_val = AS->data[Regs->IP++];
if(byte_val > 3)
goto exception;
/*Decrement the stack pointer to store the new value*/
Regs->SP--;
/*Check for stack overflow*/
if(Regs->SP == 0xFFFF)
goto exception;
/*Push value */
AS->stack[Regs->SP] = Regs->GPRs[byte_val];
//printf("PUSH R%d\n",byte_val);
break;
/*
Pop a register
AE 01 => POP R1
*/
case 0xAE :
byte_val = AS->data[Regs->IP++];
if(byte_val > 3)
goto exception;
/*
Check for stack underflow
*/
if(&AS->stack[Regs->SP] == &AS->stack[sizeof(AS->stack)/sizeof(WORD)])
goto exception;
/*Move the value into the register*/
Regs->GPRs[byte_val] = AS->stack[Regs->SP];
/*Value popped , increment SP*/
Regs->SP++;
//printf("POP R%d\n",byte_val);
break;
/*========================================================*/
/*User interaction operations (print and receive input)*/
/*
Print Word to user as integer, the value must be at the top of the stack and it is popped
C0 => print integer
*/
case 0xC0 :
/*read value then pop it*/
if(&AS->stack[Regs->SP] == &AS->stack[sizeof(AS->stack)/sizeof(WORD)])
goto exception;
word_val = AS->stack[Regs->SP++];
//printf("Print integer\n");
printf("%u\n",word_val);
break;
/*
Print string to user, the pointer must be at the top of the stack and it is popped
C2 => print string
*/
case 0xC2 :
if(&AS->stack[Regs->SP] == &AS->stack[sizeof(AS->stack)/sizeof(WORD)])
goto exception;
/*read it and pop it*/
word_val = AS->stack[Regs->SP++];
if(word_val > sizeof(AS->data))
goto exception;
//printf("Print string\n");
printf("%s",&AS->data[word_val]);
break;
/*
Scan string from user, the pointer where to store the integer must be on top of the stack
89
*/
case 0x89 :
if(&AS->stack[Regs->SP] == &AS->stack[sizeof(AS->stack)/sizeof(WORD)])
goto exception;
/*read it and pop it*/
word_val = AS->stack[Regs->SP++];
if(word_val > sizeof(AS->data))
goto exception;
//printf("Scan string\n");
//printf(" [+] Input : ");
gets((char*)&AS->data[word_val]);
break;
/*=======================================================*/
/*0xDB Debugging Only*/
/*
case 0xDB :
printf("\n===Debug Information Start===\n");
printf("+ Registers :\n");
for(i=0;i<=3;i++)
printf(" R%d : 0x%.4X\n",i,Regs->GPRs[i]);
printf(" IP : 0x%.4X\n",Regs->IP);
printf(" SP : 0x%.4X\n",Regs->SP*sizeof(WORD));
printf("+ Current Stack : (Top 4 values)\n");
if(Regs->SP == sizeof(AS->stack)/sizeof(WORD))
{
printf(" The stack is empty.\n");
goto loc;
}
for(i=0;i<4;i++)
{
if(Regs->SP + i < sizeof(AS->stack)/sizeof(WORD))
printf(" SP+%d => 0x%.4X : %.4X\n",i*2,(Regs->SP + i)*2,AS->stack[Regs->SP+i]);
}
loc:
printf("+Flags Information :\n");
printf(" Flags = 0x%.2X\n",Regs->Flags);
printf(" ZF = %d\n",Regs->ZF);
printf(" CF = %d\n",Regs->CF);
printf("===Debug Information End ===\n\n");
break;
*/
/*======================================================*/
case 0xED :
//printf("Exit\n");
exit = TRUE;
break;
default :
exception:
//printf("\n==Exception : ...Exiting==\n");
exit = TRUE;
}
}
}
int main()
{
PADDRESS_SPACE AS;
PREGS Regs;
int size;
FILE* File;
//printf("DEBUG INFO :");
//printf("Allocating Address Space\n");
AS = (PADDRESS_SPACE) malloc(sizeof(ADDRESS_SPACE));
//printf("Allocating Registers\n");
Regs = (PREGS) malloc(sizeof(REGS));
//printf("Initializing Registers\n");
Regs->IP = 0;
Regs->SP = sizeof(AS->stack) / sizeof(WORD);
Regs->Flags = 0;
/*Open code and data file and read it into */
File = fopen("vm_file","rb");
if(!File)
{
printf("Found trouble opening the file");
return 0;
}
/*Check the file size*/
fseek(File,0,SEEK_END);
size = ftell(File);
if( size > sizeof(AS->data))
{
printf("File size is larger than the storage available for data and code");
return 0;
}
rewind(File);
/*Copy the file to our VM address space*/
fread(AS->data,1,size,File);
fclose(File);
//printf("Starting Execution\n");
VmLoop(AS,Regs);
_getch();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment