Z80 には、BCD (二進化十進数) の加算や減算の後に用いて計算結果を補正する DAA
命令がある。
この命令の動作について、Zilog 社の資料 UM0080 (Z80 CPU User Manual) では記述が足りなかったので、EMUZ80 を用いて調べてみた。
今回用いた EMUZ80 には、「LH0080」と書かれた CPU が搭載されている。
アセンブラとして、MikeAssembler を用いた。
UM0080 に載っている DAA 命令の情報
UM0080 において、DAA
命令の情報は、「Z80 Instruction Set」の中の「General-Purpose Arithmetic and CPU Control Groups」内に載っている。
これを見ると、DAA
命令の実行前の
-
C
フラグ -
H
フラグ -
A
レジスタ (アキュムレータ) の値
から、
-
DAA
命令の実行後のC
フラグ -
A
レジスタ (アキュムレータ) に加算する値
を求める表が載っている。
この表の内容をまとめると、以下のようになっている。
ただし、それぞれの表の左上の C=x, H=x
は、その表における DAA
命令の実行前 (入力) のフラグを表す。
図示するとわかるように、UM0080 に載っている表の情報では、出力が定義されていない入力が多い。
(検証していないが、BCD の計算の結果としてあり得る部分しか載せていないのかな……?)
また、加算をしたか減算をしたかを表す N フラグは、いかにも DAA
命令の動作に関わってきそうであるが、表には出てきていない。
(表に Operation は載っているが、Operation が違っても他の入力が被らないか結果が同じである)
さらに致命的な点として、DAA
命令の実行後の H フラグの値は「テーブルを見てね」となっているにもかかわらず、テーブルに実行後の H フラグの情報が全く見当たらないのである。
なんでや……?
なお、残りの S・Z・P/V フラグについては実行後の A
レジスタの値から求める方法が載っており、N フラグについては「変化しない」となっている。
他の資料に載っている DAA 命令の情報
互換CPUで実際に実行して得た結果とされる表が載っている。
入力の A
レジスタの値全範囲がカバーされ、入力の N フラグとの関係も載っている。
しかし、残念ながらここにも出力の H フラグの情報が無い。
表が載っているが、UM0080 に載っているものと同じようである。
daatable/daaoutput.txt at master · ruyrybeyro/daatable
DAA
命令の入力と、それに対する H フラグの値を含む出力の表が載っている。
これは「ZX Spectrum」というものでの話らしく、同じリポジトリ内に表を生成するプログラムもある。
ただし、N=1, C=1, H=0 を入力とするときの出力はなぜか載っておらず、かわりに? N=0, C=1, H=1 を入力とするときの出力がなぜか2回ずつ載っている。
内部キャリーフラグがあるかを調べるプログラムを動かす
では、「Z80 の DEC
命令は処理結果の情報を通常のフラグレジスタとは別に保存しておき、DAA
命令の動作に影響を与える……とザイログ社の資料に書かれているが、実際にはそんなことはなさそう」と主張している。
さらに、実際に DEC
命令と DAA
命令の関係を調べるプログラムが掲載されている。
このプログラムを読んでみると、以下のような特徴が読み取れた。
-
0x100
番地から配置するように指定されている -
E
レジスタに出力する文字、C
レジスタに定数 2 を格納して0x5
番地を呼び出すことで、1文字を出力しようとしている -
DE
レジスタペアに出力するデータの先頭アドレス、C
レジスタに定数 9 を格納して0x5
番地を呼び出すことで、指定したアドレスから$
の手前までのデータを連続して出力しようとしている
そこで、このプログラムを EMUZ80 で動かしてみるため、MikeAssembler の仕様に合わせるため以下の置換を行った。
-
db
をdatab
に置換する -
([0-9a-f]+)h(?! \$)
を0x$1
に、正規表現で大文字と小文字を区別して置換する
大文字と小文字を区別しないと、ラベルの fH
が置換されてしまい、プログラムが壊れてしまった。
また、否定先読みを用い、文字列中に現れる 0ffbah
を置換しないようにした。
さらに、特徴に合わせた以下のプログラムを、掲載されているプログラムの前に追加した。
内部キャリーフラグの検証プログラムを動かすためのプログラム
target z80
JP entry
ORG 5
PUSH AF
LD A, C
CP 2
JR Z, call_imp_putc
CP 9
JR Z, call_imp_puts
return_from_call:
POP AF
RET
call_imp_putc:
CALL imp_putc
JR return_from_call
call_imp_puts:
CALL imp_puts
JR return_from_call
entry:
LD SP, 0x9000
CALL 0x100
end:
HALT
JR end
; E レジスタに格納された1文字をUARTに出力する
imp_putc:
LD A, (0xe001)
BIT 1, A
JR Z, imp_putc
LD A, E
LD (0xe000), A
RET
; DE レジスタに格納されたアドレスから '$' の手前までのデータをUARTに出力する
imp_puts:
PUSH DE
PUSH HL
LD H, D
LD L, E
imp_puts_loop:
LD E, (HL)
LD A, '$'
CP E
JR NZ, imp_puts_proceed
POP HL
POP DE
RET
imp_puts_proceed:
CALL imp_putc
INC HL
JR imp_puts_loop
プログラムを実行すると、以下の出力が得られた。
[normal]
xor a A:00 F:-Z---P--
dec a A:FF F:S-5H3-N-
daa A:99 F:S---3PNC
[set AF value]
xor a A:00 F:-Z---P--
ld hl,0ffbah A:00 F:-Z---P--
push hl A:00 F:-Z---P--
inc a A:01 F:--------
dec a A:00 F:-Z----N-
pop af A:FF F:S-5H3-N-
daa A:99 F:S---3PNC
この結果は、ページに掲載されている「HB-11実機とFS-A1ST実機Z80モード(T9769C)での実行結果」と同じである。
よって、DEC
命令が A
レジスタおよびフラグレジスタ以外に保存した情報を DAA
命令が参照していることはなさそう、と判定できる。
DAA 命令の入力と出力の関係の表を作る
DAA
命令に入力する N・C・H フラグと A
レジスタの値の全ての組み合わせを試し、A
レジスタに加算される値と出力の C・H フラグを表にする、以下のプログラムを作成した。
このプログラムは、それぞれの組み合わせについて、A
レジスタに加算される値を16進数2桁で出力し、続いて H フラグ・C フラグが立っているかをそれぞれ 2・1 を足すかで表した1桁を出力する。
表では、列 (上の見出し) が入力の A
レジスタの下位4ビットを、行 (左の見出し) が入力の A
レジスタの上位4ビットを表す。
DAA 命令の出力を表にするプログラム
target z80
define FLAG_LOOP_CNT, 0x8000
LD SP, 0x8100
; 入力のフラグの組み合わせを試す
XOR A
LD (FLAG_LOOP_CNT), A
flag_loop:
; 用いるフラグの組み合わせを出力する
LD A, (FLAG_LOOP_CNT)
LD B, A
LD A, 'N'
CALL putchar
LD A, '='
CALL putchar
LD A, B
RRA
RRA
AND 1
ADD A, '0'
CALL putchar
LD A, ','
CALL putchar
LD A, ' '
CALL putchar
LD A, 'C'
CALL putchar
LD A, '='
CALL putchar
LD A, B
RRA
AND 1
ADD A, '0'
CALL putchar
LD A, ','
CALL putchar
LD A, ' '
CALL putchar
LD A, 'H'
CALL putchar
LD A, '='
CALL putchar
LD A, B
AND 1
ADD A, '0'
CALL putchar
LD A, '\n'
CALL putchar
; ヘッダを出力する
LD B, 16
header_loop:
LD A, ' '
CALL putchar
CALL putchar
CALL putchar
LD A, 16
SUB B
CALL printhex
DJNZ header_loop
LD A, '\n'
CALL putchar
; フラグの組み合わせを構築する
LD A, (FLAG_LOOP_CNT) ; A = 00000NCH
SRL A ; A = 000000NC, C flag = H
LD D, A ; D = 000000NC
LD A, 0
RRA
RRA
RRA
RRA ; A = 000H0000
OR D ; A = 000H00NC
LD E, A
; 入力のAレジスタの値を試す
XOR A
LD D, A
test_outer_loop:
LD A, ' '
CALL putchar
LD A, D
RRA
RRA
RRA
RRA
CALL printhex
test_inner_loop:
LD A, ' '
CALL putchar
PUSH DE
POP AF
DAA
PUSH AF
POP BC
; 「処理後のAレジスタの値」から「処理前のAレジスタの値」を引き、足された数を求める
SUB D
LD B, A
RRA
RRA
RRA
RRA
CALL printhex
LD A, B
CALL printhex
; 処理後のHフラグを2、Cフラグを1として、組み合わせを求める
LD A, C
RRA
RRA
RRA
AND 2
LD B, A
LD A, C
AND 1
OR B
CALL printhex
; 次の数に進む、それが16の倍数なら改行する
INC D
LD A, D
AND 0xf
JP NZ, test_inner_loop
LD A, '\n'
CALL putchar
; 一周してなければ、続行する
LD A, D
AND A
JP NZ, test_outer_loop
; 次のフラグの組み合わせを試す
LD A, (FLAG_LOOP_CNT)
INC A
LD (FLAG_LOOP_CNT), A
CP 8
JP C, flag_loop
end:
HALT
JR end
; A レジスタに格納した1バイトをUARTに出力する
putchar:
PUSH AF
putchar_wait:
LD A, (0xe001)
BIT 1, A
JR Z, putchar_wait
POP AF
LD (0xe000), A
RET
; A レジスタの下位4ビットを16進数でUARTに出力する
printhex:
PUSH AF
AND 0xf
ADD A, '0'
CP '9' + 1
JR C, printhex_noadjust
ADD A, 'A' - '0' - 10
printhex_noadjust:
CALL putchar
POP AF
RET
このプログラムを実行すると、以下の出力が得られた。
DAA 命令の出力を表にするプログラムの実行結果
N=0, C=0, H=0
0 1 2 3 4 5 6 7 8 9 A B C D E F
0 000 000 000 000 000 000 000 000 000 000 062 062 062 062 062 062
1 000 000 000 000 000 000 000 000 000 000 062 062 062 062 062 062
2 000 000 000 000 000 000 000 000 000 000 062 062 062 062 062 062
3 000 000 000 000 000 000 000 000 000 000 062 062 062 062 062 062
4 000 000 000 000 000 000 000 000 000 000 062 062 062 062 062 062
5 000 000 000 000 000 000 000 000 000 000 062 062 062 062 062 062
6 000 000 000 000 000 000 000 000 000 000 062 062 062 062 062 062
7 000 000 000 000 000 000 000 000 000 000 062 062 062 062 062 062
8 000 000 000 000 000 000 000 000 000 000 062 062 062 062 062 062
9 000 000 000 000 000 000 000 000 000 000 663 663 663 663 663 663
A 601 601 601 601 601 601 601 601 601 601 663 663 663 663 663 663
B 601 601 601 601 601 601 601 601 601 601 663 663 663 663 663 663
C 601 601 601 601 601 601 601 601 601 601 663 663 663 663 663 663
D 601 601 601 601 601 601 601 601 601 601 663 663 663 663 663 663
E 601 601 601 601 601 601 601 601 601 601 663 663 663 663 663 663
F 601 601 601 601 601 601 601 601 601 601 663 663 663 663 663 663
N=0, C=0, H=1
0 1 2 3 4 5 6 7 8 9 A B C D E F
0 060 060 060 060 060 060 060 060 060 060 062 062 062 062 062 062
1 060 060 060 060 060 060 060 060 060 060 062 062 062 062 062 062
2 060 060 060 060 060 060 060 060 060 060 062 062 062 062 062 062
3 060 060 060 060 060 060 060 060 060 060 062 062 062 062 062 062
4 060 060 060 060 060 060 060 060 060 060 062 062 062 062 062 062
5 060 060 060 060 060 060 060 060 060 060 062 062 062 062 062 062
6 060 060 060 060 060 060 060 060 060 060 062 062 062 062 062 062
7 060 060 060 060 060 060 060 060 060 060 062 062 062 062 062 062
8 060 060 060 060 060 060 060 060 060 060 062 062 062 062 062 062
9 060 060 060 060 060 060 060 060 060 060 663 663 663 663 663 663
A 661 661 661 661 661 661 661 661 661 661 663 663 663 663 663 663
B 661 661 661 661 661 661 661 661 661 661 663 663 663 663 663 663
C 661 661 661 661 661 661 661 661 661 661 663 663 663 663 663 663
D 661 661 661 661 661 661 661 661 661 661 663 663 663 663 663 663
E 661 661 661 661 661 661 661 661 661 661 663 663 663 663 663 663
F 661 661 661 661 661 661 661 661 661 661 663 663 663 663 663 663
N=0, C=1, H=0
0 1 2 3 4 5 6 7 8 9 A B C D E F
0 601 601 601 601 601 601 601 601 601 601 663 663 663 663 663 663
1 601 601 601 601 601 601 601 601 601 601 663 663 663 663 663 663
2 601 601 601 601 601 601 601 601 601 601 663 663 663 663 663 663
3 601 601 601 601 601 601 601 601 601 601 663 663 663 663 663 663
4 601 601 601 601 601 601 601 601 601 601 663 663 663 663 663 663
5 601 601 601 601 601 601 601 601 601 601 663 663 663 663 663 663
6 601 601 601 601 601 601 601 601 601 601 663 663 663 663 663 663
7 601 601 601 601 601 601 601 601 601 601 663 663 663 663 663 663
8 601 601 601 601 601 601 601 601 601 601 663 663 663 663 663 663
9 601 601 601 601 601 601 601 601 601 601 663 663 663 663 663 663
A 601 601 601 601 601 601 601 601 601 601 663 663 663 663 663 663
B 601 601 601 601 601 601 601 601 601 601 663 663 663 663 663 663
C 601 601 601 601 601 601 601 601 601 601 663 663 663 663 663 663
D 601 601 601 601 601 601 601 601 601 601 663 663 663 663 663 663
E 601 601 601 601 601 601 601 601 601 601 663 663 663 663 663 663
F 601 601 601 601 601 601 601 601 601 601 663 663 663 663 663 663
N=0, C=1, H=1
0 1 2 3 4 5 6 7 8 9 A B C D E F
0 661 661 661 661 661 661 661 661 661 661 663 663 663 663 663 663
1 661 661 661 661 661 661 661 661 661 661 663 663 663 663 663 663
2 661 661 661 661 661 661 661 661 661 661 663 663 663 663 663 663
3 661 661 661 661 661 661 661 661 661 661 663 663 663 663 663 663
4 661 661 661 661 661 661 661 661 661 661 663 663 663 663 663 663
5 661 661 661 661 661 661 661 661 661 661 663 663 663 663 663 663
6 661 661 661 661 661 661 661 661 661 661 663 663 663 663 663 663
7 661 661 661 661 661 661 661 661 661 661 663 663 663 663 663 663
8 661 661 661 661 661 661 661 661 661 661 663 663 663 663 663 663
9 661 661 661 661 661 661 661 661 661 661 663 663 663 663 663 663
A 661 661 661 661 661 661 661 661 661 661 663 663 663 663 663 663
B 661 661 661 661 661 661 661 661 661 661 663 663 663 663 663 663
C 661 661 661 661 661 661 661 661 661 661 663 663 663 663 663 663
D 661 661 661 661 661 661 661 661 661 661 663 663 663 663 663 663
E 661 661 661 661 661 661 661 661 661 661 663 663 663 663 663 663
F 661 661 661 661 661 661 661 661 661 661 663 663 663 663 663 663
N=1, C=0, H=0
0 1 2 3 4 5 6 7 8 9 A B C D E F
0 000 000 000 000 000 000 000 000 000 000 FA0 FA0 FA0 FA0 FA0 FA0
1 000 000 000 000 000 000 000 000 000 000 FA0 FA0 FA0 FA0 FA0 FA0
2 000 000 000 000 000 000 000 000 000 000 FA0 FA0 FA0 FA0 FA0 FA0
3 000 000 000 000 000 000 000 000 000 000 FA0 FA0 FA0 FA0 FA0 FA0
4 000 000 000 000 000 000 000 000 000 000 FA0 FA0 FA0 FA0 FA0 FA0
5 000 000 000 000 000 000 000 000 000 000 FA0 FA0 FA0 FA0 FA0 FA0
6 000 000 000 000 000 000 000 000 000 000 FA0 FA0 FA0 FA0 FA0 FA0
7 000 000 000 000 000 000 000 000 000 000 FA0 FA0 FA0 FA0 FA0 FA0
8 000 000 000 000 000 000 000 000 000 000 FA0 FA0 FA0 FA0 FA0 FA0
9 000 000 000 000 000 000 000 000 000 000 9A1 9A1 9A1 9A1 9A1 9A1
A A01 A01 A01 A01 A01 A01 A01 A01 A01 A01 9A1 9A1 9A1 9A1 9A1 9A1
B A01 A01 A01 A01 A01 A01 A01 A01 A01 A01 9A1 9A1 9A1 9A1 9A1 9A1
C A01 A01 A01 A01 A01 A01 A01 A01 A01 A01 9A1 9A1 9A1 9A1 9A1 9A1
D A01 A01 A01 A01 A01 A01 A01 A01 A01 A01 9A1 9A1 9A1 9A1 9A1 9A1
E A01 A01 A01 A01 A01 A01 A01 A01 A01 A01 9A1 9A1 9A1 9A1 9A1 9A1
F A01 A01 A01 A01 A01 A01 A01 A01 A01 A01 9A1 9A1 9A1 9A1 9A1 9A1
N=1, C=0, H=1
0 1 2 3 4 5 6 7 8 9 A B C D E F
0 FA2 FA2 FA2 FA2 FA2 FA2 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0
1 FA2 FA2 FA2 FA2 FA2 FA2 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0
2 FA2 FA2 FA2 FA2 FA2 FA2 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0
3 FA2 FA2 FA2 FA2 FA2 FA2 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0
4 FA2 FA2 FA2 FA2 FA2 FA2 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0
5 FA2 FA2 FA2 FA2 FA2 FA2 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0
6 FA2 FA2 FA2 FA2 FA2 FA2 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0
7 FA2 FA2 FA2 FA2 FA2 FA2 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0
8 FA2 FA2 FA2 FA2 FA2 FA2 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0 FA0
9 FA2 FA2 FA2 FA2 FA2 FA2 FA0 FA0 FA0 FA0 9A1 9A1 9A1 9A1 9A1 9A1
A 9A3 9A3 9A3 9A3 9A3 9A3 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1
B 9A3 9A3 9A3 9A3 9A3 9A3 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1
C 9A3 9A3 9A3 9A3 9A3 9A3 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1
D 9A3 9A3 9A3 9A3 9A3 9A3 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1
E 9A3 9A3 9A3 9A3 9A3 9A3 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1
F 9A3 9A3 9A3 9A3 9A3 9A3 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1
N=1, C=1, H=0
0 1 2 3 4 5 6 7 8 9 A B C D E F
0 A01 A01 A01 A01 A01 A01 A01 A01 A01 A01 9A1 9A1 9A1 9A1 9A1 9A1
1 A01 A01 A01 A01 A01 A01 A01 A01 A01 A01 9A1 9A1 9A1 9A1 9A1 9A1
2 A01 A01 A01 A01 A01 A01 A01 A01 A01 A01 9A1 9A1 9A1 9A1 9A1 9A1
3 A01 A01 A01 A01 A01 A01 A01 A01 A01 A01 9A1 9A1 9A1 9A1 9A1 9A1
4 A01 A01 A01 A01 A01 A01 A01 A01 A01 A01 9A1 9A1 9A1 9A1 9A1 9A1
5 A01 A01 A01 A01 A01 A01 A01 A01 A01 A01 9A1 9A1 9A1 9A1 9A1 9A1
6 A01 A01 A01 A01 A01 A01 A01 A01 A01 A01 9A1 9A1 9A1 9A1 9A1 9A1
7 A01 A01 A01 A01 A01 A01 A01 A01 A01 A01 9A1 9A1 9A1 9A1 9A1 9A1
8 A01 A01 A01 A01 A01 A01 A01 A01 A01 A01 9A1 9A1 9A1 9A1 9A1 9A1
9 A01 A01 A01 A01 A01 A01 A01 A01 A01 A01 9A1 9A1 9A1 9A1 9A1 9A1
A A01 A01 A01 A01 A01 A01 A01 A01 A01 A01 9A1 9A1 9A1 9A1 9A1 9A1
B A01 A01 A01 A01 A01 A01 A01 A01 A01 A01 9A1 9A1 9A1 9A1 9A1 9A1
C A01 A01 A01 A01 A01 A01 A01 A01 A01 A01 9A1 9A1 9A1 9A1 9A1 9A1
D A01 A01 A01 A01 A01 A01 A01 A01 A01 A01 9A1 9A1 9A1 9A1 9A1 9A1
E A01 A01 A01 A01 A01 A01 A01 A01 A01 A01 9A1 9A1 9A1 9A1 9A1 9A1
F A01 A01 A01 A01 A01 A01 A01 A01 A01 A01 9A1 9A1 9A1 9A1 9A1 9A1
N=1, C=1, H=1
0 1 2 3 4 5 6 7 8 9 A B C D E F
0 9A3 9A3 9A3 9A3 9A3 9A3 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1
1 9A3 9A3 9A3 9A3 9A3 9A3 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1
2 9A3 9A3 9A3 9A3 9A3 9A3 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1
3 9A3 9A3 9A3 9A3 9A3 9A3 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1
4 9A3 9A3 9A3 9A3 9A3 9A3 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1
5 9A3 9A3 9A3 9A3 9A3 9A3 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1
6 9A3 9A3 9A3 9A3 9A3 9A3 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1
7 9A3 9A3 9A3 9A3 9A3 9A3 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1
8 9A3 9A3 9A3 9A3 9A3 9A3 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1
9 9A3 9A3 9A3 9A3 9A3 9A3 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1
A 9A3 9A3 9A3 9A3 9A3 9A3 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1
B 9A3 9A3 9A3 9A3 9A3 9A3 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1
C 9A3 9A3 9A3 9A3 9A3 9A3 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1
D 9A3 9A3 9A3 9A3 9A3 9A3 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1
E 9A3 9A3 9A3 9A3 9A3 9A3 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1
F 9A3 9A3 9A3 9A3 9A3 9A3 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1 9A1
DAA 命令の入力と出力の関係
今回の自作プログラムにより得られた結果をまとめると、以下のようになった。
N=0 (加算後) のとき
N=1 (減算後) のとき
他のページの情報との比較
の表と比較すると、このページの情報は H フラグが無い分場合分けが少ないが、A
レジスタに加算する値と C フラグの値は今回の結果と一致していた。
daatable/daaoutput.txt at master · ruyrybeyro/daatable
の結果と比較するため、この表を今回のプログラムの出力形式に変換するプログラムを作成した。
変換を行うプログラム (Perl)
#!/usr/bin/perl
use strict;
use warnings;
my %data = ();
while (my $line = <STDIN>) {
if ($line =~ /N ([01]) C ([01]) H ([01]) ([0-9A-F]{2}) N ([01]) C ([01]) H ([01]) ([0-9A-F]{2})/) {
my $key = "$1,$2,$3,$4";
my $value = sprintf("%02X%d", (hex($8) - hex($4)) & 0xff, int($6) + int($7) * 2);
if (defined($data{$key})) {
if ($data{$key} ne $value) {
warn "conflict: $key -> $data{$key} vs $value\n";
}
} else {
$data{$key} = $value;
}
}
}
for (my $N = 0; $N <= 1; $N++) {
for (my $C = 0; $C <= 1; $C++) {
for (my $H = 0; $H <= 1; $H++) {
print "N=$N, C=$C, H=$H\n";
for (my $i = 0; $i < 0x10; $i++) {
printf " %X", $i;
}
print "\n";
for (my $i = 0; $i < 0x10; $i++) {
printf " %X", $i;
for (my $j = 0; $j < 0x10; $j++) {
my $key = sprintf("%d,%d,%d,%X%X", $N, $C, $H, $i, $j);
my $value = $data{$key};
if (defined($value)) {
print " $value";
} else {
print " ???";
}
}
print "\n";
}
}
}
}
この変換結果を確認すると、表に載っていない N=1, C=1, H=0 の部分を除き、今回の結果と一致していた。
MSX でも DAA 命令を実行してみる
MSX では、Z80A (Z80 のクロックが速い版) 相当のCPUが使用されている。
そこで、MSX BASIC から DAA
命令を実行し、動作を確認してみた。
まず、MSX BASIC から呼び出され、DAA
命令を実行するマシン語を用意した。
5章 マシン語とのリンク - MSX Datapack wiki化計画
を参考に、整数を受け取って整数を返す関数の形にした。
DAA 命令を実行するマシン語のアセンブリコード
target z80
; 16ビット整数の下位8ビットにAレジスタ、上位8ビットにFレジスタの値を入れる
; (入力、出力共通)
; ただし、出力時、負の数にならないようSフラグ (最上位) を強制的に0にする
CP 2
RET NZ
PUSH HL
POP IX
LD B, (IX + 2)
LD C, (IX + 3)
PUSH BC
POP AF
DAA
PUSH AF
POP BC
LD A, C
AND 0x7f
LD (IX + 2), B
LD (IX + 3), A
RET
そして、これを埋め込んで呼び出すプログラムを作成した。
4章 BASICの内部構造 - MSX Datapack wiki化計画
を参考に、
-
SPACE$
を用い、マシン語を格納する領域を文字列として確保する - 整数型変数の値を、文字型変数の文字列格納アドレスに設定する
- 設定した変数を用いて、
DATA
で埋め込んだマシン語を文字列の領域にコピーする
という手順で、用意したマシン語を USR
で実行できる形にした。
DAA 命令を実行する MSX BASIC のプログラム
10 DEFINT N,C,H,I,J,K,P,Q,R,A,B,F
20 U$=SPACE$(&H1B):P=0
30 Q=VARPTR(U$):R=VARPTR(P)
40 POKE R,PEEK(Q+1)
50 POKE R+1,PEEK(Q+2)
60 FOR I=0 TO &H1A
70 READ C:POKE P+I,C
80 NEXT
90 DATA &HFE,&H02,&HC0,&HE5
100 DATA &HDD,&HE1,&HDD,&H46
110 DATA &H02,&HDD,&H4E,&H03
120 DATA &HC5,&HF1,&H27,&HF5
130 DATA &HC1,&H79,&HE6,&H7F
140 DATA &HDD,&H70,&H02,&HDD
150 DATA &H77,&H03,&HC9
160 DEF USR=P
170 FOR N=0 TO 1
180 FOR C=0 TO 1
190 FOR H=0 TO 1
200 FOR K=0 TO 8 STEP 8
210 PRINT "N=";HEX$(N);
220 PRINT ", C=";HEX$(C);
230 PRINT ", H=";HEX$(H)
240 FOR I=0 TO 7
250 PRINT " ";HEX$(K+I);
260 NEXT I
270 PRINT
280 FOR I=0 TO &HF
290 PRINT " ";HEX$(I);
300 FOR J=0 TO 7
310 A=I*16+K+J:F=H*&H10+N*2+C
320 B=USR(F*&H100+A)
330 F=B\&H1000 MOD 2
340 F=F*2 + B\&H100 MOD 2
350 B=B MOD &H100 - A
360 IF B<0 THEN B=B+&H100
370 B=B*&H10+F:PRINT " ";
380 IF B<&H10 THEN PRINT "0";
390 IF B<&H100 THEN PRINT "0";
400 PRINT HEX$(B);
410 NEXT J
420 PRINT
430 NEXT I
440 IF INKEY$="" THEN GOTO 440
450 NEXT K:NEXT H:NEXT C:NEXT N
それぞれの表を左右に分割して半分ずつ表示し、キーを押すと次の表に進むようにした。
これを WebMSX で実行すると、今回の結果と同様の結果が得られた。
まとめ
Z80 の DAA
命令について、入力 (A
レジスタ、および N・C・H フラグ) と出力 (A
レジスタに加算される値、および C・H フラグ) の関係を出力するプログラムを作成した。
このプログラムを用いて、EMUZ80 および WebMSX における DAA
命令の動作を確認した。
さらに、これにより得られた結果が、今回の調査で見つけた DAA
命令の動作のうち一部の情報しか載っていないページの情報と整合性が取れていることを確認した。