青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

天衣有縫

冠蓋滿京華,斯人獨憔悴~
posts - 35, comments - 115, trackbacks - 0, articles - 0
   :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

3課:輔助函數


聲明:轉載請保留

譯者http://www.shnenglu.com/jinglexy

原作者:xiaoming.mo at skelix dot org

MSN & Email: jinglexy at yahoo dot com dot cn

目標            下載源程序

 

這節課我們講述的內容與操作系統暫無太大關系,但是這些基礎函數非常重要,并且在后面的課程中經常用到。這就是我們經常聽到的內核庫。如果你對這些不是很感興趣,知道kprintfc語言里面的print一樣工作就行了。簡單掠過即可。


C用戶庫里面的printf具有高度可伸縮性,也很容易理解,相比之下C++中的IO運算符就比較難了。為了在屏幕上顯示字符串或數據,我們現在需要實現類似C庫中的printf,顯示字符在B8000開始的顯存處。我并不像完全實現printf的所有功能,因為skelix內核只需要顯示字符串,十進制和十六進制或二進制,正整數,字符就行了,并且需要支持可變參數。其他更高級的功能我們不會用到。

這里有一種方法來實現,我們直到象func(int arg1, int arg2, int arg3)這樣一個函數被調用時,它匯編后的指令應該如下(所有從左向右入棧的編譯器應該從地球上徹底消失):
pushl   arg3
pushl   arg2
pushl   arg1
call    func

 

我們看到,參數從右向左一個個入棧,參數越多,入棧越深。如果是可變參數那我們怎么知道有多少個參數呢?答案是printf格式化字符串中參數判斷:有多少個%X,就有多少個參數要解析。在32位模式下,所有小于4字節的參數都被當作4字節處理。例如一個char型參數,入棧時就是int型了,所以在解析參數時務必保證正確。

我們這樣設計kprintf參數:kprintf(color, format string, arguments...)

第一個參數定義輸出的前景/背景顏色。我們定義了很多宏來解析棧,如果你熟悉C語言應該很容易理解它們。


03/kprintf.c

#define args_list char *            // 這個宏用例轉換棧空間為字符串指針
#define _arg_stack_size(type)    (((sizeof(type)-1)/sizeof(int)+1)*sizeof(int))

                                    // 這個宏四舍五入參數大小為4字節的倍數
#define args_start(ap, fmt) do {    \
ap = (char *)((unsigned int)&fmt + _arg_stack_size(&fmt));   \
} while (0)

                                    // 參數將從格式化字符串后面開始解析,即fmt就是棧頂,上面這個宏就是取參數的首地址


#define args_end(ap)                //
到現在為止,什么也不做
#define args_next(ap, type) (((type *)(ap+=_arg_stack_size(type)))[-1])

                                    // 當前參數地址,然后設置指針為下一個參數地址,曖昧的函數名!

03/kprintf.c

static char buf[1024] = {-1};       // 注意沒有鎖保護,引用該變量的函數不可重入!
static int ptr = -1;

 

下面兩個函數解析值為指定的進制數:
static void
parse_num(unsigned int value, unsigned int base) {            //
可以打印小于等于10進制的數
    unsigned int n = value / base;
    int r = value % base;
    if (r < 0) {
        r += base;
        --n;
    }
    if (value >= base)
        parse_num(n, base);
    buf[ptr++] = (r+'0');
}

static void                                                   //
打印16進制數
parse_hex(unsigned int value) {
    int i = 8;
    while (i-- > 0) {
        buf[ptr++] = "0123456789abcdef"[(value>>(i*4))&0xf];
    }
}

 

現在我們來看一下 kprintf這個函數,它支持的格式:%s, %c, %x, %d, %%
void
kprintf(enum KP_LEVEL kl, const char *fmt, ...) {
    int i = 0;
    char *s;
    /* must be the same size as enum KP_LEVEL */
    struct KPC_STRUCT {
        COLOUR fg;
        COLOUR bg;
    } KPL[] = {
        {BRIGHT_WHITE, BLACK},
        {YELLOW, RED},
    };


enum KP_LEVEL {KPL_DUMP, KPL_PANIC} 
定義在 include/kprintf.h, 它表示兩種輸出方案, KPL_DUMP 使用黑色背景白色前景顯示字符,KPL_PANIC 使用黃色前景和紅色背景。顏色常量定義在 include/scr.h, 后面會介紹到.

 

    args_list args;
    args_start(args, fmt);

    ptr = 0;

    for (; fmt[i]; ++i) {
        if ((fmt[i]!='%') && (fmt[i]!='\\')) {
            buf[ptr++] = fmt[i];
            continue;
        } else if (fmt[i] == '\\') {
            /* \a \b \t \n \v \f \r \\ */
            switch (fmt[++i]) {
            case 'a': buf[ptr++] = '\a'; break;
            case 'b': buf[ptr++] = '\b'; break;
            case 't': buf[ptr++] = '\t'; break;
            case 'n': buf[ptr++] = '\n'; break;
            case 'r': buf[ptr++] = '\r'; break;
            case '\\':buf[ptr++] = '\\'; break;
            }
            continue;
        }

        /* 下面是支持的打印格式 */
        switch (fmt[++i]) {
        case 's':
            s = (char *)args_next(args, char *);
            while (*s)
                buf[ptr++] = *s++;
            break;
        case 'c':
            buf[ptr++] = (char)args_next(args, int);
            break;
        case 'x':
            parse_hex((unsigned long)args_next(args, unsigned long));
            break;
        case 'd':
            parse_num((unsigned long)args_next(args, unsigned long), 10);
            break;
        case '%':
            buf[ptr++] = '%';
            break;
        default:
            buf[ptr++] = fmt[i];
            break;
        }
    }
    buf[ptr] = '\0';
    args_end(args);
    for (i=0; i<ptr; ++i)
        print_c(buf[i], KPL[kl].fg, KPL[kl].bg);            /* print_c()
是下層的顯示函數,本文后面會有講解 */

}


 

由于是內核程序,我們無法使用C用戶庫。所以一下memcpymemsetmemcpy函數需要自己實現,但是需要注意的是在BSD系統中,即便使用了-nostdlib,編譯器仍然會產生System V中相關的memcpy等代碼,具體情況我也不是很清除。這些函數的效率當然無法和linux內核中的內嵌匯編相比!我們暫時這樣實現它們吧。
03/libcc.c

 

 

/* 下面函數對重疊區域也進行了處理 */
void
bcopy(const void *src, void *dest, unsigned int n) {
    const char *s = (const char *)src;
    char *d = (char *)dest;
    if (s <= d)
        for (; n>0; --n)
            d[n-1] = s[n-1];
    else
        for (; n>0; --n)
            *d++ = *s++;
}

void
bzero(void *dest, unsigned int n) {
    memset(dest, 0, n);
}

void *
memcpy(void *dest, const void *src, unsigned int n) {
    bcopy(src, dest, n);
    return dest;
}

void *
memset(void *dest, int c, unsigned int n) {
    char *d = (char *)dest;
    for (; n>0; --n)
        *d++ = (char)c;
    return dest;
}

int
memcmp(const void *s1, const void *s2, unsigned int n) {
    const char *s3 = (const char *)s1;
    const char *s4 = (const char *)s2;
    for (; n>0; --n) {
        if (*s3 > *s4)
            return 1;
        else if (*s3 < *s4)
            return -1;
        ++s3;
        ++s4;
    }
    return 0;
}

int
strcmp(const char *s1, const char *s2) {
    while (*s1 && *s2) {
        int r = *s1++ - *s2++;
        if (r)
            return r;
    }
    if (*s1 == *s2)
        return 0
    else
        return (*s1)?1:-1;
}

char *
strcpy(char *dest, const char *src) {
    char *p = dest;
    while ( (*dest++ = *src++))
        ;
    *dest = 0;
    return p;
}

unsigned int
strlen(const char *s) {
    unsigned int n = 0;
    while (*s++)
        ++n;
    return n;
}

print_c
函數
直接操作顯存區域一點也不方便,所以我們需要一個顯示模塊。這個就是我們的顯卡驅動了,是不是不敢相信驅動是這么簡單的事情?我們先來看一下一些常量定義:
03/include/scr.h

 

#define MAX_LINES    25                // bios默認設置屏幕為 80x25大小,彩色字符模式
#define MAX_COLUMNS  80
#define TAB_WIDTH    8                 //
必須是:2^n
#define VIDEO_RAM    0xb8000           //
顯存地址

 

我們曾簡要提到過這個地址,在字符模式下,適配器使用0xB8000-0xBF000作為視頻內存。通常我們處于80x25大小屏幕,有16種顏色。由于一個屏幕只需要80x25x2個字節,即4k,所以該視頻內存可以分為多個頁。我們使用所有的頁,但是當前只能有一個頁面可見。為了顯示一個字符,將用到2個字節,一個字節是字符值,另一個字節是字符屬性(即顏色)。屬性字節定義如下:

To display a single character, two bytes are being used which called the character byte and the attribute byte. The character byte contains the value of the character. The attribute byte is defined like this:

Bit 7

閃爍

Bits 6-4

背景色

Bit 3

明亮模式

Bit3 2-0

前景色

 

#define LINE_RAM    (MAX_COLUMNS*2)
#define PAGE_RAM    (MAX_LINE*MAX_COLUMNS)

#define BLANK_CHAR    (' ')
#define BLANK_ATTR    (0x70)        /*
白色前景,黑色背景 */

#define CHAR_OFF(x,y)    (LINE_RAM*(y)+2*(x))        /*
計算給定坐標xy的偏移地址(相對0xB8000 */
Calculates the offset of a given ordinary x, y from 0xB8000

 

typedef enum COLOUR_TAG {                            /* 顏色表 */
    BLACK, BLUE, GREEN, CYAN, RED, MAGENTA, BROWN, WHITE,
    GRAY, LIGHT_BLUE, LIGHT_GREEN, LIGHT_CYAN,
    LIGHT_RED, LIGHT_MAGENTA, YELLOW, BRIGHT_WHITE
} COLOUR;

 

坐標系如下:

  ___________________\

 | 00          /

 |

\|/



03/scr.c

 

static int csr_x = 0;
static int csr_y = 0;

由于我們只用到了一個視頻頁,所以上面兩個變量就可以存儲坐標了。關于多頁顯示可以在網絡上查找相關資料。


static void
scroll(int lines) {        
向上滾動屏幕多少行,就是一些內存復寫。
    short *p = (short *)(VIDEO_RAM+CHAR_OFF(MAX_COLUMNS-1, MAX_LINES-1));
    int i = MAX_COLUMNS-1;
    memcpy((void *)VIDEO_RAM, (void *)(VIDEO_RAM+LINE_RAM*lines),
           LINE_RAM*(MAX_LINES-lines));


    for (; i>=0; --i)            //
說明這個for循環有問題,覺得應該改成下面這樣:

    // for (i = i * lines; i>=0; --i)

        *p-- = (short)((BLANK_ATTR<<4)|BLANK_CHAR);

}

 

下面函數設置光標可能會引發競態條件,但是print_c只準備在內核中使用,所以沒有關中斷。它可能會引起一些bug,但是我沒有找到。譯注:全局變量沒有鎖保護在設計上就是一種錯誤。這里的代碼保護確實是沒有做!讀者應用到自己的內核時要小心了。

void
set_cursor(int x, int y) {

    csr_x = x;

    csr_y = y;

 

    outb(0x0e, 0x3d4);                                   設置光標高8位的準備工作

    outb(((csr_x+csr_y*MAX_COLUMNS)>>8)&0xff, 0x3d5);    設置光標高8

    outb(0x0f, 0x3d4);                                   設置光標低8位的準備工作
    outb(((csr_x+csr_y*MAX_COLUMNS))&0xff, 0x3d5);      
設置光標低8   
}

void
get_cursor(int *x, int *y) {
    *x = csr_x;
    *y = csr_y;
}

void
print_c(char c, COLOUR fg, COLOUR bg) {

// 用這個函數來顯示一個具體的字符到屏幕,我們可以把它看作顯卡驅動
    char *p;
    char attr;


    p = (char *)VIDEO_RAM+CHAR_OFF(csr_x, csr_y);        //
取光標位置
    attr = (char)(bg<<4|fg);                             //
屬性

 

    switch (c) {
    case '\r':
        csr_x = 0;
        break;
    case '\n':
        for (; csr_x<MAX_COLUMNS; ++csr_x) {
            *p++ = BLANK_CHAR;
            *p++ = attr;
        }
        break;
    case '\t':
        c = csr_x+TAB_WIDTH-(csr_x&(TAB_WIDTH-1));
        c = c<MAX_COLUMNS?c:MAX_COLUMNS;
        for (; csr_x<c; ++csr_x) {
            *p++ = BLANK_CHAR;
            *p++ = attr;
        }
        break;
    case '\b':
        if ((! csr_x) && (! csr_y))
            return;
        if (! csr_x) {
            csr_x = MAX_COLUMNS - 1;
            --csr_y;
        } else
            --csr_x;
        ((short *)p)[-1] = (short)((BLANK_ATTR<<4)|BLANK_CHAR);
        break;
    default:
        *p++ = c;
        *p++ = attr;
        ++csr_x;
        break;
    }
    if (csr_x >= MAX_COLUMNS) {
        csr_x = 0;
        if (csr_y < MAX_LINES-1)
            ++csr_y;
        else
            scroll(1);
    }
    set_cursor(csr_x, csr_y);        //
設置光標位置
}

函數比較簡單,沒有分析的必要了,大家自己琢磨吧。

 

 

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            久久人人爽国产| 黄网站免费久久| 亚洲午夜一区二区三区| 亚洲精品1区2区| 欧美区在线观看| 亚洲欧美春色| 久久精品视频免费观看| 136国产福利精品导航网址| 亚洲国语精品自产拍在线观看| 欧美精品一区在线观看| 亚洲欧美激情一区| 久久精品视频网| 欧美激情第3页| 久久中文字幕一区| 亚洲精品一区二区三区在线观看| 亚洲精品乱码久久久久| 国产精品v片在线观看不卡 | 你懂的国产精品| 99国产精品视频免费观看一公开| 亚洲午夜精品久久| 在线看片一区| 亚洲一二三区精品| 亚洲国产小视频| 亚洲性视频网址| 亚洲精品国产精品乱码不99按摩| 亚洲图片在线| 亚洲精品国产系列| 小嫩嫩精品导航| 欧美一级淫片aaaaaaa视频| 亚洲婷婷综合久久一本伊一区| 在线视频欧美日韩精品| 国产一区二区三区久久悠悠色av | 一区二区三区四区国产精品| 国产精品五月天| 欧美成人一区二区在线 | 久久精品国产免费观看| 一本一道久久综合狠狠老精东影业 | 亚洲欧美在线aaa| 欧美ed2k| 亚洲视屏一区| 在线成人中文字幕| 亚洲欧美日韩精品一区二区| 亚洲美女诱惑| 久久一区二区三区四区五区| 亚洲欧美中日韩| 欧美天天综合网| 亚洲国产欧美精品| 亚洲国产成人av好男人在线观看| 午夜精品免费在线| 亚洲男女毛片无遮挡| 欧美久久久久久蜜桃| 欧美激情综合色| 精品88久久久久88久久久| 亚洲欧美日韩国产综合在线| 亚洲一区二区三区四区视频| 欧美日韩国产综合久久| 亚洲国产精品va在线看黑人动漫 | 美女主播视频一区| 亚洲精品国产日韩| 久久在线免费观看视频| 免费精品视频| 亚洲国产另类精品专区| 91久久精品日日躁夜夜躁国产| 亚洲国产91| 久久综合久久久久88| 欧美高清视频在线| 亚洲国产欧美另类丝袜| 欧美jizz19hd性欧美| 亚洲大胆视频| 日韩视频一区二区在线观看| 欧美日韩国产精品 | 欧美在线视频一区二区| 国产伦一区二区三区色一情| 午夜国产欧美理论在线播放| 久久国产精品黑丝| 狠狠色综合播放一区二区| 久久婷婷蜜乳一本欲蜜臀| 久久嫩草精品久久久久| 韩日精品中文字幕| 久久只精品国产| 亚洲高清资源| 亚洲免费一在线| 国产小视频国产精品| 久久一区二区精品| 亚洲精品一区在线观看香蕉| 亚洲欧美激情视频| 黄色在线一区| 欧美电影打屁股sp| 亚洲网站在线播放| 麻豆精品视频在线| 在线中文字幕一区| 国产一区二区三区在线播放免费观看| 久久婷婷亚洲| 亚洲午夜女主播在线直播| 久久亚洲不卡| 一本久久a久久免费精品不卡| 国产精品久久久久7777婷婷| 久久精品一级爱片| 亚洲素人一区二区| 狠狠色狠狠色综合日日五| 欧美亚洲成人网| 亚洲综合视频1区| 欧美电影免费观看高清| 亚洲午夜视频在线| 亚洲国产精品女人久久久| 欧美三区在线| 免费在线播放第一区高清av| 亚洲午夜激情网站| 亚洲高清免费视频| 久久激五月天综合精品| 一区二区三区精品国产| 在线精品亚洲一区二区| 国产欧美日韩亚洲| 欧美日韩一区二区在线观看视频 | 亚洲国内精品| 久久亚洲风情| 亚洲欧美日韩国产另类专区| 亚洲激情成人在线| 国内精品久久久久久影视8 | 欧美国产综合视频| 日韩午夜黄色| 欧美精品综合| 久久国产一区| 亚洲午夜一区二区三区| 亚洲精品欧美极品| 欧美激情亚洲| 欧美成人嫩草网站| 久久婷婷蜜乳一本欲蜜臀| 欧美一级播放| 午夜老司机精品| 亚洲影院免费观看| 亚洲一区bb| 亚洲一区欧美一区| 亚洲午夜激情网页| 中国成人黄色视屏| 一区二区三区产品免费精品久久75| 亚洲韩国日本中文字幕| 亚洲福利小视频| 亚洲国产婷婷香蕉久久久久久| 揄拍成人国产精品视频| 加勒比av一区二区| 亚洲国产精品尤物yw在线观看| 在线精品视频一区二区三四| 亚洲第一精品夜夜躁人人爽| 在线日韩欧美| 欧美国内亚洲| 久久精品国产精品 | 亚洲一区二区视频| 亚洲五月婷婷| 欧美亚洲系列| 久久视频在线视频| 欧美激情视频一区二区三区不卡| 欧美激情一区二区三级高清视频 | 久久香蕉国产线看观看网| 免费成人黄色| 亚洲精品国产精品国自产在线| 亚洲黄色影片| 亚洲小视频在线观看| 欧美一区二区三区男人的天堂 | 蜜臀av一级做a爰片久久| 欧美国产精品中文字幕| 国产精品yjizz| 国产在线视频欧美| 99精品热6080yy久久| 国产精品人成在线观看免费| 国产精品系列在线播放| 国产主播喷水一区二区| 亚洲国产精品久久久久婷婷884| 亚洲美女毛片| 午夜精品www| 蜜臀va亚洲va欧美va天堂| 91久久线看在观草草青青| 亚洲天堂成人在线观看| 久久香蕉国产线看观看网| 欧美日韩国产高清| 国产午夜精品一区二区三区欧美| 亚洲国产精品女人久久久| 亚洲欧美不卡| 亚洲电影毛片| 欧美一区二区在线| 欧美日韩一区三区四区| 精品成人免费| 亚洲欧美综合v| 亚洲第一天堂av| 欧美伊久线香蕉线新在线| 欧美精品性视频| 尤物在线精品| 久久er精品视频| 日韩视频一区二区三区在线播放免费观看 | 久久亚洲一区二区| 欧美日韩在线大尺度| 激情一区二区三区| 亚洲一区在线免费| 亚洲国产精品成人| 久久九九精品| 国产日韩精品一区二区| 亚洲视屏在线播放| 亚洲经典三级| 欧美波霸影院| 亚洲国产日韩欧美在线99|