Hogyan írjunk saját BootManagert - 3. rész
Ahogy az előző részben ígértem, most a bootmanager konkrét megvalósításával fogunk foglalkozni. Egy olyan példa programot fogok bemutatni, ami 448 byte-nál kisebb, így elfér az MBR-ben, és nem kell egynél több szektort használni hozzá.
Foglaljuk össze mégegyszer mit is kell csinálnunk.
A teljes MBR-t a 0000:7C00 címre olvassa be a BIOS.
1.)
Ezt át kell másolnunk máshova, majd oda kell ugranunk.
2.)
Meg kell jeleníteni egy menüt, amiből a felhasználó kiválasztja
a kívánt operációs rendszert. (ami most a lehető legegyszerűbb lesz.)
3.)
A kiválasztott operációsrendszerhez tartozó partícióról beolvassuk a
bootszektort a 00:7c00 címre, majd odaugrunk.
Kezdjük az első lépéssel. Erre találunk egy kész rutint, ha visszafejtük az eredeti MBR-t.
00000000: xor
ax,ax
00000002: mov
ss,ax
;veremszegmens nullázása
00000004: mov
sp,07C00
;veremmutató = 7c00
00000007: sti ;
megszakítások engedélyezése
00000008: push ax
00000009: pop es ;extraszegmens nullázása
0000000A: push ax
0000000B: pop ds ;adatszegmens nullázása
0000000C: cld ;directionflag törlése a
movsb-hez
0000000D: mov
si,07C1B
; forrásindex
00000010: mov
di,0061B
; célindex
00000013: push
ax
;verembe 0 és 0061B
00000014: push di
00000015: mov cx,001E5 ; ciklusszámláló
00000018: repe
movsb ; másolás
forrás => cél, cx-szer.
0000001A: retf ; ugrás a
veremben megadott címre
0000001B: a másoló rutin után következő kód [...]
Ez a rutin másolja az 00:7C1B címről a 00:0061B címre a másoló rutin után lévő bootloadert vagy bootmanagert.
Most foglalkozzunk a kiíratásokkal és a “menüvel”. Fontos, hogy semmilyen DOS szolgáltatást, vagyis int 021H-t nem használhatunk ugyanis még semmilyen oprendszer nincs betöltve. A kiíratáshoz is csak a BIOS szolgáltatásokat használhatjuk. (INT 010H).
Csináljunk először is egy képernyő törlést. A
videomemória szegmenscíme szöveges módban B800H. A képernyőről feltételezzük, hogy 80x25 karakteres módban van. Ekkor 80*25 = 2000, de mivel
egy karakterhez 2 byte tartozik, az attribútum byte miatt, így 4000 byte-ot kell teleírnunk egy szóközzel
és mindegyikhez kell egy fekete attribútum byte.
mov
ax,0b800H
;videomem szegmens es-ben
mov
es,ax
xor
di,di ; célindex = 0
mov ax,0020H ; 20H a szóköz 00 az attribútum
mov
cx,2000
; mivel szavakat írunk, így csak 2000-szer fut le a ciklus
rep
stosw ; ciklus amíg a cx nem 0.
Ezzel a képernyőt letöröltük. Most megjelenítjük rajta a szöveget. Ehhez a 10H megszakítás 13H funkcióját fogjuk használni.
AH = 13, AL=0, BL= attribútum, BH=képernyőoldal, DX=kurzor
kezdőhelyzete, CX=karakterek száma, ES:BP
= a string címe.
A program elején definiáljuk a következő változókat. (ez csak egy példa természetesen, mindenkinek más és más)
FIRST db '[1] Mandrake'
LENGTH_FIRST equ $-FIRST
SECOND db '[2] WindowsXP'
LENGTH_SECOND equ $-SECOND
THIRD db '[3] Windows98'
LENGTH_THIRD equ $-THIRD
sTITLE db '..:: BootManager by CodeX ::..',7
LENGTH_sTITLE equ $-sTITLE
sTEXT db '--== Choose OperatingSystem. ==--'
LENGTH_sTEXT equ $-sTEXT
Kiírjuk az sTITLE szöveget,
alá az sTEXT-et, majd egymás alá a 3 operációs rendszer nevét.
mov ax, 01300H
mov
bl, 9
;kék színű szöveg
xor bh, bh
mov dl, TX ; oszlop
mov
dh, TY
; sor
mov cx, LENGTH_sTITLE ; a szöveg hossza
mov bp, offset sTITLE-100H + 7c00H ; ehhez később
fűzök magyarázatot
int 010H
; bios hívása
add dh, 2
;2 sorral alá a következő szöveg
mov cx, LENGTH_sTEXT
mov bp, offset sTEXT-100H + 7c00H
int 010H
mov dl, X ;oszlop ; első oprendszer neve
mov
dh, Y
;sor
mov cx, LENGTH_FIRST
mov bp, offset FIRST-0100H + 7c00H
int 010H
inc
dh
;sor
mov
cx, LENGTH_SECOND ; második neve
mov bp, offset SECOND-0100H + 7c00H
int 010H
inc
dh
;sor
mov
cx, LENGTH_THIRD ; harmadik neve
mov bp, offset
THIRD-0100H + 7c00H
int 010H
A címbeállításról annyit, hogy mivel com-ba fogjuk fordítani a programot, ezért az elhelyezkedés számlálót 100H-ra kell állítanunk a program elején az org direktívával. Így ezt ki kell vonnunk mindig belőle, és mivel a program még megtalálható a 00:7C00 címen, az offset string + 7c00H–nál vannak a szövegek, amiket ki akarunk írni. (az elején már átmásoltuk magunkat máshova, így ott is megvannak, azokat is használhatnánk).
Ezután egy várakozó ciklus következik, amíg a felhasználó meg nem
nyomja az 1-3 billentyűk valamelyikét. (a 3 oprendszer közül kell
választani). Ehhez a 16H-s megszakítás AH=0 funkcióját
fogjuk használni.
Definiáljuk az ASCII kódokat.
KONE equ 031H
KTWO equ 032H
KTHREE equ 033H
A 07BEH címen van az első partíciós bejegyzés. A 8 byte-tal arrébb pedig a partícióhoz tartozó bootszektor szektorszáma (logikai szektorszám, nem CHS). Addig várunk, amíg a felhasználó le nem üti az 1-3 billentyű valamelyikét. Ha az elsőt választja, akkor a 7beH+8, és 7beh+10 címről beolvassuk a dx és cx regiszterekbe a szektorszámot. A másodiknál a 7beH+8+16 és 7beH+10+16, a harmadiknál a 7beH+8+32 és 7beH+10+32 címekről olvasunk. Az si regiszterben pedig azt tároljuk, hogy hányas oprendszert választotta ki a felhasználó. (0, 1, 2 )
mov bp, 07beh ; A 07BEH címen
van az első partíciós bejegyzés
kwait:
xor ah, ah
int 016h ;al = ASCII code
cmp al, KONE
jnz skip1
;;;;;;;;;;;;;;;;;;;;;
mov dx, [word ptr bp+8] ; szektor szám alsó
szó
mov cx, [word ptr bp+8+2] ; szektor szám felső szó
xor si, si
;si = Partition 0-2
;;;;;;;;;;;;;;;;;;;;;
jmp end_cikl
skip1:
cmp al, KTWO
jnz skip2
;;;;;;;;;;;;;;;;;;;;; 2. oprendszer
add bp, 16
mov dx, [word ptr bp+8] ; szektor szám alsó szó
mov cx, [word ptr bp+8+2] ; szektor szám felső szó
mov si, 1 ;
si = 0-2
jmp end_cikl
;;;;;;;;;;;;;;;;;;;;;
skip2:
cmp al, KTHREE
jnz kwait
;;;;;;;;;;;;;;;;;;;;; 3. oprendszer
add bp, 32
mov dx, [word ptr bp+8] ; szektor szám felső szó
mov cx, [word ptr bp+8+2] ; szektor szám felső szó
mov si, 2 ;si
= Partition 0-3
;;;;;;;;;;;;;;;;;;;;;
end_cikl:
Ezután következik a megfelelő bootszektor beolvasása. Továbbá van még egy fontos dolog amit el kell végeznünk. Mielőtt ráugranánk a bootszektor betöltő rutinjára, be kell állítanunk a bootflag-et, vagyis a kiválasztott partíciót aktívra kell állítanunk.
Ehhez előbb töröljük mind a négyet és beállítjuk 80H-ra a kiválasztottat.
Az első bootflag a 07c00H+01BEH címen van, a második 16byte-tal arrébb és így tovább.
pusha ; elmentjük a regisztereket,
mert a dx-cx-ben tároltuk a szektorszámot
xor al, al
mov cx, 4
mov
di, 07c00H+01BEH
;1BEH + 7c00 első
Null_BootFlag:
stosb
add di, 15 ; azért csak 15 mert a stosb már eggyel növelte
loop
Null_BootFlag
mov di, 07c00H+01BEH
shl si, 4 ;bx (0..3) * 16 si-ben van a kiválasztott partíció száma
(0-3) ezt szorozzuk 16-tal. Ezzel 0-t, 16-t vagy 32-t adunk hozza a di regiszterhez
add di, si
mov
al, 080H ;
így a kiválasztottat állítjuk aktívra
stosb
Most ezt az MBR-t vissza kell írnunk. Ehhez az előző részben bemutatott
int13H megszakítás 3-as funkcióját fogjuk használni.
mov
bx, 07c00
;es:bx-ről írunk
mov ah, 3
mov
al, 1
; 1 szektort írunk
mov dl, DRIVE ;
a program elején DRIVE equ 080H (első winchester)
mov dh, 0
mov
cx, 1
; mov 0. oldal 0. sáv 1. szektor
int 013h ; írás
Most lesz szükség a már az előző részben használt struktúrára.
tpacket struc
psize db 16 ;mérete 16 byte
reserved db 0 ; ez mindig 0
number_of_blocks dw 1 ; 1 szektort fogunk olvasni
buffer_ofs dw 07c00H ; erre a címre (offset)
buffer_seg dw 0 ; erre a címre (szegmens)
abssec_lo dd ? ; ezt állítjuk be cx-dx-re
absec_hi dd 0 ; 64bitből 32 mindig 0
tpacket ends
packet tpacket <>
Visszatöltjük a regisztereket a veremből.
popa
lea
si, packet - 0100H + 07c00H ;a packet címe si-ben
mov
word ptr [si].abssec_lo, dx ; a partíció bootszektorának szektorszáma
mov
word ptr [si+2].abssec_lo, cx ;a partíció bootszektorának szektorszáma
mov ah, 042H
mov dl, DRIVE
int 013H ;EXTENDED BIOS READ olvasás a
0: 7C00-címre
mov ax, 0A07H ;Bee-Beep
megszólaltatjuk a pcSpeakert J
xor bh, bh
mov cx, 1
int 010H
push ds ; ugrás a
bootsectorra, 00:7c00 (ds =0 )
mov ax, 07c00H
push ax
retf
Innentől már a bootszektor betöltő rutinja vezérli a továbbiakat és ha minden jól megy akkor el kell indítania a kiválasztott operációs rendszert.
A komplett forráskódot nem csatolom, ugyanis ha valaki meg akarná valósítani, úgyis az ő partícióinak megfelelően kell megírnia a programot. Még egy nagyon fontos dolgot szeretnék leírni, a fenti példa csak elsődleges partíciókkal működik, ha esetleg elkövettük azt a hibát hogy logikai partícióra raktunk operációsrendszert, akkor máshogy kell megoldanunk a dolgot. J. Erre esetleg egy későbbi cikkben kitérünk.
A következő részben azzal foglalkozunk majd, hogyan másolhatjuk fel a bootmanagert az MBR-be.
Magyar
Attila - m.magyar3@chello.hu