マウス制御〜32ビット〜メモリ管理まで@自作OS入門

〜30日間でできる自作OS〜

8日目

  1. マウスの解読
  2. 関数化
  3. マウスの解読2
  4. マウスカーソルを動かす
  5. アセンブラの解説(32ビットモードへ)

9日目(序)

  1. ソースの整理
  2. メモリ容量チェック1
マウスの解読

マウスが有効化されると、1バイト(0xfa)が送信されます。
その後、マウスが移動すると3バイト(3ボタン+x移動量+y移動量)づつ送信されてくるので、
1バイトづつ読み込んで表示しました。

//mouse_phase = 1 マウス接続状態
//mouse_phase = 2 マウス状態取得 1バイト目
//mouse_phase = 3 マウス状態取得 2バイト目
//mouse_phase = 4 マウス状態取得 3バイト目
//i = バイト値
if (mouse_phase == 0) {
	/* マウスの0xfaを待っている段階 */
	if (i == 0xfa) {
		mouse_phase = 1;
	}
} else if (mouse_phase == 1) {
	/* マウスの1バイト目を待っている段階 */
	mouse_dbuf[0] = i;
	mouse_phase = 2;
} else if (mouse_phase == 2) {
	/* マウスの2バイト目を待っている段階 */
	mouse_dbuf[1] = i;
	mouse_phase = 3;
} else if (mouse_phase == 3) {
	/* マウスの3バイト目を待っている段階 */
	mouse_dbuf[2] = i;
	mouse_phase = 1;//状態を1バイト目取得状態へ戻す
	/* データが3バイト揃ったので表示 */
	sprintf(s, "%02X %02X %02X", mouse_dbuf[0], mouse_dbuf[1], mouse_dbuf[2]);
	boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 32, 16, 32 + 8 * 8 - 1, 31);
	putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
}
関数化

上のロジックを関数化しました。同時にマウス状態を構造化しました。

//buf[0]=マウス状態1バイト目
//buf[1]=マウス状態2バイト目
//buf[2]=マウス状態3バイト目
//phase = マウス状態取得状態
struct MOUSE_DEC {
    unsigned char buf[3], phase;
};
int mouse_decode(struct MOUSE_DEC*, unsigned char);//関数化
int mouse_decode(struct MOUSE_DEC *mdec, unsigned char dat){
    〜処理〜
}


関数化のところ"MOUSE_DEC*"の"*"を忘れて若干ハマりました。

マウスの解読2

まず、バイトチェックを入れました。

if((dat & 0xc8) == 0x08){
    〜マウス〜
}


1バイト目をチェックしています。
0xc8で上位4ビットが0〜3(マウス移動方向=0〜3が割り当てられる)
0xc8で下位4ビットが8〜F(マウスボタン=8〜Fが割り当てられる)
の範囲内かどうかチェックしています。それ以外の値の場合は、読み捨てます。
(つまり、上位2ビットが0で、上位から5ビット目が1あることをチェックしています。)

あと、ボタンと移動値をわかりやすくしました。
今までの"[A9 FF 00]"などの表記から"[lcr -1 2]"などの表記になりました。
"lcr"はボタンに対応していて、押したボタンによって大文字になります。
右ボタンだけ押すと"lcR"。


マウスでは、移動量の符号が反対なのだそうです。

mdec->y = -mdec->y;
マウスカーソル動かす

前のプログラムで移動量を取得できたので、移動量から描画すべきマウスカーソルのx,y座標を算出。
既に描かれているマウスカーソルを消して、再描画するロジックです。

boxfill8(binfo->vram,binfo->scrnx,COL8_008484,mx,my,mx+15,my+15);//マウス消 す
mx += mdec.x;//X移動量を足しこむ
my += mdec.y;//Y移動量を足しこむ
putblock8_8(binfo->vram,binfo->scrnx,16,16,mx,my,mcursor,16);//マウス描画


画面外へマウスカーソルがはみ出ないようにしました。

//画面外へはみ出ない
if(mx < 0){
    mx = 0;
}
if(my < 0){
    my = 0;
}
if(mx > binfo->scrnx - 16){
    mx = binfo->scrnx - 16;
}
if(my > binfo->scrny - 16){
    my = binfo->scrny - 16;
}
アセンブラの解説

説明されていないアセンブラ部分の説明がなされていました。
まず、画面モード切替直後のロジック。ここでは、PICとCPUの割り込みを禁止しています。

MOV		AL,0xff
OUT		0x21,AL			; マスターPICの割り込みを禁止
NOP						; OUT命令を連続させるとうまくいかない機種があるらしいので
OUT		0xa1,AL			; スレーブPICの割り込みを禁止
CLI						; さらにCPUレベルでも割り込み禁止

キーボードコントローラーのおまけ出力ポート(0xd1)に(0xdf)を出力すると、1MB以上のメモリが使用できます。

CALL	waitkbdout
MOV		AL,0xd1
OUT		0x64,AL
CALL	waitkbdout
MOV		AL,0xdf			; enable A20
OUT		0x60,AL
CALL	waitkbdout					; さらにCPUレベルでも割り込み禁止


CR0の最上位ビットを0にすると、ページング禁止。
最下位ビットを1にすることで、プロテクトモードに・・・。
プロテクトモードとは、GDTを使って仮想的に番地へアクセスする方法です。
ページングについてはこの章では触れられていませんでした。

MOV		EAX,CR0
AND		EAX,0x7fffffff	; ビット31を0にする(ページング禁止のため)
OR		EAX,0x00000001	; ビット0を1にする(プロテクトモード移行のため)
MOV		CR0,EAX


あとは、メモリマップの紹介がありました。作者が考えたそうです。

  • 起動時に使用(BIOS,VRAMなど) 1MB
  • フロッピーの内容記録用 1440KB
  • 空き 30KB
  • IDT 2KB
  • GDT 64KB
  • bootpack.hrb格納 512KB
  • スタックなど 1MB
ソース整理

ソースの整理で割り込み処理を一括処理していたソースを、キーボードとマウス用に分けました。

メモリ容量チェック1

メモリ容量チェックをBIOS経由ではなくて、自力で実装します


まず、386か、486以降のCPUか判定します。
EFLAGSレジスタの第18ビットがACフラグと呼ばれるそうです。
手順は以下の通り

  1. EFLAGSレジスタの内容を取得。
  2. ACフラグを立てる。
  3. EFLAGSレジスタに値を戻す。
  4. もう一度EFLAGSレジスタの内容を取得。

その結果、ACフラグが立っていれば、486以降なのだそうです。


次に、メモリが有効かチェック。
ポインタを使って、メモリの値を取得していきます。
メモリの値を保持して、ビットを反転。反転結果が正解かチェック。
もう一度ビットを反転して、値が元に戻ったかチェック。
両方ともチェックが正解なら、ポインタを進める。
というロジックです。