全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:2848
推到 Plurk!
推到 Facebook!

Ch 13 BCD 數(1):BCD 簡介

 
jackkcg
站務副站長


發表:891
回覆:1050
積分:848
註冊:2002-03-23

發送簡訊給我
#1 引用回覆 回覆 發表時間:2002-12-13 22:28:40 IP:61.221.xxx.xxx 未訂閱
BCD 概念 前面的幾章裏,談到十六進位數及二進位數,在這一章裏,小木偶想談談 BCD 數。何謂 BCD 數呢?我們已經知道 4 個位元最多可以表示 16 個數字,若以十六進位表示可以由 00H 到 0FH,以十進位表示可以由 00D 到 15D,而 BCD 數則是只用到前面的 10 個數來表示十進位,也就是說 BCD 數中不可能有 0A、0B、0C 等英文字出現。 BCD 數的英文是 binary-coded decimal,就字面上意義即為『二進位編碼的十進位數』,意思就是以 4 個位元的方式來表示十進位,因此有許多空間是浪費掉了,但是卻可以利用我們對十進位的熟悉,很方便的編寫程式。一般而言,BCD 數可以分成兩種,聚集的 BCD 數 (packed BCD) 和非聚集的 BCD 數 (unpacked BCD)。 雖經上述說明 BCD 數的意義,但相信還是有許多人不懂,讓小木偶舉個例子吧。例如你在 DEBUG 中輸入 AX 暫存器的數值為 0908,以非聚集 BCD 數來看表示是十進位的 98,若是以聚集 BCD 數來看是表示十進位的 908﹔如果是以十六進位來看是表示十進位的 2312。這樣說你可能會被搞糊塗了,前面不是說 AX 內的數值為十六進位嗎?怎麼一下變 98 ,一下變 908,一下又變成 2312 呢?其實這完全是看程式設計師所寫的程式如何去解釋 AX 內的數值。 當程式以類似第七或第八章的方式將 AX 暫存器的數值印出來時,AX 的數值明顯是十六進位數字。但是如果程式以下面的片段列印出來,你猜猜看在螢幕上會顯示什麼數字?在 DEBUG 內,以 A 指令輸入下面程式片段:(黃色是您要輸入的字,綠色是 BEBUG 顯示的字,[Enter]是表示按下 Enter 鍵)。 C:\WINDOWS\COMMAND>debug [Enter] -a [Enter] 1C6C:0100 mov ax,908 [Enter] 1C6C:0103 mov dl,ah [Enter] 1C6C:0105 add dl,30 [Enter] 1C6C:0107 push ax [Enter] 1C6C:0108 mov ah,2 [Enter] 1C6C:010A int 21 [Enter] 1C6C:010C pop ax [Enter] 1C6C:010D mov dl,al [Enter] 1C6C:010F add dl,30 [Enter] 1C6C:0112 mov ah,2 [Enter] 1C6C:0114 int 21 [Enter] 1C6C:0116 [Enter] -g 116 [Enter] 98 雖然 AX 之值為 908,但是我們也可以把它看成是 98,而且顯示成 98,此時 AX 之數值為非聚集的 BCD 數。當然你也可以把它顯示成 908,此時 AX 數值為聚集的 BCD 數。或是像第七﹑第八章的方式顯示成 2312,這時 AX 之數值是十六進位數。所以小木偶說,在電腦中的數值是隨程式解釋而不同。 若我們用 ADD﹑SUB﹑MUL﹑DIV 等 80X86 指令集做運算,CPU 會自動的把 所用到的運算元 (數值) 當作十六進位。如果我們要把這些數值當作 BCD 數,有兩種方法可以達成,一種是另外建立新的指令專作 BCD 數的運算;另外一種方法是先把他們當作十六進位,運算後如果超過九,再做調整。而 80X86 CPU 是採用後者。 80X86 在做 BCD 數值運算時,必定用 AL 作為調整的對象,因此必定用 AL 當作其中的一個運算元。舉例來說例如想作 8 7 的非聚集 BCD 運算,最後在 AL 暫存器應該會得到 0105 這個數,小木偶用 DEBUG 來說明: C:\WINDOWS\COMMAND>debug [Enter] -a [Enter] 1C6C:0100 mov al,8 [Enter] 1C6C:0102 add al,7 [Enter] 1C6C:0104 aaa [Enter] 1C6C:0105 [Enter] -t [Enter] AX=0008 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000 DS=1C6C ES=1C6C SS=1C6C CS=1C6C IP=0102 NV UP EI PL NZ NA PO NC 1C6C:0102 0407 ADD AL,07 -t [Enter] AX=000F BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000 DS=1C6C ES=1C6C SS=1C6C CS=1C6C IP=0104 NV UP EI PL NZ NA PE NC 1C6C:0104 37 AAA -t [Enter] AX=0105 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000 DS=1C6C ES=1C6C SS=1C6C CS=1C6C IP=0105 NV UP EI PL NZ AC PO CY 1C6C:0105 0475 ADD AL,75 - 在位址 1C6C:0100 將 8 存入 AL 暫存器,下一個指令是把 7 加到 AL 暫存器內,完成後,看到 AX 暫存器是 000F(紅色字),這是理所當然的,因為 ADD 指令本來就是將這些數字當作十六進位來看。再下一行是 AAA 指令,這一個指令就是把 AL 內的十六進位數做適當的調整,使其變為非聚集的 BCD 數。AAA 這個指令會檢查 AL 是否超過 9,如果是的話就會產生進位,使 AH 為一,並使 AL 暫存器減 0AH,所以當 AAA 指令結束後,AX 之內容並非 000F 反而是 0105。 AAA 這個 80X86 的指令,就是在做完加法後將 AL 之內容調整成非聚集的 BCD 數,因其加上 3030h 很快就變成 ASCII 數字就可印在螢幕上,AAA 這三個字母是 ASCII adjust for addition 之意。假如要將其調整成聚集的 BCD 數,過程相同,只是將 AAA 指令換成 DAA 指令,其意義為decimal adjust for addition,請看下面 DEBUG 之例子。 C:\WINDOWS\COMMAND>debug [Enter] -a [Enter] 1C6C:0100 mov al,8 [Enter] 1C6C:0102 add al,7 [Enter] 1C6C:0104 daa [Enter] 1C6C:0105 [Enter] -t [Enter] AX=0008 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000 DS=1C6C ES=1C6C SS=1C6C CS=1C6C IP=0102 NV UP EI PL NZ NA PO NC 1C6C:0102 0407 ADD AL,07 -t [Enter] AX=000F BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000 DS=1C6C ES=1C6C SS=1C6C CS=1C6C IP=0104 NV UP EI PL NZ NA PE NC 1C6C:0104 27 DAA -t [Enter] AX=0015 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000 DS=1C6C ES=1C6C SS=1C6C CS=1C6C IP=0105 NV UP EI PL NZ AC PO NC 1C6C:0105 0475 ADD AL,75 其他的四則運算,也都類似,減法調整成非聚集 BCD 數的指令為 AAS(ASCII adjust for subtraction ) ,調整成聚集 BCD 數的指令為 DAS(decimal adjust for subtraction)。乘除法只有調整成非聚集 BCD 數的指令,分別是 AAM 和 AAD (ASCII adjust for multuply 和 ASCII adjust for division )。小木偶將之整理成下表: 加法 減法 乘法 除法 非聚集調整 AAA AAS AAM AAD 聚集調整 DAA DAS -------------------------------------------------------------------------------- 非聚集 BCD 數的加法 一般的計算機只能顯示約八位數的大小(一位數是指只有個位數,二位數是指個位數和十位數,三位數是指個、十和百位數,故八位數能計算至千萬位數),但現在小木偶要寫一個程式,能擴充成可以計算很大位數的加法程式,但是此處小木偶只寫五位數相加的程式。 雖然 80X86 指令只提供 AL 暫存器作為 BCD 數加法運算之用,長度僅僅 8 位元而已,但是我們可以一次只運算一位就能算出很大位數的加法,就像小學時代,國小老師教我們的直式加法運算類似。 被加數: 9 3 5 0 4 加數: 6 1 6 8 9 ---------------------------- 和: 9 3 就像上式一樣,當我們運算時,先由個位數開始相加,如果有進位則下一次做十進位數相加時,要記得加一,只要重複上述步驟,就能將運算很大數的加法。小木偶把原始程式命名為『BCD_ADD.ASM』,其內容如下: digit equ 5 ;*************************************** code segment ;03 assume cs:code,ds:code ;04 org 100h ;05 ;--------------------------------------- main proc far ;07 start: mov si,offset v1 ;08 mov cx,digit ;09 call ubcd_print ;10 印出被加數 mov dl,' ' ;11 印出『 』字 mov ah,2 int 21h mov si,offset v2 ;14 印出加數 mov cx,digit call ubcd_print ;16 mov dl,'=' ;17 印出『=』字 mov ah,2 ;18 int 21h ;19 ;開始計算 mov ax,offset v1 ;22 mov bx,offset v2 mov dx,offset sum mov cl,digit mov ch,digit call ubcd_add ;27 mov si,offset sum ;29 印出和 mov cx,digit 1 call ubcd_print ;31 mov ax,4c00h ;33 結束 int 21h ;34 main endp ;--------------------------------------- ;在螢幕上印出非聚集 BCD 數 ;37 ;輸入:SI:指向非聚集BCD數字最低位址,最大位數於高位址 ; CX:欲印出之非BCD數的位數,若CX為零則此副程式會自動尋找(要注意資料正確) ;輸出:所有暫存器均不變 ubcd_print proc near push ax ;42 保存暫存器之值 push cx push dx jcxz u_pt0 ;45 判斷是否需要自動尋找BCD最高位址 push si ;47 不須自動尋找BCD最高位址 add si,cx ;48 因為該最高位址即等於SI CX,並存於SI pop cx jmp short u_pt2 ;50 ;自動尋找 BCD 之最高位址(向高位址處找到第一個不是阿拉伯數字的位址)並存於SI u_pt0: mov cx,si ;53 欲印出之BCD數之位址存於CX u_pt1: cmp byte ptr [si],0 jb u_pt2 cmp byte ptr [si],9 ja u_pt2 inc si jmp u_pt1 ;59 ;開始印出數字,但最大位數為零時,0不印出。SI指向要印出的BCD數的某一位 u_pt2: dec cx ;62 CX之值為BCD數的最低位址減一 u_pt3: dec si ;63 若最前面位數為零則不印出 cmp si,cx ;64 檢查是否已經是該 BCD 數的最低位址了 je u_pt5 ;65 若是,則表示該 BCD 數為零 cmp byte ptr [si],0 je u_pt3 u_pt4: mov dl,[si] ;69 已經找到最大位數不是零,並開始印出 add dl,'0' mov ah,2 int 21h dec si cmp si,cx jne u_pt4 ;75 pop dx ;77 將原數值存回暫存器 pop cx pop ax inc si ret ;81 返回父程式 u_pt5: inc si ;83 想要印出的BCD數為零 jmp u_pt4 ubcd_print endp ;85 ;--------------------------------------- ;計算兩非聚集 BCD 數之加法 ;輸入:AX:被加數位址 ; BX:加數位址 ; DX:和位址 ; CH:被加數位數 ; CL:加數位數 ;輸出:於DI所指的位址列出和 ; AX﹑BX﹑CX﹑DX均被破壞 ubcd_add proc near push si ;96 保存SI﹑DI push di mov si,ax ;98 SI=被加數最低位址 mov di,dx ;99 DI=加數最低位址 cmp ch,cl ;100 比較被加數與加數那一個位數大 jae u_a0 xchg ch,cl ;102 若加數位數大,則使被加數與加數交換最低 xchg bx,si ;103 位址(SI與DI),同時也交換位數(CH﹑CL) ;開始相加時,SI指向兩較大位數的位址,CH為較大位數 u_a0: sub ch,cl sub ax,ax clc ;計算重疊部份 u_a1: mov al,[bx] ;111 取得加數中的一位 adc al,[si] ;112 加上被加數相對應的一位 aaa ;113 加法調整 inc si mov [di],al ;115 存入和內 inc bx inc di dec cl jnz u_a1 jcxz u_a3 ;120 假如被加數與加數位數相等時,CX會等於零 ;計算只有較大位數部份 u_a2: mov al,[si] ;123 adc al,0 ;124 使AL加上零,再加上進位旗標 aaa inc si mov [di],al inc di dec ch jnz u_a2 ;130 ;處理最大位數進位 u_a3: mov al,cl ;133 使AL等於零 adc al,0 ;134 使AL加上零,再加上進位旗標 mov [di],al ;135 存入和中 pop di pop si ret ubcd_add endp ;--------------------------------------- v1 db 4,0,5,3,9 ;142 被加數=93504,十進位的九萬三千五百零四 v2 db 9,8,6,1,6 ;143 加數=61689 sum db digit 1 dup (?) ;144 和 ;--------------------------------------- code ends ;*************************************** end start 先看第 142 行到第 144 行的資料部份,這一部份是存放被加數(v1) 及加數(v2) 的 BCD 數,在這裡要注意一件事,當我們書寫數字時,左邊的數字是較大位數(例如 93504 中 9表示萬位數比千位數大)。但是在電腦中表示數值的方式,一般是大位數位於高位址,也就是說 9 所在的位址要比 3 的位址高一個位元組。假如你用 DEBUG 來觀察,應該會變成像下面這樣。 H:\HomePage\SOURCE>debug bcd_add.com [Enter] -r [Enter] AX=0000 BX=0000 CX=00C0 DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000 DS=1C8E ES=1C8E SS=1C8E CS=1C8E IP=0100 NV UP EI PL NZ NA PO NC 1C8E:0100 BEB001 MOV SI,01B0 -d 1a0 L20 [Enter] 1C8E:01A0 88 05 47 FE CD 75 F3 8A-C1 14 00 88 05 5F 5E C3 ..G..u......._^. 1C8E:01B0 04 00 05 03 09 09 08 06-01 06 00 00 00 00 00 00 ................ 看到紅色的部份,萬位數是在較高位址(1C8E:01B4),是在右邊;個位數在低位址(1C8E:01B0),是在左邊,看起來跟一般的書寫方式不同,很不習慣。或許您也可以依我們平時書寫的習慣,將大位數放在左邊,也就是低位址處,在撰寫程式時,是沒什麼問題的,但是不知有沒有硬性規定不能這樣寫。 或許您會問,小木偶怎知道 1C8E:01B0 就是被加數的位址呢?原來當 DEBUG 載入程式時,CX 的大小是載入檔案之長度,而 COM 檔是從 0100H 處開始,故 CX 加上 100H 就是檔案最後,而小木偶所寫的程式加數與被加數均在檔案最後。所以一進入 DEBUG 後,小木偶立即用『r』指令看看 CX 之大小。(但是如果是由 DOS 載入程式的話,暫存器的初始值是無意義的,要特別小心,小木偶在撰寫下一章的程式時,因粗心而誤認由 DOS 載入跟 DEBUG 一樣,結果多花了兩天的工夫才找到錯誤。) 第 144 行,和的位數必須比被加數或加數的位數還要多一,這理由應該並不難想像。因為兩數相加,有可能會有進位產生,考慮兩個最大的五位數,即 99999 相加,得到的和是六位數,所以和的位數必須比被加數或加數多一。 ubcd_print 副程式 接下來,來看看程式第 37 到第 85 行的 ubcd_print 副程式,這個副程式是把非聚集的 BCD 數印在螢幕上。 不管電腦上大位數放在左邊或右邊,要把 BCD 數印出來時,最大位數 (對 BCD_ADD.COM 而言,最大位數就是萬位數) 應最先印出來,並且在最左邊。所以這個副程式把該保留的暫存器存入堆疊後,第二要做的就是找到最大位數的位址。程式第 45 行到第 59 行就是尋找最大位數。 如果 CX 不等於零,表示副程式提供了要印出 BCD 數的位數,而 SI 又提供了該數的最低位址,故該數的最大位數位址不難求出 (見第 47 行到第 50 行)。如果 CX 為零,就得由 ubcd_print 副程式負責求出,原理如下,因為非聚集 BCD 數必是由 0﹑1﹑2﹑3……9 等十個阿拉伯數字組成,所以小木偶設計由 SI 向高位址處搜尋,如果找到第一個非阿拉伯數字的位元組,顯然就是小木偶要找的。但是這種方法是有缺點的,最好還是由副程式提供要印出的位數較好。 接下來就是由最大位數開始,一位一位地把非聚集 BCD 數印在螢幕上。如果最大位數為零,就不需要印在螢幕上,第 53 行到第 59 行就是尋找不是零的最大位數。開始依次印出來,要注意的是在印出每一位數時必須加上 30H 使之成為 ASCII 碼。 ubcd_add 副程式 第 87 行到第 140 行是個副程式,這個副程式的主要工作就是把被加數的每一位數和加數的相對應位數相加,並存於 DX 暫存器所指位址的相對應位數。 也就是說首先取得被加數的個位數,再加上加數的個位數,並將結果存於和的個位數。小木偶用三個暫存器來表示被加數、加數以及和的位址,這三個暫存器分別是 AX、BX、DX,因為個位數在最低位址,故一開始運算時將這三個暫存器指向這三個 BCD 數的最低位址,待個位數相加完畢,將這三個暫存器分別加一,取得十位數再相加,重複此步驟就可以完成大部分工作。 未考慮將來最為程式庫的元件之一,故小木偶將它考慮得較複雜些。加法不一定是相同位數相加,也可能是不同位數相加。當不同位數相加時,小木偶分成三部份,重疊部份 (紫色部份,就是被加數與加數都有的部份)﹑非重疊部份 (橘色部份,就是只有較大位數有的部份) 及進位部份。請參考下面的加法: 被加數: 9 3 5 0 4 加數: 8 9 ---------------------------- 和: 9 3 5 9 3 所以 ubcd_add 副程式一開始就是先求出被加數和加數那一個位數大?小木偶先假定被加數位數大,所以如果 CH>CL 那就直接到 106 行,否則交換被加數與加數之位數與最低位址 (第 102 行﹑第 103 行),反正加法是有『交換律』的。 再來就是計算重疊部份了 (第 111 行到第 120 行),這兒有個新指令:ADC。 新的指令:ADC 在 80X86 指令集中有一個考慮進位的加法指令,就是 ADC 指令。這個指令的語法如下: ADC 目的,來源 其中目的及來源可以是暫存器﹑記憶體或是常數,但不能同時為記憶體即可,且要注意資料長度要相同。 ADD 與 ADC 都是在做加法運算時,他們兩個不同之處是 ADD 只單純將來源加到目的就好了,而 ADC 除了將來源加到目的之外,還會加上進位旗標作為最後結果存入目的中。 這兩個指令除了相加外還會設定進位旗標,因為這兩個指令都是針對十六進位設計的,所以如果相加之後的和超過或等於 100H 或 10000H 就會發生進位。而 AAA 也會設定進位旗標,只是 AAA 指令是針對十進位的運算,所以如果超過或等於 10D,就會發生進位。不管是那一種指令產生的進位狀態,當進位發生時,進位旗標都會被設定成『一』,在 DEBUG 顯示為 CY;如果沒發生進位,進位旗標就會被清除為『零』,在 DEBUG 顯示為 NC。 再回來看看程式。當個位數相加後,如果經調整後超過十,表示進位發生,第 30 行的 AAA 指令會設定進位旗標為一,若沒進位則進位旗標設為零。好,我們用 DEBUG 觀察看看。 H:\HomePage\SOURCE>debug bcd_add.com [Enter] -g 18a [Enter] 93504 61689= AX=0000 BX=01B5 CX=0005 DX=01BA SP=FFF8 BP=0000 SI=01B0 DI=01BA DS=1C8E ES=1C8E SS=1C8E CS=1C8E IP=018A NV UP EI PL ZR NA PE NC 1C8E:018A 8A07 MOV AL,[BX] DS:01B5=09 -t [Enter] AX=0009 BX=01B5 CX=0005 DX=01BA SP=FFF8 BP=0000 SI=01B0 DI=01BA DS=1C8E ES=1C8E SS=1C8E CS=1C8E IP=018C NV UP EI PL ZR NA PE NC 1C8E:018C 1204 ADC AL,[SI] DS:01B0=04 -t [Enter] AX=000D BX=01B5 CX=0005 DX=01BA SP=FFF8 BP=0000 SI=01B0 DI=01BA DS=1C8E ES=1C8E SS=1C8E CS=1C8E IP=018E NV UP EI PL NZ NA PO NC 1C8E:018E 37 AAA 因為 ADC 相加後並沒有超過 10H 故沒有進位(見紅色字)。再繼續追蹤。 -t [Enter] AX=0103 BX=01B5 CX=0005 DX=01BA SP=FFF8 BP=0000 SI=01B0 DI=01BA DS=1C8E ES=1C8E SS=1C8E CS=1C8E IP=018F NV UP EI PL NZ AC PO CY 1C8E:018F 46 INC SI 看到上面進位旗標變成『CY』了嗎?這是因為做 AAA 調整後,發生了進位。幸好 80X86 指令中的 MOV﹑DEC﹑LOOP 指令都不會影響進位旗標,故可以保存這種進位或不進位的狀態到下次加法,這也就是我們可以利用這個性質依次做個位數、十位數、百位數……加法的理由。接下來 1C8E:0143 位址的指令是把每次做完加法後的結果存入 sum 變數中。 -t [Enter] AX=0103 BX=01B5 CX=0005 DX=01BA SP=FFF8 BP=0000 SI=01B1 DI=01BA DS=1C8E ES=1C8E SS=1C8E CS=1C8E IP=0190 NV UP EI PL NZ NA PE CY 1C8E:0190 8805 MOV [DI],AL DS:01BA=00 -t [Enter] AX=0103 BX=01B5 CX=0005 DX=01BA SP=FFF8 BP=0000 SI=01B1 DI=01BA DS=1C8E ES=1C8E SS=1C8E CS=1C8E IP=0192 NV UP EI PL NZ NA PE CY 1C8E:0192 43 INC BX -t [Enter] AX=0103 BX=01B6 CX=0005 DX=01BA SP=FFF8 BP=0000 SI=01B1 DI=01BA DS=1C8E ES=1C8E SS=1C8E CS=1C8E IP=0193 NV UP EI PL NZ NA PO CY 1C8E:0193 47 INC DI -t [Enter] AX=0103 BX=01B6 CX=0005 DX=01BA SP=FFF8 BP=0000 SI=01B1 DI=01BB DS=1C8E ES=1C8E SS=1C8E CS=1C8E IP=0194 NV UP EI PL NZ NA PE CY 1C8E:0194 FEC9 DEC CL -t [Enter] AX=0103 BX=01B6 CX=0004 DX=01BA SP=FFF8 BP=0000 SI=01B1 DI=01BB DS=1C8E ES=1C8E SS=1C8E CS=1C8E IP=0196 NV UP EI PL NZ NA PO CY 1C8E:0196 75F2 JNZ 018A -t [Enter] AX=0103 BX=01B6 CX=0004 DX=01BA SP=FFF8 BP=0000 SI=01B1 DI=01BB DS=1C8E ES=1C8E SS=1C8E CS=1C8E IP=018A NV UP EI PL NZ NA PO CY 1C8E:018A 8A07 MOV AL,[BX] DS:01B6=08 -t [Enter] AX=0108 BX=01B6 CX=0004 DX=01BA SP=FFF8 BP=0000 SI=01B1 DI=01BB DS=1C8E ES=1C8E SS=1C8E CS=1C8E IP=018C NV UP EI PL NZ NA PO CY 1C8E:018C 1204 ADC AL,[SI] DS:01B1=00 -t [Enter] AX=0109 BX=01B6 CX=0004 DX=01BA SP=FFF8 BP=0000 SI=01B1 DI=01BB DS=1C8E ES=1C8E SS=1C8E CS=1C8E IP=018E NV UP EI PL NZ NA PE NC 1C8E:018E 37 AAA 注意看白色的部份,AL 是不是等於 8 加零再加上進位旗標?再注意,上述從位址 018a 到 0196 這幾行的指令在作用完之後,均不影響進位旗標。 -t AX=0309 BX=010B CX=0006 DX=003D SP=FFFE BP=0000 SI=0104 DI=0112 DS=1C8E ES=1C8E SS=1C8E CS=1C8E IP=0143 NV UP EI PL NZ NA PE NC 1C8E:0143 8805 MOV [DI],AL DS:0112=00 這次調整之後,沒有進位吧?以後都是重複性的工作了,就不再贅述。 接下來是處理非重疊部份,這一部份和重疊部份其實幾乎一樣,只是第 124 行處是加上零而已。也不再贅述。而處理進位的這一部份,小木偶也不再重複了。 新的指令:CLC 這個指令很簡單,就是將進位旗標設成零。他沒有運算元。在 BCD_ADD.ASM 程式中,它的功用就是先把進位旗標歸零,免得往後做 ADC 運算時產生錯誤。 -------------------------------------------------------------------------------- 把 ubcd_print﹑ubcd_add 加入程式庫 可以預見,在這章所撰寫的兩個副程式 ubcd_print 和 ubcd_add 是很有用的,可為許多程式所呼叫,所以小木偶將這兩個副程式獨立出來,分別稱為 UBCD_PNT.ASM 和 UBCD_ADD.ASM。 先將 BCD_ADD.ASM 中的第 36 行到第 84 行標示並拷貝起來,然後開啟記事本,貼上,並在程式最前面及最後面加上幾行,使成下面的樣子: code segment assume cs:code,ds:code public ubcd_print ;--------------------------------------- ;在螢幕上印出非聚集 BCD 數 ;37 ……………… ;中間省略 ……………… ubcd_print endp ;85 ;--------------------------------------- code ends ;*************************************** end ubcd_print 然後存成 UBCD_PNT.ASM,同理也使 ubcd_add 副程式變成一可組譯的 UBCD_ADD.ASM,然後開啟 DOS 模式,執行下面步驟,使這兩個副程式加入第十章所建立的程式庫: H:\HomePage\SOURCE>..\masm50\masm ubcd_pnt; [Enter] 組譯 UBCD_PNT.ASM Microsoft (R) Macro Assembler Version 5.00 Copyright (C) Microsoft Corp 1981-1985, 1987. All rights reserved. 51452 381924 Bytes symbol space free 0 Warning Errors 0 Severe Errors H:\HomePage\SOURCE>..\masm50\masm ubcd_add; [Enter] 組譯 UBCD_ADD.ASM Microsoft (R) Macro Assembler Version 5.00 Copyright (C) Microsoft Corp 1981-1985, 1987. All rights reserved. 51490 381886 Bytes symbol space free 0 Warning Errors 0 Severe Errors H:\HomePage\SOURCE>..\masm50\lib [Enter] 使UBCD_PNT.ASM 內的 ubcd_print 加入 MYASMLIB.LIB 程式庫 Microsoft (R) Library Manager Version 3.07 Copyright (C) Microsoft Corp 1983-1987. All rights reserved. Library name: myasmlib [Enter] Operations: ubcd_pnt [Enter] List file: [Enter] Output library: myasmlib [Enter] H:\HomePage\SOURCE>..\masm50\lib [Enter] 使UBCD_ADD.ASM 內的 ubcd_add 加入 MYASMLIB.LIB 程式庫 Microsoft (R) Library Manager Version 3.07 Copyright (C) Microsoft Corp 1983-1987. All rights reserved. Library name: myasmlib [Enter] Operations: ubcd_add [Enter] List file: myasmlib.lst [Enter] Output library: myasmlib [Enter] H:\HomePage\SOURCE>type myasmlib.lst [Enter] PRINT_BL..........pnt_bl PRINT_BX..........pnt_bx UBCD_ADD..........ubcd_add UBCD_PRINT........ubcd_pnt pnt_bl Offset: 00000010H Code and data size: 11eH PRINT_BL pnt_bx Offset: 000000a0H Code and data size: 128H PRINT_BX ubcd_pnt Offset: 00000140H Code and data size: 3bH UBCD_PRINT ubcd_add Offset: 000001e0H Code and data size: 39H 這樣小木偶就有四個副程式了。 -------------------------------------------------------------------------------- 結論 這一章裏提到 BCD 數的概念,份量算是相當得多。但是我想最重要得是您是否能用自己的話說出來何謂 BCD 數,假如您能夠的話,相信您真的懂了,假如您不能的話,建議您用 DEBUG 去追蹤看看。至於上述介紹許多新的指令其實都是末節,如果您真的已經知道 BCD 數的意義,這些指令您自然而然的就已經能記得了,即使像小木偶一樣記不太清楚,到時查查書,幾分鐘內還是能寫出來。 至於您說,小木偶不是要寫出很大位數的加法嗎?怎麼只有五位數呢?其實很簡單,假如您想計算 20 位數,您只需要把 BCD_ADD.ASM 第一行的 digit 改成 20,然後把 v1、v2 改成您要相加的兩數(要記得每一個數都要有 20 位數),再重新編譯連結即可。 發表人 - jackkcg 於 2002/12/14 10:26:35
------
**********************************************************
哈哈&兵燹
最會的2大絕招 這個不會與那個也不會 哈哈哈 粉好

Delphi K.Top的K.Top分兩個字解釋Top代表尖端的意思,希望本討論區能提供Delphi的尖端新知
K.表Knowlege 知識,就是本站的標語:Open our mind
系統時間:2024-05-05 22:52:10
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!