harib01a
画面に出力を行うために、 naskfunc.nasに以下の関数が追加された メモリ番地を指定して書き込むのが目的とのこと
_write_mem8: ; void write_mem8(int addr, int data);
MOV ECX,[ESP+4] ; [ESP+4]にaddrが入っているのでそれをECXに読み込む
MOV AL,[ESP+8] ; [ESP+8]にdataが入っているのでそれをALに読み込む
MOV [ECX],AL
RET
C言語(bootpack.c)側では、以下で呼び出している
for (i = 0xa0000; i <= 0xaffff; i++) {
write_mem8(i, 15); /* MOV BYTE [i],15 */
}*/
どうやら、C言語からnaskの関数を呼び出す時の引数の仕様として、以下のようになるっぽい。
- 関数の読み込まれるメモリアドレス:[ESP] ※ESPレジスタに関数の格納された ‘アドレスが’ 格納される。アセンブラで ‘’ をつけることで、そのアドレスに格納された値を操作可能
- 第1引数の格納場所:[ESP + 4]
- 第1引数の格納場所:[ESP + 8]
- 第1引数の格納場所:[ESP + 8]
上記をふまえコメントを書き換えてみる。
_write_mem8: ; void write_mem8(int addr, int data);
MOV ECX,[ESP+4] ; [ESP+4]にC言語側で関数がコールされた時の第一引数=address(書き込み先アドレス)が入っているので、その値をECXレジスタに格納する
MOV AL,[ESP+8] ; [ESP+8]にC言語側で関数がコールされた時の第2引数=data(書き込むデータ)が入っているので、その値をALレジスタに格納する
MOV [ECX],AL ; [ESP+4]で指定されたアドレスにdataの値を書き込む
RET
結果的に、0xa0000 から 0xaffff までのアドレスにかたっぱしから’15’を書き込む処理となっている。
harib01b
bootpack.cのコードが変わった
void io_hlt(void);
void write_mem8(int addr, int data);
void HariMain(void)
{
int i; /* 変数宣言。iという変数は、32ビットの整数型 */
for (i = 0xa0000; i <= 0xaffff; i++) {
- write_mem8(i, 15);
+ write_mem8(i, i 0x0f);
}
for (;;) {
io_hlt();
}
}
i % 0x0f だが、はアンド演算子と呼ばれるらしい。
OR演算(どちらかが1だったら1にする)
0100 OR 0010 ⇒ 0110
1010 OR 0010 ⇒ 1010
AND演算(両方が1の時のみ1にする)
0100 AMD 1101 ⇒ 0100
1010 AND 1101 ⇒ 1000
最後にXOR演算(片方が0でもう片方が1の時のみ1にする)
0100 XOR 1101 ⇒ 1001
1010 XOR 0010 ⇒ 1000
harib01f
- char型の変数には、以下の3つのモードがある。
- signed型 データは-128~127を表す
- unsigned型 データは0~255を表す
- 型指定なし コンパイラに判断を委ねる?
- EFLAGSとは、16bitレジスタのfflagsを拡張したもので、 キャリーフラグや割り込みフラグが詰まっている。
void init_palette(void) { static unsigned char table_rgb[16 * 3] = { 0x00, 0x00, 0x00, /* 0:黒 */ 0xff, 0x00, 0x00, /* 1:明るい赤 */ 0x00, 0xff, 0x00, /* 2:明るい緑 */ 0xff, 0xff, 0x00, /* 3:明るい黄色 */ 0x00, 0x00, 0xff, /* 4:明るい青 */ 0xff, 0x00, 0xff, /* 5:明るい紫 */ 0x00, 0xff, 0xff, /* 6:明るい水色 */ 0xff, 0xff, 0xff, /* 7:白 */ 0xc6, 0xc6, 0xc6, /* 8:明るい灰色 */ 0x84, 0x00, 0x00, /* 9:暗い赤 */ 0x00, 0x84, 0x00, /* 10:暗い緑 */ 0x84, 0x84, 0x00, /* 11:暗い黄色 */ 0x00, 0x00, 0x84, /* 12:暗い青 */ 0x84, 0x00, 0x84, /* 13:暗い紫 */ 0x00, 0x84, 0x84, /* 14:暗い水色 */ 0x84, 0x84, 0x84 /* 15:暗い灰色 */ }; set_palette(0, 15, table_rgb); return; /* static char 命令は、データにしか使えないけどDB命令相当 */ }
char型の配列 table_rgb[48] を作成し、48個のデータを格納後、 set_palette関数を起動している。
void set_palette(int start, int end, unsigned char *rgb)
{
int i, eflags;
eflags = io_load_eflags(); /* 割り込み許可フラグの値を記録する */
io_cli(); /* 許可フラグを0にして割り込み禁止にする */
io_out8(0x03c8, start);
for (i = start; i <= end; i++) {
io_out8(0x03c9, rgb[0] / 4);
io_out8(0x03c9, rgb[1] / 4);
io_out8(0x03c9, rgb[2] / 4);
rgb += 3;
}
io_store_eflags(eflags); /* 割り込み許可フラグを元に戻す */
return;
}
set_palette関数では、 割り込み不可状態を作った後で、 0x03c8 及び 0x03c9の装置に対し、out命令を実行する naskの関数を実行している。
io_out8の実装は以下
_io_out8: ; void io_out8(int port, int data);
MOV EDX,[ESP+4] ; port
MOV AL,[ESP+8] ; data
OUT DX,AL
RET
引数で受け取った装置名とrgb値を4で割った値を それぞれ、EDXレジスタとALレジスタに格納後、 「OUT DX,AL」している。
DXはEDXの下位16bitになるが、なぜここでEDXではなくDXを指定しているのか 理由がわからない。。なんにせよやっていることは変わらずで、 画面に出力する色を変える命令をビデオDAコンバータ?に送っている。
※この手の装置に直接命令を送る関数は言語には存在しないらしい
harib01g
最後に、図形を画面に描画してみる。
現在の画面はX軸が320、Y軸が200の合計64,000の点で構成されている。
上記の座標に何を描画するのかは、 VRAMに格納する値で管理されており(メモリに色番号を入れるとその座標の色が変わる)、 0xa000 + x + y *320
というルールでアドレスが採番される。