[1chipMSX]

走査線割り込み関連のバグ

※情報の正確さは保証できません。

1chipMSX の VDP互換回路には、実機には無い次のような回路が含まれています。

(1) 垂直ブランキング期間に発生した走査線割り込みは保留扱いになる
(2) R#19 に書き込むと走査線割り込み要因が解除される
(3) R#0 にある条件が揃っているときに書き込むと走査線割り込み要因が解除される
(4) R#23 + 227 ライン目以降に設定された走査線割り込みが発生しない

実機やソフトエミュレータ等で検証すると、上記のような機能はありませんでした。

割り込み要因とは VDP内部の負論理信号であり、反転した信号が S#1 の bit0 に現れています。
この信号が L の間に、CPU が割り込みを受け取れる状態になると、CPU に割り込みが掛かります。

一般に、割り込み要因を L にするのは VDP の仕事、H に戻すのは CPU の仕事だと思います。
この約束を守らず、VDP からも CPU からも H に戻す処理が行われると、CPU は期待した割り込み
が入らなくて、暴走してしまったり、挙動不審になるケースがあります。

上記のバグ(?)は、走査線割り込みの一般的な使い方(1フレーム中1回程度)をしている分
には、何ら問題が発生しないので、なかなか気が付きませんが、ハードの限界まで引き出すような
シビアなソフトを動かすと露見する場合があります。


【フリーソフト SCMD での再生不都合】

SCMD というフリーソフトは、ライン番号 0Fh, 3Bh, 67h, 92h, BEh, EAh で走査線割り込みを掛けて
音楽の演奏制御を行っています。
EAh = 234 なので、垂直ブランキング期間中であり、1chipMSX では垂直ブランキング期間中に走査線
割り込みが保留になる( S#1 のフラグは 1 になるものの、割り込み信号は出てこない )ため、
234ラインで要求したはずの割り込みが、少し遅れて垂直ブランキング期間を解除した直後の 0ライン目
で発生します。
ところが、走査線割り込みの間隔を 43〜44ラインで期待しているために、0Fh = 15 ラインまでに処理を
終えられず、0Fh を過ぎた辺りで R#19 に 0Fh を書き込みます。
このために、ライン 15 で発生する走査線割り込みが、次のフレーム(1/60秒後)に延期され、現在の
フレームの他の割り込みも掛けられずに、テンポが遅れてしまいました。



緑LED の位置に VDP 内部信号を引き出して、オシロで観測してみました。
黄色が hsyncInt_n, 緑が vsyncInt_n に相当する信号で、SCMD動作中の波形です。
6回の走査線割り込みは、等間隔(43〜44ライン間隔)で入れているはずなのに、最後の EAh の割り込み
だけすこし遅れているのが確認できます。さらにその後の 0Fh の割り込みが1フレーム遅れて発生している
のも確認できます。


(1) 垂直ブランキング期間に発生した走査線割り込みは保留扱いになる を削除する [2007/02/04 修正]

上に挙げた SCMD での不都合は、これが原因です。
垂直ブランキング期間中に走査線割り込みを発生させない処理は、わざわざそのようにマスクする回路
が埋め込まれています。
ソースでは下記の部分です。

if (preDotCounter_yp = "000000000") then
 -- JP: 開始は0でよさそう。-1にすると、MSXの起動ロゴがうまく表示できなくなる。
 enaHsync <= '1';
elsif (preWindow_y = '0' ) then
 enaHsync <= '0';
end if;

これを削除して、代わりに下記の一行を追加します。

enaHsync <= not preDotCounter_yp(8);

preDotCounter_yp は、VDP互換回路内部の信号であり、走査線番号を示しています。
常に画面の一番上が 0 という点が R#19 に設定する値と違う点です。
1chipMSX の VDP互換回路は、R#18 の画面位置調整機能を実現するために、走査線番号を負数から開始するという
トリックを使っています。
一方で、R#23 による垂直スクロール機能は、画面が循環するために、R#19 = 0 のときの走査線割り込みが画面中央で
発生するようなケースもあるわけです。preDotCounter_yp + R#23 を単純に R#19 と比較すると、この循環に対応でき
ないために、preDotCounter_yp + R#23 の下位 8bit を R#19 と比較しています。
このときに、負数が不都合を起こすため、負数をとりうる垂直ブランキング期間を走査線割り込み禁止期間としている
のではないかと思います。
しかし実機では、垂直ブランキング期間の一部で走査線割り込みは発生できるので、SCMD のような、それを利用する
アプリが存在するわけです。
問題は、負数の時だけなので、素直に負数をとるときだけ走査線割り込みを禁止すればいいわけです。
比較しているのは、下記の部分。

  (preDotCounter_y(7 downto 0) = VdpR19HSyncIntLine) ) then

これを、下記のように修正します。

  (enaHsync = '1') and (preDotCounter_y(7 downto 0) = VdpR19HSyncIntLine) ) then


上の写真とスケールが違っていて、比べにくくてすみません。(^^;

黄色が走査線割り込みです。上の写真では、発生しない期間がありましたが、修正後はしっかり等間隔で
発生しているのが確認できます。


(2) R#19 に書き込むと走査線割り込み要因が解除される を削除する

VDP の書き換えは、基本的に割禁にして処理しますが、その割禁の最中に走査線割り込みが掛かり、
R#19 を書き換えた瞬間に、掛かっていた走査線割り込みが無かったことにされるので、たとえば
画面を上下に分割していて、上が空で下が海、境目でパレット切り替えしていて、波の表現で上下
するような場合に、非常に希に表示が乱れる可能性があります。

ソースでは、下記の部分にあたります。

 when "10011" =>  -- #19
  VdpR19HSyncIntLine <= VdpP1Data;
  hsyncIntAck <= hsyncIntReq;

これを下記のように修正します。
 when "10011" =>  -- #19
  VdpR19HSyncIntLine <= VdpP1Data;
-- hsyncIntAck <= hsyncIntReq;


(3) R#0 にある条件が揃っているときに書き込むと走査線割り込み要因が解除される

これにより発生する問題は思いつかないですが、少なくとも実機で走査線割り込みを発生させ、
S#1 を読むかわりに、R#0 に値を書き込んでも割り込み要因はクリアされないのが確認できたため
実機には存在しない機能だと思います。

ソースでは、下記の部分にあたります。

 when "00000" =>  -- #00
  VdpR0DispNumX <= VdpP1Data(3 downto 1);
  VdpR0HSyncIntEn <= VdpP1Data(4);
  -- under testing
  if( VdpP1Data(4) = '1' ) then
   hsyncIntAck <= hsyncIntReq;
  end if;

これを下記のように修正します。
 when "00000" =>  -- #00
  VdpR0DispNumX <= VdpP1Data(3 downto 1);
  VdpR0HSyncIntEn <= VdpP1Data(4);
  -- under testing
-- if( VdpP1Data(4) = '1' ) then
--  hsyncIntAck <= hsyncIntReq;
-- end if;

under testing ということで、テスト用に埋め込んだ回路がそのまま残っていたのでしょうかね。



(4) R#23 + 227 ライン目以降に設定された走査線割り込みが発生しない

ソースをみると、画面の一番上を 0 とするカウント値が 227 で止められています。
走査線割り込みは、この値に R#23 の値を加算後、下位 8bit が R#19 とコンパレートされて、
一致したときに割り込み要因がたつ仕組みになっていますから、たとえば R#23=0 の時は、
228ライン目以降に設定された走査線割り込みは正常に入らないだろうし、227 に至っては
カウント値がクリアされるまでの間に割り込み処理を終えてしまうと、R#19 が無変更なら、すぐに
また割り込んでしまうかもしれない。ちょっと危ないです。

FS-A1F実機で動作確認した人によると、SCREEN0〜4 は 234まで、SCREEN5〜8 は 244
までは走査線割り込みが入るのを確認できたそうですが、わざわざ割り込みの入らない位置に
R#19 をセットして、走査線割り込みを有効にしているソフトも、そうは無いでしょうから、
思い切ってカウンタを止めている回路を取り除く(全てのラインで走査線割り込み有効)ことにします。

VDP.VHD の中で、下記のような場所を探してください。

 if( preDotCounter_yp = 227) then
  preDotCounter_yp_v := preDotCounter_yp;
 else
  preDotCounter_yp_v := preDotCounter_yp + 1;
 end if;

下記のように修正してください。
-- if( preDotCounter_yp = 227) then
--  preDotCounter_yp_v := preDotCounter_yp;
-- else
  preDotCounter_yp_v := preDotCounter_yp + 1;
-- end if;

この修正後に、アレスタ2を動かしてみましたが、問題無さそうでした。

(1)〜(4) の修正を加えると、SCMD で SCCPCM 再生時に実機では発生しないノイズが混入する現象と、
テンポが半分に落ちてしまう現象が解消されます。
ついでに SCC2倍化改造もあわせて行えば、1chipMSX だけで SCMD の豪華な演奏を楽しめるようになります。

[▲上へ]