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

復活!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 &ltp16f886.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

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