IBM JX -- POST2026年05月05日 13:30

IBM JXと貴重な資料

POST(Power On Self Test)を 読みました。8088/86はこう使うのかと、勉強になります。

POSTの始まりは、こうです。

MFG_PORTに、何やら書き込んでます。この先を読んでみますと、テストステップの開始にMFG_PORTにコードを書き込んでます。

TEST
STEP
MFG_PORT
CODE
TEST ITEM
SET UP0FFHSETUP
18088
20FEH8255
30FDH64505,VIDEO
40FCHROS CHECK SUM
50FBHRAM MAPPING
60FAHBASE 4K STORAGE
70F8HROM CARTRIDGE
80F7HINTERRUPT
90F6H8259
100F5H8253
110F4HCRT ATTACHMENT
120F3HSET UP KEYBOARD PARAMETERS
130F2H32K VRAM
140F1HKJ-ROM AND GAIJI SRAM
150F0HMEMORY SIZE DETERMINE
160EFHKEYBOARD
170EEHCASETTE INTERFACE
180EDHSERIAL PORT
190ECHPARALLEL PORT
200EBHOPTIONAL ROM
210EAHDISKETTE ATTACHMENT
220E9HPCjr CARTRIDGE ROM CHECK SUM

エラーを検出すると、E_MSGサブルーチンを使って、エラーコードをMFG_PORT+1,MFG_PORT+2に書き出します。そして、ビデオのテスト前だと、ビープを2回鳴らします。出だしは、こんな感じです。

MFG_PORTを読み取る"MFG-PORT Adapter"を制作してます。

IBM JX -- POST ERROR2026年04月23日 21:45

IBM JXと貴重な資料

せんだて、入手したIBM JXにアップスキャンコンバーターを経由してVGAディスプレイを 接続したのですが、あの、本体起動直後のカラーの画面が表示されません。

BEEP音が2回しますので、POST(Power On Self Test)でハードウェア・エラーを検出しています。検出したエラーは、画面に表示されるのですが。 そもそも、ディスプレーには何も映らないので、どうして良いのやら。

まず、アップスキャンコンバーターのロゴやメニューが表示されるので、コンバーターは生きています。 更に、コンバーターは、"NO SIGNAL"と言ってきます。JXのディスプレーポートとコンバーター間の 接続を再確認。間違いなし。

念の為、JXのディスプレーポート J15 の信号を確認したところ、以下のようでした。

PIN#NAMESIGNAL
1GND
2RNO SIGNAL
3-HSYNCNO SIGNAL
4-VSYNCNO SIGNAL
5H/-LSWOK
6IOK
7BNO SIGNAL
8GND
9GND
10GNO SIGNAL
11COLOR CLOCKOK
12+12V
13DOT CLOCKOK
14AUDIO
15GND

J15に出てくる信号は、このようになってます。

"I"や"H/-L SW"が出てきているので、IC69 74LS240は、生きてるでしょう。"I"はカスタムICのIC32 SX001から来る信号なのです。IC32も生きてるように感じます。

ということは、Display関連のテストとそれ以前のテストで、POSTが障害を検出したのは間違いないのですが、このままでは、 場所を特定できません。

というわけで、POSTを読んでます。

IBM JX2026年03月20日 17:00

IBM JXと貴重な資料

IBM JX 本体を比較的安価に、入手できました。

内容は、

  1. メモリー 128KB (本体 64KB、64KB メモリーカード)
  2. 2 Diskette Drive
  3. 拡張表示カード
です。

JX 本体の上にある本は、

  • 「DOSの内部構造とプログラムインターフェイス N:SC18-2016-0」
  • 「テクニカル・リファレンス N:SY18-2104-1」
です。貴重な資料です。

電源は、入りました。BEEP音が2回鳴ったので、ハードウェアーエラーを検出したみたいです。

まずは、ディスプレーとキーボードの接続からでしょうか。

デジタル RGB ディスプレーを入手は、大変でしょうから、今時のVGAディスプレーの接続を検討します。

JX用純正キーボードを望む事も無理ですから、今時のPS/2接続のキーボードを流用を検討します。

これ以外にも、やってみたいことが。

  • XT-IDE を移植したい
  • 384KB 拡張メモリーアダプターを追加したい
  • 拡張ボックスを追加したい
などなど。

どこまで、できるかな? 当分、楽しめそうです。

GAL Writerの制作2025年04月14日 17:30

ハードウェアの制作にGALを使用できると、とても便利です。特にアドレスデコーダーの 類などは、とてもすっきりしますし、後からの変更も簡単にできますので、重宝します。 これまで、いくつかの機器の制作で使用してきました。

GALを作成するには、PALASMでヒューズデータを作成し、XGecuのTL866 II Plus で書き込みをしていました。

これはこれで、いいのですが、PALASMはDOSのプログラムで、 TL866II PlusはWindosのコントルールプログラムでした。そうです。ヒューズデータのやりとりが めんどうだったのです。できれば、DOS環境だけで完結したく思ってました。

調べてみると、GAL Writerの制作例をいくつか発見できました。その中でも、 ELM氏の発表なされた「お手GAL」は、以前より気にかけていたのですが、とてもシンプルで、 DOSの環境で使うことができ、私の悩みを解決するものです。思い切って制作することにしました。 ELM氏の「お手GAL」は ここ にありますのでご参照ください。

実際の制作にあったって、手持ちの部品を優先して使用したので、 古い大型の部品やオーバースッペクの部品が見受けられます。 また、抵抗内蔵のディジタルトランジスタは、抵抗と手持ちのトランジスタで構成しました。 デバッグをしていて、プリンタポートの出力電圧が低かったのでバッファーを追加する必要が でてしまいましたが、大きめのボードだったのでいれることができました。

これが、完成した私の「お手GAL GAL Writer」です。

これで、16V8,20V8,22V10をプログラムできます。今後とも、重宝しそうです。

高速キャリージェネレター付き8ビット加算・減算器の製作2025年02月06日 21:36

高速キャリージェネレター付き8ビット加算・減算器の製作

デジタル回路の学び直しをしています。テキストは、大川喜邦著「デジタル回路」です。組合せ回路の部分が終わりました。組合せ回路のまとめとして、高速キャリージェネレーター付きの8ビット加算・減算器を基本的なTTLだけで設計、製作しました。最終的には、TTLが30個でできました。

ウラ面はこんな感じです。 手ハンダなので、スパゲティボールです。

基板上の"Value A"と"Value B"のスイッチに-128から127までの数値を設定し、加算・減算した結果を"Result"LEDに表示します。負の数は2の補数表現です。

例えば、63+1は、このようになります。

また、1+(-1)は、

さらに、3-1は、

回路図は、ここ にあります。

CPUの中のALUの一部を解析するとこんな感じになるのでしょうね。

Modula-2 でプログラミング -- 文字列探索プログラム find の製作 (その3) --2025年01月05日 21:19

前回紹介したfindプログラムは、FileSearchモジュールの不備で、ファイル一覧の構造が 丸見えになっていました。FileSearchモジュールに2つ手続きを追加して不具合を解消しました。

新しいFileSearchモジュールの定義モジュールは以下の通り。

DEFINITION MODULE FileSearch;

FROM OpSys IMPORT FCB,FCBFileName;

EXPORT QUALIFIED pFileList,FileNameNode,
	GetNextFile,GetFileName,LoginDisk,MakeFileList,DumpFileList;

CONST
	DMABufferSize = 128;
	FileNameSize = 14;

TYPE
	pFileList = POINTER TO FileNameNode;
	FileNameNode = RECORD
		FileName: ARRAY [0..FileNameSize] OF CHAR;
		Next: pFileList
	END;
        DMA = ARRAY [0..DMABufferSize-1] OF CHAR;

PROCEDURE GetNextFile(FileLIst: pFileList): pFileList;
PROCEDURE GetFileName(FileList: pFileList;VAR FileName: ARRAY OF CHAR);
PROCEDURE LoginDisk(FCBBuf: FCB): CHAR;
PROCEDURE MakeFileList(FIleMatch: ARRAY OF CHAR): pFileList;
PROCEDURE DumpFileList(pList: pFileList);
END FileSearch.
手続き、次のファイルを取り出すGetNextFileとファイル名を取り出すGetFileNameを追加しました。 実現モジュールは以下の通り。
IMPLEMENTATION MODULE FileSearch;
FROM InOut IMPORT WriteHex,Write,WriteString,WriteLn;
FROM OpSys IMPORT FCB,FCBFileName,BdosFunctions,Bdos;
FROM SYSTEM IMPORT ALLOCATE,TSIZE,ADR,WORD;
FROM FileNames IMPORT StrToFCB,FCBToStr,NameState;

CONST
	EOS = 0C;

PROCEDURE GetNextFile(FileList: pFileList): pFileList;
BEGIN
	RETURN FileList^.Next
END GetNextFile;

PROCEDURE GetFileName(FileList: pFileList; VAR FileName: ARRAY OF CHAR);
VAR
	i: CARDINAL; 
BEGIN
	i := 0;
	WHILE ( FileList^.FileName[i] # EOS ) DO
		FileName[i] := FileList^.FileName[i];
		INC(i)
	END;
	FileName[i] := EOS
END GetFileName;

PROCEDURE LoginDisk(FCBBuf: FCB): CHAR;
VAR
	DiskCode: CARDINAL;
	Junk: WORD;

BEGIN
	IF FCBBuf.name.disk = 0C THEN
		Bdos(retCDsk,Junk,DiskCode);
		RETURN (CHR(ORD(DiskCode)+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;
	BdosRc: CARDINAL;
	DMABuffer: DMA;
	CPos: CARDINAL;
	NameStatus: NameState;
	pList: pFileList;
	pNewFileName: pFileList;
	FCBFile: FCBFileName;
	Formatted: BOOLEAN;
	Junk: WORD;

BEGIN
	pList := NIL;

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

	Disk := LoginDisk(FCBBuffer);

	(* set DMA Bufer *)
	Bdos(setDMA,ADR(DMABuffer),Junk);

	(* find first much file *)
	Bdos(searchFst,ADR(FCBBuffer),BdosRc);
	WHILE BdosRc # 255 DO
		ALLOCATE(pNewFileName,TSIZE(FileNameNode));
		FOR CPos := 0 TO 11 DO
			FCBFile.text[CPos]
				:= DMABuffer[CPos+BdosRc*32]
		END;
		FCBFile.disk := CHR(ORD(Disk) - ORD('A') + 1);
		FCBToStr(FCBFile,pNewFileName^.FileName,FALSE);
		pNewFileName^.Next := pList;
		pList := pNewFileName;

		(* find next file *)
		Bdos(searchNxt,Junk,BdosRc);
	END;	
	RETURN pList
END MakeFileList;

PROCEDURE WriteFileName(FileName: ARRAY OF CHAR);
BEGIN
	WriteString(FileName);
	WriteLn
END WriteFileName;

PROCEDURE DumpFileList(pList: pFileList);
BEGIN
	WHILE pList # NIL DO
		WriteFileName(pList^.FileName);
		pList := pList^.Next;
	END;
END DumpFileList;
END FileSearch.

これらの変更を反映したfindは、以下のとおり。

MODULE Find;
FROM InOut IMPORT Write,WriteCard,WriteString,WriteLn;
FROM Files IMPORT FILE,FileState,Open,Close,Read;
FROM CmdArgs IMPORT Argc,Argv;
FROM FileSearch IMPORT GetFileName,GetNextFile,
	FileNameNode,MakeFileList,pFileList;
FROM BasicFileIO IMPORT FileIOStruct,pFileIOStruct,
	OpenFile,CloseFile,EOFFile,FileIOStatus;
FROM StdFileIO IMPORT GetLine;

CONST
	EOS = 0C;
	MAXLINE = 128;
	FILENAMESIZE = 14;
	EOFMARK = 32C;

VAR
	FileName: ARRAY [0..FILENAMESIZE] OF CHAR;
	FileMatch: ARRAY [0..FILENAMESIZE] OF CHAR;
	Target: ARRAY [0..MAXLINE] OF CHAR;
	CPos,FileNumber,LineNumber,nArgc: CARDINAL;
	pList: pFileList;

PROCEDURE putLine(FileName:ARRAY OF CHAR;Line:CARDINAL;
		Buffer: ARRAY OF CHAR);
BEGIN
	WriteString(FileName);
	Write(' ');WriteCard(Line,1);Write(':');
	WriteString(Buffer);
	WriteLn
END putLine;

PROCEDURE findTarget(Target,Line:ARRAY OF CHAR): BOOLEAN;
VAR
	t,l: CARDINAL;
	found: BOOLEAN;
BEGIN
	found := FALSE;
	t := 0; l := 0;
	WHILE (Line[l] # EOS) AND (NOT found) DO
		IF Line[l] = Target[0] THEN
			t := 1;
			WHILE Line[l+t] = Target[t] DO
				INC(t);
			END;
			IF Target[t] = EOS THEN
				found := TRUE;
			END;
		END;
		INC(l);
	END;
	RETURN found
END findTarget;

PROCEDURE Usage();
BEGIN
	WriteString('find');WriteLn;
	WriteString('      TargetString File1 File2 FIle3,,,,');WriteLn 
END Usage;

PROCEDURE MatchLines(FileName,TargetString: ARRAY OF CHAR);
VAR
	pFileIO: pFileIOStruct;
	Line: ARRAY [0..MAXLINE-1] OF CHAR;
	Chars: CARDINAL;
	NLines: CARDINAL;
	Junk: FileIOStatus;

BEGIN
	IF OpenFile(FileName,pFileIO) = Success THEN
		NLines := 1;
		WHILE NOT EOFFile(pFileIO) DO
			Chars := GetLine(pFileIO,Line);
			IF findTarget(TargetString,Line) THEN
				putLine(FileName,NLines,Line);
			END;
			INC(NLines);
		END;
		Junk := CloseFile(pFileIO)
	END
END MatchLines;

BEGIN
	nArgc := Argc();
	IF nArgc < 1 THEN
		Usage();
		HALT
	END;
	Argv(0,Target);
	FileNumber := 1;
	WHILE FileNumber < nArgc DO
		Argv(FileNumber,FileMatch);
		pList := MakeFileList(FileMatch);
		WHILE pList # NIL DO
			GetFileName(pList,FileName);
			MatchLines(FileName,Target);
			pList := GetNextFile(pList)
		END;
		INC(FileNumber)
	END
END Find.

これで、FileSearchモジュール内部のデータ構造をFindが知る必要がなくなりました。

Modula-2でプログラミング -- 文字列探索プログラム find の制作(その1)2024年12月28日 21:00

指定された文字列を 指定されたファイルの中から探し出すプログラム find を紹介します。 このように使います。

C0#find

arguments>GetLine *.def *.mod

C:STDFILEI.DEF 6:       GetLine;
C:STDFILEI.DEF 8:PROCEDURE GetLine(VAR pFileIO: pFileIOStruct;
C:STDFILEI.MOD 10:PROCEDURE GetLine(VAR pFileIO: pFileIOStruct;
C:STDFILEI.MOD 36:END GetLine;
C:FIND.MOD 8:FROM StdFileIO IMPORT GetLine;
C:FIND.MOD 71:                  Chars := GetLine(pFileIO,Line);


63K QP/M VER 2.7   (BIOS VER 1.0)

                   (C) 1988.12.1.  K.YOSHIDA

             2018.10.28.  MODEFIED BY H.KIDA
C0#find

arguments>GetChar *.def *.mod

C:BASICFIL.DEF 7:       ReadFile,GetChar;
C:BASICFIL.DEF 28:PROCEDURE GetChar(VAR pFileIO: pFileIOStruct): CHAR;
C:STDFILEI.MOD 3:       pFileIOStruct,GetChar;
C:STDFILEI.MOD 20:              C := GetChar(pFileIO);
C:BASICFIL.MOD 65:PROCEDURE GetChar(VAR pFileIO: pFileIOStruct): CHAR;
C:BASICFIL.MOD 89:END GetChar;


63K QP/M VER 2.7   (BIOS VER 1.0)

                   (C) 1988.12.1.  K.YOSHIDA

             2018.10.28.  MODEFIED BY H.KIDA
C0#

find は、ファイルを読みますので、ファイルアクセスのルーチンが必要になります。 Bdosを直接使うことができるので、かんたんなファイル読み込みモジュール BasicFileIO を作成しました。 更に、行単位のファイル読み出しが必要なので、上位のモジュール StdFileIO を作成しました。StdFileIOは、 Bdosとは、独立しています。

まず、 BasicFileIO です。基本的なファイルアクセスルーチンの集まりです。 今回は、読み出せれば良いので、書き込み関係のルーチンは作成していません。基本的なファイルのアクセスは、 一文字単位です、 定義モジュールは、以下のとおりです。

DEFINITION MODULE BasicFileIO;
FROM OpSys IMPORT FCB,FCBFileName;

EXPORT QUALIFIED 
	FileIOStatus,FileIOStruct,pFileIOStruct,
	OpenFile,CloseFile,EOFFile,
	ReadFile,GetChar;

CONST
	DMASize = 128;
TYPE
	FileIOStatus = (Success,InvalidFileName,
		OpenError,CloseError,IOError);
	DMA = ARRAY [0..DMASize-1] OF CHAR;
	pFileIOStruct = POINTER TO FileIOStruct;
	FileIOStruct = RECORD
		FCBBuf: FCB;
		DMABuf: DMA;
		ReadPos: CARDINAL;
		WritePos: CARDINAL;
		EOFFlag: BOOLEAN;
	END;

PROCEDURE OpenFile(FileName: ARRAY OF CHAR;
	VAR pFileIO: pFileIOStruct): FileIOStatus;
PROCEDURE CloseFile(VAR pFileIO: pFileIOStruct): FileIOStatus;
PROCEDURE ReadFile(VAR pFileIO: pFileIOStruct): FileIOStatus;
PROCEDURE GetChar(VAR pFileIO: pFileIOStruct): CHAR;
PROCEDURE EOFFile(pFileIO: pFileIOStruct): BOOLEAN;
END BasicFileIO.

実装モジュールは、以下のとおりです。手続き GetChar では、CR LF と連続した場合、 LF を読み飛ばします。

IMPLEMENTATION MODULE BasicFileIO;
FROM InOut IMPORT Write,WriteString,WriteLn;
FROM OpSys IMPORT BdosFunctions,FCBFileName,Bdos,FCB;
FROM FileNames IMPORT StrToFCB,NameState;
FROM SYSTEM IMPORT ALLOCATE,ADR,TSIZE,WORD;

CONST
	CR = 15C;
	LF = 12C;
	EOFMARK = 32C;

PROCEDURE OpenFile(FileName: ARRAY OF CHAR;
	VAR pFileIO: pFileIOStruct): FileIOStatus;
VAR
	BdosRc: CARDINAL;
	Junk: WORD;

BEGIN
	(* Allocate FCB and DMA / pre-Set *)
	ALLOCATE(pFileIO,TSIZE(FileIOStruct));
	pFileIO^.FCBBuf.name.text := '';
	pFileIO^.FCBBuf.rest := '';
	pFileIO^.DMABuf := '';
	IF StrToFCB(FileName,pFileIO^.FCBBuf.name) # NameOK THEN
		RETURN InvalidFileName
	END;
	Bdos(setDMA,ADR(pFileIO^.DMABuf),Junk);
	Bdos(openF,ADR(pFileIO^.FCBBuf),BdosRc);
	IF BdosRc # 255 THEN
		pFileIO^.ReadPos := DMASize;
		pFileIO^.WritePos := 0;
		pFileIO^.EOFFlag := FALSE;
		RETURN Success
	ELSE
		RETURN OpenError
	END
END OpenFile;

PROCEDURE CloseFile(VAR pFileIO: pFileIOStruct): FileIOStatus;
VAR
	BdosRc: CARDINAL;

BEGIN
	Bdos(closeF,ADR(pFileIO^.FCBBuf),BdosRc);
	IF BdosRc = 0 THEN
		RETURN Success
	ELSE
		RETURN CloseError
	END
END CloseFile;

PROCEDURE ReadFile(VAR pFileIO: pFileIOStruct): FileIOStatus;
VAR
	BdosRc: CARDINAL;

BEGIN
	Bdos(readSeq,ADR(pFileIO^.FCBBuf),BdosRc);
	IF BdosRc = 0 THEN
		RETURN Success
	ELSE
		RETURN IOError
	END
END ReadFile;

PROCEDURE GetChar(VAR pFileIO: pFileIOStruct): CHAR;
VAR
	C: CHAR;

BEGIN
	IF DMASize = pFileIO^.ReadPos THEN
		IF ReadFile(pFileIO) = Success THEN
			pFileIO^.ReadPos := 0
		ELSE
			pFileIO^.ReadPos := 0;
			pFileIO^.DMABuf[pFileIO^.ReadPos] := EOFMARK
		END
	END;
	C := pFileIO^.DMABuf[pFileIO^.ReadPos];
	IF C = CR THEN
		IF pFileIO^.DMABuf[pFileIO^.ReadPos+1] = LF THEN
			INC(pFileIO^.ReadPos)
		END
	END;
	INC(pFileIO^.ReadPos);
	IF C = EOFMARK THEN
		pFileIO^.EOFFlag := TRUE
	END;
	RETURN C
END GetChar;

PROCEDURE EOFFile(pFileIO: pFileIOStruct): BOOLEAN;
BEGIN
	RETURN pFileIO^.EOFFlag
END EOFFile;
END BasicFileIO.

GetChar で、 LF を読み飛ばす以外は、面倒くさいところは、ないでしょう。

この、 BasicFileIO モジュールをつかって、行単位の読み出しルーチンを作ってます。 StdFileIO モジュールです。 このモジュールは、 CP/M のファイル構造と独立です。必要最低限の行単位の読み出ししか作成していません。

定義モジュールは、以下の通り。

DEFINITION MODULE StdFileIO;
FROM BasicFileIO IMPORT
	pFileIOStruct;

EXPORT QUALIFIED 
	GetLine;

PROCEDURE GetLine(VAR pFileIO: pFileIOStruct;
		VAR LineBuffer: ARRAY OF CHAR): CARDINAL;
END StdFileIO.

実装モジュールは、以下の通りです。

IMPLEMENTATION MODULE StdFileIO;
FROM BasicFileIO IMPORT
	pFileIOStruct,GetChar;

CONST
	EOS = 0C;
	EOFMARK = 32C;
	CR = 15C;

PROCEDURE GetLine(VAR pFileIO: pFileIOStruct;
		VAR LineBuffer: ARRAY OF CHAR): CARDINAL;

VAR
	C: CHAR;
	CPos: CARDINAL;

BEGIN
	CPos := 0;
	LOOP
		C := GetChar(pFileIO);
		CASE C OF
			EOFMARK:
				LineBuffer[CPos] := EOS;
				RETURN CPos
		|	CR:
				LineBuffer[CPos] := EOS;
				RETURN CPos
		END;
		LineBuffer[CPos] := C;
		INC(CPos);
		IF CPos = HIGH(LineBuffer) THEN
			LineBuffer[CPos] := EOS;
			RETURN CPos
		END
	END
END GetLine;
END StdFileIO.

GetLine は、一文字読み出しを繰り返し、 CR または EOF が来たら、その一文字前の文字までを一行とします。 また、バッファーのサイズ以上に読み取ろうとした場合は、強制的に一行にしてしまいます。溢れてしまうと困りますので。

find 本体の紹介は次回にします。

Modula-2でプログラミング -- FileFindの制作2024年11月02日 11:36

FIleSearche"モジュールを利用した、ファイマッチで指定されたファイル一覧を書き出すプログラム "FileFind"を作ってみました。

使い方は、このようにします。

C0#filefind
arguments>f*.mod *.def
C:FIND.MOD
C:FILEFIND.MOD
C:FILESEAR.MOD
C:CONIO.DEF
C:ADD16.DEF
C:SILLY.DEF
C:FILESEAR.DEF
C:STDFILEI.DEF
C:BDOSSTRU.DEF
C:BASICFIL.DEF
C:CMDARGS.DEF
C:SCREEN.DEF     

探すファイル名は、ファイルマッチを指定できます。複数指定できます。 この引数を受け取るには、以前紹介した自作の"CmdArgs"モジュール を使用しています。

プログラムは、このようになりました。

MODULE FileFind;
FROM InOut IMPORT WriteString,WriteLn;
FROM CmdArgs IMPORT Argc,Argv;
FROM FileSearch IMPORT MakeFileList,DumpFileList;

CONST
	MAXLINE = 128;

VAR
	FileMatch: ARRAY [0..MAXLINE-1] OF CHAR;
	nArgc: CARDINAL;
	NthFile: CARDINAL;

PROCEDURE Usage();
BEGIN
	WriteString('Usage: filefind filematch');WriteLn;
END Usage;

BEGIN
	nArgc := Argc();
	IF nArgc < 1 THEN
		Usage();
		HALT;
	END;

	NthFile := 0;
	WHILE NthFile < nArgc DO 
		Argv(NthFile,FileMatch);
		DumpFileList(MakeFileList(FileMatch));
		INC(NthFile);
	END;
END FileFind.


次々にファイルマッチを取り出し、該当するファイル名のリストを"MakeFileList"で作成し、 そのリストを"DumpFileLIst"で書き出します。"DumpFileList"は、"FileSearch"モジュールに実装しました。 こうすることで、ファイル名の保持のしかたをメインルーチンが知る必要がありません。

Modula-2でプログラミング -- FileSearch ファイルを探すモジュールの修正2024年11月02日 10:29

前回、ファイルを探すモジュール"FileSearch"を制作しました。これを利用したプログラムを いくつか作成して見た所、ファイル名リストのデータ構造の使い勝手が良くないことがわかりました ので"FileSearch"モジュールを修正しました。

修正した"FileSearch"モジュールのDefinition Moduleです。

DEFINITION MODULE FileSearch;

FROM OpSys IMPORT FCB,FCBFileName;

EXPORT QUALIFIED pFileList,FileNameNode,DMA,
	LoginDisk,MakeFileList,DumpFileList;

CONST
	DMABufferSize = 128;
	FileNameSize = 14;

TYPE
	pFileList = POINTER TO FileNameNode;
	FileNameNode = RECORD
		FileName: ARRAY [0..FileNameSize] OF CHAR;
		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 WriteHex,Write,WriteString,WriteLn;
FROM OpSys IMPORT FCB,FCBFileName,BdosFunctions,Bdos;
FROM SYSTEM IMPORT ALLOCATE,TSIZE,ADR,WORD;
FROM FileNames IMPORT StrToFCB,FCBToStr,NameState;

CONST
	EOS = 0C;

PROCEDURE LoginDisk(FCBBuf: FCB): CHAR;
VAR
	DiskCode: CARDINAL;
	Junk: WORD;

BEGIN
	IF FCBBuf.name.disk = 0C THEN
		Bdos(retCDsk,Junk,DiskCode);
		RETURN (CHR(ORD(DiskCode)+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;
	BdosRc: CARDINAL;
	DMABuffer: DMA;
	CPos: CARDINAL;
	NameStatus: NameState;
	pList: pFileList;
	pNewFileName: pFileList;
	FCBFile: FCBFileName;
	Formatted: BOOLEAN;
	Junk: WORD;

BEGIN
	pList := NIL;

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

	Disk := LoginDisk(FCBBuffer);

	(* set DMA Bufer *)
	Bdos(setDMA,ADR(DMABuffer),Junk);

	(* find first much file *)
	Bdos(searchFst,ADR(FCBBuffer),BdosRc);
	WHILE BdosRc # 255 DO
		ALLOCATE(pNewFileName,TSIZE(FileNameNode));
		FOR CPos := 0 TO 11 DO
			FCBFile.text[CPos]
				:= DMABuffer[CPos+BdosRc*32]
		END;
		FCBFile.disk := CHR(ORD(Disk) - ORD('A') + 1);
		FCBToStr(FCBFile,pNewFileName^.FileName,FALSE);
		pNewFileName^.Next := pList;
		pList := pNewFileName;

		(* find next file *)
		Bdos(searchNxt,Junk,BdosRc);
	END;	
	RETURN pList
END MakeFileList;

PROCEDURE WriteFileName(FileName: ARRAY OF CHAR);
BEGIN
	WriteString(FileName);
	WriteLn
END WriteFileName;

PROCEDURE DumpFileList(pList: pFileList);
BEGIN
	WHILE pList # NIL DO
		WriteFileName(pList^.FileName);
		pList := pList^.Next;
	END;
END DumpFileList;
END FileSearch.


大きな修正点は、見つかったファイルの名前をFCB形式からべたな文字列のファイル名に変換し、 それを保存している点です。 ファイル名の持ち方をべたな文字列にしたので、ファイル名を書き出すPROCEDURE "WriteFileName"を 修正しています。

他には、"BdosStruct"モジュールの使用をやめました。処理系が提供する Bdos関連機能の"OpSys"モジュールを一部修正し、"BdosStruct"モジュールが無くてもコンパイル できるようにしました。

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"紹介します。