WATCOM C / 386 Q WATCOM C / 386 を使って , 使 えるメモリの大きさを得るにはど うすればいいのですか。 A Windows の DOS 環境のよう な DPMI インタフェイスがあるとき は , DPMI のファンクションを使っ て使用可能メモリの大きさを取得 することがて、きます。しかし , のような環境にないときには , 使 用する DOS 工クステンダによっ て , 取得方法は異なります。また , 仮想記憶システムて、は , プログラ マは割り付けられる物理メモリの 大きさを取得したいときもありま す。マルチタスク環境て、は , ほか のタスクが独立にメモリを割り当 てていて , 返ってきた情報が必ず しも信頼て、きないことにも注意す る必要がります。 ( 1 ) DOS / 4G の場合 DOS/4G は int 31H という DPMI と同じインタフェイスを持ってい ます。使用可能メモリ情報を取得 するのに DPMI ファンクション 0X0 500 を使用て、きます 0List 1 に例を 示します。この例は , DOS/4G の D PMI コール 0500h を使って使用可 能メモリに関する情報を取得する 方法て、す。構造体の最初のフィー ルドのみに適切な値が入っている ことが保証されていて DOS / 4G が 値を返さないほかのフィールドに は一 1 (FFFFFFFFh) が入ってい ることに注意してください。 DPM I ファンクションに関する詳しい とは , WATCOM C / 386 User's G uide の「 INT 31H DPMI ファンク ション」の節を参照してください ( 2 ) PharLap 38 引 DOS-Extender の場合 ln 川 a ⅱ行 om m 川計 Makers ライフボート 33 : } 14 : { 29 : } PharLap 38 引 DOS-Extender には , メモリの使用状況を取得す るのに , 専用のシステムコール 0x 2520 があります。 List 2 に WATC OMC / 386 からこのシステムコール を使用する方法を示します。 List 2 は , PharLap368 ー DOS-Extender Ver. 4.0 て、搭載されている物理メ モリの大きさを取得します。 38 引 DOS-Extender のシステム コールについての詳しいことは , Pharlap 386 ー DOS-Extender リ ファレンスマニュアルの「 38 引 D OS-Extender システムコール」の 章を参照してください (3)Windows 3. x の 32 ビット環境 の場合 Windows3. x には , WATCOM C / 386 からアクセスて、きる DPMI の ホスト機能が含まれています。 の DPMI ホストへのインタフェイス は , 16 ビットインタフェイスにな っていて , 32 ビットコードから W indows3. x DPMI サービスを呼び 出すときは注意を要することがあ ります。 ES : DI レジスタを使って データバッフアへのポインタを渡 すときには AIIocAIias16 ( ) 関数を 使用して 16 ビット far ポインタにし てレジスタに値を設定し , Windo ws3. x に渡します。また , int386x 関数て、はなく int86x 関数を使用す ることに注意してください 付録ディスクに DPMI ホストとし て Windows を使用するとき , DPM I コール 0X0500 て、利用可能メモリに 関する情報を取得するサンプルプ ログラムを収録しました (WINME M. C)O WINMEM ℃ては , 構造体 の最初のフィールドのみに適切な 値が入っていることが保証されて おり , DPMI を実現するソフトウェ アが値を返さないほかのフィール ドには—1(FFFFFFFFh) が入っ ていることに注意してください DPMI サービスに関する詳しいこと MEMORY ℃ Li st 1 は DOS プロテクトモードインタフ ェイス (DPMI) 仕様書を参照して ください 1 : 2 : 3 : 4 : 5 : 6 : 7 : 8 : 9 : 10 : 11 : 12 : 13 : 14 : 16 : 19 : 22 : 23 : 24 : 25 : 26 : 28 : 29 : 30 : 32 : include く i86. h 〉 include く dos. h> include く stdio. h> include く stri . h 〉 StrUCt meminfo unsigned 10 LargestBlockAvail ; unsigned 10 unsigned 10 LargestLockablePage; unsigned 10 LinAddrSpace; unsigned 10 NumFreePagesAvail ; unsigned 10 NumPhys icalPagesFree; unsigned 10 TotalPhys icalPages; unsigned 10 FreeLinAddrSpace; unsigned 10 Size0fP eSize; unsigned 10 Reservå3] ・ ) MemInfo; 18 : #define DPMI—INT 0X31 20 : void main() union REGS regs ; struct SREGS sregs; memset( &sregs, 0 , sizeof(struct SREGS) ) ; regs. x. eax ニ 0X00000500 ; sregs. es = FP SEG( &MemInf0 ) ; regs. x. = VP—OFF( &Memlnf0 ) : int386x( DPMI INT, ®s, ®s, &sregs ) : ・ printf( ” largést avai lable block ()n bytes) : Xlu*n ” MemInf0. LargestBlockAvail ) ; Li st 2 PLSMEM40 ℃ 3 : 4 : 5 : 6 : 7 : 10 : 11 : 12 : 15 : 16 : 17 : 20 : 21 : 22 : 23 : 24 : 26 : 28 : 30 : 33 : include く dos. h 〉 include く stdio. h 〉 typedef struct { uns igned da ね [ 25 ] ; ) pharlap_mem—status; 8 : / * P h a r L a p のマニュアルで推奨されているマクロ名 * / 9 : #define APHYSPG 5 define SYSPHYSÆ 7 define NFREEÆ 21 13 : unsigned 10 memavail( void ) pharlap_mem status status ; union REGS regs : unsigned 10 amount; regs. h. 曲 = 0X25 ; regs. h. al = 0X20 ; regs. h. bl regs. x. . x = (unsigned int) &status; intdos( ®s, ®s ) ; / * 以下の式は、 nf r e e p g に関する説明で与えられています。 return( amount * 4096 ) ; amount + ニ status. ね NFREEPG ] : amount + = status. 面ね SYSPHYSPG ] : amount = status. dataC APHYSÆ ] ; 31 : void main() printf( "XIu bytes of memory avai lable*n" , memavai 1 ( ) ) : lnformation from CompiIer Makers 167
X6 k 活用講座 > ゝ GCC で学ぶ X68 ゲーム プロクラミング X68000 版 G 十十でスプライ扱いましよう ) 吉野智興 先月に続いての G 十十スプライト奮闘記です。予想 どおり ( ? ) 先月の設計ではダメだった部分もあって , 基本設計かマズイとやっぱりダメという見本になっ てしまいました。それでは続きの始まりです。 第 14 回 先月の失敗 先月のプログラムて、失敗だったのは , PC G クラスのコンストラクタて、 PCG ファイルの 読み込みファイルネームの設定などを行お うとした点にありました。先月号て、書いた ように , スプライトは PCG クラスとスプラ イトスクロールレジスタクラスを設計して , そのふたつを組み合わせて動かせそうて、す。 て、すが , ここて、失敗がありました。 このふたつを継承させて双方を包括する クラスを作ろうとした場合 , コンストラク タて、読み込みファイルネームを設定するよ うな方法をとると , 継承したクラスて、基本 クラスを操作するのが難しくなるという設 計ミスが判明しました。 PCG データを読むファイルネームの設定 と実際の PCG データを読んて、バッフアの割 り当てをコンストラクタて、行わせると , そ れを継承したクラスて、 PCG を設定しようと したときに、、基本クラスはすて、に初期化され ているクとコンパイラに怒られてしまいま す。 PCG は時と場合によって細かく設定した List PCG クラスとスプライトスクロールレジスタクラス 1 : 〃スプライトレジスタ 2 : typede f s truct short int sp—x; 4 : short int sp_y; 5 : 6 : struct 7 : 8 : 9 : 10 : 11 : 13 : 14 : uns i gned dummy—l : 13 ; uns i gned ext uns i gned pwr } sp_attr; 19 : 20 : } SP_REG; 21 : 22 : typedef s truct 24 : unsigned POSO. ・ 4 ; ・ 4 ; 25 : uns i gned POS 1. 26 : uns i gned 00S2. ・ 4 ; unsigned POS3. ・ 4 ; 28 : } PCG; 29 : 30 : typedef PCG PCG—REG[128] ; 32 : / / スプライトの動きを定義する構造体 33 : typedef s truct 34 : { short x; 35 : short y; 36 : } move ; 38 : 39 : typedef s truc t / / 垂直反転 / / 水平反転 / / カラーパレット / / スプライトコード unsigned V invert unsigned h—invert : 1 ; uns i gned dummy—O uns i gned CO 1 or : 8 ; uns igned s p—code } sp—ctl ; struct / / プライオリティ 138 C MAGAZINE 1993 1
Fig. 18 ADDDRV によるキャラクタテパイスを組み込んだときのメモリマップ 08AF : 0098 0FB7 : 0000 1003 0008 1003 : 0000 100D : 0000 10A4 : 0000 IOAF : 0000 IOBD : 0000 1590 : 0000 15FD 15FD 16D9 15FD 16E4-16E8 16E9 16E9 173E : 0000 1A42 : 0000 ICOF - A000 09 29 6 : } CONFIG. SYS の内容 DEVICE = VEMS. DRV /E /U DEVICE ニ RZ. SYS / E = 160 wx2. dev の内容 DEVICE=WXK. SYS /AI DEVICE=WX2. SYS /AI >adddrv wx2. dev 〉 zmap ZMAP vl. 00 Copyright(C) 1992 ZOBpIus DOS Version 3.30 Addr PSP S i ze 24464 160 64 21072 12352 7344 540432 く free 〉 く character-device> く character-device> く unknovn> く free> 160 く shell env. > 3504 く shell> 486 <lastdrive>= F: 19760 く buffers> = 19 ( + 1 ) 218 く fcbs> 165 く f ⅱ e s > 3 ( + 5 ) 2416 く block-device> く character-device> く config> 1040 く buffers> = 1 5 271 く files> ovnerpsp/paraneters Hayam i dev ice EMMXXXXO 67 IRZ_DISK 02 5F HS$KANJI 2F CON Hooked vectors 22 23 24 2E 09 29 2F 173E : 0000 と 1A42 : 0000 に ADDDRV により WX Ⅱ + が組み込まれている List テパイスリンクを表示する C プログラム ( MEM6. C ) 特集 M 0S メモリ活用蠡 この SCB は , MCB と同様にチェーンされ されます。 ックのとき , デバイスドライバ名が設定 デバイスドライバが使用するメモリプロ ・ ownername . リサイズを示します。 メモリプロックのノヾラグラフ単位のメモ ・ pslze . 値が設定されます。 レスて、 , SCB のセグメントアドレス十 1 の メモリプロックの先頭のセグメントアド ・ owner . す。 し ,TabIe 5 の ID コードが定義されていま LES, デバイスドライバなどの種類を示 分割されたメモリプロックを使用する FI ・ scbid } SCB , char ownername [ 8 ] char reserve [ 3 ] ; unsigned short psize , unsigned short ownerPSP 3 : 7 : 9 : ー 10 : ー 11 : 2 : 3 : コ 4 : 15 : ー 16 : '19: ー 20 : ー 21 : ー 22 : 認 3 : ー 24 : 5 : 26 : ー 28 : ー 29 : 0 : 2 : 3 : : 34 : : 35 : 叩 = MK FP(m_seg, m_off) : _asm mov m_seg, dx; asm mov m Off, ax; —asm mov dx, es: Cbx + 14h] ; —asm mov ax, es : [bx + 12h] : _asm int 21h : _asm mov ah, 52h : BUFFERS far *mp; WORD m off, m_seg; int main() } BUFFÉRS ; WORD b seg; WORD b_off; typedef struct { typedef unsigned ・ short WORD; 8 : typedef unsigned char BYTE; 6 : #endif return 0 ; 叩 = MK_FP( 叩ー〉 b_seg, mp->b_off) ; if (mp->b_off = 0xFFFF) break; printf( ” %04X: % 04X }n", FP_SEG(mp), FP_0FF(mp)) ; 1 : #include く stdio. h 〉 2 : #include く dos. h 〉 4 : # i fndef MK_FP 5 : #define MK-FP(), 0 ) ((void far*) (((unsigned 10 ) ( s ) くく 16 ) l(unsigned short) ( 0 ) ) ) ており (Fig. 19 参照 ) , 最終 SCB は , このシ ステムメモリプロックの次のメモリプロッ ク MCB とチェーンされています。 サプセグメント制御プロックにより , 常 駐プロセスだけて、はなく , デバイスドライ バやシステムメモリプロックて、さえ明示的 にメモリ管理され , メモリ情報の取得に非 常に有意義なものとなっています。 ZMAP による DOS5 のメモリマップ表示を Fig. 20 に 示します。 OS の UMB UMB は DOS3 て、は DOS の管理外の仕様て、 すが , DOS5 からは DOS が管理し DOS の外部 コマンドて、操作が可能となりました 0MCB のチェーンもコンべンショナルメモリの M CB とつなげることがて、きます (List 7 ) 。 List 7 によって UMB が設定され DOS に認 識されますが , MCB のチェーンをたどると 特集 MS-DOS メモリ活用技法 63
List 1 も必要となります。ただし , プログラムリ ストのようにスペースが意味を持っ場合も 少なくないため , 単純にスペースを除去す ることはて、きません ( 筆者はこのあたりもあ る程度考慮したユーティリティを作成して 編集部に送付したことがあるのて、すが , 校 正用の原稿を見ると使っていないようて、す。 スペースの除去を正確に処理するのはかな りたいへんて、す ) 。 シフト』 S コードと 』 S コードの変換 パソコンの漢字コードはシフト JIS コード が標準てすが , ドットプリンタの漢字コー ドは JIS コードが標準て、す。シフト JIS コー ドと JIS コードの変換を行うプログラムを L ist 1 (tojis. c) , List 2 (tosjis. c) に示します。 使用法 tojis [ く入力ファイル > ] [ く出力ファイル > ] tosjis [ く入力ファイル > ] [ く出力ファイル > ] 入出力ファイルを指定しない場合には標 準入出力を使用します。プリントアウトユ ーティリティを自作する場合はシフト JIS コ ードを JIS コードに変換する必要がありま す。 9 : 10 : #define ERR 11 : #define NOERR 12 : #define ESC 13 : 14 : char *psrcnam; : 15 : char *pdstnam; 16 : 17 : int main( int argc, char *argv ロ ) 18 : ( / * 標準入力 register FILE *fpr = stdin, 19 : / * 標準出力 *fpw = stdout; 20 : int ret = 0 ; : 22 : getargs ( argc, argv ) ; / * 引数の取得 23 : if ( ( psrcnam ! = NULL 24 : & & ( fpr = fopen( psrcnam, "r" ) ) = NULL ) 25 : Ⅱ ( pdstnam ! = NULL 26 : & & ( fpw = fopen( pdstnam, 、 " ) ) = NULL ) ) { fputs( ” \ 033 [ 33m ファイルオーフ。ンエラー判 33 [ m \ n ” , stderr ) ; 28 : return( 1 ) ; ー 29 : 30 : ret ニ tOjis ( fpr, fpw ) : fclose( fpr ) : 32 : fclose( fpw ) ; 33 : if ( ret ) { fputs( " 判 33 [ 33m 変換を失敗しました。 \ 033 [ m \ n " 35 : return( 1 ) ; 36 : return( 0 ) ; 38 : 40 : ー 41 : void getargs( int argc, char *argv[] ) / * 引数の取得 42 : { while ( --argc ) { 43 : 44 : register char *P,• 45 : 46 : usage() ; ) else if ( psrcnam = NULL ) 48 : 49 : psrcnam = p; 50 : 51 : tnam = p ; 52 : 53 : } 54 : 55 : int tojis( FILE *fpr, FILE *fpw ) register unsigned cl, c2; 58 : while ( ( int ) ( cl = ( unsigned )getc( fpr ) ) ! = EOF ) { 59 : / * 漢字コート・ 1 ハ・イト目チェック if ( is kanj i ( cl ) ) { 60 : putc( ESC, fpw ) : putc( ' K' , fpw ) : 62 : do { 63 : if ( ( int ) ( c2 ー ( unsigned )getc( fpr ) ) 64 : re turn ( ERR ) ; cl = jmstojis( ( cl くく 8 ) ー c2 ) : 66 : putc( ( cl > 〉 8 ) , fpw ) ; putc( cl, fpw ) ; 68 : if ( ( int ) ( cl = ( unsigned )getc( fpr ) ) 69 : return ( NOERR ) : } while ( iskanji( cl ) ) : 71 : putc( ESC, fpw ) ; 72 : putc( 'H', fpw ) ; 74 : putc( cl, fpw ) ; return( NOERR ) ; 77 : 80 : void usage( void ) static char *msg ロ 82 : / * ェラー時のリターン・コート・ 0 0x1b / * 入力ファイル名 / * 出力ファイル名 / * シフト JIS → JIS 変換 stderr ) ; else / * シフト JIS → JIS 変換 旧』 S と新』 S の変換 前述のように漢字コードにはいろいろな ものがありますが , 同じシフト JIS だといっ ても安心て、きません。 MS ー DOS マシンはすべてシフト JIS コード を採用しているといっても , 漢字コードに はよく悩まされます。 パソコン関連て、は漢字コードに新 JIS コー ド ( 1983 年式 ) を採用しているものが多く , ・ J ー 3100 シリーズ ・ AX マシン ・ FM-TOWNS など , ほとんどのパソコンが新 JIS コードを 採用しています ( といっても空きコードの部 = EOF ) = EOF ) 100 C MAGAZINE 1993 1
デバイスドライバも BUFFERS, FILES と同様にヘッダボインタによりリンクされ ており , 先頭のデバイスドライバは必ず NU L デバイスになっています。 DOS ファンクシ ョン 52h て、得られる内部変数領域のアドレ ス十 17h からの 18 バイトが NUL デバイスドラ イバ本体て、す。この NUL デバイスドライバ の先頭 2 ワードが次のデバイスドライバを指 すポインタとなり , ヘッダボインタによっ て次々とデバイスドライバをたどることが て、きます。そしてヘッダボインタのオフセ ットが FFFFh のデバイスドライバが最後の ドライバとなります。 NULi-•バイスドライバのヘッダボインタ が指すデバイスドライバは , 最後に組み込 まれたデバイスドライバて、メモリ上て、は上 位に位置します。したがって , DOS システ ムて、用意されているデバイスドライバは , デバイスリンクの後半に位置することにな ります。キャラクタデバイス ( 日本語 FEP や マウスドライバ ) はデバイスドライバの先頭 アドレスから十 10 にデバイス名が格納され ており , デバイスドライバの種類を判別す ることがて、きます。プロックデバイスて、は この位置にはデバイスドライバが扱うドラ イプ数が格納されています。 List 6 はデバイスドライバのリンクをたど り , そのアドレスとデバイス名を表示する C プログラム MEM6. C て、す。デバイス名の 先頭キャラクタコードが 20h 以下のときはプ ロックデバイスと判断し , ドライプ数を表 Fig. 1 1 List 4 の実行結果 08AF : 0098 0FB7 : 0000 1005 0008 1005 : 0000 1010 : 0000 10 : 0000 1267 : 0000 12D4 12D4 13B0 12D4 13BB - A000 Addr PSP Addr PSP り 001 : 0000 IODF : 0000 1161 : 0 圓 0 11A2 : 0000 0FB7 : 0 圓 0 1224 : 0000 1120 : 0000 101C : 0000 109E : 0000 105D : 0000 11E3 : 0000 10 は 7 : { : 16 : : 15 : : 14 : '13 : 2 : ー 11 : '10: Fig. 12 Fig. BUFFERS の内容 CONFIG. SYS BUFFERS : 10 zmap はオプションなしだとこれだけ多くのメモリ情報を表示する ZMAP ⅵ . 00 Copyright(C) 1992 ZOBplus Hayami 〉 zmap DOS Version 3. 30 Addr PSP S i ze 271 11488 574544 ovnerpsp/parameters dev ice く files> 1040 く buffers> : く config> 165 く files> 218 く fcbs> 9360 く buffers> 486 <lastdrjve>= F: 3504 く shell> 160 く shell env. > く free> 5 3 ( + 5 ) 9 ( + 1 ) Hooked vectors 22 23 24 2E ABF で追加したときの BUFFERS の内容 CO 円 G. SYS の内容 BUFFERS=IO DEBICE=VEHS. DRV /E /U >abf / b15 〉 zmap 全体で 15 の BUFFERS を確保 ZMAP ⅵ . 00 Copyright(C) 1992 ZOBplus Hayami DOS Version 3. 30 60304 5200 - UMB Memory 574496 405 160 11568 S i ze ovnerpsp/parameters 271 く files 〉 5 1040 く buffers> : 1 く conf ig> く character-device> 165 く files> 3 ( + 5 ) 218 く fcbs> 9360 く buffers> = 9 ( + 6 ) <lastdrive 〉 : E: 3504 く shell> 160 く shell env. 〉 く free> Area ovnerpsp/parameters く unknovn> 5200 く buffers> く free 〉 dev ice Hooked vectors 08AF : 0098 0FB7 : 0000 1003 0008 1003 : 0000 100D : 0000 1018 : 0000 1026 : 0000 126F : 0000 12D7 12D7 13B3 12D7 13BE - A000 D001 FFBF D147- E000 EHMXXXXO 67 device 22 23 24 2E Hooked vectors BUFFERS のアドレスを表示する C プログラム ( MEM4 ℃ ) 1 : #include く stdiO. h> ー 2 : #include く dos. h> ー 4 : #ifndef 盤 FP int main() ) BUFFÉRS ; WORD b seg : WORD b_off; typedef s t ruct { 9 : typedef unsigned short WORD; 8 : typedef unsigned char BYTE; : 6 : #endif 、 5 : #define MKZFP(s,o) ((void far*)(((unsigned 10 ) ( s ) くく 16 ) l(unsigned short)(o))) 60 C MAGAZINE 1993 1
セス名がこの位置に設定されます (DOS5 に おける MCB の 16 バイトの構成を C 言語の構 造体て、表すと以下のようになります ) 。 typedef struct { char mcblD unsigned short ownerPSP unsigned short msize , char reserve [ 3 ] , char ownername [ 8 ] , } MCB ZMAP は DOS3 においてはメモリプロック の使用プロセス名表示に環境変数ェリアの プロセス名文字列を検索して表示していま したが ( 環境変数工リアを解放する常駐ソフ トて、は表示て、きません ) , この MCB 内のプロ セス名の設定によりさらに容易にプロセス 名を表示て、きるようになります。 DOS システムが利用するメモリプロック においても , このオーナープロセス名は設 定され , システムのデータ領域て、は、、 SD 〃 (System Data), プログラムコード領域て、 は、、 SC ク (System Code) が設定されます。 これにより , メモリ管理のマップ情報をよ り確実にアプリケーション側て、把握て、きま す。 08AF : 0098 0FB7 : 0000 1003 0008 1003 : 圓圓 100D : 0000 10A4 : 0000 IOAF : 0000 IOBD : 00 圓 1590 : 0000 15FD 15FD 16D9 15PD 16E4 ー A000 37 : ) プグメント オーナー PSP に 0008h , オーナープロセス 名に、、 SM 〃が設定されているメモリプロッ クは , CONFIG. SYS て、定義された FILES や デバイスドライバなどの位置するメモリプ ロックになります。 DOS3.3 以前て、はこのメ モリプロックはひとつのプロックとして一 括管理されていましたが , DOS5 て、は分割管 Fig. 17 List 6 の実行結果 1003 : 0000 EMMXXXXO Fig. 16 テパイスドライバのメモリマップ ます。 ntrol Block, 以下 SCB) により行われてい セグメント制御プロック (SubsegumentCo 理され , そのメモリ制御は MCB と似たサプ >zmap DEVICE : RZ. SYS /E DEVICE : VEMS. DRV /E /U CONFIG. SYS の内容 ZHAP ⅵ . 00 C 叩 yright(C) 1992 ZOBplus Hayami DOS Version 3. 30 SCB の 16 バイトの構成を C 言語の構造体て、 表すと以下のようになります。 typedef struct { ovnerpsp/parameters device char scblD Hooked vectors Addr PSP 凵 st S i ze 24464 160 218 561600 271 く files> 1040 く buffers> く config> 5 1 く character-device> 2416 く block-device> 3 ( + 5 ) 165 く f ⅱ es 〉 く fcbs> : 19 ( + l) 19760 く buffers> 486 <lastdrive 〉 = F: 3504 く shell> 160 く shell env. > く free 〉 EMMXXXXO 67 IRZ DISK 02 5F 22 23 24 2E 日 LES を表示する C プログラム ( MEM5. C ) 1 : #include く stdio. h> 2 : #include く dos. h> 4 : #ifndef MK_FP 5 : #define MK-FP(), 。 ) ((void far*)(((unsigned 1 。 ) ( s ) くく 16 刃 ( si short) ( 。 ) ) ) 6 : #endif 8 : typedef uns igned char BYTE; 9 : typedef unsigned short WORD; 11 : typedef struct ( WO f_off; WORD f_seg; WORD f num; } FILES; ' 17 : int min() 15 : 14 : ー 12 : は 0 : 7 : 3 : : 18. (Fig. 16 と同じメモリ状態で実行 ) 0060 : 3448 5 0060 : 3436 CLOCK 0060 : 3424 AUX 圓 60 : 3412 PRN 0060 : 3400 CON 100D : 0 圓 0 1 08AF : 0048 NUL 9 : 20 : 22 : 23 : 24 : 25 : 26 : 28 : 29 : 30 : 1 : 32 : ー 33 : 34 : 36 : WORD m off, m_seg; FILES far * 叩 ; —asm mov 曲 , 52h ; —asm int 21h ; asm mov ax, es: bX 十 4 asm mov dX, es : bX 十 6 _asm mov m_Off, ax; asm mov m_seg, dx; 叩 = MK_FP(m_seg, m_off) : return 0 ; 叩 = MK_FP( 叩ー〉 f_seg, mp->f_off) ; if (mp->f_off = = 0xFFFF) break; printf("X04X: % 0 猷 X2d*n", FP_SEG(mp), FP_0FF(mp), 叩ー〉 f ー n 凹 ) : 62 C MAGAZINE 1993 1
List 1 List KANJl.c 84 : int code, unsigned char PtnC]) 85 : int getptn(uns igned / * 文字のドットパターンの取得 s truct SREGS segs; un i on REGS 88 : regs ; 89 : / * ファンクションコード設定 regs. h. ah = 0X14 ; 90 : / * 漢字を J I S コードで設定 regs. X. dx = code : 91 : regs. x. bx = FP—SEG (ptn) ; / * 格納工リアのセク・メント 92 : regs ・ x. cx = FP—OPF (ptn) ; / * 格納工リアのわセット 93 : return(int86x(0x18, ®s, ®s, &segs)) : 94 : / * CRT BIOS コール ( 0X18 ) 96 : 97 : int gprint(int ro 町 int C01 , char *Str, int tate, int YOkO, int c) ″文字列のグラフィック表示 99 : lnt x, y, len; 100 : if (row く 0 Ⅱ ro 響 > 24 ) 101 : / * パラメータェラー return ( の ; 102 : : 何もせずに戻る if (col く 0 Ⅱ C01 > 79 ) / * パラメータ工ラー 103 : return( の : 104 : : 何もせずに戻る 105 : len = strlen(str) ; / * 文字列長の取得 106 : / * 列より水平座標を計算 107 : x = C01 * 8 : / * 行より垂直座標を計算 y = ro 響 * 16 ; 108 : 109 : len ニ gpr intxy (), y, s tr, tate, YOkO, c) ; / * 文字列表示 110 : return ( len) ; / * 表示成功文字数を戻す 111 : 112 : } 113 : int gprintxy(int X, int y, char *Str, int tate, int YOkO, int C01 ) 114 : 115. 116 : uns igned code, byte, len, rtn ; 117 : Char *S : 118 : / * 文字列の先頭にポインタを合わせる * / 119 : str; S 120 : if (iskanji(*s)) { / * 漢字 ( 2 バイト文字 ) のチェック 121 : : 2 バイト文字の場合 122 : byte = 2 ; code = ( * s + + ) くく 8 : シフト J I S → J I S 変換 123 : 124 : COde 十 = * S 十十 : code = sjtoj(code) ; 125 : 126 : else { 127 : : 1 バイト文字の場合 128 : byte ニ 1 : code = 0X8000 + ( * s + + ) : 規約により 0X8000 を加える 129 : 事 / 130 : yoko, col); / * 漢字表示 rtn = kanjiprint(), y, code, 131 : tate, if (rtn ! = の break; / * 表示失敗時は終了 132 : / * 水平座標を再計算 x + = 8 * byte 事 yoko; 133 : 134 : return( len) ; / * 表示成功文字数を戻す 135 : 136 : } 137 : int kanjiprint(int x, int y, unsigned code, int tate, int yoko, int CO 1 ) 138 : 139 : ( uns igned i nt 140 : ptnmask, mask, 141 : byte, len; 142 : dotptn[10]; / * 1 列分の横方向拡大ドットバッファ * / char 143 : struct fontpattern { / * 文字パターン格納用構造体 144 : unsigned char c[2] ; 145 : unsigned char fontptnC32J : 146 : } font ; 147 : 148 : if (code 〉 = 0X800 の byte = 1 ; 1 バイトコード , 149 : / * 2 バイトコードの判別 150 : el byte = 2 ; / * 格納バッファ font の長さ len = 16 * byte; 151 : / 事 152 : / * 横倍角は、 5 倍まで。 if (yoko 〉 5 ) return(-l) : 153 : if (x + 8 事 byte * yoko > = 64 の / 本表示しきれないばあいは、 154 : 何もせずに戻る。 return ( ー 1 ) ; 155 : if (y + 16 事 tate > = 40 の / 事表示しきれないばあいは、 156 : 何もせずに戻る。 return(-l) ; 157 : 158 : getptn(code, (unsigned char *)&font) : / * 文字パターンの取得 159 : 160 : / * マスクの初期化 mask = 0X80 : 161 : / * 1 行分横方向拡大バッフアの初期化 * / 162 : for (i=0; i く 10 : i + + ) dotptn[i] 163 : for (j = 0 : j く len; j + + ) { / * 格納パッフアの長さ分繰り返し 164 : k = font. fontptn[j]; ドットパターン設定 165 : for (ptnmask = 0X80 ; ptnnask > 0 : ptnmask > > = 1 ) ( 166 : 1 : 6 : 7 : #include く stdio. h> 8 : #include く dos. h> り 9 : #define YES 1 10 : #define NO 0 り iskanji(c) ( ( 0X80 く ( c ) & & ( c ) く 0xA のⅡ (0xE0 ← (c)&&(c) く =0xFC)) 11 : #define 12 : sjtoj(int (j) ; int / * シフト J I S → J I S 変換 13 : getptn (uns igned int code, uns igned char *Ptn) : 14 : int gprint(int ro 響 , int COI, char *str, int tate, int yoko, int c) ; 15 : int gprintxy(int x, int y, char *str, int tate, int yoko, int C01 ) ; 16 : int kanjiprint(int x, int y, unsigned code, int tate, int yoko, int col) ; 17 : int lio(int liocmd) : / * L 1 0 割り込み 18 : VOid ginit(void) : 19 : VOid / * い 0- 開 D = AO 初期化 screen(int 田 , int sw, int act, int disp) ; 20 : VOid / * い 0 ー開 D ニ AI 画面モート・ view(int, int, int, int, char, char) ; 21 : VOid / * L10_CMD=A2 描画範囲 line(int, int, int, int, char, char, char,char) : / * L10_CMD=A7 直線 22 : VOid put(int x, int y, char *ptn, int len, int col) ; / * LIO_CMD=AC パターン 23 : VOid roll(int); 24 : VOid / * LIO_CMD=AE ロール clrline(int ro 響 , int lines); / * グラフィックの行消去 25 : VOid り rollup(int rows); / * ロールアップ 26 : VOid rolldown(int rows) : 27 : VOid / * ロールダウン 28 : 29 : char 響 ork [ 0X140 の ; / * グラフィック L 1 0 用ワークエリア * / / * LIO り一クエリアの ^ 。ラク・ラフ境界のセク・メント 30 : int WORK : / * WORK の先頭位置のアドレス 31 : VOid *PARAM ・ 32 : 33 : void main(void) 34 : { 35 : illt C, n; char dispC200] : 36 : printf("*x1b[2J ” ) ; 38 : / * テキストスクトンの消去 ginit(); 39 : / * LIO 初期化 screen(), 0 , 0 , 1 ) : 40 : / * スクリーンをト・初期化 ⅵ e 響 ( 0 , 0 , 639 , 399 , 0 , の ; / * 描画領域設定 41 : 42 : strcpy(disp, ”あいうえお " ) ; / * 表示文字列初期化 43 : gprint( 1 , 1 , 1 ) ; 44 : disp, 1 , / * 縦横 1 倍 0 , gpr int ( 2 , 2 , 2 ) ; 1 , / * 横 2 倍 45 : 0 , disp, gprint( 4 , 1 , 3 ) ; 46 : 0 , 2 , / * 繼 2 倍 disp, gprint( 6 , 2 , 4 ) ; 47 : / 事縦横 2 倍 0 , disp, 2 , gprint( 9 , 3 , 5 ) : 0 , 3 , / * 縦横 3 倍 disp, gprint(14, 4 , 6 ) ; / * 縦横 4 倍 0 , disp, 4 , 50 : 51 : getch(); clrline(l, 52 : 1 ) : / * 第 1 行目を 1 行分消去 rollup ( 1 ) ; 53 : / * ロールアップ 54 : 55 : getch() ; r011d0 響 n ( 1 ) ; / * ロールダウン 56 : gprint(), 0 , disp, 1 , 7 ) ; / * 1 行表示 1 , 58 : 59 : getch() ; 1i0 ( 0xA5 ) ; 60 : / * ク・ラフィックスクリーンの消去 printf( ” *xlb* ” ) ; / * テキストスクトンの消去 63 : 64 : int sjtoj(int sj) / * シフト J I S → J I S コード変換 * / / * 上位 , 下位バイト 66 : int jh, jl; り jh = ()j > > 8 ) & 0xFF; 68 : り 69 : = sj & OxFF; り 70 : if ()h > = 0xE の jh ー = 0X40 ; jh = ()h ー 0X81 ) * 2 + 0X21 : 72 : 73 : if ()l 〉 0x7f) jl- 74 : if ()l 〉 0x9D) ( 75 : 76 : jh + 十・ こ 0x9E 77 : ー 0X21 : 80 : / * シフト J I S コードを戻す 82 : else { = 0X40 ー 0X21 : return((jh くく 8 ) + jl); C MAGAZINE 146 1993 1
複数のプログラムて、使うとなると , そのプ ログラムはポジションインデイベンデント て、なければいけません。 CPU が 6809 なら簡 単てすが , セグメントべースの 8086 て、この AH=08h ようなプログラムを書くことは容易て、はあ りません。よって , HMA を効率よく利用て、 AH=09h きるようにするため , 使用サイズの最低ラ AH:OAh インを設定する API ファンクションが用意さ れているわけて、す。 AH:OBh ひとつだけなら , それは DOS のシステム AH=OCh が専有すべきて、 , 常駐ソフトなどが使うの はおこがましいといえるかもしれません。 DOS3.3 ても Mr. No 氏作の DOSHIGH ( 付 録ディスク収録 ) を使用すれば HMA に DOS AH:OEh を移動することが可能て、す。 HMA 関連の API ファンクションは Table AH:OFh 7 に示すように , 使用要求 / 解放と A20Line 関 連の制御だけてす。使用要求は必要サイズ を DX レジスタに入れてコールします。この 値と XMS ドライバを組み込むときオプショ ンて、設定した最低使用サイズとを比較して , 最低サイズより大きな要求サイズのときに かぎり割り当てを許可します。 A20Line が OFF て、すとセグメントを FFF FH にしても HMA にアクセスて、きません。 そこて、 HMA にアクセスする際には , A20L ine を ON にするためのファンクション 05H (A20Line のローカルな有効化 ) を使用しま す。 このファンクションは ,A20Line が ON 状 態のときに実行するとカウンタを 1 アップ し , ファンクション 06H (A20Line のローカ ルな無効化 ) が実行されるとそのカウンタを 1 ダウンし , カウンタが 0 ー > 1 の変更のと き , A20 を物理的に ON し , カウンタが 1 ー > 0 の変更のときは A20Line を OFF する機構と なっているのて、 , 実行前の A20Line がどのよ うな状態てあっても , ファンクション 05h と 06h を組みて、使用すれば元の状態にもどすこ とがてきます ( 一部の XMS ドライバて、これが 正常に実行て、きず。 ZMAP の作成において ずいぶん苦労しました ) 。 ZMAP< は , XMS ドライバが XMZ ( 筆者 72 C MAGAZINE 1 3 1 TabIe 8 XMS A 円ファンクション ( EMB 関連 ) Fanction 機能 EMB 関連のファンクション 08h 09h OAh OBh OCh ODh OEh OFh 空き EMB 空き工リア 状態取得 EMB の割り当て EMB の解放 EMB プロック転送 EMB のロック EMB のロック解除 EMB 八ンドル情報の 取得 EMB の再割り当て コール DX : 割り当て EMB サイズ のノ、ンドル DX= 解放する EMB ( K バイト単位 ) DS : S 転送バラメータ 構造体のアドレス DX:EMB の八ンドル EMB の八ンドル DX= ロック解除する AH:ODh 八ンドル DX : ロックする EMB の の八ンドル DX = 再割り当てする EMB サイズ ( K バイト単位 ) BX : 再割り当ての EMB の TabIe 9 XMZ オリジナル拡張ファンクション リターン AX : EMB 最大空きメモリプロック のサイズ DX : EMB 空きメモリの全サイズ AX : 0001 h 割り当て成功 0000h 割り当て不成功 AX : 0001 h 再割り当て成功 DX ニ EMB のサイズ ( K バイト単位 ) BL : EMB の空き八ンドル数 BH ニ EMB のロックカウント 0000h 情報取得不成功 AX ニ 0001 h 情報取得成功 0000h ロック解除不成功 AX : 0001 h ロック解除成功 : 0000h ロック不成功 AX ニ 0001 h ロック成功 : 0000 h 転送不成功 AX ニ 0001 h 転送成功 ニ 0000h 解放不成功 AX : 0001 h 解放成功 DX : 割り当てメモリプロックの八ンドル ニ 0000h 再割り当て不成功 リターン Fa nction FFOI h FF02h 機能 H M A 情報取得 EM B 情報取得 コール AH:FFh AL ニ 01 h AH:FFh AL : 02h 作 ) のときには , 内部変数領域をアクセスす ることにより , 最低使用サイズの表示も行 います (Fig. 40 参照 ) 。 IM ′ト以上の 巨大なメモリ空間 IB EMB は巨大なメモリ空間を管理するメモ リ規格てす。 i386 / i486 は理論上なら 4G バイ トものメモリのアクセスが可能て、すが , EM B が扱えるメモリ空間の大きさは 64M ー IM ー 64K バイトて、す。ただ , XMS の規格は 8028 6 を基本にしているため , 16M バイト以上の メモリ領域て、の使用は不完全なものと思わ れます。 16M バイトだとしても , このメモ リエリアは広大て、す。 DOS5 に付属の仮想 8 6EMS 工ミュレータ EMM386. EXE は , EM BX : HMA 使用サイズ ( K バイト ) DX : HMA 最小サイズ ( K バイト ) BX = EMB 八ンドル格納オフセットアドレス セグメントアドレスは XMS コールアドレスと同じ DX=EMB 最大ハンドル数 B によるメモリ確保を行い , そのメモリを E MS として割り当てています。よって VCPI も EMB て、管理されたメモリを使うことにな ります。 EMB におけるメモリプロックは EMB ハン ドルによって管理されており , その構造体 は以下のようになっています。 struct XHANDLES { unsigned char flags , //UNUSED(4)/USED(2)/FREE(I) unsigned char locks //Lock count unsigned short base //BIock start Address K バイト単位 unsigned short len //Block Length K バイト単位
特集 M 0S メモリ活用蠡 TabIe 1 DOS ファンクション 52h ES . BX 19 : { オフセット ES . BX 十 OCh ES . BX 十 08h ES : BX 十 04h ES . BX -2h ES . BX -8h ES : BX 十 1 7 h ES : BX 十 1 3 h ES : BX 十 1 1 h ES : BX 十 1 Oh DOS 2.1 固有変数 サイズ 2W0 「 d 1 word 2W0 「 d 2W0 「 d 2 wo rd 2 wo rd 1 バイト 1 wo rd 2W0 「 d 1 8 バイト DOS 3.1 ー 5.0 固有変数 ES . BX 十 34h ES . BX 十 22h ES . BX 十 21 h ES . BX 十 1 Ah ES : BX 十 1 6h ES . BX 十 12h ES : BX 十 1 Oh ES ES BX 十 1 Eh BX 十 20h DOS 5.0 固有変数 ES : BX 十 3Fh ES : BX 十 41 h ES : BX 十 43h ES : BX 十 45h WORD WORD BYTE WORD 1 wo rd 2W0 「 d 2 wo rd 2 word 1 word BYTE BYTE 18 バイト BYTE 解説 先頭 BUFFERS を指すポインタ 接続されているドライプのセクタの最大サイズ NUL テパイスドライバ本体 先頭 BUFFERS を指すポインタ 接続されているドライプのセクタの最大サイズ 接続ドライプ数 CON テパイスド 1 ライバのヘッダアドレス CLOCK テパイスドライノヾのヘッダアドレス 先頭日 LES を指すホインタ DPB を指すポインタ MCB の先頭プロックへを指すポインタ カレントティスクバッフアを指すポインタ 設定以上の FCB をオープンしようとしたとき , クロ FCB を指すポインタ ポインタ カレントティレクトリイを格納してある配列を指す プートドライプ ( 1 : A . ) 先き読み BUFFERS 数 BUFFERS 数 JO 爪されているドライプ数 NUL テパイスドライバ本体 LASTDRIVE 指定値 プロックテパイスの数 ーズしてはならないファイル数 工ックステンド ( プロテクト ) メモリサイズ K ノヾイト単位 MS ー DOS 未公開内部変数のアドレス取得ファンクション AH:52hlNT21h MCB 表示プログラム ( MEMI ℃ ) 返り値 : ES : BX : MS - DOS 内部変数アドレス LlSt 4 : #ifndef MK_FP 2 : #include く dos. h> ー 1 : #include く stdio. h> 3 : 7 : 靆 0 : は 3 : 14 : 18 : 20 : 22 : 23 : , 11 : struct ( 9 : unsigned short WORD; 8 : unsigned char BYTE; 6 : #endif 5 : #define MK-FP(), 0 ) ((void far*) (((unsigned 10 ) ( s ) くく 16 ) short) ( 0 ) ) ) BYTE reserve[ll] ; WORD s ize; WO ownerps p : BYTE mcbid; int } MCB; main() WORD m_seg : MCB far * 叩 : _asm mov し _asm int 21h ; 52h : DOS3 における UMB は DOS の管理外のメ モリ仕様なのて、 , UMB の先頭アドレスを D OS のファンクションを使って得ることがて、 きません。このため , ZMAP て、は MCB の最 上位から , F0000h まて、 10000h まて、 MCB の m cbID の、、 M 〃または、、 Z 〃をサーチし , 、、 Z 〃 のときは ownerPSP が 0 かどうか , 、、 M クのと きは次の mcb リンクの先頭い、 M 〃またはツ〃 になっているかどうかて、 , UMB の開始セグ メントをサーチしています。 UMB の先頭 M CB が見つかれば , その後は通常の MCB リン クと同じなのて、 , コンべンショナルメモリ と同様の方法て、メモリ情報を得ています。 UMB のような小さなメモリ空間て、も UM B ューティリティにより , デバイスドライ バ , 常駐ドライバを UMB ェリアに移動すれ ば , かなり有意義なものとなります。 Fig. 8 に示すように , UMB ユーティリテ ィと仮想 EMS ドライバの / UMB オプション により , C0000h—DFFFFh 中 <UMB として 利用て、きるエリアを自動的に検出してメモ 特集 MS-DOS メモリ活用技法 57 h(TabIe 1 参照 ) を使い , 内部変数アドレス の先頭アドレスは , DOS ファンクション 52 にあることがわかります。この BUFFERS し , それ以外の BUFFERS は 101Eh : 0000h 0FB7h : 0000h は DOS のシステム内に位置 力を示します。 BUFFERS のひとつだけは リアを表示て、きます 0Fig. 10 に ZMAP の出 ZMAP は DOS3 て、あっても BUFFERS の工 ってはかなりのメモリエリアを専有します。 のサイズとなりますのて , 設定した値によ プの最大セクタサイズ十 16 が 1 BUFFERS 置します。 DOS3 て、は接続されているドライ S = 」の領域はコンべンショナルメモリに位 CONFIG. SYS て、指定する「 BUFFER DOS の BUFFERS る UMB の状態を Fig. 9 に示します。 C ー 9801 ノーマルモードにおける ZMAP によ S を割り当て , SCSI ハードディスク搭載の P リを割り当てます 0C0000h—CFFFFh に EM
[ 問 1 8 ] のプログラム例 Li st [ 問 13 ] は文字列の処理だから C らしいプロ グラムになりそうなものて、すが , どちらも ポインタをポインタらしく使っていません。 単に配列名として使っているだけて、 , 結局 添え字によってアクセスしています。いく ら * ()p 十 j) と書いたってこれは a [j] のこ となんて、すから。 また , 各間題には「ただし , アドレスを表 すときに , & 演算子は用いないこと」という ただし書きがあります。配列 a の j 番目の要素 のアドレスの書き方には a 十 j, &a Cj] , & * ・・というふうに無限 のバリエーションがありますから , まあこ れに関しては採点者の労力を減らすという ことて、許しましよう。て、もこう書いてある ってことは & i が答えになることは絶対にな いんだろうなあ。 ( a ) 問題の意図にそった解答例 struct srec *search(struct srec *hashtabC], char *s) struct srec *p; p = hashtabChash(s) ] ; while (p ! = NULL & & strcmp(p->name, s) ! = の p = p—>next; return p; (b) こちらのほうが見やすいとは思いませんか ? struct srec *search(struct srec *hashtab[], char *s) struct srec *p; for (p = hashtabChash(s)] ; p ! = NULL; p = p- 〉 next) if (strcmp(p->name, s) return p; return NULL; 〔プログラム〕 # i nc 1 ude く s t d i 0 . h> # i nc 1 ude く s t 「 i ng . h> #def ine HASHSIZE 63 s t 「 uc t s 「 e c { S t r uc t S 「 ec *nex t : i n 0 ′ i : char ロロ レコード NULL next 560 name hashtab 0 1 NULL ー N K ・ R X \ 0 NULL 155 VO i d dp 「 i ce ( c ha r * , S t 「 uc t S 「 00 * [ ] ) : unsigned hash(char * ) : s t 「 uc t s 「 ec *sea 「 ch ( s t 「 uc t S 「 ec * , cha 「 * ) : / * 商品名 sname を検索し , その単価を表示する * / VOid dprice(char struct srec *hashtabCJ) u n S i g n e d ー : S t r uc t S 「 ec *S r ecp : 198 280 - M 1 6 \ 0 P E N srecp = search(hashtabti]' ー ) : i f ( s 「 ecp ! = NIJLL ) printf( ・ %s = ツ記 \ n 第 sname. e 1 S e printf(*%s 09T FOlJND\n•, ー ) : / * 文字列 s のハッシュ値を返す関数 * / uns igned hash(char *S) unsigned i = wh 冂 e ( *s ! = 「 e t し「 n i % HASHS I ZE : T A P E / 3 R 4 0 \ 0 NULL 580 62 120 - R W \ 0 ー N K N 0 T E - A S \ 0 図商品名・単価のデータ構造 ( 2 ) 同一商品名をもっレコードは , 複数個存在しない。 ( 3 ) 表示の形式は " 商品名 = 単価 " とする。 例 : PEN-M16 = 198 商品名がリスト中に見つからない場合は , " 商品名 NOT FOUND" と表示する。 〔設問〕プログラム中のを埋めて , プ。グラムを完成せよ。 ただしアドレスを表すときに , & 演算子は用いないこと。 ハ 0 ′ ) / * p が指すレコードのリストから商品名 s のレコードを探す関数 * / struct srec *search(struct srec *P' char *s) whiIe((CZCQZZ]!= NULL) (strcmp(p—>name.s) ! = 「 e t u 「 n p : Conference Room 159