removed unnecessary things, updated and audited the code

This commit is contained in:
2026-06-13 11:31:07 -06:00
parent 29797fb707
commit a36705a931
13 changed files with 529 additions and 1718 deletions
+8
View File
@@ -58,6 +58,10 @@ constexpr u8 ADDR_MODE_MASKS[][2] = {
{ 0x1E, 0xFF }, // GE { 0x1E, 0xFF }, // GE
{ 0x1E, 0xFF }, // LT { 0x1E, 0xFF }, // LT
{ 0x1E, 0xFF }, // LE { 0x1E, 0xFF }, // LE
{ 0x1E, 0xFF }, // GTU
{ 0x1E, 0xFF }, // GEU
{ 0x1E, 0xFF }, // LTU
{ 0x1E, 0xFF }, // LEU
{ 0xFF, 0x00 }, // JMP { 0xFF, 0x00 }, // JMP
{ 0xFF, 0x00 }, // JEQ { 0xFF, 0x00 }, // JEQ
{ 0xFF, 0x00 }, // JNE { 0xFF, 0x00 }, // JNE
@@ -190,6 +194,10 @@ constexpr u8 TYPE_SIZE_MASKS[] = {
0x0F, // GE 0x0F, // GE
0x0F, // LT 0x0F, // LT
0x0F, // LE 0x0F, // LE
0x0F, // GTU
0x0F, // GEU
0x0F, // LTU
0x0F, // LEU
0x0F, // JMP 0x0F, // JMP
0x0F, // JEQ 0x0F, // JEQ
0x0F, // JNE 0x0F, // JNE
-658
View File
@@ -1,658 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "1c7a11d9",
"metadata": {},
"source": [
"This test will test (lol) the calling convention in Spider.\n",
"As defined, it will use the CPU as a python object.\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "b9c572cd",
"metadata": {},
"outputs": [],
"source": [
"# The state of the \"my-language-name\" vm in a simple\n",
"# python object abstraction\n",
"my_language_name_vm = {\n",
" 'regs':{\n",
" 'RA':0,\n",
" 'RB':0,\n",
" 'RC':0,\n",
" 'RD':0,\n",
" 'RX':0,\n",
" 'RY':0,\n",
" 'R0':0,\n",
" 'R1':0,\n",
" 'R2':0,\n",
" 'R3':0,\n",
" 'R4':0,\n",
" 'R5':0,\n",
" 'R6':0,\n",
" 'R7':0,\n",
" 'R8':0,\n",
" 'R9':0,\n",
" 'RF':0,\n",
" 'RI':0,\n",
" 'RS':0,\n",
" 'RZ':0,\n",
" 'RE':0,\n",
" 'RH':0,\n",
" 'RV':0,\n",
" 'RM':0,\n",
" },\n",
" 'stack': [],\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "3e3989dc",
"metadata": {},
"source": [
"**Test Data Definition**\n",
"\n",
"This block defines the parameters we want to pass into our hypothetical function and what we expect it to return. We use a mix of large data structures (sizes in bytes) and booleans (size 1) to force the VM to use all of its memory allocation rules."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "a1ddd324",
"metadata": {},
"outputs": [],
"source": [
"# Input parameters, measured in bytes (booleans are size 1 for logic triggers)\n",
"input_params = [\n",
" {'name': 'int_a', 'size': 4}, # Should go to RA\n",
" {'name': 'bool_1', 'size': 1}, # Should be queued\n",
" {'name': 'bool_2', 'size': 1}, # Should be queued\n",
" {'name': 'double_b', 'size': 8}, # Should go to RB\n",
" {'name': 'big_struct', 'size': 32} # Too large for registers, forces stack usage\n",
"]\n",
"\n",
"# Define the return types here\n",
"output_return = [ \n",
" {'name': 'ret1', 'size': 8} \n",
"]"
]
},
{
"cell_type": "markdown",
"id": "bbe2c356",
"metadata": {},
"source": [
"Now, on this function it will perform the tasks of ordering the registers and stack based on the parameters.\n",
"\n",
"**The Function Call ABI**\n",
"This is the core logic. It prepares the VM for a jump to another function by safely backing up the current context and routing the parameters to either the registers or the stack according to the strict hardware rules.\n",
"\n",
"**Step 1**: It pushes the caller-saved registers (R0-R3) to the stack so they aren't lost.\n",
"\n",
"**Step 2**: It checks if the function will return a massive object (> 16 bytes). If so, it reserves a pointer space.\n",
"\n",
"**Step 3**: It attempts to put parameters into RA, RB, RC, RD, R8, R9. Booleans are packed together to save space.\n",
"\n",
"**Step 4**: Anything that overflows the registers is pushed to the stack."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "02a2ad1a",
"metadata": {},
"outputs": [],
"source": [
"def do_function_call(input_params: list, output_return: list):\n",
" global my_language_name_vm\n",
" \n",
" print(\"--- 1. Saving Caller State ---\")\n",
" # Paso 1: Respaldar registros salvados por el llamador (R0-R3) \n",
" caller_saved = ['R0', 'R1', 'R2', 'R3']\n",
" for reg in caller_saved:\n",
" my_language_name_vm['stack'].append(my_language_name_vm['regs'][reg])\n",
" \n",
" print(\"--- 2. Allocating Parameters ---\")\n",
" \n",
" # Regla: Si el retorno > 16 bytes, se agrega un puntero al INICIO de los parámetros \n",
" actual_params = input_params.copy()\n",
" ret_size = sum(r['size'] for r in output_return)\n",
" if ret_size > 16:\n",
" # Se inserta al frente para que ocupe RA (o el primer espacio disponible) \n",
" actual_params.insert(0, {'name': 'RET_PTR_ALLOCATION', 'size': 8}) \n",
"\n",
" param_regs = ['RA', 'RB', 'RC', 'RD', 'R8', 'R9'] # \n",
" reg_idx = 0\n",
" bool_queue = []\n",
" stack_params = []\n",
" \n",
" # --- FASE 1: ASIGNACIÓN A REGISTROS ---\n",
" for param in actual_params:\n",
" if reg_idx < len(param_regs):\n",
" if param['size'] == 1: # Es Booleano \n",
" bool_queue.append(param['name'])\n",
" if len(bool_queue) == 8: # Cola llena (8 bytes/bits según lógica de la VM) \n",
" my_language_name_vm['regs'][param_regs[reg_idx]] = f\"Packed_Bools({','.join(bool_queue)})\"\n",
" reg_idx += 1\n",
" bool_queue = []\n",
" elif param['size'] <= 8: # Parámetro estándar\n",
" # Antes de asignar un no-booleano, si hay booleanos pendientes, se deben flashear \n",
" if bool_queue:\n",
" my_language_name_vm['regs'][param_regs[reg_idx]] = f\"Padded_Bools({len(bool_queue)})\"\n",
" reg_idx += 1\n",
" bool_queue = []\n",
" \n",
" if reg_idx < len(param_regs):\n",
" my_language_name_vm['regs'][param_regs[reg_idx]] = param['name']\n",
" reg_idx += 1\n",
" else:\n",
" stack_params.append(param)\n",
" else:\n",
" # Si es muy grande para un registro (>8), va a la pila después \n",
" stack_params.append(param)\n",
" else:\n",
" stack_params.append(param)\n",
"\n",
" # Flashear booleanos restantes si queda espacio en registros \n",
" if bool_queue and reg_idx < len(param_regs):\n",
" my_language_name_vm['regs'][param_regs[reg_idx]] = f\"Padded_Bools({len(bool_queue)})\"\n",
" reg_idx += 1\n",
" bool_queue = []\n",
" elif bool_queue:\n",
" # Si no hubo registros, los booleanos pendientes pasan a la lógica de pila\n",
" stack_params = [{'name': b, 'size': 1} for b in bool_queue] + stack_params\n",
" bool_queue = []\n",
"\n",
" # Regla VI: Si sobran registros, guardar SP y respaldar en stack \n",
" while reg_idx < len(param_regs):\n",
" my_language_name_vm['regs'][param_regs[reg_idx]] = \"SP_Backup\"\n",
" my_language_name_vm['stack'].append(f\"SP_for_{param_regs[reg_idx]}\")\n",
" reg_idx += 1\n",
"\n",
" # --- FASE 2: ASIGNACIÓN A PILA ---\n",
" # 1. Parámetros <= 8 bytes (no booleanos)\n",
" remaining_large = []\n",
" for param in stack_params:\n",
" if param['size'] == 1:\n",
" bool_queue.append(param['name'])\n",
" if len(bool_queue) == 8: # Formó un \"byte\" (o bloque) \n",
" my_language_name_vm['stack'].append(\"Packed_Bools_Stack\")\n",
" bool_queue = []\n",
" elif param['size'] <= 8:\n",
" my_language_name_vm['stack'].append(param['name'])\n",
" else:\n",
" remaining_large.append(param)\n",
" \n",
" # Flashear booleanos de la pila con padding \n",
" if bool_queue:\n",
" my_language_name_vm['stack'].append(\"Padded_Bools_Stack\")\n",
" \n",
" # 2. Finalmente parámetros grandes en orden \n",
" for param in remaining_large:\n",
" my_language_name_vm['stack'].append(f\"LARGE_{param['name']}\")"
]
},
{
"cell_type": "markdown",
"id": "ac6440ca",
"metadata": {},
"source": [
"Now, unwind the stack and stuff to make sure the machine goes back to its previous state BEFORE the function\n",
"\n",
"**The Function Cleanup ABI**\n",
"Once the target function finishes executing and returns its answer in the registers, the caller function must clean up the mess it made on the stack before continuing.\n",
"\n",
"**Step 1**: It pops all the parameters that were pushed to the stack.\n",
"\n",
"**Step 2**: It pops the backup copies of R0-R3 and puts them back into the actual registers, restoring the caller's state perfectly."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "d2e4ce44",
"metadata": {},
"outputs": [],
"source": [
"# TODO \n",
"def undo_function_call(input_params: list, output_return: list):\n",
" global my_language_name_vm\n",
" \n",
" print(\"--- 3. Cleaning Up Stack ---\")\n",
" \n",
" # 1. Calcular EXACTAMENTE cuántos elementos se pusieron en la pila (simulando la asignación)\n",
" pops_needed = 0\n",
" \n",
" ret_size = sum(r['size'] for r in output_return)\n",
" if ret_size > 16:\n",
" pops_needed += 1\n",
" \n",
" param_regs = ['RA', 'RB', 'RC', 'RD', 'R8', 'R9']\n",
" reg_idx = 0\n",
" bool_queue = []\n",
" stack_params = []\n",
" \n",
" # Simular qué se fue a registros y qué se fue a la pila\n",
" for param in input_params:\n",
" if reg_idx < len(param_regs):\n",
" if param['size'] == 1:\n",
" bool_queue.append(param['name'])\n",
" if len(bool_queue) == 8:\n",
" reg_idx += 1\n",
" bool_queue = []\n",
" elif param['size'] <= 8:\n",
" reg_idx += 1\n",
" elif param['size'] <= 16 and (len(param_regs) - reg_idx) >= 2:\n",
" reg_idx += 2\n",
" else:\n",
" stack_params.append(param)\n",
" else:\n",
" stack_params.append(param)\n",
" \n",
" if len(bool_queue) > 0 and reg_idx < len(param_regs):\n",
" reg_idx += 1\n",
" bool_queue = []\n",
" \n",
" # Contar los respaldos del Stack Pointer (SP)\n",
" while reg_idx < len(param_regs):\n",
" pops_needed += 1 \n",
" reg_idx += 1\n",
" \n",
" # Contar los argumentos reales que cayeron en la pila\n",
" for param in stack_params:\n",
" if param['size'] == 1:\n",
" bool_queue.append(param['name'])\n",
" if len(bool_queue) == 8:\n",
" pops_needed += 1\n",
" bool_queue = []\n",
" else:\n",
" pops_needed += 1 # Tanto <=8 como >8 ocupan 1 espacio en nuestra lista de Python\n",
" \n",
" if len(bool_queue) > 0:\n",
" pops_needed += 1\n",
"\n",
" # 2. Hacer pop EXACTAMENTE de la cantidad calculada\n",
" for _ in range(pops_needed):\n",
" if my_language_name_vm['stack']:\n",
" removed = my_language_name_vm['stack'].pop()\n",
" print(f\"Popped parameter: {removed}\")\n",
" \n",
" # 3. Restaurar los Caller-Saved registers (siempre son 4)\n",
" print(\"--- 4. Restoring Caller Registers ---\")\n",
" caller_saved_order = ['R3', 'R2', 'R1', 'R0']\n",
" for reg in caller_saved_order:\n",
" if my_language_name_vm['stack']:\n",
" restored_val = my_language_name_vm['stack'].pop()\n",
" my_language_name_vm['regs'][reg] = restored_val\n",
" print(f\"Restored {reg} <- {restored_val}\")"
]
},
{
"cell_type": "markdown",
"id": "5397697f",
"metadata": {},
"source": [
"VERY GOOD\n",
"Now check the state of the machine before and after\n",
"\n",
"Try different combinations: One function call, multiple function calls, recursive calls, etc."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "b58fbf72",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"--- 1. Saving Caller State ---\n",
"--- 2. Allocating Parameters ---\n",
"\n",
"[STATE AFTER CALL]\n",
"Registers: {'RA': 'int_a', 'RB': 'Padded_Bools(2)', 'RC': 'double_b', 'RD': 'SP_Backup', 'R8': 'SP_Backup', 'R9': 'SP_Backup'}\n",
"Stack: [0, 0, 0, 0, 'SP_for_RD', 'SP_for_R8', 'SP_for_R9', 'LARGE_big_struct']\n",
"\n",
"========================================\n",
"\n",
"--- 3. Cleaning Up Stack ---\n",
"Popped parameter: LARGE_big_struct\n",
"Popped parameter: SP_for_R9\n",
"Popped parameter: SP_for_R8\n",
"Popped parameter: SP_for_RD\n",
"--- 4. Restoring Caller Registers ---\n",
"Restored R3 <- 0\n",
"Restored R2 <- 0\n",
"Restored R1 <- 0\n",
"Restored R0 <- 0\n",
"\n",
"[STATE AFTER RETURN]\n",
"Registers: {'RA': 'int_a', 'RB': 'Padded_Bools(2)', 'RC': 'double_b', 'RD': 'SP_Backup', 'R8': 'SP_Backup', 'R9': 'SP_Backup'}\n",
"Stack: []\n"
]
}
],
"source": [
"# Execute the call simulation\n",
"do_function_call(input_params, output_return)\n",
"\n",
"print(\"\\n[STATE AFTER CALL]\")\n",
"print(\"Registers:\", {k: v for k, v in my_language_name_vm['regs'].items() if v != 0})\n",
"print(\"Stack:\", my_language_name_vm['stack'])\n",
"print(\"\\n\" + \"=\"*40 + \"\\n\")\n",
"\n",
"# Execute the cleanup simulation\n",
"undo_function_call(input_params, output_return)\n",
"\n",
"print(\"\\n[STATE AFTER RETURN]\")\n",
"print(\"Registers:\", {k: v for k, v in my_language_name_vm['regs'].items() if v != 0})\n",
"print(\"Stack:\", my_language_name_vm['stack'])"
]
},
{
"cell_type": "markdown",
"id": "4f700ac0",
"metadata": {},
"source": [
"**Setup and Helper Function**\n",
"First, let's create a quick helper to reset the VM so each test starts with a clean slate."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "d225a397",
"metadata": {},
"outputs": [],
"source": [
"def reset_vm():\n",
" global my_language_name_vm\n",
" my_language_name_vm['stack'] = []\n",
" for k in my_language_name_vm['regs']:\n",
" my_language_name_vm['regs'][k] = 0\n",
"\n",
"# Standard parameters for testing\n",
"test_params = [{'name': 'data', 'size': 8}]\n",
"test_returns = [{'name': 'ret', 'size': 8}]"
]
},
{
"cell_type": "markdown",
"id": "77df0216",
"metadata": {},
"source": [
"**Test - Sequential Calls**\n",
"This tests if the VM can call a function, clean up, and immediately call another function without the stack growing infinitely."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "70347484",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"=== TEST 1: MULTIPLE SEQUENTIAL CALLS ===\n",
"[Calling Function A]\n",
"--- 1. Saving Caller State ---\n",
"--- 2. Allocating Parameters ---\n",
"--- 3. Cleaning Up Stack ---\n",
"Popped parameter: SP_for_R9\n",
"Popped parameter: SP_for_R8\n",
"Popped parameter: SP_for_RD\n",
"Popped parameter: SP_for_RC\n",
"Popped parameter: SP_for_RB\n",
"--- 4. Restoring Caller Registers ---\n",
"Restored R3 <- 0\n",
"Restored R2 <- 0\n",
"Restored R1 <- 0\n",
"Restored R0 <- 0\n",
"\n",
"[Calling Function B]\n",
"--- 1. Saving Caller State ---\n",
"--- 2. Allocating Parameters ---\n",
"--- 3. Cleaning Up Stack ---\n",
"Popped parameter: SP_for_R9\n",
"Popped parameter: SP_for_R8\n",
"Popped parameter: SP_for_RD\n",
"Popped parameter: SP_for_RC\n",
"Popped parameter: SP_for_RB\n",
"--- 4. Restoring Caller Registers ---\n",
"Restored R3 <- 0\n",
"Restored R2 <- 0\n",
"Restored R1 <- 0\n",
"Restored R0 <- 0\n",
"\n",
"Final Stack (Should be empty): []\n"
]
}
],
"source": [
"print(\"=== TEST 1: MULTIPLE SEQUENTIAL CALLS ===\")\n",
"reset_vm()\n",
"\n",
"print(\"[Calling Function A]\")\n",
"do_function_call(test_params, test_returns)\n",
"undo_function_call(test_params, test_returns)\n",
"\n",
"print(\"\\n[Calling Function B]\")\n",
"do_function_call(test_params, test_returns)\n",
"undo_function_call(test_params, test_returns)\n",
"\n",
"print(\"\\nFinal Stack (Should be empty):\", my_language_name_vm['stack'])"
]
},
{
"cell_type": "markdown",
"id": "27f37c95",
"metadata": {},
"source": [
"**Test - Recursive / Nested Calls**\n",
"This is the most critical test. If Function A calls Function B, the VM must push a second frame onto the stack without destroying Function A's caller-saved registers (R0-R3)."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "0c46e3f2",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"=== TEST 2: NESTED / RECURSIVE CALLS ===\n",
"\n",
"[1. Calling Outer Function]\n",
"--- 1. Saving Caller State ---\n",
"--- 2. Allocating Parameters ---\n",
"Outer function modifies R0: IMPORTANT_OUTER_DATA\n",
"\n",
"[2. Calling Inner Function (Nested)]\n",
"--- 1. Saving Caller State ---\n",
"--- 2. Allocating Parameters ---\n",
"Stack depth during nested call: 18\n",
"\n",
"[3. Returning from Inner Function]\n",
"--- 3. Cleaning Up Stack ---\n",
"Popped parameter: SP_for_R9\n",
"Popped parameter: SP_for_R8\n",
"Popped parameter: SP_for_RD\n",
"Popped parameter: SP_for_RC\n",
"Popped parameter: SP_for_RB\n",
"--- 4. Restoring Caller Registers ---\n",
"Restored R3 <- 0\n",
"Restored R2 <- 0\n",
"Restored R1 <- 0\n",
"Restored R0 <- IMPORTANT_OUTER_DATA\n",
"Did R0 survive the nested call?: IMPORTANT_OUTER_DATA\n",
"\n",
"[4. Returning from Outer Function]\n",
"--- 3. Cleaning Up Stack ---\n",
"Popped parameter: SP_for_R9\n",
"Popped parameter: SP_for_R8\n",
"Popped parameter: SP_for_RD\n",
"Popped parameter: SP_for_RC\n",
"Popped parameter: SP_for_RB\n",
"--- 4. Restoring Caller Registers ---\n",
"Restored R3 <- 0\n",
"Restored R2 <- 0\n",
"Restored R1 <- 0\n",
"Restored R0 <- 0\n",
"\n",
"Final Stack (Should be empty): []\n"
]
}
],
"source": [
"print(\"=== TEST 2: NESTED / RECURSIVE CALLS ===\")\n",
"reset_vm()\n",
"\n",
"# 1. We enter the Outer Function\n",
"print(\"\\n[1. Calling Outer Function]\")\n",
"do_function_call([{'name': 'outer_arg', 'size': 8}], test_returns)\n",
"\n",
"# Let's simulate the Outer Function doing some math and saving it in R0\n",
"my_language_name_vm['regs']['R0'] = \"IMPORTANT_OUTER_DATA\"\n",
"print(\"Outer function modifies R0:\", my_language_name_vm['regs']['R0'])\n",
"\n",
"# 2. Outer Function calls the Inner Function\n",
"print(\"\\n[2. Calling Inner Function (Nested)]\")\n",
"do_function_call([{'name': 'inner_arg', 'size': 8}], test_returns)\n",
"\n",
"print(\"Stack depth during nested call:\", len(my_language_name_vm['stack']))\n",
"# Notice that \"IMPORTANT_OUTER_DATA\" is now safely backed up inside the stack!\n",
"\n",
"# 3. Inner Function returns\n",
"print(\"\\n[3. Returning from Inner Function]\")\n",
"undo_function_call([{'name': 'inner_arg', 'size': 8}], test_returns)\n",
"\n",
"# 4. Check if the Outer Function's data survived\n",
"print(\"Did R0 survive the nested call?:\", my_language_name_vm['regs']['R0'])\n",
"\n",
"# 5. Outer Function returns\n",
"print(\"\\n[4. Returning from Outer Function]\")\n",
"undo_function_call([{'name': 'outer_arg', 'size': 8}], test_returns)\n",
"\n",
"print(\"\\nFinal Stack (Should be empty):\", my_language_name_vm['stack'])"
]
},
{
"cell_type": "markdown",
"id": "2439376d",
"metadata": {},
"source": [
"**1. External Interrupt**"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "48b4d8f2",
"metadata": {},
"outputs": [],
"source": [
"def trigger_external_interrupt(interrupt_handler_address):\n",
" global my_language_name_vm\n",
" print(f\"\\n--- [EXTERNAL INTERRUPT] jumping to {hex(interrupt_handler_address)} ---\")\n",
" \n",
" # 1. Guardar el registro de banderas (RF) en el stack\n",
" my_language_name_vm['stack'].append(my_language_name_vm['regs']['RF'])\n",
" \n",
" # 2. Guardar el valor actual de RV en el stack (para no perderlo)\n",
" my_language_name_vm['stack'].append(my_language_name_vm['regs']['RV'])\n",
" \n",
" # 3. El manual dice que se genera un function call al valor de RV\n",
" # Seteamos el destino en RV\n",
" my_language_name_vm['regs']['RV'] = interrupt_handler_address\n",
" \n",
" # 4. Activar el bit de \"Interrupt Request\" en el registro de banderas\n",
" # Asumiendo que es el Bit 2 (según convención estándar de este manual)\n",
" my_language_name_vm['regs']['RF'] |= (1 << 2)\n",
" \n",
" # 5. Si existe un IFH (Interrupt Finish Handle), se prepara para después\n",
" # (Por ahora simulamos que el PC/RI salta a la dirección de RV)\n",
" my_language_name_vm['regs']['RI'] = interrupt_handler_address \n",
" \n",
" print(\"Estado guardado: RF y RV están en el stack. Bit de interrupción activo.\")"
]
},
{
"cell_type": "markdown",
"id": "6ccdacec",
"metadata": {},
"source": [
"**2. Internal Interrupt**"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "6d3f0026",
"metadata": {},
"outputs": [],
"source": [
"def execute_internal_interrupt(interrupt_code):\n",
" global my_language_name_vm\n",
" \n",
" # El manual especifica el código 0x0A para llamadas al Host\n",
" if interrupt_code == 0x0A:\n",
" print(\"\\n--- [INTERNAL INTERRUPT 0x0A] Calling Host Function ---\")\n",
" \n",
" # 1. Obtener el 'handle' de la función desde RV\n",
" function_handle = my_language_name_vm['regs']['RV']\n",
" \n",
" # 2. Verificar si la función existe en la tabla del Host\n",
" # (Simulamos una tabla de funciones simple)\n",
" host_functions = {\n",
" 1: lambda: \"Hello from Host!\",\n",
" 2: lambda: 42\n",
" }\n",
" \n",
" if function_handle in host_functions:\n",
" # 3. Ejecutar y devolver resultado en registros de retorno (RA, RB, RC, RD)\n",
" result = host_functions[function_handle]()\n",
" print(f\"Executing handle {function_handle}: Result = {result}\")\n",
" \n",
" # Según la convención, el resultado va a RA\n",
" my_language_name_vm['regs']['RA'] = result\n",
" else:\n",
" # 4. Si no existe, RV debe ser seteado a cero\n",
" print(f\"Error: External function handle {function_handle} not found.\")\n",
" my_language_name_vm['regs']['RV'] = 0\n",
" else:\n",
" print(f\"Unknown internal interrupt code: {hex(interrupt_code)}\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.9"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Binary file not shown.
+15 -15
View File
@@ -144,7 +144,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 4, "execution_count": 3,
"id": "58645013", "id": "58645013",
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
@@ -152,16 +152,16 @@
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"Real instructions : 128\n", "Real instructions : 132\n",
"Reserved slots : 14\n", "Reserved slots : 10\n",
"Duplicate check : PASSED\n", "Duplicate check : PASSED\n",
"\n", "\n",
"Groups found:\n", "Groups found:\n",
"group\n", "group\n",
"Integer 19\n", "Integer 19\n",
"Boolean 16\n",
"System 15\n", "System 15\n",
"Bit Wise 14\n", "Bit Wise 14\n",
"Boolean 12\n",
"Branch 12\n", "Branch 12\n",
"Floating Point 10\n", "Floating Point 10\n",
"Casts 10\n", "Casts 10\n",
@@ -283,7 +283,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 5, "execution_count": 4,
"id": "452bc76c", "id": "452bc76c",
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
@@ -292,7 +292,7 @@
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"Masks written to: .//autogen/InstructionMasks.hpp\n", "Masks written to: .//autogen/InstructionMasks.hpp\n",
"Lines generated : 272\n" "Lines generated : 280\n"
] ]
} }
], ],
@@ -357,7 +357,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 6, "execution_count": 5,
"id": "5aaebef0", "id": "5aaebef0",
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
@@ -365,7 +365,7 @@
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"Instructions formatted: 128\n", "Instructions formatted: 132\n",
"\n", "\n",
"--- Preview (first 2 instructions) ---\n", "--- Preview (first 2 instructions) ---\n",
" // [System] 0x000 — NOP: No Operation\n", " // [System] 0x000 — NOP: No Operation\n",
@@ -380,7 +380,7 @@
"\n", "\n",
"\n", "\n",
"CPU.hpp updated successfully at: .//src//spider/runtime/cpu/CPU.hpp\n", "CPU.hpp updated successfully at: .//src//spider/runtime/cpu/CPU.hpp\n",
"Total lines in updated file: 883\n" "Total lines in updated file: 911\n"
] ]
} }
], ],
@@ -451,7 +451,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 7, "execution_count": 8,
"id": "instrmap_gen", "id": "instrmap_gen",
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
@@ -460,9 +460,9 @@
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"InstrMap.cpp written to: .//src//spider/runtime/instr/InstrMap.cpp\n", "InstrMap.cpp written to: .//src//spider/runtime/instr/InstrMap.cpp\n",
" Size : 34,157 bytes\n", " Size : 33,581 bytes\n",
" Array entries : 512 (128 populated, 384 nullptr)\n", " Array entries : 512 (132 populated, 380 nullptr)\n",
" Switch cases : 128\n", " Switch cases : 132\n",
" Line endings : LF-only verified\n" " Line endings : LF-only verified\n"
] ]
} }
@@ -545,7 +545,7 @@
" mnem = opcode_to_mnem.get(opc)\n", " mnem = opcode_to_mnem.get(opc)\n",
" if mnem:\n", " if mnem:\n",
" name = opcode_to_name[opc]\n", " name = opcode_to_name[opc]\n",
" L.append(f' &CPU::{mnem + \",\":<28s}// 0x{opc:03X} — {name}')\n", " L.append(f' &CPU::{mnem + \",\":<22s}// 0x{opc:03X} — {name}')\n",
" else:\n", " else:\n",
" tag = ''\n", " tag = ''\n",
" if opc in reserved_opcodes:\n", " if opc in reserved_opcodes:\n",
@@ -681,7 +681,7 @@
"name": "python", "name": "python",
"nbconvert_exporter": "python", "nbconvert_exporter": "python",
"pygments_lexer": "ipython3", "pygments_lexer": "ipython3",
"version": "3.14.3" "version": "3.13.7"
} }
}, },
"nbformat": 4, "nbformat": 4,
+1 -9
View File
@@ -1,18 +1,10 @@
#include "SpiderRuntime.hpp" #include "SpiderRuntime.hpp"
#include <spider/runtime/debug/LiveDebug.hpp>
#include <iostream> #include <iostream>
namespace spider { namespace spider {
const u32 RUNTIME_VERSION_NO = 0x00000000; // v0.1 const u32 RUNTIME_VERSION_NO = 0x00000001; // v0.1
const std::string RUNTIME_VERSION = "alpha v0.1"; const std::string RUNTIME_VERSION = "alpha v0.1";
}
int main() {
spider::liveDebugMain();
return 0;
} }
+20 -12
View File
@@ -76,8 +76,8 @@ namespace spider {
void CPU::fetchOperDst() { void CPU::fetchOperDst() {
// Move the operand ptrs // Move the operand ptrs
_alu = &ALU0;
_opers[1] = _opers[0]; _opers[1] = _opers[0];
_opers[0] = &ALU0;
// call specific addressing mode // call specific addressing mode
(this->*(CPU::addrModes[_addrm & 0b111]))(); // mask added here too (this->*(CPU::addrModes[_addrm & 0b111]))(); // mask added here too
@@ -85,7 +85,7 @@ namespace spider {
void CPU::fetchOperSrc() { void CPU::fetchOperSrc() {
// set ALU // set ALU
_alu = &ALU1; _opers[0] = &ALU1;
// call specific addressing mode // call specific addressing mode
(this->*(CPU::addrModes[_addrm & 0b111]))(); // mask keeps index within 0-7 (this->*(CPU::addrModes[_addrm & 0b111]))(); // mask keeps index within 0-7
@@ -95,13 +95,23 @@ namespace spider {
_addrm++; _addrm++;
} }
void CPU::fetchOperReg() {
_dst = &GPR[_reel->readU8(RI++) & 0xF];
}
void CPU::fetchOperRegReg() {
// read the full byte
u8 reg = _reel->readU8(RI++);
_src = &GPR[(reg >> 4) & 0xF];
_dst = &GPR[ reg & 0xF];
}
void CPU::execute() { void CPU::execute() {
(this->*(CPU::instrMap[_opcode]))(); // no null check needed (this->*(CPU::instrMap[_opcode]))(); // no null check needed
// vvv MUST PROFILE: Is this faster than executing an empty function?
if(_post) (this->*_post)(); if(_post) (this->*_post)();
} }
// Addressing Modes // // Addressing Modes //
/** /**
@@ -115,8 +125,7 @@ namespace spider {
* Immediate Addressing Mode * Immediate Addressing Mode
*/ */
void CPU::imm() { void CPU::imm() {
_reel->loadRegister(RI, _size, _alu); _reel->loadRegister(RI, _size, _opers[0]);
_opers[0] = _alu;
_post = &CPU::imp; _post = &CPU::imp;
RI += 1 << _size; RI += 1 << _size;
} }
@@ -127,19 +136,19 @@ namespace spider {
void CPU::abs() { void CPU::abs() {
// Load the actual ptr into the ALU // Load the actual ptr into the ALU
u8 mm = u8(getFlag(CPU::FLAG_MEMORY_MODE)); u8 mm = u8(getFlag(CPU::FLAG_MEMORY_MODE));
_reel->loadRegister(RI, mm, _alu); _reel->loadRegister(RI, mm, _opers[0]);
RI += 1 << mm; RI += 1 << mm;
// read the memory from RAM // read the memory from RAM
_store = _alu->_u64; _store = _opers[0]->_u64;
_ram->loadRegister(_store, _size, _alu); _ram->loadRegister(_store, _size, _opers[0]);
_post = &CPU::psw; _post = &CPU::psw;
} }
/** /**
* Register Addressing Mode * Register Addressing Mode
*/ */
void CPU::reg() { // NOT FINISHED void CPU::reg() {
// Two consecutive registers can be declared // Two consecutive registers can be declared
// Shift if the top part will become .reg too // Shift if the top part will become .reg too
u8 sh = ((_addrm & 0b11000) == 0b11000) * 4; u8 sh = ((_addrm & 0b11000) == 0b11000) * 4;
@@ -147,12 +156,11 @@ namespace spider {
// get byte // get byte
u8 reg = (_reel->readU8(RI) >> sh) & 0xF; u8 reg = (_reel->readU8(RI) >> sh) & 0xF;
_alu = &GPR[reg]; _opers[0] = &GPR[reg]; // explicitly sets _opers[0] = _dst
_opers[0] = _alu; // explicitly sets _opers[0] = _dst
RI += use; RI += use;
// store no-op // store no-op
_post = &CPU::imp; _post = nullptr;
} }
/** /**
+38 -23
View File
@@ -64,7 +64,6 @@ namespace spider {
struct { struct {
register_t* _dst; register_t* _dst;
register_t* _src; register_t* _src;
register_t* _alu;
}; };
register_t* _opers[2]; register_t* _opers[2];
}; };
@@ -172,6 +171,18 @@ namespace spider {
*/ */
void fetchOperSrc(); void fetchOperSrc();
/**
* Fetches special addressing where
* one register is addressed.
*/
void fetchOperReg();
/**
* Fetches special addressing where
* two registers are addressed.
*/
void fetchOperRegReg();
/** /**
* Executes an opcode, by means of directly * Executes an opcode, by means of directly
* accessing the instruction map and * accessing the instruction map and
@@ -469,36 +480,56 @@ namespace spider {
// Operation: # of 1's into Dst // Operation: # of 1's into Dst
void CNT(); void CNT();
// [Boolean] 0x030 — EQ: Equal // [Boolean] 0x02E — EQ: Equal
// Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F // Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F
// Operation: Dst == Src into Dst // Operation: Dst == Src into Dst
void EQ(); void EQ();
// [Boolean] 0x031 — NE: Not Equal // [Boolean] 0x02F — NE: Not Equal
// Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F // Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F
// Operation: Dst != Src into Dst // Operation: Dst != Src into Dst
void NE(); void NE();
// [Boolean] 0x032 — GT: Greater Than // [Boolean] 0x030 — GT: Greater Than
// Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F // Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F
// Operation: Dst > Src into Dst // Operation: Dst > Src into Dst
void GT(); void GT();
// [Boolean] 0x033 — GE: Greater or Equal Than // [Boolean] 0x031 — GE: Greater or Equal Than
// Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F // Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F
// Operation: Dst >= Src into Dst // Operation: Dst >= Src into Dst
void GE(); void GE();
// [Boolean] 0x034 — LT: Lower Than // [Boolean] 0x032 — LT: Lower Than
// Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F // Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F
// Operation: Dst < Src into Dst // Operation: Dst < Src into Dst
void LT(); void LT();
// [Boolean] 0x035 — LE: Lower or Equal Than // [Boolean] 0x033 — LE: Lower or Equal Than
// Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F // Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F
// Operation: Dst <= Src into Dst // Operation: Dst <= Src into Dst
void LE(); void LE();
// [Boolean] 0x034 — GTU: Greater Than, Unsigned
// Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F
// Operation: Dst > Src into Dst
void GTU();
// [Boolean] 0x035 — GEU: Greater or Equal Than, Unsigned
// Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F
// Operation: Dst >= Src into Dst
void GEU();
// [Boolean] 0x036 — LTU: Lower Than, Unsigned
// Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F
// Operation: Dst < Src into Dst
void LTU();
// [Boolean] 0x037 — LEU: Lower or Equal Than, Unsigned
// Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F
// Operation: Dst <= Src into Dst
void LEU();
// [Branch] 0x038 — JMP: Jump to absolute position // [Branch] 0x038 — JMP: Jump to absolute position
// Params: 1 | AddrMask1: FF AddrMask2: 00 | TypeMask: 0F // Params: 1 | AddrMask1: FF AddrMask2: 00 | TypeMask: 0F
// Operation: Dst -> Instruction Register // Operation: Dst -> Instruction Register
@@ -884,22 +915,6 @@ namespace spider {
// Operation: // Operation:
void UPY(); void UPY();
//[Easter Egg] 0x0F1 - LLGS: Injects the custom 8x4 ASCII spider logo
// into RAM [0x80-0x9F] and signs Register RA with the "LLGS" hex literal.
void LLGS();
// [Easter Eggs] 0x0F6 — DGANT: "In kaaba Spider" (Yucatec Maya: My name is Spider)
// Params: 0 | AddrMask1: 00 AddrMask2: 00 | TypeMask: 00
// Operation: Writes "IN KAABA SPIDER" one char per GP register
void DGANT();
/**
* @brief BRAD (0xF7) - Memory Integrity Checksum
* Escanea los primeros 256 bytes de memoria y valida contra una firma de seguridad.
* Implementado por Bradley Vergara Lara - Estancia 2026.
*/
void BRAD();
// </pygen-target> // // </pygen-target> //
}; };
-402
View File
@@ -1,402 +0,0 @@
#include "LiveDebug.hpp"
#include <spider/runtime/reel/InstrReelFixed.hpp>
#include <spider/runtime/Runtime.hpp>
#include <spider/runtime/util/Terminal.hpp>
#include <spider/runtime/native/distro.hpp>
#include <vector>
#include <string>
#include <iomanip>
#include <iostream>
#include <chrono>
#include <format>
#include <thread>
namespace spider {
void drawHead(Terminal& t) {
t.move(1, 1)
.style(Terminal::FG_YELLOW)
.print(" Spider Runtime Live Debug ")
.style(Terminal::RESET).print(" | ")
.style(Terminal::FG_B_CYAN).print(" Sintek Analytics @ 2026 ")
.style(Terminal::RESET).print(" | ")
.style(Terminal::FG_B_BLACK).print("Press ESC to exit")
.style(Terminal::FG_BLACK)
.style(Terminal::BG_YELLOW)
.move(3, 1).print(" // __ \\\\").print(" ") // 27
.move(4, 1).print(" \\\\( )//").print(" SPIDER v0.1 ")
.move(5, 1).print(" //()\\\\ ").print(" alpha ")
.move(6, 1).print(" \\\\ // ").print(" ")
.style(Terminal::RESET)
.style(Terminal::FG_B_BLACK) // 4x8 for the menu
.move(3, 28).print("[ STEP ]")
.move(4, 28).print("[ STOP ]")
.move(5, 28).print("[ RUN ]")
.move(6, 28).print("[ MENU ]")
.style(Terminal::RESET)
;
}
void drawCPUTempl(Terminal& t) {
i32 r = 8, c = 1;
i32 w = 35, h = 31;
t.drawBox(r, c, w, h, "CPU");
const std::string regs[] = {
"RA", "RB", "RC", "RD",
"RX", "RY", "R0", "R1",
"R2", "R3", "R4", "R5",
"R6", "R7", "R8", "R9",
"RF", "RI", "RS", "RZ",
"RE", "RN", "RV", "RM",
"ALU0", "ALU1"
};
const std::string alt[] = {
Terminal::FG_WHITE,
Terminal::FG_B_BLACK,
};
r++;
c++;
t.move(r++, c);
t.style(Terminal::FG_B_YELLOW);
t.print_center(w - 2, "GP Registers");
t.style(Terminal::RESET);
for (i32 i = 0; i < 8; i++) {
t.style(alt[i & 1]);
t.move(r + i * 2, c);
t.print(regs[i * 2]);
t.move(r + i * 2, c + 17);
t.print(regs[i * 2 + 1]);
}
t.move(r += 16, c);
t.style(Terminal::FG_B_CYAN);
t.print_center(w - 2, "System Registers");
t.style(Terminal::RESET);
r++;
for (i32 j = 0, i = 8; i < 12; j++, i++) {
t.style(alt[j & 1]);
t.move(r + j * 2, c);
t.print(regs[i * 2]);
t.move(r + j * 2, c + 17);
t.print(regs[i * 2 + 1]);
}
t.move(r += 8, c);
t.style(Terminal::FG_GREEN);
t.print_center(w - 2, "Extra Registers");
t.style(Terminal::RESET);
r++;
for (i32 j = 0, i = 12; i < 13; j++, i++) {
t.style(alt[j & 1]);
t.move(r + j * 2, c);
t.print(regs[i * 2]);
t.move(r + j * 2, c + 17);
t.print(regs[i * 2 + 1]);
}
t.flush();
}
void printU64Hex(u64 n) {
std::ios state(nullptr);
state.copyfmt(std::cout);
std::cout
<< std::hex
<< std::uppercase
<< std::setfill('0')
<< std::setw(16)
<< n;
std::cout.copyfmt(state);
}
void drawCPU(Terminal& t, CPU& cpu) {
i32 r = 8, c = 1;
const register_t* regs[] = {
&cpu.RA, &cpu.RB, &cpu.RC, &cpu.RD,
&cpu.RX, &cpu.RY, &cpu.R0, &cpu.R1,
&cpu.R2, &cpu.R3, &cpu.R4, &cpu.R5,
&cpu.R6, &cpu.R7, &cpu.R8, &cpu.R9,
//&cpu.RF, &cpu.RI, &cpu.RS, &cpu.RZ,
//&cpu.RE, &cpu.RN, &cpu.RV, &cpu.RM,
&cpu.ALU0, &cpu.ALU1
};
const u64* sys_regs[] = {
&cpu.RF, &cpu.RI, &cpu.RS, &cpu.RZ,
&cpu.RE, &cpu.RN, &cpu.RV, &cpu.RM,
};
const std::string alt[] = {
Terminal::FG_WHITE,
Terminal::FG_B_BLACK,
};
r++;
c++;
t.move(r++, c);
t.style(Terminal::RESET);
r++;
for (i32 i = 0; i < 8; i++) {
t.style(alt[i & 1]);
t.move(r + i * 2, c);
printU64Hex(regs[i * 2]->_u64);
t.move(r + i * 2, c + 17);
printU64Hex(regs[i * 2 + 1]->_u64);
}
t.move(r += 16, c);
r++;
for (i32 j = 0; j < 4; j++) {
t.style(alt[j & 1]);
t.move(r + j * 2, c);
printU64Hex(*sys_regs[j * 2]);
t.move(r + j * 2, c + 17);
printU64Hex(*sys_regs[j * 2 + 1]);
}
t.move(r += 8, c);
r++;
for (i32 j = 0; j < 1; j++) {
t.style(alt[j & 1]);
t.move(r + j * 2, c);
printU64Hex(regs[16 + j * 2]->_u64);
t.move(r + j * 2, c + 17);
printU64Hex(regs[16 + j * 2 + 1]->_u64);
}
t.flush();
}
u32 addressWidth(isize ramSize) {
if (ramSize == 0) return 1;
isize maxAddr = ramSize - 1;
u32 digits = 0;
// Shift by increments of 4 (one hex nibble)
// We use a do-while to ensure at least 1 digit is returned for small RAMs
do {
digits++;
maxAddr >>= 4;
} while (maxAddr > 0);
return digits;
}
/**
* Draws a vertical scrollbar
* @param x The column where the bar should be placed (usually box_x + width - 1)
* @param y The starting row of the track (usually box_y + 1)
* @param trackHeight The internal height of the box (box_height - 2)
* @param progress The current progress
* @param total The total
*/
void drawScrollThumb(Terminal& term, u32 x, u32 y, u32 trackHeight, isize progress, isize total) {
if (total == 0 || trackHeight <= 0) return;
// 1. Draw the background track (Light Shade: ░)
term.style(Terminal::FG_B_BLACK); // Dim the track
for (u32 i = 0; i < trackHeight; ++i) {
term.move(i32(y + i), i32(x)).print("");
}
// 2. Calculate Thumb Position
// Cap progress to total to avoid overflow
if (progress > total) progress = total;
// Calculate ratio (0.0 to 1.0)
f64 ratio = f64(progress) / f64(total);
// Map to track coordinates
u32 thumbOffset = u32(ratio * (trackHeight - 1));
// 3. Draw the Thumb (Full Block: █)
term.move(i32(y + thumbOffset), i32(x));
term.style(Terminal::FG_WHITE).print("");
term.style(Terminal::RESET);
}
/**
* Draws a hex dump of memory within a styled terminal box.
* @param term Reference to your Terminal instance
* @param ram The RAM
* @param scrollPos The starting address to display
* @param x Starting column
* @param y Starting row
* @param width Width of the box
* @param height Height of the box
*/
void drawRAM(Terminal& term, RAM& ram, u64 scrollPos) {
// 1. Draw the container box
u32 y = 3;
u32 height = 36;
// 2. Configuration for the hex layout
u32 addrWidth = addressWidth(ram.size());
u32 bytesPerRow = 8;
u32 displayRows = height - 2; // Subtract top/bottom borders
u32 width = (2 + 2 + 16 + 7 + 3 + 8 + 4) + addrWidth;
u32 x = 37;
// create box
term.drawBox(i32(y), i32(x), i32(width), i32(height), "RAM");
drawScrollThumb(term, x + width - 2, y + 1, height - 2, scrollPos, ram.size());
// Ensure scrollPos is within bounds and aligned
//if (scrollPos < 0) scrollPos = 0;
if (scrollPos > ram.size()) scrollPos = ram.size();
for (u32 i = 0; i < displayRows; ++i) {
isize currentRowAddr = scrollPos + (i * bytesPerRow);
// address lock
if (currentRowAddr >= ram.size()) {
term.move(i32(y + 1 + i), i32(x + 1));
term.print(std::string(width - 3, ' '));
continue;
}
std::stringstream ssaddr;
std::stringstream ss;
// setup ss
ssaddr << std::setfill('0') << std::uppercase << std::hex;
ss << std::setfill('0') << std::uppercase << std::hex;
// address
ssaddr << std::setw(i32(addrWidth)) << currentRowAddr << " ";
// Hex Bytes
std::string asciiPart = "";
for (u32 j = 0; j < bytesPerRow; ++j) {
isize targetAddr = currentRowAddr + j;
if (targetAddr >= ram.size()) {
ss << ""; // Padding for end of memory
asciiPart += "";
continue;
}
u8 byte = ram[targetAddr];
ss << std::setfill('0') << std::setw(2) << std::hex << u32(byte) << " ";
asciiPart += (std::isprint(byte) ? char(byte) : '.');
}
// --- Combine and Print ---
term.move(i32(y + 1 + i), i32(x + 2)); // Move inside the box
term.style(Terminal::FG_B_CYAN).print(ssaddr.str()); // Hex part in Cyan
term.style(Terminal::FG_WHITE).print(ss.str());
term.style(Terminal::FG_B_YELLOW).print(" | ");
term.style(Terminal::FG_WHITE).print(asciiPart); // ASCII part in White
}
term.style(Terminal::RESET);
term.flush();
}
std::string getTimestamp() {
std::time_t t = std::time(nullptr);
std::tm lt;
#if defined(SPIDER_OS_WINDOWS)
localtime_s(&lt, &t);
#endif
#if defined(SPIDER_OS_LINUX) || defined(SPIDER_OS_MACOS)
localtime_r(&t, &lt);
#endif
return std::format("{:02}:{:02}:{:02} {:02}/{:02}/{}",
lt.tm_hour, lt.tm_min, lt.tm_sec,
lt.tm_mday, lt.tm_mon + 1, lt.tm_year + 1900);
}
void drawTime(Terminal& t) {
//auto now = std::chrono::system_clock::now();
//auto now_l = std::chrono::current_zone()->to_local(now);
//auto now_s = std::chrono::floor<std::chrono::seconds>(now_l);
//std::string time_str = std::format("{:%H:%M:%S}", now_s); // Format: HH:mm:ss
//std::string date_str = std::format("{:%d/%m/%Y}", now_s); // Format: dd/MM/YYYY
t.move(1, 76);
t.style(Terminal::RESET);
t.print(" | ").style(Terminal::FG_GREEN).print(getTimestamp());
}
void redraw(Terminal& t, Runtime& r, u64 scroll) {
// draw CPU, RAM
drawCPU(t, r.cpu);
drawRAM(t, r.ram, scroll);
}
int liveDebugMain() {
Terminal t;
Runtime runtime(1024);
InstrReelFixed fix(100);
runtime.hookReel(&fix, false);
bool running = true, update = true;
u64 ramScroll = 0;
u8 key = Terminal::UNKNOWN;
t.println("Starting Spider live debug...");
t.altbuff(true).cursor(false);
drawTime(t);
drawHead(t);
drawCPUTempl(t);
// delay for time
auto last_exec = std::chrono::steady_clock::now();
auto delay = std::chrono::milliseconds(1000);
while (running) {
// draw time
auto now = std::chrono::steady_clock::now();
if (now - last_exec >= delay) {
drawTime(t);
last_exec = now;
}
// redraw something if it updated
if (update) {
redraw(t, runtime, ramScroll);
update = false;
}
// Handle Input
key = t.getKeyNb();
switch (key) {
case Terminal::ESC:
running = false;
break;
case Terminal::UP:
if (ramScroll >= 16) ramScroll -= 16;
update = true;
break;
case Terminal::DOWN:
if (runtime.ram.size() >= 16 && ramScroll <= runtime.ram.size() - 16) ramScroll += 16;
update = true;
break;
case Terminal::ENTER:
update = true;
runtime.cpu.fetchInstr();
runtime.cpu.execute(); // looks up instrMap[_opcode] & calls the correct instruction method (e.g. FMUL)
break;
default:
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
t.altbuff(false).println("Stopped Spider live debug.").flush();
return 0;
}
}
-7
View File
@@ -1,7 +0,0 @@
#pragma once
namespace spider {
int liveDebugMain();
}
+22 -20
View File
@@ -81,16 +81,16 @@ CPU::Fn CPU::instrMap[] = {
&CPU::ROL, // 0x02B — Rotate Left &CPU::ROL, // 0x02B — Rotate Left
&CPU::ROR, // 0x02C — Rotate Right &CPU::ROR, // 0x02C — Rotate Right
&CPU::CNT, // 0x02D — Counts bits &CPU::CNT, // 0x02D — Counts bits
nullptr, // 0x02E (reserved) &CPU::EQ, // 0x02E — Equal
nullptr, // 0x02F (reserved) &CPU::NE, // 0x02F — Not Equal
&CPU::EQ, // 0x030 — Equal &CPU::GT, // 0x030 — Greater Than
&CPU::NE, // 0x031 — Not Equal &CPU::GE, // 0x031 — Greater or Equal Than
&CPU::GT, // 0x032 — Greater Than &CPU::LT, // 0x032 — Lower Than
&CPU::GE, // 0x033 — Greater or Equal Than &CPU::LE, // 0x033 — Lower or Equal Than
&CPU::LT, // 0x034 — Lower Than &CPU::GTU, // 0x034 — Greater Than, Unsigned
&CPU::LE, // 0x035 — Lower or Equal Than &CPU::GEU, // 0x035 — Greater or Equal Than, Unsigned
nullptr, // 0x036 (reserved) &CPU::LTU, // 0x036 — Lower Than, Unsigned
nullptr, // 0x037 (reserved) &CPU::LEU, // 0x037 — Lower or Equal Than, Unsigned
&CPU::JMP, // 0x038 — Jump to absolute position &CPU::JMP, // 0x038 — Jump to absolute position
&CPU::JEQ, // 0x039 — Jumps to position if EQ flag is set &CPU::JEQ, // 0x039 — Jumps to position if EQ flag is set
&CPU::JNE, // 0x03A — Jumps to position if EQ flag is cleared &CPU::JNE, // 0x03A — Jumps to position if EQ flag is cleared
@@ -276,12 +276,12 @@ CPU::Fn CPU::instrMap[] = {
nullptr, // 0x0EE nullptr, // 0x0EE
nullptr, // 0x0EF nullptr, // 0x0EF
&CPU::UPY, // 0x0F0 — Will place "YUPI" in memory &CPU::UPY, // 0x0F0 — Will place "YUPI" in memory
&CPU::LLGS, // 0x0F1 — Spider ASCII art (LLGS easter egg) nullptr, // 0x0F1
nullptr, // 0x0F2 nullptr, // 0x0F2
nullptr, // 0x0F3 nullptr, // 0x0F3
nullptr, // 0x0F4 nullptr, // 0x0F4
nullptr, // 0x0F5 nullptr, // 0x0F5
&CPU::DGANT, // 0x0F6 nullptr, // 0x0F6
nullptr, // 0x0F7 nullptr, // 0x0F7
nullptr, // 0x0F8 nullptr, // 0x0F8
nullptr, // 0x0F9 nullptr, // 0x0F9
@@ -622,12 +622,16 @@ void CPU::executeSwLk() {
case 0x02D: CNT(); break; case 0x02D: CNT(); break;
// ── Boolean ───────────────────────────────────── // ── Boolean ─────────────────────────────────────
case 0x030: EQ(); break; case 0x02E: EQ(); break;
case 0x031: NE(); break; case 0x02F: NE(); break;
case 0x032: GT(); break; case 0x030: GT(); break;
case 0x033: GE(); break; case 0x031: GE(); break;
case 0x034: LT(); break; case 0x032: LT(); break;
case 0x035: LE(); break; case 0x033: LE(); break;
case 0x034: GTU(); break;
case 0x035: GEU(); break;
case 0x036: LTU(); break;
case 0x037: LEU(); break;
// ── Branch ────────────────────────────────────── // ── Branch ──────────────────────────────────────
case 0x038: JMP(); break; case 0x038: JMP(); break;
@@ -737,8 +741,6 @@ void CPU::executeSwLk() {
// ── Easter Eggs ───────────────────────────────── // ── Easter Eggs ─────────────────────────────────
case 0x0F0: UPY(); break; case 0x0F0: UPY(); break;
case 0x0F1: LLGS(); break;
default: default:
break; break;
+56 -24
View File
@@ -4,15 +4,16 @@
*/ */
#include <spider/runtime/cpu/CPU.hpp> #include <spider/runtime/cpu/CPU.hpp>
#include <spider/runtime/memory/RAM.hpp>
namespace spider { namespace spider {
void CPU::NOP() { void CPU::NOP() {
// TODO: Implement NOP // DONE ! //
} }
void CPU::SPDR() { void CPU::SPDR() {
// TODO: Implement SPDR RA._u64 = spider::RUNTIME_VERSION_NO;
} }
void CPU::MMODE() { void CPU::MMODE() {
@@ -21,10 +22,13 @@ namespace spider {
void CPU::INT() { void CPU::INT() {
// TODO: Implement INT // TODO: Implement INT
// We need to implement an interrupt
// table and interrupt system!
} }
void CPU::LRV() { void CPU::LRV() {
// TODO: Implement LRV fetchOperReg();
RV = _dst->_u64;
} }
void CPU::FSR() { void CPU::FSR() {
@@ -32,11 +36,13 @@ namespace spider {
} }
void CPU::FIR() { void CPU::FIR() {
// TODO: Implement FIR fetchOperReg();
_dst->_u64 = RI;
} }
void CPU::FZR() { void CPU::FZR() {
// TODO: Implement FZR fetchOperReg();
_dst->_u64 = RZ;
} }
void CPU::LSR() { void CPU::LSR() {
@@ -44,23 +50,63 @@ namespace spider {
} }
void CPU::FVR() { void CPU::FVR() {
// TODO: Implement FVR fetchOperReg();
_dst->_u64 = RV;
} }
void CPU::MOV() { void CPU::MOV() {
// TODO: Implement MOV fetchOperSrc();
fetchOperDst();
switch (_size) {
case 0b00: //byte
_dst->_u8 = _src->_u8;
break;
case 0b01: //short
_dst->_u16 = _src->_u16;
break;
case 0b10: //int
_dst->_u32 = _src->_u32;
break;
case 0b11: //long
_dst->_u64 = _src->_u64;
break;
}
} }
void CPU::MOR() { void CPU::MOR() {
// TODO: Implement MOR fetchOperRegReg();
*_dst = *_src;
} }
void CPU::AMOV() { void CPU::AMOV() {
// TODO: Implement AMOV // AMOV potential is capped at 256 bytes!
u64 amt = RA._u16;
u64 from = RX._u64;
u64 to = RY._u64;
if(amt > 256) return;
// are RX and RY in valid regions of memory?
u64 ramsize = _ram->size();
if (from >= ramsize || amt > (ramsize - from)) return;
if (to >= ramsize || amt > (ramsize - to)) return;
// Use std::copy_backward if destination overlaps ahead of source
auto ram_begin = _ram->begin();
if (to > from && to < from + amt) {
// Overlap case where destination is ahead of source: copy from back to front
std::copy_backward(ram_begin + from, ram_begin + from + amt, ram_begin + to + amt);
} else {
// No overlap, or destination is behind source: copy front to back
std::copy(ram_begin + from, ram_begin + from + amt, ram_begin + to);
}
} }
void CPU::SWP() { void CPU::SWP() {
// TODO: Implement SWP // get registers in _dst and _src
fetchOperRegReg();
ALU0 = *_dst;
*_dst = *_src;
*_src = ALU0;
} }
void CPU::AHM() { void CPU::AHM() {
@@ -86,7 +132,6 @@ namespace spider {
} }
void CPU::NEG() { void CPU::NEG() {
// TODO: Implement NEG
fetchOperDst(); fetchOperDst();
switch (_size) { switch (_size) {
case 0b00: //byte case 0b00: //byte
@@ -123,7 +168,6 @@ namespace spider {
} }
void CPU::INC() { void CPU::INC() {
// TODO: Implement INC
fetchOperDst(); fetchOperDst();
switch (_size) { switch (_size) {
case 0b00: //byte case 0b00: //byte
@@ -142,7 +186,6 @@ namespace spider {
} }
void CPU::DEC() { void CPU::DEC() {
// TODO: Implement DEC
fetchOperDst(); fetchOperDst();
switch (_size) { switch (_size) {
case 0b00: //byte case 0b00: //byte
@@ -161,7 +204,6 @@ namespace spider {
} }
void CPU::ADD() { void CPU::ADD() {
// TODO: Implement ADD
fetchOperSrc(); fetchOperSrc();
fetchOperDst(); fetchOperDst();
switch (_size) { switch (_size) {
@@ -181,7 +223,6 @@ namespace spider {
} }
void CPU::SUB() { void CPU::SUB() {
// TODO: Implement SUB
fetchOperSrc(); fetchOperSrc();
fetchOperDst(); fetchOperDst();
switch (_size) { switch (_size) {
@@ -201,7 +242,6 @@ namespace spider {
} }
void CPU::MUL() { void CPU::MUL() {
// TODO: Implement MUL
fetchOperSrc(); fetchOperSrc();
fetchOperDst(); fetchOperDst();
switch (_size) { switch (_size) {
@@ -221,7 +261,6 @@ namespace spider {
} }
void CPU::UMUL() { void CPU::UMUL() {
// TODO: Implement UMUL
fetchOperSrc(); fetchOperSrc();
fetchOperDst(); fetchOperDst();
switch (_size) { switch (_size) {
@@ -241,7 +280,6 @@ namespace spider {
} }
void CPU::DIV() { void CPU::DIV() {
// TODO: Implement DIV
fetchOperSrc(); fetchOperSrc();
fetchOperDst(); fetchOperDst();
switch (_size) { switch (_size) {
@@ -261,7 +299,6 @@ namespace spider {
} }
void CPU::UDIV() { void CPU::UDIV() {
// TODO: Implement UDIV
fetchOperSrc(); fetchOperSrc();
fetchOperDst(); fetchOperDst();
switch (_size) { switch (_size) {
@@ -281,7 +318,6 @@ namespace spider {
} }
void CPU::MOD() { void CPU::MOD() {
// TODO: Implement MOD
fetchOperSrc(); fetchOperSrc();
fetchOperDst(); fetchOperDst();
switch (_size) { switch (_size) {
@@ -301,7 +337,6 @@ namespace spider {
} }
void CPU::UMOD() { void CPU::UMOD() {
// TODO: Implement UMOD
fetchOperSrc(); fetchOperSrc();
fetchOperDst(); fetchOperDst();
switch (_size) { switch (_size) {
@@ -321,7 +356,6 @@ namespace spider {
} }
void CPU::DMOD() { void CPU::DMOD() {
// TODO: Implement DMOD
fetchOperSrc(); fetchOperSrc();
fetchOperDst(); fetchOperDst();
switch (_size) { switch (_size) {
@@ -345,7 +379,6 @@ namespace spider {
} }
void CPU::UDMD() { void CPU::UDMD() {
// TODO: Implement UDMD
fetchOperSrc(); fetchOperSrc();
fetchOperDst(); fetchOperDst();
switch (_size) { switch (_size) {
@@ -369,7 +402,6 @@ namespace spider {
} }
void CPU::FBT() { void CPU::FBT() {
// TODO: Implement FBT
fetchOperDst(); fetchOperDst();
switch (_size) { switch (_size) {
case 0b00: //byte case 0b00: //byte
-61
View File
@@ -1,61 +0,0 @@
/**
* @brief LLGS — Easter egg by Arturo Balam (Data - 7A)
*
* Opcode: 0x0F1
*
* Writes a Spider ASCII art into RAM starting at address 0x00,
* and loads the author signature into RA as a packed ASCII string.
* This version matches the custom mechanical spider design
* and is formatted to fit an 8-byte RAM viewer width.
*
* RAM layout after LLGS executes (8 characters per row, 4 rows total):
* 0x00: "// _ \\" (Row 1)
* 0x08: "\\( )// " (Row 2)
* 0x10: " //()\\ " (Row 3)
* 0x18: " \\ // " (Row 4)
*
* RA after execution: 0x4C4C475300000000ULL ("LLGS" in ASCII, zero-padded)
* (L=0x4C, L=0x4C, G=0x47, S=0x53)
*/
#include <spider/runtime/cpu/CPU.hpp>
#include <spider/runtime/memory/RAM.hpp>
namespace spider {
void CPU::LLGS() {
// -- Write Spider ASCII art into RAM ---------------------------------
// Padded with exact spaces to ensure it never wraps in an 8-byte viewer
// Row 0: "// _ \\ "
_ram->at(0x00) = '/'; _ram->at(0x01) = '/';
_ram->at(0x02) = ' '; _ram->at(0x03) = '_';
_ram->at(0x04) = ' '; _ram->at(0x05) = '\\';
_ram->at(0x06) = '\\'; _ram->at(0x07) = ' ';
// Row 1: "\\( )// "
_ram->at(0x08) = '\\'; _ram->at(0x09) = '\\';
_ram->at(0x0A) = '('; _ram->at(0x0B) = ' ';
_ram->at(0x0C) = ')'; _ram->at(0x0D) = '/';
_ram->at(0x0E) = '/'; _ram->at(0x0F) = ' ';
// Row 2: " //()\\ "
_ram->at(0x10) = ' '; _ram->at(0x11) = '/';
_ram->at(0x12) = '/'; _ram->at(0x13) = '(';
_ram->at(0x14) = ')'; _ram->at(0x15) = '\\';
_ram->at(0x16) = '\\'; _ram->at(0x17) = ' ';
// Row 3: " \\ // "
_ram->at(0x18) = ' '; _ram->at(0x19) = '\\';
_ram->at(0x1A) = '\\'; _ram->at(0x1B) = ' ';
_ram->at(0x1C) = ' '; _ram->at(0x1D) = '/';
_ram->at(0x1E) = '/'; _ram->at(0x1F) = ' ';
// -- Load mnemonic into RA ------------------------
// "LLGS" packed as ASCII bytes into RA
RA._u64 = 0x4C4C475300000000ULL;
}
} // namespace spider
-118
View File
@@ -1,118 +0,0 @@
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#ifndef M_E
#define M_E 2.71828182845904523536
#endif
#include <spider/runtime/cpu/CPU.hpp>
#include <iostream>
#include <cmath>
using namespace spider;
void check(const char* name, double result, double expected, double tolerance = 1e-9) {
bool ok = std::abs(result - expected) <= tolerance;
std::cout << (ok ? "[PASS] " : "[FAIL] ") << name
<< " = " << result
<< " (expected " << expected << ")\n";
}
int main() {
std::cout << "=== Spider VM Instruction Test: 0x068-0x079 ===\n\n";
CPU cpu;
cpu._post = &CPU::imp;
std::cout << "-- Cast Instructions --\n";
cpu.RA._f64 = 3.9;
cpu._dst = &cpu.RA;
cpu.D2I();
check("D2I (3.9 -> 3)", cpu.RA._u32, 3.0);
cpu.RA._f64 = 1e12;
cpu._dst = &cpu.RA;
cpu.D2L();
check("D2L (1e12)", (double)cpu.RA._u64, 1e12);
std::cout << "\n-- Trigonometric Instructions --\n";
cpu.RA._f64 = M_PI / 2.0;
cpu._dst = &cpu.RA;
cpu.SIN();
check("SIN(pi/2)", cpu.RA._f64, 1.0);
cpu.RA._f64 = 0.0;
cpu._dst = &cpu.RA;
cpu.COS();
check("COS(0)", cpu.RA._f64, 1.0);
cpu.RA._f64 = M_PI / 4.0;
cpu._dst = &cpu.RA;
cpu.TAN();
check("TAN(pi/4)", cpu.RA._f64, 1.0);
cpu.RA._f64 = 1.0;
cpu._dst = &cpu.RA;
cpu.ASIN();
check("ASIN(1.0)", cpu.RA._f64, M_PI / 2.0);
cpu.RA._f64 = 1.0;
cpu._dst = &cpu.RA;
cpu.ACOS();
check("ACOS(1.0)", cpu.RA._f64, 0.0);
cpu.RA._f64 = 1.0;
cpu._dst = &cpu.RA;
cpu.ATAN();
check("ATAN(1.0)", cpu.RA._f64, M_PI / 4.0);
cpu.RA._f64 = 1.0;
cpu.RB._f64 = 1.0;
cpu._dst = &cpu.RA;
cpu._src = &cpu.RB;
cpu.ATAN2();
check("ATAN2(1,1)", cpu.RA._f64, M_PI / 4.0);
std::cout << "\n-- Exponential Instructions --\n";
cpu.RA._f64 = 1.0;
cpu._dst = &cpu.RA;
cpu.EXP();
check("EXP(1)", cpu.RA._f64, M_E);
cpu.RA._f64 = M_E;
cpu._dst = &cpu.RA;
cpu.LOG();
check("LOG(e)", cpu.RA._f64, 1.0);
cpu.RA._f64 = 100.0;
cpu.RB._f64 = 10.0;
cpu._dst = &cpu.RA;
cpu._src = &cpu.RB;
cpu.LOGAB();
check("LOGAB(100,10)", cpu.RA._f64, 2.0);
cpu.RA._f64 = 2.0;
cpu.RB._f64 = 10.0;
cpu._dst = &cpu.RA;
cpu._src = &cpu.RB;
cpu.POW();
check("POW(2,10)", cpu.RA._f64, 1024.0);
cpu.RA._f64 = 9.0;
cpu._dst = &cpu.RA;
cpu.SQRT();
check("SQRT(9)", cpu.RA._f64, 3.0);
cpu.RA._f64 = 27.0;
cpu.RB._f64 = 3.0;
cpu._dst = &cpu.RA;
cpu._src = &cpu.RB;
cpu.ROOT();
check("ROOT(27,3)", cpu.RA._f64, 3.0);
std::cout << "\n=== Tests complete ===\n";
return 0;
}