Simples VM ( Virtual Machine ) - PARTE 1

1. Simples VM ( Virtual Machine ) - PARTE 1

???
gokernel

(usa Linux Mint)

Enviado em 18/02/2016 - 20:08h

Olá !!!
Para quem está interessado em VM, um pequeno exemplo:

Implementei somente 8 OPCODES para melhor entender:

    OP_HALT
    OP_NOP
    OP_PUSH_LONG
    OP_PUSH_FLOAT
    OP_PUSH_VAR
    OP_SET_VAR
    OP_INC
    OP_PRINT
 


Depois implementarei: COMPARAÇÃO e LOOPS

OBS: tentei postar no ( codepad.org ) mas falhou ... depois tentarei e REeditarei este tópico.

/*
**-------------------------------------------------------------------
**
** Simples VM:
**
** COMPILE:
**   gcc vm.c -o vm -O2 -Wall
**   ou
**   g++ vm.c -o vm -O2 -Wall
**
**-------------------------------------------------------------------
*/
#include <stdio.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;
    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

};

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;

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;

        sp = stack;

  return;
    }

    if (!vm)
  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+2);
    } 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;
}

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->pos = 0;
    }
    return vm;
}

//-------------------------------------------------------------------
//###########################  gen/emit  ############################
//-------------------------------------------------------------------
//
void g_halt (VM *vm)
{
    vm->code[vm->pos++].jmp = Gtable [ OP_HALT ];
}
void g_nop (VM *vm)
{
    vm->code[vm->pos++].jmp = Gtable [ OP_NOP ];
}
void g_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+2) = value;
    vm->pos++;
}
void g_inc (VM *vm, int index)
{
    vm->code[vm->pos].jmp = Gtable [ OP_INC ];
    vm->code[vm->pos].arg.l = index;
    vm->pos++;
}
void g_print (VM *vm, int index)
{
    vm->code[vm->pos].jmp = Gtable [ OP_PRINT ];
    vm->code[vm->pos].arg.l = index;
    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 *******
        //-------------------------
        //
        g_set_var (vm, 10, 1500); // seta ( Gvar[10].arg.l ) com o valor 1500
        g_inc     (vm, 10);       // incrementa ( Gvar[10].arg.l )
        g_print   (vm, 10);       // imprime ( Gvar[10].arg.l )  ... 1501
        g_halt    (vm);           // o OPCODE mais importante para sair da VM

        vm_run (vm);  // agora executa a VM

        // aqui: falta liberar memoria ... fica para a proxima
        // ...
    }
    printf ("Saindo Com Sucesso !!!\n");
    return 0;
}

 


Lembrando que a VM funciona somente com 1 argumento para cada instrução ( OPCODE ) mas é possível funcionar com vários argumentos por instrução veja a instrução ( OP_SET_VAR ) .

T++.


  


2. Re: Simples VM ( Virtual Machine ) - PARTE 1

???
gokernel

(usa Linux Mint)

Enviado em 19/02/2016 - 20:29h


Agora um exemplo com loop:

tipo isso:

for (i = 100; i < 150; i++) printf("i: %d\n", i);


implementado 2 jumps:
OP_JMP = pulo incondicional
OP_JG = pula se for menor.

Veja aqui:
http://codepad.org/eVkCpQkC






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts