MCS-80

さて、このあたりからはご存じの方が多いかと思います。マイクロプロセッサが急速に普及したのは、この8080Aが契機だといっても過言ではないでしょう。これ以前の、たとえば8008ではコンピュータとして動作させるのに60個以上のICから構成される回路を作成しなくてはなりませんでした。ところが8080Aでは20個程度のICから構成される回路でコンピュータの形態になります。4040と違い、8 bitの文字データを簡単に操作できますし、プログラムとデータの区別なく同じメモリに格納するようになっています。また、それまでのマイクロプロセッサ製品と異なり、プロセッサを普及させ応用できる技術者を増やすために、各社が評価用のキットを発売したというのも、事件でした。本来の対象であったすぐに応用製品を開発しなくてはならない技術者以外の、コンピュータに興味があったアマチュアが一斉に飛び付いたからです。日本でもNECからuPD8080Aを搭載したTK-80が発売され、これで8080Aを知った方も多いと思います。またMITS社のALTAIRキットのように、ミニコンピュータ風のコンピュータシステムを構築しやすい初のLSIでもありました。8008では8080の数倍規模の外付け回路が必要で、そのわりに性能が低く、汎用コンピュータ風のシステムが発売された例はごく少数です。同時にメモリの価格も低下してきていて、8080Aが使われだした時代には1 KByte数万円以下で購入できる程度になっていたのも、そういう用途への後押しになったと思われます。

IntelのWeb資料によれば、8080は1974年4月1日発表、6 umのn-MOSプロセスで約6000個のトランジスタを集積してあり、2 MHzクロックで動作するとのことです。ただし、4800個のトランジスタとしている文献もあり、おそらくこちらが正しそうです。内部レジスタに対する8 bit加算命令には最短4クロック必要ですから、2 usの実行時間で、8008の20 usと比べれば10倍の性能ということができるでしょう。

さて、では写真。これは1976年製。
Intel 8080A

ピン配置はこのようになっています。

A10    1      40 A11
VSS    2      39 A14
D4     3      38 A13
D5     4      37 A12
D6     5      36 A15
D7     6      35 A9
D3     7      34 A8
D2     8      33 A7
D1     9      32 A6
D0    10      31 A5
VBB   11      30 A4
RESET 12      29 A3
HOLD  13      28 VDD (+12 V)
INT   14      27 A2
CK2   15      26 A1
INTE  16      25 A0
DBIN  17      24 WAIT
WR*   18      23 READY
SYNC  19      22 CK1
VCC   20      21 HLDA    VBBは-5 V、VCCは+5 V

8008と比べると、ピン数が多いのはもちろんですが、なんかデータ線やアドレス線の順番が入れ代わっていたりして、マスクレイアウトの都合がそのまま反映されている感じがします。ソフトウェアや信号の意味が互換のNECのuPD753のピン配置と比べるとおもしろいかもしれません。
電源とアドレス・データ信号の意味は自明でしょう。RESETはリセット信号で正論理です。というより、負論理の信号はWR*のみで正論理信号ばかりが使われているのが特徴ともいえます。HOLDとHLDAがDMA用の信号、INTとINTEが割り込み用、CK1とCK2がクロック、WAITとREADYがメモリやI/Oへのアクセスタイム調整用信号、DBINとWR*が読み書きのタイミング指示、SYNCが内部ステータス情報のデータバス出力タイミング信号です。
以上の信号には、メモリとI/Oのアクセスを区別する信号などが含まれていません。それらはステータス情報を解読して決定してやらなくてはなりません。つまり8080Aの全機能を利用するためには、バスの制御タイミングを決定する少し面倒な回路を外付けする必要があるのです。ここがMC6800との大きな違いとなります。そのためにIntel社は8228システムコントローラといったICを別に提供していました。

ステータス情報の詳細について説明しましょう。ステータス情報は8080がバスを介してデータ転送を行うサイクルの途中、実際にデータ転送を行う前のタイミングで、SYNCがHの間にデータバスに出力されます。SYNCとステータス情報とは完全にタイミングが一致していないため、SYNCがHかつCK1がHのタイミングでラッチして情報を保持するのが普通です。ステータス情報は出力されるデータバスの個々のビットごとに異なった意味が割り当てられています。

D0  INTA  割り込み応答バスサイクルであることを示す。
D1  WO*   書き込みを行うバスサイクルであることを示す。
D2  STACK スタックポインタの指すメモリへのバスサイクルであることを示す。
D3  HLTA  HALT命令を実行していることを示す。
D4  OUT   I/Oポートへの出力サイクルであることを示す。
D5  M1    CPUが命令の第1バイトを読み込むバスサイクルであることを示す。
D6  INP   I/Oポートからの入力サイクルであることを示す。
D7  MEMR  メモリからの読み込みを行うバスサイクルであることを示す。

これらによって、完全に分類すれば10種類のステータスが表現されます。それぞれのステータスがどのようなステータス情報の組み合わせで表現されるか、お暇なら考えてみてください。とにかく256通りの組み合わせのある8 bitデータで、実際に出力されるのは下の10種類の組み合わせだけなのです。

  1. インストラクションフェッチ
  2. メモリリード
  3. メモリライト
  4. スタックリード
  5. スタックライト
  6. I/Oリード
  7. I/Oライト
  8. 割り込みアクノレッジ
  9. HALTアクノレッジ
  10. HALT中の割り込みアクノレッジ

その中には、スタックへの書き込みやスタックからの読み込みなんかも含まれていて、そうする必要があるなら、スタック専用にしか使われない特別なメモリを8080Aコンピュータシステムに付け加えることすらできます。また、実際のデータ転送に先立って、これから実行されるのが書き込みか読み込みかWO*で識別することができるため、データバスのバッファの方向制御を行いやすいとか、情報がCPUからたくさん出力される分、便利なこともあります。しかし普通に必要となる解読情報はメモリリード、メモリライト、I/Oリード、I/Oライト、割り込みアクノレッジの5種類だけで、しかもメモリリードやメモリライトはスタックかそれ以外かの区別がない形式のものだけです。当時普通に入手できたLS TTLでこのステータス情報の保持解読回路を作成すると、4個のICが必要になるなど、小規模な応用にはよけいなお世話というべきものでした。そのため、通常は保持解読回路の4個のICとデータバスバッファの2個のICをひとつのパッケージに入れたような8228が使われ、ステータス情報を直接利用するような応用はほとんどみられませんでした。

さて、私はIntelオリジナルの8080は持っていません。上の写真は8080Aであって、8080の出力ピンの駆動力を強化した改良品です(だからAが付いている)。オリジナルの8080では、アドレスバス、WAIT, INTE, WR, DBIN, HLDAの出力ピンの駆動能力はL-TTLファミリ1個分しかありませんでした。8080Aでは通常のTTLファミリ1回路分の駆動能力(IOL: -1.9 mA)が保証されています。ところで、次の写真のTexas Instruments社のTMS8080にはAが付いていませんが、8080相当品なのでしょうか、8080A相当品なのでしょうか。たぶん8080A相当品だと考えていますが、正確にはわからないので、どなたか教えてください。
と、思っていたのだけど、今、TK-80のコメントを書こうとTK-80に電源を入れて使える状態になっていることをいいことに、uPD8080AFと差し替えてみたら、数10秒で暴走する。タイミングか電圧レベルのマージンが足りないような症状で、ファンアウトが足りないと仮定するとしっくりくるから、やっぱり8080相当品の可能性が高いかもしれない。あ、TK-80は上記の信号の中でTTL 1回路分の負荷をかけてしまっているところや、LS TTL 2回路分近い負荷がある信号が存在するために、8080では正常動作しない可能性があるので。なお、これ以外のここに掲載した8080A相当品はみんなTK-80で安定して動作しました。

TMS8080, uPD8080A, uPD8080AF
Texas Instruments社のTMS8080(上)、NEC社のuPD8080A(中央)とuPD8080AF(下)。

と、読者の方から指摘がありました。特性の抜粋を知らせてくださいましたが、TMS8080は8080と8080Aの中間的な特性を持っていたようです。バスの電気的特性は8080Aに近いものでしたが、クロック信号に関するタイミングや電気的特性は8080に近いものでした。どうもクロック信号の条件が満たされていないための誤動作のようです。

写真中央のICは、文字が読み取れませんが、uPD8080Aです。一部命令の実行クロック数が異なるのと、減算のときにも10進補正命令が動作するという改良が(勝手に)行われていた石です。TK-80のオリジナルにも使われていましたね。下の方はuPD8080AF。FはFull CompatibleのFと言われていました。オリジナルの8080Aとまったく同一の動作をするように修正されたものです。当時は、命令の実行時間を利用して外部機器の制御タイミングをとることが結構行われていましたから、下手に少し早くなるような改良を行うと不評だったのですね。実行クロック数の比較は後にある命令表を参照のこと。

レジスタセットはこのようになっています。
8080 registers
B, C, D, E, H, Lがスクラッチパッドレジスタで、Aがアキュムレータです。ほとんどすべての演算は、このAレジスタと別のスクラッチパッドレジスタとの間で行われます。また、Fレジスタは演算結果によって自動的にセットされるフラグ類がまとめられているもので、演算の対象となりません。フラグの意味は、SがSignで演算結果の符号、ZがZeroで演算結果が0かどうか、HがHalf Carryで10進補正命令用の下位4 bitからの桁あげ、PがParityで演算結果の1のビットが偶数個あればセット、CがCarryで演算結果で桁あげが発生したことを示します。AF, BC, DE, HLはそれぞれ対になって、16 bit幅のレジスタとして操作されることがあります。
PCと表記されているプログラムカウンタは64 KByteに拡張されたアドレス空間を指すべく16 bit幅で、サブルーチンからの戻りアドレスを記録するスタックは外部メモリにとられるようになり、SPと表記されているスタックポインタがそのアドレスを指しています。また、このスタックはAF, BC, DE, HLのレジスタ対を個別にプッシュしたりポップしたりできるようになっていて、Intelのマイクロプロセッサでは初めて現代的なスタックになっています。ただし、スタックポインタにオフセットを加えるようなアドレッシングモードも、インデックス修飾の可能なアドレッシングモードも、共に命令体系の中には存在しませんから、スタックフレームを用いたローカル変数管理なんかを行おうとすると、手間です。が、BCレジスタ対とDEレジスタ対がスタックトップの2語、あるいはDEレジスタ対がスタックトップの1語というように解釈すれば、小さなサブルーチンならそれほどローカル変数が必要ありませんから、再帰的定義なども意外にうまく行きます。XTHL命令でスタックトップとHLレジスタを入れ替えることができますから、アキュムレータを作業用に残しておいてもBC, DE, HLとスタックトップ2 Byte分、計8 Byte分のローカル変数を持つことができます。サブルーチンを呼び出すときには破壊してはいけないレジスタをスタックにプッシュすれば、一種のローカル変数用のスタックフレームが自然とスタック上に形成されるというわけ。今から見れば整理されていない非力な命令体系のように思えるでしょうが、当時としてはそれ以前の4004とか8008などの本当に初期のマイクロプロセッサと比べていたのですから、そりゃもう至れり尽せりの強力なアーキテクチャでした。

命令セット

では具体的な命令について見ていきましょう。命令表を以下に示します。読み方については表の後にある注釈を参照。一応、これだけわかればアセンブラを作成したり、アセンブリ言語でプログラムを書ける程度には整備したつもりですが。なお、アセンブラ表記のオペランドの項にimmとある命令とIN/OUT命令は2 Byte命令で、addrかimm16が含まれる命令は3 Byte命令になります。それ以外の命令はすべて1 Byte命令です。

命令コード アセンブラ表記  clock uPDck 意味
01 ddd sss  MOV   rm, r     5/7   4/7  move
01 ddd 110  MOV   r, M       7     7   move memory to register
01 110 110  HLT              7     7   halt
00 ddd 110  MVI   rm, imm   7/10  7/10 move immediate
00 ddd 100  INR   rm        5/10  5/10 increment
00 ddd 101  DCR   rm        5/10  5/10 decrement
10 000 sss  ADD   rm        4/7   4/7  add
10 001 sss  ADC   rm        4/7   4/7  add with carry
10 010 sss  SUB   rm        4/7   4/7  subtract
10 011 sss  SBB   rm        4/7   4/7  subtract with borrow
10 100 sss  ANA   rm        4/7   4/7  and with A
10 101 sss  XRA   rm        4/7   4/7  exclusive or with A
10 110 sss  ORA   rm        4/7   4/7  or with A
10 111 sss  CMP   rm        4/7   4/7  compare with A
11 000 110  ADI   imm        7     7   add immediate to A
11 001 110  ACI   imm        7     7   add immediate to A with carry
11 010 110  SUI   imm        7     7   subtract immediate from A
11 011 110  SBI   imm        7     7   subtract immediate from A with borrow
11 100 110  ANI   imm        7     7   and immediate with A
11 101 110  XRI   imm        7     7   exclusive or immediate with A
11 110 110  ORI   imm        7     7   or immedeate with A
11 111 110  CPI   imm        7     7   compare immediate with A
00 000 111  RLC              4     4   rotate A left
00 001 111  RRC              4     4   rotate A right
00 010 111  RAL              4     4   rotate A left through carry
00 011 111  RAR              4     4   rotate A right through carry
11 000 011  JMP   addr      10    10   jump unconditional
11 011 010  JC    addr      10    10   jump on carry
11 010 010  JNC   addr      10    10   jump on no carry
11 001 010  JZ    addr      10    10   jump on zero
11 000 010  JNZ   addr      10    10   jump on no zero
11 110 010  JP    addr      10    10   jump on positive
11 111 010  JM    addr      10    10   jump on minus
11 101 010  JPE   addr      10    10   jump on parity even
11 100 010  JPO   addr      10    10   jump on parity odd
11 001 101  CALL  addr      17    17   call unconditional
11 011 100  CC    addr     11/17 11/17 call on carry
11 010 100  CNC   addr     11/17 11/17 call on no carry
11 001 100  CZ    addr     11/17 11/17 call on zero
11 000 100  CNZ   addr     11/17 11/17 call on no zero
11 110 100  CP    addr     11/17 11/17 call on positive
11 111 100  CM    addr     11/17 11/17 call on minus
11 101 100  CPE   addr     11/17 11/17 call on parity even
11 100 100  CPO   addr     11/17 11/17 call on parity odd
11 001 001  RET             10    11   return
11 011 000  RC              5/11  5/11 return on carry
11 010 000  RNC             5/11  5/11 return on no carry
11 001 000  RZ              5/11  5/11 return on zero
11 000 000  RNZ             5/11  5/11 return on no zero
11 110 000  RP              5/11  5/11 return on positive
11 111 000  RM              5/11  5/11 return on minus
11 101 000  RPE             5/11  5/11 return on parity even
11 100 000  RPO             5/11  5/11 return on parity odd
11 vvv 111  RST   vec       11    11   restart
11 011 011  IN    port      10    10   input
11 010 011  OUT   port      10    10   output
00 pp0 001  LXI   rp, imm16 10    10   load imediate register pair
11 pp0 101  PUSH  rr        11    11   push register pair on stack
11 pp0 001  POP   rr        10    10   pop register pair off stack
00 110 010  STA   addr      13    13   store A direct
00 111 010  LDA   addr      13    13   load A direct
11 101 011  XCHG             4     4   exchange D & E, H & L registers
11 100 011  XTHL            18    17   exchange top of stack, H & L
11 111 001  SPHL             5     4   H & L to stack pointer
11 101 001  PCHL             5     5   H & L to program counter
00 pp1 001  DAD   rp        10    11   add register pair to H & L
00 pp0 010  STAX  rx         7     7   store A indirect
00 pp1 010  LDAX  rx         7     7   load A indirect
00 pp0 011  INX   rp         5     5   increment register pair
00 pp1 011  DCX   rp         5     5   decrement register pair
00 101 111  CMA              4     4   complement A
00 110 111  STC              4     4   set carry
00 111 111  CMC              4     4   complement carry
00 100 111  DAA              4     4   decimal adjust A
00 100 010  SHLD  addr      16    16   store H & L direct
00 101 010  LHLD  addr      16    16   store H & L direct
11 111 011  EI               4     4   enable interrupts
11 110 011  DI               4     4   disable interrupt
00 000 000  NOP              4     4   no operation

注:2進数表記の命令コードの中に書かれた記号の意味については、dddはディスティネーションレジスタを示す数値、sssはソースレジスタを示す数値。ppはレジスタペアを表す数値。これらの具体的な数値については後で表にして示す。vvvはリスタートベクトルを表す数値(0から7)。
clockは命令実行に必要なクロック数。ただし、オペランドにrmと指定のあるものはc1/c2と表記されていて、実際にrが指定されたときにc1クロック、Mが指定されたときにはc2クロックが消費される。同じく条件命令の場合、条件が成立しなかったときにc1クロック、成立したときにc2クロックが消費される。uPDckはuPD8080Aの場合の命令実行クロック数。uPD8080AFの場合にはclockの項が適用される。
アセンブリ言語表記のオペランド部で、rはA, B, C, D, E, H, Lのいずれか、rmはA, B, C, D, E, H, L, Mのいずれかを指定する。レジスタペアを表すrpはB, D, H, SPのいずれか、rxはBかDを指定し、それぞれBC, DE, HL, SPの16 bitポインタレジスタを意味する。ただしPUSH/POP命令に関してはB, D, H, PSWをrrとして指定でき、それぞれBC, DE, HL, AFのレジスタペアを意味する。immは8 bit定数で、imm16は16 bit定数。addrは16 bitの絶対アドレスを表す数値。vecは0から7までの数値。portは8 bitのI/Oアドレスを表す。
レジスタ名と命令コード上でレジスタを表す数値との対応は次の表の通り。

レジスタ名  ddd/sss  r   rm
    A         111    *    *
    B         000    *    *
    C         001    *    *
    D         010    *    *
    E         011    *    *
    H         100    *    *
    L         101    *    *
    M         110         *

レジスタペアについては、このような値で指定する。「表記」という列はアセンブリ言語上での表記法について書き込んである。BCレジスタペアのアセンブリ言語上での表記はBとなることに注意。

ペア 表記 pp  rp  rr  rx
 BC    B   00   *   *   *
 DE    D   01   *   *   *
 HL    H   10   *   *
 SP   SP   11   *
 AF   PSW  11       *

また、そのレジスタがr, rm, rp, rxの指定に含まれる場合に各欄に*を書き込んでおいた。無印のレジスタ(例:rの場合のMレジスタ)は、そのアセンブリ言語表記でオペランドに使用するとエラーになる。

上の命令表で、LXI命令からDI命令までが8008にはなくて8080で新設された命令です。それ以外の命令は8008に備わっていますがアセンブリ言語レベルで同一表記になるだけで、バイナリコードには互換性がありません。
総じて、レジスタペアを用いる命令が強化されています。レジスタペアをポインタとして使える他、16 bitデータのインクリメントやデクリメントに加えて加算まで可能になっています。8008で書きにくかった256 Byteアラインメントを越えるデータ操作が楽になっています。PUSH/POP命令はレジスタペア単位で保存と復帰が可能な1 Byte命令で、効率良くワーキングレジスタを実質的に増やすことができます。また、PCHLはHLレジスタペアの指す場所へのジャンプ命令で、計算値や表駆動による分岐が可能になりました。XTHLもポインタを実質的に増やすための命令ですし、サブルーチン内でうまく使用するとCALL命令に続けて格納された定数に対して処理を行なうプログラムを効率良く書けます。
また、新設された命令ではありませんが、条件付きRET命令のクロック数は特徴的で、条件判定にうまく利用すると、条件不成立のときにわずか5クロックで実行されますから高速化に役立ちますし、1 Byte命令であることもあってコード密度を高めることができます。なにしろ8080の他の条件分岐命令はすべて3 Byte命令ですから。
uPD8080Aとそれ以外の8080および互換プロセッサの命令実行クロック数を比較してみると、レジスタ間のMOV命令やXTHL命令なんかがuPD8080Aの方が1クロックほど短くなっています。しかしDAD命令やRET命令のように遅くなっている命令もあります。NECでIntelのまねをして設計しているときに、設計の都合でわずかに変化してしまったという感じですね。
ADD命令と内部レジスタに対するINC命令を比較するとADD命令の方が1クロック早くなっていますが、これはディスティネーションレジスタがADD命令ではAレジスタに固定されているのに、INC命令ではAからHまでのどれかのレジスタが対象になるための違いでしょう。INC命令では対象レジスタからデータを読み出してインクリメンタに入力して、その出力を一時的にテンポラリレジスタに書き込んでから、よけいに必要な1クロックで対象レジスタにテンポラリレジスタの内容を書き込んでいたのでしょう。普通の8080ではMOV命令が4クロックでなくて5クロックなのも、テンポラリレジスタを介した転送のためではないかと思われます。するとXCHG命令の4クロックというのは異様な速度ですが、やはり入れ替えが簡単になるような特別な回路が用意されていたのでしょうね。HLレジスタとBCレジスタを入れ替えることはできませんし。

採用したコンピュータ製品

Intel SDK-80
NEC TK-80, TK-80E, TK-80BS, COMPO BS/80
東芝 EX-80, EX-80BS
AER MK-80A
MITS Altair 8800シリーズ
IMSAI 8080
POLY 88

Return to IC Collection.