исправь ошибки логические ошибки ,  и интерфейс 
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Расширенный Эмулятор GPU RTX</title>
    <!-- Подключение CodeMirror CSS -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/codemirror.min.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/theme/monokai.min.css">
    <!-- Стилизация интерфейса -->
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #1e1e1e;
            color: #fff;
            display: flex;
            flex-direction: column;
            align-items: center;
            padding: 20px;
        }
        #gpuInterface {
            margin-bottom: 20px;
            padding: 20px;
            background-color: #2e2e2e;
            border-radius: 8px;
            width: 800px;
            display: flex;
            flex-direction: column;
            gap: 10px;
        }
        #gpuInterface h2 {
            margin-top: 0;
            text-align: center;
        }
        /* CodeMirror Контейнер */
        .CodeMirror {
            height: auto;
            min-height: 150px;
            background-color: #1e1e1e;
            color: #fff;
            border: 1px solid #555;
            border-radius: 4px;
            font-family: monospace;
            font-size: 14px;
        }
        #gpuControls {
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
        }
        #gpuControls button {
            padding: 10px 20px;
            background-color: #007acc;
            border: none;
            border-radius: 4px;
            color: #fff;
            cursor: pointer;
            flex: 1;
            min-width: 120px;
        }
        #gpuControls button:hover {
            background-color: #005fa3;
        }
        #outputCanvas, #outputWebGL {
            border: 2px solid #555;
            background-color: #000;
            margin-bottom: 10px;
        }
        #outputWebGL {
            display: none;
        }
        #vramStatus {
            font-size: 14px;
        }
        #log {
            background-color: #2e2e2e;
            padding: 10px;
            border-radius: 4px;
            height: 150px;
            overflow-y: auto;
            font-family: monospace;
            font-size: 12px;
        }
        #log h3 {
            margin-top: 0;
        }
        #vramViewer {
            width: 200px;
            height: 150px;
            border: 1px solid #555;
            overflow: hidden;
            background-color: #1e1e1e;
            border-radius: 4px;
        }
        #vramCanvas {
            display: block;
        }
        /* Модальное окно помощи */
        #helpModal {
            display: none;
            position: fixed;
            top: 5%;
            left: 10%;
            width: 80%;
            height: 90%;
            background-color: #333;
            color: #fff;
            padding: 20px;
            overflow-y: auto;
            border-radius: 8px;
            z-index: 1000;
        }
        #helpModal h2 {
            text-align: center;
        }
        #helpModal ul {
            list-style: none;
            padding: 0;
        }
        #helpModal li {
            margin-bottom: 10px;
        }
        #helpModal button {
            margin-top: 20px;
            padding: 10px 20px;
            background-color: #007acc;
            border: none;
            border-radius: 4px;
            color: #fff;
            cursor: pointer;
        }
        #helpModal button:hover {
            background-color: #005fa3;
        }
    </style>
</head>
<body>
    <div id="gpuInterface">
        <h2>Эмулятор GPU RTX</h2>
        <!-- Текстовый редактор CodeMirror -->
        <textarea id="gpuProgram" placeholder="Введите программу GPU (пример ниже)"></textarea>
        <div id="gpuControls">
            <button id="runProgram">Выполнить</button>
            <button id="clearCanvas">Очистить Canvas</button>
            <button id="saveProgram">Сохранить программу</button>
            <button id="loadProgram">Загрузить программу</button>
            <button id="toggleRenderMode">Включить WebGL</button>
            <button id="showHelp">Помощь</button>
        </div>
        <div id="vramStatus">VRAM: 0 / 800000</div>
        <div id="log">
            <h3>Лог GPU:</h3>
            <div id="logContent"></div>
        </div>
        <h3>Просмотр VRAM:</h3>
        <div id="vramViewer">
            <canvas id="vramCanvas" width="200" height="150"></canvas>
        </div>
    </div>
    <!-- Два Canvas: для обычного рендеринга и WebGL -->
    <canvas id="outputCanvas" width="800" height="600"></canvas>
    <canvas id="outputWebGL" width="800" height="600"></canvas>
    
    <!-- Модальное окно помощи -->
    <div id="helpModal">
        <h2>Помощь по Инструкциям GPU</h2>
        <ul>
            <li><strong>DRAW x y r g b</strong>: Рисует пиксель в (x, y) с цветом (r, g, b).</li>
            <li><strong>DRAW_LINE x1 y1 x2 y2 r g b</strong>: Рисует линию от (x1, y1) до (x2, y2) цветом (r, g, b).</li>
            <li><strong>FILL_RECT x y width height r g b</strong>: Заполняет прямоугольник с верхним левым углом в (x, y), заданными размерами и цветом.</li>
            <li><strong>DRAW_CIRCLE x y radius r g b</strong>: Рисует окружность с центром в (x, y), заданным радиусом и цветом.</li>
            <li><strong>LOOP count</strong> / <strong>END_LOOP</strong>: Повторяет блок команд определённое количество раз.</li>
            <li><strong>WAIT ms</strong>: Приостанавливает выполнение программы на заданное количество миллисекунд.</li>
            <li><strong>SET_VARIABLE varName value</strong>: Устанавливает значение переменной.</li>
            <li><strong>GET_VARIABLE varName</strong>: Получает значение переменной.</li>
            <li><strong>VERTEX_SHADER code</strong>: Определяет вершинный шейдер.</li>
            <li><strong>FRAGMENT_SHADER code</strong>: Определяет фрагментный шейдер.</li>
            <li><strong>USE_SHADERS true/false</strong>: Включает или отключает применение шейдеров.</li>
            <li><strong>FUNCTION functionName</strong> / <strong>END_FUNCTION</strong>: Определяет функцию.</li>
            <li><strong>CALL functionName</strong>: Вызывает определённую функцию.</li>
            <li><strong>LOAD_SPRITE url x y</strong>: Загружает изображение из URL и отображает его на (x, y).</li>
            <!-- Добавьте другие инструкции по мере необходимости -->
        </ul>
        <button id="closeHelp">Закрыть</button>
    </div>

    <!-- Подключение CodeMirror JS -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/codemirror.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/javascript/javascript.min.js"></script>
    <!-- Основной скрипт эмулятора GPU -->
    <script>
        class GPUEmulator {
            constructor(width, height, ctx, logElement, vramCanvas, vramCtx) {
                this.width = width;
                this.height = height;
                this.ctx = ctx;
                this.logElement = logElement;
                this.vramCanvas = vramCanvas;
                this.vramCtx = vramCtx;
                this.vramSize = width * height; // Каждый пиксель - 1 ячейка VRAM
                this.vram = new Uint32Array(this.vramSize); // 32-битная VRAM
                this.programMemory = [];
                this.variables = {};
                this.functions = {};
                this.vertexShader = null;
                this.fragmentShader = null;
                this.useShaders = false;
                this.shaderCache = {};
                this.gl = null; // WebGL контекст
                this.program = null;
                this.positionBuffer = null;
                this.texCoordBuffer = null;
                this.texture = null;
                this.positionLocation = null;
                this.texCoordLocation = null;
                this.textureLocation = null;
            }

            log(message) {
                const logEntry = document.createElement('div');
                logEntry.textContent = message;
                this.logElement.appendChild(logEntry);
                this.logElement.parentElement.scrollTop = this.logElement.parentElement.scrollHeight;
                console.log(message);
            }

            loadProgram(program) {
                this.programMemory = program.split('\n')
                    .map(line => line.trim())
                    .filter(line => line && !line.startsWith('//'));
                this.log(`Программа загружена: ${this.programMemory.length} команд.`);
            }

            replaceVariables(parts) {
                return parts.map(part => {
                    if (part.startsWith('{') && part.endsWith('}')) {
                        const varName = part.slice(1, -1);
                        if (this.variables.hasOwnProperty(varName)) {
                            return this.variables[varName];
                        } else {
                            this.log(`Предупреждение: Переменная "${varName}" не определена.`);
                            return 0;
                        }
                    }
                    return part;
                });
            }

            setShader(type, code) {
                if (type === 'VERTEX_SHADER') {
                    try {
                        this.vertexShader = new Function('x', 'y', 'height', code);
                        this.log('Вершинный шейдер установлен.');
                    } catch (error) {
                        this.log(`Ошибка в вершине шейдера: ${error}`);
                    }
                } else if (type === 'FRAGMENT_SHADER') {
                    try {
                        this.fragmentShader = new Function('color', 'x', 'y', code);
                        this.log('Фрагментный шейдер установлен.');
                    } catch (error) {
                        this.log(`Ошибка в фрагментном шейдере: ${error}`);
                    }
                }
            }

            applyShaders(color, x, y) {
                if (this.useShaders && this.vertexShader && this.fragmentShader) {
                    try {
                        // Применяем вершинный шейдер
                        const transformed = this.vertexShader(x, y, this.height);
                        // Применяем фрагментный шейдер
                        return this.fragmentShader({...color}, transformed.x, transformed.y);
                    } catch (error) {
                        this.log(`Ошибка при применении шейдеров: ${error}`);
                        return color;
                    }
                }
                return color;
            }

            drawPixel(x, y, color) {
                if (x < 0 || x >= this.width || y < 0 || y >= this.height) return;
                let finalColor = color;
                if (this.useShaders) {
                    finalColor = this.applyShaders(color, x, y);
                }
                const index = y * this.width + x;
                this.vram[index] = (finalColor.b << 24) | (finalColor.g << 16) | (finalColor.r << 8) | finalColor.a;
            }

            drawLine(x1, y1, x2, y2, color) {
                // Алгоритм Брезенхэма
                const dx = Math.abs(x2 - x1);
                const dy = Math.abs(y2 - y1);
                const sx = x1 < x2 ? 1 : -1;
                const sy = y1 < y2 ? 1 : -1;
                let err = dx - dy;

                while (true) {
                    this.drawPixel(x1, y1, color);
                    if (x1 === x2 && y1 === y2) break;
                    const e2 = 2 * err;
                    if (e2 > -dy) { err -= dy; x1 += sx; }
                    if (e2 < dx) { err += dx; y1 += sy; }
                }
            }

            fillRect(x, y, width, height, color) {
                for (let i = x; i < x + width; i++) {
                    for (let j = y; j < y + height; j++) {
                        this.drawPixel(i, j, color);
                    }
                }
            }

            drawCircle(x, y, radius, color) {
                // Алгоритм Брезенхэма для окружности
                let dx = radius;
                let dy = 0;
                let err = 0;

                while (dx >= dy) {
                    this.drawPixel(x + dx, y + dy, color);
                    this.drawPixel(x + dy, y + dx, color);
                    this.drawPixel(x - dy, y + dx, color);
                    this.drawPixel(x - dx, y + dy, color);
                    this.drawPixel(x - dx, y - dy, color);
                    this.drawPixel(x - dy, y - dx, color);
                    this.drawPixel(x + dy, y - dx, color);
                    this.drawPixel(x + dx, y - dy, color);

                    dy += 1;
                    if (err <= 0) {
                        err += 2 * dy + 1;
                    } else {
                        dx -= 1;
                        err -= 2 * dx + 1;
                    }
                }
            }

            drawEllipse(x, y, radiusX, radiusY, color) {
                // Параметрическое уравнение эллипса
                for (let angle = 0; angle < 360; angle++) {
                    const rad = angle * (Math.PI / 180);
                    const px = Math.round(x + radiusX * Math.cos(rad));
                    const py = Math.round(y + radiusY * Math.sin(rad));
                    this.drawPixel(px, py, color);
                }
            }

            drawQuadraticSpline(x1, y1, x2, y2, x3, y3, color) {
                // Квадратичный сплайн
                for (let t = 0; t <= 1; t += 0.01) {
                    const xt = (1 - t) * (1 - t) * x1 + 2 * (1 - t) * t * x2 + t * t * x3;
                    const yt = (1 - t) * (1 - t) * y1 + 2 * (1 - t) * t * y2 + t * t * y3;
                    this.drawPixel(Math.round(xt), Math.round(yt), color);
                }
            }

            drawTriangle(x1, y1, x2, y2, x3, y3, color) {
                // Рисование треугольника через линию
                this.drawLine(x1, y1, x2, y2, color);
                this.drawLine(x2, y2, x3, y3, color);
                this.drawLine(x3, y3, x1, y1, color);
            }

            clearScreen(color) {
                for (let i = 0; i < this.vramSize; i++) {
                    this.vram[i] = (color.b << 24) | (color.g << 16) | (color.r << 8) | color.a;
                }
            }

            loadSprite(url, x, y) {
                const img = new Image();
                img.crossOrigin = "Anonymous"; // Для избежания CORS проблем
                img.src = url;
                img.onload = () => {
                    const spriteWidth = img.width;
                    const spriteHeight = img.height;
                    const tempCanvas = document.createElement('canvas');
                    tempCanvas.width = spriteWidth;
                    tempCanvas.height = spriteHeight;
                    const tempCtx = tempCanvas.getContext('2d');
                    tempCtx.drawImage(img, 0, 0);
                    const imgData = tempCtx.getImageData(0, 0, spriteWidth, spriteHeight);
                    for (let j = 0; j < spriteHeight; j++) {
                        for (let i = 0; i < spriteWidth; i++) {
                            const index = (j * spriteWidth + i) * 4;
                            const r = imgData.data[index];
                            const g = imgData.data[index + 1];
                            const b = imgData.data[index + 2];
                            const a = imgData.data[index + 3];
                            if (a > 0) { // Прозрачные пиксели не отрисовываются
                                this.drawPixel(x + i, y + j, { r, g, b, a });
                            }
                        }
                    }
                    this.log(`LOAD_SPRITE: "${url}" загружен и отрисован.`);
                    this.updateViewer();
                };
                img.onerror = () => {
                    this.log(`Ошибка: Не удалось загрузить спрайт с URL "${url}".`);
                };
            }

            logFunction(funcName) {
                // Логирование вызовов функций (если требуется)
            }

            setVariable(varName, value) {
                this.variables[varName] = value;
                this.log(`SET_VARIABLE: ${varName} = ${value}`);
            }

            getVariable(varName) {
                if (this.variables.hasOwnProperty(varName)) {
                    this.log(`GET_VARIABLE: ${varName} = ${this.variables[varName]}`);
                    return this.variables[varName];
                } else {
                    this.log(`Предупреждение: Переменная "${varName}" не определена.`);
                    return 0;
                }
            }

            defineFunction(funcName, commands) {
                this.functions[funcName] = commands;
                this.log(`FUNCTION: "${funcName}" определена с ${commands.length} командами.`);
            }

            callFunction(funcName) {
                if (this.functions.hasOwnProperty(funcName)) {
                    this.log(`CALL: Вызов функции "${funcName}"`);
                    this.executeCommands(this.functions[funcName]);
                } else {
                    this.log(`Ошибка: Функция "${funcName}" не определена.`);
                }
            }

            async executeProgram() {
                let i = 0;
                const stack = [];
                while (i < this.programMemory.length) {
                    let cmd = this.programMemory[i];
                    let parts = cmd.split(' ').filter(part => part);
                    const instruction = parts[0].toUpperCase();

                    // Замена переменных
                    parts = this.replaceVariables(parts);

                    try {
                        switch (instruction) {
                            case 'DRAW':
                                // DRAW x y r g b
                                if (parts.length !== 6) throw `Недостаточно параметров для DRAW на строке ${i + 1}`;
                                const [dx, dy, dr, dg, db] = parts.slice(1).map(Number);
                                this.drawPixel(dx, dy, { r: dr, g: dg, b: db, a: 255 });
                                this.log(`DRAW: (${dx}, ${dy}) цвет (${dr}, ${dg}, ${db})`);
                                break;
                            case 'DRAW_LINE':
                                // DRAW_LINE x1 y1 x2 y2 r g b
                                if (parts.length !== 8) throw `Недостаточно параметров для DRAW_LINE на строке ${i + 1}`;
                                const [lx1, ly1, lx2, ly2, lr, lg, lb] = parts.slice(1).map(Number);
                                this.drawLine(lx1, ly1, lx2, ly2, { r: lr, g: lg, b: lb, a: 255 });
                                this.log(`DRAW_LINE: От (${lx1}, ${ly1}) до (${lx2}, ${ly2}) цвет (${lr}, ${lg}, ${lb})`);
                                break;
                            case 'FILL_RECT':
                                // FILL_RECT x y width height r g b
                                if (parts.length !== 8) throw `Недостаточно параметров для FILL_RECT на строке ${i + 1}`;
                                const [frx, fry, frw, frh, fr, fg, fb] = parts.slice(1).map(Number);
                                this.fillRect(frx, fry, frw, frh, { r: fr, g: fg, b: fb, a: 255 });
                                this.log(`FILL_RECT: (${frx}, ${fry}), Ширина: ${frw}, Высота: ${frh}, цвет (${fr}, ${fg}, ${fb})`);
                                break;
                            case 'DRAW_CIRCLE':
                                // DRAW_CIRCLE x y radius r g b
                                if (parts.length !== 7) throw `Недостаточно параметров для DRAW_CIRCLE на строке ${i + 1}`;
                                const [cx, cy, cradius, cr, cg, cb] = parts.slice(1).map(Number);
                                this.drawCircle(cx, cy, cradius, { r: cr, g: cg, b: cb, a: 255 });
                                this.log(`DRAW_CIRCLE: Центр (${cx}, ${cy}), Радиус: ${cradius}, цвет (${cr}, ${cg}, ${cb})`);
                                break;
                            case 'DRAW_ELLIPSE':
                                // DRAW_ELLIPSE x y radiusX radiusY r g b
                                if (parts.length !== 8) throw `Недостаточно параметров для DRAW_ELLIPSE на строке ${i + 1}`;
                                const [ex, ey, erX, erY, er, eg, eb] = parts.slice(1).map(Number);
                                this.drawEllipse(ex, ey, erX, erY, { r: er, g: eg, b: eb, a: 255 });
                                this.log(`DRAW_ELLIPSE: Центр (${ex}, ${ey}), Радиусы (${erX}, ${erY}), цвет (${er}, ${eg}, ${eb})`);
                                break;
                            case 'DRAW_SPLINE':
                                // DRAW_SPLINE x1 y1 x2 y2 x3 y3 r g b
                                if (parts.length !== 10) throw `Недостаточно параметров для DRAW_SPLINE на строке ${i + 1}`;
                                const [sx1, sy1, sx2, sy2, sx3, sy3, sr, sg, sb] = parts.slice(1).map(Number);
                                this.drawQuadraticSpline(sx1, sy1, sx2, sy2, sx3, sy3, { r: sr, g: sg, b: sb, a: 255 });
                                this.log(`DRAW_SPLINE: Точки (${sx1}, ${sy1}), (${sx2}, ${sy2}), (${sx3}, ${sy3}) цвет (${sr}, ${sg}, ${sb})`);
                                break;
                            case 'DRAW_TRIANGLE':
                                // DRAW_TRIANGLE x1 y1 x2 y2 x3 y3 r g b
                                if (parts.length !== 10) throw `Недостаточно параметров для DRAW_TRIANGLE на строке ${i + 1}`;
                                const [tx1, ty1, tx2, ty2, tx3, ty3, tr, tg, tb] = parts.slice(1).map(Number);
                                this.drawTriangle(tx1, ty1, tx2, ty2, tx3, ty3, { r: tr, g: tg, b: tb, a: 255 });
                                this.log(`DRAW_TRIANGLE: Точки (${tx1}, ${ty1}), (${tx2}, ${ty2}), (${tx3}, ${ty3}) цвет (${tr}, ${tg}, ${tb})`);
                                break;
                            case 'CLEAR':
                                // CLEAR r g b
                                if (parts.length !== 4) throw `Недостаточно параметров для CLEAR на строке ${i + 1}`;
                                const [clr, clg, clb] = parts.slice(1).map(Number);
                                this.clearScreen({ r: clr, g: clg, b: clb, a: 255 });
                                this.log(`CLEAR: цвет (${clr}, ${clg}, ${clb})`);
                                break;
                            case 'SET_VARIABLE':
                                // SET_VARIABLE varName value
                                if (parts.length !== 3) throw `Недостаточно параметров для SET_VARIABLE на строке ${i + 1}`;
                                const varName = parts[1];
                                const value = parseInt(parts[2], 10);
                                if (isNaN(value)) throw `Неверное значение для SET_VARIABLE на строке ${i + 1}`;
                                this.setVariable(varName, value);
                                break;
                            case 'GET_VARIABLE':
                                // GET_VARIABLE varName
                                if (parts.length !== 2) throw `Недостаточно параметров для GET_VARIABLE на строке ${i + 1}`;
                                const getVarName = parts[1];
                                const varValue = this.getVariable(getVarName);
                                break;
                            case 'VERTEX_SHADER':
                                // VERTEX_SHADER code
                                const vertexCode = parts.slice(1).join(' ');
                                this.setShader('VERTEX_SHADER', vertexCode);
                                break;
                            case 'FRAGMENT_SHADER':
                                // FRAGMENT_SHADER code
                                const fragmentCode = parts.slice(1).join(' ');
                                this.setShader('FRAGMENT_SHADER', fragmentCode);
                                break;
                            case 'USE_SHADERS':
                                // USE_SHADERS true/false
                                if (parts.length !== 2) throw `Недостаточно параметров для USE_SHADERS на строке ${i + 1}`;
                                this.useShaders = parts[1].toUpperCase() === 'TRUE';
                                this.log(`USE_SHADERS: ${this.useShaders}`);
                                break;
                            case 'FUNCTION':
                                // FUNCTION functionName
                                if (parts.length !== 2) throw `Недостаточно параметров для FUNCTION на строке ${i + 1}`;
                                const funcName = parts[1];
                                this.functions[funcName] = [];
                                i++;
                                while (i < this.programMemory.length && this.programMemory[i].toUpperCase() !== 'END_FUNCTION') {
                                    this.functions[funcName].push(this.programMemory[i]);
                                    i++;
                                }
                                if (i === this.programMemory.length) {
                                    throw `Не найдено END_FUNCTION для FUNCTION "${funcName}"`;
                                }
                                this.log(`FUNCTION: "${funcName}" определена с ${this.functions[funcName].length} командами.`);
                                break;
                            case 'CALL':
                                // CALL functionName
                                if (parts.length !== 2) throw `Недостаточно параметров для CALL на строке ${i + 1}`;
                                const callFuncName = parts[1];
                                this.callFunction(callFuncName);
                                break;
                            case 'CALL_PROCEDURE':
                                // CALL_PROCEDURE procedureName
                                if (parts.length !== 2) throw `Недостаточно параметров для CALL_PROCEDURE на строке ${i + 1}`;
                                const callProcName = parts[1];
                                this.callFunction(callProcName); // Используем функции как процедуры
                                break;
                            case 'PROCEDURE':
                                // PROCEDURE procedureName
                                if (parts.length !== 2) throw `Недостаточно параметров для PROCEDURE на строке ${i + 1}`;
                                const procName = parts[1];
                                this.functions[procName] = [];
                                i++;
                                while (i < this.programMemory.length && this.programMemory[i].toUpperCase() !== 'END_PROCEDURE') {
                                    this.functions[procName].push(this.programMemory[i]);
                                    i++;
                                }
                                if (i === this.programMemory.length) {
                                    throw `Не найдено END_PROCEDURE для PROCEDURE "${procName}"`;
                                }
                                this.log(`PROCEDURE: "${procName}" определена с ${this.functions[procName].length} командами.`);
                                break;
                            case 'END_LOOP':
                            case 'END_FUNCTION':
                            case 'END_PROCEDURE':
                                // Эти команды обрабатываются при соответствующих инструкциях
                                break;
                            case 'LOOP':
                                // LOOP count
                                if (parts.length !== 2) throw `Недостаточно параметров для LOOP на строке ${i + 1}`;
                                const count = parseInt(parts[1], 10);
                                if (isNaN(count) || count <= 0) throw `Неверный параметр для LOOP на строке ${i + 1}`;
                                this.log(`LOOP: Начало цикла ${count} раз(а) на строке ${i + 1}`);
                                const loopCommands = [];
                                i++;
                                while (i < this.programMemory.length && this.programMemory[i].toUpperCase() !== 'END_LOOP') {
                                    loopCommands.push(this.programMemory[i]);
                                    i++;
                                }
                                if (i === this.programMemory.length) {
                                    throw `Не найдено END_LOOP для LOOP на строке ${i + 1}`;
                                }
                                for (let l = 0; l < count; l++) {
                                    this.log(`LOOP: Итерация ${l + 1} из ${count}`);
                                    await this.executeCommands(loopCommands);
                                }
                                break;
                            case 'WAIT':
                                // WAIT milliseconds
                                if (parts.length !== 2) throw `Недостаточно параметров для WAIT на строке ${i + 1}`;
                                const ms = parseInt(parts[1], 10);
                                if (isNaN(ms) || ms < 0) throw `Неверный параметр для WAIT на строке ${i + 1}`;
                                this.log(`WAIT: Пауза на ${ms} миллисекунд`);
                                await new Promise(resolve => setTimeout(resolve, ms));
                                break;
                            case 'LOAD_SPRITE':
                                // LOAD_SPRITE url x y
                                if (parts.length !== 4) throw `Недостаточно параметров для LOAD_SPRITE на строке ${i + 1}`;
                                const [url, sx, sy] = parts.slice(1);
                                this.loadSprite(url, parseInt(sx, 10), parseInt(sy, 10));
                                break;
                            default:
                                if (instruction === '') {
                                    // Пустая строка, пропускаем
                                } else {
                                    this.log(`Неизвестная инструкция "${instruction}" на строке ${i + 1}`);
                                }
                        }
                    } catch (error) {
                        this.log(`Ошибка: ${error}`);
                    }

                    i++;
                }

                // Обновление Canvas и VRAM Viewer
                this.updateCanvas();
                this.updateViewer();

                // Обновление WebGL (если используется)
                if (this.useShaders && this.gl) {
                    this.updateWebGL();
                }
            }

            async executeCommands(commands) {
                for (let cmd of commands) {
                    let parts = cmd.split(' ').filter(part => part);
                    const instruction = parts[0].toUpperCase();

                    // Замена переменных
                    parts = this.replaceVariables(parts);

                    try {
                        switch (instruction) {
                            case 'DRAW':
                                // DRAW x y r g b
                                if (parts.length !== 6) throw `Недостаточно параметров для DRAW на строке`;
                                const [dx, dy, dr, dg, db] = parts.slice(1).map(Number);
                                this.drawPixel(dx, dy, { r: dr, g: dg, b: db, a: 255 });
                                this.log(`DRAW: (${dx}, ${dy}) цвет (${dr}, ${dg}, ${db})`);
                                break;
                            case 'DRAW_LINE':
                                // DRAW_LINE x1 y1 x2 y2 r g b
                                if (parts.length !== 8) throw `Недостаточно параметров для DRAW_LINE на строке`;
                                const [lx1, ly1, lx2, ly2, lr, lg, lb] = parts.slice(1).map(Number);
                                this.drawLine(lx1, ly1, lx2, ly2, { r: lr, g: lg, b: lb, a: 255 });
                                this.log(`DRAW_LINE: От (${lx1}, ${ly1}) до (${lx2}, ${ly2}) цвет (${lr}, ${lg}, ${lb})`);
                                break;
                            case 'FILL_RECT':
                                // FILL_RECT x y width height r g b
                                if (parts.length !== 8) throw `Недостаточно параметров для FILL_RECT на строке`;
                                const [frx, fry, frw, frh, fr, fg, fb] = parts.slice(1).map(Number);
                                this.fillRect(frx, fry, frw, frh, { r: fr, g: fg, b: fb, a: 255 });
                                this.log(`FILL_RECT: (${frx}, ${fry}), Ширина: ${frw}, Высота: ${frh}, цвет (${fr}, ${fg}, ${fb})`);
                                break;
                            case 'DRAW_CIRCLE':
                                // DRAW_CIRCLE x y radius r g b
                                if (parts.length !== 7) throw `Недостаточно параметров для DRAW_CIRCLE на строке`;
                                const [cx, cy, cradius, cr, cg, cb] = parts.slice(1).map(Number);
                                this.drawCircle(cx, cy, cradius, { r: cr, g: cg, b: cb, a: 255 });
                                this.log(`DRAW_CIRCLE: Центр (${cx}, ${cy}), Радиус: ${cradius}, цвет (${cr}, ${cg}, ${cb})`);
                                break;
                            case 'DRAW_ELLIPSE':
                                // DRAW_ELLIPSE x y radiusX radiusY r g b
                                if (parts.length !== 8) throw `Недостаточно параметров для DRAW_ELLIPSE на строке`;
                                const [ex, ey, erX, erY, er, eg, eb] = parts.slice(1).map(Number);
                                this.drawEllipse(ex, ey, erX, erY, { r: er, g: eg, b: eb, a: 255 });
                                this.log(`DRAW_ELLIPSE: Центр (${ex}, ${ey}), Радиусы (${erX}, ${erY}), цвет (${er}, ${eg}, ${eb})`);
                                break;
                            case 'DRAW_SPLINE':
                                // DRAW_SPLINE x1 y1 x2 y2 x3 y3 r g b
                                if (parts.length !== 10) throw `Недостаточно параметров для DRAW_SPLINE на строке`;
                                const [sx1, sy1, sx2, sy2, sx3, sy3, sr, sg, sb] = parts.slice(1).map(Number);
                                this.drawQuadraticSpline(sx1, sy1, sx2, sy2, sx3, sy3, { r: sr, g: sg, b: sb, a: 255 });
                                this.log(`DRAW_SPLINE: Точки (${sx1}, ${sy1}), (${sx2}, ${sy2}), (${sx3}, ${sy3}) цвет (${sr}, ${sg}, ${sb})`);
                                break;
                            case 'DRAW_TRIANGLE':
                                // DRAW_TRIANGLE x1 y1 x2 y2 x3 y3 r g b
                                if (parts.length !== 10) throw `Недостаточно параметров для DRAW_TRIANGLE на строке`;
                                const [tx1, ty1, tx2, ty2, tx3, ty3, tr, tg, tb] = parts.slice(1).map(Number);
                                this.drawTriangle(tx1, ty1, tx2, ty2, tx3, ty3, { r: tr, g: tg, b: tb, a: 255 });
                                this.log(`DRAW_TRIANGLE: Точки (${tx1}, ${ty1}), (${tx2}, ${ty2}), (${tx3}, ${ty3}) цвет (${tr}, ${tg}, ${tb})`);
                                break;
                            case 'CLEAR':
                                // CLEAR r g b
                                if (parts.length !== 4) throw `Недостаточно параметров для CLEAR на строке`;
                                const [clr, clg, clb] = parts.slice(1).map(Number);
                                this.clearScreen({ r: clr, g: clg, b: clb, a: 255 });
                                this.log(`CLEAR: цвет (${clr}, ${clg}, ${clb})`);
                                break;
                            case 'SET_VARIABLE':
                                // SET_VARIABLE varName value
                                if (parts.length !== 3) throw `Недостаточно параметров для SET_VARIABLE на строке`;
                                const varName = parts[1];
                                const value = parseInt(parts[2], 10);
                                if (isNaN(value)) throw `Неверное значение для SET_VARIABLE на строке`;
                                this.setVariable(varName, value);
                                break;
                            case 'GET_VARIABLE':
                                // GET_VARIABLE varName
                                if (parts.length !== 2) throw `Недостаточно параметров для GET_VARIABLE на строке`;
                                const getVarName = parts[1];
                                const varValue = this.getVariable(getVarName);
                                break;
                            case 'VERTEX_SHADER':
                                // VERTEX_SHADER code
                                const vertexCode = parts.slice(1).join(' ');
                                this.setShader('VERTEX_SHADER', vertexCode);
                                break;
                            case 'FRAGMENT_SHADER':
                                // FRAGMENT_SHADER code
                                const fragmentCode = parts.slice(1).join(' ');
                                this.setShader('FRAGMENT_SHADER', fragmentCode);
                                break;
                            case 'USE_SHADERS':
                                // USE_SHADERS true/false
                                if (parts.length !== 2) throw `Недостаточно параметров для USE_SHADERS на строке`;
                                this.useShaders = parts[1].toUpperCase() === 'TRUE';
                                this.log(`USE_SHADERS: ${this.useShaders}`);
                                break;
                            case 'FUNCTION':
                                // FUNCTION functionName
                                if (parts.length !== 2) throw `Недостаточно параметров для FUNCTION на строке`;
                                const funcName = parts[1];
                                this.functions[funcName] = [];
                                i++;
                                while (i < this.programMemory.length && this.programMemory[i].toUpperCase() !== 'END_FUNCTION') {
                                    this.functions[funcName].push(this.programMemory[i]);
                                    i++;
                                }
                                if (i === this.programMemory.length) {
                                    throw `Не найдено END_FUNCTION для FUNCTION "${funcName}"`;
                                }
                                this.log(`FUNCTION: "${funcName}" определена с ${this.functions[funcName].length} командами.`);
                                break;
                            case 'CALL':
                                // CALL functionName
                                if (parts.length !== 2) throw `Недостаточно параметров для CALL на строке`;
                                const callFuncName = parts[1];
                                this.callFunction(callFuncName);
                                break;
                            case 'PROCEDURE':
                                // PROCEDURE procedureName
                                if (parts.length !== 2) throw `Недостаточно параметров для PROCEDURE на строке`;
                                const procName = parts[1];
                                this.functions[procName] = [];
                                i++;
                                while (i < this.programMemory.length && this.programMemory[i].toUpperCase() !== 'END_PROCEDURE') {
                                    this.functions[procName].push(this.programMemory[i]);
                                    i++;
                                }
                                if (i === this.programMemory.length) {
                                    throw `Не найдено END_PROCEDURE для PROCEDURE "${procName}"`;
                                }
                                this.log(`PROCEDURE: "${procName}" определена с ${this.functions[procName].length} командами.`);
                                break;
                            case 'CALL_PROCEDURE':
                                // CALL_PROCEDURE procedureName
                                if (parts.length !== 2) throw `Недостаточно параметров для CALL_PROCEDURE на строке`;
                                const callProcName = parts[1];
                                this.callFunction(callProcName); // Используем функции как процедуры
                                break;
                            case 'LOAD_SPRITE':
                                // LOAD_SPRITE url x y
                                if (parts.length !== 4) throw `Недостаточно параметров для LOAD_SPRITE на строке`;
                                const [url, sx, sy] = parts.slice(1);
                                this.loadSprite(url, parseInt(sx, 10), parseInt(sy, 10));
                                break;
                            default:
                                if (instruction === '') {
                                    // Пустая строка, пропускаем
                                } else {
                                    this.log(`Неизвестная инструкция "${instruction}" на строке ${i + 1}`);
                                }
                        }
                    } catch (error) {
                        this.log(`Ошибка: ${error}`);
                    }

                    i++;
                }

                // Обновление Canvas и VRAM Viewer
                this.updateCanvas();
                this.updateViewer();

                // Обновление WebGL (если используется и инициализирован)
                if (this.useShaders && this.gl) {
                    this.updateWebGL();
                }
            }

            async executeCommands(commands) {
                for (let cmd of commands) {
                    let parts = cmd.split(' ').filter(part => part);
                    const instruction = parts[0].toUpperCase();

                    // Замена переменных
                    parts = this.replaceVariables(parts);

                    try {
                        switch (instruction) {
                            case 'DRAW':
                                // DRAW x y r g b
                                if (parts.length !== 6) throw `Недостаточно параметров для DRAW на строке`;
                                const [dx, dy, dr, dg, db] = parts.slice(1).map(Number);
                                this.drawPixel(dx, dy, { r: dr, g: dg, b: db, a: 255 });
                                this.log(`DRAW: (${dx}, ${dy}) цвет (${dr}, ${dg}, ${db})`);
                                break;
                            case 'DRAW_LINE':
                                // DRAW_LINE x1 y1 x2 y2 r g b
                                if (parts.length !== 8) throw `Недостаточно параметров для DRAW_LINE на строке`;
                                const [lx1, ly1, lx2, ly2, lr, lg, lb] = parts.slice(1).map(Number);
                                this.drawLine(lx1, ly1, lx2, ly2, { r: lr, g: lg, b: lb, a: 255 });
                                this.log(`DRAW_LINE: От (${lx1}, ${ly1}) до (${lx2}, ${ly2}) цвет (${lr}, ${lg}, ${lb})`);
                                break;
                            case 'FILL_RECT':
                                // FILL_RECT x y width height r g b
                                if (parts.length !== 8) throw `Недостаточно параметров для FILL_RECT на строке`;
                                const [frx, fry, frw, frh, fr, fg, fb] = parts.slice(1).map(Number);
                                this.fillRect(frx, fry, frw, frh, { r: fr, g: fg, b: fb, a: 255 });
                                this.log(`FILL_RECT: (${frx}, ${fry}), Ширина: ${frw}, Высота: ${frh}, цвет (${fr}, ${fg}, ${fb})`);
                                break;
                            case 'DRAW_CIRCLE':
                                // DRAW_CIRCLE x y radius r g b
                                if (parts.length !== 7) throw `Недостаточно параметров для DRAW_CIRCLE на строке`;
                                const [cx, cy, cradius, cr, cg, cb] = parts.slice(1).map(Number);
                                this.drawCircle(cx, cy, cradius, { r: cr, g: cg, b: cb, a: 255 });
                                this.log(`DRAW_CIRCLE: Центр (${cx}, ${cy}), Радиус: ${cradius}, цвет (${cr}, ${cg}, ${cb})`);
                                break;
                            case 'DRAW_ELLIPSE':
                                // DRAW_ELLIPSE x y radiusX radiusY r g b
                                if (parts.length !== 8) throw `Недостаточно параметров для DRAW_ELLIPSE на строке`;
                                const [ex, ey, erX, erY, er, eg, eb] = parts.slice(1).map(Number);
                                this.drawEllipse(ex, ey, erX, erY, { r: er, g: eg, b: eb, a: 255 });
                                this.log(`DRAW_ELLIPSE: Центр (${ex}, ${ey}), Радиусы (${erX}, ${erY}), цвет (${er}, ${eg}, ${eb})`);
                                break;
                            case 'DRAW_SPLINE':
                                // DRAW_SPLINE x1 y1 x2 y2 x3 y3 r g b
                                if (parts.length !== 10) throw `Недостаточно параметров для DRAW_SPLINE на строке`;
                                const [sx1, sy1, sx2, sy2, sx3, sy3, sr, sg, sb] = parts.slice(1).map(Number);
                                this.drawQuadraticSpline(sx1, sy1, sx2, sy2, sx3, sy3, { r: sr, g: sg, b: sb, a: 255 });
                                this.log(`DRAW_SPLINE: Точки (${sx1}, ${sy1}), (${sx2}, ${sy2}), (${sx3}, ${sy3}) цвет (${sr}, ${sg}, ${sb})`);
                                break;
                            case 'DRAW_TRIANGLE':
                                // DRAW_TRIANGLE x1 y1 x2 y2 x3 y3 r g b
                                if (parts.length !== 10) throw `Недостаточно параметров для DRAW_TRIANGLE на строке`;
                                const [tx1, ty1, tx2, ty2, tx3, ty3, tr, tg, tb] = parts.slice(1).map(Number);
                                this.drawTriangle(tx1, ty1, tx2, ty2, tx3, ty3, { r: tr, g: tg, b: tb, a: 255 });
                                this.log(`DRAW_TRIANGLE: Точки (${tx1}, ${ty1}), (${tx2}, ${ty2}), (${tx3}, ${ty3}) цвет (${tr}, ${tg}, ${tb})`);
                                break;
                            case 'CLEAR':
                                // CLEAR r g b
                                if (parts.length !== 4) throw `Недостаточно параметров для CLEAR на строке`;
                                const [clr, clg, clb] = parts.slice(1).map(Number);
                                this.clearScreen({ r: clr, g: clg, b: clb, a: 255 });
                                this.log(`CLEAR: цвет (${clr}, ${clg}, ${clb})`);
                                break;
                            case 'SET_VARIABLE':
                                // SET_VARIABLE varName value
                                if (parts.length !== 3) throw `Недостаточно параметров для SET_VARIABLE на строке`;
                                const varName = parts[1];
                                const value = parseInt(parts[2], 10);
                                if (isNaN(value)) throw `Неверное значение для SET_VARIABLE на строке`;
                                this.setVariable(varName, value);
                                break;
                            case 'GET_VARIABLE':
                                // GET_VARIABLE varName
                                if (parts.length !== 2) throw `Недостаточно параметров для GET_VARIABLE на строке`;
                                const getVarName = parts[1];
                                const varValue = this.getVariable(getVarName);
                                break;
                            case 'VERTEX_SHADER':
                                // VERTEX_SHADER code
                                const vertexCode = parts.slice(1).join(' ');
                                this.setShader('VERTEX_SHADER', vertexCode);
                                break;
                            case 'FRAGMENT_SHADER':
                                // FRAGMENT_SHADER code
                                const fragmentCode = parts.slice(1).join(' ');
                                this.setShader('FRAGMENT_SHADER', fragmentCode);
                                break;
                            case 'USE_SHADERS':
                                // USE_SHADERS true/false
                                if (parts.length !== 2) throw `Недостаточно параметров для USE_SHADERS на строке`;
                                this.useShaders = parts[1].toUpperCase() === 'TRUE';
                                this.log(`USE_SHADERS: ${this.useShaders}`);
                                break;
                            case 'FUNCTION':
                                // FUNCTION functionName
                                if (parts.length !== 2) throw `Недостаточно параметров для FUNCTION на строке`;
                                const funcName = parts[1];
                                this.functions[funcName] = [];
                                i++;
                                while (i < this.programMemory.length && this.programMemory[i].toUpperCase() !== 'END_FUNCTION') {
                                    this.functions[funcName].push(this.programMemory[i]);
                                    i++;
                                }
                                if (i === this.programMemory.length) {
                                    throw `Не найдено END_FUNCTION для FUNCTION "${funcName}"`;
                                }
                                this.log(`FUNCTION: "${funcName}" определена с ${this.functions[funcName].length} командами.`);
                                break;
                            case 'CALL':
                                // CALL functionName
                                if (parts.length !== 2) throw `Недостаточно параметров для CALL на строке`;
                                const callFuncName = parts[1];
                                this.callFunction(callFuncName);
                                break;
                            case 'PROCEDURE':
                                // PROCEDURE procedureName
                                if (parts.length !== 2) throw `Недостаточно параметров для PROCEDURE на строке`;
                                const procName = parts[1];
                                this.functions[procName] = [];
                                i++;
                                while (i < this.programMemory.length && this.programMemory[i].toUpperCase() !== 'END_PROCEDURE') {
                                    this.functions[procName].push(this.programMemory[i]);
                                    i++;
                                }
                                if (i === this.programMemory.length) {
                                    throw `Не найдено END_PROCEDURE для PROCEDURE "${procName}"`;
                                }
                                this.log(`PROCEDURE: "${procName}" определена с ${this.functions[procName].length} командами.`);
                                break;
                            case 'CALL_PROCEDURE':
                                // CALL_PROCEDURE procedureName
                                if (parts.length !== 2) throw `Недостаточно параметров для CALL_PROCEDURE на строке`;
                                const callProcName = parts[1];
                                this.callFunction(callProcName); // Используем функции как процедуры
                                break;
                            case 'LOAD_SPRITE':
                                // LOAD_SPRITE url x y
                                if (parts.length !== 4) throw `Недостаточно параметров для LOAD_SPRITE на строке`;
                                const [url, sx, sy] = parts.slice(1);
                                this.loadSprite(url, parseInt(sx, 10), parseInt(sy, 10));
                                break;
                            default:
                                if (instruction === '') {
                                    // Пустая строка, пропускаем
                                } else {
                                    this.log(`Неизвестная инструкция "${instruction}" на строке`);
                                }
                        }
                    } catch (error) {
                        this.log(`Ошибка: ${error}`);
                    }
                }

                // Обновление Canvas и VRAM Viewer
                this.updateCanvas();
                this.updateViewer();

                // Обновление WebGL (если используется и инициализирован)
                if (this.useShaders && this.gl) {
                    this.updateWebGL();
                }
            }

            executeFunction(funcName) {
                if (this.functions.hasOwnProperty(funcName)) {
                    this.log(`CALL: Вызов функции "${funcName}"`);
                    this.executeCommands(this.functions[funcName]);
                } else {
                    this.log(`Ошибка: Функция "${funcName}" не определена.`);
                }
            }

            async callFunction(funcName) {
                if (this.functions.hasOwnProperty(funcName)) {
                    this.log(`CALL: Вызов функции/процедуры "${funcName}"`);
                    await this.executeCommands(this.functions[funcName]);
                } else {
                    this.log(`Ошибка: Функция/Процедура "${funcName}" не определена.`);
                }
            }

            updateCanvas() {
                const imageData = this.ctx.createImageData(this.width, this.height);
                const data = imageData.data;

                for (let i = 0; i < this.vramSize; i++) {
                    const pixel = this.vram[i];
                    data[i * 4] = (pixel >> 8) & 0xFF;    // R
                    data[i * 4 + 1] = (pixel >> 16) & 0xFF; // G
                    data[i * 4 + 2] = (pixel >> 24) & 0xFF; // B
                    data[i * 4 + 3] = pixel & 0xFF;         // A
                }

                this.ctx.putImageData(imageData, 0, 0);
            }

            updateViewer() {
                const scaleX = this.vramCanvas.width / this.width;
                const scaleY = this.vramCanvas.height / this.height;
                const imageData = this.vramCtx.createImageData(this.vramCanvas.width, this.vramCanvas.height);
                const data = imageData.data;

                for (let y = 0; y < this.vramCanvas.height; y++) {
                    for (let x = 0; x < this.vramCanvas.width; x++) {
                        const srcX = Math.floor(x / scaleX);
                        const srcY = Math.floor(y / scaleY);
                        const indexSrc = srcY * this.width + srcX;
                        const pixel = this.vram[indexSrc];
                        data[(y * this.vramCanvas.width + x) * 4] = (pixel >> 8) & 0xFF;    // R
                        data[(y * this.vramCanvas.width + x) * 4 + 1] = (pixel >> 16) & 0xFF; // G
                        data[(y * this.vramCanvas.width + x) * 4 + 2] = (pixel >> 24) & 0xFF; // B
                        data[(y * this.vramCanvas.width + x) * 4 + 3] = pixel & 0xFF;         // A
                    }
                }

                this.vramCtx.putImageData(imageData, 0, 0);
            }

            initWebGL() {
                const canvas = document.getElementById('outputWebGL');
                this.gl = canvas.getContext('webgl');
                if (!this.gl) {
                    this.log('WebGL не поддерживается.');
                    this.useShaders = false;
                    return;
                }

                // Настройка шейдеров WebGL
                const vertexShaderSource = `
                    attribute vec4 a_position;
                    attribute vec2 a_texCoord;
                    varying vec2 v_texCoord;

                    void main() {
                        gl_Position = a_position;
                        v_texCoord = a_texCoord;
                    }
                `;
                const fragmentShaderSource = `
                    precision mediump float;
                    varying vec2 v_texCoord;
                    uniform sampler2D u_texture;

                    void main() {
                        gl_FragColor = texture2D(u_texture, v_texCoord);
                    }
                `;
                this.vertexShader = this.createShader(this.gl.VERTEX_SHADER, vertexShaderSource);
                this.fragmentShader = this.createShader(this.gl.FRAGMENT_SHADER, fragmentShaderSource);
                this.program = this.createProgram(this.vertexShader, this.fragmentShader);

                this.positionLocation = this.gl.getAttribLocation(this.program, 'a_position');
                this.texCoordLocation = this.gl.getAttribLocation(this.program, 'a_texCoord');
                this.textureLocation = this.gl.getUniformLocation(this.program, 'u_texture');

                this.positionBuffer = this.gl.createBuffer();
                this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer);
                // Прямоугольник от -1 до 1 по обеим осям
                this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array([
                    -1, -1,
                     1, -1,
                    -1,  1,
                    -1,  1,
                     1, -1,
                     1,  1,
                ]), this.gl.STATIC_DRAW);

                this.texCoordBuffer = this.gl.createBuffer();
                this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.texCoordBuffer);
                // Координаты текстуры от 0 до 1
                this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array([
                    0, 0,
                    1, 0,
                    0, 1,
                    0, 1,
                    1, 0,
                    1, 1,
                ]), this.gl.STATIC_DRAW);

                // Создание текстуры
                this.texture = this.gl.createTexture();
                this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);
                this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST);
                this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST);
                this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
                this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
            }

            createShader(type, source) {
                const shader = this.gl.createShader(type);
                this.gl.shaderSource(shader, source);
                this.gl.compileShader(shader);
                const success = this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS);
                if (success) {
                    return shader;
                }
                console.error(this.gl.getShaderInfoLog(shader));
                this.gl.deleteShader(shader);
            }

            createProgram(vertexShader, fragmentShader) {
                const program = this.gl.createProgram();
                this.gl.attachShader(program, vertexShader);
                this.gl.attachShader(program, fragmentShader);
                this.gl.linkProgram(program);
                const success = this.gl.getProgramParameter(program, this.gl.LINK_STATUS);
                if (success) {
                    return program;
                }
                console.error(this.gl.getProgramInfoLog(program));
                this.gl.deleteProgram(program);
            }

            updateWebGL() {
                // Обновление текстуры из VRAM
                const imageData = new Uint8Array(this.vram.buffer);
                this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);
                this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.width, this.height, 0,
                                   this.gl.RGBA, this.gl.UNSIGNED_BYTE, imageData);

                // Рендеринг
                this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height);
                this.gl.clearColor(0, 0, 0, 0);
                this.gl.clear(this.gl.COLOR_BUFFER_BIT);

                this.gl.useProgram(this.program);

                // Настройка позиционного буфера
                this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer);
                this.gl.enableVertexAttribArray(this.positionLocation);
                this.gl.vertexAttribPointer(this.positionLocation, 2, this.gl.FLOAT, false, 0, 0);

                // Настройка буфера координат текстуры
                this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.texCoordBuffer);
                this.gl.enableVertexAttribArray(this.texCoordLocation);
                this.gl.vertexAttribPointer(this.texCoordLocation, 2, this.gl.FLOAT, false, 0, 0);

                // Активируем текстуру
                this.gl.activeTexture(this.gl.TEXTURE0);
                this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);
                this.gl.uniform1i(this.textureLocation, 0);

                // Рисуем прямоугольник
                this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);
            }

            updateViewer() {
                const scaleX = this.vramCanvas.width / this.width;
                const scaleY = this.vramCanvas.height / this.height;
                const imageData = this.vramCtx.createImageData(this.vramCanvas.width, this.vramCanvas.height);
                const data = imageData.data;

                for (let y = 0; y < this.vramCanvas.height; y++) {
                    for (let x = 0; x < this.vramCanvas.width; x++) {
                        const srcX = Math.floor(x / scaleX);
                        const srcY = Math.floor(y / scaleY);
                        const indexSrc = srcY * this.width + srcX;
                        const pixel = this.vram[indexSrc];
                        data[(y * this.vramCanvas.width + x) * 4] = (pixel >> 8) & 0xFF;    // R
                        data[(y * this.vramCanvas.width + x) * 4 + 1] = (pixel >> 16) & 0xFF; // G
                        data[(y * this.vramCanvas.width + x) * 4 + 2] = (pixel >> 24) & 0xFF; // B
                        data[(y * this.vramCanvas.width + x) * 4 + 3] = pixel & 0xFF;         // A
                    }
                }

                this.vramCtx.putImageData(imageData, 0, 0);
            }
        }

        window.addEventListener('DOMContentLoaded', () => {
            const canvas = document.getElementById('outputCanvas');
            const ctx = canvas.getContext('2d');
            const canvasWebGL = document.getElementById('outputWebGL');
            const gpuProgramTextarea = document.getElementById('gpuProgram');
            const runProgramButton = document.getElementById('runProgram');
            const clearCanvasButton = document.getElementById('clearCanvas');
            const saveProgramButton = document.getElementById('saveProgram');
            const loadProgramButton = document.getElementById('loadProgram');
            const toggleRenderModeButton = document.getElementById('toggleRenderMode');
            const showHelpButton = document.getElementById('showHelp');
            const closeHelpButton = document.getElementById('closeHelp');
            const logContent = document.getElementById('logContent');
            const vramCanvas = document.getElementById('vramCanvas');
            const vramCtx = vramCanvas.getContext('2d');
            const helpModal = document.getElementById('helpModal');

            // Инициализация CodeMirror
            const gpuProgramEditor = CodeMirror.fromTextArea(gpuProgramTextarea, {
                mode: 'javascript',
                lineNumbers: true,
                theme: 'monokai',
                autofocus: true,
                indentUnit: 4,
                tabSize: 4,
                lineWrapping: true,
                extraKeys: {"Ctrl-Space": "autocomplete"}
            });

            // Пример расширенной программы GPU
            const exampleProgram = `
// Пример расширенной программы GPU
CLEAR 10 10 10
FILL_RECT 150 100 300 200 0 128 255
DRAW_LINE 100 50 700 550 255 255 0
DRAW_CIRCLE 400 300 100 255 0 255

// Определение функции для рисования рамки
FUNCTION drawFrame
    FILL_RECT 0 0 800 10 255 255 255
    FILL_RECT 0 590 800 10 255 255 255
    FILL_RECT 0 0 10 600 255 255 255
    FILL_RECT 790 0 10 600 255 255 255
END_FUNCTION

// Вызов функции
CALL drawFrame

// Использование переменных
SET_VARIABLE x 100
SET_VARIABLE y 100

// Цикл для анимации
LOOP 50
    FILL_RECT {x} {y} 50 50 255 0 0
    WAIT 50
    CLEAR 10 10 10
    SET_VARIABLE x {x} + 5
END_LOOP

// Загрузка спрайта
LOAD_SPRITE https://i.imgur.com/4aiwY6A.png 300 200
            `;
            gpuProgramEditor.setValue(exampleProgram);

            // Инициализация эмулятора GPU
            const gpu = new GPUEmulator(canvas.width, canvas.height, ctx, logContent, vramCanvas, vramCtx);

            // Функция для очистки Canvas и лога
            function clearCanvasAndLog() {
                ctx.clearRect(0, 0, canvas.width, canvas.height);
                gpu.clearScreen({ r: 0, g: 0, b: 0, a: 255 });
                logContent.innerHTML = '';
                gpu.updateViewer();
                if (gpu.useShaders && gpu.gl) {
                    gpu.updateWebGL();
                }
            }

            // Обработчик выполнения программы
            runProgramButton.addEventListener('click', async () => {
                const program = gpuProgramEditor.getValue();
                gpu.loadProgram(program);
                await gpu.executeProgram();
                gpu.updateVRAM(vramStatus);
            });

            // Обработчик очистки Canvas
            clearCanvasButton.addEventListener('click', () => {
                clearCanvasAndLog();
                gpu.log('Canvas и лог очищены.');
            });

            // Обработчик сохранения программы
            saveProgramButton.addEventListener('click', () => {
                const program = gpuProgramEditor.getValue();
                const blob = new Blob([program], { type: 'text/plain' });
                const url = URL.createObjectURL(blob);
                const a = document.createElement('a');
                a.href = url;
                a.download = 'gpu_program.txt';
                a.click();
                URL.revokeObjectURL(url);
                gpu.log('Программа сохранена как "gpu_program.txt"');
            });

            // Обработчик загрузки программы
            loadProgramButton.addEventListener('click', () => {
                const input = document.createElement('input');
                input.type = 'file';
                input.accept = '.txt';
                input.onchange = e => {
                    const file = e.target.files[0];
                    if (!file) return;
                    const reader = new FileReader();
                    reader.onload = function(event) {
                        gpuProgramEditor.setValue(event.target.result);
                        gpu.log(`Программа "${file.name}" загружена.`);
                    };
                    reader.readAsText(file);
                };
                input.click();
            });

            // Обработчик переключения режима рендеринга
            toggleRenderModeButton.addEventListener('click', () => {
                gpu.useShaders = !gpu.useShaders;
                if (gpu.useShaders && gpu.gl) {
                    toggleRenderModeButton.textContent = 'Отключить WebGL';
                    canvas.style.display = 'none';
                    canvasWebGL.style.display = 'block';
                    gpu.initWebGL();
                    gpu.log('Рендеринг переключён на WebGL.');
                } else {
                    toggleRenderModeButton.textContent = 'Включить WebGL';
                    canvas.style.display = 'block';
                    canvasWebGL.style.display = 'none';
                    gpu.useShaders = false;
                    gpu.log('Рендеринг переключён на Canvas.');
                }
            });

            // Обработчик отображения помощи
            showHelpButton.addEventListener('click', () => {
                helpModal.style.display = 'block';
            });

            // Обработчик закрытия помощи
            closeHelpButton.addEventListener('click', () => {
                helpModal.style.display = 'none';
            });
        });
    </script>
</body>
</html>