- 追加された行はこのように表示されます。
- 削除された行は
このように表示されます。
{{include unfinished_page}}
,{t},{BSIZE:1 0|SIZE:100%}
,{BSIZE:0}←[[4章 BASICの内部構造]] ,{BSIZE:0|RIGHT}[[6章 内部ルーチン]] →
これまで説明したように、MSX BASICは強力な機能を備えていますが、さらに実行速度を上げたり、MSX2のハードウェア機能を限界まで引き出す場合は、マシン語を使用しなければなりません。この章では、その必要が生じた時のために、BASICからマシン語プログラムを呼び出す方法、およびその際に知っておくべき情報について説明します。
!!5.1 USR関数
BASICからのマシン語の呼び出しは、以下の手順で行います。USR関数のカッコの中の値は、引数としてマシン語に渡すためのものです。引数は数式、文字式いずれでもかまいません。
+DEF USR文で、マシン語プログラムの実行開始アドレスを指定する。
+USR関数でマシン語を呼び出す
+マシン語からBASICに戻る場合はRET命令(C9H)を実行する
,例{SIZE:100|CENTER}
C000H番地が実行アドレスであるマシン語プログラムを呼び出す。
DEF USR=&HC000
A=USR(0)
!!5.2 USR関数の引数と戻り値によるデータの受け渡し
BASICからマシン語に引数を渡す場合、マシン語側ではレジスタAの内容でその型を判断します(表2.18)。目的の値は引数の型により、図2.27のような形式で格納されていますから、それに応じて値を受け取ります。例として、文字列型の引数を受け取るプログラムをリスト2.5に示します。
'''表2.18 レジスタAに代入される引数の型'''
,{t},{BSIZE:2 0}
,{c},{BSIZE:0|SIZE:20},{BSIZE:0|SIZE:60},{BSIZE:0|SIZE:300}
,,2 ,2バイト整数型
,,3 ,文字列型
,,4 ,単精度実数型
,,8 ,倍精度実数型
{{svg 図2.27.svg}}
'''図2.27 引数による値のわたされ方'''
'''リスト2.5 文字型の引数の使用例'''
;
; USR関数で渡した文字列をそのまま表示する
; 使用法:DEF USR=&Hxxxx: A$=USR("STRING")
;
chput equ 00a2h ; character output
rdarg::
cp 3
ret nz ; parameter is not string
push de
pop ix ; IX:= string descripter
ld a,(ix+0) ; get string length
ld l,(ix+1) ; get string pointer(low)
ld h,(ix+2) ; get string pointer(high)
or a
ret z ; if length = 0
rd1:
push af
ld a,(hl) ; get a character
call chput ; get a character
pop af
dec a
ret z
inc hl
jr rd1
end
逆に、引数として渡された値をマシン語の中で処理することによって、USR関数の値としてBASICに引き渡すこともできます。この場合、【VALTYP(0F663H)】を変更することにより、戻り値をBASICからの引数の型以外のものに変更することも可能です。ただし、文字列型の場合、文字数を変えることはできません。
!!5.3 命令の増設
MSXでは現在「CMD」と「IPL」という予約語が未使用となっており、この予約語のフック(それぞれFE0DH、FE03H)を自分で作成した任意のマシン語ルーチンにジャンプするよう書き換えるだけで、新しい命令を作成することができます。簡単な例をリスト2.6に示します。
'''リスト 2.6 CMD命令の増設'''
;
; CMDコマンドの使用法(CAPSのON/OFF)
; コマンドの初期化: DEF USR=&Hxxxx: A=USR(0)
; コマンドの使用法: CMD
;
chgcap equ 0132h ; CAPS LAMP on/off
capst equ 0fcabh ; CAPS LOCK status
hcmd equ 0fe0dh ; CMD HOOK
; CMD コマンドの増設
start::
ld bc,5 ;NEW HOOK SET
ld de,hcmd
ld hl,hdat
ldir
ret
; HOOK に書き込むデータ
hdat:
pop af
jp capkey
nop
; CMD コマンドの実体
capkey:
call chgcap
ld a,(capst)
cpl
ld (capst),a
ret
end
フックに書き込む最初の「pop af」は、「CMD」の実行時にスタックに積まれるエラー処理アドレスを捨てるためのものです。これを入れておかないと、「RET」命令でBASICに戻るはずが、エラー処理ルーチンにジャンプしてしまいます。ユーザールーチン内でエラーを出したい場合は、このアドレスを取っておくのも1つの方法です。
なお、これらのフックは本来は将来の拡張用にリザーブしてある場所なので、商用のアプリケーションプログラムで使用してはいけません。
!!5.4 BASICの内部ルーチン
より複雑なステートメントの拡張を行うにはCMD命令にも引数を渡せると便利です。マシン語を呼び出した際、HLレジスタがBASICテキスト中の「CMD」の次の位置を指していますから、これを利用して後に続く文字列の評価を行えば目的は果たせます。これらを行うために有効な内部ルーチンを以下で説明します。
より複雑なステートメントの拡張を行うには、CMD命令にも引数を渡せると便利です。マシン語を呼び出した際、HLレジスタがBASICテキスト中の「CMD」の次の位置を指していますから、これを利用して後に続く文字列の評価を行えば目的は果たせます。これらを行うために有効な内部ルーチンを以下で説明します。
----
!CHRGTR(4666H/MAIN){{anchor ABS}}
,機 能{SIZE:100|CENTER}
テキストから1文字を取り出します。
テキストから1文字取り出します。
,コール手順{SIZE:100|CENTER}
HL テキストを指すアドレス
,戻り値{SIZE:100|CENTER}
HL 取り出した文字のアドレス
A 取り出した文字
Zフラグ 文末(「:」または00H)ならばON
CYフラグ 0〜9ならばON
,変更レジスタ{SIZE:100|CENTER}
,解 説{SIZE:100|CENTER}
(HL+1)の場所のテキストから1文字取り出します。スペースはスキップされます。
[図の挿入]
{{svg CHRGTR.svg}}
!FRMEVL(4C64H/MAIN){{anchor ABS}}
,機 能{SIZE:100|CENTER}
テキスト中の式を評価します。
,コール手順{SIZE:100|CENTER}
HL テキスト中の式の先頭アドレス
HL テキスト中の式の先頭アドレス
,戻り値{SIZE:100|CENTER}
HL 式の次のアドレス
HL 式の次のアドレス
【VALTYP(0F663H)】 型に応じて2、3、4、8の値が入る
【DAC(0F7F6H)】 式の評価結果
【DAC(0F7F6H)】 式の評価結果
,変更レジスタ{SIZE:100|CENTER}
すべて
,解 説{SIZE:100|CENTER}
式を評価し、型に応じて出力します。[[5.2 USR関数の引数と戻り値によるデータの受け渡し|http://ngs.no.coocan.jp/doc/wiki.cgi/datapack?page=5%BE%CF+%A5%DE%A5%B7%A5%F3%B8%EC%A4%C8%A4%CE%A5%EA%A5%F3%A5%AF]]
参照。
[図の挿入]
式を評価し、型に応じて出力します。5.2 USR関数の引数と戻り値によるデータの受け渡し参照。
{{svg FRMEVL.svg}}
!FRMQNT(542FH/MAIN){{anchor ABS}}
,機 能{SIZE:100|CENTER}
式を2バイト整数型で評価します。
,コール手順{SIZE:100|CENTER}
HL テキスト中の式の先頭アドレス
HL テキスト中の式の先頭アドレス
,戻り値{SIZE:100|CENTER}
HL 式の次のアドレス
DE 式の評価結果
HL 式の次のアドレス
DE 式の評価結果
,変更レジスタ{SIZE:100|CENTER}
すべて
,解 説{SIZE:100|CENTER}
式を評価し、整数型(INT)で出力します。結果が2バイト整数型の範囲に納まらない場合には、「Overflow」エラーを発生し、BASICコマンドレベルに戻ります。
!GETBYT(521CH/MAIN){{anchor ABS}}
,機 能{SIZE:100|CENTER}
式を1バイト整数型で評価します。
,コール手順{SIZE:100|CENTER}
HL テキスト中の式の先頭アドレス
HL テキスト中の式の先頭アドレス
,戻り値{SIZE:100|CENTER}
HL 式の次のアドレス
A、E 式の評価結果(AとEには同じ値が入る)
HL 式の次のアドレス
A、E 式の評価結果(AとEには同じ値が入る)
,変更レジスタ{SIZE:100|CENTER}
すべて
,解 説{SIZE:100|CENTER}
式を評価し、1バイト型で出力します。結果が1バイト整数型の範囲に納まらない場合には、「Illegal function call」エラーを発生し、BASICコマンドレベルに戻ります。
!FRESTR(67D0H/MAIN){{anchor ABS}}
,機 能{SIZE:100|CENTER}
文字列を登録します。
,コール手順{SIZE:100|CENTER}
【VALTYP(0F663H)】 型(文字列でないとエラーになる)
【DAC(0F7F6H)】 ストリングディスクリプタへのポインタ
【DAC(0F7F6H)】 ストリングディスクリプタへのポインタ
,戻り値{SIZE:100|CENTER}
HL ストリングディスクリプタへのポインタ
HL ストリングディスクリプタへのポインタ
,変更レジスタ{SIZE:100|CENTER}
すべて
,解 説{SIZE:100|CENTER}
FRMEVLで得た文字列型の結果を文字列領域に登録し、そのストリングディスクリプタを得ます。文字列を評価した場合はFRMEVLと組み合わせて以下のように使用するのが一般的です。
:
:
call fmevl
push hl
push hl ; テキストポインタをセーブする
call FRESTR
ex de,hl
ex de,hl ; ストリングディスクリプタを得る
pop hl
ld a,(de)
ld a,(de) ; 入力された文字列の長さを得る
:
:
!PTRGET(5EA4H/MAIN){{anchor ABS}}
,機 能{SIZE:100|CENTER}
変数の格納アドレスを獲得します。
,コール手順{SIZE:100|CENTER}
HL テキスト中の変数名の先頭アドレス
HL テキスト中の変数名の先頭アドレス
【SUBFLG(0F6A5H)】 0=単純変数、0以外=配列変数
,戻り値{SIZE:100|CENTER}
HL 変数名の次のアドレス
DE 目的の変数の内容が格納されているアドレス
HL 変数名の次のアドレス
DE 目的の変数の内容が格納されているアドレス
,変更レジスタ{SIZE:100|CENTER}
すべて
,解 説{SIZE:100|CENTER}
変数(または配列変数)の格納アドレスを得ます。目的の変数の領域が確保されていなかった場合は、領域の確保も行われます。【SUBFLG(0F6A5H)】の値を0以外に設定しておくと配列の個々の要素ではなく、その配列の先頭アドレスが得られます。
BASICで、USR関数により実行中の機械語プログラムから、BASICの変数(ここではS%)に直接結果を返すには、以下のようにします。
vars:
defd 'S%',0
ld hl,vars
call ptrget
[図の挿入]
{{svg PTRGET.svg}}
!CRUNCH(42B2H/MAIN){{anchor ABS}}
,機 能{SIZE:100|CENTER}
BASICのステートメントを中間コードに変換します。
,コール手順{SIZE:100|CENTER}
HL 00Hで終わるASCII形式のテキストの先頭アドレス
HL 00Hで終わるASCII形式のテキストの先頭アドレス
,戻り値{SIZE:100|CENTER}
【KBUF(0F41FH)】 中間コード
,変更レジスタ{SIZE:100|CENTER}
すべて
,解 説{SIZE:100|CENTER}
BASICステートメントを中間コードに変換します([[表2.17参照]])。
[図の挿入]
{{svg CRUNCH.svg}}
!NEWSTT(4601H/MAIN){{anchor ABS}}
,機 能{SIZE:100|CENTER}
任意のテキストを実行します。
,コール手順{SIZE:100|CENTER}
HL 実行するテキストのアドレス
HL 実行するテキストのアドレス
,戻り値{SIZE:100|CENTER}
なし
,変更レジスタ{SIZE:100|CENTER}
すべて
,解 説{SIZE:100|CENTER}
テキストの実行を行いますテキストは下図の状態となっている必要があります。
[図の挿入]
{{svg NEWSTT.svg}}
これらの内部ルーチンは、BASICテキストを対象とするものであるため、エラーが発生した場合は、BASICと同様のエラー処理が行われます。その場合【H.ERRO(0FFB1H)】の書き換えにより、ユーザーがエラー処理を行うこともできます。その場合は、Eレジスタにエラー番号が入ります。リスト2.7を参照してください。
'''リスト 2.7 エラー処理ルーチンの変更'''
;
; エラー処理ルーチンの変更
; 使用法:DEF USR=&Hxxxx: A=USR(0)
;
herr equ 0ffb1h ; error HOOK
synerr equ 2 ; syntax error code
chput equ 00a2h ; character output BIOS
newstt equ 04601h ; run
readyr equ 0409bh
; エラー処理ルーチンの変更
start::
ld bc,5 ; set new HOOK
ld de,herr
ld hl,hdat
ldir
ret
hdat:
jp error
nop
nop
; エラー処理ルーチンの実体
error:
ld a,e ; when in error,E holds error code
cp synerr ; syntax error?
ret nz ; no
ld hl,data1 ; yes
loop: ld a,(hl) ; put new error message
cp '$'
jr z,exit
push hl
call chput
pop hl
inc hl
jr loop
exit:
jp readyr
data1:
defm 'マチガッテイマス' ; new error start
defb 07h,07h,07h,'$
end
!!5.5 拡張ステートメント
4.4で紹介したのは、「CMD」という未使用の予約語を使って、BASICの機能を拡張する方法でした。しかし、この方法は予約語を使っている関係で、市販のアプリケーションソフトウェアでは使用できません。個々で紹介する拡張ステートメントは、ROMカートリッジでの使用という条件がつきますが、将来にわたって互換性が保証されている方法ですので、市販のソフトウェアで採用するのに最適です。
4.4で紹介したのは、「CMD」という未使用の予約語を使って、BASICの機能を拡張する方法でした。しかし、この方法は予約語を使っている関係で、市販のアプリケーションソフトウェアでは使用できません。ここで紹介する拡張ステートメントは、ROMカートリッジでの使用という条件がつきますが、将来にわたって互換性が保証されている方法ですので、市販のソフトウェアで採用するのに最適です。
拡張ステートメントはCALL命令で始まり、以下の書式で使用します。
CALL <拡張ステートメント名>[(<引数>[,<引数>...])]
拡張ステートメントは、MSXのカートリッジヘッダーのSTATEMENTに拡張ステートメントルーチンのアドレスを書き込むことにより実行できます。その実現方法などについては、「xxx カートリッジヘッダー」をご参照ください。
添付のフロッピーディスクに、BASICの拡張ステートメントのサンプルプログラムが入っています。ファイル名は「FOO_BAR.MAC」です。このソースプログラムを、
M80 = foo_bar
でアセンブルして、
L80 /P:4000,foo_bar,foo_bar /n/x/e
でリンクすると、FOO_BAR.HEXというファイルができます。
このファイルをPROMライターに転送してROMに書き込み、カートリッジにすると「FOO」と「BAR」という拡張ステートメントが使用できるようになります。「FOO」と「BAR」は以下の機能を持っています。
::CALL FOO
,書式{SIZE:100}
,書式{SIZE:100|CENTER}
CALL FOO(<文字変数>,<文字列>)
,機能{SIZE:100}
,機能{SIZE:100|CENTER}
<文字列>を大文字に変換して、<文字変数>に代入します。
,文例{SIZE:100}
,文例{SIZE:100|CENTER}
CALL FOO(A$,"This is an example")
PRINT A$
【表示】THIS IS AN EXAMPLE
::CALL BAR
,書式{SIZE:100}
,書式{SIZE:100|CENTER}
CALL BAR(<数値変数>,<数式1>,<数式2>)
,機能{SIZE:100}
,機能{SIZE:100|CENTER}
<数式1>と<数式2>を足し算して、その結果を<数値変数>に代入します。
,文例{SIZE:100}
,文例{SIZE:100|CENTER}
CALL BAR(A,100,23)
PRINT A
【表示】123
,{t},{BSIZE:1 0|SIZE:100%}
,{BSIZE:0}←[[4章 BASICの内部構造]] ,{BSIZE:0|RIGHT}[[6章 内部ルーチン]] →