Now that we know how to compile our C++ kernel and boot the binary using GRUB, we can start to do some cool things in C/C++.
Printing to the screen console
We are going to use VGA default mode (03h) to display some text to the user. The screen can be directly accessed using the video memory at 0xB8000. The screen resolution is 80x25 and each character on the screen is defined by 2 bytes: one for the character code, and one for the style flag. This means that the total size of the video memory is 4000B (80B25B 2B).
In the IO class (io.cc ),:
x,y : define the cursor position on the screen
real_screen : define the video memory pointer
putc(char c) : print a unique character on the screen and manage cursor position
printf(char* s, ...) : print a string
We add a method putc to the IO Class to put a character on the screen and update the (x,y) position.
Copy /* put a byte on screen */
void Io :: putc ( char c){
kattr = 0x 07 ;
unsigned char * video;
video = ( unsigned char * ) (real_screen + 2 * x + 160 * y);
// newline
if (c == '\n' ) {
x = 0 ;
y ++ ;
// back space
} else if (c == '\b' ) {
if (x) {
* (video + 1 ) = 0x 0 ;
x -- ;
}
// horizontal tab
} else if (c == '\t' ) {
x = x + 8 - (x % 8 );
// carriage return
} else if (c == '\r' ) {
x = 0 ;
} else {
* video = c;
* (video + 1 ) = kattr;
x ++ ;
if (x > 79 ) {
x = 0 ;
y ++ ;
}
}
if (y > 24 )
scrollup (y - 24 );
}
We also add a useful and very known method: printf
Copy /* put a string in screen */
void Io :: print ( const char * s , ...){
va_list ap;
char buf [ 16 ];
int i , j , size , buflen , neg;
unsigned char c;
int ival;
unsigned int uival;
va_start (ap , s);
while ((c = * s ++ )) {
size = 0 ;
neg = 0 ;
if (c == 0 )
break ;
else if (c == '%' ) {
c = * s ++ ;
if (c >= '0' && c <= '9' ) {
size = c - '0' ;
c = * s ++ ;
}
if (c == 'd' ) {
ival = va_arg (ap , int );
if (ival < 0 ) {
uival = 0 - ival;
neg ++ ;
} else
uival = ival;
itoa (buf , uival , 10 );
buflen = strlen (buf);
if (buflen < size)
for (i = size , j = buflen; i >= 0 ;
i -- , j -- )
buf [i] =
(j >=
0 ) ? buf [j] : '0' ;
if (neg)
print ( "- %s " , buf);
else
print (buf);
}
else if (c == 'u' ) {
uival = va_arg (ap , int );
itoa (buf , uival , 10 );
buflen = strlen (buf);
if (buflen < size)
for (i = size , j = buflen; i >= 0 ;
i -- , j -- )
buf [i] =
(j >=
0 ) ? buf [j] : '0' ;
print (buf);
} else if (c == 'x' || c == 'X' ) {
uival = va_arg (ap , int );
itoa (buf , uival , 16 );
buflen = strlen (buf);
if (buflen < size)
for (i = size , j = buflen; i >= 0 ;
i -- , j -- )
buf [i] =
(j >=
0 ) ? buf [j] : '0' ;
print ( "0x %s " , buf);
} else if (c == 'p' ) {
uival = va_arg (ap , int );
itoa (buf , uival , 16 );
size = 8 ;
buflen = strlen (buf);
if (buflen < size)
for (i = size , j = buflen; i >= 0 ;
i -- , j -- )
buf [i] =
(j >=
0 ) ? buf [j] : '0' ;
print ( "0x %s " , buf);
} else if (c == 's' ) {
print (( char * ) va_arg (ap , int ));
}
} else
putc (c);
}
return ;
}
Assembly interface
A large number of instructions are available in Assembly but there is not equivalent in C (like cli, sti, in and out), so we need an interface to these instructions.
In C, we can include Assembly using the directive "asm()", gcc use gas to compile the assembly.
Caution: gas uses the AT&T syntax.
Copy /* output byte */
void Io :: outb ( u32 ad , u8 v){
asmv ( "outb %% al, %% dx" :: "d" (ad) , "a" (v));;
}
/* output word */
void Io :: outw ( u32 ad , u16 v){
asmv ( "outw %% ax, %% dx" :: "d" (ad) , "a" (v));
}
/* output word */
void Io :: outl ( u32 ad , u32 v){
asmv ( "outl %% eax, %% dx" : : "d" (ad) , "a" (v));
}
/* input byte */
u8 Io :: inb ( u32 ad){
u8 _v; \
asmv ( "inb %% dx, %% al" : "=a" (_v) : "d" (ad)); \
return _v;
}
/* input word */
u16 Io :: inw ( u32 ad){
u16 _v; \
asmv ( "inw %% dx, %% ax" : "=a" (_v) : "d" (ad)); \
return _v;
}
/* input word */
u32 Io :: inl ( u32 ad){
u32 _v; \
asmv ( "inl %% dx, %% eax" : "=a" (_v) : "d" (ad)); \
return _v;
}