How_to_Make_a_Computer_Operating_System_in_Chinese

第五章: 管理x86体系结构的基础类

现在,我们知道如何编译c++内核和使用GRUB引导二进制程序了, 我们可以开始用C/C++做一些很酷的东西了.

打印到屏幕控制台

我们将使用VGA默认模式(03h)向用户显示一些文本. 利用0xB8000视频内存, 屏幕可以被直接访问. 屏幕分辨率是80x25, 在屏幕上的每个字符被定义为2字节: 一个字符代码, 和一个格式标志. 这意味着视频内存的总大小是4000B(80B25B2B).

在IO类(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

我们在IO Class中添加一个方法putc来在屏幕上放置一个字符, 并更新(x, y)位置.

/* put a byte on screen */
void Io::putc(char c){
    kattr = 0x07;
    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) = 0x0;
            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);
}

我们也添加了一个有用且有名的方法: printf

/* 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;
}

汇编接口

大量的指令在汇编层可用, 但在C(像cli, sti, in, out)中没有等效的方法. 所以我们需要这一个这些指令的接口.

在C中, 我们用指令"asm()"来内联汇编, gcc用gas编译汇编.

注意: gas使用AT&T语法.

/* 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;
}