Emulando Linus Torvalds: Crie seu próprio sistema operacional do zero (II)

Bem-vindo a outro post sobre como criar nosso próprio sistema operacional, neste caso o NextDivel.

Se voltarmos ao código do primeira postagem no final de tudo, deveríamos ter chegado a algo assim:

PróximoDivel-1

Se estiver correto, podemos continuar. Vou usar o sistema e a estrutura que tenho no GitHub (http://github.com/AdrianArroyoCalle/next-divel), pois é mais confortável para mim e para você. Como você pode ver, o texto é um texto básico, não é atraente. Pode parecer algo fora do comum. Mas como diz o ditado, para provar as cores, e em nosso sistema operacional haverá cores. As primeiras cores que vamos poder colocar serão as que definem as placas VGA e são 0:

  1. Preto
  2. Azul
  3. Verde
  4. Ciano
  5. Vermelho
  6. Magenta
  7. Marrom
  8. Cinza claro
  9. Cinza escuro
  10. Blue Light
  11. Luz verde
  12. Ciano claro
  13. Vermelho claro
  14. Magenta claro
  15. Castanho claro
  16. Branco

Vamos definir essas cores em um cabeçalho para torná-lo mais acessível e, talvez, no futuro, se tornar parte da API do sistema. Portanto, criamos o arquivo ND_Colors.hpp no ​​include NextDivel.

#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 vamos definir novas funções para escrever na tela de uma forma mais confortável (não, não vamos implementar printf ainda, sei que você quer isso). Criaremos um arquivo e seu cabeçalho para um conjunto de funções relacionadas à tela (ND_Screen.cpp e ND_Screen.hpp). Neles criaremos funções para: mudar a cor das letras e do fundo, escrever frases e letras, limpar a tela e mover-se pela tela. Continuamos usando as telas VGA, mas agora usaremos alguns bytes que darão a cor. ND_Screen.cpp seria semelhante a:

/**
* @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;
}

O cabeçalho será muito básico, então não o incluo aqui, mas destaco a definição do tipo ND_SIDE

typedef enum ND_SIDE{
ND_SIDE_BACKGROUND,
ND_SIDE_FOREGROUND
} ND_SIDE;

También mencionar que hacemos uso del header ND_Types.hpp, este header nos define unos tipos básicos para uint8_t, uint16_t, etc basado en los char y los int. Realmente este header es el en el estándar C99 y de hecho mi ND_Types.hpp es un copia/pega del archivo desde Linux, así que podeis intercambiarlos y no pasaría nada (solo hay definiciones, ninguna función).

Para testar se este código funciona, vamos modificar o ponto de entrada C do kernel:

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 seguirmos essas etapas, obteremos este resultado

PróximoDivel-3

Graças a essas funções que criamos, podemos começar a fazer pequenas GUIs, como um kernel panic que mostraremos sempre que houver um erro irrecuperável. Algo assim:

PróximoDivel-4

E essa pequena GUI que criamos apenas com estas funções:

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 até aqui o post. Lembro as instruções 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 agradecer a excelente recepção que teve o primeiro post.


Deixe um comentário

Seu endereço de email não será publicado. Campos obrigatórios são marcados com *

*

*

  1. Responsável pelos dados: Miguel Ángel Gatón
  2. Finalidade dos dados: Controle de SPAM, gerenciamento de comentários.
  3. Legitimação: Seu consentimento
  4. Comunicação de dados: Os dados não serão comunicados a terceiros, exceto por obrigação legal.
  5. Armazenamento de dados: banco de dados hospedado pela Occentus Networks (UE)
  6. Direitos: A qualquer momento você pode limitar, recuperar e excluir suas informações.

  1.   f3niX dito

    Excelente amigo, mesmo assim estou matando meu capacete entendendo o código em c ++.

    Saudações.

  2.   Pandacriss dito

    esses itens são ótimos. Eles despertaram minha curiosidade sobre o desempenho de baixo nível dos processadores mais uma vez.
    talvez se eu tiver tempo eu comece a jogar com o próximo divel.
    Não envio um artigo há muito tempo. já precisava

  3.   Jon toca dito

    Tudo bem, esse é o caminho.

  4.   miguel dito

    Há muito tempo queria saber como construir um sistema operacional.

    Esperando sua próxima postagem. Felicidades

  5.   Giuliano dito

    Grande amigo!
    Eu só tenho um problema, alguém pode me passar um arquivo C deste exemplo?
    Sempre me envia erros no terminal