在單片機的實際應用中,除法運算是以比較常見的運算。
以MCS-51單片機為例,雖然提供了除法指令,但只能進行單字節的運算。如果要進行多字節的除法運算,就得自己設計算法。目前,許多資料上都介紹了四字節除以二字節的算法,但它們主要有以下幾點不足:
1. 只能求出商,不能求出余數;
2. 在被除數高二字節大于除數時,不能進行運算;
3. 商只有兩個字節。 例如,被除數是0FFFFFFFFH,除數是0004H時,商數應該是3FFFFFFFH,余數是0003H。
但是,用以前的算法是無法進行運算的。 在實際運用中,參與運算的數是任意的,有時需要求出余數,有時商數要求有四個字節,因此,以前的算法在實際應用中受到了很大的限制。 為了滿足實際運用中的需要,有一套新的四字節除以二字節的算法,克服了上述算法中的缺點,可以適合廣泛的實際需要。下面以MCS-51匯編語言為例進行說明。 該算法增加了兩字節的余數單元,并把被除數單元用來存放商數。運算時,首先判斷除數是否為零,若為零時,則設溢出標志為1,然后退出。若除數不為零,則采用移位相減法進行運算。
首先,把進位位和余數單元清零。再將進位位、余數單元和被除數單元按順序首尾相連,逐位進行向左循環移位,共移位32次。每移位一次,余數單元都和除數作一次減法運算,若夠減,余數單元內容更新為兩者之差,并且將被除數最末一位置為1;若不夠減,則余數單元內容保持不變,且將被除數最末一位置為0。判斷是否夠減的方法是:在作減法之前,先保存進位位,再看作完減法后的進位位。僅在作減法之前進位位為0,并且作減法之后進位位為1時判為不夠減,其余情況均視為夠減。這樣,等到全部運算結束時,商數為四個字節,存放在被除數單元中;余數為兩個字節,存放在余數單元中。
例如,被除數是0FFFFFFFFH,除數是0004H時,運行新的算法,商數是3FFFFFFFH,存放在被除數單元中,余數是0003H,存放在余數單元中。 這個算法自然、流暢,運算結果商數為四個字節,余數為兩個字節,尤其是在求除以某數的N次方時,只需連續調用N次該算法子程序就可以了,省去了繁瑣的數據轉存語句。該算法還可以依實際需要擴充為位數更高的多字節除數算法,也可以移植到其它的單片機平臺上。
本算法已在AT89C51單片機上調試通過。下面給出算法的程序代碼清單。
divdll data 20h ;定義被除數單元
divdlh data 21h
divdhl data 22h
dlvdhh data 23h
divl data 24h ;定義除數單元
divh data 25h
templ data 26h ;定義余數單元
temph data 27h
divd: push acc
push b
mov a,divdh ;判除數是否為零
orl a,divl
jnz divd0
setb ov ;除數為零,置溢出標志
pop b
pop acc
ret
divd0: mov templ,#00h ;除數不為零,進行運算
mov temph,#00h
mov b,#20h ;置循環次數
divd1:clr c ;進位位、余數單元和
mov a,divdll ;被除數單元全體逐個
rlc a ;向左循環移位
mov divdll,a
mov a,divdlh
rlc a
mov divdlh,a
mov a,divdhl
rlc a
mov divdhl,a
mov a,divdhh
rlc a
mov divdhh,a
mov a,templ
rlc a
mov templ,a
xch a,temph
rlc a
xch a,temph
mov f0,c ;保存進位位
clr c
subb a,divl ;用余數減去除數
mov r7,a
mov a,temph
subb a,divh
jc divd2
mov templ,r7 ;夠減,刷新余數單元
mov temph,a
inc divdll ;商上1
divd2: djnz b,divd1
clr ov
pop b
pop acc
ret
end
對于上述算法,其思想是正確的。但在DICE-51單片機仿真系統具體操作中還存在部分問題。下面我以DICE系列的DICE-51單片機仿真系統軟件為例,給出四除三字節的算法的程序代碼清單已在DICE-51單片機仿真系統軟件調試通過。首先,因為DICE-51單片機仿真系統軟件在做除法運算時,若產生借位,不能置位,所以要用CJNE比較指令進行置位運算。
;定義被除數單元
DIVDLL EQU 40H
DIVDLH EQU 41H
DIVDHL EQU 42H
DIVDHH EQU 43H
;定義除數單元
DIVDL EQU 44H
DIVDM EQU 45H
DIVDH EQU 46H
;定義余數單元
TEMPL EQU 50H
TEMPM EQU 51H
TEMPH EQU 52H
;定義輔助單元
FZ1 EQU 47H
FZ2 EQU 48H
DIV0: MOV A,DIVDH ;以下四行判斷除數是否為零
ORL A,DIVDM
ORL A,DIVDL
JNZ DIV1 ;如果(A)不等于0,則跳轉;否則順序執行
SETB OV ;除數為零,置溢出標志位OV為1,程序結束
SJMP LOOP1
DIV1: MOV TEMPL,#00H ;余數單元清零
MOV TEMPM,#00H
MOV TEMPH,#00H
MOV B,#20H ;置循環次數32次
DIV2: CLR C ;進位位清零
MOV A,DIVDLL ;以下三行被除數最低字節左移一位(帶進位)
RLC A
MOV DIVDLL,A
MOV A,DIVDLH ;以下三行被除數低16位高字節左移一位(帶進位)
RLC A
MOV DIVDLH,A
MOV A,DIVDHL ;以下三行被除數高16位低字節左移一位(帶進位)
RLC A
MOV DIVDHL,A
MOV A,DIVDHH ;以下三行被除數最高字節左移一位(帶進位)
RLC A
MOV DIVDHH,A
MOV A,TEMPL ;以下三行余數低字節左移一位(帶進位)
RLC A
MOV TEMPL,A
MOV A,TEMPM ;以下三行余數中間字節左移一位(帶進位)
RLC A
MOV TEMPM,A
MOV A,TEMPH ;以下三行余數高字節左移一位(帶進位)
RLC A
MOV TEMPH,A
MOV A,TEMPL ;把余數最低字節送到累加器A中
CLR C
SUBB A,DIVDL ;用余數減去除數
MOV R7,A ;低字節相減結果送R7保存
MOV A,TEMPL
CJNE A,DIVDL,LP ;通過比較運算實現因軟件設計原因相減不能借位
;而產生的借位
LP1: MOV A,DIVDM
ADDC A,#00H
MOV FZ1,A ;計算除數中間字節與低字節相減產生的進位位之和
;將結果送FZ1保存
MOV A,TEMPM
CJNE A,FZ1,LP2 ;通過比較運算實現因軟件設計原因相減不能借位
;而產生的借位
LP2: PUSH PSW ;對程序狀態字PSW壓棧保護
MOV F0,C ;將C的值送給F0保存
CLR C
MOV A,TEMPM
SUBB A,FZ1
MOV R6,A ;中間字節帶進位相減結果送R6保存
MOV C,F0
POP PSW ;程序狀態字PSW彈棧,恢復現場
MOV A,DIVDH
ADDC A,#00H
MOV FZ2,A ;計算除數高字節與中間字節相減產生的進位位之和
;將結果送FZ2保存
MOV A,TEMPH
CJNE A,FZ2,LP3 ;通過比較運算實現因軟件設計原因相減不能借位
;而產生的借位
LP3: PUSH PSW ;對程序狀態字PSW壓棧保護
MOV F0,C
CLR C
MOV A,TEMPH
SUBB A,FZ2 ;高字節帶進位相減結果送R5保存
MOV R5,A
MOV C,F0
POP PSW ;程序狀態字PSW彈棧,恢復現場
JC DIV3 ;不夠減,則跳轉到DIV3
DIV4: MOV TEMPL,R7 ;夠減,刷新余數單元
MOV TEMPM,R6
MOV TEMPH,R5
MOV R0,#DIVDLL ;夠減,將被除數最低位置1
MOV A,@R0
INC A
XCH A,@R0
DIV3: DJNZ B,DIV2 ;判斷B是否為零,若為零,循環結束;否則,跳轉繼續循環
CLR F0
LOOP1:NOP
RET
