Tajemnice ATARI

PISZEMY DEMO

W poniższym artykule postaramy się zakończyć rozważania nad ANTIC'em.

Dmactl

    Układając program dla ANTIC'a nie wspomnieliśmy o najważniejszym rejestrze Dmactl ($D400) i jego cieniu Dmactls ($22F). Jest to rejestr kontroli dostępu do pamięci dla ANTIC'a. Poszczególne bity mają różne znaczenie:

   Bity 0-1 określają szerokość obrazu na ekranie:

00 - brak obrazu
01 - obraz wąski (szerokość: 192 punkty)
10 - obraz normalny (szerokość: 320 punktów)
11 - obraz szeroki (szerokość: 384 punkty)

   Bity 2-4 dotyczą PMG (Player/Missile Graphics - grafika graczy i pocisków). Grafiką tą zajmiemy się w przyszłości przy omawianiu układu GTIA.

   Bit 5 określa czy DMA dla programu ANTIC'a jest włączony (bit ustawiony) czy wyłączony (bit skasowany). DMA (Direct Memory Access) to bezpośredni dostęp do pamięci bez pośrednictwa procesora.

   Bity 6-7 pozostają niewykorzystane.

    Po włączeniu komputera Dmactls przyjmuje wartość $22 (obraz normalny, włączony DMA). Przy otwieraniu trybu graficznego lub tekstowego przez system operacyjny Dmactls ustawiane jest zawsze na tę wartość.

    Obraz szeroki umożliwia osiągnięcie 384 punktów w jednej linii. Niestety na niektórych monitorach lub telewizorach większość danych będzie niewidoczna (poza ekranem). Dlatego też producent podaje największą rozdzielczość jako 320x192, mimo że możliwe jest osiągnięcie rozdzielczości nawet 384x216.

Zestawy znaków

    W trybach graficznych dane pamięci obrazu wyświetlane są bezpośrednio na ekranie. Natomiast w trybach tekstowych bajt pamięci obrazu określa numer znaku. Wygląd znaku zawarty jest w zestawie znaków. Zestaw znaków to kilobajtowy obszar pamięci. Jego początek wskazywany jest przez rejestr Chbase ($D409) i cień Chbas ($2F4). W rejestrze tym umieszczony jest starszy bajt adresu zestawu, natomiast młodszy bajt ma zawsze wartość zero. Z powyższej informacji wynika, że zestaw musi rozpoczynać się od początku strony pamięci. Ponadto w trybach $02-$05 ANTIC'a powinien on rozpoczynać się od wielokrotności kilobajta, gdyż przy tworzeniu obrazu nie brane są pod uwagę bity 0-1 rejestru Chbase. W trybach tekstowych $06 i $07 ANTIC'a ignorowany jest bit 0 rejestru Chbase, co oznacza, że starszy bajt adresu zestawu znaków musi być liczbą parzystą.

    Wygląd każdego znaku w zestawie zajmuje osiem bajtów. Każdy znak zawiera się w matrycy 8x8 punktów. Jeden bajt definiuje osiem poziomych punktów znaku. Bit ustawiony oznacza, że punkt ma być zapalony.

    Aby odnaleźć adres danych określających wygląd znaku należy posłużyć się wzorem:
adres_zestawu + nr_znaku * 8

    UWAGA: Znaki numerowane są według IOCODE.

    Pełny zestaw zawiera dane dla 128 znaków, połówka zestawu - dane dla 64 znaków. Dlatego też w trybach $02 i $03 ANTIC-a bit 7 decyduje o inwersie znaku, a trybach $06 i $07 ANTIC-a bity 6-7 określają kolor znaku. Bity te nie biorą udziału w określaniu adresu danych definiujących znak.

    W trybie $03 ANTIC'a tworzenie znaków na ekranie jest nieco inne. Po wyświetleniu znaku o kodzie $00-$5F ANTIC dodaje dwie linie, co powoduje, że znak ma wielkość 8x10. Znaki o kodach $60-$7F tworzone są w inny sposób: ANTIC pozostawia u góry znaku dwie linie, natomiast dwa pierwsze bajty wyglądu znaku umieszczane są w dwóch ostatnich liniach. Ze względu na takie ułożenie danych na ekranie możliwe jest utworzenie czytelnego tekstu po odpowiednim zaprojektowaniu zestawu znaków. Z trybu tego korzystają różne edytory tekstów.

    Standardowo Chbas wskazuje na zestaw umieszczony w ROM'ie komputera - Charset1 o adresie $E000. Ponadto w ROM'ie znajduje się jeszcze jeden zestaw zawierający definicję znaków międzynarodowych - Charset2 o adresie $CC00.

    A teraz kilka przykładów.

Program 1

    Przykład demonstruje poszczególne fazy zmniejszania i zwiększania okręgu na przedefiniowanym znaku spacji.
Procedure Equ $8800

List_mem  Equ %00000110
List_err  Equ %00000101
Code_mem  Equ %00010000
Code_dsk  Equ %00100000

Charset1  Equ $E000
Chbas     Equ $02F4
Clrscr    Equ $F420
Crsinh    Equ $02F0
Kbcodes   Equ $02FC
Rtclok    Equ $0012

Zestaw	  Equ $9000


          Opt List_err+Code_mem

          Org Procedure

          Lda #$01
          Sta Crsinh
          Jsr Clrscr
          Lda >Zestaw 
          Sta Chbas
     
          Ldx #$00
L1        Txa 
          Pha 
          Lda Pomoc_tab,x
          Tax
          Ldy #$00
L2        Lda Znaki,x 
          Sta Zestaw,y 
          Inx
          Iny
          Cpy #$08
          Bne L2
          Ldx #$04
          Jsr Wait
          Pla
          Tax
          Inx
          Cpx #$06
          Bne L3
          Ldx #$00
L3        Lda Kbcodes
          Cmp #$FF
          Beq L1
          Rts

Znaki     Equ *
       Dta B($7E),B($81),B($81)
       Dta B($81),B($81),B($81)
       Dta B($81),B($7E)

       Dta B($00),B($3C),B($42)
       Dta B($42),B($42),B($42)
       Dta B($3C),B($00)

       Dta B($00),B($00),B($18)
       Dta B($24),B($24),B($18)
       Dta B($00),B($00)
 
       Dta B($00),B($00),B($00)
       Dta B($18),B($18),B($00)
       Dta B($00),B($00)

Pomoc_tab Equ *
       Dta B($00),B($08),B($10)
       Dta B($18),B($10),B($08)

          End of file
    Na początku wyłączamy kursor (poprzez umieszczenie wartości $01 w rejestrze Crsinh) i czyścimy ekran (procedura systemowa Clrscr). Następnie rejestr Chbas ustawiamy na wolny obszar w RAM'ie ($9000). Ponieważ będziemy modyfikować tylko znak spacji, a inne znaki nie będą widoczne, dlatego też nie ma potrzeby przepisywać z ROM'u (lub definiować) całego zestawu.

    Tablica Znaki zawiera cztery kolejne fazy zmniejszania się okręgu, natomiast Pomoc_tab zawiera kolejność faz (co ile bajtów znajduje się nowa faza w tablicy Znaki).

    Program główny to dwie pętle. Pętla wewnętrzna przepisuje osiem bajtów definiujących wygląd okręgu w odpowiednie miejsce w zestawie, druga zlicza fazy. Program można przerwać, naciskając dowolny klawisz.

    UWAGA: W przykładzie korzystamy ze znanej nam procedury Wait.

Program 2

    Przykład ten, podobny do poprzedniego przedefiniowuje sześć kolejnych znaków, przy czym każdy z nich otrzymuje mną fazę okręgu.
Procedure Equ $8800

List_mem  Equ %00000110
List_err  Equ %00000101
Code_mem  Equ %00010000
Code_dsk  Equ %00100000

Charset1  Equ $E000
Chbas     Equ $02F4
Kbcodes   Equ $02FC
Rtclok    Equ $0012
Savmsc    Equ $0058

Pomoc     Equ $00F0
Zestaw    Equ $9000

          
          Opt List_err+Code_mem
          Org Procedure

          Lda >Zestaw
          Sta Chbas
          Ldx Savmsc
          Ldy Savmsc+$01
          Stx Pomoc
          Sty Pomoc+$01
          Ldx #$00
L1        Txa
          Pha
          Ldy #$00
          Ldx #$00
L2        Txa
          Sta (Pomoc),y
          Inx
          Iny
          Cpy #$06
          Bne L2
          Clc
          Lda Pomoc
          Adc #$06
          Sta Pomoc
          Bcc L3
          Inc Pomoc+$01
L3        Pla
          Tax
          Inx
          Cpx #$A0
          Bne L1

L4        Ldy #$00
L5        Tya
          Lsr @
          Lsr @
          Lsr @
          Tax
          Lda Pomoc_tab,x
          Tax
L6        Lda Znaki,x
          Sta Zestaw,y
          Inx
          Iny
          Tya
          And #$07
          Bne L6
          Cpy #$30
          Bne L5
          Ldx #$00
          Lda Pomoc_tab
          Pha
L7        Lda Pomoc_tab+$01,x 
          Sta Pomoc_tab,x
          Inx
          Cpx #$05
          Bne L7
          Pla
          Sta Pomoc_tab+$05
 
          Ldx #$03
          Jsr Wait

          Lda Kbcodes
          Cmp #$FF
          Beq L4
          Rts

Znaki  Equ *
       Dta B($7E),B($81),B($81)
       Dta B($81),B($81),B($81)
       Dta B($81),B($7E)

       Dta B($00),B($3C),B($42)
       Dta B($42),B($42),B($42)
       Dta B($3C),B($00)

       Dta B($00),B($00),B($18)
       Dta B($24),B($24),B($18)
       Dta B($00),B($00)

       Dta B($00),B($00),B($00)
       Dta B($18),B($18),B($00)
       Dta B($00),B($00)

       Dta B($00),B($00),B($18) 
       Dta B($24),B($24),B($18) 
       Dta B($00),B($00)
 
       Dta B($00),B($3C),B($42)
       Dta B($42),B($42),B($42)
       Dta B($3C),B($00)

Pomoc_tab Equ *
       Dta B($00),B($08),B($10)
       Dta B($18),B($20),B($28)

          End of file
    Jak poprzednio, rejestr Chbas ustawiany jest na wolny obszar w RAM'ie. Następnie pętla przepisuje kolejne sześć znaków w pamięć ekranu aż do jego całkowitego zapełnienia. Od etykiety L4 zaczyna się animacja: pierwsza pętla przepisuje odpowiednio poukładane fazy (kolejność zapisana w tablicy Pomoc_tab) w pierwsze sześć znaków zestawu. Pętla druga natomiast zmienia kolejność faz. Następnie wywoływana jest procedura opóźniająca i jeśli nie został naciśnięty żaden klawisz wszystko powtarza się od nowa.

Program 3

    W kolejnym przykładzie również modyfikujemy wygląd poszczególnych znaków, jednak tym razem procedura ta może być wykorzystana w większych programach jako "sprytny" licznik. Podoby efekt obserwuje się z grach samochodowych (np. Road Race).
Procedure Equ $8800

List_mem  Equ %00000110
List_err  Equ %00000101
Code_mem  Equ %00010000
Code_dsk  Equ %00100000

Charset1  Equ $E000
Chbas     Equ $02F4
Clrscr    Equ $F420
Kbcodes   Equ $02FC 
Rtclok    Equ $0012
Savmsc    Equ $0058

Zestaw    Equ $9000


          Opt List_err+Code_mem
          Org Procedure

          Jsr Clrscr
          Ldx #$00
L1        Lda Charset1+$80,x
          Sta Znaki,x
          Cpx #$07
          Bcs L2
          Sta Znaki+$50,x
L2        Inx
          Cpx #$50
          Bne L1

          Ldy #$01 
L3        Tya
          Dey
          Sta (Savmsc),y 
          Iny
          Iny
          Cpy #$04
          Bne L3
          Tya
          Eor #$80
          Dey
          Sta (Savmsc),y

L4        Ldy #$00
          Ldx #$00 
L5        Txa
          Pha
          Lda Licznik,x
          Tax 
L6        Lda Znaki,x
          Sta Zestaw+$08,y
          Inx
          Iny
          Tya
          And #$07
          Bne L6
          Pla
          Tax
          Inx
          Cpx #$04
          Bne L5

          Ldx #$03 
L7        Inc Licznik,x
          Lda Licznik,x
          Cmp #$49
          Bcc L9
          Cmp #$50
          Bne L8
          Lda #$00
          Sta Licznik,x 
L8        Dex
          Bpl L7

L9        Ldx #$02 
          Jsr Wait

          Lda Kbcodes
          Cmp #$FF 
          Beq L4
          Rts

Licznik   Equ *
       Dta B($00),B($00),B($00)
       Dta B($00)

Znaki     Org *+$58

          End of file
    Program czyści ekran, następnie przepisuje z zestawu znaków w ROM'ie 10 znaków (wygląd cyfr). Znaki układane są w kolejności od 0 do 9 i po 9 występuje jeszcze jeden znak - zero. Następna pętla umieszcza na ekranie cztery znaki (cyfry licznika), przy czym ostatni jest w inwersie. Cztery bajty od etykiety Licznik zawierają pozycje, od których pobierane będą dane z tablicy znaków (Znaki) dla kolejnych cyfr licznika.

    Pętla od etykiety L4 wykonywana przy każdym "obrocie" licznika przepisuje dane dla czterech cyfr z tablicy znaki w zestaw.

    Następna pętla modyfikuje cztery bajty od etykiety Licznik. Jeżeli jedna z pozycji osiągnie wartość większą lub równą $49 oznacza to, że wyświetlana jest cyfra 9 na pozycji licznika określanej przez rejestr X procesora i konieczne jest przesunięcie kolejne cyfry licznika. Jeśli jedna z pozycji osiągnie wartość $50 (wskazanie licznika równe zero) jest ona zerowana. Program wykonywany jest dopóki nie zostanie naciśnięty klawisz.

Program 5

    Panoramicznia "kasza".
Procedure Equ $880

List_mem  Equ %00000110
List_err  Equ %00000101
Code_mem  Equ %00010000
Code_dsk  Equ %00100000

Charsetl  Equ $E000
Chbas     Equ $02F4
Kbcodes   Equ $02FC
Ramtop    Equ $006A
Random    Equ $D20A
Savmsc    Equ $0058

Pomoc     Equ $00F0
Zestaw    Equ $9000

          Opt List_err+Code_mem
          Org Procedure


          Lda >Zestaw
          Sta Chbas
Rob_kasze Ldx Zestaw
          Stx Pomoc
          Sty Pomoc+$01
          Ldy #$00
          Ldx #$03
L1        Lda Random
          Sta (Pomoc),y
          Iny
          Bne L1
          Inc Pomoc+$01
          Dex
          Bpl L1

          Ldx Savmsc+$01
          Stx Pomoc+$01
          Ldy Savmsc
          Lda #$00
          Sta Pomoc 
L2        Lda Random
          Sta (Pomoc),y
          Iny
          Bne L2
          Inc Pomoc+$01
          Ldx Pomoc+$01
          Cpx Ramtop
          Bne L2
          Lda Kbcodes
          Cmp #$FF
          Beq Rob_kasze
          Rts
      
          End of File
    Cały program składa się z dwóch pętli. Pierwsza wypełnia obszar pamięci przeznaczony na zestaw znaków losowymi wartościami pobieranymi z rejestru Random ($D20A). Pętla druga natomiast wypełnia ekran losowymi znakami, co tworzy żądany efekt.

Program 5

    Zadaniem poniższego programu jest stworzenie zestawu znaków l co 1/50 sekundy obracanie dwóch kolejnych znaków, pierwszy w prawo o 90 stopni, drugi w lewo. W ten sposób obracany jest cały zestaw.
Procedure Equ $880

List_mem  Equ %00000110
List_err  Equ %00000101
Code_mem  Equ %00010000
Code_dsk  Equ %00100000

Cdtma2    Equ $0228
Cdtmv2    Equ $021A
Charsetl  Equ $E000
Chbas     Equ $02F4

Pomoc     Equ $00F0
Pomoc2    Equ $00F2
Zestaw    Equ $9000

          Opt List_err+Code_mem
          Org Procedure

          Jsr Przepisz
          Lda >Zestaw
          Sta Chbas
          Ldx Zestaw
          Stx Pomoc
          Sty Pomoc+$01
          Ldx Przerw
          Stx Cdtma2
          Sty Cdtma2+$01
Koniec    Lda #$01
          Sta Cdtmv2
          Rts

O_prawo   Jsr Przep_2
          Ldy #$07 
L1        Ldx #$07 
L2        Lsr Tablica,x 
          Lda (Pomoc),y 
          Rol @
          Sta (Pomoc),y 
          Dex 
          Bpl L4 
          Dey 
          Bpl L3
          Rts

O_lewo    Jsr Przep_2
          Ldy #$07
L3        Ldx #$07
L4        Asl Tablica,x
          Lda (Pomoc),y
          Ror @
          Sta (Pomoc),y
          Dex
          Bpl L4
          Dey
          Bpl L3
          Rts

Przep_2   Ldy #$07
L5        Lda (Pomoc),y
          Sta Tablica,y
          Dey
          Bpl L5
          Rts

Dodaj     Clc
          Lda Pomoc
          Adc #$08
          Sta Pomoc
          Bcc L6
          Inc Pomoc+$01
          Lda Pomoc+$01
          And #$FB
          Sta Pomoc+$01
L6        Rts

Przerw    Jsr O_prawo 
          Jsr Dodaj 
          Jsr O_lewo 
          Jsr Dodaj 
          Jmp Koniec

Przepisz  Ldx #Charset1
          Ldy >Charsetl
          Stx Pomoc
          Sty Pomoc+$01
          Ldx Zestaw
          Stx Pomoc2
          Sty Pomoc2+$01
          Ldx #$03
          Ldy #$00
L7        Lda (Pomoc),y 
          Sta (Pomoc2),y
          Iny
          Bne L7
          Inc Pomoc+$01
          Inc Pomoc2+$01
          Dex
          Bpl L7
          Lda >Zestaw
          Sta Chbas
          Rts

Tablica   Org *+8
          End of File
    W programie głównym przepisywany jest zestaw znaków z ROM'u do RAM'u (procedura Przepisz). Ponadto wektor Pomoc wskazuje na zestaw znaków zawarty w RAM'ie. Będzie on ustawiany na znak, który ma się obracać. Następnie uaktywniane jest przerwanie...

Timer 2

    Przerwanie Timer'a 2 jest przerwaniem typu IRQ (Interrupt Request - żądanie przerwania, nazwą tą objęte są wszystkie przerwania maskowalne, czyli takie, które mogą nie zostać zrealizowane przez procesor). Przerwanie to wykonywane jest, jeśli dwubajtowy licznik Cdtmv2 ($21A) osiągnie wartość zero. Licznik ten zmniejsza się 50 razy na sekundę. Na procedurę przerwania wskazuje wektor Cdtma2 ($228).

    Tyle na razie o przerwaniu Timer 2. W przyszłości wrócimy do przerwań i omówimy rejestry kontrolujące je.

    Wróćmy do naszego programu. W procedurze przerwania (etykieta Przerw) wywoływane są trzy procedury. Procedury O_prawo i O_lewo obracają znak wskazywany przez wektor Pomoc o 90 stopni w odpowiednim kierunku przy użyciu dodatkowej tablicy Tablica. Po każdym wywołaniu procedury O_prawo i O_lewo wykonywana jest procedura Dodaj, która zwiększa wektor Pomoc o 8 bajtów, tak aby wskazywał on na następny znak. Jeśli skończy się zestaw znaków, wektor Pomoc otrzyma adres jego początku. Procedury O_lewo i O_prawo korzystają z podprocedury Przep2 tworzącej kopię wyglądu znaku w pomocniczej tablicy.

    Uważamy, że zastosowane przez nas procedury obrotu znaku są najkrótsze, szczególnie wtedy, gdy program główny wymieniałby dwa rozkazy (ASL na LSR i ROR na ROL). Krótsze lub ciekawsze rozwiązania tego problemu jak i zawartego w poprzednim artykule problemu najkrótszej tęczy pracującej na przerwaniu Display List będą mile widziane i opublikowane na łamach TA.

    W następnym artykule przejdziemy do GTIA.

Rafał Bielecki
Tomasz Bielak

Dziś pytanie - dziś odpowiedź

    Uważam, że przytoczone tu procedury obrotu znaku nie są najkrótsze, na dowód czego zamieszczam krótsze:
O_prawo   Ldy #7 
L1        Lda (Pomoc),y
          Ldx #7 
L2        Lsr @
          Rol Tablica,x
          Dex
          Bpl L2
          Dey
          Bpl L1
          Bmi Przep_2 (Jmp)

O_lewo    Ldy #$07 
L3        Lda (Pomoc),y
          Ldx #7
L4        Asl @
          Ror Tablica,x
          Dex
          Bpl L4
          Dey
          Bpl L3
Przep_2   Ldy #$07 
L5        Lda Tablica,y
          Sta (Pomoc),y
          Dey
          Bpl L5
          Rts
    Zastosowałem te same nazwy, aby można było wymienić odnośny fragment.

    Istnieje teoria, która mówi, że KAŻDY program można skrócić, nie zdziwię się więc, gdy ktoś nadeśle jeszcze krótszą metodę.

Janusz B. Wiśniewski



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

Pixel 2002