附錄八 追蹤實例

這個附錄是小木偶在撰寫第 25 章把 IEEE 754 短實數轉換成 packed BCD 數時,想起 SYMDEB 也可以把實數顯示於螢幕上,一時好奇想看看 SYMDEB 到底是如何做的?於是追蹤 SYMDEB,而把過程記錄下來,當然小木偶只追蹤 DT 指令

您要知道,雖然在這堣p木偶似乎只一步就找到 DT 指令的核心部分,但事實上,小木偶也是經過許多次嘗試錯誤與猜測才找到這段程式的。


準備工作

要追蹤 SYMDEB 如何顯示暫時實數,小木偶寫了一個小程式,TRACE.ASM,來作為測試程式。

                                ;*************************************** 
 0000                           code    segment 
                                        assume  cs:code,ds:code 
 0100                                   org     100h 
                                ;--------------------------------------- 
 0100                           start:
 0100  16 A0 D0 5F A6           x       dt      0.00123456 
       F5 D0 A1 F5 3F
                                ;--------------------------------------- 
 010A                           code    ends 
                                ;*************************************** 
                                end     start

上面的檔案是列表檔,TRACE.LST,您應該很容易把它變成組合語言原始檔,TRACE.ASM。小木偶只列出列表檔的原因是,它可以看出暫時實數變數,x 之值為 0.00123456,以 IEEE 754 編碼為 16 A0 D0 5F A6 F5 D0 A1 F5 3F,而小木偶的目的就是要在 SYMDEB 堙A找出把 16 A0 D0 5F A6 F5 D0 A1 F5 3F 變成 0.123456E-2 字串的程式片段。接著把 TRACE.ASM 組譯成 TRACE.COM 可執行檔,然後用 SYMDEB 來追蹤 SYMDEB.EXE 如何顯示存在於 TRACE.COM 的 x 暫時實數變數。首先開啟 DOS 模式,然後在 DOS 提示下,下指令『symdeb symdeb.exe trace.com』,橙色字的『symdeb.exe』表示被追蹤 ( 或除錯 ) 的程式,而追蹤程式也是 SYMDEB。

E:\HomePage\SOURCE>SYMDEB SYMDEB.EXE TRACE.COM [Enter]
Microsoft (R) Symbolic Debug Utility  Version 4.00
Copyright (C) Microsoft Corp 1984, 1985.  All rights reserved.

Processor is [80286]
-G [Enter] →表示被追蹤的SYMDEB.EXE已準備好,可以被追蹤了。好,執行它吧!
Microsoft (R) Symbolic Debug Utility  Version 4.00
Copyright (C) Microsoft Corp 1984, 1985.  All rights reserved.

Processor is [80286]
-DT 100 L1 [Enter] →顯示 X 變數
181D:0100   16 A0 D0 5F A6 F5 D0 A1 F5 3F  +0.123456E-2
-Q [Enter] →跳出被追蹤的SYMDEB.EXE 

Program terminated normally (0)
-Q [Enter]

E:\HomePage\SOURCE>

往後,小木偶辛苦的追蹤工作,就從上面的『G』指令開始,但必須把『G』指令改成單步追蹤的『T』指令,才能一步一步的找到 SYMDEB 如何顯示短實數。換句話說,白色部分就是小木偶想探究追蹤的部分,尤其是印出『+0.123456E-2』這部份。這部份會先印出位址,再印出兩個空白,再印出構成暫時實數的十個位元組的十六進位數 ( 也就是 IEEE 754 編碼而成的十六進位數 ),再印出兩個空白,最後才是十進位數。


開始囉!

因為小木偶不想追蹤整個 SYMDEB,也不想了解整個 SYMDEB 如何運作 ( 在沒有任何資料的情形下,這太困難了 ),小木偶要做的只想快速找到顯示暫時實數的那段程式片段,所以初期只要先找到 JMP、Jx、RET、CALL 等分岔指令記下來其位址,再去執行它即可。之所以要記下來的目的是,等下次追蹤時,就直接以『g』指令直接執行到該位址即可,不用重新開始。

執行 SYMDEB 來追蹤 SYMDEB.EXE 如何顯示暫時實數:

E:\HomePage\SOURCE>SYMDEB SYMDEB.EXE TRACE.COM [Enter]
Microsoft (R) Symbolic Debug Utility  Version 4.00
Copyright (C) Microsoft Corp 1984, 1985.  All rights reserved.

Processor is [80286]
-U [Enter]
1580:000E 50             PUSH   AX
1580:000F 06             PUSH   ES
1580:0010 0E             PUSH   CS
1580:0011 1F             POP    DS
1580:0012 8B0E0C00       MOV    CX,[000C]
1580:0016 8BF1           MOV    SI,CX
1580:0018 4E             DEC    SI
1580:0019 89F7           MOV    DI,SI
-U [Enter]
1580:001B 8CDB           MOV    BX,DS
1580:001D 031E0A00       ADD    BX,[000A]
1580:0021 8EC3           MOV    ES,BX
1580:0023 FD             STD
1580:0024 F3             REPZ
1580:0025 A4             MOVSB
1580:0026 53             PUSH   BX
1580:0027 B82C00         MOV    AX,002C
-U [Enter]
1580:002A 50             PUSH   AX
1580:002B CB             RETF →找到分岔指令了
1580:002C 2E8B2E0800     MOV    BP,CS:[0008]
1580:0031 8CDA           MOV    DX,DS
1580:0033 89E8           MOV    AX,BP
1580:0035 3D0010         CMP    AX,1000
1580:0038 7603           JBE    003D
1580:003A B80010         MOV    AX,1000
-G 2B [Enter] →直接執行到位址 1580:002B 處,並記錄其位址
AX=002C  BX=17E0  CX=0000  DX=0000  SP=0078  BP=0000  SI=FFFF  DI=FFFF
DS=1580  ES=17E0  SS=17F7  CS=1580  IP=002B   NV DN EI PL NZ NA PO NC
1580:002B CB             RETF
-T [Enter] →執行 RETF
AX=002C  BX=17E0  CX=0000  DX=0000  SP=007C  BP=0000  SI=FFFF  DI=FFFF
DS=1580  ES=17E0  SS=17F7  CS=17E0  IP=002C   NV DN EI PL NZ NA PO NC
17E0:002C 2E8B2E0800     MOV    BP,CS:[0008]                       CS:0008=069C
-U [Enter] →再找下一個分岔指令
17E0:0031 8CDA           MOV    DX,DS
17E0:0033 89E8           MOV    AX,BP
17E0:0035 3D0010         CMP    AX,1000
17E0:0038 7603           JBE    003D     →找到了,但猜測它不跳躍到 003D,或者您看不管
17E0:003A B80010         MOV    AX,1000  有沒有跳躍,最後還是會執行 003D 位址
17E0:003D 29C5           SUB    BP,AX
17E0:003F 29C2           SUB    DX,AX
17E0:0041 29C3           SUB    BX,AX
-U [Enter]
17E0:0043 8EDA           MOV    DS,DX
17E0:0045 8EC3           MOV    ES,BX
17E0:0047 B103           MOV    CL,03
17E0:0049 D3E0           SHL    AX,CL
17E0:004B 89C1           MOV    CX,AX
17E0:004D 48             DEC    AX
17E0:004E D1E0           SHL    AX,1
17E0:0050 8BF0           MOV    SI,AX

通常程式內會有許多地方要判斷『如果某數怎樣怎樣,就跳到某處續執行』,而且為數不少,所以很少會真的一個個去測試,而且大部分都像上面一樣,即使有跳躍也不太重要,所以大部分時候小木偶都是用猜測的,假如猜錯了,就只好重新載入,但是因為先前有記錄,所以進展會很快。依此方法,最後找到下一個重要的跳躍是在 17E0:0155。

-G 155 [Enter] →記錄此位址
AX=0000  BX=0000  CX=0004  DX=1480  SP=0100  BP=0000  SI=17CE  DI=0100
DS=0ED4  ES=0ED4  SS=17CE  CS=17E0  IP=0155   NV UP EI PL ZR NA PE NC
17E0:0155 2EFF2F         JMP    FAR CS:[BX]                        CS:0000=0109

這個指令會根據 BX 之值進行區段跳躍。執行它!

-T [Enter]
AX=0000  BX=0000  CX=0004  DX=1480  SP=0100  BP=0000  SI=17CE  DI=0100
DS=0ED4  ES=0ED4  SS=17CE  CS=0EE4  IP=0109   NV UP EI PL ZR NA PE NC
0EE4:0109 A2A66D         MOV    [6DA6],AL                          DS:6DA6=00
-U [Enter] →再依上述方法猜測找下一個重要的位址
0EE4:010C FA             CLI
0EE4:010D 8CC8           MOV    AX,CS
0EE4:010F 8ED0           MOV    SS,AX
0EE4:0111 BCA08F         MOV    SP,8FA0
0EE4:0114 FB             STI
0EE4:0115 8EC0           MOV    ES,AX
0EE4:0117 33F6           XOR    SI,SI
0EE4:0119 8BFE           MOV    DI,SI

最後找到下一個最重要的副程式,CALL 0A4B,執行它後發現,它沒有立刻顯示出執行後各暫存器之值,反倒是出現一個 SYMDEB 的提示符號,顯然它並沒有真正執行完,小木偶猜測這正是 SYMDEB 等待使用者輸入命令的程式。於是輸入顯示短實數,X,的指令。

您不要以為小木偶一下子就找到這個輸入指令的副程式,小木偶也是經過好幾次重新載入 SYMDEB,重新執行,一次一次試驗,才找到它。有時候運氣也是很重要的,但是話又說回來,您經驗越豐富,運氣也越好。好了,回歸正傳,看看執行結果如何:

-G 55F [Enter]
Microsoft (R) Symbolic Debug Utility  Version 4.00
Copyright (C) Microsoft Corp 1984, 1985.  All rights reserved.

Processor is [80286]
AX=332D  BX=0ED4  CX=0000  DX=0901  SP=8FA0  BP=0000  SI=7F30  DI=0100
DS=0EE4  ES=0EE4  SS=0EE4  CS=0EE4  IP=055F   NV UP EI PL NZ NA PO NC
0EE4:055F E8E904         CALL   0A4B
-P [Enter] →執行此副程式,沒立即顯示各暫存器之值,猜測為輸入指令之副程式
-DT 100 L1 [Enter] →輸入顯示短實數指令
AX=330D  BX=0ED4  CX=0000  DX=0901  SP=8FA0  BP=0000  SI=7F2E  DI=7F38
DS=0EE4  ES=0EE4  SS=0EE4  CS=0EE4  IP=0562   NV UP EI PL ZR NA PE NC
0EE4:0562 E86105         CALL   0AC6
-U [Enter]
0EE4:0565 74CC           JZ     0533
0EE4:0567 BE8805         MOV    SI,0588
0EE4:056A 803C00         CMP    Byte Ptr [SI],00
0EE4:056D 743B           JZ     05AA
0EE4:056F 3804           CMP    [SI],AL
0EE4:0571 7403           JZ     0576
0EE4:0573 46             INC    SI
0EE4:0574 EBF4           JMP    056A
-BP 533 [Enter]
-BP 5AA [Enter]
-BP 576 [Enter]

這堣p木偶示範一個技巧,利用中斷點來把各種條件跳躍位址設為中斷點,然後執行『G』指令,這樣就不用去一一比較。請看下面,

-G [Enter]
AX=3344  BX=0ED4  CX=0000  DX=0901  SP=8FA0  BP=0000  SI=0593  DI=7F38
DS=0EE4  ES=0EE4  SS=0EE4  CS=0EE4  IP=05AA   NV UP EI PL ZR NA PE NC
0EE4:05AA 2C41           SUB    AL,41                         ;BR1

執行後,發現原來到 05AA 去執行了。此外注意這個指令『SUB AL,41H』以及 AL 之值,假如您對 ASCII 敏感的話,應該會發現 AL 之值,就是剛剛所輸入的『DS 100 L1』之『D』指令。繼續看下面,果然是處理這個『D』指令。

-U [Enter]
0EE4:05AC 7210           JB     05BE
0EE4:05AE 3C19           CMP    AL,19
0EE4:05B0 770C           JA     05BE
0EE4:05B2 D0E0           SHL    AL,1
0EE4:05B4 98             CBW
0EE4:05B5 93             XCHG   AX,BX
0EE4:05B6 2EFF975F0B     CALL   CS:[BX+0B5F]
0EE4:05BB E963FF         JMP    0521

05AC 與 05B0 這兩個位址,是檢查 AL 之值不在 'A' 到 'Z' 等指令之外,故跳到 05BE 去處理,不管如何,小木偶還是像剛才一樣設定中斷點,然後執行『G』指令:

-BP 5BE [Enter]
-G 5BB [Enter]
181D:0100  16 A0 D0 5F A6 F5 D0 A1 F5 3F  +0.123456E-2
AX=0000  BX=000A  CX=0000  DX=0002  SP=8FA0  BP=C74E  SI=010A  DI=6D0F
DS=181D  ES=0EE4  SS=0EE4  CS=0EE4  IP=05BB   NV UP EI PL ZR NA PE NC
0EE4:05BB E963FF         JMP    0521
-

發現 SYMDEB 已經把短實數顯示出來了,由此小木偶可以知道 0EE4:05B6 這個位址很重要,當然也要記錄下來。在這個位址的 CALL 指令依據 BX 的內容,呼叫不同的位址,而 BX 之值是根據我們所輸入指令的第一個字母來決定的。好了,小木偶已經找到這段副程式了,先結束這段追蹤,回到 DOS,再用 SYMDEB 對這段副程式做詳細討論。此外,當您以『Q』指令結束時,會出現無法關閉的視窗,按確定即可關閉。


尋找顯示短實數副程式

追蹤正主了。這次小木偶應該很快就能找到顯示暫時實數的副程式,這是因為上面記錄了幾個重要的分岔位址。

E:\HomePage\SOURCE>SYMDEB SYMDEB.EXE TRACE.COM [Enter]
Microsoft (R) Symbolic Debug Utility  Version 4.00
Copyright (C) Microsoft Corp 1984, 1985.  All rights reserved.

Processor is [80286]
-G 2B [Enter]
AX=002C  BX=17E0  CX=0000  DX=0000  SP=0078  BP=0000  SI=FFFF  DI=FFFF
DS=1580  ES=17E0  SS=17F7  CS=1580  IP=002B   NV DN EI PL NZ NA PO NC
1580:002B CB             RETF
-T [Enter]
AX=002C  BX=17E0  CX=0000  DX=0000  SP=007C  BP=0000  SI=FFFF  DI=FFFF
DS=1580  ES=17E0  SS=17F7  CS=17E0  IP=002C   NV DN EI PL NZ NA PO NC
17E0:002C 2E8B2E0800     MOV    BP,CS:[0008]                       CS:0008=069C
-G 155 [Enter]
AX=0000  BX=0000  CX=0004  DX=1480  SP=0100  BP=0000  SI=17CE  DI=0100
DS=0ED4  ES=0ED4  SS=17CE  CS=17E0  IP=0155   NV UP EI PL ZR NA PE NC
17E0:0155 2EFF2F         JMP    FAR CS:[BX]                        CS:0000=0109
-T [Enter]
AX=0000  BX=0000  CX=0004  DX=1480  SP=0100  BP=0000  SI=17CE  DI=0100
DS=0ED4  ES=0ED4  SS=17CE  CS=0EE4  IP=0109   NV UP EI PL ZR NA PE NC
0EE4:0109 A2A66D         MOV    [6DA6],AL                          DS:6DA6=00
-G 5B6
Microsoft (R) Symbolic Debug Utility  Version 4.00
Copyright (C) Microsoft Corp 1984, 1985.  All rights reserved.

Processor is [80286]
-DT 100 L1
AX=0ED4  BX=0006  CX=0000  DX=0901  SP=8FA0  BP=0000  SI=0593  DI=7F38
DS=0EE4  ES=0EE4  SS=0EE4  CS=0EE4  IP=05B6   NV UP EI PL NZ AC PE NC
0EE4:05B6 2EFF975F0B     CALL   CS:[BX+0B5F]                       CS:0B65=10FC
-T [Enter]
AX=0ED4  BX=0006  CX=0000  DX=0901  SP=8F9E  BP=0000  SI=0593  DI=7F38
DS=0EE4  ES=0EE4  SS=0EE4  CS=0EE4  IP=10FC   NV UP EI PL NZ AC PE NC
0EE4:10FC B344           MOV    BL,44                         ;'D'
-U [Enter]
0EE4:10FE EB02           JMP    1102
0EE4:1100 B345           MOV    BL,45                         ;'E'
0EE4:1102 8B36716D       MOV    SI,[6D71]
0EE4:1106 FC             CLD
0EE4:1107 AC             LODSB
0EE4:1108 3C0D           CMP    AL,0D
0EE4:110A 7414           JZ     1120
0EE4:110C 3C20           CMP    AL,20                         ;' '
-D 6D71 L2 [Enter] →看看 SI 指向那兒?
0EE4:6D70     2F 7F                                          /.
-D 7F2F L11 [Enter]
0EE4:7F20                                               54                 T
0EE4:7F30  20 31 30 30 20 4C 31 0D-00 00 00 00 00 00 00 00   100 L1.........
→原來 SI 指向我們輸入的指令
-U 110E [Enter]
0EE4:110E 7410           JZ     1120
0EE4:1110 3C09           CMP    AL,09
0EE4:1112 740C           JZ     1120
0EE4:1114 3C3B           CMP    AL,3B                         ;';'
0EE4:1116 7408           JZ     1120
0EE4:1118 3C30           CMP    AL,30                         ;'0'
0EE4:111A 7C11           JL     112D
0EE4:111C 3C39           CMP    AL,39                         ;'9'
-U [Enter]
0EE4:111E 7F0D           JG     112D
0EE4:1120 A01F6C         MOV    AL,[6C1F]
0EE4:1123 80FB44         CMP    BL,44                         ;'D'
0EE4:1126 7403           JZ     112B
0EE4:1128 A0206C         MOV    AL,[6C20] →以上幾行應該是檢查輸入語法,
0EE4:112B EB03           JMP    1130     但不論如何都會跳到 1130 處
0EE4:112D E896F9         CALL   0AC6
0EE4:1130 BE6111         MOV    SI,1161
-G 1130 [Enter]
AX=0E54  BX=0044  CX=0000  DX=0901  SP=8F9E  BP=0000  SI=7F30  DI=7F38
DS=0EE4  ES=0EE4  SS=0EE4  CS=0EE4  IP=1130   NV UP EI PL NZ AC PE NC
0EE4:1130 BE6111         MOV    SI,1161
-T [Enter]
AX=0E54  BX=0044  CX=0000  DX=0901  SP=8F9E  BP=0000  SI=1161  DI=7F38
DS=0EE4  ES=0EE4  SS=0EE4  CS=0EE4  IP=1133   NV UP EI PL NZ AC PE NC
0EE4:1133 803C00         CMP    Byte Ptr [SI],00                   DS:1161=41
-D 1161 LF [Enter]
0EE4:1160     41 42 44 4C 53 54 57-00 CA 12 D9 13 31 13 A5   ABDLSTW.J.Y.1.% →這一段應該是檢查 D 指令的指令群
-U [Enter]
0EE4:1136 744D           JZ     1185
0EE4:1138 3804           CMP    [SI],AL →檢查是 D 指令的那一個『子』指令
0EE4:113A 7403           JZ     113F
0EE4:113C 46             INC    SI
0EE4:113D EBF4           JMP    1133
0EE4:113F 81EE6111       SUB    SI,1161
0EE4:1143 D1E6           SHL    SI,1
0EE4:1145 80FB45         CMP    BL,45                         ;'E'
-U [Enter]
0EE4:1148 7409           JZ     1153
0EE4:114A A21F6C         MOV    [6C1F],AL    →把『子』指令存入 6C1F 中
0EE4:114D 2EFF946911     CALL   CS:[SI+1169] →跳到『子』指令處執行
0EE4:1152 C3             RET
0EE4:1153 A2206C         MOV    [6C20],AL
0EE4:1156 C606756DFF     MOV    Byte Ptr [6D75],FF
0EE4:115B 2EFF947711     CALL   CS:[SI+1177]
0EE4:1160 C3             RET
-G 114D [Enter]
AX=0E54  BX=0044  CX=0000  DX=0901  SP=8F9E  BP=0000  SI=000A  DI=7F38
DS=0EE4  ES=0EE4  SS=0EE4  CS=0EE4  IP=114D   NV UP EI NG NZ AC PE CY
0EE4:114D 2EFF946911     CALL   CS:[SI+1169]                       CS:1173=11AB
-T [Enter]
AX=0E54  BX=0044  CX=0000  DX=0901  SP=8F9C  BP=0000  SI=000A  DI=7F38
DS=0EE4  ES=0EE4  SS=0EE4  CS=0EE4  IP=11AB   NV UP EI NG NZ AC PE CY
0EE4:11AB BB0A00         MOV    BX,000A →暫時實數所佔位元組個數
-U [Enter]
0EE4:11AE EB04           JMP    11B4
0EE4:11B0 90             NOP
0EE4:11B1 BB0400         MOV    BX,0004
0EE4:11B4 B90100         MOV    CX,0001
0EE4:11B7 E8D2FF         CALL   118C
0EE4:11BA 83F900         CMP    CX,+00
0EE4:11BD 7F01           JG     11C0
0EE4:11BF C3             RET
-G 11B7 [Enter]
AX=0E54  BX=000A  CX=0001  DX=0901  SP=8F9C  BP=0000  SI=000A  DI=7F38
DS=0EE4  ES=0EE4  SS=0EE4  CS=0EE4  IP=11B7   NV UP EI NG NZ AC PE CY
0EE4:11B7 E8D2FF         CALL   118C →比較執行 118C 副指令前後,發現 DS:SI 變成暫時實數所在位址
-P [Enter]                            ,因此 118C 副指令的作用為取得暫時實數位址
AX=181D  BX=000A  CX=0001  DX=0100  SP=8F9C  BP=181D  SI=0100  DI=6DB5
DS=181D  ES=0EE4  SS=0EE4  CS=0EE4  IP=11BA   NV UP EI PL ZR NA PE NC
0EE4:11BA 83F900         CMP    CX,+00 →我們所要印出的暫時實數就在 181D:0100 處
-U [Enter]                     這個位址已存於 DS:SI 中
0EE4:11BD 7F01           JG     11C0 →看到白色部份 CX=1,故必跳到 11C0 處
0EE4:11BF C3             RET
0EE4:11C0 2E803E886DFF   CMP    Byte Ptr CS:[6D88],FF
0EE4:11C6 750E           JNZ    11D6
0EE4:11C8 8BC1           MOV    AX,CX
0EE4:11CA F6F3           DIV    BL
0EE4:11CC 8AC8           MOV    CL,AL
0EE4:11CE 32ED           XOR    CH,CH
-G 11C0 [Enter]
AX=181D  BX=000A  CX=0001  DX=0100  SP=8F9C  BP=181D  SI=0100  DI=6DB5
DS=181D  ES=0EE4  SS=0EE4  CS=0EE4  IP=11C0   NV UP EI PL NZ NA PO NC
0EE4:11C0 2E803E886DFF   CMP    Byte Ptr CS:[6D88],FF              CS:6D88=01
-T [Enter]
AX=181D  BX=000A  CX=0001  DX=0100  SP=8F9C  BP=181D  SI=0100  DI=6DB5
DS=181D  ES=0EE4  SS=0EE4  CS=0EE4  IP=11C6   NV UP EI PL NZ AC PO CY
0EE4:11C6 750E           JNZ    11D6
-T [Enter]
AX=181D  BX=000A  CX=0001  DX=0100  SP=8F9C  BP=181D  SI=0100  DI=6DB5
DS=181D  ES=0EE4  SS=0EE4  CS=0EE4  IP=11D6   NV UP EI PL NZ AC PO CY
0EE4:11D6 56             PUSH   SI
-T [Enter]
AX=181D  BX=000A  CX=0001  DX=0100  SP=8F9A  BP=181D  SI=0100  DI=6DB5
DS=181D  ES=0EE4  SS=0EE4  CS=0EE4  IP=11D7   NV UP EI PL NZ AC PO CY
0EE4:11D7 E83BF9         CALL   0B15
-T [Enter] →追蹤 0B15 副程式,0B15 執行完後,會回到 11DA
AX=181D  BX=000A  CX=0001  DX=0100  SP=8F98  BP=181D  SI=0100  DI=6DB5
DS=181D  ES=0EE4  SS=0EE4  CS=0EE4  IP=0B15   NV UP EI PL NZ AC PO CY
0EE4:0B15 8CDA           MOV    DX,DS
-U [Enter]
0EE4:0B17 E81500         CALL   0B2F →處理 DS,待處理完會跳回下一位址 0B1A
0EE4:0B1A B03A           MOV    AL,3A                         ;':'
0EE4:0B1C E87901         CALL   0C98 →存入『:』字元
0EE4:0B1F 8BD6           MOV    DX,SI
0EE4:0B21 EB0C           JMP    0B2F →處理 SI
0EE4:0B23 8CC2           MOV    DX,ES
0EE4:0B25 E80700         CALL   0B2F
0EE4:0B28 B03A           MOV    AL,3A                         ;':'

由副程式 0B15 前面的程式碼看來,似乎是把 DS:SI 這一個位址印在螢幕上 ( 再繼續追蹤,會發現不是印在螢幕上,而是存在一個字串 ),因為這兩個都是十六位元的數值,處理方法一樣,所以先把它們放在 DX 中,再呼叫 0B2F 副程式,只是第二次呼叫時用 JMP 0B2F。

現在,來追蹤 0B2F 及 0C98 這兩個副程式。為了使 0B2F 副程式更清楚,所以用粉紅色字表示。

-U [Enter]
0EE4:0B2A E86B01         CALL   0C98
0EE4:0B2D 8BD7           MOV    DX,DI
0EE4:0B2F 8AC6           MOV    AL,DH →先處理 DH 暫存器,為保存 DX,所以用 AL 等於 DH
0EE4:0B31 E80200         CALL   0B36
0EE4:0B34 8AC2           MOV    AL,DL →等 DH 處理完,再處理 DL
0EE4:0B36 8AE0           MOV    AH,AL →把 AL 的低半位元組暫時存於 AH 中
0EE4:0B38 51             PUSH   CX
0EE4:0B39 B104           MOV    CL,04
-U [Enter]
0EE4:0B3B D2E8           SHR    AL,CL →向右移四位使 AL 的高半位元組變成低半位元組
0EE4:0B3D 59             POP    CX
0EE4:0B3E E80200         CALL   0B43
0EE4:0B41 8AC4           MOV    AL,AH →等 AL 內的高半位元組處理完,再由 AH 取回低半位元組
0EE4:0B43 240F           AND    AL,0F  → 0B43 到 0B4A 使 AL 內的半位元組變成 ASCII 字元
0EE4:0B45 0490           ADD    AL,90
0EE4:0B47 27             DAA
0EE4:0B48 1440           ADC    AL,40                         ;'@'
-U [Enter]
0EE4:0B4A 27             DAA
0EE4:0B4B E84A01         CALL   0C98
0EE4:0B4E C3             RET
0EE4:0B4F B020           MOV    AL,20                         ;' '
0EE4:0B51 E94401         JMP    0C98
0EE4:0B54 83F900         CMP    CX,+00
0EE4:0B57 7E05           JLE    0B5E
0EE4:0B59 E8F3FF         CALL   0B4F

由上面程式流程看起來,小木偶猜測副程式 0B2F 應該是用來把 DX 暫存器的十六進位數變成 ASCII 字元,有一個細微的徵兆 ( 如下面白色的位址 ) 給小木偶提示,SYMDEB 的『DT』指令會先印出位址來,而之前已經把區段位址與偏移位址存入 DS:SI 堣F。然後直接使 0B2F 反組譯,發現這段程式碼果真是做這樣的事。

181D:0100  16 A0 D0 5F A6 F5 D0 A1 F5 3F  +0.123456E-2

把 DX 堛漱Q六進位換成 ASCII 字元,共有四個 ASCII 字元,副程式 0B2F 先處理 DH 堛漕潃茈b位元組 ( 一個位元組是 8 個位元,故半位元組就是指 4 個位元,例如 AL=12H,則 1 是高半位元組,2 是低半位元組 ),也就是兩個字元。再處理 DL 的兩個半位元組,而處理 DH 與 DL 這兩個暫存器的方法一樣,所以用呼叫同一個副程式來處理,就是 0B36 副程式。而不管是處理 DH 或是 DL 都要處理兩個字元,高半位元組先右移四個位元 ( 位址 0B39、0B3B 的程式碼 ),低半位組則是與 0FH 做『且』( AND ) 運算 ( 位址 0B43 的程式碼 ),再把存於 AL 暫存器內的半位元組轉換成 ASCII 碼 ( 位址 0B45∼0B4A,您會發現寫這段程式的人,的確是高手,不用比較、條件跳躍,直接就轉換了 )。好了,小木偶猜測,0C98 副程式應該會把『181D:0100』字串依序存在記憶體某處,或直接印在螢幕上吧?進入此副程式追蹤看看。

-G B4B [Enter]
AX=1831  BX=000A  CX=0001  DX=181D  SP=8F92  BP=181D  SI=0100  DI=6DB5
DS=181D  ES=0EE4  SS=0EE4  CS=0EE4  IP=0B4B   NV UP EI PL NZ NA PO CY
0EE4:0B4B E84A01         CALL   0C98
-T [Enter] →開始進入 0C98 副程式
AX=1831  BX=000A  CX=0001  DX=181D  SP=8F90  BP=181D  SI=0100  DI=6DB5
DS=181D  ES=0EE4  SS=0EE4  CS=0EE4  IP=0C98   NV UP EI PL NZ NA PO CY
0EE4:0C98 2E803E786D00   CMP    Byte Ptr CS:[6D78],00              CS:6D78=00
-U [Enter]                  └→根據 6D78 之值決定副程式功用,此時會跳到 0D4D 處
0EE4:0C9E 7503           JNZ    0CA3
0EE4:0CA0 E9AA00         JMP    0D4D
0EE4:0CA3 2E803E8A6D01   CMP    Byte Ptr CS:[6D8A],01
0EE4:0CA9 746B           JZ     0D16
0EE4:0CAB 2E803E8A6D02   CMP    Byte Ptr CS:[6D8A],02
0EE4:0CB1 7463           JZ     0D16
0EE4:0CB3 50             PUSH   AX
0EE4:0CB4 247F           AND    AL,7F
-U D4D [Enter]
0EE4:0D4D 56             PUSH   SI
0EE4:0D4E 52             PUSH   DX
0EE4:0D4F 50             PUSH   AX
0EE4:0D50 247F           AND    AL,7F →轉換成大寫
0EE4:0D52 BE7A6B         MOV    SI,6B7A →設定起始位址
0EE4:0D55 2E0336FA6B     ADD    SI,CS:[6BFA] →加上指標,得到真正存入之位址
0EE4:0D5A 2E8804         MOV    CS:[SI],AL →存入
0EE4:0D5D 2EFF06FA6B     INC    Word Ptr CS:[6BFA] →使指標加一,指向下一個位址
-U [Enter]
0EE4:0D62 58             POP    AX
0EE4:0D63 3C0A           CMP    AL,0A
0EE4:0D65 7503           JNZ    0D6A →不是換行字元,跳到 0D6A 處
0EE4:0D67 E80300         CALL   0D6D
0EE4:0D6A 5A             POP    DX
0EE4:0D6B 5E             POP    SI
0EE4:0D6C C3             RET
0EE4:0D6D 50             PUSH   AX

副程式 0C98 會根據 6D78 之數值決定程式分支,但此處 6D78 為零,所以都會跳到 0D4D 處執行。0D4D∼0D6D 的程式碼會把 AL 內的 ASCII 字元存在記憶體 CS:6B7A 處,每存一個位元組,6BFA 位址之值便增加一,指向下一個位址。假如被存入的字元為 0AH,則會跳到 0D6D 副程式,存入 0AH 字元,應該表示字串結束。

-G D6C [Enter]
AX=1831  BX=000A  CX=0001  DX=181D  SP=8F90  BP=181D  SI=0100  DI=6DB5
DS=181D  ES=0EE4  SS=0EE4  CS=0EE4  IP=0D6C   NV UP EI PL NZ AC PE NC
0EE4:0D6C C3             RET
-T [Enter]
AX=1831  BX=000A  CX=0001  DX=181D  SP=8F92  BP=181D  SI=0100  DI=6DB5
DS=181D  ES=0EE4  SS=0EE4  CS=0EE4  IP=0B4E   NV UP EI PL NZ AC PE NC
0EE4:0B4E C3             RET →已處理完 DS 最高的半位元組
-G B1A [Enter] →其他三個半位元組都是一樣的,故不再詳細追蹤
AX=1D44  BX=000A  CX=0001  DX=181D  SP=8F98  BP=181D  SI=0100  DI=6DB5
DS=181D  ES=0EE4  SS=0EE4  CS=0EE4  IP=0B1A   NV UP EI PL NZ AC PE NC
0EE4:0B1A B03A           MOV    AL,3A                         ;':'
-D CS:6B7A L16 [Enter]
0EE4:6B70                                31 38 31 44 00 00            181D..
0EE4:6B80  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
-G 11DA [Enter] → SI 的處理方法和 DS 相同,也不詳細追蹤
AX=0030  BX=000A  CX=0001  DX=0100  SP=8F9A  BP=181D  SI=0100  DI=6DB5
DS=181D  ES=0EE4  SS=0EE4  CS=0EE4  IP=11DA   NV UP EI PL NZ AC PO NC
0EE4:11DA E872F9         CALL   0B4F
-D CS:6B7A L16 [Enter] →看看 0B15 副程式執行完的結果
0EE4:6B70                                31 38 31 44 3A 30            181D:0
0EE4:6B80  31 30 30 00 00 00 00 00-00 00 00 00 00 00 00 00  100.............

好了,SYMDEB 終於把位址處理好了,接下來,應該是把構成暫時實數的十個位元組變成。先反組譯看看,第一個副程式 0B4F 是做什麼的。

-U B4F [Enter]
0EE4:0B4F B020           MOV    AL,20 → 20H = ASCII 的空白字元
0EE4:0B51 E94401         JMP    0C98  →0C98 副程式是把 AL 字元存於以位址 6B7A 為參考點,再加上變數 [6BFA] 所指位址
0EE4:0B54 83F900         CMP    CX,+00
0EE4:0B57 7E05           JLE    0B5E
0EE4:0B59 E8F3FF         CALL   0B4F
0EE4:0B5C E2F6           LOOP   0B54
0EE4:0B5E C3             RET
0EE4:0B5F A02F65         MOV    AL,[652F]
-P [Enter]
AX=0020  BX=000A  CX=0001  DX=0100  SP=8F9A  BP=181D  SI=0100  DI=6DB5
DS=181D  ES=0EE4  SS=0EE4  CS=0EE4  IP=11DD   NV UP EI PL NZ AC PO NC
0EE4:11DD 5E             POP    SI

原來副程式 0B4F 只是把空白字元存入字串中,不值得追蹤,直接執行『P』指令快速執行過。

-U [Enter]
0EE4:11DE 1E             PUSH   DS
0EE4:11DF 51             PUSH   CX
0EE4:11E0 56             PUSH   SI
0EE4:11E1 53             PUSH   BX
0EE4:11E2 8BCB           MOV    CX,BX →暫時實數共十個位元組
0EE4:11E4 BF216C         MOV    DI,6C21
0EE4:11E7 FC             CLD
0EE4:11E8 E864F9         CALL   0B4F →再存入一個空白字元
-U [Enter]
0EE4:11EB AC             LODSB →取得暫時實數的第一個位元組
0EE4:11EC AA             STOSB
0EE4:11ED 36803E756DFF   CMP    Byte Ptr SS:[6D75],FF
0EE4:11F3 7505           JNZ    11FA
0EE4:11F5 E2F4           LOOP   11EB
0EE4:11F7 EB16           JMP    120F
0EE4:11F9 90             NOP
0EE4:11FA E839F9         CALL   0B36
-U [Enter]
0EE4:11FD E2E9           LOOP   11E8
0EE4:11FF 2E8936B56D     MOV    CS:[6DB5],SI
0EE4:1204 2E8C1EB76D     MOV    CS:[6DB7],DS
0EE4:1209 E843F9         CALL   0B4F
0EE4:120C E840F9         CALL   0B4F
0EE4:120F 8CC8           MOV    AX,CS
0EE4:1211 8ED8           MOV    DS,AX
0EE4:1213 BE216C         MOV    SI,6C21
-G 11EC [Enter]
AX=0016  BX=000A  CX=000A  DX=0100  SP=8F94  BP=181D  SI=0101  DI=6C21
DS=181D  ES=0EE4  SS=0EE4  CS=0EE4  IP=11EC   NV UP EI PL NZ AC PO NC
0EE4:11EC AA             STOSB → AL 已經存入暫時實數的最低位元組
-T [Enter]
AX=0016  BX=000A  CX=000A  DX=0100  SP=8F94  BP=181D  SI=0101  DI=6C22
DS=181D  ES=0EE4  SS=0EE4  CS=0EE4  IP=11ED   NV UP EI PL NZ AC PO NC
0EE4:11ED 36803E756DFF   CMP    Byte Ptr SS:[6D75],FF              SS:6D75=00
-T [Enter]
AX=0016  BX=000A  CX=000A  DX=0100  SP=8F94  BP=181D  SI=0101  DI=6C22
DS=181D  ES=0EE4  SS=0EE4  CS=0EE4  IP=11F3   NV UP EI PL NZ AC PO CY
0EE4:11F3 7505           JNZ    11FA
-T [Enter]
AX=0016  BX=000A  CX=000A  DX=0100  SP=8F94  BP=181D  SI=0101  DI=6C22
DS=181D  ES=0EE4  SS=0EE4  CS=0EE4  IP=11FA   NV UP EI PL NZ AC PO CY
0EE4:11FA E839F9         CALL   0B36

副程式 0B36 其實在前面已經解析過了,它是副程式 0B2F 的一部份,僅僅處理 8 位元的資料,它把 AL 暫存器的十六進位數,變成兩個 ASCII 字元存於 CS:6B7A 起始的字串中。而暫時實數由十個位元組構成,存於 CX 中,所以也不用詳細追蹤,直接以『P』指令帶過。

-P [Enter]
AX=1636  BX=000A  CX=000A  DX=0100  SP=8F94  BP=181D  SI=0101  DI=6C22
DS=181D  ES=0EE4  SS=0EE4  CS=0EE4  IP=11FD   NV UP EI PL NZ AC PO NC
0EE4:11FD E2E9           LOOP   11E8
-P [Enter]
AX=3F46  BX=000A  CX=0000  DX=0100  SP=8F94  BP=181D  SI=010A  DI=6C2B
DS=181D  ES=0EE4  SS=0EE4  CS=0EE4  IP=11FF   NV UP EI PL NZ AC PE NC
0EE4:11FF 2E8936B56D     MOV    CS:[6DB5],SI                       CS:6DB5=0100
-D CS:6B7A L36 [Enter] →觀察看看要印出的字串
0EE4:6B70                                31 38 31 44 3A 30            181D:0
0EE4:6B80  31 30 30 20 20 31 36 20-41 30 20 44 30 20 35 46  100  16 A0 D0 5F
0EE4:6B90  20 41 36 20 46 35 20 44-30 20 41 31 20 46 35 20   A6 F5 D0 A1 F5
0EE4:6BA0  33 46 00 00 00 00 00 00-00 00 00 00 00 00 00 00  3F..............
-U [Enter]
0EE4:1204 2E8C1EB76D     MOV    CS:[6DB7],DS →DS:SI 指向下一個暫時實數位址,181D:010A
0EE4:1209 E843F9         CALL   0B4F →連同下一行,總共在 CS:6B7A 處存入兩個空白字元
0EE4:120C E840F9         CALL   0B4F
0EE4:120F 8CC8           MOV    AX,CS
0EE4:1211 8ED8           MOV    DS,AX
0EE4:1213 BE216C         MOV    SI,6C21
0EE4:1216 5B             POP    BX
0EE4:1217 53             PUSH   BX
-U [Enter]
0EE4:1218 83FB0A         CMP    BX,+0A →檢查是否為 10 個位元組
0EE4:121B 740D           JZ     122A
0EE4:121D 83FB08         CMP    BX,+08 →檢查是否 8 個位元組
0EE4:1220 7403           JZ     1225
0EE4:1222 E8F052         CALL   6515
0EE4:1225 E8864D         CALL   5FAE   →處理長實數的有效數
0EE4:1228 EB03           JMP    122D
0EE4:122A E8EA4C         CALL   5F17   →處理暫時實數的有效數
-G 1218 [Enter]
AX=0EE4  BX=000A  CX=0000  DX=0100  SP=8F94  BP=181D  SI=6C21  DI=6C2B
DS=0EE4  ES=0EE4  SS=0EE4  CS=0EE4  IP=1218   NV UP EI PL NZ AC PO NC
0EE4:1218 83FB0A         CMP    BX,+0A
-T [Enter]
AX=0EE4  BX=000A  CX=0000  DX=0100  SP=8F94  BP=181D  SI=6C21  DI=6C2B
DS=0EE4  ES=0EE4  SS=0EE4  CS=0EE4  IP=121B   NV UP EI PL ZR NA PE NC
0EE4:121B 740D           JZ     122A
-T [Enter]
AX=0EE4  BX=000A  CX=0000  DX=0100  SP=8F94  BP=181D  SI=6C21  DI=6C2B
DS=0EE4  ES=0EE4  SS=0EE4  CS=0EE4  IP=122A   NV UP EI PL ZR NA PE NC
0EE4:122A E8EA4C         CALL   5F17

現在雖不知道副程式 5F17 的功用,但根據前後文推測,它極有可能就是把 IEEE 754 暫時實數格式轉變成十進位字串的副程式。理由是前面我們已經追蹤到 SYMDEB 已經把暫時實數位址、十個位元組內容都處理完畢了,接下來應該是把它轉換成十進位字串了。第二個理由是觀察下面的程式片段,122D∼127C 這段程式,發現與要印出來的格式『+0.123456E-2』很像,先是印出符號,再來是『0.』與係數,接著是『E』與指數符號,最後是指數。假如真是如此,那麼 AX 就應該是副程式 5F17 的返回值,如果 AX 等於 1,表示此暫時實數是一般數值,否則可能是意義。

-P [Enter]
AX=0001  BX=0020  CX=0006  DX=FFFE  SP=8F94  BP=C74E  SI=6D0A  DI=6D0F
DS=0EE4  ES=0EE4  SS=0EE4  CS=0EE4  IP=122D   NV UP EI PL NZ NA PE CY
0EE4:122D 3D0000         CMP    AX,0000 →檢查 AX 是否為零,如果為零,表示此數為 NaN
-U [Enter]
0EE4:1230 7512           JNZ    1244
0EE4:1232 B03F           MOV    AL,3F                         ;'?'
0EE4:1234 E861FA         CALL   0C98
0EE4:1237 E85EFA         CALL   0C98
0EE4:123A E85BFA         CALL   0C98
0EE4:123D C6061A6CF9     MOV    Byte Ptr [6C1A],F9
0EE4:1242 EB6F           JMP    12B3
0EE4:1244 80FB20         CMP    BL,20 →檢查正或負數
-U [Enter]
0EE4:1247 7502           JNZ    124B
0EE4:1249 B32B           MOV    BL,2B                         ;'+'
0EE4:124B 8AC3           MOV    AL,BL
0EE4:124D E848FA         CALL   0C98
0EE4:1250 B030           MOV    AL,30                         ;'0'
0EE4:1252 E843FA         CALL   0C98
0EE4:1255 B02E           MOV    AL,2E                         ;'.'
0EE4:1257 E83EFA         CALL   0C98 →位址 1250∼1257 是存入『0.』到 CS:6B7A 所指字串
-U [Enter]
0EE4:125A 8A04           MOV    AL,[SI]
0EE4:125C A21A6C         MOV    [6C1A],AL
0EE4:125F 8BDE           MOV    BX,SI
0EE4:1261 803C00         CMP    Byte Ptr [SI],00
0EE4:1264 7405           JZ     126B
0EE4:1266 1E             PUSH   DS
0EE4:1267 07             POP    ES
0EE4:1268 E803FC         CALL   0E6E
-U [Enter]
0EE4:126B B045           MOV    AL,45                         ;'E'
0EE4:126D E828FA         CALL   0C98
0EE4:1270 8BC2           MOV    AX,DX
0EE4:1272 D1D0           RCL    AX,1
0EE4:1274 B02B           MOV    AL,2B                         ;'+'
0EE4:1276 7304           JNB    127C
0EE4:1278 B02D           MOV    AL,2D                         ;'-'
0EE4:127A F7DA           NEG    DX
-U [Enter]
0EE4:127C E819FA         CALL   0C98
0EE4:127F E85533         CALL   45D7
0EE4:1282 81FA0F27       CMP    DX,270F
0EE4:1286 7205           JB     128D
0EE4:1288 80061A6C04     ADD    Byte Ptr [6C1A],04
0EE4:128D 81FA0F27       CMP    DX,270F
0EE4:1291 7F20           JG     12B3
0EE4:1293 FE0E1A6C       DEC    Byte Ptr [6C1A]
-G 1282 [Enter]
AX=0000  BX=6D0B  CX=0006  DX=0002  SP=8F94  BP=C74E  SI=6D0A  DI=6D0F
DS=0EE4  ES=0EE4  SS=0EE4  CS=0EE4  IP=1282   NV UP EI PL NZ AC PE NC
0EE4:1282 81FA0F27       CMP    DX,270F
-D CS:6B7A L46 [Enter]
0EE4:6B70                                31 38 31 44 3A 30            181D:0
0EE4:6B80  31 30 30 20 20 31 36 20-41 30 20 44 30 20 35 46  100  16 A0 D0 5F
0EE4:6B90  20 41 36 20 46 35 20 44-30 20 41 31 20 46 35 20   A6 F5 D0 A1 F5
0EE4:6BA0  33 46 20 20 2B 30 2E 31-32 33 34 35 36 45 2D 32  3F  +0.123456E-2
0EE4:6BB0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

上面的程式中,在位址 126D 處有一指令『CALL 0E6E』很可能是把副程式 5F17 所產生的係數字串移到 CS:6B7A 字串堙A把 0E6E 副程式反組譯看看,

-U E6E [Enter]
0EE4:0E6E 56             PUSH   SI
0EE4:0E6F 51             PUSH   CX
0EE4:0E70 1E             PUSH   DS
0EE4:0E71 33C9           XOR    CX,CX
0EE4:0E73 268A0F         MOV    CL,ES:[BX]
0EE4:0E76 80F900         CMP    CL,00
0EE4:0E79 740E           JZ     0E89
0EE4:0E7B 43             INC    BX
-U [Enter]
0EE4:0E7C 8BF3           MOV    SI,BX
0EE4:0E7E 8CC0           MOV    AX,ES
0EE4:0E80 8ED8           MOV    DS,AX
0EE4:0E82 FC             CLD
0EE4:0E83 AC             LODSB
0EE4:0E84 E811FE         CALL   0C98
0EE4:0E87 E2F9           LOOP   0E82
0EE4:0E89 1F             POP    DS
-U [Enter]
0EE4:0E8A 59             POP    CX
0EE4:0E8B 5E             POP    SI
0EE4:0E8C C3             RET
0EE4:0E8D 06             PUSH   ES
0EE4:0E8E 52             PUSH   DX
0EE4:0E8F 8EC0           MOV    ES,AX
0EE4:0E91 EB6A           JMP    0EFD
0EE4:0E93 90             NOP
-D ES:6D0A L16 [Enter]
0EE4:6D00                                06 31 32 33 34 35            .12345
0EE4:6D10  36 30 30 30 30 30 30 30-30 30 30 05 31 23 4E 41  60000000000.1#NA-

觀察上面的 0E6E 副程式,發現它的確是把 ES:BX 所指的字串移到 CS:6B7A 字串堙C而 BX 是在位址 125F 處,有一道『MOV BX,SI』決定了 BX,而 SI 則是在 5F17 副程式返回時,就已經決定了。換句話說,在執行到位址 122D 時,就知道 SI 為 6D0A ( 上面紅色字 )。因此小木偶下達『D ES:6D0A L16』指令,檢視其內容,果然發現真的是我們要印出來的字串,而此字串的第一個數字『06』( 上面以灰色字表示 ) 應該就是係數的實際長度,此處為六個位元組。


取得 IEEE 754 暫時實數轉換成十進位字串的程式碼

底下小木偶就來詳細追蹤 5F17 副程式,綜合上面初步追蹤的結果,有下面個結論:

  1. F517 副程式會把 CS:6C21 所指的十位元組暫時實數 ( IEEE 754 格式 ) 的有效數部份變成十進位字串,並存於位址 CS:6D0A 處。
  2. 這個十進位的有效數字串的第一個位元組是有幾位有效數。( 因為小數點後面的零不算 )
  3. 這個十進位的有效數字串是 0 到 1 之間的數。
  4. 至於指數部份還不太清楚,但是猜測在 5F17 副程式應可得知答案。
  5. 副程式 5F17 返回時,假如 AX 不為零,表示此暫時實數為正常的數值,否則可能是另有其他意義,例如,NaN ( Not a Number,不是一個數 )。

雖然小木偶已經能把這段程式碼萃取出來,但是有些地方仍不太明瞭,所以底下的註解只寫出已了解的部份。如果有前輩高人能夠解釋,小木偶感激不盡。

-U 5F17 5F67 [Enter]
0EE4:5F17 06             PUSH     ES
0EE4:5F18 8CD8           MOV      AX,DS
0EE4:5F1A 8EC0           MOV      ES,AX
0EE4:5F1C FC             CLD
0EE4:5F1D BFFE6C         MOV      DI,6CFE →把 CS:6C21 處的 IEEE 754 暫時實數移到
0EE4:5F20 57             PUSH     DI        CS:6CFE,每次移一字組,所以 CX=5
0EE4:5F21 B90500         MOV      CX,0005
0EE4:5F24 F3             REPZ
0EE4:5F25 A5             MOVSW
0EE4:5F26 5E             POP     SI
0EE4:5F27 8B4C08         MOV     CX,[SI+08] →CX 為暫時實數的指數部份
0EE4:5F2A 8064097F       AND     Byte Ptr [SI+09],7F →使暫時實數的變為正值
0EE4:5F2E B320           MOV     BL,20                         ;' '
0EE4:5F30 8B14           MOV     DX,[SI]
0EE4:5F32 0B5402         OR      DX,[SI+02]
0EE4:5F35 0B5404         OR      DX,[SI+04]
0EE4:5F38 0AD6           OR      DL,DH
0EE4:5F3A 32F6           XOR     DH,DH
0EE4:5F3C 0B5406         OR      DX,[SI+06]
0EE4:5F3F 8B4408         MOV     AX,[SI+08]
0EE4:5F42 0BC0           OR      AX,AX
0EE4:5F44 7438           JZ      5F7E  →若 AX=0,表示此暫時實數為 0 或極小,跳到位址 5F7E 處
0EE4:5F46 0BC9           OR      CX,CX →若 CX 的最高位元為 1,表示此暫時實數為負
0EE4:5F48 7902           JNS     5F4C    否則為正值,若為正值跳至 5F4C 處
0EE4:5F4A B32D           MOV     BL,2D                         ;'-'
0EE4:5F4C F7D1           NOT     CX    →若指數部份為 7FFF,會跳到 5F56,否則會跳到 5F66
0EE4:5F4E F7C1FF7F       TEST    CX,7FFF
0EE4:5F52 F7D1           NOT     CX
0EE4:5F54 7510           JNZ     5F66
0EE4:5F56 D1E2           SHL     DX,1
0EE4:5F58 D0D1           RCL     CL,1
0EE4:5F5A D1E2           SHL     DX,1
0EE4:5F5C D0D1           RCL     CL,1
0EE4:5F5E D0E1           SHL     CL,1
0EE4:5F60 D0E1           SHL     CL,1
0EE4:5F62 D0E1           SHL     CL,1
0EE4:5F64 EB28           JMP     5F8E
0EE4:5F66 53             PUSH    BX
0EE4:5F67 E98000         JMP     5FEA

5F17 副程式一開始把暫時實數複製一份到 CS:6CFE 處,接著取出符號及指數部份存於 CX,留待稍後處理。接著使暫時實數變為正,因為真正的正負號已存入 CX 第 16 位元了,所以並不影響結果。5F30∼5F3C 應該是檢查此暫時實數的有效數部份 ( 共 8 個位元組 ) 是否全部為零,如果是的話,最後 DX 應為零。位址 5F3F 是取出暫時實數的指數部份存於 AX,如果 AX 為零,表示此暫時實數的數值非常小 ( 比 2-16382 小 ),屬於 Denormals 或 Pseudo-denormals,視為零,跳至位址 5F7E 處執行。

接下來檢查指數部份是否為 7FFF,若為 7FFF 可能是無窮大、非數值 ( NAN ) 或是 FPU 不支援的數 ( IND ),跳到 5F56 處繼續執行,否則跳到 5F66。

底下 5F6A∼5F7D 有兩個暫時實數,

5FEA 處開始要把一般實數 ( Normals ) 轉變成 ASCII 字串,程式先取得有效數的最高位元組,存入 BH。再取得指數部份存入 AX、CX,接著把 AX 乘以一個常數,4D104DH。這麼做的目的是求出換成十進位後,整數部份是個位數、十位數、還是百位數……;或者是在 1 到 -1 之間的數時,從十分位,還是百分位,還是千分位……開始。這個數值在位址 600F 處被計算出來,並存在 DI 堙C舉幾個例子:

實數:0.12   0.00123456  4089.12345   6.02E4931
DI:  0      FFFE(-2)    4            1344

第一個例子,沒有整數,且小數從十分位開始就不為零,DI 等於 0;第二個例子也沒有整數,且小數從千分位開始就不為零,DI 等於 -2;第三個例子,整數有四位,DI 等於 4;最後一個例子,整數有 4932 位,DI 等於十六進位的 1344,即十進位的 4932。為什麼指數乘以 4D104D 就會得到 DI 值呢?原來 4D104D 其實是 log 2 的 1000000H 倍,讀者應該還記得高中時,如果要求某數的位數,就對該數取對數值,例如:

log 200.00= 2.301029995664 →200 是三位數,log 200 的整數部份再加一就是 200 的位數
log 0.0005=-3.301029995664 →0.0005 小數點後有三個零,log 0.0005 的整數部份式-3

在 IEEE 754 編碼中,實數,R,可以寫成:

R = (-1)sign×significand×2exponent

但是符號部份在 5F2A 處已經使之變成正數,所以

log |R| = log ( significand×2exponent )
        = log significand + log 2exponent
        = log significand + exponent×log 2

而 exponent 其實還必須減去基準值,16383,所以還得再減去 16383×log 2,亦即再減去 1343.12F4H。這段求出位數或是小數點後有幾個零的程式碼在 5FF5 到 600F 之間,而最後 DI 就存有暫時實數的位數,或小數點之後有幾個零。

-D 5F6A 5F7D
5F60:                         -      66 FC FF FF FF FF          ..f|....
5F70:  FF FF FE 3F CD CC CC CC-CC CC CC CC FB 3F        ..~?MLLLLLLL{?>.
-U 5F7E 60CA [Enter]
0EE4:5F7E BE0A6D         MOV     SI,6D0A →若為零的暫時實數,會跳到此處繼續執行
0EE4:5F81 C7040130       MOV     Word Ptr [SI],3001
0EE4:5F85 33C0           XOR     AX,AX →使 AX=1,DX=0,並在位址 6D0A 處填上 01 30
0EE4:5F87 8BD0           MOV     DX,AX   ( 01 表示一個位元組,30H 的 ASCII 碼是『0
0EE4:5F89 40             INC     AX      』) 後返回。AX=1 是返回值,表示為一數值
0EE4:5F8A B320           MOV     BL,20                         ;' '
0EE4:5F8C 07             POP     ES    →BL=0,表示會印出『+』號
0EE4:5F8D C3             RET
0EE4:5F8E 0BD2           OR      DX,DX
0EE4:5F90 750B           JNZ     5F9D
0EE4:5F92 80E10F         AND     CL,0F
0EE4:5F95 740B           JZ      5FA2
0EE4:5F97 81F908FF       CMP     CX,FF08
0EE4:5F9B 740A           JZ      5FA7
0EE4:5F9D BE1B6D         MOV     SI,6D1B →NAN
0EE4:5FA0 EB08           JMP     5FAA
0EE4:5FA2 BE216D         MOV     SI,6D21 →無窮大 ( INF )
0EE4:5FA5 EB03           JMP     5FAA
0EE4:5FA7 BE276D         MOV     SI,6D27 →未定值 ( IND )
0EE4:5FAA 07             POP     ES
0EE4:5FAB 33C0           XOR     AX,AX
0EE4:5FAD C3             RET
0EE4:5FAE 06             PUSH    ES
0EE4:5FAF 8CD8           MOV     AX,DS
0EE4:5FB1 8EC0           MOV     ES,AX
0EE4:5FB3 FC             CLD
0EE4:5FB4 BFFE6C         MOV     DI,6CFE
0EE4:5FB7 57             PUSH    DI
0EE4:5FB8 B90400         MOV     CX,0004
0EE4:5FBB F3             REPZ
0EE4:5FBC A5             MOVSW
0EE4:5FBD 5E             POP     SI
0EE4:5FBE 8B4C06         MOV     CX,[SI+06]
0EE4:5FC1 8064077F       AND     Byte Ptr [SI+07],7F
0EE4:5FC5 B320           MOV     BL,20                         ;' '
0EE4:5FC7 8B04           MOV     AX,[SI]
0EE4:5FC9 0B4402         OR      AX,[SI+02]
0EE4:5FCC 0B4404         OR      AX,[SI+04]
0EE4:5FCF 8BD0           MOV     DX,AX
0EE4:5FD1 0B4406         OR      AX,[SI+06]
0EE4:5FD4 74A8           JZ      5F7E
0EE4:5FD6 0BC9           OR      CX,CX
0EE4:5FD8 7902           JNS     5FDC
0EE4:5FDA B32D           MOV     BL,2D                         ;'-'
0EE4:5FDC F7D1           NOT     CX
0EE4:5FDE F7C1F07F       TEST    CX,7FF0
0EE4:5FE2 F7D1           NOT     CX
0EE4:5FE4 74A8           JZ      5F8E
0EE4:5FE6 53             PUSH    BX
0EE4:5FE7 E8D9FE         CALL    5EC3
0EE4:5FEA 33FF           XOR     DI,DI   →一般實數轉換成 ASCII 字串開始處
0EE4:5FEC BE056D         MOV     SI,6D05 →指向有效數最高位元組
0EE4:5FEF AC             LODSB
0EE4:5FF0 8AF8           MOV     BH,AL
0EE4:5FF2 AD             LODSW
0EE4:5FF3 8BC8           MOV     CX,AX   →CX=指數
0EE4:5FF5 BA104D         MOV     DX,4D10
0EE4:5FF8 F7E2           MUL     DX
0EE4:5FFA 91             XCHG    AX,CX
0EE4:5FFB B04D           MOV     AL,4D                         ;'M'
0EE4:5FFD F6E4           MUL     AH
0EE4:5FFF 03C8           ADD     CX,AX
0EE4:6001 13D7           ADC     DX,DI
0EE4:6003 B09A           MOV     AL,9A
0EE4:6005 F6E7           MUL     BH
0EE4:6007 03C8           ADD     CX,AX
0EE4:6009 13FA           ADC     DI,DX
0EE4:600B 81E9F412       SUB     CX,12F4
0EE4:600F 81DF4313       SBB     DI,1343
0EE4:6013 57             PUSH    DI
0EE4:6014 F7DF           NEG     DI
0EE4:6016 BEFE6C         MOV     SI,6CFE →指向 IEEE 754 暫時實數位址
0EE4:6019 C706506D0200   MOV     Word Ptr [6D50],0002
0EE4:601F 4E             DEC     SI
0EE4:6020 4E             DEC     SI
0EE4:6021 E8B804         CALL    64DC
0EE4:6024 46             INC     SI
0EE4:6025 46             INC     SI
0EE4:6026 BB6A5F         MOV     BX,5F6A
0EE4:6029 5F             POP     DI
0EE4:602A E87BFE         CALL    5EA8
0EE4:602D 720F           JB      603E
0EE4:602F 47             INC     DI
0EE4:6030 57             PUSH    DI
0EE4:6031 BB745F         MOV     BX,5F74
0EE4:6034 4E             DEC     SI
0EE4:6035 4E             DEC     SI
0EE4:6036 4B             DEC     BX
0EE4:6037 4B             DEC     BX
0EE4:6038 E89000         CALL    60CB
0EE4:603B 46             INC     SI
0EE4:603C 46             INC     SI
0EE4:603D 5F             POP     DI
0EE4:603E 57             PUSH    DI
0EE4:603F AD             LODSW
0EE4:6040 97             XCHG    AX,DI
0EE4:6041 AD             LODSW
0EE4:6042 95             XCHG    AX,BP
0EE4:6043 AD             LODSW
0EE4:6044 92             XCHG    AX,DX
0EE4:6045 AD             LODSW
0EE4:6046 93             XCHG    AX,BX
0EE4:6047 AD             LODSW
0EE4:6048 91             XCHG    AX,CX
0EE4:6049 81E9FE3F       SUB     CX,3FFE
0EE4:604D F7D9           NEG     CX
0EE4:604F 8BF7           MOV     SI,DI
0EE4:6051 33C0           XOR     AX,AX
0EE4:6053 E30C           JCXZ    6061
0EE4:6055 D1EB           SHR     BX,1
0EE4:6057 D1DA           RCR     DX,1
0EE4:6059 D1DD           RCR     BP,1
0EE4:605B D1DE           RCR     SI,1
0EE4:605D D0DC           RCR     AH,1
0EE4:605F E2F4           LOOP    6055
0EE4:6061 81D69A03       ADC     SI,039A
0EE4:6065 13E9           ADC     BP,CX
0EE4:6067 13D1           ADC     DX,CX
0EE4:6069 13D9           ADC     BX,CX
0EE4:606B BF0B6D         MOV     DI,6D0B
0EE4:606E B91000         MOV     CX,0010
0EE4:6071 32C0           XOR     AL,AL
0EE4:6073 51             PUSH    CX
0EE4:6074 53             PUSH    BX
0EE4:6075 52             PUSH    DX
0EE4:6076 55             PUSH    BP
0EE4:6077 56             PUSH    SI
0EE4:6078 50             PUSH    AX
0EE4:6079 D0E4           SHL     AH,1
0EE4:607B D1D6           RCL     SI,1
0EE4:607D D1D5           RCL     BP,1
0EE4:607F D1D2           RCL     DX,1
0EE4:6081 D1D3           RCL     BX,1
0EE4:6083 D0D0           RCL     AL,1
0EE4:6085 D0E4           SHL     AH,1
0EE4:6087 D1D6           RCL     SI,1
0EE4:6089 D1D5           RCL     BP,1
0EE4:608B D1D2           RCL     DX,1
0EE4:608D D1D3           RCL     BX,1
0EE4:608F D0D0           RCL     AL,1
0EE4:6091 59             POP     CX
0EE4:6092 12E5           ADC     AH,CH
0EE4:6094 59             POP     CX
0EE4:6095 13F1           ADC     SI,CX
0EE4:6097 59             POP     CX
0EE4:6098 13E9           ADC     BP,CX
0EE4:609A 59             POP     CX
0EE4:609B 13D1           ADC     DX,CX
0EE4:609D 59             POP     CX
0EE4:609E 13D9           ADC     BX,CX
0EE4:60A0 1400           ADC     AL,00
0EE4:60A2 D0E4           SHL     AH,1
0EE4:60A4 D1D6           RCL     SI,1
0EE4:60A6 D1D5           RCL     BP,1
0EE4:60A8 D1D2           RCL     DX,1
0EE4:60AA D1D3           RCL     BX,1
0EE4:60AC D0D0           RCL     AL,1
0EE4:60AE 59             POP     CX
0EE4:60AF 0430           ADD     AL,30                         ;'0' 
0EE4:60B1 AA             STOSB
0EE4:60B2 E2BD           LOOP    6071
0EE4:60B4 4F             DEC     DI
0EE4:60B5 49             DEC     CX
0EE4:60B6 B030           MOV     AL,30                         ;'0' 
0EE4:60B8 FD             STD
0EE4:60B9 F3             REPZ
0EE4:60BA AE             SCASB
0EE4:60BB FC             CLD
0EE4:60BC 83C112         ADD     CX,+12
0EE4:60BF BE0A6D         MOV     SI,6D0A
0EE4:60C2 880C           MOV     [SI],CL
0EE4:60C4 B80100         MOV     AX,0001
0EE4:60C7 5A             POP     DX
0EE4:60C8 5B             POP     BX
0EE4:60C9 07             POP     ES
0EE4:60CA C3             RET
-U 64DC 6514
0EE4:64DC BBC461         MOV    BX,61C4
0EE4:64DF 0BFF           OR     DI,DI  
0EE4:64E1 7905           JNS    64E8   
0EE4:64E3 BB2063         MOV    BX,6320
0EE4:64E6 F7DF           NEG    DI     
0EE4:64E8 57             PUSH   DI     
0EE4:64E9 5F             POP    DI     
0EE4:64EA 83C354         ADD    BX,+54 
0EE4:64ED 0BFF           OR     DI,DI  
0EE4:64EF 7423           JZ     6514   
0EE4:64F1 8BC7           MOV    AX,DI  
0EE4:64F3 D1EF           SHR    DI,1   
0EE4:64F5 D1EF           SHR    DI,1   
0EE4:64F7 D1EF           SHR    DI,1   
0EE4:64F9 57             PUSH   DI     
0EE4:64FA 250700         AND    AX,0007
0EE4:64FD 74EA           JZ     64E9   
0EE4:64FF 53             PUSH   BX     
0EE4:6500 D0E0           SHL    AL,1   
0EE4:6502 D0E0           SHL    AL,1   
0EE4:6504 8AE0           MOV    AH,AL  
0EE4:6506 D0E0           SHL    AL,1   
0EE4:6508 02C4           ADD    AL,AH  
0EE4:650A 32E4           XOR    AH,AH  
0EE4:650C 03D8           ADD    BX,AX  
0EE4:650E E8BAFB         CALL   60CB   
0EE4:6511 5B             POP    BX     
0EE4:6512 EBD5           JMP    64E9   
0EE4:6514 C3             RET
-u 5f7e
5F7E:   MOV    SI,6D0A ;6D0A存放已轉換好的字串,第零位元組為字串長度
5F81:   MOV    Word Ptr [SI],3001 
5F85:   XOR    AX,AX 
5F87:   MOV    DX,AX 
5F89:   INC    AX 
5F8A:   MOV    BL,20                         ;' ' 
5F8C:   POP    ES 
5F8D:   RET 
5F8E:   OR     DX,DX 
5F90:   JNZ    5F9D 
5F92:   AND    CL,0F 
5F95:   JZ     5FA2 
5F97:   CMP    CX,FF08 
5F9B:   JZ     5FA7 
5F9D:   MOV    SI,6D1B 
5FA0:   JMP    5FAA 
5FA2:   MOV    SI,6D21 
5FA5:   JMP    5FAA 
5FA7:   MOV    SI,6D27 
5FAA:   POP    ES 
5FAB:   XOR    AX,AX 
5FAD:   RET 
5FAE:   PUSH   ES 
5FAF:   MOV    AX,DS 
5FB1:   MOV    ES,AX 
5FB3:   CLD 
5FB4:   MOV    DI,6CFE 
5FB7:   PUSH   DI 
5FB8:   MOV    CX,0004 
5FBB:   REPZ
5FBC:   MOVSW
5FBD:   POP    SI 
5FBE:   MOV    CX,[SI+06] 
5FC1:   AND    Byte Ptr [SI+07],7F 
5FC5:   MOV    BL,20                         ;' ' 
5FC7:   MOV    AX,[SI] 
5FC9:   OR     AX,[SI+02] 
5FCC:   OR     AX,[SI+04] 
5FCF:   MOV    DX,AX 
5FD1:   OR     AX,[SI+06] 
5FD4:   JZ     5F7E 
5FD6:   OR     CX,CX 
5FD8:   JNS    5FDC 
5FDA:   MOV    BL,2D   ;'-' 
5FDC:   NOT    CX 
5FDE:   TEST   CX,7FF0 
5FE2:   NOT    CX 
5FE4:   JZ     5F8E 
5FE6:   PUSH   BX 
5FE7:   CALL   5EC3 
5FEA:   XOR    DI,DI 
5FEC:   MOV    SI,6D05 ;指向有效數最高位元組
5FEF:   LODSB 
5FF0:   MOV    BH,AL 
5FF2:   LODSW 
5FF3:   MOV    CX,AX   ;AX=CX=指數                  若ST(0)=4089.12345 ST(0)=0.001234   ST(0)=6.02E4931   ST(0)=6.02E-4932
5FF5:   MOV    DX,4D10 ;DX:AX=4D103FFBh=1292910587  DX:AX=4D10400A     DX:AX=4D103FF5   DX:AX=4D107FFE    DX:AX=4D100001
5FF8:   MUL    DX      ;(DX:AX)*19728=13427EB0h     DX:AX*DX=134702A0  DX:AX=1234B050   DX:AX=268765E0    DX:AX=4D10
5FFA:   XCHG   AX,CX   ;交換後:CX=7EB0,AX=3FFB    CX=02A0,AX=400A   CX=B0B5,AX=3FF5 CX=65E0,AX=7FFE  CX=4D10,AX=1
5FFB:   MOV    AL,4D
5FFD:   MUL    AH      ;AX*AH=3F4D*3F=12F3h=4851d   AX=1340            AX=12F3          AX=7F4D           AX=0
5FFF:   ADD    CX,AX   ;CX=91A3                     CX=15E0            CX=C343          CX=8C13           CX=4D10
6001:   ADC    DX,DI   ;DX=1342,DI=0               DX=1347,DI=0      DX=1340          DX=2687,DI=0     DX=0,DI=0
6003:   MOV    AL,9A   ;AX=129A                     AX=139A            AX=129A          AX=269A           AX=9A
6005:   MUL    BH      ;AX*FC=9798h                 AX*FF=9966         AX*A1=60DA       AX*81=4D9A        AX*E5=89C2
6007:   ADD    CX,AX   ;CX+AX=91A3+9798=1293B       15E0+9966=AF46     C343+60DA=241D CY8C13+4D9A=D9AD NC CX=D6D2 NC
6009:   ADC    DI,DX   ;DI+DX=0+1342+CY=1343        0+1347=1347        DI=1341          DI=2687           DI=0
600B:   SUB    CX,12F4 ;CX-12F4=293B-12F4=1647      CX=9C52            CX=1129          CX=C6B9 NC        CX=C3DE NC
600F:   SBB    DI,1343 ;DI=0                        DI=4               DI=FFFE(-2)      DI=1344           DI=ECBD
6013:   PUSH   DI
6014:   NEG    DI
6016:   MOV    SI,6CFE ;指向10位元組的暫時實數
6019:   MOV    Word Ptr [6D50],0002
601F:   DEC    SI
6020:   DEC    SI
6021:   CALL   64DC    ;                            呼叫64DC,64DC會呼叫60CB,在64DC
6024:   INC    SI      ;                            媟|BX=6374或63C8執行一迴圈,迴圈呼叫                 ST(0)=6.02E4931或ST(0)=
6025:   INC    SI      ;SI=6CFE                     60CB,由64DC返回時,                                  6.02E-4932,呼叫64DC後
6026:   MOV    BX,5F6A ;                            [6CFE]=81 76 4D 69 79     [6CFE]=99 DD 93 87 85       [6CFE]=79 E9 26 31 08
6029:   POP    DI      ;DI=0                        DI=4   F5 5C D1 FD 3F    DI=FFFE 5A D3 FC FB 3F              AC 1C 9A FE 3F=0.602E0
602A:   CALL   5EA8 
602D:   JB     603E    ;CY,跳到603E,BX=5F6A       CY,跳到603E,BX=5F6A
602F:   INC    DI 
6030:   PUSH   DI 
6031:   MOV    BX,5F74 
6034:   DEC    SI 
6035:   DEC    SI 
6036:   DEC    BX 
6037:   DEC    BX 
6038:   CALL   60CB 
603B:   INC    SI 
603C:   INC    SI 
603D:   POP    DI 
603E:   PUSH   DI      ;DI=0,SI=6CFE               DI=4,SI=6CFE
603F:   LODSW          ;[6CFE]與原REAL10相同        [6CFE]=81 76 4D 69 79
6040:   XCHG   AX,DI   ;                                   F5 5C D1 FD 3F
6041:   LODSW                                   ?????
6042:   XCHG   AX,BP 
6043:   LODSW 
6044:   XCHG   AX,DX 
6045:   LODSW
6046:   XCHG   AX,BX 
6047:   LODSW 
6048:   XCHG   AX,CX 
6049:   SUB    CX,3FFE 
604D:   NEG    CX 
604F:   MOV    SI,DI 
6051:   XOR    AX,AX 
6053:   JCXZ   6061 
6055:   SHR    BX,1 
6057:   RCR    DX,1 
6059:   RCR    BP,1 
605B:   RCR    SI,1 
605D:   RCR    AH,1 
605F:   LOOP   6055 
6061:   ADC    SI,039A 
6065:   ADC    BP,CX 
6067:   ADC    DX,CX 
6069:   ADC    BX,CX 
606B:   MOV    DI,6D0B 
606E:   MOV    CX,0010 
6071:   XOR    AL,AL 
6073:   PUSH   CX 
6074:   PUSH   BX 
6075:   PUSH   DX 
6076:   PUSH   BP 
6077:   PUSH   SI 
6078:   PUSH   AX 
6079:   SHL    AH,1
607B:   RCL    SI,1
607D:   RCL    BP,1
607F:   RCL    DX,1
6081:   RCL    BX,1
6083:   RCL    AL,1
6085:   SHL    AH,1
6087:   RCL    SI,1
6089:   RCL    BP,1
608B:   RCL    DX,1
608D:   RCL    BX,1
608F:   RCL    AL,1
6091:   POP    CX
6092:   ADC    AH,CH
6094:   POP    CX
6095:   ADC    SI,CX
6097:   POP    CX
6098:   ADC    BP,CX 
609A:   POP    CX 
609B:   ADC    DX,CX 
609D:   POP    CX
609E:   ADC    BX,CX
60A0:   ADC    AL,00
60A2:   SHL    AH,1
60A4:   RCL    SI,1
60A6:   RCL    BP,1
60A8:   RCL    DX,1
60AA:   RCL    BX,1
60AC:   RCL    AL,1
60AE:   POP    CX
60AF:   ADD    AL,30                         ;'0' 
60B1:   STOSB
60B2:   LOOP   6071
60B4:   DEC    DI
60B5:   DEC    CX
60B6:   MOV    AL,30                         ;'0' 
60B8:   STD
60B9:   REPZ
60BA:   SCASB
60BB:   CLD
60BC:   ADD    CX,+12 
60BF:   MOV    SI,6D0A 
60C2:   MOV    [SI],CL 
60C4:   MOV    AX,0001 
60C7:   POP    DX 
60C8:   POP    BX 
60C9:   POP    ES 
60CA:   RET