30日OS自作入門4日目(Win10)
初めに
今日は、画面表示するみたいです。やっとだー!
目次
本文
C言語からメモリに書き込みたい
画面描写を行いたいが、そのためには[VRAM]
に書き込む必要があるため、アセンブリを介してメモリ操作を行っているそうです。
この際[VRAM]
を利用して画面描写が目的なので、0x1234
に0x56
を代入したいわけです。また、この際32Bitモードに移行しているのでレジスタも32bitのものを使えるし、制限は少なくなっています。
次に、C言語の修正です。内容としては、0xa0000
から0xaffff
までを15の値で埋め尽くして、埋め尽くし終わると、CPU停止命令を無限ループする感じですね
しましま模様
bootpack.c
に少しの修正を施し、しましま模様にするようです。ここでは、i and 0x0f
をすることで色の数の最大以内に収まるように、VRAMのアドレスを下位4桁以外0にして画素の色をパターンにしてるみたいです。すごいなぁ…こんな方法自分は思いつかない…
こんな感じ
ポインタへの挑戦
来た…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、スタック構造になります。
実行結果
色が変わった!
四角を描く(harib01g)
ここでは、画面に絵を描く方法が説明されていました。
haribote01gでの画面モードには、320×200=64000個もの画素が設定できるので、左上から右下に座標を(0,0)から設定する場合の[VRAM]のアドレスの求め方は
0xa0000 + x + y * 320
となります。これで1点の描写ができるようです。
この1点を、まずx方向に繰り返し線を作り、その線をy方向の逆向きに繰り返すことで四角を作ることができます。
流れ:
①1点の描写
②右方向へ繰り返し(線)
③下方向へ繰り返し(四角)
といった流れ
キレイですなぁ
最後の仕上げ(harib01h)
OSっぽい画面に描写を行うらしい!
Harimainを指示された通りに変更すると
おぉ…!
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
ポインタの場合の型
・char
はBYTE
・short
はWORD
・int
はDWORD
#define
プリプロセッサへの命令の一つ、使い道としては
・記号定数の定義
・関数マクロ
・条件付き取り込み
本では記号定数の定義として使用されてるっぽい。
参考
ショーもないミス一覧
コンパイル通ったのに黒画面
VRAMの開始アドレスを0xa0000でなく、0xa000にしていた