ROMカセットのPage#0を使えるようにする
ご無沙汰しております。
さて、春まっさかりのシーズンになりました。
今回のテーマはこちら。
「ROMカセットのPage#0を使えるようにする」
です。
ROMカセットには0000H - FFFFHまで(Page#0 - Page#3まで)ありますが、いままでの記事では4000H - BFFFH(Page#1 - Page#2)までしかプログラミングエリアとして使っていませんでした。
そのため拙作のゲームでは32KBの壁が常につきまとっていました。
ROMカセットはPage#0からPage#3まであるのに、もったいない・・
と思っていろいろと調べてみたところ、ROMカセットのPage#0をスロット切り替えすればプログラミングエリアにすることが可能であると知りました。Page#3を切り替えると大変なことになるのでやってはいけません。
(では、なんのためにROMカセットにPage#3があるのか?という点は知りません・・)
当記事以降、新しいゲーム制作にとりかかるわけですが、まずはプログラムエリアを拡げるべくROMカセットのPage#0を有効活用する、その手法についての解説になります。とはいってもスロット切替(というか仕組み)がいまだに腹落ちしていないため、どっかのサイトからコードを拾ってそれで試した結果となっています。
サンプルコード
今回のサンプルコードはこちらです。
https://github.com/sailorman-msx/games/tree/main/src/sample022
実行結果はこんな感じ。ただ文字列を表示しているだけ。
コード説明
いままではPage#1からコードを書いていたので、org $4000 という記述をしていましたが、今回のサンプルでは org $0000 になっています。
ざっくりしたコードの説明ですが
a). 0000H - 3FFFH
Page#0の処理を記述。基本スロットのPage#0を置き換えるため、BIOSが使えない。なので画面出力はVDPを直接叩く(OUT命令)で実現している。
b). 4000H - 7FFFH
ROMカートリッジを挿入した場合は4000Hから処理が開始されるようになっているので、ROMのPage#1(厳密には4010H)がプログラム開始アドレスとなる。このPageの処理では、最初にROMカセットのPage#0を使えるようにして(スロットを切り替えて)からPage#0の処理を呼び出す。
その後、基本スロットのPage#0に戻して、なおかつROMカセットのPage#2をENASLTで使えるようにしている。
c). 8000H - BFFFH
ROMのPage#2に該当する部分。前述したENASLTによって利用可能となっている。
処理順としては
b) → a) → b) → c) というような流れになっています。
openMSX debuggerで見てみよう
サンプルプログラムを動かしながらPageの状態を見てみます。
まず最初に、$4010がプログラム開始位置なのでそこでブレークしてみます。
画像の緑色の枠で囲ってる箇所が各Pageのスロット番号になるのですが、この時点ではPage#0は0-0、つまり初期状態のままです。実際に$0000近辺のコードを見てみると今回のサンプルとは異なっていて「あー、まだシステムROMなんだなあ」と理解できますね。
次にプログラムの以下のcall 0000Hの手前でブレークしてみます。
(中略)
; ROMカセットのPage#2をMain-RAMのPage#2として使えるようにす
out ($A8), a
ld a, ($FFFF)
cpl
and 00111111B
ld b, a
ld a, d
and 11000000B
or b
ld ($FFFF), a ; 拡張スロット情報を書き換える
;-------------------------
; ROMカセットの0000Hを呼び出す(Page#1からPage#0の処理を呼ぶ)
;-------------------------
call 0000h
;-------------------------
; 基本スロット情報と拡張スロット情報を元の状態に戻す
;-------------------------
ld a, e
out ($A8), a
ld a, d
ld ($FFFF), a
緑色の枠の部分がさっきと違っているのがわかるでしょうか?Page#0のスロット番号が1(ROMカセットのスロット番号)に切り替わっています。
この状態で$0000近辺を確認するとサンプルコードの内容と同じ内容になっていることが確認できます。
ですが、この時点ではまだENASLTを実施していないのでPage#2($8000-)は基本スロットのままになっています。MSXの初期状態では$8000には何もないので空っぽ(00HやFFH)になってます。
次にENASLTから戻った時点でブレークしてみます。
緑色の枠の内容を見るとPage#0はスロット番号0(システムROM)、Page#2はROMのスロット番号に切り替わってることが確認できます。
まあ、ENASLTが使える時点でPage#0はスロット番号0になってるわけですけどね・・
$8000近辺を確認すると今回のサンプルコードの内容になっていることが確認できますね。
Page#0を使うときの注意点
基本スロットのPage#0をROMカセットのPage#0と切り替えて使う場合、当たり前ですがBIOSサブルーチンはいっさい使えません。今回のサンプルコードではPage#0の処理内部で文字列を出力していますが、VDPを直接叩く(=VRAMに直接アクセスする)ことで画面表示を実現しています。また、Page#0に切替えた場合は、必ずDI(Disable Interrupt)しておく必要があります。これは割り込みがかかると大変なことになるためです。とりあえずPage#0が基本と異なってる場合は、DIは絶対です。もちろんPage#0の処理が終わったらEI(Enable Interrupt)しておくのを忘れずに。
Page#0をどんなときに使うの?
データですよデータ。ROMプログラミングではとにかく固定データ(スプライト、PCG、マップデータなどなど)が邪魔な存在になります。ほんの一瞬しか使わないのにずっとROMに残ってるんです。なので、Page#1(16KB)とPage#2(16KB)の32KBを純粋なプログラムエリアとして使い、一瞬しか使わないようなデータをPage#0(16KB)に追い出すことが出来ます。VDPは直接叩いて良いので、キャラクタデータをPage#0に寄せるのが効果的な気がします。もちろん、頻繁に使うサブルーチンは絶対に置かないほうが良さそうですね。。
以上で今回の記事では「ROMカセットのPage#0を有効活用する方法」の説明でした。これから、新作ゲーム作りをやるぞ、やるぞ、やるぞーー!!!
と、いうことで、ではまた!ノシ
セーラー服が似合うおじさんです。猫好き、酒好き、ガジェット好き、楽しいことならなんでも好き。そんな「好き」をつらつらと書き留めていきます。