Дисклеймер?
Хай Хабр! Это серия статей по написанию моей ОС с нуля. Я лютый фанат ретропрограммирования, поэтому я мгновенно забуду про существование EDК. Просьба не писать комменты по типу "BIOS давно устарела где UEFI?". Пишу это просто чтобы было, что почитать вечером и порелаксить. Спасибо.
Давайте договоримся
Если вы не владеете языком ассемблера, то можете испытать сложности в понимании происходящего. Пользуюсь я ассемблером FASM
Включение ПК
При включении ПК, процессор загружает в ОЗУ БИОС, она (БИОС) в свою очередь считывает носители на наличие загрузочной сигнатуры - слова 0х55АА по смещению 0x1FE. Если она (сигнатура) присутствует, то первые 512 байт с носителя загружается в ОЗУ по адресу 0х7С00 и БИОС передает управление этому коду.
К делу
Напишем загрузчик, который очистит экран и напечатает "Привет, мир!":
format binary as "sec" use16 org 0x7C00 jmp boot nop db 'HEXOS ' ; db 8 dw 512 ; bytes per sector db 1 ; sectors per cluster dw 1 ; number of reserver sectors db 2 ; count of FAT data structures dw 224 ; count of 32-byte dir. entries (224*32 = 14 sectors) dw 2880 ; count of sectors on the volume (2880 for 1.44 mbytes disk) db 0f0h ; f0 - used for removable media dw 9 ; count of sectors by one copy of FAT dw 18 ; sectors per track dw 2 ; number of heads dd 0 ; count of hidden sectors dd 0 ; count of sectors on the volume (if > 65535) db 0 ; int 13h drive number db 0 ; reserved db 29h ; Extended boot signature db 0 ; Volume serial number db 'HEXOS ' ; Volume label (db 11) db 'HAT16 ' ; file system type (db 8) msg db "Hello, World!!", 0x0D, 0x0A, 0x00 printsz: mov ah, 0x0E .cycle: lodsb test al, al jz .end int 0x10 jmp .cycle .end: ret ; ; boot: mov ax, 0x0003 int 0x10 mov si, msg call printsz ; cli hlt ; times 512-$+$$-2 db 0x00 db 0x55, 0xAA
Итак, запускаем это чудо в qemu и видим:

Ура! Всё работает. Но я называю это "Загрузчик", несмотря на то, что он ничего не загружает. Нехорошо. Нам нужно также избавится от ограничения в 512 байт. в такой маленький обьем мало-ли что уместится.
Подходим к делу серьезно
Итак, нам нужно избавиться от ограничения в 512 байт. для этого, как вариант, можно написать весь интересующий нас код отдельно, а потом просто подгрузить его. Да, для этого и существуют загрузчики.
К делу 2
Напишем загрузчик, который будет загружать остальной код с диска в ОЗУ по адресу 0х7Е00(или же 0х7С00+512):
boot.asm
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Copyright (C) HexOS author 2019-2022. All rights reserved. ;; ;; Distributed under terms of the GNU General Public License ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; format binary as "sec" use16 org 7C00h jmp boot_entry nop include "bpb.inc" msg db "HexOS bootloader v2.1.3 by Ivan Chetchasov", 0Dh, 0Ah, 0x00 log db "Loading second stage...", 0Dh, 0Ah, 0x00 include "boot.inc" boot_entry: cls printsz msg printsz log mov ah, 02h mov al, 10h mov cx, 0002h mov bx, 7E00h movs es, 0000h int 13h mov sp, 7E0h movs ds, 7E00h jmp 0000:7E00h cli hlt jmp $-2 times 200h-2h-$+$$ db 00h dw 0AA55h
boot.inc
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Copyright (C) HexOS author 2019-2022. All rights reserved. ;; ;; Distributed under terms of the GNU General Public License ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; __print_stringz__: mov ah, 0x0E __print_stringz__.__print_loop__: lodsb test al, al jz __print_stringz__.__print_ending__ int 10h jmp __print_stringz__.__print_loop__ __print_stringz__.__print_ending__: ret macro printsz charptr { push ax si mov si, charptr call __print_stringz__ pop si ax } macro cls { push ax mov ax, 0003h int 10h pop ax } macro movs reg, src { push ax mov ax, src mov es, ax pop ax }
second.asm
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) HexOS author 2019-2022. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
format binary as "sec"
org 7E00h
start:
use16
; header zone
jmp second_entry
nop
; import zone
include "second.inc"
; executable zone
second_entry:
cls
;printsz msg0
mov ah, 02h
mov al, 08h
mov cx, 0012h; 0012h is correct
; dl was not modified
mov bx, 8100h
movs es, 0000h
int 13h
jc err0
mov sp, 810h
movs ds, 8100h
jmp 0000:8100h
err0:
; construct BSOD stylish
cls
MOV AH, 06h
XOR AL, AL
XOR CX, CX
MOV DX, 184Fh
MOV BH, 17h
INT 10h
; print data
printsz bsod0
jmp endall
endall:
cli
hlt
jmp $-2
; data zone
msg0 db "HexOS Second-stage Bootloader v2.2.1 by Ivan Chetchasov", newline
db "LOG: Loading HAT16 filetable", newline, 00h
bsod0:
db newline
db newline
db " ((((((", newline
db " ((::::::( ERROR OCCURRED", newline
db " ((:::::::( At position: 00007E32h", newline
db " (:::::::((", newline
db " (::::::( Reason: cannot load kernel", newline
db " :::::: (:::::( Maybe your disk is corrupted", newline
db " :::::: (:::::( So try to re-install system", newline
db " :::::: (:::::( Or append file 'System/kernel.hex'", newline
db " (:::::( To your disk with other PC", newline
db " (:::::( (be careful, maybe virus killed", newline
db " (:::::( your PC, don`t infect other one!)", newline
db " :::::: (::::::( ", newline
db " :::::: (:::::::(( ", newline
db " :::::: ((:::::::( ", newline
db " ((::::::(", newline
db " ((((((", newline
db newline
db "Errcode: 0000000Dh Errname: ERROR_CANNOT_LOAD_KERNEL", newline, 00h
; filler
times 200h*16-1+start-$ db 00h
; magic
db EOF
second.inc
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Copyright (C) HexOS author 2019-2022. All rights reserved. ;; ;; Distributed under terms of the GNU General Public License ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; __print_stringz__: mov ah, 0x0E __print_stringz__.__print_loop__: lodsb test al, al jz __print_stringz__.__print_ending__ int 10h jmp __print_stringz__.__print_loop__ __print_stringz__.__print_ending__: ret macro printsz charptr { push ax si mov si, charptr call __print_stringz__ pop si ax } macro cls { push ax mov ax, 0003h int 10h pop ax } macro movs reg, src { push ax mov ax, src mov es, ax pop ax } newline equ 0Dh, 0Ah EOF equ 128
kernel.asm
format binary as "hex" org 8100h mov ah, 0x0E mov al, "X" int 0x10 cli hlt times 0E0h*200h+$$-$ db 0x00
image.asm
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Copyright (C) HexOS author 2019-2022. All rights reserved. ;; ;; Distributed under terms of the GNU General Public License ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; file "boot\boot.sec" file "boot\second.sec" file "kernel\kernel.hex"
при запуске в qemu, мы видим во-такую букву "Х":

Думаю, на этом пока все. Спасибо за внимание!
П.Ы.: Принимаю любые предложения по развитию проекта в лс Хабра
Ссылки:
