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
|