2012.3.5
前へ
次へ
ホームページトップへ戻る

復活!CP/M ワンボードマイコンでCP/Mを!
CP/MがTK−80互換のワンボードマイコンの上で復活します
ND80ZVとMYCPU80の上でCP/Mが走ります

[第51回]

●ND80ZVをご紹介していただきました

ND80ZV組立キットをご購入いただいたお客様が、ホームページでND80ZV組立キットをご紹介してくださいました。
製作の過程を写真でご紹介していただき、「価格、内容共に最高峰といえるキット」とのお褒めの言葉をいただきました。
ご紹介いただきましたのは、こちらのホームページです。

[2012.7.30注記]
ご紹介させていただいたホームページは、その後URLが変更になったようです。
現在はこちらのページでご紹介いただいています。[注記 ここまで]

SINTA様。有難うございました。
今後ともよろしくお願いいたします。

●COPYプログラム

前回はファンクションコール0FH(ファイルオープン)と同14H(シーケンシャルファイルリード)を使ったプログラムをご紹介しました。
ファイルリードのあとは、当然ファイルライトということになります。
今回は、ファイルオープン、ファイルリードに加えて、新規ファイル作成とファイルライト、クローズについてのテストプログラムを作成します。

MSDOSにはファイルコピーコマンドとして、その名の通りのCOPYコマンドがありますが、CP/MにはCOPYコマンドはありません。
CP/MではI/O装置間のファイル転送コマンドとしてPIPというコマンドを使います。
そのPIPコマンドを使ってディスク間のファイルコピーも行ないます。

しかしいまどきPIPなどという馴染みの無いコマンドを持ってくるのもいかがなものかと思いますので、MSDOSにならってCOPYコマンドの機能をトランジェントプログラムとして作ってみました。

COPYプログラムのソースリストです。

; BDOS TEST5 COPY
;2012/3/4
;
        ORG $8100
        FCALL=$8005
	FCBWK=$803B
	RECNO2=$805B
        FCB=$805C
	FCB2=$806C
        RECNO=$807C
        DMA=$8080
;
	LD HL,FCB2
	LD DE,FCBWK
	LD B,0C;=12
LOOP1:LD A,(HL)
	LD (DE),A
	INC HL
	INC DE
	DEC B
	JP NZ,LOOP1
;
        LD C,0F;open
        LD DE,FCB
        CALL FCALL
        INC A;if FFH?
        JP Z,NOFILERR
;
	LD C,13;delete
	LD DE,FCBWK
	CALL FCALL
;
	LD C,16;make file
	LD DE,FCBWK
	CALL FCALL
	INC A;if FFH?
	JP Z,DFULERR
;
        XOR A
        LD (RECNO),A
	LD (RECNO2),A
;
LOOP2:  LD C,14;read
        LD DE,FCB
        CALL FCALL
        OR A
        JP NZ,CLOSE;read end
	LD C,15;write
	LD DE,FCBWK
	CALL FCALL
	INC A;if FFH?
	JP Z,DFULERR
	JP LOOP2
;
CLOSE:LD C,10;close
	LD DE,FCBWK
	CALL FCALL
	INC A;if FFH?
	JP Z,CLOSERR
	LD DE,OK
	JP MSGOUT
;
NOFILERR:LD DE,CANTOPN
	JP MSGOUT
DFULERR:LD DE,DFULL
	JP MSGOUT
CLOSERR:LD DE,CANTCLS
MSGOUT:LD C,09
        CALL FCALL
        RET
;
CANTOPN:"can'"
        "t op"
        "en!"
        DB 0D
        DB 0A
        DB 24;$
DFULL:"disk"
        " ful"
        "l!"
        DB 0D
        DB 0A
        DB 24;$
CANTCLS:"can'"
        "t cl"
        "ose!"
        DB 0D
        DB 0A
        DB 24;$
OK:"copy"
        " don"
	"e"
        DB 0D
        DB 0A
        DB 24;$
;

このソースプログラムをZASM.COMでアセンブルして、それをCOPY.COMという名前でRAMディスクにセーブします。
使い方はMSDOSのCOPYコマンドと同じです。
copy filename1 filename2[Enter]
の入力で、filename1をfilename2という名前のファイルとしてコピーします。

●COPYのファイル名の順序について

ここで、COPYプログラムのファイル名の順序について説明をしておきたいと思います。
CP/MのPIPコマンドでは、
PIP filename2 filename1
のように、目的になるファイル名を先に書いて、元になるファイル名を後に書きます。

ちょっと奇異な感じがするかも知れませんが、実はマシン語的にはCP/Mの表記のほうが正統なのです。
たとえば8080アセンブラのMOV命令は、MOV A,Bと書いた場合、Bレジスタの値をAレジスタに入れる、という命令になります。
Z80アセンブラの表記でも同じで、LD A,Bと書きますが、これはBレジスタの値をAレジスタに入れる、という命令です。

ただ、そういうことを離れて
COPY A B
という表記を見たとき、直感的には、AをBにコピーする、という意味のほうが無理無く理解できそうな気がします。
そこでMSDOSではマシン語的な慣習にあえて逆らって、AをBにコピーするという意味の表記として、COPY A B にしたのではないか、と推察します。

そのいわれはともかくとしまして、いまさらMSDOSの表記に逆らってみるのも無益なことですから、ここは素直にMSDOSの表記通り、AをBにコピーする、という場合には、
COPY A B
という順序にすることにいたしました。

●COPYプログラムのアセンブルリスト

今回のプログラムはかなりややこしい処理をしています。
その説明の都合で、アセンブルリストをもとにして説明をすることに致します。
これが上のソースプログラムをZASM.COMでアセンブルして出力された、アセンブルリストです。


2012/3/5  8:25  ftst5cpy.txt
END=81AE
              ; BDOS TEST5 COPY
              ;2012/3/4
              ;
                      ORG $8100
                      FCALL=$8005
              	      FCBWK=$803B
              	      RECNO2=$805B
                      FCB=$805C
              	      FCB2=$806C
                      RECNO=$807C
                      DMA=$8080
              ;
8100 216C80   	      LD HL,FCB2
8103 113B80   	      LD DE,FCBWK
8106 060C     	      LD B,0C;=12
8108 7E       LOOP1:LD A,(HL)
8109 12       	      LD (DE),A
810A 23       	      INC HL
810B 13       	      INC DE
810C 05       	      DEC B
810D C20881   	      JP NZ,LOOP1
              ;
8110 0E0F             LD C,0F;open
8112 115C80           LD DE,FCB
8115 CD0580           CALL FCALL
8118 3C               INC A;if FFH?
8119 CA6481           JP Z,NOFILERR
              ;
811C 0E13     	      LD C,13;delete
811E 113B80   	      LD DE,FCBWK
8121 CD0580   	      CALL FCALL
              ;
8124 0E16     	      LD C,16;make file
8126 113B80   	      LD DE,FCBWK
8129 CD0580   	      CALL FCALL
812C 3C       	      INC A;if FFH?
812D CA6A81   	      JP Z,DFULERR
              ;
8130 AF               XOR A
8131 327C80           LD (RECNO),A
8134 325B80   	      LD (RECNO2),A
              ;
8137 0E14     LOOP2:  LD C,14;read
8139 115C80           LD DE,FCB
813C CD0580           CALL FCALL
813F B7               OR A
8140 C25281           JP NZ,CLOSE;read end
8143 0E15     	      LD C,15;write
8145 113B80   	      LD DE,FCBWK
8148 CD0580   	      CALL FCALL
814B 3C       	      INC A;if FFH?
814C CA6A81   	      JP Z,DFULERR
814F C33781   	      JP LOOP2
              ;
8152 0E10     CLOSE:LD C,10;close
8154 113B80   	      LD DE,FCBWK
8157 CD0580   	      CALL FCALL
815A 3C       	      INC A;if FFH?
815B CA7081   	      JP Z,CLOSERR
815E 11A381   	      LD DE,OK
8161 C37381   	      JP MSGOUT
              ;
8164 117981   NOFILERR:LD DE,CANTOPN
8167 C37381   	      JP MSGOUT
816A 118781   DFULERR:LD DE,DFULL
816D C37381   	      JP MSGOUT
8170 119481   CLOSERR:LD DE,CANTCLS
8173 0E09     MSGOUT:LD C,09
8175 CD0580           CALL FCALL
8178 C9               RET
              ;
8179 63616E27 CANTOPN:"can'"
817D 74206F70         "t op"
8181 656E21           "en!"
8184 0D               DB 0D
8185 0A               DB 0A
8186 24               DB 24;$
8187 6469736B DFULL:"disk"
818B 2066756C         " ful"
818F 6C21             "l!"
8191 0D               DB 0D
8192 0A               DB 0A
8193 24               DB 24;$
8194 63616E27 CANTCLS:"can'"
8198 7420636C         "t cl"
819C 6F736521         "ose!"
81A0 0D               DB 0D
81A1 0A               DB 0A
81A2 24               DB 24;$
81A3 636F7079 OK:"copy"
81A7 20646F6E         " don"
81AB 65       	      "e"
81AC 0D               DB 0D
81AD 0A               DB 0A
81AE 24               DB 24;$
              ;
CANTCLS      =8194  CANTOPN      =8179  CLOSE        =8152  
CLOSERR      =8170  DFULERR      =816A  DFULL        =8187  
DMA          =8080  FCALL        =8005  FCB          =805C  
FCB2         =806C  FCBWK        =803B  LOOP1        =8108  
LOOP2        =8137  MSGOUT       =8173  NOFILERR     =8164  
OK           =81A3  RECNO        =807C  RECNO2       =805B  

最初にFCB2からの内容をFCBWKにコピーしています。
FCBエリアについては、[第45回]で説明をしました。

copy filename1 filename2

のように入力した場合、FCBエリアには、ディスクディレクトリのファイル名表記にしたがって、次のように格納されます。

805CH〜806BH filename1
806CH〜807BH filename2

ところで805CHからのFCBエリアを、filename1をオープンするために使うと、ディスクディレクトリから805CH以下の32バイトに転記が行なわれてしまいます。
その時点で806CH以下にあったfilename2は失われてしまいます。
FCB2(806CH)以下のデータをFCBWK(803BH)以下にコピーしているのはそのためです。

なお、FCBWKは、使われていない領域が33バイト以上あるならどこに置いてもよいのですが、CP/Mのシステム領域に使われていないところがちょうど33バイトありましたので、そこ(803BH〜805BH)を使いました。

●CP/Mのシステムエリア

ちょうどよい機会ですので、CP/Mのシステムエリアについて、簡単に整理しておくことにいたします。
以下は本来のCP/Mのシステムエリアアドレスとしてまとめました。
現行の仮CP/Mでのシステムエリアは8000H〜80FFHになります。

0000−0002 ウォームスタート(BIOSへのジャンプ命令)
0003       I/Oバイト
0004       ログインディスク
0005−0007 ファンクションコール(BDOSへのジャンプ命令)
0008−0037 割り込みベクトル(未使用)
0038−003A RST7(デバッガで使用)
003B−005B 未使用
005C       ドライブa@         **** FCBエリア ここから
005D−0064 ファイルネーム(8バイト)   
0065−0067 拡張子(3バイト)        
0068       ディレクトリ拡張a@      
0069−006A CP/M内部で使用       
006B       ディレクトリ拡張レコードa@  
006C−007B ディスクアローケーションマップ 
007C       レコードアクセスカウンタ    
007D−007F ランダムアクセスレコードa@**** FCBエリア ここまで
0080−00FF DMAバッファ(128バイト)

上のマップにありますように、006Cからのアドレスに読み込まれるセカンドファイルネームは、ディスクアローケーションマップに重なっています。
005Cからに置かれたファーストファイルネームを指定してファイルオープンを実行すると、ディレクトリにある、そのファイルネームのアローケーションマップが006C以下に転記されるため、セカンドファイルネームは消されてしまいます。

そこでCOPYプログラムの先頭のところで、ドライブaAファイル名、拡張子合わせて12バイトを、未使用エリアの803BH(本来は003BH)以下にコピーしているのです。
実はここに間違いがあったのですが、それについては後ほど説明することにいたします。

プログラムの説明に戻ります。
アドレス8110で、filename1が置かれたFCBアドレスをDEに入れて、ファンクションコール0FH(ファイルオープン)をします。
ファイルオープンに失敗するとFFHが返されますから、エラールーチンにジャンプします。

●ファンクションコール13H(ファイルデリート)

CP/Mのディスクシステムでは、ファイルの新規作成時に、同じファイル名があるかどうかの検出は行われないようです。
そこで新規作成に先だって、まずファンクションコール13Hのファイルデリートを実行しておきます。
DEレジスタに新規作成したいファイル名のFCBアドレス(ここではFCBWK)を入れて、Cレジスタに13Hを入れて、システムをコールします。
該当するファイル名がある場合にはそのファイルは削除されます。
該当するファイル名がなくてもそれはそれで構いませんから、エラーチェックは不要です。

●ファンクションコール16H(新規ファイルオープン)

アドレス8124で、ファンクションコール16H(新規ファイルオープン)を行ないます。
新規に作成したいファイル名のFCBアドレスをDEレジスタに入れ、Cレジスタに16Hを入れて、システムをコールします。
新規ファイルオープンに失敗する(おそらくはディレクトリエリアに空きがない)と、FFHが返されますからエラールーチンにジャンプします。

このあと8130Hで、filename1とfilename2のそれぞれのレコードカウンタを00にセットします。
レコードカウンタについては前回([第50回])説明をしました。

8137HのLOOP2:からは繰り返しです。
ファンクションコール14H(ファイルリード)ではfilename1のFCBをDEに入れてシステムをコールします。
ファイルの最後まで読み出しが終わるとFFHが返されますからファイルクローズの処理にジャンプします。
それ以外は読み出したデータを新規ファイルに書き出します。

●ファンクションコール15H(ファイルライト)

ファンクションコール14H(ファイルリード)で読み出した1レコード128バイトのデータはデフォルトのDMAエリアに入れられます。

ファンクションコール15H(シーケンシャルファイルライト)では、filename2のFCB(ここではFCBWK)をDEレジスタに入れ、Cレジスタに15Hを入れて、システムをコールすると、デフォルトのDMAエリアにある128バイトのデータがディスクに書き込まれます。
ファイルの書き込みに失敗する(おそらくはディスクに空きがなくなった)とFFHが返されますから、エラールーチンにジャンプします。

●ファンクションコール10H(ファイルクローズ)

最後に新規作成したファイルをクローズします。
filename2のFCB(ここではFCBWK)をDEレジスタに入れて、Cレジスタに10Hをいれて、システムをコールします。
ファイルクローズに失敗する(どういう状況か不明)とFFHが返されますから、エラールーチンにジャンプします。
ファイルをクローズしてはじめてディレクトリに登録が行なわれます。
読み出しのためにオープンしたファイルをクローズする必要はありません。

次回はCOPYプログラムを実行してみることにいたします。

ワンボードマイコンでCP/Mを![第51回]
2012.3.5upload

前へ
次へ
ホームページトップへ戻る