自作のWASMプリプロセッサを使ってPSGエミュレータを作った。
mwasmという自作のプリプロセッサ拡張がわりと使えるようになったので何か書いてみようと思った。PSGが興味の対象となっていたので、Cで書かれたエミュレータ・ソースコードを移植してみることにした。
元のソースコードはem2149.cというもので、Cで書かれた短いものである。psgのエミュレータのソースコードはたくさんあって、おそらくmameのものが一番有名じゃないかなと思うんだけど、このコードは短くてすっきりしていたので、移植のターゲットにはもってこいだと考えた。これをwasmテキスト・フォーマットでハンドで移植することにした。以下がそのソースコードである。
WASMのテキスト・フォーマットは「アセンブリ」であるとはいえ、純粋なアセンブリ言語ではなく仮想スタックマシン上で動く中間言語のようなものである。構文はS式が使え、スタックマシン上のインストラクションを比較的わかりやすく書くことができるようになっている。
;; IEEE754 float64のビットパターンを持つ2つの32ビット値(high,low)を元にして、64bit floatを返す
(func $i64tof64 (param $low i32) (param $high i32) (param $minus i32) (result f64)
(f64.reinterpret_i64
(i64.xor
(i64.or
(i64.shl
(i64.extend_i32_u (local.get $high))
(i64.const 32)
)
(i64.extend_i32_u (local.get $low))
)
(i64.shl
(i64.extend_i32_u (local.get $minus))
(i64.const 32)
)
)
)
そして、PSGエミュレータのコードを移植し、デバッグしてWeb上で動くようにしたのが以下のページである。
Audio Workletを使ってるので、Chromeのみで動くと思う。
以下は動画にしてみたもの。
PSGのコードはビット演算の嵐で、そこはWASMの得意分野なので、Cよりもすっきり書けているのではないかと思う。バイナリのサイズは2Kくらいでそこそこ小さくできたように思う。PSGの波形合成やエンベロープジェネレータあたりのアルゴリズムはなかなか面白いので興味のある方は元ソースを読んでみることをお勧めする。
自作のプリプロセッサを使ってみた感想としてはやはりリニアメモリのオフセットアドレスをラベルでアクセスできるのは絶大な威力だし、初期値をデータストリングに変換する機能もなかなかイケてるのはないかと思っている。自画自賛ですな(笑)。
今後はこれを使ってローレゾグラフィックのレトロ風シューティングゲームを作ろうかなと思っているところである。