にゃごすwiki

techhan/5.7

%第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レジスタ要求
0OPEN
2CLOSE
4ランダムアクセス
6シーケンシャル出力
8シーケンシャル入力
10LOC関数
12LOF関数
14EOF関数
16FPOS関数
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”を行う場合は、注意して下さい。