2020-12-06 22:16 — asano
カテゴリー:
SBCF8が動きましたので早速Universal Monitorを移植してみます。
以前のSC/MPも癖が強かったですが、このF8もそれに劣らず癖の強いプロセッサです。
- コードフェッチ以外のメモリアクセスはすべて
DC0
レジスタ間接でなくてはならず、常にポストインクリメントされます。もう一つあるDC1
(アクセスには使えない)との間で値の交換が出来るのがせめてもの救いです。 - サブルーチン呼び出しはスタックではなく戻り番地はスタックレジスタ
PC1
に退避されます。 - メモリを指すことの出来るプログラムカウンタ
PC0
,PC1
,DC0
,DC1
への値の出し入れは(これらがCPU外にあるため)非常に限定されています。即値やスクラッチパッド(後述)の決められた場所との間でやり取りが可能なだけで、可能な組み合わせも限られています。 - 絶対番地によるジャンプ(
JMP
命令)・サブルーチン呼び出し(PI
命令)を行なうとアキュムレータA
が破壊されます。 - スクラッチパッドと呼ばれる64バイトのメモリがありますが、比較的自由にアクセスできるのは先頭の16バイトだけで、残りのエリアは
ISAR
と呼ばれるポインタ経由でのみアクセスできます。このISAR
はポストインクリメント・デクリメントできますが下3ビットのみでビット2からビット3への繰り上がりはありません。 - ほとんどの演算・代入はアキュムレータ
A
との間でしか行なえません。
こんな状況でUniversal Monitorの移植を考える上で真っ先に問題になったのはサブルーチン呼び出しをどうするかです。
SC/MPやTMS9900のようにスタックを実装すれば良いのですが、ポインタのやりくりが大変です。不可能ではありませんがかなりのコードを必要とします。スタックをメモリではなくスクラッチパッドにおけば若干楽になりますが大変なことに変わりありません。
リエントラントにしなくて良ければ(Universal Monitorではリエントラントは不要)各ルーチンごとに固定の場所に保存すればよくかなり楽になります。メモリの消費が増えますが、呼び出しチェーンに同時に現れないルーチンで共有できますからある程度の節約は可能です。
以上を考えてサブルーチンを勝手に次のように分類してみました。
- 第4種:サブルーチンを呼び出さないもの
- 第3種:第4種サブルーチンだけを呼び出すもの
- 第2種:直接・間接に自分を呼び出さないもの
- 第1種:直接・間接に自分を呼び出す可能性のあるもの
- 番外:一箇所からしか呼び出されないもの
第1種はリエントラントを必要としますからスタックまたはそれに類するものが必要ですが、幸い今回は対象となるものはありません。Tiny BASICを移植しようとすると式評価ルーチンが括弧や関数の引数の処理で式評価ルーチンを呼び出す場面が出てきそうです。
第4種はPC1
を保存する必要がありませんから簡単です。
ENTRY:
<処理>
POP
最後のPOP
でPC1
に保存されている戻り番地をPC0
に復帰させます。
第3種では別のルーチン呼び出し時にPC1
が破壊されるのでどこかに保存しておかなくてはなりません。
ENTRY:
LR K,P
<処理>
PI <第4種サブルーチン>
<処理>
LR P,K
POP
ここでP
はPC1
のアセンブリ言語での表記です。K
はスクラッチパッドの12,13番地のことです。
第2種では呼び出した先の第3種サブルーチンでK
も破壊されます。K
以外に保存できればよいのですが、PC1
とやり取りが出来るのはK
だけなのでまずK
を保存しなくてはなりません。
ENTRY:
LR A,KU
LR 7,A
LR A,KL
LR 8,A
LR K,P
<処理>
PI <第3種サブルーチン>
<処理>
LR P,K
LR A,7
LR KU,A
LR A,8
LR KL,A
POP
最初にK
をスクラッチパッドの7,8番地に保存します。続いてPC1
をK
に保存します。
今回はスクラッチパッドの16未満の番地に保存したからこれで済んでいますが、保存先が外部メモリだったりしたらDC0
の設定から(場合によってはDC0
の保存も必要だったり)行なわなくてはならず大変です。
第1種の場合は上記の保存先をずらしていく処理がさらに必要になります。
番外は逆に楽なパターン、呼び出し元が一箇所ということは戻り先も固定なので保存の必要もありません。決まったアドレスにJMP
で戻ればOKです。
(続く)
コメントを追加