1981年製の64ピンセラミックパッケージのTMS9900。
今でこそ普通の64ピンDIPパッケージですが、当時は40ピンDIPでもマイクロプロセッサ関係の特別なLSIにしか使われておらず(そう、マイクロプロセッサというのは普通のICじゃなかったのよ)、40ピンですら大きめの印象があったのだから、このパッケージは巨大な印象を持ちました。足の取り付け方が時代を感じます。
その他、48 MHzを分周して作成した4相クロックを要求するとか、汎用レジスタを内部に持たないアーキテクチャとか、わがままで変わった構成のマイクロプロセッサという感じを持ちました。
16 bit CPUですが、アドレスはByte単位で割り当て、アドレッシング範囲も16
bitなので、64 KByteつまり32 KWordのメモリ空間を持ちます。16 bit CPUにしては小さなメモリ空間という印象になるかもしれませんが、これが発表された当時は1
KByteのメモリチップ代だけで1万円を越える時代です。バッファや制御回路とともに基板に取り付けたメモリボードは4
KByteでも10万円程度になります。16 KByteもメモリを搭載したコンピュータというと、かなり高度な応用にしか使われず、マイクロコンピュータ化する利点が減ってしまいますから、これで良かったのでしょう。
CPUに内蔵されているレジスタは、なんと次の3個しかありません。
PCはプログラムカウンタ。WPはワークスペースポインタというもので、STがステータスレジスタです。なんとスタックポインタすらありませんし、アキュムレータのような演算対象となりそうなレジスタもありません。この鍵はWPの役割にあるのですが、その前に注意すべき点とSTの意味について。
TMS9900とそのファミリーでは、1語の中のビットの番号付けがMotorolaやIntelのマイクロプロセッサの流儀と異なっています。図にも示した通り、MSBがビット0で、LSBがビット15です。メモリアドレスは前述の通りにバイト単位で付けられていますが、16
bitデータをメモリに格納する場合、上位バイトが指定されたメモリアドレスに、下位バイトが指定されたアドレスを+
1したアドレスに格納されます。
ステータスレジスタSTにはフラグ類や割り込みマスクが格納されています。STのビット0
(以降ST0と略記)は論理大なりフラグで無符号2進数の大小関係によって変化します。ST1は算術大なりフラグで、符号付き2進数として演算対象を解釈した場合の大小関係によって変化します。ST2のEQフラグは演算結果が0かどうかと、ビットテストであるTB命令の結果が反映されます。ST3は他のマイクロプロセッサと同じ役割のキャリーフラグで、ST4がオーバーフローフラグになります。ST5のOPはOdd
Parityの略で、奇数パリティのときにセットされます。OPに関してはワード単位の演算では変化せず、バイト単位の演算の場合に変化することに注意してください。ST6のXOPフラグはXOP命令実行中であることを示すフラグです。ST12からST15は割り込みマスクで1111ですべての割り込み許可、0000でレベル0のマスク不可の割り込みだけが許可、0001でレベル1割り込みとレベル0割り込みが許可、という意味になります。XOPとIMは条件判断ではなくプロセッサの状態を保持するためのビットで、それ以外は条件分岐用のフラグとして使われるのが主な役目です。
さて、普通のCPUでいう汎用レジスタ、演算対象としてのレジスタがどこにあるかというと、メモリ上にあります。そのレジスタの場所を指し示すのがWP、ワークスペースポインタです。レジスタは16
bit幅で、0から15までの番号が振られていて、演算対象としてだいたい同じ役目を果たします。レジスタ0以外の15個のレジスタは、インデックスレジスタとしても使用できます。TMS9900にはスタックポインタがなくて、レジスタのPUSH/POPなんかもできませんが、WPを書き換えることでレジスタを保存したり復帰したりが自在にできるわけです。CPU内部にはPC,
WP, STしか存在しないというのは非力なように見えますが、割り込みなんかでレジスタをすべて退避する場合、PC,
WP, STを退避してWPに別の値を入れてしまえば割り込み前の汎用レジスタを書き換える心配はなくなるわけで、コンテキストスイッチの高速化が期待できます。割り込み応答速度が重要な応用では有利になりますね。ついでに多数のトランジスタが必要なレジスタ類を内蔵しなくて済むので、ハードウェアが単純化されます。
スタックポインタが存在しないと、サブルーチン呼び出しはどうするんだろうと心配になるかもしれません。一番単純なBL
(Branch and Link)命令ではレジスタ11にリターンアドレスを格納します。サブルーチンから帰る場合にはレジスタ11を指定したレジスタ間接アドレッシングによる分岐命令を使用します。サブルーチンの中でレジスタ11を保存するとか、WPを書き換えるとかすれば、入れ子になったサブルーチンも実現できるという仕組みです。もう少し高度なサブルーチン呼び出しではBLWP
(Branch and Load Workspace Pointer)命令を使用します。この命令では分岐先アドレスの他に16
bit定数も指定して、その定数がWPにロードされます。ロード直前のWPやPCの値は、新レジスタに格納されます。レジスタ13に旧WPが、レジスタ14に旧PCが、レジスタ15に旧STが、保存されます。この3種類の値を保存すれば、BLWP実行前のプログラムのコンテキストがすべて保存されたことになるわけです。このサブルーチンからの復帰には、専用のRTWP
(Return Workspace Pointer)命令を使用します。
じつは古い大型機や最初期のミニコンピュータでは、スタックポインタを持たないアーキテクチャも多かったのです。Algol系の高級言語が普及してスタックというデータ構造が重要視される前は、わざわざコンピュータアーキテクチャの側でサポートする必要もありませんでした。ではどうしていたかというと、このTMS9900のように、特定のレジスタ(あるいは指定された任意のレジスタ)にリターンアドレスを格納するというパターンと、サブルーチンヘッダ擬似命令などをアセンブラに装備しておいて、サブルーチンの先頭アドレスにリターンアドレスを格納してしまうものがあります。後者では、サブルーチン本体はサブルーチンの先頭アドレスの次の語から始まるわけです。当然、再帰呼び出しはプログラムコードで細工をしなくては実現できません。しかし、FORTRANやCOBOLで書かれたプログラムを実行するのなら、それほど不便ではなかったのですね。マイクロプロセッサは組み込み用途が応用の中心に想定されていましたから、プログラムをROMに格納する都合上、後者のサブルーチン方式は使えませんでした。レジスタが多数あるアーキテクチャではレジスタに格納する方式も使われましたが(TMS9900の他にもuPD751などが採用しています)、大多数のマイクロプロセッサではスタックアーキテクチャの優位性が認識されていたため、スタックに戻りアドレスを格納するようになっています。L-16Aのように、サブルーチン呼び出しはスタックを使用するけれども、割り込みに関してはメモリの特定アドレスにコンテキストを保存するアーキテクチャも存在します。また、PDP-11のように、レジスタ型とスタック型を併せ持ったサブルーチン方式を使用しているアーキテクチャも存在します。PDP-11のJSR命令は、JSR
R1, addrのように、リターンアドレスを保持するレジスタも同時に指定します。レジスタにリターンアドレスを保持すると同時にスタックへもリターンアドレスをプッシュします。こうして、旧来のプログラムテクニックも使えるようになっていたわけです。しかし、普通はこれだとレジスタをひとつ書き換えてしまって無駄です。そのような場合、PDP-11ではPCもレジスタの一種であるという特徴を利用してJSR
PC, addrのようにすれば、リターンアドレスをPCとスタックトップに格納してからサブルーチンアドレスをPCに書き込みますから、PCが上書きされてレジスタを余分に消費する必要がなくなります。まぁ、普通にJSR
addrと書けば、アセンブラがJSR PC, addrと解釈するはずですけどね。
このように、サブルーチンというごく普通の仕組みを実現する方式ですら、歴史的にいくつものやり方が提案されて実装されてきたわけです。
話題をTMS9900の命令体系に戻しましょう。TMS9900は16 bit幅の汎用レジスタを16個備えていますから、たいていの演算はレジスタ−レジスタ間演算で済みそうです。でもアーキテクチャ上はメモリ−メモリ間演算が基本です。もちろんアドレッシングモードとしてレジスタも指定できますから、レジスタ−メモリ間の演算やメモリ−レジスタ間の演算も可能で、PDP-11に近いくらいの強力な命令セットになっています。
TMS9900のアドレッシングモードは以下の8種類あります。
TMS9900の命令フォーマットは9種類に分類されます。そのフォーマットごとに命令を見ていくことにします。
タイプ1 : 一般2項演算
これに属する命令はソースとディスティネーションの指定をオペランドに持ちます。フォーマットは
XXXXddDDDDssSSSS
という形です。ここでXXXXは命令ごとに違うビットパターンで、ddはディスティネーションのアドレッシングモード指定ビット、DDDDはディスティネーションのレジスタ指定、ssはソースのアドレッシングモード、SSSSはソースのレジスタ指定です。レジスタアドレッシングの場合、アドレッシングモード指定ビットは00に、レジスタ間接アドレッシングの場合は01に、レジスタ間接オートインクリメントの場合は11に、インデックスか直接アドレッシングの場合は10になります。インデックスアドレッシングと直接アドレッシングの違いは、レジスタ指定が0000以外か0000になっているかという点です。
アドレッシングモードによって修飾ワードが付加されるものがあります(インデックスアドレッシングなど)。その場合、命令の直後に配置されますが、ソースに関する修飾ワードが命令の直後に配置されます。ソース修飾ワードがなくてディスティネーション修飾ワードだけ存在する場合はそれが命令の直後に配置されますが、両方とも存在する場合はソース、ディスティネーションの順になります。
タイプ1の命令は12種類あります。
A s, d A000 Add
s + d -> d
AB s, d B000 Add Bytes
s.b + d.b -> d.b
C s, d 8000 Compare
s - d
CB s, d 9000 Compare Bytes
s.b - d.b
MOV s, d C000 Move
s -> d
MOVB s, d D000 Move Bytes
s.b -> d.b
S s, d 6000 Subtract
d - s -> d
SB s, d 7000 Subtract Bytes
d.b - s.b -> d.b
SOC s, d E000 Set Ones Corresponding
s OR d -> d
SOCB s, d F000 Set Ones Corresponding Bytes
s.b OR d.b -> d.b
SZC s, d 4000 Set Zeros Corresponding
NOT(s) AND d -> d
SZCB s, d 5000 Set Zeros Corresponding Bytes
NOT(s.b) AND d.b -> d.b
2項演算としてANDやORといった名称がありませんが、ORはSOC命令として、ANDの変形がSZC命令として備わっています。XORに関してはタイプ3のレジスタ−メモリ演算として定義されています。ただし、ANDやORのイミディエート命令に関してはANDIやORIとして定義されていますから、少し変な感じもしますけど。
あと、ディスティネーションが後ろに書かれることに注意してください。8080系のアセンブリ言語とは逆になっています。
タイプ2 : Jump命令グループ
これはJMP系かCRU I/Oに関する命令で、相対アドレッシングを行うものです。すべて1
Word命令で、次のフォーマットになります。
0001XXXXDDDDDDDD
ここでXXXXが命令によって変化する部分で、DDDDDDDDがディスプレースメントです。属する命令は次の16種類です。
JEQ a 1300 Jump if Equal
on EQ = 1
JGT a 1500 Jump if Greater
Than
on A> = 1
JH a 1B00 Jump if High
on L> = 1 AND EQ = 0
JHE a 1400 Jump if High or
Equal
on L> = 1 OR EQ = 1
JL a 1A00 Jump if Low
on L> = 0 AND EQ = 0
JLE a 1200 Jump if Low or Equal
on L> = 0 OR EQ = 1
JLT a 1100 Jump if Less Than
on A> = 0 AND EQ = 0
JMP a 1000 Jump unconditionally
always
JNC a 1700 Jump if No Carry
on C = 0
JNE a 1600 Jump if Not Equal
on EQ = 0
JNO a 1900 Jump if No Overflow
on OV = 0
JOC a 1800 Jump On Carry
on C = 1
JOP a 1C00 Jump if Odd Parity
on OP = 1
SBO a 1D00 Set Bit to One
Set CRU bit
SBZ a 1E00 Set Bit to Zero
Reset CRU bit
TB a 1F00 Test Bit
Test CRU bit
ほとんどは分岐命令ですが、SBO, SBZ, TB命令はCRU関係のビット操作命令です。
タイプ3 : レジスタ−メモリ間演算
このタイプの命令はタイプ1のディスティネーションがレジスタに固定されているものです。
001XXXRRRRssSSSS
という形式で、次の5種類があります。
COC s, r 2000 Compare Ones Corresponding
s AND NOT(r)
CZC s, r 2400 Compare Zeros Corresponding
NOT(s) AND r
DIV d, r 3C00 Divide
(r, r+1)/s -> (r, r+1)
MPY d, r 3800 Multiply
r * s -> (r, r+1)
XOR s, r 2800 Exclusive OR
s EOR r -> r
DIVはRnとRn+1で表される32 bitの数値をソースの内容で割り、商をRnに、剰余をRn+1に格納します。MPYは16
bit同士の乗算です。COCとCZCは比較用の命令で、EQフラグだけが影響を受けます。
タイプ4 : CRU命令
この命令はCRU I/Oと複数ビットの転送を行います。命令フォーマットは
00110XCCCCssSSSS
となっていて、ソースで指示されたワードのLSBから順にCCCCで指定される任意のビット数だけ、転送します。
LDCR s, c 3000 Load Communication Register
STCR s, c 3400 Store Communication Register
タイプ5 : シフト命令
これに属するのはシフト関係の4種類で、
000010XXCCCCRRRR
という命令形式を持ちます。
SLA r, c 0A00 Shift Left Arithmetic
bit 0 -> C, 0 -> bit 15
SRA r, c 0800 Shift Right Arithmetic
bit 0 -> bit 0, bit 15 -> C
SRC r, c 0B00 Shift Right Circular
bit 15 -> bit 0, bit 15 -> C
SRL r, c 0900 Shift Right Logical
0 -> bit 0, bit 15 -> C
cで指定した回数だけシフトを行えますが、cに0が指定された場合に限り、R0の内容に従ってシフト量が決められます。
タイプ6 : 単項演算
この型の命令は、操作対象がひとつだけのもので、インクリメントなどのおなじみのものが多いのですが、分岐先アドレスも操作対象の一種だということで分岐やサブルーチン呼び出しが含まれています。
000001XXXXssSSSS
というフォーマットです。
ABS d 0740 Absolute value
| d | -> d
B s 0440 Branch
s -> PC
BL s 0680 Branch and
Link
PC -> R11, s -> PC
BLWP s 0400 Branch & Load Workspace
Pointer WP -> R13, PC -> R14, ST -> R15, s -> WP, s + 2 ->
PC
CLR d 04C0 Clear
0 -> d
DEC d 0600 Decrement
d - 1 -> d
DECT d 0640 Decrement by Two
d - 2 -> d
INC d 0580 Increment
d + 1 -> d
INCT d 05C0 Increment by Two
d + 2 -> d
INV d 0540 Invert
NOT(d) -> d
NEG d 0500 Negate
- d -> d
SETO d 0700 Set to Ones
FFFF -> d
SWPB d 06C0 Swap Bytes
d.0-7 <-> d.8-15
X s 0480 Execute
the instruction at s
BLWP命令についてはすでに説明しましたね。インクリメントやデクリメントに2を単位とする専用の命令があって、ワード単位のポインタ操作が容易になっています。SETOは全ビットを1にする命令で、SWPB命令は上下バイトを入れ替える命令です。X命令は他のプロセッサには見られない変わった命令かもしれません。ソースとして指定された場所の内容を、命令として解釈して実行する命令です。
タイプ7 : オペランドなし
以下の命令はオペランドを持たず、オペコードが修飾されることはありません。
CKOF 03C0 Clock Off
CKON 03A0 Clock On
IDLE 0340 Computer Idle
Wait for Interrupt
LREX 03E0 Load or Restart
Execution
RSET 0360 Reset External
Diveces
0 -> IM0-3
RTWP 0380 Return Workspace
Pointer R15 -> ST,
R14 -> PC, R13 -> WP
タイプ8 : イミディエート命令
タイプ8は主にレジスタにイミディエート値を適用するものですが、レジスタ指定のないものや、レジスタ指定だけでイミディエート値のないものも含まれます。一般的な命令フォーマットは
0000001XXXX0RRRR
という形で、RRRRがレジスタ指定、イミディエート値をオペランドにもつ命令は、この後に16
bit定数が続きます。
AI r, i 0220 Add Immediate
r + i -> r
ANDI r, i 0240 AND Immediate
r AND i -> r
CI r, i 0280 Compare Immediate
r - i
LI r, i 0200 Load Immediate
i -> r
LIMI i 0300 Load Interrupt Mask Immediate
i -> IM0-3
LWPI i 02E0 Load Workspace Pointer
Immediate i -> WP
ORI r, i 0260 OR Immediate
r OR i -> r
STST r 02C0 Store Status Register
ST -> r
STWP r 02A0 Store Workspace Pointer
WP -> r
タイプ9 : XOP命令
このグループに属するのはXOP命令だけで、要するにソフトウェア割り込みです。
001011CCCCssSSSS
という形式で、CCCCに割り込みベクタを示す定数が入ります。
XOP s, c 2C00 Extended Operation
s -> R11, WP -> R13, PC -> R14, ST -> R15, (40 + 4*c) -> WP, (42 + 4*c)
-> PC
16進数表示の40にcを4倍したものを加えたアドレスが割り込みベクタです。
その他
たいていのTMS9900用アセンブラには次の組み込みマクロが備わっていると思います。
NOP 1000 No Operation
JMP 0
RT 045B Return
B *R11
仮にアセンブリ言語にサポートが無かったとしても、機械語でのデバッグなんかで必要となるビットパターンだとは思いますけど。
Return to IC Collection