DOSでマウスのボタンをクリックしたらアプリケーションを終了するというだけのプログラムをC言語で作りたかったんだけど、これが上手くいかなかった。マウスドライバーの機能12(割り込みサブルーチンの設定)を使って、マウスが操作されたときにユーザーサブルーチンが呼び出されるようにしていた。このルーチンからアクセスする変数がデータセグメントにあり、一方、サブルーチン呼び出し時点でデータセグメントアドレスはマウスドライバーを挿していたため、一致していないことが問題だった。
MASMなら.CODEディレクティブの後に変数宣言を入れれば、そのデータはコードセグメントに配置されることは知っていたけど、MS-C 8.0 (Visual C++ 1.5)での方法を知らなかった。この方法はサポート技術情報 KB401803 - [MSVC] データをコードセグメントに確保する方法に書いてあった。変数宣言の後に__based(__segname("_CODE"))を書けばいいらしい。このキーワードはMS-C 6.0以降でサポートされている。
以下、DOS用の簡単なマウスプログラム。この程度ならアセンブリ言語で書くのと大差ないが、コードが増えていくとアセンブリ言語は一気に可読性が下がるから、先人達がやっていたように、ハードウェア制御のコア部分だけインラインアセンブリで書くというのは、理にかなっていると思う。
#include <stdio.h>
#include <dos.h>
#define LOOP_TOEXIT 1
int __based(__segname("_CODE")) nextcmd = 0;
void far mouse_handler(void)
{
nextcmd = LOOP_TOEXIT;
return;
}
void main(void)
{
struct SREGS segregs;
union REGS regs;
regs.x.ax = 0; /* Init DOS mouse driver */
int86(0x33, ®s, ®s);
if (regs.x.ax == -1)
{
regs.x.ax = 12; /* MS MOUSE v1.0+ - DEFINE INTERRUPT SUBROUTINE PARAMETERS */
regs.x.cx = 2; /* Call the mouse handler when the left button pressed */
regs.x.dx = (int)mouse_handler;
segregs.es = ((long)mouse_handler) >> 16;
int86x(0x33, ®s, ®s, &segregs);
printf("Press the left mouse button to exit.\n");
}
else
{
printf("Error: Can't capture the mouse.\n");
return;
}
/* Main loop */
while (1)
{
if (nextcmd == LOOP_TOEXIT)
break;
}
/* Reset the mouse driver to release the interrupt */
regs.x.ax = 0;
int86(0x33, ®s, ®s);
return;
}