標準TTLだけ(!)でCPUをつくろう!(組立てキットです!)
(ホントは74HC、CMOSなんだけど…)
[第253回]

●MOV H,Mが誤動作している?

前回、RETキーが正しく機能していないようだ、ということで、その原因を追求してみましたら、MOV H,M命令のところで、Hレジスタに変な値が入ってしまう、というところまで書きました。

MOV A,Mは正しく動作していますし、MOV L,Aもまともです。
なぜかMOV H,Mだけが、おかしいのです。

回路図を調べているうちに、気がつきました。
なんという、おばかなことを!

前回、そのヒントは[第233回]にあります、と書きました。

[第233回]は、8080の内部バス、外部バスとレジスタの関係などについて説明をしています。
そして、それに関連して、「つくるCPU」の外部アドレスバスについて次のように説明しました。

8080では外部アドレスバス出力部に「アドレスラッチ」が使われていますが、この「つくるCPU」ではラッチを使わずレジスタの出力ゲート(74HC244)を介して直接外部アドレスバスにつながっています。

でも、これは間違いでした。
8080のように、やっぱりラッチ回路が必要だったのです。

どういうことなのか、タイミングチャートで説明しましょう。

●MOV r,Mのタイミングチャート

MOV r,Mのタイミングチャートは回路の変更にともなって、[第42回][第45回]にわたって、出てきていますが、それでも今から見れば、「初期」のタイミングチャートです。
ですので、ここであらためて再掲することにします。


MOV r,M命令は、HLレジスタで示されるメモリアドレスのデータをレジスタに書き込む命令です。メモリのデータを読み出すために、外部アドレスバスA0〜A15にはHLレジスタの値が出力されています。

もう、おわかりだと思います。
もしも書き込む相手のレジスタがHレジスタだったとすると、T4のタイミングでデータバスにM(HLレジスタが示すメモリアドレス)からのデータが出力され、そしてregWRがLになると、そのデータバスの値が、Hレジスタに書き込まれます。
すると、Hの値が変わってしまいますから、その瞬間に外部アドレスバスA0〜A15も変化してしまいます。

ところがレジスタ回路に使われている74HC373はregWRが↓のときに値をラッチするのではなくて、Lの期間中、入力が出力に筒抜けになってしまいます。データがラッチされるのは、regWRが↑のときです。

[09.6.19追記ここから]
上の2行の表現が不適切でしたので、追記いたします。
タイミングチャートも差し替えました。

74HC373はG入力がHの期間、入力データが出力に筒抜けになります。
G入力が↓の瞬間に入力データをラッチします。
レジスタの回路(HLレジスタの回路は、[第23回]HLレジスタ回路図を参照してください)の74HC373のG入力には、regWR信号を74HC238で、各レジスタのみをアクセスするHパルスに変換した信号が入ります([第72回]regWR回路)。
ですから上の2行の説明は、少し冗長になりますが、こうなります。

ところがレジスタ回路に使われている74HC373はregWRが↓のとき(G入力が↑のとき)に値をラッチするのではなくて、regWRがLの期間中(G入力がHの期間中)、入力が出力に筒抜けになってしまいます。データがラッチされるのは、regWRが↑のとき(G入力が↓のとき)です。
[09.6.19追記ここまで]

ということは、アドレスバスが変化すると、当然メモリアドレスが変化してしまうため、そこから読み出されるデータバスの値も変化してしまい、そしてそれがまた、Hレジスタの74HC373のところで筒抜けになって、また外部アドレスバスを変化させてしまうという、とんでもない動作をすることになるのです。

ここではMOV H,Mについて説明しましたが、もちろんMOV L,Mについても全く同様の現象となって現れます。

●なぜRETはダメで、RUNはよかったのか?

ところで、今の説明のように、MOV H,Mが正しく機能しないことが、RETキー動作がうまく働かなかった原因だったとすると、同じようにキーコードからジャンプ先アドレスを求めている他のキールーチン、とりわけRUNルーチンが正しく動作しているのは、なぜなのでしょうか?

実はそれにも理由がありました。
もう一度、キーコードからジャンプアドレスを求めるプログラムと、ジャンプテーブルを見てみましょう。


0064 78         MOV A,B
0065 E60F       ANI 0F
0067 0600       MVI B,00
0069 87         ADD A
006A 4F         MOV C,A
006B 217400     LXI H,TABL
006E 09         DAD B
006F 7E         MOV A,M
0070 23         INX H
0071 66         MOV H,M
0072 6F         MOV L,A
0073 E9         PCHL
              ;
0074 CC00     TABL:DW GOTO
0076 F901       DW RESRG
0078 9400       DW ADSET
007A B800       DW ADDCX
007C 9D00       DW ADINX
007E C200       DW MEMW
0080 D500       DW SDATA
0082 0701       DW LDATA
              ;

RUNキーの処理ルーチンは00CCにあります。
ですから、ジャンプテーブルの一番上の2バイトをHLレジスタに入れて、PCHL命令で、00CCにジャンプします。
ここで、MOV H,Mが実行されるときの状況を詳しく見てみます。

006E DAD Bから0070 INX Hの命令の実行によって、HLレジスタはジャンプテーブルのRUN(GOTO)の上位アドレスを示す0075になっています。
そしてMOV H,Mによって、メモリアドレス0075の値、00がデータバスに出力されます。

すると、さきほどの説明によって、このデータバスの値がHレジスタ回路を通して、アドレスバスの上位8ビットにつつぬけになってしまいます。
ところが、幸いにして、つつぬけになるデータの00は、もとからのHレジスタの値(であると同時に外部アドレスバスの上位8ビット)と同じなので、この場合に限っては、正しく動作してしまうのです。

一方RETの場合には、ジャンプ先アドレスが01F9で、その上位アドレスデータをHに入れるときの、MOV H,M命令での、HLレジスタの値は0077になっています。Hレジスタの値は00です。
そしてMOV H,Mの実行の結果Hレジスタに読み込まれる値は01です。
MOV H,M命令の実行の途中で、Mを示すHLレジスタのうちのHレジスタの値が00から01に変わってしまうために、異常な動作をすることになってしまったのです。

●たまたまRETルーチンの上位アドレスが01だったから

そうなんです。
たまたま、RET命令のジャンプ先アドレスの上位8ビットが01で、そのテーブルが置かれているアドレスの上位8ビットの00と異なっていたから、今回の異常動作となって、回路の不備が発見できたのですが、もしも、それが01ではなくて、RUNキーなどと同じ00だったなら、多分そんなこととは全く知らないで、脳天気にも基板を発注してしまっていたことでしょう。

うわあ。

こういうことを経験してしまうと、やっぱり、テストプログラムを作って、全命令の動作を再度確認してからでないと、ちょっと、恐くて、基板の発注はできません。
それ(命令の動作テスト)が済むまで、基板配線図の最後の修正作業も一時停止です。

●ところで、MOV H,Mはどうするの?

そうでした。
これは、困った。
今更、こんなベースになる部分での変更なんて、とてもじゃないけどムリ…。
だって、ですねえ。もう、隙間が無いんですから…。
ここに、16ビット分のラッチを追加するなんて、そりぁあ、いくらなんでも、不可能ですってば。
勘弁してほしいなぁ、もう。

うー。
何から何まで8080と同じにしなくたって、よいじゃ、ありませんか。
1つぐらい実行できない命令があったって、まあ、愛嬌みたいなもので…。

都合により、MOV L,MとMOV H,Mは使えません。
たとえば、
MOV E,M
MOV D,M
XCHG
のようにしていただければ…。
そうそう、それでいきましょう…。

いやあ、だめ。だめです。
やっぱり、だめ。
せっかくここまで苦労してやってきて、最後にそんないいかげんな妥協で、終わってしまうなんて。
それは、できません。
大げさなたとえですけれど、こういうのを、「九仞の功を一簣にかく」というんですよねえ。

頭を抱え込んで、沈思黙考いたしましたよ。
これは、つらい。
ほとんど、不可能。
だったのですけれど。ついに。

お。お。お。
なんとか、なりそうな…。
しかし、本当に、それでいけるのか?
うう。いけそう、なのでは…!

現在の基板配線の状態で、そこからなんとか変更可能な範囲で、対策をとることができそうな、回路変更案を思いつきました。
2009.6.18upload
2009.6.19追記

前へ
次へ
ホームページトップへ戻る