Ontwerp en bouw een besturingssysteem/Blue Screen of Death/De console
Wat verwachten we van de console:
- tekst invoegen op de plaats van de cursor;
- tekstkleur kunnen veranderen.
Variabelen
bewerkenAangezien we te maken hebben met de console, is een pointer naar het consolegeheugen zeker wenselijk. Verder moeten we weten op welke regel en kolom eventuele nieuwe tekst wordt ingevoegd. Ten slotte is het een goed idee om de tekstkleur te bewaren. Het nieuw te maken console.c
bestand begint dus zo:
/kernel/src/console.c
// Pointer naar het consolegeheugen.
uint16* consolegeheugen = 0xB8000;
// De coordinaten van waar de nieuwe tekst zal worden ingevoegd.
nint cursor_x = 0;
nint cursor_y = 0;
// De kleuren van nieuwe tekst.
uint16 tekstkleur = 0x0F00;
Een karakter afdrukken
bewerkenElke string bestaat uit een aantal karakters. Die karakters komen uit de ASCII tekenset, en de meeste zijn zichtbare symbolen. Sommige karakters, zoals de tab en de backspace, moeten speciaal behandeld worden. Schrijf in het console.c
bestand een functie om een karakter af te drukken.
/kernel/src/console.c - printKarakter()
// De grootte van een tab.
#define CONSOLE_TABSIZE 4
// Drukt een enkel karakter af op het scherm.
static void printKarakter(char c)
{
uint16* locatie;
// Is het een speciaal karakter?
if (c == 0x00)
{
// Backspace: één positie terug.
if (cursor_x != 0)
cursor_x--;
}
else if (c == 0x09)
{
// Tab: naar de volgende tabstop.
cursor_x = (cursor_x + CONSOLE_TABSIZE) & ~(CONSOLE_TABSIZE - 1);
}
else if (c == '\r')
{
// Carriage return: terug naar het begin van de regel.
cursor_x = 0;
}
else if (c == '\n')
{
// Line feed: naar het begin van de volgende regel.
cursor_x = 0;
cursor_y++;
}
else if (c >= ' ')
{
// Elk ander afdrukbaar karakter: gewoon afdrukken.
locatie = consolegeheugen + (cursor_y * 80 + cursor_x);
*locatie = c | (tekstkleur << 8);
cursor_x++;
}
// Controleer of de cursor voorbij het einde van het scherm is.
if (cursor_x >= 80)
{
// Ga naar het begin van de volgende regel.
cursor_x = 0;
cursor_y++;
}
}
De functie is static
, wat betekent dat de functie alleen geldig is in het bestand waar hij in voorkomt. Om hem overal in het bestand te kunnen gebruiken, moet je hem bovenaan het bestand declareren, als volgt:
/kernel/src/console.c (deel)
static void printKarakter(char c);
// Andere declaraties van static functies komen hier.
// De functies zelf:
...
Scherm verschuiven
bewerkenAls we bij de onderkant van het scherm zijn beland, dan moet de gehele inhoud van het scherm een regel naar boven schuiven. Hiervoor kunnen we de hulpfunctie Memory_Move()
goed gebruiken.
--- EXTRA ---
Waarom is Memory_Copy()
een minder goede keus?
--- EXTRA ---
/kernel/src/console.c - verschuifTekst()
// Als de huidige regel de laatste regel van het scherm is,
// schuif dan alle tekst een regel omhoog.
static void verschuifTekst()
{
unint spatie = 0x20 | (tekstkleur << 8);
unint tijdelijk;
// Als Y = 25 (de laatste regel), moeten we verschuiven.
if(cursor_y >= 25)
{
// Kopieer de huidige tekst een regel terug.
tijdelijk = cursor_y - 25 + 1;
Memory_Move(consolegeheugen, consolegeheugen + tijdelijk * 80, (25 - tijdelijk) * 80 * 2);
// Maak de laatste regel leeg door hem vol met spaties te zetten.
Memory_SetW(consolegeheugen + (25 - tijdelijk) * 80, spatie, 80);
// De cursor kan weer een regel omhoog.
cursor_y = 25 - 1;
}
}
Het handigst is om na elk afgedrukt karakter te kijken of het scherm een regel moet opschuiven. Dus we eindigen de voorgaande functie printKarakter()
als volgt:
/kernel/src/console.c - printKarakter()
(deel)
...
static void printKarakter(char c)
{
...
verschuifTekst();
}
Cursor verplaatsen
bewerkenOm de cursor te verplaatsen, schrijven we eerst 0x0E naar I/O poort 0x03D4 (VGA controller). De controller verwacht daarna de hoge 8 bits van de nieuwe cursorpositie. Ndat we die hebben doorgegeven op I/O poort 0x03D5, schrijven we 0x0F naar die eerste poort 0x03D4. Als we daarna de lage 8 bits sturen, weet de VGA controller waar we de cursor willen hebben, en wordt de cursor verplaatst.
/kernel/src/console.c - cursorVerplaatsen()
// Verplaatst de cursor naar de gegeven coordinaten.
// 0 <= x < 80
// 0 <= y < 25
static void cursorVerplaatsen(nint x, nint y)
{
unint positie = y * 80 + x;
System_OutPortByte(0x03D4, 0x0E);
System_OutPortByte(0x03D5, positie >> 8);
System_OutPortByte(0x03D4, 0x0F);
System_OutPortByte(0x03D5, positie);
}
--- EXTRA ---
Cursor verbergen
bewerkenSchrijf nu zelf de code om de cursor te weergeven en verbergen. Kijk in Appendix B - I/O poorten voor informatie over naar welke poorten je wat moet schrijven. Vergeet niet een declaratie naar de functie toe te voegen bovenaan het codebestand.
/kernel/src/console.c - cursorZichtbaar()
// Laat de cursor (on)zichtbaar zijn.
static void cursorZichtbaar(bool zichtbaar)
{
...
}
--- EXRA ---
TODO: void Video_Initialize() void Video_ResetColors() void Video_SetColors(byte forecolor, byte backcolor) void Video_PutString(const char* string)