三十天自制操作系統(13)

第25天

這本書的這一章一開始就講如果控制主板上的蜂鳴發專聲器發聲,看到這個我很興奮。因為到目前為止我還沒有用windows api或者自己寫程序讓電腦發出聲音,終于可以嘗試一下了,雖然不是聲卡發聲,只是最低級的蜂鳴發聲器,但是還是很興奮。

我們控制主板上的發聲器也和控制中斷處理器是一樣的,也是要使用in和out指令進行操作。

蜂鳴發聲器的打開和關閉:

  • 使用端口0x61控制
  • 打開:IN(AL, 0X61); AL |= 0X03; AL &= 0X0F; OUT(0X61, AL);
  • 關閉:IN(AL, 0X61); AL &= 0X0D; AL &= 0X0D; OUT(0X61, AL);

如果打開之后控制發聲器的發聲頻率:

  1. AL = 0XB6; OUT(0X43, AL);
  2. AL = 設定值的低8位; OUT(0X42, AL);
  3. AL = 設定值的高8位; OUT(0X42, AL);
  4. 當設定值為0時當作65536來處理
  5. 發聲的頻率為時鐘除以設定值,也就是說設定值為1000時相當于發也1.19318KHZ的聲音

api設計

  • edx = 20
  • eax = 聲音頻率(單位是mHz, 即毫HZ),當頻率為0時表示停止發聲
    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);
            io_out8(0x42, i >> 8);
            i = io_in8(0x61);
            io_out8(0x61, (i | 0x03) & 0x0f);
        }
    }


    _api_beep:          ; void api_beep(int tone);
        MOV     EDX,20
        MOV     EAX,[ESP+4]         ; tone
        INT     0x40
        RET

看一下應用程序如何寫:

    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.01秒 */
            if (api_getkey(1) != 128) {
                break;
            }
        }
        api_beep(0);
        api_end();
    }

關于聲音就到這里,接下來看看如何增加更多的顏色。

到目前為止我們只用了16程模式。我們使用的是顯卡的調色板模式,理論上有256種顏色,我們就想辦法讓操作系統支持更多的顏色。

剩下的240個顏色我們設置么顏色呢?調色板對應的是RGB,一共24位,我們給每個三原色中的一種分6個,那么一共就可以定義6 * 6 * 6 = 216種顏色

    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);

RGB取值分別為0, 51, 102, 153, 204, 255。這樣調色板設置好了之后如果要取(51, 102, 153)這個顏色的話就是137號。

將下來我們寫一個應用程序測試一下。

    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();
    }

我們現在只能在命令行窗口運行應用程序,但是命令行窗口只有一個,所以我們同時只能運行一個應用程序,那么多任務對于應用程序來說沒有任何意義。我們應用讓操作系統可以同時運行多個命令行以支行多個應用程序。

如果想要支持多個命令行窗口,那么就需要改造操作系統的程序結構。我們之前把每個命令行窗口的數據段和窗口數據存到操作系統的內存中。

    int ds_base = *((int *) 0xfe8);
    struct CONSOLE *cons = (struct CONSOLE *) *((int *) 0x0fec);

如果有多個命令行窗口的話這樣的方式就不能用了,反正要為每個命令行窗口創建一個任務,而每個任務有會維護一個TASK結構的數據,我們可以改造這個結構將這兩個值保存到這個變量中。

    struct TASK {
        int sel, flags; /* sel?íGDT?ì”????ì?±?? */
        int level, priority;
        struct FIFO32 fifo;
        struct TSS32 tss;
        struct CONSOLE *cons;
        int ds_base;
    };

之前我們在運行應用程序的時候,直接給應用程序分配段號,現在由于存在多個命令行窗口,可能會運行多個應用程序就不能用這樣的方式分配段號了。

    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);

接下來處理關閉應用程序時候的問題了。

    if (i == 256 + 0x3b && key_shift != 0) {
        task = key_win->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();
        }
    }

以上是按下鍵盤時關閉應用程序。

    if (sht->bxsize - 21 <= x && x < sht->bxsize - 5 && 5 <= y && y < 19) {
        /* ?點擊關閉按扭 */
        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();
        }
    }

以上是點擊關閉按鈕時關閉應用程序。這樣處理完之后就可以支持多個應用程序窗口了,為了寫程序方便,這本書就使用了操作系統運行時打開2個命令行窗口,估計接下來還會通過輸入命令運行命令行窗口的功能。

這個操作系統還有一個和其他操作系統不一樣的地方。一開機時候自動行打開一個窗口也就是task_a窗口。為了使操作系統更像個操作系統應該把這個窗口給取消了。

如果要取消這個窗口其實只要刪除跟運行這個窗口有關的代碼就行了,可以刪好多東西,刪除之后看看結果怎么樣結果是操作系統出錯了。原來的操作系統運行流程是這樣的:首先運行主程序,打開task_a窗口,然后把操作系統的焦點放在這個窗口,光標也在task_a窗口閃爍,直到用戶按下了tab或者用鼠標切換窗口之后才切換。但是現在不是了,現在我們想要讓光標直接在第一個打開的命令行窗口閃爍就出問題了。因了task_a的優先級要高,當task_a運行完之前就向命令行窗口發送光標閃爍的消息時候,命令行窗口連消息隊列都還沒有建立,所以出錯了。解決方法也很簡單,只要把命令行窗口建立消息隊列的程序語句放在發送消息之前就可以了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,578評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,701評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,691評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,974評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,694評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,026評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,015評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,193評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,719評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,442評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,668評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,151評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,846評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,255評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,592評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,394評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容