はじめに
以下の記事の続きで、今回はRAMのサーチ処理について調査した内容を記します。
調査の各種前提条件などは上記記事をご確認ください。
RAMのサーチ
(1) MSX Datapack の解説を読んでみる
以下、MSX Datapackの1.1.2 RAMのサーチの内容を丸々転載します。
MSXではRAMの存在するスロットを特に規定していないので、RAMのあるスロットを捜さなければなりません。RAMのページは2、3の順番で捜します。
ページ2のRAMはすべてのスロットに対し、以下の順番でチェックします。
0BF00Hから0BFFFHまで1バイトずつ上位アドレス方向に 0BE00Hから0BEFFHまで1バイトずつ上位アドレス方向に : : 08000Hから080FFHまで1バイトずつ上位アドレス方向に
チェックは
- 1バイト読み出す
- 1の補数を取る
- その結果を書き込む
- それを比較する
- データを元に戻す
という手順で行いますので、元の内容が破壊されることはありません。途中でRAMが実装されていない(正しく比較されなかった)と判断された時点で、そのスロットに対するチェックは中断します。
これを全てのスロットに対して繰り返し、最大の容量を持つスロット(同じ容量のスロットが存在する場合は、スロット番号の若いもの)が選択され、ページ2に現れます。
基本スロットが拡張されたスロットであるかどうかの判断も、ページ2のRAMチェックと同時に行います。拡張スロットセレクトレジスタは、「書き込んだ値の1の補数が読み込まれる」と規定されていることを利用して拡張されているかどうかのチェックをしています。
ページ3のRAMも同じような手順で捜しますが、アドレスが以下のようになります。
0FE00Hから0FEFFHまで1バイトずつ上位アドレス方向に 0FD00Hから0FDFFHまで1バイトずつ上位アドレス方向に : : 0C000Hから0C0FFHまで1バイトずつ上位アドレス方向に
このようにして見つかったページ3のRAMがシステムのワークエリアとして使用されます。
fmfm...
明確に書かれていて良い感じです。
仕様書はこうあるべき...MSX Datapackは仕様書ではないかもしれませんが。
仕様が明確なので、特に補完が必要な点は無さそうですが、
- 実際どういうコードになっているのか(コードレビュー)
- ランタイムベースでどのように動いているか(テスト)
という切り口で検証してみます。
(2) ディスアセンブル検証(コードレビュー)
以下、コードインラインでコメント(# コメント
)を入れます。
# ===== ページ2のRAMサーチ用の初期化 =====
ld de,$ffff ;[0431] 11 ff ff
xor a ;[0434] af
ld c,a ;[0435] 4f
# ページ2の検証ループ起点
out ($a8),a ;[0436] d3 a8
# |
# +---> $00, $50, $A0, $F0
sla c ;[0438] cb 21
ld b,$00 ;[043a] 06 00
ld hl,$ffff ;[043c] 21 ff ff
ld (hl),$f0 ;[043f] 36 f0
ld a,(hl) ;[0441] 7e
sub $0f ;[0442] d6 0f
jr nz,$0451 ;[0444] 20 0b
# |
# +---> 拡張スロットレジスタに $f0 を書いた結果が反転($0f)なら拡張スロット有りと判定
# 拡張スロット無しなら $0451 へジャンプ
# 拡張スロットがある場合 2-0〜2-3 をチェックする為の処理
ld (hl),a ;[0446] 77
ld a,(hl) ;[0447] 7e
inc a ;[0448] 3c
jr nz,$0451 ;[0449] 20 06
inc b ;[044b] 04
set 0,c ;[044c] cb c1
ld ($ffff),a ;[044e] 32 ff ff
# $bfff 〜 8000 まで
# read → cpl → write → 比較(cp) → write(復帰)
# → cp結果がzならRAMと判定してループ継続
# RAMでなければ $0463 へジャンプ(break)
ld hl,$bf00 ;[0451] 21 00 bf
ld a,(hl) ;[0454] 7e
cpl ;[0455] 2f
ld (hl),a ;[0456] 77
cp (hl) ;[0457] be
cpl ;[0458] 2f
ld (hl),a ;[0459] 77
jr nz,$0463 ;[045a] 20 07
inc l ;[045c] 2c
jr nz,$0454 ;[045d] 20 f5
dec h ;[045f] 25
jp m,$0454 ;[0460] fa 54 04
ld l,$00 ;[0463] 2e 00
inc h ;[0465] 24
ld a,l ;[0466] 7d
sub e ;[0467] 93
ld a,h ;[0468] 7c
sbc d ;[0469] 9a
jr nc,$0476 ;[046a] 30 0a
# 拡張スロット対応処理
ex de,hl ;[046c] eb
ld a,($ffff) ;[046d] 3a ff ff
cpl ;[0470] 2f
ld l,a ;[0471] 6f
in a,($a8) ;[0472] db a8
ld h,a ;[0474] 67
ld sp,hl ;[0475] f9
ld a,b ;[0476] 78
and a ;[0477] a7
jr z,$0484 ;[0478] 28 0a
ld a,($ffff) ;[047a] 3a ff ff
cpl ;[047d] 2f
add $10 ;[047e] c6 10
cp $40 ;[0480] fe 40
jr c,$044e ;[0482] 38 ca
# ページ2〜3のプライマリスロットをインクリメント
# 0b00(0) → 0b01(1) → 0b10(2) → 0b11(3) → 0b00(0)<Carry> で抜ける形
in a,($a8) ;[0484] db a8
add $50 ;[0486] c6 50
jr nc,$0436 ;[0488] 30 ac
# ===== ページ3のRAMサーチ用の初期化 =====
# ページ2とだいたい同じなので解説省略
ld hl,$0000 ;[048a] 21 00 00
add hl,sp ;[048d] 39
ld a,h ;[048e] 7c
out ($a8),a ;[048f] d3 a8
ld a,l ;[0491] 7d
ld ($ffff),a ;[0492] 32 ff ff
ld a,c ;[0495] 79
rlca ;[0496] 07
rlca ;[0497] 07
rlca ;[0498] 07
rlca ;[0499] 07
ld c,a ;[049a] 4f
ld de,$ffff ;[049b] 11 ff ff
in a,($a8) ;[049e] db a8
and $3f ;[04a0] e6 3f
out ($a8),a ;[04a2] d3 a8
ld b,$00 ;[04a4] 06 00
rlc c ;[04a6] cb 01
jr nc,$04b4 ;[04a8] 30 0a
inc b ;[04aa] 04
ld a,($ffff) ;[04ab] 3a ff ff
cpl ;[04ae] 2f
and $3f ;[04af] e6 3f
ld ($ffff),a ;[04b1] 32 ff ff
ld hl,$fe00 ;[04b4] 21 00 fe
ld a,(hl) ;[04b7] 7e
cpl ;[04b8] 2f
ld (hl),a ;[04b9] 77
cp (hl) ;[04ba] be
cpl ;[04bb] 2f
ld (hl),a ;[04bc] 77
jr nz,$04c8 ;[04bd] 20 09
inc l ;[04bf] 2c
jr nz,$04b7 ;[04c0] 20 f5
dec h ;[04c2] 25
ld a,h ;[04c3] 7c
cp $c0 ;[04c4] fe c0
jr nc,$04b7 ;[04c6] 30 ef
ld l,$00 ;[04c8] 2e 00
inc h ;[04ca] 24
ld a,l ;[04cb] 7d
sub e ;[04cc] 93
ld a,h ;[04cd] 7c
sbc d ;[04ce] 9a
jr nc,$04db ;[04cf] 30 0a
ex de,hl ;[04d1] eb
ld a,($ffff) ;[04d2] 3a ff ff
cpl ;[04d5] 2f
ld l,a ;[04d6] 6f
in a,($a8) ;[04d7] db a8
ld h,a ;[04d9] 67
ld sp,hl ;[04da] f9
ld a,b ;[04db] 78
and a ;[04dc] a7
jr z,$04e7 ;[04dd] 28 08
ld a,($ffff) ;[04df] 3a ff ff
cpl ;[04e2] 2f
add $40 ;[04e3] c6 40
jr nc,$04b1 ;[04e5] 30 ca
in a,($a8) ;[04e7] db a8
add $40 ;[04e9] c6 40
jr nc,$04a2 ;[04eb] 30 b5
# 次のブートシーケンスへジャンプ
jp $7b61 ;[04ed] c3 61 7b
(3) ランタイム検証(テスト)
私が作ったZ80エミュレータを使って、コマンドラインで動くMSX2+の簡素なエミュレータをサクッと作り、FS-A1WSXのBIOS等を読み込んだスロット構成の状態で、実際に上記のコードを実行し、ページのスロット構成が切り替わったタイミングでトレースログを出力する形で動かしてみました。
Pages #0:0-0(MAIN), #1:0-0(MAIN), #2:0-0(n/a), #3:0-0(n/a) <reset:$00>
【Slot0(拡張あり)のRAMサーチ】
Pages #0:0-0(MAIN), #1:0-0(MAIN), #2:0-3(n/a), #3:0-3(n/a) <updateSecondary:$F0>
Pages #0:0-0(MAIN), #1:0-0(MAIN), #2:0-0(n/a), #3:0-0(n/a) <updateSecondary:$00>
Pages #0:0-0(MAIN), #1:0-0(MAIN), #2:0-1(n/a), #3:0-0(n/a) <updateSecondary:$10>
Pages #0:0-0(MAIN), #1:0-0(MAIN), #2:0-2(n/a), #3:0-0(n/a) <updateSecondary:$20>
Pages #0:0-0(MAIN), #1:0-0(MAIN), #2:0-3(n/a), #3:0-0(n/a) <updateSecondary:$30>
【Slot1(基本のみ)のRAMサーチ】
Pages #0:0-0(MAIN), #1:0-0(MAIN), #2:1-0(n/a), #3:1-0(n/a) <updatePrimary:$50>
Pages #0:0-0(MAIN), #1:0-0(MAIN), #2:1-0(n/a), #3:1-0(n/a) <updateSecondary:$F0>
【Slot2(基本のみ)のRAMサーチ】
Pages #0:0-0(MAIN), #1:0-0(MAIN), #2:2-0(n/a), #3:2-0(n/a) <updatePrimary:$A0>
Pages #0:0-0(MAIN), #1:0-0(MAIN), #2:2-0(n/a), #3:2-0(n/a) <updateSecondary:$F0>
【Slot3(拡張あり)のRAMサーチ】
Pages #0:0-0(MAIN), #1:0-0(MAIN), #2:3-0(RAM), #3:3-0(RAM) <updatePrimary:$F0>
Pages #0:0-0(MAIN), #1:0-0(MAIN), #2:3-3(FIRM), #3:3-3(FIRM) <updateSecondary:$F0>
Pages #0:0-0(MAIN), #1:0-0(MAIN), #2:3-0(RAM), #3:3-0(RAM) <updateSecondary:$00>
Pages #0:0-0(MAIN), #1:0-0(MAIN), #2:3-1(KNJ), #3:3-0(RAM) <updateSecondary:$10>
Pages #0:0-0(MAIN), #1:0-0(MAIN), #2:3-2(n/a), #3:3-0(RAM) <updateSecondary:$20>
Pages #0:0-0(MAIN), #1:0-0(MAIN), #2:3-3(FIRM), #3:3-0(RAM) <updateSecondary:$30>
【検出完了: ページ2~3にRAM を設定】
Pages #0:0-0(MAIN), #1:0-0(MAIN), #2:3-0(RAM), #3:3-0(RAM) <updateSecondary:$00>
ページ2 → ページ3 の順番で、拡張スロットを含む全スロットをチェックして、最終的に以下の構成になったことが分かります。
- Page0 = MAIN
- Page1 = MAIN
- Page2 = RAM
- Page3 = RAM
これでようやく RAM が使えるようになりました。
ここからはスタックを使った命令(CALL/RET/PUSH/POPなど)の実行や、RAMへの変数の保存なども出来るようになるのでコードの複雑さのレベルが下がるのではないかと思われます。
次回