| Zeile 1: | Zeile 1: | ||
| − | Die Programmierung eines [[Kernel|Kernels]] in [[C++]] unterscheidet sich in einigen Punkten von der mit [[C]]. | + | Die Programmierung eines [[Kernel|Kernels]] in [[C++]] unterscheidet sich in einigen Punkten von der mit [[C]]. Einen ausführlicheren Überblick zu den relevanten Unterschieden zu C gibt der Artikel [[C++]]. Dieses Tutorial arbeitet genau wie [[C-Kernel mit GRUB]] mit dem Bootloader [[GRUB]]. |
| − | |||
== Kernel == | == Kernel == | ||
| − | Wir werden mit [[GCC]] und [[NASM]] arbeiten. | + | Wir werden mit [[GCC]] und [[NASM]] arbeiten. Es kann auch Windows verwendet werden, allerdings ist dazu etwas Vorarbeit in Bezug auf die Tools von Nöten, siehe dazu [[C-Kernel_mit_GRUB#Hinweis_f.C3.BCr_Windowsianer|C-Kernel mit GRUB]]. |
=== Kernel.cpp === | === Kernel.cpp === | ||
Da wir ja schon mit C++ arbeiten, wäre auch etwas OOP angebracht. Deshalb erstellen wir ein Klasse namens Video, die wir in einen Header stecken. Dadurch ist unsere kernel.cpp sehr übersichtlich: | Da wir ja schon mit C++ arbeiten, wäre auch etwas OOP angebracht. Deshalb erstellen wir ein Klasse namens Video, die wir in einen Header stecken. Dadurch ist unsere kernel.cpp sehr übersichtlich: | ||
<cpp> | <cpp> | ||
| − | #include " | + | #include "Video.h" // Einbinden unseres Header |
| − | void main( | + | void main() { |
Video screen; // Screen instanzieren | Video screen; // Screen instanzieren | ||
screen.color(0x7000); // Vordergrund schwarz, Hintergrund hellgrau | screen.color(0x7000); // Vordergrund schwarz, Hintergrund hellgrau | ||
screen.clear(); // Bildschirm leeren | screen.clear(); // Bildschirm leeren | ||
| − | screen.write( | + | screen.write("Willkommen im C++-TestKernel!"); // Textausgabe |
} | } | ||
</cpp> | </cpp> | ||
| − | === | + | === Video.h === |
Der Header ist dann doch etwas umfangreicher: | Der Header ist dann doch etwas umfangreicher: | ||
<cpp> | <cpp> | ||
| − | #ifndef | + | #ifndef VIDEO_H |
| − | #define | + | #define VIDEO_H |
| + | |||
class Video | class Video | ||
{ | { | ||
| Zeile 31: | Zeile 31: | ||
~Video(); // Destruktor | ~Video(); // Destruktor | ||
void clear(); // Leeren des Bildschirms, die Größe beträgt 80x25 Zeichen | void clear(); // Leeren des Bildschirms, die Größe beträgt 80x25 Zeichen | ||
| − | void write(char *cp); | + | void write(const char* cp); // Textausgabe |
void put(char c); // Ausgabe eines einzelnen Zeichens | void put(char c); // Ausgabe eines einzelnen Zeichens | ||
void color(int color); // Setzen der FG/BG-Farben | void color(int color); // Setzen der FG/BG-Farben | ||
private: | private: | ||
| − | unsigned short *videomem; // Zeiger auf den Videospeicher | + | unsigned short* videomem; // Zeiger auf den Videospeicher |
unsigned int off; // Y-Position der Textausgabe, je volle Zeile +80 | unsigned int off; // Y-Position der Textausgabe, je volle Zeile +80 | ||
unsigned int pos; // X-Position der Textausgabe, ab Zeilenanfang | unsigned int pos; // X-Position der Textausgabe, ab Zeilenanfang | ||
unsigned int screen_color; // FB/BG-Farbe | unsigned int screen_color; // FB/BG-Farbe | ||
}; | }; | ||
| + | |||
| + | #endif | ||
| + | </cpp> | ||
| + | |||
| + | === Video.cpp === | ||
| + | |||
| + | Die Implementierung der in Video.hpp definierten Memberfunktionen kommt in eine eigene Quellcodedatei: | ||
| + | |||
| + | <cpp> | ||
| + | #include "Video.h" | ||
| + | |||
Video::Video(){ | Video::Video(){ | ||
screen_color = 0x0700; // Setzen der Farbe auf Hellgrau auf Schwarz | screen_color = 0x0700; // Setzen der Farbe auf Hellgrau auf Schwarz | ||
| − | pos=0; | + | pos = 0; // Setzen der Textausgabe auf |
| − | off=0; | + | off = 0; // links oben. |
videomem = (unsigned short*) 0xb8000; // Startadresse des Videospeichers | videomem = (unsigned short*) 0xb8000; // Startadresse des Videospeichers | ||
} | } | ||
| − | Video::~Video(){} | + | |
| + | Video::~Video(){ | ||
| + | } | ||
| + | |||
void Video::clear(){ | void Video::clear(){ | ||
| − | for(int i=0;i<(80*25);i++) | + | for(int i = 0;i < (80*25);i++) |
{ | { | ||
| − | videomem[i] = (unsigned char)' ' |screen_color; | + | videomem[i] = (unsigned char)' ' | screen_color; // Füllen des Bildschirms mit Leerzeichen |
} | } | ||
pos = 0; // Zurücksetzen der Textausgabe nach links oben | pos = 0; // Zurücksetzen der Textausgabe nach links oben | ||
off = 0; | off = 0; | ||
} | } | ||
| − | void Video::write(char *cp){ | + | |
| − | + | void Video::write(const char *cp){ | |
| − | + | while (*cp != '\0'){ | |
| − | put(*ch); // Für jedes einzelne Zeichen wird put() aufgerufen | + | put(*ch++); // Für jedes einzelne Zeichen wird put() aufgerufen |
} | } | ||
} | } | ||
| + | |||
void Video::put(char c){ | void Video::put(char c){ | ||
if(pos >= 80){ // Wenn die Textausgabe den rechten... | if(pos >= 80){ // Wenn die Textausgabe den rechten... | ||
| Zeile 66: | Zeile 81: | ||
off += 80; | off += 80; | ||
} | } | ||
| − | if(off>=(80*25)){ // ...oder den unteren Bildschirmrand erreicht, gibt es einen | + | if(off >= (80*25)){ // ...oder den unteren Bildschirmrand erreicht, gibt es einen |
clear(); // Umbruch bzw. es wird aufgeräumt. | clear(); // Umbruch bzw. es wird aufgeräumt. | ||
} | } | ||
| − | videomem[off + pos] = (unsigned char)c|screen_color; | + | videomem[off + pos] = (unsigned char)c | screen_color; // Setzen des Zeichens und der Farbe in den Videospeicher |
pos++; | pos++; | ||
} | } | ||
| − | void Video::color(int color){ | + | |
| − | screen_color = color; | + | void Video::color(int color){ |
| + | screen_color = color; | ||
} | } | ||
| − | |||
</cpp> | </cpp> | ||
| − | === | + | === Startup.cpp === |
| − | Um | + | Um die Konstruktoren bzw. die Destruktoren von globalen/statischen Objekten aufzurufen (siehe auch [[C++#globale.2Fstatische_Objekte|C++]]), werden diese beiden Funktionen später mit in den Kernel gelinkt und in der asmKernel.asm aufgerufen: |
| − | < | + | <cpp> |
| − | void | + | void constructors() |
{ | { | ||
extern void (*__CTOR_LIST__)(); | extern void (*__CTOR_LIST__)(); | ||
| − | void (**constructor)() = | + | void (**constructor)() = &__CTOR_LIST__; |
int total = *(int *) constructor; | int total = *(int *) constructor; | ||
constructor++; | constructor++; | ||
| Zeile 96: | Zeile 111: | ||
} | } | ||
| − | void | + | void destructors() |
{ | { | ||
extern void (*__DTOR_LIST__)(); | extern void (*__DTOR_LIST__)(); | ||
| Zeile 109: | Zeile 124: | ||
} | } | ||
} | } | ||
| − | </ | + | </cpp> |
=== asmKernel.asm === | === asmKernel.asm === | ||
| Zeile 118: | Zeile 133: | ||
global loader ; Unser Einsprungspunkt | global loader ; Unser Einsprungspunkt | ||
extern main ; main() aus Kernel.cpp | extern main ; main() aus Kernel.cpp | ||
| − | extern | + | extern destructors ; aus Startup.cpp |
| − | extern | + | extern constructors ; aus Startup.cpp |
FLAGS equ 0 | FLAGS equ 0 | ||
| Zeile 136: | Zeile 151: | ||
push eax ; Multiboot-Magicnumber auf den Stack legen | push eax ; Multiboot-Magicnumber auf den Stack legen | ||
push ebx ; Adresse der Multiboot-Structure auf den Stack legen | push ebx ; Adresse der Multiboot-Structure auf den Stack legen | ||
| − | call | + | call constructors ; Konstruktoren aufrufen |
| − | call main ; | + | call main ; main aufrufen |
| − | call | + | call destructors ; Destruktoren aufrufen |
| − | + | ||
| − | + | stop: | |
| − | stop | + | jmp stop ; Endlosschleife nach Beendigung unseres Kernels |
| − | |||
</asm> | </asm> | ||
| Zeile 150: | Zeile 164: | ||
nasm -f elf -o asmKernel.o asmKernel.asm | nasm -f elf -o asmKernel.o asmKernel.asm | ||
| + | |||
| + | Video.cpp: | ||
| + | |||
| + | gcc -m32 -fno-use-cxa-atexit -nostdlib -fno-builtin -fno-rtti -ffreestanding -fno-exceptions -fno-leading-underscore -c -o Video.o Video.cpp | ||
Kernel.cpp: | Kernel.cpp: | ||
| − | gcc -nostdlib -fno-builtin -fno-rtti -ffreestanding -fno-exceptions -c | + | gcc -m32 -fno-use-cxa-atexit -nostdlib -fno-builtin -fno-rtti -ffreestanding -fno-exceptions -fno-leading-underscore -c -o Kernel.o Kernel.cpp |
| − | + | Startup.cpp: | |
| − | gcc - | + | gcc -m32 -fno-use-cxa-atexit -nostdlib -fno-builtin -fno-rtti -ffreestanding -fno-exceptions -fno-leading-underscore -c -o Startup.o Startup.c |
Nun zu einer der wichtigsten Stellen, dem Linkvorgang: | Nun zu einer der wichtigsten Stellen, dem Linkvorgang: | ||
| − | Wir müssen | + | Wir müssen asmKernel.o, Startup.o, Video.o und Kernel.o zu einer kernel.bin linken, die dann von GRUB geladen werden kann. |
Unser Linkerscript (link.txt) | Unser Linkerscript (link.txt) | ||
| Zeile 197: | Zeile 215: | ||
Nur noch ein kurzer Aufruf von LD, und wir haben unseren fertigen Kernel in der Hand (bzw. Maus ;-)): | Nur noch ein kurzer Aufruf von LD, und wir haben unseren fertigen Kernel in der Hand (bzw. Maus ;-)): | ||
| − | ld -T link.txt -o kernel.bin asmKernel.o | + | ld -T link.txt -o kernel.bin asmKernel.o Startup.o Video.o cKernel.o |
Und fertig ist unser Kernel! Wie der Kernel nun mitsamt GRUB auf einer Diskette landet, kann im Artikel [[C-Kernel_mit_GRUB|C-Kernel mit GRUB]] nachgelesen werden, dort verfährt man einfach analog. | Und fertig ist unser Kernel! Wie der Kernel nun mitsamt GRUB auf einer Diskette landet, kann im Artikel [[C-Kernel_mit_GRUB|C-Kernel mit GRUB]] nachgelesen werden, dort verfährt man einfach analog. | ||
| − | |||
| − | |||
| − | |||
== Siehe auch == | == Siehe auch == | ||