カテゴリー
          
      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と同様に書けるようになります。
全般的にいろいろ落とし穴はありますが慣れれば意外に書きやすいのかも、というのが現時点での感想ですね。
関連項目
          
      
Add new comment