今回はTMPZ84C015上のモニターでSDCCによるc言語でポート出力と時間稼ぎルーチンを作成・実行してみたいと思います。
まずは、ポートのイニシャライズですが、Bポートを使用して、関数の引数として、out/inを決定する値を与えるようにしました。
関数はアセンブラで作成し、out/inを設定するところで値を渡しています。基本的に、モニタで使用しているルーチンと同じです。
;; TMPZ84C015 PIOB Initialize set mode3
;;
;; int piob_init(uint8_t data)
;;
;; data: 0:output 1:input
;;
PORTAD .equ 0x1c
PORTAC .equ 0x1d
PORTBD .equ 0x1e
PORTBC .equ 0x1f
_piob_init::
ld hl, #0x0002
add hl, sp
ld a, #00 ;interrupt vector word
out (PORTBC),a
ld a, #0b11001111 ;mode 3
out (PORTBC),a
ld a,(hl) ;0:output 1:input
out (PORTBC),a
ld a, #0b00000000 ;interrupt control woed
out (PORTBC),a
ld a, #0b00000000 ;mask control word
out (PORTBC),a
ld hl, #0x0000
ret
上記のプログラムをpiob_init.asmで保存した後、アセンブルし、piob_init.relを取得します。
$ asz80 -l -s -o piob_init.asm
次に実際にポートに値を出力するためのポート出力関数を作成します。
これもモニタで使用しているポート出力部を使用します。
引数として、ポートアドレスと、出力したい値を与えます。
;; uint8_t (uint8_t port,uint8_t data)
_io_out ::
ld hl, #0x0002
add hl, sp
ld c, (hl)
inc hl
ld a, (hl)
out (c), a
ld hl, #0x0000
ret
上記のプログラムをio_out.asmで保存した後、アセンブルし、io_out.relを取得します。
$ asz80 -l -s -o io_out.asm
ポートのイニシャライズとポート出力関数ができたので早速、C言語で、LEDをLちかしてみたいと思います。
ポートBのPB0ビット(CN3/2Pin)にLEDを接続し、1KΩを介してGNDに接続しました。
piob_initには引数として,0xfe(0bitのみ出力)を与えています。
// TMPZ84C015 piob bit0 output:1
//
#include <stdio.h>
#include <stdint.h>
extern uint8_t io_out(uint8_t,uint8_t);
extern uint8_t piob_init(uint8_t);
const uint8_t PortBD = 0x1e;
const uint8_t PortBC = 0x1f;
int main(void){
piob_init(0xfe); //port PB0 output
io_out(PortBD,0x01); //port b bit0 High output
return(0);
}
コンパイルします。
$ sdcc -mz80 --out-fmt-ihx --code-loc 0xa000 --no-std-crt0 -o piob_led.ihx mycrt0.rel piob_led.c io_out.rel piob_init.r
el
Conflicting flags in area _CODE
Conflicting flags in area _DATA
Conflicting flags in area _CODE
Conflicting flags in area _DATA
Conflicting flags in area _CODE
Conflicting flags in area _DATA
モニタ上のlコマンドで読みこみます。A000をコールします
>l 20A000003EFEF533CD2DA033211AA0463E01F533C533CD20A0F1210000C91E1FCD1C00C938
20A02000210200394E237EED79210000C9210200393E00D31F3ECFD31F7ED31F3E00D31F5A
08A040003E00D31F210000C9FE
00000001FF
OK
>
>c a000 y:
>
つぎは、LEDの点滅を行ってみます。
時間稼ぎのルーチンとして、sleep関数を作成ました。
NOPとDJNZを使用して約1mSの時間稼ぎを行っています。
この1mSのループをDEレジスタでカウントダウンしています。
sleep(0x0001)で約1mS、sleep(0xffff)で約65535mSです。
;; uint8_t sleep(uint16_t)
;; TMPZ84C015 CPU
;; 24.576MHz clock sleep(0x0001) = 1mS
_sleep::
ld hl, #0x0002
add hl, sp
ld e, (hl)
inc hl
ld d, (hl)
loop1:
ld b, #248
loop2: nop
nop
nop
nop
nop
nop
nop
nop
nop
djnz loop2
dec de
ld a,d
or e
jr nz, loop1
ld hl, #0x0000
ret
上記のプログラムをsleep.asmで保存した後、アセンブルし、sleep.relを取得します。
$ asz80 -l -s -o sleep.asm
時間稼ぎのルーチンができたので、0.5秒周期で点滅させたいと思います。
// TMPZ84C015 piob bit0 tenmetu(0.5S)
//
#include <stdint.h>
extern uint8_t piob_init(uint8_t);
extern uint8_t io_out(uint8_t,uint8_t);
extern uint8_t sleep(uint16_t);
const uint8_t PortBD = 0x1e;
const uint8_t PortBC = 0x1f;
int main(void){
piob_init(0b11111110); //port PB0 output
while(1){
io_out(PortBD,0b00000001); //port b bit0 High output
sleep(500); // 500mS wait
io_out(PortBD,0b00000000); //port b bit0 low output
sleep(500); //500mS wait
}
return(0);
}
コンパイルします。
return(0)で warning が出ますが無視しています。
$ sdcc -mz80 --out-fmt-ihx --code-loc 0xa000 --no-std-crt0 -o piob_tenmetu.ihx mycrt0.rel piob_tenmetu.c io_out.rel piob_init.rel sleep.rel
piob_tenmetu.c:21: warning 126: unreachable code
Conflicting flags in area _CODE
Conflicting flags in area _DATA
Conflicting flags in area _CODE
Conflicting flags in area _DATA
Conflicting flags in area _CODE
Conflicting flags in area _DATA
Conflicting flags in area _CODE
Conflicting flags in area _DATA
モニタ上のlコマンドで読みこみます。A000をコールします
>l
20A000003EFEF533CD46A0332133A0463E01F533C533CD39A021F401E3CD61A0F12133A00B
20A0200046AFF533C533CD39A021F401E3CD61A0F118D51E1FCD3500C9210200394E237E6D
20A04000ED79210000C9210200393E00D31F3ECFD31F7ED31F3E00D31F3E00D31F21000034
1EA06000C9210200395E235606F800000000000000000010F51B7AB320EE210000C9A3
00000001FF
OK
>
>c a000 y:
無限ルーチンのためモニタには帰ってきません。
0.5秒周期でLEDが点滅します。
次回はLCDのC言語プログラムを作成したいと思います。