CPU実験室

誰も見向きもしない古いCPUをいじって動かしてみようというプロジェクトです

モニタコーディング中

慣れないAm29kアセンブラでプログラムを書いていますがかなり苦戦しています。アドレッシングモードの制限やスタックの扱いなど本来は高級言語コンパイラがサポートする部分なのでしょうが手作業で試行錯誤している状態です。

コーディングしながら気づいた点ですが

1.オペランドの並びが変

アセンブラ言語のソースとディストネーションの並び順は86系と68系では逆だったりしますが、普通は1つのCPUの命令セットの中では統一されています。ところがこのAm29kプロセッサではインストラクションによって順番が変わることがあります。たとえばLOADSTOREはメモリに対して読出し/書込みで作用は逆ですがオペランドの並びもひっくり返ります。


Syntax: LOAD 0, cntl, destination, source
Operation: destination ← EXTERNAL WORD [source]


Syntax: STORE 0, cntl, source, destination
Operation: EXTERNAL WORD [destination] ← source

 

これを他のCPUと同じように代入の矢印の向きはいつも同じと思い込んでアセンブラコードを書くと思った通りのメモリ参照ができず悩むことになります。 

 

2.レジスタ間転送がない(たぶん・・・

Z80LD A,Bとか8086MOV BX,AXのように内容を変更しないでそのままコピーする命令がありません。むりくりやるとすると演算結果が変化しない演算で行うしかないのかもしれません。

ADD destination,source,0    :destination←source+0

とか。

 

3.即値演算のデータ幅が8ビットしかない

Am29kの命令長は32ビット固定で代表的なフォーマットは

OPCODE destination,source1,source2

の形をとります。それぞれのフィールドが8ビット幅なので即値を指定できるsource2も8ビット幅しかなく32ビット幅レジスタにアライメントされるとき上位ビットは勝手に0拡張されてしまいます。これはレジスタの使用効率が悪い(8086ではAXAH/ALに分割して使えるのに・・・って貧乏性かっ!)だけでなくレジスタの上位ビットにゴミが入っていると比較命令でマッチせず嵌まることになります

 

4.即値代入のデータ幅が16ビットしかない

単純にレジスタに即値をセットする場合は定数セット命令

CONST destination,imm16

が使えます。この場合は16ビット幅の即値が使えますがセットされるのは下位16ビットで上位ビットはやはり勝手に0拡張されてしまいます。では上位ビットをセットするのどうするかというと

CONSTH destination,imm16

という別の専用命令を使うことになります。ここで要注意なのは転送先のレジスタは上位下位半分づつ独立してアクセスしているわけではないので

CONSTH destination,imm16H(D31..D16)

CONST destination,imm16L(D15..D0)

の順で実行すると期待する結果になりません。2行目がせっかく1行目でセットした上位16ビットをクリアしてしまいます

 

5.アドレスの即値指定

上に関連しますが引数が32ビット幅アドレス、例えば固定文字列の先頭アドレスをラベルで渡す場合、一度に代入できるのは下位16ビットのみなので上位16ビットはデータが存在するアドレスを探して別途与えなければなりません。アドレス空間は4Gバイトリニアなのに64kbyteのセグメントがあるような感じで何か気持ちが悪いところです。

以下はコーディング中のモニタのソースの一部でオープニングメッセージ文字列のあるアドレスMSG1を文字列出力ルーチンSTROUTに渡している部分で、STROUTルーチン内で上位ビットをセットしています

 

f:id:O3I:20201219113128j:plain


6.スタックの処理

Am29kプロセッサの最大の特徴が潤沢にあるレジスタによる「レジスタウィンドウ」である、とのことですがこれの使い方が理解できていません。スタックの処理もここに含まれていると思われ、サブルーチン(AMDではプロシジャーコールと云っている)の作り方にもかかわってきます。

今の段階では特定レジスタ1個に戻りアドレスを退避してジャンプ、という作りにしていますが当然1個ではサブルーチンの中から別のサブルーチンを呼ぶことができません。苦肉の策として退避レジスタを複数用意してそのサブルーチンのネストの深さ(請負レベル)によってレジスタを使い分けることにしました。

以下はモニタに含まれるダンプ時のヘキサ表示ルーチンですがコール関係は

 モニタプログラムメインルーチン(請負レベル0)

  ↓

 HEX2DSP:HEX2桁表示ルーチン(請負レベル1)・・戻りアドレスは tpc2に退避

  ↓

 HEXDSP:HEX1桁表示ルーチン(請負レベル2) ・・戻りアドレスは tpc1に退避

  ↓

 TXCHAR:1文字出力ルーチン(請負レベル3)  ・・戻りアドレスは tpcに退避

となっています。

f:id:O3I:20201219101313j:plain

とりあえずアセンブラレベルのコーディングではこれで凌いでおいて、スタック処理はGCCが使えるようになったらコンパイラに任せてしまうということにします。