標準TTLだけ(!)でCPUをつくろう!(組立てキットです!)
(ホントは74HC、CMOSなんだけど…)
[第657回]

●ルート計算プログラム(TK−80応用プログラム)(その4)

前回はTK−80用に書かれたルート計算プログラムのサブルーチンHIKUSUADD2の中で使われている
RLA
AND 01
という命令について、「実に巧妙なテクニックです」と書きました。

実は昨晩そのように書きまして、ホームページにUPしてから、帰宅するため夜道を歩いていましたら、
「あれま、私としたことが」
思い違いをしてしまったことに気が付きました。

巧妙なテクニックではありませんでした。
そのようなプログラムになったのは、おそらくこのプログラムを書いた方が思い違いをされたためだと思います。
おかしなことをやってるなあ、と思ったものですから、
「それにはきっとそれなりの訳があるに違いない」
とまあそのように考えて、それで、つい乗せられてしまいました。

HIKUSUADD2サブルーチンのプログラムリストです。

              ;
              ; HIKUSU=HIKUSU+2
              ;
8093 0603     HIKUSUADD2:LD B,03
8095 21B382         LD HL,HIKUSU78
8098 7E             LD A,(HL)
8099 C602           ADD A,02
809B 27             DAA
809C 77             LD (HL),A
809D 17       HIKUSUADD2_2:RLA
809E E601           AND 01
80A0 2B             DEC HL
80A1 86             ADD A,(HL)
80A2 27             DAA
80A3 77             LD (HL),A
80A4 05             DEC B
80A5 C29D80         JP NZ,HIKUSUADD2_2
80A8 C9             RET


アドレス809D以下の部分は、その前の、アドレス8099の
ADD A,02
DAA
およびアドレス80A1の
ADD A,(HL)
DAA
の計算の結果、上位桁へのキャリーが発生した場合にその繰り上げを計算するためのパートです。
それなら、普通は

              ;
              ; HIKUSU=HIKUSU+2
              ;
8093 0603     HIKUSUADD2:LD B,03
8095 21B382         LD HL,HIKUSU78
8098 7E             LD A,(HL)
8099 C602           ADD A,02
809B 27             DAA
809C 77             LD (HL),A
809D 2B       HIKUSUADD2_2:DEC HL
809E 7E             LD A,(HL)
809F CE00           ADC A,00
80A1 27             DAA
80A2 77             LD (HL),A
80A3 05             DEC B
80A4 C29D80         JP NZ,HIKUSUADD2_2
80A7 C9             RET

というように書けばよいはずです。
じつは、そのように書いてから、たった今気がついたのですが、このルート計算プログラムには同じような計算をしているところが他にもあって、そこではちゃんとごく普通のプログラムになっています。


              ;
              ;TISU=TISU-HIKUSU
              ;
8070 21B382   HIKIZAN:LD HL,HIKUSU78
8073 11AF82         LD DE,TISU78
8076 0604           LD B,04
8078 AF             XOR A
8079 1A       HIKIZAN_2:LD A,(DE)
807A 9E             SBC A,(HL)
807B 27             DAA
807C 12             LD (DE),A
807D 2B             DEC HL
807E 1B             DEC DE
807F 05             DEC B
8080 C27980         JP NZ,HIKIZAN_2
8083 C9             RET

むむむ。
これは一体どういうことなのでしょう。
はて、このように普通に書いてあるところもあるということになりますと、先ほどのHIKUSUADD2サブルーチンの書き方はいよいよもって理解しがたいことになります。
が。
多分、DAAで発生したキャリーフラグが、そのあとで実行されるDEC Bによって影響を受けてしまうのではないかと、つい思い違いをしてしまったのではないか、としか思えません。
RLA命令を実行すると、Aレジスタの値がキャリーフラグを含めて左にローテイトします。
Aレジスタのビット7がキャリーフラグに入って、キャリーフラグの値がAレジスタのビット0に入れられます。
その後で、AND 01を実行すると、キャリーフラグが立っていたときにはAレジスタの値が01になり、キャリーフラグが立っていなかったときには、Aレジスタの値は00になります。
ですからその後で
DEC HL
ADD A,(HL)
とすることで、確かに下位桁からの繰り上げが上位桁に伝わります。

しかし。
ここは、わざわざそんなことをしなくても、普通に書けばいいのです。
8080、Z80では、8ビットレジスタA〜Lに対するDEC、INC命令は、キャリーフラグだけは変化しないのです。

[出典]Intel社「8080 Microcomputer Systems User’s Manual September 1975」

8080のManualですから、INR、DCR命令になっていますが、8080でもZ80でも命令としての動作は同じです。
Note:のところにちゃんと書いてあります。
All condition flags except CY are affected.
です。

●HIKUSUADD2サブルーチンを直しました

ということで、HIKUSUADD2サブルーチンを直しました。
変更後のソースプログラムです。


;;; root program for ND80Z3
;;; for ND80Z monitor
; 2010/11/08 11/09 11/10
;
      ORG $8000
;
      SEGCG=$05C0
      KEYIN=$0616
;
      LEDDP1=$FFF4
      LEDDP3=$FFF6
;
      LED1=$FFF8
      LED2=$FFF9
      LED3=$FFFA
      LED4=$FFFB
      LED5=$FFFC
;
      TISU12=$82AC
      TISU78=$82AF
      HIKUSU78=$82B3
      KOTAE12=$82B4
      KOTAE34=$82B5
;
START:LD B,0A
      LD HL,TISU12
      XOR A
;
;TISU CLEAR
;
TISUCLR:LD (HL),A
      INC HL
      DEC B
      JP NZ,TISUCLR
;
;TISU KEYIN
;
      CALL KEYIN
      RLCA
      RLCA
      RLCA
      RLCA
      LD HL,TISU12
      LD (HL),A
      PUSH HL
      CALL KEYIN
      POP HL
      ADD A,(HL)
      LD (HL),A
      LD A,(HL)
      LD DE,LEDDP1
      LD (DE),A
;
;KEISAN START
;
      LD A,01
      LD (HIKUSU78),A
KEISAN_LOOP:CALL HIKIZAN
      JP C,KEISAN_END
      CALL KOTAE_ADD1
      CALL HIKUSUADD2
      JP KEISAN_LOOP
;
;KEKKA NO HYOJI
;
KEISAN_END:LD HL,KOTAE12
      LD B,02
      LD DE,LEDDP3
HYOJI_LOOP:LD A,(HL)
      LD (DE),A
      INC HL
      INC DE
      DEC B
      JP NZ,HYOJI_LOOP
      CALL SEGCG
;
;LED HYOJI O HENSYU SURU
;
      LD HL,LED3
      LD DE,LED2
      LD A,(DE)
      LD (HL),A
      LD HL,LED2
      LD DE,LED1
      LD A,(DE)
      LD (HL),A
      LD HL,LED4
      LD A,48;"="
      LD (HL),A
      LD HL,LED1
      LD A,0E;"root"
      LD (HL),A
      LD HL,LED5
      LD A,80;"."
      OR (HL)
      LD (HL),A
      JP START
;
;subroutine
;
;
;TISU=TISU-HIKUSU
;
HIKIZAN:LD HL,HIKUSU78
      LD DE,TISU78
      LD B,04
      XOR A
HIKIZAN_2:LD A,(DE)
      SBC A,(HL)
      DAA
      LD (DE),A
      DEC HL
      DEC DE
      DEC B
      JP NZ,HIKIZAN_2
      RET
;
;KOTAE_ADD1
;
KOTAE_ADD1:LD HL,KOTAE34
      LD A,(HL)
      ADD A,01
      DAA
      LD (HL),A
      DEC HL
      LD A,(HL)
      ADC A,00
      DAA
      LD (HL),A
      RET
;
; HIKUSU=HIKUSU+2
;
HIKUSUADD2:LD B,03
      LD HL,HIKUSU78
      LD A,(HL)
      ADD A,02
      DAA
      LD (HL),A
HIKUSUADD2_2:DEC HL
      LD A,(HL)
      ADC A,00
      DAA
      LD (HL),A
      DEC B
      JP NZ,HIKUSUADD2_2
      RET


Z80アセンブラZASMでアセンブルして作成されたアセンブルリストです。

2010/11/10  8:32  root4.txt
END=80A7
              ;;; root program for ND80Z3
              ;;; for ND80Z monitor
              ; 2010/11/08 11/09 11/10
              ;
                    ORG $8000
              ;
                    SEGCG=$05C0
                    KEYIN=$0616
              ;
                    LEDDP1=$FFF4
                    LEDDP3=$FFF6
              ;
                    LED1=$FFF8
                    LED2=$FFF9
                    LED3=$FFFA
                    LED4=$FFFB
                    LED5=$FFFC
              ;
                    TISU12=$82AC
                    TISU78=$82AF
                    HIKUSU78=$82B3
                    KOTAE12=$82B4
                    KOTAE34=$82B5
              ;
8000 060A     START:LD B,0A
8002 21AC82         LD HL,TISU12
8005 AF             XOR A
              ;
              ;TISU CLEAR
              ;
8006 77       TISUCLR:LD (HL),A
8007 23             INC HL
8008 05             DEC B
8009 C20680         JP NZ,TISUCLR
              ;
              ;TISU KEYIN
              ;
800C CD1606         CALL KEYIN
800F 07             RLCA
8010 07             RLCA
8011 07             RLCA
8012 07             RLCA
8013 21AC82         LD HL,TISU12
8016 77             LD (HL),A
8017 E5             PUSH HL
8018 CD1606         CALL KEYIN
801B E1             POP HL
801C 86             ADD A,(HL)
801D 77             LD (HL),A
801E 7E             LD A,(HL)
801F 11F4FF         LD DE,LEDDP1
8022 12             LD (DE),A
              ;
              ;KEISAN START
              ;
8023 3E01           LD A,01
8025 32B382         LD (HIKUSU78),A
8028 CD7080   KEISAN_LOOP:CALL HIKIZAN
802B DA3780         JP C,KEISAN_END
802E CD8480         CALL KOTAE_ADD1
8031 CD9380         CALL HIKUSUADD2
8034 C32880         JP KEISAN_LOOP
              ;
              ;KEKKA NO HYOJI
              ;
8037 21B482   KEISAN_END:LD HL,KOTAE12
803A 0602           LD B,02
803C 11F6FF         LD DE,LEDDP3
803F 7E       HYOJI_LOOP:LD A,(HL)
8040 12             LD (DE),A
8041 23             INC HL
8042 13             INC DE
8043 05             DEC B
8044 C23F80         JP NZ,HYOJI_LOOP
8047 CDC005         CALL SEGCG
              ;
              ;LED HYOJI O HENSYU SURU
              ;
804A 21FAFF         LD HL,LED3
804D 11F9FF         LD DE,LED2
8050 1A             LD A,(DE)
8051 77             LD (HL),A
8052 21F9FF         LD HL,LED2
8055 11F8FF         LD DE,LED1
8058 1A             LD A,(DE)
8059 77             LD (HL),A
805A 21FBFF         LD HL,LED4
805D 3E48           LD A,48;"="
805F 77             LD (HL),A
8060 21F8FF         LD HL,LED1
8063 3E0E           LD A,0E;"root"
8065 77             LD (HL),A
8066 21FCFF         LD HL,LED5
8069 3E80           LD A,80;"."
806B B6             OR (HL)
806C 77             LD (HL),A
806D C30080         JP START
              ;
              ;subroutine
              ;
              ;
              ;TISU=TISU-HIKUSU
              ;
8070 21B382   HIKIZAN:LD HL,HIKUSU78
8073 11AF82         LD DE,TISU78
8076 0604           LD B,04
8078 AF             XOR A
8079 1A       HIKIZAN_2:LD A,(DE)
807A 9E             SBC A,(HL)
807B 27             DAA
807C 12             LD (DE),A
807D 2B             DEC HL
807E 1B             DEC DE
807F 05             DEC B
8080 C27980         JP NZ,HIKIZAN_2
8083 C9             RET
              ;
              ;KOTAE_ADD1
              ;
8084 21B582   KOTAE_ADD1:LD HL,KOTAE34
8087 7E             LD A,(HL)
8088 C601           ADD A,01
808A 27             DAA
808B 77             LD (HL),A
808C 2B             DEC HL
808D 7E             LD A,(HL)
808E CE00           ADC A,00
8090 27             DAA
8091 77             LD (HL),A
8092 C9             RET
              ;
              ; HIKUSU=HIKUSU+2
              ;
8093 0603     HIKUSUADD2:LD B,03
8095 21B382         LD HL,HIKUSU78
8098 7E             LD A,(HL)
8099 C602           ADD A,02
809B 27             DAA
809C 77             LD (HL),A
809D 2B       HIKUSUADD2_2:DEC HL
809E 7E             LD A,(HL)
809F CE00           ADC A,00
80A1 27             DAA
80A2 77             LD (HL),A
80A3 05             DEC B
80A4 C29D80         JP NZ,HIKUSUADD2_2
80A7 C9             RET
HIKIZAN      =8070  HIKIZAN_2    =8079  HIKUSU78     =82B3  
HIKUSUADD2   =8093  HIKUSUADD2_2 =809D  HYOJI_LOOP   =803F  
KEISAN_END   =8037  KEISAN_LOOP  =8028  KEYIN        =0616  
KOTAE12      =82B4  KOTAE34      =82B5  KOTAE_ADD1   =8084  
LED1         =FFF8  LED2         =FFF9  LED3         =FFFA  
LED4         =FFFB  LED5         =FFFC  LEDDP1       =FFF4  
LEDDP3       =FFF6  SEGCG        =05C0  START        =8000  
TISU12       =82AC  TISU78       =82AF  TISUCLR      =8006  

念の為に、変更後のプログラムをND80ZVで実行してみましたが、もとのプログラムと全く同じ結果が表示されました。

●HIKUSUADD2サブルーチンを検証してみました

8080 Manualの記載と、そのように変更して実行してみた結果から、以上のように考えてよい、ということははっきりしました。
しかし、DAA命令で発生した上位桁への桁上げが、間にDEC命令やそのほかの命令をはさんでもちゃんと実行される様子が確認できれば、なおのこと納得がいくのではありませんでしょうか?

ということで、実際にHIKUSUADD2サブルーチンだけを取り出して、そのあたりの動きを確認してみることにしました。
動作の確認はZB3BASICのマシン語デバッグ機能利用して行なうのが簡単です。
ZB3BASICもND80ZV組立キットに付属しています。

まずはテストを行なうために、HIKUSUADD2サブルーチンだけを取り出してソースプログラムファイルにしました。
rootsbt.txtです。

;;; root subroutine HIKUSUADD2 test
;;; for ND80Z monitor
; 2010/11/10
;
      ORG $8000
;
      HIKUSU78=$82B3
;
; HIKUSU=HIKUSU+2
;
HIKUSUADD2:LD B,03
      LD HL,HIKUSU78
      LD A,(HL)
      ADD A,02
      DAA
      LD (HL),A
HIKUSUADD2_2:DEC HL
      LD A,(HL)
      ADC A,00
      DAA
      LD (HL),A
      DEC B
      JP NZ,HIKUSUADD2_2
      RET


rootsbt.txtをzasmでアセンブルして、バイナリファイルrootsbt.binを作成したあと、ZB3BASICを起動します。



ZB3BASICを起動したら、rootsbt.binをアドレス8000にLOADします。



4バイトのワークレジスタ82B0〜82B3に8桁の10進数(BCD数)が入れられているとき、rootsbt.binを実行すると、そのワークレジスタの値に2が加算されます。
その計算は最下位の2桁(アドレス82B3)に02を加算し、その結果桁上げが発生すれば、上位桁に対して桁上げの計算を行なうというように行なわれます。
桁上げの動作がはっきり確認できるように、アドレス82B0〜82B3には、00099999という値を入れておきます。
00099999+2の計算が行なわれるはずですから、計算の結果は、00100001になることが期待されます。

メモリに値を書き込むにはcm(cheng memory)コマンドを使います。
上のコマンドプロンプト画面で、cm 82b0を実行して、82B0〜82B3に00099999を書き入れています。

プログラムを実行して、その様子を確認するためにはブレークポイントを設定して、実行の途中でブレークさせてそのときのレジスタの値を確認する、という方法が効果的です。

下はZ80アセンブラ(ZASM)でアセンブルしたrootsbt.txtのアセンブルリストです。

2010/11/10  8:48  rootsbt.txt
END=8014
              ;;; root subroutine HIKUSUADD2 test
              ;;; for ND80Z monitor
              ; 2010/11/10
              ;
                    ORG $8000
              ;
                    HIKUSU78=$82B3
              ;
              ; HIKUSU=HIKUSU+2
              ;
8000 0603     HIKUSUADD2:LD B,03
8002 21B382         LD HL,HIKUSU78
8005 7E             LD A,(HL)
8006 C602           ADD A,02
8008 27             DAA
8009 77             LD (HL),A
800A 2B       HIKUSUADD2_2:DEC HL
800B 7E             LD A,(HL)
800C CE00           ADC A,00
800E 27             DAA
800F 77             LD (HL),A
8010 05             DEC B
8011 C20A80         JP NZ,HIKUSUADD2_2
8014 C9             RET
HIKUSU78     =82B3  HIKUSUADD2   =8000  HIKUSUADD2_2 =800A  

あとはまた、さきほどのコマンドプロンプト画面の説明をしていきます。
プログラムを実行したら、最初の加算ADD A,02の直前でまずブレークさせてみることにします。
ADD A,02はアドレス8006ですから、
bp 8006
と入力します。
bpはブレークポイントをセットするコマンドです。

今回のテストプログラムrootsbt.binはマシン語サブルーチンですから、ZB3BASICのマシン語サブルーチンコール命令のUSR()を使います。
usr($8000)
と入力すると、アドレス8000からのマシン語サブルーチンが実行されます。

ブレークポイントが設定されていますから、アドレス8006でブレークします。
先ほどのコマンドプロンプト画面を見ていただければ、ブレークした結果の表示も確認できるのですが、何回も戻るのは面倒なことですから、画面表示と同時に作成保存されるログファイルから切り取ってその部分を下に示します。

>bp 8006
>usr($8000)

A F  B C  D E  H L  A'F' B'C' D'E' H'L'  PC   SP   IX   IY  I  SZ H PNC
996A 0306 4FCD 82B3 0044 0000 0000 0000 8006 F7FA 0000 F1A1 FF 01101010

アドレス8000〜8005までのプログラム(下記)が実行された結果、HLレジスタには82B3が入り、Bレジスタには03が入り、そしてAレジスタにはHLレジスタで示されるアドレス82B3の値99が入れられています。PC(プログラムカウンタ)はブレークしたアドレス8006になっています。
そのあたりのプログラムリストをしたに示します。


8000 0603     HIKUSUADD2:LD B,03
8002 21B382         LD HL,HIKUSU78
8005 7E             LD A,(HL)
8006 C602           ADD A,02
8008 27             DAA
8009 77             LD (HL),A


次にブレークポイントを8008にセットして、ADD A,02を実行させてからブレークさせます。

>bp 8008
>rt

A F  B C  D E  H L  A'F' B'C' D'E' H'L'  PC   SP   IX   IY  I  SZ H PNC
9B88 0306 4FCD 82B3 0044 0000 0000 0000 8008 F7FA 0000 F1A1 FF 10001000

rtはもとのマシン語プログラムにリターンして、ブレークしたところから実行を再開するためのコマンドです。
先にbp 8008を入力してブレークポイントを設定してからrtコマンドを実行しましたから、8006のADD A,02が実行されるとすぐにまたブレークします。
99に02が加算されましたから、Aレジスタの値は9Bになっています。
ここはまだ十進数の加算ではなくて2進数の加算になっています。

そのあと、DAA(Decimal Adjast十進補正)命令の実行結果を確認するために、ブレークポイントをアドレス8009にセットしてからrtコマンドを実行します。

>bp 8009
>rt

A F  B C  D E  H L  A'F' B'C' D'E' H'L'  PC   SP   IX   IY  I  SZ H PNC
0111 0306 4FCD 82B3 0044 0000 0000 0000 8009 F7FA 0000 F1A1 FF 00010001

DAAが実行された結果、Aレジスタの値が十進数加算99+02の結果に正しく補正されて、01になりました。
また上位桁に桁上げがあることを示すキャリーフラグ(右端のC)がセットされて1になっています。

説明の途中ですが時間がなくなってしまいましたので、続きは次回にすることにいたします。
2010.11.10upload

前へ
次へ
ホームページトップへ戻る