自作OSに挑戦する日記 23日目
「30日でできる!OS自作入門」を読んで分かったことや、とりあえず書いておきたいことなどを書いていきます。
この本はChapterが1から30まであるので、各チャプター毎に1記事書いていきます。
Chapter 23 「グラフィックいろいろ」
今回やった内容
- malloc
- グラフィックいろいろ
- キー入力
環境
- メインPC
- MacBook Pro (13-inch, 2017, Four Thunderbolt 3 Ports)
- macOS Mojave 10.14
- 自作OSを動作させる環境
作業記録
malloc
アプリ用のmallocを作ります。
OS用のMEMMANで確保されたメモリにアプリからアクセスすることは出来ないので、アプリのデータセグメントを別のMEMMANで管理するようにします。
本では BIN2HRB
を使ってmalloc用の領域を指定する…とありますが、今の状態では使えないので har.ld
を変更して少し多めにアプリのセグメントを準備するようにしておきます。
/* api.c */ if(edx == 8){ // memman初期化 memman_init((struct MEMMAN *) (ebx + ds_base)); ecx &= 0xfffffff0; memman_free((struct MEMMAN *) (ebx + ds_base), eax, ecx); return 0; } if(edx == 9){ // malloc ecx = (ecx + 0x0f) & 0xfffffff0; reg[7] = memman_alloc((struct MEMMAN *) (ebx + ds_base), ecx); return 0; } if(edx == 10){ // mfree ecx = (ecx + 0x0f) & 0xfffffff0; memman_free((struct MEMMAN *) (ebx + ds_base), eax, ecx); return 0; }
/* api_link_obj.asm */ api_init_memman: ; api_init_memnan() PUSH EBX MOV EDX, 8 MOV EBX, [CS:0x0020] MOV EAX, EBX ADD EAX, 32*1204 MOV ECX, [CS:0x0000] SUB ECX, EAX INT 0x40 POP EBX RET api_malloc: ; api_malloc(int size) PUSH EBX MOV EDX, 9 MOV EBX, [CS:0x0020] MOV ECX, [ESP + 8] INT 0x40 POP EBX RET api_mfree: ; api_mfree(char *addr, int size) PUSH EBX MOV EDX, 10 MOV EBX, [CS:0x0020] MOV EAX, [ESP + 8] MOV ECX, [ESP + 12] INT 0x40 POP EBX RET
初期化する際にEAXに 32 * 1024
を足していますが、これはMEMMAN構造体用です。MEMMAN構造体を使うときにアドレスしか渡していませんが、こうすると指定したアドレス以降の必要な領域を自動で計算して使ってくれます。(最初ちょっと混乱しましたが…)
これでアプリ用のmallocが完成しました!
グラフィックいろいろ
これからとにかく実装するだけです。OSに既にある関数をAPIを通してアプリから呼び出せるようにします。
点を描画する
ESIにX座標、EDIにY座標、EAXに色番号を指定します。
/* api.c */ if(edx == 11){ // ウィンドウに点を描画する struct SHEET *sheet = (struct SHEET *) (ebx & 0xfffffffe); sheet->buf[sheet->buf_width * edi + esi] = eax; return 0; }
ウィンドウリフレッシュ
EAX/ECX/ESI/EDI に x0/y0/x1/y1 を指定します。
/* api.c */ if(edx == 12){ // ウィンドウリフレッシュ struct SHEET *sheet = (struct SHEET *) ebx; sheet_refresh(sheet, eax, ecx, esi, edi); return 0; }
リフレッシュ用APIが完成したので、既に実装済みのいくつかのグラフィックAPIを変更します。どのような変更かというと、EBXのSHEETアドレスが奇数ならリフレッシュを自動で行わないようにします。SHEETアドレスは必ず偶数になるのでその性質を使います。0xfffffffe
とANDを取ることで下位1ビットのみを取り出し、そのビットが1かどうかで偶奇を判定しています。
struct SHEET *sheet = (struct SHEET *) (ebx & 0xfffffffe); if(ebx & 0xfffffffe == 0){ sheet_refresh(sheet, eax, ecx, esi + 1, edi + 1); }
線を描画する
線を描画する関数は新しく作ります。x0/y0/x1/y1 から傾きを求めて、線を描画するようになっています。傾きを求める際、小さな数を擬似的に扱うため最初にXとYの値を 1024
倍しています。使うときは 10
ビット右にシフトすることで 1024
で割って元の数に戻しています。単純に 1000
倍するのではなくシフト演算を使うことで描画スピードを上げています。
/* graphic.c */ void drawline(struct SHEET *sheet, int x0, int y0, int x1, int y1, int color){ int dx = x1 - x0; int dy = y1 - y0; int x = x0 << 10; int y = y0 << 10; int len = 0; if(dx < 0) dx = -dx; if(dy < 0) dy = -dy; // 変化量設定 // (2 * (bool) - 1) => boolがtrueなら1, falseなら-1を返す if(dy <= dx){ len = dx + 1; dx = 1024 * (2 * (x0 < x1) - 1); dy = (y1 - y0 + (2 * (y0 <= y1) - 1) << 10) / len; }else{ len = dy + 1; dy = 1024 * (2 * (x0 < x1) - 1); dx = (x1 - x0 + (2 * (x0 <= x1) - 1) << 10) / len; } for(int cnt = 0; cnt < len; ++ cnt){ sheet->buf[sheet->buf_width * (y >> 10) + (x >> 10)] = color; x += dx; y += dy; } }
ウィンドウを閉じる
sheet_freeを呼びます!!楽!!
/* api.c */ if(edx == 14){ // ウィンドウを閉じる sheet_free((struct SHEET *) ebx); return 0; }
ちゃんと動きます!綺麗…
wow pic.twitter.com/JEnfbmbJ9K
— ゆん (@yn0014) 2019年5月5日
グラフィック関連APIはとにかく実装…という感じなので疾走感があります。
キー入力
アプリがキー入力を受け取れるようにします。実装は特に難しくなく、FIFOバッファをチェックしてデータが存在するなら返す…とするだけです。オプションでキー入力がされるまで待つか、1回だけFIFOバッファを確認してすぐ結果を返すかを選べるようにしています。もしキー入力までずっと待機する場合は、カーソルタイマーなどの処理を間時間に行うようにします。
/* api.c */ if(edx == 15){ // キー入力 while(1){ // FIFO確認 io_cli(); if(fifo32_status(&task->fifo) == 0){ if(eax != 0){ task_sleep(task); }else{ io_sti(); reg[7] = -1; return 0; } } // キー入力を待っている間は他のことをしたり… int data = fifo32_get(&task->fifo); io_sti(); if(data <= 1){ // タイマー timer_init(console->cursor_timer, &task->fifo, 1); timer_set(console->cursor_timer, 50); }else if(data == 2){ // カーソルON console->cursor_color = COL8_FFFFFF; }else if(data == 3){ // カーソルOFF console->cursor_color = -1; }else if(256 <= data && data <= 512){ // キー入力だった reg[7] = data - 256; return 0; } } }
前回の方法と同じように、スタックの値を書き換えることによって値を返しています。
…ということでアプリでキー入力を受け取れるようになったので簡単なゲーム(?)らしきものを作りました。23日目完成です!
23日目〜! pic.twitter.com/2y3bGrDluV
— ゆん (@yn0014) 2019年5月5日
(動画内でちょっとだけ出ていますが、強制終了したときにもウィンドウが閉じられるようになっています)
まとめ
グラフィック関連のAPIが充実してきました。キー入力も受け取れるになったので簡単なゲームならもう作れるようになった…ような気がします。多分作れます。
自作OS本もあと1週間になりました。終わったらどんな風に改造していくか…楽しみです。