標準TTLだけ(!)でCPUをつくろう!(組立てキットです!)
(ホントは74HC、CMOSなんだけど…)
[第97回]
●CALL命令
プログラムを書いていると同じような処理を何回も書かなければいけないときが出てきます。
そんなときにCALL命令を使うとプログラムが短くなり、スッキリとしたわかりやすいプログラムになります。
CALL命令によって呼び出されるプログラムのことをサブプログラムまたはサブルーチンといいます。
これに対して、CALLする側のプログラムをメインプログラム、またはメインルーチンといいます。
サブルーチンの中にもCALL命令があって、別のサブルーチンを呼び出す場合もあります。
普通はサブルーチンはメインルーチンが終わった、その後ろにまとめて書きます。
サブルーチンと普通のプログラムの違いは、その終わり方にあります。
普通のプログラム(メインルーチン)は、最後はSTOPするかブレイクするか、あるいはシステムに戻るか、そうでなければエンドレスである同じ流れを永久に繰り返すようにします。
これに対してサブルーチンの最後は必ず、そのサブルーチンが呼び出された場所(CALL命令の次のアドレス)に戻る命令で終わります。
メインルーチンに戻る命令はRET(return)命令です。
プログラムの最後がRET(return)命令で終わっているのがサブルーチンです。
RET命令については、のちほど説明します。
CALL命令は[第61回]で説明したJMP命令と似ています。
命令コードは
11001101 (16進数ではCD)
です。
アセンブラ表記は
CALL 1234
です。
マシン語命令コードCDに続く2バイトの値が示すメモリアドレスにあるサブルーチンをCALLします。
[第61回]でJMP命令の動作を次のように説明しました。
メモリアドレス0010から続く3バイトが次の値だったとき、
0010 C3
0011 56
0012 78
CPUは0012番地まで読み取ったあと、7856番地にジャンプします(5678番地ではありません)。
8080は16ビット、2バイトの値をメモリに置くときには、若いアドレスに下位バイトが来るように配置します。
ですからCPUは上の例では、まず命令コードのC3を読み、次に続くジャンプ先を指定するアドレスの下位アドレスを先に読み、続いて上位アドレスを読むことになります。
CALL命令もJMP命令と同じ動作をします。
上の例を次のように書き換えたとき、
0010 CD
0011 56
0012 78
CPUはJMP命令と同じように、0012番地まで読み取ったあと、7856番地にジャンプします(5678番地ではありません)。
JMP命令は、指定するアドレスに行ったら、もとには戻ってきません。戻ってくるには、またJMP命令を使わなければなりません。たとえばさきほどのJMP命令のプログラムで、JMP命令が書いてある次のアドレス(0013番地)に戻ってくるためには、行ったさきのプログラムの最後に、JMP 0013(マシン語コードC31300)と書いておかなければなりません。
これに対してCALL命令では、行った先のプログラム(サブルーチン)の最後に書いてあるRET(return)命令で、CALL命令の次のアドレス(上のプログラム例では0013番地)に戻ってくることができます。
RET(return)命令で戻ってくるためには、戻り先のアドレスをどこかに記憶しておかなければなりません。
その戻り先アドレスを記憶するために利用されるのが、[第67回]で説明したスタックです。
CALL命令は、CALL命令が書いてあるアドレスの次のアドレスをスタックに保存してから、指定アドレスにジャンプします(ここがJMP命令と違うところです)。
RET命令は、スタックから戻り先アドレスを取り出すことによって、正しいアドレスに戻ってくることができるのです。
●CALL命令のタイミングチャートです
JMP命令と同じように、まずCALL命令のOPコードに続く2バイトのCALL先のアドレスをWKレジスタ、WKHとWKLに一時的に保存します。
2バイトのCALL先アドレスを読むと、PC(プログラムカウンタ)は、その次のアドレスになります。
そのアドレスが、サブルーチンの実行終了後にRET命令で戻ってくるアドレスですから、このPC(プログラムカウンタ)の値をスタックに保存します。
最後にWKレジスタに一時的に保存しておいた、CALL先アドレスをPC(プログラムカウンタ)に入れることによって、CALL先アドレスにジャンプします。
CALL命令のタイミングチャートでM4の期間(T8、T9の期間)が空いているように見えます。
この期間はアドレスを新しいスタックに保存するために、先にSP(スタックポインタ)を−1しておく必要があるからです。
SPclkパルスを先に出すのであれば、T4〜T7の期間を利用してもよさそうなものなのですが、残念ながらそれはできないのです。
JMP命令と同じように、CALL命令にも条件CALL命令があります。
動作としては条件JMP命令と全く同じです。
条件が成立したときは、通常の無条件CALL命令と同じ動作になります。
条件が成立しないときは、サブルーチンCALLを行わず、すぐ次の命令を実行します。
条件CALL命令は条件が成立したときは、普通のCALL命令と同じ動作をするために、条件CALL命令と無条件CALL命令は同じ回路になっています。
問題は条件が不成立の場合です。
その場合には、CALL命令のタイミングチャートの一番下にあるように、T8のタイミングでMclrパルスを出して、それ以後の動作をキャンセルします。
もしもT4〜T7の期間に、SPclkを出してSP(スタックポインタ)を先に−1してしまうと、そのあとのT8でその先の実行をキャンセルしても、すでにSP(スタックポインタ)の値が変わってしまっているので、それをもとにもどす必要が出てきてしまい、都合が悪いのです。
SPclkをT4〜T7で出さずに、T9の期間に出しているのは以上の理由からです。
WKレジスタ、PC(プログラムカウンタ)、SP(スタックポインタ)は、regRD、regWRとs3−s0、d3−d0によってセレクトされ、読み、書きされます。
s3−s0、d3−d0とレジスタの関係については、「レジスタコード表」([第27回])を参照してください。
●CALL命令の回路図です
回路図の一番下のPCtoReg16は、PC(プログラムカウンタ)の値を、WKレジスタなどに転送するための回路です。
PCtoReg16の回路は、JMP命令の回路図([第62回])にあります。
●条件CALL命令
8080の条件CALL命令は全部で8命令あります。
マシン語コードとニーモニックとで示します。
命令コード | ニーモニック | 意味 |
11111100 | CM adrs | 値が負のときにCALL実行 |
11110100 | CP adrs | 値が正のときにCALL実行 |
11101100 | CPE adrs | 偶数パリティのときにCALL実行 |
11100100 | CPO adrs | 奇数パリティのときにCALL実行 |
11011100 | CC adrs | キャリーがあるときにCALL実行 |
11010100 | CNC adrs | キャリーがないときにCALL実行 |
11001100 | CZ adrs | 結果が0のときにCALL実行 |
11000100 | CNZ adrs | 結果が0でないときにCALL実行 |
adrsは16ビットの値で、条件が成立したときにジャンプする、飛び先のメモリアドレスです。
命令コードの次のメモリアドレスに、ジャンプ先アドレスの下位バイトを置き、さらにその次のメモリアドレスに、ジャンプ先アドレスの上位バイトを置きます(上位バイト・下位バイトの順ではなくて、逆の下位バイト・上位バイトの順です)。
条件が成立するときは普通のCALL命令と同じ動作になりますから、CALL命令の回路図でわかるように、普通のCALL命令と条件CALL命令(CX)は同じ回路になっています。
条件が成立しないときに、T8以後の動作をキャンセルする仕組みについては、条件JMP命令と同じです。
[第63回]でくわしく説明していますので、そちらを参照してください。
2008.10.16upload
前へ
次へ
ホームページトップへ戻る