目次
前
次
MiniCを復活
Z80関係の開発ツール一式の中に、SYSTEM LOAD社の
「MiniC」の評価版が含まれていたので、これを
利用して、Z80のファームウエア開発を短縮する
ことにします。
(現在ネット検索で出てくるSYSTEM LOAD社ではありません。)
MiniCは、Cのソースコードをコンパイルし
Z80ニモニックのアセンブリ言語コードを
生成するだけなので、アセンブラ、リンカ
が別途必要になります。
開発ツールの中には、SYSTEM LOAD社のアセンブラ
リンカであるXA80があったので、この2つでHEX
ファイルを生成し、ROMライターでファームウエア
を作成します。
コンパイル、アセンブル、リンクと続く処理を
ひとつのバッチファイルmmc.batにまとめました。
mc %1.c
xa80 %1 %1 %1 %1
Windowsが動作しているPersonalComputerでは、DOS窓
を利用して、バッチ処理で、HEXファイルまで生成でき
ます。
MiniCの仕様を読むと、スタートアップルーチンは
ヘッダファイルに記述することと、I/O関係は自作
して対応すると分かりました。
I/O関係は、入力関数をinportb、出力関数をoutportb
とし、TurboCの仕様に合わせて、定義します。
void outportb(int port,int data)
{
#asm
push af
push bc
push hl
ld hl,8
add hl,sp
ld a,(hl)
ld hl,10
add hl,sp
ld c,(hl)
out (c),a
pop hl
pop bc
pop af
#endasm
}
int inportb(int port)
{
#asm
push af
push bc
ld hl,6
add hl,sp
ld c,(hl)
in a,(c)
ld l,a
ld h,0
pop bc
pop af
#endasm
}
Cは、スタックを利用し関数と関数の
間で値を渡します。
スタックに値を入れる方式は、パラメータ
を左から順に、データサイズ分だけ、積み
上げていきます。レジスタペアHL、スタック
ポインタSPの演算で、データの在りかを求め
転送します。
関数からの戻り値は、HLレジスタペアに入れる
ので、in命令でデータを入力したなら、2つの
レジスタH、Lに転送しておきます。
ヘッダファイルdos.hに、この内容をいれるため
次のように記述します。
/* dos.h */
void outportb(int address,int data)
{
#asm
push af
push bc
push hl
;
ld hl,8
add hl,sp
ld a,(hl)
;
ld hl,10
add hl,sp
;
ld c,(hl)
out (c),a
;
pop hl
pop bc
pop af
#endasm
}
int inportb(int address)
{
#asm
push af
push bc
;
ld hl,6
add hl,sp
;
ld c,(hl)
in a,(c)
;
ld l,a
ld h,0
;
pop bc
pop af
#endasm
}
PIOAを入力、PIOBを出力にし、スイッチとLEDを
接続して、入力の状態を出力に反映する処理を
記述してみます。
#include "dos.h"
#include "aki80c.h"
#define OFF 0
#define ON OFF+1
#define MASKFF 0x00ff
/* function prototype */
void init();
void main()
{
int tmp ;
/* initialize */
init();
/* endless loop */
while ( ON ) {
/* get data */
tmp = inportb(PAD);
/* invert */
tmp ^= MASKFF ;
/* impress */
outportb(PBD,tmp);
}
}
void init()
{
/* PIOA (mode 1) input no interrupt */
outportb(PAC,0x4f);
outportb(PAC,0x03);
/* PIOB all zero */
outportb(PBD,0x00);
/* PIOB (mode 0) output no interrupt */
outportb(PBC,0x0f);
outportb(PBC,0x03);
}
利用しているヘッダファイルのうち、aki80c.hは
CTC、PIO、SIOのI/Oアドレスを定義しています。
/* counter / timer */
#define CTC0 0x10
#define CTC1 CTC0+1
#define CTC2 CTC0+2
#define CTC3 CTC0+3
/* serial I/O */
#define CHAB 0x18
#define CHACS CHAB+1
#define CHBB 0x1A
#define CHBCS CHBB+1
/* parallel I/O */
#define PAD 0x1C
#define PAC PAD+1
#define PBD PAD+2
#define PBC PAD+3
コンパイルし、アセンブリ言語生成させると
以下のようになっていました。
;System Load Compiler [mini-'C' V1.0] for Z80/KC80 1995.3
;By System Load CO.,LTD. & K.Kino Copyright (C) All rights reserved
;
include "startup.h"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; [SLC] liblary file -- [ OLIBZ.LIB ] ;
; File make time: Tue Feb 14 1995 ;
; ... by System Load.,ltd ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
BDOS EQU 00005h
;
JP __CPM ;
JP MC_GCHAR ;
JP MC_SXT ;
JP MC_CDR ;
JP MC_GINT ;
JP MC_PINT ;
JP MC_OR ;
JP MC_OROR ;
JP MC_XOR ;
JP MC_AND ;
JP MC_ANDAND ;
JP MC_EQ ;
JP MC_NE ;
JP MC_GT ;
JP MC_LE ;
JP MC_GE ;
JP MC_LT ;
JP MC_CMP ;
JP MC_LNEG ;
JP MC_UGE ;
JP MC_ULT ;
JP MC_UGT ;
JP MC_ULE ;
JP MC_UCMP ;
JP CUCMP1 ;
JP MC_ASR ;
JP MC_ASL ;
JP MC_ASL4 ;
JP MC_SUB ;
JP MC_NEG ;
JP MC_COM ;
JP MC_MULT ;
JP MC_MLT1 ;
JP CMLT2 ;
JP MC_DIV ;
JP MC_DIV1 ;
JP MC_DIV2 ;
JP MC_DIV3 ;
JP MC_DENEG ;
JP MC_BCNEG ;
JP MC_RDEL ;
JP MC_PBCDE ;
JP DEBUGSP ;
__CPM:
POP HL ;
POP DE ;
POP BC ;
PUSH BC ;
PUSH DE ;
PUSH HL ;
CALL BDOS ;
JR MC_SXT ;
MC_GCHAR:
LD A,(HL) ;
MC_SXT:
LD L,A ;
RLCA ;
SBC A,A ;
LD H,A ;
RET ;
MC_CDR:
INC HL ;
INC HL ;
MC_GINT:
LD A,(HL) ;
INC HL ;
LD H,(HL) ;
LD L,A ;
RET ;
MC_PINT:
POP BC ;
POP DE ;
PUSH BC ;
LD A,L ;
LD (DE),A ;
INC DE ;
LD A,H ;
LD (DE),A ;
RET ;
MC_OROR:
POP BC ;
POP DE ;
PUSH BC ;
LD A,L ;
OR H ;
JP NZ,__OR2R ;
LD A,E ;
OR D ;
JP NZ,__OR2R ;
LD HL,0 ;
RET ;
__OR2R: ;
LD HL,1 ;
RET
MC_OR:
POP BC ;
POP DE ;
PUSH BC ;
LD A,L ;
OR E ;
LD L,A ;
LD A,H ;
OR D ;
LD H,A ;
RET ;
MC_XOR:
POP BC ;
POP DE ;
PUSH BC ;
LD A,L ;
XOR E ;
LD L,A ;
LD A,H ;
XOR D ;
LD H,A ;
RET ;
MC_ANDAND:
POP BC ;
POP DE ;
PUSH BC ;
LD A,L ;
OR H ;
JP Z,__AND2R ;
LD A,E ;
OR D ;
JP Z,__AND2R ;
LD HL,1 ;
RET
__AND2R: ;
LD HL,0 ;
RET ;
MC_AND:
POP BC ;
POP DE ;
PUSH BC ;
LD A,L ;
AND E ;
LD L,A ;
LD A,H ;
AND D ;
LD H,A ;
RET ;
MC_EQ:
POP BC ;
POP DE ;
PUSH BC ;
CALL MC_CMP ;
RET Z ;
DEC HL ;
RET ;
MC_NE:
POP BC ;
POP DE ;
PUSH BC ;
CALL MC_CMP ;
RET NZ ;
DEC HL ;
RET ;
MC_GT:
POP BC ;
POP DE ;
PUSH BC ;
EX DE,HL ;
CALL MC_CMP ;
RET C ;
DEC HL ;
RET ;
MC_LE:
POP BC ;
POP DE ;
PUSH BC ;
CALL MC_CMP ;
RET Z ;
RET C ;
DEC HL ;
RET ;
MC_GE:
POP BC ;
POP DE ;
PUSH BC ;
CALL MC_CMP ;
RET NC ;
DEC HL ;
RET ;
MC_LT:
POP BC ;
POP DE ;
PUSH BC ;
CALL MC_CMP ;
RET C ;
DEC HL ;
RET ;
MC_CMP:
LD A,E ;
SUB L ;
LD E,A ;
LD A,D ;
SBC A,H ;
LD HL,00001H ;
JP M,MC_CMP1 ;
OR E ;
RET ;
MC_CMP1:
OR E ;
SCF ;
RET ;
MC_LNEG:
LD A,H
OR L
LD HL,0
JR Z,MC_LNEG1
RET
MC_LNEG1:
INC L
RET
MC_UGE:
POP BC ;
POP DE ;
PUSH BC ;
CALL MC_UCMP ;
RET NC ;
DEC HL ;
RET ;
MC_ULT:
POP BC ;
POP DE ;
PUSH BC ;
CALL MC_UCMP ;
RET C ;
DEC HL ;
RET ;
MC_UGT:
POP BC ;
POP DE ;
PUSH BC ;
EX DE,HL ;
CALL MC_UCMP ;
RET C ;
DEC HL ;
RET ;
MC_ULE:
POP BC ;
POP DE ;
PUSH BC ;
CALL MC_UCMP ;
RET Z ;
RET C ;
DEC HL ;
RET ;
MC_UCMP:
LD A,D ;
CP H ;
JR NZ,CUCMP1 ;
LD A,E ;
CP L ;
CUCMP1:
LD HL,00001H ;
RET ;
MC_ASR:
EX DE,HL ;
DEC E ;
RET M ;
LD A,H ;
RLA ;
LD A,H ;
RRA ;
LD H,A ;
LD A,L ;
RRA ;
LD L,A ;
JR MC_ASR+1 ;
MC_ASL:
POP BC ;
POP DE ;
PUSH BC ;
EX DE,HL ;
MC_ASL4:
DEC E ;
RET M ;
ADD HL,HL ;
JR MC_ASL4 ;
MC_SUB:
POP BC ;
POP DE ;
PUSH BC ;
LD A,E ;
SUB L ;
LD L,A ;
LD A,D ;
SBC A,H ;
LD H,A ;
RET ;
MC_NEG:
CALL MC_COM ;
INC HL ;
RET ;
MC_COM:
LD A,H ;
CPL ;
LD H,A ;
LD A,L ;
CPL ;
LD L,A ;
RET ;
MC_MULT:
POP BC ;
POP DE ;
PUSH BC ;
LD B,H ;
LD C,L ;
LD HL,00000H ;
MC_MLT1:
LD A,C ;
RRCA ;
JR NC,CMLT2 ;
ADD HL,DE ;
CMLT2:
XOR A ;
LD A,B ;
RRA ;
LD B,A ;
LD A,C ;
RRA ;
LD C,A ;
OR B ;
RET Z ;
XOR A ;
LD A,E ;
RLA ;
LD E,A ;
LD A,D ;
RLA ;
LD D,A ;
OR E ;
RET Z ;
JR MC_MLT1 ;
MC_DIV:
LD B,H ;
LD C,L ;
LD A,D ;
XOR B ;
PUSH AF ;
LD A,D ;
OR A ;
CALL M,MC_DENEG ;
LD A,B ;
OR A ;
CALL M,MC_BCNEG ;
LD A,010H ;
PUSH AF ;
EX DE,HL ;
LD DE,00000H ;
MC_DIV1:
ADD HL,HL ;
CALL MC_RDEL ;
JR Z,MC_DIV2 ;
CALL MC_PBCDE ;
JP M,MC_DIV2 ;
LD A,L ;
OR 001H ;
LD L,A ;
LD A,E ;
SUB C ;
LD E,A ;
LD A,D ;
SBC A,B ;
LD D,A ;
MC_DIV2:
POP AF ;
DEC A ;
JR Z,MC_DIV3 ;
PUSH AF ;
JR MC_DIV1 ;
MC_DIV3:
POP AF ;
RET P ;
CALL MC_DENEG ;
EX DE,HL ;
CALL MC_DENEG ;
EX DE,HL ;
RET ;
MC_DENEG:
LD A,D ;
CPL ;
LD D,A ;
LD A,E ;
CPL ;
LD E,A ;
INC DE ;
RET ;
MC_BCNEG:
LD A,B ;
CPL ;
LD B,A ;
LD A,C ;
CPL ;
LD C,A ;
INC BC ;
RET ;
MC_RDEL:
LD A,E ;
RLA ;
LD E,A ;
LD A,D ;
RLA ;
LD D,A ;
OR E ;
RET ;
MC_PBCDE:
LD A,E ;
SUB C ;
LD A,D ;
SBC A,B ;
RET ;
MC_SWITCH:
EX DE,HL
POP HL
SWLOOP:
LD C,(HL)
INC HL
LD B,(HL)
INC HL
LD A,B
OR C
JR Z,SWEND
LD A,(HL)
INC HL
CP E
LD A,(HL)
INC HL
JR NZ,SWLOOP
CP D
JR NZ,SWLOOP
LD H,B
LD L,C
SWEND:
JP (HL)
DEBUGSP:
LD HL,2
DEBUGSP2:
ADD HL,SP
PUSH HL
POP IY
RET
;;;;;;;;; END IOLIBZ.LIB ;;;;;;;;;;;;;;
__outportb:
;#asm
push af
push bc
push hl
;
ld hl,8
add hl,sp
ld a,(hl)
;
ld hl,10
add hl,sp
;
ld c,(hl)
out (c),a
;
pop hl
pop bc
pop af
;#endasm
RET
__inportb:
;#asm
push af
push bc
;
ld hl,6
add hl,sp
;
ld c,(hl)
in a,(c)
;
ld l,a
ld h,0
;
pop bc
pop af
;#endasm
RET
__main:
PUSH BC
CALL __init
mc_4:
LD HL,00H
PUSH HL
LD HL,01H
POP DE
ADD HL,DE
LD A,H
OR L
JP Z,mc_5
LD HL,00H
ADD HL,SP
PUSH HL
LD HL,1CH
PUSH HL
CALL __inportb
POP BC
CALL mc_pint
LD HL,00H
ADD HL,SP
PUSH HL
CALL mc_gint
PUSH HL
LD HL,0FFH
CALL mc_xor
CALL mc_pint
LD HL,1CH
PUSH HL
LD HL,02H
POP DE
ADD HL,DE
PUSH HL
LD HL,02H
ADD HL,SP
CALL mc_gint
PUSH HL
CALL __outportb
POP BC
POP BC
JP mc_4
mc_5:
POP BC
RET
__init:
LD HL,1CH
PUSH HL
LD HL,01H
POP DE
ADD HL,DE
PUSH HL
LD HL,4FH
PUSH HL
CALL __outportb
POP BC
POP BC
LD HL,1CH
PUSH HL
LD HL,01H
POP DE
ADD HL,DE
PUSH HL
LD HL,03H
PUSH HL
CALL __outportb
POP BC
POP BC
LD HL,1CH
PUSH HL
LD HL,02H
POP DE
ADD HL,DE
PUSH HL
LD HL,00H
PUSH HL
CALL __outportb
POP BC
POP BC
LD HL,1CH
PUSH HL
LD HL,03H
POP DE
ADD HL,DE
PUSH HL
LD HL,0FH
PUSH HL
CALL __outportb
POP BC
POP BC
LD HL,1CH
PUSH HL
LD HL,03H
POP DE
ADD HL,DE
PUSH HL
LD HL,03H
PUSH HL
CALL __outportb
POP BC
POP BC
RET
END
;; --- End of Compilation ---
ざっと眺めて、評価版のMiniCコンパイラでは
どういうことをやっているのか理解します。
ヘッダファイルstartup.hを利用し、ROM、RAMの
割当てアドレスを確認しています。
ヘッダファイルstartup.hは、次のように定義
されています。
CPM EQU 0
if (CPM EQ 0)
;RAM_TOP EQU 0fe00h
RAM_TOP EQU 8000h
;
ORG 0h
DI
LD SP,0
CALL __main
__st_loop__:
DI
nop
JR __st_loop__
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
8080やZ80用のOSとして、CP/M80の存在を忘れることが
できないので、MiniCでもCP/M80が動いている環境で
走るプログラムか、それ以外かを判断しています。
CP/M80では、プログラムは必ず100hから始まるので
00h〜FFhをスキップするかどうかを指定できます。
他に、RAMとスタックの存在エリアを利用できる
ようにしてあります。
MiniCでは、内部ラベルの前と後にアンダースコアを
2つ付加するのが、お約束のようです。
Cのソースをアセンブリ言語に展開するために、必要な
各種サブルーチンは、MC_で始まるラベルを使います。
機能拡張をする場合、ヘッダファイルを定義し、その中に
必要な関数を記述するスタイルを採用しています。
生成されたアセンブリ言語から、理解できることを
挙げてみます。
Cはパラメータを値渡しし、参照渡しではないので
スタック上に関数の引数を、どういう方式で渡す
のか、そのカラクリがわかるアセンブリ言語ソース
コードを生成しています。
MiniCを使い、ハードウエアを扱うファーム
ウエアを作成してみます。
目次
前
次