MSX-DOSでのプログラミング第4回
今回はMSX-DOSならではのファイルアクセスを使ったプログラミングについてです。ちょっと時間がかかりましたが、、、
ネタさがし
ファイルアクセスをネタにするために、なんかこう少しは面白そうなネタ、MSX-DOSだと便利だー!!と感じれるようなネタはないものかと探していました。そのため前回のMSX-DOS記事から少し時間があきました。
筆者は絵が得意ではないので綺麗なアニメ絵とかは題材にしづらく、かといってマップエディタでハイドライドのようなタイル絵を作るのもしんどい。
なにかないかと考えていたのですが、ありました。
「迷路を描く」です。
今回は以下の記事を参考にして、迷路をMSXの画面いっぱいに描くことにしましょう!
順序は次のとおりです。
上記サイトをもとにして巨大な迷路を作成し、そのデータをバイナリ(0と1のデータ)に変換。全体を255x191ドットのデータに変換したのちに、そのデータをVRAMのキャラクタパターンになるよう8バイトずつに変換して、VRAM転送用のファイルを作成。サンプルプログラムではそのファイルを読み込んで、画面上に迷路を描く。という流れ。
迷路作成のアルゴリズムや迷路データをバイナリ変換する方法などは当記事のテーマとは関係ないので割愛します。それではMSX-DOSでのファイル読み込みプログラムの作り方について、はじまり、はじまり〜
FCB(ファイルコントロールブロック)
ファイルを扱うためにまず覚えなければならないものは、FCBです。
FCBは次のような形になっています。
先頭0バイト目にドライブ番号(0:Aドライブ、1:Bドライブ…)
次の11バイトがファイル名(8バイト)と拡張子(3バイト)になっていて、ファイルをオープンするときにはこの部分にあらかじめ値をセットしてからファイルオープンのファンクションコールを呼び出すとファイルがオープンされます。ファンクションコールの一覧はふうせん様のこちらがシンプルにまとめられていて、わかりやすかったです。
またファイルを操作する場合は、DTA(Data Transfer Address:データ転送アドレス)というものを使います。あらかじめメモリ上のどこかにアドレスを決めておき、そこをDTAとして使うと読み込んだ内容はそのアドレスに格納されていく。という仕組みになっています。
ファイル読み込みの順序というかルールは以下のような感じです。
1. FCBをメモリ上のどこかに決めておく。
2. DTAをメモリ上のどこかに決めておく。
3. ファイルをオープンする。
4. ファイルを読み込む。読み込んだデータはDTAのアドレス以降に格納される。
5. ファイルを読み込み終えたらファイルをクローズする。
単純ですね。
それではサンプルコードを見てみましょう。サンプルコードをゲットだぜ!!
https://github.com/sailorman-msx/games/tree/main/src/sample021
コードを見てみる
DOSMAC02.asmがサンプルコードです。
上記説明と同じ箇所はここ↓になります。
(中略)
; BDOSルーチン(MSX-DOS用)
FNCCAL:equ $0005 ; ファンクションコール
; ファンクションコール関連
_TERM0:equ $00 ; MSX-DOSシステムに戻る
_FOPEN:equ $0F ; ファイル読み込み
_FCLOSE:equ $10 ; ファイルクローズ
_SETDTA:equ $1A ; DTAの設定(データ読み込み先アドレスの設定)
_RDSEQ:equ $14 ; シーケンシャルファイル読み込み(128byteずつ読み込む)
; ファイル読み込みに必要な情報
BUFAD:equ $4000 ; ファイル読み込み内容の格納先アドレス
(中略)
;-------------------------------------------
; MSX-DOSのファンクションコールを使って
; ファイルを読み込み
; パターンテーブルを書き換える
;-------------------------------------------
; (読み込みモードでのファイルオープン)
; 1. DEレジスタにFCB(*)のアドレスをセットする
; 2. Cレジスタに_FOPEN(05H)を指定して
; BDOSコールする
; *読み込み前だと、ファイル名だけの指定となる
ld de, FCB
ld c, _FOPEN
call FNCCAL
; ファイルオープン時に失敗した場合は
; Aレジスタに0以外がセットされる
; ファイルアクセス失敗時はMS-DOSに戻るようにしておく
or a
jp nz, RETMSXDOS
; データ読み込み先を設定しておく
; 設定方法
; 1. DEレジスタにバッファアドレスを設定する
; 2. Cレジスタに_SETDTA(1AH)をセットして
; BDOSコールする
ld de, BUFAD
ld c, _SETDTA
call FNCCAL
; 今回のサンプルでは128バイトずつファイルを読む
; 128バイト読んだらVRAMのパターンテーブル0000Hから順に
; 128バイト読み込んだデータを転送していく
; 合計で6144バイト埋めていく(0000H - 17FFH)
; DEレジスタに書き込み先のVRAMアドレスをセットして
; 変数に退避しておく
; *ファンクションコールを呼び出すとレジスタはたいてい破壊される
ld de, $0000
ld (DEREGBACK), de
FREAD_LOOP:
; ファイル読み込み
; 読み込み方法(128バイトずつのシーケンシャルアクセス)
; 1. DEレジスタにFCBのアドレスをセットする
; 2. Cレジスタに_RDSEQ(14H)をセットする
; 3. BDOSコールする
; 4. 読み込んだデータがDTAのアドレスに格納される
; _RDSEQで読み込む場合、データが128バイトに満たなかったら
; 足りない部分は0で埋められる。
ld de, FCB
ld c, _RDSEQ
call FNCCAL
; 読み込みに失敗したら
; Aレジスタに01Hがセットされる
; ファイルをオープンしているのでクローズ処理にジャンプする
cp $01
jr z, FREAD_LOOPEND
; バッファの内容をVRAMに転送する
ld de, (DEREGBACK)
ld hl, BUFAD
ld bc, 128
call WRTVRMSERIAL
; 読み込みデータのVRAM書き込み先アドレスを+128しておく
ld de, (DEREGBACK)
ld hl, 128
add hl, de
ld de, hl
ld (DEREGBACK), de
; 次のデータを読みにいく
jr FREAD_LOOP
FREAD_LOOPEND:
; ファイルをクローズする
; クローズの方法
; 1. DEレジスタにFCBのアドレスをセットする
; 2. Cレジスタに_FCLOSE(10H)をセットする
; 3. BDOSをコールする
ld de, FCB
ld c, _FCLOSE
call FNCCAL
(中略)
;--------------------------------------------------------
; ここからFCB
; FCBは37バイトとなっていて
; ファイル読み込み前の状態であれば
; カレントドライブとファイル名以外は0にしておく
; ファイルをオープンすると0にしていた部分が
; 書き換えられ「完全にオープンされた」という状態になる
;--------------------------------------------------------
FCB:
defb 0 ; カレントドライブ(Aドライブ)
defb "SAMPL021DAT" ; ファイル名はピリオド無しで、ファイル名(8バイト固定),拡張子(3バイト固定)
; ファイル名が8文字に満たない場合は末尾はブランクにしておく
; 先頭13バイト目以降は読み込み前は0にしておく
defb 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; FCB+12 - FCB+21
defb 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; FCB+22 - FCB+31
defb 0, 0, 0, 0, 0 ; FCB+32 - FCB+36
FCBには読み込み対象データのファイル名を指定しています。
SAMPL021.DATというファイルにVRAMのキャラクタパターンが格納されています。ファイルサイズは6144バイトです。
DTAは4000Hを指定してます。
RDSEQを使っているため、データを読み込むと読み込んだデータの128バイトがこのアドレスにデータが転送されてきます。
常に4000H-407FHに読み込んだデータ(128バイト)が入る。ということです。
データを読むとFCBのカレントブロックやカレントレコードなどの値が更新されていき意識せずとも次の128バイトが読めるようになっています。
今回はRDSEQという128バイト固定でのファイル読み込み処理を使っていますが、ランダムブロック読み込みとかっていうのもあります。
詳しくは前述したファンクションコールを参照してください。
特筆すべきはプログラムの本体自体は453バイトしかないことです。小さな体で大きなデータを扱っているというサンプルになってます。ROMだとこのデータ6144バイトもプログラムの実体に組み込まれることになるわけなので、便利ですね。
読み込んだデータをGRAPHIC2モードのキャラクタパターンテーブル(上段、中段、下段)に転送しまくって迷路を描いています。
今回はてみじかに。
ファンクションコールはさまざまなものがあります。どちらかというと、テキストベースでのビジネス用のプログラムを作るために便利なイメージを筆者は受けました。今までの記事では音を鳴らしたり絵を描いたりのゲームだったので少し「今までとはなんか違う感」がありますね。
とりあえずサンプルプログラムが動作すると次のような迷路が表示されます。左下がスタート地点で右上がゴールです。白が壁、黒が通路です。
「穴掘り法」と呼ばれるアルゴリズムで作られた迷路なので必ずゴールに到達できます。(誰も遊ばないと思うけど・・)
今回の記事でファイル読み込みができるようになりました。
これで今までのROMの制限以上のデータを扱うことができるようになったので、より巨大な何かを作れることでしょう。
ですが、MSX-DOSまわりやFDDまわりがBASICのROM部分に入っていたり、Microsoftや各ハードウェアベンダの権利がらみで配布したりなんたり、っていうのがしんどそう。
筆者のように、ホビーユースでちょうどいいのは、やっぱりC-BIOSオンリーでROMかなーなんて思ったりしています。
では、また!!ノシ
セーラー服が似合うおじさんです。猫好き、酒好き、ガジェット好き、楽しいことならなんでも好き。そんな「好き」をつらつらと書き留めていきます。