Thursday 1 March 2012

How to make a Operating System ..The SCreeN(TEXT_MODE+VGA)

Well hello guys ones again!!.Well last time we learned how to make a hello-bootloader, but i'll be using from now on grub as my bootloader so that we can focus more on our kernel.Its also very safe to use grub because we dont have to worry about machine architecture before loading our kernel. 
So let start make our very first module of kernel ...


 The SCREEN Module:


Memory Layout

So we are here interested in 0xB8000-0xBFFFF which constitutes our video memory.Well its just a basic video area which have few  colors only. For VGA(vesa) we have to use A0000 but right now i dont want to discuss these things.
The area of memory known as the framebuffer is accessible just like normal RAM, at address 0xB8000 it controls a screen of characters (not pixels) 80 wide by 25 high.


Printing Character
Okay, so how do we print a character at any x/y location on screen?
A special property about memory is how it is linear. If we reach the end of a line being displayed, the next byte is on the line right below it. Because of linear addressing, we have to be able to convert an x/y location to a linear address to render it to screen. And, a special forumula to do that is: x + y * screen width.






Cursor Position


The framebuffer is just an array of 16-bit words about which we talked above, each 16-bit value representing the display of one character. The offset from the start of the framebuffer of the word that specifies a character at position xy is:

(y * 80 + x) * 2


If you are thinking why its multiplied by two then here is its answer
 because each element is 2 bytes (16 bits) long. If you're indexing an array of 16-bit values, for example, your index would just be x+(y*80)

4 bits for a colour code gives us 15 possible colours we can display:

  • 0 - Black
  • 1 - Blue
  • 2 - Green
  • 3 - Cyan
  • 4 - Red
  • 5 - Magenta
  • 6 - Brown
  • 7 - Light Gray
  • 8 - Dark Gray
  • 9 - Light Blue
  • 10 - Light Green
  • 11 - Light Cyan
  • 12 - Light Red
  • 13 - Light Magenta
  • 14 - Light Brown
  • 15 - White

The VGA controller also has some ports on the main I/O bus, which you can use to send it specific instructions. (Among others) it has a control register at 0x3D4 and a data register at 0x3D5. We will use these to instruct the controller to update it's cursor position.




Now Lets Move to the Implementation Part....


So first define some common things which we will need all the time like 
1:int,char, short long;
2:functions for reading from i/o bus;
3:string manipulation function like string copy,concatenate,compare etc..
4:memory comparison and copy function etc.







// common.h -- Defines typedefs and some global functions.
//           @author<Vishal Mishra><email:vishalmishra.ietjhansi@gmail.com>
//           @change fri-Mar-2,12:01


#ifndef COMMON_H
#define COMMON_H


// Some nice typedefs, to standardise sizes across platforms.
// These typedefs are written for 32-bit X86.
typedef unsigned int   u32int;
typedef          int   s32int;
typedef unsigned short u16int;
typedef          short s16int;
typedef unsigned char  u8int;
typedef          char  s8int;


void outb(u16int port, u8int value);
void outw(u16int port, u16int value);
u8int inb(u16int port);
u16int inw(u16int port);
u32int zkmalloc(u32int sz);
u32int kmalloc_int(u32int sz, int align, u32int *phys);


#endif // COMMON_H















//common.c -- Defines some global functions.
//            @author<Vishal Mishra><email:vishalmishra.ietjhansi@gmail.com>
//            @change fri-Mar-2,12:01


#include "common.h"


// Write a byte out to the specified port.
void outb(u16int port, u8int value)
{
    asm volatile ("outb %1, %0" : : "dN" (port), "a" (value));
}


//write a word to specific port
void outw(u16int port, u16int value)
{
    asm volatile ("outw %1, %0" : : "dN" (port), "a" (value));

}




//Read a byte from a Port
u8int inb(u16int port)
{
    u8int ret;
    asm volatile("inb %1, %0" : "=a" (ret) : "dN" (port));
    return ret;
}

//Read a word from a Port
u16int inw(u16int port)
{
    u16int ret;
    asm volatile ("inw %1, %0" : "=a" (ret) : "dN" (port));
    return ret;
}















// text_mode.h -- Defines the interface for text_mode.h
//           @author<Vishal Mishra><email:vishalmishra.ietjhansi@gmail.com>
//           @change fri-Mar-2,12:01


#ifndef TEXT_MODE_H
#define TEXT_MODE_H


#include "common.h"


//Write a single charactere out to the screen of specific background and forecolour value
void eva_put(char c,u8int backColour,u8int foreColour);


// Write a single character out to the screen.
void eva_puts(char c);


// Clear the screen to 'bg' color code.
void eva_clear(int bg);


// Output a null-terminated ASCII string .
void eva_write(char *c);




#endif // TEXT_MODE_H













// text_mode.c -- Defines functions for writing to the text mode.
//           @author<Vishal Mishra><email:vishalmishra.ietjhansi@gmail.com>
//           @change fri-Mar-2,12:01


#include "text_mode.h"


// The VGA framebuffer starts at 0xB8000.
u16int *video_memory = (u16int *)0xB8000;
// Stores the cursor position.
u8int cursor_x ;
u8int cursor_y ;


// Updates the hardware cursor.
static void move_cursor()
{
    // The screen is 80 characters wide...
    u16int cursorLocation = cursor_y * 80 + cursor_x;
    outb(0x3D4, 14);                  // Tell the VGA board we are setting the high cursor byte.
    outb(0x3D5, cursorLocation >> 8); // Send the high cursor byte.
    outb(0x3D4, 15);                  // Tell the VGA board we are setting the low cursor byte.
    outb(0x3D5, cursorLocation);      // Send the low cursor byte.
}


// Scrolls the text on the screen up by one line.
static void scroll()
{


    // Get a space character with the default colour attributes.
    u8int attributeByte = (0 /*black*/ << 4) | (15 /*white*/ & 0x0F);
    u16int blank = 0x20 /* space */ | (attributeByte << 8);


    // Row 25 is the end, this means we need to scroll up
    if(cursor_y >= 25)
    {
        // Move the current text chunk that makes up the screen
        // back in the buffer by a line
        int i;
        for (i = 0*80; i < 24*80; i++)
        {
            video_memory[i] = video_memory[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++)
        {
            video_memory[i] = blank;
        }
        // The cursor should now be on the last line.
        cursor_y = 24;
    }
}


// Writes a single character out to the screen.
void eva_put(char c,u8int backColour,u8int foreColour)
{




    // The background colour is black (0), the foreground is white (15).
    


    // The attribute byte is made up of two nibbles - the lower being the 
    // foreground colour, and the upper the background colour.
    u8int  attributeByte = (backColour << 4) | (foreColour & 0x0F);
    // The attribute byte is the top 8 bits of the word we have to send to the
    // VGA board.
    u16int attribute = attributeByte << 8;
    u16int *location;


    // Handle a backspace, by moving the cursor back one space
    if (c == 0x08 && cursor_x)
    {
        cursor_x--;
    }


    // Handle a tab by increasing the cursor's X, but only to a point
    // where it is divisible by 8.
    else if (c == 0x09)
    {
        cursor_x = (cursor_x+8) & ~(8-1);
    }


    // Handle carriage return
    else if (c == '\r')
    {
        cursor_x = 0;
    }


    // Handle newline by moving cursor back to left and increasing the row
    else if (c == '\n')
    {
        cursor_x = 0;
        cursor_y++;
    }
    // Handle any other printable character.
    else if(c >= ' ')
    {
        location = video_memory + (cursor_y*80 + cursor_x);
        *location = c | attribute;
        cursor_x++;
    }


    // Check if we need to insert a new line because we have reached the end
    // of the screen.
    if (cursor_x >= 80)
    {
        cursor_x = 0;
        cursor_y ++;
    }


    // Scroll the screen if needed.
    scroll();
    // Move the hardware cursor.
    move_cursor();


}




void eva_puts(char c)
{


eva_put(c,0,15);




}




// Clears the screen, by copying lots of spaces to the framebuffer.
void eva_clear(int bg)
{

    // Make an attribute byte for the default colours
    u8int attributeByte = (bg /*black*/ << 4) | (15 /*white*/ & 0x0F);
    u16int blank = 0x20 /* space */ | (attributeByte << 8);


    int i;
    for (i = 0; i < 80*25; i++)
    {
        video_memory[i] = blank;
    }


    // Move the hardware cursor back to the start.
    cursor_x = 0;
    cursor_y = 0;
    move_cursor();
}


// Outputs a null-terminated ASCII string to the monitor.
void eva_write(char *c)
{
    int i = 0;
    while (c[i])
    {
        eva_puts(c[i++]);
    }
}


void eva_write_hex(u32int n)
{


s32int tmp;


    eva_write("0x");


    char noZeroes = 1;


    int i;
    for (i = 28; i > 0; i -= 4)
    {
        tmp = (n >> i) & 0xF;
        if (tmp == 0 && noZeroes != 0)
        {
            continue;
        }
    
        if (tmp >= 0xA)
        {
            noZeroes = 0;
            eva_puts (tmp-0xA+'a' );
        }
        else
        {
            noZeroes = 0;
            eva_puts( tmp+'0' );
        }
    }
  
    tmp = n & 0xF;
    if (tmp >= 0xA)
    {
        eva_puts (tmp-0xA+'a');
    }
    else
    {
        eva_puts (tmp+'0');
    }




    
}


void eva_write_dec(u32int n)
{
    if (n == 0)
    {
        eva_puts('0');
        return;
    }


    s32int acc = n;
    char c[32];
    int i = 0;
    while (acc > 0)
    {
        c[i] = '0' + acc%10;
        acc /= 10;
        i++;
    }
    c[i] = 0;


    char c2[32];
    c2[i--] = 0;
    int j = 0;
    while(i >= 0)
    {
        c2[i--] = c[j++];
    }
    eva_write(c2);
}













Moving the cursor(In  move_cursor() Function)

To move the hardware cursor, we must firstly work out the linear offset of the x,y cursor coordinate. We do this by using the equation above. Next, we have to send this offset to the VGA controller. For some reason, it accepts the 16-bit location as two bytes. We send the controller's command port (0x3D4) the command 14 to tell it we are sending the high byte, then send that byte to port 0x3D5. We then repeat with the low byte, but send the command 15 instead.






 VGA mode(pixel):





#ifndef VGA_H
#define VGA_H


void VgaTest(void);
void VgaUpdateMouse(int x, int y);
void VgaDrawString(int x, int y, int reps, char font[8], char color);
void VgaDrawFont(int x, int y, char font[8], int c1);
void VgaDrawWindow(int x, int y, int size);
void VgaDrawRectangle(int x, int y, int w, int h, u8int color);
void vga_draw_vert(int x, int y, int length, u8int color);
void vga_draw_hort(int x, int y, int length, u8int color);
void VgaFillRectangle(int x, int y, int w, int h, u8int color);
void VgaGetFont(char *buffer);
void mode3(void);
void mode13(void);
void VgaClearScreen(void);
void VgaClear(u8int color);
u8int VgaReadPixel(int x, int y);
void VgaDrawPixel(unsigned x, unsigned y, unsigned c);
void VgaSetColor(u8int color, u8int red, u8int green, u8int blue);
#endif











// vga.c
//           @author<Vishal Mishra><email:vishalmishra.ietjhansi@gmail.com>
//           @change fri-Mar-2,12:01
#include "common.h"


#include "vga.h"


int mx, my; // mouse data
char bg;




void VgaSetColor(u8int color, u8int red, u8int green, u8int blue)
{
outb(0x3C8, color);
outb(0x3C9, red);
outb(0x3C9, green);
outb(0x3C9, blue);
}


void VgaDrawPixel(unsigned x, unsigned y, unsigned c)
{
u8int *vga = (u8int*)0xA0000;
int offset = y * 320 + x;
vga[offset] = c;
}


u8int VgaReadPixel(int x, int y)
{
u8int *vga = (u8int*) 0xA0000;
int offset = y * 320 + x;
return vga[offset];
}


void VgaClear(u8int color)
{
int w, h;
for (w = 0; w < 320; w++)
{
for (h = 0; h < 200; h++)
VgaDrawPixel(w, h, color);
}
}


void VgaClearScreen(void)
{
VgaClear(0);
}


void mode13(void)
{
const u8int w[7] = { 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x28 }; // width = 320
const u8int h[8] = { 0xBF, 0x1F, 0x41, 0x9C, 0x8E, 0x8F, 0x96, 0xB9 }; // height = 200
const u8int w_regs[7] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x13 };
const u8int h_regs[8] = { 0x6, 0x7, 0x9, 0x10, 0x11, 0x12, 0x15, 0x16 };
int i;



outb(0x3C2, 0x63); // mode switch
outw(0x3D4, 0x0E11); // enable registers


for (i = 0; i < 7; i++)
outw(0x3D4, (short)((w[i]<<8)+w_regs[i]));
for (i = 0; i < 8; i++)
outw(0x3D4, (short)((h[i]<<8)+h_regs[i]));
outw(0x3D4, 0x0008);
outw(0x3D4, 0x4014);
    outw(0x3D4, 0xA317);
    outw(0x3C4, 0x0E04);


outw(0x3C4, 0x0101);
    outw(0x3C4, 0x0F02); // enable writing to all planes


    outw(0x3CE, 0x4005); // 256color mode
outw(0x3CE, 0x0506); // graph mode & A000-AFFF

inb(0x3da);
outb(0x3C0, 0x30); 
outb(0x3C0, 0x41);
outb(0x3C0, 0x33);
outb(0x3C0, 0x00);


// EGA Colors
for (i = 0; i < 16; i++)
{
outb(0x3C0, (u8int)i);
outb(0x3C0, (u8int)i);
}
outb(0x3C0, 0x20); // enable video
}








void VgaGetFont(char *buffer)
{
int i;
char *vga = (char *) 0xA0000;
outb(0x3CE, 0x0204);
for (i = 0; i < 0x2000; i++)
buffer[i] = vga[i];
}




void VgaFillRectangle(int x, int y, int w, int h, u8int color)
{
int a, b;
for (a = y; a < (y + h); a++)
{
for (b = x; b < (x + w); b++)
VgaDrawPixel(b, a, color);
}
}




//void VgaDrawRectangle(Rectangle_t *rect, int color)
//{


void vga_draw_hort(int x, int y, int length, u8int color)
{
int a;
for (a = x; a < (x + length); a++)
VgaDrawPixel(a, y, color);
}


void vga_draw_vert(int x, int y, int length, u8int color)
{
int a;
for (a = y; a < (y + length); a++)
VgaDrawPixel(x, a, color);
}


void VgaDrawRectangle(int x, int y, int w, int h, u8int color)
{
vga_draw_hort(x, y, w, color); // Draw top border
vga_draw_hort(x, y+h, w, color); // Draw bottom border


vga_draw_vert(x, y, h, color); // Draw left border
vga_draw_vert(x+w, y, h, color); // Draw right border
}


void VgaDrawWindow(int x, int y, int size)
{
VgaFillRectangle(x, y, size, size, 2); // Green
VgaDrawRectangle(x, y, size, size, 0); // Black
}


void VgaDrawFont(int x, int y, char font[8], int c1)
{
int i, l;
int j = x;
int h = y;
int c;
for (l = 0; l < 8; l++)
{
for (i = 0; i < 8; i++)
{
if ((font[l] & (1<<i)))
c = c1; // New color
else
c = VgaReadPixel(j, h); // Don't overwrite background
VgaDrawPixel(j++, h, c);
}
h++;
j = x;
}
}


void VgaDrawString(int x, int y, int reps, char font[8], char color)
{
int i;
int a = x;
for (i = 0; i < reps; i++)
{
VgaDrawFont(a, y, font, color);
a += 8;
}
}


void VgaUpdateMouse(int x, int y)
{
VgaDrawPixel(mx, my, bg);
VgaDrawPixel(x, y, 0);
mx = x;
my = y;
}


void VgaTest(void)
{
mode13();


char a[10]="hi wassup";

bg = 0;
VgaClear(bg);

VgaDrawWindow(96, 36, 128);






char font_a[8] = {0x18, 0x3C, 0x66, 0x7E,
0x66, 0x66, 0x66, 0x00};
char font_b[8] = {0x7E, 0x66, 0x66, 0x7E,
0x66, 0x66, 0x7E, 0x00 };

 


}




void init_vga(void)
{
VgaTest();


}











Now our main file that will call all function



// eva.c -- Defines the C-code kernel entry point, calls initialisation routines.
//           @author<Vishal Mishra><email:vishalmishra.ietjhansi@gmail.com>
//           @change fri-Mar-2,12:01 


#include "text_mode.h"
#include "vga.h"


int eva(struct multiboot *mboot_ptr)
{
    // Initialise the screen (by clearing it)
    eva_clear(0);
    // Write out a sample string
   
   
     eva_write(" Eva Operating System ....Loading   ");
    eva_put('V',0,4);
 eva_puts('-');
    eva_puts('1');
    //VgaTest();
   //uncomment above statement to switch to vga mode 
   
    
    return 0;
}








Output after compiling and linking ....


Eva Operating System




After uncommenting the VgaTest function output looks like...


  






To compile it use following commands in linux(ubuntu)

1: sudo make -f Makefile
2: qemu -kernel kernel



Reference:
http://www.jamesmolloy.co.uk
http://viralpatel.net/taj/operating-system-tutorial.php
http://www.brokenthorn.com/Resources/



0 comments:

Post a Comment