; bootloader.asm ; Bootloader dzialajacy dla dyskow twardych (albo pendrive bo tez dziala jak dysk) sformatowanych na FAT16 ; Szuka w katalogu glownym pliku kernel.bin, wczytuje go do pamieci i odpala ;) ; By Fishcake ; Najpierw stworzymy poprawny MBR z danymi odczytanymi z dysku recznie org 0x7C00 jmp start OEM_Name db "CursedOS" ; nazwa systemu BytesPerSector dw 0x0200 ; bajtow na sektor SectorsPerCluster db 0x04 ; sektorow na klaster ReservedSectors dw 0x0001 ; zarezerwowanych sektorow FATCopies db 0x02 ; liczba kopii FAT MaxRootDirEntries dw 0x0200 ; liczba wpisow w katalogu glownym NumberOfSectorsSmall dw 0xFFF0 ; liczba sektorow dla malych partycji MediaDescriptor db 0xF8 ; F8 - dysk twardy, F0 - dyskietka SectorsPerFAT dw 0x0040 ; liczba sektorow przypadajacych na jedna FAT SectorsPerTrack dw 0x0020 ; liczba sciezek NumberOfHeads dw 0x0040 ; liczba glowic HiddenSectors dd 0x00000000 ; liczba ukrytych sektorow NumberSectorsHuge dd 0x00000000 ; Liczba sektorow dla duzych dyskow (NumberOfSectorsSmall jakby nie styklo) LogicalDriveNumber dw 0x0080 ; numer napedu ExtendedSignature db 0x29 ; sygnaturka SerialNumber dd 0x5678D47D ; numer seryjny VolumeName db "CursedOS " ; etykietka FATName db "FAT16 " ; nazwa wersji fata start: ; tu zaczynamy impreze cli ; wylaczamy obsluge przerwan - bedziemy ustawiac rejestry segmentowe push word 0x0000 ; adres do naszego wykorzystania pop ss ; ustawiamy tam segment stosu push word 0xFFF0 pop sp ; a wielkosc ustawiamy na najwieksza mov ax, cs ; tak mov ds, ax mov es, ax ; ladujemy do dwoch rejestrow segmentowych mov fs, ax mov gs, ax sti ; i odpalamy obsluge przerwan ; W tym miejscu za pomoca przerwania 13h i funkcji 0x08h pobierzemy dane o dysku i zapiszemy je w pamieci mov ah, 0x08 ; read disc paramz mov dl, byte [LogicalDriveNumber] ; numer dysku int 13h inc ch ; te zamotanie to dlatego ze z byte musialem word zrobic xor ax, ax mov al, ch mov word [maxTrack], ax ; wrzucamy tytaj parametry inc dh mov al, dh mov word [maxHead], ax mov al, cl mov word [maxSector], ax ; Obliczamy wielkosc Root Directory i zapisujemy w CX zeby pasowalo do funkcji ReadSectors mov ax, 0x0020 ; 32-bajtowy opis kazdego pliku w katalogu xor dx, dx ; czyscimy DX (DX:AX mul costam) mul word [MaxRootDirEntries] ; mnozymy przez maksymalna liczbe wpisow otrzymujac wielkosc :) div word [BytesPerSector] ; i dzielimy przez liczbe bajtow na sektor otrzymujac ilosc sektorow ktore to zajmuje xchg ax, cx ; wsadzamy do cx wartosc ax ; Obliczamy polozenie Root Directory i zapisujemy w AX z tego samego powodu co wyzej ;) mov ax, word [SectorsPerFAT] ; wrzucamy liczbe sektorow na jedna tablice FAT xor dx, dx mul word [FATCopies] ; mnozymy przez liczbe tablic FAT add ax, word [ReservedSectors] ; dodajemy liczbe sektorow mov word [DataSector], ax ; zapisujemy to w pamieci add word [DataSector], cx ; i dodajemy wielkosc root dir otrzymujac poczatek sekcji z danymi ; Odczytujemy ten sztycht mov bx, 0x7E00 ; odczytujemy po 512 bajtow wiec zaczynamy impreze od 0x0000:0x7E00 (obszar zaraz za bootloaderem) call ReadSectors ; Tutaj zaczynamy szukac naszego pliku i sprawdzac czy wogole on tam jest mov cx, word [MaxRootDirEntries] ; bedziemy odczytywac nie wiecej razy niz jest mozliwe. W CX jest licznik dla instrukcji loop xor di, di mov di, 0x7E00 ; adres poczatku obszaru przeszukiwanego xor si, si mov si, KernelName ; adres nazwy do wyszukania SearchForKernel: push cx ; zeby nam czasem licznik sie nie zepsul mov cx, 0xB ; 11 znakow ma kernelek push di ; di / si jest zmieniane przez cmpsb to trzeba zachowac push si rep cmpsb ; sprawdz ten sztycht pop si ; polozylismy to podnosimy pop di je LoadKernel ; jesli nazwa pasuje to ladujemy kernela pop cx ; jesli nie to bierzemy licznik ktory schowalismy add di, 0x20 ; przestawiamy na nastepny wpis w RootDirectory (kazdy ma 32b) loop SearchForKernel ; i patrzymy na nastepny jmp KernelNotFound ; jesli nie ma no to skaczemy ze nie ma ;) LoadKernel: mov ax, [di+0x1A] ; na tej pozycji jest numer klastra gdzie lezy nasz kernelson mov word [cluster], ax ; zapisujemy pierwszy klaster ; tutaj zaladujemy tablice FAT mov cx, word [SectorsPerFAT] ; ladujemy do CX liczbe sektorow ktora zajmuje FAT mov ax, word [ReservedSectors] ; a do ax poczatek mov bx, 0x7E00 ; ladujemy to zamiast wczesniej odczytanego RootDir - bo nam juz niepotrzebny call ReadSectors push word 0x0100 ; ustawiamy wartosc ES pop es mov ax, word [cluster] xor bx, bx ; adres pod ktory wrzucamy (zaczynamy od 0x0100:0x0000) FATLoop: call ClusterToSector ; zamieniamy numer klastra na adres mov cl, byte [SectorsPerCluster] ; ilosc sektorow do odczytania call ReadSectors ; odczytujemy pierwszy sektor push bx mov ax, word [cluster] shl ax, 1 ; mnozymy klaster razy 2 otrzymujac offset z ktorego odczytamy mov bx, ax ; wrzucamy do bx mov ax, [bx+0x7E00] ; odczytujemy wartosc z tablicy FAT mov word [cluster], ax ; wrzucamy to do numeru klastra bez wzgledu na to co tam jest pop bx cmp ax, 0xFFFF ; porownujemy wartosc odczytana, jesli jest 0xFFFF to znaczy ze to ostatni klaster pliku jne FATLoop ; wiec jesli nie pasuje to czytamy nastepny jmp 0x0100:0x0000 ; i odpalamy odczytany kernel KernelNotFound: jmp $ ; -------- ; Potrzebne funkcyjki ; -------- ; -------- ; ClusterToSector ; Funkcja przyjmuje numer klastra i przelicza go na adres pliku (w AX) ; -------- ClusterToSector: push dx ; kladziemy uzywane zmienne na stos push cx sub ax, 2 ; odejmujemy 2 xor dx, dx xor cx, cx mov cl, byte [SectorsPerCluster] ; mnozymy przez liczbe sektorow na klaster mul cx add ax, word [DataSector] ; i dodajemy miejsce poczatku sektora z danymi majac polozenie pliku pop cx pop dx ; przywracamy zmienne ret ; -------- ; SectorCHS - konwersja adresu w postaci numeru sektora liczac od poczatku na CHS (czyli cylinder head sector) ; Funkcja przyjmuje w rejestrze AX numer ; -------- SectorCHS: push bx ; kladziemy ax na stos push ax xor bx, bx mov ax, word [maxSector] mul word [maxHead] xor dx, dx xchg ax, bx ; tu mamy obliczenie cylindra pop ax div bx mov byte [absoluteTrack], al mov ax, dx xor dx, dx ; zerujemy do dzielenia. Dzielenie wyglada tak: (DX:AX div costam) stad musimy wyzerowac dx... div word [maxSector] ; dzielimy przez maksymalna liczbe sektorow otrzymujac tym samym w ax numer glowicy a w dx sektor mov byte [absoluteHead], al ; zapisujemy ;) inc dl mov byte [absoluteSector], dl pop bx ret ; i wracamy ;------- ; ReadSectors - odczytuje cx sektorow z dysku zaczynajac od ax pod adres es:bx ; Przyjmowane argumenty: ; cx - liczba sektorow do odczytu ; ax - poczatek obszaru do odczytu ; es:bx - adres w pamieci pod ktory odbywa sie odczyt ;------- ReadSectors: push ax push bx push cx ; zapisujemy je na stosie bo bedziemy zmieniac a sa nam potrzebne call SectorCHS ; zamieniamy adres na CHS mov ah, 0x02 ; czytanie sektorow z dysku mov al, 0x01 ; ilosc - 1 mov ch, byte [absoluteTrack] ; sciezka mov cl, byte [absoluteSector] ; sektor mov dh, byte [absoluteHead] ; glowica mov dl, byte [LogicalDriveNumber] ; dysk int 13h ; czytamy pop cx pop bx pop ax add bx, word [BytesPerSector] ; przelaczamy sie na nastepny sektor inc ax loop ReadSectors ; i powtarzamy az sie nie skonczy ilosc sektorow do odczytania ret ; jesli koniec to wracamy ; ------- ; Zmienne ; ------- absoluteTrack db 0x00 ; sciezka (cylinder) absoluteHead db 0x00 ; glowica absoluteSector db 0x00 ; sektor maxTrack dw 0x0000 maxHead dw 0x0000 ; miejsce na dane o nosniku maxSector dw 0x0000 DataSector dw 0x0000 ; polozenie sektora z danymi (jego poczatku) cluster dw 0x0000 KernelName db "KERNEL BIN" times 510-($-$$) db 0x00 dw 0xAA55