2020-04-21 20:53 — asano
カテゴリー:
TMS9995ボードにUniversal Monitorの基本部分(D,G,Sコマンド)の移植ができましたので、アセンブリ言語でプログラミングしてみて感じたことを書いてみようと思います。
- 豊富なレジスタ
16と数が多いだけではなく基本的に対等なのでやりくりに苦労することがあまりありません。まぁ実態はメモリの一部なので直接メモリアクセスするのに比べメリットは少ない(命令長が短くなる・一部レジスタ専用の命令がある)ですが。 - レジスタの退避・復帰
上述のようにレジスタはメモリの一部を割り当てるのですが、メモリのどこを使うかはWP
レジスタによって指定します。つまりこのWP
レジスタを書き換えるだけでレジスタの一括退避・復帰ができます。サブルーチン呼び出しと同時にWP
レジスタを更新する命令もあります。
Universal Monitorでは現状これは使用していません。R
コマンド対応には使おうかなと考えています。 - スタックが無い
最近のCPUに慣れた目には信じられないかもしれませんがスタックはありません。それではサブルーチン呼び出しはどうするのかというと戻りアドレスが特定のレジスタ(R11
レジスタ)に格納されます。多重呼び出しをする場合にこのR11
レジスタを保存するのはプログラマの責任です。
今回当初は他レジスタに退避していたのですが、結局スタックを実装してしまいました。 - レジスタ長
レジスタ長が16ビットありアドレス計算も一発で可能です。6502などアドレスを加算するだけでかなりの命令数を必要としたのに比べるととても楽でした。
その代わりにキャリーを含む加減算命令がないようで、16ビットを超える多倍長演算は面倒そうです。 - Compare系の命令でキャリーが変化しない
これ最初にハマりました。
Universal Monitorは基本他のCPUからの移植なので、大小判定をキャリーで書いてしまったのです。1行入力の文字レンジチェックが正しく働かずに何も入力できないことでアレっと思い、データシート読み直して気付きました。 - SubtractとCompareで引き算順序が逆
上記修正中になぜか大小判定が逆で悩んだ点、減算が dst - src → dst なので、比較は当然 dst - src だと思い込んだら、実は src - dst だったという。 - 8ビット演算命令
多くの命令が16ビット値を対象としますが、一部に8ビット値を対象とする命令があります。例えばMOVB R0,*R4
はR0
レジスタの値をR4
レジスタの指すアドレスに1バイト書き込みますが、R0
の上位・下位どちらが使われるでしょうか?
実は上位が使われるのです。8ビットの即値命令は無いので一旦どれかのレジスタに入れてから8ビットの命令を使うのですが、このとき上位バイトに入れないといけないのです。おかげでソースが「<< 8
」だらけになってしまっています。
上でも書いたスタック操作などいくつかのマクロを定義したので紹介しておきます。
;;; -*- asm -*-
;;;
;;; MACROs definition for TMS9900
;;;
NOP MACRO
JMP 0
ENDM
RT MACRO
B *R11
ENDM
PUSH MACRO reg ; PUSH register to stack (SP=R15)
DECT R15
MOV reg,*R15
ENDM
POP MACRO reg ; POP register from stack (SP=R15)
MOV *R15+,reg
ENDM
RTS MACRO ; RETURN using stack (SP=R15)
MOV *R15+,R11
B *R11
ENDM
NOP
はいいですね。
RT
はサブルーチンからの復帰命令、戻りアドレスはR11
に入っているのでそこへ飛びます。
PUSH
はスタックに任意のレジスタを積みます。スタックポインタ(SP
)としてはR15
を想定しています。
POP
はスタックから任意のレジスタに取り出します。オートインクリメントを使うことで1命令で実現できました。
RTS
は頻出するPOP
+RT
を一まとめにしたもの、サブルーチン先頭でPUSH R11
、末尾でRTS
すれば他の一般的なCPUと同様に書けるようになります。
全般的にいろいろ落とし穴はありますが慣れれば意外に書きやすいのかも、というのが現時点での感想ですね。
関連項目:
コメントを追加