目次

MiniCによるハードウエア操作

 MiniCを利用して、ハードウエアを制御する
 コードを作成します。

 Z80ボードは、梅澤無線のUEC02Aを使います。



 Z80PIOの2ポートは、コネクタで外部デバイス
 に接続できるようにしてあります。

 また、シリアルインタフェースは、2チャネルを
 使えるようにしました。


8LED flashing  I/Oポートに接続した8個のLEDを、次のように  点滅するファームウエアを記述します。 ○●●●●●●○     ↓ ●○●●●●○●     ↓ ●●○●●○●●     ↓ ●●●○○●●●     ↓ ●●○●●○●●     ↓ ●○●●●●○●     ↓  はじめにもどる ○ 点灯 ● 消灯  LEDの点灯パターンは、決まっているので  配列に8ビットで、保存しておくことに。  MiniCでは、変数の初期化はできないので  配列を宣言した後で、関数による初期化  が必要です。  次のように初期化専用の関数を定義します。 #define STATE_MAX 6 int led_pat[STATE_MAX] ; void init_led_pat() { *(led_pat+0) = 0x81 ; *(led_pat+1) = 0x42 ; *(led_pat+2) = 0x24 ; *(led_pat+3) = 0x18 ; *(led_pat+4) = 0x24 ; *(led_pat+5) = 0x42 ; }  mainの中で、配列中の点灯パターンを取出し  I/Oポートに出力します。  配列には、インデックスが必要なので  変数stateをインデックスとして利用  します。  インデックスは、0から順番に+1し  STATE_MAXになったなら、0に戻します。  これで、無限ループに入っていても必ず  同じことを繰返せます。  Z80の動作は、8MHzや4MHzと高速なので  人間の目で、LEDの点灯が変化していく  ことを確認できるよう、遅延を入れます。  遅延は、次の関数で実現します。 #define MAX_DLY 0x0fff void x_wait() { int last ; last = MAX_DLY ; while ( last ) { last--; } }  全体をソースコードにまとめます。 #include "dos.h" #include "aki80c.h" #define OFF 0 #define ON OFF+1 #define MASKFF 0x00ff #define STATE_MAX 6 #define MAX_DLY 0x0fff char state ; int led_pat[STATE_MAX] ; /* function prototype */ void init(); void init_led_pat(); void x_wait(); void main() { int tmp ; /* initialize */ init(); init_led_pat(); /* endless loop */ while ( ON ) { /* get data */ tmp = *(led_pat+state); /* impress */ outportb( PBD , tmp ); /* update */ state++ ; /* judge */ if ( state == STATE_MAX ) { state = 0 ; } /* delay */ x_wait(); } } void init() { /* PIOB all zero */ outportb(PBD,0x00); /* PIOB (mode 0) output no interrupt */ outportb(PBC,0x0f); outportb(PBC,0x03); /* sequencer */ state = 0 ; } void init_led_pat() { *(led_pat+0) = 0x81 ; *(led_pat+1) = 0x42 ; *(led_pat+2) = 0x24 ; *(led_pat+3) = 0x18 ; *(led_pat+4) = 0x24 ; *(led_pat+5) = 0x42 ; } void x_wait() { int last ; last = MAX_DLY ; while ( last ) { last--; } }  これでハードウエアの操作を実現できますが  他のことは何もできないので、割込みを使い  遅延処理をタイマーに任せてみます。  手元にある梅澤無線のUEC、秋月電子のAKI80には  Z80CTCが内蔵されているので、CTCによるタイマー  割込みを利用してみます。  CTCの初期化は、次の関数で定義します。 void init_ctc() { #asm ; ; put CTC interrupt vector ; ld a,80h ; xx80h out (10h),a ; ; put control word and count ; ld c,17h ld b,80h ld a,c out (10h),a ld a,b out (10h),a ld a,c out (11h),a ld a,b out (11h),a ld a,c out (12h),a ld a,b out (12h),a ; ; enable interrupt ; ld a,0D7h out (13h),a ld a,62 out (13h),a #endasm }  CTCのうち、CTC3による割込みを利用できるように  設定します。  MiniCのコンパイラは、スタートアップルーチンに  startup.hを利用します。評価版のコンパイラでは  コンパイルスイッチを使えず、ヘッダファイルは  startup.hの内容をバックアップしておき、修正  します。  今回は、次のように変更しました。 CPM EQU 0 if (CPM EQ 0) RAM_TOP EQU 8000h ; ORG 0h DI LD SP,0 IM 2 LD A,7Fh LD I,A LD A,80h OUT (10h),A CALL __main __st_loop__: DI nop JR __st_loop__ DUMMY: EI RETI ORG 7F80h CTC_VECT DW DUMMY DW DUMMY DW DUMMY DW __inter_time3 endif if ( CPM EQ 1) RAM_TOP EQU 8000H ; ORG 100h jp start start: DI LD SP,7000h CALL __main __st_loop__: jp 0 endif  スタートアップルーチンの中に、割込みベクタを  記述し、モード2割込みを使えるようにします。  割込みハンドラは、アセンブリ言語のラベルの  前後に2つのアンダースコアをつけて定義して  います。Cのソースコードでは、ラベルをその  まま使います。  割込みハンドラに相当するCの関数は、interruptを  つけて、一般の関数と区別します。  タイマー割込みを利用した、LED flasherは  次のソースコードにします。 #include "dos.h" #include "aki80c.h" #define OFF 0 #define ON OFF+1 #define MASKFF 0x00ff #define STATE_MAX 6 #define MAX_DLY 0x0fff char state ; char tflag ; char cnt ; int led_pat[STATE_MAX] ; /* function prototype */ void init(); void init_led_pat(); void init_ctc(); void led_handler(); void ei(); interrupt inter_time3(); void main() { /* initialize */ init(); init_led_pat(); /* enable interrupt */ ei(); /* endless loop */ while ( ON ) { /* timer interrupt */ if ( tflag == ON ) { /* clear flag */ tflag = OFF ; /* perform */ led_handler(); } } } void init() { /* PIOB all zero */ outportb(PBD,0x00); /* PIOB (mode 0) output no interrupt */ outportb(PBC,0x0f); outportb(PBC,0x03); /* sequencer */ state = 0 ; cnt = 0 ; /* clear flag */ tflag = OFF ; } void init_led_pat() { *(led_pat+0) = 0x81 ; *(led_pat+1) = 0x42 ; *(led_pat+2) = 0x24 ; *(led_pat+3) = 0x18 ; *(led_pat+4) = 0x24 ; *(led_pat+5) = 0x42 ; } void led_handler() { int tmp ; /* get data */ tmp = *(led_pat+state); /* impress */ outportb( PBD , tmp ); /* update */ state++ ; /* judge */ if ( state == STATE_MAX ) { state = 0 ; } } void init_ctc() { #asm ; ; put CTC interrupt vector ; ld a,80h ; xx80h out (10h),a ; ; put control word and count ; ld c,17h ld b,80h ld a,c out (10h),a ld a,b out (10h),a ld a,c out (11h),a ld a,b out (11h),a ld a,c out (12h),a ld a,b out (12h),a ; enable interrupt ; ld a,0D7h out (13h),a ld a,62 out (13h),a #endasm } void ei() { #asm ei #endasm } interrupt inter_time3() { /* judge */ if ( cnt == 10 ) { cnt = 0 ; tflag = ON ; } /* increment */ cnt++; }  割込み許可は、別途関数eiを定義して使います。  Z80には、モード0、1、2の3種類の割込みが  あります。CTCはモード2の割込みに対応して  いるので、Vectorを定義し、CTC割込みが高速に  実行されるようにします。  MiniCでは、割込みハンドラをデータ型「interrupt」を  利用して区別しています。他のコンパイラでは、#pragma  のような修飾子をつけて、割込みハンドラを指定する  こともあります。  「interrupt」を指定すると、関数=サブルーチンからの  もどりには、「reti」を利用。通常は「ret」であるので  区別するため、「interrupt」の追加があると考えます。
目次

inserted by FC2 system