Emulating Linus Torvalds: Create your own operating system from scratch (II)

Welcome to another post on how to create our own operating system, in this case NextDivel.

If we go back to the code of the first post at the end of everything we should have come up with something like this:

Next Level-1

If this is correct we can continue. I'm going to use the system and structure that I have on GitHub (http://github.com/AdrianArroyoCalle/next-divel) as it is more comfortable for me and you. As you can see the text is a basic text, it is not attractive. It may seem like something out of the ordinary. But as the saying goes, to taste colors, and in our operating system there will be colors. The first colors that we will be able to put are going to be those that define the VGA cards and they are 0:

  1. Black
  2. BLUE
  3. Verde
  4. Cyan
  5. RED
  6. Magenta
  7. Brown
  8. Light gray
  9. Dark Grey
  10. Light Blue
  11. Light green
  12. Cyan clear
  13. Light red
  14. Light magenta
  15. Light brown
  16. Blanco

We are going to define these colors in a header to have it more handy and perhaps in the future become part of the system API. So we create the file ND_Colors.hpp in the 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

At the same time we are going to define new functions to write on the screen in a more comfortable way (no, we are not going to implement printf yet, I know you are wanting it). We will create a file and its header for a set of screen-related functions (ND_Screen.cpp and ND_Screen.hpp). In them we are going to create functions to: change the color of the letters and the background, write phrases and letters, clean the screen and move around the screen. We continue using the VGA screens but now we will use a few bytes that will give the color. ND_Screen.cpp would look like:

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

The header will be very basic so I do not include it here, but highlight the definition of the ND_SIDE type

typedef enum ND_SIDE{
ND_SIDE_BACKGROUND,
ND_SIDE_FOREGROUND
} ND_SIDE;

Also mention that we use the ND_Types.hpp header, this header defines some basic types for uint8_t, uint16_t, etc. based on char and int. Actually this header is the one in the C99 standard and in fact my ND_Types.hpp is a copy/paste of the file desde Linux, so you can exchange them and nothing would happen (there are only definitions, no functions).

To test if this code works we are going to modify the C entry point of the 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");

And if we follow these steps we would get this result

Next Level-3

Thanks to these functions that we have created, we can start to make small GUIs, such as a kernel panic that we will show every time there is an unrecoverable error. Something like this:

Next Level-4

And this little GUI we made it only with these functions:

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);
}

And up to here the post. I remind you of the instructions to compile the system from 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

And I take this opportunity to thank you for the excellent reception that the first post had.


5 comments, leave yours

Leave a Comment

Your email address will not be published. Required fields are marked with *

*

*

  1. Responsible for the data: Miguel Ángel Gatón
  2. Purpose of the data: Control SPAM, comment management.
  3. Legitimation: Your consent
  4. Communication of the data: The data will not be communicated to third parties except by legal obligation.
  5. Data storage: Database hosted by Occentus Networks (EU)
  6. Rights: At any time you can limit, recover and delete your information.

  1.   f3niX said

    Excellent friend, even so I'm killing my helmet understanding the code in c ++.

    Greetings.

  2.   pandacriss said

    these items are great. They have piqued my curiosity about the low-level performance of the processors once again.
    maybe if I have time I will start playing with next-divel.
    I have not sent an article for a long time. already needed

  3.   Jon burrows said

    All right, that's the way.

  4.   Miguel said

    I've long wanted to know how to build an operating system.

    Waiting for your next post. Cheers

  5.   Giuliano said

    Great friend!
    I just have one problem, can someone pass me a C file of this example?
    It always sends me errors in the terminal