AVR Asembler. Konwersja liczb szesnastkowych na dziesiętne.

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

http://www.tomaszbogusz.blox.pl/

Dodaj nowy komentarz

Zawartość pola nie będzie udostępniana publicznie.