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

●プログラムミスがありました

前回、PICの直接アドレッシングの説明をするために書いたプログラムにミスがありました。
今日になってから、アセンブラによって出力された機械語コードのリストをながめていて、プログラムミスに気がつきました。
これがそのソースプログラムでした。


どこが間違っていたかといいますと、真中あたりにある
bcf STATUS,0;bank 0
が間違いだったのです。

PIC16F88のデータメモリが4つのメモリバンクに分かれていることは、すでに説明しました。
そのバンクを選択するのに、PIC18F4550ではBSRレジスタを使うのですが、PIC16F88ではSTATUSレジスタを使います。
こちらがPIC16F88のSTATUSレジスタの説明です。


[出典]Microchip社PIC16F88Data Sheet

PIC16F88ではSTATUS registerのbit5とbit6によってバンクを切り換えています。
ですから本当はbit5だけではなくてbit6もセット、リセットしなければならないのですが、今回のプログラムではバンク0とバンク1しか使わなかったことと、リセットによってSTATUSレジスタのビット5とビット6は’0’になることから、今回のプログラムでは、Bポートを出力に設定するため、バンク1にあるTRISBに00を書くときに、STATUSのbit5を’1’にし、そのあとでポートにデータを出力するため、バンク0にあるPORTBがアクセスできるように、STATUSのbit5を’0’にするつもりでした。
ところがそこのところをうっかりして、bit5ではなくてbit0をクリアしていたのです。

ということは、ずっとバンク1が選択されていたことになります。
すると当然PORTBに出力はできなかったはずです。
じつはこの部分のプログラムミスは、前回お見せしたプログラムだけではなくて、はじめにパルス出力プログラムのサンプルとして書いたものがもとになっていて、[第410回]の最初のパルス出力プログラムも[第411回]のプログラム変更をした2本のプログラムも、全部同じところが間違っていました。

でも、そうすると、当然PORTBにはアクセスできませんから、パルスを出力することはできなかったはずです。
PIC18F4550のアセンブラは「かしこいアセンブラ」なので、勝手に都合よくパラメータを付加してくれましたが、PIC16F88の場合にはそういう芸当はできません。
機械語に変換されたリストを眺めてみても、STATUSのビットを勝手にセット、リセットしたりはしていないようです。
すると、アクセスできないはずのPORTBから、なぜパルスが出力されたのでしょうか?

これはこれで問題です。
たまたま今回はプログラムにミスがあったのですが、そのミスが無視される形で、期待した通りの結果が得られました。
しかし、CPUは忠実な僕(しもべ)であるべきなので、たとえご主人さまが間違った命令を下したとしても、それにさからうことは許されません。

むむ、それともウチのPIC16F88は、ひょっとすると、自ら考える知能を獲得してしまったのか?
こ、こういうものは直ちに破壊してしまわなければ、未来からターミネータが攻撃してくるかも…。

んなわけは、ありませんでした。
ターミネータの妄想と戦いながら、必死で考えていましたら、なんともあっけなく謎が解けてしまいました。
いやあ、よかった、よかった。
正月早々、てっきりターミネータと戦わねばならぬのか、と思ってしまったじゃありませんかぁ(おお、こわぁ)。

プログラムのミスのために、本来は選択されるべきバンク0が選択されず、バンク1が選択されていたことは間違いがありません。
当然、
xorwf PORTB
という命令も、PORTBをアクセスすることはできません。
ではどこをアクセスしていたのでしょう?

ここでぜひとも思い出していただきたいのが、前回の「直接アドレッシング」の説明です。
xorwf PORTB
のようにアセンブラで書くと、PORTBをアクセスして当たり前だと思ってしまうのですが、じつは、
xorwf 06
と書いたのと同じことなのでした。
このように書くと、むむ、本当にこれでPORTBをアクセスするのだろうか?とちょっと自信がゆらいできてしまいます。

前回もお見せしましたが、PIC16F88のメモリマップです。

[出典]Microchip社PIC16F88Data Sheet

PORTBはアドレス06にあります。メモリバンク0です。
その同じ位置のメモリバンク1には何があるでしょうか?
TRISBです。アドレス86にあります。
TRISBはPORTBの向きを設定するためのレジスタです。
TRISBは、今回のプログラムでも使っています。
前回お見せしたアセンブラによって出力された機械語コードのリストをもう一度見てみましょう。



PORTBを出力に設定するために、TRISBに0を書き込んでいます。
その部分は、アドレス0009にあります。
movwf TRISBの機械語コードは0086になっています。

PORTBに値を出力するための、movwf PORTBも、機械語コードは0086です。
この’86’はTRISBのアドレスの86ではなくて、もともとmovwfの機械語コードの下位8ビットのbit7は1なのです。bit6〜bit0に対象になるメモリアドレスが入ります(前回[第415回]参照)。
メモリアドレス部分は7ビットしかありませんから、TRISBの最上位ビットの’1’は無視されてしまうため、movwf PORTBもmovwf TRISBも全く同じ機械語コードになってしまうのです。
(問題のパルス出力部分は、movwf PORTBではなくて、xorwf PORTBなのですが、理屈は全く同じです)

この場合PORTBに出力するか、TRISBに出力するかを決定しているのが、STATUSのビット5、ビット6なのです。
では、なぜTRISBにしかアクセスできなかったはずなのに、パルスが出力されたのでしょうか?

TRISBはPORTBの入力、出力の向きを決定します。
TRISBのビットに’1’を書き込むとPORTBの対応するビットの向きが入力になり、’0’を書き込むとPORTBのそのビットは出力になります。
PORTBの入力に設定されたビットの出力はハイインピーダンスになります。
抵抗でプルアップしていますから、見かけ上は’1’が出力されたように見えます。
PORTBの出力に設定されたビットはいつも’0’が出力されるわけではありませんが、power−onしたとき、たまたま出力ラッチがクリアされていると、’0’が出力されます。
つまり、’1’と’0’が交互に出力されていたのではなくて、「入力」と「出力」に向きが交互に切り替わっていて、たまたま出力が’0’であるとパルスが出力されているように見えたのです。

どうりで、今回のPIC16F88の動きはおかしい、と思っていました。
ときどきpower onのタイミングによってパルスが出力されないことがあったり、そもそもData Sheetには「オープンドレイン」とは書いてないにもかかわらず、どの出力ビットも’1’出力がハイインピーダンスだったのです。

何日かぶりに、やっと、納得。でしたけれど、まあ、ほんとに、正月からなんともみっともないお話になってしまいました(恥)。

2010.1.8upload
2010.1.9一部加筆

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