Pisanie boot-sektorow
Autor: Bogdan Drozdowski, bogdandr (at) op.pl


Gdy juz choc srednio znacie assemblera, to po pewnym czasie pojawiaja sie
 pytania (moga one byc spowodowane tym, co uslyszeliscie na grupach
 dyskusyjnych lub Wasza wlasna ciekawoscia):

 - Co sie dzieje, gdy ma zostac uruchomiony jest system operacyjny?
 - Skad BIOS ma wiedziec, ktora czesc systemu uruchomic?
 - Jak BIOS odroznia systemy operacyjne, aby moc je uruchomic?

Odpowiedz na pytanie 2 brzmi: nie wie. Odpowiedz na pytanie 3 brzmi: wcale.
 Wszystkie Wasze watpliwosci rozwieje odpowiedz na pytanie 1.


Gdy zakonczyl sie POST (Power-On Self Test), wykrywanie dyskow i innych
 urzadzen, BIOS przystepuje do czytania pierwszych sektorow tych urzadzen,
 na ktorych ma byc szukany system operacyjny (u mnie jest ustawiona
 kolejnosc: CD-ROM, stacja dyskietek, dysk twardy).

Gdy znajdzie sektor odpowiednio zaznaczony: bajt nr 510 = 55h i bajt
 511 = AAh (pamietajmy, ze 1 sektor ma 512 bajtow, a liczymy od zera),
 to wczytuje go pod adres bezwzgledny 07C00h i uruchamia kod w nim zawarty
 (po prostu wykonuje JMP). Nie nalezy jednak polegac na tym, ze CS = 0 i
 IP=7C00h (choc najczesciej tak jest).


To wlasnie boot-sektor jest odpowiedzialny za ladowanie odpowiednich czesci
 wlasciwego systemu operacyjnego. Na komputerach z wieloma systemami
 operacyjnymi sprawa tez nie jest tak bardzo skomplikowana. Pierwszy sektor
 dysku twardego, zwany Master Boot Record (MBR), uruchamia program ladujacy
 (tzw. Boot Manager, jak LILO czy GRUB), ktory z kolei uruchamia boot-sektor
 wybranego systemu operacyjnego.



My oczywiscie nie bedziemy operowac na dyskach twardych, gdyz byloby to
 niebezpieczne. Z dyskietkami zas mozna ekperymentowac do woli...
A instrukcja jest prosta: umieszczamy nasz programik w pierwszym sektorze
 dyskietki, zaznaczamy go odpowiednimi ostatnimi bajtami i tyle.
 No wlasnie... niby proste, ale jak o tym pomyslec to ani to pierwsze, ani to
 drugie nie jest sprawa banalna.


Do zapisania naszego bootsektorka na dyskietke mozemy oczywiscie uzyc
 "gotowcow" - programow typu rawwrite itp. Ma to pewne zalety - program byl
 juz uzywany przez duza liczbe osob, jest sprawdzony i dziala.

Ale cos by bylo nie tak, gdybym w kursie programowania w assemblerze kazal
 Wam uzywac cudzych programow. Do napisania sowjego wlasnego programu
 zapisujacego dany plik w pierwszy sektor dyskietki w zupelnosci wystarczy
 Wam wiedza uzyskana po przeczytaniu czesci mojego kursu poswieconej
 operacjom na plikach wraz z tym (wyciag oczywiscie ze Spisu Przerwan
 Ralfa Brown'a):

============================
INT 13 - DISK - WRITE DISK SECTOR(S)
        AH = 03h
        AL = number of sectors to write (must be nonzero)
        CH = low eight bits of cylinder number
        CL = sector number 1-63 (bits 0-5)
             high two bits of cylinder (bits 6-7, hard disk only)
        DH = head number
        DL = drive number (bit 7 set for hard disk)
        ES:BX -> data buffer
Return: CF set on error
        CF clear if successful
============================


Jak widac, sprawa juz staje sie prosta. Oczywiscie, AL=1 (bo zapisujemy
 1 sektor), DX=0 (bo stacja ma 2 glowice, a pierwsza ma numer 0, zas numer
 dysku 0 wskazuje stacje A:), CX=1 (bo numery sektorow zaczynaja sie od 1,
 a zapisujemy w pierwszym cylindrze i ma on numer 0).

Tak wiec, schemat jest taki:
 - Otworz plik
 - Przeczytaj z niego 512 bajtow do pamieci
 - Zapisz je na dyskietce
 - Zamknij plik
Sprawa jest tak prosta, ze tym razem nie podam "gotowca".


Gdy juz mamy program zapisujacy bootsektor na dyskietke, trzeba sie postarac
 o to, aby nasz programik (ktory ma stac sie tym bootsektorem) mial
 dokladnie 512 bajtow i aby 2 ostatnie jego bajty to 55h, AAh.

Oczywiscie, nie bedziemy recznie dokladac tylu bajtow, ile trzeba, aby
 dopelnic nasz program do tych 512. Zrobi to za nas kompilator. Wystarczy
 po calym kodzie i wszystkich danych, na szarym koncu, umiescic takie cos
 (NASM/FASM):

		times 510 - ($ - start) db 0
		dw 0aa55h

Dla TASMa powinno to wygladac mniej wiecej tak:

		db 510 - ($ - offset start) dup (0)
		dw 0aa55h
end start


To wyrazenie mowi tyle: od biezacej pozycji w kodzie odejmij pozycje poczatku
 kodu (tym samym obliczajac dlugosc calego kodu), otrzymana liczbe odejmij
 od 510 - i doloz tyle wlasnie bajtow zerowych. Gdy juz mamy program dlugosci
 510 bajtow, to dokladamy jeszcze znacznik i wszystko jest dobrze.

Jest jednak jeszcze jedna sprawa, o ktorej nie wspomnialem - ustawienie DS i
 wartosci "org" dla naszego kodu. Otoz, jesli stwierdzimy, ze nasz kod
 powinien zaczynac sie od offsetu 0 w naszym segmencie, to ustawmy sobie
 "org 0" i DS=07C0h (tak, ilosc zer sie zgadza), ale mozemy tez miec
 "org 7C00h" i DS=0. Zadne z tych nie wplywa w zaden sposob na dlugosc
 otrzymanego programu, a nalezy o to zadbac, gdyz nie mamy gwarancji, ze DS
 bedzie pokazywal na nasze dane po uruchomieniu bootsektora.


Teraz, uzbrojeni w niezbedna wiedze, zasiadamy do pisania kodu naszego
 bootsektora. Nie musi to byc cos wielkiego - tutaj pokaze cos, co w lewym
 gornym rogu ekranu pokaze cyfre "1" (o bezposredniej manipulacji ekranem
 mozecie przeczytac w moim innym artykule) i po nacisnieciu dowolnego
 klawisza zresetuje komputer (na jeden ze sposobow podanych w jeszcze
 innym artykule...).


Oto nasz kod (NASM):

======================================
; nasm -o boot.bin -f bin boot.asm

org 7c00h				; lub "org 0"

start:
	mov	ax, 0b800h
	mov	es, ax			; ES = segment pamieci ekranu

	mov	byte [es:0], '1'	; piszemy '1'

	xor	ah, ah
	int	16h			; czekamy na klawisz

	mov	bx, 40h
	mov	ds, bx
	mov	word [ds:72h], 1234h	; 40h:72h = 1234h - wybieramy
					; goracy reset

	jmp	0ffffh:0000h		; reset

times 510 - ($ - start) db 0		; dopelnienie do 510 bajtow
dw 0aa55h				; znacznik
======================================


Nie bylo to dlugie ani trudne, prawda? Rzecz jasna, nie mozna w bootsektorach
 uzywac zadnych przerwan systemowych, np. DOSowego int 21h, bo zaden system
 po prostu nie jest uruchomiony i zaladowany. Tak napisany programik
 kompilujemy do formatu binarnego. W TASMie wygladaloby to jakos tak
 (po dodaniu w programie dyrektyw ".model tiny", ".code", ".8086" i
 "end start"):

	tasm bootsec1.asm
	tlink bootsec1.obj,bootsec1.bin /t

Po kompilacji umieszczamy go na dyskietce przy uzyciu programu napisanego juz
 przez nas wczesniej. Resetujemy komputer (i upewniamy sie, ze BIOS sprobuje
 uruchomic system z dyskietki), wkladamy dyskietke i.... cieszymy sie swoim
 dzielem (co prawda ta jedynka bedzie malo widoczna, ale rzeczywiscie
 znajduje sie na ekranie).



Zauwazcie tez, ze ani DOS ani Windows nie rozpoznaje juz naszej dyskietki,
 mimo iz przedtem byla sformatowana. Dzieje sie tak dlatego, ze w
 bootsektorze umieszczane sa informacje o dysku.


"Prawidlowy" DOSowy/Windowsowy bootsektor powinien sie zaczynac tak:

org 7c00h			; lub "org 0", oczywiscie

start:
	jmp short kod
	nop

;==================================

	db '        '		; nazwa OS i wersja OEM (8B)
	dw 512			; bajtow/sektor (2B)
	db 1			; sektory/jednostke alokacji (1B)
	dw 1			; zarezerwowane sektory (2B)
	db 2			; liczba tablic alokacji (1B)
	dw 224			; liczba pozycji w katalogu glownym (2B),
				; zwykle 224

	dw 2880			; liczba sektorow (2B)
	db 0f0h			; Media Descriptor Byte (1B)
	dw 9			; sektory/FAT (2B)
	dw 18			; sektory/sciezke (2B)
	dw 2			; liczba glowic (2B)
	dd 0			; liczba ukrytych sektorow (4B)
	dd 0			; liczba sektorow (czesc 2), jesli
				; wczesniej 0 (4B)

	db 0			; numer dysku (1B)
	db 0			; zarezerw. (1B)
	db 0			; rozszerzona sygnatura bloku ladujacego (1B)
	dd 0bbbbddddh		; numer seryjny dysku (4B)
	db '           '	; etykieta (11B)
	db 'FAT 12  '		; typ FAT (8B), zwykle "FAT 12  "

;==================================

kod:
	; tutaj dopiero kod bootsektora

======================================



Ta porcja oczywiscie uszczupla ilosc kodu, ktora mozna umiescic w
 bootsektorze. Nie jest to jednak duzy problem, gdyz i tak jedyna rola
 wiekszosci bootsektorow jest uruchomienie innych programow (tzw. second
 stage boot-loaders), ktore dopiero zajmuja sie ladowaniem wlasciwego
 systemu.



Jeszcze ciekawostka: co wypisuje BIOS, gdy dysk jest niewlasciwy
  (tj. niesystemowy)?
Otoz - nic! BIOS bardzo chetnie przeszedlby do kolejnego urzadzenia.
Dlaczego wiec tego nie robi i skad ten napis o niewlasciwym dysku systemowym??
Odpowiedz jest prosta - sformatowana dyskietka posiada bootsektor!

Dla BIOSu jest wszystko OK, uruchamia wiec ten bootsektor. Dopiero ten
 wypisuje informacje o niewlasciwym dysku, czeka na nacisniecie klawisza,
 po czym uruchamia int 19h. O tym, co robi przerwanie 19h mozecie
 przeczytac w artykule o resetowaniu.


Milego bootowania systemu!

P.S. Jesli nie chcecie przy najdrobniejszej zmianie kodu resetowac komputera,
 mozecie poszukac w Internecie programow, ktore symuluja procesor (w tym
 faze ladowania systemu). Jednym z takich programow jest Bochs, ktory
 znajdziecie tu: http://bochs.sourceforge.net/.

