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

●プログラムを直して再実行してみました

前回、PICのパルス出力プログラムにミスがみつかったことについて書きました。
毎度のことなのですが、それについて説明をしただけでタイムアウトになってしまって、肝心のプログラムの修正にまでは入れませんでした。
今日になってから、プログラムを実際に直して、再度実行してみました。

修正したプログラムリストです。
[第411回]でお見せしたtest6.asmです。



前回説明しましたように、 bcf STATUS,0 のところが間違っていましたので、 bcf STATUS,5 に直しました。
これをアセンブルして、PIC16F88に書き込んで、POWER ONして動作を確認してみました。



[第411回]の波形の写真と比較してみてください。
立ち上がりエッジがシャープになったのがわかりますでしょうか?
プログラムを修正する前は、’H’出力がハイインピーダンスになってしまうので、プルアップ抵抗で見かけ上H出力にしていました。そのため立ち上がりエッジがなまっています。
今回はH出力もハイインピーダンスではなくて、「自前」でHレベルを出力していますから、立ち上がりがシャープになっています。

●PIC16F88の間接アドレッシング

プログラムミスのために説明の予定が遅くなってしまいました。
PIC16F88の間接アドレッシングは、FSRレジスタとINDFレジスタを使って行います。
すでに説明をしておりますように、PICにおける間接アドレッシングは、8080のHLレジスタを使うメモリアクセスと全く同じなのです。
8080アセンブラでは、Mというバーチャルな「レジスタ」を用意して、このMにアクセスする命令を書けば、それはHLレジスタで示されたメモリアドレスにアクセスすることになる、ということになっていました。

PICの場合もこれと全く同じでINDFというレジスタにアクセスする命令を書けば、それはFSRで示されたメモリアドレスにアクセスするということなのです。
おそらく、このように文章で、これだけの説明を読めば、特に誤解することもなく、そういうものだな、と理解していただけるはずです。

ところがPIC16F88(に限らないのですが)のData Sheetを「深読み」してしまうと、そこのところが、混乱してしまうのです。
すでに何回もお見せしていますが、PIC16F88のメモリマップです。


[出典]Microchip社PIC16F88Data Sheet

FSRはアドレス04にあります。
メモリバンクを切り換えてもアクセスできるように、全てのバンクの同じ位置にFSRがあります。
で、INDFなのですが、これは本来8080のMと同じ「バーチャル」な存在のはずです。
いうなれば数式のXとかYなどと同じで、「仮にINDFと書くけれど、これは実在するレジスタではなくて、FSRで間接的に示されるメモリアドレスのことなのですよ」と説明すべきものなのですけれど、それが上のメモリマップ上に書いてしまってあるのです。
アドレス00のindirect addrというのがINDFなのです。
このindirect addrというのも全バンク共通に配置されているようです。
ちょっとコピーが小さくて見にくいのですが、右肩に(*)がついていて、表の下にその説明があります。
これも小さくて読みにくいのですが、
* Not a physical register
と書いてあります。

ううう。こういうわけのわからぬことをしてくれるものだから、私のような単細胞はつい迷ってしまうのですよお。

PIC16F88Data Sheetで、FSRとINDFについて説明しているところのコピーは、[第414回]にあります。
PICで間接アドレッシングのサンプルプログラムを書いて、それをテストして確かめてみる、ということは、ポートの出力プログラムのように簡単ではありません。
ですから、単なるプログラムリストになってしまいますけれど、その使い方の例をあげて説明してみます。

ユーザー用のデータメモリとしては、バンク0ではアドレス20〜7Fの範囲を使うことができますから、たとえば20〜2Fを何かのデータバッファとして利用することを考えてみます。
そこに先頭から順番に何かの処理結果を格納していくことにします。
処理結果はwレジスタに入っているものとします。
するとその部分のプログラムは、まず最初の部分で、
movlw 20
movwf FSR
と書いて、FSRにバッファの先頭アドレスを入れておいてから、プログラムのデータを格納するところでは

movwf INDF
incf FSR

というように書けば、データバッファに1バイトの値を入れたあとで、FSRをインクリメントしてバッファメモリアドレスを+1する、という動作になります。

さてここから先が、「深読み」のお話です。
この
movwf INDF
incf FSR
という部分をMPLABのアセンブラにかけてみるとどうなるでしょうか?

簡単なサンプルプログラムです。
プログラムに大した意味はありません。



loopの次の行にある、
movwf INDF
が間接アドレッシングです。
FSRで指定するメモリアドレスにwレジスタの値を入れています。

[注記]
このプログラムを実行すると、メモリアドレス20〜2Fを0クリアする結果になりますが、メモリをクリアするにはclrfを使って、
clrf INDFとした方が簡単になります。
またこの例に限ってはdecfsz cntr などを使わなくても、もっと賢い方法があります([第414回]PIC16F88Datasheet EXAMPLE2−2参照)。
ここでは、一般的なINDFの使い方として、上のソースプログラムのような書き方をしています。
メモリをクリアすることが目的で書いたプログラムではありませんので、「メモリをクリアするときにはこう書くのだ」と思ってしまわないようにしてください。[注記 ここまで]

さて、このプログラムをアセンブルすると、どのような機械語コードが生成されるでしょうか。



movwf INDFの機械語コードはアドレス000Aにあります。0080です。
ん?
でもこれって、前に説明した直接アドレッシングでないのお?
movwfの機械語は、下位8ビットが 1fff ffff で、fのところに7ビットのメモリアドレスが入ります。と説明しました。
INDFのメモリアドレスは00ですから、機械語コードは確かに0080になります。

んでも、それって、メモリアドレス00に対する直接アドレッシングでしょう。
どうしてアドレス00をアクセスしているのに、それが別のアドレスをアクセスしたことになってしまうのお?
理解できないなあ。
で、私は迷ってしまったのでした。

これはMicrochip社がいけないのです。
誰もPICのアセンブル後の機械語コードを読んだりはしないはずですから(読んだりするのは私くらいのものか)、INDFに00というアドレスを与えることは内緒にしておいて、メモリマップのアドレス00のところは、reservedにしておけばよかったのです(と私は思います)。

じつは8080でも同じことをやっているのです。
これは機械語コードを作るうえでそうするほうが都合がいいからです。
8080ではレジスタに3ビットの番号を与えていました。
Bレジスタが000、Cレジスタは001、…Aレジスタは111です。そしてMは110でした。
機械語コードの上ではMはまさにバーチャルなレジスタだったのです。

Microchip社もPICのINDFで同じことをしました。
ただし8080と違って、wレジスタ以外のシステムのレジスタをメモリの上に割り当てたのです。
STATUSレジスタがアドレス03、FSRレジスタがアドレス04というように。
そしてバーチャルなINDFレジスタを「メモリアドレス」00に割り付けてしまったのです(あたかもINDFがメモリ上に実在するかのように!)。

だから、そうすると、このINDFは、アドレス00なのだから、そうしたらアドレス00を間接アドレッシングしてみたらどうなるだろうか?
などと、考えなくてもいいことを考えてしまう人たちが出てきてしまうのです。
そこのところを、多分Microchip社もあとになってから気がついたのでしょう。
で、書かなくてもいい苦しい説明をわざわざ書くことになったのでしょう。
[第414回]でお見せしたPIC16F88DataSheetのFSR/INDFの説明部分を再掲いたします。


[出典]Microchip社PIC16F88Data Sheet(赤線は筆者)

そう。その赤線を引いたところです。
「FSRにINDFレジスタのアドレス(00)を入れて、間接的にアクセスしても無駄だから、そーゆーことはやらないでね」という断り書きです。

アセンブラ(機械語)の世界って、なかなか興味深いものがある、と思いませんでしょうか?
2010.1.9upload
2010.1.10[注記]を追記しました

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