10進表示
このプログラムで、Z80 vs 6502 の比較は一旦終了。
割り算ルーチンがあるものとして、10進表示ルーチンを作ります。
目次
Z80 vs 6502 トップページ(別ページ)
概要
16bit の数値を 10進数で表示するための、文字列を作ります。
文字列は ASCII で、末尾に 0(ヌル文字) が入るとします。つまり、普通の C 言語文字列として扱える、上位 4bit が 3 であるゾーンBCD形式を作ります。
文字列は DECIMAL: とラベルのつけられたアドレスから格納します。
16bit なので最大5桁ですが、末尾の 0 も含めた 6byte 分のメモリが確保されています。
(高速化のため必要であれば、もっと多くのメモリがあると想定しても構いません)
文字列の頭は 0 や空白を付けず、10進の最上位桁から始まるようにしてください。
(文字列は常に5桁ではなく、1~5桁になります)
DIV というラベルで、割り算ルーチンを呼び出せるものとします。
Z80 では DE に被除数 A に除数を入れて呼び出すと、DE に商、A に剰余が帰ります。
6502 では、>0 >1 に被除数 A に除数を入れて呼び出すと、>0 >1 に商、Aに剰余が帰ります。
仮に、6502 では 400クロック、Z80 では 800 クロックが除算にかかるものとします。
MUL というラベルで、16bit × 8bit の掛け算ルーチンを呼び出せるものとします。
(ただし、結果は 16bit で返ります。被乗数はあまり大きく設定できません)
値の受け渡し法は DIV に準じます。
16bit の被乗数が破壊されて、結果が帰ります。
6502 で 150クロック、Z80 で 300 クロックとします。
乗除算ルーチンは共に、他のレジスタやメモリは破壊されないものとします。
8086 という数値を10進で文字列化してください。
除算はただでさえ重い処理ですが、ここで桁数だけ繰り返し呼び出されます。
表示用の10進文字列を作る処理は、非常に重たいことになります。
(実際には、任意の数字での割り算ルーチンではなく、10で割った商と剰余を求める専用ルーチンを用意したりするのですが)
Z80基本
LD HL,DECIMAL+5 ; 10 (11)
LD (HL),0 ; 10 (11)
LD DE,8086 ; 10 (11)
LD BC,1 ; 10 (11)
LOOP:
LD A,10 ; 7 (8)
CALL DIV ; 17 (18) + 800
OR 30H ; 7 (8)
DEC HL ; 6 (7)
LD (HL),A ; 7 (8)
INC C ; 4 (5)
LD A,D ; 4 (5)
OR E ; 4 (5)
JR NZ,LOOP ; 12 / 7 (13 / 8)
AFTER:
LD A,C ; 4 (5)
CP 6 ; 7 (8)
JR Z,END ; 12 / 7 (13 / 8)
LD DE,DECIMAL ; 10 (11)
LDIR ; 21 / 16 (23 / 18)
END:
これがやりたくて前回除算を作ったのだけど、除算の存在を前提にすると簡単すぎるな (^^;
数値の10進化、というと、10で割った余りをスタックに一旦貯めて、後で取り出しながら並べる、と言うのが基本です。
しかし、Z80 のスタックは 16bit 単位のアクセスしかできません。
ここでは、仮に右詰で並べてから、ブロック転送命令で左詰めしています。
ブロック転送は過去のプログラムで高速化の手法がありましたので、もっと高速化できそうですが「基本」なので簡単に…
前処理が 44 クロック
ループ内が、1桁の処理で 77+800 クロック。800クロックは除算ルーチンです。
ただし、最後の桁は5クロック速くなります。
4桁あるので、877*4 -5 = 3503 クロックになります。
後処理も桁数で異なりますが、4 桁の時が最も遅く、119 クロック。
44+3503+119 = 3666
総計は、3666 クロックになります。
6502基本
LDA #>8086 ; 2
STA >0 ; 3
LDA #<8086 ; 2
STA >1 ; 3
LDA #0 ; 2
PHA ; 3
LOOP:
LDA #10 ; 2
JSR DIV ; 6 + 400
ORA #$30 ; 2
PHA ; 3
LDA >0 ; 3
ORA >1 ; 3
BNE LOOP ; 3 / 2
LDX #-1 ; 2
AFTER:
INX ; 2
PLA ; 4
STA DECIMAL,X ; 5
BNE AFTER ; 3 / 2
END:
6502 では、素直にスタックを使用しています。
教科書に乗ってそうなプログラムで、特に説明はいらないかな…
前処理に 15 クロック。
1桁の処理に 22 クロック。
最後は1クロック速くなり、次の処理のために 2 クロックかかります。
つまり、最後は +1 クロック。
後処理に、1桁ごとに 14+400 クロック。400 は除算ルーチンです。
これも最後は 1 クロック速くなります。
15 + 22*4 +1 +414*4 -1 = 1759
総計は 1759 クロックになります。
速度判定
6502 は 1759 クロック。周波数を考慮するため2倍すると、MSX での 3518 クロック相当の実時間になります。
Z80 は 3666 クロックなので、148クロック、4% くらいファミコンが速い。
これ、前提としている割り算部分の速度が非常に大きいですね。
実際には Z80 と 6502 で割り算の必要クロックも変わりそうですが、ここではある程度の値で決め打ちし、両者同じ速度になるようにしています。
掛け算をうまく使って 10 固定の除算を作り出すと、もう少し早くなるかな?