Jste zde

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

1.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
;*******

1.1.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.

2. 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 vyž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)

tj. 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 O 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 když 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.

Poznámka: 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
;*******

2.1 Shrnutí

Z výše uvedeného plyne, že algoritmus se rozpadne do tří samostatných větví. Pouze tá poslední (dělení word/byte ) má krátkou (a navíc dělenou) iterační smyčku, ale s pevným počtem průchodů. Exekuční doba není proto konstantní a je závislá na hodnotě vstupních čísel. Navíc použití tohoto algoritmu přináší více jako trojnásobné zkrácení doby výpočtu oproti klasickému řešení. Dělení word / word pro čísla se znaménkem je potřeba doplnit o znaménkovou aritmetiku.

3. 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
;*******

3.1 Shrnutí

Pro vstupní údaj > 65 535D nastane přetečení do 17 bitu, který při výstupu je v příznaku carry (C).

4. 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ů tj. 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.

Příklad výpisu programu:

;*********************************************
;  konverze bin -> BCD pro rozsah čísla do 65 535
; superrychlá -  max 84 cyklů procesoru
;  rozsah kodu (8051) - 85 byte
;*********************************************
;vstup	v re3 je udaj_H a re2 je udaj_L
;vystup	v re3 je tis_sta a re2 je des_jed
;       v acc desítky tisíc
;
;	mění reg.: Acc,re2,re3, psw
;	možnost zvolit prac registry******
re2	equ 	r2
re3	equ 	r3
;*******
bin_bc:
push B         		;odlož obsah B
mov a,re3
clr C
rrc a           	;vydělí re3 číslem 2 = posune o 1 doprava
xch a,re2       	;re2->acc, re3/2 ->re2
rrc a           	;vydělí re2 číslem 2, b0 re2->C
mov f0,C         	;bit0 z re2 schová do f0
xch a,re2       	;re2/2->re2 , re3/2->acc
clr C
rrc a           	;vydělí re3/2 číslem 2 = posune o 1 doprava
xch a,re2       	;re2/2->acc, re3/4 ->re2
rrc a           	;vydělí re2/2 číslem 2, b1 re2->Cy
push psw        	;odloží b1 re2 do Cy
xch a,re2       	;re3/4->acc, re2/4 ->re2
mov re3,a       	;re3/4 -> re3
mov b,#06H
mul ab
add a,re2      		;připočte re2/4
xch a,b        		;bajt_H ->acc , bajt_L -> b
addc a,#0H
jz correct1
xch a,r3
add a,re3     		;konečný počet tisíců
xch a,re3     		;konečný počet tisíců -> re3,bajt_H ->acc
add a,#06H
jbc acc.0,correct4	;acc byl 1
add a,#04H		;acc byl 2
correct4:
add a,b
jnc correct3		;přenos z výrazu (u_H*6 + u_L) - bod C
inc r3
add a,#06H
correct3:
mov b,a
correct1:
mov a,#25D     		;bude dělit 25D tj. 100/4
xch a,b       		;b->25D,zbytek po dělení 1000D -> acc
div ab        		;v acc stovky , v b zbytek
cjne a,#10D,correct2	;viz bod D
clr a
inc re3
correct2:
mov re2,a     		;re2 ->počet stovek
mov a,#10D    		;pro určení desítek
xch a,b       		;b->10D,zbytek po dělení 100D -> acc
pop psw       		;;obnoví b1 z re2 v Cy
rlc a
mov C,f0      		;násobí 4 a doplňuje odložené
rlc a         		;bity
div ab
swap a
orl a,b      		;BCD desítky_jednotky -> acc
xch a,re3    		;acc -> re3,konečný počet tisíců -> acc
mov b,#10D    		;pro urceni desitek tisic
div ab        		;v acc desitky tisic , v b tisice
xch a,b       		; acc <-> b
swap a        		;tisice do H_nibble
orl a,re2    		;BCD tisíce a sta -> acc
xch a,re3    		;BCD desítky_jednotky -> acc,BCD tisice a sta -> re3
mov re2,a    		;BCD desítky_jednotky -> re2
mov a,b      		;odložené des_tisíc do Acc
pop b        		;obnova b
ret
;*******

4.1. A teď trochu rozšířená funkčnost procedúry:

( doufám, že anglický komentař není prřekážkou)

;*********************************************
;  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

4.2.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í.

5.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
;*******

6.Sčí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
;*******

7. 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.

Závěr

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.

Další podrobnosti na e- adrese: brudny@atlas.cz .

Zbyhněv Brudny

DOWNLOAD & Odkazy

Hodnocení článku: