Дисклеймер?
Хай Хабр! Это серия статей по написанию моей ОС с нуля. Я лютый фанат ретропрограммирования, поэтому я мгновенно забуду про существование 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, мы видим во-такую букву "Х":
Думаю, на этом пока все. Спасибо за внимание!
П.Ы.: Принимаю любые предложения по развитию проекта в лс Хабра
Ссылки:
Комментарии (24)
netricks
07.06.2022 11:45Из скольки частей будет состоять эта серия статей?
TalismanChet Автор
07.06.2022 12:13+1Честно не знаю, я в процессе работы. Спойлер: будут защищенный и долгий режимы.
netricks
07.06.2022 12:27+28Тут просто такая ситуация…
На Хабре есть пара десятков статей, с названием «пишем ос своими руками, часть первая, загрузчик», которые как правило, не имеют продолжений. Я не в коем случае не хочу сказать, что не надо пробовать писать оси, или что не нужно выкладывать таких статей.
Но, то обстоятельство, что дальше загрузчика дело обычно не уходит, хочу отметить.TalismanChet Автор
07.06.2022 12:48+7Хах, жизненно. До того, как все это изучить, я тоже зачастую натыкался на эту проблему. Что же, я все равно продолжу писать эту серию.
maisvendoo
07.06.2022 22:05Писать свой загрузчик - контрпродуктивно. Лучше изучить спецификацию multiboot и применить grub2
Например вот так
http://phantomexos.blogspot.com/2013/07/phantomex-multiboot.html
http://phantomexos.blogspot.com/2013/07/phantomex-hdd-grub2.html
red_crocodile
07.06.2022 11:50Надо будет запустить это на реальном ПК, интересно что получится в конце
alexxisr
07.06.2022 12:02я тоже лет 20 назад делал такое, но после перехода в защищенный режим и разрешения прерываний всё падало (хотя пару буковок на экран успевало вывести). Через пару лет я узнал что С выравнивает поля в структурах :) и поэтому нельзя просто так писать дескрипторы в них.
TalismanChet Автор
07.06.2022 12:14+2Круто! но я знаю решение. нужно просто добавить __attribute__ ((packed)), если я правильно помню
urvanov
07.06.2022 12:50Я тоже когда в университете учился, то по фану делал штуку, которая в реальном режиме с дискеты загружается и что-то рисует. Но потом дальше не осилил. Хотя хотел работу с FAT запилить.
TalismanChet Автор
07.06.2022 12:55+1Здорово! Думаю, полноценную ФС уже потом сделаю. Это пока что не очень совместимо с целью. Но уже есть наброски. Думаю, что можно сделать этот проект "коллективным", создав, например, какой-нибудь чат? Надо будет над этим подумать.
amarao
07.06.2022 14:03Для ретропрограммирования вы используете слишком новые технологии. Последний раз, когда я на них смотрел, там был предзагрузчик с выставлением адреса с административной консоли оператора с помощью переключателей.
TalismanChet Автор
07.06.2022 14:05Ну не знаю. Все равно, нашлись же те, кому понравилось. Значит, все ОК :)
ViacheslavNk
07.06.2022 15:44+1Лучший на мой взгляд "курс" по написанию OS включая многопоточность, файловую систему, сетевой стек и еще на С++
TalismanChet Автор
07.06.2022 16:57+1Спасибо, я его уже посмотрел. Очень хороший "курс". Но я пока ограничусь ассемблером :)
ULTRAWEN
07.06.2022 17:53В 90-х так же баловался, но есть проблема, любой антивирус воспринимает это как бут вирус и стирает сразу.
TalismanChet Автор
07.06.2022 18:00хм, не знал об этом. Может, когда закончу mbr-версию, сделаю и efi.
ULTRAWEN
07.06.2022 18:07Я делал на дискете так как там проще файловая система и проще бут сектор найти. Код вбивал туда вручную в Disk Edit. Это был заранее подготовленный com файл в HEX. Это был челленж. Антивирусы очень сильно ругались :)
Silverthorne
07.06.2022 23:16+1Если кому интересно, вот написание ОС на Rust от Georgia Institute of Technology - https://tc.gts3.org/cs3210/2020/spring/lab.html
Bromles
08.06.2022 14:17+2Вот целый блог по созданию ОС на Rust: https://os.phil-opp.com/
С прерываниями, планировщиком с поддержкой асинхронности, vga text mode, обработкой ошибок, загрузчиком, пагинацией памяти и тд
AlexSky
08.06.2022 11:10+1О, прям как я в 2004. Писал все на ассемблере. Был переход в защищённый режим, переключатель юзерспейсных потоков, несколько сисколлов. До полноценного менеджера памяти не дотянул - забросил.
1MK-Ultra
08.06.2022 14:20Fasm работает из под windows. Это не честно. Нужно написать свою ОС, без помощи других ОС. Вот это будет с нуля.
netricks
Тут просто такая ситуация…
На Хабре есть пара десятков статей, с названием «пишем ос своими руками, часть первая, загрузчик», которые как правило, не имеют продолжений. Я не в коем случае не хочу сказать, что не надо пробовать писать оси, или что не нужно выкладывать таких статей.
Но, то обстоятельство, что дальше загрузчика дело обычно не уходит, хочу отметить.