AVR Asembler. Konwersja liczb szesnastkowych na dziesiętne.
Najprostszą metodą konwersji liczb szesnastkowych na dziesiętne jest liczenie ile razy dana waga dziesiętna „zmieści się” w liczbie szesnastkowej. W języku asembler operację tę przeprowadza się najczęściej przez odejmowanie wagi dziesiętnej i liczenie ilości operacji odejmowania do momentu ustawienia flagi przeniesienia C (Carry bit).
Przykład programowania: konwersja szesnastkowych na dziesiętne
Na listingu 1 przedstawiono przykład realizacji funkcji konwersji liczb szesnastkowych o długości 16 bitów (a więc z zakresu od 0 do 65535) na dziesiętne. W celu lepszego zrozumienia zasady działania, procedura napisana jest mało oszczędnie. Oczywiście może być wykorzystana w takiej postaci jak prezentowana na listingu 1, może być również zmodyfikowana tak, aby używała mniej zasobów mikrokontrolera.
Początek przykładowego programu wykorzystującego konwersję, zawiera definicję typu użytego mikrokontrolera oraz tablicy wektorów przerwań. Następnie zadeklarowane są zmienne, które będą przechowywać poszczególne mnożniki poszczególnych wag dziesiętnych cyfry po konwersji. Noszą one nazwy bcd_1, bcd_10, bcd_100, bcd_1tys i bcd_10tys w zależności od potęgi mnożnika przechowywanej wagi dziesiętnej (tzn. bcd_1 to 100, bcd_10 to 101 itd.). Zmienne var1msb, var1lsb, var2msb i var2lsb wykorzystywane są przez funkcje arytmetyczne (dodawanie i odejmowanie 16-bitowe). Zmienne z „2” wewnątrz nazwy zadeklarowane są w obszarze rejestrów umożliwiających realizację rozkazów zawierających stałe jako argumenty wywołania. Zmienna temp to tradycyjnie już zmienna ogólnego przeznaczenia, na wszelkie tymczasowo przeprowadzane obliczenia. Nie przechowuje ona żadnych istotnych dla pracy aplikacji informacji. Do zmiennych A1 i A2 należy przed wywołaniem funkcji hex2dec wpisać liczbę do konwersji. Przykład wywołania umieszczony jest w pętli głównej programu.
Konwersja rozpoczyna się od etykiety hex2dec. Jak wspomniano wyżej, przed jej wywołaniem, należy do zmiennych A2 i A1 wpisać odpowiednio starszy i młodszy bajt liczby do konwersji. Zmienne te nie są modyfikowane w czasie pracy funkcji.
Funkcja dodawania i funkcja odejmowania oczekują argumentów w zmiennych var1msb:var1lsb oraz var2msb:var2lsb. Pierwsza para zmiennych oprócz tego, że zawiera składnik sumy lub odjemną różnicy, to również przechowywać będzie wynik operacji dodawania czy odejmowania. Istotną cechą funkcji arytmetycznych jest również ta, że nie modyfikują one wartości drugiej pary rejestrów. W związku z tym, na przykład odjemna może być raz załadowana i rozkaz ten nie musi być powtarzany. Rozkazy ldi var2lsb,low(c10tys) oraz ldi var2msb,high(c10tys) ładują dzielnik dla wagi dziesiątek tysięcy tu równoważny z odjemnikiem dla operacji odejmowania. Następnie sprawdzane jest, ile razy można odjąć wartość c10tys od liczby do konwersji aby bit przeniesienia (oznaczający, że odjemna jest mniejsza od odjemnika) nie został ustawiony. Przy każdej operacji odejmowania zwiększany jest stan licznika zawierającego mnożnika wagi dziesiętnej – w tym przypadku – dziesiątek tysięcy.
loop1_10tys:
inc bcd_10tys
rcall sub_16_16
brcc loop_10tys
Jeśli flaga przeniesienia zostanie ustawiona, oznacza to, że odjemna jest mniejsza od odjemnika. W takim przypadku ostatnio wykonana operacja odejmowania musi być cofnięta przez operację odwrotną, tj. dodanie odjemnika oraz zmniejszenie licznika wykonanych operacji. Funkcja realizująca odejmowanie nie modyfikuje wartości odjemnika. W związku z tym, że umieszczony jest on w parze zmiennych wspólnych dla zaimplementowanych funkcji dodawania i odejmowania oraz wykorzystywanych jako drugi argument funkcji dodawania, proste wykonanie dwóch rozkazów asemblera rozwiązuje problem uzupełnienia wartości:
rcall add_16_16
dec bcd_10tys
Taki sam tok postępowania obowiązuje w celu policzenia mnożnika dla tysięcy, setek i dziesiątek. Tylko w przypadku jednostek wystarczy proste przepisanie wartości młodszego bajtu do licznika jednostek po wykonaniu całości procedury konwersji od dziesiątek tysięcy do 10.
List. 1. Program konwersji 2-bajtowych liczb szesnastkowych na dziesiętne
;przykład realizacji funkcji konwersji liczby 16 bitowej
;liczby szesnastkowej na BCD
.include "8515def.inc"
;zmienne przechowujące po konwersji:
.def bcd_1 = R0 ;-jednostki
.def bcd_10 = R1 ;-dziesiątki
.def bcd_100 = R2 ;-setki
.def bcd_1tys = R3 ;-tysiące
.def bcd_10tys = R4 ;-dziesiątki tysięcy
.def var1lsb = R5 ;zmienne dla operacji arytmetycznych
.def var1msb = R6
.def var2lsb = R19
.def var2msb = R20
.def temp = R16 ;zmienna ogólnego przeznaczenia
.def A1 = R17 ;tu liczba do konwersji (młodszy bajt)
.def A2 = R18 ;- / / - (starszy bajt)
.org 0
;---------------------------------------------
;wektory obsługi przerwań
;---------------------------------------------
rjmp RESET ;po Reset
reti ;External Interrupt 0
reti ;External Interrupt 1
reti ;T/C1 Capture Event
reti ;T/C1 Compare Match A
reti ;T/C1 Compare Match B
reti ;T/C1 Overflow
reti ;T/C0 Overflow
reti ;SPI Transfer Complete
reti ;UART Rx Complete
reti ;UART Data Register Empty
reti ;UART Tx Complete
reti ;Analog Comparator
;---------------------------------------------
;program główny
;---------------------------------------------
RESET:
ldi temp,low(RAMEND) ;ustawienie wskaźnika stosu
out SPL,temp
ldi temp,high(RAMEND)
out SPH,temp
ldi A1,$A0 ;liczba EAA0 (60064 dzies.) jako parametr
ldi A2,$EA ;wywołania funkcji konwersji
rcall hex2dec ;uruchomienie konwersji
loop:
rjmp loop
;---------------------------------------------
;konwersja HEX na BCD
;wejście: liczba do konwersji w A2:A1
;zakończenie: wynik konwersji w bcd_1... bcd_10000
;---------------------------------------------
;wykonywana dla liczby 16-bitowej, to jest z zakresu
;od 0 do 65535
.equ c10tys = 10000 ;stałe (odjemniki)
.equ c1tys = 1000
.equ c100 = 100
.equ c10 = 10
hex2dec:
clr bcd_1 ;zerowanie zmiennych - liczników
clr bcd_10
clr bcd_100
clr bcd_1tys
clr bcd_10tys
mov var1lsb,A1 ;załadowanie liczby do konwersji do
;rejestrów roboczych
mov var1msb,A2
;KONWERSJA „10 TYS.”
ldi var2lsb,low(c10tys) ;załadowanie odjemnika 10 tys.
ldi var2msb,high(c10tys)
loop_10tys: ;liczenie ile razy 10 tys.zmieści się w liczbie
inc bcd_10tys ;zwiększenie licznika 10-tysięcy o 1
rcall sub_16_16 ;wywołanie funkcji odejmowania
brcc loop_10tys ;powtórka, jeśli nie było przeniesienia
rcall add_16_16 ;dodanie odjemnika aby wrócić do wartości
;sprzed działania
dec bcd_10tys ;zmniejszenie liczby 10-tysięcy
;KONWERSJA „1 TYS.”
ldi var2lsb,low(c1tys) ;załadowanie odjemnika 1 tys.
ldi var2msb,high(c1tys)
loop_1tys: ;liczenie ile razy 1 tys. zmieści się w liczbie
inc bcd_1tys ;zwiększenie licznika tysięcy o 1
rcall sub_16_16 ;wywołanie funkcji odejmowania
brcc loop_1tys ;powtórka, jeśli nie było przeniesienia
rcall add_16_16 ;dodanie odjemnika aby wrócić do wartości
;sprzed działania
dec bcd_1tys ;zmniejszenie liczby tysięcy
;KONWERSJA „100”
ldi var2lsb,c100 ;załadowanie odjemnika 100
clr var2msb
loop_100: ;liczenie ile razy 100 zmieści się w liczbie
inc bcd_100
rcall sub_16_16
brcc loop_100
rcall add_16_16
dec bcd_100
;KONWERSJA „10”
ldi var2lsb,c10 ;załadowanie odjemnika 10
clr var2msb
loop_10: ;liczenie ile razy 100 zmieści się w liczbie
inc bcd_10
rcall sub_16_16
brcc loop_10
rcall add_16_16
dec bcd_10
;KONWERSJA „1”
;pozostała reszta z operacji odejmowania to jednostki
mov bcd_1,var1lsb
ret
;---------------------------------------------
;dodawanie dwóch liczb 16-bitowych
;pierwsza liczba w var1msb:var1lsb, druga w var2msb:var2lsb
;wynik przechowywany w var1msb:var1lsb + flaga C
;---------------------------------------------
add_16_16:
add var1lsb,var2lsb ;dodanie młodszych bajtów bez przeniesienia
adc var1msb,var2msb ;dodanie starszych bajtów z przeniesieniem
ret ;powrót do wywołania z ustawioną lub nie flagą C
;---------------------------------------------
;odejmowanie dwóch liczb 16-bitowych
;pierwsza liczba w var1msb:var1lsb, druga w var2msb:var2lsb
;wynik przechowywany w var1msb:var1lsb + flaga C
;---------------------------------------------
sub_16_16:
sub var1lsb,var2lsb ;odejmowanie młodszych bajtów
sbc var1msb,var2msb ;odejmowanie starszych bajtów z przeniesieniem
ret ;powrót do wywołania z ustawioną lub nie flagą C
Jacek Bogusz
j.bogusz@easy-soft.net.pl
Dodaj nowy komentarz