MIPS Virtual Machine
VÃdeo
Pequeño vÃdeo en el que se clona el código del repositorio, se compila y se muestra el funcionamiento del proyecto.
Terminando
Bueno, han sido unas duras semanas dando los últimos retoques al proyecto, no tuve tiempo para ir poniendo los progresos en el blog asà que los resumiré rápidamente.
- Todas las instrucciones de la ALU soportadas.
- Creación de la carpeta examples con varios ejemplos y un Makefile para su compilación.
- Corrección de bugs y cambios menores.
Al final, no tuve tiempo de añadir las instrucciones de la FPU ni para soportar la pila, pero no importa, cuando term[...]
Instrucciones lw, lb y visualización de la memoria del programa
En este último commit conseguà cargar datos de memoria con las instrucciones lb (Load Byte) y lw (Load Word). Hice una pequeña modificación y ahora nos muestra la memoria fÃsica de nuestro programa al terminar su ejecución e implementé la syscall que imprime una cadena de caracteres en pantalla.
Voy a poner un ejemplo del funcionamiento de lb y lw:
.data
N: .word 4
arrayA: .float 1.0, 2.0, 3.0, 4.0
head1: .asciiz “Hello”
N2: .word 9
Direcciones de memoria
Estuve trabajando bastante y ahora consigo distinguir secciones .text y .data. La cabecera .data en el código fuente siempre tiene que ir encima de la sección .text.
Almaceno todos los tipos de datos y el contenido en un buffer, luego el ensamblador añade una sección al ejecutable en formato elf para escribir toda la sección .data.
La máquina virtual reserva memoria para el bytecode de nuestro programa y para la memoria de este. Por ahora sólo funciona la instrucción lui[...]
¡Implementadas todas las instrucciones de salto!
Hoy añadà soporte para las siguientes instrucciones: jalr, jr, mthi, mtlo, bgezal, bltzal y jal.
Con este commit, quedan listas las instrucciones de salto. Ahora voy a empezar a trabajar con las secciones en el código fuente, tengo que poder distinguir en el ensamblador las secciones .text y .data. Una vez hecho esto, voy a establecer que la sección .data vaya al principio del código fuente para declarar cadenas de texto, números, arrays, etc… A continuación irá la secciÃ[...]
Situación actual
El motivo de este post es para comentar la situación actual del proyecto e ideas futuras.
Situación actual:
El ensamblador es capaz de generar archivos binarios en formato elf para el juego de instrucciones de la arquitectura MIPS R3000, a partir de un archivo de código fuente en ensamblador. TodavÃa no soporta instrucciones de carga y modificación de variables alojadas en memoria, ni soporta instrucciones de la FPU.
La Máquina Virtual es capaz de obtener el código[...]
Formato elf
Estuve estudiando el formato elf y conseguà realizar progresos. Ahora el ensamblador genera ficheros elf con el código máquina MIPS en una sección .text y la máquina virtual carga esa sección y la interpreta.
Los archivos elf tienen 4 secciones principales:
1.- La cabecera elf.
Está formada por 52 bytes y contiene una firma al principio del archivo compuesta por los bytes 0x7F, ‘E’, ‘L’ y ‘F’. A continuación tie[...]
Más instrucciones soportadas
Estuve algo liado con las prácticas de la Universidad y no pude dedicarle mucho tiempo al proyecto. Estos dÃas tengo tiempo libre, a ver si consigo terminar de implementar todas las instrucciones del R3000.
En la última actualización añadà soporte para las instrucciones: bgtz, blez, bltz, xori, sub y subu. Además corregà algunos pequeños fallos.
Me quedan por implementar las instrucciones: jr, sra, srl, srlv, bgezal, bgltzal, lb, lui, lw, sb, sw y jal. Las instruccione[...]
Saltos relativos con bne y beq
Por fin, tras muchas horas picando código y rompiéndome la cabeza he finalizado la implementación de estas instrucciones, tanto en el ensamblador como en la máquina virtual.
Para probar su funcionamiento he diseñado un pequeño programa en ensamblador MIPS.
inicio:
addi $v0, $v0, 5 # $v0 = 5
syscall # read_integer en $v0
addi $t0, $t0, 2 # $t0 = 2
div $v0, $t0 # $LO = $v0 / $t0; $HI = $v0 % $t0
mfhi $t0 # $t0 = $v0 % $t0
beq $t0, $zero, p[...]
Saltos j
Después de varias horas programando, conseguà el primer salto. El ensamblador y la máquina virtual sólo soportan la instrucción de salto j, el ensamblador necesita que la etiqueta de salto esté antes que la instrucción j (de momento). Tengo que terminar la lista para buscar las etiquetas posteriores a la instrucción que se está procesando.
En este código:
inicio:
addi $t0, $t0, 1 #t0 = t0 + 1
j inicio
Tenemos 2 instru[...]
Detección de saltos
Hice modificaciones en el ensamblador, utilizando expresiones regulares, con las funciones de la librerÃa <regex.h> dentro del estándar POSIX, consigo detectar las etiquetas de salto en el código fuente.
En el archivo saltos.c cree una serie de funciones para trabajar con listas enlazadas, de esta forma voy a ir almacenando las etiquetas de salto detectadas en el código fuente y su posición en memoria.
También ordené un poco el código y todas las f[...]
Comentarios en los programas
Ahora el ensamblador ignora los comentarios y detecta los errores más frecuentes en las lÃneas. TodavÃa falta mucho por hacer en este sentido, pero el soporte de comentarios hace que el proceso sea más llevadero.
Código de prueba:
#Programa 3
addi $s0, $s0, 4 #s0 = 4
#Esto es un comentario
#Los comentarios empiezan por #
addi $a0, $a0, 14 #a0 = 14
esto da un error
addi #wdqwdqwddw
qwdqw qdwqdq qwdqwd qwdqwd qwdq
Si ensamblamos el c[...]
Soporte de más instrucciones y syscalls
Añadà soporte de depuración utilizando el programa Valgrind. Tuve algún que otro problemilla de memoria y este me ayudó a solucionarlo. Modifiqué los Makefiles para introducir la opción “debug”.
La Máquina Virtual soporta nuevas instrucciones y soporta las siguientes syscalls:
print_integer: $a0 = entero a imprimir. Código en $v0 = 1.
read_integer: $v0 = entero leÃdo. Código en $v0 = 5.
exit: Código en $v0 = 10.
print_char: $a0 = carÃ[...]
Primeras instrucciones soportadas en la MV
Una vez acabados los exámenes tengo tiempo para continuar con el proyecto. Estuve trabajando en la Máquina Virtual y ahora soporta un par de instrucciones: add y addi. El núcleo de la Máquina Virtual es un simple bucle:
void execute()
{
uint32_t opcode = 0;
while (cpu.PC < cpu.program_size && cpu.PC >= 0)
{
opcode = cpu.byteCode[cpu.PC >> 2]; //Dividimos entre 4
printf(“Opcode: %.8x\n”, opcode);
interpretarI[...]