text: //
  // fb.c
  // KripayaOS
  //
  
  #include <io/io.h>
  #include <libkern/stdarg.h>
  #include <libkern/stdio.h>
  #include <libkern/string.h>
  
  void set_cursor(int offset)
  {
      offset /= 2;
  
      port_out(VGA_CTRL_REGISTER, VGA_OFFSET_HIGH);
      port_out(VGA_DATA_REGISTER, (uint8_t)(offset >> 8));
      port_out(VGA_CTRL_REGISTER, VGA_OFFSET_LOW);
      port_out(VGA_DATA_REGISTER, (uint8_t)(offset & 0xff));
  }
  int get_offset(int col, int row) { return 2 * (row * MAX_COLS + col); }
  int get_cursor()
  {
      port_out(VGA_CTRL_REGISTER, VGA_OFFSET_HIGH);
      int offset = port_in(VGA_DATA_REGISTER) << 8;
      port_out(VGA_CTRL_REGISTER, VGA_OFFSET_LOW);
      offset += port_in(VGA_DATA_REGISTER);
      return offset * 2;
  }
  
  int get_row_from_offset(int offset) { return offset / (2 * MAX_COLS); }
  
  int move_offset_to_new_line(int offset)
  {
      return get_offset(0, get_row_from_offset(offset) + 1);
  }
  
  int scroll_ln(int offset)
  {
      memcpy((char*)(get_offset(0, 1) + VIDEO_ADDRESS),
          (char*)(get_offset(0, 0) + VIDEO_ADDRESS),
          MAX_COLS * (MAX_ROWS - 1) * 2);
  
      for (int col = 0; col < MAX_COLS; col++) {
          set_cursor(get_offset(col, MAX_ROWS - 1));
          print_char(' ');
      }
  
      return offset - 2 * MAX_COLS;
  }
  
  void clear_screen()
  {
      // Place spaces everywhere
      for (int i = 0; i < MAX_COLS * MAX_ROWS; ++i) {
          set_cursor(i * 2);
          print_char(' ');
      }
      // Once the whole screen is empty, we're going to set the cursor to 0, 0
      set_cursor(get_offset(0, 0));
  }
  
  void print_char(char character)
  {
      uint32_t offset = get_cursor();
      uint8_t* vidmem = (uint8_t*)VIDEO_ADDRESS;
      if (character == '\n') {
          set_cursor(move_offset_to_new_line(get_cursor()));
          set_cursor(get_cursor() + 1);
          printf(" ");
          set_cursor(get_cursor() - 1);
      } else if (character == '\t')
          for (int c = 0; c < 3; c++)
              print_char(' ');
      else {
          vidmem[offset] = character;
          vidmem[offset + 1] = WHITE_ON_BLACK;
          set_cursor(offset + 2);
      }
  }
  
  #define PL011_BASE 0x20201000
  #define PL011_DR (PL011_BASE + 0x00)
  #define PL011_FR (PL011_BASE + 0x18)
  