Z88DKでC言語プログラムからBIOSを呼び出す方法
Z88DKではC言語内にアセンブラのソースを書くことができ、C言語の関数・戻り値と連動させることもできます。
Z88DK自体のインストールは、別エントリを参照ください。
[MSX] Z88DKを使ってC言語でマシン語プログラムをつくる #MSX #MSX0
https://mobileff.blogspot.com/2023/12/msx-z88dkc-msx-msx0.html
アセンブラのソースをCソース内に記述する
C言語ソース内に#asm
〜#endasm
で囲った内部にはアセンブラのソースを書くことができます。
EQUなどの定数定義は、ソースコードの先頭に書いておけば、以後は関数内でも参照することが可能です。
アセンブラの記法はZASMのものがそのまま使えるような印象です。
定数定義の例
#asm
WRSLT EQU 0014H ;MAIN-ROM BIOS CALL WRSLT
CALSLT EQU 001CH ;MAIN-ROM BIOS CALL CALSLT
ENASLT EQU 0024H ;MAIN-ROM BIOS CALL ENASLT
CHGET EQU 009FH ;MAIN-ROM BIOS CALL CHGET
CHPUT EQU 00A2H ;MAIN-ROM BIOS CALL CHPUT
CLS EQU 00C3H ;MAIN-ROM BIOS CALL CLS
EXPTBL EQU 0FCC1H ; EXPTBL
SYSTEMCALL EQU 0F37DH ; DISK-BASIC SYSTEM CALL
CREATE_FILE EQU 016H ;
SET_DMA EQU 01AH ;
OPEN_FILE EQU 00FH ;
RND_WRITE EQU 026H ;
RND_READ EQU 027H ;
CLOSE_FILE EQU 010H ;
#endasm
関数内でアセンブラソースを書く
関数内でアセンブラを実行する場合、関数定義内で#asm
〜#endasm
で囲みます。
void msx_cls() {
#asm
CALL CLS ; BIOS(CLS)をコール(定数CLSはEQUで定義済みの前提)
RET ; リターン
#endasm
}
関数の引数をアセンブラと連携する
関数の引数は、以下のようにして任意のレジスタに格納して使うことができます。
void msx_chput(char c) {
#asm
LD IX, 2 ; SPに加算したい値をIXにロード
ADD IX, SP ; IXにSPの値を加算(SP+2をIXに代入)
LD A, (IX) ; (IX)のメモリ内容をAレジスタにロード
CALL CHPUT ; BIOS(CHPUT)をコール(定数CHPUTはEQUで定義済みの前提)
RET
#endasm
}
スタックポインタSPの2バイト後ろから引数の内容が格納されていきます。 ただし、引数が複数あるときは、引数順と逆順に格納されていくので、取り出しかたに注意が必要です。
void msx_ldir_mem2dma(char* dma_buffer,unsigned short int record_size,unsigned short int read_address) {
#asm
; 関数に渡された引数の値を取り出し
LD IX, 2
ADD IX, SP
LD D, (IX+5) ; 引数 dma_bufferの上位バイト
LD E, (IX+4) ; 引数 dma_bufferの下位バイト
LD B, (IX+3) ; 引数 record_sizeの上位バイト
LD C, (IX+2) ; 引数 record_sizeの下位バイト
LD H, (IX+1) ; 引数 read_addressの上位バイト
LD L, (IX) ; 引数 read_addressの下位バイト
LDIR
#endasm
}
このように最後の引数を(IX)
とし、前の引数は(IX+1)
、(IX+2)
・・・のように指定して格納していきます。
関数の戻り値をアセンブラと連携する
戻り値については、int固定のようで、リターン直前にHLレジスタに格納された値が戻り値となります。 BIOSコールの場合は結果がAレジスタ(1バイト)で得られるものも多いですが、その場合はHレジスタに0をロードし、LレジスタにAレジスタをロードしておくと良いと思います。
char msx_chget() {
#asm
CALL CHGET
LD H,0x00
LD L,A
RET
#endasm
}
その他
関数内でアセンブラソースとCソースを混在させることも可能とは思いますが、関数内のアセンブラと関数内のCプログラムで値を連携する方法は調べていないのでよくわかりません。
利用例
上記に出てきたような関数を作って、テキスト表示と文字入力をMSX BIOSコールで作ってみると以下のようになります。
mymsx.h
void msx_chput(char c);
void msx_chput_CRLF();
void msx_cls();
void msx_print(char[] str);
void msx_println(char[] str);
char msx_chget();
char* msx_input();
char* msx_input_line_headless();
char* msx_input_with_prompt(char[] prompt);
mymsx.c
#include <stdlib.h>
#include "mymsx.h"
#asm
CHGET EQU 009FH ;MAIN-ROM BIOS CALL CHGET
CHPUT EQU 00A2H ;MAIN-ROM BIOS CALL CHPUT
CLS EQU 00C3H ;MAIN-ROM BIOS CALL CLS
#endasm
char* input_buffer = NULL;
void msx_chput(char c) {
#asm
LD IX, 2
ADD IX, SP
LD A, (IX)
CALL CHPUT
RET
#endasm
}
void msx_chput_CRLF() {
msx_chput(0x0D);
msx_chput(0x0A);
}
void msx_cls() {
#asm
CALL CLS
RET
#endasm
}
void msx_print(char[] str) {
for(int i=0;i<256;i++) {
if(str[i]==0) {
return;
}
msx_chput(str[i]);
}
}
void msx_println(char[] str) {
msx_print(str);
msx_chput_CRLF();
}
// 1文字入力
char msx_chget() {
#asm
CALL CHGET
LD H,0x00
LD L,A
RET
#endasm
}
// 1行入力
char* msx_input() {
msx_chput('?');
if (input_buffer == NULL) {
input_buffer = (char*)malloc(sizeof(char)*256);
}
for(int i=0;i<256;i++) {
input_buffer[i]=0;
}
int index=0;
while(index<256) {
char s = msx_chget();
if (s==0x0D) {
msx_chput_CRLF();
input_buffer[index]=0;
return input_buffer;
}
if (s==0x08 && index>0) {
index--;
}
input_buffer[index++]=s;
msx_chput(s);
}
return input_buffer;
}
// 1行入力
char* msx_input_line_headless() {
if (input_buffer == NULL) {
input_buffer = (char*)malloc(sizeof(char)*256);
}
for(int i=0;i<256;i++) {
input_buffer[i]=0;
}
int index=0;
while(index<256) {
char s = msx_chget();
if (s==0x0D) {
input_buffer[index]=0;
return input_buffer;
}
input_buffer[index++]=s;
}
return input_buffer;
}
char* msx_input_with_prompt(char[] prompt) {
msx_print(prompt);
return msx_input();
// return prompt;
}
hello.c
#include <stdlib.h>
#include "mymsx.h"
void main() {
msx_println("Hello!MSX0!");
char* str = msx_input_with_prompt("INPUT TEXT=");
msx_print(str);
}
コンパイルは、以下のようなコマンドを使います。
zcc +msx -create-app hello.c mymsx.c -subtype=disk -o hello.bin -DAMALLOC -lm
コンパイル後に、作成されたhello.msx
をMSX本体に転送します。
Disk BASICでは、
bload "hello.msx",r
でプログラムを実行できます。
※hello.bin
というファイルも生成されていますが、こちらはBSAVE形式ではないため、BASICで直接呼び出すことはできませんのでご注意ください。
コメント
コメントを投稿