PROGRAMMERING AV
ETT OPERATIVSYSTEM

av Simon Gustafsson, TE3EB

Ett praktiskt arbete som går ut på att programmera ett enkelt operativsystem. Operativsystemet ska innehålla de mest grundläggande funktionerna som behövs för att starta en kommandohanterare, som kan utföra enkla uppgifter samt ladda och köra små program direkt från disk.

Specialarbete
Älvkulle-gymnasiet
Vårterminen 1998


Inledning
Operativsystemet
Normal uppbootning
bootsektorn uppgifter
startfilens uppgifter
Minneshantering
Filhantering
Fillistan
Filallokeringstabellen
Kommandohanteraren
Kommandon
Körbara filer
Interna kommandon
Tjänster som nås via INT 20H


Inledning

Målet med detta specialarbete är att designa ett enkelt operativsystem. Varför jag valde ett sånt stort arbete som detta vet jag nog inte riktigt, men jag har alltid tyckt om datorer. Förutom att programmera själva operativsystemet, så har jag tänkt mig att tillverka några applikationer. Applikationerna är till för att visa hur man använder operativsystemet. Jag har programmerat operativsystemet helt i assembler. För att inte tråka ut läsaren av detta arbete med långa assemblerlistningar (Startfilen är på över 1400 rader, c:a 25 sidor), så finns dessa med på diskett.


Operativsystemet

Normal uppbootning

Här beskriver jag vad som ska hända efter att datorn slagits på. Beskrivningen börjar med det ögonblicket då biosen lämnar över kontrollen till bootblocket. Det kommer att hända lite olika saker beroende på om operativsystemet botas från diskett eller från hårddisk. Om man botar från hårddisken, så hamnar man på hårddiskens första sektor. Denna sektor innehåller "the Master Boot Record", som innehåller information om diskens partitionering, plus kod för att ladda in bootblocket från en av hårddiskens partition. Eftersom jag inte har så lång tid på mig att göra hela operativsystemet, så nöjer jag mig vid att köra operativsystemet på diskett, för då behöver jag inte skriva lika mycket kod.

Först en liten beskrivning av vad biosen gör när den bootar upp.
• Läser Sector 1, Track 0, Head 0 från boot-enheten till absolut address 07C00h-07DFFh
• Kollar om 16-bitars ordet på den absoluta addressen 07DFEh inehåller AA55h.
Detta är en signatur som talar om att sektorn innehåller en giltig bootsector.
• Laddar DL med
00h om bootsektorn laddades från enhet A
80h om bootsektorn laddades från enhet C
• Hoppar till 0000:7C00h, som är början på bootsectorn.

bootsektorn uppgifter

När bootsektorn fått kontrollen av biosen, så har den egentligen bara en uppgift. Den ska ladda en startfil från disketten till minnet, och lämna över kontrollen till startfilen så att operativsystemet kan starta. Om det inte går så ska ett meddelande visas, som berättar hur det står till med datorn. Bootblocket är också viktigt, eftersom vissa uppgifter om hur data ligger på disken endast finns lagrade här.

startfilens uppgifter

Startfilen är den viktigaste filen i hela operativsystemet. Den innehåller all kod som behövs för att få igång en enkel kommandotolk. När startfilen börjar exekveras, börjar den med att kopiera sig själv så långt ner i minnet som det är möjligt. Efter det ändras interruptvektor 20H så att den pekar på en procedur, som låter program anropa funktioner i operativsystemet. Till sist får kommandohanteraren kontrollen, och man får en prompt som man använder för att köra program, somliga från diskett, och somliga från datorns minne

Minneshantering

Minnsehanteringen i operativsystemet kan göras väldigt enkel. Eftersom det inte kommer att gå att köra mer än ett program i taget, behöver man bara hålla reda på vad som hör till programmet, och vad som hör till operativsystemet och eventuella drivrutiner. För att hålla reda på detta behövs det bara en enda variabel, som berättar var gränsen går mellan operativsystemet och programmet. Om man vill lägga till drivrutiner, startar man drivrutinen, så lägger den gränsen för var vanliga program kommer att köras efter sig själv innan kontrollen går tillbaka till operativsystemet.

Filhantering

Filhanteringen är mycket enkel. Grunden i filsystemet består av två delar, en filallokeringstabell, samt en lista med alla filer. Alla funktioner för filhantering finns med i tabellen Tjänster som nås via INT 20H. Ett program som vill använda andra filer får än så länge göra väldigt mycket själv.

fillistan

Fillistan är en tabell som berättar var på disketten som varje fil startar. Fillistan kan också innehålla attribut, storlek, datum när filen skapats mm. I mitt operativsystem så kommer bara filnamn, attribut, samt startsektorn för filen att anges. Själva filsystemet ligger på en ganska låg nivå, och för att förstå det så är det enklast att titta på exempelfilerna som bifogas som bilaga.

filallokeringstabellen

Filallokeringstabellen är i grunden likadan som den i dos. Den skiljer sig genom att den är 16-bitars (dos använder bara 12 bitar på disketter), samt att vi inte har flera kopior av samma tabell. Grundprincipen är att man länkar ihop en fil genom att varje ord i listan pekar på nästa ord och nästa sektor. Sen använder man en speciell kod för att berätta när man nått sista ordet.

 
Fillistan
 Fil1:  1
Fil2:  5
 
Filallokeringstabellen
1: 6 2: 0 (ledig) 3: 0 (ledig) 4:  0 (ledig)
5: FFFFh 6: 7 7: 8 8: FFFFh

Exempel på hur en disketts utrymme kan användas

Startsektor:   Beskrivning:
 Sektor 0 Bootblocket Cylinder 0, Sektor 1, Huvud 0. Får kontrollen direkt från BIOSen. Skall ladda startfilen
 Kan variera Startfil Laddas in i minnet av bootblocket. Startfilen innehåller allt som behövs för att få igång operativsystemet och kommandohanteraren (som är inbygd i denna fil).
 Kan variera Filallokeringstabell Används för att hålla reda på vilka sektorer som hör till samma fil, samt vilka sektorer som är lediga
 Kan variera Katalogpost för rotkatalogen Håller reda på filnamn mm
 Anges i katalogposten Vanliga filer Kan vara program eller data
  Ev fortsättning av rot-
katalogens katalogposter
 
 Sista sektorn Ledigt utrymme  

Kommandohanteraren

kommandon

Kommandohanteraren är gränssnittet som operativsystemet har mot användaren. Jag kommer att ha en enkel kommandohanterare. Den ska bara visa en prompt, och vänta på att man skriver något. När man skrivit klart, kollar kommandohanteraren om kommandot är internt, och i så fall hoppar den till den procedur i minnet som kommandot anger. Finns inte kommandot, så letar den efter en fil med samma namn som kommandot, och börjar exekvera filen. Om kommandot inte finns visar kommandohanteraren ett felmeddelande, och går tillbaka till sin vanliga prompt.

körbara filer

Kommandohanteraren är ganska "rå" när den laddar en fil för exekvering. Den läser in hela filen direkt till minnet utan att göra något med den. Sen sätter den CS:IP att peka till början av filen, och IP kommer alltid att vara 0000h. Kommandohanteraren sätter också DS:SI att peka på strängen som skrevs in för att starta filen, så att programmet kan kolla om den fick några extra parametrar på kommandolinjen.

interna kommandon

Det finns vissa kommandon som ligger lagrade i kommandohanteraren. För tillfället är det tre stycken sådana - dir, mem och reg.
dir: Visar en lista med alla filer på disketten, tillsammans med attributen. Det finns två attribut. ‘E’ betyder att filen är ett program, och att den kan startas genom att man skriver dess namn.
mem: Visar hur mycket minne som är ledigt. Visar även en variabel kallad USERBASE, som visar var gränsen kommer att gå mellan användarprogram och operativsystem.
reg: Visar några register i datorn. Finns med i första hand för att se om stackpekaren strular till sig, men vanliga användare har egentligen ingen användning för detta kommando.


Tjänster som nås via INT 20H

INST_DRIVER Används av program för att bli residenta.
AH = 00hBX = Antal paragrafer som ska bli residenta från början av programmet
returnerar: AX = status
0000hFFFFh allt gick bradet gick inte att flytta upp gränsen BX paragrafer
Programmet måste anropa EXITPROG efter att anropat INST_DRIVER.

EXITPROG Avslutar ett program som laddats från disk
AH = 01h
returnerar: Hoppar aldrig tillbaka till programmet

GET_KEY Läser in ett tecken från tangentbordet
AH = 03h
returnerar: AH = ScankodAL = ASCII tecken eller 00h för specialtangenter

GET_KEY_ECHO Läser in ett tecken från tangentbordet, och visar tecknet på skärmen
AH = 04h
returnerar: AH = ScankodAL = ASCII tecken eller 00h för specialtangenter

PRINT_CHAR Skriver ut ett tecken på skärmen
AH = 05hAL = ASCII tecken
returnerar: ingenting

GET_STRING Läser in en hel sträng av tecken från tangentbordet. Ekar till skärmen.
AH = 06hDS:SI ā pekare till utrymme för strängen[DS:SI] = maxlängd för strängen
returnerar: strängutrymmet fylls med tecken.
<RETUR> samt specialtangenter avbryter inmatningen. Inmatningen avbryts också om maxlängden för strängen överskrids. Maxlängden är sparad i den första byten i strängen. Strängen kommer alltid att avslutas med en nolla. Det är möjligt att radera inskrivna tecken genom att trycka på <backspace>.

PRINT_STRING Skriver ut en nollterminerad sträng på skärmen
AH = 07hDS:SI ā ASCIIZ sträng som skaskrivas ut
returnerar: ingenting

CHECK_FOR_INPUT Kollar vad som finns i tangentbordsbufferten
AH = 08h
returnerar: AX = 0000h om det inte finns någon scankod i buffertenAH = scankodAL = ASCII tecken

READSECTOR Läser en 512 byte lång sektor från disk till minne
AH = 09hAL = enhet (00h=A, 01h=B)ESI = logisk sektor att läsaES:BX = vart den ska läsas
returnerar: AX = status
0000hFFFFh allt gick branågonting gick snett
Man behöver inte bry sig om DMA-gränser, utan man kan läsa sektorn vart man vill.

WRITESECTOR Skriver en sektor till disk
AH = 0AhAL = enhet (00h=A, 01h=B)ESI =logisk sektorES:BX = varifrån den ska läsas
returnerar: AX = status
0000hFFFFh allt gick branågonting gick snett
Man behöver inte bry sig om DMA-gränser, utan man kan läsa sektorn vart man vill.

CHECK_FAT_FOR_NEXT Letar upp nästa sektor som hänger ihop med den i ESI
AH = 0BhESI = Sektor
returnerar: AX = nästa sektor.Undantag: AX = FFFFh Sektorn som fanns i ESI var den sista i en kedja AX = 0 Sektorn i ESI hör inte till någon fil

CHECK_FAT_FOR_FREE Letar upp en fri sektor i fatten
AH = 0Ch
returnerar: AX = första fria sektor enligt fattenUndantag: AX = FFFFh Det finns inga lediga sektorer i fatten

WRITE_FAT Skriver i fatten
AH = 0DhBX = GrundsektorCX = Nästa sektor för sektorn BX
returnerar: AX = status
0000hFFFFh allt gick branågonting gick snett
Används av program tillsammans med CHECK_FAT_FOR_FREE för att t.ex förlänga en fil, eller skriva en ny fil.

CREATE_FILE Skapar en ny fil
AH = 0EhBL = Filttribut (‘E’ = körbar fil, ‘D’ = fil med data)DS:SI = Pekare till ett 16 bytes långt filnamn
returnerar: AX = Första sektor som allokerats till filenUndantag: AX = 0h Det gick inte att skapa filen
Filnamnet måste vara 16 bytes långt. Är det kortare måste det förlängas med spaces (20H). Funktionen allokerar alltid en sektor för filen när den skapas. Använd CHECK_FAT_FOR_FREE och WRITE_FAT för att förlänga filen.

DELETE_FILE Tar bort en redan existerande fil
AH = 0FhDS:SI = Pekare till ett 16 bytes långt filnamn
returnerar: AX = status
0000h Filen har tagits bort FFFFh Filen gick inte att ta bort

FIND Letar efter en fil
AH = 16hDS:SI pekar på filnamnet till filen som ska hittas
returnerar: AX = Första sektor för filen.BL = filattributAX = 0000h om det inte fungerar
Använd CHECK_FAT_FOR_NEXT och READSECTOR för att läsa in en hel fil.


Källor:

HELPPC - information om biosens interrupt. Finns på internet
INTERHELP - information om biosens interrupt. Finns på internet
Egenhändigt debuggade bootblock från DOS v3.3 och v5.0. Jag bifogar ena på diskett.

Litteratur för den som vill fördjupa sig:
Operating System Concepts, Abraham Silberschatz, ISBN 0-201-54873-9