|
前項ではプログラムのメイン・パート直前までの流れを追ってみたが、内容は所謂「仕込み」と思って貰って良いだろう。メインからは、プログラムが実際に動作する領域となる。
まず最初に、ROMの先頭番地を指定する。
.section program,code,locate=h'0034
今回は、ベクターアドレスの直後に設定している。(注1) ROMの内容をグループ分けして複数書き込み、立ち上げ時に先頭アドレスを指定することで、1チップで機能選択を行えるようにもなるが、今回は単機能のみの書き込みとなっている。
次に、マイコンのメインルーティンとなる訳だが、
start:
mov.l #h'ff7f,er7 ; stack pointer set
jsr @memory_set ; Work RAM clear and FIFO pointer
jsr @sci_init
jsr @wait_500msec ; Wait
jsr @input_enable ; Port init
jsr @midi_start ; MIDI rx start
loop:
jsr @rx_data_receive
jsr @sw_preset_out
jmp @loop
ルーティン頭でデータの待避を行うためのスタックポインタのアドレスを指定する。これも、デバイス毎に番地が異なるので、その都度確認をしよう。 次に、メモリーの内容を初期化し、シリアル・インターフェイスを初期化、0.5秒待った後、入力端子の初期化を行い、midi機能のスタート・ルーティンとなる。その後のパートは電源を落とすまでのループ動作となる。内容は、データ受信、エンコードした受信データの出力と、これらのタスクを相互に行っている。
ここで、忘れていたレジスタについての説明を簡単に行っておこう。Registerとはお店のレジと同じ意味で、要するにデータを貯め込んでおく場所だ。H8は汎用レジスタと呼ばれる32ビットレジスタを8本持っている。このうちER7はスタックポインタと言って、レジスタの内容をメモリーに待避させる際のポインタの役割を持つもので、ユーザーはこれ以外の7本を使い廻すことになる。その構造を下記に示す。ER1は32ビット幅、E1,R1はそれぞれ16ビット幅、R1H、R1Lは8ビット幅となる。
アセンブル命令のデータ幅は32ビットのロングワードが L、16ビットのワードが W、 8ビットのバイトが B 、となっている。例えばロングワードの転送命令を行う際は、、、
mov.l er5, er1
このように表記する。なお、掛け算の場合はディスティネーション(コンマの後にくる、右側のレジスタ。ここに結果が反映される。)のサイズが倍になることに注意しよう。
次の部分からは、前掲したルーティンを記述したパートとなる。呼び出される順番は、上記のタイミングとなるので、記述の順番に拘る必要は厳密にはない。が、ある程度動作と相関を持たせた順番で記述を行ったほうが後々デバッグ時に役に立つこともあるので、その辺を考慮してコードを書き始めよう。
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; MIDI sw data Receive
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
rx_data_receive:
cmp.w r5,e5 ;r5: rx1_top, e5: rx1_end
bne initialize
rts
initialize:
sub.l er1,er1
mov.w e5,r1 ;rx data address
data_Read:
mov.b @(rx_fifo,er1),r0h ; rx data -> [r0h]
inc.w #1,r1
bclr #1,r1h ; rx buffer 512
mov.w r1,e5 ; reWrite rx data address
cmp.b #h'F0,r0h ; sysex data thru
bls ditector
rts
最初に見出しを付けておくと、これもデバッグ時に役立つので、必ず解りやすい表現で見出しを書いておこう。 例のように、ライン毎に動作内容の意図を記述しておけば、作業内容を忘れ去ってしまったであろう数ヶ月後にプログラムを書き直すことになっても、ある程度理解を助けることになる。 では、動作の内容を説明していこう。
いきなり難易度が高そうな内容であるが、実はここがMIDI受信部分のキモである。 r5/e5 は受信データの格納場所 rx_fifo のアドレスを示したポインタだ。この内容が一致した時点でこのルーティンのデータは読み尽くされたことになる。一致するまでは、読み出しのルーティンが続く。最初にレジスタの内容を消去してデータアドレスを代入する。消去の方法は色々とあるが、自分で自分を引き算する sub.l er1, er1 この方法が、一番処理が速くて、概念上も解りやすい。二番目の er1 はディスティネーションといって、計算結果が格納される場所となる。 次の mov.b @(rx_fifo,er1),r0h はアドレスを直接指定してデータを参照する手法で、アドレス (er1)にはロングワードを代入する必要がある。結果は r0h に返っているが、データ幅は初期設定したサイズとなることに注意。次に、読み出しアドレスが inc.w #1,r1 で、1つ進められ、その都度 bclr #1,r1h で512バイトのデータ容量に対する読み出しアドレスがオーヴァーフローしないかを監視している。最後に、一コマ進めたアドレスをポインタに格納した後、読み出したデータの内容を確認する。Sysexヘッダー(#h’F0)の場合はそのまま黙殺、それ以外の場合は、ディテクターのルーティンに進む。
|
|