Tajemnice ATARI

PROGRAMOWANIE PROCESORA 6502
W KOMPUTERACH ATARI XL/XE

    Na ile sposobów można rozumieć liczbę? Niezliczenie wiele! Może to być wysokość dźwięku, numer buta, dzień tygodnia, wielkość gwiazdy... Co tylko zapragniesz! Niestety, im więcej znaczeń, tym większy kłopot. Im więcej języków, tym trudniej się porozumieć.

ASCII

    Mówiliśmy już, że jednym z możliwych sposobów interpretacji liczby zawartej w pojedynczym bajcie pamięci jest znak pisarski, taki jak cyfra, litera, kreska, kropka, itd. W takim ujęciu mówimy o liczbach, że stanowią kody znaków, a prościej: ich numery. Trzeba pamiętać, że odbierany przez nas efekt wizualny, np. kształt litery "x". Jest szczególnym sposobem odwzorowania na ekranie liczby 120 przez procesor obrazu. Znaczna część cywilizowanego świata podporządkowała się umowie, że liczba 65 oznacza znak "A", liczba 66 - "B" itd. Cyfra "0" ma numer porządkowy 48, cyfra "1" - 49 itd. Ten standard, chyba najszerzej rozpowszechniony, nazywa się ASCII (czyta się to "aski", a nie "ask 2"!).

    Jest tych znaków 128 (kody od 0 do 127), lecz te od 0 do 31 są "niewidzialne", ponieważ służą nie do wyświetlania, lecz do wymuszania pewnych działań, jak ruch kursora na ekranie lub ustawienie zgęszczonego druku w drukarce.

    Jeżeli posługujemy się asemblerem, to zbędne jest pamiętanie kodów poszczególnych znaków. Aby wziąć do akumulatora kod litery "A", piszemy
       LDA #'A'
a QA wygeneruje identyczny rozkaz maszynowy. Jak w przypadku
       LDA #65
    Symbol 'A' jest więc traktowany tak, jak liczba. Może być stosowany wszędzie, gdzie asembler spodziewa się wartości liczbowej. Poniższa procedura służy do zamiany małych liter na duże, innym znakom nie czyni krzywdy. Domniema się, że znak został umieszczony w akumulatorze, tam też pojawia się odpowiedź.
UPPER CMP #'a' 
      BCC OK  mniejsze od a
      CMP #'z'+1
      BCS OK  większe niż z
      SEC
      SBC #'a'-'A'
OK    RTS
    Proszę zauważyć, że asembler sam oblicza wyrażenia 'a', 'z'+1, 'a'-'A'. W rozkazach programu maszynowego zostaną użyte odpowiednio liczby: 97, 123, 32, lecz o tym nie potrzeba nam wiedzieć. Zapamiętajmy: budowa wyrażeń w języku asemblera nie ma wpływu na złożoność programu wynikowego. Nie oszczędzajmy więc kosztem czytelności! Otrzymałem ostatnio list, w którym Czytelnik pyta, czy programista powinien mieć w głowie tablicę ASCII. Zapewne czytał przewrotny tekst o "Prawdziwych Programistach". To jakieś nieporozumienie. Kochani, programista powinien mieć w głowie rozum!

    Do umieszczenia w programie całych ciągów znaków służy rozkaz DTA typu C, np.
      DTA C'Tajemnice ATARI'
    Ponieważ 128 znaków nie wyczerpuje wszystkich możliwości bajtu, istnieje wiele odmian "poszerzonego ASCII" lub "ASCII 8-bitowego". To poszerzenie polega na dodaniu 128 dowolnych znaków. Tak, to nie żart. Znaki 0..127 mają obowiązek być zawsze takie same, lecz dla kodów od 128 do 255 nie ma jednolitego standardu. To jest bardzo wygodne.

    Rosjanie umieszczają tu cyrylicę, Polacy swoje ą i ę, Niemcy umlauty i basy, zwykle też zmieszczą się jakieś znaczki semigraficzne, symbole matematyczne, itd.

    Przy przenoszeniu danych między komputerami różnych systemów trzymamy się bezpiecznego 7-bitowego zestawu. Na nim też oparte są wszystkie języki programowania. Idylla. Jaka szkoda, że nasze ATARI nie używa ASCII.

ATASCII

    To bodajże największa (prócz niestandardowego złącza we/wy) krzywda wyrządzona temu komputerowi przez jego twórców. Duża niezgodność "ATari ASCII" z ASCII powoduje, że nie jest możliwe bezpośrednie przenoszenie danych między naszym ulubieńcem i resztą świata. Trudności napotyka też podłączenie typowych urządzeń zewnętrznych, np. drukarek, modemów itp.

    Niektórzy mówią o 256-znakowym zestawie ATASCII, lecz nie wierzcie im. Mamy do czynienia ze 128 różnymi znakami, a najstarszy bit daje tylko informację o kolorze - te same znaki mogą być wyświetlane w inny sposób (patrz Mapa Pamięci w tym numerze TA). Na dodatek w niektórych trybach liczba znaków jest redukowana do 64, by tym kosztem uzyskać "aż" dwa bity definiujące kolor znaku.

    Mści się potraktowanie ATARI jako komputera do zabawy. Zagospodarowano kody 0..31 dla znaków semigraficznych (byle więcej ślicznych znaczków), rolę znaków sterujących przerzucając w różne nieoczekiwane regiony, np. funkcje TAB i BACKSPACE zajmują miejsca przewidzianych standardem nawiasów "{" i "}". W codziennej pracy redakcyjnej wielokrotnie muszę przenosić pliki z ATARI do IBM-a i z powrotem. Technicznie ten problem dawno został rozwiązany. Można się posłużyć złączem RS 232 lub dyskietką (niech żyje TOMS!), lecz te ciągłe konwersje znaków doprowadzają mnie do rozpaczy.

Zestaw znaków ANTIC-a

    Jakby tych atrakcji było mało, procesor obrazu w naszym komputerze posługuje się zgoła odrębnym zestawem znaków. Muszę przyznać, że go rozumiem. W niektórych trybach każą mu wyświetlać znaki w czterech kolorach. Kod znaku ma zatem tylko 6 bitów (2 przeznaczono na numer koloru). A przecież znaki 0..63, możliwe do wyrażenia tym sposobem, nie obejmują liter! Dlatego dokonano przetasowania. Kody ANTIC-a 0..63 odpowiadają znakom 32..95 zestawu ATASCII. Znaki ATASCII 0..31 mają teraz kody 64..95, tylko znaki 96.. 127 pozostały na swoim miejscu.

    Ten pokraczny "standard" znaków bywa w literaturze zwany peek-poke, ekranowy, internal (wewnętrzny) lub iocode. Początkujący programista nie zdaje sobie sprawy z jego istnienia. Pisze
PRINT "ATARI"
i na ekranie widzi
ATARI
    To systemowe procedury wyświetlania tekstów dokonują po cichu konwersji naszego napisu. Ale próba bezpośredniego wysłania znaku na ekran zawodzi.
EKRAN   EQU $58

        LDY #2
        LDA #'A'
        STA (EKRAN),Y
    a na ekranie pojawia się coś całkiem innego. Aby otrzymać znak "A" trzeba wysłać, zgodnie z nabytą wiedzą, znak o kodzie o 32 mniejszym, czyli
        LDA #'A'-32
    Można definiować całe napisy zgodne z zestawem ANTIC-a za pomocą DTA typu D, np.
       DTA D'Tajemnice ATARI'
    Ten tekst (odmiennie niż DTA C'...') można przenieść bezpośrednio na ekran. Wielu programistów stosuje tę sztukę dla przyspieszenia i skrócenia programu, odpada bowiem konieczność konwersji. Ja jednak nie jestem zwolennikiem tej metody, gdyż w praktyce niejednokrotnie trzeba odwoływać się do systemu, na przykład podając nazwę programu (koniecznie ATASCII!) lub pobierając listę plików poleceniem DIR.

    Operowanie na zestawie znaków ATASCII daje wymierne korzyści. Więc jednak konwersja. Procedura zamieniająca znak w akumulatorze z kodu ATASCU na ANTIC (7-bitowy) mogłaby wyglądać tak:
KONWER1 CMP #32
        BCC DOD64
        CMP #96
        BCC ODE32
        RTS
DOD64   CLC
        ADC #64
        RTS
ODE32   SEC
        SBC #32
        RTS
    Nie uwzględnia ona jednak bitu koloru (musi on być w tym wypadku zerem). Po odpowiednim usprawnieniu otrzymujemy taki oto podprogram (krótki i szybki, lecz o ileż mniej zrozumiały!).
KONWER2 ASL @
        PHP
        CMP #192
        BCS OK
        SBC #63
        BCS OK
        ADC #192
OK      PLP
        ROR @
        RTS
    Niekiedy okazuje się, że taka konwersja, przy wypełnianiu znakami całego ekranu. Jest zbyt czasochłonna. Można się wówczas posłużyć 256-bajtową tablicą, którą wstępnie wypełnimy przy pomocy powyższej procedury:
        LDX #0
WYP     TXA
        JSR KONWER2
        STA TAB,X
        INX
        BNE WYP
    Tablicę należy zadeklarować jako
TAB     ORG *+256
    Odtąd można się już posługiwać taką błyskawiczną procedurą:
KONWER3 TAX
        LDA TAB,X
        RTS
    Nawiasem mówiąc, czasem i to jest zbyt wolne. Nie pozostaje nic innego, jak zmusić ANTIC, by wyświetlał dane bezpośrednio w kodach ATASCII. Jak? Wiem, ale nie powiem. A może ktoś z Was sam zgadnie? Czekam na listy.

Kody klawiatury

    Klawiatura ma swoją własną interpretację znaków i przynajmniej w tym jednym przypadku jest to w pełni usprawiedliwione. Ponieważ elektrycznie zbudowana jest ona na zasadzie matrycy krzyżujących się przewodów, naturalnie sygnał od naciśniętego klawisza niesie informację o wierszu i kolumnie, na przecięciu których znajduje się dany klawisz. Klawisze leżące blisko siebie mają podobne kody. Ich kolejność nie jest więc alfabetyczna, lecz, rzec można, topograficzna. Nie ma też żadnej, dającej się ująć w ramy algorytmu, zależności między tymi kodami, a zestawem ATASCII. Jedynym ratunkiem jest tabela konwersji, której adres znajduje się we współczesnych komputerach w słowie $79. Sposób postępowania jest doskonale opisany w bieżącym odcinku Mapy Pamięci, nie będę więc strzępił gęby po próżnicy. Warto tylko podkreślić, że korzystanie z systemowego wejścia (przez kanał otwarty dla urządzenia K:) uwalnia nas od tych zawiłych problemów: otrzymujemy bowiem gotowe znaki w standardzie ATASCII.

Znak bez znaku

    Ponieważ każdy znak można utożsamić z jego jednobajtowym kodem i z drugiej strony każdy bajt zawiera kod jakiegoś znaku. Istnieje tendencja do zamiennego stosowania nazw "znak" i "bajt". Zwłaszcza w językach wysokiego poziomu, gdzie rzadziej operuje się na bajtach, terminem "znak" określa się liczbę jednobajtową, na każdą inną liczbę mówiąc po prostu "liczba".

    To prowadzi do zabawnego paradoksu: liczby, które określamy mianem znaków, mogą mieć znak (plus, minus), lub go nie mieć. Nie jest to oczywiście cecha liczb, lecz naszego subiektywnego na nie spojrzenia. Pisałem już o tym: jeśli myślimy, że bajt zawiera liczbę od 0 do 255, to jest ona zawsze nieujemna, ale jeżeli umówić się, że ustawiony bit 7 oznacza liczbę ujemną, to mamy do czynienia z zakresem -128..127. Jaka to różnica dla komputera? Żadna. Procesor 6502 wykonuje operacje arytmetyczne w obu trybach na raz! Weźmy liczby: 30 i 140. Ich suma powinna dać 170. W dwójkowych rejestrach procesora odbędzie się to tak:

   00011110
+ 10001100
= 10101010

    Dla tych, którzy chcą to sprawdzić, przypominam technikę dodawania na papierze. Sumujemy odpowiednie cyfry poczynając od końca:

0 + 0       = 0
1 + 0       = 1
1 + 1       = 0, przenosimy 1
1 + 1 + 1 = 1, przenosimy 1
1 + 0 + 1 = 0, przenosimy 1
0 + 0 + 1 = 1
0 + 0       = 0
0 + 1       = 1

    Wynik, liczba %10101010, to rzeczywiście 170. Jeśli jednak uświadomimy sobie, że liczba 140 jest w rozumieniu ze znakiem równa -116, to mamy do czynienia z działaniem 30 + (-116) = -86. Nikogo chyba nie zdziwi, że dwójkowo ten wynik zapisuje się %10101010.

    Również porównywanie takich liczb odbywa się zwykle bez kłopotów, gdyż
LDA #30
CMP #170
    spowoduje skasowanie znacznika C (jako, że 30 < 170) i jednocześnie skasowanie znacznika N (ponieważ 30 > -86). Dla liczb bez znaku, by skoczyć "gdy mniejsze", stosujemy więc rozkaz BCC, a dla liczb ze znakiem - BMI. Nasz procesor Jest przygotowany na obie ewentualności. Trzeba jednak mieć na uwadze, że porównywanie ze znakiem daje niepoprawny rezultat w sytuacjach, w których odejmowanie tych samych liczb ustawia znacznik V. Dotyczy to porównania liczb o różnych znakach, gdy suma ich wartości bezwzględnych przekracza 127. Z tego powodu porównania ze znakiem mają bardzo ograniczone zastosowanie (głównie dla niewielkich liczb).

    Nietrudno w literaturze natknąć się na przepis własnoręcznego wykonania liczb ujemnych, np. -5 robimy tak: bierzemy liczbę 5 (%00000101), zamieniamy wszystkie bity na przeciwne (%11111010) i dodajemy jedynkę (%11111011). To właśnie jest -5. Ta metoda wydaje mi się jednak zbyt skomplikowana, przez co trudna do zapamiętania. Ja rozumuję tak: -5 to 0 - 5, czyli

   (1)00000000
-      00000101
=     11111011

    Jedynka w nawiasie oznacza dodatkowy bit, z którego można zaczerpnąć "pożyczkę" - w procesorze 6502 jest to znacznik C. Ta metoda ma dodatkową zaletę: można zwalić całą robotę na komputer. Rozkaz
        LDA #%100000000-5
umieści w akumulatorze liczbę -5. Sposób zawiłego ręcznego przekształcania liczb ma więc tylko znaczenie ciekawostkowe. Nie ma potrzeby się go uczyć.

BCD

    Kolejna stosowana w komputerze ATARI konwencja dotycząca liczb, to tryb dziesiętny. Tym się różni od pozostałych, że nie wszystkie z 256 kombinacji bitów są dozwolone. W tym ujęciu pojedynczym elementem jest połówka bajtu (cztery bity), która wyraża jedną cyfrę z zakresu 0..9. A zatem bajt zwiera liczbę dziesiętną (zapisaną dwójkowo) z zakresu 0..99. Ten temat jest jednak na tyle obszerny, że poświęcę mu więcej miejsca w którymś z następnych odcinków.


Janusz B. Wiśniewski



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

Pixel 2002