PC-DOS 2000 で遊ぶ -- その3 DOSCALLSライブラリーのバージョンアップ2020年08月03日 14:56

PC-DOSのDOS機能呼び出しを楽に使うためのライブラリー製作の続きです。 まずは、申し訳ございません。配慮が足らず多くのバグを作り込んでしましました。 実際に使って見て、DOSCALLSライブラリーでは、レジスタを保存することにしました。 また、DOSが検出したエラーを容易に判断できるように、DOSが返してきた、キャリー フラグとAXレジスタの値を確実に返すこととしました。

コマンドラインの引数を走査する、getAegv,getArgcは、スパゲッティーボールなところがあり、 気に入らなかったので、引数を分解するロジックを作り直しました。見通しが良くなったと 思います。

更に、ファイル操作があまりにも貧弱なので、ファイルの基本操作を容易にする FILELIBを追加しました。FILELIBは、少し高級言語風のファイルの操作ができます。

	INCLUDE	CONSTANT.INC
	INCL_FILELIB EQU	0
	INCLUDE	DSOCALLS.MAC
	INCLUDE	DSOCALLS.DEF

	BUFSIZ	EQU	80

	AFileName DB	"FileName.DAT", EOS
	Handle	DW	0
	AccessMode DW	FL_READ ; 読み出しでオープン。このほかに、
			; FL_WRITE     書き込みでオープン
			; FL_APPEND    追加書き込みでオープン
			; FL_READWRITE 読み書き用にオープンで、オープンできる。
	Buffer	DB	BUFSUZ DUP(BLANK)
	BSize	DW	BUFSIZ
	
	
		@Open	Handle, AFileName, AccessMode	; ファイルをオープン
		JC		DOSErr	; DOSのエラー検出
		CMP		AX, FL_NOERROR
		JNE		FILELIBErr	; FILELIBのエラー検出

		@get	Handle, Buffer, Bsize	; BufferへBSizeバイト読み取り
					; BSizeには、実際に読み取ったバイト数が返る
		JC		DOSErr	; DOSのエラー検出
		CMP		AX, FL_NOERROR
		JNE		FILELIBErr	; FILELIBのエラー検出

		@Close	Handle		; ファイルをクローズ
		JC		DOSErr	; DOSのエラー検出
		CMP		AX, FL_NOERROR
		JNE		FILELIBErr	; FILELIBのエラー検出

	DOSErr:
		; AXを見て、エラーを判断しリカバリー処理

	FILELIBErr:
		; AXを見て、エラーを判断しリカバリー処理

この様な感じです。

ファイルの読み書きは、ブロック読み出し/書き込み、バイト単位の読み出し/書き込み、 行単位の読み出し/書き込みが有ります。

		@get	Handle, Buffer, Size
		@getchar handle, char
		@getline handle, line, length

		@put	handle, Buffer, Size
		@putchar handle, char
		@putline handle, line

@get,@getchar,@getlineは、ファイルの終わりを検出します。@getlineは、現在のファイル 位置から行末までを読み取り、最後にEOSを追加します。行末は捨てられます。 バッファーに入りきらない場合、切り捨てます。

@put,@putchar,@putlineは、DISKFULLを検出します。@putはサイズ分書き込めなかった場合は、 書き込めたバイト数をsizeに返します。@putlineは、EOSで終端されている文字列を書き出し、 行末を追加します。

備忘録をかねて、必要そうな情報をDOSCALLS.TXTにまとめました。製作したライブラリーの インターフェイスの使い方をまとめてあります。

DOSCALLSライブラリーは、これ DOSCALLS.ZIPです。このパッケージには、DOSCALLSライブラリーのテストプログラムと サンプルもまとめてあります。ご参照ください。

PC-DOS 2000 で遊ぶ -- その2 DOSCALLSライブラリー2020年04月29日 18:22

随分昔のことです。IBM JXを手に入れた頃、アセンブラでプログラミング に挑戦しました。8086アセンブラを理解するには、経験値が少なくて、無謀 な挑戦でした。8086マクロアセンブラーやDOSの内部構造やプログラムインタ ーフェイスの資料など、いろいろと集めていたようで、手元に残ってます。 今回はよい機会と思い、アセンブラプログラミングに再挑戦しました。

目標をDOSのファンクションコールを使ったプログラムの作成をサポート するライブラリーを作ることとしました。生でファンクションコールを使お うとすると、マニュアルを参照しながらレジスタに値を設定し"INT 21H"を 実行することになりますが、もう少し見通しのよいやり方を模索してみまし た。結果、マクロとファンクションコールのライブラリーを作り込みました。

アプリケーションからファンクションコールを使うには、マクロ呼び出し を使い、ファンクションコールライブラリーをリンク時にリンクする事にし ました。今回は、FCBを使ったファイルの操作や代替のある古いファンクショ ンコールなど、割愛した機能があります。

まずは、サンプルプログラム"xchdir"から抜き出したコードをご覧くださ い。@DosCurDisk、@DosChdir、@DosDispString、@DosExitが今回作成したマ クロの呼び出しです。これらのマクロに引数を渡すことにより、高級言語っ ぽくコードを書く事ができます。

main            PROC
                MOV     AX, SEG myData  ; setup DS
                MOV     DS, AX

                @getArgc argc
                CMP     argc, 1
                JE      main1
                @DosDispString errMsg1
                JMP     main0
main1:          
                @getArgv argc directory
                MOV     SI, OFFSET directory
                CMP     BYTE PTR [SI+1], ':'
                JNE     main2
                MOV     AL, BYTE PTR [SI]
                MOV     drive, AL
                @DosCurDisk drive
                JNC     main2
                @DosDispString errMsg2
                JMP     main0
main2:
                @DosChdir directory
                JNC     main0
                @DosDispString errMsg3
                
main0:          
                MOV     rc, 00H
                @DosExit rc
main            ENDP

マクロの引数は、アドレス渡しで統一されていますので冗長になってし まう部分がありますし、また、ライブラリーを呼び出すオーバーヘッドが生じ ますが、プログラムの見通しを良くする手段として妥当であると判断しました。

作成したファンクションコールライブラリーは、 DOSCALLS.ZIPです。 サンプルプログラムは、 SAMPLES.ZIPです。ご参照下さい。

ファイルの操作に関しては、もう一枚、ラップする必要がありますね。 少なくとも、入出力エラーを含めることと、一文字単位の入出力や行単位の 入出力は欲しいところです。実際、"typeout.asm"では、不完全ですがこれら のルーチンを作成しています。 

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

-- PureMind -- 日本語でプログラミング(その2)2020年02月16日 15:12

Mindでのプログラミングにだいぶ慣れてきました。前回掲載した練習プログラムも 実用レベルになりましたので、再掲載します。日常使いしています。

※ which.src -- 環境変数PATHに設定されたディレクトリを辿って指定されたファイルを探す。
※ 見つからない時は、何も表示しない。
※ ファイル名が'\'で始まる場合、'\'を無視する。

フルパス名文字列は 文字列実体 長さ 64。
元パス文字列は 文字列実体 長さ 128。

使い方表示とは
        "使い方: WHICH ファイル名"を 表示し 改行すること。

メインとは
        ファイル名文字列は 文字列

        起動パラメータを ファイル名文字列に 入れ
        ファイル名文字列が 空列? ならば
                使い方表示し 終り
        つぎに
        
        ファイル名文字列の 左端文字が '\'と 等しい ならば
                1だけ ファイル名文字列から 切り出し
        つぎに
        
        "PATH="で 環境変数の検索し
        環境変数を 元パス文字列に 入れ
        1だけ 元パス文字列を 右側取り出し ";"と 等しい文字列 でなければ
                元パス文字列に ';'を 一文字追加し
        つぎに
        
    ここから
        元パス文字列が 空列? ならば 打ち切り
        つぎに
            元パス文字列から ';'を 一文字検索
            元パス文字列を 分断し フルパス名文字列に 入れ
                        1だけ フルパス名文字列を 右側取り出し "\"と 等しい文字列 でなければ
                                '\'を フルパス名文字列に 一文字追加し
                        つぎに        
                        ファイル名文字列を フルパス名文字列に 追加し
                        フルパス名文字列 ファイル有り? ならば
                            フルパス名文字列を 大文字変換し
                            フルパス名文字列を 表示し 改行し
                                打ち切り
                        つぎに
        繰り返す。

このほかに、バッチファイルで使用するプログラムを2本作りました。バッチプログラムを組んでいると、 処理が終わった後に、現在のディレクトリ戻ってくるようにしたくなるときがあります。この様なときに 使用する小品です。一つは、現在のディレクトリを保存する"pushcdir.src"、保存したディレクトリに 移動する"popcdir.src"です。いずれもシンプルな物ですが、結構使い勝手があります。

まずは、"pushcdir.src"から。現在のディレクトリを取得してファイルに保存するだけです。

※ pushcdir.src -- 現行ドライブの現行ディレクトリを"C:\$$CDIR$$.TXT"に書き出す。

現行ディレクトリは 文字列実体 長さ 64。
現行ディレクトリ保管先は ファイル。

メインとは
        カレントドライブを 現行ディレクトリに 一文字追加し
        現行ディレクトリに ':'を 一文字追加し
        カレントドライブの 指定ドライブのカレントディレクトリを 現行ディレクトリに 追加し
        "C:\$$CDIR$$.TXT"で 現行ディレクトリ保管先を 新規オープンし
        現行ディレクトリを 現行ディレクトリ保管先に 一行書き込み
        現行ディレクトリ保管先を クローズする。

"popcdir.src"はこの様になっています。

※ popcdir.src -- "$$CDIR$$.TXT"ファイルから復元するディレクトリーを読みだし、
※ 現行ディレクトリーを変更する。

復元ディレクトリ保管先は ファイル。

メインとは
        復元ディレクトリは 文字列

        "C:\$$CDIR$$.TXT"で 復元ディレクトリ保管先を オープンし
        復元ディレクトリ保管先から 一行読み出し
        復元ディレクトリ保管先を クローズし
        読み出し文字列を 復元ディレクトリに 入れ
        復元ディレクトリ 0を 指定ドライブにカレントディレクトリ設定する。

この様にしてバッチファイルの中で使用します。

	@echo off
	pushcdir
	chdir \directory1
	JOOB1
	chdir \directory2
	JOB2
	popcdir
	@echo on

まず現在のディレクトリを保存し、directory1に移動してJOB1を実行し、 次にdirectory2に移動してJOB2を実行し、元のディレクトリに戻ります。

-- PureMind -- 日本語でプログラミング2020年02月05日 18:48

Pure Mind 5.1のマニュアル類

1985年頃でしょうか、パソコンが普及し始めたころ、日本語Basicとか「日本語」を冠したプログラミング 言語が沢山ありましたが、これらはダブルバイトの文字をデータとして取り扱えるとかコメントに書き込めることを 意味していました。この様な中で純粋な日本語でプログラムを書き下すことができる言語がありました。 Rigy Corporation/Micro Software Associatesから販売されていた「Mind」です。 当初はNECのPC-9801向けのパッケージでしたが、1990年頃には機種依存部分を取り除いた「Pure Mind」が 販売されました。当時から言語オタクであった私は後先考えず手を出しました。当時の私のパソコンには、 ハードディスクが無く、かろうじて日本語入力ができる程度でしたので、Mindのプログラムを心置きなく プログラムを書くには貧弱でしたので、いつしか、Mindから遠ざかってしまいました。

机を整理していたときに、Mindの資料やディスケットが出てきて懐かしく思い調べてみたところ、 DOS/Vの機能を使うためのパッケージなどが追加されて、Free WareとしてVectorから配布されていたことが 分かりました。現在は、Windows版が配布されています。PureMindをもう少し調べてみると Scripts Lab. Inc.にたどり着きました。最近は検索エンジンに 使われていたり、Androidのアプリ開発ができたりと進化しています。

DOS/V用の追加パッケージを入手したくダメ元で問い合わせたところ、Mindの開発者、片桐明氏から直々に メールをいただき、DOS/Vのパッケージだけでなく、PureMind 5.2基本パッケージまで入手することができました。 ありがとうございます。

早速DOS/V機にインストールし、プログラム作成の練習。Mindはforthと同様な構造で プログラムを組み立てます。短いロジックならばそのまま書けるのですが、ちょっと込み入ってくると、 お手上げ状態です。なんとか練習プログラムを完成させることができましたので、公開します。いわゆる"Which"です。 PATHに設定されたリストをたどって、引数の実行ファイルを探すプログラムです。

プログラムはこの様な感じです。 しゃべり言葉で書き下した感じがするプログラムになりました。なんとなく分かっていただけでしょうか。

フルパス名文字列は 文字列実体 長さ 64。

使い方表示とは
        "使い方: WHICH ファイル名"を 表示し 改行すること。

メインとは
    元パス文字列は 文字列
        ファイル名文字列は 文字列

        起動パラメータを ファイル名文字列に 入れ

        ファイル名文字列が 空列? ならば 使い方表示し 終り
        つぎに
        
        "PATH="で 環境変数の検索し
        環境変数を 元パス文字列に 入れ
        
    ここから
        元パス文字列が 空列? ならば 打ち切り
        つぎに
            元パス文字列から ';'を 一文字検索
            元パス文字列を 分断し フルパス名文字列に 入れ
                        '\'を フルパス名文字列に 一文字追加し
                        ファイル名文字列を フルパス名文字列に 追加し
                        フルパス名文字列 ファイル有り? ならば
                            フルパス名文字列を 大文字変換し
                            フルパス名文字列を 表示し 改行し
                        つぎに
        繰り返す。