パソコン活用研究C、C++であそぼ

共用体その2

前回は、共用体の基本について触れましたが、あまり共用体を使う便利さを実感できなかった
かもしれません。今回はC言語のコンパイラに用意されているレジスタの共用体を使ってみたい
と思います。少し共用体の価値を感じることができると思います。

1. 準備(8086のレジスター)
CPU8086には16bitの汎用レジスターとして、AX,BX,CX,DXがあります。 
これらの汎用レジスタはみな16bitですが、それぞれ半分にして8bitレジスタとしても使用できます。
8bitレジスターとして使用する場合は以下の通りです。
AX -->AL,AH
BX -->BL,BH
CX -->CL,CH
DX -->DL,DH
すなわち、AL、AHとして単独の8bitレジスタとして使用することもできるし、AL+AH=AXとして
16bitレジスタとしても使用できます。
どうですか、何か共用体のにおいがしてきましたか。(賢いですねー)

レジスターについて詳しくは「パソコン活用研究ラピュタへの道/8086CPUの基礎」を参照して
下さい。


2. レジスターの共用体宣言
Cコンパイラのdos.hを見ると、レジスターの共用体宣言があります。
LSICのものを抜粋してみましょう。

union REGS {
struct XREGS {
unsigned short ax, bx, cx, dx, si, di, bp, ds, es, flags, cflag;
} x;
struct HREGS {
unsigned char al, ah, bl, bh, cl, ch, dl, dh;
} h;
};

struct SREGS {
unsigned short es, cs, ss, ds;
};
#endif

char bdos(char, int, char);
char bdosp(char, void *);
void segread(struct SREGS *);
int int86(int, union REGS *, union REGS *);
int int86y(int, union REGS *, union REGS *);
int int86x(int, union REGS *, union REGS *, struct SREGS *);
int intdos(union REGS *, union REGS *);
int intdosy(union REGS *, union REGS *);
int intdosx(union REGS *, union REGS *, struct SREGS *);

REGSという共用体に、XREGSという16Bitレジスターの構造体(構造体変数名x)と
HREGSという8Bitレジスターの構造体(構造体変数名h)がメンバーとして宣言されています。
後半は各種のシステムコール関数が定義されています。

それでは、共用体REGSのメモリーがどのように確保されるか以下に図示してみます。

XREGS(x) ax bx cx dx  si  di  bp  ds  es flags cflags
HREGS(h) al ah bl bh cl ch dl dh              

それぞれのレジスターにアクセスするための記述の仕方を見てみましょう。
union REGS in;
とプログラムで宣言されていたら、axにアクセスする場合は in.x.ax です。
alにアクセスするなら in.h.al ですね。

3. 使用例
共用体REGSの簡単な使用例をみてみましょう。
DOSのバージョンを表示するプログラムです。(付けたしで、レジスターAX、AL、AHも表示させています)

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

union REGS in,out;

main()
{
in.h.ah=0x30;
intdos(&in,&out);
printf ("Dos Version %d.%d\n",out.h.al,out.h.ah);
printf("Registerの値(16進) AX: %04x AL: %02x AH: %02x \n",out.x.ax,out.h.al,out.h.ah);
}

intdos はいわゆるint 21h のシステムコールをする関数です。
このプログラムでは、REGS共用体として呼び出し時のレジスターの値用に共用体変数in、
システムコールの戻り値用に共用体変数outを用意し
intdos(&in,&out);
と記述してシステムコールをしています。 
システムコールの記述については、割り込み(システムコールの利用)を参照して下さい。

レジスターAHに30Hをセットしてコールすると、DOSのバージョンを取得できます。(in.h.ah=0x30;)
戻り値は、レジスターALにメジャーバージョン、AHにマイナーバージョンが返されます。

実行画面をみて下さい
Windows98で実行した例です。Windows98のDOS(窓)はバージョン7.10であることがわかります。
なお、その下の A:\>ver はDOSにある通常のバージョン表示コマンドの実行例ですが、これでは
Windows98と表示されています。(DOSにあるverコマンドはシステムコールをそのまま利用している
わけではなさそうです。)


A:\>version
Dos Version 7.10
Registerの値(16進) AX: 0a07 AL: 07 AH: 0a

A:\>ver

Windows 98 [Version 4.10.1998]

少しは共用体が見えてきたでしょうか。

TopPage