[MSX] Z88DKでC言語プログラムからBIOSを呼び出す #MSX #MSX0 12月 23, 2023 リンクを取得 Facebook Twitter Pinterest メール 他のアプリ - [Z88DKでC言語プログラムからBIOSを呼び出す方法](#Z88DKでC言語プログラムからBIOSを呼び出す方法) - [アセンブラのソースをCソース内に記述する](#アセンブラのソースをCソース内に記述する) - [関数内でアセンブラソースを書く](#関数内でアセンブラソースを書く) - [関数の引数をアセンブラと連携する](#関数の引数をアセンブラと連携する) - [関数の戻り値をアセンブラと連携する](#関数の戻り値をアセンブラと連携する) - [その他](#その他) - [利用例](#利用例) ## Z88DKでC言語プログラムからBIOSを呼び出す方法 Z88DKではC言語内にアセンブラのソースを書くことができ、C言語の関数・戻り値と連動させることもできます。 Z88DK自体のインストールは、別エントリを参照ください。 __[MSX] Z88DKを使ってC言語でマシン語プログラムをつくる #MSX #MSX0__ [https://mobileff.blogspot.com/2023/12/msx-z88dkc-msx-msx0.html](https://mobileff.blogspot.com/2023/12/msx-z88dkc-msx-msx0.html) ## アセンブラのソースをCソース内に記述する C言語ソース内に`#asm`〜`#endasm`で囲った内部にはアセンブラのソースを書くことができます。 EQUなどの定数定義は、ソースコードの先頭に書いておけば、以後は関数内でも参照することが可能です。 アセンブラの記法はZASMのものがそのまま使えるような印象です。 __定数定義の例__ ```asm #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`で囲みます。 ```asm void msx_cls() { #asm CALL CLS ; BIOS(CLS)をコール(定数CLSはEQUで定義済みの前提) RET ; リターン #endasm } ``` ### 関数の引数をアセンブラと連携する 関数の引数は、以下のようにして任意のレジスタに格納して使うことができます。 ```c 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バイト後ろから引数の内容が格納されていきます。 ただし、引数が複数あるときは、引数順と逆順に格納されていくので、取り出しかたに注意が必要です。 ```c 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レジスタをロードしておくと良いと思います。 ```c 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 #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 ```c #include #include "mymsx.h" void main() { msx_println("Hello!MSX0!"); char* str = msx_input_with_prompt("INPUT TEXT="); msx_print(str); } ``` コンパイルは、以下のようなコマンドを使います。 ```shell zcc +msx -create-app hello.c mymsx.c -subtype=disk -o hello.bin -DAMALLOC -lm ``` コンパイル後に、作成された`hello.msx`をMSX本体に転送します。 Disk BASICでは、 ```BASIC bload "hello.msx",r ``` でプログラムを実行できます。 ※`hello.bin`というファイルも生成されていますが、こちらはBSAVE形式ではないため、BASICで直接呼び出すことはできませんのでご注意ください。 コメント
コメント
コメントを投稿