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

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

ND80ZV組立キットをご購入いただいた北海道のM様から、TK−80応用プログラムを送っていただきました。
01〜99の範囲の整数のみですが、ルート(平方根)を求めるプログラムです。
前回は送っていただいた8080のマシン語プログラムをもとに、Z80逆アセンブラ(ZDAS)によってZ80のソースプログラムを逆生成して、さらにそこで使われているラベル名を意味のある名前に書き換えてみました。
今回はその続きです。

Z80逆アセンブラで生成されるZ80アセンブラ用のソースプログラムは、プログラム範囲外のアドレスを仮のラベル名をつけて、プログラムの先頭に集めて表示します。
それだけではなくてプログラム内でCALLしているサブルーチンやジャンプ先のアドレスにもラベルをつけて示します。
しかしプログラムを解析して、一体何をやっているのかがわからないと意味の有るラベル名はつけられません。
それからプログラムが使っているワークエリアについても同様に、プログラムがどういう目的でそのワークエリアを使っているのかがわからないことには、適切な変数名をつけることはできません。

実は前回お見せしたプログラムリストを解析した結果、そのプログラムが何をやっているのかがわかりましたので(皆様はおわかりになりましたでしょうか?)、前回お見せしたソースプログラムにさらに手を加えて、どういうことをやっているのかわかりやすいようにコメントを付加するとともに、プログラム内でCALL、ジャンプしているアドレスへのラベル名やワークアドレスの変数名も意味のあるものに直してみました。

それからもとのプログラムには、複数のサブルーチンがありますが、そのサブルーチンの間にNOP(No Operation)がいくつか置かれています。
これは意味のあるものではなくて、単にそのメモリエリアを将来のプログラムの変更にそなえてリザーブするために使われているか、あるいはプログラム変更によってできた空きを埋めるために使われているものだと思います。
昔は一般にはアセンブラなどを使うことはできませんでしたから、皆ノートの上にプログラムを書いて、それを命令ごとに16進コードに置き換えて(これをハンドアセンブルと言いました)、マイコンつまりTK−80にはそのようにして手作業で翻訳したマシン語コード(16進コード)を直接入力していました。
プログラムデバッグの過程で、プログラムの一部が短くなったりしても、その後ろにあるプログセムを前に詰めようとすると、せっかく入力したその部分を再入力しなければなりませんし、ジャンプアドレスなども変わってきてしまいますから、空いた部分はそのままにしておくか、あとでわかりにくくなってしまうのを避けるためにNOPやFFコードで埋めたりすることはよくありました。

しかし今回はZ80アセンブラを利用しますから、間のNOPはソースプログラムの段階で全て削除してしまっても全く問題はありません。
そこで、プログラムをより見やすくするために、もとのプログラムで置かれていたNOPは削除しました。

●最終的に編集したルート計算プログラム(ソースプログラム)です

;;; root program for ND80Z3
; 2010/11/08 11/09
;
      ORG $8000
;
      SEGCG=$01C0
      KEYIN=$0216
;
      LEDDP1=$83F4
      LEDDP3=$83F6
;
      LED1=$83F8
      LED2=$83F9
      LED3=$83FA
      LED4=$83FB
      LED5=$83FC
;
      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:RLA
      AND 01
      DEC HL
      ADD A,(HL)
      DAA
      LD (HL),A
      DEC B
      JP NZ,HIKUSUADD2_2
      RET


●編集したソースプログラムをZ80アセンブラにかけてみました

上のようにして編集作成したソースプログラムをZ80アセンブラにかけてみました。



Z80アセンブラによって作成されたアセンブルリストです。

2010/11/9  7:25  root2.txt
END=80A8
              ;;; root program for ND80Z3
              ; 2010/11/08 11/09
              ;
                    ORG $8000
              ;
                    SEGCG=$01C0
                    KEYIN=$0216
              ;
                    LEDDP1=$83F4
                    LEDDP3=$83F6
              ;
                    LED1=$83F8
                    LED2=$83F9
                    LED3=$83FA
                    LED4=$83FB
                    LED5=$83FC
              ;
                    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 CD1602         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 CD1602         CALL KEYIN
801B E1             POP HL
801C 86             ADD A,(HL)
801D 77             LD (HL),A
801E 7E             LD A,(HL)
801F 11F483         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 11F683         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 CDC001         CALL SEGCG
              ;
              ;LED HYOJI O HENSYU SURU
              ;
804A 21FA83         LD HL,LED3
804D 11F983         LD DE,LED2
8050 1A             LD A,(DE)
8051 77             LD (HL),A
8052 21F983         LD HL,LED2
8055 11F883         LD DE,LED1
8058 1A             LD A,(DE)
8059 77             LD (HL),A
805A 21FB83         LD HL,LED4
805D 3E48           LD A,48;"="
805F 77             LD (HL),A
8060 21F883         LD HL,LED1
8063 3E0E           LD A,0E;"root"
8065 77             LD (HL),A
8066 21FC83         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 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
HIKIZAN      =8070  HIKIZAN_2    =8079  HIKUSU78     =82B3  
HIKUSUADD2   =8093  HIKUSUADD2_2 =809D  HYOJI_LOOP   =803F  
KEISAN_END   =8037  KEISAN_LOOP  =8028  KEYIN        =0216  
KOTAE12      =82B4  KOTAE34      =82B5  KOTAE_ADD1   =8084  
LED1         =83F8  LED2         =83F9  LED3         =83FA  
LED4         =83FB  LED5         =83FC  LEDDP1       =83F4  
LEDDP3       =83F6  SEGCG        =01C0  START        =8000  
TISU12       =82AC  TISU78       =82AF  TISUCLR      =8006  

さて、プログラムが理解し易くなったところで、いったいルートの計算をどのようにして実現しているのか、その驚くべき、まさに目からウロコの解法を皆様とともに見ていきたいと思います。

それでは順をおって見ていくことにいたします。
最初のところはアドレス82AC〜82B5(置数〜計算結果のためのワークエリア)を00でクリアしています。
XOR A
はAレジスタに00をセットする目的でよく使われます。

次は置数のKEY−INです。
ローマ字と英語をちゃんぽんに使ってしまいましたから、ちょっと読みにくくなってしまいました。
2桁の置数を1バイトのBCD数として置数レジスタの上位アドレス(TISU12、$82AC)に格納すると同時に最終結果の表示とともに置数も表示するためにLED表示用レジスタ(LEDDP1、$83F4)にも格納します。

そしていよいよ計算スタートです。
なんとルートの計算なのに、簡単な引き算しか行ないません。
それで平方根が求まってしまいます。

計算は置数(実は2桁の置数部は10進数8桁の最上位2桁でそれより下位の6桁は全て0です)から、最初は1を引くところからスタートします。
引く数も8桁で最初は最下位2桁に01をセットします。それより上位の6桁は全て0です。
引き算のサブルーチンはHIKIZANです。

サブルーチンHIKIZANの処理内容です。
BCD8桁(4バイト)の数の引き算を下位桁から順に2桁(1バイト)ずつ行なっています。
2進数の演算ではなくて10進数の計算ですから、引き算(SBC命令)のあとでDAA(Decimal Adjust)命令を実行して十進補正を行なっています。
BCD数とDAA(十進補正)につきましては[第10回]あたりで説明をしておりますのでそちらをご参照願います。

減算の結果をDAAで十進補正した結果上位桁からのボローが発生するとキャリーフラグがセットされます。
そのキャリーフラグを使って、つぎの上位桁の引き算のときに、下位桁からのボローとして−1するために、ただのSUB命令ではなくてSBC命令を使っています。
アドレス8078のXOR Aは、最初の下位桁の引き算のときにあるはずのないボローを引いてしまわないために、キャリーフラグをクリアする目的で使っています。
キャリーフラグをクリアするために、XOR命令やOR命令がよく使われます。

ふたたびメインルーチンに戻ります。
最上位桁まで引き算した結果、引ききれなくなってキャリーオーバーになったら計算終了です。
そうではなかった場合には、答えが格納されるレジスタ(2バイト、BCD4桁)に1が加算されます。
サブルーチンKOTAE_ADD1です。

問題はその次に実行されるサブルーチンHIKUSUADD2です。
ここではなにをやっているのかといいますと、引く数に2を加算しているのです。
引く数の最下位桁HIKUSU78に02を加算したあと十進補正のためにDAA命令を実行しています。
その次の
RLA
AND 01
は何をやっているのでしょうか。
これは実に巧妙なテクニックです。
ここは、下位桁の加算結果を十進補正して、上位桁へのキャリーが発生した場合にそれを順次上位桁に桁上げしていく処理をしているのですが、なぜこんなところで一見意味不明なRLA(左ローテイト)命令とAND 01が使われているのでしょうか?
ちょうど時間もなくなってしまいましたので、なぜこんなところでこんな命令が使われているのか、次回までにじっくりお考えになってくださいませ。

[2010.11.10追記]
上記のように書いたのですが…。
私の思い違いであることに気が付きました。
巧妙なテクニックではありませんでした。
このプログラムを書いた方の思い違い(だと思います)に、つい乗せられてしまいました。
詳しくは次回にて。

2010.11.9upload
2010.11.10追記

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