En este tema, aprenderemos a escribir un cargador de arranque simple utilizando el lenguaje ensamblador. Un cargador de arranque (bootloader) es un programa que se ejecuta cuando se enciende la computadora y se encarga de cargar el sistema operativo en la memoria.

Objetivos del Tema

  • Comprender el propósito y funcionamiento de un cargador de arranque.
  • Configurar el entorno para desarrollar y probar un cargador de arranque.
  • Escribir un cargador de arranque básico en ensamblador.
  • Probar el cargador de arranque en un emulador.

Conceptos Clave

¿Qué es un Cargador de Arranque?

Un cargador de arranque es un programa que se ejecuta al inicio del sistema y se encarga de cargar el sistema operativo en la memoria. Es el primer código que se ejecuta después del BIOS/UEFI.

Funcionamiento Básico

  1. Inicialización del Hardware: Configura los dispositivos básicos necesarios para el arranque.
  2. Carga del Sistema Operativo: Localiza y carga el núcleo del sistema operativo en la memoria.
  3. Transferencia de Control: Transfiere el control al sistema operativo.

Requisitos

  • Conocimiento básico de ensamblador.
  • Un entorno de desarrollo configurado (NASM, QEMU).

Configuración del Entorno

Instalación de NASM y QEMU

Para escribir y probar nuestro cargador de arranque, necesitamos NASM (Netwide Assembler) y QEMU (Quick Emulator).

Instalación en Linux

sudo apt-get update
sudo apt-get install nasm qemu

Instalación en Windows

  1. Descarga e instala NASM desde nasm.us.
  2. Descarga e instala QEMU desde qemu.org.

Escribiendo el Cargador de Arranque

Estructura del Cargador de Arranque

Un cargador de arranque debe cumplir con ciertos requisitos:

  • Debe ser de 512 bytes.
  • Debe terminar con la firma de arranque 0x55AA.

Código del Cargador de Arranque

A continuación, se muestra un ejemplo de un cargador de arranque simple que imprime un mensaje en la pantalla.

[BITS 16]          ; Estamos en modo real de 16 bits

start:
    mov si, msg    ; Cargar la dirección del mensaje en SI
    call print_string

hang:
    jmp hang       ; Bucle infinito

print_string:
    mov ah, 0x0E   ; Función de BIOS para imprimir un carácter
.next_char:
    lodsb          ; Cargar el siguiente byte en AL
    cmp al, 0      ; ¿Es el byte nulo?
    je .done       ; Si es nulo, terminar
    int 0x10       ; Llamar a la interrupción de BIOS para imprimir
    jmp .next_char ; Repetir para el siguiente carácter
.done:
    ret            ; Retornar de la subrutina

msg db 'Hello, World!', 0  ; Mensaje a imprimir

times 510-($-$$) db 0      ; Rellenar hasta 510 bytes
dw 0xAA55                  ; Firma de arranque

Compilación y Ejecución

  1. Compilar el código:

    nasm -f bin -o bootloader.bin bootloader.asm
    
  2. Ejecutar en QEMU:

    qemu-system-x86_64 -drive format=raw,file=bootloader.bin
    

Ejercicio Práctico

Ejercicio 1: Modificar el Mensaje

Modifica el mensaje del cargador de arranque para que imprima tu nombre.

Solución

[BITS 16]

start:
    mov si, msg
    call print_string

hang:
    jmp hang

print_string:
    mov ah, 0x0E
.next_char:
    lodsb
    cmp al, 0
    je .done
    int 0x10
    jmp .next_char
.done:
    ret

msg db 'Hello, [Tu Nombre]!', 0

times 510-($-$$) db 0
dw 0xAA55

Ejercicio 2: Añadir un Bucle de Contador

Añade un bucle que cuente de 0 a 9 y muestre los números en la pantalla antes de imprimir el mensaje.

Solución

[BITS 16]

start:
    mov cx, 10       ; Contador de 10 iteraciones
    mov bx, 0        ; Inicializar el contador en BX
count_loop:
    mov al, bl       ; Mover el valor de BX a AL
    add al, '0'      ; Convertir a carácter ASCII
    mov ah, 0x0E     ; Función de BIOS para imprimir un carácter
    int 0x10         ; Llamar a la interrupción de BIOS
    inc bx           ; Incrementar el contador
    loop count_loop  ; Repetir hasta que CX sea 0

    mov si, msg
    call print_string

hang:
    jmp hang

print_string:
    mov ah, 0x0E
.next_char:
    lodsb
    cmp al, 0
    je .done
    int 0x10
    jmp .next_char
.done:
    ret

msg db 'Hello, [Tu Nombre]!', 0

times 510-($-$$) db 0
dw 0xAA55

Conclusión

En esta sección, hemos aprendido a escribir un cargador de arranque simple en ensamblador. Hemos cubierto la configuración del entorno, la estructura básica de un cargador de arranque y cómo compilar y ejecutar nuestro código. Además, hemos practicado con ejercicios para reforzar los conceptos aprendidos. En el siguiente tema, exploraremos cómo crear un núcleo básico de sistema operativo.

© Copyright 2024. Todos los derechos reservados