三十天自制操作系统(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阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容