高速キャリージェネレター付き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"紹介します。

ヴィルト先生2024年01月06日 13:10

チューリッヒ連邦工科大学のOberonチームから、悲しい知らせが届きました。Pascal, Modulaといった プログラミング言語を世に送り出し、更にOberonという言語で記述したテキスト指向のオペレーティングシステムの開発者でもある ニクラウス ヴィルト先生が、なくなりました。

私が、ヴィルト先生のことを知ったのは、学生時代に参加したPascalの勉強会ででした。使用したテキストが、 ヴィルト先生らが書いた「Pascal(原題 Pascal User Manual and Report)」でした。この勉強会を通じて、Fortranとは違う プログラミング・パラダイムを知り、構造化されたプログラミングスタイルを身につけることができました。 このことは、仕事についてから、Cでのプログラム開発に、大いに役立ちました。 また、その後の、仕事においても、趣味においてでも、Pascalは重要な位置を締めてきました。

ヴィルト先生の著作を もう2冊持っています。「アルゴリズム+データ構造=プログラム(原題 ALGORITHMS+DATA STRUCTURES=PROGRAM)」と「アルゴリズムとデータ構造(原題 ALGORITHMS & DATA STRUCTURES)」です。この2冊は 兄弟になる著作でしょう。前者は、Pascalで、後者はModula-2でアルゴリズムの選択やそれに適したデータ構造の構築について 議論がなされています。これら3冊は、私にとってバイブル的存在であり、すぐに手に取れるところにおいています。

私がプログラマーとして成長するのに、大きな影響を受けたヴィルト先生のご冥福をお祈りましす。

ASSIT09で、8-Queens2023年12月21日 17:18

8-Queensを解くプログラムです。これは、8x8のマスにお互いが鑑賞しないようにQueenのコマの配置を求めるプログラムです。オリジナルで作成しようと頑張ったのですが、行き詰まってしまいました。いつものように、先人の方々のお知恵を拝借しました。調べてみると、ヴィルトの「アルゴリズム+データ構造=プログラム」にPascalで書かれたスマートな解がありました。暑中参照している「マイクロコンピュータのプログラミング」にもMicroPlanでの解が掲載されています。このMicroPlanの解を参照して、プログラムを作成しました。

さて、下敷きにしたプログラムは、Queenの配置を求めるルーチンが再帰的に書かれています。これをMC6809のアセンブラで書き下すのがキーポイントでしょう。想像しているより簡単に実現できました。MC6809にはスタックが、システムスタック Sとユーザースタック Uの2つがあります。このユーザースタックに、再帰的関数内部のローカル変数を退避させることでうまく行きました。 プログラム、アセンブルリスト、実行結果などは、こちらです。 8Queen.zip

いつものように、Lコマンドでロード・モジュールをダウンロードし、Cコマンドで、C000から実行させます。

Queenの配置が、次々と出てきます。

         === 8 Queens ===
           0 4 7 5 2 6 1 3
           0 5 7 2 6 3 1 4
           0 6 3 5 7 1 4 2
           0 6 4 7 1 3 5 2
           1 3 5 7 2 0 6 4
           1 4 6 0 2 7 5 3
           1 4 6 3 0 7 5 2
                  :
                  :
                  :
例えば、"0 4 7 5 2 6 1 3"は、
          カラム 0のQueenは、ロウ 0に
          カラム 1のQueenは、ロウ 4に
          カラム 2のQueenは、ロウ 7に
          カラム 3のQueenは、ロウ 5に
          カラム 4のQueenは、ロウ 2に
          カラム 5のQueenは、ロウ 6に
          カラム 6のQueenは、ロウ 1に
          カラム 7のQueenは、ロウ 3に
にあることを示しています。こんな配置です。
            0 1 2 3 4 5 6 7
          0 Q . . . . . . .
          1 . . . . . . Q .
          2 . . . . Q . . .
          3 . . . . . . . Q
          4 . Q . . . . . .
          5 . . . Q . . . .
          6 . . . . . Q . .
          7 . . Q . . . . .

ASSIT09で、マイクロトレック2023年11月07日 20:48

Assist09で動くアプリケーションを考えていたところ、石田晴久編「マイクロコンピュータのプログラミング」に 掲載されている、「宇宙戦争ケームのプログラム」が目に止まりました。いわゆる、スタートレックものです。石田先生作の Tiny-BASICで書かれた100行程度のプログラムリストが掲載されています。これを人間コンパイラになって、 6809アセンブラに変換しました。もとのリストには幾つか不具合がありましたが、解決しました。 結果、約700行のプログラムができました。 ここから、アセンブラ・ソース、アセンブル・リスト、ロード・モジュールを含むZIPファイルをダウンロードできます。 mtrek.zip

Assist09で実行するには、Lコマンドでロード・モジュールをダウンロードし、Cコマンドで、C000から実行させます。 こんな感じです。

"CAPTAIN :"に続いて、コマンドを入力します。 コマンドは、1:ミサイル発射、2:セクター移動、3:新しいギャラクシーにワープ、です。ミサイルでクリンゴンをすべて やっつければ"WIN"、時間切れやエネルー切れで、"LOOSE"です。



このプログラムは、ポジションインディペントなプログラムです。Lコマンドにオフセットをつけてロードしても、 問題なく動作します。

ASSIST09で、Hello World!2023年10月29日 08:15

モトローラのMC6809を使った自作マイコン"MyOwn6809"は、Assist09というモトローラが発表した モニタープログラムを搭載しています。Assist09には、12のサービスルーチンがあります。

  1. INCHP 文字入力
  2. OUTCH 文字出力
  3. PDATA1 文字列送出
  4. PDATA 改行と文字列送出
  5. OUT2HS 1バイトの16進化
  6. OUT4HS 1ワードの16進化
  7. PCRLF 改行
  8. SPACE スペース送出
  9. MONITOR Assist09の起動
  10. VCTRSW ベクタ・スワップ
  11. BRKPT ユーザ・ブレイク・ポイント
  12. PAUSE プログラム・ブレークとチェック

これらのサービスを使うことで、プログラムの開発をスムーズに進めることができます。

いつもの Hello World!を作ってみました。

* HELLO.ASM -- SAY "HELLO WORLD!"
* ASSIST09 SERVICE ENTRY CODE
PDATA   EQU     3       PUT CR/LF AND STRING TO CONSOLE

* CONSTANT VALUES
EOT     EQU     $04     END OF TEXT
CR      EQU     $0D     CARIGE RETURN
LF      EQU     $0A     LINE FEED
RAMTOP  EQU     $C000
*
        ORG     RAMTOP
MAIN:
        LEAX    HELLO,PCR
        SWI
        FCB     PDATA
        RTS
*
HELLO:  FCC     'HELLO WORLD!'
        FCB     CR,LF,EOT
*
        END

このプログラムは、RAMエリアの先頭、C000からロードするように作ってあります。このようにして、実行します。

このプログラムには、プログラムをどのような位置にロードしても実行できるように、おまじないをかけています。 論より証拠、C000からロードして、更にC020からにロードして実行してみました。

C020から実行しても正常に動いています。 いわゆる「リロケータブル」なプログラムになっています。変数のアドレスを指定するのに、プログラムカウンタの値を基準にして 指定しているので、このようなことができます。プログラムカウンタ相対です。

このプログラム中、出力文字列のアドレスを指定する部分、

        LEAX    HELLO,PCR
で、"PCR"がプログラムカウンタ相対を指定しています。