2019年8月6日 星期二

自製64位元作業系統19──簡易列印函式

利用以前的列印方法,加了一個緩衝區系統,我們製作出以下列印函式。我們輸出字元會先進入緩衝區,直到我們呼叫kfflush函式以後,才會把緩衝區複製到視訊記憶體。首先是我們的兩個游標的函式。
#include <text.h>

#include <kstring.h>
#include <x86.h>

#include <type.h>
#include <stdarg.h>


static __u16 getCursorPosition(void)
{
 __u16 ret;
 outb(0x0F, 0x3D4);
 ret = inb(0x3D5);
 outb(0x0E, 0x3D4);
 ret |= (__u16)inb(0x3D5) << 8;
 return ret;
}

static void setCursorPosition(__u16 position)
{
 outb(0x0F, 0x3D4);
 outb(position & 0xFF, 0x3D5);
 outb(0x0E, 0x3D4);
 outb((position >> 8) & 0xFF, 0x3D5);
}
接下來是我們其他的部分,包含格式化輸出等等。我們的函式大多回傳一個int,只要沒有錯誤,就是回傳0。
static __u16 *screen_start_addr = (__u16 *)0xFFFFFFFF800B8000ULL;
static __u16 *buffer_start_addr = (__u16 *)0xFFFFFFFF800B9000ULL;
static __u16 buffer_cursor_position = 0;

int init_text(void)
{
 memcpy(buffer_start_addr, screen_start_addr, 0xFA0);
 buffer_cursor_position = getCursorPosition();
 return 0;
}

static void bufferScroll(void)
{
 memcpy(buffer_start_addr, buffer_start_addr + 80, 0xE60);
 buffer_cursor_position = buffer_cursor_position - (buffer_cursor_position % 80);
 memset(buffer_start_addr + 0x730, 0, 0x140); 
}

int kfflush(void)
{
 memcpy(screen_start_addr, buffer_start_addr, 0xFA0);
 setCursorPosition(buffer_cursor_position);
 return 0;
}

static void newline(void)
{
 buffer_cursor_position += 80 - buffer_cursor_position % 80;
 if (buffer_cursor_position >= 0x780) {
  buffer_cursor_position -= 80;
  bufferScroll();
 }
}


#define COLOR 0x7
static void bufferPutc(char ch)
{
 switch (ch) {
 case '\n':
 case '\r':
  newline();
  break;
 case '\b':
  --buffer_cursor_position;
  break;
 case '\t':
  buffer_cursor_position = (buffer_cursor_position & (~7)) + 8;
  break;
 default:
  *(buffer_start_addr + buffer_cursor_position++) = (__u16)COLOR << 8 | ch;
  break;
 }

 if (buffer_cursor_position >= 1920) {
  newline();
 }
}

int kputc(char ch)
{
 bufferPutc(ch);
 kfflush();
 return 0;
}

static inline void kputs_nnl(const char *s)
{
 while (*s) {
  bufferPutc(*s++);
 }
}

int kputs(const char *s)
{
 kputs_nnl(s);
 newline();
 kfflush();
 return 0;
}

static void kprintu(__u64 num)
{
 static char buf[20];
 int i = 19;
 do {
  buf[i--] = (num % 10) + '0';
  num /= 10;
 } while (num > 0);

 while (++i <= 19) {
  bufferPutc(buf[i]);
 }
}

static void kprintx(__u64 num)
{
 static char buf[16];
 int i = 15;
 do {
  buf[i--] = (num & 0xF);
  num >>= 4;
 } while (num > 0);

 while (++i <= 15) {
  bufferPutc(buf[i] > 9 ? (buf[i]-10 + 'A') : (buf[i] + '0'));
 }
}

int kprintf(const char *format, ...)
{
 va_list args;
 va_start(args, format);
 while (*format) {
  if (*format == '%') {
   switch (*++format) {
   case '%':
    bufferPutc('%');
    break;
   case 'u':
    kprintu(va_arg(args, __u32));
    break;
   case 'x':
    kprintx(va_arg(args, __u32));
    break;
   case 's':
    kputs_nnl(va_arg(args, const char *));
    break;
   case '\0':
    return 0;
   default: // unknown option
    bufferPutc(*format);
    break;
   }
  } else {
   bufferPutc(*format);
  }
  ++format;
 }
 kfflush();
 va_end(args);
 return 0;
}