diff --git a/Instr_020-03F.cpp b/Instr_020-03F.cpp new file mode 100644 index 0000000..8ddf03f --- /dev/null +++ b/Instr_020-03F.cpp @@ -0,0 +1,452 @@ +/** + * @brief AUTO-GENERATED by pygen.ipynb BUT editable by hand! + * + */ + +#include + +namespace spider { + + void CPU::STB() { + // TODO: Implement STB + fetchOperSrc(); + fetchOperDst(); + switch(_size){ + case 0b00: //byte + _dst->_u8 |= (1 << _src->_u8); + case 0b01: //short + _dst->_u16 |= (1 << _src->_u16); + case 0b10: //int + _dst->_u32 |= (1 << _src->_u32); + case 0b11: //long + _dst->_u64 |= (1 << _src->_u64); + } + (this->*_post)(); + } + + void CPU::CRB() { + // TODO: Implement CRB + fetchOperSrc(); + fetchOperDst(); + switch(_size){ + case 0b00: //byte + _dst->_u8 &= ~(1 << _src->_u8); + case 0b01: //short + _dst->_u16 &= ~(1 << _src->_u16); + case 0b10: //int + _dst->_u32 &= ~(1 << _src->_u32); + case 0b11: //long + _dst->_u64 &= ~(1 << _src->_u64); + } + (this->*_post)(); + } + + void CPU::TSB() { + // TODO: Implement TSB + fetchOperSrc(); + fetchOperDst(); + switch(_size){ + case 0b00: //byte + switch (((RF >> _src->_u8) & 1) != ((_dst->_u8 >> _src->_u8) & 1)){ + case 1: + RF |= (1 << _src->_u8); + + case 0: + RF &= ~(1 << _src->_u8); + } + case 0b01: //short + switch (((RF >> _src->_u16) & 1) != ((_dst->_u16 >> _src->_u16) & 1)){ + case 1: + RF |= (1 << _src->_u16); + + case 0: + RF &= ~(1 << _src->_u16); + } + case 0b10: //int + switch (((RF >> _src->_u32) & 1) != ((_dst->_u32 >> _src->_u32) & 1)){ + case 1: + RF |= (1 << _src->_u32); + + case 0: + RF &= ~(1 << _src->_u32); + } + case 0b11: //long + switch (((RF >> _src->_u64) & 1) != ((_dst->_u64 >> _src->_u64) & 1)){ + case 1: + RF |= (1 << _src->_u64); + + case 0: + RF &= ~(1 << _src->_u64); + } + } + (this->*_post)(); + } + + void CPU::BOOL() { + // TODO: Implement BOOL + fetchOperDst(); + switch(_size){ + case 0b00: //byte + _dst->_u8 = _dst != 0; + case 0b01: //short + _dst->_u16 = _dst != 0; + case 0b10: //int + _dst->_u32 = _dst != 0; + case 0b11: //long + _dst->_u64 = _dst != 0; + } + (this->*_post)(); + } + + // ── 0x024 — NOT: Tests Dst == 0, updates Equal Flag ── + void CPU::NOT() { + fetchOperDst(); + bool isZero = false; + switch(_size) { + case 0b00: isZero = (_dst->_u8 == 0); break; + case 0b01: isZero = (_dst->_u16 == 0); break; + case 0b10: isZero = (_dst->_u32 == 0); break; + case 0b11: isZero = (_dst->_u64 == 0); break; + } + + if (isZero) { + RF |= CPU::FLAG_EQUAL; // Si es 0, el resultado de !0 es true (1), actualizamos bandera + // Dependiendo de la implementación de BOOL, podrías querer guardar el resultado en Dst + _dst->_u64 = 1; + } else { + RF &= ~CPU::FLAG_EQUAL; + _dst->_u64 = 0; + } + (this->*_post)(); + } + + // ── 0x025 — AND + void CPU::AND() { + fetchOperSrc(); + fetchOperDst(); + switch(_size) { + case 0b00: _dst->_u8 &= _src->_u8; break; + case 0b01: _dst->_u16 &= _src->_u16; break; + case 0b10: _dst->_u32 &= _src->_u32; break; + case 0b11: _dst->_u64 &= _src->_u64; break; + } + (this->*_post)(); + } + + // ── 0x026 — OR + void CPU::OR() { + fetchOperSrc(); + fetchOperDst(); + switch(_size) { + case 0b00: _dst->_u8 |= _src->_u8; break; + case 0b01: _dst->_u16 |= _src->_u16; break; + case 0b10: _dst->_u32 |= _src->_u32; break; + case 0b11: _dst->_u64 |= _src->_u64; break; + } + (this->*_post)(); + } + + // ── 0x027 — XOR + void CPU::XOR() { + fetchOperSrc(); + fetchOperDst(); + switch(_size) { + case 0b00: _dst->_u8 ^= _src->_u8; break; + case 0b01: _dst->_u16 ^= _src->_u16; break; + case 0b10: _dst->_u32 ^= _src->_u32; break; + case 0b11: _dst->_u64 ^= _src->_u64; break; + } + (this->*_post)(); + } + + // ── 0x028 — SHL + void CPU::SHL() { + fetchOperSrc(); + fetchOperDst(); + switch(_size) { + case 0b00: _dst->_u8 <<= _src->_u8; break; + case 0b01: _dst->_u16 <<= _src->_u16; break; + case 0b10: _dst->_u32 <<= _src->_u32; break; + case 0b11: _dst->_u64 <<= _src->_u64; break; + } + (this->*_post)(); + } + + // ── 0x029 — SHR + void CPU::SHR() { + fetchOperSrc(); + fetchOperDst(); + switch(_size) { + case 0b00: _dst->_u8 >>= _src->_u8; break; + case 0b01: _dst->_u16 >>= _src->_u16; break; + case 0b10: _dst->_u32 >>= _src->_u32; break; + case 0b11: _dst->_u64 >>= _src->_u64; break; + } + (this->*_post)(); + } + + // ── 0x02A — SSR + void CPU::SSR() { + fetchOperSrc(); + fetchOperDst(); + switch(_size) { + case 0b00: _dst->_i8 >>= _src->_u8; break; + case 0b01: _dst->_i16 >>= _src->_u8; break; + case 0b10: _dst->_i32 >>= _src->_u8; break; + case 0b11: _dst->_i64 >>= _src->_u8; break; + } + (this->*_post)(); + } + + // ── 0x02B — ROL: Rotate Left ── + void CPU::ROL() { + fetchOperSrc(); + fetchOperDst(); + switch(_size) { + case 0b00: _dst->_u8 = (_dst->_u8 << _src->_u8) | (_dst->_u8 >> (8 - _src->_u8)); break; + case 0b01: _dst->_u16 = (_dst->_u16 << _src->_u8) | (_dst->_u16 >> (16 - _src->_u8)); break; + case 0b10: _dst->_u32 = (_dst->_u32 << _src->_u8) | (_dst->_u32 >> (32 - _src->_u8)); break; + case 0b11: _dst->_u64 = (_dst->_u64 << _src->_u8) | (_dst->_u64 >> (64 - _src->_u8)); break; + } + (this->*_post)(); + } + + // ── 0x02C — ROR: Rotate Right ── + void CPU::ROR() { + fetchOperSrc(); + fetchOperDst(); + switch(_size) { + case 0b00: _dst->_u8 = (_dst->_u8 >> _src->_u8) | (_dst->_u8 << (8 - _src->_u8)); break; + case 0b01: _dst->_u16 = (_dst->_u16 >> _src->_u8) | (_dst->_u16 << (16 - _src->_u8)); break; + case 0b10: _dst->_u32 = (_dst->_u32 >> _src->_u8) | (_dst->_u32 << (32 - _src->_u8)); break; + case 0b11: _dst->_u64 = (_dst->_u64 >> _src->_u8) | (_dst->_u64 << (64 - _src->_u8)); break; + } + (this->*_post)(); + } + + // ── 0x02D — CNT: Counts bits (# of 1's into Dst) ── + void CPU::CNT() { + fetchOperDst(); + switch(_size) { + case 0b00: _dst->_u8 = __builtin_popcount(_dst->_u8); break; + case 0b01: _dst->_u16 = __builtin_popcount(_dst->_u16); break; + case 0b10: _dst->_u32 = __builtin_popcount(_dst->_u32); break; + case 0b11: _dst->_u64 = __builtin_popcountll(_dst->_u64); break; + } + (this->*_post)(); + } + + // ── 0x030 — EQ: Dst == Src into Dst ── + void CPU::EQ() { + fetchOperSrc(); + fetchOperDst(); + bool res = false; + switch(_size) { + case 0b00: res = (_dst->_u8 == _src->_u8); break; + case 0b01: res = (_dst->_u16 == _src->_u16); break; + case 0b10: res = (_dst->_u32 == _src->_u32); break; + case 0b11: res = (_dst->_u64 == _src->_u64); break; + } + _dst->_u64 = res ? 1 : 0; + (this->*_post)(); + } + + // ── 0x031 — NE: Dst != Src into Dst ── + void CPU::NE() { + fetchOperSrc(); + fetchOperDst(); + bool res = false; + switch(_size) { + case 0b00: res = (_dst->_u8 != _src->_u8); break; + case 0b01: res = (_dst->_u16 != _src->_u16); break; + case 0b10: res = (_dst->_u32 != _src->_u32); break; + case 0b11: res = (_dst->_u64 != _src->_u64); break; + } + _dst->_u64 = res ? 1 : 0; + (this->*_post)(); + } + + // ── 0x032 — GT: Dst > Src into Dst ── + void CPU::GT() { + fetchOperSrc(); + fetchOperDst(); + bool res = false; + switch(_size) { + case 0b00: res = (_dst->_i8 > _src->_i8); break; + case 0b01: res = (_dst->_i16 > _src->_i16); break; + case 0b10: res = (_dst->_i32 > _src->_i32); break; + case 0b11: res = (_dst->_i64 > _src->_i64); break; + } + _dst->_u64 = res ? 1 : 0; + (this->*_post)(); + } + + // ── 0x033 — GE: Dst >= Src into Dst ── + void CPU::GE() { + fetchOperSrc(); + fetchOperDst(); + bool res = false; + switch(_size) { + case 0b00: res = (_dst->_i8 >= _src->_i8); break; + case 0b01: res = (_dst->_i16 >= _src->_i16); break; + case 0b10: res = (_dst->_i32 >= _src->_i32); break; + case 0b11: res = (_dst->_i64 >= _src->_i64); break; + } + _dst->_u64 = res ? 1 : 0; + (this->*_post)(); + } + + // ── 0x034 — LT: Dst < Src into Dst ── + void CPU::LT() { + fetchOperSrc(); + fetchOperDst(); + bool res = false; + switch(_size) { + case 0b00: res = (_dst->_i8 < _src->_i8); break; + case 0b01: res = (_dst->_i16 < _src->_i16); break; + case 0b10: res = (_dst->_i32 < _src->_i32); break; + case 0b11: res = (_dst->_i64 < _src->_i64); break; + } + _dst->_u64 = res ? 1 : 0; + (this->*_post)(); + } + + // ── 0x035 — LE: Dst <= Src into Dst ── + void CPU::LE() { + fetchOperSrc(); + fetchOperDst(); + bool res = false; + switch(_size) { + case 0b00: res = (_dst->_i8 <= _src->_i8); break; + case 0b01: res = (_dst->_i16 <= _src->_i16); break; + case 0b10: res = (_dst->_i32 <= _src->_i32); break; + case 0b11: res = (_dst->_i64 <= _src->_i64); break; + } + _dst->_u64 = res ? 1 : 0; + (this->*_post)(); + } + + // ── 0x038 — JMP: Dst -> Instruction Register (PC) ── + // The IR adds 1 at the end, so we subtract 1 to compensate. + void CPU::JMP() { + fetchOperDst(); + u64 target; + switch(_size) { + case 0b00: target = static_cast(_dst->_u8); break; + case 0b01: target = static_cast(_dst->_u16); break; + case 0b10: target = static_cast(_dst->_u32); break; + case 0b11: target = _dst->_u64; break; + } + RI = target - 1; + (this->*_post)(); + } + + // ── 0x039 — JEQ: Jump if EQ flag is set ── + void CPU::JEQ() { + fetchOperDst(); + if (RF & CPU::FLAG_EQUAL) { + u64 target; + switch(_size) { + case 0b00: target = static_cast(_dst->_u8); break; + case 0b01: target = static_cast(_dst->_u16); break; + case 0b10: target = static_cast(_dst->_u32); break; + case 0b11: target = _dst->_u64; break; + } + RI = target - 1; + } + (this->*_post)(); + } + + // ── 0x03A — JNE: Jumps if EQ flag is cleared ── + void CPU::JNE() { + fetchOperDst(); + if (!(RF & CPU::FLAG_EQUAL)) { + u64 target; + switch(_size) { + case 0b00: target = static_cast(_dst->_u8); break; + case 0b01: target = static_cast(_dst->_u16); break; + case 0b10: target = static_cast(_dst->_u32); break; + case 0b11: target = _dst->_u64; break; + } + RI = target - 1; + } + (this->*_post)(); + } + + // ── 0x03B — JIF: Jumps if Src is booleanly true ── + void CPU::JIF() { + fetchOperSrc(); + fetchOperDst(); + if (_src->_u64 != 0) { + u64 target; + switch(_size) { + case 0b00: target = static_cast(_dst->_u8); break; + case 0b01: target = static_cast(_dst->_u16); break; + case 0b10: target = static_cast(_dst->_u32); break; + case 0b11: target = _dst->_u64; break; + } + RI = target - 1; + } + (this->*_post)(); + } + + // ── 0x03C — JMR: Dst + Instruction Register -> Instruction Register ── + void CPU::JMR() { + fetchOperDst(); + i64 offset; + switch (_size) { + case 0b00: offset = static_cast(_dst->_i8); break; // 1 byte + case 0b01: offset = static_cast(_dst->_i16); break; // 2 bytes + case 0b10: offset = static_cast(_dst->_i32); break; // 4 bytes + case 0b11: offset = _dst->_i64; break; // 8 bytes + } + RI = static_cast(static_cast(RI) + offset); + } + + // ── 0x03D — JER: Dst + Instruction Register -> Instruction Register IF Flags.EQ ── + void CPU::JER() { + fetchOperDst(); + if (RF & CPU::FLAG_EQUAL) { + i64 offset; + switch (_size) { + case 0b00: offset = static_cast(_dst->_i8); break; + case 0b01: offset = static_cast(_dst->_i16); break; + case 0b10: offset = static_cast(_dst->_i32); break; + case 0b11: offset = _dst->_i64; break; + } + RI = static_cast(static_cast(RI) + offset); + } + } + + // ── 0x03E — JNR: Dst + Instruction Register -> Instruction Register IF NOT Flags.EQ ── + void CPU::JNR() { + fetchOperDst(); + if (!(RF & CPU::FLAG_EQUAL)) { + i64 offset; + switch (_size) { + case 0b00: offset = static_cast(_dst->_i8); break; + case 0b01: offset = static_cast(_dst->_i16); break; + case 0b10: offset = static_cast(_dst->_i32); break; + case 0b11: offset = _dst->_i64; break; + } + RI = static_cast(static_cast(RI) + offset); + } + } + + // ── 0x03F — JIR: Dst + Instruction Register -> Instruction Register IF Src ── + void CPU::JIR() { + fetchOperSrc(); + fetchOperDst(); + if (_src->_u64 != 0) { + i64 offset; + switch (_size) { + case 0b00: offset = static_cast(_dst->_i8); break; + case 0b01: offset = static_cast(_dst->_i16); break; + case 0b10: offset = static_cast(_dst->_i32); break; + case 0b11: offset = _dst->_i64; break; + } + RI = static_cast(static_cast(RI) + offset); + } + } + +} diff --git a/calling-convention.ipynb b/calling-convention.ipynb new file mode 100644 index 0000000..c71af20 --- /dev/null +++ b/calling-convention.ipynb @@ -0,0 +1,658 @@ +{ + "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 +}