目次
前
次
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」の追加があると考えます。
目次
前
次