DEV Community

iadeveloper
iadeveloper

Posted on

Scratch for kernels

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>BlockC Pro Ultimate - Framebuffer corregido (32/24/16/8 bits)</title>
    <style>
        * { box-sizing: border-box; }
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: #0a0e1a;
            margin: 0;
            padding: 20px;
            height: 100vh;
            display: flex;
            flex-direction: column;
            overflow: hidden;
        }
        .header {
            text-align: center;
            color: white;
            margin-bottom: 15px;
        }
        .header h1 {
            background: linear-gradient(135deg, #ff8c42, #ffd966);
            -webkit-background-clip: text;
            background-clip: text;
            color: transparent;
            margin: 0;
        }
        .mode-bar {
            display: flex;
            justify-content: center;
            gap: 15px;
            margin-bottom: 15px;
        }
        .mode-btn {
            background: #1e2a36;
            border: none;
            padding: 6px 20px;
            border-radius: 30px;
            color: white;
            cursor: pointer;
            font-weight: bold;
        }
        .mode-btn.active {
            background: #e67e22;
            box-shadow: 0 0 8px #e67e22;
        }
        .main-layout {
            display: flex;
            gap: 15px;
            flex: 1;
            min-height: 0;
            overflow: hidden;
        }
        .palette {
            width: 280px;
            background: #111827;
            border-radius: 20px;
            padding: 12px;
            overflow-y: auto;
            border: 1px solid #ff8c4244;
        }
        .palette h3 {
            color: #ffd966;
            margin: 12px 0 6px;
            font-size: 1rem;
        }
        .block-tpl {
            background: #f9f3d9;
            border-left: 10px solid #f4b942;
            border-radius: 10px;
            padding: 8px 10px;
            margin-bottom: 8px;
            cursor: grab;
            transition: 0.05s linear;
        }
        .block-tpl:active { cursor: grabbing; }
        .block-tpl:hover { transform: translateX(3px); background: #fff3e0; }
        .block-title { font-weight: bold; font-size: 0.85rem; }
        .block-preview { font-family: monospace; font-size: 0.7rem; background: #e9e2c7; border-radius: 6px; padding: 2px 6px; margin-top: 3px; }
        .workspace {
            flex: 2;
            background: #ecf0f1;
            border-radius: 20px;
            padding: 12px;
            display: flex;
            flex-direction: column;
            overflow-y: auto;
        }
        .workspace-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            border-bottom: 2px solid #bdc3c7;
            padding-bottom: 6px;
            margin-bottom: 12px;
        }
        .workspace-actions button {
            background: #e67e22;
            border: none;
            color: white;
            padding: 4px 12px;
            border-radius: 20px;
            cursor: pointer;
            margin-left: 6px;
        }
        #blocksContainer {
            display: flex;
            flex-direction: column;
            gap: 10px;
        }
        .workspace-block {
            background: white;
            border-radius: 14px;
            padding: 10px;
            display: flex;
            justify-content: space-between;
            align-items: flex-start;
            border-left: 8px solid #3498db;
            gap: 8px;
        }
        .block-content {
            display: flex;
            flex-wrap: wrap;
            align-items: center;
            gap: 6px;
            flex: 1;
            font-family: monospace;
            font-size: 0.8rem;
        }
        .block-label {
            background: #ecf0f1;
            padding: 2px 10px;
            border-radius: 16px;
            font-size: 0.75rem;
        }
        .block-input {
            border: 1px solid #bdc3c7;
            border-radius: 16px;
            padding: 4px 10px;
            font-family: monospace;
            width: 130px;
        }
        .block-textarea {
            border: 1px solid #bdc3c7;
            border-radius: 12px;
            padding: 4px;
            font-family: monospace;
            font-size: 0.7rem;
            width: 180px;
            resize: vertical;
        }
        .delete-block {
            background: #e74c3c;
            color: white;
            border: none;
            border-radius: 30px;
            width: 26px;
            height: 26px;
            cursor: pointer;
            font-weight: bold;
        }
        .code-panel {
            width: 480px;
            background: #0a0e17;
            border-radius: 20px;
            display: flex;
            flex-direction: column;
            padding: 12px;
            color: #f8f8f2;
            border: 1px solid #ff8c4255;
        }
        .code-panel h3 {
            margin: 0 0 6px;
            color: #f1c40f;
            font-size: 1rem;
        }
        .code-area {
            background: #000000aa;
            border-radius: 14px;
            padding: 10px;
            font-family: 'Fira Code', monospace;
            font-size: 0.7rem;
            overflow: auto;
            flex: 1;
            white-space: pre-wrap;
        }
        .copy-btn, .download-btn, .hw-btn {
            background: #2c7da0;
            margin-top: 10px;
            border: none;
            padding: 6px;
            border-radius: 30px;
            font-weight: bold;
            cursor: pointer;
            color: white;
            width: 100%;
        }
        .download-btn { background: #27ae60; }
        .hw-btn { background: #8e44ad; }
        .button-group {
            display: flex;
            gap: 8px;
        }
        .boot-info {
            font-size: 0.65rem;
            text-align: center;
            margin-top: 8px;
            background: #00000066;
            padding: 6px;
            border-radius: 12px;
        }
        .tab-buttons {
            display: flex;
            gap: 10px;
            margin-bottom: 10px;
        }
        .tab-btn {
            background: #1e2a36;
            border: none;
            padding: 4px 12px;
            border-radius: 20px;
            color: white;
            cursor: pointer;
            font-size: 0.75rem;
        }
        .tab-btn.active { background: #e67e22; }
        .note {
            font-size: 0.7rem;
            background: #ffd96622;
            padding: 4px;
            border-radius: 8px;
            text-align: center;
            margin-top: 6px;
        }
        .modal {
            display: none;
            position: fixed;
            z-index: 1000;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0,0,0,0.7);
            backdrop-filter: blur(4px);
        }
        .modal-content {
            background-color: #1e2a36;
            margin: 5% auto;
            padding: 20px;
            border-radius: 24px;
            width: 80%;
            max-width: 800px;
            max-height: 80vh;
            overflow-y: auto;
            color: #f0f0f0;
            border: 1px solid #ff8c42;
        }
        .modal-content h2, .modal-content h3 { color: #ffd966; }
        .modal-content code {
            background: #0a0e1a;
            padding: 3px 6px;
            border-radius: 8px;
            font-family: monospace;
        }
        .modal-content pre {
            background: #0a0e1a;
            padding: 10px;
            border-radius: 12px;
            overflow-x: auto;
            font-size: 0.8rem;
        }
        .close {
            color: #ff8c42;
            float: right;
            font-size: 28px;
            font-weight: bold;
            cursor: pointer;
        }
        .close:hover { color: #ffd966; }
        .os-tabs {
            display: flex;
            gap: 8px;
            flex-wrap: wrap;
            margin: 15px 0;
        }
        .os-tab {
            background: #2c3e50;
            padding: 6px 12px;
            border-radius: 30px;
            cursor: pointer;
        }
        .os-tab.active { background: #e67e22; }
        .os-content { display: none; }
        .os-content.active { display: block; }
    </style>
</head>
<body>
<div class="header">
    <h1>⌨️ BlockC Pro Ultimate - Framebuffer multicorrección</h1>
    <p>Soporta profundidades de color 32/24/16/8 bits, volatile y alineación Multiboot2</p>
</div>
<div class="mode-bar">
    <button class="mode-btn" data-mode="linux">🐧 Modo Linux</button>
    <button class="mode-btn active" data-mode="kernel">🖥️ Modo Kernel (UEFI/BIOS)</button>
</div>
<div class="main-layout">
    <div class="palette">
        <h3>🖥️ VGA / Framebuffer</h3>
        <div class="block-tpl" draggable="true" data-block-type="vga_print"><div class="block-title">📢 Imprimir texto</div><div class="block-preview">print("texto\n");</div></div>
        <div class="block-tpl" draggable="true" data-block-type="vga_clear"><div class="block-title">🧹 Limpiar pantalla</div><div class="block-preview">clear_screen();</div></div>
        <div class="block-tpl" draggable="true" data-block-type="set_color"><div class="block-title">🎨 Cambiar color (VGA)</div><div class="block-preview">set_color(0x0A);</div></div>
        <h3>⏱️ Delay</h3>
        <div class="block-tpl" draggable="true" data-block-type="delay"><div class="block-title">⏳ Esperar (ms)</div><div class="block-preview">delay_ms(500);</div></div>
        <h3>⌨️ Teclado (driver PS/2)</h3>
        <div class="block-tpl" draggable="true" data-block-type="kb_getchar"><div class="block-title">🔤 Leer carácter</div><div class="block-preview">char c = keyboard_getchar();</div></div>
        <div class="block-tpl" draggable="true" data-block-type="kb_wait_key"><div class="block-title">⏎ Esperar tecla</div><div class="block-preview">keyboard_wait_key();</div></div>
        <div class="block-tpl" draggable="true" data-block-type="kb_have_key"><div class="block-title">📥 ¿Tecla disponible?</div><div class="block-preview">if (keyboard_have_key())</div></div>
        <div class="block-tpl" draggable="true" data-block-type="kb_print_key"><div class="block-title">🔡 Mostrar tecla</div><div class="block-preview">mostrar tecla</div></div>
        <div class="block-tpl" draggable="true" data-block-type="kb_check_specific"><div class="block-title">🔍 ¿Tecla específica?</div><div class="block-preview">if (last_key == 'a')</div></div>
        <h3>🧠 Lógica</h3>
        <div class="block-tpl" draggable="true" data-block-type="int_var"><div class="block-title">🔢 int variable</div><div class="block-preview">int x = 0;</div></div>
        <div class="block-tpl" draggable="true" data-block-type="assign"><div class="block-title">✏️ Asignar</div><div class="block-preview">x = 42;</div></div>
        <div class="block-tpl" draggable="true" data-block-type="if_stmt"><div class="block-title">⚖️ if</div><div class="block-preview">if (x > 0) { }</div></div>
        <div class="block-tpl" draggable="true" data-block-type="while_loop"><div class="block-title">🔄 while</div><div class="block-preview">while (cond) { }</div></div>
        <div class="block-tpl" draggable="true" data-block-type="for_loop"><div class="block-title">🔁 for</div><div class="block-preview">for(...)</div></div>
        <h3>🔌 Puerto serie</h3>
        <div class="block-tpl" draggable="true" data-block-type="serial_put"><div class="block-title">📡 Imprimir por serie</div><div class="block-preview">serial_print("debug\n");</div></div>
    </div>
    <div class="workspace">
        <div class="workspace-header">
            <h3>🧩 Tus bloques</h3>
            <div class="workspace-actions">
                <button id="clearBtn">🗑️ Limpiar</button>
                <button id="demoKernelVga">🎮 Demo: Mini consola</button>
                <button id="demoLinesBtn">📝 Demo: Saltos de línea</button>
                <button id="hwManualBtn" style="background:#8e44ad;">💿 ISO híbrida (UEFI+BIOS)</button>
            </div>
        </div>
        <div id="blocksContainer"></div>
        <div class="note">💡 Escribe en los campos sin perder foco. El kernel ahora soporta cualquier profundidad de color.</div>
    </div>
    <div class="code-panel">
        <div class="tab-buttons">
            <button class="tab-btn active" data-tab="c">📄 kernel.c</button>
            <button class="tab-btn" data-tab="asm">⚙️ boot.asm</button>
        </div>
        <div class="code-area" id="codeArea"></div>
        <div class="button-group">
            <button id="copyBtn" class="copy-btn">📋 Copiar código</button>
            <button id="downloadBtn" class="download-btn">💾 Descargar kernel.c</button>
        </div>
        <div class="boot-info" id="bootInfo"></div>
    </div>
</div>

<div id="hwModal" class="modal">
    <div class="modal-content">
        <span class="close">&times;</span>
        <h2>💿 Crear ISO híbrida (UEFI + BIOS)</h2>
        <p>Instrucciones actualizadas con modo de color 32 bits para framebuffer.</p>
        <div class="os-tabs">
            <div class="os-tab active" data-os="debian">🐧 Debian/Ubuntu</div>
            <div class="os-tab" data-os="arch">🎯 Arch Linux</div>
            <div class="os-tab" data-os="alpine">🗻 Alpine</div>
            <div class="os-tab" data-os="windows">🪟 Windows (WSL)</div>
        </div>
        <div id="os-debian" class="os-content active">
            <pre><code># Instalar herramientas
sudo apt update
sudo apt install nasm gcc-multilib xorriso grub-pc-bin grub-efi-amd64-bin

# Compilar
nasm -f elf32 boot.asm -o boot.o
gcc -ffreestanding -nostdlib -m32 -fno-stack-protector -c kernel.c -o kernel.o
ld -m elf_i386 -Ttext 0x100000 boot.o kernel.o -o kernel.elf

# Crear estructura ISO
mkdir -p iso/boot/grub/fonts
cp kernel.elf iso/boot/
cp /usr/share/grub/unicode.pf2 iso/boot/grub/fonts/ 2>/dev/null || echo "Fuente no encontrada, continuar sin ella"

# grub.cfg con resolución y profundidad 32 bits
cat > iso/boot/grub/grub.cfg << "EOF"
set gfxmode=1024x768x32,auto
set gfxpayload=keep
insmod efi_gop
insmod efi_uga
insmod vbe
insmod font
if loadfont ${prefix}/fonts/unicode.pf2; then
    set gfxmode=auto
    set gfxpayload=keep
    terminal_output gfxterm
fi
set timeout=3
set default=0
menuentry "BlockC Kernel" {
    multiboot2 /boot/kernel.elf
    boot
}
EOF

# Generar ISO híbrida
grub-mkrescue -o blockc.iso iso/

# Probar en QEMU (BIOS)
qemu-system-x86_64 -cdrom blockc.iso -serial stdio
# Probar en QEMU (UEFI)
qemu-system-x86_64 -bios /usr/share/ovmf/OVMF.fd -cdrom blockc.iso -serial stdio

# Grabar en USB (cuidado con el dispositivo)
sudo dd if=blockc.iso of=/dev/sdX bs=4M status=progress</code></pre>
        </div>
        <div id="os-arch" class="os-content"><pre><code>sudo pacman -S nasm gcc-multilib xorriso grub
# Mismos comandos de compilación y copia de fuente</code></pre></div>
        <div id="os-alpine" class="os-content"><pre><code>apk add nasm gcc-multilib xorriso grub</code></pre></div>
        <div id="os-windows" class="os-content"><pre><code>Usa WSL (Ubuntu) y sigue pasos de Debian.</code></pre></div>
        <p><strong>✅ La ISO ahora fuerza modo 32 bits, eliminando errores de escritura en profundidades incompatibles.</strong></p>
    </div>
</div>

<script>
    let blocks = [];
    let nextId = 1;
    let currentMode = "kernel";
    let currentTab = "c";

    // boot.asm MULTIBOOT2 completo (igual que antes, funciona correctamente)
    const BOOT_ASM = `[bits 32]
section .text
align 8
    dd 0xE85250D6
    dd 0x00
    dd (header_end - header_start)
    dd -(0xE85250D6 + 0x00 + (header_end - header_start))

header_start:
    align 8
    dw 5, 0
    dd 20
    dd 0, 0, 0
    align 8
    dw 0, 0
    dd 8
header_end:

global _start
extern kernel_main

_start:
    cli
    mov esp, stack_space
    push ebx
    push eax
    call kernel_main
    hlt

section .bss
resb 8192
stack_space:`;

    // Persistencia, descarga, escape
    function saveProject() { localStorage.setItem("blockc_project", JSON.stringify(blocks)); }
    function loadProject() {
        const data = localStorage.getItem("blockc_project");
        if (data) {
            blocks = JSON.parse(data);
            if (blocks.length) nextId = Math.max(...blocks.map(b=>b.id)) + 1;
        }
    }
    function downloadFile(name, content) {
        const a = document.createElement("a");
        const blob = new Blob([content], { type: "text/plain" });
        a.href = URL.createObjectURL(blob);
        a.download = name;
        a.click();
        URL.revokeObjectURL(a.href);
    }
    function escapeForC(str) {
        return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r').replace(/\t/g, '\\t');
    }

    function blockDataTemplate(type) {
        if (type === 'vga_print') return { text: "Hola mundo", color: "0x07" };
        if (type === 'vga_clear') return {};
        if (type === 'set_color') return { color: "0x0A" };
        if (type === 'delay') return { millis: "500" };
        if (type === 'kb_getchar') return { var_name: "c" };
        if (type === 'kb_wait_key') return {};
        if (type === 'kb_have_key') return {};
        if (type === 'kb_print_key') return {};
        if (type === 'kb_check_specific') return { key: "a" };
        if (type === 'int_var') return { var_name: "x", init_val: "0" };
        if (type === 'assign') return { var_name: "x", value: "42" };
        if (type === 'if_stmt') return { condition: "x > 0", body: "    // acción" };
        if (type === 'while_loop') return { condition: "1", body: "    // bucle" };
        if (type === 'for_loop') return { init: "int i=0", cond: "i<5", inc: "i++", body: "    print(\"i\");" };
        if (type === 'serial_put') return { text: "debug\\r\\n" };
        return {};
    }

    function blockToLines(block) {
        const t = block.type;
        const d = block.data;
        if (t === 'vga_print') return [`print("${escapeForC(d.text)}");`];
        if (t === 'vga_clear') return [`clear_screen();`];
        if (t === 'set_color') return [`set_color(${d.color});`];
        if (t === 'delay') return [`delay_ms(${d.millis});`];
        if (t === 'kb_getchar') return [`char ${d.var_name} = keyboard_getchar();`];
        if (t === 'kb_wait_key') return [`keyboard_wait_key();`];
        if (t === 'kb_have_key') return [`if (keyboard_have_key()) {`, `    // tecla disponible`, `}`];
        if (t === 'kb_print_key') return [`char key = keyboard_getchar();`, `print_char(key);`];
        if (t === 'kb_check_specific') return [`if (last_key == '${d.key}') {`, `    print("Tecla detectada!");`, `}`];
        if (t === 'int_var') return [`int ${d.var_name} = ${d.init_val};`];
        if (t === 'assign') return [`${d.var_name} = ${d.value};`];
        if (t === 'if_stmt') return [`if (${d.condition}) {`, `    ${d.body.replace(/\n/g, '\n    ')}`, `}`];
        if (t === 'while_loop') return [`while (${d.condition}) {`, `    ${d.body.replace(/\n/g, '\n    ')}`, `}`];
        if (t === 'for_loop') return [`for (${d.init}; ${d.cond}; ${d.inc}) {`, `    ${d.body.replace(/\n/g, '\n    ')}`, `}`];
        if (t === 'serial_put') return [`serial_print("${escapeForC(d.text)}");`];
        return [`/* ${t} */`];
    }

    function needsKeyboard() {
        return blocks.some(b => ['kb_getchar','kb_wait_key','kb_have_key','kb_print_key','kb_check_specific'].includes(b.type));
    }

    // GENERADOR DE KERNEL.C CORREGIDO
    function generateKernelC() {
        let lines = [];
        lines.push("// kernel.c - Framebuffer con soporte de profundidad variable, volatile, alineación Multiboot2");
        lines.push("#include <stdint.h>");
        lines.push("");
        lines.push("// ---------- Puertos serie (debug) ----------");
        lines.push("#define COM1 0x3f8");
        lines.push("static inline void outb(uint16_t port, uint8_t val) { asm volatile(\"outb %0, %1\" : : \"a\"(val), \"Nd\"(port)); }");
        lines.push("static inline uint8_t inb(uint16_t port) { uint8_t ret; asm volatile(\"inb %1, %0\" : \"=a\"(ret) : \"Nd\"(port)); return ret; }");
        lines.push("static void serial_putchar(char c) { while(!(inb(COM1+5)&0x20)); outb(COM1,c); }");
        lines.push("void serial_print(const char *s) { while(*s) serial_putchar(*s++); }");
        lines.push("");
        lines.push("// ---------- Framebuffer con soporte de profundidad ----------");
        lines.push("static volatile uint32_t* fb_addr = 0;   // volatile para evitar optimización");
        lines.push("static volatile uint32_t fb_width = 0, fb_height = 0, fb_pitch = 0;");
        lines.push("static volatile uint8_t fb_bpp = 0;");
        lines.push("static volatile int fb_enabled = 0;");
        lines.push("static int fb_cursor_x = 0, fb_cursor_y = 0;");
        lines.push("");
        lines.push("// Escribe un píxel con color (RGB, dependiendo de la profundidad)");
        lines.push("static void fb_put_pixel(uint32_t x, uint32_t y, uint32_t color) {");
        lines.push("    if (!fb_addr || x >= fb_width || y >= fb_height) return;");
        lines.push("    uint8_t* ptr = (uint8_t*)fb_addr + y * fb_pitch + x * (fb_bpp/8);");
        lines.push("    switch(fb_bpp) {");
        lines.push("        case 32: *(uint32_t*)ptr = color; break;");
        lines.push("        case 24: ptr[0] = (color>>16)&0xFF; ptr[1] = (color>>8)&0xFF; ptr[2] = color&0xFF; break;");
        lines.push("        case 16: {");
        lines.push("            uint16_t c16 = ((color>>19)&0x1F)<<11 | ((color>>10)&0x3F)<<5 | ((color>>3)&0x1F);");
        lines.push("            *(uint16_t*)ptr = c16;");
        lines.push("            break;");
        lines.push("        }");
        lines.push("        case 8: *ptr = (color>>16)&0xFF; break;");
        lines.push("        default: break;");
        lines.push("    }");
        lines.push("}");
        lines.push("");
        lines.push("// Dibuja un carácter (8x16) usando el color dado");
        lines.push("static void fb_putchar(char c, uint32_t color) {");
        lines.push("    (void)c; // por simplicidad dibujamos un cuadrado de color");
        lines.push("    for (int py = 0; py < 16; py++)");
        lines.push("        for (int px = 0; px < 8; px++) {");
        lines.push("            uint32_t x = fb_cursor_x*8 + px;");
        lines.push("            uint32_t y = fb_cursor_y*16 + py;");
        lines.push("            if (x < fb_width && y < fb_height)");
        lines.push("                fb_put_pixel(x, y, color);");
        lines.push("        }");
        lines.push("    fb_cursor_x++;");
        lines.push("    if (fb_cursor_x >= fb_width/8) { fb_cursor_x = 0; fb_cursor_y++; }");
        lines.push("}");
        lines.push("");
        lines.push("// ---------- VGA modo texto (fallback) ----------");
        lines.push("static char* const VGA_MEM = (char*)0xB8000;");
        lines.push("static int vga_x = 0, vga_y = 0;");
        lines.push("static uint8_t vga_color = 0x07;");
        lines.push("");
        lines.push("static void vga_scroll(void) {");
        lines.push("    for (int y = 1; y < 25; y++)");
        lines.push("        for (int x = 0; x < 80; x++) {");
        lines.push("            int from = (y*80+x)*2, to = ((y-1)*80+x)*2;");
        lines.push("            VGA_MEM[to] = VGA_MEM[from]; VGA_MEM[to+1] = VGA_MEM[from+1];");
        lines.push("        }");
        lines.push("    for (int x = 0; x < 80; x++) {");
        lines.push("        int pos = (24*80+x)*2;");
        lines.push("        VGA_MEM[pos] = ' '; VGA_MEM[pos+1] = vga_color;");
        lines.push("    }");
        lines.push("    vga_y = 24;");
        lines.push("}");
        lines.push("");
        lines.push("static void vga_putchar(char c) {");
        lines.push("    if (c == 8) {");
        lines.push("        if (vga_x > 0) vga_x--;");
        lines.push("        int pos = (vga_y*80+vga_x)*2;");
        lines.push("        VGA_MEM[pos] = ' '; VGA_MEM[pos+1] = vga_color;");
        lines.push("        return;");
        lines.push("    }");
        lines.push("    if (c == '\\n') { vga_x = 0; vga_y++; }");
        lines.push("    else {");
        lines.push("        int pos = (vga_y*80+vga_x)*2;");
        lines.push("        VGA_MEM[pos] = c; VGA_MEM[pos+1] = vga_color;");
        lines.push("        vga_x++;");
        lines.push("        if (vga_x >= 80) { vga_x = 0; vga_y++; }");
        lines.push("    }");
        lines.push("    if (vga_y >= 25) vga_scroll();");
        lines.push("}");
        lines.push("");
        lines.push("void set_color(uint8_t color) { vga_color = color; }");
        lines.push("");
        lines.push("void clear_screen() {");
        lines.push("    if (fb_enabled && fb_addr) {");
        lines.push("        for (uint32_t y = 0; y < fb_height; y++)");
        lines.push("            for (uint32_t x = 0; x < fb_width; x++)");
        lines.push("                fb_put_pixel(x, y, 0x00000000);");
        lines.push("        fb_cursor_x = fb_cursor_y = 0;");
        lines.push("        serial_print(\"Framebuffer limpiado (\");");
        lines.push("        serial_print((fb_bpp==32?\"32\":fb_bpp==24?\"24\":fb_bpp==16?\"16\":\"8\"));");
        lines.push("        serial_print(\" bpp)\\r\\n\");");
        lines.push("    } else {");
        lines.push("        for (int i = 0; i < 80*25; i++) { VGA_MEM[i*2] = ' '; VGA_MEM[i*2+1] = vga_color; }");
        lines.push("        vga_x = vga_y = 0;");
        lines.push("        serial_print(\"VGA limpiado\\r\\n\");");
        lines.push("    }");
        lines.push("}");
        lines.push("");
        lines.push("void print_char(char c) {");
        lines.push("    if (fb_enabled && fb_addr) fb_putchar(c, 0x00FFFFFF);");
        lines.push("    else vga_putchar(c);");
        lines.push("}");
        lines.push("");
        lines.push("void print(const char *s) { while(*s) print_char(*s++); }");
        lines.push("");
        lines.push("// ---------- Delay ----------");
        lines.push("static void delay_cycles(int count) { for(volatile int i=0;i<count;i++) asm volatile(\"nop\"); }");
        lines.push("void delay_ms(int ms) { for(int i=0;i<ms;i++) delay_cycles(2000); }");
        lines.push("");
        lines.push("// ---------- Teclado (opcional) ----------");
        if (needsKeyboard()) {
            lines.push("#define KEYBOARD_DATA 0x60");
            lines.push("#define KEYBOARD_STATUS 0x64");
            lines.push("static char last_key = 0;");
            lines.push("static int shift_pressed = 0;");
            lines.push("static const char keymap[128] = {");
            lines.push("0,27,'1','2','3','4','5','6','7','8','9','0','-','=',8,9,");
            lines.push("'q','w','e','r','t','y','u','i','o','p','[',']',13,0,'a','s',");
            lines.push("'d','f','g','h','j','k','l',';','\\'','`',0,'\\\\','z','x','c','v',");
            lines.push("'b','n','m',',','.','/',0,'*',0,' ',0};");
            lines.push("static uint8_t keyboard_read_scancode(void) { while(!(inb(KEYBOARD_STATUS)&1)); return inb(KEYBOARD_DATA); }");
            lines.push("char keyboard_getchar(void) {");
            lines.push("    uint8_t sc;");
            lines.push("    while(1) {");
            lines.push("        sc = keyboard_read_scancode();");
            lines.push("        if(sc == 0x2A || sc == 0x36) { shift_pressed = 1; continue; }");
            lines.push("        if(sc == 0xAA || sc == 0xB6) { shift_pressed = 0; continue; }");
            lines.push("        if(sc & 0x80) continue;");
            lines.push("        char c = keymap[sc];");
            lines.push("        if(c) { last_key = c; return c; }");
            lines.push("    }");
            lines.push("}");
            lines.push("int keyboard_have_key(void) { return (inb(KEYBOARD_STATUS) & 1); }");
            lines.push("void keyboard_wait_key(void) { keyboard_getchar(); }");
        }
        lines.push("");
        lines.push("// ---------- Parseo Multiboot2 con alineación correcta ----------");
        lines.push("struct multiboot_tag { uint32_t type; uint32_t size; };");
        lines.push("struct multiboot_tag_fb {");
        lines.push("    uint32_t type; uint32_t size; uint64_t addr;");
        lines.push("    uint32_t pitch; uint32_t width; uint32_t height;");
        lines.push("    uint8_t bpp; uint8_t type_fb;");
        lines.push("};");
        lines.push("");
        lines.push("static void multiboot2_init(unsigned long magic, unsigned long addr) {");
        lines.push("    serial_print(\"multiboot2_init\\r\\n\");");
        lines.push("    if (magic != 0x36d76289) { serial_print(\"Magic incorrecto\\r\\n\"); return; }");
        lines.push("    if (!addr) { serial_print(\"Puntero nulo\\r\\n\"); return; }");
        lines.push("    uint32_t* tags = (uint32_t*)addr;");
        lines.push("    while (1) {");
        lines.push("        uint32_t type = tags[0];");
        lines.push("        uint32_t size = tags[1];");
        lines.push("        if (type == 0) break;");
        lines.push("        if (type == 8) {");
        lines.push("            struct multiboot_tag_fb* fb = (struct multiboot_tag_fb*)tags;");
        lines.push("            if (fb->addr && fb->width && fb->height) {");
        lines.push("                fb_addr = (uint32_t*)(uintptr_t)fb->addr;");
        lines.push("                fb_width = fb->width;");
        lines.push("                fb_height = fb->height;");
        lines.push("                fb_pitch = fb->pitch;");
        lines.push("                fb_bpp = fb->bpp;");
        lines.push("                fb_enabled = 1;");
        lines.push("                serial_print(\"Framebuffer detectado \");");
        lines.push("                serial_print((fb_bpp==32?\"32\":fb_bpp==24?\"24\":fb_bpp==16?\"16\":\"8\"));");
        lines.push("                serial_print(\" bpp\\r\\n\");");
        lines.push("            }");
        lines.push("        }");
        lines.push("        // Alineación a 8 bytes según especificación Multiboot2");
        lines.push("        size = (size + 7) & ~7;");
        lines.push("        tags = (uint32_t*)((char*)tags + size);");
        lines.push("    }");
        lines.push("}");
        lines.push("");
        lines.push("void kernel_main(unsigned long magic, unsigned long addr) {");
        lines.push("    serial_print(\"Kernel_main\\r\\n\");");
        lines.push("    multiboot2_init(magic, addr);");
        lines.push("    clear_screen();");
        lines.push("    print(\"BlockC Pro Ultimate\\n\");");
        lines.push("    print(\"Framebuffer + VGA\\n\");");
        for (let blk of blocks) {
            let blines = blockToLines(blk);
            for (let l of blines) lines.push("    " + l);
        }
        const hasInfiniteLoop = blocks.some(b => b.type === 'while_loop' && b.data.condition.trim() === "1");
        if (!hasInfiniteLoop) lines.push("    while(1);");
        lines.push("}");
        return lines.join("\n");
    }

    function updateDisplay() {
        const area = document.getElementById('codeArea');
        if (currentTab === 'c') area.innerText = generateKernelC();
        else area.innerText = BOOT_ASM;
        const infoDiv = document.getElementById('bootInfo');
        if (currentMode === 'kernel') {
            infoDiv.innerHTML = `🔧 Compilar y crear ISO híbrida con modo color 32 bits:<br>
            nasm -f elf32 boot.asm -o boot.o<br>
            gcc -ffreestanding -nostdlib -m32 -fno-stack-protector -c kernel.c -o kernel.o<br>
            ld -m elf_i386 -Ttext 0x100000 boot.o kernel.o -o kernel.elf<br>
            mkdir -p iso/boot/grub/fonts<br>
            cp kernel.elf iso/boot/<br>
            cp /usr/share/grub/unicode.pf2 iso/boot/grub/fonts/ 2>/dev/null<br>
            (seguir el manual para grub.cfg)<br>
            grub-mkrescue -o blockc.iso iso/`;
        } else {
            infoDiv.innerHTML = `🐧 Modo Linux: gcc kernel.c -o kernel && ./kernel (solo simula)`;
        }
    }

    function refreshCodeAndSave() { updateDisplay(); saveProject(); }

    function renderWorkspace() {
        const container = document.getElementById('blocksContainer');
        container.innerHTML = '';
        for (let block of blocks) {
            const div = document.createElement('div');
            div.className = 'workspace-block';
            const contentDiv = document.createElement('div');
            contentDiv.className = 'block-content';
            const t = block.type;
            const d = block.data;

            if (t === 'vga_print') {
                contentDiv.appendChild(labelSpan("print:"));
                const ta = document.createElement('textarea');
                ta.className = 'block-textarea';
                ta.value = d.text;
                ta.rows = 2;
                ta.addEventListener('input', e => { d.text = e.target.value; refreshCodeAndSave(); });
                contentDiv.appendChild(ta);
                contentDiv.appendChild(labelSpan("color (VGA):"));
                const inp = createInputNoRender(d.color, v=>{ d.color=v; refreshCodeAndSave(); }, "0x07");
                contentDiv.appendChild(inp);
            } else if (t === 'vga_clear') {
                contentDiv.appendChild(labelSpan("clear_screen()"));
            } else if (t === 'set_color') {
                contentDiv.appendChild(labelSpan("set_color("));
                contentDiv.appendChild(createInputNoRender(d.color, v=>{ d.color=v; refreshCodeAndSave(); }));
                contentDiv.appendChild(labelSpan(")"));
            } else if (t === 'delay') {
                contentDiv.appendChild(labelSpan("delay_ms("));
                contentDiv.appendChild(createInputNoRender(d.millis, v=>{ d.millis=v; refreshCodeAndSave(); }));
                contentDiv.appendChild(labelSpan(")"));
            } else if (t === 'kb_getchar') {
                contentDiv.appendChild(labelSpan("char"));
                contentDiv.appendChild(createInputNoRender(d.var_name, v=>{ d.var_name=v; refreshCodeAndSave(); }));
                contentDiv.appendChild(labelSpan("= keyboard_getchar()"));
            } else if (t === 'kb_wait_key') {
                contentDiv.appendChild(labelSpan("keyboard_wait_key()"));
            } else if (t === 'kb_have_key') {
                contentDiv.appendChild(labelSpan("if (keyboard_have_key())"));
            } else if (t === 'kb_print_key') {
                contentDiv.appendChild(labelSpan("Mostrar tecla"));
            } else if (t === 'kb_check_specific') {
                contentDiv.appendChild(labelSpan("if tecla == "));
                contentDiv.appendChild(createInputNoRender(d.key, v=>{ d.key=v; refreshCodeAndSave(); }));
            } else if (t === 'int_var') {
                contentDiv.appendChild(labelSpan("int"));
                contentDiv.appendChild(createInputNoRender(d.var_name, v=>{ d.var_name=v; refreshCodeAndSave(); }));
                contentDiv.appendChild(labelSpan("="));
                contentDiv.appendChild(createInputNoRender(d.init_val, v=>{ d.init_val=v; refreshCodeAndSave(); }));
            } else if (t === 'assign') {
                contentDiv.appendChild(createInputNoRender(d.var_name, v=>{ d.var_name=v; refreshCodeAndSave(); }));
                contentDiv.appendChild(labelSpan("="));
                contentDiv.appendChild(createInputNoRender(d.value, v=>{ d.value=v; refreshCodeAndSave(); }));
            } else if (t === 'if_stmt') {
                contentDiv.appendChild(labelSpan("if ("));
                contentDiv.appendChild(createInputNoRender(d.condition, v=>{ d.condition=v; refreshCodeAndSave(); }));
                contentDiv.appendChild(labelSpan(") {"));
                const ta = createTextareaNoRender(d.body, v=>{ d.body=v; refreshCodeAndSave(); });
                contentDiv.appendChild(ta);
            } else if (t === 'while_loop') {
                contentDiv.appendChild(labelSpan("while ("));
                contentDiv.appendChild(createInputNoRender(d.condition, v=>{ d.condition=v; refreshCodeAndSave(); }));
                contentDiv.appendChild(labelSpan(") {"));
                const ta = createTextareaNoRender(d.body, v=>{ d.body=v; refreshCodeAndSave(); });
                contentDiv.appendChild(ta);
            } else if (t === 'for_loop') {
                contentDiv.appendChild(labelSpan("for ("));
                contentDiv.appendChild(createInputNoRender(d.init, v=>{ d.init=v; refreshCodeAndSave(); }));
                contentDiv.appendChild(labelSpan(";"));
                contentDiv.appendChild(createInputNoRender(d.cond, v=>{ d.cond=v; refreshCodeAndSave(); }));
                contentDiv.appendChild(labelSpan(";"));
                contentDiv.appendChild(createInputNoRender(d.inc, v=>{ d.inc=v; refreshCodeAndSave(); }));
                contentDiv.appendChild(labelSpan(") {"));
                const ta = createTextareaNoRender(d.body, v=>{ d.body=v; refreshCodeAndSave(); });
                contentDiv.appendChild(ta);
            } else if (t === 'serial_put') {
                contentDiv.appendChild(labelSpan("serial_print:"));
                const ta = document.createElement('textarea');
                ta.className = 'block-textarea';
                ta.value = d.text;
                ta.rows = 2;
                ta.addEventListener('input', e => { d.text = e.target.value; refreshCodeAndSave(); });
                contentDiv.appendChild(ta);
            }
            const del = document.createElement('button');
            del.innerText = '';
            del.className = 'delete-block';
            del.onclick = () => { blocks = blocks.filter(b=>b.id!==block.id); renderWorkspace(); updateDisplay(); saveProject(); };
            div.appendChild(contentDiv);
            div.appendChild(del);
            container.appendChild(div);
        }
        updateDisplay();
        saveProject();
    }

    function labelSpan(t) { let s = document.createElement('span'); s.className = 'block-label'; s.innerText = t; return s; }
    function createInputNoRender(val, onChange, ph="") {
        let inp = document.createElement('input');
        inp.className = 'block-input';
        inp.value = val;
        inp.placeholder = ph;
        inp.addEventListener('input', e => onChange(e.target.value));
        return inp;
    }
    function createTextareaNoRender(val, onChange) {
        let ta = document.createElement('textarea');
        ta.className = 'block-textarea';
        ta.value = val;
        ta.rows = 2;
        ta.addEventListener('input', e => onChange(e.target.value));
        return ta;
    }

    function addBlock(type) {
        blocks.push({ id: nextId++, type: type, data: blockDataTemplate(type) });
        renderWorkspace();
    }

    // Drag & Drop
    document.querySelectorAll('.block-tpl').forEach(el => {
        el.setAttribute('draggable', 'true');
        el.addEventListener('dragstart', e => e.dataTransfer.setData('text/plain', el.getAttribute('data-block-type')));
    });
    document.querySelector('.workspace').addEventListener('dragover', e => e.preventDefault());
    document.querySelector('.workspace').addEventListener('drop', e => {
        e.preventDefault();
        const type = e.dataTransfer.getData('text/plain');
        if (type) addBlock(type);
    });

    // Modo Linux/Kernel
    document.querySelectorAll('.mode-btn').forEach(btn => {
        btn.addEventListener('click', () => {
            document.querySelectorAll('.mode-btn').forEach(b=>b.classList.remove('active'));
            btn.classList.add('active');
            currentMode = btn.getAttribute('data-mode');
            updateDisplay();
        });
    });
    // Tabs
    document.querySelectorAll('.tab-btn').forEach(btn => {
        btn.addEventListener('click', () => {
            document.querySelectorAll('.tab-btn').forEach(b=>b.classList.remove('active'));
            btn.classList.add('active');
            currentTab = btn.getAttribute('data-tab');
            updateDisplay();
        });
    });

    document.getElementById('clearBtn').onclick = () => { blocks = []; renderWorkspace(); };
    document.getElementById('demoKernelVga').onclick = () => {
        blocks = [];
        addBlock('vga_clear');
        addBlock('vga_print');
        addBlock('while_loop');
        setTimeout(() => {
            if(blocks[1]) { blocks[1].data.text = "=== Mini Consola BlockC ===\nEscribe y presiona Enter...\n> "; }
            if(blocks[2]) {
                blocks[2].data.condition = "1";
                blocks[2].data.body = `char c = keyboard_getchar();
if(c == '\\n') print("\\n> ");
else print_char(c);`;
            }
            renderWorkspace();
        },10);
    };
    document.getElementById('demoLinesBtn').onclick = () => {
        blocks = [];
        addBlock('vga_clear');
        addBlock('vga_print');
        addBlock('vga_print');
        addBlock('vga_print');
        setTimeout(() => {
            if(blocks[1]) { blocks[1].data.text = "¡Hola!\nEsto es una línea nueva.\n"; }
            if(blocks[2]) { blocks[2].data.text = "Y aquí otra línea.\n"; }
            if(blocks[3]) { blocks[3].data.text = "Usa \\n en tus textos.\n¡Disfruta BlockC!"; }
            renderWorkspace();
        },10);
    };
    document.getElementById('copyBtn').onclick = () => {
        let content = (currentTab === 'c') ? generateKernelC() : BOOT_ASM;
        navigator.clipboard.writeText(content);
        alert("✅ Código copiado");
    };
    document.getElementById('downloadBtn').onclick = () => {
        let content = generateKernelC();
        downloadFile("kernel.c", content);
    };

    // Modal manual
    const modal = document.getElementById('hwModal');
    const hwBtn = document.getElementById('hwManualBtn');
    const spanClose = document.getElementsByClassName('close')[0];
    hwBtn.onclick = () => modal.style.display = "block";
    spanClose.onclick = () => modal.style.display = "none";
    window.onclick = (event) => { if(event.target == modal) modal.style.display = "none"; };
    document.querySelectorAll('.os-tab').forEach(tab => {
        tab.addEventListener('click', () => {
            const os = tab.getAttribute('data-os');
            document.querySelectorAll('.os-tab').forEach(t=>t.classList.remove('active'));
            tab.classList.add('active');
            document.querySelectorAll('.os-content').forEach(c=>c.classList.remove('active'));
            document.getElementById(`os-${os}`).classList.add('active');
        });
    });

    // Inicializar
    loadProject();
    if (blocks.length === 0) {
        addBlock('vga_print');
        setTimeout(() => { if(blocks[0]) { blocks[0].data.text = "BlockC Pro Ultimate\nArranca en UEFI/BIOS con framebuffer 32/24/16/8 bits"; } renderWorkspace(); },10);
    } else renderWorkspace();
</script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Top comments (0)