Skip to content

Instantly share code, notes, and snippets.

@tsutsui
Last active December 7, 2025 03:31
Show Gist options
  • Select an option

  • Save tsutsui/86b7aaf8972e1646d929b8a3336993fe to your computer and use it in GitHub Desktop.

Select an option

Save tsutsui/86b7aaf8972e1646d929b8a3336993fe to your computer and use it in GitHub Desktop.
p6psgmmlc — PC-6001 PSG音源ドライバ用 MMLコンパイラ README ドラフト

p6psgmmlc

PC-6001 PSG音源ドライバ用 MMLコンパイラ (C言語コマンドライン版)

  • 現在のバージョン: 0.1.0 (2025/12/07)
  • 更新履歴は 更新履歴 項を参照

これはなに?

よっしゅさん作の PC-6001用PSG音源ドライバ ver1.1c 向けの MMLソーステキストを、音源ドライバがそのまま演奏可能な バイナリデータにコンパイルするコマンドラインツールです。

同様のツールとして TINY野郎さん作の MML2P6DRVがありますが、 個人的にPC-6001デモをNetBSD開発環境で作成する際に
「MML作成後、エミュレータ用P6バイナリと実機CLOAD用 wavファイル作成までを make 一発で行えるようにする」
ことを目標に作成しています。

まずはオリジナルの PC-6001版 MMLコンパイラと同等の演奏ができることを前提に C言語で書き直しています。

  • 入力: テキスト形式の MML ファイル
  • 出力: 各チャンネル先頭アドレス + 3チャンネル分の PSGデータを含むバイナリ
  • 想定環境: PC-6001 および それを流用した互換環境上のPSGドライバ

※PSG演奏ドライバ本体は含まれません。

自分用に作ったものなので issue や pull request には対応できない場合があります。


ビルド方法

必要な環境

  • C コンパイラ (C99 相当)
  • NetBSD や Linux といった今どきのPOSIX +αの環境を想定
    難しい関数やライブラリは使っていないので、以下程度の関数があればいいはず……
    • getopt(3)
    • basename(3) (<libgen.h>)
    • err(3), errx(3) (<err.h>)

NetBSD / Linux などの Unix系OSでの使用を想定しています。

例: NetBSD / Linux でのビルド

configure などの自動設定は未サポートです。

make

エラーが出る場合は適当に Makefile などを修正してください。

DEBUG マクロを定義すると、簡単なデバッグ出力 (DPRINTF) が有効になります。

make -DDEBUG

使い方

p6psgmmlc [-b addr] input.mml output.bin
  • input.mml コンパイル対象の MML テキストファイル
  • output.bin コンパイル結果の PSG データバイナリ
  • -b addr 出力データのベースアドレス (16bit)。 ドライバから見たロードアドレスに合わせて指定します。 書式は 0x8000 のような 16 進数も使用可能です。

出力フォーマット

コンパイル結果の PSGデータバイナリの先頭は以下の構造になっています (リトルエンディアン):

オフセット 内容
0 チャンネル 1 (D) 先頭アドレス (word)
2 チャンネル 2 (E) 先頭アドレス (word)
4 チャンネル 3 (F) 先頭アドレス (word)
6 予約 (未使用)
8〜 各チャンネルの PSG データ

各チャンネルのデータ末尾には、ドライバ仕様に従って 0xFF が付加されます。


MML の書式

チャンネル指定

  • 行頭の 1 文字でチャンネルを指定します:

    • D … トーン 1
    • E … トーン 2
    • F … トーン 3
  • MML記述例

D   t8,3L48L+32 m12,1,2,1 J
D   e24f48f+16&f+48f32e2.&e32&e24
D   v12o6r4e16r4.r16e16r4.
D   r16e16r4.r16e16r8.
D   e16r8.e16r4r16<b16r16>a16r8.
E   t8,3L8L+4 s-2,4,-1,0,0 J
E   <I112e8>I113e8<I114e8>I115e8<I112e8>I113e8<I114e8>I115e8
E   <I0e8>I1e8<I2e8>I3e8<I0e8>I1e8<I2e8>I3e8
E   <I0c8>I1c8<I2c8>I3c8<I0c8>I1c8<I2c8>I3c8
E   <I0d8>I1d8<I2d8>I3d8<I0d8>I1d8<I2d8>I3d8
F   t8,3L8L+16 m8,1,2,1 J
F   v13o5e4>e16r16<c+8f+4>e16r16<d+8
F   g+8a8g+8e8&e24f48f+8&f+24f48e4
F   e16c16<b16>c16e16c16<b16>c16e16c16<b16>c16e16c16<b16>c16
F   d8.r16c8.r16<b16a16g16.r32f+16.r32d16.r32
  • 行頭に空白やタブがある場合はスキップされ、 その次の文字が D / E / F の場合に その行がコンパイル対象になります。
  • D / E / F の各パートを記述する順序は任意です。 (先頭から行単位で走査して出てきたパート毎にコンパイルします)

コンパイル一時停止 (X)

  • 行頭の X で、その行以降の各行のコンパイルをトグルします。

    X          ; ここから D/E/F 行を無視
    D O4CDEF   ; 無視される
    X          ; ここから再び有効
    D O5CDEF   ; コンパイルされる
    

サポートされているコマンド

ここでは、主に C 実装側で解釈している範囲を記載します。 詳細な意味やデフォルト値はオリジナルのドライバ仕様書を参照してください。

音符 / 休符

  • A B C D E F G … 音符
  • R … 休符
  • # / + … 半音上げ
  • - … 半音下げ
  • . … 付点 (複数指定可能)
  • ^ … タイ相当の音長加算
  • & … タイフラグ

例:

D O4 C4. D8 E16^E16 F4&

オクターブ

  • On … オクターブ設定 (1〜8)
  • >n … オクターブを n 上げる (n 省略時は 1)
  • <n … オクターブを n 下げる (n 省略時は 1)

オリジナルの PC-6001用音源ドライバ用コンパイラでは、 「演奏上のオクターブ」と「最後に出力したオクターブ」を分けて管理し、 音符コマンド出力時にオクターブが変化していた場合のみ音符コマンド前に オクターブコマンドを出力する、という仕様のようなので、 このコンパイラもその仕様に合わせています。

このため、ドライバの初期オクターブ値 (オリジナルのドライバでは O4) がコンパイラの想定と異なる場合はコンパイラ側の初期値の修正が必要です。 (MML側の先頭で明示的にO4を指定してもオクターブコマンドは出力されない)

音長指定

L / L+ によるデフォルト音長設定

Ln     ; 通常の L 音長
L+n    ; L+ (サブ音長)
  • n は 1,2,3,4,6,8,12,16,24,32,48,96 のいずれか
  • 内部的には 96 分音符単位に変換して管理しています

直接音長指定 %

C%96      ; 96 (96分音符単位で全音符相当) を直接指定
L%24      ; L 音長を 24 (4分音符相当) に直接指定
L%+192    ; L+ 音長を 192 に直接指定
  • C%n などの音符の n は 1〜32767
  • L%n のデフォルト音長の n は 1〜255
  • 範囲外の指定はエラーになります。

付点 .

  • . を 1 つ付けると 1/2 分
  • .. で 1/2 + 1/4 分
  • それ以上も同様

付点計算前の実音長が奇数の場合 (32.など) はエラーになります。

その他コマンド一覧

実装されている主なコマンドと対応バイト列は以下の通りです。

  • On … オクターブ設定 (1〜8) → 0x80 + n ですが、前述の通り音出力時に出力する仕様
  • <n / >n … オクターブアップ / ダウン (1〜8, 省略時 1) → 音源ドライバ仕様としては相対値指定はなく、コンパイル時の絶対値管理
  • Vn … ボリューム設定 (0〜15) → 0x90 + n
  • (n / )n … ボリュームアップ / ダウン (1〜15, 省略時 1) → 0xB0 + n / 0xA0 + n
  • In … ワークエリアへの変数書き込み (0〜255) → 0xF4, n
  • J … 曲の終わりでこの位置に戻る → 0xFE
    • ネスト中 ([ ] の内側) では使用不可
  • M … ビブラート設定 (4 パラメータ / M%n 形式)
  • N … ビブラート有効/無効スイッチ → 0xF6
  • Pn … ノイズモード (1〜3) → 0xED / 0xEE / 0xEF
  • Qn … ゲートタイム (0〜255) → 0xFA, n
  • S ... … ソフトウェアエンベロープ (S n1,n2,n3,n4,n5)
    • n1 == 0 の場合は OFF 扱いで、以降のパラメータは出力しません
  • T n1,n2 … テンポ設定 → 0xF8, n1, n2
  • U%n, U+n, U-n … デチューン (-127〜+127) → 0xFB / 0xFC
  • Wn, W+n, W-n … ノイズ周波数 (0〜31 / -31〜+31) → 0xEB / 0xEC
  • _n … 転調 (-12〜+12)
    • 各音符ごとに「論理オクターブ + 転調」による実効オクターブを計算し、 範囲外になる場合はコンパイルエラーとします。
  • [ / ] / : … ループ構文 (ネスト 4段まで)
  • X … 当該チャンネルのコンパイル停止 (現状実装では「このチャンネルの MML解析終了」は対応していません)
  • ; … 行末までコメント

エラーメッセージ

エラーが発生した場合は標準エラー出力に以下の形式で表示されます:

エラー: <メッセージ> (<行番号> 行目, <桁番号> 桁目)
<該当行の内容>
   ^   ← エラー位置

例:

エラー: 音長の値が不正です (1,2,3,4,6,8,12,16,24,32,48,96) (9 行目, 5 桁目)
D L64 C D E     ; L64 は許可されていない音長
    ^

ネストを閉じないままファイル末尾に到達した場合なども、 最後にそのチャンネルでコンパイルした行を表示してエラー位置を示します。


既知の問題および注意事項

  • 一般(?)の方はTINY野郎さんのMML2P6PSGDRVを使ったほうが便利かと思います。
  • MMLの解釈およびバイナリデータ仕様については 音源ドライバマニュアル記載とPC-6001版コンパイラのZ80コード実装に なるべく寄せていますが、一般的なMML記述で演奏に影響の少ない部分については 厳密な1:1対応はさせてはいません。
  • あまり使わないコマンドについては (まだ) きちんとテストしていません。
  • オリジナル Z80コンパイラの仕様とはエラー判定など一部の仕様が異なります。
    • L音長指定でも ^ が使用可能、音符の32768〜65535 の音長がエラーにならない、など
      (オリジナルではCB,RLとでパーサーが別だがこのコンパイラでは共用)
  • 前述の通り、初期オクターブの動作はドライバ側のデフォルト値に依存します。
  • コンパイル途中でエラーが発生した場合でも、MMLファイルのコンパイルは ファイルの最後まで継続しますが、[ : ] などのループコマンドで ネスト関連エラーがある場合、仕様上それ以降の行におけるネスト異常判定は 正しく行われません。
  • オリジナルのドライバのマニュアルで「ネストに関する謎」という説明にあるとおり [ : ] のループ内部で音長指定やオクターブ指定をした場合の挙動は 仕様定義がドライバ実装都合寄りになっています。
    • TINY野郎さんの MML2P6PSGDRVではこのネストの仕様をオリジナルから変更されていますが、 このコンパイラではオリジナルコンパイラの実装設計意図に合わせています。
      (オリジナルコンパイラの「多重ネストで音長とオクターブが正しく再設定されない」バグは修正してあります)
    • これらの仕様差異に影響されないように、MML記述時にはループ内では音長指定を行わない、 ループ内で音長指定行う場合はループ脱出後に音長の再設定を行う、としたほうがよいと思います。

更新履歴

  • v0.1.0 (2025/12/07)
    • 初公開版
    • PC-6001 PSG音源ドライバ ver1.1c 相当のMMLをC言語コマンドラインツールとしてコンパイル可能
    • 投稿がしたいのに のデモ曲と 一千光年 デモ曲も それっぽく鳴ったので公開版として整理

ライセンス

2条項BSDライセンスとしていますが、詳細は各ソースファイル冒頭のライセンス文を参照してください。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment