• <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)系 :: 聚合  :: 管理

            5課:中斷和異常2      下載源代碼


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

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

            原作者:xiaoming.mo at skelix dot org

            MSN & Email: jinglexy at yahoo dot com dot cn

            目標(biāo)


            在上一節(jié)課中,我們介紹了如何處理異常。本課中,我們講學(xué)習(xí)如何在skelix中使用時鐘中斷和鍵盤。


            可編程定時器通常使用主板上的8253/8254芯片。經(jīng)過設(shè)置后,它每隔一段時間會觸發(fā)一個中斷,我們將利用它來實現(xiàn)多任務(wù)的搶占。這個定時器芯片有3個獨(dú)立的計數(shù)器模塊,可以使用0x40~0x42端口來操作它們,每個計數(shù)器有自己的16為計數(shù)寄存器,并且可以在6中狀態(tài)下工作運(yùn)行,這個計數(shù)器可以使用BCD或者16進(jìn)制。通過0x43端口可以訪問這3個計數(shù)寄存器,該控制端口操作字格式如下:

             

             7,6

            Select Counter ,選擇對那個計數(shù)器進(jìn)行操作。 “00” 表示選擇 Counter 0 “01” 表示選擇 Counter 1 “10” 表示選擇 Counter 2 “11” 表示 Read-Back Command (僅對于 8254 ,對于 8253 無效)

            5,4

            Read/Write/Latch 格式位。 “00” 表示鎖存( Latch )當(dāng)前計數(shù)器的值; “01” 只讀寫計數(shù)器的高字節(jié) ( MSB ); “10” 只讀寫計數(shù)器的低字節(jié)( LSB ); “11” 表示先讀寫計數(shù)器的 LSB ,再讀寫 MSB

            3-1

            Mode bits ,控制各通道的工作模式。 “000” 對應(yīng) Mode 0 “001” 對應(yīng) Mode 1 “010” 對應(yīng) Mode 2 “011”對應(yīng) Mode 3 “100” 對應(yīng) Mode 4 “101” 對應(yīng) Mode 5

              0

            控制計數(shù)器的存儲模式。 0 表示以二進(jìn)制格式存儲, 1 表示計數(shù)器中的值以 BCD 格式存儲

             

            根據(jù)Intel手冊,系統(tǒng)上電后,8254狀態(tài)是不可知的。工作模式,計數(shù)值,輸出基數(shù)都是不可知的。只有對它進(jìn)行編程,才能使用各個計數(shù)器模塊。現(xiàn)在我們來看下程序:
            05/timer/time.c

             

            void timer_install(int hz) {        // 設(shè)置定時器多長時間發(fā)送一個中斷給cpu
                unsigned int divisor = 1193180/hz;

            由于外部晶振電路頻率是1193180, 所以設(shè)置計數(shù)器值為1193180/hz

            表示當(dāng)計數(shù)從0累加到1193180/hz后發(fā)一個方波脈沖給cpu


                outb(0x36, 0x43);               //
            二進(jìn)制,工作模式為3,先寫LSB再寫MSB
                outb(divisor&0xff, 0x40);
                outb(divisor>>8, 0x40);
                outb(inb(0x21)&0xfe, 0x21);     //
            設(shè)置PIC1 的掩碼第0位:允許時鐘中斷
            }

            volatile unsigned int timer_ticks = 0;

            void do_timer(void) {               //
            時鐘中斷處理程序
                int x , y;
                ++timer_ticks;
                get_cursor(&x, &y);
                set_cursor(71, 24);
                kprintf(KPL_DUMP, "%x", timer_ticks);
                set_cursor(x, y);
                outb(0x20, 0x20);              //
            發(fā)送eoi:通知PIC1 中斷例程已處理完成,可以接收新的中斷了

            因為timer只鏈接在PIC1上,所以不需要 oub(0x20, 0xa0) 來告知PIC2

            }

             

            另外我們還需要改一些其他文件:
            05/timer/include/isr.h

            #define VALID_ISR    (32+1)        // 我們添加了一個新的ISR例程到isr表中


            05/timer/isr.s

            isr:    .long    divide_error, isr0x00, debug_exception, isr0x01
                    .long    breakpoint, isr0x02, nmi, isr0x03
                    .long    overflow, isr0x04, bounds_check, isr0x05
                    .long    invalid_opcode, isr0x06, cop_not_avalid, isr0x07
                    .long    double_fault, isr0x08, overrun, isr0x09
                    .long    invalid_tss, isr0x0a, seg_not_present, isr0x0b
                    .long    stack_exception, isr0x0c, general_protection, isr0x0d
                    .long    page_fault, isr0x0e, reversed, isr0x0f
                    .long    coprocessor_error, isr0x10, reversed, isr0x11
                    .long    reversed, isr0x12, reversed, isr0x13
                    .long    reversed, isr0x14, reversed, isr0x15
                    .long    reversed, isr0x16, reversed, isr0x17
                    .long    reversed, isr0x18, reversed, isr0x19
                    .long    reversed, isr0x1a, reversed, isr0x1b
                    .long    reversed, isr0x1c, reversed, isr0x1d
                    .long    reversed, isr0x1e, reversed, isr0x1f
                    .long    do_timer, isr0x20      # <<<<<
            添加的項 >>>>>>
                    isrNoError        0x1b
                    isrNoError        0x1c
                    isrNoError        0x1d
                    isrNoError        0x1e
                    isrNoError        0x1f
                    isrNoError        0x20          # <<<<<
            添加的項 >>>>>>

             

            05/timer/init.c

            void
            init(void) {
                char wheel[] = {'\\', '|', '/', '-'};
                int i = 0;

                idt_install();
                pic_install();
                timer_install(100);            //
            每秒中100次時鐘中斷,linux是這樣的,windows200

                sti();                         // 不要忘了使能中斷哦

                for (;;) {        __asm__ ("movb    %%al,    0xb8000+160*24"::"a"(wheel[i]));
                    if (i == sizeof wheel)
                        i = 0;
                    else
                        ++i;
                }
            }

             

            我們還是使用以前的Makefile,當(dāng)然需要加入新的模塊到 KERNEL_OBJS 中:
            05/timer/Makefile

            KERNEL_OBJS= load.o init.o isr.o timer.o libcc.o scr.o kprintf.o exceptions.o

            運(yùn)行make編譯一把,在讓vmware執(zhí)行一下final.img,是不是很有成就感?

            鍵盤

            好啦,我們已經(jīng)知道在屏幕上顯示一些東西了,現(xiàn)在學(xué)習(xí)按鍵處理,然后在顯示出來。

            當(dāng)一個鍵按下時,一個8位掃描碼會發(fā)送給計算機(jī)。例如,‘a’鍵按下后,掃描碼0x1e(取決于鍵盤布局,不要告訴我你用的日本鍵盤,鄙視一個)發(fā)送,當(dāng)翻開按鍵時,掃描碼最高位置一并發(fā)送:0x1e | 0x80 = 0x9e。對于一些特殊按鍵,如breakhome鍵等,這里不做處理,有興趣的同學(xué)可以查找相關(guān)資料。一些可見按鍵對于本課來說已足夠。

            下面是我使用的按鍵碼表,如果你確實用到了特殊鍵盤,最好找到對應(yīng)的資料。

            掃描碼

            掃描碼

            .

            ......

            ......

            ......

            ......

             

             

             

            01

            ESC

            02

            1!

            03

            2@

            04

            3#

            05

            4$

            06

            5%

            07

            6^

            08

            7&

            09

            8*

            0A

            9(

            0B

            0)

            0C

            -_

            0D

            =+

            0E

            <--

            0F

            TAB

            10

            qQ

            11

            wW

            12

            eE

            13

            rR

            14

            tT

            15

            yY

            16

            uU

            17

            iI

            18

            oO

            19

            pP

            1A

            [{

            1B

            ]}

            1C

            Enter

            1D

            LCTL

            1E

            aA

            1F

            sS

            20

            dD

            21

            fF

            22

            gG

            23

            hH

            24

            jJ

            25

            kK

            26

            lL

            27

            ;:

            28

            '"

            29

            `~

            2A

            LSHT

            2B

            \|

            2C

            zZ

            2D

            xX

            2E

            cC

            2F

            vV

            30

            bB

            31

            nN

            32

            mM

            33

            ,<

            34

            .>

            35

            /?

            36

            RSHT

            37

            **

            38

            LALT

            39

            SPACE

            3B-44

            F1-F10

            57

            F11

            58

            F12

            正如你看到的,ctrlshiftalt鍵以普通掃描碼發(fā)送,所以我們可以重新映射這些鍵以顯示到屏幕上。

            訪問鍵盤控制器仍然經(jīng)由端口:

            05/keyboard/kb.c

            void
            do_kb(void) {
                int com;
                void (*key_way[0x80])(void) = {
                    /*00*/unp, unp, pln, pln, pln, pln, pln, pln,
                    /*08*/pln, pln, pln, pln, pln, pln, pln, pln,
                    /*10*/pln, pln, pln, pln, pln, pln, pln, pln,
                    /*18*/pln, pln, pln, pln, pln, ctl, pln, pln,
                    /*20*/pln, pln, pln, pln, pln, pln, pln, pln,
                    /*28*/pln, pln, shf, pln, pln, pln, pln, pln,
                    /*30*/pln, pln, pln, pln, pln, pln, shf, pln,
                    /*38*/alt, pln, unp, fun, fun, fun, fun, fun,
                    /*40*/fun, fun, fun, fun, fun, unp, unp, unp,
                    /*48*/unp, unp, unp, unp, unp, unp, unp, unp,
                    /*50*/unp, unp, unp, unp, unp, unp, unp, fun,
                    /*58*/fun, unp, unp, unp, unp, unp, unp, unp,
                    /*60*/unp, unp, unp, unp, unp, unp, unp, unp,
                    /*68*/unp, unp, unp, unp, unp, unp, unp, unp,
                    /*70*/unp, unp, unp, unp, unp, unp, unp, unp,
                    /*78*/unp, unp, unp, unp, unp, unp, unp, unp,
                };

            上面是一堆函數(shù)指針,當(dāng)獲取到掃描碼后,我們使用這個表找到相關(guān)的處理函數(shù),進(jìn)行相關(guān)的處理。unp是未定義的鍵,pln是可顯示字符,ctl是控制鍵ctrlshf是控制鍵shiftalt是控制鍵altfun函數(shù)用于F1F12

                com = 0;
                scan_code = inb(0x60);                //
            80420x60端口獲取掃描碼

                (*key_way[scan_code&0x7f])();         // 0x7f是放開按鍵的掩碼


                /*
            按鍵已處理 */
                outb((com=inb(0x61))|0x80, 0x61);     //
            當(dāng)我們從0x60端口讀完掃描碼后,這個掃描碼并不會自動刪除,

                outb(com&0x7f, 0x61);                 // 同時也阻止了我們讀下一系列按鍵,所以我們需要通知鍵盤控制器按鍵已處理,

                                                      // 做法很簡單:只需要通過0x61端口的最高位disablere-enable鍵盤即可,

                                                      //  7: 0=Enable keyboard; 1=Disable keyboard
                outb(0x20, 0x20);                     //
            發(fā)送EOI:中斷處理已完成
            }

            static unsigned char shf_p = 0;           //
            保存Ctrl, Shift  Alt鍵的狀態(tài)
            static unsigned char ctl_p = 0;
            static unsigned char alt_p = 0;
            static unsigned char scan_code;           //
            當(dāng)前處理的掃描碼

            /*
            這個函數(shù)用于打印可打印字符 */
            static void
            pln(void) {
                static const char key_map[0x3a][2] = {
                    /*00*/{0x0, 0x0}, {0x0, 0x0}, {'1', '!'}, {'2', '@'},
                    /*04*/{'3', '#'}, {'4', '$'}, {'5', '%'}, {'6', '^'},
                    /*08*/{'7', '&'}, {'8', '*'}, {'9', '('}, {'0', ')'},
                    /*0c*/{'-', '_'}, {'=', '+'}, {'\b','\b'},{'\t','\t'},
                    /*10*/{'q', 'Q'}, {'w', 'W'}, {'e', 'E'}, {'r', 'R'},
                    /*14*/{'t', 'T'}, {'y', 'Y'}, {'u', 'U'}, {'i', 'I'},
                    /*18*/{'o', 'O'}, {'p', 'P'}, {'[', '{'}, {']', '}'},
                    /*1c*/{'\n','\n'},{0x0, 0x0}, {'a', 'A'}, {'s', 'S'},
                    /*20*/{'d', 'D'}, {'f', 'F'}, {'g', 'G'}, {'h', 'H'},
                    /*24*/{'j', 'J'}, {'k', 'K'}, {'l', 'L'}, {';', ':'},
                    /*28*/{'\'','\"'},{'`', '~'}, {0x0, 0x0}, {'\\','|'},
                    /*2c*/{'z', 'Z'}, {'x', 'X'}, {'c', 'C'}, {'v', 'V'},
                    /*30*/{'b', 'B'}, {'n', 'N'}, {'m', 'M'}, {',', '<'},
                    /*34*/{'.', '>'}, {'/', '?'}, {0x0, 0x0}, {'*', '*'},
                    /*38*/{0x0, 0x0}, {' ', ' '} };

             

            定義可打印字符碼表,鍵key_map[?][0]shift未按下的掃描碼對應(yīng)的字符,

            key_map[?][1]責(zé)對應(yīng)按下shift的鍵碼。


                if (scan_code & 0x80)
                    return;

                // 已經(jīng)按下這個鍵了,那就什么也不做

                print_c(key_map[scan_code&0x7f][shf_p], WHITE, BLACK);    //
            打印它:黑紙白字,清清楚楚
            }

            /* Ctrl
            */
            static void
            ctl(void) {
                ctl_p ^= 0x1;
            }

            /* Alt
            */
            static void
            alt(void) {
                alt_p
            ^= 0x1;
            }

            /* Shift
            */
            static void
            shf(void) {
                shf_p ^= 0x1;
            }

            /* F1, F2 ~ F12
            */
            static void
            fun(void) {
            }

            /*
            暫不實現(xiàn)特殊鍵 */
            static void
            unp(void) {
            }

             

            當(dāng)有shift鍵按下時,我們打印大寫字母。
            void
            kb_install(void) {
                outb(inb(0x21)&0xfd, 0x21);
            }

             

            我們幾乎實現(xiàn)完了,最后在中斷入口表中添加一項:
            05/keyboard/isr.s

                    .long    do_timer, isr0x20, do_kb, isr0x21  # 注意:加入了按鍵處理項
                    isrNoError        0x20
                    isrNoError        0x21      #
            這里也加一個宏定義

            05/keyboard/include/isr.h

            #define VALID_ISR    (32+2)         # ISR個數(shù)再加1


            05/keyboard/init.c

                timer_install(100);
                kb_install();      /*
            安裝鍵盤處理 */
                sti();

             

            MakefileKERNEL_OBJS 加入新的模塊:

            05/keyboard/MakefileKERNEL_OBJS= load.o init.o isr.o timer.o libcc.o scr.o kprintf.o exceptions.o kb.o

             

            運(yùn)行make編譯一把,再用vmware執(zhí)行,看看是不是可以處理按鍵了,退格鍵也可以了。

            你甚至可以再上面寫一個hello, world,只是不能編譯:)

             

             

            午夜精品久久久内射近拍高清| 久久er热视频在这里精品| 久久影视综合亚洲| 久久久久精品国产亚洲AV无码| 亚洲精品乱码久久久久久中文字幕| 精品精品国产自在久久高清| 久久精品女人天堂AV麻| 久久久久亚洲AV无码网站| 国产99久久久国产精品~~牛| 亚洲欧美日韩精品久久亚洲区 | 久久久久久久综合综合狠狠| 一本色道久久HEZYO无码| 热久久国产精品| avtt天堂网久久精品| 久久久久亚洲av综合波多野结衣 | 久久99久久99精品免视看动漫| 欧美777精品久久久久网| 久久亚洲精品无码AV红樱桃| 久久强奷乱码老熟女网站| 久久精品国产69国产精品亚洲| 亚洲精品乱码久久久久久蜜桃图片| 欧美粉嫩小泬久久久久久久 | 好属妞这里只有精品久久| 亚洲国产精品无码久久98| 日日狠狠久久偷偷色综合96蜜桃 | 亚洲国产精品久久久久网站| 国产精品9999久久久久| 狼狼综合久久久久综合网| 中文国产成人精品久久不卡| 亚洲七七久久精品中文国产 | 久久婷婷五月综合色奶水99啪| 久久国产香蕉一区精品| 精品国产青草久久久久福利| 青青草国产精品久久久久| 69久久夜色精品国产69| 91精品国产综合久久婷婷| 精品综合久久久久久97超人| 91麻豆精品国产91久久久久久| 99久久综合狠狠综合久久| 久久一区二区免费播放| 2020久久精品亚洲热综合一本|