復活!CP/M ワンボードマイコンでCP/Mを!
CP/MがTK−80互換のワンボードマイコンの上で復活します
ND80ZVとMYCPU80の上でCP/Mが走ります!
[第513回]
●TIMER1割込みとテーブル参照とPCLATHレジスタ
一週間ぶりの更新になってしまいました。
もちろん遊んでいたわけでもサボっていたわけでもありません。
お得意さまから受けた特注品が思いのほか大変な回路で、この一週間というもの、ハードとソフトの両方で苦闘していました。
今回はZ80ではなくてPICを使った回路です。
相当に込み入ったプログラムでそこそこの長さになってしまった上に、TIMER1の割込みと複数のテーブル参照が必要で、そこのところで見事にこけてしまい、解決までに貴重な丸1日をたっぷりと費やしてしまいました。
その過程でネット検索をしまして参考になる記事などを探しましたところ、なんだかよくわからないで書いていらっしゃる方の記事だとか、舌足らずで、もろ、誤解をしてしまう書き方のものだとかに遭遇してしまいまして、ますます迷宮の奥深く落ち込んでしまいました。
私の場合、TIMER1割込みで初歩的なミスをしてしまったことと、アドレスが00FFを越える位置に置いたテーブル参照に必要な適切なプログラムを書かなかったことが複合して利いてしまい、どこがどうなっているのかわからぬ状態でもがいていたことが、あとになって判明いたしました。
えっと。
なんだか今回は本来のテーマとは全く関係ないお題について書き始めてしまっております。
本来でしたら、そのことの顛末につきましてはここではなくて、PICについてまとめた記事の続きとして書くべきなのでありましょうが、こうやってずっと長い間書いてきますと、そのようにテーマ別に振り分けることが余り適切ではないように思えます。
当初「ブログ風」ということで書き始めたのでありますが、これではまさにブログそのものであります。
ま、しかし、PICはテーマ違いではありますが、Z80ではなくとも、PICプログラムも8ビットプログラムの範疇に入りますゆえ、しばし道草のお付き合いをお願いいたします。
あ。まだ、その特注品の作業は完了してはおりません。
この連休もつぶして作業継続中でありまして、依然として時間はまったく無いのでありますが、ここでまとめておきませんとせっかく解決に至った内容をそのうち忘れてしまいます。
そうなる前に何かの形で整理して書きとめておくべきでありましょう。
当記事も気がつきましたら、一週間も穴があいてしまいました。
前々から書いてきましたことの続きも整理して書かねばならないのですけれど、なにしろマルチタスクが全く苦手な単細胞でありますので、目下のところ仕上げを急いでおりますPICのシステムに全脳細胞を集中してしまい、他のことが考えられない状態になっております。
そういうことからしましても、ここはPICについて書くしかありませぬ。
その考えには昨日あたりからおおよそ到達はしていたのでありますけれど、やっぱり記事にまとめようとしますと、いろいろ準備が必要になります。
実際のなまのプログラムをお見せするわけにはいきませんし、余計な情報が多すぎて不適切ということもあります。
すると適当なサンプルプログラムも作らなければならないでしょうし、そうなるとそれを実際にPICに書いて、正しく動くことも確認しなければなりません。
それはちょいと、大変なことではあります。
ではありますが、しかし、一週間の穴はやっぱりなんとかしませんと…。
というようなわけで、本日は覚悟を決めまして朝からそちらのほうの準備にかかりました。
ま、なんとかサンプルプログラムもできて、その動作も確認できたのですけれど、やっぱり本業のほうの続きもしなければなりませぬ。
そのような次第でありますから、今回のところは恐らくさわりていどで終わってしまうことになろうかと思います。
毎度のことながら、気長にお付き合いをお願いいたします。
しばらく書かなかったものですから、どうも今ひとつ要領を得ない書き出しになってしまいました。
矢鱈前書きばかりが長くてなかなか本題に入れません。
いい加減にしまして、本題に入ることにいたします。
●PICのテーブル参照 retlw命令とPCLATHレジスタ
プログラムによってはテーブル参照が必要になる場合があります。
コード変換などもそのひとつで、Z80ではたとえばひとつの方法として、テーブルの先頭アドレスをHLレジスタに入れておいて、コードの値をHLレジスタに加算することで、そのアドレスに置いた変換後のデータを得ることができます。
Z80ではそのようなデータ群を生のパイナリデータとしてメモリに直接置く方法が普通にとられます。
テーブル参照に限らず、文字列データなどでも同じようにして扱います。
Z80のアセンブラでは、そのようなテーブルやデータの作成には、DB(デファインバイト)やDW(デファインワード)などの擬似命令や、” ”で囲んでそのアドレス位置に直接文字列を書き込んだりします。
PICの場合、テーブル参照には全く別の方法を使います。
そのためにretlwという命令が用意されています。
おお、そうでした。
今思い出しました。
E−80ミニコンのIPL(イニシャルプログラムローダー)はPIC16F886に書き込んでおいて、起動時にその内容をRAMに転送して、それからCPUがそれを読み込んで実行するという仕掛けになっているのですが、そこでこのretlw命令を使ったテーブル参照を行なっていたのでした。
そこのところのプログラムは[第455回]でお見せしましたが、参加までに下に再掲いたします。
ついでにそのときの説明も一緒に再掲いたします。
;;; for E-80 ;;; IPL & MIDI ;2012/12/15 ;2013/1/14 1/15 1/16 1/17 1/18 1/19 1/20 1/22 1/26 1/28 ;5/31 6/4 8/2 ; #include <p16f886.inc> __CONFIG _CONFIG1,_WDT_ON& _EC_OSC & _PWRTE_ON & _MCLRE_ON & _LVP_OFF __CONFIG _CONFIG2 ; ;EXT CLOCK 10MHz ;WDT period is normally 18ms ; ; cf=0 zf=2 f=1 w=0 ; ; rbfcnt equ 20 rbftop equ 21 rbfend equ 22 errmk equ 23 datawk equ 24 dcntr equ 25 tcntr equ 26 tcntr2 equ 27 tcntr3 equ 28 acntr equ 29 ; rbf equ 30;end=6f ; savew equ 70;=f0 in bank1 savests equ 71;=f1 in bank1 ; org 00 goto start ; org 04 goto int ; org 05 start clrf STATUS;bk0 movlw 0af;bit4,bit6=0 ,bit4 is also cpureset movwf PORTA;cpu()z8s180)reset pulse >6clock,3us movlw 60;bk3 movwf STATUS clrf ANSEL clrf ANSELH bcf STATUS,6;bk1 movlw 0c;ra2,ra3=in movwf TRISA clrf TRISB clrf TRISC ;baud rate set movlw 4;31250bps movwf SPBRG movlw 20 movwf TXSTA movwf PIE1;read int enable bcf STATUS,5 ;bank 0 movlw 90 movwf RCSTA clrf errmk clrf rbfcnt movlw rbf movwf rbftop movwf rbfend ;intset movlw 0c0 movwf INTCON ; ;initial loader go or nogo check movf PORTA,w andlw 0c btfsc STATUS,zf goto runcpu ; ;initial loader program set & run cpu ; ;movlw 78;=120 movlw 0e;=14 movwf dcntr clrf acntr clrf PORTC ; loaderset clrwdt movf acntr,w call table call loadersub decfsz dcntr,f goto loaderset movf PORTA,w andlw 0c;dipsw iorlw 30;30,34,38,3c call loadersub movlw 6a;=106 movwf dcntr loaderset2 clrwdt movf acntr,w call table call loadersub decfsz dcntr,f goto loaderset2 goto runcpu ; loadersub movwf PORTB;DATA set bcf PORTA,5;memwr nop bsf PORTA,5 incf acntr,f movf acntr,w andlw 3f movwf PORTC;A0-A5 set btfsc acntr,6 bsf PORTA,6;A6 set return ; ; z80 loader program table table addwf PCL,f ; retlw 21 retlw 0F retlw 00; LD HL,DTTOP retlw 11 retlw 80 retlw 0FF; LD DE,LDRTOP retlw 01 retlw 6a retlw 00; LD BC,$005D retlw 0ED retlw 0B0; LDIR retlw 0C3 retlw 80 retlw 0FF; JP LDRTOP ; retlw 00;dipswdata=000E ; ;dttop=000F retlw 31 retlw 00 retlw 00; LDRTOP:LD SP,$0000 retlw 3E retlw 88; LD A,88 retlw 0D3 retlw 0FB; OUT (FB),A retlw 3E retlw 0FF; LD A,FF retlw 0D3 retlw 0FA; OUT (FA),A retlw 3E; refresh off retlw 30 retlw 0ED retlw 39 retlw 36 retlw 0ED; memory wait off retlw 39 retlw 32 retlw 3E; memory bank set retlw 80 retlw 0ED retlw 39 retlw 3A retlw 0CD retlw 0C3 retlw 0FF; CALL LDSUB retlw 6F; LD L,A retlw 0CD retlw 0C3 retlw 0FF; CALL LDSUB retlw 67; LD H,A;HL=load top address retlw 0CD; retlw 0C3 retlw 0FF; CALL LDSUB retlw 5F; LD E,A retlw 0CD retlw 0C3; retlw 0FF; CALL LDSUB retlw 57; LD D,A;DE=load end address retlw 0CD retlw 0C3 retlw 0FF; CALL LDSUB retlw 4F; LD C,A retlw 0CD retlw 0C3 retlw 0FF; CALL LDSUB retlw 47; LD B,A;BC=JP address after loaded retlw 0C5; PUSH BC retlw 13; INC DE retlw 0EB; EX DE,HL retlw 0B7; OR A retlw 0ED retlw 52; SBC HL,DE retlw 0EB; EX DE,HL;DE=load bytes ; retlw 0CD retlw 0C3 retlw 0FF; LOOP:CALL LDSUB retlw 77;LD (HL),A retlw 23;INC HL retlw 1B;DEC DE retlw 7A; LD A,D retlw 0B3; OR E retlw 20 retlw 0F6; JR NZ,LOOP retlw 0E1; POP HL retlw 0E9; JP (HL) ; ;load subroutine retlw 3E retlw 04; LDSUB:LD A,04;bit2=L,bit3=H,bit0-4 are INVERT retlw 0D3 retlw 0FE; OUT (FE),A retlw 0DB retlw 0FE; LP1:IN A,(FE) retlw 0E6 retlw 0C0; AND C0 retlw 28 retlw 12; JR Z,LP3;no data retlw 0FE retlw 80; CP 80 retlw 20 retlw 0F6; JR NZ,LP1 retlw 0DB retlw 0FC; IN A,(FC) retlw 0F5; PUSH AF retlw 0AF; XOR A retlw 0D3 retlw 0FE; OUT (FE),A retlw 0DB retlw 0FE; LP2:IN A,(FE) retlw 0E6 retlw 40; AND 40 retlw 28 retlw 0FA; JR Z,LP2 retlw 0F1; POP AF retlw 0C9; RET retlw 0AF; LP3:XOR A;bit2=H,bit3=H retlw 0D3 retlw 0FE; OUT (FE),A retlw 0DB retlw 0FE; LP4:IN A,(FE) retlw 0E6 retlw 40; AND 40 retlw 28 retlw 0FA; JR Z,LP4 retlw 18 retlw 0D9; JR LDSUB ; runcpu bsf STATUS,5;bk1 movlw 0bf;RC6=out;others=in movwf TRISC movlw 4c;ra6,ra3,ra2=in movwf TRISA movlw 0ff movwf TRISB bcf STATUS,5;bk0 movwf PORTA;run cpu |
PICプログラムでは定数データはこのプログラムのようにretlwを使って記述するのが定番です。
table以下がデータテーブルであるとともにサブルーチンでもあります。
サブルーチンの先頭でプログラムカウンタ(下位レジスタ)にWの値を加算するところがミソです。
retlwはWレジスタに定数を入れてリターンする命令です。
メインプログラムの側でWレジスタをインクリメントしながらtableをcallすると、そのたびにテーブルの値が順にWレジスタに入ってメインプログラムに戻ってきます。
ということはtableをcallするとWの値は破壊されてしまいますから、実はテーブルを参照するためのカウンタは別に設けておいて(acntr)、そちらをインクリメントしながらtableをcallする直前にWレジスタにmovします。
こういうところがPICのマシン語プログラムの嫌われるところなのですが、まあもともとマシン語なんてものはそういうものなのですから、慣れてしまえばそれほど苦にはなりません。
acntrをインクリメントしているところはloadersubにあります。
なおWは8ビットレジスタですから最大255個のデータまでしか扱うことができません。
それより大きいデータは同じ方法で複数個のテーブルに分けて扱います。
以上が[第455回]の説明ですが、今から考えますと、この説明は間違いではありませんが、よく理解していない者が書いたことが、もろわかってしまう、お恥ずかしい文章です。
Wは8ビットレジスタなので、そのままでは256個のデータしかアクセスできません(255個ではなくて256個でした)。
しかし256個以上のテーブルは扱えない、というのは私の早とちりでありました。
それを可能にするためにPCLATHレジスタが用意されていたのでした。
しかしこのPCLATHレジスタの扱いに関しては、ネット上にはどうも誤解されているらしい書き方やら、いまひとつ的確でない説明が多いようで、読めば読むほどわけがわからなくなってしまいました。
あ。もちろん今はすべてクリアしまして、スッキリしております。
ですのでこれからそのあたりの説明にとりかかるつもりなのでありますが、やっぱり時間がなくなってしまいました。
まだ説明にとりかかったばかりなのですが、この続きは次回にすることにいたします。
ワンボードマイコンでCP/Mを![第513回]
2013.11.23upload
前へ
次へ
ホームページトップへ戻る