拙作 Newシティヒーローの冒頭ローディング画面の処理で、カセットテープのロード中にアニメをしているシーンがあります。今回はこの実装について説明します。

ゲーム中のワークエリアは 0xE51A から使用していますので、実際の空きは41バイトとなっています。バックバッファを 0xE700 からとしています。0xF300 からが VRAMです。一見、どこにも空きがないように見えますが、たった一箇所、空きがあります。それが VRAMのさらに後ろ、0xFEB8 - 0xFFFF です。
最初は OUT命令を使って自分で制御しようかとも思ったのですが、Newシティヒーローでは PC-8001 / PC-8801 / mk2 / SR / PasocomMini という幅広い環境で動かすため、特にハードの違いが大きいと予想されるカセットテープの読み込みは、自分で制御は少し危険と感じました。そのため、BASIC ROMルーチンを呼び出す事で、データの読み込みを行う事にしたのです。ROM内ルーチンの場所と画面の初期化はこちらで確認してください。画面を初期化したら、続いてカセットテープ読み込み開始の手続きを行います。
最初に読み込まれるのは、冒頭の「ぴーーーっ」という音です。内容的には GAP という数値が返ってきます。この GAP は 0x3A です。0x3A が続く限りは最初は空読みを続けます。これは先頭位置合わせですね。
続いてブロックサイズを読み込みます。1ブロックの最大サイズは256までです。おそらく 255 が入ってきますが、最終最後には端数が入ってきます。ここで得られた値でループします。また、この読み込んだ値もチェックサム対象なので保存します。
ここからはブロックサイズ分ループしながらメモリに読み込んだデータを書き込みつつ、チェックサムを更新しつつ進行します。この部分に僅かに処理する時間があるので、ここにロゴアニメの処理を入れています。サウンド演奏ルーチンを入れたりすれば、中村光一氏のドアドアのような事も出来るかもしれません。

まるでスレッドで動いているようにも見えますが、実際にはテープロードの次回バイト読み込みまでの待ち時間内で、ロゴを動かしているに過ぎません。PC-8001 ではあまりこのような処理を見かけなかったので試してみたというワケです。
- カセットテープの読み込みをどこに配置するか
- 読み込み中は BASIC ROMは生かしておく
- ゲーム本体の読み込み範囲にローダーを置けない
ゲーム中のワークエリアは 0xE51A から使用していますので、実際の空きは41バイトとなっています。バックバッファを 0xE700 からとしています。0xF300 からが VRAMです。一見、どこにも空きがないように見えますが、たった一箇所、空きがあります。それが VRAMのさらに後ろ、0xFEB8 - 0xFFFF です。
実はここもゲーム中はプレイヤーのワークやパターン反転テーブルで使い切っているのですが、0xFF00 - 0xFFFF の反転テーブルはロード終了時にプログラムで作成して、そのプログラムは自動的に消えてしまうので、少なくともロード中は空いています。そこで、ゲーム本体のロードプログラムを 0xFF40 - 0xFFFF に配置する事にしました。
- テープ読み込みの前処理
以後、call BIOS.CMT_READ を呼び出すたびに、カセットテープから読み込んだ1バイトが Acc に返却されます。読み込みが終わったら、
ld a, %00101001
out (0x30), a ; カセットモーターON
call BIOS.CMT_OPEN_R ; CMT 読み込み開始
ld a, %00100001
out (0x30), a ; カセットモーターOFF
と制御して、カセットテープを停止します。実はテープの停止は義務ではないので、無視しても問題ないです。止めなくてもカセットテープは壊れません。
- テープから読み込んだデータの処理
最初に読み込まれるのは、冒頭の「ぴーーーっ」という音です。内容的には GAP という数値が返ってきます。この GAP は 0x3A です。0x3A が続く限りは最初は空読みを続けます。これは先頭位置合わせですね。
続いて読み込み先アドレスが H, L, Sum と3バイト読み込まれます。H と L を足して NEG した結果が Sum と異なっていたらエラーとなります。
.serial
call BIOS.CMT_READ
cp 0x3A
jr nz, .serial
ここからはデータブロックの読み込みとなっていきます。
call BIOS.CMT_READ
ld h, a
call BIOS.CMT_READ
ld l, a ; HL 読み込みアドレス
call BIOS.CMT_READ ; Acc チェックサム
ld c, a
ld a, h
add a, l
neg
cp c
jp nz, BIOS.CMT_CLOSE ; チェックサムエラーで停止
ブロックとブロックの間に1バイトの 0x3A(GAP)が挟まれていますので、それを読み込んでスキップします。
; ブロック間 GAPを読み飛ばす
.read
call BIOS.CMT_READ
cp 0x3A
jr nz, .read
; ブロックサイズ読み込み
call BIOS.CMT_READ ; ブロックサイズ
ld b, a ; B 読み込みループ回数
ld c, a ; C チェックサム初期値
; ブロックデータ読み込み
.store
call BIOS.CMT_READ ; Acc 1バイト読み込み
ld (hl), a ; メモリに配置する
inc hl
add a, c
ld c, a ; チェックサム加算
;
; ** ここにアニメ処理を記述する **
;
djnz .store ; ブロック数分ループする
; チェックサム確認
ld a, c
neg
ld c, a ; 全ての加算値を NEG
call BIOS.CMT_READ ; チェックサム値読み込み
cp c ; 比較して
jp nz, BIOS.CMT_CLOSE ; チェックサムエラーで停止
jr .read ; 読み込みを続ける
続いてブロックサイズを読み込みます。1ブロックの最大サイズは256までです。おそらく 255 が入ってきますが、最終最後には端数が入ってきます。ここで得られた値でループします。また、この読み込んだ値もチェックサム対象なので保存します。
ここからはブロックサイズ分ループしながらメモリに読み込んだデータを書き込みつつ、チェックサムを更新しつつ進行します。この部分に僅かに処理する時間があるので、ここにロゴアニメの処理を入れています。サウンド演奏ルーチンを入れたりすれば、中村光一氏のドアドアのような事も出来るかもしれません。
最後に 0x3A, 0x3A, 0x3A と3回 GAPが続いて本当の終了となります。読み込んでも意味が無いので、私は無視してしまいました(汗
コメント