Chapter 5: Base classes for managing x86 architecture

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 (80B25B2B).

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.

  1. /* put a byte on screen */
  2. void Io::putc(char c){
  3. kattr = 0x07;
  4. unsigned char *video;
  5. video = (unsigned char *) (real_screen+ 2 * x + 160 * y);
  6. // newline
  7. if (c == '\n') {
  8. x = 0;
  9. y++;
  10. // back space
  11. } else if (c == '\b') {
  12. if (x) {
  13. *(video + 1) = 0x0;
  14. x--;
  15. }
  16. // horizontal tab
  17. } else if (c == '\t') {
  18. x = x + 8 - (x % 8);
  19. // carriage return
  20. } else if (c == '\r') {
  21. x = 0;
  22. } else {
  23. *video = c;
  24. *(video + 1) = kattr;
  25. x++;
  26. if (x > 79) {
  27. x = 0;
  28. y++;
  29. }
  30. }
  31. if (y > 24)
  32. scrollup(y - 24);
  33. }

We also add a useful and very known method: printf

  1. /* put a string in screen */
  2. void Io::print(const char *s, ...){
  3. va_list ap;
  4. char buf[16];
  5. int i, j, size, buflen, neg;
  6. unsigned char c;
  7. int ival;
  8. unsigned int uival;
  9. va_start(ap, s);
  10. while ((c = *s++)) {
  11. size = 0;
  12. neg = 0;
  13. if (c == 0)
  14. break;
  15. else if (c == '%') {
  16. c = *s++;
  17. if (c >= '0' && c <= '9') {
  18. size = c - '0';
  19. c = *s++;
  20. }
  21. if (c == 'd') {
  22. ival = va_arg(ap, int);
  23. if (ival < 0) {
  24. uival = 0 - ival;
  25. neg++;
  26. } else
  27. uival = ival;
  28. itoa(buf, uival, 10);
  29. buflen = strlen(buf);
  30. if (buflen < size)
  31. for (i = size, j = buflen; i >= 0;
  32. i--, j--)
  33. buf[i] =
  34. (j >=
  35. 0) ? buf[j] : '0';
  36. if (neg)
  37. print("-%s", buf);
  38. else
  39. print(buf);
  40. }
  41. else if (c == 'u') {
  42. uival = va_arg(ap, int);
  43. itoa(buf, uival, 10);
  44. buflen = strlen(buf);
  45. if (buflen < size)
  46. for (i = size, j = buflen; i >= 0;
  47. i--, j--)
  48. buf[i] =
  49. (j >=
  50. 0) ? buf[j] : '0';
  51. print(buf);
  52. } else if (c == 'x' || c == 'X') {
  53. uival = va_arg(ap, int);
  54. itoa(buf, uival, 16);
  55. buflen = strlen(buf);
  56. if (buflen < size)
  57. for (i = size, j = buflen; i >= 0;
  58. i--, j--)
  59. buf[i] =
  60. (j >=
  61. 0) ? buf[j] : '0';
  62. print("0x%s", buf);
  63. } else if (c == 'p') {
  64. uival = va_arg(ap, int);
  65. itoa(buf, uival, 16);
  66. size = 8;
  67. buflen = strlen(buf);
  68. if (buflen < size)
  69. for (i = size, j = buflen; i >= 0;
  70. i--, j--)
  71. buf[i] =
  72. (j >=
  73. 0) ? buf[j] : '0';
  74. print("0x%s", buf);
  75. } else if (c == 's') {
  76. print((char *) va_arg(ap, int));
  77. }
  78. } else
  79. putc(c);
  80. }
  81. return;
  82. }

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.

  1. /* output byte */
  2. void Io::outb(u32 ad, u8 v){
  3. asmv("outb %%al, %%dx" :: "d" (ad), "a" (v));;
  4. }
  5. /* output word */
  6. void Io::outw(u32 ad, u16 v){
  7. asmv("outw %%ax, %%dx" :: "d" (ad), "a" (v));
  8. }
  9. /* output word */
  10. void Io::outl(u32 ad, u32 v){
  11. asmv("outl %%eax, %%dx" : : "d" (ad), "a" (v));
  12. }
  13. /* input byte */
  14. u8 Io::inb(u32 ad){
  15. u8 _v; \
  16. asmv("inb %%dx, %%al" : "=a" (_v) : "d" (ad)); \
  17. return _v;
  18. }
  19. /* input word */
  20. u16 Io::inw(u32 ad){
  21. u16 _v; \
  22. asmv("inw %%dx, %%ax" : "=a" (_v) : "d" (ad)); \
  23. return _v;
  24. }
  25. /* input word */
  26. u32 Io::inl(u32 ad){
  27. u32 _v; \
  28. asmv("inl %%dx, %%eax" : "=a" (_v) : "d" (ad)); \
  29. return _v;
  30. }