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

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

[第36回]

●cpm22.z80のバグ(2)

前回からの続きです。
下のCHECKSUMルーチンにはバグがあります。
どこにバグがあるか、おわかりになりますでしょうか?
というところまでが、前回のお話でした。

                     	;
                      	;   Compute the check-sum for the directory buffer. Return
                      	; integer sum in (A).
                      	;
  C8FA  0E80          	CHECKSUM: LD	C,128		;length of buffer.
  C8FC  2AC0D1        		LD	HL,(DIRBUF)	;get its location.
  C8FF  AF            		XOR	A		;clear summation byte.
  C900  C600          	CHKSUM1:ADD	A,M		;and compute sum ignoring carries.
  C902  23            		INC	HL
  C903  0D            		DEC	C
  C904  C200C9        		JP	NZ,CHKSUM1
  C907  C9            		RET	

Z80アセンブラを理解してみえる方でしたら、すぐにおわかりになったと思います。
CHKSUM1:ADD A,M
が、バグなのです。
こんな表記はZ80アセンブラにはありません。
これは、8080アセンブラの表記です。

ここは、
CHKSUM1:ADD A,(HL)
と書くべきところです。

私の作ったZ80アセンブラですと、ADD A,Mはエラーではじき出します。
しかしZASM1.64ではエラーにはならないようです。
エラーにする代わりに、誤ったコードを作成してしまいます。
上記リストの左側部分がソースプログラムをアセンブルした結果作成されたマシン語コードです。
CHKSUM1:ADD A,M の、その部分を見ますと
C600
になっています。

これはADD A,(HL) のマシン語コードではなくて、
ADD A,00
のマシン語コードです。
ちなみに、ADD A,(HL)のマシン語コードは、
86
です。
全然違ってしまいます。

当然、正しい結果は得られません。
上のCHECKSUMルーチンの実行結果は常にA=00になってしまいます。

まあ、しかし、このチェックサムは、フロッピーディスクを処理の途中で交換してしまったかどうかをチェックするための目的で使われるもののようでありますから(それが検出されると、そのディスクにはライトプロテクトがかかってしまうらしい)、私が計画しております、仮想フロッピーディスクシステムでは、無用のものになります。

そういうことでありますから、ここは、まあ、このままにしておいてもよい、でありましょう。

ということで、本件は終わり。
のつもり、だったのでありますが…。

●cpm22.z80のバグはとんでもないバグでありました

ここで、はて?
と、また疑問が生じてきました。
一体このバグは、どこから来たのだろう?

上に書きましたように、ADD A,Mは8080アセンブラの表記です。
このソースプログラムの作者(リストの先頭には、by Clark A. Calkinsの署名があります)は、この部分を間違えて、うっかり8080アセンブラで書いてしまったのでしょうか?

しかし、もともとこのソースプログラムはCalkins氏が書いたのではなくて、あるシステム用のCP/Mのマシン語コードをもとにZ80逆アセンブラを使ってソースプログラムを逆生成したものに、あとから氏がコメント文を追加したもののようです。

作者がニーモニックそのものを直接書いたということではない、とすると…?

おお!
ひょっとすると、この部分 ADD A,M を書いたのは、氏が逆アセンブルするのに使った、Z80逆アセンブラそのものではあるまいか?

もしも、そうだったとすると…。
うわっ。
他にも同じ間違いがあるのでは!

その通りでありました。
「,M」で検索をかけてみましたら。
なんと。
他にも同じバグがみつかりました。

2043 CHKSUM1:ADD	A,M		;and compute sum ignoring carries.

2168 	SBC	A,M		;set carry if no more names.

3340	SBC	A,M

3348	SBC	A,M

行の先頭にある数字はTerapadでcpm22e.txtを開いたときに表示される行番号です。
ADD A,Mは前回から説明をしておりますCHKSUM:の1ヶ所だけですが、そのほかにSBC A,Mが3ヶ所みつかりました。
みつかったSBC A,Mの1つを、アセンブルリストで見てみますと、下のようになっています。



これもADD A,Mと同じで、Z80ニーモニックではなくて8080ニーモニックです。
生成されたマシン語コードはDE00です。
DE00は、SBC A,00のマシン語コードです。
Mが未定義のラベルなので、00に置きかえられてしまったようです。
ここは、本来はSBC A,(HL)と表記すべきところです。
SBC A,(HL)のマシン語コードは、9Eです。

うむむ。
Calkins氏がお使いになったZ80逆アセンブラはどのようなものかは知りませぬが、Z80ニーモニックの代わりに8080ニーモニックを吐き出してしまうとは、さてさてとんでもない逆アセンブラのようであります。

●CP/Mソースプログラムの再々修正

さすがにこのままのソースプログラムでは、おそらくそのうちに挙動不審な動作をして悩むことになると思いますから、再度、修正しておくことに致します。
前回([第35回])作成保存しましたcpm22e.txtをTeraPadで開いて、下記のように4ヶ所を修正してください。

2043 CHKSUM1:ADD	A,(HL)		;and compute sum ignoring carries.

2168 	SBC	A,(HL)		;set carry if no more names.

3340	SBC	A,(HL)

3348	SBC	A,(HL)

行の先頭にある数字はTeraPadでcpm22e.txtを開いたときに表示される行番号です。
A,Mとなっているところを上のリストのようにA,(HL)に直します。

今回はこの4ヶ所のみですから、それぞれ手作業で修正してもそれほど手間はかかりません。
もっと変更箇所が多い場合には、[検索]→[置換]を使って一括変換をすると便利です。
ただし、特にプログラムソースに対して、今回のように命令の一部を変更する場合は余程注意しないと、とんでもない落とし穴があって変換してはならないところが変換されてしまったりします。
その意味では、「すべて置換」は使わない方が安全です。

参考までに「検索」→「置換」の方法を簡単に説明します。
ツールバーの「検索」→「置換」をクリックします。



「検索する文字列」に「,M」を、「置換後の文字列」に「,(HL)」を入れます(共に半角で入力します)。



最初の一回だけは「先頭から検索」をクリックして、先頭にある「,M」を置き換えます。
その置換完了後にもう一度[検索]→[置換]と操作して、「連続置換」をクリックすると、その次の「,M」から順次置換えをすることができます。
ここで「先頭から検索」をしないでいきなり「連続置換」をすると、行の先頭からではなくて、現在のカーソルポイントから検索(置換え)が行なわれてしまいます。

以上のように修正ができましたら、ファイル名をcpm22f.txt にして保存してください。
保存したら、ZASM1.64でアセンブルしてください。


アセンブルの結果作成されたcpm22f.prnを開いてみました。


おお。
BIOSジャンプテーブルのアドレスがD203から始まっています。
ここがD200にならないのは、CP/Mプログラムの先頭に初期設定ルーチンへのJP命令を追加しているためです([第32回]参照。#44 START:JP SETENTRY)。
このジャンプ命令さえ無ければ、今回のバグ修正によって、もともとのCP/MオリジナルのようにBIOSジャンプテーブルのアドレスが××00Hから始まります。

むむ。
そうか。
やっぱり、先頭のジャンプ命令はまずいですね。
またまた変更で申し訳ありませんが、そこは次回に変更することにいたします。

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

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