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