個人用雑記

勉強したことを書いていければなーと

30日でできる!OS自作入門(Day 5)

またもや

大分Day 4から期間が空いてしまった・・・今回は完全に怠慢で、本自体はもう6日目の半分くらいまでは読み終わってたり。

構造体

さすがに構造体はすでに勉強してるし、わかっているつもりでいるのでここは流し読み。ただ、はじめ(*binfo).scrnxのようにポインタから参照する書き方で、これで教えるの??と思っていたらその次ページにちゃんとbinfo->scrnxの説明があった。さすがに矢印表記も教えるよね・・・

構造体といえば有名なアレ

※自分の知識を述べているだけなので正確性の保証は0。真相が気になったら調べてください。
OS自作入門とは本質的に何も関係ないけど、例えば下記のコードのような二つの構造体SizeTest1とSizeTest2があった場合、

/*前提としてintは4バイトcharは1バイトとする*/
typedef struct{
    int hoge;
    char fuga;
} SizeTest1;

typedef struct{
    int hoge;
    char fuga1, fuga2, fuga3, fuga4;
} SizeTest2;

上記二つの構造体のサイズはそれぞれどうなるかってお話。直感で言えばintは4バイト、charは1バイトなんだから上は5バイトで下は8バイトのように見えるけど、試しに↓こんなコードで試してみると、

#include<bits/stdc++.h>
using namespace std;

typedef struct{
    int hoge;
    char fuga;
} SizeTest1;

typedef struct{
    int hoge;
    char fuga1, fuga2, fuga3, fuga4;
} SizeTest2;

int main(void){
    cout << sizeof(SizeTest1) << endl;
    cout << sizeof(SizeTest2) << endl;
}

出力結果はどっちも8になる。
こうなる原因は探せばいくらでも出てくると思うけど、アライメント的に4バイトのintと1バイトのcharを一緒の構造体に入れると、大きいサイズの4バイトを基準にしてメモリを取るから、charみたいに1バイトだけつかうと後ろの3バイト分には基本的にもう何も入れられなくてメモリの無駄遣いになりがちっていうお話でした。(逆に言えばSizeTest2みたいにchar4つ入れることはできるしメモリ的に無駄はない。)

だから↓みたいなコードはメモリを余分に使っちゃうから気をつけようね。

/*SizeTest1のサイズは8バイトではなく12バイト*/
typedef struct{
    char fuga1;
    int hoge;
    char fuga2, fuga3, fuga4;
} SizeTest1;

ちなみにこの処理は言語側の規定ではなくて処理系依存らしい。
だいぶ余計な文章が長くなったけど、本題に戻る。

文字出力

Day 4で色のついた四角形はだせるようになったわけだけど、四角形が出せたところで文字にはならないのでOSとしてはまだまだ。しかし、Day 2あたりでやったような文字出力はBIOSの力を借りたからできていただけで、今はもう使えない。文字自体は例えばAならAの形に添った図形と考えれば出力できなくはなさそう・・・
とはいえ毎回文字の形に合わせて図形を作ってその部分だけ色付けて出力なんてやっていられないので、それぞれの文字に対応した出力を先に作ってしまう=フォントを作ることになる。
話はそんなに難しくなくて、下記のように1画素ずつ考えて黒い所と白い所を別の記号にして後はそれに従って色をつけるようにするだけ。
f:id:rei_624:20190423215610p:plain

でもこの本はあくまでOSの自作がメインであってフォントの自作をしたいわけではないのでとりあえずはすでにあるフォントを流用。
そんな感じで文字でも文字列でも出力できるようになった。

変数の値の表示

え、ふつうにprintf("%d",a);のように書けばいいじゃんと考えてしまいがちだけど、printfはOS依存なのでOS自作では使うことができない。一方でsprintfはあくまで指定したメモリの番地に変数の値を指定した書式で文字列として格納するだけだからOSに関係なく用いることができる。(つまり変数の値を表示するにはsprintfと格納した値を表示する別の関数をセットで使う必要がある。)

マウスカーソルも表示できる

マウスカーソルも図形として書くことができるから文字と同じように画面に出力させられる。試しに付属のマウスを少し改変して出力してみた。
f:id:rei_624:20190423220400p:plain

画面の画素数がそもそも少ないからなんか大きく見えるのは気のせい。
一点気をつけなければいけないのは所詮マウスカーソルの形をした図形を出力しただけであって、マウスカーソルを移動させられるわけではないということ。それに今のままだと文字に被った時どっちが先に描画されるかで変わっちゃいそうだし・・・

GDTとIDT

Day 5の内容は実質これだけという気がしなくもない。ちなみに読むまでこの二つの単語はどっちも聞いたことすらなかった。
簡単に言えば(簡単な内容ではないのだけれど。)、GDTはセグメントの設定一覧でIDTは割り込み機能の設定一覧。。。いや、わからない。わからないのでここから数日間の内容を読みつつ理解できなかったらまた帰ってきて勉強しなおす形を取らざるを得なかった。具体例が出てきたらきっとわかる。

まず、セグメンテーション。各プログラムで毎回そのプログラムがメモリのどの番地に読み込まれるのか指定してたらマルチタスクにおいて制限ばかりだし、下手をしたらメモリの衝突まで起こる。そこでセグメンテーションという機能を用いると、メモリを切り分けてそれぞれの先頭アドレスを仮想的に0として扱えるようになる機能。これがあればどんなプログラムでも同じ書き方ができる。というかできなかったら怖くてプログラムなんてできない。
さてここでわからないことが、それぞれのプログラムで必要なメモリはばらばらなのに先にセグメントの一覧を作らなければならないのかという点。解決していない。わからない。また明日以降で・・・
というよりは初期化段階では中身が決まってなくてよくて、後から変更する形なのかな・・・わからない・・・

次に割り込み命令。先ほど出てきたマウスカーソルの移動やキーボードの入力等々は全部いつ起こるかわからないけどいつも入力があるか見るわけにはいかないから、この割り込み命令を使うことでそれぞれの入力に対応できるようになるという感じ。この内容は6日目にあるらしいのでまだ。

Day 6へ

こんな理解で先に進んでいいのだろうかという気しかしないけれど、本を読んでもこれ以上でもこれ以下でもない感じだし、進むしかなさそう。というか構造体の話に対してGDTとIDTの話が短すぎる。

余計な構造体の話を書いていたらいつもよりも長くなってしまった・・・