パソコン活用研究C&C++であそぼ(C、C++、の活用研究)

割り込みA(intdos使用例)

---------------------------------------------------------------------------------
割り込み@で例示したものも含めて、DOSのバージョンの取得、日付の取得・設定、時刻の取得・設定
など、intdosで可能な割り込みの例をいくつか集め、それをライブラリにしてみたいと思います。
---------------------------------------------------------------------------------


1 intdosを利用したシステムコールの例
(1) MS-DOSのバージョンの取得(ファンクション30H)
シンプルな例として、int21Hシステムコールのファンクション30H(MS-DOSのバージョンの取得)
を利用したプログラムを作ってみます。これは割り込み@でもやりましたが、今回はライブラリ化する
ために、関数の形にします。関数の引数として、バージョンの整数部と小数部を取得します。
コール時に設定するレジスター値およびコールから復帰後のレジスター値の内容は、以下の通りです。

コール(&in) AH  30H
リターン(&out) AL  バージョン番号の整数部
AH  バージョン番号の小数部
BH  OEMのシリアル番号
BL:CX 24ビットのユーザ番号
dos_ver.c
/* DOSのバージョン */
/* *ver バージョン */
/* *minor  マイナー番号 */
#include <dos.h>
union REGS in,out;

int dos_ver(int *ver, int *minor)
{
in.h.ah=0x30;
intdos(&in,&out);
*ver=out.h.al;
*minor=out.h.ah;
return;
}


(2) 日付の取得(ファンクション2aH)
これも割り込み@でもとりあげた例ですが、日付の取得をする関数を作ります。関数の引数として
年月日を取得し、関数の戻り値で曜日をリターンします。

コール(&in) AH  2AH
リターン(&out) AL  曜日(0・・日 1・・月  6・・土)
CX  年
DL  日
DH  月
get_date.c
/* 日付の取得 */
/* *year 年 1980〜2099 */
/* *month 月 1〜12 */
/* *date  日 1〜31 */
/* 戻り値 曜日 0 日曜 1 月曜  6 土曜 */
#include<dos.h>

union REGS in,out;

int get_date(int *year, int *month, int *date)
{
in.h.ah=0x2a;
intdos(&in, &out);
*year=out.x.cx;
*month=out.h.dh;
*date=out.h.dl;
return (out.h.al);
}


(3) 日付の設定
日付の設定用の関数です。関数の引数に年月日の値を渡して、設定します。戻り値にはエラー情報が
リターンされます。無効な日付を設定した場合は-1を返す。

コール(&in) AH    2BH
CX 年(1980〜2099)
DH 月(1〜12)
DL 日(1〜31)
リターン(&out) AL 00H 有効な日付
   FFH 無効な日付
set_time.c
/* 日付の設定 */
/* year 年 1980〜2099 */
/* month 月 1〜12 */
/* date  日 1〜31 */
/* 戻り値 エラー情報 -1の時無効な日付 */
#include <dos.h>

union REGS in,out;

int set_date(int year, int month, int date)
{
int v=0;
in.h.ah=0x2b;
in.x.cx=year;
in.h.dh=month;
in.h.dl=date;
intdos(&in,&out);
if (out.h.al) v=-1;
return (v);
}


(4)時間の取得(ファンクション2c)
時間の取得をする関数です。関数の引数として時・分・秒を取得します。

コール(&in) AH 2CH
リターン(&out) CH 時 (0〜23)
CL 分 (0〜59)
DH 秒 (0〜59)
get_time.c
/* 時刻の取得        */
/* *hour  時  0〜23 */
/* *minute 分  0〜59 */
/* *second 秒 0〜59 */
#include <dos.h>

union REGS in,out;

int get_time(int *hour, int *minute, int *second)
{
in.h.ah=0x2c;
intdos(&in, &out);
*hour=out.h.ch;
*minute=out.h.cl;
*second=out.h.dh;
return;
}


(5)時間の設定(ファンクション2d)
時間の設定を行う関数です。関数の引数に時・分・秒の値を渡し、設定して戻ります。
戻り値にはエラー情報がリターンされます。無効な時間を設定した場合は-1を返す。

コール(&in) AH 2DH
CH 時 (0〜23)
CL 分 (0〜59)
DH 秒 (0〜59)
リターン(&out) AL 00H 有効な時刻
   FFH 無効な時刻
set_time.c
/* 時刻の設定        */
/* hour  時  0〜23 */
/* minute 分  0〜59 */
/* second 秒 0〜59 *
/* 戻り値 エラー情報  -1 無効な時刻 */
#include<dos.h>

union REGS in,out;

int set_time(int hour, int minute, int second)
{
int v=0;
in.h.ah=0x2d;
in.h.ch=hour;
in.h.cl=minute;
in.h.dh=second;
intdos(&in,&out);
if (out.h.al) v=-1;
return(v);
}


2 ライブラリにする

コンパイラはLSIC86を使った例です。
上記の5個の関数をオブジェクトファイルに変換します。オブジェクトファイルのみを生成する場合は、
lccに-cオプションをつけます。

D:\win95\C>lcc -c dos_ver.c

D:\win95\C>lcc -c get_date.c

D:\win95\C>lcc -c set_date.c

D:\win95\C>lcc -c get_time.c

D:\win95\C>lcc -c set_time.c

オブジェクトファイルができたかどうかの確認です。5個の関数のオブジェクトファイルができました。

D:\win95\C>dir *.obj
ドライブ D のボリューム ラベルがありません。
ボリューム シリアル番号は 8832-AEB9 です

D:\win95\C のディレクトリ

2008/11/01 21:49 232 DOS_VER.OBJ
2008/11/01 21:57 256 GET_DATE.OBJ
2008/11/01 22:01 248 GET_TIME.OBJ
2008/11/01 21:59 249 SET_DATE.OBJ
2008/11/01 22:01 249 SET_TIME.OBJ
8 個のファイル 3,725 バイト
0 個のディレクトリ 9,807,024,128 バイトの空き領域

5個のオブジェクトファイルを結合してライブラリファイルを作ります。
LSIC86のライブラリアンはoarです。以下のように指定します。
oar [オプション] ライブラリフェイル オブジェクトファイル

5個のオブジェクトファイルからsyscalla.libというライブラリファイルを作ります。

D:\win95\C>oar qc syscalla.lib dos_ver.obj get_date.obj get_time.obj set_date.ob
j set_time.obj
oar: in_: redefined (ignored)
oar: in_: redefined (ignored)
oar: in_: redefined (ignored)
oar: in_: redefined (ignored)
oar: out_: redefined (ignored)
oar: out_: redefined (ignored)
oar: out_: redefined (ignored)
oar: out_: redefined (ignored)


3 ライブラリをリンクして使ってみる
(1) システムコール関数を呼び出すメインプログラム syscalla.c
上記の5個の関数を呼び出すプログラムです。メニューは以下の通り。
1 DOSのバージョン表示→dos_()を呼ぶ
2 日付の表示→get_date()を呼ぶ
3 日付の設定→set_date()を呼ぶ
4 時間の表示→get_time()を呼ぶ
5 時間の設定→set_time()を呼ぶ
6 終了

メインプログラムは、
日付と時刻設定の場合(set_date, set_time)は必要な引数をセットし、関数をコールして、戻り値をチェックし、
エラーかどうか表示するだけです。DOSのバージョン取得、日付、時間の取得(dos_ver, get_date, get_time)は
関数をコールし、リターン後の引数を表示するだけです。この場合は、引数は参照渡しになっている点に注意
しておいて下さい。

関数をライブラリにしておくと、メインプログラム側は簡単ですね。

syscalla.c
#include <stdio.h>
#include <dos.h>

int dos_ver(int *, int *);
int get_date(int *, int *, int *);
int set_date(int,int,int);
int get_time(int *, int *, int *);
int set_time(int,int,int);

main()
{
int v, r, f,ver, minor,year, month, date,day,hour,minute,second;

while(1) {
v=0; r=0; f=0;
printf("1 DOSのバージョン表示  2 日付の表示  3 日付の設定 \n");
printf("4 時間の表示 5 時間の設定 6 終了 \n");
scanf("%d", &v);
switch(v) {
case 1:
dos_ver(&ver, &minor);
printf ("Dos Version %d.%d\n",ver,minor);
break;
case 2:
day=get_date(&year, &month, &date);
printf("%d/%d/%d\n",year,month,date);
break;
case 3:
printf("年(1980〜2099):");scanf("%d",&year);
printf("月(1〜12):");scanf("%d",&month);
printf("日(1〜31):");scanf("%d",&date);
r=set_date(year,month,date);
if (r==-1) printf("設定した日付が不正です\n");
break;
case 4:
get_time(&hour, &minute, &second);
printf("%d:%d:%d\n",hour,minute,second);
break;
case 5:
printf("時(0〜23):");scanf("%d",&hour);
printf("分(0〜59):");scanf("%d", &minute);
printf("秒(0〜59):");scanf("%d", &second);
r=set_time(hour,minute,second);
if (r==-1) printf("設定した時刻が不正です\n");
break;
case 6:
f=1;
break;
}
if (f==1) break;}
}


syscalla.cをライブラリsyacalla.libとリンクして実行してみます。
ソースファイルにオブジェクトファイルやライブラリをリンクして実行ファイルを作る場合は、lccの引数にに
ソースファイルとオブジェクトファイルやライブラリを記述するだけです。lccが適宜判断してリンクして実行
ファイルを作ります。

D:\win95\C>lcc syscalla.c syscalla.lib


D:\win95\C>syscalla
1 DOSのバージョン表示  2 日付の表示  3 日付の設定
4 時間の表示 5 時間の設定 6 終了 
1
Dos Version 5.0
1 DOSのバージョン表示  2 日付の表示  3 日付の設定
4 時間の表示 5 時間の設定 6 終了 
2
2008/11/2
1 DOSのバージョン表示  2 日付の表示  3 日付の設定
4 時間の表示 5 時間の設定 6 終了 
4
9:39:20
1 DOSのバージョン表示  2 日付の表示  3 日付の設定
4 時間の表示 5 時間の設定 6 終了 
3
年(1980〜2099):2000
月(1〜12):1
日(1〜31):1
1 DOSのバージョン表示  2 日付の表示  3 日付の設定
4 時間の表示 5 時間の設定 6 終了 
2
2000/1/1
1 DOSのバージョン表示  2 日付の表示  3 日付の設定
4 時間の表示 5 時間の設定 6 終了 
5
時(0〜23):10
分(0〜59):1
秒(0〜59):1
1 DOSのバージョン表示  2 日付の表示  3 日付の設定
4 時間の表示 5 時間の設定 6 終了 
4
10:1:4
1 DOSのバージョン表示  2 日付の表示  3 日付の設定
4 時間の表示 5 時間の設定 6 終了 
6

メニュー1はint21Hシステムコールのファンクション30H(MS-DOSのバージョンの取得)ですが、これだと
なぜかDOSのバージョンは5.0となります。
また、メニュー3で日付を変更するとPCのシステム時間の日付もちゃんと変わります。一方メニュー5の
時間の設定では、このDOSプロンプトの中でのみ時間が変更され、PCのシステム時間の時刻には影響
ありません。

4 補足

LSIC86には、librというコンパイラドライバがあり、これを使うとライブラリの作成、保守が容易になります。
librの動作は
(1)ひとつのファイルにまとめられたライブラリモジュールを分解
(2)各ライブラリモジュールのオブジェクトファイルを生成 lcc -c の動作
(3)各オブジェクトファイルを結合してライブラリを生成 oar qc の動作
になります。
libr用のソースファイルには専用の書式ルールがあります。詳しくはLSIC86のマニュアルを見て下さい。

以下は、
(1)syscall.lというlibr用ファイル(この中にget_ver.cというモジュールがある)からlibrでライブラリファイルを生成。
 libroutというライブラリファイルができる。(デフォルトでlibroutというファイル名になります)
(2)libroutをsyscall.libにリネーム
(3)get_ver.cとsyscall.libをリンクして実行ファイルget_ver.exeを生成
ということをやっています。

syscall.l
--
#include <dos.h>

-get_ver.c
union REGS in,out;

int get_ver(int *ver, int *minor)
{
in.h.ah=0x30;
intdos(&in,&out);
*ver=out.h.al;
*minor=out.h.ah;
return;
}

get_ver.c
#include <stdio.h>
#include <dos.h>

main()
{
int ver, minor;
get_ver(&ver, &minor);
printf ("Dos Version %d.%d\n",ver,minor);
}


実行例

D:\win95\C>libr syscall.l ---------(1) libroutができる
lcc -c get_ver.c
oar qc libr.out get_ver.obj

D:\win95\C>ren librout syscall.lib ------(2)

D:\win95\C>lcc get_ver.c syscall.lib ----(3)
get_ver.c 7: Warning: function 'get_ver' undefined -- assumed to be int
lld @link.i

syscall.libをリンクしてコンパイルした。


D:\win95\C>get_ver
Dos Version 5.0



ついでに、オブジェクトファイル(intdos30.obj)を作り、メインプログラムget_ver.cにリンクして
実行ファイルget_ver2.exeを作った例。これが古典的な方法ですね。

intdos30.c
#include <dos.h>
union REGS in,out;

int get_ver(int *ver, int *minor)
{
in.h.ah=0x30;
intdos(&in,&out);
*ver=out.h.al;
*minor=out.h.ah;
return;
}


実行例

D:\win95\C>lcc -c intdos30.c

D:\win95\C>lcc -o get_ver2.exe get_ver.c intdos30.obj
get_ver.c 7: Warning: function 'get_ver' undefined -- assumed to be int
lld @link.i

オブジェクトファイルintdos30.objをリンクしてコンパイルした。

D:\win95\C>get_ver2
Dos Version 5.0


TopPage