Z80の最近のブログ記事

ラッチを使ってZ80との入出力ができることがわかったので、修正した回路図です。 Webページに収まるようになるべく小さく描いたので見にくいのはご勘弁。


20081225-AVR_SD-OR3.PNG

う~ん、4075(3入力OR)は持ってないぞ。 32(2入力OR)ならあるんですが、効率が悪いんですよね。 4078(8入力OR)を使ってもいいですね。 ゲートの段数も少なくなってすっきりします。 こんな感じ。

20081225-AVR_SD-OR8.PNG

その場合、グランドになっている部分を変更することでA1までデコードできます。 AVRにA0だけしか入れられなくなるけど。 まだPCからのインタフェースは設計途上なのですが、一応下位のアドレスラインはデコードして使うかと思い、残してあります。

ゲートはすべてHCシリーズを想定しています。 Z80へデータを出力するときのみ、レベルあわせのために5Vを供給しています。 ちゃんとバッファICを使ってないけど、大丈夫かなこんなんで?

事実上、NORが1ゲートあまっている(4-5-6ピン)ので、必要ならA6~A2のどこかにNOT代わりに入れればI/Oアドレスを調整できますね。

今のところ最下位アドレス(0x00~0x03)を使う想定になっています。 AVRのI/Oが3本ほど余っていますが、外部クロックを使うことになったときに使うもよし、他の機器をコントロールするもよし。

...とりあえずデバッグLED用かな?


とりあえず、前回のテストは期待通りの動作をさせることができました。 原因はあちらこちらの断線でした。

UEWを使っていて、しょっちゅう回路を変更するものだから、そのたびに線をいじったりしているうちに断線してしまっているのでした。 直しては他の箇所が断線するいたちごっこ状態。 こんなんじゃ先が思いやられるなぁ。

ちょっと面白かったのは、LS373の電源(Vcc)が断線していたときのことです。 入力端子の電圧が3.3V程度あり、そのときの出力端子の電圧が2.3Vくらいあるんですね。 もちろんVccがないので出力コントロール(^OE)が利かず、バス直結のラインにその電圧を出力していたわけですが、なぜかPC側本体側は暴走しませんでした。 本体側でデータバスがぶつかってるのに。

どうも、拡張スロットへのデータバス入出力にはLS245または他のバッファが接続されているようです。 拡張メモリを使っていない状態だったため、拡張スロットへの入出力がこの実験用回路に向けたもののみだったのが幸いしたのでしょう。

今回はPC側から見て入力のみを実験しましたが、同じ手法で出力もできます。 というか、出力のほうが楽(あらかじめデータを用意しなくていいから)ですね。 また回路変更で断線しまくりそうで怖いですが。

さて、そうなると次はSDカードのアクセスか...。

I/O回路の要求には高速応答性が必要で、到底AVRの割り込み反応時間では間に合わないことがわかったので、バッファを入れる実験をしました。

例により、簡略化した回路です。 手持ちのラッチがLS373しかなかったので、電圧が混在しています。

20081221-Test.PNG

プログラムのほうは、なんかoutしたら、そのタイミングでデータを出力し、inで読めるようにしてみました。単純にデータをインクリメントしているだけです。

.org	0
	rjmp	RESET
	rjmp	INT_IN
	rjmp	INT_OUT
INT_IN:
	inc		Data
	; buffer input close
	cbi		PORTB, 1
	reti
INT_OUT:
	; output data
	mov		Temp, Data
	ori		Temp, 0x40
	out		PORTC, Temp
	mov		Temp, Data
	ori		Temp, 0x3f
	out		PORTD, Temp
	; load data to the buffer
	sbi		PORTB, 1
	reti
RESET:
	ldi		Temp, low(RAMEND)
	out		SPL, Temp
	ldi		Temp, high(RAMEND)
	out		SPH, Temp
	ldi		Data, 0x00
	ldi		Temp, 0x12
	out		DDRB, Temp
	ldi		Temp, 0x3f
	out		DDRC, Temp
	ldi		Temp, 0xc0
	out		DDRD, Temp
	ldi		Temp, 0x0a
	out		MCUCR, Temp
	ldi		Temp, 0xc0
	out		GICR, Temp
	sei
LOOP:
	rjmp	LOOP

とりあえずの「データを読ませる」という結果としては良好です。 AVRからのデータらしきものがPC側から読めています。

ただ、なんかD0がふらふらする変な挙動です。 outしないでinし続けると0が入力され続けるはずなのに、ときどき1が混じります。 out/inを交互に繰り返すと、大部分が2回ずつ同じ値を読んで、次は2インクリメントします。

データラインがショートしたりているわけではなさそうなのですが、いまのところ原因はわかりません。接触が悪い可能性はありますが、直しても同じなのが謎。そこら辺から剥ぎ取ったラッチが変なのかも。

とりあえずラッチ意味があったということで勘弁しといてあげよう(弱気)。


Z80からのI/O信号に対し、どのくらいでAVRによる割り込み反応が得られるのかを調査してみました。 回路は前回作ったものを使います。

PC-6001側からI/O命令を発行し、AVRの外部割込み入力をトリガにして反応を調べます。 まずは、主な信号線の様子。

20081214-INT0.PNG

ここで、INT0が活性化されている時間(A-B)は、630ns。

実行されているプログラムは以下のような感じです。

.org	1
	rjmp	INT_IN
INT_IN:
	sbic	PIND, 2	; 割り込み終了だったらInputへ
	rjmp	Input
	; omit...
Input:
	out	DDRC, r18
	out	PORTC, r19
	sbi	PORTB, 4
	reti

INT0は両トリガでの割り込みにしているのですが、 AVRの割り込みルーチンが反応するときには、INT0はHレベルになっているのか、常にInputルーチンへ飛びます。

上図にあるLEDという信号線は実際にはAVRのポートB4で、これがLからHになる(sbi PORTB, 4)まで2,040nsかかっています。 8MHzだと約16クロック分です。

割り込みへの反応速度を見るために、プログラムを少し変えてみました。

.org	1
	rjmp	INT_IN
INT_IN:
	sbi	PORTB, 4
	; omit...

割り込みがアクティブになってから、rjmpおよびsbiのみの実行です。 タイミングは以下のようになっています。

20081214-INT1.PNG

これで、LED(PORTB4)の変化までの反応時間は1,200nsでした。8MHzで9.6クロック分。 rjmpとsbiの実行時間4クロックを差し引くと、割り込みの反応に700ns(5.6クロック)ほどかかることになります。

ちなみに、割り込みをダウンエッジのみにして追試してみましたが、反応時間は1,160nsで、それほど大きな差はありませんでした。

これを、最初の図と照らし合わせると、INT0の活性化時間が700nsに満たないため、割り込みルーチン開始時にはどう足掻いてもI/Oには間に合わない、ということになります。

やはりどうしてもZ80とAVRの間にはラッチが必要ということになりますね。


レトロパソコンのインテリジェントな周辺機器を作るのに 大きな間違いに気づいてショックな今日この頃。 デコーダ・バッファは8255が入手困難な現在、手持ちのCPLD(Xilinx XC9536XL)があるのでそれでやってみようと思うのですが、困ったことにライタがありません。

ライタを作ろうにも、そのための部品が手持ちでは苦しいところです。 またしても鶏卵問題に陥りそうなのですが、悩んでいても埒が明かないので、とりあえずやれることからやってみることに。


まずは、テスト用に以前の回路を手直しして、以下のような簡単な回路を作ってみました。

20081213-Test.PNG

入出力に対応させてありますが、とりあえずそれは気にしないでin命令に応じてLEDを点滅させてみることにしました。

AVRのプログラムは以下のとおり。

.include	"m8def.inc"
.org	0
	rjmp	RESET
INTERRUPT:
	sbic	PORTB, 4	; 前回は点灯ならスキップ
	rjmp	LEDON
	sbi	PORTB, 4	; LEDを消灯
	reti
LEDON:
	cbi	PORTB, 4	; LEDを点灯
	reti
RESET:
	ldi	r24, low(RAMEND)
	out	SPL, r24
	ldi	r24, high(RAMEND)
	out	SPH, r24
	ldi	r24, 0x10
	out	DDRB, r24
	cbi	PORTB, 4	; 初期状態は点灯
	ldi	r24, 0x02
	out	MCUCR, r24	; 割り込みモード(INT0ダウンエッジ)
	ldi	r24, 0x40
	out	GICR, r24	; INT0割り込みイネーブル
	sei	      		; 割り込み許可
LOOP:
	rjmp	LOOP

LOOPでの無限ループ中に、割り込みがかかるとINTERRUPTルーチンが走ります(INT0のベクタアドレスに直接書いています)。 割り込み条件はINT0のダウンエッジです。

INT0は回路にあるように、IORQ | RD | A7 | A6なので、これらがすべて0のとき、すなわちBASICなどで

a=inp(0)
  

を実行すると点滅します。

ところが、不思議なことに放っておいても(BASICのテキストモードで何もしなくても)不規則に点滅します。

割り込みの条件から考えると、0x00~0x3fのI/Oリード要求により点滅するはずなのですが...。 何かおかしいんでしょうかねぇ。

以前設計した回路には、重大な盲点がありました。 2点あります。

  1. 割り込み要求線に^RD/^WRを入れておかないと、おかしなタイミングで割り込み要求がかかる
  2. Z80からのI/O要求に対する応答が間に合わない

割り込み要求線

以前の設計では、割り込み要求は次のようにしていました。

20081213-AVR1.png

IORQの確定とRD/WRの確定には時間差があるようで、どうもIORQの確定のほうが早いようです。 そのため、割り込み要求後にRD/WR線を調べないと本当にI/Oアクセスがかかっているのか分からないです。

そのため、INT線に入れる要求はRDまたはWRとORを取っておく必要があります。

20081213-AVR2.png

なお、デコードに常にA7を入れているのは、PC-6001の場合、I/Oポート0x80~はシステムによって予約されているためです。


I/O要求のタイミング

以前設計していたときは、Z80(約4MHz)のin/out命令のスピードが最低でも13クロックかかるため、8MHzのAVRなら26命令分の余裕があると思っていました。

ところが、これが大間違いだったのです。 I/Oの実際の信号が出るのは、命令読み込みではなく命令実行のタイミングのため、IORQが確定するのはかなり遅くなります。

もっとも高速なIN A, (n)やOUT (n), Aで考えても、M1ステートで命令を読み込み、M2ステートでI/Oアドレスを読み込むので、実際のI/O処理が行われるのはM3ステートのみです。

データシートを元に考えると、おおよそ以下の図のようになります。

IN A, (n)のM3ステート時の動作

20081213-Z80_RD.png

OUT (n), AのM3ステート時の動作

20081213-Z80_WR.png

すると、I/Oリクエストによる割り込みからデータ入出力まではZ80クロック基準で2.5クロック分くらいしか余裕がないのです。

これに対処するには、8MHzのAVRの割り込み処理ではとても追いつきません。 専用のデコーダと入出力用のデータバッファが必要になります。

ていうか、一般的にはこれは常識っぽいですよね。 8255がある理由がそうですから。 なんと間抜けなことをしたことか...。


Hexameter 2.1.2をリリースしました.

Sourcceforgeからダウンロードできます.

変わったのは、SDOS 1.1用のテンプレートを用意したことだけですのでたいして大きな変化ではないですが、もしSDカードアダプタをお持ちの方でSDカードをアクセスしたいという奇特な(?)方がいらっしゃればぜひご利用ください. また、SDOS開発時に作成したいろいろ有用なルーチンも用意してますので、良かったら使ってみてください. 戦士カートリッジ版、1M ROMアダプタ版双方で動作するはずです.

演算ライブラリについて
sdccには基本的な標準ライブラリが入っています。 その中には

stdio.h
stddef.h
stdlib.h
string.h  
など、C言語として当たり前のものもあるのですが、その他に、基本的な演算や浮動小数点なども含まれます。
たとえば、整数の掛け算や割り算をする場合も、関連するライブラリがリンク時に埋め込まれます。z80は標準で掛け算や割り算の命令を持たないので、これは仕方のないところです。

ただ、演算ライブラリは関数単位ではなくオブジェクト単位で埋め込まれるので、不要なものまで入ってしまうことがあります。

演算ライブラリのリンク例

たとえば、次のような簡単なプログラムをコンパイルしてみましょう。
-- test.c
long mul(long a, long b) {
  return a * b;
}
void main () {
  mul(654321, 123456);
}
-- 
今回のプログラムは試しやすいようにアーカイブを用意しました。

sdcc5.zip (sdccとMakeのできる環境(UNIX系のOSやCygwinなど)が必要です)

Makefileが入っていますのでmakeのみでコンパイル可能です。 まずは、アーカイブのsample1ディレクトリでmakeしてみてください。

Makefileにはいろいろ書いてありますが、要は次のようにしているのと同じです。

% sdcc -mz80 --code-loc 0 --no-std-crt0 test.c  
今回は話を簡単にするため、crt0は使っていません。

さて、これでオブジェクトのてtest.ihxのほか、マッピングファイルのtest.mapが出力されていると思います。
mapファイルは、グローバルシンボルがどこに配置されているか、各エリアのサイズがどのくらいかが示されています。

そのmapファイルを見てみましょう。

-- test.map(抜粋)
(中略)
Area                               Addr   Size   Decimal Bytes (Attributes)
--------------------------------   ----   ----   ------- ----- ------------
_CODE                              0000   090D =   2317. bytes (REL,CON)

      Value  Global
   --------  --------------------------------
     0000    _mul
     0000    _mul_start
     0032    _main
     0032    _main_start
     0032    _mul_end
     004A    __mullong_rrf_s
     004A    __mullong_rrx_s
     004A    _main_end
     004D    __modslong_rrf_s
     004D    __modslong_rrx_s
     0050    __modulong_rrf_s
     0050    __modulong_rrx_s
     0053    __divslong_rrf_s
     0053    __divslong_rrx_s
     0056    __divulong_rrf_s
     0056    __divulong_rrx_s
     0059    __mulint_rrf_s
     005F    __divsint_rrf_s
     0065    __divuint_rrf_s
     006B    __mulschar_rrf_s
     0071    __divschar_rrf_s
     0077    __muluchar_rrf_s
     007D    __divuchar_rrf_s
     0083    __modschar_rrf_s
     0089    __moduchar_rrf_s
     008F    __modsint_rrf_s
     0095    __moduint_rrf_s
     009B    __rrulong_rrf_s
     00A1    __rrslong_rrf_s
     00A7    __rlulong_rrf_s
     00AD    __rlslong_rrf_s
     00B3    __divschar_rrx_s
     00BA    __divschar_rrx_hds
     00C1    __modschar_rrx_s
     00C8    __modschar_rrx_hds
     00CF    __divsint_rrx_s
     00DB    __divsint_rrx_hds
     00E3    __modsint_rrx_s
     00EF    __modsint_rrx_hds
     00F7    __divuchar_rrx_s
     00FE    __divuchar_rrx_hds
     0105    __moduchar_rrx_s
     010C    __moduchar_rrx_hds
     0113    __divuint_rrx_s
     011F    __divuint_rrx_hds
     0127    __moduint_rrx_s
     0133    __moduint_rrx_hds
     013B    .div8
     013B    .mod8
     0143    .div16
     0143    .mod16
     0180    .divu8
     0180    .modu8
     0183    .divu16
     0183    .modu16
     01B8    __rrulong_rrx_s
     01D5    __rrslong_rrx_s
     01F2    __rlslong_rrx_s
     01F2    __rlulong_rrx_s
     020F    __modslong
     020F    __modslong_start
     02E8    __modslong_end
     02E8    __modulong
     02E8    __modulong_start
     03FF    __divslong
     03FF    __divslong_start
     03FF    __modulong_end
     04E1    __divslong_end
     04E1    __muluchar_rrx_s
     04F4    __mulschar_rrx_s
     04FB    __mulschar_rrx_hds
     0507    __mulint_rrx_s
     0513    __mulint_rrx_hds
     0513    __muluchar_rrx_hds
     052C    __divulong
     052C    __divulong_start
     0610    __divulong_end
     0610    __mullong
     0610    __mullong_start
     090D    __mullong_end
-- 
いかがでしょうか。test.c本体は0x0000~0x0049に配置されています。staticスコープではない関数はstartとendのシンボルが付加されます。_main_endなどの終了位置は終了アドレス+1を示します。

そして、0x004a~0x090cはすべて演算用モジュールです。見ると、divだのmodだの、関係ないものまでたくさん入っていて、そのサイズは実に2,243バイトに達します。

では、もし標準ライブラリをまったくリンクしないとどうなるでしょう。

% sdcc -mz80 --code-loc 0 --no-std-crt0 --nostdlib test.c
?ASlink-Warning-Undefined Global '__mullong_rrx_s' referenced by module 'test'
こうなると思います。つまり、掛け算で'__mullong_rrx_s'という関数を呼び出しているわけです。

この関数が定義されているのは、 SDCC/lib/z80/stubs.o というオブジェクトなのですが、実はこのオブジェクトで演算の本体が実装されているわけではありません。名前のとおりこのオブジェクトはただのスタブで、演算に関するありとあらゆるオブジェクトを参照するようになっています。

リンカはオブジェクト単位ですべての参照を解決しないと出力を生成しないため、スタブが参照しているオブジェクトをすべて埋め込む結果となってしまいます。

オブジェクトを追いかけてみる

ライブラリがリンクするオブジェクトを追いかけるためには、オブジェクトファイルの構造を知ることが必要ですが、ここでは最低限知っておけばいいことだけ挙げます。
先ほどのstubs.oを見ると、次のようになっています。

-- stubs.o (抜粋)
(中略)
S __muluchar_rrx_s Ref0000
S __mullong Ref0000
(中略)
S __rrulong_rrf_s Def0051
S __mullong_rrx_s Def0000
(中略)
-- 
最初の方にこのような"S"で始まる行がたくさんあります。これはシンボル定義で、シンボル名と参照・定義の種別を表しています。Refとあるのは他のオブジェクトで定義されているシンボルの参照、Defとあるのがこのオブジェクトで定義されているシンボルと定義されているアドレスです。

リンカはリンクしようとしたオブジェクトにRefで示されるシンボルがあると、それに対応するDefのシンボルが定義されているオブジェクトをリンクします。stubs.oは大量のRefがあるため、それに対応するDefのあるオブジェクトをすべてリンクするまではリンクが終了しません。

ソースを追いかけてみる

この問題を解決するためには、オブジェクトファイルを直接いじる手もあるのですが、幸いsdccにはソースがついているので、ソースを直接インポートしてしまうのが手っ取り早いでしょう。

ソースは、 SDCC/lib/src/z80/ に入っています。stubs.s を見ると、以下のようになっています。

-- stubs.s (抜粋)
__mullong_rrx_s::
__mullong_rrf_s::
        jp      __mullong
--   
何のことはない、__mullongに飛んでいるだけです。__mullongは

SDCC/lib/src/__mullong.c
に入っています。
そこで、stubs.sをコピーして、必要なmullong_rrx_s以外の定義を削除してしまいます。次に__mullong.cを個別にコンパイルしてリンクします。すると次は、

?ASlink-Warning-Undefined Global '__muluchar_rrx_s' referenced by module '_mullong'
というリンクエラーが出ます。この定義は

SDCC/lib/src/z80/mul.s
に入っているもっとも基本のライブラリなので、これもアセンブルするようにします。

これらを済ませたものがアーカイブのsample2ディレクトリに入っています。
このディレクトリでmakeしてみてください。リンクエラーは出なくなったと思います。

結果生成されたmapファイルを見てみると、

-- test.map(抜粋)
Area                               Addr   Size   Decimal Bytes (Attributes)
--------------------------------   ----   ----   ------- ----- ------------
_CODE                              0000   0395 =    917. bytes (REL,CON)

      Value  Global
   --------  --------------------------------
     0000    _mul
     0000    _mul_start
     0032    _main
     0032    _main_start
     0032    _mul_end
     004A    __mullong
     004A    __mullong_start
     004A    _main_end
     0347    __mullong_end
     0347    __mullong_rrf_s
     0347    __mullong_rrx_s
     034A    __muluchar_rrx_s
     035D    __mulschar_rrx_s
     0364    __mulschar_rrx_hds
     0370    __mulint_rrx_s
     037C    __mulint_rrx_hds
     037C    __muluchar_rrx_hds
-- 
今度はリンクされる関数がかなり少なくなりました。リンクされたライブラリの大きさは818バイトです。

__mullong.cを最適化したり、mul.sで使われていないルーチンを削ったりすることでもっとサイズを削減することも可能です。
まぁ、そのような場合は計算用ライブラリを個別に用意して、それを呼び出した方がいいかもしれません。

ちなみに、SDOSでは、以下については標準ライブラリは使わず、アセンブリ言語で独自の実装を行っています。

  • 乗算(16ビット x 16ビット = 32ビット)
  • 除算(32ビット / 8ビット = 32ビット)
どちらが絶対的によいと言うわけではないのですが、16ビットまでの大部分の演算はライブラリを呼び出さずにインライン展開されるようなので、必要な演算の種類やメモリ・速度の制約などのトレードオフを勘案して使い分ければいいと思います。

いま、PC-6001のPSGでPCM再生を試みています。
全体的にかなりいい感じで進んではいるのですが、クロック計算がなかなか合わないというのが困りものです。

カタログ上のPC-6001のクロック周波数は3,993,600Hzです。半端に見えますが、因数分解すると
3993600 = 2^12 * 5^2 * 3 * 13
となります。カセット入出力やRS-232Cなどで作られている1,200Hzの2の累乗倍で考えると、
3993600 = 1200 * 256 * 13
となり、4MHzのZ80のスペックに従いつつかなり切りのいい数字なんですね。

さて、PCM再生には正確なタイミングが不可欠です。これまでの実験で、とりあえずCD音質の44,1KHzは不可能ではないのですが、S/N比が悪くバランスが悪いことが判りました。なにせPSGの音量コントロールだと、4bitでの再生になりますからね。

その1/4の11.025KHzだと、音量に12bitを割くことができ、音質的にもかなりいい感じです。
ああ、この辺は説明してませんね。PSGを3チャンネル使うことにより可能になるんですが、私も理解が追いついていないのでこれはまた後ほどということで。

とりあえずこれで実験を続けてみました。計算だと、

3993600/11025 ~= 362.23

となります。まぁこの際小数点以下は無視するとして、3チャンネルのPSG音量をセットアップするのに362T(-state)かけられる、ということです。もちろん、外部影響を無視するため、VDGからのDMAはoffにし、割り込みも止めています。

ところが、これで再生すると、遅いんです。当然音程も低いんです。そりゃもう、一青窈が平井堅になるほどに(嘘)。

処理には余裕があるので、16Tほど削減してみました。すなわち346Tです。すると、まぁいい感じです。あ、今回はM1サイクルのT-stateをちゃんとカウントしています。
でも、完璧じゃないと言うか、やっぱり少しはずれるんです。大体、削減した16Tというのも適当に削減するウェイト用の命令を選んだだけで、なんら根拠はありません。
.......これは何かがあるに違いない、とは思うのですが、確実な原因はわからないのです。

いくつか気になることは以下のとおりです。
  • CPUの周波数が3993600Hzなのに対し、PSG(AY-3-8910)の周波数は3578545Hzです。そしてこれはMSXのCPU周波数と同じです。もっともPSGにクロックいっぱいいっぱいで出力を与えられるわけはないのですが。
  • 仮に周波数をPSGの持つ3578545Hzとして計算すると、362Tで出る周波数は約9885Hz、修正後の346Tで出る周波数は10343Hzです。
  • MSX版でPSG再生するプログラムを参考にしたのですが、そこでは
    out (c), r
    にかかるステートがなぜか15となっていました(M1 wait込み)。Z80の仕様上は12です。M1ウェイトを入れても13です。
    そして、そのMSXのCPU周波数はPC-6001のPSGと同じ、3578545Hzなのです。
どなたか教えて、Z80 Geekな人!

#注記
上記の記事を書いているときは、実は再生時にdiを忘れると言う大ポカをやってました。なので、そのときのタイミングはかなり当てになりませんでした。ただし、割り込みを禁止して実験してみたところ、やはり計算どおりには行かず、平井堅です。どうしたものでしょうね??

プリンタポートの場合

カードリーダをプリンタポートに接続する場合のコードを考えてみました。昔のパソコンの大部分は入力がBUSY信号のみですが、幸いなことにSPIで必要とする入力信号は1本なので、これで間に合います。

PC-6001のプリンタ関連のポートは以下のとおりです。
	0x91	data output
	0xc0	busy(bit1)
  
なお、回路では出力の際、インバータを通しているので、ビットは反転出力する必要があります。

では、早速コードを見てみましょう。例により、レジスタEを出力としています。
-- 
	ld	d, #0xff
_main:
	ld	a, d
	out	(#0x91), a
	dec	a
	out	(#0x91), a
	in	a, (#0xc0)
	add	a, d
	rl	e
	; _mainからを8回繰り返す
	ret
-- 
  
ここではハードウェアの設定として、以下を仮定しています。
  • CLKはビット0とします。
  • DIはビット1~7のどこでもかまいません。
ここでのトリックは、

	add	a, d
です。ポート0xc0からの入力は、ビット1以外は常に0なので、これで入力がある(a=0x02)場合は0xffを足すとキャリーが立ちます。しかも、レジスタDを使いまわせるので、利用するレジスタがA, F, D, Eの4つのみです。

結構これは重要で、このルーチンの外側でループを使ってバッファに書き込みを行うので、

	ld	(hl), e
  
のようにバッファへのポインタ(この場合HL)とループカウンタを利用します。ジョイスティックの場合はバイト入力でAFDEHLを使ってしまい、残りのレジスタは2つ。ループカウンタ用のレジスタをBにしてdjnzでまわしても、ぎりぎりレジスタが足りないので、実は裏レジスタを利用しています。惜しい...。

プリンタポートだとその必要がないのでexxと裏表のレジスタ転送の分が浮きます。バイトごとにこの処理が必要なので、1セクタあたり12T*512回分。合計6,144Tで、意外に馬鹿になりません。

もし、SDカードからの入力信号が1のときにポート0xc0の値が0になるようであれば、上記の代わりに

	ld	c, #0x02
を準備しておいて、

	sub	c
とするとよいでしょう。

T-stateは1bitあたり53、1バイトで441となります。裏レジスタを利用しなくて済む分も考慮して、1セクタあたりの時間は約260,000T、期待できる転送レートは3kB/s(VDG on)または6kB/s(VDG off)となります。前回よりさらに1.5倍のスピードになりますね。

なお、これは現在のSDカードアダプタを、ケーブル配線のみ変更すれば適用可能です。ただ、テストはしていませんのでご注意ください。

このアーカイブについて

このページには、過去に書かれたブログ記事のうちZ80カテゴリに属しているものが含まれています。

前のカテゴリはWeb 2.0です。

最近のコンテンツ アーカイブ

December 2008: 月別アーカイブ