Image: i8042 KBD I/Oポート64hコマンドD0h実行時の割り込み?

沖電気OEMのMS-DOS 3.21にはPhoenix TechnologiesのControl/386というメモリマネージャーが入っているのだが、DOSVAXで起動するとメッセージ後のプロンプトに「1」が必ず入力される。これは元々のDOSBoxのバグなんじゃないかと色々悩みながら調査していた。I/Oポートの操作によるハードウェア割り込みが絡んでいたので、原因になっている処理が分かるまでにかなり時間が掛かってしまった。

処理の流れはこうなっている。

  1. Control/386はアドレスバス A20を有効にするため、キーボード・コントローラー・コマンド・バイト (I/O 64h) に D1h (出力ポート書き込み) を出力した後、同データ・バイト (I/O 60h) に DFh を出力する。
  2. 次に、Control/386は出力ポートへの反映をチェックするため、同コマンド・バイト (I/O 64h) に D0h (出力ポート読み出し) を出力する。
  3. キーボードコントローラーは出力ポートのデータを出力バッファーに書き込む。この時、ハードウェア割り込み (IRQ1) が発生するが、Control/386によって事前に割り込み禁止 (CLI) となっているため、保留になる。
  4. Control/386が割り込み許可 (STI) にしたとき、割り込み (IRQ1) 処理によって出力バッファーのデータ (02h) がスキャンコードとしてキーバッファーに転送される。
  5. キー入力待ち(プロンプト)処理になったときにキーバッファーの内容が読み込まれ、ユーザーがまだキーを押していないにも関わらずキーが1回押された扱いになる。スキャンコード02hは1キーとして解釈される。

実はIBMの技術解説書には、キーボードコントローラー(以下i8042とする)のD0h (出力ポート読み出し) というコマンドが割り込みを発生するかどうかは明記されていない。他のコマンドには割り込みを発生させる場合はそのように記されている。また、キーボードやマウスのデータが出力バッファーに置かれるときも割り込みが発生すると明記されている。しかし、D0hについては何も書かれていないので、これを「割り込みは発生しない」と解釈することもできる。

これを調査するために簡単なプログラムを書こうとしてみるも、MASMではなくVC++のインラインアセンブリを使おうとして、書き方から色々手こずった。VC++でのインラインアセンブリの情報が少なすぎ。EQUの代わりにdefineプリプロセッサを使うとか、そういう情報が欲しかったんだが。

#include <stdio.h>
#define I8042_OUTP 0x60 /* KBD Data Buffer */
#define I8042_STAT 0x64 /* KBD W:Command Byte, R:Status Byte */
int main(void)
{
	int out_data = 0xFF;
	__asm {
		lea	bx, out_data	;set pointer for out_data
		cli			;clear interrupt flag
		call	wait_8042
		mov	ax, 0xd0	;command (Outport on buffer)
		out	I8042_STAT, al	;
		call	wait_8042
		in	al, I8042_OUTP
		mov	[bx], ax	;set result of command
		sti			;set interrupt flag
		jmp	end		;IRQ1 activated and scancode added to key buf
	wait_8042:
		in	al, I8042_STAT
		and	al, 2		;test input buffer full flag
		loopnz	wait_8042
		ret
	end:
		nop
	};
	printf("Outport: %02X\r\n", out_data);
	return 0;
}
/* Return to COMMAND.COM, then key buf is popd. */

自分でテストしたいという方が居るかもしれないので、実行ファイルをここに置いておく。コンパイラはVisual C++ 1.5を使用した。PC/AT互換機のDOS以外での動作は保証しない。

A20.7z

このプログラムをマザーボードによって結果が変わるか調査するため、HIMEM.SYS導入済みのDOS環境で実行した所、実機ではASUS P2B、P8P67ともに「4」がプロンプトに返ってきた。ここでの4は左の数字キーで考えるとスキャンコード05hとなり、A20ライン有効と辻褄が合わない。テンキーの4、つまり 92h がキーバッファーに追加されたと予想できる。

DOSBoxの挙動は実機と同じであることが分かった。では、キーバッファーを処理しないControl/386に問題があるかというと、あながちそうとも言えない。このディスクが使われる沖電気のパソコンでは割り込みが発生しない仕様だった可能性は残っている。

そもそもD0h (出力ポート読み出し)というのは、DOSBoxでは「A20ラインが有効なら20h、無効なら00hをセットする」と簡素化されていることから分かるが、ほとんど使われない機能だろう。A20ラインが有効かどうかは実際にメモリをアクセスして調べた方が確実だ。(下のコード中の0xdeadbeefは草😆)

syslinux/gateA20.c at master · joyent/syslinux · GitHub

出力ポート読み出しにIRQ1が発生することがクソ仕様な気するが、HIMEM.SYSですら使わないレア機能だから誰も気にしなかったのかもしれない。DOSBoxも実機の仕様に則っているので(沖電気のPCは知らん)、ここではControl/386が悪いとしか言いようが無い。

参考文献

  1. “OADGテクニカル・リファレンス(ハードウェア)”, PCオープン・アーキテクチャー推進協議会, 2000年.

※コメント欄が表示されない場合はdisqusについてJavascriptが有効であることを確認して下さい.