命令
TANACOM-1の命令セット
TANACOM-1 の命令は、16bit固定長です。
メモリ参照命令系
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
0 | OP Code | R1/ R0 |
Ix | Id | M/ im |
Address or immediate |
OP Code | ニーモニック | 命 令 | 動 作 | |||||||||
0 | 0 | 0 | L | Load Rx | Rx ← □ | |||||||
0 | 0 | 1 | ST | Store Rx | Rx → □ | |||||||
0 | 1 | 0 | A | Add Rx | Rx ← Rx + □ | |||||||
0 | 1 | 1 | IA | If Add Rx | Rx ← Rx + □ ( もしR1のLSB=1ならば ) | |||||||
1 | 0 | 0 | S | Subtrect Rx | Rx ← Rx - □ | |||||||
1 | 0 | 1 | N | And Rx | Rx ← Rx AND □ | |||||||
1 | 1 | 0 | X | Xor Rx | Rx ← Rx XOR □ | |||||||
1 | 1 | 1 | C | Compare Rx | Rx - □ ( 比較する ) |
1.レジスタの指定
R1/R0 | 0=R0 , 1=R1 |
2.アドレッシング
TANACOM-1は、ページアドレッシング方式を採用している。
1ページの大きさは、256 wordである。
指示内容 | オペランド対象(□)となる実アドレス | |||||||
無指定 | ページベース値 + Address | |||||||
Ix = 1 | ページベース値 + Address + R1 ・・・インデックス方式 | |||||||
Id = 1 | ( ページベース値+Address ) の値が実アドレス ・・・間接方式 | |||||||
Ix = 1、Id = 1 | ( ページベース値+Address )の値 + R1 が実アドレス・・・間接インデックス方式 |
3.メモリ参照か即値データ
M/im | 1=メモリ参照方式 , 0=即値データ方式( immediateそのものをデータとする) |
4.コンディション・フラグへの反映について
メモリ参照系命令に於いて、メモリ参照か即値かに関わらず、ロード( L )とストア( ST )を除く、残り6つの命令の実行後の結果について、コンディション・フラグへの反映をされます。
但し、I/O空間( 0x8000 ~ 0xFFFF)に対するロード( L )命令が実行された場合に限り、ロードした結果について、コンディション・フラグへの反映をされます(当初設計時では、そうではなかったのですが、後日改良しました)。
Branch命令系
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
1 | OP Code | CNT | Ix | Id | C/ Z |
Address |
OP Code | ニーモニック | 命 令 | 動 作 | |||||||||
0 | 0 | 0 | B | Branch | 指定されたアドレスにジャンプ | |||||||
0 | 0 | 1 | BAL | Branch And Link | PC値をR1に待避して、指定されたアドレスにジャンプ | |||||||
0 | 1 | 0 | BNZ | Branch Non Zero | コンディションフラグ ZERO=0 ならば、ジャンプ | |||||||
0 | 1 | 1 | BNM | Branch Non Minus | 〃 MINUS=0 ならば、ジャンプ | |||||||
1 | 0 | 0 | BNP | Branch Non Plus | 〃 PLUS=0 ならば、ジャンプ | |||||||
1 | 0 | 1 | BZ | Branch Zero | 〃 ZERO=1 ならば、ジャンプ | |||||||
1 | 1 | 0 | BC | Branch Carry | 〃 CARRY=1 ならば、ジャンプ |
1.CNT指定
CNT | 0=そのままジャンプを継続 1=CNTが非0ならば、CNTを+1して、ジャンプを実施( 0なら何もしない ) |
2.アドレッシング
上記メモリ参照命令と同様
3.ページベースの指示
C / Z | 1=カレントページを指定 , 0=ゼロページを指定 |
注.ゼロページとは、メモリ上の 0x0000 ~ 0x00FF 番地の256wordを意味します。
注.ゼロページ直接の場合、アドレス修飾(IxやId)は無効となります、注意してください。
コントロール命令系
- Shift Right
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
1 | 1 | 1 | 1 | OP Code | R0 | 1/ 0 |
Arth | Carry | R1 | 1/ 0 |
Arth | Carry |
OP Code | ニーモニック | 命 令 | 動 作 | |||||||||
0 | 1 | 0 | 1 | SR | Shift Right | 指定したレジスタ(上記R0、R1に1が立ったもの)を、右に1bitシフト | ||||||
その際、MSBより何をいれるのかを1/0、Arth、Carryで指定可。 | ||||||||||||
Arthとは、MSBを引き継ぎながらシフトすること、算術的シフト。 | ||||||||||||
R0とR1同時指定の場合は連結され、R0のLSBもR1のMSBに入っていく。 |
- Shift Left
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
1 | 1 | 1 | 1 | OP Code | R0 | 1/ 0 |
MSB | - | R1 | 1/ 0 |
Sin | Carry |
OP Code | ニーモニック | 命 令 | 動 作 | |||||||||
0 | 1 | 1 | 0 | SL | Shift Left | 指定したレジスタ(上記R0、R1に1が立ったもの)を、左に1bitシフト | ||||||
その際、LSBより何をいれるのかを1/0、MSB、Sin、Carryで指定可。 | ||||||||||||
MSB指定あればローテートすることになる。 | ||||||||||||
Sinとは、シリアル入力からのデータを取り込む。 | ||||||||||||
R0とR1同時指定の場合は連結され、R1のMSBもR0のLSBに入っていく。 |
- ループ専用カウンタ
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
1 | 1 | 1 | 1 | OP Code | Count Data |
OP Code | ニーモニック | 命 令 | 動 作 | |||||||||
0 | 1 | 1 | 1 | CNT | Count Set | CNTにCount Dataをセットする |
- Set Timer
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
1 | 1 | 1 | 1 | OP Code | - | - | - | - | Timer Code |
OP Code | ニーモニック | 命 令 | 動 作 | |||||||||
1 | 0 | 1 | 0 | STM | Set Timer Code | インターバルタイマーにタイマーコードをセットする | ||||||
タイマーカウンタとタイマーフラグはこの時リセットされる。 | ||||||||||||
割込許可フラグがセットされておれば指定時間後に割込がかかる | ||||||||||||
TimerCode | 時間 | TimerCode | 時間 | |||||||||
0000 | 100μS | 0111 | 500mS | |||||||||
0001 | 500μS | 1000 | 1S | |||||||||
0010 | 1mS | 1001 | 5S | |||||||||
0011 | 5mS | 1010 | 10S | |||||||||
0100 | 10mS | 1011 | 50S | |||||||||
0101 | 50mS | 1100 | 100S | |||||||||
0110 | 100mS | 1111 | マスク | |||||||||
- その他のコントロール
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
1 | 1 | 1 | 1 | OP Code | - | - | - | - | - | - | - | - |
OP Code | ニーモニック | 命 令 | 動 作 | |||||||||
0 | 0 | 0 | 0 | NOP | No Operation | なにもしない | ||||||
0 | 0 | 0 | 1 | HALT | Halt | CPU停止 | ||||||
0 | 0 | 1 | 1 | MOV | Move R1,R0 | R0の内容を、R1にコピー | ||||||
0 | 1 | 0 | 0 | LSW | Load Switch | SWRの値をR0に読み込む | ||||||
1 | 0 | 0 | 0 | SE | Set Enable | 割込許可フラグをセットする | ||||||
1 | 0 | 0 | 1 | RE | Reset Enable | 割込許可フラグをリセットする | ||||||
1 | 1 | 0 | 0 | FLG | Flag Out | R0のMSBを、シリアル出力FLGに出力する |
以上、 26命令
このCPUで工夫した点
掛け算、割り算をスピードアップ
TANACOM-1には、ALU ( Arithmetic and Logic Unit、算術論理演算装置 ) として、SN74181を採用しており、16bitの加算、減算は、ハードウェア的に出来ます。
しかし、掛け算、割り算は、その機構を持たないので、ソフトウェアで行うしかありません。
私は、とにかく、この「掛け算、割り算」のスピードを上げたかったのです。
それは、より短いステップ数でその演算が出来れば良いのです。
そこで私は、命令に下記のような工夫をしました。
掛け算において
TANACOM-1において、メモリ上のa番地の値とb番地の値を掛け、答えをc番地に入れるプログラムは、下記のように書きます。
16bit×16bitの積は32bitになります。但し、あくまでも使用する数値は16bitにしたいので、答えの上位16bitは捨て下位16bitのみ採用します。
L 0,0 ; R0をゼロにする L 1,b ; R1にb番地の値を CNT 0x0F1 ; 16回ループする値をカウンタにセット Loop IA 0,a ; R1のLSBが1であれば、R0にa番地を加算 SR R0.Carry.R1 ; Carry-R0-R1右に1bitシフト B_cnt Loop ; カウンタが回りきるまでジャンプ ST 1,c ; 積をc番地に格納 HALT ; 停止
掛け算では、おなじ計算を16回ループさせるのですが、ここで登場するのが「ループ専用カウンタ」です。
TANACOM-1には、8bit長のカウンタ ( SN74161 x 2 ) 1個を持ち、ここに1~255の数値をセット出来ます。
これが1つ目の工夫、CNT命令です。
これとセットで使用するのが、2つ目の工夫、B_cnt命令です。
これは「カウンタを調べ、0ならば、何もしない。非0ならば、カウンタを+1してから、ジャンプを実行する」という働きをします。
カウンタは、8bitしかないので、255の時点でカウントアップされると、桁溢れしてオール0に戻ってくれます。
これらを使う事で、本来であれば、R0やR1にある計算途中の数値をどこか待避させ、ループ役の変数を更新し16回まわったかを判断させるステップが必要なのですが、R0やR1にさわる事なく、それらが出来るのが、「CNTとB_cnt命令」のセットなのです。
3つ目の工夫は、IA( if-ADD )命令です。
掛け算の中で、「桁送りをしながら、R1のLSB( 最下位bit )が 1 だったら、R0にb番地の値を加える」という作業があるのですが、それを1ステップでやるのが、この「IA命令」なのです。文字通り「イフ-アッド、もし~だったら足せ」です。
しかし、この「IA命令」を増設するために、「OR命令」オア(論理和)命令を削ってしまいました。
前記命令セットの表を見ていただけると解りますが、TANACOM-1には何と「OR命令」が存在しないのです(ORは、ANDと加算の組み合わせで、代替出来るので、良し、としました)。
4つ目の工夫は、シフト命令にあります。
上記の「SR R0.Carry.R1」とは、手前の加算演算での「桁上がり」を示すCarryフラグをR0のMSB( 最上位bit )に入れ、R0のLSB( 最下位bit )をR1のMSBに入れながら、R0R1を同時に右へ1bitシフトさせる事を1ステップでやるのがこの命令です。
これら4つの工夫により、掛け算プログラムのステップ数は大幅に減り、スピードは約2倍になり、およそ52μSで掛け算が出来るようになりました。
割り算において
TANACOM-1において、メモリ上のa番地の値をb番地の値で割り、商をc番地に、余りをd番地に入れるプログラムは、下記のように書きます。
割り算は、32bit÷16bitとし、答えは商16bitおよび余り16bitになります。
被除数自身は16bitですが、上位16bitがオール0である32bitとして計算開始します。
L 1,a ; R1にa番地の値を L 0,0 ; R0をゼロにする CNT 0x0F0 ; 17回ループする値をカウンタにセット Loop C 0,b ; R0とb番地を比較 BZ J1 ; ゼロであればJ1にジャンプ BNP J2 ; 正でなければJ2にジャンプ J1 S 0,b ; R0からb番地を引く J2 SL R0.R1.Carry ; R0-R1-Carry左に1bitシフト B_cnt Loop ; カウンタが回りきるまでジャンプ SR R0 ; 行き過ぎを戻し ST 0,d ; 余りR0をd番地に格納 ST 1,c ; 商R1をc番地に格納 HALT ; 停止
5つ目の工夫は、シフト命令にあります。
上記の「SL R0.R1.Carry」とは、手前の減算演算での「桁下がり、ボロー」を示すCarryフラグをR1のLSB( 最下位bit )に入れ、R1のMSB( 最上位bit )を、R0のLSB( 最下位bit )に入れながら、R0R1を同時に左へ1bitシフトさせる事を1ステップでやるのがこの命令です。
この工夫と、先の「CNTとB_cnt命令」の組み合わせにより、割り算プログラムのステップ数も大幅に減り、スピードは約1.5倍になり、およそ110μSで割り算が出来るようになりました。
簡易UARTの仕組みを実装
TANACOM-1本体が完成して間もない頃、TANACOM-1には何の入出力装置も存在していませんでした(キャラクタディスプレーもまだ先です)。
構想としては、IOボックスを作り、それにコントローラー回路を組み込み、入出力(キーボード、シリアルイン、シリアルアウト、Beepスピーカー等)を行う計画でした。
しかし、実現にはまだまだ時間が掛かる状況でした。
このままでは、書き込んだプログラムを保存する手段すら存在しません。
電源を入れるたびに、毎回すべてのプログラムは、SWR( フロントパネルにあるスイッチ )でパチパチと入力しなければなりません。
当時、プログラムの保存には、オーディオカセットテープレコーダーを使うのが主流でした。
「あ~TANACOM-1にも、まずはカセットインターフェースが必要だ。でも、IOボックスはいつになったら完成するやら」と嘆いているとふと気づきました。
汎用レジスタのR0、R1には、両方ともシフトレジスタであるSN74198を使っています。
この機能を利用しない手はありません。
さっそく、回路変更を考え、命令自身も改造しました。
- シリアルアウト機能
R0のMSB( 最上位bit )の状態をSN7474を使って記憶させ、これを「FLG」と
命名しました( UARTでいうTx )。
そして、FLG専用命令を増設しました。
FLGの出力は、簡単なレベル変換をしてアナログ化し、イヤホンジャック( OUT )
から出します。
- シリアルイン機能
イヤホンジャック( IN )を設置し、オーディオ信号を入れます。
それをトランジスタを使った簡単な復調回路を通し、0か1の信号に替えR1のLSB
( 最下位bit )に接続し、左シフト命令で取り込ませるのです( UARTでいうRx )。
この仕組みを作った事により、TANACOM-1にシリアル入力、シリアル出力を持つ事が出来て、ほどなくカセットインターフェースが完成しました。
そこから先、カセットインターフェースを通じて、作ったプログラムのLOADとSAVEが出来るようになり、飛躍的に進歩したのです。