2021-01-14 21:48 — asano
カテゴリー:
前回予告したように今回はCALR
命令について書いてみようと思います。
CALR
命令は相対サブルーチンコール命令で、JP
に対してJR
があるようにCALL
に対してあるのがこれです。同様に相対番地でメモリアクセスするLDR
命令や、アドレスを取得するLDAR
命令もあります。
Z80の位置独立なコードを書けない(著しく困難)問題に対処しています。
さて、Universal Monitorの移植にあたりいつものようにコンソールドライバを書いたあとオープニングメッセージの表示に必要な最小限を書いてテストしていました。ドライバの初期化や1文字出力の呼び出しにはCALR
を使っていました。位置独立のためではなく単に短く速いからです。
ところがまったく表示されません。
最初はZ85230 ESCCの初期化パラメータを疑ったりしていたのですが、ふとESCCのCE端子にカウンタを繋いでみたところ0のままでESCCへのアクセスが一切されていないことがわかりました。
そうなると初期化ルーチンが呼ばれていない可能性が高くなります。
そこでCALR
命令をすべてCALL
に書き換えたところ無事動き始めました。モニタが動くようになるまでは実験などやりづらいので、とりあえずCALR
を避けて移植作業を進めました。幸いJR
やDBJNZ
といった他の相対分岐命令は問題出ませんでした。
その後モニタのR(egister)コマンドも使えるようになり実験の準備が整ったので調査開始です。
短いテストプログラムをアセンブルさせたものとハンドアセンブルしたものを比較してみても特に問題なさそうに見えます。おかしいなと思ってシャープのマニュアルをよく読みなおしていたところ、動作としてとんでもないことが書かれていました。
非セグメント・モード
SP←SP-2
@SP←PC
PC←PC-(2×disp)
えっ、マイナス!? PCに足すのではなく引くの!?
てっきり足すのだと思ってハンドアセンブルしてましたし、ASもそういうコードを吐いていました。
ASのメーリングリストで指摘したところ、作者の方より参照している資料では足すことになっているとの回答がありました。
プラスとマイナス双方の資料が存在するなら実機確認してみるしかありません。以下のようなコードを走らせてみました。
8000 : DFFF CALR (disp = -1)
8002 : 7FFF SC #255 (BREAK)
8004 : 7FFF
8006 : 7FFF
すると8004Hでブレークがかかりました。基準アドレス8002H - (2×-1) = 8004Hと考えれば辻褄が合います。
8000HをD000 (disp = 0) にすると8002Hでブレークかかりました。
8000HをD001 (disp = 1) にするとハングアップ、自分のアドレスをコールし続けて無限ループを形成したようです。リセットしてメモリを確認するとRAMが8002H(戻りアドレス)で埋められていました。
この情報を元に再度報告したところ次回リリースにて修正予定とのことでした。
ソースのCALL
命令のうち変更可能なもの(レジスタ間接アドレッシングのできない特殊入出力命令をRAMに書き込んでコールしている箇所以外すべて変更可能でした)をCALR
命令に書き換えてリリースを待っているところです。
コメントを追加