PC-6001: January 2009アーカイブ

前回の回路がまた間違ってました。

  1. アドレス線を使いたければ、データ線と同様ラッチしなければいけない
    これまでデータ線のみでアドレスのラッチを忘れていました。データのタイミングがAVRの割り込みからは間に合わないので、同然アドレスもラッチする必要があります。
  2. ラッチではなくてフリップフロップを使う必要がある(かも)
    出力リクエスト時の^WRとデータ線の確定タイミングは、8080ではデータが先に確定します。これに対し、8085や8086では^WRが先に確定するので、ラッチだと瞬間的にでたらめなデータがAVRに取り込まれる可能性があります。 Z80のドキュメントを調べてみたところ、CMOS版のZ84C004では"Data stable prior to /WR Fall"が-140ns~-10nsで、WRが先に確定することがあるようです。 実際にはAVRの反応はこれよりはるかに遅いことと、^IORQなど他の信号線とORをとっていることで実害はないと思いますが、念のためFlip Flop(374)を使うことにします。

先日日本に行った際に4075(3OR)は手に入らなかったので(代わりに4072(4OR)を買ったけど)、4078(8OR)を使って、占有I/Oアドレスをひとつだけで行くことにしました。 ソフトウェア的にはコマンドのための通信量が増えるのですが、まぁこれはトレードオフでしょう。 これ以上汎用ロジックICを増やすと、通常のDIPを使っている関係上、基板に乗り切らなくなってしまいますし、CPLDを使ったほうが簡単になるので。

というわけで、修正した回路が以下です。 I/Oアドレスは0x00に固定してしまいました。安直...。

20090127-AVR_SD-OR8.PNG

この修正で、ATmegaの6本ものI/Oが余ることになりました。 あと一つ減らすとATtiny861(20ピン)でも足りることになるのですが、実際にはRAM容量の関係(512Bytes)でちょっと厳しいですね。 FATでは512バイトのバッファを取らないとかなり性能的に厳しいので。

あ、XCKもXTAL1/2も空いているので、ATmega88(以上)なら外部クロックを使って高速に動作させることができますね。

性懲りもなくPSGPCMと格闘しています。

PSGによるPCM再生は、以前11kHz/9ビット相当の比較的高音質の再生を成功させましたが、マシンパワーおよびメモリを非常に消費する方法でした。

そのため、来るべき(?)画像と音声の同時再生に向け、いかに「軽く」発音させるかについても少し検討してみることにしました。 まずはテストとして、PSG 1チャネルで8kHz/4ビットの再生を試しました。

すると、またしてもクロック数計算が合わない事象に出くわしてしまいました。 以前はI/Oウェイトの考慮漏れが原因でしたが、今回はそうでもない感じです。

データはpacked 4bit(1バイトの上位4ビット、下位4ビットにそれぞれ音量データを格納する方式)で作成しました。 再生ルーチンの中心部は次のようになっています。

	; 割り込みは禁止、DMA OFF
	; あらかじめ音量や周波数は設定しておく
	; ポート0xa0に8を出力しておく(出力チャネルのラッチ)
	; HL=データ開始アドレス、BC=データ長
_loop:
	ld      a, (hl)		;8
	ld	d, a		;5
	and	a, #0xf0	;8
	rra			;5
	rra			;5
	rra			;5
	rra			;5
	; output high nibble
	out     (#0xa1), a	;13 -> 54 (a)
	call	_wait	 	;18 + _wait
	inc	hl		;7
	dec     bc		;7
	ld	a, d		;5
	and	a, #0x0f	;8
	; dummy
	inc	hl		;7
	dec	hl		;7
	ld	d, #1		;8
	ld	d, #1		;8
	nop			;5
	; output low nibble
	out     (#0xa1), a	;13 -> 75 (b)
	call	_wait	 	;18 + _wait
	ld      a, b		;5
	or      c		;5
	jp      nz, _loop	;11 -> 21 (c)
	; 終了処理(省略)

_wait:
   	ld      e, #24		;8
_wait_loop:			; 16 per loop
	dec	e		;5
	jp      nz, _wait_loop	;11
	ret			;11
  

ニーモニックのあとに表記しているのは実行クロックです。 M1 waitおよびI/O waitを考慮した数値です。 CB/DD/ED/FD修飾のある命令は存在せず、すべてRAM上で実行させるので、これ以外のウェイトは入らないと思います。

元データを壊してもいいならrldを使えばもっと高速になったりしますが、今回は単に実験なのであまり深く考えず、上位/下位のそれぞれが同じ時間で再生されるようにします。 同じwaitルーチンを使うために、処理の速いlow nibbleはダミーを入れています。 これで

  high nibble = (a) + (c) + wait(w/ call) = 75 + wait(w/ call)
   low nibble = (b) +       wait(w/ call) = 75 + wait(w/ call)
  

と、同じ時間で処理できます。

これだと高速すぎるので、waitルーチン内においてeレジスタの設定値で時間調整をします。 クロック周波数3993600Hzで8000Hzの再生をするためには、 1サンプルあたり、

  3993600 / 8000 = 499.2
  

となり、ひとつのサンプルがおよそ499クロックになるようにウェイト時間を調整すればいいことがわかります。 callに18クロック、ウェイトのループ以外の処理に19クロック、ループ1回に16クロックなので、逆算すると、

  (499.2 - (75 + 18 + 19)) / 16 = 24.2
  

となり、eレジスタの値を24にしています。

これを再生させると、7.25秒分のサンプルの再生に実測でおよそ11秒かかってしまいます。 当然音程も低くなります。

ひとつのサンプルにかける時間を350クロック程度にすると、おおよそ原音に近い時間と音程になります。 なぜでしょうね...??

実験はエミュレータ(PC-6001VW ver 205c)で行いました。 いま分解ですごいことになっているので、実機では試験できていません。