30日OS自作入門4日目(Win10)

初めに

今日は、画面表示するみたいです。やっとだー!

目次

本文

C言語からメモリに書き込みたい

画面描写を行いたいが、そのためには[VRAM]に書き込む必要があるため、アセンブリを介してメモリ操作を行っているそうです。
この際[VRAM]を利用して画面描写が目的なので、0x12340x56を代入したいわけです。また、この際32Bitモードに移行しているのでレジスタも32bitのものを使えるし、制限は少なくなっています。
次に、C言語の修正です。内容としては、0xa0000から0xaffffまでを15の値で埋め尽くして、埋め尽くし終わると、CPU停止命令を無限ループする感じですね

しましま模様

bootpack.cに少しの修正を施し、しましま模様にするようです。ここでは、i and 0x0fをすることで色の数の最大以内に収まるように、VRAMのアドレスを下位4桁以外0にして画素の色をパターンにしてるみたいです。すごいなぁ…こんな方法自分は思いつかない…
f:id:No000:20191023192734p:plain こんな感じ

ポインタへの挑戦

来た…C言語第一の難関…ポインタ…まぁアドレスですね。これで「C言語からメモリに書き込みたい」をC言語でもできるそうです。この際に注意するのが、メモリアドレスにいきなり入れようとしてもエラーを食らうわけで、その理由はアセンブリの時みたいに、レジスタ以外はちゃんとデータのサイズを伝えてあげないといけなかったからだそうです。
これでポインタで[VRAM]を操作できるようになるみたいです。

さらに詳しくポインタを

キャスト

アドレスにつける型のことBYTE[i]みたいな

ポインタ

p = (char )i;  ⇒  レジスタECXにiを代入しているということが行われている。
p = i & 0x0f;  ⇒  レジスタECXに格納されたメモリアドレスに代入を行っている。BYTE [p]
要は、レジスタにメモリアドレスを入れるかメモリアドレスにデータを入れるかの違い

変数の注意

char *p q;
これはpはポインタとしてqは1バイトの変数として定義されている

ポインタの応用(1)

pにあらかじめアドレスを代入して処理

ポインタの応用(2)

ポインタを配列っぽく(配列ではない)見せる省略表現
なのでp[i]をi[p]と表現することもできる

色番号設定

8ビットカラーモードだと255個しか色が使えないが、255個の色の中身は指定することができる、この機能が「パレット」呼ばれている機能を設定している。
他に書いてあることを箇条書きで書くと
staticでRESB命令の処理をDB命令にすることと
・unsignedで符号をなしに対しコンパイラが0xffを‐1と勘違いしないようにすること
・CPUからサウンドカードやLANカードにアクセスするにはCPUのポートを利用し、IN命令・OUT命令(C言語にはなし)で制御する
といったことを行っています。
また、パレットにアクセスする際に割り込み処理を一旦止めてあげなければいけないらしくCLI命令 とSTI命令を使い制御するみたいです。
この時、変更前のCFのデータが分からないので、変数EFLAGSにセットしSTI命令を実行するか確認しています 。 これらを利用し、EFLAGSのデータをEAX等のレジスタに格納したり、EFLAGSに格納できるようにしているみたいです。

次に、naskfunc.asmには、C言語でできない処理を書いてます。そして、新しい要素なのがPUSHFDとPOPFD、スタック構造になります。

実行結果
f:id:No000:20191027165944p:plain
色が変わった!

四角を描く(harib01g)

ここでは、画面に絵を描く方法が説明されていました。
haribote01gでの画面モードには、320×200=64000個もの画素が設定できるので、左上から右下に座標を(0,0)から設定する場合の[VRAM]のアドレスの求め方は
0xa0000 + x + y * 320 となります。これで1点の描写ができるようです。
この1点を、まずx方向に繰り返し線を作り、その線をy方向の逆向きに繰り返すことで四角を作ることができます。
流れ: ①1点の描写
②右方向へ繰り返し(線)
③下方向へ繰り返し(四角)
といった流れ
f:id:No000:20191029182656p:plain
キレイですなぁ

最後の仕上げ(harib01h)

OSっぽい画面に描写を行うらしい!
Harimainを指示された通りに変更すると
f:id:No000:20191029194115p:plain
おぉ…!

4日目の感想

めっちゃゆっくりな進み方だったけど、画面にOSっぽいものは表示された!あと、先の章をチラ見した時に21日目がめっちゃ面白そうだったのでまだまだ頑張ります!

アセンブリ

文法

[<レジスタ> + <定数>]

レジスタに入っているアドレスを足したり、引いたりして使用する際に、このように使える

MOV ECX,[ESP+4]

[INSTRSET <CPU名>]

これにより、16bitと32bitの違いにアセンブラが勘違いせずに済む

[INSTRSET 486]

$(忘れてたやつ)

その命令がある行までのプログラムのバイト数を教えてくれる

命令

RESB命令

NASM等だと指定の量のバイトをあけておいてという意味になり、NASKだと全部0で埋めることになる

CLI命令

割り込みフラグ(interrupt flag)を0にする命令

STI命令

割り込みフラグ(interrupt flag)を1にする命令

PUSHFD命令

EFLAGSの内容をダブルワードでスタックに格納する命令

POPFD命令

EFLAGSの内容をダブルワードでスタックから取り出す命令

レジスタ(EFLAGS)

FLAGSという16ビットのレジスタを32ビットに拡張したもの(64ビットはRFLAGS)で、キャリーフラグや割り込みフラグが詰まったレジスタ。基本は一つ一つ1bitで形成されている(IOPLが例外)
参考:Intel® 64 and IA-32 Architectures Software Developer ManualsのP74(2019年1月のやつ)

CF(キャリーフラグ)

算術演算が桁上げ(キャリー)桁下がり(ボローアウト)を生成するときに設定され、符号なし整数演算等で使用されます

PF(パリティフラグ)

結果の再開バイトに1bitが含まれている場合にセットされる

AF(調整フラグ)

CFのBCD演算バージョン

ZF(ゼロフラグ)

結果が0になったときにセットされる

SF(符号フラグ)

結果の最上位ビットに対応し、符号が負になったときにセットされる

TF(トラップフラグ)

デバッグモードでのシングルステップを行うときにセットされる、解除はクリア

IF(割り込み可能フラグ)

マスク(禁止)可能な割り込みに応答するように設定します、セットで割り込み可クリアで割り込み禁止

DF(方向フラグ)

ストリームの方向を制御(命令実行の流れを反転できる?)

OF(オーバーフローフラグ)

符号付き演算の結果がレジスタに格納できな程でかくなった場合にセット

IOPL(I/O特権レベルフィールド)

実行中のプログラムのI/O特権レベルを示し、他にも制限や変更する際の条件(CPL)があるらしいけど省きます

NT(ネクストタスクフラグ)

割り込みやタスクの流れ(チェーン)?を制御します。現在のタスクが以前に実行されたタスクにリンクされているときセットされます。
他にもありますけどこのくらいで…

C言語

文法

アセンブリ言語を関数として利用する際のレジスタ

C言語の仕様上「EAX、ECX、EDX」以外のレジスタは使えない、他のレジスタは値を参照することだけに使える

i++

i = i + 1;

for文

for文は、{}内の命令を繰り返し条件に従って繰り返す。

for (<変数の初期設定> ; <繰り返し条件> ; <条件チェック前の変数への指示>)
{
    asdc();
}

条件のカッコ内を(;;)にすることで無限ループができる

static

staticを付けることにより、a[3]をRESB命令で処理するのではなくDB命令で処理してくれるようになる。

unsigned

符号なしで処理してくれ!ということ、符号なしってこういう風に使うんだなぁって

演算命令
OR

特定のビットを1にするイメージ、変化させたい場所に1を変化なしの場所に0
1000 OR 0011 = 1011

AND

特定のビットを0にするイメージ、変化させたい場所に0を変化なしの場所に1
1111 AND 0011 = 0011

XOR

特定のビットを反転させるイメージ、変化させたい場所に1を変化なしの場所に0
1010 XOR 0011 = 1001

ポインタの場合の型

charBYTE
shortWORD
intDWORD

#define

プリプロセッサへの命令の一つ、使い道としては
・記号定数の定義
・関数マクロ
・条件付き取り込み
本では記号定数の定義として使用されてるっぽい。
参考

C言語のdefineについて

C言語のマクロの基本

ショーもないミス一覧

コンパイル通ったのに黒画面

VRAMの開始アドレスを0xa0000でなく、0xa000にしていた