MSX Datapack wiki化計画

2章 インタースロットコールの変更点

  • 追加された行はこのように表示されます。
  • 削除された行はこのように表示されます。
{{include unfinished_page}}

,{t},{BSIZE:1 0|SIZE:100%}
,{BSIZE:0}←[[1章 スロット]]                                                                   ,{BSIZE:0|RIGHT}[[3章 カートリッジソフトの作成法|3章 カートリッジソフトの作成法]]                         →

 前述のように、MSXではプログラムが異なったスロットに分かれているため、現在選択されているスロット上にないプログラムが必要になることもあります。これは主に以下のような場合が考えられます。

+ MSX-DOSの環境から、MAIN ROMにあるBIOSを呼び出す。
+ BASICの環境から、SUB ROMにあるBIOSを呼び出す(MSX2以降)。
+ カートリッジソフトから、MAIN ROMあるいはSUB ROMのBIOSを呼び出す。

 これらの作業を行う際スロット切り換えが簡単かつ安全に行えるように、インタースロットコールという一群のBIOSルーチンが存在し、どのスロットに存在するルーチンでも呼び出せるようになっています。本説では、このインタースロットコールの使用法を説明しましょう。

!!2.1 インタースロットコールの動作

 例えば、MSX-DOSからMAIN ROM上のBIOSを呼び出す場合、スロットの状態の遷移は以下に示すとおりです。

+ 初めはMSX-DOSモードなので64Kのアドレス空間すべてにRAMが選択されており、このままではBASIC-ROMをアクセスできない(図5.6-1)。
+ ROMのBIOSを呼び出すためにページ0をBASICのMAIN ROMに切り換え、アクセス可能な状態にする。そしてBIOSを呼びだす(図5.6-2)。
+ BIOSの処理が終わったら再び元の状態に戻し、初めに呼ばれたアドレスにリターンする。

{{div_begin style='text-align:center;'}}
{{svg 図5.6.svg}}
'''図5.6 インタースロットコールのされ方'''
{{div_end}}

 このとき、MSX-DOS側のプログラムが、ページ0以外に存在するならば話は簡単なのですが、呼びだす側のプログラムが呼びだされる側のBIOSと同じページ0に存在しているときは、非常に複雑になります。呼び出す方のプログラムがページ0を切り換えたとたんに自分自身がいなくなって暴走してしまう、ということのないように配慮しなければいけません。
 インタースロットコールでは、いったんページ3へ分岐してから実際のスロット切り換えを行うという方法で、この問題を解決しています。
 このような複雑な状況をインタースロットコールはサポートするので、ユーザーは簡単に他のスロットのプログラムを呼び出すことができます。

!!2.2 インタースロットコールの使用法

 インタースロットコールは、以下に述べるいくつかの方法で行うことができます。これらはBIOSとしてMAIN ROM内に含まれているものですが、その中のいくつかはMSX-DOSの環境下においてもまったく同一のものが用意され、MSX-DOS使用時のインタースロットコールを可能としています(「2.2.2 MSX-DOSのインタースロットコール」参照)。

!2.2.1 BIOSのインタースロットコールルーチン

,{c T},{SIZE:100|CENTER}
,{c S},{SIZE:80}
,{c R},{SIZE:100}
,{c B},{BSIZE:0}

::RDSLT(000CH/MAIN)
,機能{T}

,{t},{BSIZE:0}
,{c},{B|S},{B} 
,,指定スロットの指定アドレスから値を読み出します。

,コール手順{T}

,{t},{BSIZE:0}
,{c},{B|S},{B|R},{B}
,,A ,スロット指定(図5.5と同じフォーマット)
,,HL,読み出すアドレス

,戻り値{T}

,{t},{BSIZE:0}
,{c},{B|S},{B|R},{B}
,,A ,読み出した値

,変更レジスタ{T}

,{t},{BSIZE:0}
,{c},{B|S},{B} 
,,"AF, BC, DE"

,解説{T}

,{t},{BSIZE:0}
,{c},{B|S},{B} 
,,指定したスロットの指定したアドレスの内容を読み出し、Aレジスタに格納する。スロットの指定はAレジスタによって図5.5の形式で行う。このとき、目的のスロットが基本スロットならば、上位6ビットはすべて「0」に設定し、下位2ビットでスロット#0〜#3を決める。もし拡張スロットを指定するならば、同様にビット0とビット1で基本スロットを指定し、その基本スロットに接続されるどの拡張スロットかということをビット2とビット3で指定、さらにビット7を「1」にします。
,,指定したスロットの指定したアドレスの内容を読み出し、Aレジスタに格納します。スロットの指定はAレジスタによって図5.5の形式で行います。このとき、目的のスロットが基本スロットならば、上位6ビットはすべて「0」に設定し、下位2ビットでスロット#0〜#3を決めます。もし拡張スロットを指定するならば、同様にビット0とビット1で基本スロットを指定し、その基本スロットに接続されるどの拡張スロットかということをビット2とビット3で指定、さらにビット7を「1」にします。

::WRSLT(0014H/MAIN)

,機能{T}

,{t},{BSIZE:0}
,{c},{B|S},{B} 
,,指定スロットの指定アドレスに値を書き込みます。

,コール手順{T}

,{t},{BSIZE:0}
,{c},{B|S},{B|R},{B}
,,A ,スロット指定(図5.5と同じフォーマット)
,,HL,書き込むアドレス
,,E ,書き込む値

,戻り値{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,なし

,変更レジスタ{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,"AF, BC, D"

,解説{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,Aレジスタで指定したスロット(指定の形式は図5.5と同じ)のHLレジスタで指定したアドレスに、Eレジスタの値を書き込みます。

::CALSLT(001CH/MAIN)

,機能{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,指定したスロットの指定アドレスをコールする

,コール手順{T}

,{t},{BSIZE:0}
,{c},{B|S},{B|R},{B|R},{B}
,,IY,上位8ビット,スロット(図5.5と同じフォーマット)
,,IX,           ,コールするアドレス

,戻り値{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,呼び出し先プログラムの実行結果により異なる

,変更レジスタ{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,呼び出し先プログラムの実行結果により異なる

,解説{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,IYレジスタの上位8ビットで指定したスロット(指定の形式は図5.5と同じ)の、IXレジスタで指定するアドレスに存在するルーチンをコールする。
,,IYレジスタの上位8ビットで指定したスロット(指定の形式は図5.5と同じ)の、IXレジスタで指定するアドレスに存在するルーチンをコールします。

::ENASLT(0024H/MAIN)

,機能{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,スロットを切り換える
,,スロットを切り換えます。

,コール手順{T}

,{t},{BSIZE:0}
,{c},{B|S},{B|R},{B}
,,A ,スロット(図5.5と同じフォーマット)
,,HL,上位2ビットでスロット切り換えを行うページを指定する

,戻り値{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,なし

,変更レジスタ{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,すべて

,解説{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,HLレジスタの上位2ビットで指定したページを、Aレジスタで指定したスロットに切り換える。
,,HLレジスタの上位2ビットで指定したページを、Aレジスタで指定したスロットに切り換えます。


::CALLF(0030H/MAIN)

,機能{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,指定スロットの指定アドレスをコールする
,,指定スロットの指定アドレスをコールします。

,コール手順{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,インライン・パラメータ形式でスロットとアドレスを指定する
,,インラインパラメータ形式でスロットとアドレスを指定する

,戻り値{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,呼び出し先のプログラムの実行結果により異なる

,変更レジスタ{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,呼び出し先のプログラムの実行結果により異なる

,解説{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,指定したスロットの、指定したアドレスをコールするが、前述のCALSLTと異なり、スロットおよびアドレスの指定は次に示すように、インライン・パラメータ形式で行う。すなわち、このCALLFを呼びだす命令の直後にスロットを指定する値1バイト(RDSLTと同じフォーマット)を置き、その次にアドレスを指定する数値2バイトを置くという形でパラメータを渡すのである。“CALL 0030H”の代わりに“RST 30H”というRST(リスタート)命令を使ってもよい。その場合、4バイトでインタースロットコールが実現可能である。
,,指定したスロットの、指定したアドレスをコールしますが、前述のCALSLTと異なり、スロットおよびアドレスの指定は次に示すように、インラインパラメータ形式で行います。すなわち、このCALLFを呼びだす命令の直後にスロットを指定する値1バイト(RDSLTと同じフォーマット)を置き、その次にアドレスを指定する数値2バイトを置くという形でパラメータを渡します。「CALL 0030H」の代わりに「RST 30H」というRST(リスタート)命令を使ってもかまいません。その場合、4バイトでインタースロットコールが実現可能になります。

{{div_begin style='text-align:center;'}}
'''リスト5.1 インタースロットコールの実行例'''
{{div_end}}
         rst      30h               ;interslot call
         db       00000000b         ;select slot#0
         dw       006ch             ;call address = 006Ch
         dw       006ch             ;call address = 006ch


::RSLREG(0138H/MAIN)

,機能{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,基本スロット選択レジスタの読み出し
,,基本スロット選択レジスタの内容を読み出します。

,コール手順{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,なし

,戻り値{T}

,{t},{BSIZE:0}
,{c},{B|S},{B|R},{B}
,,A,読み込んだ値
,,A,読み込んだ値(図5.5と同じフォーマット)

,変更レジスタ{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,なし
,,A

,解説{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,基本スロット選択レジスタの内容を読み出し、Aレジスタに入れる。
,,基本スロット選択レジスタの内容を読み出し、Aレジスタに入れます。

::WSLREG(013BH/MAIN)

,機能{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,基本スロット選択レジスタへの書き込み
,,基本スロット選択レジスタへ値を書き込みます。

,コール手順{T}

,{t},{BSIZE:0}
,{c},{B|S},{B|R},{B}
,,A,書き込む値
,,A,書き込む値(図5.5と同じフォーマット)

,戻り値{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,なし

,変更レジスタ{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,なし

,解説{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,基本スロット選択レジスタにAレジスタの値を書き込みスロットを選択します。
,,基本スロット選択レジスタにAレジスタの値を書き込み、スロットを選択します。

::SUBROM(015CH/MAIN)

,機能{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,SUB ROMの指定したアドレスをコールする
,,SUB ROMの指定したアドレスをコールします。

,コール手順{T}

,{t},{BSIZE:0}
,{c},{B|S},{B|R},{B}
,,IX,コールするアドレス、PUSH IX (352ページ参照)
,,IX,コールするアドレス、PUSH IX (「A.1.2 SUB ROM」参照)

,戻り値{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,呼び出し先のプログラムの実行結果により異なる

,変更レジスタ{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,"裏レジスタとIX, IYレジスタは保存される"

,解説{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,特にBASICのSUB ROMを呼びだすためのルーチンである。SUB ROMの存在するスロットは自動的に調べられる。普通は、次のEXTROMを使う。
,,BASICのSUB ROMを呼びだすためのルーチンです。SUB ROMの存在するスロットはBIOSが自動的に調べます。普通は、次のEXTROMを使います。

::EXTROM(015FH/MAIN)

,機能{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,SUB ROMの指定したアドレスをコールする
,,SUB ROMの指定したアドレスをコールします。

,コール手順{T}

,{t},{BSIZE:0}
,{c},{B|S},{B|R},{B}
,,IX,コールするアドレス

,戻り値{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,呼び出し先のプログラムの実行結果により異なる

,変更レジスタ{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,裏レジスタとIYレジスタは保存される

,解説{T}

,{t},{BSIZE:0}
,{c},{B|S},{B}
,,BASICのSUB ROMを呼び出すためのルーチンである。上記のSUB ROMとこのEXTROMとは、IXレジスタの値をプッシュするか否かという点だけが異なる。
,,BASICのSUB ROMを呼び出すためのルーチンです。上記のSUB ROMとこのEXTROMとは、IXレジスタの値をpushするか否かという点だけが異なります。

!2.2.2 MSX-DOSのインタースロットコール

 MSX-DOSでは、以下に示す5種類のインタースロットコールが用意され、MSX-DOSジャンプベクタにそのエントリアドレスが定義してあります。これらはBIOS内の同名のルーチンとまったく同じものですから、機能や使用法に関しては、上記のBIOSをご覧ください。
 なお、MSX-DOSからSUB ROM内のルーチンを呼びだす場合、これらのルーチンを使用せず、「2.2.3 MSX-DOSからのSUB ROMコール」の方法を使います。
 MSX-DOSでは、以下に示す5種類のインタースロットコールが用意され、MSX-DOSジャンプベクタにそのエントリアドレスが定義してあります。これらはBIOS内の同名のルーチンとまったく同じものですから、機能や使用法に関しては、前述のBIOSをご覧下さい。
 なお、MSX-DOSからSUB ROM内のルーチンを呼びだす場合は、これらのルーチンは使用せず、「2.2.3 MSX-DOSからのSUB ROMコール」の方法を使います。

,{t},{BSIZE:0}
,{c},{B|S},{B|R},{B}
,,RDSLT(000CH) ,指定スロットの指定アドレスから値を読み出す
,,WRSLT(0014h) ,指定スロットの指定アドレスに値を書き込む
,,CALSLT(001Ch),指定スロットの指定アドレスを呼び出す
,,ENASLT(0024h),指定スロットを使用可能な状態にする
,,CALLF(0030H) ,指定スロットの指定アドレスを呼び出す


'''リスト5.2 MSX-DOSからBIOSを呼び出す'''
 calslt    equ    001ch             ; inter slot call
 exptbl    equ    0fcc1h            ; slot address of MAIN ROM
 
           ld     iy,(exptbl-1)     ; load slot address of the MAIN ROM
                                    ; in high byte of IY
           ld     ix,address of the BIOS jump table
           call   calslt

! 2.2.3 MSX-DOSからのSUB ROMコール

 MSX-DOSの環境からSUB ROMを呼ぶ場合は、以下のような特別な配慮が必要です

+ CALSUBのエントリ条件は、IXにアドレスを入れるだけです。
+ スタックはページ0以外でなければなりません。
+ 割り込み禁止は必要ありません。
+ 終了後、NMIフックの解除は必要ありません。


 ここで問題となるのは、EXTROMに必要なIXレジスタの値が渡せないことです。したがって、BIOS ROMが呼び出された後にIXがセットされ、EXTROMにジャンプする必要があります。
 具体的には、スタック上に次のようなルーチンを用意し、NMIのフックからジャンプさせるようにしておきます。次に、BIOSのNMIエントリ(フックは用意されているが使用されない)をインタースロットコールします。リスト5.3を参照して下さい。

'''リスト5.3 DOSからのSUB ROMコール'''
 ;       +0    inc    sp
 ;       +1    inc    sp
 ;       +2    ld     ix,<sub-ROM entry>
 ;       +6    nop
 ;       +7    jp     extrom
 calslt    equ    001ch
 extrom    equ    015fh                 ; Non-maskable interrupt
 nmi       equ    0066h                 ; Hook for NMI
 exptbl    equ    0fcc1h
 ;
 _calsub::
           exx                          ; Save argument registers over setup
           ex     af,af'
           ld     hl,extrom
           push   hl
           ld     hl,0c300h             ; jp xxxx, nop
           push   hl
           push   ix                    ; SUB ROM entry
           ld     hl,021ddh             ; ld ix,xxxx
           push   hl
           ld     hl,03333h             ; inc sp, inc sp
           push   hl
           ld     hl,0
           add    hl,sp
           ld     a,0c3h
           ld     (h.nmi),a
           ld     (h.nmi+1),hl
           ex     af,af'                ; Restore registers
           exx
 ;
           ld     ix,nmi
           ld     iy,(exptbl-1)
           call   calslt
           ei
 ;
           ld     hl,10                 ; Throw away the interface routine
           add    hl,sp
           ld     sp,hl
           ret
           
           end

!!2.3 スロットの状態を知るためのワークエリア
 スロットに関するワークエリアには、以下のものがあります。

【EXBRSA(FAF8H,1)】SUB ROMのスロットアドレス
{{div_begin style='text-align:center;'}}
{{svg 図5.7.svg}}
{{div_end}}
'''図5.7 SUB ROMのスロットアドレス'''

【EXPTBL(FCC1H,4)】基本スロットの拡張の有無
{{div_begin style='text-align:center;'}}
{{svg 図5.8.svg}}
{{div_end}}
'''図5.8 基本スロットの選択'''

【SLTTBL(FCC5H,4)】拡張スロット選択レジスタ値の保存エリア
{{div_begin style='text-align:center;'}}
{{svg 図5.9.svg}}
{{div_end}}
'''図5.9 拡張スロットの選択'''

【SLTATR(FCC9H,64)】各スロット、ページにおけるアプリケーションの有無
{{div_begin style='text-align:center;'}}
{{svg 図5.10.svg}}
{{div_end}}
'''図5.10 アプリケーションの有無'''

【SLTWRK(FD09H,128)】アプリケーション用ワークエリア
{{div_begin style='text-align:center;'}}
{{svg 図5.11.svg}}
{{div_end}}
'''図5.11 アプリケーション用ワークエリア'''

,{t},{BSIZE:1 0|SIZE:100%}
,{BSIZE:0}←[[1章 スロット]]                                                                   ,{BSIZE:0|RIGHT}[[3章 カートリッジソフトの作成法|3章 カートリッジソフトの作成法]]                         →