значит решил я написать загрузчик. я не очень в этом прокачен, поэтому обратился сюда.
stage1.asm:
org 0x7C00
bits 16
start:
cli
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7C00
sti
mov si, msg_loading
call print_string
; Загружаем stage2 с диска
mov ah, 0x02
mov al, 1
mov ch, 0
mov cl, 2 ; Сектор 2 (следующий после MBR)
mov dh, 0
mov dl, 0x80
mov bx, 0x7E00
int 0x13
jc disk_error
;передача управления stage2
jmp 0x7E00
print_string:
lodsb
or al, al
jz .done
mov ah, 0x0E ; Функция BIOS "вывести символ"
int 0x10 ; Вызываем BIOS
jmp print_string
.done:
ret
disk_error:
mov si, msg_disk_error
call print_string
hlt
msg_loading db "Loading BoxOS Stage2...", 0x0D, 0x0A, 0
msg_disk_error db "Disk error!", 0x0D, 0x0A, 0
times 510 - ($ - $$) db 0
dw 0xAA55
stage2.asm:
; =============================================
; stage2.asm - Загрузчик (переход в Protected Mode)
; =============================================
org 0x7E00 ; Загружаемся после stage1 (0x7E00)
bits 16
start:
cli ; Отлючаем прерывания
; Вывод сообщения
mov si, msg_stage2
call print_string
; Включаем A20 линию!
call enable_a20
; Загружаем ядро (kernel.bin) в память по адресу 0x100000 (1 МБ)
mov ebx, 0x1000 ; Сегмент (0x1000 << 4 = 0x10000)
mov es, bx
mov bx, 0x0000 ; Смещение (0x10000 + 0x0000 = 0x10000)
mov ecx, 0x3 ; Начинаем с сектора 3 (после stage1 и stage2)
mov edx, 10 ; Читаем 10 секторов (5 КБ — хватит для ядра)
call load_kernel
mov si, msg_kernel_loaded
call print_string
lgdt[gdt_descriptor] ; Загружаем GDT (Global Descriptor Table)
; Включаем Protected Mode
mov eax, cr0
or eax, 0x1
mov cr0, eax
jmp CODE_SEG:init_pm ; Дальний прыжок для активации Protected Mode
; ====== Включение A20 линии ======
enable_a20:
; Проверяем, включена ли уже A20
call check_a20
cmp ax, 1
je .done
; Способ 1: Через BIOS
mov ax, 0x2401
int 0x15
; Способ 2: Через клавиатурный контроллер
cli
call .wait_kbd
mov al, 0xAD
out 0x64, al
call .wait_kbd
mov al, 0xD0
out 0x64, al
call .wait_kbd2
in al, 0x60
push eax
call .wait_kbd
mov al, 0xD1
out 0x64, al
call .wait_kbd
pop eax
or al, 2
out 0x60, al
call .wait_kbd
mov al, 0xAE
out 0x64, al
sti
.done:
ret
.wait_kbd:
in al, 0x64
test al, 2
jnz .wait_kbd
ret
.wait_kbd2:
in al, 0x64
test al, 1
jz .wait_kbd2
ret
; Проверка статуса A20
check_a20:
pushf
push ds
push es
push di
push si
xor ax, ax
mov es, ax
mov di, 0x0500
mov ax, 0xFFFF
mov ds, ax
mov si, 0x0510
mov al, [es:di]
push ax
mov al, [ds:si]
push ax
mov byte [es:di], 0x00
mov byte [ds:si], 0xFF
cmp byte [es:di], 0xFF
pop ax
mov [ds:si], al
pop ax
mov [es:di], al
mov ax, 0
je .exit
mov ax, 1
.exit:
pop si
pop di
pop es
pop ds
popf
ret
; ====== Функция загрузки ядра ======
load_kernel:
; Загружаем ядро в 0x10000 (временный буфер)
mov ax, 0x1000 ; Сегмент 0x1000 (адрес 0x10000)
mov es, ax
xor bx, bx ; ES:BX = 0x1000:0x0000 = 0x10000
; Параметры для int 0x13 (CHS)
mov ah, 0x02 ; Функция чтения
mov al, 10 ; Секторов для чтения (ядро ~5KB)
mov ch, 0 ; Цилиндр 0
mov cl, 3 ; Сектор 3 (после stage1 и stage2)
mov dh, 0 ; Головка 0
mov dl, 0x80 ; Жесткий диск 0x80
int 0x13
jc .error ; Если ошибка (CF=1)
; Копируем из 0x10000 в 0x100000 (1MB)
mov esi, 0x10000 ; Источник
mov edi, 0x100000 ; Назначение
mov ecx, 5120 ; 10 секторов * 512 байт
cld ; Направление копирования (вперед)
rep movsb ; Копируем байт за байтом
; Проверка копирования (первые 4 байта)
mov eax, [0x100000]
cmp eax, 0x000900BC ; Сигнатура из hexdump (первые 4 байта ядра)
jne .error
ret
.error:
mov si, msg_disk_error
call print_string
hlt
; ====== Подпрограммы (16-bit) ======
print_string:
lodsb
or al, al
jz .done
mov ah, 0x0E
int 0x10
jmp print_string
.done:
ret
; ====== Данные (16-bit) ======
msg_stage2 db "Entering Protected Mode...", 0x0D, 0x0A, 0
msg_disk_error db "Kernel load error!", 0x0D, 0x0A, 0
msg_kernel_loaded db "Kernel loaded!", 0x0D, 0x0A, 0
; ====== GDT (Global Descriptor Table) ======
gdt_start:
; Нулевой дескриптор (обязательно)
dq 0x0
gdt_code:
; Дескриптор кода (32-битный сегмент)
dw 0xFFFF ; Лимит (0-15)
dw 0x0 ; База (0-15)
db 0x0 ; База (16-23)
db 10011010b ; Флаги доступа (P=1, DPL=00, S=1, Type=1010)
db 11001111b ; Флаги + лимит (16-19) (G=1, D/B=1, L=0, AVL=0)
db 0x0 ; База (24-31)
gdt_data:
; Дескриптор данных (32-битный сегмент)
dw 0xFFFF
dw 0x0
db 0x0
db 10010010b ; Type=0010 (данные)
db 11001111b
db 0x0
gdt_end:
gdt_descriptor:
dw gdt_end - gdt_start - 1 ; Размер GDT
dd gdt_start ; Адрес GDT
CODE_SEG equ gdt_code - gdt_start ; Селектор кода (смещение 8)
DATA_SEG equ gdt_data - gdt_start ; Селектор данных (смещение 16)
bits 32
init_pm:
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov esp, 90000 ; Устанавливаем стек
; Выводим сообщение (теперь через VGA)
mov si, msg_pm
mov edi, 0xB8000 ; Видеопамять
call print_string_pm
; Переход в ядро
jmp 0x100000
cli
.hlt_loop:
hlt ; Останавливаем процессор
jmp .hlt_loop ; На случай пробуждения
print_string_pm:
mov ah, 0x03 ; Атрибут текста (Ярко бирюзовый, cyan)
.loop:
lodsb
or al, al
jz .done
stosw ; [EDI] = AX (символ + атрибут)
jmp .loop
.done:
ret
msg_pm db "BoxOS: Protected Mode Active!", 0
; ====== Заполняем до 512 байт ======
times 512 - ($ - $$) db 0
файл kernel.asm:
bits 32
section .text
global _start
_start:
extern kernel_main
_start:
; Настраиваем стек
mov esp, 0x90000
call kernel_main
cli
.hlt_loop:
hlt
jmp .hlt_loop
linker.ld:
ENTRY(_start)
SECTIONS
{
. = 0x100000;
.text : { *(.text) }
.data : { *(.data) }
.bss : { *(.bss) }
}
kernel.c:
// Ядро работает в 32-битном Protected Mode
void kernel_main() {
// Указатель на видеопамять (текстовый режим 80x25)
volatile unsigned short *vga_buffer = (unsigned short *)0xB8000;
// Очищаем экран
for (int i = 0; i < 80 * 25; i++) {
vga_buffer[i] = (unsigned short)0x0700; // Чёрный фон, серый текст
}
// Выводим строку
const char *str = "Hello, BoxOS Kernel!";
for (int i = 0; str[i] != '\0'; i++) {
vga_buffer[i] = (unsigned short)(0x0700 | str[i]); // Атрибут + символ
}
// Бесконечный цикл
while (1) {
__asm__ volatile ("hlt"); // Останавливаем процессор
}
}
start.sh:
nasm -f bin src/boot/stage1/stage1.asm -o stage1.bin
nasm -f bin src/boot/stage2/stage2.asm -o stage2.bin
gcc -m32 -ffreestanding -c src/kernel/kernel.c -o kernel.o
nasm -f elf32 src/kernel/kernel.asm -o kernel_asm.o
ld -m elf_i386 -T src/kernel/linker.ld -o kernel.elf kernel_asm.o kernel.o
objcopy -O binary kernel.elf kernel.bin
cat stage1.bin stage2.bin kernel.bin > boxos.img
qemu-system-x86_64 -drive format=raw,file=boxos.img -monitor stdio
структура проекта (пока написано то что я прислал):
.
├── Makefile
├── boxos.img
├── docs
│ └── specs.md
├── kernel.bin
├── kernel.elf
├── kernel.o
├── kernel_asm.o
├── qemu.log
├── src
│ ├── boot
│ │ ├── stage1
│ │ │ ├── stage1.asm
│ │ │ └── stage1.bin
│ │ ├── stage2
│ │ │ ├── config.inc
│ │ │ ├── fs.asm
│ │ │ ├── stage2.asm
│ │ │ └── stage2.bin
│ │ └── stage3
│ │ └── stage3.c
│ ├── kernel
│ │ ├── arch
│ │ │ └── x86
│ │ ├── drivers
│ │ ├── fs
│ │ ├── kernel.asm
│ │ ├── kernel.bin
│ │ ├── kernel.c
│ │ ├── kernel.elf
│ │ ├── kernel.o
│ │ ├── kernel_asm.o
│ │ ├── linker.ld
│ │ └── mm
│ └── lib
│ └── string.asm
├── stage1.bin
├── stage2.bin
├── start.sh
└── tools
└── mkbootimg.py
при запуске start.sh
не загружается, выводится сообщение "Kernel load error!". Что мне надо исправть или добавть, чтобы запускался kernel.c. Буду очень благодарен за помощь!
