• <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>

            天衣有縫

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

            3課:輔助函數(shù)


            聲明:轉(zhuǎn)載請保留

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

            原作者:xiaoming.mo at skelix dot org

            MSN & Email: jinglexy at yahoo dot com dot cn

            目標(biāo)            下載源程序

             

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


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

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

             

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

            我們這樣設(shè)計(jì)kprintf參數(shù):kprintf(color, format string, arguments...)

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


            03/kprintf.c

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

                                                // 這個(gè)宏四舍五入?yún)?shù)大小為4字節(jié)的倍數(shù)
            #define args_start(ap, fmt) do {    \
            ap = (char *)((unsigned int)&fmt + _arg_stack_size(&fmt));   \
            } while (0)

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


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

                                                // 當(dāng)前參數(shù)地址,然后設(shè)置指針為下一個(gè)參數(shù)地址,曖昧的函數(shù)名!

            03/kprintf.c

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

             

            下面兩個(gè)函數(shù)解析值為指定的進(jìn)制數(shù):
            static void
            parse_num(unsigned int value, unsigned int base) {            //
            可以打印小于等于10進(jìn)制的數(shù)
                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進(jìn)制數(shù)
            parse_hex(unsigned int value) {
                int i = 8;
                while (i-- > 0) {
                    buf[ptr++] = "0123456789abcdef"[(value>>(i*4))&0xf];
                }
            }

             

            現(xiàn)在我們來看一下 kprintf這個(gè)函數(shù),它支持的格式:%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, 后面會(huì)介紹到.

             

                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()
            是下層的顯示函數(shù),本文后面會(huì)有講解 */

            }


             

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

             

             

            /* 下面函數(shù)對重疊區(qū)域也進(jìn)行了處理 */
            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
            函數(shù)
            直接操作顯存區(qū)域一點(diǎn)也不方便,所以我們需要一個(gè)顯示模塊。這個(gè)就是我們的顯卡驅(qū)動(dòng)了,是不是不敢相信驅(qū)動(dòng)是這么簡單的事情?我們先來看一下一些常量定義:
            03/include/scr.h

             

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

             

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

            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))        /*
            計(jì)算給定坐標(biāo)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;

             

            坐標(biāo)系如下:

              ___________________\

             | 00          /

             |

            \|/



            03/scr.c

             

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

            由于我們只用到了一個(gè)視頻頁,所以上面兩個(gè)變量就可以存儲(chǔ)坐標(biāo)了。關(guān)于多頁顯示可以在網(wǎng)絡(luò)上查找相關(guān)資料。


            static void
            scroll(int lines) {        
            向上滾動(dòng)屏幕多少行,就是一些內(nèi)存復(fù)寫。
                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)            //
            說明這個(gè)for循環(huán)有問題,覺得應(yīng)該改成下面這樣:

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

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

            }

             

            下面函數(shù)設(shè)置光標(biāo)可能會(huì)引發(fā)競態(tài)條件,但是print_c只準(zhǔn)備在內(nèi)核中使用,所以沒有關(guān)中斷。它可能會(huì)引起一些bug,但是我沒有找到。譯注:全局變量沒有鎖保護(hù)在設(shè)計(jì)上就是一種錯(cuò)誤。這里的代碼保護(hù)確實(shí)是沒有做!讀者應(yīng)用到自己的內(nèi)核時(shí)要小心了。

            void
            set_cursor(int x, int y) {

                csr_x = x;

                csr_y = y;

             

                outb(0x0e, 0x3d4);                                   設(shè)置光標(biāo)高8位的準(zhǔn)備工作

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

                outb(0x0f, 0x3d4);                                   設(shè)置光標(biāo)低8位的準(zhǔn)備工作
                outb(((csr_x+csr_y*MAX_COLUMNS))&0xff, 0x3d5);      
            設(shè)置光標(biāo)低8   
            }

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

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

            // 用這個(gè)函數(shù)來顯示一個(gè)具體的字符到屏幕,我們可以把它看作顯卡驅(qū)動(dòng)
                char *p;
                char attr;


                p = (char *)VIDEO_RAM+CHAR_OFF(csr_x, csr_y);        //
            取光標(biāo)位置
                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);        //
            設(shè)置光標(biāo)位置
            }

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

             

             

            久久99热精品| 亚洲精品乱码久久久久久中文字幕| 人妻丰满?V无码久久不卡| 久久99精品久久久久久久久久| 久久乐国产综合亚洲精品| 亚洲国产精品久久66| 99久久婷婷国产一区二区| 久久91精品国产91久久小草| 人妻无码αv中文字幕久久| 久久久久av无码免费网| 久久综合久久综合亚洲| 伊人久久大香线蕉综合影院首页| 综合久久国产九一剧情麻豆 | 国产99久久精品一区二区| 狠狠精品久久久无码中文字幕| 日本五月天婷久久网站| 久久久久亚洲AV无码观看| 蜜臀av性久久久久蜜臀aⅴ麻豆 | 日本一区精品久久久久影院| 91精品国产91久久久久久青草| 香蕉久久一区二区不卡无毒影院| 国产福利电影一区二区三区久久老子无码午夜伦不 | 国产成人精品综合久久久| 久久青青国产| 99久久夜色精品国产网站| 精品久久久久久| 中文字幕无码久久久| 精品熟女少妇a∨免费久久| 国产精品成人精品久久久| 久久亚洲AV无码精品色午夜| 久久国产高潮流白浆免费观看| 91精品国产91久久| 亚洲伊人久久大香线蕉综合图片| 久久精品国产91久久麻豆自制 | 亚洲欧洲精品成人久久曰影片 | 国产午夜电影久久| 久久久久久国产精品美女| 欧美综合天天夜夜久久| 天堂久久天堂AV色综合| 精品人妻伦九区久久AAA片69| 久久综合综合久久综合|