Set de instrucciones:
0x00 | halt | detiene el cpu |
0x01 | lit | cargar en acumulador un valor inmediato |
0x02 | load | cargar en acumulador valor de un registro |
0x03 | store | almacenar valor acumulador en un registro |
0x04 | inc | incrementar por 1 el acumulador |
0x05 | dec | decrementar por 1 el acumulador |
0x06 | putl | poner en memoria un valor inmediato |
0x07 | putr | poner en registro un valor de memoria |
0x08 | geta | cargar en acumulador un valor de memoria |
0x09 | getr | cargar en registro un valor de memoria |
0x0A | cmpl | comparar acumulador con valor inmediado. establece flag |
0x0B | cmpr | comparar acumulador con valor registro. establece flag |
0x0C | jmp | saltar a direccion de codigo (inicio == 0x00) |
0x0D | jmpl | saltar a direccion de codigo si flag == 1 ( < ) |
0x0E | jmpe | saltar a direccion de codigo si flag == 2 ( = ) |
0x0F | jmpg | saltar a direccion de codigo si flag == 3 ( > ) |
Especificaciones técnicas:
El CPU consta de 4 registros de usuario, un registro flag y el registro acumulador. La máquina dispone de memoria a modo de pila (tengo otra versión con memoria dinámica, en vez de usar una pila); también se dispone de una memoria de sólo lectura para el código.
main.c:
Código
/*! Miky Gonzalez Virtual Machine Contact: mikygonzalez95@gmail.com Miky Gonzalez Virtual Machine por Miky Gonzalez se encuentra bajo una Licencia Creative Commons Atribución-NoComercial-CompartirIgual 3.0 Unported. Más información: http://creativecommons.org/licenses/by-nc-sa/3.0/deed.es */ #include <stdio.h> #include <string.h> #include <stdlib.h> //#include <inttypes.h> //#include <time.h> #include "pila.h" /* MVM - Estructura de la MV */ typedef struct CPU { short int registro[4]; short int acumulador; unsigned int inst_pointer; unsigned char *memoria_codigo; unsigned char flag; pila_ pila; } cpu_t; unsigned int crear_cpu(cpu_t *cpu, unsigned char *memoria_codigo) { if(!memoria_codigo || !cpu) return 0; //memset(cpu->registro, 0, sizeof(cpu->registro) / sizeof(*cpu->registro)); unsigned short int i = 0; for(; i < 4; i++) cpu->registro[i] = 0; cpu->acumulador = 0; cpu->inst_pointer = 0; cpu->memoria_codigo = memoria_codigo; cpu->flag = 0; cpu->pila = NULL; return 1; } void ejecutar_cpu(cpu_t *cpu) { if(!cpu) { return; } while(1) { switch(cpu->memoria_codigo[cpu->inst_pointer]) { case 0x00: /*! halt */ return; case 0x01: /*! lit */ cpu->acumulador = cpu->memoria_codigo[++cpu->inst_pointer]; break; case 0x02: /*! load */ if(cpu->memoria_codigo[++cpu->inst_pointer] > 0x03) { return; } cpu->acumulador = cpu->registro[cpu->memoria_codigo[cpu->inst_pointer]]; break; case 0x03: /*! store */ if(cpu->memoria_codigo[++cpu->inst_pointer] > 0x03) { return; } cpu->registro[cpu->memoria_codigo[cpu->inst_pointer]] = cpu->acumulador; break; case 0x04: /*! inc */ cpu->acumulador++; break; case 0x05: /*! dec */ cpu->acumulador--; break; case 0x06: /*! pushl */ push(&cpu->pila, cpu->memoria_codigo[++cpu->inst_pointer]); break; case 0x07: /*! pushr */ if(cpu->memoria_codigo[++cpu->inst_pointer] > 0x03) { return; } push(&cpu->pila, cpu->registro[cpu->memoria_codigo[cpu->inst_pointer]]); break; case 0x08: /*! pop */ if(pila_vacia(&cpu->pila)) { return; } pop(&cpu->pila); break; case 0x09: /*! loadt */ if(pila_vacia(&cpu->pila)) { return; } cpu->acumulador = pop(&cpu->pila); break; case 0x0A: /*! cmpl */ if(cpu->acumulador < cpu->memoria_codigo[++cpu->inst_pointer]) cpu->flag = 1; else if(cpu->acumulador == cpu->memoria_codigo[cpu->inst_pointer]) cpu->flag = 2; else if(cpu->acumulador > cpu->memoria_codigo[cpu->inst_pointer]) cpu->flag = 3; break; case 0x0B: /*! cmpr */ if(cpu->acumulador < cpu->registro[cpu->memoria_codigo[++cpu->inst_pointer]]) cpu->flag = 1; else if(cpu->acumulador == cpu->registro[cpu->memoria_codigo[cpu->inst_pointer]]) cpu->flag = 2; else if(cpu->acumulador > cpu->registro[cpu->memoria_codigo[cpu->inst_pointer]]) cpu->flag = 3; break; case 0x0C: /*! jmp */ cpu->inst_pointer = cpu->memoria_codigo[cpu->inst_pointer] - 1; break; case 0x0D: /*! jmpl */ if(cpu->flag == 1) cpu->inst_pointer = cpu->memoria_codigo[cpu->inst_pointer] - 1; break; case 0x0E: /*! jmpe */ if(cpu->flag == 2) cpu->inst_pointer = cpu->memoria_codigo[cpu->inst_pointer] - 1; break; case 0x0F: /*! jmpg */ if(cpu->flag == 3) cpu->inst_pointer = cpu->memoria_codigo[cpu->inst_pointer] - 1; break; default: return; } cpu->inst_pointer++; printf("r0: %d\tr1: %d\tr2: %d\tr3: %d\tac: %d\n", cpu->registro[0], cpu->registro[1], cpu->registro[2], cpu->registro[3], cpu->acumulador); contar_nodos(cpu->pila); } } int main(int argc, char *argv[]) { /* Inicializar datos y comprobar argumentos */ if(argc < 2) { return 0; } if(!codigo_archivo) { return 0; } /* Calcular tamaño (volumen) del código */ unsigned int codigo_volumen; if(!codigo_volumen) { return 0; } /* Reservar el espacio necesario para almacenar * el código en memoria. Si existe, copiarlo. */ return 0; /* Crear CPU e inicializar los datos */ cpu_t cpu; if(!crear_cpu(&cpu, codigo)) { return 0; } /* Ejecutar CPU hasta instrucción fin o error */ ejecutar_cpu(&cpu); printf("r0: %d\tr1: %d\tr2: %d\tr3: %d\tac: %d\n", cpu.registro[0], cpu.registro[1], cpu.registro[2], cpu.registro[3], cpu.acumulador); contar_nodos(cpu.pila); eliminar_pila(&cpu.pila); return 0; }
pila.c:
Código
/*! Miky Gonzalez Virtual Machine Contact: mikygonzalez95@gmail.com Miky Gonzalez Virtual Machine por Miky Gonzalez se encuentra bajo una Licencia Creative Commons Atribución-NoComercial-CompartirIgual 3.0 Unported. Más información: http://creativecommons.org/licenses/by-nc-sa/3.0/deed.es */ /*! Implementación de una pila para memoria de datos MVM */ #include <stdio.h> #include <stdlib.h> #include "pila.h" void push(pila_ *pila, int valor) { if(nodo != NULL) { nodo->valor = valor; nodo->nodo_siguiente = *pila; *pila = nodo; } } int pop(pila_ *pila) { nodo_ nodo; int valor = 0; nodo = *pila; valor = (*pila)->valor; *pila = (*pila)->nodo_siguiente; return valor; } //#define pila_vacia(a) *a == NULL ? 1:0; unsigned short int pila_vacia(pila_ *pila) { return (*pila == NULL ? 1 : 0); } unsigned short int contar_nodos(nodo_ nodo) { if(nodo == NULL) return 0; unsigned short int nodos = 0; while(nodo != NULL) { nodos++; nodo = nodo->nodo_siguiente; } return nodos; } // #define eliminar_pila(a) {if(pila_vacia(a))return;while(!pila_vacia(a))pop(a);} void eliminar_pila(pila_ *pila) { if(pila_vacia(pila)) return; while(!pila_vacia(pila)) pop(pila); return; }
pila.h:
Código
/*! Miky Gonzalez Virtual Machine Contact: mikygonzalez95@gmail.com Miky Gonzalez Virtual Machine por Miky Gonzalez se encuentra bajo una Licencia Creative Commons Atribución-NoComercial-CompartirIgual 3.0 Unported. Más información: http://creativecommons.org/licenses/by-nc-sa/3.0/deed.es */ #ifndef MVM_PILA #define MVM_PILA /* Estructura de nodos de pila */ typedef struct NODO { int valor; struct NODO *nodo_siguiente; } nodo_t; typedef nodo_t *nodo_; typedef nodo_t *pila_; void push(pila_ *pila, int valor); int pop(pila_ *pila); unsigned short int pila_vacia(pila_ *pila); unsigned short int contar_nodos(nodo_ nodo); void eliminar_pila(pila_ *pila); #endif
Actualmente no dispongo de tiempo para terminar el ensamblador, así que tendrán que crear los ejecutables editandolos hexadecimalmente, hice un programa para facilitarme el trabajo de prueba:
Código
#include <stdio.h> int main(int argc, char **argv) { char codigo[] = {0x00}; FILE *fd; if(!fd) return 0; return 0; }
Algunos ejemplos ya programados, con sus respectivos opcodes:
Código:
#========================================
# CALCULAR RESTA DE DOS NUMEROS
#========================================
# Usando 2 registros se pueden hacer
# funciones de resta de números.
# resultado: r0
#========================================
# Inicializacion de los datos
lit 25 # ac: 25
store r0 # r0: 25
lit 17 # ac: 17
store r1 # r1: 17
lit 0 # ac: 0
# Bucles
load r1 # ac: r1
dec # ac: ac--
store r1 # r1: ac
load r0 # ac: r0
dec # ac: ac--
store r0 # r0: ac
lit 1 # ac: 1
cmpr r1 # comparar ac & r1
jmpg 16 # ac > r1 --> jmp 16
jmp 6 # jmp 6
halt # stop
# 0x01 25 0x03 0x00 0x01 17 0x03 0x01 0x01 0x00 0x02 0x01 0x05
# 0x03 0x01 0x02 0x00 0x05 0x03 0x00 0x01 0x01 0x0B 0x01 0x0F 28
# 0x0C 10 0x00
Código:
#========================================
# CALCULAR SUMA DE DOS NUMEROS
#========================================
# Usando 2 registros se pueden hacer
# funciones de suma de números.
# resultado: r0
#========================================
# Inicializacion de los datos
lit 17 # ac: 17
store r0 # r0: 17
lit 25 # ac: 25
store r1 # r1: 25
lit 0 # ac: 0
# Bucles
load r1 # ac: r1
dec # ac: ac--
store r1 # r1: ac
load r0 # ac: r0
inc # ac: ac++
store r0 # r0: ac
lit 1 # ac: 1
cmpr r1 # comparar ac & r1
jmpg 16 # ac > r1 --> jmp 16
jmp 6 # jmp 6
halt # stop
# 0x01 25 0x03 0x00 0x01 17 0x03 0x01 0x01 0x00 0x02 0x01 0x05
# 0x03 0x01 0x02 0x00 0x04 0x03 0x00 0x01 0x01 0x0B 0x01 0x0F 28
# 0x0C 10 0x00
Código:
#========================================
# CALCULAR MULTIPLICACION DE DOS NUMEROS
#========================================
# Utilizando 3 registros (incluso menos) se
# pueden hacer funciones de multiplicación
# de números.
# resultado: r2.
#========================================
# Inicialización de los datos
lit 10 # ac: 10
store r0 # r0: 10
lit 3 # ac: 3
store r1 # r1: 3
lit 0 # ac: 0
# Bucles
load r1 # ac: r1
dec # ac: ac--
store r1 # r1: ac
cmpl 0 # comparar ac & 0
jmpe 23 # ac == 0 --> jmp 23
lit 10 # ac: 10
store r0 # r0: 10
load r2 # ac: r2
inc # ac: ac++
store r2 # r2: ac
load r0 # ac: r0
dec # ac: ac--
store r0 # r0: ac
lit 0 # ac: 0
cmpr r0 # comparar ac & r0
jmpl 13 # ac < r0 --> jmp 13
jmp 6 # jmp 6
lit 0 # ac: 0
store r1 # r1: 0
halt # stop
#0x01 0x0A 0x03 0x00 0x01 0x03 0x03 0x01 0x01 0x00
#0x02 0x01 0x05 0x03 0x01 0x0A 0x00 0x0D 0x29 0x01
#0x0A 0x03 0x00 0x02 0x02 0x04 0x03 0x02 0x02 0x00
#0x05 0x03 0x00 0x01 0x00 0x0B 0x00 0x0D 23 0x0C 10
#0x01 0x00 0x03 0x01 0x00
Espero que este proyecto les guste, y si alguien puede continuarlo, espero que lo haga y tenga buenas ideas, tal vez aunque no puedo no seria malo proponerlas, nunca se sabe si volvere a continuar con la máquina virtual.
Saludos,
Miky Gonzalez