MCS-4

Intel社の4004は世界初のマイクロプロセッサとして有名ですね。Intel社によれば1971年11月15日に発表で10 umのp-MOSプロセスで作成され約2300個のトランジスタが集積されているとのことです。これが実物の写真です。ピンとピンの間隔が2.54 mmなので、全体のサイズはそこから想像してください。

4004
パッケージは16ピンDIPで、左がセラミックパッケージで1976年製のもの、右がプラスチックパッケージで1980年製。意外なほど長く供給されていたようですね。

実は4004のデータシートには最高クロック周波数が直接規定されているわけではありません。そのかわりクロック周期が最大で2 us、最短で1.35 usと規定されています。この逆数がクロック周波数となりますから、最大741 kHzのクロック周波数が求められます。p-MOSロジックはクロック周波数の動作範囲が狭く、最小クロックは500 kHzで、これ以下の周波数では正常動作を期待できません。公称最短命令実行時間は10.8 usとされています。Intelの推奨回路では5.185 MHzの水晶発振回路の出力を7分周しながら非対称2相クロックを作成し、1命令の実行には8クロックか16クロックが必要となるため、10.8 usという最短命令実行時間が求められます。
ピン配置は、このようになっています。

D0   1      16 CM-RAM0
D1   2      15 CM-RAM1
D2   3      14 CM-RAM2
D3   4      13 CM-RAM3
Vss  5      12 Vdd
CLK1 6      11 CM-ROM
CLK2 7      10 TEST
SYNC 8      9  RESET

CM-RAMnはRAMチップの動作指示信号で、特別な回路なしで最大16個まで4002を接続できることになっています。CM-ROMはプログラムメモリの動作指示信号で、TESTは1 bitの入力ポートとして使える端子、CLK1とCLK2は非対称2相クロックで内部動作の基準や外部周辺ICとの同期に使います。SYNCは外部のICとの同期信号。D0 - D3がバスラインになります。
データバスは4 bitだから4本で、アドレスバスは・・・見当たらないぞなどと思っちゃいますが、汎用の4本のバスがあるだけです。D0 - D3のバスには、メモリアドレスが3回に分けて出力された後、8 bitの命令が2回に分けて読み込まれ、それから転送されるべきデータの入出力が行われます。どんなにがんばっても、1命令の実行には最低8クロックかかるため、異様に遅い命令実行時間になっています。もちろん、製造は容易だけれども速度の遅いp-MOSプロセスでクロック周波数が低いこともありますけど。
p-MOSなので、電源電圧は-15 Vです。おまけにロジックレベルが、Hが-1 V前後、Lが-4 V前後なので、TTLに接続するため、本来のグラウンド端子であるVssを5 Vにして、電源端子のVddには-10 Vを加えるという小技を使う必要がありました(当時としては常識的な小技です)。すると、ロジックレベルがTTLとコンパチブルになるわけです。
バスタイミングとデータ転送については、さらに別の秘密があります。実は、メモリにデータを書き込めとか、入力ポートからデータを読み込めといった意味の信号が、このLSIにはありません。メモリやI/Oポートの側で常に命令を監視し、CPUがそういった種類の命令を実行するタイミングに合わせて、適切な処理をします。しかも、どのアドレスとデータをやり取りするかは、別の命令によって指示されていて、実際にデータ転送が行われるまで、メモリやI/Oポートの側に保持しておかなくてはなりません。たとえば、出力ポートへアキュムレータの内容を出力する命令が読み込まれると、CPUの側では命令を読み込んだ後の命令実行サイクルでバスにアキュムレータの内容を出力するだけです。I/Oポートの側では、CPUと同じタイミングで命令を読み取り、命令を解読して、出力命令であると判明したら(そして事前に指示されているI/Oポートアドレスに自分が一致していると判断したとき)、CPUがバスにアキュムレータの内容を出力しているタイミングで、データを取り込み、適切なポートに書き込みます。この間、ほかのI/Oポートやメモリは(自分でそれぞれ命令を解読して)何もしないでいます。結局、CPUの命令解読機構やポインタレジスタの一部が、メモリやI/Oポートの側にも分散して存在する仕組みになっています。

その特徴から4004は単独で使用されることはなく、必ず専用の周辺LSIと組み合わせて使われていました。(いや、べつに専用LSIと同じような回路を汎用ロジックで自作したって使えるのですが、また後述の汎用メモリコントローラが使えるようになるまでは普通のメモリICを接続して使うために実際にそうする必要があったのですが、かなりかったるいわけです。)
そこで、代表的な周辺LSIは「MCS-4 周辺LSI」として別項目で紹介しています。

なぜ、4 bitのバスをこれほど時分割して使用したのか、28ピンパッケージにしてアドレス信号を別に出力した方が、使いやすく性能も数倍になるのに、というような疑問を感じてしまいます。この答は[1]にありました。当時は標準的なICパッケージは16ピンまでで、それ以上のピン数のパッケージは特殊であったので、どれだけ売れるかわからないICを特殊なパッケージに入れるだけの投資をしたくなかったようです。なにしろ、当時は資金繰りの厳しい小さな企業でしたから。

4004の命令体系などをこれから説明しますが、その前にプログラマから見たレジスタモデルを図にしてみました。こんなふうになっています。
4004 registers

4 bitのアキュムレータに、スクラッチパッドレジスタとしても使える4 bitのレジスタが16個。この16個のレジスタは、図で隣同士になっている2個をペアにして8 bitのインデックスレジスタとしても使えます。サブルーチン呼び出しは3レベルまでネストできます。12 bit幅のサブルーチン用のスタックレジスタが4個、CPUに内蔵されているためです(そのうち1個は最初からプログラムカウンタとして使用されるため3レベル)。キャリーフラグがあって、加減算やローテート命令で使用される他、条件分岐命令の条件にもなります。他にアキュムレータが0か、およびTEST端子の入力レベルの、3要因で条件判定が行えるようになっています。通常のレジスタとは別にコマンドラインレジスタというものが内蔵されていて、ここに設定した値によって外部に接続したROMチップやRAMチップを選択して、入出力対象を指定できるようになっています。
割り込みやDMAの機能はなく、4 bitの加減算やローテートなど46種類の命令が使えます。ただし、10進補正命令はありますが、ANDやORのような2項ビット演算がなかったり、レジスタペアを8 bit分まとめてインクリメントする命令などもなく、16桁までの電卓的な計算以上の応用プログラムを書くとすれば、相当苦労するはずです(乗除算を考えると、これでも充分に困難か)。どちらにしろ、プログラムメモリが4 K Byteしかないという点もつらいでしょう。
命令解説ですが、例によって細かい知識を私が持ち合わせていないので、不確かなものになっています。4004の命令はマシン命令、入出力およびRAM命令、アキュムレータ命令の3グループに別れています。入出力およびRAM命令の機械語は16進数でExという形になっていてxに0からFまでの16種類が定義されています。アキュムレータ命令はFxという形式でxには0からDまでが定義されています。マシン命令は上位4 bitだけが命令の種類を表すのに使われ、下位4 bitは原則としてインデックスレジスタの番号を指定するのに使われています。
マシン命令には以下の16種類があります。Codeは機械語を大文字を使用した16進数で表していますが、一部特別な意味を持つビット列にはPやaなどの特別な記号文字で示しています。詳細は後で。

MNE. Code   解説
NOP  00    No Operation
JCN  1c aa Jump if Condition is true
FIM  2P dd Fetch Immediate
SRC  2p    Send Register Control
FIN  3P    Fetch Indirect from ROM
JIN  3p    Jump Indirect
JUM  4a aa Jump unconditional
JMS  5a aa Jump to Subroutine
INC  6R    Increment contents of register
ISZ  7R aa Increment contents of register. Jump if result is not zero.
ADD  8R    Add contents of register to accumulator with carry
SUB  9R    Subtract contents of register to accumulator with borrow
LD   AR    Load contents of register to accumulator
XCH  BR    Exchange contents of register and accumulator
BBL  Cd    Branch Back and Load data to accumulator
LDM  Dd    Load data to accumulator

NOP命令以外は少々細かく説明します。
JCN命令は条件分岐命令で、機械語のcの部分の4 bitで条件を、aaの部分でジャンプ先のアドレスを指定します。アドレスは8 bit分しかないため、上位4 bitがJCN命令のアドレスと同一の範囲にしか分岐できません。つまり256 Byteを1ページとして、同一ページ内でしか分岐できないわけです。条件指定については、4 bit分のcの各ビットをMSBからLSBにかけてc3, c2, c1, c0とします。c0が1ならTEST信号が0のときにジャンプという意味になります。c1が1ならキャリーが1ならジャンプという意味です。c2が1ならアキュムレータが0ならジャンプです。c3は特別なビットで、0ならば今までの条件どおりの意味になりますが、1なら今までの条件の否定が真になったときにジャンプします。さて、c0, c1, c2のうちの複数のビットが1になっている場合はどうなるか、残念ながら不明です。手元に実動する4004の開発システムでもあれば確かめられるんですけど。
FIM命令は8 bitの定数ddをPで指定されたインデックスレジスタ対にロードする命令です。インデックスレジスタ対は0から7までの値で表現可能ですが、それを1 bit左にシフトして最下位のビットを0にしたものがPです。つまりPは0, 2, 4, 6, 8, A, C, Eの値を取り、それぞれ順にインデックスレジスタ対の0から7に対応します。
SRC命令はpで指定されたインデックスレジスタ対の内容をRAMチップとROMチップのレジスタコントロールへ書き込む命令です。pはFIM命令のPと同じ意味ですが、最下位ビットが1になっている点だけが異なります。要するにFIM命令とSRC命令は最下位ビットによって判別されるわけです。これ以外の命令でもPとpの意味は同じように解釈されます。SRC命令で送出されたレジスタコントロールデータは、RAMチップのアドレス情報として使用されます。このアドレス情報の上位2 bitは同一CM-RAMn信号に接続されたRAMバンクに含まれる4個のRAMチップを選択するのに使われます。次の2 bitは1個のRAMチップに内蔵される4レジスタのうちのひとつを選択するのに使われます。残りの下位4 bitは選択されたレジスタに含まれるメインメモリキャラクタ16個のうちのひとつを選択するのに使われます。また、SRC命令で送出されたデータの上位4 bitはROMチップにも読み込まれ、それ以降のROMポートへのI/O命令であるRDR, WRR命令で使用されるROMポートの属するROMチップ番号を選択するのに使用されます。4004にはROMチップの4001を最大16個まで接続できますので、上位4 bitだけでROMチップを指定できます。
FIN命令はROMの中に納められているテーブルを参照するための命令です。インデックスレジスタ対0の内容を選択されているROMに送出して、そこから読み込んだ8 bitデータをPで選択されたインデックスレジスタ対へと読み込みます。8 bitデータの変換が(限界はあるにしろ)可能なので、文字コード変換やLEDの表示パターンへの変換などに有用でしょう。
JIN命令はpで指定されたレジスタ対の内容をアドレスとして解釈して、そこへと分岐する命令です。上位4 bitは同一ページになります。計算型GOTOというか、表を用いた分岐先管理なんかが可能になります。
JUMは無条件ジャンプで、2 Byte命令になっているため、aaaで指定された任意のアドレスへと分岐できます。他にページ境界をまたぐジャンプ命令はないので、任意の場所へと分岐するには必ずこの命令を使用しなくてはなりません。
JMS命令はサブルーチンへの分岐で、スタック操作を行う以外はJUM命令と同一です。サブルーチン呼び出しは全アドレス空間の中の任意のアドレスを指定することが可能です。ただし、サブルーチンのネストは3レベルだけということを忘れてはいけません。サブルーチンからの復帰には後述のBBL命令を使用します。
INC命令はインデックスレジスタの内容をインクリメントする命令です。Rに0からFまでの数値を入れて、16個のレジスタのうちの任意のひとつをインクリメントできるようになっています。
ISZ命令はループ用の命令です。Rで指定されたレジスタをインクリメントして、その結果が0でなければジャンプします。ジャンプアドレスはaaで指定され、同一ページ内への分岐だけが許されます。たとえば、R3にCという値を入れておけば、R3を指定したISZ命令のところに来るたびに3回まで分岐して、4回目で分岐を行わないようなことが可能になります。
ADD命令とSUB命令は加減算で、アキュムレータとレジスタの間で行います。キャリーも同時に加減算されますので、必要に応じてCLC命令やSTC命令でキャリーを設定しておきます。
LD命令はRで指定されたレジスタの内容をアキュムレータにロードします。
XCH命令はRで指定されたレジスタの内容とアキュムレータの内容を交換する命令です。さて、アキュムレータの内容をレジスタに格納する命令が4004には存在しません。そのため、このXCH命令で代用することになります。レジスタに値を格納した後もアキュムレータに同じ値が入っている必要があるなら、XCH命令の次にLD命令で再度アキュムレータの内容をロードします。
BBL命令はサブルーチンからのリターン命令ですが、4 bitの定数をオペランドに持ち、リターンすると同時にアキュムレータに定数をロードします。ロードする定数はサブルーチンが正常終了したか、エラーがあったか、などを示す値として使用できます。ただ一般的にサブルーチン内で求めた値を返す場合には、アキュムレータの内容がBBL命令で必ず書き変わるため、レジスタに値を入れるような仕様にする必要があります。
LDM命令はアキュムレータへの定数のロードです。
以上でマシン命令は終わりました。

入出力およびRAM命令は次の16種類あります。

MNE. Code   解説
WRM  E0    Write the contents of Acc. into the RAM main memory character
WMP  E1    Write the contents of Acc. into the RAM output port
WRR  E2    Write the contents of Acc. into the ROM output port
WPM  E3    Write the contents of Acc. into the half byte of read/write program memory
WR0  E4    Write the contents of Acc. into RAM status character 0
WR1  E5    Write the contents of Acc. into RAM status character 1
WR2  E6    Write the contents of Acc. into RAM status character 2
WR3  E7    Write the contents of Acc. into RAM status character 3
SBM  E8    Subtract the RAM main memory character from Acc. with borrow
RDM  E9    Read the RAM main memory character into the Acc.
RDR  EA    Read the contents of ROM input port into the Acc.
ADM  EB    Add the RAM main memory character from Acc. with carry
RD0  EC    Read the RAM status character 0 into Acc.
RD1  ED    Read the RAM status character 1 into Acc.
RD2  EE    Read the RAM status character 2 into Acc.
RD3  EF    Read the RAM status character 3 into Acc.

E0からE7までが出力方向、E8からEFまでが入力方向となっています。これらの命令の使用に先立って、DCL命令とSRC命令で、どのチップのどのアドレスが選択されるのか指定しておかなくてはなりません。特にRAMチップ(4002)では80 nibble (4 bit character)分のアドレスが20 characterごとの4レジスタ群に分割されて管理されていて、各レジスタ群に16個のメインメモリキャラクタと4個のステータスキャラクタが属しています。メインメモリキャラクタに書き込むにはSRC命令でチップ番号とRAMレジスタ群の中のひとつを指定すると同時に、レジスタ群の中のアドレスも選択します。RAMメインメモリキャラクタに関係する命令にはWRM, SBM, RDM, ADMの4命令があります。RAMステータスキャラクタに関してはチップ番号とRAMレジスタ群のひとつをSRC命令で指定するだけで、ステータスキャラクタのどれにアクセスするかは機械語の一部(ニーモニックの一部)で指定しています。それがWR0 - WR3とRD0 - RD3の8命令です。入出力ポートも同様の手順でチップ番号を選択しておいて、データの転送だけを担当するのがWMP, WRR, RDR命令です。
WPM命令だけは特殊で、この命令を使用するにはプログラムメモリの一部に4008/4009か4289で管理されたRWMを使用している必要があります。プログラムメモリに書き込むための命令でアドレスは基本的にSRC命令で指定しますが、プログラムメモリは8 bit単位であるのに4004の扱うデータは4 bit単位であるため、1 Byteのうちの上半分と下半分を区別して書き込まなくてはなりません。これらのスタンダードメモリインターフェースには、内部にFirst/LastフリップフロップがあってWPM命令が実行するたびに反転するようになっています。このフリップフロップの状態によって半バイトのうちの上位と下位が決定されます。First/Lastフリップフロップはリセット時にクリアされますが、それ以降はプログラムによって明示的に設定する手段がありません。したがって、WPM命令の実行が奇数回目か偶数回目か、しっかりと意識してプログラムを組まないとトラブルの元です。

アキュムレータ関係の命令は14種類あります。

MNE. Code   解説
CLB  F0    Clear Acc. and Carry
CLC  F1    Clear Carry
IAC  F2    Increment Acc.
CMC  F3    Complement Carry
CMA  F4    Complement Acc.
ROL  F5    Rotate Left (Acc. and Carry)
ROR  F6    Rotate Right (Acc. and Carry)
TCC  F7    Transmit Carry to Acc. and Clear Carry
DAC  F8    Decrement Acc.
TCS  F9    Transfer Carry Subtract and Clear Carry
STC  FA    Set Carry
DAA  FB    Decimal Adjust Acc.
KBP  FC    Keyboard Process
DCL  FD    Designate Command Line

以上です。
CLB, CLC, IAC, CMC, CMA, ROL, ROR, DAC, STCの各命令についてはおおよそのことはわかるでしょう。TCC命令はキャリーフラグが1ならアキュムレータに1を、キャリーフラグが0ならアキュムレータに0を、ロードして、キャリーをクリアする命令です。4004のADD命令などは常にキャリーを加算するタイプですが、それとは別に桁上がりの数値を必要とする場合に使用できます。TCSはTCC命令と似ていますが、BCDコードの減算に使用する桁借り分の数値のロードのため、ロードされる定数が異なります。キャリーが1なら9を、キャリーが0なら10を、アキュムレータにロードしてから、キャリーをクリアします。
DAAは10進補正で、アキュムレータの内容がA-Fなら6を加算します。KBPはキーボードプロセスとなっていますが、アキュムレータの4 bitの内の1 bitだけが1になっている値が入っているとして、それを2進数のコードに変換します。キーボードの状態を入力ポートからスキャンした時、それを表を使わずにバイナリコードに変換するための命令なのでしょう。いかにも電卓用の命令です。
DCLは特定のRAMチップを選択して動作タイミングを指示するCM-RAMnの出力選択を行う命令です。RAMチップのバンク選択命令と考えることができます。この命令で使用されるのはアキュムレータの下位3 bitだけです。アキュムレータの下位3 bitが0ならCM-RAM0のバンクが、1ならCM-RAM1のバンクが、2ならCM-RAM2、3ならCM-RAM3が、この命令実行後から有効となり、該当するCM-RAMn信号に接続されたRAMチップが選択されます。リセット直後はCM-RAM0が指定された状態となっています。
したがってRAMを参照するには、アキュムレータにRAMバンクアドレスをロードしてからDCL命令を実行し特定のRAMバンクを指定して、RAMアドレスを入れておいたインデックスレジスタ対をオペランドに持つSRC命令を実行して該当バンクに含まれる特定のレジスタとそのレジスタ内のメインメモリキャラクタアドレスを指定してから、RAM命令を実行することになります。このメモリ構造は明らかに電卓応用に特化したもので、メインメモリキャラクタが電卓の置数データやデータメモリなどに対応する演算対象データの格納に使われ、ステータスキャラクタが演算対象データの属性値に使われることを想定していたと思われます。8008以降のフラットなメモリ構造に慣れた頭からすると面倒に感じますが、電卓用の固定長のBCDデータが格納されるレジスタが複数あるという発想に立てば、レジスタ番号の指定とBCDデータの特定の桁の指定がDCL命令とSRC命令で行われるという考え方に馴染めるかもしれません。ソースレジスタとディスティネーションレジスタを指示するインデックスレジスタを2対用意して、それらをインクリメントしながらBCDデータを下位桁から加減算するとかのデータ処理が中心となれば、比較的自然なメモリ構成だったのでしょう。なお大規模な応用でない限りRAMチップが2, 3個でも充分でしょうから、DCL命令を使用しないことも多かったのではないかと思われます。

ところで、インテルジャパンのマイクロプロセッサのテクノロジの情報も4004や8008についてはクロック周波数が誤っていますね。どうもIntel本社の資料からして間違っているようです。とはいえ、インテルミュージアムのページをたどるとダイの写真も見られますから、インテルジャパンのWebを眺めるのも良いかもしれません。

[1]William Aspray, "The Intel 4004 Microprocessor: What Constituted Invention?", IEEE Annals of the History of Computing, Vol. 19, No. 3, 1997, pp. 4-15

最後に、4004と主要な周辺LSIが手元にあるので、組み上げて動作させてみたいと思っているのですが、ハードウェアの資料が不足しています。また、ソフトウェア面でも一部命令の機能で理解できない点などもあり、資料をお持ちの方がいらっしゃいましたら、閲覧させてもらえないでしょうか。お願いします。

Return to IC Collection.