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

復活!CP/M ワンボードマイコンでCP/Mを!
CP/MがTK−80互換のワンボードマイコンの上で復活します
ND80ZVとMYCPU80の上でCP/Mが走ります

[第353回]


●内蔵タイマーの設定と割り込み開始

前回からの続きです。
前回は演奏速度を設定するための計算処理について説明をしました。
MIDIでは、音符の長さをティックという単位で表しています。
たとえば1分間当たりの4分音符=120という速さで、4分音符の分解能=480という場合のティックは1041μsになります(端数切捨て)。
MIDI演奏プログラムではZ8S180内蔵のタイマーの割込みを利用して演奏を行ないます。
Z8S180内蔵のタイマーは単純な構造をしていて、CPUクロックを20分周したクロックを使って、設定値をダウンカウントします。
したがってCPUクロックが10MHzの場合、内蔵タイマーの1クロックタイムは2μsになります。
そこで上記ティックの値1041/2=520を内蔵タイマーに与えれば、1040μsごとに割り込みを発生させることができます。
1ティックごとに1μsの誤差が発生しますが1/1000の誤差ですから普通に演奏する分には問題はないでしょう(2分で約0.1秒の誤差です)。
前回はその内蔵タイマーに与える値を計算によって求めるサブルーチン(TMPSB)の説明をしました。
求められた値は(TIMEDT)に保存されます。

今回は(TIMEDT)の値をZ8S180の内蔵タイマーに設定し、割込みをスタートさせます。
前回もお見せした下のプログラムの
CALL TMSB
です。

808A 01A107   START10:LD BC,$07A1;(tempodata=0.5s)
808D CD8281     CALL TMPSB
8090 CDD381     CALL TMSB

下がTMSBプログラム(サブルーチン)です。

               ;timer set
81D3 010C00   TMSB:LD BC,$000C
81D6 2AD682     LD HL,(TIMEDT)
81D9 ED69       OUT (C),L;timer0 data registerL
81DB 0C         INC C
81DC ED61       OUT (C),H;timer0 data registerH
81DE 0C         INC C
81DF ED69       OUT (C),L;timer0 reload registerL
81E1 0C         INC C
81E2 ED61       OUT (C),H;timer0 reload registerH
81E4 0C         INC C
81E5 3E11       LD A,11;INT enable,countdown start
81E7 ED79       OUT (C),A
81E9 FB         EI
81EA C9         RET

Z8S180は16ビットのタイマーを2つ内蔵しています。
MIDI演奏プログラムはそのうちのタイマー0を使います。
内蔵タイマー0はI/Oアドレス000C、000Dのタイマー0データレジスタにセットした16ビットの値をCPUクロックを20分周したクロックごとにダウンカウントします。
データレジスタの値が0000になると、以下の動作が発生します。
割込みが設定されているときは、タイマー0割り込みが発生します。
データレジスタにはリロードレジスタ(I/Oアドレス000E、000F)の値がロードされます。
そのように動作するため、データレジスタとリロードレジスタに(TIMEDT)の値を書き込みます。
I/Oアドレス0010Hは内蔵タイマー0とタイマー1のコントロールレジスタです。
ビット4に1を書き込むとタイマー0からの割り込み出力が許可されます。
またビット0に1を書き込むとタイマー0のカウントダウンがスタートします。

●タイマー割り込みプログラム

タイマー0のデータレジスタの値が0000になると割り込みが発生します。
タイマー0の割り込みが発生すると、アドレス827FHの割込みプログラムが実行されます。
割り込みプログラムのジャンプアドレスの設定については[第350回]で説明をしました。

下が割り込みプログラムです。

              ;INT routine
              ;
827F DDE5     INT:PUSH IX
8281 E5         PUSH HL
8282 D5         PUSH DE
8283 C5         PUSH BC
8284 F5         PUSH AF
8285 011000     LD BC,$0010
8288 ED78       IN A,(C);
828A 0E0C       LD C,0C
828C ED78       IN A,(C);clear TIF0 flag
828E DD21DE82   LD IX,TPRMTOP
8292 3A0B84     LD A,(TRKNO)
8295 4F         LD C,A
8296 DD7E01   INT1:LD A,(IX+01)
8299 B7         OR A
829A CAC182     JP Z,INT2
829D DD6E02     LD L,(IX+02)
82A0 DD6603     LD H,(IX+03)
82A3 DD4604     LD B,(IX+04)
82A6 78         LD A,B
82A7 B7         OR A
82A8 CAB482     JP Z,INT12
82AB 7C         LD A,H
82AC B5         OR L
82AD C2B482     JP NZ,INT12
82B0 05         DEC B
82B1 DD7004     LD (IX+04),B    
82B4 2B       INT12:DEC HL
82B5 DD7502     LD (IX+02),L
82B8 DD7403     LD (IX+03),H
82BB 7C         LD A,H
82BC B5         OR L
82BD B0         OR B
82BE DD7701     LD (IX+01),A
82C1 0D       INT2:DEC C
82C2 CACD82     JP Z,INT3
82C5 110800     LD DE,$0008
82C8 DD19       ADD IX,DE
82CA C39682     JP INT1
82CD F1       INT3:POP AF
82CE C1         POP BC
82CF D1         POP DE
82D0 E1         POP HL
82D1 DDE1       POP IX
82D3 FB         EI
82D4 ED4D       RETI

割り込みプログラムの説明です。

割り込みプログラムはメインプログラムの実行中に実行されるため、割り込みプログラムで使用するレジスタは必ずプログラムの先頭でスタックに退避しておかなくてはいけません。

タイマー0のデータレジスタがカウントダウンによって0000になると、タイマーコントロールレジスタ(I/Oアドレス10H)のビット6が1になります。
割り込みを使う場合にはこのビットを確認する必要はないのですが、このビットをクリアするためには、
(1)タイマーコントロールレジスタ(I/Oアドレス10H)から値を読み出す
(2)タイマー0データレジスタ(I/Oアドレス0C、0D)から値を読む
という2つの動作が必要です。
今気がついたのですが、データレジスタは2バイトなので、本当に値を読み出すには、I/Oアドレス0Cと0Dから2回読み出さなくてはいけないのですが、ここではI/Oアドレス0Cから1度読んでいるだけです。
それでも特に動作に以上はありませんから、そういうことでもよいようです。

アドレス828EHからが割り込みプログラムの実際の処理部分です。
トラックデータの先頭からトラックの数だけ繰り返し処理を行ないます。
最初に先頭の(IX+00)を確認します。ここが00のときは、このトラックの処理は完了していますから、パスします。
そうではない場合には次の(IX+01)を確認します。
ここは時間データが最初から0か、割り込み処理が進んで0になったときには00が入ります。
それ以外は非00の数が入っています。
ここが00のときは割り込みプログラムは、このトラックのデータにはさわりません。パスします。
ここが00ではないときには、その次の(IX+02)〜(IX+04)の3バイトの時間データの値を−1して、その結果の値を(IX+02)〜(IX+04)に書き込みます。
そしてその結果が0になったときには(IX+01)には00が書き込まれます。
0ではない場合には(IX+01)には非00が書き込まれます。
この(IX+01)はとても大切な役目をしています。
そのことについては次回あたりで説明をすることになると思います。

割り込みプログラムの最後では、スタックに退避しておいたレジスタの値を元に戻します。
最後は必ず
割り込み許可命令EIとそれに続いて割り込みプログラムからのリターン命令RETIを書きます。

ワンボードマイコンでCP/Mを![第353回]
2013.3.22upload

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