Дисклеймер?

Хай Хабр! Это серия статей по написанию моей ОС с нуля. Я лютый фанат ретропрограммирования, поэтому я мгновенно забуду про существование 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, мы видим во-такую букву "Х":

X
X

Думаю, на этом пока все. Спасибо за внимание!

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

Ссылки:

  1. HexOS-GitHub

  2. Следующая часть

Комментарии (24)


  1. netricks
    07.06.2022 12:27
    +28

    Тут просто такая ситуация…

    На Хабре есть пара десятков статей, с названием «пишем ос своими руками, часть первая, загрузчик», которые как правило, не имеют продолжений. Я не в коем случае не хочу сказать, что не надо пробовать писать оси, или что не нужно выкладывать таких статей.

    Но, то обстоятельство, что дальше загрузчика дело обычно не уходит, хочу отметить.


  1. netricks
    07.06.2022 11:45

    Из скольки частей будет состоять эта серия статей?


    1. TalismanChet Автор
      07.06.2022 12:13
      +1

      Честно не знаю, я в процессе работы. Спойлер: будут защищенный и долгий режимы.


      1. netricks
        07.06.2022 12:27
        +28

        Тут просто такая ситуация…

        На Хабре есть пара десятков статей, с названием «пишем ос своими руками, часть первая, загрузчик», которые как правило, не имеют продолжений. Я не в коем случае не хочу сказать, что не надо пробовать писать оси, или что не нужно выкладывать таких статей.

        Но, то обстоятельство, что дальше загрузчика дело обычно не уходит, хочу отметить.


        1. TalismanChet Автор
          07.06.2022 12:48
          +7

          Хах, жизненно. До того, как все это изучить, я тоже зачастую натыкался на эту проблему. Что же, я все равно продолжу писать эту серию.


        1. 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


  1. red_crocodile
    07.06.2022 11:50

    Надо будет запустить это на реальном ПК, интересно что получится в конце


    1. TalismanChet Автор
      07.06.2022 12:13

      ага :)


  1. alexxisr
    07.06.2022 12:02

    я тоже лет 20 назад делал такое, но после перехода в защищенный режим и разрешения прерываний всё падало (хотя пару буковок на экран успевало вывести). Через пару лет я узнал что С выравнивает поля в структурах :) и поэтому нельзя просто так писать дескрипторы в них.


    1. TalismanChet Автор
      07.06.2022 12:14
      +2

      Круто! но я знаю решение. нужно просто добавить __attribute__ ((packed)), если я правильно помню


    1. urvanov
      07.06.2022 12:50

      Я тоже когда в университете учился, то по фану делал штуку, которая в реальном режиме с дискеты загружается и что-то рисует. Но потом дальше не осилил. Хотя хотел работу с FAT запилить.


      1. TalismanChet Автор
        07.06.2022 12:55
        +1

        Здорово! Думаю, полноценную ФС уже потом сделаю. Это пока что не очень совместимо с целью. Но уже есть наброски. Думаю, что можно сделать этот проект "коллективным", создав, например, какой-нибудь чат? Надо будет над этим подумать.


  1. amarao
    07.06.2022 14:03

    Для ретропрограммирования вы используете слишком новые технологии. Последний раз, когда я на них смотрел, там был предзагрузчик с выставлением адреса с административной консоли оператора с помощью переключателей.


    1. TalismanChet Автор
      07.06.2022 14:05

      Ну не знаю. Все равно, нашлись же те, кому понравилось. Значит, все ОК :)


  1. ViacheslavNk
    07.06.2022 15:44
    +1

    Лучший на мой взгляд "курс" по написанию OS включая многопоточность, файловую систему, сетевой стек и еще на С++

    https://www.youtube.com/watch?v=1rnA6wpF0o4&list=PLHh55M_Kq4OApWScZyPl5HhgsTJS9MZ6M&ab_channel=WriteyourownOperatingSystem


    1. TalismanChet Автор
      07.06.2022 16:57
      +1

      Спасибо, я его уже посмотрел. Очень хороший "курс". Но я пока ограничусь ассемблером :)


  1. ULTRAWEN
    07.06.2022 17:53

    В 90-х так же баловался, но есть проблема, любой антивирус воспринимает это как бут вирус и стирает сразу.


    1. TalismanChet Автор
      07.06.2022 18:00

      хм, не знал об этом. Может, когда закончу mbr-версию, сделаю и efi.


      1. ULTRAWEN
        07.06.2022 18:07

        Я делал на дискете так как там проще файловая система и проще бут сектор найти. Код вбивал туда вручную в Disk Edit. Это был заранее подготовленный com файл в HEX. Это был челленж. Антивирусы очень сильно ругались :)


        1. TalismanChet Автор
          07.06.2022 18:43
          +1

          джедай среди простых людей)


  1. Silverthorne
    07.06.2022 23:16
    +1

    Если кому интересно, вот написание ОС на Rust от Georgia Institute of Technology - https://tc.gts3.org/cs3210/2020/spring/lab.html


    1. Bromles
      08.06.2022 14:17
      +2

      Вот целый блог по созданию ОС на Rust: https://os.phil-opp.com/

      С прерываниями, планировщиком с поддержкой асинхронности, vga text mode, обработкой ошибок, загрузчиком, пагинацией памяти и тд


  1. AlexSky
    08.06.2022 11:10
    +1

    О, прям как я в 2004. Писал все на ассемблере. Был переход в защищённый режим, переключатель юзерспейсных потоков, несколько сисколлов. До полноценного менеджера памяти не дотянул - забросил.


    1. TalismanChet Автор
      08.06.2022 11:49

      Что же, может моя работа возобновит интерес к данной теме :)


  1. 1MK-Ultra
    08.06.2022 14:20

    Fasm работает из под windows. Это не честно. Нужно написать свою ОС, без помощи других ОС. Вот это будет с нуля.