自作OSに挑戦する日記 25日目

 「30日でできる!OS自作入門」を読んで分かったことや、とりあえず書いておきたいことなどを書いていきます。
 この本はChapterが1から30まであるので、各チャプター毎に1記事書いていきます。

Chapter 25 「ウィンドウ操作」

今回やった内容

  • BEEP音再生
  • 使える色を増やす
  • 複数のコンソールウィンドウ

環境

作業記録

BEEP音再生

 BEEP音を再生できるようにします。アプリからも使えるようにしたいのでAPIにも登録します。
 BEEP音の制御にはPIT(Programmable Interval Timer)を使います。タイマを使えるようにした時以来ですが、なんでBEEP制御はPITを使ってやるんでしょうか…?ざっくり調べてみるとどうやら発振機が関係しているみたいです。BEEP音再生はシステムスピーカーを発振させて行うので、それを制御するためにPITに仕事が割り振られたのでしょうか(本当か?)。

 PITを制御するためにはIN/OUT命令を使います。

/* api.c */

if(edx == 20){      // BEEP音再生
    if(eax == 0){
        int beep_conf = io_in8(0x61);
        io_out8(0x61, beep_conf & 0x0d);
    }else{
        int beep_conf = 1193180000 / eax;
        io_out8(0x43, 0xb6);
        io_out8(0x42, beep_conf & 0xff);
        io_out8(0x42, beep_conf >> 8);
        beep_conf = io_in8(0x61);
        io_out8(0x61, (beep_conf | 0x03) & 0x0f);
    }
}

 EAXレジスタに0が入った状態でBEEP再生APIが呼ばれるとBEEP再生を停止するようになっています。それ以外は指定された周波数のBEEPを再生します。


 早速試してみたいのですが、QEMUではBEEP音のエミュレートが出来ないため出来ないみたいです(実際できなかった)…。自由に使える実機が舞い込んできたら試してみようと思います。

使える色を増やす

 今の動作モードでは最大256色まで使えるようにできるのですが、まだ16色しか登録していません。アプリからもウィンドウを扱えるようになったので、もう少し色を登録します。RGBそれぞれ6段階の階調を持たせた216色を新しく追加します。

/* graphic.c */

// +216色
unsigned char table_rgb_alpha[216 * 3];
for(int b = 0; b < 6; ++ b){
    for(int g = 0; g < 6; ++ g){
        for(int r = 0; r < 6; ++ r){
            table_rgb_alpha[(r + g * 6 + b * 36) * 3 + 0] = r * 51;
            table_rgb_alpha[(r + g * 6 + b * 36) * 3 + 1] = g * 51;
            table_rgb_alpha[(r + g * 6 + b * 36) * 3 + 2] = b * 51;
        }
    }
}
set_palette(16, 231, table_rgb_alpha);

 新しく増えた色は 16 + R + G * 6 + B * 36 をVRAMにセットすることで使うことができます(16は既に登録されていた数、RGBはそれぞれ0 ~ 6段階で設定)。


 …ということでアプリを作って動かしてみました。

f:id:guguru0014:20190514223705p:plain
colorapp

 綺麗ですね!


 これで使える色が格段に増えたのですが、もっと増やす方法があるみたいのなので実装します。以下の画像にように、異なる色を交互に配置することで擬似的に色を増やすことができます。それぞれの色について3色ずつ中間色は増えるので '(6 + 5 * 3) ^ 3` で計9261色表現できるようになります(!)。

 

f:id:guguru0014:20190514224012j:plain
中間色

 …実際に動かしてみるとこのようになります。

 colorapp 1 と比べると色の移り変わりがとても綺麗です!

コンソールを増やす

 コンソールを増やします。…今まではコンソールが1つしか存在しなかったのでアプリの複数起動が出来ませんでした。今の実装だと、コンソールが増えれば増えるほどアプリを複数起動することができます。

コンソールタスク・コンソールウィンドウを複数管理できるように

 今まではコンソールタスクやウィンドウ描画のための領域を1つしか用意していなかったため、これを配列にして複数扱えるようにします。

/* bootpack.c */

unsigned char *buf_console[2];
struct TASK *task_console[2];
int *fifo_console[2];

for(int idx = 0; idx < 2; ++ idx){
    sheet_console[idx] = sheet_alloc(sheet_ctl);
    buf_console[idx] = (unsigned char *) memman_alloc_4k(memman, 400 * 300);
    sheet_setbuf(sheet_console[idx], buf_console[idx], 400, 290, -1);
    task_console[idx] = task_alloc();

    ~タスクに関しての設定など~
}

出力対応

 タスクやウィンドウを増やしただけでは上手く動きません。コンソールへ出力を行う処理が決め打ちで行われていたので修正する必要があります。
 タスクを管理する構造体でコンソールの情報を管理するようにし、出力はこの情報を使って行うことにします。データ読み込みのために必要なセグメント情報も、合わせてタスク構造体で管理するようにします。

/* bootpack.h */
struct TASK{
    int sel, flags;
    int level, priority;
    struct FIFO32 fifo;
    struct TSS32 tss;
    struct CONSOLE *console;
    int ds_base;
};

/* bootpack.c */
int *haribote_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 *console = task->console;

    ~~APIの処理~~
}

複数アプリを実行できるように

 アプリを実行するときに設定するセグメント番号も決め打ちで行なっていたため修正します。TSSのセグメント番号を元に設定するようにしました。

/* console.c  */

// セグメント設定
// アクセス権に0x60を足すとアプリケーション用のセグメントになる
task->ds_base = (int) app_mem_addr;
set_segmdesc(gdt + task->sel / 8 + 1000, file_info->size - 1, (int) exec_addr, AR_CODE32_ER + 0x60);
set_segmdesc(gdt + task->sel / 8 + 2000, seg_size - 1, (int) app_mem_addr, AR_DATA32_RW + 0x60);

アプリ強制終了

 アプリを強制終了させるための処理も決め打ちで行なっていたため修正します。key_to_window には有効になっているタスクの情報が格納されていて、もしそのタスクがコンソールで、かつアプリが実行中なら強制終了処理を実行するようになっています。

/* bootpack.c */

// コンソール上のアプリケーションを強制終了する(backspace + shift)
if(data == 256 + 0x0e && key_shift){
    struct TASK *task = key_to_window->task;
    if(task != 0 && task->tss.ss0 != 0){
        // 強制終了割り込み表示
        struct CONSOLE *console = task->console;
        console_putstr(console, "\nKeyboard Interrupt\n");

        // end_appに飛ぶようにする
        io_cli();
        task->tss.eax = (int) &(task->tss.esp0);
        task->tss.eip = (int) end_app;
        io_sti();
        continue;
    }
}

 …修正に修正を重ねて複数コンソール完成しました!

 timerapp や colorapp の複数起動が出来ています!25日目完成!
 入出力処理や強制終了処理もバグなく動いています。

まとめ

 コンソールを増やすことが出来ました〜!表示しているウィンドウは2つだけですが、やろうと思えば何個でも立ち上げることが出来るようになっています。

 OS自作本も終盤に近づいてきました。どこまで行けるか楽しみです!(改造はまたその後で…)