/*
**-------------------------------------------------------------------
**
** Simples VM:
**
** COMPILE 32 BITS:
** gcc vm.c -o vm -O2 -Wall -m32
** ou
** g++ vm.c -o vm -O2 -Wall -m32
**
** COMPILE 64 BITS:
** gcc vm.c -o vm -O2 -Wall -m64
** ou
** g++ vm.c -o vm -O2 -Wall -m64
**
**-------------------------------------------------------------------
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define NEXT goto *(++vm->ip)->jmp
#define GTABLE_SIZE 255
#define GVAR_SIZE 255
#define STACK_SIZE 1024
typedef struct VM VM;
typedef union VALUE VALUE;
typedef struct VAR VAR;
typedef struct OPCODE OPCODE;
typedef struct LABEL LABEL;
struct VM {
OPCODE *ip;
OPCODE *code;
LABEL *label;
LABEL *jump;
int pos;
};
union VALUE {
long l;
float f;
char *s;
void *p;
};
struct VAR {
char *name;
int type;
VALUE value;
};
struct OPCODE {
void *jmp;
VALUE arg; // argumento 1
};
struct LABEL {
char *text;
int pos;
LABEL *next;
};
enum { // opcodes:
OP_HALT = 0, // para sair da VM
OP_NOP, // no opcode
OP_PUSH_LONG, // da um "push" ... e seta o topo da pilha ( *sp ) com um valor long
OP_PUSH_FLOAT, // da um "push" ... e seta o topo da pilha ( *sp ) com um valor float
OP_PUSH_VAR, // da um "push" ... e seta o topo da pilha ( *sp ) com uma variavel
//-------------------------------------------
// OBS: Essa instrucao utiliza 2 argumentos:
// 1: short = o index de Gvar [ ]
// 2: long = o valor da variavel
//-------------------------------------------
OP_SET_VAR, // seta um valor de uma variavel ... somente tipo long
OP_INC, // incrementa uma variavel ( Gvar[] )
OP_PRINT, // imprime o valor de uma variavel
OP_CMP_LONG, // compara 2 valores tipo long , seta (flag_long) e da 2 "pop"
OP_JMP, // pula para um opcode
OP_JG
};
void *Gtable [ GTABLE_SIZE ];
VAR Gvar [ GVAR_SIZE ];
VALUE stack [ STACK_SIZE ]; // a pilha base
VALUE *sp; // o topo da pilha
int flag_long;
float flag_float;
int count;
void vm_run (VM *vm)
{
if (Gtable[0] == NULL) {
Gtable [ OP_HALT ] = && op_halt;
Gtable [ OP_NOP ] = && op_nop;
Gtable [ OP_PUSH_LONG ] = && op_push_long;
Gtable [ OP_PUSH_FLOAT ] = && op_push_float;
Gtable [ OP_PUSH_VAR ] = && op_push_var;
Gtable [ OP_SET_VAR ] = && op_set_var;
Gtable [ OP_INC ] = && op_inc;
Gtable [ OP_PRINT ] = && op_print;
Gtable [ OP_CMP_LONG ] = && op_cmp_long;
Gtable [ OP_JMP ] = && op_jmp;
Gtable [ OP_JG ] = && op_jg;
sp = stack;
return;
}
if (vm==NULL)
return;
vm->ip = vm->code;
goto *vm->ip->jmp; // pula para a primeira "inctrucao"
//---------------------------------------------
//########## OPCODES implementacao ##########
//---------------------------------------------
//
op_halt:
printf ("Instrucao OP_HALT ... saindo da VM\n");
return;
op_nop:
// sem opcode ...
NEXT;
op_push_long: {
sp++;
sp->l = vm->ip->arg.l;
} NEXT;
op_push_float: {
sp++;
sp->f = vm->ip->arg.f;
} NEXT;
op_push_var: {
sp++;
// somente tipo long por enquanto
sp->l = Gvar [ vm->ip->arg.l ].value.l;
} NEXT;
op_set_var: {
// somente tipo long por enquanto
Gvar [ *(short*)vm->ip->arg.s ].value.l = *(long*)(vm->ip->arg.s+sizeof(short));
} NEXT;
op_inc: {
// somente tipo long por enquanto
Gvar [ vm->ip->arg.l ].value.l++;
} NEXT;
op_print: {
// somente tipo long por enquanto
printf ("Gvar[ %ld ] = %ld\n", vm->ip->arg.l, Gvar [ vm->ip->arg.l ].value.l);
} NEXT;
op_cmp_long: {
flag_long = (sp-1)->l - (sp)->l;
sp -= 2; // pop 2
} NEXT;
op_jmp: {
goto *(vm->ip = vm->code + vm->ip->arg.l)->jmp;
}
op_jg: {
if (flag_long > 0) { goto *(vm->ip = vm->code + vm->ip->arg.l)->jmp; }
} NEXT;
}//: vm_run ()
VM *vm_new (int size)
{
VM *vm = (VM*) malloc (sizeof(VM));
if (vm) {
if ( (vm->code = (OPCODE*) malloc (sizeof(OPCODE)*size)) == NULL)
return NULL;
vm->ip = vm->code;
vm->label = NULL;
vm->jump = NULL;
vm->pos = 0;
}
return vm;
}
void vm_label (VM *vm, char *text)
{
LABEL *n;
if (!text)
return;
// verifica se ja existe ... entao sai
n = vm->label;
while (n) {
if (!strcmp(n->text, text)) {
printf ("Label ja existe(%s)\n", text);
return;
}
n = n->next;
}
n = (LABEL*) malloc (sizeof(LABEL));
if (n && text) {
n->text = strdup (text);
n->pos = vm->pos;
// add on top
n->next = vm->label;
vm->label = n;
}
}
void vm_new_jump (VM *vm, char *text)
{
LABEL *n = (LABEL*) malloc (sizeof(LABEL));
if (n && text) {
n->text = strdup (text);
n->pos = vm->pos;
// add on top
n->next = vm->jump;
vm->jump = n;
}
}
void vm_resolve_jump (VM *vm)
{
LABEL *label, *jump;
label = vm->label;
while (label) {
jump = vm->jump;
while (jump) {
if (!strcmp(label->text, jump->text)) {
// muda aqui:
vm->code[ jump->pos ].arg.l = label->pos;
}
jump = jump->next;
}
label = label->next;
}
}
void vm_free (VM *vm)
{
int i;
LABEL *temp;
// libera: o "OPCODE"(OP_SET_VAR) que eh um string
for (i=0;i<vm->pos;i++)
if (vm->code[i].jmp == Gtable [OP_SET_VAR] && vm->code[i].arg.s != NULL) {
free (vm->code[i].arg.s);
vm->code[i].arg.s = NULL;
}
// libera: o label
while (vm->label != NULL) {
temp = vm->label->next;
if (vm->label->text)
free(vm->label->text);
free (vm->label);
vm->label = temp;
}
// libera: o jump
while (vm->jump != NULL) {
temp = vm->jump->next;
if (vm->jump->text)
free(vm->jump->text);
free (vm->jump);
vm->jump = temp;
}
free (vm->code);
free (vm);
}
//-------------------------------------------------------------------
//########################### gen/emit ############################
//-------------------------------------------------------------------
//
void gen_halt (VM *vm)
{
vm->code[vm->pos++].jmp = Gtable [ OP_HALT ];
}
void gen_nop (VM *vm)
{
vm->code[vm->pos++].jmp = Gtable [ OP_NOP ];
}
void gen_push_long (VM *vm, long value)
{
vm->code[vm->pos].jmp = Gtable [ OP_PUSH_LONG ];
vm->code[vm->pos].arg.l = value;
vm->pos++;
}
void gen_push_float (VM *vm, float value)
{
vm->code[vm->pos].jmp = Gtable [ OP_PUSH_FLOAT ];
vm->code[vm->pos].arg.f = value;
vm->pos++;
}
void gen_push_var (VM *vm, int index)
{
vm->code[vm->pos].jmp = Gtable [ OP_PUSH_VAR ];
vm->code[vm->pos].arg.l = index;
vm->pos++;
}
void gen_set_var (VM *vm, short index, long value)
{
vm->code[vm->pos].jmp = Gtable [ OP_SET_VAR ];
vm->code[vm->pos].arg.s = (char*)malloc ((sizeof(short)+sizeof(long))+1);
*(short*)(vm->code[vm->pos].arg.s) = index;
*(long*)(vm->code[vm->pos].arg.s+sizeof(short)) = value;
vm->pos++;
}
void gen_inc (VM *vm, int index)
{
vm->code[vm->pos].jmp = Gtable [ OP_INC ];
vm->code[vm->pos].arg.l = index;
vm->pos++;
}
void gen_print (VM *vm, int index)
{
vm->code[vm->pos].jmp = Gtable [ OP_PRINT ];
vm->code[vm->pos].arg.l = index;
vm->pos++;
}
void gen_cmp_long (VM *vm)
{
vm->code[vm->pos].jmp = Gtable [ OP_CMP_LONG ];
vm->pos++;
}
void gen_jmp (VM *vm, char *text)
{
vm->code[vm->pos].jmp = Gtable [ OP_JMP ];
vm->code[vm->pos].arg.l = vm->pos;
vm_new_jump (vm, text);
vm->pos++;
}
void gen_jg (VM *vm, char *text)
{
vm->code[vm->pos].jmp = Gtable [ OP_JG ];
vm->code[vm->pos].arg.l = vm->pos;
vm_new_jump (vm, text);
vm->pos++;
}
int main (int argc, char *argv[])
{
VM *vm;
//-----------------------------------------
// cria uma VM com maximo de 100 instrucoes
//-----------------------------------------
//
if ((vm = vm_new(100)) != NULL) {
vm_run (NULL); // setup Gtable [ ]
//-------------------------
//####### gen/emit #######
//-------------------------
//
// CODIGO SIMILAR C:
//
// for (i = 100; i < 150; i++) printf("i: %d\n", i);
//
gen_set_var (vm, 10, 100); // seta ( Gvar[10].arg.l ) com o valor 100
vm_label (vm, "loop_inicio");
gen_push_var (vm, 10);
gen_push_long (vm, 150-1);
gen_cmp_long (vm); // compara 2 valores
gen_jg (vm, "loop_fim"); // pula para o fim do loop se ( flag_long > 0 )
//######### o bloco do loop: ########
gen_print (vm, 10); // imprime ( Gvar[10].arg.l )
gen_inc (vm, 10); // incrementa ( Gvar[10].arg.l )
//###################################
gen_jmp (vm, "loop_inicio"); // pula para o inicio do loop.
vm_label (vm, "loop_fim");
gen_halt (vm); // o OPCODE mais importante para sair da VM
//#########################
vm_resolve_jump (vm); // muda as posicoes dos "jumps"
vm_run (vm); // agora executa a VM
vm_free (vm); // libera a memoria
}
printf ("Saindo Com Sucesso !!!n");
return 0;
}