diff --git a/autogen/InstructionMasks.hpp b/autogen/InstructionMasks.hpp index 34a0909..54ad74a 100644 --- a/autogen/InstructionMasks.hpp +++ b/autogen/InstructionMasks.hpp @@ -58,6 +58,10 @@ constexpr u8 ADDR_MODE_MASKS[][2] = { { 0x1E, 0xFF }, // GE { 0x1E, 0xFF }, // LT { 0x1E, 0xFF }, // LE + { 0x1E, 0xFF }, // GTU + { 0x1E, 0xFF }, // GEU + { 0x1E, 0xFF }, // LTU + { 0x1E, 0xFF }, // LEU { 0xFF, 0x00 }, // JMP { 0xFF, 0x00 }, // JEQ { 0xFF, 0x00 }, // JNE @@ -190,6 +194,10 @@ constexpr u8 TYPE_SIZE_MASKS[] = { 0x0F, // GE 0x0F, // LT 0x0F, // LE + 0x0F, // GTU + 0x0F, // GEU + 0x0F, // LTU + 0x0F, // LEU 0x0F, // JMP 0x0F, // JEQ 0x0F, // JNE diff --git a/calling-convention.ipynb b/calling-convention.ipynb deleted file mode 100644 index c71af20..0000000 --- a/calling-convention.ipynb +++ /dev/null @@ -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 -} diff --git a/docs/Spider Instructions.xlsx b/docs/Spider Instructions.xlsx index fd32457..d9da606 100644 Binary files a/docs/Spider Instructions.xlsx and b/docs/Spider Instructions.xlsx differ diff --git a/pygen.ipynb b/pygen.ipynb index 5b814a6..79251c1 100644 --- a/pygen.ipynb +++ b/pygen.ipynb @@ -144,7 +144,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "id": "58645013", "metadata": {}, "outputs": [ @@ -152,16 +152,16 @@ "name": "stdout", "output_type": "stream", "text": [ - "Real instructions : 128\n", - "Reserved slots : 14\n", + "Real instructions : 132\n", + "Reserved slots : 10\n", "Duplicate check : PASSED\n", "\n", "Groups found:\n", "group\n", "Integer 19\n", + "Boolean 16\n", "System 15\n", "Bit Wise 14\n", - "Boolean 12\n", "Branch 12\n", "Floating Point 10\n", "Casts 10\n", @@ -283,7 +283,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "id": "452bc76c", "metadata": {}, "outputs": [ @@ -292,7 +292,7 @@ "output_type": "stream", "text": [ "Masks written to: .//autogen/InstructionMasks.hpp\n", - "Lines generated : 272\n" + "Lines generated : 280\n" ] } ], @@ -357,7 +357,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "id": "5aaebef0", "metadata": {}, "outputs": [ @@ -365,7 +365,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Instructions formatted: 128\n", + "Instructions formatted: 132\n", "\n", "--- Preview (first 2 instructions) ---\n", " // [System] 0x000 — NOP: No Operation\n", @@ -380,7 +380,7 @@ "\n", "\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", - "execution_count": 7, + "execution_count": 8, "id": "instrmap_gen", "metadata": {}, "outputs": [ @@ -460,9 +460,9 @@ "output_type": "stream", "text": [ "InstrMap.cpp written to: .//src//spider/runtime/instr/InstrMap.cpp\n", - " Size : 34,157 bytes\n", - " Array entries : 512 (128 populated, 384 nullptr)\n", - " Switch cases : 128\n", + " Size : 33,581 bytes\n", + " Array entries : 512 (132 populated, 380 nullptr)\n", + " Switch cases : 132\n", " Line endings : LF-only verified\n" ] } @@ -545,11 +545,11 @@ " mnem = opcode_to_mnem.get(opc)\n", " if mnem:\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", " tag = ''\n", " if opc in reserved_opcodes:\n", - " tag = ' (reserved)'\n", + " tag = ' (reserved)'\n", " L.append(f' {\"nullptr,\":<28s}// 0x{opc:03X}{tag}')\n", "\n", "L.append('};')\n", @@ -681,7 +681,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.14.3" + "version": "3.13.7" } }, "nbformat": 4, diff --git a/src/spider/SpiderRuntime.cpp b/src/spider/SpiderRuntime.cpp index 8c84608..fd35d78 100644 --- a/src/spider/SpiderRuntime.cpp +++ b/src/spider/SpiderRuntime.cpp @@ -1,18 +1,10 @@ #include "SpiderRuntime.hpp" -#include - #include namespace spider { - const u32 RUNTIME_VERSION_NO = 0x00000000; // v0.1 - const std::string RUNTIME_VERSION = "alpha v0.1"; - + const u32 RUNTIME_VERSION_NO = 0x00000001; // v0.1 + const std::string RUNTIME_VERSION = "alpha v0.1"; } - -int main() { - spider::liveDebugMain(); - return 0; -} diff --git a/src/spider/runtime/cpu/CPU.cpp b/src/spider/runtime/cpu/CPU.cpp index 7bba8a4..92159a9 100644 --- a/src/spider/runtime/cpu/CPU.cpp +++ b/src/spider/runtime/cpu/CPU.cpp @@ -76,8 +76,8 @@ namespace spider { void CPU::fetchOperDst() { // Move the operand ptrs - _alu = &ALU0; _opers[1] = _opers[0]; + _opers[0] = &ALU0; // call specific addressing mode (this->*(CPU::addrModes[_addrm & 0b111]))(); // mask added here too @@ -85,7 +85,7 @@ namespace spider { void CPU::fetchOperSrc() { // set ALU - _alu = &ALU1; + _opers[0] = &ALU1; // call specific addressing mode (this->*(CPU::addrModes[_addrm & 0b111]))(); // mask keeps index within 0-7 @@ -95,13 +95,23 @@ namespace spider { _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() { (this->*(CPU::instrMap[_opcode]))(); // no null check needed + // vvv MUST PROFILE: Is this faster than executing an empty function? if(_post) (this->*_post)(); } - // Addressing Modes // /** @@ -115,8 +125,7 @@ namespace spider { * Immediate Addressing Mode */ void CPU::imm() { - _reel->loadRegister(RI, _size, _alu); - _opers[0] = _alu; + _reel->loadRegister(RI, _size, _opers[0]); _post = &CPU::imp; RI += 1 << _size; } @@ -127,19 +136,19 @@ namespace spider { void CPU::abs() { // Load the actual ptr into the ALU u8 mm = u8(getFlag(CPU::FLAG_MEMORY_MODE)); - _reel->loadRegister(RI, mm, _alu); + _reel->loadRegister(RI, mm, _opers[0]); RI += 1 << mm; // read the memory from RAM - _store = _alu->_u64; - _ram->loadRegister(_store, _size, _alu); + _store = _opers[0]->_u64; + _ram->loadRegister(_store, _size, _opers[0]); _post = &CPU::psw; } /** * Register Addressing Mode */ - void CPU::reg() { // NOT FINISHED + void CPU::reg() { // Two consecutive registers can be declared // Shift if the top part will become .reg too u8 sh = ((_addrm & 0b11000) == 0b11000) * 4; @@ -147,12 +156,11 @@ namespace spider { // get byte u8 reg = (_reel->readU8(RI) >> sh) & 0xF; - _alu = &GPR[reg]; - _opers[0] = _alu; // explicitly sets _opers[0] = _dst + _opers[0] = &GPR[reg]; // explicitly sets _opers[0] = _dst RI += use; // store no-op - _post = &CPU::imp; + _post = nullptr; } /** diff --git a/src/spider/runtime/cpu/CPU.hpp b/src/spider/runtime/cpu/CPU.hpp index 07709e9..73f9c66 100644 --- a/src/spider/runtime/cpu/CPU.hpp +++ b/src/spider/runtime/cpu/CPU.hpp @@ -64,7 +64,6 @@ namespace spider { struct { register_t* _dst; register_t* _src; - register_t* _alu; }; register_t* _opers[2]; }; @@ -172,6 +171,18 @@ namespace spider { */ 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 * accessing the instruction map and @@ -469,36 +480,56 @@ namespace spider { // Operation: # of 1's into Dst void CNT(); - // [Boolean] 0x030 — EQ: Equal + // [Boolean] 0x02E — EQ: Equal // Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F // Operation: Dst == Src into Dst void EQ(); - // [Boolean] 0x031 — NE: Not Equal + // [Boolean] 0x02F — NE: Not Equal // Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F // Operation: Dst != Src into Dst void NE(); - // [Boolean] 0x032 — GT: Greater Than + // [Boolean] 0x030 — GT: Greater Than // Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F // Operation: Dst > Src into Dst void GT(); - // [Boolean] 0x033 — GE: Greater or Equal Than + // [Boolean] 0x031 — GE: Greater or Equal Than // Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F // Operation: Dst >= Src into Dst void GE(); - // [Boolean] 0x034 — LT: Lower Than + // [Boolean] 0x032 — LT: Lower Than // Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F // Operation: Dst < Src into Dst void LT(); - // [Boolean] 0x035 — LE: Lower or Equal Than + // [Boolean] 0x033 — LE: Lower or Equal Than // Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F // Operation: Dst <= Src into Dst 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 // Params: 1 | AddrMask1: FF AddrMask2: 00 | TypeMask: 0F // Operation: Dst -> Instruction Register @@ -541,22 +572,22 @@ namespace spider { // [System] 0x040 — SFB: Store (User) Flag Bit // Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F - // Operation: + // Operation: void SFB(); // [System] 0x041 — LFB: Load (User) Flag Bit // Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F - // Operation: + // Operation: void LFB(); // [Branch] 0x042 — JUF: Jump to absolute position, if user flag is true // Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F - // Operation: + // Operation: void JUF(); // [Branch] 0x043 — JUR: Jump to relative position, if user flag is true // Params: 2 | AddrMask1: 1E AddrMask2: FF | TypeMask: 0F - // Operation: + // Operation: void JUR(); // [Memory] 0x044 — PUSH: Push to stack @@ -601,7 +632,7 @@ namespace spider { // [Floating Point] 0x050 — FLI: Float Load Immediate // Params: 1 | AddrMask1: FF AddrMask2: 00 | TypeMask: 0C - // Operation: + // Operation: void FLI(); // [Floating Point] 0x051 — FNEG: Float negate @@ -816,89 +847,73 @@ namespace spider { // [Matrix] 0x080 — MADD: Matrix Addition // Params: 0 | AddrMask1: 00 AddrMask2: 00 | TypeMask: 00 - // Operation: + // Operation: void MADD(); // [Matrix] 0x081 — MSUB: Matrix Subtraction // Params: 0 | AddrMask1: 00 AddrMask2: 00 | TypeMask: 00 - // Operation: + // Operation: void MSUB(); // [Matrix] 0x082 — MMUL: Matrix Multiply // Params: 0 | AddrMask1: 00 AddrMask2: 00 | TypeMask: 00 - // Operation: + // Operation: void MMUL(); // [Matrix] 0x083 — MINV: Matrix Inverse // Params: 0 | AddrMask1: 00 AddrMask2: 00 | TypeMask: 00 - // Operation: + // Operation: void MINV(); // [Matrix] 0x084 — MTRA: Matrix Transpose // Params: 0 | AddrMask1: 00 AddrMask2: 00 | TypeMask: 00 - // Operation: + // Operation: void MTRA(); // [Matrix] 0x085 — MDET: Matrix Determinant // Params: 0 | AddrMask1: 00 AddrMask2: 00 | TypeMask: 00 - // Operation: + // Operation: void MDET(); // [Quaternion] 0x086 — QMKA: Quaternion Make from Angles // Params: 0 | AddrMask1: 00 AddrMask2: 00 | TypeMask: 00 - // Operation: + // Operation: void QMKA(); // [Quaternion] 0x087 — QMUL: Quaternion Multiply // Params: 0 | AddrMask1: 00 AddrMask2: 00 | TypeMask: 00 - // Operation: + // Operation: void QMUL(); // [SIMD] 0x08A — XADD: SIMD Addition // Params: 0 | AddrMask1: 00 AddrMask2: 00 | TypeMask: 00 - // Operation: + // Operation: void XADD(); // [SIMD] 0x08B — XSUB: SIMD Subtract // Params: 0 | AddrMask1: 00 AddrMask2: 00 | TypeMask: 00 - // Operation: + // Operation: void XSUB(); // [SIMD] 0x08C — XAMA: SIMD Alternate Multiply-Add // Params: 0 | AddrMask1: 00 AddrMask2: 00 | TypeMask: 00 - // Operation: + // Operation: void XAMA(); // [SIMD] 0x08D — XMUL: SIMD Multiply // Params: 0 | AddrMask1: 00 AddrMask2: 00 | TypeMask: 00 - // Operation: + // Operation: void XMUL(); // [SIMD] 0x08E — XDIV: SIMD Divide // Params: 0 | AddrMask1: 00 AddrMask2: 00 | TypeMask: 00 - // Operation: + // Operation: void XDIV(); // [Easter Eggs] 0x0F0 — UPY: Will place "YUPI" in memory // Params: 0 | AddrMask1: 00 AddrMask2: 00 | TypeMask: 00 - // Operation: + // Operation: 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(); // // diff --git a/src/spider/runtime/debug/LiveDebug.cpp b/src/spider/runtime/debug/LiveDebug.cpp deleted file mode 100644 index 0c81c3f..0000000 --- a/src/spider/runtime/debug/LiveDebug.cpp +++ /dev/null @@ -1,402 +0,0 @@ -#include "LiveDebug.hpp" - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -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(<, &t); -#endif -#if defined(SPIDER_OS_LINUX) || defined(SPIDER_OS_MACOS) - localtime_r(&t, <); -#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(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; - } - -} diff --git a/src/spider/runtime/debug/LiveDebug.hpp b/src/spider/runtime/debug/LiveDebug.hpp deleted file mode 100644 index 69140cb..0000000 --- a/src/spider/runtime/debug/LiveDebug.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -namespace spider { - - int liveDebugMain(); - -} diff --git a/src/spider/runtime/instr/InstrMap.cpp b/src/spider/runtime/instr/InstrMap.cpp index 35e8f78..c6a29c0 100644 --- a/src/spider/runtime/instr/InstrMap.cpp +++ b/src/spider/runtime/instr/InstrMap.cpp @@ -35,149 +35,149 @@ namespace spider { * if (fn) (cpu.*fn)(); */ CPU::Fn CPU::instrMap[] = { - &CPU::NOP, // 0x000 — No Operation - &CPU::SPDR, // 0x001 — Will place the Spider version of the interpreter in RA - &CPU::MMODE, // 0x002 — Set Memory Mode - &CPU::INT, // 0x003 — Interrupt - &CPU::LRV, // 0x004 — Load Interrupt Vector Register - &CPU::FSR, // 0x005 — Fetch System Register - &CPU::FIR, // 0x006 — Fetch Instruction Register - &CPU::FZR, // 0x007 — Fetch Stack Base Register - &CPU::LSR, // 0x008 — Load System Register - &CPU::FVR, // 0x009 — Fetch Interrupt Vector Register - &CPU::MOV, // 0x00A — Moves values - &CPU::MOR, // 0x00B — Moves registers - &CPU::AMOV, // 0x00C — Array Move, uses X and Y as ptrs, A as amount - &CPU::SWP, // 0x00D — Swap registers - &CPU::AHM, // 0x00E — Ask Host for Memory - nullptr, // 0x00F (reserved) - &CPU::COM, // 0x010 — One's complement - &CPU::NEG, // 0x011 — Two's complement - &CPU::EXS, // 0x012 — Extend Sign - &CPU::INC, // 0x013 — Increment - &CPU::DEC, // 0x014 — Decrement - &CPU::ADD, // 0x015 — Addition - &CPU::SUB, // 0x016 — Subtraction - &CPU::MUL, // 0x017 — Multiplication - &CPU::UMUL, // 0x018 — Unsigned Multiplication - &CPU::DIV, // 0x019 — Division - &CPU::UDIV, // 0x01A — Unsigned Division - &CPU::MOD, // 0x01B — Modulus - &CPU::UMOD, // 0x01C — Unsigned Modulus - &CPU::DMOD, // 0x01D — Division and Modulus - &CPU::UDMD, // 0x01E — Unsigned Division and Modulus - &CPU::FBT, // 0x01F — Test and update Flag Register (Integer) Bits - &CPU::STB, // 0x020 — Set Bit - &CPU::CRB, // 0x021 — Clear Bit - &CPU::TSB, // 0x022 — Test Bit - &CPU::BOOL, // 0x023 — Sets the booleaness of a value - &CPU::NOT, // 0x024 — Sets the inverse booleaness of a value (! BOOL) - &CPU::AND, // 0x025 — Boolean AND operation - &CPU::OR, // 0x026 — Boolean OR operation - &CPU::XOR, // 0x027 — Boolean XOR operation - &CPU::SHL, // 0x028 — Arithmetic Shift Left - &CPU::SHR, // 0x029 — Arithmetic Shift Right - &CPU::SSR, // 0x02A — Signed Shift Right - &CPU::ROL, // 0x02B — Rotate Left - &CPU::ROR, // 0x02C — Rotate Right - &CPU::CNT, // 0x02D — Counts bits - nullptr, // 0x02E (reserved) - nullptr, // 0x02F (reserved) - &CPU::EQ, // 0x030 — Equal - &CPU::NE, // 0x031 — Not Equal - &CPU::GT, // 0x032 — Greater Than - &CPU::GE, // 0x033 — Greater or Equal Than - &CPU::LT, // 0x034 — Lower Than - &CPU::LE, // 0x035 — Lower or Equal Than - nullptr, // 0x036 (reserved) - nullptr, // 0x037 (reserved) - &CPU::JMP, // 0x038 — Jump to absolute position - &CPU::JEQ, // 0x039 — Jumps to position if EQ flag is set - &CPU::JNE, // 0x03A — Jumps to position if EQ flag is cleared - &CPU::JIF, // 0x03B — Jumps if value provided is booleanly true - &CPU::JMR, // 0x03C — Jump Relative - &CPU::JER, // 0x03D — Jumps to relative position if EQ flag is set - &CPU::JNR, // 0x03E — Jumps to relative position if EQ flag is cleared - &CPU::JIR, // 0x03F — Jumps to relative position if value provided is booleanly true - &CPU::SFB, // 0x040 — Store (User) Flag Bit - &CPU::LFB, // 0x041 — Load (User) Flag Bit - &CPU::JUF, // 0x042 — Jump to absolute position, if user flag is true - &CPU::JUR, // 0x043 — Jump to relative position, if user flag is true - &CPU::PUSH, // 0x044 — Push to stack - &CPU::POP, // 0x045 — Pop from stack - &CPU::ALLOC, // 0x046 — Allocate to heap - &CPU::HFREE, // 0x047 — Delete from heap - nullptr, // 0x048 (reserved) - nullptr, // 0x049 (reserved) - &CPU::CALL, // 0x04A — Call function at instruction index - &CPU::RET, // 0x04B — Return from a function - &CPU::EDI, // 0x04C — Enable/Disable External Interrupts - &CPU::SHSS, // 0x04D — Set Hotswap Signal Bit - nullptr, // 0x04E (reserved) - nullptr, // 0x04F (reserved) - &CPU::FLI, // 0x050 — Float Load Immediate - &CPU::FNEG, // 0x051 — Float negate - &CPU::FADD, // 0x052 — Float add - &CPU::FSUB, // 0x053 — Float subtract - &CPU::FMUL, // 0x054 — Float multiplication - &CPU::FDIV, // 0x055 — Float division - &CPU::FMOD, // 0x056 — Float modulus - &CPU::FDMOD, // 0x057 — Float division and modulus - &CPU::FEPS, // 0x058 — Sets the float epsilon value, for comparison - &CPU::FEEP, // 0x059 — Float Enable/Disable Epsilon - &CPU::FEQ, // 0x05A — Float Equal - &CPU::FNE, // 0x05B — Float Not Equal - &CPU::FGT, // 0x05C — Float Greater Than - &CPU::FGE, // 0x05D — Float Greater or Equal Than - &CPU::FLT, // 0x05E — Float Lower Than - &CPU::FLE, // 0x05F — Float Lower or Equal Than - &CPU::F2D, // 0x060 — F32 (Float) to F64 (Double) - &CPU::D2F, // 0x061 — F64 (Double) to F32 (Float) - &CPU::I2F, // 0x062 — I32 (Integer) to F32 (Float) - &CPU::I2D, // 0x063 — I32 (Integer) to F64 (Double) - &CPU::L2F, // 0x064 — I64 (Long) to F32 (Float) - &CPU::L2D, // 0x065 — I64 (Long) to F64 (Double) - &CPU::F2I, // 0x066 — F32 (Float) to I32 (Integer) - &CPU::F2L, // 0x067 — F32 (Float) to I64 (Long) - &CPU::D2I, // 0x068 — F64 (Double) to I32 (Integer) - &CPU::D2L, // 0x069 — F64 (Double) to I64 (Long) - nullptr, // 0x06A (reserved) - nullptr, // 0x06B (reserved) - &CPU::SIN, // 0x06C — Sine Function - &CPU::COS, // 0x06D — Cosine Function - &CPU::TAN, // 0x06E — Tangent Function - &CPU::ASIN, // 0x06F — Arc Sine Function - &CPU::ACOS, // 0x070 — Arc Cosine Function - &CPU::ATAN, // 0x071 — Arc Tangent Function - &CPU::ATAN2, // 0x072 — Arc Tangent Function with 2 Arguments - nullptr, // 0x073 (reserved) - &CPU::EXP, // 0x074 — Exponential Function - &CPU::LOG, // 0x075 — Natural Logarithm - &CPU::LOGAB, // 0x076 — Logarithm A of B - &CPU::POW, // 0x077 — Power Function - &CPU::SQRT, // 0x078 — Square Root - &CPU::ROOT, // 0x079 — General Root - nullptr, // 0x07A (reserved) - nullptr, // 0x07B (reserved) - &CPU::ADC, // 0x07C — Add with Carry - &CPU::SWC, // 0x07D — Subtract with Carry (Borrow) - &CPU::MWO, // 0x07E — Multiply with Overflow - &CPU::UMO, // 0x07F — Unsigned Multiply with Overflow - &CPU::MADD, // 0x080 — Matrix Addition - &CPU::MSUB, // 0x081 — Matrix Subtraction - &CPU::MMUL, // 0x082 — Matrix Multiply - &CPU::MINV, // 0x083 — Matrix Inverse - &CPU::MTRA, // 0x084 — Matrix Transpose - &CPU::MDET, // 0x085 — Matrix Determinant - &CPU::QMKA, // 0x086 — Quaternion Make from Angles - &CPU::QMUL, // 0x087 — Quaternion Multiply + &CPU::NOP, // 0x000 — No Operation + &CPU::SPDR, // 0x001 — Will place the Spider version of the interpreter in RA + &CPU::MMODE, // 0x002 — Set Memory Mode + &CPU::INT, // 0x003 — Interrupt + &CPU::LRV, // 0x004 — Load Interrupt Vector Register + &CPU::FSR, // 0x005 — Fetch System Register + &CPU::FIR, // 0x006 — Fetch Instruction Register + &CPU::FZR, // 0x007 — Fetch Stack Base Register + &CPU::LSR, // 0x008 — Load System Register + &CPU::FVR, // 0x009 — Fetch Interrupt Vector Register + &CPU::MOV, // 0x00A — Moves values + &CPU::MOR, // 0x00B — Moves registers + &CPU::AMOV, // 0x00C — Array Move, uses X and Y as ptrs, A as amount + &CPU::SWP, // 0x00D — Swap registers + &CPU::AHM, // 0x00E — Ask Host for Memory + nullptr, // 0x00F (reserved) + &CPU::COM, // 0x010 — One's complement + &CPU::NEG, // 0x011 — Two's complement + &CPU::EXS, // 0x012 — Extend Sign + &CPU::INC, // 0x013 — Increment + &CPU::DEC, // 0x014 — Decrement + &CPU::ADD, // 0x015 — Addition + &CPU::SUB, // 0x016 — Subtraction + &CPU::MUL, // 0x017 — Multiplication + &CPU::UMUL, // 0x018 — Unsigned Multiplication + &CPU::DIV, // 0x019 — Division + &CPU::UDIV, // 0x01A — Unsigned Division + &CPU::MOD, // 0x01B — Modulus + &CPU::UMOD, // 0x01C — Unsigned Modulus + &CPU::DMOD, // 0x01D — Division and Modulus + &CPU::UDMD, // 0x01E — Unsigned Division and Modulus + &CPU::FBT, // 0x01F — Test and update Flag Register (Integer) Bits + &CPU::STB, // 0x020 — Set Bit + &CPU::CRB, // 0x021 — Clear Bit + &CPU::TSB, // 0x022 — Test Bit + &CPU::BOOL, // 0x023 — Sets the booleaness of a value + &CPU::NOT, // 0x024 — Sets the inverse booleaness of a value (! BOOL) + &CPU::AND, // 0x025 — Boolean AND operation + &CPU::OR, // 0x026 — Boolean OR operation + &CPU::XOR, // 0x027 — Boolean XOR operation + &CPU::SHL, // 0x028 — Arithmetic Shift Left + &CPU::SHR, // 0x029 — Arithmetic Shift Right + &CPU::SSR, // 0x02A — Signed Shift Right + &CPU::ROL, // 0x02B — Rotate Left + &CPU::ROR, // 0x02C — Rotate Right + &CPU::CNT, // 0x02D — Counts bits + &CPU::EQ, // 0x02E — Equal + &CPU::NE, // 0x02F — Not Equal + &CPU::GT, // 0x030 — Greater Than + &CPU::GE, // 0x031 — Greater or Equal Than + &CPU::LT, // 0x032 — Lower Than + &CPU::LE, // 0x033 — Lower or Equal Than + &CPU::GTU, // 0x034 — Greater Than, Unsigned + &CPU::GEU, // 0x035 — Greater or Equal Than, Unsigned + &CPU::LTU, // 0x036 — Lower Than, Unsigned + &CPU::LEU, // 0x037 — Lower or Equal Than, Unsigned + &CPU::JMP, // 0x038 — Jump to absolute position + &CPU::JEQ, // 0x039 — Jumps to position if EQ flag is set + &CPU::JNE, // 0x03A — Jumps to position if EQ flag is cleared + &CPU::JIF, // 0x03B — Jumps if value provided is booleanly true + &CPU::JMR, // 0x03C — Jump Relative + &CPU::JER, // 0x03D — Jumps to relative position if EQ flag is set + &CPU::JNR, // 0x03E — Jumps to relative position if EQ flag is cleared + &CPU::JIR, // 0x03F — Jumps to relative position if value provided is booleanly true + &CPU::SFB, // 0x040 — Store (User) Flag Bit + &CPU::LFB, // 0x041 — Load (User) Flag Bit + &CPU::JUF, // 0x042 — Jump to absolute position, if user flag is true + &CPU::JUR, // 0x043 — Jump to relative position, if user flag is true + &CPU::PUSH, // 0x044 — Push to stack + &CPU::POP, // 0x045 — Pop from stack + &CPU::ALLOC, // 0x046 — Allocate to heap + &CPU::HFREE, // 0x047 — Delete from heap + nullptr, // 0x048 (reserved) + nullptr, // 0x049 (reserved) + &CPU::CALL, // 0x04A — Call function at instruction index + &CPU::RET, // 0x04B — Return from a function + &CPU::EDI, // 0x04C — Enable/Disable External Interrupts + &CPU::SHSS, // 0x04D — Set Hotswap Signal Bit + nullptr, // 0x04E (reserved) + nullptr, // 0x04F (reserved) + &CPU::FLI, // 0x050 — Float Load Immediate + &CPU::FNEG, // 0x051 — Float negate + &CPU::FADD, // 0x052 — Float add + &CPU::FSUB, // 0x053 — Float subtract + &CPU::FMUL, // 0x054 — Float multiplication + &CPU::FDIV, // 0x055 — Float division + &CPU::FMOD, // 0x056 — Float modulus + &CPU::FDMOD, // 0x057 — Float division and modulus + &CPU::FEPS, // 0x058 — Sets the float epsilon value, for comparison + &CPU::FEEP, // 0x059 — Float Enable/Disable Epsilon + &CPU::FEQ, // 0x05A — Float Equal + &CPU::FNE, // 0x05B — Float Not Equal + &CPU::FGT, // 0x05C — Float Greater Than + &CPU::FGE, // 0x05D — Float Greater or Equal Than + &CPU::FLT, // 0x05E — Float Lower Than + &CPU::FLE, // 0x05F — Float Lower or Equal Than + &CPU::F2D, // 0x060 — F32 (Float) to F64 (Double) + &CPU::D2F, // 0x061 — F64 (Double) to F32 (Float) + &CPU::I2F, // 0x062 — I32 (Integer) to F32 (Float) + &CPU::I2D, // 0x063 — I32 (Integer) to F64 (Double) + &CPU::L2F, // 0x064 — I64 (Long) to F32 (Float) + &CPU::L2D, // 0x065 — I64 (Long) to F64 (Double) + &CPU::F2I, // 0x066 — F32 (Float) to I32 (Integer) + &CPU::F2L, // 0x067 — F32 (Float) to I64 (Long) + &CPU::D2I, // 0x068 — F64 (Double) to I32 (Integer) + &CPU::D2L, // 0x069 — F64 (Double) to I64 (Long) + nullptr, // 0x06A (reserved) + nullptr, // 0x06B (reserved) + &CPU::SIN, // 0x06C — Sine Function + &CPU::COS, // 0x06D — Cosine Function + &CPU::TAN, // 0x06E — Tangent Function + &CPU::ASIN, // 0x06F — Arc Sine Function + &CPU::ACOS, // 0x070 — Arc Cosine Function + &CPU::ATAN, // 0x071 — Arc Tangent Function + &CPU::ATAN2, // 0x072 — Arc Tangent Function with 2 Arguments + nullptr, // 0x073 (reserved) + &CPU::EXP, // 0x074 — Exponential Function + &CPU::LOG, // 0x075 — Natural Logarithm + &CPU::LOGAB, // 0x076 — Logarithm A of B + &CPU::POW, // 0x077 — Power Function + &CPU::SQRT, // 0x078 — Square Root + &CPU::ROOT, // 0x079 — General Root + nullptr, // 0x07A (reserved) + nullptr, // 0x07B (reserved) + &CPU::ADC, // 0x07C — Add with Carry + &CPU::SWC, // 0x07D — Subtract with Carry (Borrow) + &CPU::MWO, // 0x07E — Multiply with Overflow + &CPU::UMO, // 0x07F — Unsigned Multiply with Overflow + &CPU::MADD, // 0x080 — Matrix Addition + &CPU::MSUB, // 0x081 — Matrix Subtraction + &CPU::MMUL, // 0x082 — Matrix Multiply + &CPU::MINV, // 0x083 — Matrix Inverse + &CPU::MTRA, // 0x084 — Matrix Transpose + &CPU::MDET, // 0x085 — Matrix Determinant + &CPU::QMKA, // 0x086 — Quaternion Make from Angles + &CPU::QMUL, // 0x087 — Quaternion Multiply nullptr, // 0x088 nullptr, // 0x089 - &CPU::XADD, // 0x08A — SIMD Addition - &CPU::XSUB, // 0x08B — SIMD Subtract - &CPU::XAMA, // 0x08C — SIMD Alternate Multiply-Add - &CPU::XMUL, // 0x08D — SIMD Multiply - &CPU::XDIV, // 0x08E — SIMD Divide + &CPU::XADD, // 0x08A — SIMD Addition + &CPU::XSUB, // 0x08B — SIMD Subtract + &CPU::XAMA, // 0x08C — SIMD Alternate Multiply-Add + &CPU::XMUL, // 0x08D — SIMD Multiply + &CPU::XDIV, // 0x08E — SIMD Divide nullptr, // 0x08F nullptr, // 0x090 nullptr, // 0x091 @@ -275,13 +275,13 @@ CPU::Fn CPU::instrMap[] = { nullptr, // 0x0ED nullptr, // 0x0EE nullptr, // 0x0EF - &CPU::UPY, // 0x0F0 — Will place "YUPI" in memory - &CPU::LLGS, // 0x0F1 — Spider ASCII art (LLGS easter egg) + &CPU::UPY, // 0x0F0 — Will place "YUPI" in memory + nullptr, // 0x0F1 nullptr, // 0x0F2 nullptr, // 0x0F3 nullptr, // 0x0F4 nullptr, // 0x0F5 - &CPU::DGANT, // 0x0F6 + nullptr, // 0x0F6 nullptr, // 0x0F7 nullptr, // 0x0F8 nullptr, // 0x0F9 @@ -622,12 +622,16 @@ void CPU::executeSwLk() { case 0x02D: CNT(); break; // ── Boolean ───────────────────────────────────── - case 0x030: EQ(); break; - case 0x031: NE(); break; - case 0x032: GT(); break; - case 0x033: GE(); break; - case 0x034: LT(); break; - case 0x035: LE(); break; + case 0x02E: EQ(); break; + case 0x02F: NE(); break; + case 0x030: GT(); break; + case 0x031: GE(); break; + case 0x032: LT(); break; + case 0x033: LE(); break; + case 0x034: GTU(); break; + case 0x035: GEU(); break; + case 0x036: LTU(); break; + case 0x037: LEU(); break; // ── Branch ────────────────────────────────────── case 0x038: JMP(); break; @@ -737,8 +741,6 @@ void CPU::executeSwLk() { // ── Easter Eggs ───────────────────────────────── case 0x0F0: UPY(); break; - case 0x0F1: LLGS(); break; - default: break; diff --git a/src/spider/runtime/instr/Instr_000-01F.cpp b/src/spider/runtime/instr/Instr_000-01F.cpp index d946869..3b39bab 100644 --- a/src/spider/runtime/instr/Instr_000-01F.cpp +++ b/src/spider/runtime/instr/Instr_000-01F.cpp @@ -4,15 +4,16 @@ */ #include +#include namespace spider { void CPU::NOP() { - // TODO: Implement NOP + // DONE ! // } void CPU::SPDR() { - // TODO: Implement SPDR + RA._u64 = spider::RUNTIME_VERSION_NO; } void CPU::MMODE() { @@ -21,10 +22,13 @@ namespace spider { void CPU::INT() { // TODO: Implement INT + // We need to implement an interrupt + // table and interrupt system! } void CPU::LRV() { - // TODO: Implement LRV + fetchOperReg(); + RV = _dst->_u64; } void CPU::FSR() { @@ -32,11 +36,13 @@ namespace spider { } void CPU::FIR() { - // TODO: Implement FIR + fetchOperReg(); + _dst->_u64 = RI; } void CPU::FZR() { - // TODO: Implement FZR + fetchOperReg(); + _dst->_u64 = RZ; } void CPU::LSR() { @@ -44,23 +50,63 @@ namespace spider { } void CPU::FVR() { - // TODO: Implement FVR + fetchOperReg(); + _dst->_u64 = RV; } 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() { - // TODO: Implement MOR + fetchOperRegReg(); + *_dst = *_src; } 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() { - // TODO: Implement SWP + // get registers in _dst and _src + fetchOperRegReg(); + ALU0 = *_dst; + *_dst = *_src; + *_src = ALU0; } void CPU::AHM() { @@ -69,321 +115,307 @@ namespace spider { void CPU::COM() { fetchOperDst(); - switch(_size){ - case 0b00: //byte - _dst->_u8 = ~_dst->_u8; - break; - case 0b01: //short - _dst->_u16 = ~_dst->_u16; - break; - case 0b10: //int - _dst->_u32 = ~_dst->_u32; - break; - case 0b11: //long - _dst->_u64 = ~_dst->_u64; - break; + switch (_size) { + case 0b00: //byte + _dst->_u8 = ~_dst->_u8; + break; + case 0b01: //short + _dst->_u16 = ~_dst->_u16; + break; + case 0b10: //int + _dst->_u32 = ~_dst->_u32; + break; + case 0b11: //long + _dst->_u64 = ~_dst->_u64; + break; } } void CPU::NEG() { - // TODO: Implement NEG fetchOperDst(); - switch(_size){ - case 0b00: //byte - _dst->_u8 = 1 + ~_dst->_u8; - break; - case 0b01: //short - _dst->_u16 = 1+ ~_dst->_u16; - break; - case 0b10: //int - _dst->_u32 = 1 + ~_dst->_u32; - break; - case 0b11: //long - _dst->_u64 = 1 + ~_dst->_u64; - break; + switch (_size) { + case 0b00: //byte + _dst->_u8 = 1 + ~_dst->_u8; + break; + case 0b01: //short + _dst->_u16 = 1 + ~_dst->_u16; + break; + case 0b10: //int + _dst->_u32 = 1 + ~_dst->_u32; + break; + case 0b11: //long + _dst->_u64 = 1 + ~_dst->_u64; + break; } } void CPU::EXS() { fetchOperDst(); - switch(_size){ - case 0b00: //byte - _dst->_i16 = static_cast(_dst->_i8); - break; - case 0b01: //short - _dst->_i32 = static_cast(_dst->_i16); - break; - case 0b10: //int - _dst->_i64 = static_cast(_dst->_i32); - break; - case 0b11: //long - _dst->_i64 = _dst->_i64; - break; + switch (_size) { + case 0b00: //byte + _dst->_i16 = static_cast(_dst->_i8); + break; + case 0b01: //short + _dst->_i32 = static_cast(_dst->_i16); + break; + case 0b10: //int + _dst->_i64 = static_cast(_dst->_i32); + break; + case 0b11: //long + _dst->_i64 = _dst->_i64; + break; } } void CPU::INC() { - // TODO: Implement INC fetchOperDst(); - switch(_size){ - case 0b00: //byte - _dst->_u8 += 1; - break; - case 0b01: //short - _dst->_u16 += 1; - break; - case 0b10: //int - _dst->_u32 += 1; - break; - case 0b11: //long - _dst->_u64 += 1; - break; + switch (_size) { + case 0b00: //byte + _dst->_u8 += 1; + break; + case 0b01: //short + _dst->_u16 += 1; + break; + case 0b10: //int + _dst->_u32 += 1; + break; + case 0b11: //long + _dst->_u64 += 1; + break; } } void CPU::DEC() { - // TODO: Implement DEC fetchOperDst(); - switch(_size){ - case 0b00: //byte - _dst->_u8 -= 1; - break; - case 0b01: //short - _dst->_u16 -= 1; - break; - case 0b10: //int - _dst->_u32 -= 1; - break; - case 0b11: //long - _dst->_u64 -= 1; - break; + switch (_size) { + case 0b00: //byte + _dst->_u8 -= 1; + break; + case 0b01: //short + _dst->_u16 -= 1; + break; + case 0b10: //int + _dst->_u32 -= 1; + break; + case 0b11: //long + _dst->_u64 -= 1; + break; } } void CPU::ADD() { - // TODO: Implement ADD 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; + 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::SUB() { - // TODO: Implement SUB 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; + 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::MUL() { - // TODO: Implement MUL fetchOperSrc(); fetchOperDst(); - switch(_size){ - case 0b00: //byte - _dst->_i8 = _src->_i8 * _dst->_i8; - break; - case 0b01: //short - _dst->_i16 = _src->_i16 * _dst->_i16; - break; - case 0b10: //int - _dst->_i32 = _src->_i32 * _dst->_i32; - break; - case 0b11: //long - _dst->_i64 = _src->_i64 * _dst->_i64; - break; + switch (_size) { + case 0b00: //byte + _dst->_i8 = _src->_i8 * _dst->_i8; + break; + case 0b01: //short + _dst->_i16 = _src->_i16 * _dst->_i16; + break; + case 0b10: //int + _dst->_i32 = _src->_i32 * _dst->_i32; + break; + case 0b11: //long + _dst->_i64 = _src->_i64 * _dst->_i64; + break; } } void CPU::UMUL() { - // TODO: Implement UMUL fetchOperSrc(); fetchOperDst(); - switch(_size){ - case 0b00: //byte - _dst->_u8 = _src->_u8 * _dst->_u8; - break; - case 0b01: //short - _dst->_u16 = _src->_u16 * _dst->_u16; - break; - case 0b10: //int - _dst->_u32 = _src->_u32 * _dst->_u32; - break; - case 0b11: //long - _dst->_u64 = _src->_u64 * _dst->_u64; - break; + switch (_size) { + case 0b00: //byte + _dst->_u8 = _src->_u8 * _dst->_u8; + break; + case 0b01: //short + _dst->_u16 = _src->_u16 * _dst->_u16; + break; + case 0b10: //int + _dst->_u32 = _src->_u32 * _dst->_u32; + break; + case 0b11: //long + _dst->_u64 = _src->_u64 * _dst->_u64; + break; } } void CPU::DIV() { - // TODO: Implement DIV fetchOperSrc(); fetchOperDst(); - switch(_size){ - case 0b00: //byte - _dst->_i8 = _dst->_i8 / _src->_i8; - break; - case 0b01: //short - _dst->_i16 = _dst->_i16 / _src->_i16; - break; - case 0b10: //int - _dst->_i32 = _dst->_i32 / _src->_i32; - break; - case 0b11: //long - _dst->_i64 = _dst->_i64 / _src->_i64; - break; + switch (_size) { + case 0b00: //byte + _dst->_i8 = _dst->_i8 / _src->_i8; + break; + case 0b01: //short + _dst->_i16 = _dst->_i16 / _src->_i16; + break; + case 0b10: //int + _dst->_i32 = _dst->_i32 / _src->_i32; + break; + case 0b11: //long + _dst->_i64 = _dst->_i64 / _src->_i64; + break; } } void CPU::UDIV() { - // TODO: Implement UDIV fetchOperSrc(); fetchOperDst(); - switch(_size){ - case 0b00: //byte - _dst->_u8 = _dst->_u8 / _src->_u8; - break; - case 0b01: //short - _dst->_u16 = _dst->_u16 / _src->_u16; - break; - case 0b10: //int - _dst->_u32 = _dst->_u32 / _src->_u32; - break; - case 0b11: //long - _dst->_u64 = _dst->_u64 / _src->_u64; - break; + switch (_size) { + case 0b00: //byte + _dst->_u8 = _dst->_u8 / _src->_u8; + break; + case 0b01: //short + _dst->_u16 = _dst->_u16 / _src->_u16; + break; + case 0b10: //int + _dst->_u32 = _dst->_u32 / _src->_u32; + break; + case 0b11: //long + _dst->_u64 = _dst->_u64 / _src->_u64; + break; } } void CPU::MOD() { - // TODO: Implement MOD fetchOperSrc(); fetchOperDst(); - switch(_size){ - case 0b00: //byte - _dst->_i8 = _dst->_i8 % _src->_i8; - break; - case 0b01: //short - _dst->_i16 = _dst->_i16 % _src->_i16; - break; - case 0b10: //int - _dst->_i32 = _dst->_i32 % _src->_i32; - break; - case 0b11: //long - _dst->_i64 = _dst->_i64 % _src->_i64; - break; + switch (_size) { + case 0b00: //byte + _dst->_i8 = _dst->_i8 % _src->_i8; + break; + case 0b01: //short + _dst->_i16 = _dst->_i16 % _src->_i16; + break; + case 0b10: //int + _dst->_i32 = _dst->_i32 % _src->_i32; + break; + case 0b11: //long + _dst->_i64 = _dst->_i64 % _src->_i64; + break; } } void CPU::UMOD() { - // TODO: Implement UMOD fetchOperSrc(); fetchOperDst(); - switch(_size){ - case 0b00: //byte - _dst->_u8 = _dst->_u8 % _src->_u8; - break; - case 0b01: //short - _dst->_u16 = _dst->_u16 % _src->_u16; - break; - case 0b10: //int - _dst->_u32 = _dst->_u32 % _src->_u32; - break; - case 0b11: //long - _dst->_u64 = _dst->_u64 % _src->_u64; - break; + switch (_size) { + case 0b00: //byte + _dst->_u8 = _dst->_u8 % _src->_u8; + break; + case 0b01: //short + _dst->_u16 = _dst->_u16 % _src->_u16; + break; + case 0b10: //int + _dst->_u32 = _dst->_u32 % _src->_u32; + break; + case 0b11: //long + _dst->_u64 = _dst->_u64 % _src->_u64; + break; } } void CPU::DMOD() { - // TODO: Implement DMOD fetchOperSrc(); fetchOperDst(); - switch(_size){ - case 0b00: //byte - RX._i8 = _dst->_i8 / _src->_i8; - RY._i8 = _dst->_i8 % _src->_i8; - break; - case 0b01: //short - RX._i16 = _dst->_i16 / _src->_i16; - RY._i16 = _dst->_i16 % _src->_i16; - break; - case 0b10: //int - RX._i32 = _dst->_i32 / _src->_i32; - RY._i32 = _dst->_i32 % _src->_i32; - break; - case 0b11: //long - RX._i64 = _dst->_i64 / _src->_i64; - RY._i64 = _dst->_i64 % _src->_i64; - break; + switch (_size) { + case 0b00: //byte + RX._i8 = _dst->_i8 / _src->_i8; + RY._i8 = _dst->_i8 % _src->_i8; + break; + case 0b01: //short + RX._i16 = _dst->_i16 / _src->_i16; + RY._i16 = _dst->_i16 % _src->_i16; + break; + case 0b10: //int + RX._i32 = _dst->_i32 / _src->_i32; + RY._i32 = _dst->_i32 % _src->_i32; + break; + case 0b11: //long + RX._i64 = _dst->_i64 / _src->_i64; + RY._i64 = _dst->_i64 % _src->_i64; + break; } } void CPU::UDMD() { - // TODO: Implement UDMD fetchOperSrc(); fetchOperDst(); - switch(_size){ - case 0b00: //byte - RX._u8 = _dst->_u8 / _src->_u8; - RY._u8 = _dst->_u8 % _src->_u8; - break; - case 0b01: //short - RX._u16 = _dst->_u16 / _src->_u16; - RY._u16 = _dst->_u16 % _src->_u16; - break; - case 0b10: //int - RX._u32 = _dst->_u32 / _src->_u32; - RY._u32 = _dst->_u32 % _src->_u32; - break; - case 0b11: //long - RX._u64 = _dst->_u64 / _src->_u64; - RY._u64 = _dst->_u64 % _src->_u64; - break; + switch (_size) { + case 0b00: //byte + RX._u8 = _dst->_u8 / _src->_u8; + RY._u8 = _dst->_u8 % _src->_u8; + break; + case 0b01: //short + RX._u16 = _dst->_u16 / _src->_u16; + RY._u16 = _dst->_u16 % _src->_u16; + break; + case 0b10: //int + RX._u32 = _dst->_u32 / _src->_u32; + RY._u32 = _dst->_u32 % _src->_u32; + break; + case 0b11: //long + RX._u64 = _dst->_u64 / _src->_u64; + RY._u64 = _dst->_u64 % _src->_u64; + break; } } void CPU::FBT() { - // TODO: Implement FBT fetchOperDst(); - switch(_size){ - case 0b00: //byte - RF = u8((RF & ~(0x3 << 9))) | ((_dst->_u8 >> 9) & 0x3) << 9; - break; - case 0b01: //short - RF = u16((RF & ~(0x3 << 9))) | ((_dst->_u16 >> 9) & 0x3) << 9; - break; - case 0b10: //int - RF = u32((RF & ~(0x3 << 9))) | ((_dst->_u32 >> 9) & 0x3) << 9; - break; - case 0b11: //long - RF = u64((RF & ~(0x3 << 9))) | ((_dst->_u64 >> 9) & 0x3) << 9; - break; + switch (_size) { + case 0b00: //byte + RF = u8((RF & ~(0x3 << 9))) | ((_dst->_u8 >> 9) & 0x3) << 9; + break; + case 0b01: //short + RF = u16((RF & ~(0x3 << 9))) | ((_dst->_u16 >> 9) & 0x3) << 9; + break; + case 0b10: //int + RF = u32((RF & ~(0x3 << 9))) | ((_dst->_u32 >> 9) & 0x3) << 9; + break; + case 0b11: //long + RF = u64((RF & ~(0x3 << 9))) | ((_dst->_u64 >> 9) & 0x3) << 9; + break; } } diff --git a/src/spider/runtime/instr/Instr_LLGS.cpp b/src/spider/runtime/instr/Instr_LLGS.cpp deleted file mode 100644 index 8df5e36..0000000 --- a/src/spider/runtime/instr/Instr_LLGS.cpp +++ /dev/null @@ -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 -#include - -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 \ No newline at end of file diff --git a/src/spider/test_instrucciones.cpp b/src/spider/test_instrucciones.cpp deleted file mode 100644 index dab20b6..0000000 --- a/src/spider/test_instrucciones.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif -#ifndef M_E -#define M_E 2.71828182845904523536 -#endif - -#include -#include -#include - -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; -} \ No newline at end of file