Tajemnice ATARI

Programowanie
procesora 6502
w komputerach ATARI XL/XE

BCD


Bardzo Ciekawe Dane

    Prawdziwi Programiści pogardzają liczbami BCD. Wszak komputery myślą dwójkowo, czyż to nie wystarczający argument? Kod BCD został stworzony specjalnie dla maminsynków, którzy nie mają dość rozumu, żeby się nauczyć myśleć jak komputer...

   A teraz poważnie: BCD, czyli liczby dziesiętne kodowane dwójkowo (Binary Coded Decimal), stanowią pomost, ogniwo pośrednie między wewnętrzną reprezentacją liczb w komputerze, a postacią, w jakiej zwykli śmiertelnicy wolą oglądać liczby: ciągiem cyfr dziesiętnych. Liczba wyświetlona na ekranie lub wydrukowana na papierze składa się z cyfr, będących znakami z zestawu ASCII. Dla sterownika obrazu czy dla drukarki nie ma przy tym rozróżnienia między literami, cyframi i innymi znaczkami-dziwaczkami. Każdy jest po prostu jakimś zestawem ośmiu bitów, czyli bajtem. Wyobraźmy sobie jednak, że wyświetlamy (drukujemy) TYLKO liczby dodatnie. Przyjrzyjmy się kodom potrzebnych znaków:

'0' = %00110000
'1' = %00110001
'2' = %00110010
'3' = %00110011
'4' = %00110100
'5' = %00110101
'6' = %00110110
'7' = %00110111
'8' = %00111000
'9' = %00111001

   Wyraźnie widać, że cztery starsze bity są zawsze takie same. A zatem w przypadku przechowywania w pamięci danych tego typu połowa informacji jest zupełnie zbędna. Do zidentyfikowania znaku, o którym wiemy, że jest cyfrą dziesiętną, wystarczą cztery bity. Więc może by tak umieścić po dwie cyfry w każdym bajcie? Wtedy np.

'17' = %00010111
'46' = %01000110
'92' = %10010010

itd.

Na zapisanie liczby 1993 potrzeba tylko dwóch bajtów:

'1993' = %0001100110010011

Taką konwencję zapisu liczb dziesiętnych nazywamy właśnie BCD.
W tym miejscu warto uczynić dwa ważne spostrzeżenia.

  • Nie wszystkie możliwe kombinacje czterech bitów są wykorzystane (dozwolone). Wskutek tego "gęstość zapisu" liczb w pamięci jest w tym systemie mniejsza niż przy naturalnym dla komputera, dwójkowym sposobie. Jeden bajt BCD może przechować liczbę od 0 do 99 (dwójkowo od 0 do 255).
  • Wartość liczby odczytanej z bajtu będzie różna w zależności od sposobu interpretacji. Przykład: %01000110 oznacza binarne 70, a tylko 46 w BCD; z kolei 64 zapisuje się dwójkowo jako %01000000, a w BCD jako %01100100.

    Po co tyle zamętu?

    Dane w formacie BCD są bardzo łatwe do prezentacji wizualnej (zamiany na cyfry ASCII). Wystarczy podzielić bajt na połówki, do każdej doczepić cztery bity wzięło np. z cyfry '0' i gotowe. Natomiast dane dwójkowe są trudne do prezentacji w formie dziesiętnej i najczęściej przekształca się je w celu wyświetlenia właśnie do formatu BCD, co nie jest łatwe i zajmuje masę czasu. Dlatego, o ile inne względy nie przemawiają przeciwko temu, wygodnie jest przechowywać liczby w postaci BCD. Prosta procedura wyświetlająca młodszą cyfrę BCD może wyglądać tak:
    SAVMSC EQU 88
    
    DISP_1 AND #%00001111
           ORA #'0'-32
           STA (SAVMSC),Y
           DEY
           RTS
    
       Aby nie zaciemniać algorytmu dodatkowymi zabiegami, wyświetlamy cyfrę w pobliżu początku pamięci obrazu (wektor SAVMSC). Odjęcie liczby 32 od kodu '0' jest najprostszym sposobem zamiany z ASCII na kod ANTIC-a. Przed wywołaniem procedury należy oczywiście umieścić stosowną liczbę w akumulatorze i zainicjować rejestr Y. Zmniejszenie wartości tego rejestru po wyświetleniu cyfry ma taki skutek, że kolejne wywołania procedury DISP_1 będą umieszczać następne cytry na lewo od poprzednich. Wyświetlenie całego bajtu, czyli dwóch cyfr BCD:
    DISP_2 PHA
           JSR DISP_1
           PLA
           LSR @
           LSR @
           LSR @
           LSR @
           JSR DISP_l
           RTS
    
       Pierwsze wywołanie DISP_1 wyświetla młodszą cyfrę, ale niszczy przy tym starszą, stąd konieczność przechowania bajtu na stosie. Przesunięcie bitów o cztery pozycje w prawo sprawi, że za drugim razem starsza cyfra znajdzie się na miejscu młodszej.

       Zwolennicy upraszczania zauważą zapewne, że można zamienić sekwencję JSR DISP_1, RTS na równoważny jej rozkaz JMP DISP_1. Rozkaz ten z kolei można całkowicie pominąć. Jeżeli umieści się procedurę DISP_2 bezpośrednio przed DISP_1. Komu się to jednak wydaje zbyt skomplikowane, niech lepiej zaniecha tych uproszczeń.

       Aby zadeklarować dane typu BCD (asemblery na ogół nic oferują dla nich osobnego formatu), trzeba posłużyć się prostą sztuczką. Należy zastosować tryb szesnastkowy z tym zastrzeżeniem, że używa się tylko cyfr od '0' do '9'. A więc liczbę 1956 zapiszemy jako
    YEAR   DTA A($1956)
    
       Wyświetlenie takiej liczby jest łatwe i przyjemne:
    LEN    EQU 2
    DISP_Y LDX #0
           LDY #5
    DSP    LDA YEAR,X
           JSR DISP_2
           INX
           CPX #LEN
           BCC DSP
           RTS
    
       Jak łatwo zauważyć, nie nakłada się tu żadnego ograniczenia na długość liczby: może ona zajmować dowolnie wiele kolejnych bajtów. W naszym przypadku są dwa, odlicza je rejestr X.

    Arytmetyka BCD

       Liczby dwójkowe podlegają ścisłym prawom, w myśl których

       %00001001
    + %00000001
    - %00001010

    czyli szesnastkowo: $09 + $01 = $0A (dziesiętnie: 9 + 1 = 10). W przypadku cyfr BCD dla osiągnięcia poprawnego rezultatu chciałoby się, żeby

       %00001001
    + %00000001
    = %00010000

    a więc szesnastkowo: $09 + $01 = $10 (dziesiętnie 9 + 1 = 16). Aby zmusić nasz procesor do dodawania , w tak niezwykły sposób, należy ustawić znacznik D. Spróbuj zasemblować i wykonać:
           SED
           CLC
           LDA #%1001
           ADC #%0001
           CLD
           BRK
    
    i zaobserwuj wynik. Pod wpływem znacznika D dodawanie przebiega zgodnie z regułami BCD, przepełnienie ponad 9 powoduje przeniesienie jedynki z młodszej połówki bajtu do starszej, ze starszej zaś - do znacznika C. Pamiętaj, że znacznik D ma wpływ wyłącznie na rozkazy ADC i SBC, zaś inne (jak INC, DEC) mu nic ulegają. Istnieje zdrowy zwyczaj włączania trybu dziesiętnego TYLKO na czas wykonywania operacji na liczbach BCD, natychmiast potem należy go wyłączyć (rozkazem CLD), aby zapobiec nieporozumieniom w czasie przyszłych rachunków.

    Konwersja liczby
    dwójkowej na BCD

       Często, gdy zachodzi potrzeba wyświetlenia liczby, stajemy przed koniecznością jej zamiany z postaci binarnej na BCD. Typowym przykładem jest kod błędu, zwracany przez procedury we/wy w rejestrze Y.
    BYTE   EQU $CB
    WORD   EQU $CC
           
           STY BYTE
           JSR CONVR
           LDY #5
           LDA WORD
           JSR DISP_2
           LDA WORD+1
           JSR DISP_1
    
       Przepisujemy kod błędu do komórki BYTE, procedura konwersji umieszcza kod W słowie WORD. Ponieważ liczba z zakresu 0..255 jest co najwyżej trzycyfrowa, wywołuje się najpierw wyświetlenie dwu cyfr. a z drugiego bajtu - jednej. Procedura konwersji może mieć taką postać:
    COHVR  LDA #0
           STA WORD
           STA WORD+1
           LDX #8
           SED
    CV1    ASL BYTE
           LDA WORD
           ADC WORD
           STA WORD
           ROL WORD+1
           DEX
           BNE CV1
           CLD
           RTS
    
       Zastosowana tu metoda polega na "przelewaniu bitów" z liczby binarnej do liczby w formacie BCD. Wychodzące w wyniku ASL (ten rozkaz działa ZAWSZE binarnie) bity "chwyta" się za pomocą znacznika C do tworzonego słowa WORD, gdzie kolejne mnożenia przez 2 (za pomocą dodawania w trybie BCD) nadadzą im właściwą wartość. Przykładowo, bit 7 z bajtu BYTE wejdzie w pierwszym obiegu pętli jako 1 (lub 0)do słowa WORD. W wyniku pozostałych siedmiu mnożeń będzie pomnożony przez 128.,czyli osiągnie swą wagę z bajtu BYTE. Każdy następny bit dostąpi odpowiednio mniej mnożeń, aż do bitu nr 0, który wejdzie na najniższą pozycję i tam już pozostanie.

       Inną metodą konwersji może być "przelewanie jednostek". Algorytm ten polega na zmniejszaniu liczby binarnej (np. przez DEC) z równoczesnym zwiększaniem liczby BCD (rozkazem ADC #1 w trybie dziesiętnym) aż do osiągnięcia przez tę pierwszą wartości 0. Ta metoda, prosta i łatwa do zrozumienia, jest jednak znacznie mniej efektywna od tej, jaką pokazuje procedura CONVR.

       O trybie dziesiętnym należy wiedzieć i pamiętać nawet wtedy, gdy go nie używamy. Szczególnie w procedurach obsługi przerwań warto mieć na uwadze to, że mogą być one wywoływane w dowolnej chwili także w takiej, kiedy jest ustawiony znacznik D. Dlatego każda szanująca się obsługa przerwania zaczyna się od sakramentalnego CLD.

    Poczta 6502

       Zostałem wprost zasypany odpowiedziami na pytanie postawione w poprzednim odcinku. Obie uważnie przeczytałem i jedna z nich okazała się prawidłowa. Aby ANTIC wyświetlał znaki ATASCII, bez potrzeby żadnych konwersji, należy odpowiednio przetasować generator znaków tak, by kodowi 0 odpowiadał znak serduszka, kodowi 65 - 'A' itd. Wszystkim, którzy wpadli na ten pomysł, serdecznie gratuluję.

    Napisał do mnie Pan Iks, który zastrzegł, by nie ujawniać jego nazwiska. Pan Iks bierze w obronę wszystkich niedorozwiniętych czytelników tej rubryki, obarczając mnie winą za ich kłopoty z przyswajaniem przedstawionego tu materiału. Niesprawiedliwie przemilcza przy tym fakt, że na rozwój szarych komórek (jak dowiedli uczeni) ma również wpływ zanieczyszczenie środowiska, zanik warstwy ozonowej wskutek użycia dezodorantów i lodówek, nieumiarkowane stosowanie środków odurzających, a także obciążenia dziedziczne. Pan Iks wyraził w związku ze swą urazą wolę użycia wobec mnie przemocy fizycznej. Szanowny Panie, adres Pan zna, zapraszam.

    Janusz B. Wiśniewski

  • Powrót na start | Powrót do spisu treści | Powrót na stronę główną

    Pixel 2001