%第1版第15刷 P.317-339
7章 スロットとカートリッジ
MSXで使用されているCPU(Z80)は、通常64Kバイト(0000H〜FFFFH)のアドレス空間しかアクセスできません。しかし、MSXは1M(メガ)バイトに相当する空間を自由にアクセスすることができます。これはMSXが“スロット”の概念を採用し、同一のアドレスに複数のメモリあるいはデバイスを割り当てる機能を備えているからです。
本章では、このスロットの使用法、およびスロットを介してMSXにカートリッジソフトや新しいデバイスを接続するために必要となる情報を紹介していきます。
7.1 スロット
スロットは大量のアドレス空間を確保するためのインターフェイスの役目をするもので、MSXのアドレスバスに接続されるメモリやデバイスは、すべてスロットを介して実装されています。それは、本体内にあるBASICのROMであろうとMSX-DOSモード時のRAMであろうと例外ではありません。カートリッジソフトを差す場所も、1つのスロットです。ここでは、スロットに接続されたソフトウェアやデバイスの取り扱い方を説明します。
7.1.1 基本スロットと拡張スロット
スロットには基本スロットと拡張スロットの2種類があります。“基本スロット”とは、図5.36に示すようにCPUのアドレスバスに直結するスロットを指し、MSXの仕様では最大4個持つことができます。基本スロットは、スロット拡張ボックスを接続することにより(本体内で拡張されていることもある)、最高4個のスロットに拡張でき、この時のスロットを“拡張スロット”と呼びます。4個の基本スロットをそれぞれ4つの拡張スロットに拡張した場合、スロットの数は最大の16個となり、16スロット×64Kバイト=1Mバイトのアドレス空間をアクセスすることが可能です。
なお、拡張スロットにさらに拡張ボックスを差し込んだ場合はシステム自体が起動できなくなりますので、このようなことは行わないでください(MSX標準のカートリッジ用スロットはかならず基本スロットだが、各機種のオプションハードウェア専用コネクタは、拡張スロットに接続されていることがある)。
図5.36 基本スロットと拡張スロット
各スロットは0000H〜FFFFHまでの64Kバイトのアドレス空間を持ちますが、MSXではそれを16Kバイトずつ4つに分け“ページ”として管理しています。CPUはページごとに任意のスロットを選択してアクセスでき、図5.37のように、いくつかのスロットから必要な部分だけを選んで組み合わせることも可能です。ただし、ある番号のページを異なる番号のページに割り当てることはできません(つまり、各スロットのページnは、CPUから見てもページnである)。
図5.37 ページの選択の例
7.1.2 スロットの選択
スロットの選択方法は、基本スロットと拡張スロットでは異なります。基本スロットの場合はA8H番地のI/Oポートによって行い(図5.38)、拡張スロットの場合は実装された拡張カートリッジの“拡張スロット選択レジスタ(FFFFH)”によって行います(図5.39)。しかし、それらを直接変更することは大変危険ですから、スロットの切り換えは不要意に行わないでください。特に、プログラムが自分自身のいるページのスロットを切り換えた場合、動作は保証されません。他のスロットにあるプログラムを呼び出したい場合は、次節で説明するインタースロットコールを使用してください。
図5.38 基本スロットの選択
図5.39 拡張スロットの選択
どこのスロットにMAIN-ROMやRAMが実装されているか、またカートリッジ用のスロットが何番のスロットであるかは機種によって異なります。もし手持ちのMSXがどのようにスロットを使用しているかを知りたい人はマニュアルなどで調べてください。しかし、MSXはどのスロットに何があろうと正常な動作ができるように仕様が決められていますから、その仕様に準じている限り、通常はスロットの使用状況を気にする必要はありません。
しかし、場合によっては特定のソフトウェアが何番のスロット上に置かれているのかを知る必要が生じることもあります。たとえば、従来はBASICのMAIN-ROMは基本スロット#0、または基本スロット#0を拡張した拡張スロット#0-0に置くという仕様でしたが、MSX1にMSX-VIDEOとBASIC ver2.0のROMを増設してMSX2の機能を持たせる場合には、MAIN-ROMがスロット#0やスロット#0-0以外の上に置かれることになります。また、MSX2のSUB-ROMが入っているスロットは機種によってまちまちで、このような場合、下記のワークエリアを参照することにより、BASICインタープリタのROMが置かれているスロットを知ることが可能です(スロット情報は図5.40のフォーマットで得られる)。DOSからBIOSを呼び出す時も、この方法を使用してMAIN-ROMのスロットを調べてください。
・EXPTBL(FCC1H,1) MAIN-ROMのスロット
・EXBRSA(FAF8H,1) SUB-ROMのスロット(MSX1では0)
図5.40 スロットのフォーマット
また、あるルーチンがページ1とページ2(4000H〜BFFFH)にまたがっている場合を考えてください。このルーチンの中で1ページから2ページへ分岐が起こる時には、ページ2にページ1と同じスロットが選択されていなければなりません。そのためには、ページ1でまず自分のいるスロットを調べ、ページ2をそのスロットに切り換えるという作業が必要です。現在自分がどのスロットにいるのか、という情報を知るためにはリスト5.11のプログラムを実行してください。
リスト5.11 自分のいるスロットを知るためのプログラム
;========================================================== ; List 5.11 to know where you are ;========================================================== ; Suppose your program cartridge is 32k bytes ; long (4000H..0BFFFH). You set the ID at 4000H ; and 4001H and the execution start address within ; page 1 (4000H..7FFFH). MSX passes control ; to this address so the part which resides in ; page 2 is not yet enabled at this point. You ; have to know where you are (in what primary ; slot. in what secondary slot) and enable the ; part at page 2. Below is the sample program ; to do this. ENASLT EQU 0024H ;enable slot RSLREG EQU 0138H ;read primary slot select register EXPTBL EQU 0FCC1H ;slot is expanded or not ;----- program start ----- ENAP2: CALL RSLREG ;read primary slot # RRCA ;move it to bit 0,1 of [Acc] RRCA AND 00000011B LD C,A LD B,0 LD HL,EXPTBL ;see if this slot is expanded or not ADD HL,BC LD C,A ;save primary slot # LD A,(HL) ;get the slot is expanded or not AND 80H OR C ;set MSB if so LD C,A ;save it to [C] INC HL ;Point to SLTTBL entry INC HL INC HL INC HL LD A,(HL) ;Get what is currently output ;to expansion slot register AND 00001100B OR C ;Finaly form slot address LD H,80H JP ENASLT ;enable page 2 : END
7.2 インタースロットコール(スロット間コール)
前述のように、MSXではプログラムが異なったスロットに分かれているため、現在選択されているスロット上にないプログラムが必要になることもあります。これは主に以下のような場合が考えられます。
(1)MSX-DOSレベルから、MAIN-ROMにあるBIOSを呼び出す。
(2)BASICレベルから、SUB-ROMにあるBIOSを呼び出す(MSX2のみ)。
(3)カートリッジソフトから、MAIN-ROMあるいはSUB-ROMのBIOSを呼び出す。
図5.41 インタースロットコールのされ方
これらの作業を行う際スロット切り換えが簡単かつ安全に行えるように、インタースロットコールという一群のBIOSルーチンが存在し、どのスロットに存在するルーチンでも呼び出せるようになっています。本説では、このインタースロットコールの使用法を説明しましょう。
7.2.1 インタースロットコールの動作
たとえばMSX-DOSからMAIN-ROM上のBIOSを呼び出す場合、スロットの状態の遷移は以下に示すとおりです。
(1)初めはMSX-DOSモードなので64Kのアドレス空間すべてにRAMが選択されており、このままではBASIC-ROMをアクセスできない(図5.41-イ)。
(2)ROMのBIOSを呼び出すためにページ0をBASICのMAIN-ROMに切り換え、アクセス可能な状態にする。そしてBIOSを呼びだす(図5.41-ロ)。
(3)BIOSの処理が終わったら再び元の状態に戻し、初めに呼ばれたアドレスにリターンする。
この時、MSX-DOS側のプログラムが、ページ0以外に存在するならば話は簡単なのですが、呼びだす側のプログラムが呼びだされる側のBIOSと同じページ0に存在している時は、そう単純にはいきません。呼び出す方のプログラムがページ0を切り換えたとたんに自分自身がいなくなって暴走してしまう、ということのないように配慮しなければならないのです。インタースロットコールでは、いったんページ3へ分岐してから実際のスロット切り換えを行うという方法で、この問題を解決しています。
7.2.2 インタースロットコールの使用法
インタースロットコールは、以下に述べるいくつかの方法で行うことができます。これらはBIOSとしてMAIN-ROM内に含まれているものですが、その中のいくつかはMSX-DOSの環境下においてもまったく同一のものが用意され、MSX-DOS使用時のインタースロットコールを可能としています。
(1) BIOSのインタースロットコール・ルーチン
● RDSLT(000CH/MAIN) ……… 指定スロットの指定アドレスから値を読む
入力:Aレジスタ←スロット指定
HLレジスタ←読み出すアドレス
出力:Aレジスタ←読み出した値
使用:AF, BC, DE
機能:指定したスロットの、指定したアドレスの内容を読み出し、Aレジスタに格納する。スロットの指定はAレジスタによって図5.40の形式で行う。この時、目的のスロットが基本スロットならば、上位6ビットはすべて“0”に設定し、下位2ビットでスロット#0〜#3を決める。もし拡張スロットを指定するならば、同様にビット0とビット1で基本スロットを指定し、その基本スロットに接続されるどの拡張スロットかということをビット2とビット3で指定、さらにビット7を“1”とする。
● WRSLT(0014H/MAIN) ……… 指定スロットの指定アドレスに値を書き込む
入力:Aレジスタ←スロット指定(図5.40と同じフォーマット)
HLレジスタ←書き込むアドレス
Eレジスタ←書き込む値
出力:―――
使用:AF, BC, D
機能:Aレジスタで指定したスロット(指定の形式は図5.40と同じ)の、HLレジスタで指定したアドレスに、Eレジスタの値を書き込む。
● CALSLT(001CH/MAIN) ……… 指定したスロットの指定アドレスをコールする
入力:IYレジスタの上位8ビット←スロット(図5.40と同じフォーマット)
IXレジスタ←コールするアドレス
出力:呼び出し先プログラムの実行結果により異なる
使用:呼び出し先プログラムの実行結果により異なる
機能:IYレジスタの上位8ビットで指定したスロット(指定の形式は図5.40と同じ)の、IXレジスタで指定するアドレスに存在するルーチンをコールする。
● ENASLT(0024H/MAIN) ……… スロットを切り換える
入力:Aレジスタ←スロット(図5.40と同じフォーマット)
HLレジスタ←上位2ビットでスロット切り換えを行うページを指定する
出力:―――
使用:すべて
機能:HLレジスタの上位2ビットで指定したページを、Aレジスタで指定したスロットに切り換える。
● CALLF(0030H/MAIN) ……… 指定スロットの指定アドレスをコールする
入力:インライン・パラメータ形式でスロットとアドレスを指定する
出力:呼び出し先のプログラムの実行結果により異なる
使用:呼び出し先のプログラムの実行結果により異なる
機能:指定したスロットの、指定したアドレスをコールするが、前述のCALSLTと異なり、スロットおよびアドレスの指定は次に示すように、インライン・パラメータ形式で行う。すなわち、このCALLFを呼びだす命令の直後にスロットを指定する値1バイト(RDSLTと同じフォーマット)を置き、その次にアドレスを指定する数値2バイトを置くという形でパラメータを渡すのである。“CALL 0030H”の代わりに“RST 30H”というRST(リスタート)命令を使ってもよい。その場合、4バイトでインタースロットコールが実現可能である。
RST 30H ;interslot call DB 00000000B ;select slot#0 DW 006CH ;call address = 006CH
図5.42 インタースロットコールの実行例
● RSLREG(0138H/MAIN) ……… 基本スロット選択レジスタの読み出し
入力:―――
出力:Aレジスタ←読み込んだ値
使用:―――
機能:基本スロット選択レジスタの内容を読み出し、Aレジスタに入れる。
● WSLREG(013BH/MAIN) ……… 基本スロット選択レジスタへの書き込み
入力:Aレジスタ←書き込む値
出力:―――
使用:―――
機能:基本スロット選択レジスタにAレジスタの値を書き込み、スロットを選択する。
● SUBROM(015CH/MAIN) ……… SUB-ROMの指定したアドレスをコールする
入力:IXレジスタ←コールするアドレス、PUSH IX (352ページ参照)
出力:呼び出し先のプログラムの実行結果により異なる
使用:裏レジスタとIX, IYレジスタは保存される
機能:特にBASICのSUB-ROMを呼びだすためのルーチンである。SUB-ROMの存在するスロットは自動的に調べられる。普通は、次のEXTROMを使う。
● EXTROM(015FH/MAIN) ……… SUB-ROMの指定したアドレスをコールする
入力:IXレジスタ←コールするアドレス
出力:呼び出し先のプログラムの実行結果により異なる
使用:裏レジスタとIYレジスタは保存される
機能:BASICのSUB-ROMを呼び出すためのルーチンである。上記のSUB-ROMとこのEXTROMとは、IXレジスタの値をプッシュするか否かという点だけが異なる。
(2) MSX-DOSのインタースロットコール
MSX-DOSでは、以下に示す5種類のインタースロットコールが用意され、MSX-DOSジャンプベクタにそのエントリアドレスが定義してあります。これらはBIOS内の同名のルーチンとまったく同じものですから、機能や使用法に関しては、上記のBIOSをご覧ください。なお、MSX-DOSからSUB-ROM内のルーチンを呼びだす場合、これらのルーチンを使用してはいけません。
・RDSLT(000CH) ……… 指定スロットの指定アドレスから値を読み出す
・WRSLT(0014h) ……… 指定スロットの指定アドレスに値を書き込む
・CALSLT(001Ch) ……… 指定スロットの指定アドレスを呼び出す
・ENASLT(0024h) ……… 指定スロットを使用可能な状態にする
・CALLF(0030H) ‥‥‥ 指定スロットの指定アドレスを呼び出す
リスト5.12 MSX-DOSからBIOSを呼び出す
;============================================================ ; List 5.12 How to use BIOS from MSX-DOS. ;============================================================ ; CALSLT EQU 001CH ;Inter slot call EXBRSA EQU 0FAF8H ;Slot address of BIOS (main) ROM EXPTBL EQU 0FCC1H ;Slot address of extended ROM ; ; LD IY,(EXPTBLE-1) ;Load slot address of the BIOS ROM ; ;in high byte of IY ; ID IX,address of the BIOS jump table ; CALL CALSLT ; ; ; ; ; ; ; ;----- Sample program to set text mode ----- INITEX EQU 006CH LINL40 EQU 0F3AEH ; TOTEXT: LD B,40 LD A,(EXBRSA) ;slot address of SUB-ROM OR A ;0 if MSX1 JR Z,TO40 LD B,80 TO40: LD (LINL40),B ;set width into work area LD IX,INITXT LD IY,(EXPTBL-1) ;get expanded slot status to IYH CALL CALSLT ;perform an inter-slot call EI ;because CALSLT do DI RET END
7.2.3 スロットの状態を知るためのワークエリア
スロットに関するワークエリアには、以下のものがあります。
● EXBRSA(FAF8H,1) ……… SUB-ROMのスロット
図5.43 SUB-ROMのスロット
● EXPTBL(FCC1H,4) ……… 基本スロットの拡張の有無
図5.44 基本スロットの選択
● SLTTBL(FCC5H,4) ……… 拡張スロット選択レジスタ値の保存エリア
図5.45 拡張スロットの選択
● SLTATR(FCC9H,64) ……… 各スロット/ページにおけるアプリケーションの有無
図5.46 アプリケーションの有無
● SLTWRK(FD09H,128) ……… アプリケーション用ワークエリア
図5.47 アプリケーション用ワークエリア
7.3 カートリッジソフトの作成法
MSXには最低1つのスロットが表に出ており、ここに差し込まれるハードウェアを“カートリッジ”といいます。カートリッジにはアプリケーション・プログラムやゲームなどのROMカートリッジ、ディスク・インターフェースやRS-232Cインターフェイスなどの入出力装置カートリッジ、RAMを増やすための増設RAMカートリッジ、スロットを増やすためのスロット拡張ボックスなどいろいろと接続でき、これによりMSXは機能の拡大を簡単に行うことができます。また、BASICやマシン語プログラムをROM化(カートリッジ化)することも簡単です。ここではソフトウェアをカートリッジ化する方法を説明します。
7.3.1 カートリッジ・ヘッダ
MSXのカートリッジは、16バイトの共通のヘッダを持っており、システムがリセットされると、このヘッダに記述された情報によりカートリッジの初期設定が行われます。BASICやマシン語のプログラムをROM化したカートリッジの場合は、このヘッダに書き込む情報によってオートスタートさせることも可能です。カートリッジ・ヘッダの構成を図5.48に示します。
図5.48 プログラムカートリッジのヘッダ
● ID
ROMカートリッジであれば、この2バイトに“AB”(41H, 42H)というコードを入れておきます。ちなみにSUB-ROMのカートリッジのIDは“CD”となっています。
● INIT
この2バイトには、カートリッジがワークエリアやI/Oなどの初期化を行うのであれば、その初期化ルーチンのアドレスを書き込み、行わなければ0000Hとしておきます。初期化ルーチンの中でワークエリアの確保など必要な処理を行ったら、“RET”で終了させてください。この時SP以外のレジスタは内容を破壊してもかまいません。なお、ゲームのようにカートリッジ内でループしていればよいマシン語プログラムの場合は、ここからそのまま目的のプログラムを実行することが可能です。
● STATEMENT
この2バイトには、カートリッジがCALL文の拡張を行うのであれば、ステートメント拡張ルーチンのアドレスを書き込み、行わなければ0000Hとしておきます。CALL文の拡張を行う場合、ステートメント拡張ルーチンは4000H〜7FFFHになければいけません。
CALL命令は以下の書式で記述されます。
CALL <拡張ステートメント名> [(<引数>[,<引数>…])]
拡張ステートメント名は15文字以下でなければいけません。CALLの省略形として“_”(アンダースコア)も使用できます。
BASICインタープリタはCALL文を見つけると、ワークエリアのPROCNM(FD89H,16)に拡張ステートメント名を入れ、ヘッダのSTATEMENTの内容が0以外のカートリッジにスロット番号の小さい方から順に制御を渡してきます。この時HLレジスタは拡張ステートメント名の次のテキストアドレスを指しています(図5.49a)。
図5.49 拡張ステートメント処理ルーチンの入出力
そこで、ステートメント拡張ルーチンを作成する場合は、まずPROCNMに書かれた拡張ステートメント名を識別し、もしそれが自分の処理すべきものではなかった場合は、HLレジスタの変更はせずに、キャリーフラグを“1”にしてリターンしてください(図5.49b)。拡張ステートメント名が自分の処理すべきものであった場合は、それに応じた処理をし、HLレジスタ(テキストポインタ)を自分が処理したステートメントの次(普通は00Hまたは3AHが入っているところ)に更新し、最後にキャリーフラグを“0”にしてリターンしてください(図5.49c)。
BASICインタープリタはキャリーフラグの状態によって、CALL文が処理されたかどうかを判定し、処理されていない場合は次のカートリッジを呼び出します。すべてのカートリッジが処理をしなかった場合(最後までキャリーフラグが“1”だった時)は“SYNTAX ERROR”を表示します。ステートメントの引数評価には第2部4.4節の“ステートメント拡張のための内部ルーチン”を利用すると便利です。
● DEVICE
この2バイトには、カートリッジがデバイスの拡張(入出力装置の拡張)を行うのであれば、デバイス拡張ルーチンのアドレスを書き込み、行わなければ0000Hとしておきます。デバイスの拡張を行う場合、デバイス拡張ルーチンは4000H〜7FFFHになければいけません。デバイスは1つのカートリッジについて、4つまで持つことができます。また、拡張デバイス名は15文字以下でなければいけません。
BASICインタープリタは定義されていないデバイス名を見つけると、PROCNM(FD89H,16)にそれを格納し、AレジスタにFFHを入れて、DEVICEの内容が0以外のカートリッジにスロット番号の小さい方から順に制御を渡してきます(図5.50a)。
そこで、デバイス拡張ルーチンを作成する場合は、初めにPROCNMのファイルディスクリプタを識別し、もしそれが自分の処理すべきでないデバイスに対するものだった場合は、キャリーフラグを1にしてリターンしてください(図5.50b)。自分の処理すべきデバイスに対するものだった場合は、それに応じた処理を行い、装置ID(0〜3)をAレジスタに設定し、最後にキャリーフラグを0にしてリターンしてください(図5.50c)。
BASICインタープリタはキャリーフラグの状態によって処理されたかどうかを判定し、もし処理されていない場合は次のカートリッジを呼び出します。すべてのカートリッジが処理をしなかった場合(最後までキャリーフラグが“1”だった時)は“Bad file name ERROR”を表示します。
実際の入出力操作をする時には、BASICインタープリタはDEVICE(FD99H)に装置ID(0〜3)を、Aレジスタにデバイスに対する要求(表5.6)を設定してデバイス拡張ルーチンを呼び出します。デバイス拡張ルーチンではそれを参照し、要求に応じた処理を行ってください。
a インタプリタが拡張デバイス処理ルーチンを呼ぶ時の入力設定 OPEN "ABC:"……… Aレジスタ=FFH CYフラグ=1 ファイルディスクリプタの終わり PROCNM A B C 00H b 拡張デバイス処理を行わなかった場合の出力設定 CYフラグ=1 c 拡張デバイス処理を行った場合の出力設定 Aレジスタ=装置ID (0〜3) CYフラグ=0
図5.50 デバイス拡張ルーチンへの入出力
Aレジスタ | 要求 |
---|---|
0 | OPEN |
2 | CLOSE |
4 | ランダムアクセス |
6 | シーケンシャル出力 |
8 | シーケンシャル入力 |
10 | LOC関数 |
12 | LOF関数 |
14 | EOF関数 |
16 | FPOS関数 |
18 | バックアップキャラクタ |
表5.6 デバイスに対する要求
● TEXT
この2バイトには、カートリッジ内のBASICプログラムをオートスタート(リセット時に実行)実行するのであれば、BASICプログラムのテキストポインタを入れておき、行わなければ0000Hとしておきます。プログラムのサイズは8000H〜BFFFHの16K以下でなければいけません。
BASICインタープリタはイニシャライズ(INIT)を終え、システムを起動した後、ヘッダのTEXTの内容をスロット番号の小さい順に調べます。そして、そこが0000H以外であった場合、そのアドレスをBASICテキストポインタとしてそこから実行を始めるようになっています(図5.51)。この時のBASICプログラムは、中間コード形式で格納されている必要があり、またBASICプログラムの初め(TEXTが指すアドレス)にはプログラムの開始を示す00Hが付いていなければいけません。
なお、GOTO文などの分岐先の行番号を、あらかじめ分岐先のテキストポインタの絶対アドレスにしておくと、プログラムの実行速度が上がります。
図5.51 BASICプログラムカートリッジの実行
● BASICプログラムのROM化の方法
1 BASICのテキスト格納先頭アドレスを8021Hに変える。
POKE &HF676,&H21:POKE &HF677,&H80:POKE &H8020,0:NEW
(注)必ず1行にまとめて実行する。
2 目的のBASICプログラムをロードする。
LOAD"PROGRAM"
3 IDを作成する。
AD=&H8000
FOR I=0 TO 31
POKE AD+I,0 IDエリアのクリアー
NEXT I
POKE &H8000,ASC("A")
POKE &H8001,ASC("B")
POKE &H8008,&H20
POKE &H8009,&H80
4 8000H〜BFFFHをそのままROMに焼き込む。
7.3.2 カートリッジ用ソフトの作成に関する諸注意
● カートリッジで使用するワークエリアの確保
他のカートリッジに入っているプログラムといっしょに実行する必要がないプログラム(ゲームカートリッジのようなスタンドアロンのソフトウェア)では、BIOSの使用するワークエリア(F380H)よりアドレスの小さい部分は自由に使用することができます。
しかし、BASICインタープリタの機能を利用して実行されるプログラムでは、同じ領域をワークエリアとして使用するわけにはいきません。その対策としては以下の3つの方法があります。
(1)カートリッジ自体にRAMをのせてしまう(最も安全確実な方法)。
(2)必要なワークエリアが1バイトあるいは2バイトならばSLTWRK(FD09H〜)の自分に対応する2バイトをワークとして使用する。
(3)必要なワークエリアが3バイト以上ならばBASICで使用するRAMから確保する。具体的には、まずBOTTOM(FC48H)の内容をSLTWRK(FD09H〜)の対応するエリアに書き写し、BOTTOMの値を必要なワークエリア分増やし、そこをワークエリアとして確保する(図5.52)。
図5.52 ワークエリアの確保
(2)と(3)については以下のリストを参考にしてください。
リスト5.13 ワークエリア確保の実行例
;======================================================= ; List 5.13 subroutines to support slot ; for ROM in 1 page ;======================================================= RSLREG EQU 0138H EXPTBL EQU 0FCC1H BOTTOM EQU 0FC48H HIMEM EQU 0FC4AH SLTWRK EQU 0FD09H ;------------------------------------------------------------ ; ; GTSL1 Get slot number of designated page ; Entry None ; Return A Slot address as follows ; Modify Flags ; FxxxSSPP ; | |||| ; | ||++-- primary slot # (0-3) ; | ++---- secondary slot # (0-3) ; | 00 if not expanded ; +--------- 1 if secondary slot # specified ; ; This value can later be used as an input ; parameter for the RDSLT, WRSLT, CALSLT, ENASLT ; and 'RST 30H' ; PUBLIC GTSL1@ GTSLI@: PUSH HL ;Save registers PUSH DE ; CALL RSLREG ;read primary slot # RRCA RRCA AND 11B ;[A]=000000PP LD E,A LD D,0 ;[DE]=000000PP LD HL,EXPTBL ADD HL,DE ;[HL]=EXPTBL+000000PP LD E,A ;[E]=000000PP LD A,(HL) ;[A]=(EXPTBL+000000PP) AND 80H ;Use only MSB JR Z,GTSL1NOEXP OR E ;[A]=F00000PP LD E,A ;save primary slot number INC HL ;point to SLTTBL entry INC HL INC HL INC HL LD A,(HL) ;get currently expansion slot register RRCA RRCA AND 11B ;[A] = 000000SS RLCA RLCA ;[A] = 0000SS00 OR E ;[A] = F000SSPP ; GTSL1END: POP DE POP HL RET GTSL1NOEXP: LD A,E ;[A] = 000000PP JR GTSL1END ;------------------------------------------------------------ ; ; ASLW1 Get address of slot work ; Entry None ; Return HL address of slot work ; Modify None ; PUBLIC ASLW1@ ASLW1@: PUSH DE PUSH AF CALL GTSL1@ ;[A] = F000SSPP, SS = 00 if not expanded AND 00001111B ;[A] = 0000SSPP LD L,A ;[L] = 0000SSPP RLCA RLCA RLCA RLCA ;[A] = SSPP0000 AND 00110000B ;[A] = 00PP0000 OR L ;[A] = 00PPSSPP AND 00111100B :[A] = 00PPSS00 OR 01B ;[A] = 00PPSSBB ; ; Now. we have the sequence number for this ; cartridge as follows. ; ; 00PPSSBB ; |||||| ; ||||++-- higher 2 bits of memory address (1) ; ||++---- secondary slot # (0..3) ; ++------ primary slot # (0..3) ; RLCA ;*= 2 LD E,A LD D,0 ;[DE] = 0PPSSBB0 LD HL,SLTWRK ADD HL,DE POP AF POP DE RET ;------------------------------------------------------------ ; ; RSLW1 Read slot work ; Entry None ; Return HL Content of slot work ; Modify None ; PUBLIC RSLW1@ RSLW1@: PUSH DE CALL ASLW1@ ;[HL] = address of slot work LD E,(HL) INC HL LD D,(HL) ;[DE] = (slot work) EX DE,HL ;[HL] = (slot work) POP DE RET ;------------------------------------------------------------ ; ; WSLW1 Write slot work ; Entry HL Data to write ; Return None ; Modify None ; PUBLIC WSLW1@ WSLW1@: PUSH DE EX DE,HL ;[DE] = data to write CALL ASLW1@ ;[HL] = address of slot work LD (HL),E INC HL LD (HL),D EX DE,HL ;[HL] = data to write POP DE RET ;------------------------------------------------------------ ; ; How to allocate work area for cartridges ; If the work area is greater than 2 bytes, make the SLTWRK ; Point to the system variable BOTTOM (0FC48H), then update ; it by the amount of memory required. BOTTOM is set up by ; the initialization code to point to the bottom of equipped ; RAM. ; Ex. if the program is at 4000H..7FFFH. ; ; WORKB allocate work area from BOTTOM ; (my slot work) <- (old BOTTOM) ; Entry HL required memory size ; Return HL start address of my work area = old BOTTOM ; 0 if can not allocate ; Modify None ; PUBLIC WORKB@ WORKB@: PUSH DE PUSH BC PUSH AF ; EX DE,HL ;[DE] = Size LD HL,(BOTTOM) ;Get current RAM bottom CALL WSLW1@ ;Save BOTTOM to slot work PUSH HL ;Save old BOTTOM ADD HL,DE ;[HL] = (BOTTOM) + SIZE LD A,H ;Beyond 0DFFFH? CP 0E0H JR NC,NOROOM ;Yes, cannot allocate this much LD (BOTTOM),HL ;Update (BOTTOM) POP HL ;[HL] = old BOTTOM WORKBEND: POP AF POP BC POP DE RET ; ; BOTTOM became greater than 0DFFFH, there is ; no RAM left to be allocated. ; NOROOM: LD HL,0 CALL WSLW1@ ;Clear slot work JR WORKBEND ;Return 0 in [HL] END
● フック
MSX-BASICが使用するワークエリアのFD9AH〜FFC9Hには“フック”というBASICの機能拡張のための領域があります。1つのフックは5バイトあり、そこには普通“RET”が書かれています。
MSX-BASICはある処理(ワークエリアのフックの説明で示すような処理)を行うと、そこから一度このフックをコールします。“RET”の場合はすぐ戻ってしまいますが、その5バイトをあらかじめイニシャライズ(INIT)ルーチンによりカートリッジ内のプログラムにインタースロットコールするよう書き換えておけば、BASICの機能を拡張することができるわけです(図5.53参照)。
この例として、割り込み処理用のH.KEYIというフックをカートリッジが利用する場合のプログラムをリスト5.14に示します。
図5.53 フックの設定
リスト5.14 フックの使用
;============================================================ ; List 5.14 Sample program to use HOOK ;============================================================ ; ; Start-up initialize entry ; This procedure will be called when system initializing. ; H.KEYI EQU 0FD9AH ; interrupt hook EXPTBL EQU 0FCC1H ; slots expanded or not PSLTRG EQU 0A8H ; I/O port address of primary slot register EXT MYINT ; my interrupt handler INIT: ; <<< Please insert other initialize routine here, if you need. >>> ; Set interrupt entry DI ; start of critical region ; Get old interrupt entry inter-slot call hook LD DE,OLDINT ; set address of old interrupt hook saved area LD HL,H.KEYI ; set address of interrupt entry hook LD BC,5 ; length of hook is 5 bytes LDIR ; transfer ; What slot address is this cartridge placed CALL GTMSLT ; get my slot address ; Set new inter-slot call of interrupt entry LD (H.KEYI+1),A ; set slot address LD A,0F7H ; `RST 30H' inter-slot call operation code LD (H.KEYI),A ; set new hook op-code LD HL,INTENT ; get our interrupt entry point LD (H.KEYI+2),HL ; set new interrupt entry point LD A,0C9H ; 'RET' operation code LD (H.KEYI+4),A ; set operation code of `RET' EI ; end of critical region RET ;------------------------------------------------------------ ; What slot address is this cartridge placed ; Entry: No ; Action: Compute my slot address ; Return: A = slot address ; Modify: Flag GTMSLT: PUSH BC ; save environment PUSH HL IN A,(PSLTRG) ; read primary slot register RRCA ; move it to bit 0,1 of A RRCA AND 00000011B ; get bit 1,0 LD C,A ; set primary slot No. LD B,0 LD HL,EXPTBL ; see is this slot is expanded or not ADD HL,BC OR (HL) ; set MSB if so LD C,A INC HL ; point to SLTTBL entry INC HL INC HL INC HL LD A,(HL) ; get what is currently output to expansion ; slot register AND 00001100B ; get bit 3,2 OR C ; finely form slot address POP HL ; restore environment POP BC RET ; return to main ;----- Interrupt entry ----- INTENT: CALL MYINT ; call interrupt handler JP OLDINT ; go old interrupt handler ;----- HOOK save area ----- OLDINT: DS 5 END
● スタックポインタの初期化
ディスク内蔵のMSXの場合、スロットの位置によってはカートリッジよりもディスク・インターフェイスROMの方が先に初期化動作を行い、ワークエリア確保のためスタックポインタを下位アドレス方向に下げることがあります。この時、ディスクを使わないソフトウェアはカートリッジに制御が渡ってきてからもう一度スタックポインタを設定し直さないと、スタック領域がなくなり暴走などのトラプルを生じる可能性があります。プログラムの初めにはスタックポインタのイニシャライズを忘れずに行ってください。
● 拡張スロットでの動作チェック
一般の市販ソフトウェアが拡張スロットに差し込まれた場合やRAMが拡張スロットにある場合に、アプリケーションプログラムが動作しないという問題があります。MSX2の多くの機種は本体内部で拡張スロットを使用しているので、拡張スロットでの動作不良は致命的です。市販されるソフトウェアは、それが拡張スロットに入れられている場合とRAMが拡張スロットにある場合について、かならず動作チェックを行ってください。
特にFFFFH番地には拡張スロットレジスタが置かれますので、そこをRAMのつもりで参照してはいけません。たとえば、プログラム中で“LD SP,0”を行ってFFFFH番地にスタックを設定すると、そのプログラムは拡張スロットを用いた機種では暴走します。
● CALSLT使用時の注意
CALSLTおよびCALLFでインタースロットコールを行うと、IX, IY, および裏レジスタの内容は破壊されます。また、このルーチンから帰ってくる時、MSX1では割り込みが禁止されていますが、MSX2では呼び出す前の状態に戻ります。
CALSLTまたはCALLFを使ってインタースロットコールを行うと、目的のプログラムが呼ばれる時(図の?)と呼び出し元に戻る時(図の?)に必ず割り込みが禁止されています。
呼び出し元 インタースロットコールルーチン 呼び出し先 LD IY.SLOT LD IX,CALLME CALLME: CALL CALSLT RET
MSX2では、インタースロットコールルーチンの前後で割り込みの状態が保存されます。つまり、図の?の時に?と同じ状態になり、?の時に?と同じ状態になります。呼び出されたプログラムが“EI”や“DI”を行う場合は、注意して下さい。