第25天 增加命令行窗口

第25天 增加命令行窗口

2020.4.30

1. 蜂鳴器發聲(harib22a)

  • 蜂鳴器和定時器一樣,都是由PIT來控制的,而PIT位於芯片組中,因此所有型號的電腦都能使用它。

  • 關於蜂鳴器的相關資料:

    • 其中,赫茲大小 = 1.19318MHz / 設定值
    • PIT時鐘頻率與CPU時鐘頻率無關,PIT時鐘頻率大小恆定爲1.19318MHz。
  • 設計蜂鳴器發聲的API:

    • EDX = 20
    • EAX = 聲音頻率(單位是mHz,毫赫茲),比如EAX=200000,發出200Hz的聲音。當EAX=0時,停止發聲。
  • 修改hrb_api:

    int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax)
    {
        ……
        } else if (edx == 20) {
            if (eax == 0) { /*停止發聲*/
                i = io_in8(0x61);
                io_out8(0x61, i & 0x0d);
            } else {
                i = 1193180000 / eax; /*獲得設定值*/
                io_out8(0x43, 0xb6); 
                io_out8(0x42, i & 0xff);  /低8位*/
                io_out8(0x42, i >> 8); /*高8位*/
                i = io_in8(0x61);
                io_out8(0x61, (i | 0x03) & 0x0f);
            }
        }
        return 0;
    }
    
  • 編寫可以供C語言調用20號API的函數api_beep:

    _api_beep:			; void api_beep(int tone);
            MOV		EDX,20
            MOV		EAX,[ESP+4]			; tone
            INT		0x40
            RET
    
  • 編寫應用程序beepdown.c:

    void api_end(void);
    int api_getkey(int mode);
    int api_alloctimer(void);
    void api_inittimer(int timer, int data);
    void api_settimer(int timer, int time);
    void api_beep(int tone);
    
    void HariMain(void)
    {
        int i, timer;
        timer = api_alloctimer();
        api_inittimer(timer, 128);
        for (i = 20000000; i >= 20000; i -= i / 100) {
            /* 20KHz到20Hz : 人類可以聽見的聲音的範圍 */
            /* i以1%的速度遞減 */
            api_beep(i);
            api_settimer(timer, 1);		/* 0.01s */
            if (api_getkey(1) != 128) {
                break;
            }
        }
        api_beep(0); /*停止發聲*/
        api_end();
    }
    
    • 應用程序每0.01s便降低一次發出聲音的頻率,當聲音頻率降至20Hz或者用戶按下任意鍵時結束。
  • make後用VMware運行:

2. 增加更多的顏色(1)(harib22b)

  • 到目前爲止,此OS只使用了16種顏色,雖然現在已經能夠使用256色的顯示模式。剩下240種顏色還可以用。

  • 規定,爲RGB三原色中每種顏色賦予6個色階,這樣就可以定義6*6*6=216種顏色。此時還剩下24種顏色了。

    • 色階:亮度
  • 修改init_palette函數(graphic.c):

    void init_palette(void)
    {
        static unsigned char table_rgb[16 * 3] = {
            0x00, 0x00, 0x00,	/*  0:黑 */
            0xff, 0x00, 0x00,	/*  1:亮紅 */
            0x00, 0xff, 0x00,	/*  2:亮綠 */
            0xff, 0xff, 0x00,	/*  3:亮黃 */
            0x00, 0x00, 0xff,	/*  4:亮藍 */
            0xff, 0x00, 0xff,	/*  5:亮紫 */
            0x00, 0xff, 0xff,	/*  6:淺亮藍 */
            0xff, 0xff, 0xff,	/*  7:白 */
            0xc6, 0xc6, 0xc6,	/*  8:亮灰 */
            0x84, 0x00, 0x00,	/*  9:暗紅 */
            0x00, 0x84, 0x00,	/* 10:暗綠 */
            0x84, 0x84, 0x00,	/* 11:暗黃 */
            0x00, 0x00, 0x84,	/* 12:暗青 */
            0x84, 0x00, 0x84,	/* 13:暗紫 */
            0x00, 0x84, 0x84,	/* 14:淺暗藍 */
            0x84, 0x84, 0x84	/* 15:暗灰 */
        };
        unsigned char table2[216 * 3];
        int r, g, b;
        set_palette(0, 15, table_rgb);
        for (b = 0; b < 6; b++) {
            for (g = 0; g < 6; g++) {
                for (r = 0; r < 6; r++) {
                    table2[(r + g * 6 + b * 36) * 3 + 0] = r * 51;
                    table2[(r + g * 6 + b * 36) * 3 + 1] = g * 51;
                    table2[(r + g * 6 + b * 36) * 3 + 2] = b * 51;
                }
            }
        }
        set_palette(16, 231, table2);
        return;
    }
    
    • 比如,R的6個色階分別是0, 51, 102, 153, 204, 255。
    • 現在可以指定的顏色是RGB=[51*a, 51*b, 51*c],其中abc是整數,且都介於0和6之間。也就是說RGB=[1, 2, 3]是不合法的。
    • 當指定RGB=[51, 102, 153]時,此時a=1,b=2,c=3,16 + 1 \* 6^0 + 2 \* 6^1 + 3 \* 6^2 = 137,那麼只要使用色號137即可。
    • 當這樣設定時,色號0和色號16的RGB都是#000000,浪費了一個色號。同樣,#ff0000等顏色也會發生重複。
  • 編寫應用程序color.c

    int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
    void api_initmalloc(void);
    char *api_malloc(int size);
    void api_refreshwin(int win, int x0, int y0, int x1, int y1);
    void api_linewin(int win, int x0, int y0, int x1, int y1, int col);
    int api_getkey(int mode);
    void api_end(void);
    
    void HariMain(void)
    {
        char *buf;
        int win, x, y, r, g, b;
        api_initmalloc();
        
        buf = api_malloc(144 * 164);
        win = api_openwin(buf, 144, 164, -1, "color");
        for (y = 0; y < 128; y++) {
            for (x = 0; x < 128; x++) {
                r = x * 2;
                g = y * 2;
                b = 0;
                buf[(x + 8) + (y + 28) * 144] = 16 + (r / 43) + (g / 43) * 6 + (b / 43) * 36;
            }
        }
        api_refreshwin(win, 8, 28, 136, 156);   
        api_getkey(1);  
        api_end();
    }
    
    • 神奇的數字之43。設定的顯示顏色的區域大小是128*128。x*2是R,那麼R的範圍是[0, 256),同理G,而B恆爲0。
    • 色階數是6,128/6=21,如果一行顯示6個色階,每個色階需要佔用21(或22)個像素,x每隔21就需要改變一種色階,r=x*2,也就是r每隔42就要變換一種色階。16 + a + b \* 6 + c \* 36代表一種色號,那麼也就是ab每隔42就要讓這個數值發生變化,這裏向上取整,因此得到了神奇的數字43。
  • make後用VMware運行:

3. 增加更多的顏色(2)(harib22c)

  • 顯然,如果不使用256色模式,而是使用VESA的全色彩模式的話,就可以很容易地顯示更多的色彩。

    • 這樣做的問題有二:
      • 其一,時間不允許,如果使用VESA的全色彩模式,可能需要花上一整天的時間,這有點偏離了我們的初心(笑)。
      • 其二,有的電腦不支持VESA的全色彩模式。
  • 使用一個小技巧輕鬆解決增加更多顏色的問題。

    • 6種不同的亮度,是6級色階。那麼每兩種相鄰的亮度可以產生3種介於這兩種亮度之間的亮度,一共有5個空位,那麼就會多產生15級色階,因此共21級色階。
  • 編寫應用程序color2.c:

    int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
    void api_initmalloc(void);
    char *api_malloc(int size);
    void api_refreshwin(int win, int x0, int y0, int x1, int y1);
    void api_linewin(int win, int x0, int y0, int x1, int y1, int col);
    int api_getkey(int mode);
    void api_end(void);
    
    unsigned char rgb2pal(int r, int g, int b, int x, int y);
    
    void HariMain(void)
    {
        char *buf;
        int win, x, y;
        api_initmalloc();
        buf = api_malloc(144 * 164);
        win = api_openwin(buf, 144, 164, -1, "color2");
        for (y = 0; y < 128; y++) {
            for (x = 0; x < 128; x++) {
                buf[(x + 8) + (y + 28) * 144] = rgb2pal(x * 2, y * 2, 0, x, y);
            }
        }
        api_refreshwin(win, 8, 28, 136, 156);
        api_getkey(1); 
        api_end();
    }
    
    unsigned char rgb2pal(int r, int g, int b, int x, int y)
    {
        static int table[4] = { 3, 1, 0, 2 };
        int i;
        x &= 1; /* 判斷奇偶 */
        y &= 1;
        i = table[x + y * 2];	/* 用來產生中間色的變量 */
        r = (r * 21) / 256;	/* r爲0~20 */
        g = (g * 21) / 256;
        b = (b * 21) / 256;
        r = (r + i) / 4;	/* r爲0~5 */
        g = (g + i) / 4;
        b = (b + i) / 4;
        return 16 + r + g * 6 + b * 36;
    }
    
    • 這裏的函數rgb2pal用多個if語句也可以實現。
    • rgb2pal函數沒看懂。
  • make後用VMware運行:

4. 窗口初始位置(harib22d)

  • 做個小修改:讓窗口總是位於畫面中央,並且能夠判斷當前屏幕中窗口的數量並自動顯示在最上面(但還是在鼠標圖層的下面)。

  • 修改hrb_api:

    int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax)
    {
        ……
        } else if (edx == 5) {
            sht = sheet_alloc(shtctl);
            sht->task = task;
            sht->flags |= 0x10;
            sheet_setbuf(sht, (char *) ebx + ds_base, esi, edi, eax);
            make_window8((char *) ebx + ds_base, esi, edi, (char *) ecx + ds_base, 0);
            sheet_slide(sht, (shtctl->xsize - esi) / 2, (shtctl->ysize - edi) / 2);
            sheet_updown(sht, shtctl->top); /* 將窗口圖層高度指定爲當前鼠標所在圖層的高度,鼠標移到上層 */
            reg[7] = (int) sht;
        } else if (edx == 6) {
        ……
    }
    
    • 注意:sheet_updown(sht, shtctl->top);,不是sheet_updown(sht, shtctl->top - 1);
      • 當沒有新增圖層時,top-1,因爲top是鼠標圖層。
      • 當有新增圖層時,top,因爲此時鼠標圖層高度是top+1。
      • 可以看sheet_updown函數
  • make後用VMware運行:

5. 增加命令行窗口(1)(harib22e)

  • 現在的問題是:當運行應用程序時,命令行就不能再進行其他命令的輸入了。

  • 解決方法有二:

    • 其一:修改命令行窗口,使其能夠在應用程序運行過程中也可以輸入下一條指令。
      • 顯然,這樣修改的話,修改的代碼量巨大。
    • 其二:增加命令行窗口的數量。
      • 命令行窗口是任務,這樣修改的話代碼量不大。
  • 在harib22e中只修改bootpack.c。

    • 準備兩個命令行:
      • task_cons --> task_cons[0] 和 task_cons[1]。
      • 用數組是爲了以後的拓展性。
    • 將task_cons的相關變量都準備兩份。
  • 修改bootpack.c中HariMain:

    void HariMain(void)
    {
        ……
        unsigned char *buf_back, buf_mouse[256], *buf_win, *buf_cons[2];
        struct SHEET *sht_back, *sht_mouse, *sht_win, *sht_cons[2];
        struct TASK *task_a, *task_cons[2];
        ……
        /* sht_cons */
        for (i = 0; i < 2; i++) {
            sht_cons[i] = sheet_alloc(shtctl);
            buf_cons[i] = (unsigned char *) memman_alloc_4k(memman, 256 * 165);
            sheet_setbuf(sht_cons[i], buf_cons[i], 256, 165, -1); 
            make_window8(buf_cons[i], 256, 165, "console", 0);
            make_textbox8(sht_cons[i], 8, 28, 240, 128, COL8_000000);
            task_cons[i] = task_alloc();
            task_cons[i]->tss.esp = memman_alloc_4k(memman, 64 * 1024) + 64 * 1024 - 12;
            task_cons[i]->tss.eip = (int) &console_task;
            task_cons[i]->tss.es = 1 * 8;
            task_cons[i]->tss.cs = 2 * 8;
            task_cons[i]->tss.ss = 1 * 8;
            task_cons[i]->tss.ds = 1 * 8;
            task_cons[i]->tss.fs = 1 * 8;
            task_cons[i]->tss.gs = 1 * 8;
            *((int *) (task_cons[i]->tss.esp + 4)) = (int) sht_cons[i];
            *((int *) (task_cons[i]->tss.esp + 8)) = memtotal;
            task_run(task_cons[i], 2, 2); /* level=2, priority=2 */
            sht_cons[i]->task = task_cons[i];
            sht_cons[i]->flags |= 0x20;
        }
        ……
        sheet_slide(sht_back,  0,  0);
        sheet_slide(sht_cons[1], 56,  6); /*命令行2窗口位置*/
        sheet_slide(sht_cons[0],  8,  2); /*命令行1窗口位置*/
        sheet_slide(sht_win,  64, 56);
        sheet_slide(sht_mouse, mx, my);
        sheet_updown(sht_back,     0);
        sheet_updown(sht_cons[1],  1); /*命令行2的圖層高度是1*/
        sheet_updown(sht_cons[0],  2); /*命令行1的圖層高度是2*/
        sheet_updown(sht_win,      3);
        sheet_updown(sht_mouse,    4);
        key_win = sht_win;
        ……
        for (;;) {
            ……
            io_cli();
            if (fifo32_status(&fifo) == 0) {
                ……
            } else {
                ……
                if (256 <= i && i <= 511) { /* 鍵盤數據 */
                    ……
                    if (i == 256 + 0x3b && key_shift != 0 && task_cons[0]->tss.ss0 != 0) {	/* Shift+F1 */
                        cons = (struct CONSOLE *) *((int *) 0x0fec);
                        cons_putstr0(cons, "\nBreak(key) :\n");
                        io_cli();
                        task_cons[0]->tss.eax = (int) &(task_cons[0]->tss.esp0);
                        task_cons[0]->tss.eip = (int) asm_end_app;
                        io_sti();
                    }
                    ……
                } else if (512 <= i && i <= 767) { /* 鼠標數據 */
                    if (mouse_decode(&mdec, i - 512) != 0) {
                        ……
                        if ((mdec.btn & 0x01) != 0) {
                            /* 按下左鍵 */
                            if (mmx < 0) {
                                for (j = shtctl->top - 1; j > 0; j--) {
                                    ……
                                    if (0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize) {
                                        if (sht->buf[y * sht->bxsize + x] != sht->col_inv) {
                                            ……
                                            if (sht->bxsize - 21 <= x && x < sht->bxsize - 5 && 5 <= y && y < 19) {
                                                if ((sht->flags & 0x10) != 0) {		
                                                    cons = (struct CONSOLE *) *((int *) 0x0fec);
                                                    cons_putstr0(cons, "\nBreak(mouse) :\n");
                                                    io_cli();	
                                                    task_cons[0]->tss.eax = (int) &(task_cons[0]->tss.esp0);
                                                    task_cons[0]->tss.eip = (int) asm_end_app;
                                                    io_sti();
                                                }
                                            }
                                            break;
                                        }
                                    }
                                }
                            } else {
                                ……
                            }
                        } else {
                            ……
                        }
                    }
                } else if (i <= 1) { /* 光標定時器 */
                    ……
                }
            }
        }
    }
    
    • 關於Shift+F1和“X”按鈕的地方,不加判斷直接寫cons[0]的原因是讓編譯器通過。代碼很快就會修復。所以在harib22e中千萬不要使用Shift+F1和“X”按鈕關閉應用程序。
  • make

    • 用VMware運行試試
      • 出現了兩個命令行窗口。可以實現窗口的切換。
    • 執行命令試試:
      • 好像可以執行命令。
    • 啓動應用程序試試(啓動a.hrb):
      • 在左邊的命令行窗口執行a.hrb,輸出好像到了右邊的命令行窗口裏去了。
      • 在右邊的命令行窗口執行a.hrb,輸出正常。
      • harib22e的輸出好像只能是一個命令行。

6. 增加命令行窗口(2)(harib22f)

  • harib22e出現問題的原因是hrb_api中的代碼:

    struct CONSOLE *cons = (struct CONSOLE *) *((int *) 0x0fec);
    
    • 從地址0xfec獲取cons變量,而cons是用來判斷“要向哪個命令行窗口輸出字符”的
    • 順便提一下:
      • 0xfe8:獲取ds_base
      • 0xfe4:獲取shtctl
    • cons變量的地址是從內存地址0xfec獲得的,而無論從哪個任務讀取這個內存地址的值,得到的肯定是一個相同的值,因此不管在那個窗口運行a.hrb,都只能在一個窗口中輸出。
    • 因此,需要爲不同的命令行窗口獲取不同的cons
      • 顯然,寫入內存的方法已經不行了。
      • 所以,只能修改數據結構。
  • 修改TASK結構體:

    struct TASK {
        int sel, flags; 
        int level, priority;
        struct FIFO32 fifo;
        struct TSS32 tss;
        struct CONSOLE *cons;
        int ds_base;
    };
    
    • 每個任務都有各自的TASK結構,因此,只需要將cons存放在TASK結構中,就能根據不同的任務讀取不同的cons了。
    • 將ds_base也放入到了TASK結構中,理由和cons類似。
      • ds_base之前是從0xfe8讀取的。顯然,cons[0]的應用程序的數據段地址和cons[1]的應用程序的數據段地址是不同的。如果不加以區分,字符串的顯示就會出問題。
  • 修改console.c:

    void console_task(struct SHEET *sheet, int memtotal)
    {
        ……
        task->cons = &cons; /*保存cons*/
        ……
    }
    
    int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline)
    {
        ……
        if (finfo != 0) {
            ……
            if (finfo->size >= 36 && strncmp(p + 4, "Hari", 4) == 0 && *p == 0x00) {
                ……
                task->ds_base = (int) q; /*記錄應用程序數據段地址*/
                ……
            } else {
                cons_putstr0(cons, ".hrb file format error.\n");
            }
            ……
        }
        return 0;
    }    
    
    int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax)
    {
        struct TASK *task = task_now();
        int ds_base = task->ds_base;
        struct CONSOLE *cons = task->cons;
        ……
    }
    
    int *inthandler0c(int *esp)
    {
        struct TASK *task = task_now();
        struct CONSOLE *cons = task->cons;
        ……
    }
    
    int *inthandler0d(int *esp)
    {
        struct TASK *task = task_now();
        struct CONSOLE *cons = task->cons;
        ……
    }
    
  • make後用VMware運行:

    • 在左邊和右邊的命令行中執行應用程序a.hrb都正確地顯示在了各自的窗口中。
    • 且實現了同時運行兩個應用程序的功能(color.hrb和color2.hrb)。
    • 注意:此時退出color.hrb和color2.hrb不能使用Shift+F1和“X”按鈕。可以使用回車退出。
      • 不好,使用回車鍵退出應用程序color.hrb時怎麼產生了一般保護性異常?
    • 接下來就要使應用程序能夠用Enter正常退出。

7. 增加命令行窗口(3)(harib22g)

  • harib22f在退出應用程序時產生一般保護性異常的原因是:應用程序的內存段突然消失了。

    • 問題出在cmd_app:
      set_segmdesc(gdt + 1003, finfo->size - 1, (int) p, AR_CODE32_ER + 0x60);
      set_segmdesc(gdt + 1004, segsiz - 1,      (int) q, AR_DATA32_RW + 0x60);
      ……
      start_app(0x1b, 1003 * 8, esp, 1004 * 8, &(task->tss.esp0));
      
    • 首先,color.hrb在某個窗口運行,啓動程序一切順利,然後顯示窗口並繪圖,接下來等待鍵盤輸入並進入休眠狀態。
    • 然後在另一個命令行窗口啓動了color2.hrb,程序順利啓動、顯示窗口並繪圖,隨後進入休眠狀態。
    • 其實現在就已經產生了問題,那就是明明是兩個應用程序,卻共用了數據段和代碼段。後運行的color2.hrb已經把先運行的color.hrb的內存段覆蓋了。因而再使用Enter喚醒color.hrb時,結果錯誤地運行了color2.hrb,顯然,當應用程序試圖訪問其他應用程序的內存段時就會產生0x0d中斷。
    • 因此,只需要爲不同的應用程序分配不同的內存段即可
  • 修改cmd_app:

    int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline)
    {
        ……
        if (finfo != 0) {
            ……
            if (finfo->size >= 36 && strncmp(p + 4, "Hari", 4) == 0 && *p == 0x00) {
                ……
                set_segmdesc(gdt + task->sel / 8 + 1000, finfo->size - 1, (int) p, AR_CODE32_ER + 0x60);
                set_segmdesc(gdt + task->sel / 8 + 2000, segsiz - 1,      (int) q, AR_DATA32_RW + 0x60);
                ……
                start_app(0x1b, task->sel + 1000 * 8, esp, task->sel + 2000 * 8, &(task->tss.esp0));
                ……
            } else {
                cons_putstr0(cons, ".hrb file format error.\n");
            }
            ……
        }
        return 0;
    }
    
    • task->sel代表的是寫入TSS的段號*8。因此task->sel/8就代表段號,段號的範圍是31002。將其加上1000,得到的範圍是10032002,將這個task->sel/8 + 1000作爲應用程序的代碼段編號。
      • 注意:GDT的1號是OS的數據段,GDT的2號是OS的代碼段。3~1002是多任務使用的(共1000個)。
    • 同理,task->sel/8 + 2000就代表應用程序用的數據段編號。
    • 一個任務只能運行一個應用程序。
  • make後用VMware運行:

    • 能夠使用Enter讓color.hrb和color2.hrb正常退出。
    • 接下來接下來就要使應用程序能夠用Shift+F1和“X”按鈕退出。

8. 增加命令行窗口(4)(harib22h)

  • 修改bootpack.c中的HariMain:

    void HariMain(void)
    {
        ……
        struct TASK *task_a, *task_cons[2], *task; /*task用於指向正在運行的任務*/
        ……
        for (;;) {
            ……
            if (fifo32_status(&fifo) == 0) {
                ……
            } else {
                ……
                if (256 <= i && i <= 511) { /* 鍵盤數據 */
                    ……
                    if (i == 256 + 0x3b && key_shift != 0) {
                        task = key_win->task; /*獲取當前處於輸入狀態的窗口指向的task*/
                        if (task != 0 && task->tss.ss0 != 0) {	/* Shift+F1 */
                            cons_putstr0(task->cons, "\nBreak(key) :\n");
                            io_cli();
                            task->tss.eax = (int) &(task->tss.esp0);
                            task->tss.eip = (int) asm_end_app;
                            io_sti();
                        }
                    }
                   ……
                } else if (512 <= i && i <= 767) { /* 鍵盤數據 */
                    if (mouse_decode(&mdec, i - 512) != 0) {
                        ……
                        if ((mdec.btn & 0x01) != 0) {
                            /* 鼠標左鍵按下 */
                            if (mmx < 0) {
                                for (j = shtctl->top - 1; j > 0; j--) {
                                    ……
                                    if (0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize) {
                                        if (sht->buf[y * sht->bxsize + x] != sht->col_inv) {
                                            ……
                                            if (sht->bxsize - 21 <= x && x < sht->bxsize - 5 && 5 <= y && y < 19) {
                                                /* “X”按鈕 */
                                                if ((sht->flags & 0x10) != 0) {		/* 是否是應用程序的窗口 */
                                                    task = sht->task;
                                                    cons_putstr0(task->cons, "\nBreak(mouse) :\n");
                                                    io_cli();	
                                                    task->tss.eax = (int) &(task->tss.esp0);
                                                    task->tss.eip = (int) asm_end_app;
                                                    io_sti();
                                                }
                                            }
                                            break;
                                        }
                                    }
                                }
                            } else {
                                ……
                            }
                        } else {
                            ……
                        }
                    }
                } else if (i <= 1) { /* 光標定時器 */
                    ……
                }
            }
        }
    }
    
    • 就是將原來是task_cons[0]的地方改成key_win->tasksht->task,這樣,用鍵盤強制結束(Shift+F1)時會根據當前處於輸入狀態的窗口進行結束,當點擊“X”按鈕時,會以被點擊的窗口進行結束。
  • make後用VMware運行:

    • 可以使用Shift+F1和“X”按鈕結束應用程序了。
    • 至此,完成了增加命令行窗口的任務。

9. 變得更像真正的操作系統(1)(harib22i)

  • Windows和其他OS中可沒有向task_a這樣的窗口。task_a窗口不是應用程序,而是操作系統的一部分,連關都關不掉。

    • 因此,現在就讓task_a消失吧!如果哪天突然想念task_a窗口了,只要寫一個應用程序出來就好了。而且應用程序的話,可以隨時關掉呢(笑)。
    • 在bootpack.c中,只有task_a這個窗口時搞特殊化的,刪掉“搞特殊化”的task_a的窗口。
    • 只是刪除task_a的窗口,而不是刪除task_a,task_a運行的是HariMain,是不可以關掉的
  • 修改bootpack.c中的HariMain:

    void HariMain(void)
    {
        ……
        int mx, my, i; /*刪掉了cursor_x和cursor_c*/
        unsigned int memtotal;
        struct MOUSE_DEC mdec;
        struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
        unsigned char *buf_back, buf_mouse[256], *buf_cons[2]; /*刪掉了buf_win*/
        struct SHEET *sht_back, *sht_mouse, *sht_cons[2];
        struct TASK *task_a, *task_cons[2], *task;  
        /*刪掉了用於光標閃爍的timer*/
        static char keytable0[0x80] = { /*向keytable0和keytable1中追加了退格鍵和回車鍵的編碼*/
            0,   0,   '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '^', 0x08, 0,
            'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '@', '[', 0x0a, 0, 'A', 'S',
            ……
        };
        static char keytable1[0x80] = {
            0,   0,   '!', 0x22, '#', '$', '%', '&', 0x27, '(', ')', '~', '=', '~', 0x08, 0,
            'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '`', '{', 0x0a, 0, 'A', 'S',
            ……
        };
        ……
        /* sht_back */
        ……
    
        /* sht_cons */
        ……
    
        /*刪掉了sht_win的相關部分*/
    
        /* sht_mouse */
        ……
        sheet_slide(sht_back,  0,  0);
        sheet_slide(sht_cons[1], 56,  6);
        sheet_slide(sht_cons[0],  8,  2);
        /*刪掉了sht_win*/
        sheet_slide(sht_mouse, mx, my);
        sheet_updown(sht_back,     0);
        sheet_updown(sht_cons[1],  1);
        sheet_updown(sht_cons[0],  2);
        sheet_updown(sht_mouse,    3);
        key_win = sht_cons[0]; /*將當前輸入狀態窗口變成命令行1*/
        keywin_on(key_win);
        ……
        for (;;) {
            ……
            if (fifo32_status(&fifo) == 0) {
                ……
            } else {
               ……
                if (256 <= i && i <= 511) { /* 鍵盤數據 */
                    ……
                    if (s[0] != 0) { /* 一般字符,退格鍵,回車鍵 */
                        fifo32_put(&key_win->task->fifo, s[0] + 256);
                    }
                    if (i == 256 + 0x0f) {	/* Tab */
                        keywin_off(key_win);
                        j = key_win->height - 1;
                        if (j == 0) {
                            j = shtctl->top - 1;
                        }
                        key_win = shtctl->sheets[j];
                        keywin_on(key_win);
                    }
                    ……
                    /*刪掉了重新顯示光標的部分*/
                } else if (512 <= i && i <= 767) { /* 鼠標數據 */
                    if (mouse_decode(&mdec, i - 512) != 0) {
                        ……
                        if ((mdec.btn & 0x01) != 0) {
                            /* 左鍵按下 */
                            if (mmx < 0) {
                                for (j = shtctl->top - 1; j > 0; j--) {
                                    ……
                                    if (0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize) {
                                        if (sht->buf[y * sht->bxsize + x] != sht->col_inv) {
                                            sheet_updown(sht, shtctl->top - 1);
                                            if (sht != key_win) {
                                                keywin_off(key_win);
                                                key_win = sht;
                                                keywin_on(key_win);
                                            }
                                            ……
                                        }
                                    }
                                }
                            } else {
                                ……
                            }
                        } else {
                            ……
                        }
                    }
                }
                /*刪掉了光標定時器部分*/
            }
        }
    }
    
    • 修改keywin_off和keywin_on:
      void keywin_off(struct SHEET *key_win)
      {
          change_wtitle8(key_win, 0);
          if ((key_win->flags & 0x20) != 0) {
              fifo32_put(&key_win->task->fifo, 3); 
          }
          return;
      }
      
      void keywin_on(struct SHEET *key_win)
      {
          change_wtitle8(key_win, 1);
          if ((key_win->flags & 0x20) != 0) {
              fifo32_put(&key_win->task->fifo, 2); 
          }
          return;
      }
      
    • 這樣修改,少了70行代碼,清爽了不少。
  • make後用VMware運行試試:

    • 貌似兩個命令行窗口的光標都沒顯示啊?
    • 這裏並沒有出現書上說說不斷重啓,無法操作的現象。相反,點幾下兩個命令行窗口就恢復了。

10. 變得更像真正的操作系統(2)(harib22j)

  • harib22i出現上述問題的原因是沒有對緩衝區進行初始化。

  • 因爲命令行窗口的優先級比較低,只有當bootpack.c中的HariMain(task_a)休眠之後纔會運行命令行窗口,如果不運行命令行任務的話,console_task就不會執行,命令行任務的FIFO緩衝區就不會被初始化,這就是爲什麼剛啓動時沒有命令行窗口光標的原因。

  • 因此,只需要將console_task中對命令行緩衝區初始化的代碼移動到bootpack.c中去就行了,修改bootpack.c中的HariMain:

    void HariMain(void)
    {
        ……
        int fifobuf[128], keycmd_buf[32], *cons_fifo[2]; /*cons_fifo代表緩衝區*/
        ……
        /* sht_cons */
        for (i = 0; i < 2; i++) {
            ……
            sht_cons[i]->task = task_cons[i]; 
            sht_cons[i]->flags |= 0x20;	
            cons_fifo[i] = (int *) memman_alloc_4k(memman, 128 * 4); /*爲緩衝區分配內存空間*/
            fifo32_init(&task_cons[i]->fifo, 128, cons_fifo[i], task_cons[i]); /*初始化緩衝區*/
        }  
    }
    
  • 修改console.c中的console_task:

    void console_task(struct SHEET *sheet, int memtotal)
    {
        struct TASK *task = task_now();
        struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
        int i, *fat = (int *) memman_alloc_4k(memman, 4 * 2880); /*刪掉了fifobuf[128]*/
        struct CONSOLE cons;
        char cmdline[30];
        cons.sht = sheet;
        cons.cur_x =  8;
        cons.cur_y = 28;
        cons.cur_c = -1;
        task->cons = &cons;
    
        /*刪掉了fifo32_init(&task->fifo, 128, fifobuf, task)*/
        cons.timer = timer_alloc();
        timer_init(cons.timer, &task->fifo, 1);
        ……
    }
    
  • make後用VMware運行:

    • 剛啓動就有光標在命令行上閃爍。

11. 寫在5.1和5.2

  • 現在是2020.5.2 14:04。
  • 昨晚終於下雨了,幾近一月沒有下雨的家鄉,已經乾涸地像一塊乾癟的牛肉。
  • 真是春雨貴如油,趁着剛下完的雨,爸媽趕緊去種花生了。
  • 昨日,去參加了喜宴,沒太寫文檔,到今日才完成昨日應該完成的任務,真是羞赧萬分啊。
  • 就要走到黑暗盡頭了,衝刺穿過最後的荊棘吧!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章