Modula-2でプログラミング -- FileSearch ファイルを探すモジュールの制作2024年10月19日 09:49

ファイルを探すことは、結構よくあることです。CP/Mでプログラムを作る場合でも同じこと。 今回は、Modula-2からBDOSの機能を呼び出し、ファイルを探す"FileSearch"モジュールを紹介します。

"FileSearch"モジュールは、処理系が提供するモジュールをいくつか使用しています。

  • "OpSys"モジュールは、BDOSやBIOSを呼び出すためのBdos手続きやFCBなどのレコード型などを提供しています。
  • "FileNames"モジュールは、FCBのファイル名領域にファイル名を設定する手続きや、その手続きの戻り値の型を提供しています。
  • "SYSTEM"モジュールは、メモリーの動的確保や変数のサイズなどを調べる手続きを提供しています。

このほかに自作モジュール"BdosStruct"があります。これは、BDOSを呼び出すBdos手続きがちょっと使いにくかったので、 処理系を騙す仕組みを作り込みました。

"FileSearch"モジュールのDefinition Moduleです。

DEFINITION MODULE FileSearch;

FROM InOut IMPORT Write,WriteString,WriteLn;
FROM OpSys IMPORT FCB,CPMStringBuffer,BdosFunctions,Bdos;
FROM SYSTEM IMPORT ALLOCATE,SIZE,ADR,WORD;
FROM FileNames IMPORT StrToFCB,NameState;
FROM BdosStruct IMPORT BdosCommand,BdosReturn;

EXPORT QUALIFIED pFileList,FileList,FileStruct,DMA,
        LoginDisk,MakeFileList,DumpFileList;

CONST
        DMABufferSize = 128;

TYPE
        FileStruct = RECORD
                CASE BOOLEAN OF
                        TRUE:   Disk: CHAR;
                                Fill1: CHAR;
                                Name: ARRAY [0..7] OF CHAR;
                                Fill2: CHAR;
                                Extention: ARRAY [0..2] OF CHAR;
                |       FALSE:  Text: ARRAY [0..13] OF CHAR;
                END;
        END;
        pFileList = POINTER TO FileList;
        FileList = RECORD
                File: FileStruct;
                Next: pFileList;
        END;
        DMA = ARRAY [0..DMABufferSize-1] OF CHAR;

PROCEDURE LoginDisk(FCBBuf: FCB): CHAR;
PROCEDURE MakeFileList(FileMatch: ARRAY OF CHAR): pFileList;
PROCEDURE DumpFileList(pList: pFileList);
END FileSearch.

Imprementation Moduleは、このとおり。

IMPLEMENTATION MODULE FileSearch;

FROM InOut IMPORT Write,WriteString,WriteLn;
FROM OpSys IMPORT FCB,BdosFunctions,Bdos;
FROM SYSTEM IMPORT ALLOCATE,SIZE,ADR,WORD;
FROM FileNames IMPORT StrToFCB,NameState;
FROM BdosStruct IMPORT BdosCommand,BdosReturn,FileStruct,DMA;

PROCEDURE LoginDisk(FCBBuf: FCB): CHAR;
VAR
        BdosCmd: BdosCommand;
        Junk: WORD;
        DiskCode: BdosReturn;

BEGIN
        IF FCBBuf.name.disk = 0C THEN
                BdosCmd.Func := retCDsk;
                Bdos(BdosCmd.Cmd,Junk,DiskCode.Rc);
                RETURN (CHR(ORD(DiskCode.Cc)+ORD('A')));
        ELSE
                RETURN (CHR(ORD(FCBBuf.name.disk)+ORD('A')-1));
        END;
END LoginDisk;

PROCEDURE MakeFileList(FileMatch: ARRAY OF CHAR): pFileList;
VAR
        Disk: CHAR;
        FCBBuffer: FCB;
        BdosFunc: BdosCommand;
        BdosRc: BdosReturn;
        DMABuffer: DMA;
        ToPos, FromPos: CARDINAL;
        NameStatus: NameState;
        pList: pFileList;
        pNewFile: pFileList;
        NewFile: FileList;
        Junk: WORD;

BEGIN
        (* Clear FCB Buffer and set file name *)
        FCBBuffer.name.text := '';
        FCBBuffer.rest := '';
        NameStatus := StrToFCB(FileMatch,FCBBuffer.name);

        (* get target disk *)
        Disk := LoginDisk(FCBBuffer);

        (* set DMA Bufer *)
        BdosFunc.Func := setDMA;
        Bdos(BdosFunc.Cmd,ADR(DMABuffer),Junk);

        (* initialize File Name List pointer *)
        pList := NIL;

        (* find first much file *)
        BdosFunc.Func := searchFst;
        Bdos(BdosFunc.Cmd,ADR(FCBBuffer),BdosRc.Rc);
        WHILE BdosRc.Cc # 255 DO
                (* copy from FCB buffer to file struct *)
                ALLOCATE(pNewFile,SIZE(NewFile));
                pNewFile^.File.Disk := Disk;
                pNewFile^.File.Fill1 := ':';
                ToPos := 0;
                FOR FromPos := 1 TO 8 DO
                        pNewFile^.File.Name[ToPos] 
                                := DMABuffer[FromPos+BdosRc.Cc*32];
                        INC(ToPos);
                END;

                pNewFile^.File.Fill2 := '.';
                ToPos := 0;
                FOR FromPos := 9 TO 11 DO
                        pNewFile^.File.Extention[ToPos]
                                := DMABuffer[FromPos+BdosRc.Cc*32];
                        INC(ToPos);
                END;
                (* insert new one *)
                pNewFile^.Next := pList;
                pList := pNewFile;

                (* find next file *)
                BdosFunc.Func := searchNxt;
                Bdos(BdosFunc.Cmd,Junk,BdosRc.Rc);
        END;    
        RETURN pList
END MakeFileList;

PROCEDURE WriteFileStruc(FileStruc: FileStruct);
BEGIN
        Write(FileStruc.Disk);
        Write(FileStruc.Fill1);
        WriteString(FileStruc.Name);

        Write(FileStruc.Fill2);
        WriteString(FileStruc.Extention);
        WriteLn;
END WriteFileStruc;

PROCEDURE DumpFileList(pList: pFileList);
BEGIN
        WHILE pList # NIL DO
                WriteFileStruc(pList^.File);
                pList := pList^.Next;
        END;
END DumpFileList;

END FileSearch.

MakeFileList手続きが処理の中心です。ファイルを探すにはBDOSの機能番号16 最初のデータを探す(SearchFirst)、 機能番号17 次にデータを探す(SearchNext)を使います。 はじめに、FCBとDMAを準備します。そして、FCB領域を初期化します。

        (* Clear FCB Buffer and set file name *)
        FCBBuffer.name.text := '';
        FCBBuffer.rest := '';
        NameStatus := StrToFCB(FileMatch,FCBBuffer.name);
ここで躓きました。"FCBBuffer.rest"を初期化をしなかったために、 最初のデータを取得できたりできなかったりと、動作が不安定でした。 アセンブラでテストルーチンを書くとうまく動作します。悩みました。 使用したアセンブラでは、確保したデータ領域を自動的に0を埋め込んでいました。 しかし、Modula-2処理系では領域内の値は不定になるため、動作がおかしくなった様です。

最初のデータを見つけてしまえば、後は順に見つけることができました。

見つけたファイル名をリスト構造にして、返します。

DumpFileList手続きは、ファイル名リストを表示します。下受けルーチンとして、WriteFileStruc手続きを 使っています。

特に難しいロジックはありません。

BdosStructは、Definition Moduleだけです。以下のようになっています。

DEFINITION MODULE BdosStruct;

FROM OpSys IMPORT BdosFunctions;
FROM SYSTEM IMPORT WORD;

EXPORT QUALIFIED
        BdosCommand,BdosReturn;2

TYPE
        BdosCommand = RECORD
                CASE BOOLEAN OF
                        TRUE:   Func: BdosFunctions;
                |       FALSE:  Cmd: WORD;
                END;
        END;
        BdosReturn = RECORD
                CASE BOOLEAN OF
                        TRUE:   Rc: WORD;
                |       FALSE:  Cc: CARDINAL;
                END;
        END;
END BdosStruct.                     

次回は、これを応用したファイルマッチで指定されたファイルをリストするコマンド"FileFind"紹介します。