Článek stažen z HW server


Nestandardní, rychlé rutiny 16bitové aritmetiky pro procesory rodiny 8051.


Rutiny pro násobení, dělení, převod bin->BCD a BCD->bin bezesporu patří k běžné výbavě knihoven matematických programů, které používají všichni programátoři. Nabízená řešení vycházejí z analýzy požadovaného matematického úkonu s přihlédnutím na plné využití instrukční sady procesoru. Myslím si, že jsou docela nestandardní a navíc velmi rychlá.


     Uložme si omezení na použití dalších registrů mimo těch, v kterých vnášíme do procedury vstupní údaje (s výjimkou pracovních: Acc a B). Dvojitá úspora (času a registrové paměti) je totiž přesně v duchu "softwarové ekologie" a její nosné myšlenky: z procesoru je možno vytěžit nejvíce, když maximálně šetříme jeho "vnitřní zdroje". Navíc v programech aplikovaná formální konvence volby přiřazení registrů odstraňuje potřebu tzv. "jalových přesunů dat" a podporuje "in-flow" zřetězení rutin podle potřeby.


Násobení word x word
Postup bude vysvětlen na násobení m16 × 16 bez znaménka.

     Protože instrukční soubor rodiny 8051 má pouze násobení byte × byte, běžně je používána rutina s postupným binárním přičítáváním násobence. Tato rutina má pro rozsah word × word celkově 16 iteračních cyklů. Celkový počet cyklů procesoru je pak větší jako 170 cyklů.
     Nabízím popis velmi rychlého řešení násobení word x word pro procesory rodiny 8051 (resp. i pro ostatní procesory vybavené 8bitovým násobením) bez použití výše uvedené časově náročné iterační smyčky.

Mějme čísla v bin tvaru v registrech ren3 (Hi byte = m1H) a ren2 (Lo byte = m1L) - pro první násobenec a v registrech ren5 (Hi byte = m2H) a ren4 (Lo byte = m2L) - pro druhý násobenec. Jejich vynásobení pak můžeme zapsat ve tvaru:

(256*ren3 + ren2)*(256*ren5 + ren4) = po úpravě dostaneme:

65536*(ren3*ren5) + 256(ren2*ren5 + ren4*ren3) + ren2*ren4
nebo podle konvence použité v popisu programu:
65536*(m1H*m2H) + 256(m1H*m2L + m2H*m1L) + m1L*m2L

      Vidíme, že potřebujeme pouze 4krát násobení byte × byte - "mul ab" a vhodný způsob sčítání. Navíc, při zvolení si vhodného postupu matematických úkonů, nebudeme potřebovat další pomocné registry (kromě dvou: Acc a B) pro odkládání mezivýsledků při výpočtu.

Výpis programu
;*********************************************
;  násobení 16b x 16b bez znaménka - výsledek 32b
; superrychlá - 54 cykly procesoru
; rozsah kodu (8051) - 50 byte
;*********************************************
; kontrola na nulu násobitele nebo násobence
;   není nutná !! (pouze pro skrácení času)
;  - max 15 cyklů procesoru, rozsah kódu - 18 byte
; dobu exekuce mul16x16 zvětší o max 9 cyklů
;*********************************************
;vstup	v ren3 je bajt_H a ren2 je bajt_L násobence  - m1
;	v ren5 je bajt_H a ren4 je bajt_L násobitele - m2
;v ren 5 je tedy m2H, v ren4 - m2L,v ren3 - m1H a v ren2 -m1L
;
;výstup	v ren5 je 4_byte a ren4 je 3_byte	    
;	v ren3 je 2_byte a ren2 je 1_byte
; v popisu označíme výsledek res po bajtech res4..res1
;v ren 5 je tedy res4, v ren4 - res3,v ren3 - res2 a v ren2 -res1
;	mění reg.: Acc,ren2,ren3,ren4,ren5,Psw
;	možnost zvolit si prac. registry
ren2	equ 	r2
ren3	equ 	r3
ren4	equ 	r4
ren5	equ 	r5
;*******
mul16x16k:
	clr a
	cjne ren5,#0,mul16x16	;kontrola m2H na 0
	cjne ren4,#0,mul16x16e	;kontrola m2L na 0
	mov ren2,a		;0 -> m1L 
	mov ren3,a		;0 -> m1H 
mul16x16e:
	cjne ren3,#0,mul16x16	;kontrola m1H na 0
	cjne ren2,#0,mul16x16	;kontrola m1L na 0
	mov ren4,a		;0 -> m2L
	mov ren5,a		;0 -> m2H 
        ret
;*******
mul16x16:
	push B
	mov a,ren5		;musí odložit m2H
	push acc
	mov a,ren2		;m1L do acc
	mov b,ren4		;m2L do B
	mul ab			;násobí m1L* m2L
	xch a,ren2		;res1 do ren2 ,m1L do acc 
	xch a,b			;do b m1L, res2 do acc
	xch a,ren5		;res2 do ren5 , m2H do acc
	mul ab          	;násobí m2H*m1L
	add a,ren5		;připočítá res2
	xch a,ren4		;res2 do ren4 , m2L do acc
	xch a,b			;m2L do b, res3 do acc 
	addc a,#0		;zohledni přenos z 2_B do 3
	mov f0,C		;zapamatuje přenos z 3_B do 4
	xch a,ren3		;res3 do ren3 , m1H do acc
	mov ren5,a		;m1H odloží do ren5			
	mul ab          	;násobí m1H*m2L
	add a,ren4		;připočítá předch. výsled v res2
	xch a,ren3		;res2 do ren3 , res3 do acc
	addc a,b		;připočítá novy mezivýsledek ku res3
	xch a,ren5		;m1H do acc , res3 do ren5
	mov b,a			;m1H do b
	clr a
	rlc a			;přenos do 4-bajtu
	mov C,f0
	addc a,#0		;přenos do 4-bajtu
	mov ren4,a		;ulozi res4 do ren4 
	pop acc			;m2H do acc 
	mul ab          	;násobí m1H*m2H
	add a,ren5		;připecte res3
	xch a,ren4		;res3 do ren4 , res4 do acc
	addc a,b
	mov ren5,a		;výsledek 4_bajt
	pop b
 	ret	
;*******
Shrnutí
Z výše uvedeného plyne, že algoritmus nemá žádnou iterační smyčku, je jednoprůchodový, tudíž exekuční doba je konstantní a nezávislá na hodnotě vstupních číslech. Navíc jeho použití přináší trojnásobné zkrácení doby výpočtu oproti klasickému řešení. Násobení word × word pro čísla se znaménkem je potřeba doplnit o znaménkovou aritmetiku.


Dělení word / word
Jako u násobení m16 × 16 postup bude vysvětlen na dělení d16/16 bez znaménka. Protože instrukční soubor rodiny 8051 má pouze dělení byte / byte, běžně je používána rutina s postupným binárním odpočítáváním dělitele. Tato rutina má pro rozsah word / word celkově 17 iteračních cyklů. Celkový počet cyklů procesoru je pak větší než 280 až 310 cyklů.

Nabízím popis poměrně rychlého řešení dělení word / word s použitím instrukcí "mul ab" a "div ab". Mějme čísla v bin tvaru v registrech DH (Hi byte ) a DL (Lo byte) - pro dělence a v registrech dih a dil - pro dělitele. Jejich vydělení pak můžeme zapsat ve tvaru:
(256*DH + DL) / (256*dih + dil)
nyní provedeme rozbor možností podle horního bytu dělitele:

A - pro dih různé od 0 dostaneme:

AA - pro DH rovno 0 je výsledek 0, protože:

(256*DH + DL) < (256*dih + dil)

AB - pro DH různé od 0 v první kroku využijme toho, že 256*dih >> dil, tedy chyba přiblížení je menší než 1*256 neboli ±1 ve vztahu k dih, pak výraz upravíme do tvaru:

(256*DH + DL) / 256*dih = (DH / dih) + (DL/256* dih)

t.j. celá část podílu má pouze dolní byte (DH / dih) s případnou odchylkou -1 a zbytek. Pro DH < dih je výsledek 0. Hodnotu podílu P vypočteme P = div (DH)(dih). Zbytek a korekci určíme odpočítáním od hodnoty dělence výsledku násobení: (256*dih + dil) * P. Jedná se o dělení w/w.
B - pro dih rovno 0 dostaneme:

BA - pro DH rovno 0 dostaneme:
div (DL)(dil)
je to dělení By/By - instrukce "div ab".

BB - pro DH různé od 0 dostaneme dělení w/B výraz můžeme přepsat do tvaru :

(256*DH + DL) / (dil)= div (DH)(dil) * 256 + div (DL)(dil).
Horní byte podílu je roven celé části div (DH)(dil), označme zb/dil - zbytek div (DH)(dil) potom dolní byte podílu vypočteme z výrazu:
(zb/dil)*256 + (DL)/(dil) = (zb*256 + DL)/ dil.

      Všimněme si, že dělíme pouze číslem v rozsahu 1 byte, a dále požadovaná operace je totožná s principem binárního dělení (Boothův algoritmus - odečítaní dělitele od zbytku po bitech a přiřazení bitového výsledku 1 k dyž je rozdíl kladný). Víme, že (zb/dil) < dil, počet opakování je 8 (dil je Byte). Všimněme si, že (2*(zb/dil) + bit)/ dil může po použití instrukce "div ab" dáti výsledek pouze 0 nebo 1 plus zbytek v registru B. Zápis řešení vidíme ve smyčce za návěštím divBB1. Pro dělitele > 81H je třeba ošetřit přetečení během kompletace nového dělence.

      Prvním úkonem podprogramu musí samozřejmě býti kontrola na dělení nulou !! Opět, zvolením si vhodné posloupnosti matematických úkonů, nebudeme potřebovat další pomocné registry (kromě obligátních: Acc a B).

Výpis programu
;*********************************************
;  dělení 16b / 16b bez znaménka 
;doba exekuce - min 7 cyklů pro div by 0
;             - w/w max 39 cyklů
;             - w/B max 143 cyklů
;             - B/B max 27 cyklů
;             - průměr  70 cyklů
; rozsah kódu (8051) - 93 byte
;*********************************************
;vstup	v DH je bajt_H a DL je bajt_L dělence
;vstup	v dih je bajt_H a dil je bajt_L dělitele
;výstup Cy =1 pro div by 0,  jinak Cy =0 a 
;  	v DH je bajt_H a DL je bajt_L   - zbytku
;  	v dih je bajt_H a dil je bajt_L - podílu
;
;	mění reg.: Acc,DH,DL,dH,dL,b,Psw
;	možnost zvolit si prac. registry*****
DH	equ r3
DL	equ r2
dih	equ r5
dil	equ r4
;*******
div16x16:
	cjne dih,#0,divAA
	cjne dil,#0,divBA
	setb C
	ret            		;divide by 0
divAA: 		
        mov a,DH
        mov b,dih
        div ab
        jnz divAB      		;výsledek ma H_bajt
        mov dih,a
        mov dil,a
        ret            		;divide finalized, result=0
divAB:   
        push b        		;odlož zbytek
        mov DH,a      		;ulož vysl_L do rp
        mov b,dil
        mul ab
        xch a,DL
        subb a,DL
        xch a,DL
        pop acc
        subb a,b
        jnc divABcont
        dec DH
        xch a,DL
        add a,dil
        xch a,DL
        addc a,dih
        clr C        
divABcont:
        xch a,DH
        mov dil,a
        mov dih,#0
        ret            		;divide finalized
divBA:   
        mov b,dil
        mov a,DH
        div ab
        mov dih,a       	;ulož H_podíl
        mov DH,b
        cjne DH,#0,divBB
        mov b,dil
        mov a,DL
        div ab
        mov dil,a
        mov DL,b
        ret
divBB:   
        mov DH,#8
divBB1:   
        mov a,DL
        rlc a
        mov DL,a
        mov a,dil
        xch a,b
        rlc a
        jc divBB2
        div ab
        rrc a
        djnz DH,divBB1
        mov a,b
        xch a,DL  		;zbytek do DL, do acc mezivysl_L 
        rlc a
        mov dil,a
        ret
divBB2: clr C
        subb a,b
        mov b,a  
        djnz DH,divBB1
        mov a,b
        xch a,DL  		;zbytek do DL, do acc mezivysl_L 
        rlc a
        mov dil,a
        ret
;*******

Převod čísla BCD - 5 nibble -> do tvaru bin word
     Opět variace na optimalizované násobeni pro procesory rodiny 8051.
Mějme číslo v BCD tvaru n5, n4n3, n2n1, pak jeho převod do binárního tvaru můžeme zapsat:

(10000D)*n5 + (1000D)*n4 +(100D)*n3 + (10D)*n2 + n1
jednotlivé půlbajty (nibbles) jsou samozřejmě v rozsahu bajtu (<0AH), převeďme konstanty do hexa tvaru :

(2710H)*n5 + (03E8H)*n4 +(64H)*n3 + (0AH)*n2 + n1 =
256*(n5*27H) + n5*10H + 256(n4*03H) + n4*0E8H + n3*64H +n2*0AH + n1 =
256*(n5*27H + 2*n4 +n4) + (n4*0E8H + n3*64H + n2*0AH + n1 + swap(n5))
kde n4*03H jsme nahradili rychlejším násobením 2 a připočtením původní hodnoty a pro n5*10H si stačí uvědomit, pro čísla < 10H jedná se o výměnu nibblů v rámci bajtu - instrukce "swap a". Vidíme, že potřebujeme pouze 4krát násobení byte × byte - "mul ab" a vhodný způsob sčítání. Také zde zvolením si vhodného postupu matematických úkonů ušetříme na dalších pomocných registrech (kromě Acc a B) pro odkládání mezivýsledků při výpočtu.

Výpis programu
;*********************************************
;  konverze BCD -> bin rozsah čísla do 99 999 (65 535) 
; superrychlá - 69 cyklu (max), 54(min)
; rozsah kodu (8051) - 72 byte
;*********************************************
;vstup	v re3 je tis_sta a re2 je des_jed
;       v acc desítky tisíc
;vystup	v re3 je udaj_H a re2 je udaj_L
;
;	mění reg.: Acc,re2,re3,psw
;	možnost zvolit prac registry******
re2	equ 	r2
re3	equ 	r3
;*******
bcd_bin:
	push B
	push acc		;desítky tisíc do stacku
	mov a,re2
	anl a,#0F0H		;desítky
	swap a
	mov b,#10D
	mul ab			;n2*0AH
	xch a,re2	
	anl a,#0FH		;jednotky
	add a,re2
	mov re2,a		;desítky a jednotky spolu bin
	mov b,#100D
	mov a,re3
	anl a,#0FH		;stovky
	mul ab			;n3*64H
	add a,re2		;připočti k průběžnému výsledku
	mov re2,a
	clr a
	addc a,b
	xch a,re3
	anl a,#0F0H		;tisíce -> acc
	swap a
	mov b,a			;tisíce -> b
	add a,acc		;acc * 2
	add a,b			;spolu acc = 3* tisíce
	add a,re3		;připočti k průběžnému výsledku
	mov re3,a
	mov a,#0E8H		;L_byte of 03E8H=1000D
	mul ab			;n4*0E8H
	add a,re2		;připočti k průběžnému výsledku
	mov re2,a
	mov a,b
	addc a,re3
	mov re3,a
	pop acc			;obnoví desítky_tisíc ze stacku
	jz converBCDbin_end
	mov b,a			;desítky_tisíc -> b
	swap a			;násobeni* 10H - L_byte čísla 2710H=10000D
	add a,re2
	mov re2,a
	clr a			;přenos C
	addc a,re3
	mov re3,a
	mov a,#27H		;H_byte čísla 2710H=10000D
	mul ab			;n5*27H
	add a,re3		;připočti k průběžnému výsledku
	mov re3,a
	orl C,b.0		;carry pro čísla > 65 535
converBCDbin_end:
	pop b
	ret
;*******
Shrnutí
Pro vstupní údaj > 65 535D nastane přetečení do 17 bitu, který při výstupu je v příznaku carry (C).


Převod čísla word ve tvaru bin -> do tvaru BCD
      Pro převody čísel větších jako 100D je všeobecně používaná iterační smyčka s odpočítáváním jednotlivých dekadických řádů (10000D,1000D a 100D), počítadlem průchodů a testem na znaménko výsledku odpočtu. Tato metoda je velmi přehledná a jednoduchá, má však tyto dvě základní nevýhody:

rychlost převodu závisí od velikosti vstupního čísla (počet průchodů smyčkou)
po ukončení smyčky pro příslušný řád dostaneme záporný výsledek, který musíme korigovat na kladný (připočtením dříve odečítaného čísla) před spuštěním převodu v dalším desetinném řádu.

     Obě nevýhody zpomaluji rychlost převodu a také zvětšují rozsah programu. Mějme číslo v bin tvaru v registrech rii (Hi byte) a ri (Lo byte) v rozsahu od 0 po 65 535D - tj. 0FFFFH. Pro převod potřebujeme zjistit počet obsažených jednotlivých řádů - to jest vydělit vstupní údaj 100D, 1000D a 10000D. Mějme na zřeteli následující skutečnosti:

A) když zjistíme počet tisíců v čísle, tak v něm máme obsažený i počet desetitisíců t.j. dostaneme číslo max 65D, z kterého desetitisíce "vyseparujeme" pomocí instrukce "div ab" pro b=10D. Počet tisíců v čísle zjistíme pomocí matematicé konverze - viz bod C.
B) instrukční sada 8051 však obsahuje pouze dělení v rozsahu 8 bitů! Stačí, když si uvědomíme, že 100 = 4*25 (a podobně 1000 = 4*250 a 10000 = 4*2500), jinak řečeno když vstupní údaj vydělíme 4 (dva binární posuvy doprava) - potom výsledek po dělení čtyřmi se nám vejde do rozsahu 1 byte (pro určení počtu stovek), který pak vydělíme 25 - s použitím instrukci div ab. Počet stovek bude v registru A. Celkový zbytek po dělení 100D rekonstruujeme v opačném pořadí - zbytek po dělení (v registru B) násobíme 4 a připočteme zbytek po dělení 4, které jsme navrhli na začátku odseku B.
C) když vydělení vstupního čísla "číslem 4" aplikujeme před určením počtu tisíců - viz odsek A, potom musíme místo číslem 1000D dělit číslem 250D = 0FAH. Vstupní údaj vydělený 4 označme rii/4 horní bajta ri/4 dolní bajt. Co můžeme zapsat ve tvaru :
(rii/4)*256D + (ri/4) = (rii/4)*(250D + 6) + (ri/4) = (rii/4)* 250D + ( (rii/4)*6 + (ri/4) )

z výše uvedeného plyne, že (rii/4) je předběžným počtem tisíců !! Zůstane nám výraz : ( (rii/4)*6 + (ri/4) ), který může mít hodnotu > 256D = 0FFH (ale vždy je menší jako 6*(3FH) + 0FFH = 279H ) Označme horní bajt výsledku výrazu u_H a horní bajt u_L můžeme pak zapsat :

( (rii/4)*6 + (ri/4) ) = u_H*256D + u_L = u_H*250D + (u_H*6 + u_L)

podle přecházejíci úvahy připočteme u_H ku (rii/4) s přihlednutím na případný přenos z výrazu (u_H*6 + u_L) a máme finální počet tisíců! Výsledek výrazu (u_H*6 + u_L) má hodnotu < 256D podle požadavku bodu A.

D) Musíme však pamatovat na specifický případ, že do výpočtu stovek vstupuje hodnota dolního bajtu vstupního údaje, která může býti > 250D (transponovaný tisíc). Podíl takého čísla dělitelem 25D (transponovaná stovka) je pak 0AH = 10D. Potom stačí inkrementovat počet tisíců a vynulovat počet stovek. Jako v programech uvedených dříve, vhodnou posloupností matematických úkonů, se vyhneme nutnosti použití dalších pomocných registrů (kromě : Acc , B pro instrukce "mul", "div" ) pro odkládání mezivýsledků při výpočtu.

Výpis programu
;*********************************************
;  konverze BCD -> bin rozsah čísla do 99 999 (65 535) 
; superrychlá - 69 cyklu (max), 54(min)
; rozsah kodu (8051) - 72 byte
;*********************************************
;vstup	v re3 je tis_sta a re2 je des_jed
;       v acc desítky tisíc
;vystup	v re3 je udaj_H a re2 je udaj_L
;
;	mění reg.: Acc,re2,re3,psw
;	možnost zvolit prac registry******
re2	equ 	r2
re3	equ 	r3
;*******
bcd_bin:
	push B
	push acc		;desítky tisíc do stacku
	mov a,re2
	anl a,#0F0H		;desítky
	swap a
	mov b,#10D
	mul ab			;n2*0AH
	xch a,re2	
	anl a,#0FH		;jednotky
	add a,re2
	mov re2,a		;desítky a jednotky spolu bin
	mov b,#100D
	mov a,re3
	anl a,#0FH		;stovky
	mul ab			;n3*64H
	add a,re2		;připočti k průběžnému výsledku
	mov re2,a
	clr a
	addc a,b
	xch a,re3
	anl a,#0F0H		;tisíce -> acc
	swap a
	mov b,a			;tisíce -> b
	add a,acc		;acc * 2
	add a,b			;spolu acc = 3* tisíce
	add a,re3		;připočti k průběžnému výsledku
	mov re3,a
	mov a,#0E8H		;L_byte of 03E8H=1000D
	mul ab			;n4*0E8H
	add a,re2		;připočti k průběžnému výsledku
	mov re2,a
	mov a,b
	addc a,re3
	mov re3,a
	pop acc			;obnoví desítky_tisíc ze stacku
	jz converBCDbin_end
	mov b,a			;desítky_tisíc -> b
	swap a			;násobeni* 10H - L_byte čísla 2710H=10000D
	add a,re2
	mov re2,a
	clr a			;přenos C
	addc a,re3
	mov re3,a
	mov a,#27H		;H_byte čísla 2710H=10000D
	mul ab			;n5*27H
	add a,re3		;připočti k průběžnému výsledku
	mov re3,a
	orl C,b.0		;carry pro čísla > 65 535
converBCDbin_end:
	pop b
	ret
;*******
A nyní trochu rozšířená funkčnost procedury:
;*********************************************
;  konverze bin -> BCD pro rozsah čísla do  to 131 071 
; superrychlá -  průměr 85 cyklů  procesoru 
;  	         max 100 cyklů  procesoru 
; velkost kodu  - 77 byte
;*********************************************
;vstup	v re3 je udaj_H a re2 je udaj_L
;       v Cy je bit b17			- v bin formatu
;vystup	v re3 je tis_sta a re2 je des_jed
;       v acc stovky_tisíc a desítky_tisíc- v BCD formatu
;
;	mění reg.: Acc,re2,re3, psw
;	možnost zvolit prac registry******
re2	equ 	r2
re3	equ 	r3
;*******
bin_bc16:
	clr C
bin_bc17:
	push B           ;store content of B
	mov a,re3
	rrc a            ;to divide re3 with 2 = to shift one right
	xch a,re2       ;re2->acc, re3/2 ->re2
	rrc a           ;to divide re2 with 2, b0 re2->C
	mov f0,C	;bit0 from re2 in f0
	xch a,re2       ;re2/2->re2 , re3/2->acc
	clr C
	rrc a           ;to divide re3/2 with 2 = to shift one right
	xch a,re2       ;re2/2->acc, re3/4 ->re2
	rrc a           ;to divide re2/2 with 2, b1 re2->C
	push psw        ;store b1 re2 in Cy
	xch a,re2       ;re3/4->acc, re2/4 ->re2
	mov re3,a       ;re3/4 -> re3
corre4:
	mov b,#06H
	mul ab
	add a,re2      ;to add re2/4
	mov re2,a      ;store result
	xch a,b        ;byte_H ->acc , byte_L -> b
	addc a,#0H
	jz corre1                       
	xch a,r3
	add a,re3     ;final count of thousands
	xch a,re3     ;final count of thousands -> re3,bajt_H ->acc
	sjmp corre4
corre1:
	mov a,#25D     ;will to divide 250 = 100/4
	xch a,b       ;b->25D,remainder of divide 1000D -> acc
	div ab        ;in acc hundreds , in b remainder
	cjne a,#10D,corre2
	clr a
	inc re3
corre2:                        
	mov re2,a     ;count of hundreds -> re2
	mov a,#10D    ;for tens
	xch a,b       ;b->10D,remainder of divide 100D -> acc
	pop psw       ;restore b1 of re2 in Cy
	rlc a
        mov C,f0      ;to multiply by 4 and to comlete
	rlc a         ;stored bits
	div ab
	swap a
	orl a,b      ;BCD tens and units -> acc
	xch a,re3    ;acc -> re3,final count of thousands -> acc
	mov b,#10D    ;for calculate tenththousands
	div ab        ;in acc tenththousands , in b thousands
	xch a,b       ; acc <-> b
	swap a        ;thousands into H_nibble
	orl a,re2    ;BCD thousands and hundreds -> acc
	xch a,re3    ;BCD tens and units -> acc,BCD thousands_hundreds -> re3
	mov re2,a    ;BCD tens and units -> re2
	mov a,b      ;stored tenththousands  into Acc
	add a,#0
	da  a        ;tenththousands into BCD
	pop b        ;rebuilt of reg b
 	ret
Shrnutí
Z výše uvedeného plyne, že 1.algoritmus nemá žádnou iterační smyčku, je jednoprůchodový a proto je extrémně rychlý. Druhý má krátkou korekční smyčku (návěští - "corre4: " s max počtem průchodů -2).


Pro úplnost připojuji banální rutiny pro sčítání a odčítání.

Sčítání word + word

;*********************************************
;  součet 16b + 16b  - result 16b
; rychlý -  8 cycklů  processoru
; kód  -  7 byte
;*********************************************
;Vstup	v ren3 je byte_H , ren2 je byte_L prvního sčítance  -m1
;   	v ren5 je byte_H , ren4 je byte_L druhého sčítance -m2
;	tj.:v ren 5 je m2H, v ren4 - m2L, ren3 - m1H , ren2 -m1L
;
;výstup	v ren5 je byte_H , ren4 je byte_L součtu	    
;v příznaku Cy může být přetečení
;	mění reg.: Acc,re4,re5, psw
;	možnost zvolit prac registry******
ren2	equ 	r2
ren3	equ 	r3
ren4	equ 	r4
ren5	equ 	r5
;*******
add16to16:
	xch a,ren4		;m2L <-> acc
	add a,ren2
	xch a,ren5		;m2H <-> result of Lbytes sum
	addc a,ren3
	xch a,ren5		;result of Lbytes sum <-> result of Hbytes sum
	xch a,ren4		;acc <-> result of Lbytes sum
 	ret	
;*******
Odčítání word - word

Analogicky jakou součet.
;*********************************************
;  rozdíl 16b + 16b  - result 16b
; rychlý -  9 cycklů  processoru
; kód  -  8 byte
;*********************************************
;Vstup:	v ren3 je byte_H , ren2 je byte_L odčítance  -m1
;   	v ren5 je byte_H , ren4 je byte_L odčítatele -m2
;	tj.:v ren 5 je m2H, v ren4 - m2L, ren3 - m1H , ren2 -m1L
;
;výstup: v ren5 je byte_H , ren4 je byte_L rozdílu	    
;v příznaku Cy může být přetečení, v příznaku OV - znaménko
;	mění reg.: Acc,re4,re5, psw
;	možnost zvolit prac registry******
ren2	equ 	r2
ren3	equ 	r3
ren4	equ 	r4
ren5	equ 	r5
;*******
sub16to16:
	xch a,ren4		;m2L <-> acc
	clr C			;necessarily
	subb a,ren2
	xch a,ren5		;m2H <-> result of Lbytes odds
	subb a,ren3
	xch a,ren5		;result of Lbytes odds <-> result of Hbytes odds
	xch a,ren4		;acc <-> result of Lbytes odds
 	ret	
;*******

O nepoužití registru pro počítadlo
     Na konci navrhneme řešení pro zdánlivě nesouvisející úlohu - převod byte do inverzního tvaru (tj.:b0 na pozici b7, b1->b6,....,b7->b0), ale bez použití pomocného počítadla. Stačí, když si uvědomíme, že bajt se skládá s osmi bitů.

Vše je jasné z výpisu programu:
;**************************************
;inverze znaku b7b6...b1b0 na b0b1..b6b7
;****************************************
; vstup - rei, vystup - rei
;	mění registry: Acc, psw a rei
;	možnost zvolit prac registr**********
rei	equ 	r3

inverze:
	mov a,#01H		;ve funkci počítadla x8  
inverze1:			;pro posuv doleva
	xch a,rei		;acc <-> rei
	rrc a
	xch a,rei		;acc <-> rei
	rlc a
	jnc inverze1
	xch a,rei		;výsledek -> rei
	ret
;*******
     Tento způsob úspory počítadla můžeme použít vždy, je-li počet opakování 8 a současně v těle smyčky máme posuv o jeden bit. Další aplikací této "finty" může býti sériový příjem (vyslání) byte.


     Algoritmy první a třetí jsou aplikovatelné nejen na procesorech rodiny 8051 ale i na ostatních procesorech (např. řada ATmega rodiny AVR od Atmelu ) vybavených instrukcí "mul8x8".
     Druhý a čtvrtý algoritmus už není tak široce použitelný - používá 8bitovou instrukci "div ab", která mimo rodiny 8051 už není tak běžně zastoupená v instrukčních sadách jiných procesorů. Zkrácení exekuční doby oproti použití klasických algoritmů je zhruba 3 až 4násobné. Princip použitý v prvních dvou procedurách ( tj. expanze z osmi bitů na 16 pro 8bitový procesor je možno také použít pro případnou expanzí ze 16 bitů na 32 bity pro vhodný 16bitový procesor.


Vytisknout stránku Uvedené zdrojové soubory v textové formě, velikost 4 kByte

Zpátky
© DH servis 2002 -