Emulando a Linus Torvalds: crea o teu propio sistema operativo desde cero (II)

Benvido a outra publicación sobre como crear o noso propio sistema operativo, neste caso NextDivel.

Se volvemos ao código do primeira publicación ao final de todo, deberiamos ter algo así:

NextDivel-1

Se isto é correcto, podemos continuar. Vou usar o sistema e a estrutura que teño en GitHub (http://github.com/AdrianArroyoCalle/next-divel) xa que é máis cómodo para min e para ti. Como podes ver, o texto é un texto básico, non é atractivo. Pode parecer algo fóra do normal. Pero como di o refrán, para probar as cores e no noso sistema operativo haberá cores. As primeiras cores que imos poder poñer serán as que definen as tarxetas VGA e son 0:

  1. Negro
  2. Azul
  3. Verde
  4. Ciano
  5. Vermello
  6. Maxenta
  7. Marrón
  8. Gris claro
  9. Gris escuro
  10. Azul claro
  11. Verde claro
  12. Cian claro
  13. Vermello claro
  14. Maxenta claro
  15. Marrón claro
  16. Branco

Imos definir estas cores nunha cabeceira para que sexan máis útiles e quizais no futuro sexamos parte da API do sistema. Así que creamos o ficheiro ND_Colors.hpp no ​​NextDivel include.

#ifndef ND_COLOR_HPP
#define ND_COLOR_HPP
typedef enum ND_Color{
ND_COLOR_BLACK = 0,
ND_COLOR_BLUE = 1,
ND_COLOR_GREEN = 2,
ND_COLOR_CYAN = 3,
ND_COLOR_RED = 4,
ND_COLOR_MAGENTA = 5,
ND_COLOR_BROWN = 6,
ND_COLOR_LIGHT_GREY = 7,
ND_COLOR_DARK_GREY = 8,
ND_COLOR_LIGHT_BLUE = 9,
ND_COLOR_LIGHT_GREEN = 10,
ND_COLOR_LIGHT_CYAN = 11,
ND_COLOR_LIGHT_RED = 12,
ND_COLOR_LIGHT_MAGENTA = 13,
ND_COLOR_LIGHT_BROWN = 14,
ND_COLOR_WHITE = 15
} ND_Color;
#endif

Ao mesmo tempo imos definir novas funcións para escribir na pantalla dun xeito máis cómodo (non, aínda non imos implementar printf, sei que o desexas). Crearemos un ficheiro e a súa cabeceira para un conxunto de funcións relacionadas coa pantalla (ND_Screen.cpp e ND_Screen.hpp). Nelas imos crear funcións para: cambiar a cor das letras e o fondo, escribir frases e letras, limpar a pantalla e moverse pola pantalla. Seguimos usando as pantallas VGA pero agora usaremos algúns bytes que darán a cor. ND_Screen.cpp tería o seguinte aspecto:

/**
* @file ND_Screen.cpp
* @author Adrián Arroyo Calle
* @brief Implements four easy functions for write strings directly
*/
#include <ND_Types.hpp>
#include <ND_Color.hpp>
#include <ND_Screen.hpp>
uint16_t *vidmem= (uint16_t *)0xB8000;
ND_Color backColour = ND_COLOR_BLACK;
ND_Color foreColour = ND_COLOR_WHITE;
uint8_t cursor_x = 0;
uint8_t cursor_y = 0;
/**
* @brief Gets the current color
* @param side The side to get the color
* */
ND_Color ND::Screen::GetColor(ND_SIDE side)
{
if(side==ND_SIDE_BACKGROUND){
return backColour;
}else{
return foreColour;
}
}
/**
* @brief Sets the color to a screen side
* @param side The side to set colour
* @param colour The new colour
* @see GetColor
* */
void ND::Screen::SetColor(ND_SIDE side, ND_Color colour)
{
if(side==ND_SIDE_BACKGROUND)
{
backColour=colour;
}else{
foreColour=colour;
}
}
/**
* @brief Puts the char on screen
* @param c The character to write
* */
void ND::Screen::PutChar(char c)
{
uint8_t attributeByte = (backColour << 4) | (foreColour & 0x0F);
uint16_t attribute = attributeByte << 8; uint16_t *location; if (c == 0x08 && cursor_x) { cursor_x--; }else if(c == '\r') { cursor_x=0; }else if(c == '\n') { cursor_x=0; cursor_y=1; } if(c >= ' ') /* Printable character */
{
location = vidmem + (cursor_y*80 + cursor_x);
*location = c | attribute;
cursor_x++;
}
if(cursor_x >= 80) /* New line, please*/
{
cursor_x = 0;
cursor_y++;
}
/* Scroll if needed*/
uint8_t attributeByte2 = (0 /*black*/ << 4) | (15 /*white*/ & 0x0F);
uint16_t blank = 0x20 /* space */ | (attributeByte2 << 8); if(cursor_y >= 25)
{
int i;
for (i = 0*80; i < 24*80; i++)
{
vidmem[i] = vidmem[i+80];
}
// The last line should now be blank. Do this by writing
// 80 spaces to it.
for (i = 24*80; i < 25*80; i++)
{
vidmem[i] = blank;
}
// The cursor should now be on the last line.
cursor_y = 24;
}
}
/**
* @brief Puts a complete string to screen
* @param str The string to write
* */
void ND::Screen::PutString(const char* str)
{
int i=0;
while(str[i])
{
ND::Screen::PutChar(str[i++]);
}
}
/**
* @brief Cleans the screen with a color
* @param colour The colour to fill the screen
* */
void ND::Screen::Clear(ND_Color colour)
{
uint8_t attributeByte = (colour /*background*/ << 4) | (15 /*white - foreground*/ & 0x0F);
uint16_t blank = 0x20 /* space */ | (attributeByte << 8);
int i;
for (i = 0; i < 80*25; i++)
{
vidmem[i] = blank;
}
cursor_x = 0;
cursor_y = 0;
}
/**
* @brief Sets the cursor via software
* @param x The position of X
* @param y The position of y
* */
void ND::Screen::SetCursor(uint8_t x, uint8_t y)
{
cursor_x=x;
cursor_y=y;
}

A cabeceira será moi básica polo que non a inclúo aquí, pero resaltar a definición do tipo ND_SIDE

typedef enum ND_SIDE{
ND_SIDE_BACKGROUND,
ND_SIDE_FOREGROUND
} ND_SIDE;

Mencione tamén que facemos uso do encabezado ND_Types.hpp, este encabezado define algúns tipos básicos para uint8_t, uint16_t, etc. baseados en char e int. De feito, este encabezado é o estándar C99 e de feito o meu ND_Types.hpp é unha copia / pega do ficheiro de Linux, polo que podes intercambialos e non pasaría nada (só hai definicións, non hai funcións).

Para probar se este código funciona, imos modificar o punto de entrada C do núcleo:

ND::Screen::Clear(ND_COLOR_WHITE);
ND::Screen::SetColor(ND_SIDE_BACKGROUND,ND_COLOR_WHITE);
ND::Screen::SetColor(ND_SIDE_FOREGROUND,ND_COLOR_GREEN);
ND::Screen::PutString("NextDivel\n");
ND::Screen::SetColor(ND_SIDE_FOREGROUND,ND_COLOR_BLACK);
ND::Screen::PutString("Licensed under GNU GPL v2");

E se seguimos estes pasos obteriamos este resultado

NextDivel-3

Grazas a estas funcións que creamos, podemos comezar a facer pequenas GUI, como un pánico do núcleo que amosaremos cada vez que haxa un erro irrecuperable. Algo coma isto:

NextDivel-4

E esta pequena GUI só a conseguimos con estas funcións:

void ND::Panic::Show(const char* error)
{
ND::Screen::Clear(ND_COLOR_RED);
ND::Screen::SetColor(ND_SIDE_BACKGROUND, ND_COLOR_WHITE);
ND::Screen::SetColor(ND_SIDE_FOREGROUND, ND_COLOR_RED);
ND::Screen::SetCursor(29,10); //(80-22)/2
ND::Screen::PutString("NextDivel Kernel Error\n");
ND::Screen::SetCursor(15,12);
ND::Screen::PutString(error);
}

E aquí o post. Lémbroche as instrucións para compilar o sistema a partir de 0:

git clone http://github.com/AdrianArroyoCalle/next-divel
cd next-divel
mkdir build && cd build
cmake ..
make
make DESTDIR=next install
chmod +x iso.sh
./iso.sh
qemu-system-i386 nextdivel.iso

E aproveito para agradecerlle a excelente acollida que tivo o primeiro post.


O contido do artigo adhírese aos nosos principios de ética editorial. Para informar dun erro faga clic en aquí.

5 comentarios, deixa os teus

Deixa o teu comentario

Enderezo de correo electrónico non será publicado. Os campos obrigatorios están marcados con *

*

*

  1. Responsable dos datos: Miguel Ángel Gatón
  2. Finalidade dos datos: controlar SPAM, xestión de comentarios.
  3. Lexitimación: o seu consentimento
  4. Comunicación dos datos: os datos non serán comunicados a terceiros salvo obrigación legal.
  5. Almacenamento de datos: base de datos aloxada por Occentus Networks (UE)
  6. Dereitos: en calquera momento pode limitar, recuperar e eliminar a súa información.

  1.   F3niX dixo

    Excelente amigo, aínda así estou matando o meu casco entendendo o código en c ++.

    Saúdos.

  2.   pandacriss dixo

    estes elementos son xeniais. Eles espertaron a miña curiosidade polo rendemento de baixo nivel dos procesadores unha vez máis.
    quizais se teño tempo empecei a xogar co seguinte divel.
    Hai moito tempo que non envío un artigo. xa é necesario

  3.   Jon burrows dixo

    Está ben, ese é o camiño.

  4.   miguel dixo

    Hai tempo que quero saber facer un sistema operativo.

    Agardando a túa próxima publicación. Graciñas

  5.   Giuliano dixo

    ¡Gran amigo!
    Só teño un problema. ¿Alguén me pode pasar un ficheiro C deste exemplo?
    Sempre me envía erros no terminal