復活!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
前へ
次へ
ホームページトップへ戻る