PC-DOS 2000で遊ぶ2020年03月07日 14:25

最近は、DOSで遊んでいます。DOS-BOXではなくネイティブのPC-DOSS 2000です。

仕事で最初に使ったDOSは、PC-DOS 2.11でした。360KBのディスケット1枚に収まって いたように思います。HDDナシでも使用することができました。 また、コマンドも最低限度の物しかなく、バッチファイルを沢山作って対応したものです。 現在のPC-DOS 2000は、CD-ROMか11枚のディスケットからHDDにしかインストールできな くなっています。また、非常に多くの機能を含んでいます。ものすごい進歩です。

久しぶりにDOSをインストールし動かして昔を懐かしんでもつまらないので、 過去にいじってみて断念したPSP(プログラム・セグメント接頭部)を使ったコマンドラインの 引数や環境変数の取り込みモジュールを作成してみました。

Cでかくと、コマンドラインの引数はargcと*argv[]で、環境変数はgetenv()で、それぞれ 取得することができますが、PSPを使ってこれらの機能を実現してみたかったのです。もちろん アセンブラで書きます。

PSPは、DOSからアプリケーションに制御が移った時点のDSとESにPSPが配置されている セグメントアドレスが入っています。オフセットは0000Hです。このほかに、 DOSのファンクションコール62HでBXにセグメントアドレスを得る事ができます。 今回は、ファンクションコールを利用しました。

環境変数は、PSPのオフセット2CHから1ワードに環境変数が配置されている セグメントアドレスが入っていて、オフセット0000Hから環境変数が入っています。

コマンドラインの引数は、PSPのオフセット80Hにコマンドラインの文字数(コマンドの次の ブランクからおしりの0DHまで)が、コマンドラインの文字列がオフセット81Hからに入っています。 この領域は、省略時のディスク転送領域(DTA)も兼ねているので、ファイルを操作するプログラムでは 破壊されてしまいますので、要注意です。今回はモジュール内部にコピーを取っています。

コマンドラインの引数は、デリミタ文字(スペース、タブ、カンマ)で区切られており、 クオート文字(シングルクオートかダブルクオート)で始まる場合は次のクオート文字までを ひとまとめとして取り扱います。コンマでパラメータが始まっている場合、 最初の引数はヌルストリングと見なします。また、コンマが二つ続く場合はコンマの間に ヌルストリングがあると見なします。引数はとりあえず20まで扱えるようにしてありますが、 CONSTANT.INC中のMAXARGSを変更し再ビルドすることで、増減できます。引数一つあたり1ワード 消費します。

引数の例

          abc "DEF ghijk"  ---> [abc]、[DEF ghijk]の二つ ("["、"]"は目印の文字)
          , 123<TAB>567 ------> []、[123]、[567] (<TAB>はタブ文字)
          ABCDEF ,  , XYZ ----> [ABCDEF]、[]、[XYZ]
          XYZ "abc'def" ------> [XYZ]、[abc'def]
          abc 'def'ghi -------> [abc]、[def]、[ghi]
          xyz"abc<TAB>def ----> [xyz]、[abc<TAB>def]

作ってみたところ、マクロや定数を含めて約500行のプログラムができあがりました。 完成した物は envaergs.zipです。

作り込んだのは、@getArgc,@getArgv,@getEnvの三つです。

@getArgcは、引数の個数を返します。

@getArgvは、指定された位置の引数を返します。

@getEnvは、指定された環境変数名の値を返します。

サンプルを2本作りました。

コマンドラインの引数を一行に一つづつ表示する"args.asm"。これは引数 を取得するサンプルです。引数の書き出しは目印に"["と"]"で囲んでいます。

もう一つのサンプルは、"printenv.asm"です。これは、引数で受け取った環境変数名を 探しだしその値を書き出します。変数名とその値は目印"["、"]"で囲んで表示します。

printenv.asmは以下の通りです。ちなみに関数の呼び出しは全てマクロでラップしてあります。 いかがでしょうか。

                PAGE    ,132
                TITLE   PRINTENV.ASM -- Test Stub
                %OUT PRINTENV.ASM
                INCLUDE CONSTANT.INC
                INCLUDE ENVARGS.INC

myStack         SEGMENT PARA STACK
                DB      128 DUP("@")
myStack         ENDS

myData          SEGMENT PARA PUBLIC 'DATA'
Msg0            DB      "[", EOS
Msg1            DB      "]=[", EOS
Msg2            DB      "]", CRCHAR, LFCHAR, EOS
Argc            DB      0
ArgIndex        DB      0
EnvName         DB      128 DUP('*'), EOS
EnvValue        DB      128 DUP('@'), EOS
myData          ENDS

myTest          SEGMENT PARA
                ASSUME  CS:myTest, DS:myData, ES:myData, SS:myStack
main            PROC
                MOV     AX, myData
                MOV     DS, AX

                MOV     Argc, 0
                MOV     ArgIndex, 1

                @getArgc Argc
main2:
                MOV     AL, ArgIndex
                CMP     AL, Argc
                JG      main1

                MOV     SI, OFFSET Msg0
                CALL    putstr

                @getArgv ArgIndex, EnvName
                MOV     SI, OFFSET EnvName
                CALL    toupper
                CALL    putstr

                MOV     SI, OFFSET Msg1
                CALL    putstr

                @getEnv EnvName, EnvValue
                MOV     SI, OFFSET EnvValue
                CALL    putstr

                MOV     SI, OFFSET Msg2
                CALL    putstr

                INC     ArgIndex
                JMP     main2
main1:
                MOV     AL, 0
                MOV     AH, 4CH
                INT     21H
main            ENDP

toupper         PROC
                MOV     DI, SI
toupper2:
                MOV     AL, BYTE PTR [DI]
                CMP     AL, EOS
                JE      toupper1
                CMP     AL, 'a'
                JL      toupper3
                CMP     AL, 'z'
                JG      toupper3
                SUB     AL, 'a'-'A'
                MOV     [DI], AL
toupper3:
                INC     DI
                JMP     toupper2
toupper1:
toupper         ENDP

putstr          PROC
putstr2:
                MOV     AL, BYTE PTR DS:[SI]
                CMP     AL, EOS
                JE      putstr1
                MOV     BX, STDOUT
                MOV     CX, 1
                MOV     DX, SI
                MOV     AH, 40H
                INT     21H
                INC     SI
                JMP     putstr2
putstr1:
                RET
putstr          ENDP

myTest          ENDS
                END     main