C++-Kernel mit GRUB — Änderungen 6402 → 6405

Zeile 1: Zeile 1:
−
Die Programmierung eines [[Kernel|Kernels]] in [[C++]] unterscheidet sich in einigen Punkten von der mit [[C]]. Da wären zum Beispiel der Aufruf der Konstruktoren, bzw. der Destruktoren, die vor bzw. nach dem eigentlichen Kernel aufgerufen werden. Unser Kernel wird mittels [[GRUB]] geladen.
+
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. Ich verwende den [[Crosscompiler_f%C3%BCr_Windows|Cross-Compiler]] von [[Benutzer:Jidder|Jidder]], es sollte aber keinen Unterschied zu einem "normalen" gcc unter Linux geben.
+
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 "MyOS.h" // Einbinden unseres Header
+
#include "Video.h" // Einbinden unseres Header
  
−
void main(void){
+
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((char *)"Wilkommen im C++-TestKernel!"); // Textausgabe
+
screen.write("Willkommen im C++-TestKernel!"); // Textausgabe
 
}
 
}
 
</cpp>
 
</cpp>
  
−
=== MyOS.h ===
+
=== Video.h ===
  
 
Der Header ist dann doch etwas umfangreicher:
 
Der Header ist dann doch etwas umfangreicher:
  
 
<cpp>
 
<cpp>
−
#ifndef MyOS_H
+
#ifndef VIDEO_H
−
#define MyOS_H
+
#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); // Textausgabe
+
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; // Setzen der Textausgabe auf
+
pos = 0; // Setzen der Textausgabe auf
−
off=0; // links oben.
+
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; // Füllen des Bildschirms mit Leerzeichen
+
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){
+
 
−
char *str = cp, *ch;
+
void Video::write(const char *cp){
−
for(ch = str; *ch; ch++){
+
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; // Setzen des Zeichens und der Farbe in den Videospeicher
+
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;
 
}
 
}
−
#endif
 
 
</cpp>
 
</cpp>
  
−
=== cKernel.c ===
+
=== Startup.cpp ===
  
−
Um den Konstruktor bzw. den Destruktor aufzurufen, werden diese beiden Funktionen später mit in den Kernel gelinkt:
+
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:
  
−
<c>
+
<cpp>
−
void _cmain()
+
void constructors()
 
{
 
{
 
extern void (*__CTOR_LIST__)();
 
extern void (*__CTOR_LIST__)();
−
void (**constructor)() = &__CTOR_LIST__;
+
void (**constructor)() = &__CTOR_LIST__;
 
int total = *(int *) constructor;
 
int total = *(int *) constructor;
 
constructor++;
 
constructor++;
Zeile 96: Zeile 111:
 
}
 
}
  
−
void _catexit()
+
void destructors()
 
{
 
{
 
extern void (*__DTOR_LIST__)();
 
extern void (*__DTOR_LIST__)();
Zeile 109: Zeile 124:
 
}
 
}
 
}
 
}
−
</c>
+
</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 _cmain ; _cmain() aus cKernel.c
+
extern destructors ; aus Startup.cpp
−
extern _catexit ; _catexit() aus cKernel.c
+
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 _cmain      ; main-Funktion des C++-Kernels aufrufen
+
   call constructors      ; Konstruktoren aufrufen
−
   call main      ; cmain-Funktion des C-Kernels aufrufen
+
   call main      ; main aufrufen
−
   call _catexit   ; atexit-Funktion des C-Kernels aufrufen
+
   call destructors   ; Destruktoren aufrufen
−
  jmp stop
+
 
−
    
+
stop:
−
stop: ; Endlosschleife nach Beendigung unseres Kernels
+
   jmp stop ; Endlosschleife nach Beendigung unseres Kernels
−
jmp stop
 
 
   
 
   
 
</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 -Os -o Kernel.o Kernel.cpp
+
  gcc -m32 -fno-use-cxa-atexit -nostdlib -fno-builtin -fno-rtti -ffreestanding -fno-exceptions -fno-leading-underscore -c -o Kernel.o Kernel.cpp
  
−
cKernel.cpp:
+
Startup.cpp:
  
−
  gcc -Wall -Werror -nostdinc -m32 -ffreestanding -fno-leading-underscore -c -o cKernel.o cKernel.c  
+
  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 die asmKernel.o, die Kernel.o und die cKernel.o zu einer kernel.bin linken, die dann von GRUB geladen werden kann.
+
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 cKernel.o Kernel.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.
−
== Anmerkungen ==
 
−
Es kann unter Umständen nötig sein, __CTOR_LIST__ etc. durch _CTOR_LIST__ zu ersetzen, also das _ wegzulassen. Ich vermute, dass das von der GCC-Version abhängig ist.
 
−
 
  
 
== Siehe auch ==
 
== Siehe auch ==