YAMAHA YM2203 FM音源ボード(3) CP/M用VGMプレーヤー

YM2203

KZ80マイコンにYM2203 FM音源を搭載してVGMデータの音楽を鳴らすことができるようになりました。ただ、音楽データはオンメモリーを前提としているためマイコンの搭載メモリー以上のデータは演奏できません。
KZ80マイコンにはGrantさんのCP/Mを移植して搭載していますので、CP/Mのディスク(CFカードですが)に音楽データを入れれば大きなファイルも演奏できるはず。

CP/M用にVGMプレーヤーを改造

CP/Mでファイルを扱うプログラムは、わたしもCP/M上のHitech-Cを使ったC言語でのプログラムしか経験がありませんでした。またいかんせん古いOSのため、英語の解説サイトは多数あるのですが、日本語のサイトはそれほど多くありません。昔はCP/Mの専門書が本屋に多数あったものですが…
(入門CP/Mとか応用CP/Mとか、以前わたしも持ってました。)

CP/Mの機能の呼び出し方

CP/Mでファイルを読み書きする方法ですが、CP/Mそのものの解説ではないのですが CP/M互換のMSX-DOSでのファイル読み書き方法が以下のサイトでZ80アセンブラソース例つきで紹介されています。ありがたや。

4章 システムコールの使用法 - テクハンwiki

CP/Mの内部ルーチンを呼び出すためには、BDOSコールという00005h番地を呼び出す方法が提供されています。上記リンク先にも例がでてますが、以下のようなコードになります。

  LD	A, 00H
  LD	C, 01FH
  CALL	0005H

システムコールの種類によりますが、パラメーターはAレジスタやDEレジスタへセットし、Cレジスタにファンクション番号というシステムコールの種類(ファイルオープン、ファイル読み出し…etc)をセットして0005h番地をCallするという流れです。

戻り値はシステムコールの種類によりますが、Aレジスタなどに0/1などが返ってきます。

これ以降は私のVGMプレーヤー CP/M版でどのようにCP/Mの機能呼び出しをしたかを書いていきます。ソースはgithubにUPしました。

KZ80-YM2203/sample/VGMCPM.asm at main · kuninet/KZ80-YM2203
KZ80マイコン用 YM2203 FM音源ボード. Contribute to kuninet/KZ80-YM2203 development by creating an account on GitHub.

ファイルオープンまでの流れ

CP/MはMS-DOSの原型とも言えるOSで、Windowsのコマンドプロンプトによく似たインターフェースを持っています。ドライブ名があるところがMS-DOSそっくりですね。ただし階層ディレクトリはありません。以下の例だと”A>”はCP/Mシステムが出力してきます。カレントドライブはAドライブにいますよという表示です。

A>コマンド パラメーター1 パラメーター2

“PLAY M1.VGM” みたいな感じで入力した場合は、”PLAY”が”PLAY.COM”という実行ファイルを実行しなさいということで、第一パラメーターに読み込むファイル名”M1.VGM”を指定しているということです。

CP/Mの実行ファイルが起動されるとCP/Mが持っているFCB(ファイルコントロールブロック)というところに第一パラメーターのファイル名がセットされた状態でプログラムがスタートするらしいです。プログラムでは第一パラメーターのファイルを読んだり、もしくはそのファイルへ書き込んだりということをするはずなのでおぜん立てしてくれているのはたすかります。

FCBとは以下の構造になっていて、ファイル名などファイルをOPENしたりするための情報を管理するCP/Mのメモリー領域です。アドレスは005Chからの固定となっています。

アドレス説明
5ChドライブNo.
5Dh~64hファイル名(本体)
65h~67hファイル名(拡張子)
68hカレントエクステント(ブロック)
69h~6Ahシステム予約
6Bhエクステント数
6Ch~7Bhディレクトリエントリイメージ
7Chエクステント内の現レコード
7Dh~7Fhランダムアクセスレコード番号

ファイルのオープンにはBDOSコールの機能番号0Fhを使います。
入力パラメーターはDEレジスタにFCBのアドレス(CP/Mデフォルトで05Ch)をセット、エラーコードはAレジスタに戻りますので正常(A=0)かどうかを判定します。

FCB	EQU	005CH
;
	LD	DE,FCB		
	LD	C,0FH		;ファイルOPEN
	CALL	BDOS
	OR	A			;成功?
	JP	NZ,OPEN_ERROR

ファイルバッファの指定とシーケンシャル読み出し

CP/MではデフォルトでDMAと呼ばれるファイルバッファ(0080h番地~)が用意されているようですが、MSXテクハンの作例でも自分のプログラム内にバッファを準備していたので、それに倣ってNEWDMAという128バイトのエリアを準備しました。FM音源の音楽データをファイルから順次(シーケンシャル)読み出しをしながら演奏しようと思います。CP/Mのシーケンシャル読み出し機能は128バイト固定長で読み出されるようです。

CP/Mにこちらから準備した入力バッファのアドレスを知らせるために機能番号1AhのBDOSコールを呼び出してバッファを指定します。また、それに続いて1つ目の128バイト分のデータをNEWDMAエリアへ機能番号1AhのBDOSコールを使って読み出します。読み出す際の入力パラメーターは、DEレジスタにファイルをオープンしたFCBのアドレスを指定します。
音楽データはHLレジスタでポイントしてなめることにするので、ファイルデータを読み出したNEWDMAの先頭アドレスを指すことにします。

MAIN1:
	LD	DE,NEWDMA
	LD	C,1AH		;set DMA address function
	CALL	BDOS

	LD	DE,FCB
	LD	C,014h
	CALL BDOS
	OR  A
	JP  NZ,READ_ERROR
;
	LD HL,NEWDMA

演奏用に1バイト取り出しルーチンを作成

FM音源データの演奏にあたって1バイト分取得しては処理をするという動きが基本となります。またデータによっては続く2バイトに意味があったりする場合もありますので1挙動につき1~数バイトを取得することになります。CFカード(ディスク)からCP/Mが読み出せるデータは128バイトなので、129バイト目が欲しいと言われたらまたCFカード(ディスク)から128バイト分読み出す必要があります。以下のようなコードとしました。

;
GETDATA:
	PUSH DE
	LD	A,(HL)	; GET COMMAND
	PUSH AF
	INC	HL
;
; バッファEND判定
;
	LD  DE,DMAEND
	EX  DE,HL
	SBC HL,DE
	JP  NZ,GETDATA_END
;
; シーケンシャル読み出し
;
	LD	DE,FCB
	LD	C,014h
	CALL BDOS
	OR  A
	JP  NZ,READ_ERROR
;
	LD  DE,NEWDMA
;
GETDATA_END:
	EX DE,HL
	POP AF
	POP DE
	RET

演奏データをポイントしているHLレジスタのアドレスをインクリメント(+1番地)してNEWDMAエリアのアドレスを超えたらCFカード(ディスク)から読み出しを行って、ポインターとなっているHLレジスタをNEWDMAエリアの先頭へ戻します。(アドレスをDEレジスタへ設定してますが最後に EX命令でHLレジスタとひっくり返します。)

割込み処理などを使ってバックグラウンドでちょこちょこ読んでおく方がパフォーマンスに影響が出ないと思うのですが、今回は力業のコーディングでまずは動かしてみます。

ERMSG1:	DB	'Cannot open that file.$'
ERMSG2:	DB	'file read error.$'
NEWDMA  DS  128
DMAEND	EQU $
END

ちなみに、ファイルオープンエラーやファイルREADエラーが出た場合に、機能番号09hのBDOSコールでエラーメッセージを表示するために文字列も定義しておきます。C言語のようにNULL(00h)が終端ではなく、CP/Mでは”$”が終端文字のようです。

CP/Mのコマンドファイル(COMファイル)の作成

CP/Mで実行できるファイルはCOMファイルというバイナリファイルです。COMファイルは0100h番地から始まるZ80(8080)のバイナリマシン語データであれば良いようです。COMファイルの作り方の王道としてはCP/M上のアセンブラやCコンパイラでCOMファイルを作ったり、IntelHex形式のテキストデータをCP/MのLOADコマンドでCOMファイルにすることのようです。

今回はいつも使っている The Macroassembler AS についているp2binというオブジェクトをバイナリ化いるプログラムでCOMファイルを作ることにしました。

以下がビルド用のMakefileです。

#
# Makefile
#

.SUFFIXES: .asm .p .hex .sr .com

all:	VGM2203.hex m1.hex VGMCPM.com

.p.com:
	p2bin $*.p $*.com

.p.hex:
	p2hex -F Intel $*.p $*.hex

.p.sr:
	p2hex -F Moto $*.p $*.sr

.asm.p:	
	asl -L $*.asm

clean:
	rm -f *.p *.hex *.sr *.lst

いつもはMac/Windows上で The Macroassembler ASを使ってオブジェクトファイル(pファイル)を作ったのち、p2hexでIntelHex形式を作成しています。今回はCOMファイルを作りたいので “.p.com:”という節を追加してp2binを起動するルールを書きました。

“.SUFFIXES:”に”.com”を追加するのと、”all:”節に “VGMCPM.com”という出来上がりのファイル名を指定しておきます。最終的にはTeraTERMのXMODEM送信機能でKZ80のCP/MマイコンへはCOMファイルのデータを送りますので拡張子が小文字でも良いかなと。

$ make

Makefileの準備ができたら、 “make”コマンドでビルドが実行されます。
ちなみに私はWIndows10 WSL2環境で The Macroassembler AS を使用しています。(なので、アセンブリコマンドは “asl”です。)

asl -L VGMCPM.asm
Macro Assembler 1.42 Beta [Bld 182]
(x86_64-unknown-linux)
(C) 1992,2020 Alfred Arnold
Motorola MPC821 Additions (C) 2012 Marcin Cieslak
68RS08-Generator (C) 2006 Andreas Bolsch
Mitsubishi M16C-Generator also (C) 1999 RMS
XILINX KCPSM(Picoblaze)-Generator (C) 2003 Andreas Wassatsch
TMS320C2x-Generator (C) 1994/96 Thomas Sailer
TMS320C5x-Generator (C) 1995/96 Thomas Sailer
Panafacom MN1610/1613-Generator (C) 2019 Haruo Asano
MIL-STD 1750 Generator also (C) 2019 Oliver Kellogg


Assembling VGMCPM.asm
PASS 1
VGMCPM.asm(180)
PASS 2
VGMCPM.asm(180)

0.08 seconds assembly time

    180 lines source file
      2 passes
      0 errors
      0 warnings
p2bin VGMCPM.p VGMCPM.com
P2BIN/C V1.42 Beta [Bld 182]
(C) 1992,2020 Alfred Arnold
Deduced address range: 0x00000100-0x00000209
VGMCPM.p==>>VGMCPM.com  (266 Bytes)

無事120kバイトOverのVGMデータが鳴った~♪

上記で作ったCOMファイル(実行ファイル)と音楽バイナリデータをTeraTERM経由でKZ80マイコンへXMODEMで送信します。

以下の記事でも書いてますが、CP/Mに標準コマンドといっしょに skyriverさん作のXMODEMも送っておくと大変便利です。おすすめ。
KZ80-ZilogIOでGrant’s CP/M(2) ROM準備とCP/M起動

3チップ構成Pic24CPMマイコン(その5)XMODEMでファイル送受信: PICマイコンは面白い
 「レトロマイコンZ80ボードの構想(その14)CP/MでのWM動作実験」の記事のコメントに書いたようにCP/Mが動作するワンボードマイコンPic24CPMとPCとのファイルのやり取りはSDカード経由では面倒なのでコンソール接続のシリアル通信を利用して XMODEM プロトコルで行うのが便利です。  上記のコメントに...

オンメモリー版だったVGMプレーヤーに上記のCP/Mファイル読み出し部分の改造をしたおかげで、CP/Mファイルシステムに登録しておいた120kバイトOverのVGM音楽データを鳴らすことができました。いままでは、オンメモリーでしかFM音源音楽データを鳴らせなかったので32kバイト程度が限界でしたが、これからはもっと長い楽曲データも鳴らすことができるようになりました!

コメント