PSG(正確にはDCSGですが)のチップであるSN76489ANをアクセスすると、19〜34クロックのウェイトがかかります。なるべく高速にPSGをアクセスしたい場合、PSGのアクセス特性に合わせた書き込みを行う必要があります。PSGとCPUに入っているクロックは共通ですので、CPUの命令実行にかかるクロックを数えることでPSGと同期して動くプログラムを書くことができます。
PSGのアクセス特性は、ソフトウェア(CPU)から見て
まず、最初のI/Oライトについてはタイミングの取りようがないので、適当に出力するしかありません。一度出力を行えば、ウェイトによって同期が取られるため、2回目以降のI/Oライトに関して上記のアクセス特性に従ってタイミングを合わせることができるようになります。
LD A, 9Fh OUT (0F2h), A OUT (0F2h), A1つ目のOUT命令にかかるウェイトは不明ですが、命令終了時点でPSG側のタイミングと同期が完了します。
この場合、2つ目のOUT命令は
4(M1サイクル) + 3(メモリリードサイクル) + 2+23(I/Oライトサイクル)
というふうに実行されます。I/Oライトサイクル間の実行時間が9クロック未満(7クロック)ですので、残り2クロックはI/Oライトサイクルのウェイトとして消費されます。
よって、32クロック周期で1バイトを書き込むことができます。
(ただし、同じ値しか書き込めないので実用性はありません)
LD C, 0F2h OUT (C), A OUT (C), Bこの場合、2つ目のOUT命令は
4(M1サイクル) + 4(M1サイクル) + 1+23(I/Oライトサイクル)
というふうに実行されます。I/Oライトサイクル間の実行時間が1クロック増えましたが、まだ9クロック未満(8クロック)ですので、I/Oライトサイクルの追加ウェイトが1に減るだけで、全体の実行時間は例1と変わりません。
よって、例1と同じく、32クロック周期で1バイトを書き込むことができます。
LD A, 9Fh OUT (0F2h), A NOP OUT (0F2h), Aこの場合、NOP以降は
4(M1サイクル:NOP) + 4(M1サイクル:OUT) + 3(メモリリードサイクル) + 14+23(I/Oライトサイクル)
というふうに実行されます。I/Oライトサイクル間の実行時間が9クロックを超えてしまったため、2つ目のI/Oライトサイクルが+16クロック分後ろにずれ、その結果ウェイトで消費されるクロックが増えてしまいました。よって、48クロック周期で1バイトを書き込むことになります。
LD A, 9Fh OUT (0F2h), A NOP NOP NOP INC HL OUT (0F2h), Aこの場合、NOP以降は
18(NOP×3,INC HL) + 4(M1サイクル:OUT) + 3(メモリリードサイクル) + 23(I/Oライトサイクル)
というふうに実行されます。例3よりも命令が増えていますが、例3ではウェイトで消費されていたクロックを命令実行に充てているため、例3と同じく48クロック周期で1バイトを書き込むことができます。
LD A, 9Fh OUT (0F2h), A OR A RET C NOP INC HL OUT (0F2h), A例4にさらに1クロック追加しました。この場合、
19 + 4(M1サイクル:OUT) + 3(メモリリードサイクル) + 15+23(I/Oライトサイクル)
というふうに実行されます。I/Oライトサイクル間の実行時間が9+16クロックを超えてしまったため、2つ目のI/Oライトサイクルがさらに+16クロック分後ろにずれ、ウェイトで消費されるクロックが増えて、64クロック周期で1バイトを書き込むことになります。
※この方法でタイミングを合わせられるのは、ポート F2h, F3h を介して各PSGに単体アクセスを行ったときだけです。ポート E9h を使って2つのPSGに同時にアクセスした場合、タイミングは不定です。
OTIROTIR命令は 12+4(I/Oライト)+5 のサイクルで実行されます。PSGアクセスでは
17 + 8+23(I/Oライトサイクル)
というふうに実行されます。48クロック周期で1バイトを書き込むことができます。