Operační Systém
Začnu tím, že se pokusím definovat pojem operační systém. Kdysi „dávno” před osobními počítači, kdy byly počítače velké jako tělocvičny a neexistovaly OS, program se nadírkoval (nahrál) a když skončil, nadírkoval se další. Ale počítačový čas byl moc drahý na takové plýtvání, a tak vznikly první OS, a to jako programy, které jen postupně pouští ostatní programy. Bylo také velmi nepraktické, že každý program musel mít vlastní ovladače k hardwaru a skoro každý prvek hardwaru byl unikátní, takže přenositelnost programů byla minimální. Takže se do OS začalo integrovat jednotné rozhraní mezi programy a periferiemi (ovladač).
Postupným vývojem jsme se dostali až k dnešní podobě, kdy, díky hardwarové podpoře CPU multitaskingu a multiprocesingu, může dnešní OS střídat běžící programy tak, že slyšíme hudbu, stahujeme soubory a zároveň třeba programujeme. Co ale tedy je operační systém? Je to program, který přiděluje programům paměť a hlavně je „pouští” na procesor. Nedílnou součástí je jednotný přístup k periferiím, sítím, diskům, souborovým systémům. Dnešní OS obsahují i širokou škálu vnitřních nástrojů jako sdílené knihovny nebo komunikaci mezi jednotlivými programy (tzv. systém zpráv). Je to tedy program, který je správce (hardwarových) prostředků.
CPU
Při každém novém procesoru se v minulosti také skoro vždy změnila architektura, buď změnou registrů, nebo se jednoduše přešlo z 4 bitů na 8 bitů. To znamenalo, že se každý program, OS nevyjímaje, musel přepsat, ne-li napsat zcela znovu, což bylo velice nákladné a brzdilo rozvoj.
Intel & x86
V roce 1978 firma Intel přišla na trh z procesorem Intel 8086. Byl zpětně kompatibilní s 8b procesory. Byl 16 bitový, ale adresovací sběrnici měl 20 bitovou, mohl tedy adresovat 1 MB fyzické paměti, pracoval v reálném režimu (real mode). Každý program mohl přistupovat do celé paměti, takže bezpečnost a separovatelnost programů byla na minimální úrovni. Podporoval přerušení jak hardwarové, tak i softwarové, které známe třeba z programování pod dosem. Důležité ale je, že od roku 1978 mluvíme o architektuře x86.
Dalším milníkem se stal procesor Intel 386, který byl 32b. A podporoval chráněný režim (protected mode), což je systém správy paměti, který umožňuje alokovat 4GB fyzické paměti, podporuje tzv. stránkování a virtuální paměť, kde může OS oddělit jednotlivé programy od sebe a simulovat větší paměť ukládáním stránek na disk (swap). Chráněný režim také napomáhá operačnímu systému s multitaskingem. Není ale zpětně kompatibilní s reálným režimem, což nám ale i386 vynahrazuje virtuálním režimem, který simuluje reálný. A tak můžeme programy psané pro reálný režim za kooperace OS pouštět i v chráněném. Chráněný režim podporoval už procesor Intel 286, který byl ale ještě 16bitový.
Budeme se zabývat pouze procesory x86 kompatibilními, což je asi nejpoužívanější architektura osobních počítačů. Důležité je, že každý procesor po startu (restartu) pracuje v 16 bitovém reálném adresovacím režimu a nezáleží na tom, jestli to je „stařičká” i386 nebo nejnovější 64bitový procesor architektury ia64. Je už jen na operačním systému, aby se přepnul do jiných režimů.
Boot systému
Jak tedy probíhá boot samotného systému? Po startu se nahraje do paměti BIOS a pustí se. My si nastavíme bootování z disketové mechaniky. BIOS vezme náš označený (poslední dva Byte mají speciální hodnotu) boot sector, což je první sector na disketě. Nahraje ho do paměti na místo 0000:7c00h a spustí ho.
Reálný režim
Teď si více osvětlíme reálný režim, ať víme, jak náš boot sector napsat. K dispozici máme 14 16-bitových registrů. Všeobecné: AX, BX, CX, DX, indexové a ukazatelové: SP, BP, SI, DI, segmentové: CS, DS, SS, ES a dále: IP a FLAGS. Důležitý je vztah segmentových registrů. Jak již víme, adresa je tvořena 20bity, ale registry máme 16 bitové. Adresa se tedy vytváří sečtením jednoho ze segmentových registrů posunutého o 4 bity (a vynásobeného 16) a zbytku adresy, tzv. offsetu. Adresa = segment * 16 + offset. Zapisujeme: „segment:offset”. 12 bitů se překrývá, neboli jednu adresu můžeme zapsat několika způsoby, třeba 0000:7c00h = 07c0:0000h = 00c0:7000h. Podle následujícího schematu se implicitně používají segmentové registry: SS:SP, SS:BP, DS:BX, DS:SI, DS:DI, ES:DI (řetězcové operace). Explicitní přiřazení v intel syntaxi vypadá následovně:
mov ah,es:[ukazatel]
mov ah,es:offset
Příklady
Ukážeme si příklad boot sectoru:
; boot_sector1.asm
org 0x7c00 ;nastaveni segmentu
jmp main
;promene
boot_msg db 'Muj prvni OS...',13,10,0
klav_msg db 'Stisknete jakoukoliv klavesu k restartu',13,10,0
bootdev db 0
zprava: ;funkce na vypis na obrazovku
lodsb
or al,al
jz short zprava_konec
mov ah,0eh
mov bx,0007h
int 10h
jmp zprava
zprava_konec:
ret
klavesa: ;funkce na stisk klavesy
mov ah,00h
int 16h
ret
main:
cli
mov ax,9000h
mov ss,ax
mov sp,0xffff ;nastaveni zasobniku
sti
mov [bootdev],dl ;dl = zarizeni ze ktereho je bootovano
mov si,boot_msg ;vypis boot zpravy
call zprava
mov si,klav_msg
call zprava
call klavesa
jmp 0xffff:0000h ;restart
times 510-($-$) db 90h ;sector musi byt dlouhy 512B
dw 0xaa55 ;oznaceni boot sectoru
Program přeložíme do výstupního formátu: plain binary file a nahrajeme na disketu:
nasm -o boot_sector.asm -f bin boot_sector.bin
dd if=boot_sector.bin of=/dev/fd0 bs=512
Pokud se budeme chtít podívat, jak soubor formátu plain binary vypadá po přeložení:
ndisasm -b 16 boot_sector.bin
První direktiva programu (org 0×7c00) říká překladači, aby každému skoku na návěstí (call zprava), popřípadě adresaci proměnné (mov si,boot_msg) přičetl danou hodnotu, a to z důvodu umístění programu v paměti. Program se začne vykonávat prvním bytem souboru, a proto nesmí být začátek souboru datový, ale instrukční, bohatě však postačí, když nás jednoduše první instrukce přesune do těla programu ( main ) a hned potom následuje datová část. Hned na začátku těla programu nastavíme zásobník, abychom mohli volat funkce. Dále si uložíme hodnotu registru DL uchovávající driver, z nějž je bootováno. Zbytek je už na vaší fantazii. V celém programu můžeme užívat pouze funkce (přerušení) BIOSu, nikoliv DOSu, protože dos se vůbec nenahraje. Dále adresa ffff:0000h je adresou, kde začíná procesor po startu vykonávat první instrukci, proto nám k restartu stačí skočit na tuto adresu. Na konci zdrojového kódu musíme uvést direktivu, která nastaví soubor na délku 512 B a na konec přidá hodnotu ( AA55h ), která označuje boot sector.
Při bootu se z diskety nahraje pouze jeden sector dlouhý 512 B, což není nijak moc, ale bohatě stačí na program, který nahraje další sectory z diskety a spustí je.
Příklad takového boot sectoru:
;boot_sector2.asm
org 0x7c00
jmp main
;promene
boot_msg db 'Muj prvni OS...',13,10,0
load_msg db 'Zavadim jadro ...',13,10,0
bootdev db 0
zprava:
lodsb
or al,al
jz short zprava_konec
mov ah,0eh
mov bx,0007h
int 10h
jmp zprava
zprava_konec:
ret
reset_dev: ;reset driveru
mov ah,00h
mov dl,[bootdev]
int 13h
ret
load_image:
mov ax,0h
mov es,ax
mov ah,02h
mov dl,[bootdev]
mov dh,0h
mov cx,2
mov al,1 ;pocet sectoru k nahrani
mov bx,8000h
int 13h
ret
main:
cli
mov ax,9000h
mov ss,ax
mov sp,0xffff
sti
mov [bootdev],dl
mov si,boot_msg
call zprava
mov si,load_msg
call zprava
call reset_dev
call load_image
jmp 0000:8000h ;skok do pameti k nahranemu programu
smycka:
jmp smycka
times 510-($-$) db 90h
dw 0xaa55
Tento program nahraje druhý sector z diskety do paměti na adresu 0000:8000h a spustí jej. Můžeme dokonce nahrát více sectorů naráz, a to změnou hodnoty registru CX, ve funkci: load_image. Takže nahrávaný program nemusí mít pouze 512 B. Program může být libovolný, stejně jako u boot sectoru, musí být na začátku instrukce. Zásobník je již nastaven. A na konci kódu již nemusíme soubor zarovnávat na délku dělitelnou 512 B.
Ukázka:
; jadro.asm
org 8000h
jmp main
;promnene
wel_msg db 'Jadro nahrano.',13,10,0
end_msg db 'Stisknete jakoukoliv klavesu k restartu',13,10,0
zprava:
lodsb
or al,al
jz short zprava_konec
mov ah,0eh
mov bx,0007h
int 10h
jmp zprava
zprava_konec:
ret
klavesa:
mov ah,00h
int 16h
ret
main:
mov si,wel_msg
call zprava
mov si,end_msg
call zprava
call klavesa
jmp 0xffff:0000h
Pokud máme tyto dva soubory, musíme je spojit v jeden image soubor, a to následovně:
; image.asm
incbin 'boot_sector.bin'
incbin 'jadro.bin'
Boot sector a druhý program nahrajeme na disketu následovně:
nasm -o boot_sector.asm -f bin boot_sector.bin
nasm -o jadro.asm -f bin jadro.bin
nasm -o image.asm -f bin image.bin
dd if=image.bin of=/dev/fd0 bs=512