OS Development: Video Memory and putchar…
Well folks, as promised here is my next exciting installment of the ‘OS Development’ series!
Continuing on from my last post, ‘OS Development: Where to start?’, I’m now going to go over some simple things such as implementing a simple putchar(char c) function which you can then use to implement your printf function. This, apart from my boot-loader, was the first thing I developed in MouthOS. It’s a really good place to start as once you’ve got it sorted you can start printing out debug information such as stack traces, the CPU registers etc.
So, onto the theory! In order to put stuff on the screen you’re going to need to have access to the video memory. On the x86 architecture the video memory is found between 0×000A0000 and 0×000C7FFF. It is split up as follows:
0×000A0000 - 0×000BFFFF: Video RAM Memory.
0×000B0000 - 0×000B7777: Monochrome Video Memory (multiple pages).
0×000B8000 - 0×000BFFFF: Colour Video Memory (multiple pages).
0×000C0000 - 0×000C7FFF: Video ROM Memory.
To begin with the simplest thing to do would be to use the monochrome video memory. However, this is a bit boring as you may want to highlight things in different colours etc. As such the best place to start is to write to the Colour Video Memory.
When writing to the Colour Video Memory you should note that for every character you need two bytes worth of data. The first byte is the character to be displayed and the second is the colour to be used. To start with I just created a header file (video.h) with all my colours and video memory locations defined as below.
video.h /* Colours are defined as FOREGROUND_BACKGROUND */ #define BLACK_WHITE 0×0 #define BLUE_BLACK 0×1 #define GREEN_BLACK 0×2 #define CYAN_BLACK 0×3 #define RED_BLACK 0×4 #define MAGENTA_BLACK 0×5 #define BROWN_BLACK 0×6 #define GRAY_BLACK 0×7 #define DARK_GRAY_BLACK 0×8 #define BRIGHT_BLUE_BLACK 0×9 #define BRIGHT_GREEN_BLACK 0xA #define BRIGHT_CYAN_BLACK 0xB #define PINK_BLACK 0xC #define BRIGHT_MAGENTA_BLACK 0xD #define YELLOW_BLACK 0xE #define WHITE_BLACK 0xF
/* Locations for Video Memory. */ #define VIDEO_MEM_RAM 0x000A0000 #define VIDEO_MEM_MON 0x000B0000 #define VIDEO_MEM_COL 0x000B8000 #define VIDEO_MEM_ROM 0x000C0000
/* Define the maximum rows and columns available on screen. */ #define X_MAX 80 #define Y_MAX 25
/* Functions in video.c that the rest of the kernel is allowed to use. */ extern void init_video(void); extern void cls(void); extern void setcolour(int _colour); extern void putchar(const char c);
With that done I started to implement my functions. The first one was the init_video(void) function. The aim of this function was two fold. Firstly it was to gain access to the video memory via a pointer and secondly it was to clear the contents of the video memory (as all the BIOS and/or GRUB loading information is still in video memory). On the whole this is a really simple function…
video.c
#include “video.h”
unsigned char vidmemptr*;
int colour, crsx, csry;
void move_csr(int _x, int _y);
void init_video(void) {
vidmemptr = (unsigned char *) VIDEO_MEM_COL; colour = WHITE_BLACK; cls(); }
With this completed the next step was to implement the cls(void) function to clear the video memory. This simply involved moving along the entire video memory setting the characters to be equal to ‘ ‘ and the colour to be the currently selected colour (in this case WHITE_BLACK)…
void cls(void) {
int i; int j = Y_MAX * ( X_MAX * 2 );
for ( i = 0; i < j; i++ ) {
vidmemptr[i] = ' ';
i++;
vidmemptr[i] = colour;
}
move_csr(0, 0);
}
As can be seen this still won’t compile as the function move_csr(int _x, int _y) still doesn’t exist. This function is used to position the cursor to a specific x, y co-ordinate. It is very simple in my implementation below, simply checking that the given values are not out of bounds of the screen and then setting csrx and csry. However in my full implementation in MouthOS it also sets the blinking cursor and calls another function ’scroll’ to scroll the text up the screen. Unfortunately adding the implementation of these functions to this blog entry would make it twice it’s current size - perhaps for my next entry I’ll go over implementing these extra functions. However for now here is a simple move_csr(int _x, int _y) function.
void move_csr(int _x, int _y) {
if ( _x <= X_MAX && _y <= Y_MAX ) {
csrx = _x;
csry = _y;
}
}
Having implemented move_csr there are two functions left. The first is the setcolour(int colour) function. This is a simple ’setter’ function…
void setcolour(int _colour) {
colour = _colour;
}
Leaving the function you’ve all been waiting for - putchar(const char c)…
void putchar(const char c) {
int position = ( csrx * ( X_MAX * 2 ) ) + ( csrx * 2);
int i;
if ( c == '\n') {
move_csr(0, csry + 1);
} else {
vidmemptr[position] = c; position++; vidmemptr[position] - colour; move_csr(csrx + 1, csry);
} }
From here there are many places you could branch to. I implemented a scroll() function to scroll the text up as the screen got full. This also required me to implement a memcpy(src, dest, size) function. I also made some modifications to the move_csr(int _x, int _y) function to move the blinking cursor to where the text was appearing and I then went on to write printf(fmt, args) which required the implementation of the va_start, va_arg and va_end macros. As they say the sky is the limit.
Leave a Reply