これはuPD7807で一般的なシュリンク64ピンDIPですね。以前はQUIPというジグザグに長さの異なるピンが出ている小型64ピンパッケージが主に使われていました。
これは8 KByteのUV EPROMを内蔵したuPD78P09Rで、窓付きセラミックQUIPという珍しいパッケージに収められている。QUIPはQuad
In-line Packageの略で、4列の2.54 mmピッチのピンが出ている。ただし、内側の2列は、外側と1.27
mmだけ位置がずれている。
写真の縮尺は上のuPD7807と同程度でずいぶんと大きく感じられますが、普通のQUIPはパッケージの真横から1.27
mmピッチで引き出されたリードが互い違いに折り曲げて4列に整形されているので、シュリンクDIPより小型に感じられるはずです。
内部のプロセッサ部のアーキテクチャについて、例によってレジスタセットから眺めることにしましょうか。
このように、8080とかZ80 CPUとそれほど違わないレジスタ構成になっています。
まずPSWはプログラムステータスワードの略で、フラグ類が納められたレジスタです。詳細は後述します。
8 bit幅のレジスタは、V, A, B, C, D, E, H, Lと、それに'記号の付いたレジスタがもうひと組あります。'付きはZ80
CPUの俗に言う裏レジスタに相当します。NECではZilogがZ80 CPUの2組のレジスタをそう呼んでいるように、MAINレジスタとALTレジスタという呼び方をしています。
16 bit幅のレジスタは拡張アキュムレータEAと、そのALTレジスタEA'、およびプログラムカウンタPCとスタックポインタSPがあります。オリジナルのuCOM-87アーキテクチャにはEAおよびEA'レジスタはありませんでした。uPD7807,
uPD7809, uPD7810, uPD7811のように16 bit ALUを備えたプロセッサに追加されたものです。なお、これ以降のアーキテクチャ紹介では、その拡張されたuPD7807について扱いますから、uCOM-87ファミリのプロセッサが最初からこれほど高機能であったとは思わないでください。
レジスタの名称の紹介は済みましたが、まだ機能には触れていませんね。Aレジスタはアキュムレータとして8
bit演算に使用されます。Vレジスタは、MC6809のDPレジスタに相当するもので、8
bitダイレクトアドレッシングで操作するメモリの上位8 bitアドレスを指定するレジスタです。VAレジスタペアとして、8080のAFレジスタペアのようにPUSH,
POP命令で退避したり、EXA命令でEAレジスタとともにV'A'レジスタペアおよびEA'レジスタと交換することができます。
B, C, D, E, H, Lレジスタは、やはり8080と同じように、スクラッチパッドレジスタと同じように使えるほか、BC,
DE, HLレジスタペアとしてポインタレジスタにもなります。さらに、DE, HLレジスタペアをポインタとして使う場合、8
bitデータ操作後にオートインクリメントやオートデクリメントする機能、16 bitデータ操作時には2だけオートインクリメントする機能もあります。しかもDEレジスタペアには8
bitデータでアドレス修飾を行う機能もあり、インデックスレジスタ的に使えます。HLレジスタペアには8
bitデータの修飾のほか、A, B, EAレジスタによる修飾も可能で、8080とは異なる多様なアドレッシングモードがあります。ただし、命令とアドレッシングモードの直交性はそれほど高くなく、比較的限定された命令でのみ使えると考えてください。EXR命令でEAからHLまでの全MAINレジスタをALTレジスタで入れ替えられるほか、EXX命令でBC,
DE, HLだけの入れ替え、EXHでHLレジスタペアだけの入れ替えが可能です。
EAレジスタは16 bitの拡張アキュムレータですから、当然16 bit単位の演算命令を中心に使用されます。その他、乗除算命令でも使われます。
PSWは、それ自体が命令の操作対象となることは、割り込みから復帰するRETI命令とソフトウェア割り込み命令SOFTI以外にはありません。個別のフラグ単位の操作はあり得ますが、全体を別のレジスタに転送するような命令は存在しません。割り込み時にスタックに保存されるだけです。割り込み許可フリップフロップなど割り込み関係の状態はPSWとは別の場所にあり、専用の割り込み許可・禁止命令やI/O操作によって操作することができますし、特別なテスト命令で調べることのできるものもあります。
PSWに話を戻すと、有効なフラグは6種類、残りの2 bitは0になっています。演算の結果によって影響を受けて条件判断に利用できる一般的なフラグが、ゼロフラグのZ、ハーフキャリーのHC、キャリーのCYの3種類です。命令実行シーケンス制御に使われるのが、スキップ条件成立を表すSK、MVI
A, byte命令の縦積み命令シーケンス実行中を表すL1、MVI L, byteやLXI H, word命令の縦積み命令シーケンス実行中を表すL0の3種類です。
SK, L1, L0フラグの意味はuCOM-87ファミリのプロセッサ独特の実行制御方式に関係しています。
まずSKフラグについてですが、uCOM-87ファミリには条件分岐命令がありません。ジャンプ命令は無条件のものだけです。では、どのように条件判断を行うのかというと、条件スキップということを行います。ある命令の実行時に、特定の条件が成立したら、次の命令を実行しないでもうひとつ先の命令を実行するという機能です。
スキップによる条件判定というのは、命令固定長のミニコンピュータなどではよく使われていました。私の手持ちではたとえばL-16Aがそうです。分岐命令には分岐先のアドレスを組み込まなくてはならないため、条件を示すビットパターンを分岐命令に追加すると命令長が長くなりがちで固定長命令に納めにくくなります。そのため、条件分岐命令をあきらめて、レジスタレジスタ間演算しかしない場合にはレジスタ指定フィールド以外にビットパターンが余るので、そこにスキップ条件を組み込み、演算した直後の命令をスキップするかどうかで条件分岐を実現する方法です。命令が1語固定長のコンピュータの場合なら、プログラムカウンタをよけいにインクリメントするだけで命令のスキップが可能で、ややこしい制御回路も必要ありません。
しかし、uCOM-87は可変長命令です。1 Byte命令から4 Byte命令まで多岐にわたっているので、プログラムカウンタをよけいに+1するだけでは命令のスキップがうまくいきません。もちろん、スキップできるのは特定の命令だけだという制限をつけるアーキテクチャもあり得るでしょうが、uCOM-87では任意の可変長命令をスキップできるようにしています。そこで、スキップ条件を満足したら、PSWのSKフラグをセットします。SKフラグがセットされた状態でも、次の命令をきちんと読み込んで命令の解釈を行いますが、実質的な命令実行を行いません。ただ、その命令の長さを決定して、その分だけ読み込みながら無視していきます。したがって、ある命令をスキップしても、その命令をスキップしなかった場合と比べてほぼ同じクロック数の時間が消費されます。さて、ここまでの話なら、PSWにわざわざSKフラグを組み込まなくても、内部フリップフロップを使えば実現できます。PSWにSKフラグを定義した理由は割り込みにあります。スキップ判定を含む命令の実行中に割り込みが発生すると、その命令の実行後に割り込みシーケンスが開始されます。割り込みサービスルーチンの最初の命令をスキップしては正しいプログラム動作を行えませんし、割り込みから復帰したときには割り込み前のスキップ判定結果に応じて適切にスキップしたりしなかったりできなくてはなりません。そこでPSWにSKフラグを組み込んで、割り込み時にはスタックに退避しておくようになっています。割り込みから戻るとPSWの内容も復元されるので、以前のSKフラグの状態に応じてスキップが行われるようになっています。
次にPSWのL1とL0の役割について説明します。これらのフラグは命令の縦積み制御に用いられます。uCOM-87では、MVI
A, byte命令、つまり8 bit定数をAレジスタにロードする命令が複数連続して配置されていると、2回め以降のMVI
A, byte命令は読み込まれるけれどもAレジスタへのロードは実行されないという機能があります。このようなMVI
A, byte命令の連続配置を縦積みと呼び、最初の命令しか実行されないことを縦積み効果と呼んでいます。このようなしくみが何の役に立つのか、簡単なコードで確認しましょう。Aレジスタの第3ビットが1になっていればPAポートに45Hを、0なら24Hを出力しなくてはならないというような場面を仮定します。このようなことは、機械か何かの制御を行うような用途では、よく見られるはずです。命令の縦積みを利用すると、次のように書けます。
    ONI    A, 08H   ; A
& 08Hが0以外ならスキップ
    MVI    A, 24H   ; 縦積み
    MVI    A, 45H
    MOV    PA, A   
; Aの内容をPAに出力
最初のONI命令は、Aと定数の論理積が非0の場合にスキップを行う命令で、Aの内容は変化しないテスト命令の一種です。スキップしなかった場合、Aに24Hがロードされた後にMVI
A, 45Hが読み込まれますが、この命令は読み捨てられるだけでAの内容は変化せずに24Hのままです。スキップされた場合にはMVI
A, 45Hが最初の縦積み命令ですから、Aに45Hが入ります。バイナリコードでは8
Byteの長さになります。仮に縦積み制御がなければ、分岐命令がいくつか必要になるか、トリッキーなコードになるでしょう。このように、表を引くことによってコード変換をするには大げさだけれども分岐命令を用いるとコードが長くなりがちなコード変換の一種に縦積み命令は役に立ちます。
    ONI    A, 08H
    JR     L1       
; 1 Byte分岐命令
    MVI    PA, 45H
    JR     L2
L1: MVI    PA, 24H
L2:
これが分岐命令を用いた同じコードです。uCOM-87では6 bitオフセットの1
Byteジャンプ命令があるため、このコードでも10 Byteしか占有しませんが、先のコードより長くなっているのは確かです。
縦積み効果は3命令以上でも、命令が連続している限り有効ですから、たとえば今までの例題に先立ってAの第2ビットが1ならPAポートに出力する値が40Hだというような条件を付け加えると、こうなります。
    OFFI   A, 04H
    JR     L1
    ONI    A, 08H
    MVI    A, 24H
    MVI    A, 45H
L1: MVI    A, 40H
    MOV    PA, A
これで合計13 Byteの長さのコードになります。OFFI命令はONI命令の逆でAと定数の論理積の結果が0のときにスキップを行うテスト命令です。縦積み命令の場合、スキップと同じように実行されない命令も読み込まれて縦積み命令かどうかの判定を行うことになりますから、あまり多くの命令を縦積みしてしまうと遅くなりがちです。あまりに多くの場合分けがある場合、表を引いた方が結局はコードも短く高速になります。しかし、この例の程度の規模だと、縦積みとスキップの規則を知っていればコンパクトでそれほど読みにくくもないことがわかるでしょう。
具体的にどのように縦積み命令の処理を行っているかというと、PSWにはMVI
A, byte命令でセットされ、この命令以外ではリセットされるL1フラグがあります。MVI
A, byte命令をプロセッサが読み込むと、L1フラグがセットされているかどうかをまず調べて、L1フラグがセットされていればAレジスタにデータを転送せず、ただ命令を読み飛ばしてL1フラグをセットします。L1フラグがリセットされていれば、Aレジスタにデータを転送して、L1フラグをセットします。このようにして、縦積み命令の処理を行います。ですから一連の縦積み命令の実行中に割り込みが発生しても、L1フラグを含むPSWが割り込み時に保存されますから、割り込み復帰後に正しく縦積み命令を処理できます。
L0フラグについてはMVI L, byte命令とLXI H, word命令についてL1フラグと同じように使われて、これらの命令の縦積み処理に使われます。
uCOM-87のメモリ空間は、すでにPCの幅などから想像できると思いますが、16
bitアドレス空間の64 KByteあります。IntelのMCS-48やMCS-51と異なり、プログラムもデータも同一のアドレス空間で、チップの内部にあるか外部にあるかでプログラムからのアクセス方式が変わることもありません。単一16
bitアドレス空間です。この点は、組み込み用シングルチップというよりは汎用プロセッサ的になっています。I/Oポートアドレス空間はありませんが、内蔵I/Oの制御に用いるレジスタは特殊レジスタ扱いで、専用のMOV命令などによってアクセスされるため、一種のI/Oアドレス空間とみなすこともできます。ただし、そのI/Oアドレス空間に別のポートを増設することはできません。Z80
CPUにおけるRレジスタやIレジスタと同様の形で内蔵I/Oポート制御レジスタ類が扱われていると思ってください。一部の特殊レジスタには、ビット操作命令や演算命令も適用可能です。
64 KByteのメモリ空間のうち、1000H未満には命令やハードウェアにとって特別な役割が与えられています。uPD7807は内蔵ROMを持ちませんが、uPD7808で0000Hから0FFFHまで、uPD7809で0000Hから1FFFHまでの間に内蔵ROMが割り当てられていましたから、そこに特別な役割を持たせるのは当然でしょうね。
まず、uCOM-87はリセット時にPCが0クリアされアドレス0000Hからプログラムの実行が始まります。したがって、アドレス0000Hがコールドスタート地点という意味を持ちます。さらに割り込みが発生すると、PCに特定の値がセットされ、そこから割り込みサービスルーチンが始まります。そのエントリポイントが、割り込みの種類に従って0004H,
0008H, 0010H, 0018H, 0020H, 0028H, 0060Hにあります。
次に0080Hから00BFHの64 Byteはコールアドレステーブルとしても使用できます。uCOM-87には16
bitアドレスをオペランドに持つ一般的な3 Byte長のサブルーチン呼び出し命令のCALL命令の他に、1
Byteサブルーチン呼び出し命令のCALT命令もあります。CALT命令では5 bit分のベクタ番号を命令に含み、最大32種類のサブルーチンを呼び出すことができるようになっています。それで、その32種類のサブルーチンの開始アドレスをどこに定義しておくかというのが、このコールアドレステーブルで、2
Byteで1エントリのアドレスを登録しておきます。たとえばCALT 0なら0080Hと0081Hからサブルーチンアドレスを読み出し、そこを呼び出します。CALT
1なら0082Hと0083Hという具合です。CALT命令とCALL命令の実行クロック数は等しいため、純粋にプログラムコードサイズを減らすのに役立ちます。上手にCALT命令を使えば、CALL命令を用いた場合に比べて200
Byteくらいはすぐに節約できるでしょう。4 KByteとか8 KByteのROMしか使えない時代ですからコードの節約は大事ですし、プログラムカウンタ相対の分岐命令のあるuCOM-87ではコードが短くなった方が相対分岐命令のアドレス範囲外エラーになる可能性も減りますからね。
そうして、最後の0800Hから0FFFHまでの範囲もサブルーチン呼び出しに関係しています。この2
KByteの範囲にあるサブルーチンは、2 Byteサブルーチン呼び出し命令であるCALF命令で呼び出せることになっています。CALF命令は11
bit分のアドレスフィールドを持ち、上位5 bitに00001Bを補ったアドレスへのサブルーチン呼び出しを行います。内蔵ROMが4
KByteしかなかった頃には、そのROMの半分の領域に納まったサブルーチンを呼び出す場合に1
Byte得をするのですから、それなりに役立ったのでしょう。
メモリ空間の0FF00Hから0FFFFHまでの256 Byteには内蔵RWMが配置されていて、プログラムから自由に使えます。さらに、そのRWMの内の0FFE0Hから0FFFFHまでの32
Byteはバッテリバックアップ可能ということになっていますが、uPD7807はn-MOSプロセスで製造されてCMOSでないわけでして、1
mA程度のバックアップ電流が必要なため、瞬停対策用でしょうね。uCOM-87ファミリの中でもCMOS化されたものでは使いやすくなりましたが。内蔵RWMは特殊レジスタへの設定で、アクセスを禁止できます。
命令体系は、8080のIntelニーモニック表記に近いので、レジスタセットと同じように8080風かなと思えますが、意外に違いがあります。スキップ命令と数の多い複合機能命令もありますし、B,
C, D, E, H, Lレジスタ間で自由にMOVできないとか、独自の命令セットと考えるべきでしょう。
NECの資料によると命令の種類は165命令となっていますが、命令表の項目数を単純に数えると168項目となっていますが、どうやらビット操作命令として追加されたMOV,
SK, SKN命令のニーモニックが、レジスタ間データ転送命令のMOV、スキップ命令のSK,
SKN命令のニーモニックと等しいので、165命令という数になっているようです。
これまで見てきた範囲では分岐命令やサブルーチン呼び出しに1 Byte命令が割り当てられているなど、コードを短くする工夫が凝らされているように思えますが、レジスタレジスタ間演算命令もほとんど2
Byte命令になっていて、平均的には1命令あたり2 Byteくらい消費されます。なお1
Byte命令から4 Byte命令まで存在します。
uPD7807は最大12 MHzクロックで動作し、3クロックで1ステートという内部処理単位が構成されます。つまり1ステート時間は250
nsです。最短の命令は4ステートで実行されますから、1 usが最短命令実行時間です。乗除算以外の一番長い命令が20ステートですから5
us、8 bitデータ同士の乗算で32ステートの8 us、16 bit割る8 bitの除算命令で59ステートの14.75
usです。なお、これはメモリウエイトステートのない場合で、リセット時には外部メモリアクセスに1ウエイトステートが挿入されるようになっているため、条件によっては実行時間も少し遅くなります。
これから命令の詳細について見ていくことにします。命令の機能でグループ化して順番に紹介します。
8 bitデータ転送命令には以下のものがあります。
    命令         
第1バイト 第2バイト 第3 第4 state skip 動作
MOV    r1, A      
00011rrr                    
4        r1 <- A
MOV    A, r1      
00001rrr                    
4        A <- r1
MOV    sr, A      
01001101  11ssssss         
10*       sr <- A
MOV    A, sr1      01001100 
11ssssss          10*      
A <- sr1
MOV    r, addr     01110000 
01101rrr  al  ah  17       
A <- (addr)
MOV    addr, r     01110000 
01111rrr  al  ah  17       
(addr) <- A
MVI    r, byte     01101rrr 
dddddddd           7*      
r <- byte
MVI    sr2, byte   01100100  s0000sss 
dd      14       
sr2 <- byte
MVIW   wa, byte    01110001  wwwwwwww 
dd      13*      
(V.wa) <- byte
MVIX   rpa1, byte  010010aa  dddddddd         
10*       (rpa1) <- byte
STAW   wa         
01100011  wwwwwwww         
10*       (V.wa) <- A
LDAW   wa         
00000001  wwwwwwww         
10*       A <- (V.wa)
STAX   rpa2       
a0111aaa  dddddddd(*1)     7/13*     
(rpa2) <- A
LDAX   rpa2       
a0101aaa  dddddddd(*1)     7/13*     
A <- (rpa2)
EXR               
01001000  10101101          
8        VA BC DE HL EA入れ替え
EXX               
01001000  10101111          
8        BC DE HL入れ替え
EXA               
01001000  10101100          
8        VA EA入れ替え
EXH               
01001000  10101110          
8        HL入れ替え
BLOCK  D+         
00010000                  
13(C+1)    (DE)+ <- (HL)+, C <- C - 1, until CY
BLOCK  D-         
00010001                  
13(C+1)    (DE)- <- (HL)-, C <- C - 1, until CY
uPD7807には最大4 Byte命令があります。ここでは第1バイトと第2バイトは2進数表記、第3バイトと第4バイトは16進数的2桁表記で示します。state欄は命令実行時のステート数で、1ステートが3クロックに相当します。たとえば12
MHzクロックで動作しているとき、1ステートは250 nsですからMOV r1, A命令の場合には1
usの実行時間となります。state欄の数値の後についている'*'マークの意味は命令表の末尾に説明します。skipはスキップ条件を表しますが、8
bitデータ転送命令にはスキップ条件付きの命令はありませんので、空欄になっています。命令オペランドのr1,
sr, sr1などは命令表の後にあるオペランドの表を参照してください。ここで使われているオペランドのうち、大文字はレジスタそのものを表します。addrは16
bitアドレス定数、byteはイミディエートデータ、waはVレジスタと併用される8
bitアドレス定数です。上位アドレスをVレジスタの内容、下位アドレスを8 bit定数でアクセスするアドレッシングモードをワーキングレジスタアドレッシングと呼び、メモリ上の任意の256
Byteページに設定できる256本のワーキングレジスタセットと考えているので、waと呼びます。
MOV命令は一般的な8 bitデータ転送命令です。ただ、8080系列のプロセッサと異なり、レジスタ・レジスタ間転送は自由に行えず、必ず片方がAレジスタになります。つまり、MOV
B, CのようなことはuPD7807では実行できません。ダイレクトアドレッシングでメモリとのデータ転送を行うのもMOV命令の機能のうちです。8080ならSTA命令やLDA命令の役割ですが、Aレジスタにしか転送できない8080と異なり、uPD7807では任意のスクラッチパッドレジスタに直接転送できるようになっているのでMOVのニーモニックが与えられているようです。ダイレクトアドレッシングでは16
bitアドレスを用いますが、下位バイト(al)の方が命令の第3バイトに、上位バイト(ah)が命令の第4バイトに配置されます。
MVI命令は普通のレジスタ以外に特殊レジスタの主だったものに直接定数をロードできます。アキュムレータを書き換えずに特殊レジスタの内容を変更できるので有益でしょう。
MVIW命令はワーキングレジスタに定数をロードする命令で、MVIX命令はポインタアドレッシングで指定されるアドレスに定数をロードする命令です。
STAWとLDAW命令はアキュムレータとワーキングレジスタとのデータ転送です。ニーモニック側にAレジスタの文字が入っているため、オペランド側にはAのレジスタ名を記入しませんが、MOV
r1, Aと書くのに比べて対称的でないようでちょっと嫌な感じもしますね。レジスタ・レジスタ間転送は必ず一方のオペランドがAレジスタなのだから、LDA 
r1とかSTA r1というような命令表記の方が統一感があるような気もします。単なる決まりといえばそれだけのことなのですが。
STAXとLDAX命令はポインタアドレッシングやさまざまなインデックス修飾のできる命令です。定数オフセットを必要とするアドレッシングモードの場合にだけ、第2バイトが必要となります。つまり表の中で(*1)となっているddddddddはアドレッシングモードによって省略される場合もあります。命令ステート数は1
Byte命令のときに7ステート、2 Byte命令のときに13ステートという意味になります。
EXR, EXX, EXA, EXHの各命令はそれぞれ関係するレジスタのALT側とMAIN側を入れ替える命令です。
BLOCK D+命令とBLOCK D-命令はブロック転送命令です。HLでポイントされるアドレスからDEでポイントされるアドレスへと、それぞれのポインタをインクリメントないしデクリメントしながら、(C
+ 1) Byteだけ転送します。正確に転送継続条件を記述すれば、Cレジスタをデクリメントして、その結果桁借りが発生してCYがセットされる場合に転送を中断します。つまりCレジスタに0が入っていると1
Byteの転送が行われます。そこには少し注意が必要です。なお、uPD7810など、この時点で存在する他のuCOM-87アーキテクチャのプロセッサにはインクリメント転送を行うBLOCK命令だけが存在して、デクリメント転送するBLOCK
D-命令は存在しません。そのため、他のプロセッサではBLOCK命令にオペランドも記入する必要もありません。おまけに他のプロセッサとはアセンブリ言語命令でほぼ上位互換になっていますが、バイナリコードは異なりますので、ファミリ内でも汎用ルーチンを他のプロセッサに移植する場合はアセンブリしなおす必要があります。そのへん、ファミリとはいえ、注意が必要です。組み込み用途のプロセッサで、内蔵I/Oの種類が異なるので、アプリケーションプログラム全体をそのまま移植する場面は考えにくいですが、単純なメモリのブロック移動やクリアルーチンもオブジェクトコード互換にはならないというのは期待を裏切りがちですから。
16 bitデータ転送命令には以下のものがあります。
    命令         
第1バイト 第2バイト 第3 第4 state skip 動作
DMOV   rp3, EA     101101pp                    
4       rp3 <- EA
DMOV   EA, rp3     101001pp                    
4       EA <- rp3
DMOV   sr3, EA     01001000 
1101001u          14      
sr3 <- EA
DMOV   EA, sr4     01001000 
110000vv          14      
EA <- sr4
SBCD   addr       
01110000  00011110  al  ah  20      
(addr) <- C, (addr + 1) <- B
SDED   addr       
01110000  00101110  al  ah  20      
(addr) <- E, (addr + 1) <- D
SHLD   addr       
01110000  00111110  al  ah  20      
(addr) <- L, (addr + 1) <- H
SSPD   addr       
01110000  00001110  al  ah  20      
(addr) <- SPl, (addr + 1) <- SPh
STEAX  rpa3        01001000 
1001aaaa  dd(*2) 14/20     (rpa3) <- EA
LBCD   addr       
01110000  00011111  al  ah  20      
C <- (addr), B <- (addr + 1)
LDED   addr       
01110000  00101111  al  ah  20      
E <- (addr), D <- (addr + 1)
LHLD   addr       
01110000  00111111  al  ah  20      
L <- (addr), H <- (addr + 1)
LSPD   addr       
01110000  00001111  al  ah  20      
SPl <- (addr), SPh <- (addr + 1)
LDEAX  rpa3        01001000 
1000aaaa  dd(*2) 14/20     EA <- (rpa3)
PUSH   rp1        
10110ppp                   
13       (SP - 1) <- rp1h, (SP - 2) <-
rp1l, SP <- SP - 2
POP    rp1        
10100ppp                   
10       rp1l <- (SP), rp1h <- (SP
+ 1), SP <- SP + 2
LXI    rp2, word   0ppp0100  wwwwwwwl 
wh      10*      rp2
<- word
TABLE             
01001000  10101000         
17       C <- (PC + 3 + A), B <- (PC
+ 3 + A + 1)
DMOV命令は16 bit転送で対象がEAレジスタという点を除けばMOV命令と同じです。特殊レジスタに対するDMOV命令は、16
bitアクセスが必要なレジスタにのみ適用可能になっています。
SBCDからSSPDおよびLBCDからLSPD命令はダイレクトアドレッシングによるレジスタペア・メモリ間データ転送です。SBCD命令など、減算命令の一種かBCD演算命令の一種に誤解しそうなニーモニックになっていますが、Store
BC Directの略なのでしょう。
STEAX命令とLDEAX命令は8 bitデータ転送命令のSTAX命令およびLDAX命令と対応したものです。(*2)の付いている第3バイトは、やはり8
bit定数修飾のあるアドレッシングモードにだけ存在します。2 Byte命令か3 Byte命令かに応じて実行ステート数も14ステートと20ステートの差が生じます。なお、DMOV命令やSTEAX命令とLDEAX命令でメモリ上のデータ配置は、他のレジスタペアの場合と同じく、EAの下位バイトが小さなメモリアドレスに対応します。
PUSH命令とPOP命令は8080命令と同様にレジスタペア単位でスタックに出し入れします。スタック上のデータ配置も、小さなアドレスにレジスタの下位バイトが対応します。
LXI命令はレジスタペア用のMVI命令です。
TABLE命令は、なかなか特徴的な複合命令です。Aレジスタの内容と16 bitデータとのコード変換を行う命令で、プログラムコードの中に定数表を埋め込めるようになっています。普通は1
Byte相対分岐命令のJR命令と組み合わせて定数表を飛び越えます。C言語のswitch文的な多重分岐を行う場合は、TABLE命令で行き先の番地をBCレジスタペアにロードして、ただちに1
Byte命令のJB命令で分岐することも可能です。定数表をプログラムコードの途中に埋め込みたくなければ、LDEAX
H+A命令などで似たようなことができますけどね。
次はレジスタ用の8 bit演算命令の表です。
    命令         
第1バイト 第2バイト 第3 第4 state skip 動作
ADD    A, r       
01100000  11000rrr          
8       A <- A + r
ADD    r, A       
01100000  01000rrr          
8       r <- r + A
ADC    A, r       
01100000  11010rrr          
8       A <- A + r + CY
ADC    r, A       
01100000  01010rrr          
8       r <- r + A + CY
ADDNC  A, r        01100000 
10100rrr           8 
!CY  A <- A + r
ADDNC  r, A        01100000 
00100rrr           8 
!CY  r <- r + A
SUB    A, r       
01100000  11100rrr          
8       A <- A - r
SUB    r, A       
01100000  01100rrr          
8       r <- r - A
SBB    A, r       
01100000  11110rrr          
8       A <- A - r - CY
SBB    r, A       
01100000  01110rrr          
8       r <- r - A - CY
SUBNB  A, r        01100000 
10110rrr           8 
!CY  A <- A - r
SUBNB  r, A        01100000 
00110rrr           8 
!CY  r <- r - A
ANA    A, r       
01100000  10001rrr          
8       A <- A & r
ANA    r, A       
01100000  00001rrr          
8       r <- r & A
ORA    A, r       
01100000  10011rrr          
8       A <- A | r
ORA    r, A       
01100000  00011rrr          
8       r <- r | A
XRA    A, r       
01100000  10010rrr          
8       A <- A ^ r
XRA    r, A       
01100000  00010rrr          
8       r <- r ^ A
GTA    A, r       
01100000  10101rrr          
8  !CY  A - r - 1
GTA    r, A       
01100000  00101rrr          
8  !CY  r - A - 1
LTA    A, r       
01100000  10111rrr          
8   CY  A - r
LTA    r, A       
01100000  00111rrr          
8   CY  r - A
NEA    A, r       
01100000  11101rrr          
8  !Z   A - r
NEA    r, A       
01100000  01101rrr          
8  !Z   r - A
EQA    A, r       
01100000  11111rrr          
8   Z   A - r
EQA    r, A       
01100000  01111rrr          
8   Z   r - A
ONA    A, r       
01100000  11001rrr          
8  !Z   A & r
OFFA   A, r       
01100000  11011rrr          
8   Z   A & r
アキュムレータとレジスタの間を組み合わせて演算を行いますが、アキュムレータに結果が入るものとレジスタに結果が入るものの2種類が用意されています。このグループの命令は、普通の組み込み用マイクロプロセッサなら1
Byte命令が普通でしょうが、すべて2 Byte命令となっています。これもuCOM-87アーキテクチャの特徴といえるでしょう。実際、他の命令まで調べるとわかるように、1
Byte命令の方が珍しいのです。普通の考え方なら、命令を短く符号化してメモリ効率を高める設計方針をとることでしょう。そのかわり、どうやらuCOM-87の考え方では、多種類の命令を用意しておけば同じ機能のプログラムでも少ない命令数で記述することができ、その結果、個々の命令長が多少長くなってもプログラム全体のサイズが大きくはならないだろうとしているようです。
ADD, ADC, SUB, SBB, ANA, ORA, XRAの各命令は、Aレジスタがディスティネーションとなるかソースになるかの違いによって、2種類の命令パターンがあります。その点以外は、ごく一般的な演算命令です。
ADDNC命令とSUBNB命令は、スキップ条件付きの加減算命令です。表のskipの欄に!CYとあるのは、条件にNOTが含まれていることを表す'!'マークとスキップ対象のCYフラグを組み合わせたものです。つまり桁上がりがなければスキップということです。演算と一種の条件分岐が組み合わされた複合命令です。
GTA, LTA, NEA, EQA命令は比較命令で、演算結果は残らずにフラグだけが変化して、それに合わせてスキップが生じます。NEA命令とEQA命令ではディスティネーションとソースを入れ替える必要はないように思えるかもしれませんが、CYフラグの変化が異なります。たとえばNEA命令でふたつのオペランドが等しいかテストしてから、ただちにSK命令で大小判定を行うこともありますから、2種類用意されている意味はきちんと存在します。
ONA命令とOFFA命令は、論理積演算でマスクした後のビットがすべて0かどうかをテストする命令で、Aとrの順序を入れ替えても同じ意味となるので、一方の命令パターンしか存在しません。
さて、次はメモリ用の8 bit演算命令です。
    命令         
第1バイト 第2バイト 第3 第4 state skip 動作
ADDX   rpa        
01110000  11000aaa         
11       A <- A + (rpa)
ADCX   rpa        
01110000  11010aaa         
11       A <- A + (rpa) + CY
ADDNCX rpa         01110000 
10100aaa          11 
!CY  A <- A + (rpa)
SUBX   rpa        
01110000  11100aaa         
11       A <- A - (rpa)
SBBX   rpa        
01110000  11110aaa         
11       A <- A - (rpa) - CY
SUBNBX rpa         01110000 
10110aaa          11 
!CY  A <- A - (rpa)
ANAX   rpa        
01110000  10001aaa         
11       A <- A & (rpa)
ORAX   rpa        
01110000  10011aaa         
11       A <- A | (rpa)
XRAX   rpa        
01110000  10010aaa         
11       A <- A ^ (rpa)
GTAX   rpa        
01110000  10101aaa         
11  !CY  A - (rpa) - 1
LTAX   rpa        
01110000  10111aaa         
11   CY  A - (rpa)
NEAX   rpa        
01110000  11101aaa         
11  !Z   A - (rpa)
EQAX   rpa        
01110000  11111aaa         
11   Z   A - (rpa)
ONAX   rpa        
01110000  11001aaa         
11  !Z   A & (rpa)
OFFAX  rpa        
01110000  11011aaa         
11   Z   A & (rpa)
レジスタ用の8 bit演算命令と同じことを、ソースをメモリ上のデータとして実行します。ディスティネーションはAレジスタに限定されています。オペランドがrpaで表されている通り、BC,
DE, HLをポインタレジスタとして使えるほか、DEとHLのオートインクリメントやオートデクリメントアドレッシングも利用可能で、複数バイトデータの加減算などが容易になっています。
イミディエートデータ用演算命令は、このようになっています。
    命令         
第1バイト 第2バイト 第3 第4 state skip 動作
ADI    A, byte     01000110 
dddddddd           7*     
A <- A + byte
ADI    r, byte     01110100 
01000rrr  dd      11      
r <- r + byte
ADI    sr2, byte   01100100  s1000sss 
dd      20      
sr2 <- sr2 + byte
ACI    A, byte     01010110 
dddddddd           7*     
A <- A + byte + CY
ACI    r, byte     01110100 
01010rrr  dd      11      
r <- r + byte + CY
ACI    sr2, byte   01100100  s1010sss 
dd      20      
sr2 <- sr2 + byte + CY
ADINC  A, byte     00100110  dddddddd          
7* !CY  A <- A + byte
ADINC  r, byte     01110100  00100rrr 
dd      11  !CY  r <- r + byte
ADINC  sr2, byte   01100100  s0100sss 
dd      20  !CY  sr2 <- sr2 + byte
SUI    A, byte     01100110 
dddddddd           7*     
A <- A - byte
SUI    r, byte     01110100 
01100rrr  dd      11      
r <- r - byte
SUI    sr2, byte   01100100  s1100sss 
dd      20      
sr2 <- sr2 - byte
SBI    A, byte     01110110 
dddddddd           7*     
A <- A - byte - CY
SBI    r, byte     01110100 
01110rrr  dd      11      
r <- r - byte - CY
SBI    sr2, byte   01100100  s1110sss 
dd      20      
sr2 <- sr2 - byte - CY
SUINB  A, byte     00110110  dddddddd          
7* !CY  A <- A - byte
SUINB  r, byte     01110100  00110rrr 
dd      11  !CY  r <- r - byte
SUINB  sr2, byte   01100100  s0110sss 
dd      20  !CY  sr2 <- sr2 - byte
ANI    A, byte     00000111 
dddddddd           7*     
A <- A & byte
ANI    r, byte     01110100 
00001rrr  dd      11      
r <- r & byte
ANI    sr2, byte   01100100  s0001sss 
dd      20      
sr2 <- sr2 & byte
ORI    A, byte     00010111 
dddddddd           7*     
A <- A | byte
ORI    r, byte     01110100 
00011rrr  dd      11      
r <- r | byte
ORI    sr2, byte   01100100  s0011sss 
dd      20      
sr2 <- sr2 | byte
XRI    A, byte     00010110 
dddddddd           7*     
A <- A ^ byte
XRI    r, byte     01110100 
00010rrr  dd      11      
r <- r ^ byte
XRI    sr2, byte   01100100  s0010sss 
dd      20      
sr2 <- sr2 ^ byte
GTI    A, byte     00100111 
dddddddd           7*
!CY  A - byte - 1
GTI    r, byte     01110100 
00101rrr  dd      11  !CY  r -
byte - 1
GTI    sr5, byte   01100100  s0101sss 
dd      14  !CY  sr5 - byte - 1
LTI    A, byte     00110111 
dddddddd           7* 
CY  A - byte
LTI    r, byte     01110100 
00111rrr  dd      11   CY 
r - byte
LTI    sr5, byte   01100100  s0111sss 
dd      14   CY  sr5 - byte
NEI    A, byte     01100111 
dddddddd           7*
!Z   A - byte
NEI    r, byte     01110100 
01101rrr  dd      11  !Z  
r - byte
NEI    sr5, byte   01100100  s1101sss 
dd      14  !Z   sr5 - byte
EQI    A, byte     01110111 
dddddddd           7* 
Z   A - byte
EQI    r, byte     01110100 
01111rrr  dd      11   Z  
r - byte
EQI    sr5, byte   01100100  s1111sss 
dd      14   Z   sr5 - byte
ONI    A, byte     01000111 
dddddddd           7*
!Z   A & byte
ONI    r, byte     01110100 
01001rrr  dd      11  !Z  
r & byte
ONI    sr5, byte   01100100  s1001sss 
dd      14  !Z   sr5 & byte
OFFI   A, byte     01010111 
dddddddd           7* 
Z   A & byte
OFFI   r, byte     01110100 
01011rrr  dd      11   Z  
r & byte
OFFI   sr5, byte   01100100  s1011sss 
dd      14   Z   sr5 &
byte
イミディエートデータ演算命令は数が多いように見えますが、同一ニーモニックに3種類の命令パターンが属しているだけです。それはディスティネーションの違いで、Aレジスタになっているものと一般の8
bitレジスタになっているものと特殊レジスタの3パターンです。一般の8 bitレジスタの中にはAレジスタも含まれますが、Aレジスタがディスティネーションとなる頻度は高いだろうということで他のパターンより短い2
Byte命令のコードが割り当てられていますね。一般のレジスタおよび特殊レジスタがディスティネーションの場合には3
Byte命令になります。命令長が長くなりがちで使いにくいかといえばそんなことはなくて、Aレジスタに影響を与えずにレジスタ計算を行ったり比較を行ったりできるわけですから、結構便利です。しかも、I/Oポートの複数ビットを同時に変化させたり、あるいはテストもできるわけで、一度Aレジスタに持ってきてから演算を行って出力し直すような手間を考えると、わずか3
Byteでスクラッチパッドレジスタにも影響を与えずにI/O操作ができるのは、頻繁にI/O操作を行う組み込み用マイクロプロセッサの実際の応用にはとても有利です。
次はワーキングレジスタ用演算命令です。
    命令         
第1バイト 第2バイト 第3 第4 state skip 動作
ADDW   wa         
01110100  11000000  ww      14      
A <- A + (V.wa)
ADCW   wa         
01110100  11010000  ww      14      
A <- A + (V.wa) + CY
ADDNCW wa         
01110100  10100000  ww      14 
!CY  A <- A + (V.wa)
SUBW   wa         
01110100  11100000  ww      14      
A <- A - (V.wa)
SBBW   wa         
01110100  11110000  ww      14      
A <- A - (V.wa) - CY
SUBNBW wa         
01110100  10110000  ww      14 
!CY  A <- A - (V.wa)
ANAW   wa         
01110100  10001000  ww      14      
A <- A & (V.wa)
ORAW   wa         
01110100  10011000  ww      14      
A <- A | (V.wa)
XRAW   wa         
01110100  10010000  ww      14      
A <- A ^ (V.wa)
GTAW   wa         
01110100  10101000  ww      14 
!CY  A - (V.wa) - 1
LTAW   wa         
01110100  10111000  ww      14  
CY  A - (V.wa)
NEAW   wa         
01110100  11101000  ww      14 
!Z   A - (V.wa)
EQAW   wa         
01110100  11111000  ww      14  
Z   A - (V.wa)
ONAW   wa         
01110100  11001000  ww      14 
!Z   A & (V.wa)
OFFAW  wa         
01110100  11011000  ww      14  
Z   A & (V.wa)
ANIW   wa, byte    00000101  wwwwwwww 
dd      19*      (V.wa)
<- (V.wa) & byte
ORIW   wa, byte    00010101  wwwwwwww 
dd      19*      (V.wa)
<- (V.wa) | byte
GTIW   wa, byte    00100101  wwwwwwww 
dd      13* !CY  (V.wa) - byte - 1
LTIW   wa, byte    00110101  wwwwwwww 
dd      13*  CY  (V.wa) - byte
NEIW   wa, byte    01100101  wwwwwwww 
dd      13* !Z   (V.wa) - byte
EQIW   wa, byte    01110101  wwwwwwww 
dd      13*  Z   (V.wa) - byte
ONIW   wa, byte    01000101  wwwwwwww 
dd      13* !Z   (V.wa) & byte
OFFIW  wa, byte    01010101  wwwwwwww 
dd      13*  Z   (V.wa) & byte
Aレジスタとワーキングレジスタ間の演算は、Aレジスタがディスティネーションとなっています。標準的な15種類の演算が用意されています。
イミディエート演算の場合には、当然ながらワーキングレジスタがディスティネーションになります。こちらはビット演算2種類と比較6種類の合計8命令だけが使えるようになっています。イミディエートでも3
Byte命令にするために、オペコードを1 Byteに限定したので15種類を割り当てることができなかったのかもしれません。
uCOM-87アーキテクチャでは、8 bitデータの記憶場所の階層をCPUにもっとも近い方から並べてみると、Aレジスタ、その他のスクラッチパッドレジスタ、ワーキングレジスタ、一般のメモリという順になります。先に出てきた方が演算に便利でしかも高速に扱える傾向があります。つまり、ワーキングレジスタはスクラッチパッドレジスタにも納めきれなかったデータの置き場なわけで、それを考えると適切な演算命令が選択されて、それらしい命令長になっているようにも思われます。なお、ワーキングレジスタは後述の増減命令のオペランドとなることもできて、ループカウンタをスクラッチパッドレジスタに納めきれない場面でも、その代わりに使えるように配慮されています。
次は16 bit演算命令と乗除算命令をまとめて紹介します。
    命令         
第1バイト 第2バイト 第3 第4 state skip 動作
EADD   EA, r2      01110000 
110000rr          11      
EA <- EA + r2
DADD   EA, rp3     01110100 
110001pp          11      
EA <- EA + rp3
DADC   EA, rp3     01110100 
110101pp          11      
EA <- EA + rp3 + CY
DADDNC EA, rp3     01110100  101001pp         
11  !CY  EA <- EA + rp3
ESUB   EA, r2      01110000 
011000rr          11      
EA <- EA - r2
DSUB   EA, rp3     01110100 
111001pp          11      
EA <- EA - rp3
DSBB   EA, rp3     01110100 
111101pp          11      
EA <- EA - rp3 - CY
DSUBNB EA, rp3     01110100  101101pp         
11  !CY  EA <- EA - rp3
DAN    EA, rp3     01110100 
100011pp          11      
EA <- EA & rp3
DOR    EA, rp3     01110100 
100111pp          11      
EA <- EA | rp3
DXR    EA, rp3     01110100 
100101pp          11      
EA <- EA ^ rp3
DGT    EA, rp3     01110100 
101011pp          11 
!CY  EA - rp3 - 1
DLT    EA, rp3     01110100 
101111pp          11  
CY  EA - rp3
DNE    EA, rp3     01110100 
111011pp          11 
!Z   EA - rp3
DEQ    EA, rp3     01110100 
111111pp          11  
Z   EA - rp3
DON    EA, rp3     01110100 
110011pp          11 
!Z   EA & rp3
DOFF   EA, rp3     01110100 
110111pp          11  
Z   EA & rp3
MUL    r2         
01001000  001011rr         
32       EA <- A * r2
DIV    r2         
01001000  001111rr         
59       EA <- EA / r2, r2 <- EA %
r2
以上の命令のディスティネーションはすべてEAレジスタに固定されています。
EADD命令とESUB命令はソースが8 bitレジスタになっています。16 bitと8
bitの加減算が必要な場面というのは案外多くて、まぁ8 bit同士の加減算のあとにキャリーの分だけ上位バイトをインクリメントないしデクリメントすればいいんで、それほど手間ではないのも確かですが、最初から用意されている分にはありがたいですね。ただし、80系の命令体系だと16
bitと8 bitの加減算を表操作のようなアドレス計算に使うことが多かったのですが、そのようなものについてはuPD7807では専用命令やアドレッシングモードとして用意されていますから、案外と使用頻度は低いかもしれません。
それ以外の、Dで始まる命令が16 bit演算で、8 bit演算に許されている15種類と同じ演算や比較操作が可能になっています。やはりディスティネーションがEAレジスタで、ソースの方はBC,
DE, HLのレジスタペアです。さすがにEAがソース側になる命令とか、イミディエート演算が可能な16
bit演算命令は用意されていませんが、レジスタレジスタ間演算に限られていても8
bit演算と同じ15種類の命令が用意されているのは強力です。すぐ後に出てきますが、このほかに16
bit演算の一種としてレジスタペアに対するインクリメントとデクリメントが用意されています。したがって、EAに上限(下限)を設定しておいて、レジスタペアをインクリメント(デクリメント)しながらEAと比較するパターンで16
bitのループカウンタも簡単に扱えます。
MUL命令とDIV命令が乗除算命令で、8 bit×8 bitの演算と、16 bit/8 bitの演算が許されています。さすがに他の命令とは別格の時間が必要となっていますね。
次は増減命令です。
    命令         
第1バイト 第2バイト 第3 第4 state skip 動作
INR    r2         
010000rr                    
4   CY  r2 <- r2 + 1
INRW   wa         
00100000  wwwwwwww         
16*  CY  (V.wa) <- (V.wa) + 1
INX    rp         
00pp0010                    
7       rp <- rp + 1
INX    EA         
10101000                    
7       EA <- EA + 1
DCR    r2         
010100rr                    
4   CY  r2 <- r2 - 1
DCRW   wa         
00110000  wwwwwwww         
16*  CY  (V.wa) <- (V.wa) - 1
DCX    rp         
00pp0011                    
7       rp <- rp - 1
DCX    EA         
10101001                    
7       EA <- EA - 1
ここで特徴的なのが、8 bitの増減命令であるINR, INRW, DCR, DCRW命令がスキップ条件付きということと、その中でもレジスタオペランドのINR,
DCR命令がオペランドにr2をとるということです。
オペランドがr2であることから、A, B, Cレジスタしかインクリメント・デクリメントできません。D,
E, H, Lレジスタは1 Byte命令でインクリメントなどを行えないのです。ずいぶんと思いきった命令体系にしたものです。考えてみれば、uCOM-87アーキテクチャではD,
E, H, LレジスタはそれぞれDE, HLレジスタペアとして使われる場合が多くて、その際のループカウンタにはB,
Cレジスタのどちらかというのがほとんどでしょう。しかもループカウンタとして用いるのなら、ついでにスキップ動作も可能な方が便利という判断なのでしょう。
仮にスキップ動作が邪魔な場合、スキップされるかもしれない命令にNOPを指定すれば良いわけですが、対象がAレジスタの場合にはADI
A, 1などの命令の方が1ステート分だけ高速です。B, Cレジスタの場合には、ADI
B, 1などよりINR B; NOPという形式の方が命令長も短く高速になります。
16 bitの増減命令のINX命令とDCX命令はオペランドにSP, BC, DE, HL, EAの16
bitレジスタをとります。こちらはスキップ動作を行いません。16 bit分の計数を行うようなことを考えているのでしょうか。あるいは組み込み用のシングルチップであるuPD7807だと内蔵RWMが256
Byteですから、RWM上のデータをスキャンしながら何かの操作を行うようなループでも、8
bitのループカウンタで間に合って16 bitのループカウンタが必要となる場面がほとんどないと想定されているのかもしれません。なお、メモリ上に並べられたデータの操作の場合には、INX,
DCX命令を使ってポインタのインクリメントやデクリメントを行うより、オートインクリメントやオートデクリメントのアドレッシングモードを活用した方が便利です。
uPD7807系列に特有のビット操作命令には次のようなものがあります。
     命令       
第1バイト 第2バイト 第3 第4 state skip 動作
MOV    CY, bit     01011111 
bbbbbbbb          10*     
CY <- (bit)
MOV    bit, CY     01011010 
bbbbbbbb          13*     
(bit) <- CY
AND    CY, bit     01010001 
bbbbbbbb          10*     
CY <- CY & (bit)
OR     CY, bit     01011100 
bbbbbbbb          10*     
CY <- CY | (bit)
XOR    CY, bit     01011110 
bbbbbbbb          10*     
CY <- CY ^ (bit)
SETB   bit        
01011000  bbbbbbbb         
13*      (bit) <- 1
CLR    bit        
01011011  bbbbbbbb         
13*      (bit) <- 0
NOT    bit        
01011001  bbbbbbbb         
13*      (bit) <- !(bit)
SK     bit        
01011101  bbbbbbbb         
10*  (b) skip if (bit) = 1
SKN    bit        
01010000  bbbbbbbb         
10* !(b) skip if (bit) = 0
CYをアキュムレータ代わりにしたロード、セーブと論理演算を行えるほか、メモリ上のビットに対する単項演算も実行できます。また、メモリ上の特定のビットに応じて条件分岐を行うことも可能になっています。CYに対する単項演算が不足しているように見えますが、それはこの直後に出てくるその他の演算命令のグループに納められています。CY操作はビット操作命令を持たないuCOM-87にも必要な命令ですから。
これらの命令のオペランドに必要なbitというのは、ビットアドレスを表します。ビットアドレスは8
bitの整数で表現され、そのMSBによってメモリビットアドレスとハードウェアビットアドレスの2種類にわかれます。メモリビットアドレスはワーキングレジスタの先頭16
Byteに含まれる128 bitの特定のビットを表します。ハードウェアビットアドレスはsr5に含まれる特殊レジスタのビットを表します。
具体的に、まずメモリビットアドレスの場合を考えます。対象メモリアドレス範囲はワーキングレジスタの先頭16
Byteですから、上位アドレスがVレジスタの内容、下位アドレス8 bitの内の上位4
bitが0、下位4 bitが0から0FHとなります。この下位4 bitの部分を3 bit左にシフトして、シフトした後にできた下位3
bitの空き部分にビット番号を格納したものがビットアドレスとなります。ビット番号はLSBを0、MSBを7とした番号です。ビットアドレスのMSBは0になります。たとえば、(V.03H)のMSBのビットアドレスは、1FHとなります。
ハードウェアビットアドレスは次の表のように定義されています。
| レジスタ名 | bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 | 
| PA | 087H | 086H | 085H | 084H | 083H | 082H | 081H | 080H | 
| PB | 08FH | 08EH | 08DH | 08CH | 08BH | 08AH | 089H | 088H | 
| PC | 097H | 096H | 095H | 094H | 093H | 092H | 091H | 090H | 
| PD | 09FH | 09EH | 09DH | 09CH | 09BH | 09AH | 099H | 098H | 
| -- | - | - | - | - | - | - | - | - | 
| PF | 0AFH | 0AEH | 0ADH | 0ACH | 0ABH | 0AAH | 0A9H | 0A8H | 
| MKH | 0B7H | 0B6H | 0B5H | 0B4H | 0B3H | 0B2H | 0B1H | 0B0H | 
| MKL | 0BFH | 0BEH | 0BDH | 0BCH | 0BBH | 0BAH | 0B9H | 0B8H | 
| -- | - | - | - | - | - | - | - | - | 
| SMH | 0CFH | 0CEH | 0CDH | 0CCH | 0CBH | 0CAH | 0C9H | 0C8H | 
| -- | - | - | - | - | - | - | - | - | 
| EOM | 0DFH | 0DEH | 0DDH | 0DCH | 0DBH | 0DAH | 0D9H | 0D8H | 
| -- | - | - | - | - | - | - | - | - | 
| TMM | 0EFH | 0EEH | 0EDH | 0ECH | 0EBH | 0EAH | 0E9H | 0E8H | 
| PT | 0F7H | 0F6H | 0F5H | 0F4H | 0F3H | 0F2H | 0F1H | 0F0H | 
| -- | - | - | - | - | - | - | - | - | 
次はその他の演算命令です。
    命令         
第1バイト 第2バイト 第3 第4 state skip 動作
DAA               
01100001                    
4       BCD補正
STC               
01001000  00101011          
8       CY <- 1
CLC               
01001000  00101010          
8       CY <- 0
CMC               
01001000  10101010          
8       CY <- !CY
NEGA              
01001000  00111010          
8       A <- !A + 1
DAA命令は加算命令の後でAレジスタの内容をBCD補正するものです。STC, CLC,
CMC命令はCYフラグの操作を行います。サブルーチンから返すエラーフラグの処理とか、ビット操作命令の補助として使うことができます。NEGA命令はAレジスタの内容を符号付き2進数とした場合の符号反転命令です。
ローテーション・シフト命令には次のようなものがあります。
    命令         
第1バイト 第2バイト 第3 第4 state skip 動作
RLD               
01001000  00111000         
17       Rotate Left Digit
RRD               
01001000  00111001         
17       Rotate Right Digit
RLL    r2         
01001000  001101rr          
8       r2.(n+1) <- r2.n, r2.0 <- CY,
CY <- r2.7
RLR    r2         
01001000  001100rr          
8       r2.(n-1) <- r2.n, r2.7 <- CY,
CY <- r2.0
SLL    r2         
01001000  001001rr          
8       r2.(n+1) <- r2.n, r2.0 <- 0,
CY <- r2.7
SLR    r2         
01001000  001000rr          
8       r2.(n+1) <- r2.n, r2.0 <- 0,
CY <- r2.7
SLLC   r2         
01001000  000001rr          
8   CY  r2.(n+1) <- r2.n, r2.0 <- 0, CY <- r2.7
SLRC   r2         
01001000  000000rr          
8   CY  r2.(n+1) <- r2.n, r2.0 <- 0, CY <- r2.7
DRLL   EA         
01001000  10110100          
8       EA.(n+1) <- EA.n, EA.0 <- CY,
CY <- EA.7
DRLR   EA         
01001000  10110000          
8       EA.(n-1) <- EA.n, EA.7 <- CY,
CY <- EA.0
DSLL   EA         
01001000  10100100          
8       EA.(n+1) <- EA.n, EA.0 <- 0,
CY <- EA.7
DSLR   EA         
01001000  10100000          
8       EA.(n+1) <- EA.n, EA.0 <- 0,
CY <- EA.7
RLD, RRD命令はZ80 CPUでおなじみのBCD一桁分、4 bitをAレジスタの下位4
bitと(HL)の間でローテートさせる命令です。
RLL, RLR, SLL, SLRは、ごく一般的なローテート、シフト命令です。SLL命令とSLR命令にスキップ条件を付けたのがSLLC命令とSLRC命令で、RLL,
RLR, SLL, SLR命令を16 bitに拡張したのがDRLL, DRLR, DSLL, DSLR命令です。8
bitオペランドの命令はr2が対象ですが、16 bitオペランドの場合にはEAレジスタに固定されています。EAだけでなくレジスタペアを指定できたらと思うこともありますが、加減算などの他の16
bit演算命令もディスティネーションがEAレジスタに限られていますから、これだけ拡張してもしかたないかもしれません。
ジャンプ、コール、リターン命令をまとめて表にしました。
    命令         
第1バイト 第2バイト 第3 第4 state skip 動作
JMP    addr       
01010100  aaaaaaal  ah      10*     
PC <- addr
JB                
00100001                    
4       PC <- BC
JR     addr       
11jjjjjj                   
10       PC <- PC + 1 + jjjjjj
JRE    addr       
0100111j  jjjjjjjj         
10*      PC <- PC + 2 + jjjjjjjjj
JEA               
01001000  00101001          
8       PC <- EA
CALL   addr       
01000000  aaaaaaal  ah      16*     
push PC + 3, PC <- addr
CALB              
01001000  00101001         
17       push PC + 2, PC <- BC
CALF   addr       
01111jjj  jjjjjjjj         
13*      push PC + 2, PC <- 00001jjjjjjjjjjjB
CALT   addr       
100ttttt                   
16       push PC + 1, PC <- (80H + 2*ttttt)
SOFTI             
01110010                   
16       push PSW, push PC + 1, PC <-
0060H
RET               
10111000                   
10       pop PC
RETS              
10111001                   
10   1   pop PC, skip
RETI              
01100010                   
13       pop PC, pop PSW
JMP命令は16 bit絶対アドレスをオペランドに持ち任意のアドレスにジャンプ可能な3
Byte命令です。JB命令はBCレジスタペアで示されるアドレスにジャンプする高速な1
Byte命令です。
JR命令は1 Byteの相対ジャンプ命令です。そのため、オフセットはjjjjjjで表されているように6
bitしかありません。ジャンプ範囲は限られていても、入力ポートの状態が変換するのを待つようなタイトなループでは有用です。しかし、このJR命令のために命令の第1バイトで使用可能な256パターンのうちの64パターンを消費しているため、他の演算命令が2
Byte長中心になってしまうことにもなっています。
JRE命令が標準的な2 Byteの相対ジャンプ命令ですが、オフセットが9 bit許されるようになっています。他の標準的な8
bitマイクロプロセッサより1 bit分だけお得なオフセットですね。JR, JRE命令の両者とも、命令コードのバイト数に関係なくJMP命令と同じ10ステートの実行時間を必要とします。
JEA命令はJB命令のEAレジスタ版で、EAレジスタの新設とともに追加された命令です。
CALL命令は標準的な3 Byte長のサブルーチン呼び出し命令で、CALB命令はJB命令と同様のBCレジスタペアによる呼び出し命令です。しかしCALB命令のほうがCALL命令より実行時間が長くかかるのが気になりますが。
CALF命令は少々変わっていて、2 Byte命令ですが下位11 bitのアドレスを命令コード中に持っていて、上位5
bitが00001に固定されています。つまり0800Hから0FFFHの間の2 KByteの範囲のサブルーチンを呼びだすことができる命令です。この範囲内に配置されたサブルーチンなら、CALL命令より1
Byteだけ短いコードで、しかも3ステート短い時間で呼び出せます。
もっと短いバイト数でサブルーチン呼び出しを行うのがCALT命令で、5 bitのベクタ番号を使用します。0080Hから始まる2
Byte単位でサブルーチンアドレスを登録したベクタテーブルのインデックスがベクタ番号となります。したがって、32種類のサブルーチンに限っては1
Byte長のコードで呼びだすことが可能です。
SOFTI命令はソフトウェア割り込みを発生させるもので、普通のサブルーチン呼び出しとはPSWがスタックに保存される点が異なります。スキップ条件に関してはSOFTI命令は特殊で、絶対にスキップされません。SOFTI命令の次に配置された命令がスキップされます。
RET命令はCALL, CALB, CALF, CALT命令で呼び出されたサブルーチンから戻るための命令です。
RETS命令は少々変わったRET命令で、戻った直後の命令をスキップします。つまり、たとえばCALL命令であるサブルーチンを呼び出したとき、そのサブルーチンからRETS命令で戻ると、CALL命令の次の命令がスキップされます。RET命令とRETS命令をうまく使うと、たとえばサブルーチンが正常終了したときにはRETS命令で戻り、エラーが生じたときにはRET命令で戻るようにすれば、CALL命令の直後にエラー処理用の命令(普通はエラー対処ルーチンへのジャンプ命令でしょう)を配置することで、エラー処理がスマートになるかもしれません。その他にも、たとえば文字コードがアルファベットならスキップするサブルーチンなど、なにかを判定するサブルーチンを書きやすくなります。
RETI命令は割り込みサービスルーチンからの復帰用の命令で、PSWも復帰するほか、割り込みマスクや割込み制御回路の操作も同時に行います。
スキップ命令には次の4種類が含まれます。
    命令         
第1バイト 第2バイト 第3 第4 state skip 動作
SK     f          
01001000  00001fff          
8   f   skip if f = 1
SKN    f          
01001000  00011fff          
8  !f   skip if f = 0
SKIT   irf        
01001000  010fffff          
8   irf skip if irf = 1, then reset irf
SKNIT  irf        
01001000  011fffff          
8  !irf skip if irf = 0, or reset irf
これらの命令は指定されたフラグの状態によってスキップを行うものです。SK命令とSKN命令はプロセッサ内部の演算に関係したCY,
HC, Zフラグの判定を行います。HCフラグの判定ができるのは珍しい機能ですね。減算のBCD補正を比較的短いコードで実現できそうです。
SKIT命令とSKNIT命令は割り込みや特定のI/O機能に関係したフラグについて判定するものです。条件判定とともに、その対象フラグが1にセットされている場合には、リセットも同時に行います。SK,
SKN命令はSKフラグに影響を与えますが、CY, HC, Zフラグの状態を変更することはありません。
最後にCPU制御命令を示します。
    命令         
第1バイト 第2バイト 第3 第4 state skip 動作
NOP               
00000000                    
4       no operation
EI                
10101010                    
4       enable interrupt
DI                
10111010                    
4       disable interrupt
HLT               
01001000  00111011         
11       halt
NOP命令は単にプログラムカウンタを1進めるだけの命令ですが、すべて0のビットパターンが割り当てられています。
EIとDIは割り込み許可と禁止を行う命令で、HLT命令はプログラム実行を停止する命令です。
これまでの命令表のstateの項目に*印が記入されている命令がありました。これは命令がスキップされる場合の例外規定を意味しています。一般に、命令がスキップされる場合には命令のデコードまでは行われますが、実際の処理は行われません。そのため、表に記入したステート数と同じか短いステート数を消費することになります。で、実際のスキップ時に必要となるステート数を表にして示します。
| 命令種別 | ステート数 | 
| 1 Byte命令 | 4 | 
| 2 Byte命令*付き | 7 | 
| 2 Byte命令 | 8 | 
| 3 Byte命令*付き | 10 | 
| 3 Byte命令 | 11 | 
| 4 Byte命令 | 14 | 
以上の命令表に含まれる命令コードの中に埋め込まれているレジスタやオペランド指定ビットを表にして説明します。
8 bit幅のレジスタオペランドにはr, r1, r2の3種類のグループがあります。それぞれ、表の右半分の3列に対応するレジスタ名が記入されています。r2には3種類のレジスタしか含まれません。
| R2 | R1 | R0 | r | r1 | r2 | 
| 0 | 0 | 0 | V | EAH | - | 
| 0 | 0 | 1 | A | EAL | A | 
| 0 | 1 | 0 | B | B | B | 
| 0 | 1 | 1 | C | C | C | 
| 1 | 0 | 0 | D | D | - | 
| 1 | 0 | 1 | E | E | - | 
| 1 | 1 | 0 | H | H | - | 
| 1 | 1 | 1 | L | L | - | 
I/Oポート用レジスタなどを含む特殊レジスタオペランドについてはsr, sr1, sr2, sr3, sr4, sr5の6種類のグループがあります。そのうちのsr, sr1, sr2, sr5については、次の表に示すレジスタを含み、S5 - S0のビットパターンで命令に表現されます。指定されているレジスタが書き込み専用か読み出し専用かなどによって異なる扱いを受けているわけです。
| S5 | S4 | S3 | S2 | S1 | S0 | sr | sr1 | sr2 | sr5 | レジスタの役割 | 
| 0 | 0 | 0 | 0 | 0 | 0 | PA | PA | PA | PA | port A, ポートAデータ | 
| 0 | 0 | 0 | 0 | 0 | 1 | PB | PB | PB | PB | port B, ポートBデータ | 
| 0 | 0 | 0 | 0 | 1 | 0 | PC | PC | PC | PC | port C, ポートCデータ | 
| 0 | 0 | 0 | 0 | 1 | 1 | PD | PD | PD | PD | port D, ポートDデータ | 
| 0 | 0 | 0 | 1 | 0 | 1 | PF | PF | PF | PF | port F, ポートEデータ | 
| 0 | 0 | 0 | 1 | 1 | 0 | MKH | MKH | MKH | MKH | mask high, 割り込みマスクレジスタ上位 | 
| 0 | 0 | 0 | 1 | 1 | 1 | MKL | MKL | MKL | MKL | mask low, 割り込みマスクレジスタ下位 | 
| 0 | 0 | 1 | 0 | 0 | 1 | SMH | SMH | SMH | SMH | serial mode high, シリアルモードレジスタ上位 | 
| 0 | 0 | 1 | 0 | 1 | 0 | SML | - | - | - | serial mode low, シリアルモードレジスタ下位 | 
| 0 | 0 | 1 | 0 | 1 | 1 | EOM | EOM | EOM | EOM | timer/event counter output mode, タイマ・イベントカウンタ出力モードレジスタ | 
| 0 | 0 | 1 | 1 | 0 | 0 | ETMM | - | - | - | timer/event counter mode, タイマ・イベントカウンタモードレジスタ | 
| 0 | 0 | 1 | 1 | 0 | 1 | TMM | TMM | TMM | TMM | timer mode, タイマモードレジスタ | 
| 0 | 0 | 1 | 1 | 1 | 0 | - | PT | - | PT | port T, コンパレータ入力ポートTデータ | 
| 0 | 1 | 0 | 0 | 0 | 0 | MM | - | - | - | memory mapping, メモリ割り付けレジスタ | 
| 0 | 1 | 0 | 0 | 0 | 1 | MCC | - | - | - | mode control C, モードコントロールC | 
| 0 | 1 | 0 | 0 | 1 | 0 | MA | - | - | - | mode A, ポートAモード | 
| 0 | 1 | 0 | 0 | 1 | 1 | MB | - | - | - | mode B, ポートBモード | 
| 0 | 1 | 0 | 1 | 0 | 0 | MC | - | - | - | mode C, ポートCモード | 
| 0 | 1 | 0 | 1 | 1 | 1 | MF | - | - | - | mode F, ポートFモード | 
| 0 | 1 | 1 | 0 | 0 | 0 | TXB | - | - | - | Tx buffer, シリアル送信バッファ | 
| 0 | 1 | 1 | 0 | 0 | 1 | - | RXB | - | - | Rx buffer, シリアル受信バッファ | 
| 0 | 1 | 1 | 0 | 1 | 0 | TM0 | - | - | - | timer register 0, タイマレジスタ0 | 
| 0 | 1 | 1 | 0 | 1 | 1 | TM1 | - | - | - | timer register 1, タイマレジスタ1 | 
| 1 | 0 | 0 | 1 | 0 | 0 | WDM | WDM | - | - | watchdog timer mode, ウォッチドッグタイマモードレジスタ | 
| 1 | 0 | 0 | 1 | 0 | 1 | MT | - | - | - | mode T, ポートTモード | 
| U | sr3 | レジスタの役割 | 
| 0 | ETM0 | timer/event counter register 0, タイマ・イベントカウンタレジスタ0 | 
| 1 | ETM1 | timer/event counter register 1, タイマ・イベントカウンタレジスタ1 | 
| V1 | V0 | sr4 | レジスタの役割 | 
| 0 | 0 | ECNT | timer/event counter upcounter, タイマ・イベントカウンタのアップカウンタ | 
| 0 | 1 | ECPT0 | timer/event counter capture 0, タイマ・イベントカウンタのキャプチャレジスタ0 | 
| 1 | 0 | ECPT1 | timer/event counter capture 1, タイマ・イベントカウンタのキャプチャレジスタ1 | 
レジスタペアの指定には、rp, rp1, rp2, rp3の4種類のグループがあり、それぞれ次の表に記入されているレジスタペアを含みます。この表でSP / SPと記載されている欄は、前の方がアセンブリ言語表記で後の方が実際のレジスタペアを表します。つまりB / BCの項はアセンブリ言語でBと書くことになっていて、実際の操作対象がBCレジスタペアであることを示します。
| P2 | P1 | P0 | rp | rp1 | rp2 | rp3 | 
| 0 | 0 | 0 | SP / SP | V / VA | SP / SP | - | 
| 0 | 0 | 1 | B / BC | B / BC | B / BC | B / BC | 
| 0 | 1 | 0 | D / DE | D / DE | D / DE | D / DE | 
| 0 | 1 | 1 | H / HL | H / HL | H / HL | H / HL | 
| 1 | 0 | 0 | - | EA / EA | EA / EA | - | 
アドレッシングモードの指定にはrpa, rpa1, rpa2, rpa3の3種類があります。rpa3は16 bitオペランド用のアドレッシングモードで、それ以外は8 bitオペランド用のアドレッシングモードです。ここでも/の左がアセンブリ言語表記で、/の右が実際の動作を意味します。ZilogのZ80 CPU的表記に慣れると省略し過ぎで変な表記と感じるかもしれませんが、Intel式の8080アセンブリ言語表記はこのようなものでしたから。
| A3 | A2 | A1 | A0 | rpa | rpa1 | rpa2 | rpa3 | 動作 | 
| 0 | 0 | 0 | 0 | - | - | - | - | - | 
| 0 | 0 | 0 | 1 | B / (BC) | B / (BC) | B / (BC) | - | BCポインタ | 
| 0 | 0 | 1 | 0 | D / (DE) | D / (DE) | D / (DE) | D / (DE) | DEポインタ | 
| 0 | 0 | 1 | 1 | H / (HL) | H / (HL) | H / (HL) | H / (HL) | HLポインタ | 
| 0 | 1 | 0 | 0 | D+ / (DE)+ | - | D+ / (DE)+ | D++ / (DE)++ | DEオートインクリメント | 
| 0 | 1 | 0 | 1 | H+ / (HL)+ | - | H+ / (HL)+ | H++ / (HL)++ | HLオートインクリメント | 
| 0 | 1 | 1 | 0 | D- / (DE)- | - | D- / (DE-) | - | DEオートデクリメント | 
| 0 | 1 | 1 | 1 | H- / (HL)- | - | H- / (HL)- | - | HLオートデクリメント | 
| 1 | 0 | 1 | 1 | - | - | D+byte / (DE+byte) | D+byte / (DE+byte) | DEベース定数オフセット | 
| 1 | 1 | 0 | 0 | - | - | H+A / (HL+A) | H+A / (HL+A) | HLベースAオフセット | 
| 1 | 1 | 0 | 1 | - | - | H+B / (HL+B) | H+B / (HL+B) | HLベースBオフセット | 
| 1 | 1 | 1 | 0 | - | - | H+EA / (HL+EA) | H+EA / (HL+EA) | HLベースEAオフセット | 
| 1 | 1 | 1 | 1 | - | - | H+byte / (HL+byte) | H+byte / (HL+byte) | HLベース定数オフセット | 
SK命令とSKN命令で使われるフラグオペランドfにはPSW中に含まれる次の3種類があります。
| F2 | F1 | F0 | f | 
| 0 | 0 | 0 | - | 
| 0 | 1 | 0 | CY | 
| 0 | 1 | 1 | HC | 
| 1 | 0 | 0 | Z | 
| F4 | F3 | F2 | F1 | F0 | irf | ビット略称 | フラグの役割 | 
| 0 | 0 | 0 | 0 | 0 | FNMI | INTFNMI | NMI*端子の状態を表す | 
| 0 | 0 | 0 | 0 | 1 | FT0 | INTFT0 | タイマ0のコンパレータ一致信号でセット | 
| 0 | 0 | 0 | 1 | 0 | FT1 | INTFT1 | タイマ1のコンパレータ一致信号でセット | 
| 0 | 0 | 0 | 1 | 1 | F1 | INTF1 | INT1端子への立ち上がりエッジ入力でセット | 
| 0 | 0 | 1 | 0 | 0 | F2 | INTF2 | INT2*端子への立ち下がりエッジ入力でセット | 
| 0 | 0 | 1 | 0 | 1 | FE0 | INTFE0 | タイマ・イベントカウンタのECNTとETM0レジスタ一致でセット | 
| 0 | 0 | 1 | 1 | 0 | FE1 | INTFE1 | タイマ・イベントカウンタのECNTとETM1レジスタ一致でセット | 
| 0 | 0 | 1 | 1 | 1 | FEIN | INTFEIN | タイマ・イベントカウンタのCI入力かTOの立ち下がりエッジでセット | 
| 0 | 1 | 0 | 0 | 1 | FSR | INTFSR | シリアルの受信バッファフルでセット | 
| 0 | 1 | 0 | 1 | 0 | FST | INTFST | シリアルの送信バッファエンプティでセット | 
| 0 | 1 | 0 | 1 | 1 | ER | ER | シリアルのパリティ、フレーム、オーバーラン受信エラーでセット | 
| 0 | 1 | 1 | 0 | 0 | OV | OV | タイマ・イベントカウンタのECNTオーバーフローでセット | 
| 0 | 1 | 1 | 1 | 1 | IEF2 | IEF2 | NMI受付時に割り込み許可状態ならセット | 
| 1 | 0 | 1 | 0 | 0 | SB | SB | Vdd端子の立ち上がり入力でセット | 
I/O機能の手がかりとして、まずピン配置を次に示します。
   PA0  1     64 Vcc
   PA1  2     63 Vdd
   PA2  3     62 PD7/AD7
   PA3  4     61 PD6/AD6
   PA4  5     60 PD5/AD5
   PA5  6     59 PD4/AD4
   PA6  7     58 PD3/AD3
   PA7  8     57 PD2/AD2
   PB0  9     56 PD1/AD1
   PB1 10     55 PD0/AD0
   PB2 11     54 PF7/AB15
   PB3 12     53 PF6/AB14
   PB4 13     52 PF5/AB13
   PB5 14     51 PF4/AB12
   PB6 15     50 PF3/AB11
   PB7 16     49 PF2/AB10
   PC0 17     48 PF1/AB9
   PC1 18     47 PF0/AB8
   PC2 19     46 ALE
   PC3 20     45 WR*
   PC4 21     44 RD*
   PC5 22     43 HLDA
   PC6 23     42 HOLD
   PC7 24     41 PT7
  NMI* 25     40 PT6
  INT1 26     39 PT5
 MODE1 27     38 PT4
RESET* 28     37 PT3
 MODE0 29     36 PT2
    X2 30     35 PT1
    X1 31     34 PT0
   Vss 32     33 Vth
PA, PB, BC, PD, PFはuPD7808などROM内蔵型では双方向のI/Oポートであり、PTはコンパレータ入力ポートです。ただしuPD7807はPDポートをデータ入出力と下位アドレス出力端子として使用し、PFポートの一部あるいは全部を上位アドレス出力端子として使用します。
NMI*はノンマスカブル割り込みでネガティブエッジトリガの入力信号です。
INT1はポジティブエッジトリガの割り込み入力です。さらにACゼロクロス入力や16
bitタイマ・カウンタのトリガ源としても使えるようになっています。
MODE1とMODE0はuPD7807の外付けメモリ容量を決定する入力端子で、設定によって4
KByte, 16 KByte, 64 KByteのモードになります。それに従って上位アドレス出力端子として使用されるPFポートの信号本数が決まります。つまり4
KByteモードのときにはPF0からPF3までがアドレス出力端子、PF4からPF7までが入出力ポートとして使用され、16
KByteモードのときにはPF0からPF5までがアドレス出力端子でPF6, PF7が入出力ポートになり、64
KByteモードのときにはPF0からPF7まですべてがアドレス出力端子となります。MODE1とMODE0はリセット時に1が入力されていると出力端子としても使われるようですが、詳細はわかりません。設定時には電源に直接接続するのではなく、抵抗を介してプルアップすべきでしょう。uPD7809の場合にはMODE0をLに、MODE1をHに設定します。
RESET*はシステムリセット入力端子で、X1とX2は水晶振動子とコンデンサを接続する端子でクロック信号発生を行います。
VthはPTポート用のスレッショルド電圧設定端子で、PT0 - PT7が各コンパレータの+入力に接続されているのに対し、VthはPT0
- PT7の8個のコンパレータすべての-入力に供給される電圧の設定端子です。ただし、Vthの電圧が直接コンパレータに接続されているわけではなく、16段階のプログラマブルな分圧回路を介して-入力に接続されています。
HOLDとHLDAはDMA用の端子で、RD*, WR*, ALEがIntelの8085と同じ形式のバスインターフェース信号です。ただし、8085と異なりI/Oアドレス空間が存在しませんから、IO/M*信号は存在しません。RD*,
WR*, ALEだけで外部メモリの読み書きを行います。
VccとVssが主電源で5 V単一電源、Vddが32 Byte分の内蔵RWMバックアップ用電源です。
概してピン配置は比較的規則正しく端子が並べられています。
では個別にポートを見ていきます。
PAとPBはビットごとに入出力方向を決められる比較的単純なI/Oポートです。それぞれMODE
Aレジスタ(MA)とMODE Bレジスタ(MB)という制御レジスタを持ち、そのレジスタの1になっているビットが入力端子に、0になっているビットが出力端子に設定されます。リセット時にはすべて1に初期設定されて強制的に入力となります。実際のデータの入出力に用いるレジスタは、それぞれPA,
PBレジスタです。PA, PBレジスタの読み込みで得られるデータのうち、端子が出力に設定されているビットに関しては、前もって各レジスタに書き込まれていた値となり、出力端子の状態を反映しない回路となっています。出力しているはずの状態が得られると言い換えても良いでしょう。
さて、PA, PB, MA, MBといったレジスタは、最初から存在しないI/Oポート空間に割り当てられているわけではなく、特殊レジスタ扱いになっています。そうして、特別な命令でアクセスされます。ただし、PAとMAが同じ種類の命令で操作されるのではなく、MAはMOV
MA, A命令でしか使用できないのに対し、PAはAレジスタと双方向のデータ転送を行えるほか、ビット操作命令でCYフラグとの演算や操作、さらにイミーディアトデータ演算命令でAレジスタに影響を与えずに各ビットのセットやリセット、テストも可能となっています。つまり、普通の使い方ならMA,
MBレジスタはリセット後の初期設定を行うだけか、せいぜい双方向データ通信のために設定しなおすだけなので、Aレジスタからのデータ書き込みだけしかできず、読み込みも含めたそれ以外の操作を省略してしまっています。逆にPAやPBレジスタは入出力のために頻繁にアクセスする必要があるため、広範な演算操作が可能です。慣れるまではどの命令が適用可能かたいへんですが、コード効率は良くなります。特にuCOM-87アーキテクチャではAレジスタの役割が大きいため、Aレジスタに影響を与えずに入出力を効率的に行えるのは便利です。たとえばORI
PA, DATAで、PAと8 bit定数データのDATAのORを行ってPAに書き込むことができ、PAの任意のビットをセットできます。また、ONI
PA, DATAでPAと8 bit定数データDATAのANDを行って、その結果が0でなければスキップを行うという形で、I/Oポートのテストが行えます。どちらの命令も、Aレジスタをはじめとする汎用レジスタは変化しません。何かの機器に組み込まれるコンピュータは、モータや何かの回路のオンオフのためとかセンサの状態確認のため、ビットごとに頻繁に入出力する場面が数多くあります。そんなとき、I/Oへの直接操作がこれだけ簡単に行える命令体系というのは、実用上は便利です。
PCもPAやPBと同じようにビットごとに入出力方向を決められるI/Oポートですが、他の内蔵回路との端子の共用が行われて、少し複雑になっています。MODE
Cレジスタ(MC)とMODE CONTROL Cレジスタ(MCC)の設定により、次のように機能が割り振られます。
 
| 端子 | MCC = 1, MC = X | MCC = 0, MC = 0 | MCC = 0, MC = 1 | 
| PC0 | TxD出力 | 出力 | 入力 | 
| PC1 | RxD入力 | 出力 | 入力 | 
| PC2 | SCK*入出力 | 出力 | 入力 | 
| PC3 | INT2*/TI入力 | 出力 | 入力 | 
| PC4 | TO出力 | 出力 | 入力 | 
| PC5 | CI入力 | 出力 | 入力 | 
| PC6 | CO0出力 | 出力 | 入力 | 
| PC7 | CO1出力 | 出力 | 入力 | 
つまりMCCのあるビットが1になっていると、それに対応するPCの端子がタイマ、カウンタ、シリアル入出力インターフェースなどの内蔵回路の入出力端子になり、0なら一般のポート入出力端子となってPAなどと同じようにMCレジスタとPCレジスタで操作できます。リセット時にはMCCはすべて0クリアされ、MCはすべて1にセットされて、各信号端子は入力ポートモードに初期化されます。例によってPCレジスタだけはデータ転送や演算などで特別扱いされて多くの命令で操作できます。
PDはuPD7807ではデータやアドレスの入出力端子専用に使われていて、I/Oポートとしては使用できません。uPD7809でシングルチップモードで動作させる場合には、MMレジスタの設定によってバイト単位で入出力方向を決められます。PFに関しては、外付けするメモリのサイズに応じて、一部をI/Oポートとして使用することができます。そのような端子に限り、MODE
Fレジスタ(MF)とPFレジスタでPAと同様の入出力操作を行えます。
最後に残ったのがPTで、入力専用のポートです。PT0からPT7までの入力端子のほか、基準電圧設定用の入力端子Vthがあります。PT入力には個別にアナログコンパレータとラッチが用意されていて、アナログコンパレータの+入力にPT端子が接続されています。すべてのアナログコンパレータの-入力には、Vthを分圧したものが接続されています。分圧レベルはMODE
Tレジスタ(MT)の下位4 bitでプログラムでき(MTの上位4 bitは未使用)、Vthの16/16から1/16までの16段階の電圧を与えられるようになっています。アナログコンパレータの出力にはラッチ回路が接続されていて、読み込み時のばたつきによる誤動作を回避しているようです。1回の比較には48ステート必要としますから、端子の新しい状態を読み込めるまで最悪96ステートかかります。12
MHzクロックで24 usですね。基準電圧が8本の入力について共通というところが扱いにくいかもしれませんが、うまく使えば簡易A/Dコンバータ代わりに使えるかもしれません。そのほか、TTLレベルでないロジック信号的なもの、たとえばある種のセンサからの信号を入力する場合なんかには便利です。もちろん基準電圧を1.4
V程度に設定すれば、少々遅いけれども普通のTTLレベル入力としても使えるでしょう。PTレジスタも各ビットのテストなどさまざまな命令を利用できます。
パラレル入出力以外の内蔵I/Oとして、8 bitタイマ2回路、16 bitのタイマ・イベントカウンタ1回路、シリアルインターフェース1回路、あとI/Oではありませんがウオッチドッグタイマ回路が内蔵されています。レジスタの詳細にまで踏み込むと煩雑すぎるし、図を書かなくてはいけないので、簡単に済ませます。
8 bitタイマ回路はアップカウンタで、設定値と一致したらリセットして0からカウントを再び開始するようなプログラマブルなタイマです。設定値と一致したときには割込みを発生させることもできるし、出力フリップフロップを反転させることもでき、そのフリップフロップの出力をPC4/TO出力に出力したり、タイマ・イベントカウンタやウオッチドッグタイマのクロック入力に接続したり、シリアルインターフェースのビットレートクロックとして使用することもできます。ただし、出力フリップフロップはふたつのタイマに共通のものがひとつあるだけで、どちらかのタイマ出力として使用する場合、もう一方のタイマは割り込み発生源にしか使えません。タイマのクロック入力は内部クロック周波数2種類とPC3/TI入力の切り替えですが、一方のタイマだけ、もう一方の一致出力をクロック入力として使用でき、1本の16
bitタイマとして使用できるようにもなっています。
16 bitのタイマ・イベントカウンタ回路は、インターバルタイマ、外部イベントカウンタ、周波数測定、パルス幅測定、プログラマブル矩形波出力、ワンパルス出力の6種類の機能を持つ多目的カウンタです。関係する端子はPC5/CI入力、PC6/CO0出力、PC7/CO1出力の3種類で、CI入力がHになっている時間を測定したり、CO0とCO1に位相差付きのタイミング信号を出力したりできます。
シリアルインターフェースは、調歩同期モード、同期モード、I/Oインターフェースモードの3種類の動作モードがあります。共にビットレートは内部固定クロックを使用することもタイマ出力を使用することも外部から供給することも可能です。調歩同期モードでは、7
bit/8 bitデータの切り替え、パリティの有無と偶奇の設定、1/2ストップビットの設定が可能で、それなりの機能をもっています。同期モードでは、さすがに同期キャラクタの判定までは行ってくれませんが、同期キャラクタを見つけるための1
bit受信単位での割り込みモードも持っていて、ソフトウェア併用で同期確立を行えるようになっています。I/OインターフェースモードはMotorolaでいうSPIのようなインターフェースで、シフトレジスタのようなものをI/Oとして利用できるモードです。同期モードとはビットを送りだす順序がLSBからではなくてMSBからであるところが特に異なります。
Return to IC Collection