JavaScriptを有効にすると、ここにメニューを表示します。
FM Towns Sprite Busyタイミングの研究

ysflight.com

 

はじめに

FM TownsのSprite Busyのタイミングについて研究してみた。結論を先に書いてしまうと、以下のようなことがわかった。

  1. Sprite Busyフラグが0から1に変わるのタイミングはVSYNCの立ち上がり。
  2. Sprite Busy期間の長さはスプライト表示個数(実際に表示する個数ではなくI/Oで指定した個数)×57us+画面消去にかかる時間。画面消去にかかる時間が32usであるかどうかははっきり確認できなかったが、近い値。
  3. Sprite BusyがVSYNC期間に終わった場合、次のVSYNC立ち上がりまでの間(ほぼCRTのリフレッシュサイクル)Sprite Busy=0
  4. VSYNC期間(I/O FDA0hのVSYNCフラグが立っている期間)の長さは1リフレッシュサイクルあたり60us。

VSYNCとSprite Busyフラグの関係については、FM Townsテクニカルデータブック(通称赤本)の369ページ[1]に記述がある。この赤本は、FM Townsの技術情報のバイブルのようなもので、非常に貴重な情報がたくさん載っている反面、エラーも多かった。まず、1について、赤本ではSpriteハードウェアが画面消去を開始するタイミングはVSYNC終了時であるという記述がある。この図ではSprite転送終了でSprite Readyになると記述してあるものの、どの時点でSprite Busyになるのかの説明が無い。ただし、本文によるとこの図は「スプライトの動作と同期信号の関係」とあるので、この図がSprite Busyフラグについて書いていると考えるのが自然と言える。しかし、この図ではSpriteハードウェアの動作が始まるのはVSYNC終了時と解釈できるが、計測によるとVSYNC開始時だった。

そして、赤本369ページの図では、最初の32usで画面消去、その後スプライト1枚につき75usの転送時間がかかると書いてある。しかし、FM Towns II MXで計測した結果、スプライト1枚の転送にかかる時間は57usだった。当時の印刷は今ほど自動化されていなくて、原稿と印刷の間にかなりの手作業が入る。この手作業で上位と下位の桁を間違えてしまったのではないか。(追記: その後津軽ユーザの方からの情報でMA/MXでは初期型よりもスプライトが高速化しているとのことなので初期型は75usだったのかもしれない。2Fは手元にあるけどすぐに引っ張り出してテストできない。)

また、赤本369ページの図によると、スプライトハードウェアは動作を始めて最初の32usでVRAMをクリアするとある。この32usはSprite Busyフラグは1なのか0なのか図からは曖昧でわからなかった。Sprite Busyフラグの意図としては、SpriteハードウェアがSprite RAMをアクセスしている間にCPUが同時にアクセスしないようにするということだと考えられる。だとするとVRAMをクリアしている間はCPUがSprite RAMをアクセスするのは自由だからBusyフラグは0でもいいはず。この疑問は実機での計測によって、クリア中もBusyフラグは1であることが確認できた。VRAMクリアにかかる時間は、実機計測だと約29usという結果になったが、大雑把な値なので32usは十分近い値と言える。

それからもう一点わからなかったのはVSYNCに実際どの程度の時間がかかるのか。これも調べるついでに計測したところ、60usということがわかった。CRTのリフレッシュサイクルは1/60秒だから、16.7ms。水平同期周波数で480ラインを走査するのにかかる時間を差し引くと1400us程度になる計算だったのだが、実際はその23分の1程度しか時間がかかっていないことがわかった。

実機計測

FM TownsのSprite Busyフラグの動作とVSYNCの関係を見るために、簡単なCプログラムを書いた。最初、プログラムは、

  1. Sprite Readyを待つ
  2. Sprite Busyを待つ
  3. 256回1usごとにVSYNCとSprite Busyフラグをサンプルして記録
  4. 画面に表示
この処理を繰り返すように書いた。これによってすぐわかったのは、Sprite Busyの立ち上がりはVSYNCの立ち上がりと一致するということだった。赤本が言うようにVSYNCの経ち下がりでSprite Busyが立ち上がるわけではなかった。

そこで、プログラムを以下のように修正した。

  1. Sprite Readyを待つ
  2. VSYNC=0を待つ
  3. VSYNC=1を待つ
  4. 2560回26usごとにVSYNCとSprite Busyフラグをサンプルして記録
  5. 画面に表示
この方法で、640回のサンプルで16640usをカバーするので、画面を横いっぱいに使うとCRTのリフレッシュサイクル1回分、1/60秒を一行に表示できる。また、ゲームパッドでスプライト表示個数を上下させることができるようにした。まず、次の図はスプライト個数を10個としてFM Towns II MXでサンプルした結果。

このグリーンがSprite Busy、白がVSYNCを表す。ここから徐々にスプライト表示個数を増やしていくと、289個までは確実に1リフレッシュサイクルで転送が完了することがわかった。

しかし、表示個数が290個に達すると、結果が不定になり、1リフレッシュサイクルで完了する場合と完了しない場合があった。291個でも結果は同様で、292個になると確実に1リフレッシュサイクルで完了しなくなった。

結果が一定しないので、表示もサンプルするごとに変化するのがわかる。

表示個数が292個になると、ふたたび結果が安定。必ず1リフレッシュサイクルでは完了しなくなった。

また、個数582個ではちょうど2リフレッシュサイクルで転送が完了する。

個数583個になると、ちょうど3リフレッシュサイクル目の最初で転送が完了するようで、290個のときのような不安定性は見られなかった。

このことから、1リフレッシュサイクルにかかる時間16666usを290で割ることで、スプライト1枚の転送にかかる時間は57.46us程度であることがわかる。(VRAMクリアに32usかかると仮定しても16634/290=57.35us)この値はFM Townsシリーズを通して一定のはず。そうでないとスプライトBusy/Readyでタイミングを取っているゲームがMXの高速モードでスピードが変わったりするはずだ。もうひとつわかったのは、Sprite ReadyからBusyに切り替わるのは必ずVSYNCの立ち上がりであるということ。おそらくSpriteのハードウェアはVSYNCの立ち上がりで動作を開始している。赤本が言うように経ち下がりではない。このことから、スプライトの転送が次のVSYNCの立ち上がりまでに間に合わなかった場合、次の動作開始はその次のVSYNCの立ち上がりまで発生しない。

もう少し精密に計測できないか、サンプリングインターバルを変更できるようにして計測してみた結果が以下の通り。サンプリングインターバルを5usまで下げても結果は安定していたが、4usでギリギリ、3usに下げると不正確な値が出始めた。





なお、数字"12 5"は、VSYNCが1と計測された回数が12回、サンプリングインターバルが5usを意味する。このことから、VSYNCが1となる期間の長さは12*5=60usであることがわかった。予想では、1リフレッシュサイクルから480ラインの走査に必要な時間を差し引くと1400us程度かかると見ていた。実際はVSYNCが1になるのは、それよりもかなり短い期間ということがわかった。

また、スプライト個数を10個~14個まで変化させたとき、かかる時間は、119*5=595us, 130*5=650us, 141*5=705us, 153*5=765us, 164*5=820usという結果になった。サンプル数が少ないので大雑把だが、これを最小二乗法でy=ax+bにフィットすると、
y=56.5x+29
という結果になる。スプライト1枚あたり56.5usとして、VRAMクリアにかかる時間が29us。32usに近い。

結論

VSYNCとSprite Busyのタイミングがはっきりわかっていなかったので、FM Townsエミュレータ「津軽」でGenocide 1/2, Shadow of the Beast等でスプライトが更新されなくなる問題が起きていたが、実機サンプルを取ったことで、曖昧だった実装を実機に合わせることができた。また、一部ではFPGAでTownsを再現しようという動きもあるようなので (FPGAのDOSから始めれば十分可能のはず)、スプライトのタイミングを解明できたのは、Townsの保存にそれなりに有益かもしれない。

しかし、こんなことを調べても直近には何の役にも立たないので、何の役にも立たないプログラムを書くのはやっぱり楽しい、と、思った。(それが結論か!)

参考文献

千葉憲明, FM Townsテクニカルデータブック, ASCII, 1989
Comments are welcome.  Send E-Mail to: 

Back to http://www.ysflight.com