From 9610957f1b1bb043a768a3c7a651ae598e3181f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Luis=20Monta=C3=B1es=20Ojados?= Date: Tue, 3 Mar 2026 01:18:47 +0100 Subject: [PATCH] add turingcomplete cpu compiler --- docs/todo.md | 312 ++++++++++++++++++++++ mycpu.md | 103 ++++++++ mycpu_v2.md | 178 +++++++++++++ projects/mycpu/assign.j | 3 + projects/mycpu/functions.j | 6 + projects/mycpu/ifs.j | 12 + projects/mycpu/while.j | 4 + run.exe | Bin 66815 -> 110959 bytes src/backend/mycpu/gencode.h | 504 ++++++++++++++++++++++++++++++++++++ src/backend/mycpu/opcodes.h | 129 +++++++++ src/frontend/lexer.h | 17 +- src/frontend/parser.h | 12 +- src/main.c | 10 +- 13 files changed, 1284 insertions(+), 6 deletions(-) create mode 100644 docs/todo.md create mode 100644 mycpu.md create mode 100644 mycpu_v2.md create mode 100644 projects/mycpu/assign.j create mode 100644 projects/mycpu/functions.j create mode 100644 projects/mycpu/ifs.j create mode 100644 projects/mycpu/while.j create mode 100644 src/backend/mycpu/gencode.h create mode 100644 src/backend/mycpu/opcodes.h diff --git a/docs/todo.md b/docs/todo.md new file mode 100644 index 0000000..0069912 --- /dev/null +++ b/docs/todo.md @@ -0,0 +1,312 @@ +# TODO: Backend mycpu_v2 para j-lang + +Roadmap de implementación del generador de código para el CPU v2 de 16-bit. +El objetivo es que `gencode.h` tome el AST del frontend y produzca: +- **Texto ensamblador** legible (para debug) +- **Binario** (array de bytes para cargar en PROM) + +--- + +## Referencia rápida del CPU v2 + +``` +Instrucción: 8 bytes = [OPCODE:16][PARAM1:16][PARAM2:16][TARGET:16] +PC cuenta en words de 16-bit → instrucción N está en PC = N × 4 + +Registros libres: REG0-REG11 (12 registros, 16 bits cada uno) +Registros especiales: REG12(RAM_VAL), REG13(RAM_ADDR), REG14(PC), REG15(I/O) + +Modos de direccionamiento (codificados en el opcode): + base + 0x00 = registro, registro + base + 0x40 = inmediato, registro + base + 0x80 = registro, inmediato + base + 0xC0 = inmediato, inmediato + +RAM: + Leer: Escribir dirección en REG13 → REG12 se actualiza automáticamente + Escribir: REG12 = valor, REG13 = addr → RSTR (0x18) +``` + +### Pseudo-instrucciones útiles + +```asm +MOV #valor, REGn → ADD #valor, #0, REGn (opcode 0xC0) +MOV REGa, REGb → ADD REGa, #0, REGb (opcode 0x80) +JMP #addr → EQ #0, #0, addr (opcode 0xD0, siempre true) +NOP → ADD #0, #0, REG0 (opcode 0xC0) +``` + +--- + +## Convención de registros + +``` +REG0-REG3 → Temporales para evaluación de expresiones (expression stack) +REG4-REG5 → Auxiliares (spill de expresiones profundas) +REG6-REG11 → Libres / reserva futura (frame pointer, etc.) +REG12 → RAM VALUE (especial, no tocar directamente) +REG13 → RAM ADDR (especial, no tocar directamente) +REG14 → PC (especial) +REG15 → I/O (especial) +``` + +## Almacenamiento de variables + +Todas las variables en **RAM**. Tabla nombre→dirección en tiempo de compilación. + +```asm +; Leer variable 'x' (dirección addr) → REGn +ADD #addr, #0, REG13 ; REG13 = addr +ADD REG12, #0, REGn ; REGn = RAM[addr] (lectura automática) + +; Escribir variable 'x' (valor en REGn, dirección addr) +ADD #addr, #0, REG13 ; REG13 = addr +ADD REGn, #0, REG12 ; REG12 = valor +RSTR ; RAM[addr] = REG12 +``` + +--- + +## Fase 0: Infraestructura del emisor + +**Objetivo**: Estructuras y funciones base para emitir instrucciones. + +- [ ] Struct `Instruction` (opcode, param1, param2, target) +- [ ] Buffer de instrucciones (array dinámico donde se acumulan) +- [ ] Función `emit(opcode, p1, p2, target)` — agrega instrucción al buffer +- [ ] Tabla de variables: mapeo nombre→dirección_RAM (compilación) +- [ ] Función `lookupOrCreateVar(name)` — busca o asigna dirección RAM +- [ ] Sistema de labels/backpatching: + - [ ] `emitPlaceholder()` → emite instrucción con target=0, retorna índice + - [ ] `patchTarget(index, target)` → rellena el target de instrucción emitida + - [ ] `currentAddr()` → posición actual (nº de instrucción) +- [ ] Output ASM: recorrer buffer → texto legible con mnemonicos +- [ ] Output binario: recorrer buffer → array de bytes (8 bytes/instrucción) + +**Criterio**: Emitir instrucciones hardcoded, ver texto ASM y binario generados. + +--- + +## Fase 1: Constantes, asignaciones y print + +**Objetivo**: Compilar `x = 42` y `print x`. + +- [ ] Compilar `NODE_INT_LIT` → cargar inmediato en REG[depth] + ```asm + ADD #42, #0, REG0 ; MOV #42, REG0 + ``` +- [ ] Compilar `NODE_ASSIGN` → evaluar expr → REG0, store en RAM + ```asm + ; (resultado ya en REG0) + ADD #addr, #0, REG13 ; REG13 = dirección de variable + ADD REG0, #0, REG12 ; REG12 = valor + RSTR ; RAM[addr] = valor + ``` +- [ ] Compilar `NODE_VAR` → leer de RAM a REG[depth] + ```asm + ADD #addr, #0, REG13 ; REG13 = dirección + ADD REG12, #0, REG0 ; REG0 = RAM[addr] + ``` +- [ ] Compilar `NODE_PRINT` → evaluar expr → REG0, copiar a I/O + ```asm + ADD REG0, #0, REG15 ; OUTPUT = REG0 + ``` +- [ ] Compilar `NODE_BLOCK` → iterar y compilar cada statement + +**Test**: `simple.j` (x = 10). `print 10` → escribe 10 en REG15. + +--- + +## Fase 2: Expresiones aritméticas + +**Objetivo**: Compilar `x = 10 + 20 * 3`. + +**Estrategia**: Register depth counter. Cada sub-expresión deposita resultado en `REG[depth]`. + +- [ ] Variable `int reg_depth = 0` para tracking +- [ ] Compilar `NODE_BINOP`: + ``` + compilar left → resultado en REG[depth] + depth++ + compilar right → resultado en REG[depth] + depth-- + emit OP REG[depth], REG[depth+1], REG[depth] + ``` +- [ ] Manejar profundidad > 4 → PUSH/POP al stack (spill) +- [ ] Mapeo de operadores: + - `+` → ADD (0x00) + - `-` → SUB (0x01) + - `*` → MUL (0x02) + - `/` → DIV (0x03) + +**Test**: `sum.j`, `resta.j`. Verificar que `2 + 3 * 4` da 14. + +--- + +## Fase 3: Comparaciones y control de flujo + +**Objetivo**: Compilar `if` y `while`. + +### if + +- [ ] Compilar `NODE_IF`: + ``` + compilar condición left → REG0 + compilar condición right → REG1 + emit CONDICIONAL_INVERSO REG0, REG1, [placeholder] + compilar bloque then + patch placeholder → currentAddr() × 4 + ``` +- [ ] Mapeo de condicionales **inversos** (saltar si la condición es FALSA): + - `==` en AST → emit `NEQ` (0x11) + - `!=` en AST → emit `EQ` (0x10) + - `<` en AST → emit `GRE` (0x15) — saltar si >= + - `>` en AST → emit `LSE` (0x13) — saltar si <= + +### while + +- [ ] Compilar `NODE_WHILE`: + ``` + loop_start = currentAddr() + compilar condición left → REG0 + compilar condición right → REG1 + emit CONDICIONAL_INVERSO REG0, REG1, [placeholder_exit] + compilar cuerpo + emit EQ #0, #0, (loop_start × 4) ; JMP incondicional + patch placeholder_exit → currentAddr() × 4 + ``` + +### Recordar +- **PC = instrucción_index × 4** (cada instrucción = 4 words de 16-bit) +- El salto incondicional es `EQ #0, #0, target` (0xD0, siempre true) + +**Test**: `if.j`, `while.j`. While que cuenta de 0 a 10. + +--- + +## Fase 4: Funciones (CALL/RET) + +**Objetivo**: Compilar `fn` definitions y llamadas. + +### Convención de llamada + +``` +1. Caller pushea argumentos al stack (derecha a izquierda) +2. Caller ejecuta CALL #dirección (pushea PC+1 al stack, salta) +3. Callee popea argumentos → variables locales en RAM +4. Callee ejecuta cuerpo +5. Callee deja resultado en REG0 +6. Callee ejecuta RET (popea PC del stack, salta) +7. Caller usa REG0 como valor de retorno +``` + +### Tareas + +- [ ] Compilar `NODE_FN_DEF`: + ``` + emit JMP [placeholder_skip] ; saltar sobre el cuerpo + fn_addr = currentAddr() + para cada param (de derecha a izq): + emit POP → REGn + store REGn → RAM[param_addr] + compilar cuerpo + emit RET + patch placeholder_skip → currentAddr() × 4 + registrar fn_name → fn_addr en tabla de funciones + ``` +- [ ] Compilar `NODE_CALL`: + ``` + para cada argumento (de izq a der): + compilar argumento → REG0 + emit PUSH REG0 + emit CALL #(fn_addr × 4) + ; resultado queda en REG0 + ``` +- [ ] Compilar `NODE_RETURN`: + ``` + compilar expresión → REG0 + emit RET + ``` +- [ ] Resolver scope de variables locales: + - **Opción simple**: cada función tiene su propio rango de RAM + - **Opción avanzada**: frame pointer (registro base + offset para locales) + +**Test**: `functions.j`, `custom_fn.j`. + +--- + +## Fase 5: Strings y objetos (avanzado) + +**Objetivo**: Soportar strings, clases, campos e instancias. + +- [ ] Strings en RAM — caracteres consecutivos, variable apunta a dirección base +- [ ] Print de strings — loop: leer cada char de RAM → escribir en REG15 +- [ ] Instancias — bloque de RAM con campos, variable apunta a base +- [ ] Campos — offset fijo desde base de instancia +- [ ] Métodos — funciones con `self` (dirección de instancia) como primer arg +- [ ] Constructor — reservar espacio en RAM, llamar a `init` + +**Nota**: Requiere un allocator en runtime para reservar memoria dinámica en RAM. + +### Estrategia de allocator en runtime + +Hay dos opciones, de menor a mayor complejidad: + +**Opción A: Bump allocator (recomendado para empezar)** + +La más simple. Una dirección de RAM fija (ej: `RAM[0x00FF]`) actúa como "heap pointer" que empieza al final de las variables estáticas. Cada asignación avanza el pointer. No tiene `free`. + +```asm +; alloc(size) — size en REG1, retorna dirección en REG0 +ADD #0x00FF, #0, REG13 ; REG13 = dirección del heap_ptr +ADD REG12, #0, REG0 ; REG0 = heap_ptr actual (dirección a retornar) +ADD REG12, REG1, REG12 ; REG12 = heap_ptr + size +RSTR ; guardar nuevo heap_ptr en RAM[0x00FF] +; REG0 = dirección del bloque asignado +``` + +~4 instrucciones. Suficiente para strings literales y concatenaciones simples. + +**Opción B: Allocator con metadata (como `allocator.h`, pero en ASM del CPU v2)** + +Mismo diseño conceptual que `src/memory/allocator.h` pero implementado como rutina en ensamblador del CPU v2: +- Cada bloque en RAM: `[size:16][in_use:16][payload...]` +- Loop que recorre bloques con comparaciones + saltos (first-fit) +- `free` marca `in_use = 0` +- ~30-50 instrucciones del CPU v2 + +Solo necesario si se van a liberar strings (reasignar variables string, concatenaciones temporales). + +**Recomendación**: Empezar con bump allocator. Si más adelante se necesita `free`, implementar opción B usando el diseño de `allocator.h` como referencia conceptual. + +**Test**: `str.j`, `classes.j`. + +--- + +## Diagrama de dependencias + +``` +Fase 0 (infraestructura) + │ + └── Fase 1 (constantes, asignación, print) + │ + └── Fase 2 (aritmética) + │ + └── Fase 3 (if/while) + │ + └── Fase 4 (funciones) + │ + └── Fase 5 (strings/objetos) +``` + +## Verificación por fase + +| Fase | Archivos de test | +|------|-----------------| +| 1 | `simple.j` | +| 2 | `sum.j`, `resta.j` | +| 3 | `if.j`, `while.j` | +| 4 | `functions.j`, `custom_fn.j` | +| 5 | `str.j`, `classes.j` | + +Comparar output generado (ASM + binario) con lo que la VM produce para la misma entrada. diff --git a/mycpu.md b/mycpu.md new file mode 100644 index 0000000..7f69618 --- /dev/null +++ b/mycpu.md @@ -0,0 +1,103 @@ +# Motivación +A partir de completar el juego "Turing Complete" y tener un CPU de 8bit con arquitectura LEG funcional, me dispongo a crear un compilador para mi lenguaje "J-LANG". + + +# Estructura del CPU + +Arquitectura: 8bit +PROM: 256 bytes +RAM: 256 bytes +STACK: 256 bytes + +## Direcciones + +0x00 REG0 +0x01 REG1 +0x02 REG2 +0x03 REG3 +0x04 REG4 | RAM VALUE +0x05 REG5 | RAM ADDR PTR +0x06 PROGRAM COUNTER +0x07 INPUT/OUTPUT + +### Registros basicos + +Desde: 0x00 +Hasta: 0x03 + +Son registros que almacenan 1 byte. + +### Registros conectados a la RAM + +Estos registros hacen de "puente" con la ram. + +0x05 REG5 es el counter de la ram. +0x04 REG4 es el valor que contiene RAM[counter(0x05)] + +### input/output + +Son 1 byte de entrada y otro de salida para interactuar con el juego "Turing Complete" + +## OPCODES + +Las instrucciones de este cpu tienen que tener un tamaño de 4 bytes cada una. +Se permiten dos modos de direccionamiento, desde un registro o modo inmediato. + +- Desde registro: Se usa el byte para indicar el registro que guarda el valor +- Inmediato: Se usa el byte como valor directamente + +La estructura de una instrucción es: +[OPCODE] [INPUT0] [INPUT1] [TARGET] + +El ultimo byte "target" indica en que registro debe guardarse el resultado de la instruccion. + +En los opcodes condicionales, el ultimo byte (TARGET) indica el valor que se escribirá en el PROGRAM_COUNTER si se cumple la condicion. + +======== ALU ======== +0x00 ADD r0 r1 t0 +0x01 SUB r0 r1 t0 +0x02 AND r0 r1 t0 +0x03 OR r0 r1 t0 +0x04 NOT r0 r1 t0 +0x05 XOR r0 r1 t0 +--------------------- +0x40 ADD r0 #1 t0 ; # significa inmediato +0x41 SUB r0 #1 t0 +0x42 AND r0 #1 t0 +0x43 OR r0 #1 t0 +0x44 NOT r0 #1 t0 +0x45 XOR r0 #1 t0 +-------------------- +0x80 ADD #0 r1 t0 +0x81 SUB #0 r1 t0 +0x82 AND #0 r1 t0 +0x83 OR #0 r1 t0 +0x84 NOT #0 r1 t0 +0x85 XOR #0 r1 t0 +-------------------- +0xC0 ADD #0 #1 t0 +0xC1 SUB #0 #1 t0 +0xC2 AND #0 #1 t0 +0xC3 OR #0 #1 t0 +0xC4 NOT #0 #1 t0 +0xC5 XOR #0 #1 t0 +====== CONDITIONAL ====== +0x30 EQ r0 r1 pc ; equal +0x31 NEQ r0 r1 pc ; not_equal +0x32 LS r0 r1 pc ; less +0x33 LSE r0 r1 pc ; less_or_equal +0x34 GR r0 r1 pc ; greater +0x35 GRE r0 r1 pc ; greater_or_equal +======== RAM ======== +0xE0 RAM_ST ?? ?? ?? ; store value in REG4 in REG5 position RAM[REG5] = REG4 +0xE1 RAM_LD ?? ?? ?? ; no es usa en realidad, la ram siempre está haciendo output en la direccion 0x04 +====== STACK ====== +0x22 PUSH r0 ?? t0 ; ?? no se usa pero debe estar, t0 sobrescribe dicha direccion a 0 +0x23 POP ?? ?? t0 +0xE2 PUSH #0 ?? t0 +0xE3 POP ?? ?? t0 +====== FUNCTIONS ====== +0x08 CALL r0 ?? ?? ; push de pc+1 en stack y setea pc al valor que contiene r0 +0x09 RET ?? ?? ?? ; pop del stack y escribe el valor en el pc +0x88 CALL #0 +0x89 RET ?? ?? ?? diff --git a/mycpu_v2.md b/mycpu_v2.md new file mode 100644 index 0000000..b397f5d --- /dev/null +++ b/mycpu_v2.md @@ -0,0 +1,178 @@ +# Especificaciones + +Arquitectura: 16bit +Tamaño de instruccion: 16bit +PROM: Ilimitado +RAM: 1kB - 20kB +STACK: 256 - Ilimitado + +# Registros + +Cada registro puede almacenar 16 bits + + +| ADDR | NAME | NOTES | +| ---- | -------- | --------- | +| 0x00 | REG0 | | +| 0x01 | REG1 | | +| 0x02 | REG2 | | +| 0x03 | REG3 | | +| 0x04 | REG4 | | +| 0x05 | REG5 | | +| 0x06 | REG6 | | +| 0x07 | REG7 | | +| 0x08 | REG8 | | +| 0x09 | REG9 | | +| 0x0A | REG10 | | +| 0x0B | REG11 | | +| 0x0C | REG12 | RAM VALUE | +| 0x0D | REG13 | RAM ADDR | +| 0x0E | PC | | +| 0x0F | IN/OUT | | + +# Opcodes + +Las instrucciones en este CPU tienen un tamaño total de 8 bytes, es decir, 4 parametros de 16bit cada uno. + +[OPCODE] [PARAM1] [PARAM2] [TARGET1] + +PARAM1 y PARAM2 soportan 2 modos de direccionamiento: +- Modo registro +- Modo inmediato + +TARGET1 indica el registro donde se guardará el resultado. + +## ALU + +| OPCODE | ADDR | PARAM1 | PARAM2 | TARGET1 | DESCRIPTION | +| ------ | ---- | ------ | ------ | ------- | ----------- | +| ADD | 0x00 | R0 | R1 | T1 | | +| SUB | 0x01 | R0 | R1 | T1 | | +| MUL | 0x02 | R0 | R1 | T1 | | +| DIV | 0x03 | R0 | R1 | T1 | | +| AND | 0x04 | R0 | R1 | T1 | | +| OR | 0x05 | R0 | R1 | T1 | | +| NOT | 0x06 | R0 | R1 | T1 | | +| NAND | 0x07 | R0 | R1 | T1 | | +| NOR | 0x08 | R0 | R1 | T1 | | +| XOR | 0x09 | R0 | R1 | T1 | | +| XNOR | 0x0A | R0 | R1 | T1 | | +| NEG | 0x0B | R0 | R1 | T1 | | +| - | 0x0C | | | | | +| - | 0x0D | | | | | +| - | 0x0E | | | | | +| - | 0x0F | | | | | +| ------ | ---- | ------ | ------ | ------- | ----------- | +| ADD | 0x40 | #0 | R1 | T1 | | +| SUB | 0x41 | #0 | R1 | T1 | | +| MUL | 0x42 | #0 | R1 | T1 | | +| DIV | 0x43 | #0 | R1 | T1 | | +| AND | 0x44 | #0 | R1 | T1 | | +| OR | 0x45 | #0 | R1 | T1 | | +| NOT | 0x46 | #0 | R1 | T1 | | +| NAND | 0x47 | #0 | R1 | T1 | | +| NOR | 0x48 | #0 | R1 | T1 | | +| XOR | 0x49 | #0 | R1 | T1 | | +| XNOR | 0x4A | #0 | R1 | T1 | | +| NEG | 0x4B | #0 | R1 | T1 | | +| - | 0x4C | | | | | +| - | 0x4D | | | | | +| - | 0x4E | | | | | +| - | 0x4F | | | | | +| ------ | ---- | ------ | ------ | ------- | ----------- | +| ADD | 0x80 | R0 | #1 | T1 | | +| SUB | 0x81 | R0 | #1 | T1 | | +| MUL | 0x82 | R0 | #1 | T1 | | +| DIV | 0x83 | R0 | #1 | T1 | | +| AND | 0x84 | R0 | #1 | T1 | | +| OR | 0x85 | R0 | #1 | T1 | | +| NOT | 0x86 | R0 | #1 | T1 | | +| NAND | 0x87 | R0 | #1 | T1 | | +| NOR | 0x88 | R0 | #1 | T1 | | +| XOR | 0x89 | R0 | #1 | T1 | | +| XNOR | 0x8A | R0 | #1 | T1 | | +| NEG | 0x8B | R0 | #1 | T1 | | +| - | 0x8C | | | | | +| - | 0x8D | | | | | +| - | 0x8E | | | | | +| - | 0x8F | | | | | +| ------ | ---- | ------ | ------ | ------- | ----------- | +| ADD | 0xC0 | #0 | #1 | T1 | | +| SUB | 0xC1 | #0 | #1 | T1 | | +| MUL | 0xC2 | #0 | #1 | T1 | | +| DIV | 0xC3 | #0 | #1 | T1 | | +| AND | 0xC4 | #0 | #1 | T1 | | +| OR | 0xC5 | #0 | #1 | T1 | | +| NOT | 0xC6 | #0 | #1 | T1 | | +| NAND | 0xC7 | #0 | #1 | T1 | | +| NOR | 0xC8 | #0 | #1 | T1 | | +| XOR | 0xC9 | #0 | #1 | T1 | | +| XNOR | 0xCA | #0 | #1 | T1 | | +| NEG | 0xCB | #0 | #1 | T1 | | +| - | 0xCC | | | | | +| - | 0xCD | | | | | +| - | 0xCE | | | | | +| - | 0xCF | | | | | + +## CONDITIONALS + +En los condicionales TARGET1 representa a la direccion del PC (Program Counter) que se saltará si se cumple la condicion. + +| OPCODE | ADDR | PARAM1 | PARAM2 | TARGET1 | DESCRIPTION | +| ------ | ---- | ------ | ------ | ------- | ----------- | +| EQ | 0x10 | R0 | R1 | T1 | equal | +| NEQ | 0x11 | R0 | R1 | T1 | not equal | +| LS | 0x12 | R0 | R1 | T1 | less | +| LSE | 0x13 | R0 | R1 | T1 | less or eq | +| GR | 0x14 | R0 | R1 | T1 | greater | +| GRE | 0x15 | R0 | R1 | T1 |greater or eq| +| | 0x16 | R0 | R1 | T1 | | +| | 0x17 | R0 | R1 | T1 | | +| ------ | ---- | ------ | ------ | ------- | ----------- | +| EQ | 0x50 | #0 | R1 | T1 | equal | +| NEQ | 0x51 | #0 | R1 | T1 | not equal | +| LS | 0x52 | #0 | R1 | T1 | less | +| LSE | 0x53 | #0 | R1 | T1 | less or eq | +| GR | 0x54 | #0 | R1 | T1 | greater | +| GRE | 0x55 | #0 | R1 | T1 |greater or eq| +| | 0x56 | #0 | R1 | T1 | | +| | 0x57 | #0 | R1 | T1 | | +| ------ | ---- | ------ | ------ | ------- | ----------- | +| EQ | 0x90 | R0 | #1 | T1 | equal | +| NEQ | 0x91 | R0 | #1 | T1 | not equal | +| LS | 0x92 | R0 | #1 | T1 | less | +| LSE | 0x93 | R0 | #1 | T1 | less or eq | +| GR | 0x94 | R0 | #1 | T1 | greater | +| GRE | 0x95 | R0 | #1 | T1 |greater or eq| +| | 0x96 | R0 | #1 | T1 | | +| | 0x97 | R0 | #1 | T1 | | +| ------ | ---- | ------ | ------ | ------- | ----------- | +| EQ | 0xD0 | #0 | #1 | T1 | equal | +| NEQ | 0xD1 | #0 | #1 | T1 | not equal | +| LS | 0xD2 | #0 | #1 | T1 | less | +| LSE | 0xD3 | #0 | #1 | T1 | less or eq | +| GR | 0xD4 | #0 | #1 | T1 | greater | +| GRE | 0xD5 | #0 | #1 | T1 |greater or eq| +| | 0xD6 | #0 | #1 | T1 | | +| | 0xD7 | #0 | #1 | T1 | | + + +## CONTROL UNIT + +| OPCODE | ADDR | PARAM1 | PARAM2 | TARGET1 | DESCRIPTION | +| ------ | ---- | ------ | ------ | ------- | ----------- | +| RSTR | 0x18 | -- | -- | - | | +| PUSH | 0x19 | R1 | -- | - | | +| POP | 0x1A | -- | -- | T1 | | +| ------ | ---- | ------ | ------ | ------- | ----------- | +| PUSH | 0x59 | #1 | -- | - | | + +## FUNCTIONS + +| OPCODE | ADDR | PARAM1 | PARAM2 | TARGET1 | DESCRIPTION | +| ------ | ---- | ------ | ------ | ------- | ----------- | +| CALL | 0x20 | R1 | -- | - | | +| RET | 0x21 | -- | -- | - | | +| HALT | 0x22 | -- | -- | - | | +| ------ | ---- | ------ | ------ | ------- | ----------- | +| CALL | 0x60 | #1 | -- | - | | \ No newline at end of file diff --git a/projects/mycpu/assign.j b/projects/mycpu/assign.j new file mode 100644 index 0000000..005e579 --- /dev/null +++ b/projects/mycpu/assign.j @@ -0,0 +1,3 @@ +x = 10 +y = 512 +z = x + y \ No newline at end of file diff --git a/projects/mycpu/functions.j b/projects/mycpu/functions.j new file mode 100644 index 0000000..7e33c25 --- /dev/null +++ b/projects/mycpu/functions.j @@ -0,0 +1,6 @@ +g = 2 + +fn suma(x, y): + return x + y + +x = suma(5, 2) - g + 1 \ No newline at end of file diff --git a/projects/mycpu/ifs.j b/projects/mycpu/ifs.j new file mode 100644 index 0000000..0966c10 --- /dev/null +++ b/projects/mycpu/ifs.j @@ -0,0 +1,12 @@ +counter = 0 + +fn inc(): + counter = counter + 1 + +fn main(): + if counter < 30: + inc() + main() + + +main() \ No newline at end of file diff --git a/projects/mycpu/while.j b/projects/mycpu/while.j new file mode 100644 index 0000000..ca8988f --- /dev/null +++ b/projects/mycpu/while.j @@ -0,0 +1,4 @@ +counter = 0 + +while counter < 65000: + counter = counter + 1 diff --git a/run.exe b/run.exe index cb1ae3770597c205238f0322e03e56b4676deb19..6c86f8af756fe149e7af681931cc0052105a0b35 100644 GIT binary patch literal 110959 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 Wj^Y7QSy-S~rYOP#02$77sQDaVQ>+iGN6NOJ7- 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 UW!X)dWram_offset; + + // Increase offset + chunk->ram_offset += size; + + return current_addr; +} + +short cpu_emit(CPUChunk *chunk, CPUInstruction instr) { + chunk->code[chunk->code_count++] = instr; + return chunk->code_count - 1; +} + +CPUInstruction cpu_make_instruction(CPUOpCode op) { + CPUInstruction instr = {0}; + instr.op = op; + return instr; +} + +unsigned short cpu_find_name(CPUChunk *chunk, char *name) { + for (int i = 0; i < chunk->name_count; i++) { + if (strcmp(chunk->names[i], name) == 0) { + return chunk->name_addr[i]; + } + } + + return CPU_NOT_FOUND; +} + +unsigned short cpu_add_name(CPUChunk *chunk, char *name) { + for (int i = 0; i < chunk->name_count; i++) { + if (strcmp(chunk->names[i], name) == 0) { + return chunk->name_addr[i]; + } + } + + chunk->names[chunk->name_count++] = name; + + // Asignar hueco en la ram + chunk->name_addr[chunk->name_count - 1] = cpu_malloc(chunk, 1); + + return chunk->name_addr[chunk->name_count - 1]; +} + +void cpu_comment(CPUChunk *chunk, char *comment) { + unsigned short pc = chunk->code_count; + // Get comment count for pc + int total_comments = chunk->comments_per_code[pc]; + chunk->comments_per_code[pc] += 1; + + if (total_comments == 0) { + // Allocate space for comments + chunk->comments[pc] = malloc(sizeof(char *) * 64); + } + + chunk->comments[pc][total_comments] = comment; +} + +short cpu_compile_node(CPUChunk *chunk, ASTNode *node, int depth) { + if (depth > 12) { + printf("error: max register depth reached\n"); + exit(1); + } + + switch (node->type) { + case NODE_INT_LIT: { + // Mostrar comentario + char comment[64]; + snprintf(comment, sizeof(comment), "NODE_INT_LIT %d", node->data.int_val); + cpu_comment(chunk, strdup(comment)); + + // Cargar inmediato en REG[depth] + CPUInstruction instr = cpu_make_instruction(II_ADD); + instr.param1 = node->data.int_val; + instr.param2 = 0; + instr.target = depth; // REG[depth] + cpu_emit(chunk, instr); + break; + } + case NODE_VAR: { + // Leer de ram a REG[depth] + + // Obtener addr de la variable de la tabla o crearla + unsigned short var_addr; + if (chunk->current_fn) { + char mangled[128]; + snprintf(mangled, sizeof(mangled), "%s.%s", chunk->current_fn, + node->data.string_val); + + // Primero buscar variable local de la funcion + var_addr = cpu_find_name(chunk, strdup(mangled)); + + if (var_addr == CPU_NOT_FOUND) { + // Si no existe buscamos la variable global + var_addr = cpu_add_name(chunk, node->data.string_val); + } + } else { + var_addr = cpu_add_name(chunk, node->data.string_val); + } + + // Add comment + cpu_comment(chunk, "REG13 = var_addr"); + + // REG13 = var_addr + CPUInstruction set_addr = cpu_make_instruction(II_ADD); + set_addr.param1 = var_addr; + set_addr.param2 = 0; + set_addr.target = 0x0D; + cpu_emit(chunk, set_addr); + + // REG12 -> REG[DEPTH] + CPUInstruction mov_val = cpu_make_instruction(RI_ADD); + mov_val.param1 = 0x0C; + mov_val.param2 = 0; + mov_val.target = depth; + cpu_emit(chunk, mov_val); + + break; + } + case NODE_ASSIGN: { + + char comment[64]; + snprintf(comment, sizeof(comment), "ASSIGN %s", node->data.assign.name); + cpu_comment(chunk, strdup(comment)); + + // 1. Compilar la expresion, el resultado va a REG[depth] + cpu_compile_node(chunk, node->data.assign.value, depth); + + // 2. Obtener direccion ram de la variable + unsigned short addr; + if (chunk->current_fn) { + char mangled[128]; + snprintf(mangled, sizeof(mangled), "%s.%s", chunk->current_fn, + node->data.assign.name); + addr = cpu_find_name(chunk, strdup(mangled)); + if (addr == CPU_NOT_FOUND) { + addr = cpu_add_name(chunk, node->data.assign.name); + } + } else { + addr = cpu_add_name(chunk, node->data.assign.name); + } + + // 3. REG13 = addr + CPUInstruction set_addr = cpu_make_instruction(II_ADD); + set_addr.param1 = addr; + set_addr.param2 = 0; + set_addr.target = 0x0D; + cpu_emit(chunk, set_addr); + + // 4. REG12 = REG[depth] + CPUInstruction set_value = cpu_make_instruction(RI_ADD); + set_value.param1 = depth; // REG[depth] + set_value.param2 = 0; + set_value.target = 0x0C; + cpu_emit(chunk, set_value); + + // 5. RSTR -> RAM[REG13] = REG12 -> STORE RAM + cpu_emit(chunk, cpu_make_instruction(RR_STR)); + break; + } + case NODE_CALL: { + char comment[64]; + snprintf(comment, sizeof(comment), "NODE_CALL %s", node->data.call.name); + cpu_comment(chunk, strdup(comment)); + + // Pushear argumentos al stack + for (int i = 0; i < node->data.call.arg_count; i++) { + cpu_compile_node(chunk, node->data.call.args[i], depth); + + // push reg[depth] + cpu_comment(chunk, "RI_PUSH"); + CPUInstruction push_arg = cpu_make_instruction(RI_PUSH); + push_arg.param1 = depth; + push_arg.target = + 0xff; // si se deja en 0, el push sobrescribe ese registro a 0 + cpu_emit(chunk, push_arg); + } + + // Buscar funcion en la tabla de funciones + unsigned short fn_addr = CPU_NOT_FOUND; + for (int i = 0; i < chunk->func_count; i++) { + if (strcmp(chunk->functions[i].name, node->data.call.name) == 0) { + fn_addr = chunk->functions[i].entry_point; + } + } + + if (fn_addr == CPU_NOT_FOUND) { + printf("error: funcion '%s' no definida\n", node->data.call.name); + exit(1); + } + + cpu_comment(chunk, "IR_CALL"); + CPUInstruction call_fn = cpu_make_instruction(IR_CALL); + call_fn.param1 = fn_addr * 4; + cpu_emit(chunk, call_fn); + + if (depth != 0) { + CPUInstruction mov = cpu_make_instruction(RI_ADD); + mov.param1 = 0; + mov.param2 = 0; + mov.target = depth; + cpu_emit(chunk, mov); + } + break; + } + case NODE_BLOCK: { + // Iterar statments y compilar + for (int i = 0; i < node->data.block.count; i++) { + cpu_compile_node(chunk, node->data.block.stmts[i], depth); + } + break; + } + case NODE_RETURN: { + if (node->data.ret.value) { + // Por convencion el resultado va a REG0 + cpu_compile_node(chunk, node->data.ret.value, 0); + } + + CPUInstruction ret = cpu_make_instruction(RR_RET); + ret.target = 0xff; + cpu_emit(chunk, ret); + break; + } + case NODE_BINOP: { + cpu_compile_node(chunk, node->data.binop.left, depth); + cpu_compile_node(chunk, node->data.binop.right, depth + 1); + + CPUOpCode op; + switch (node->data.binop.op) { + case '+': { + op = RR_ADD; + break; + } + case '-': { + op = RR_SUB; + break; + } + case '*': { + op = RR_MUL; + break; + } + case '/': { + op = RR_DIV; + break; + } + default: { + op = RR_ADD; + break; + } + } + + CPUInstruction binop = cpu_make_instruction(op); + binop.param1 = depth; + binop.param2 = depth + 1; + binop.target = depth; + cpu_emit(chunk, binop); + break; + } + + case NODE_FN_DEF: { + char comment[64]; + snprintf(comment, sizeof(comment), "NODE_FN_DEF %s", node->data.fn_def.name); + cpu_comment(chunk, strdup(comment)); + + // emitir jmp para ignorar la funcion por defecto + CPUInstruction jump = cpu_make_instruction(II_ADD); + jump.param1 = 0x00; + jump.param2 = 0x00; + jump.target = 0x0E; + short jump_idx = cpu_emit(chunk, jump); + + // Registrar entrypoint + short entry_point = chunk->code_count; + CPUFunctionEntry *fn = &chunk->functions[chunk->func_count++]; + fn->name = node->data.fn_def.name; + fn->entry_point = entry_point; + fn->param_count = node->data.fn_def.param_count; + fn->param_names = node->data.fn_def.params; + + CPUInstruction pop_ret = cpu_make_instruction(RR_POP); + pop_ret.target = 0x0B; // REG11 como temp + cpu_emit(chunk, pop_ret); + + // Pop de los params del stack a RAM + for (int i = node->data.fn_def.param_count - 1; i >= 0; i--) { + // POP -> reg0 + CPUInstruction pop = cpu_make_instruction(RR_POP); + pop.target = 0x00; + cpu_emit(chunk, pop); + + // guardar en ram como variable local + char *param_name = node->data.fn_def.params[i]; + char mangled[128]; + snprintf(mangled, sizeof(mangled), "%s.%s", node->data.fn_def.name, + param_name); + short addr = cpu_add_name(chunk, strdup(mangled)); + CPUInstruction set_addr = cpu_make_instruction(II_ADD); + set_addr.param1 = addr; + set_addr.target = 0x0D; + cpu_emit(chunk, set_addr); + + CPUInstruction set_val = cpu_make_instruction(RI_ADD); + set_val.param1 = 0x00; + set_val.target = 0x0C; + cpu_emit(chunk, set_val); + + cpu_emit(chunk, cpu_make_instruction(RR_STR)); + } + + CPUInstruction push_ret = cpu_make_instruction(RR_PUSH); + push_ret.param1 = 0x0B; + cpu_emit(chunk, push_ret); + + chunk->current_fn = node->data.fn_def.name; + // Compilar body + cpu_compile_node(chunk, node->data.fn_def.body, 0); + + chunk->current_fn = NULL; + + // RET implicito + cpu_emit(chunk, cpu_make_instruction(RR_RET)); + + // Backpatch del jump + chunk->code[jump_idx].param1 = chunk->code_count * 4; + break; + } + + case NODE_IF: { + cpu_comment(chunk, "NODE_IF"); + ASTNode *cond = node->data.if_statement.cond; + + // Compilar miembros de la expresion y mover a depth y depth+1 + cpu_compile_node(chunk, cond->data.binop.left, depth); + cpu_compile_node(chunk, cond->data.binop.right, depth + 1); + + // Opcode inverso (saltar si la conficion es falsa) + CPUOpCode skip_op; + switch (cond->data.binop.op) { + case '>': { + skip_op = RR_LSE; + break; + } + case '<': { + skip_op = RR_GRE; + break; + } + case 'e': { + skip_op = RR_NEQ; + break; + } + case 'n': { + skip_op = RR_EQ; + break; + } + + default: { + printf("error: binop op not supported '%c'\n", cond->data.binop.op); + exit(1); + } + } + + // Emitir salto con placeholder + CPUInstruction jmp = cpu_make_instruction(skip_op); + jmp.param1 = depth; + jmp.param2 = depth + 1; + jmp.target = 0; + short jmp_idx = cpu_emit(chunk, jmp); + + // Compilar body + cpu_compile_node(chunk, node->data.if_statement.body, depth); + + // Backpatch + chunk->code[jmp_idx].target = chunk->code_count * 4; + break; + } + case NODE_WHILE: { + + cpu_comment(chunk, "NODE_WHILE"); + // Guardar entrada al bucle + short entry = chunk->code_count * 4; + + // chequear condicional, si no se cumple, salir del bucle + ASTNode *cond = node->data.while_loop.cond; + + // Compilar miembros de la expresion y mover a depth y depth+1 + cpu_compile_node(chunk, cond->data.binop.left, depth); + cpu_compile_node(chunk, cond->data.binop.right, depth + 1); + + // Opcode inverso (saltar si la conficion es falsa) + CPUOpCode skip_op; + switch (cond->data.binop.op) { + case '>': { + skip_op = RR_LSE; + break; + } + case '<': { + skip_op = RR_GRE; + break; + } + case 'e': { + skip_op = RR_NEQ; + break; + } + case 'n': { + skip_op = RR_EQ; + break; + } + + default: { + printf("error: binop op not supported '%c'\n", cond->data.binop.op); + exit(1); + } + } + + // Emitir salto con placeholder + CPUInstruction jmp = cpu_make_instruction(skip_op); + jmp.param1 = depth; + jmp.param2 = depth + 1; + jmp.target = 0; + short jmp_idx = cpu_emit(chunk, jmp); + + // Compilar body + cpu_compile_node(chunk, node->data.while_loop.body, depth); + + // Saltar al principio + CPUInstruction entry_jmp = cpu_make_instruction(II_ADD); + entry_jmp.param1 = entry; + entry_jmp.target = 0x0E; + cpu_emit(chunk, entry_jmp); + + // Backpatch + chunk->code[jmp_idx].target = chunk->code_count * 4; + + break; + } + default: + break; + } + + return -1; +} + +CPUChunk *compileAST(ASTNode *root) { + CPUChunk *chunk = (CPUChunk *)malloc(sizeof(CPUChunk)); + memset(chunk, 0, sizeof(CPUChunk)); + + cpu_compile_node(chunk, root, 0); + + // Añadir HALT + cpu_comment(chunk, "HALT"); + CPUInstruction halt = cpu_make_instruction(RR_HALT); + cpu_emit(chunk, halt); + + return chunk; +} + +void cpu_print_code(CPUChunk *chunk, int show_comments) { + printf("========= BINARY =========\n"); + for (int i = 0; i < chunk->code_count; i++) { + + if (show_comments) { + // Mostrar comentarios + for (int c = 0; c < chunk->comments_per_code[i]; c++) { + printf("# %s\n", chunk->comments[i][c]); + } + } + + CPUInstruction instr = chunk->code[i]; + printf("0x%04X 0x%04X 0x%04X 0x%04X\n", instr.op, instr.param1, + instr.param2, instr.target); + } +} + +#endif \ No newline at end of file diff --git a/src/backend/mycpu/opcodes.h b/src/backend/mycpu/opcodes.h new file mode 100644 index 0000000..d37ac23 --- /dev/null +++ b/src/backend/mycpu/opcodes.h @@ -0,0 +1,129 @@ +#ifndef JLANG_MYCPU_OOCODES_H +#define JLANG_MYCPU_OOCODES_H + +typedef enum { + // Register, Register + RR_ADD, + RR_SUB, + RR_MUL, + RR_DIV, + RR_AND, + RR_OR, + RR_NOT, + RR_NAND, + RR_NOR, + RR_XOR, + RR_XNOR, + RR_NEG, + + RR_EQ = 0x10, + RR_NEQ, + RR_LS, + RR_LSE, + RR_GR, + RR_GRE, + + RR_STR = 0x18, + RR_PUSH, + RR_POP, + + RR_CALL = 0x20, + RR_RET, + RR_HALT, + + // Inmediate, Register + IR_ADD = 0x40, + IR_SUB, + IR_MUL, + IR_DIV, + IR_AND, + IR_OR, + IR_NOT, + IR_NAND, + IR_NOR, + IR_XOR, + IR_XNOR, + IR_NEG, + + IR_EQ = 0x50, + IR_NEQ, + IR_LS, + IR_LSE, + IR_GR, + IR_GRE, + + IR_STR = 0x58, + IR_PUSH, + IR_POP, + + IR_CALL = 0x60, + IR_RET, + IR_HALT, + + // Register, Inmediate + RI_ADD = 0x80, + RI_SUB, + RI_MUL, + RI_DIV, + RI_AND, + RI_OR, + RI_NOT, + RI_NAND, + RI_NOR, + RI_XOR, + RI_XNOR, + RI_NEG, + + RI_EQ = 0x90, + RI_NEQ, + RI_LS, + RI_LSE, + RI_GR, + RI_GRE, + + RI_STR = 0x98, + RI_PUSH, + RI_POP, + + RI_CALL = 0xA0, + RI_RET, + RI_HALT, + + // Inmediate, Inmediate + II_ADD = 0xC0, + II_SUB, + II_MUL, + II_DIV, + II_AND, + II_OR, + II_NOT, + II_NAND, + II_NOR, + II_XOR, + II_XNOR, + II_NEG, + + II_EQ = 0xD0, + II_NEQ, + II_LS, + II_LSE, + II_GR, + II_GRE, + + II_STR = 0xD8, + II_PUSH, + II_POP, + + II_CALL = 0xE0, + II_RET, + II_HALT, +} CPUOpCode; + +typedef struct { + CPUOpCode op; + unsigned short param1; + unsigned short param2; + unsigned short target; +} CPUInstruction; + +#endif \ No newline at end of file diff --git a/src/frontend/lexer.h b/src/frontend/lexer.h index 1ba9e4c..fe53811 100644 --- a/src/frontend/lexer.h +++ b/src/frontend/lexer.h @@ -115,8 +115,21 @@ Token *tokenize(const char *source, int *token_count) { tokens[count++] = make_token(TOK_SLASH, "/"); pos++; } else if (c == '=') { - tokens[count++] = make_token(TOK_ASSIGN, "="); - pos++; + if (source[pos + 1] == '=') { + tokens[count++] = make_token(TOK_EQ, "=="); + pos += 2; + } else { + tokens[count++] = make_token(TOK_ASSIGN, "="); + pos++; + } + } else if (c == '!') { + if (source[pos + 1] == '=') { + tokens[count++] = make_token(TOK_NEQ, "!="); + pos += 2; + } else { + printf("WARN: caracter no reconocido '!' en pos %d\n", pos); + exit(1); + } } else if (c == '<') { tokens[count++] = make_token(TOK_LT, "<"); pos++; diff --git a/src/frontend/parser.h b/src/frontend/parser.h index cada036..ba4aeff 100644 --- a/src/frontend/parser.h +++ b/src/frontend/parser.h @@ -206,8 +206,16 @@ ASTNode *parse_expr(Token *tokens) { ASTNode *left = parse_term(tokens); while (tokens[pos].type == TOK_PLUS || tokens[pos].type == TOK_MINUS || - tokens[pos].type == TOK_LT || tokens[pos].type == TOK_GT) { - char op = tokens[pos].value[0]; // +,-,*,/ + tokens[pos].type == TOK_LT || tokens[pos].type == TOK_GT || + tokens[pos].type == TOK_EQ || tokens[pos].type == TOK_NEQ) { + char op; + if (tokens[pos].type == TOK_EQ) { + op = 'e'; + } else if (tokens[pos].type == TOK_NEQ) { + op = 'n'; + } else { + op = tokens[pos].value[0]; // +,-,*,/ + } pos++; ASTNode *right = parse_term(tokens); diff --git a/src/main.c b/src/main.c index 31025e5..b86f6b1 100644 --- a/src/main.c +++ b/src/main.c @@ -1,10 +1,11 @@ #include "backend/eval/eval.h" #include "backend/bytecode/compiler.h" #include "backend/bytecode/vm.h" +#include "backend/mycpu/gencode.h" int main(int argc, char **argv) { if (argc != 3) { - printf("usage: %s eval|vm|asm \n", argv[0]); + printf("usage: %s eval|vm|asm|mycpu \n", argv[0]); exit(1); } // Creamos un allocator @@ -26,12 +27,13 @@ int main(int argc, char **argv) { fclose(fptr); + printf("=== CODE ===\n"); printf("%s\n", buff); // Lexer test int totalTokens = 0; Token *tokens = tokenize(buff, &totalTokens); - printf("totalTokens=%d\n", totalTokens); + printf("=== INFO ===\n"); ASTNode *block = parse(tokens, totalTokens); ast_debug(block); @@ -51,6 +53,10 @@ int main(int argc, char **argv) { // printf("\n"); // JLANG_visualize(allocPtr); + } else if (strcmp(argv[1], "mycpu") == 0){ + CPUChunk* chunk = compileAST(block); + cpu_print_code(chunk, 1); + } else { printf("panic: WIP\n"); }