Ontwerp en bouw een besturingssysteem/Hallo wereld/Tekst weergeven
Vanaf nu gaan we het meeste programmeerwerk in C doen. Daarvoor moet op de een of andere manier de controle vanaf start.asm
naar de nieuw te schrijven C code overgaan. Daarnaast is het scherm leeg maken en een zinnetje weergeven het enige dat die C code nog moet doen.
Van assembler naar C
[bewerken]De fysieke start van onze kernel is in start.asm
, maar we gaan een functie kernelmain()
maken waar het echte werk gaat gebeuren.
- Maak de functie
void kernelmain()
, die eindigt met een oneindige loop.
init.c
void kernelmain()
{
// Oneindige loop.
for(;;);
}
Tip! Laat al je codebestanden eindigen met een of meer witregels. Doe je dat niet, dan verschijnt de volgende (overigens onschuldige) waarschuwing: init.c:00:0: warning: no newline at end of file |
Deze functie zal moeten worden aangeroepen vanuit de assemblercode. Hoewel er verschillende mogelijke afspraken zijn over hoe C functies worden aangeroepen, is de standaard calling convention om de argumenten (TODO: ?van rechts naar links?) op de stack te pushen, en dan met call
de functie aan te roepen. We hebben op dit moment nog geen argumenten voor de kernelmain()
functie, maar hij moet nog wel aangeroepen worden. Om de functie succesvol te kunnen aanroepen moet aangegeven worden dat het symbool kernelmain
(dat in init.c
is gedefinieerd) buiten het assembler bestand is gedefinieerd, door middel van extern
. Daarom moet je start.asm
aanpassen. Ergens in je document staat global start
. Dat, tot en met jmp $
moet je vervangen:
start.asm (gedeelte)
...
global start
start:
mov esp, _kernel_stack
; kernelmain() aanroepen.
extern kernelmain
call kernelmain
jmp $
...
Compileren, assembleren en linken
[bewerken]Het bestand init.c
moet nog gecompileerd worden, en daarna gelinkt. Om het bestand te compileren gebruik je gcc
, waarbij je alle waarschuwingen wilt zien (-Wall
), de belangrijkste optimalizaties wilt toepassen (-O
), (-fstrength-reduce
), (-fomit-frame-pointer
), (-finline-functions
), (-fno-builtin
), en de standaardbibliotheek niet wilt gebruiken (-nostdinc
):
gcc -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions -nostdinc -fno-builtin -c -o init.o init.c
TIP: gebruik de cross-platform compiler gcc (i686-unknown-linux-gnu-gcc.exe
) als je een cross-platform toolchain hebt gemaakt.
Ook moet start.asm
weer opnieuw geassembleerd worden. Zie het vorige hoofdstuk als je niet meer weet hoe dat ging.
Ten slotte moeten de beide bestanden gelinkt worden tot één uitvoerbaar bestand. Gebruik hiervoor de cross-platform linker als je die hebt gemaakt:
ld -T link.ld -o kernel.bin start.o init.o
Vervolgens kopieert u kernel.bin
naar de cdimage map
en maakt u van het geheel een iso.
Als het goed is verloopt het assembleren, compileren en linken zonder (fout)meldingen.
Notabene Zorg ervoor dat het start.o bestand als eerste wordt gelinkt (door het vooraan de rij met te linken bestanden te zetten). Doe je dat niet, dan loop je het risico dat de multiboot header (zie hoofdstuk Hoofdstuk) door de voorafgaande code niet meer in de eerste 8192 bytes van het resulterende bestand staat, en GRUB Legacy je kernel dan niet kan laden. De foutmelding die je dan ziet is:
TODO: FOUTMELDING |
Tekst afdrukken
[bewerken]We gaan nu eindelijk de zin Hallo wereld!
afdrukken. De computer beschikt over een stuk geheugen dat gereserveerd is voor de weer te geven tekst. Dit begint op adres 0xB8000. Elke positie in het consolegeheugen is twee bytes lang, waarbij de minst significante byte (bits 0-7) de waarde voor de af te drukken letter bevat. We gaan nu onze tekst op de eerste 15 posities van dit beeld afdrukken. De code spreekt als het goed is voor zichzelf. Pas de kernelmain()
functie als volgt aan:
init.c (gedeelte)
...
void kernelmain()
{
// De tekst die we gaan afdrukken:
char* tekst = "Hallo wereld! ";
// Pointer naar de het consolegeheugen
// waar de weer te geven tekst wordt opgeslagen.
unsigned short* consolegeheugen = (unsigned short*)0xB8000;
// Tijdelijke pointer naar de geheugenlocatie voor het volgende karakter.
unsigned short* locatie;
int i;
// 15 keer herhalen ('text' is 15 karakters lang).
for (i = 0; i < 15; i++)
{
// Locatie is het begin van het consolegeheugen,
// plus het aantal geschreven karakters.
locatie = consolegeheugen + i;
// Schrijf de i-de letter naar het geheugen,
// en geef het een witte letter op zwarte achtergrond (0x0F00).
*locatie = tekst[i] | 0x0F00;
}
// Oneindige loop.
for(;;);
}
...
Opnieuw init.c
compileren, het geheel linken, en op een diskette (image) zetten. Ondanks dat de GRUB Legacy tekst nog zichtbaar is, staat onze tekst Hallo wereld!
bovenaan. Het werkt!
In het volgende deel gaan we ons verdiepen in foutopsporing. Maar we beginnen met routines die het scherm leeg kunnen maken en strings kunnen afdrukken.