あんたCPUなんか創ってどうするのよ?! Vol.1 第2章 第2節

~TD4とかいうCPUがあまりにも残念だったので拡張してみた~

2016/8/14
コミックマーケット C90 3日目 西g16b

第2章 第2節 TD4の命令を改めて掘り下げる

では肝心のTD4について、もっと深く見ていきましょう。

以下にTD4の命令一覧を示します。

表1 TD4の命令コード表
命令 オペコード 説明
ADD A, Im 0000 Aレジスタの内容に命令中で指定された定数(Im)を加算
MOV A, B 0001 AレジスタにBレジスタの内容を転送
IN A 0010 入力ポートからデータを取り込み、Aレジスタに格納
MOV A, Im 0011 Aレジスタに命令中で指定された定数(Im)を格納
MOV B, A 0100 BレジスタにAレジスタの内容を転送
ADD B, Im 0101 Bレジスタの内容に命令中で指定された定数(Im)を加算
IN B 0110 入力ポートからデータを取り込み、Bレジスタに格納
MOV B, Im 0111 Bレジスタに命令中で指定された定数(Im)を格納
OUT B 1001 Bレジスタの内容を出力ポートに出力
OUT Im 1011 命令中で指定された定数を出力ポートに出力
JNC Im 1110 キャリー(C)がゼロならば指定アドレス(Im)にジャンプ
JMP Im 1111 指定アドレス(Im)にジャンプ

「CPUの創りかた」の第6 章139ページに明記されていますが、TD4ではイミディエイトデータ、つまり命令中で指定されている定数との加算しかできません。筆者としては非常に不満です。

ともあれ、加算しか出来ないのは入手可能な演算用ICが加算しかできない74HC283というチップだからです。詳しくは第8章に書いてあります。従って命令一覧にも加算命令しかありません。

しかし問題はありません。

もしあなたが2の補数をご存じなら、TD4に減算機能を追加するのは造作もないからです。苦労するのは命令デコーダーの拡張くらいでしょう。極限までICチップを減らしたTD4に新しい命令を追加するのは非常に困難ですが、汚い方法で良ければ手軽な代替手段が無い訳ではありません。例えば出力ポートのどれかのビットを加算/減算の切り替えに使う、という手があります。これならば命令デコーダーの拡張をせずに減算命令を追加できます。

実際にやったらプログラマーたちからボロクソに叩かれるとは思いますが。

図1 2の補数を用いた減算回路
図1 2の補数を用いた減算回路

もっともTD4は定数(イミディエイト)との加算しかできませんから、プログラムを記述する際に最初から定数を2の補数にしておけば余分な回路なしに減算できてしまいます。これは一部のRISC CPUで使われている手法ですから、ご存知の方も多いでしょう。

さて命令一覧を眺めていると、いくつか気づくことがあると思います。

  • オペコード0~3はAレジスタに対する操作
  • オペコード4~7はBレジスタに対する操作
  • オペコード9とBは出力命令
  • オペコードEとFはジャンプ命令

「CPUの創りかた」のなかでは真理値表を使って説明しているのでピンと来なかった人も多いと思いますが、実はTD4の命令は上記の4つに分類できるのです。これはオペコードの上位2ビットで判別されます。

具体的には以下のようになります。

表2 TD4の命令コード (bit7とbit6の意味)
bit7 bit6 説明
0 0 加算を行い、結果をAレジスタに格納
0 1 加算を行い、結果をBレジスタに格納
1 0 加算を行い、結果をOUTレジスタに格納
1 1 加算を行い、結果をプログラムカウンタに格納

命令デコーダーの回路をよく見ると、オペコードのbit7とbit6をデコードしてAレジスタ、Bレジスタ、OUTレジスタ、プログラムカウンタのLOAD端子を制御していることに気づくでしょう。

第8章204ページにあるようにTD4は常に加算を行っています。加算をしたくないときは、プログラム中でゼロとの加算を行うように指定します。命令デコーダーの簡素化のためには止むを得ない処置ですが、一般的なCPUとしては決して良い方法ではありません。将来の拡張では「加算を行わない」回路を追加する予定です。


さらに命令一覧をよく見ると、以下のことにも気づくでしょう。

  • オペコード0,4,8はAレジスタの内容を使って加算を行なう
  • オペコード1,5,9はBレジスタの内容を使って加算を行なう
  • オペコード2,6は入力ポートの内容を使って加算を行なう
  • オペコード3,7,B,Fは加算をせず、命令中で指定された定数(Im)を使う

オペコード0~7と8~Fでは動作が若干変わりますが、基本的にはオペコードの下位2ビットは以下のように解釈されます。

表2 TD4の命令コード (bit5とbit4の意味)
bit5bit4説明
00Aレジスタの内容を使って加算を行なう
01Bレジスタの内容を使って加算を行なう
10入力ポートの内容を使って加算を行なう
11命令中で指定された定数(Im)をそのまま使う

オペコード8~Fの場合は条件ジャンプ命令のためにbit4が無視され、以下のように解釈されます。(条件ジャンプ命令とジャンプ命令については次章でさらに深く掘り下げます。)

表3 TD4の命令コード (ジャンプ命令の場合)
bit5bit4説明
0Bレジスタの内容を使って加算を行なう
1命令中で指定された定数(Im)をそのまま使う

このようにオペコードの上位2ビットと下位2ビットの組み合わせで命令が出来上がります。

例えばオペコードがゼロ、つまり2進数で0000の場合はAレジスタの内容を使って加算を行ない、結果をAレジスタに格納する命令が出来上がります。

ADD A, Im

同様にオペコードが5、つまり2進数で0101の場合はBレジスタの内容を使って加算を行い、結果をBレジスタに格納する命令が出来上がります。

ADD B, Im

さらに、ジャンプ命令は以下のように実現します。

第8章217ページにあるように、ジャンプ命令はプログラムカウンタへの転送命令で実現します。つまり

JMP Im

という命令は

MOV PC, Im

と解釈します。オペコードのbit7とbit6は11にすることでプログラムカウンタへの転送命令とし、bit5を1にすることで命令中に指定してある定数(Im)だけを使わせます。するとIm がそのままプログラムカウンタに書き込まれ、ジャンプ命令が実行される訳です。

なおbit4はジャンプ命令(JMP)と条件ジャンプ命令(JNC)の判別に使います。