2020-12-11 23:58 — asano
カテゴリー:
2回にわたりサブルーチンについて書きましたが、他にもプログラミング上厄介な点はあります。
まず特定のレジスタ・スクラッチパッドを要求されることが多い点です。
- 演算や値の移動ではいくつか例外はあるもののアキュムレータ
A
相手にしか出来ません - スクラッチパッドの16以降へのアクセスには
ISAR
間接しか使えません - 外部メモリへのアクセスには
DC0
しか使えません DC0
と値のやり取りが出来るのはDC1
との交換・即値代入を除くとH
,Q
のみです- プログラムカウンタ
PC0
に代入出来るのはJMP
,PI
命令による絶対アドレスの他はK
,Q
のみです PC1
と値のやり取りが出来るのはK
のみです- ステータス
W
と値のやり取りが出来るのはJ
のみです
ここでJ
, H
, K
, Q
はスクラッチパッド上の決まった場所です。
その1でも書きましたが、JMP
などでもA
が破壊されます。
これは
JMP
命令の実行を追ってみるとわかります。まずPC0
のアドレスからオペコードを読んで(直後にPC0
を+1、以下同様)JMP
命令であることがわかります。次に1進んだPC0
から飛び先の上位バイトを読みます。これは最終的にはPC0
の上位に代入しますが、ここでPC0
を変更してしまうと3バイト目の下位アドレスの読み込みが出来なくなってしまうのでどこかに保存しておく必要があります。この保存場所をケチってA
で代用していると思われます。
その他なにをするにしてもA
を必要とするので何らかの値を入れたままにしておくことが出来ません。
そういうわけでZ80などでレジスタに割り当てていたものはスクラッチパッドの0~15に、入力バッファ(他の用途にも流用していますが)は16~31に、その他ワークエリアは32~に配置しました。D
コマンドのアドレスのようにDC0
とのやり取りが発生するものを優先的にH
などに割り当てています。
ISAR
, DC0
も原則として短期使用として必要に応じてスクラッチパッド(0~15)に退避するようにしています。
コンソール用の8251にアクセスするところは例外で、XDC
命令でDC1
に退避します。これは頻繁に使われるのでメインルーチンの負担を増やさないためです。
他にも細かな不便な点はいろいろあります。
CI
(Compare Immediate)命令が逆
(A
- imm ) なプロセッサが多い中これは逆なので境界条件バグを多発させてしまいました。CS
(Compare Scratchpad)に相当する命令がない
DPB
ルーチンでは一致判定だったのでXS
(XOR Scratchpad)で代用しています。INC
はあるのにDEC
が無い- 加算命令はあるのに減算命令がない
Universal Monitorでは即値ばかりなので2の補数の加算で済みました。 - Z80の
OR A
に相当する命令が無い?
最初は素直に次のように書いていました。LR A,8 CI 0
途中からはちょっと短く速く改めています。
CLR AS 8
LM
,ST
で外部メモリにアクセスするとDC0
が勝手にインクリメントされる
デクリメントするのは結構面倒なのでH
かQ
に保存しておくほうが良いでしょう。デクリメントするにも一旦どちらかに転送しないと出来ませんし。ISAR
のポストインクリメント・デクリメント
ISAR
はスクラッチパッドのアクセス後に+1することも-1することも不変も可能ですが、下3ビットのみです。このことを失念していてASCIIダンプのバグを出してしまいました。結局一旦A
に移してINC
しています。逆順にしていればBR7
が使えたのかもしれません。IN
/OUT
には短縮命令INS
/OUTS
があるが...
完全に互換ではないので注意が必要です。例えばF3850内蔵のポート0,1にはINS
/OUTS
でしかアクセスできません。
逆に便利だと思った点もあります。
LNK
命令
キャリーのみ加算する命令で16ビットのINC
の上位バイトに使いました。
そんなこんなでD
,S
,G
,L
,I
,O
の各コマンドが使えるところまで実装できています。
コメントを追加