/* VM by Souhail Hammou : custom instruction set data space and stack space are customizable. Important : In calculations the VM is using unsigned values. */ #include #include #include #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; }