Tajemnice ATARI

CIO

    Konstruktorzy systemu operacyjnego ATARI XL/XE stworzyli narzędzie pozwalające programiście wygodnie komunikować się z urządzeniami zewnętrznymi. Mowa o CIO (Central Input/Output). Umiejętne ustawienie bloku sterowania wejścia/wyjścia (IOCB-Input/Output Control Block), któryiInformuje CIO o potrzebach programisty, umożliwia obsłużenie każdego urządzenia zewnętrznego. W skrócie wygląda to tak: IOCB należy ustawić do wykonania polecenia otwarcia kanału we/wy (OPEN) dla danej operacji (odczytu, zapisu, itp.), wywołać procedurę CIO. Zakończenie poprawności uaktywnienia IOCB-U można sprawdzić, testując znacznik ujemności (ustawiony - oznacza błąd) lub sprawdzając wartość rejestru Y zawierającego status operacji (wartość powyżej 127 oznacza błąd). Jeśli operacja OPEN zakończyła się pomyślnie (zwykle Y=1), to IOCB należy ustawić do wykonania właściwej operacji we/wy, a następnie ponownie wywołać CIO. Analogicznie, jak wyżej, sprawdzić należy poprawność transmisji danych. Jeżeli wszystkie dane zostały już przesłane, to IOCB ustawiamy do wykonania operacji zamknięcia kanału we/wy (CLOSE) i znów wywołujemy CIO. Jeśli i ta operacja została zakończona pomyślnie, to komunikacja komputera z urządzeniem zewnętrznym odbyła się poprawnie. Procedurę CIO wywołuje się wykonując skok (JSR, JMP) do wektora umieszczonego pod adresem 58454 ($E456). Nie będę opisywał struktury IOCB-U, Czytelnika odsyłam do "Mapy pamięci" w TA 11-12/92. Dla łatwiejszego zrozumienia działania CIO i ustrzeżenia się od błędów proponuję analizę procedur CIO (wszystkie adresy zapisane są w systemie szesnastkowym).
ICCOMT EQU $17
ZIOCB  EQU $20
ICHIDZ EQU ZIOCB
ICDNOZ EQU ZIOCB+1
ICCOMZ EQU ZIOCB+2
ICSTAZ EQU ZIOCB+3
ICBALZ EQU ZIOCB+4
ICBAHZ EQU ZIOCB+5
ICPTLZ EQU ZIOCB+6
ICPTHZ EQU ZIOCB+7
ICBLLZ EQU ZIOCB+8
ICBLHZ EQU ZIOCB+9
ICAX1Z EQU ZIOCB+10
ICAX2Z EQU ZIOCB+11
ICAX3Z EQU ZIOCB+12
ICAX4Z EQU ZIOCB+13
ICAX5Z EQU ZIOCB+14
ICAX6Z EQU ZIOCB+15
HATABS EQU $31A
IOCB   EQU $340
CIOVEC EOU $E456

* początek procedury obsługi
* kanału we/wy,
* ten adres wskazuje wektor
* CIOVEC ($E456)
* zapamiętaj rejestry:
* X - numer IOCB * 16,
* A - przesyłany bajt (operacja
* zapisu-PUT BYTE)
E4DF STA ICAX6Z 
E4E1 STX ICAX5Z
* sprawdź, czy rejestr X jest
* podzielny
* przez 16 lub większy od 128
E4E3 TXA
E4E4 AND #$0F
E4E6 BME E4EC
E4E8 CPX #$80
E4EA BCC E4F1
* błąd; zły numer kanału IOCB 
E4EC LDY #$86
E4EE JMP E670
* skopiuj pierwszych 12 bajtów
* IOCB-u na
* stronę zerową (ZIOCB); dzięki
* tej
* operacji CIO będzie działało
* szybciej,
* a procedury zajmują mniej
* miejsca 
E4F1 LDY #0 
E4F3 LDA IOCB,X 
E4F6 STA ZIOCB,Y 
E4F9 INX 
E4FA INY 
E4FB CPY #$0C 
E4FD BCC E4F3 
* sprawdź, czy status specjalny 
E4FF LDA ICHIDZ 
E501 CMP #$7F 
E503 BNE E51A
* czy komendą jest zamknięcie
* kanału we/wy? 
E505 LDA ICCOMZ 
E507 CMP #$0C 
E509 BEQ E57C
* czy jakakolwiek operacja jest
* dozwolona? 
E50B LDA $2E9 
E50E BNE E515
* błąd: zła nazwa urządzenia
* zewnętrznego 
E510 LDY #$82 
E512 JMP E670
* sprawdź urządzenie zewnętrzne 
E515 JSR CA29 
E518 BMI E512 
E51A LDY #$84 
E51C LDA ICCOMZ
* jeśli numer komendy jest
* mniejszy od
* 3 to błąd: niewłaściwy kod
* rozkazu 
E51E CMP #$03 
E520 BCC E547 
E522 TAY
E523 CPY #$0E 
E525 BCC E529
* jeśli numer komendy jest
* większy od 14,
* to ustaw ten numer na 14 
E527 LDY #$0E 
E529 STY ICCOMT
* ustal typ komendy 
E52B LDA $E72A,Y
* zawartość rejestru
* A w zależności od
* kodu komendy:
* A=0 dla komendy 3 (OPEN),
* A=2 dla komendy 12 (CLOSE),
* A=4 dla komend od 4 do
* 7 (rozkazy czytania),
* A=6 dla komend od 8 do 11
* (rozkazy zapisu),
* A=8 dla komendy 13 (STATUS),
* A=10 dla komendy 14 lub
* większej (SPECIAL)
* czy otwarcie kanału IOCB? 
E52E BEQ E53F
* czy zamknięcie kanału IOCB? 
E530 CMP #$02 
E532 BEQ E57C
* czy STATUS lub SPECIAL? 
E534 CMP #$08 8 lub 10 
E536 BCS E597
* czy komendy czytania danych? 
E538 CMP #$04 
E53A BEQ E5B2
* komendy zapisu danych 
E53C JMP E61E
* początek procedury otwarcia
* kanału we/wy. 
E53F LDA ICHIDZ
* zawartość rejestru A=255
* oznacza aktualnie nieużywany
* IOCB
E541 CMP #$FF
E543 BEQ E54A
* błąd: ponowne użycie
* instrukcji OPEN
* dla już otwartego kanału we/wy 
E545 LDY #$81 
E547 JMP E670
* sprawdź, czy operacja OPEN
* jest dozwolona 
E54A LDA $2E9 
E54D BNE E576
* wywołanie procedury obsługi
* urządzenia;
* powrót z ustawionym carry
* oznacza błąd 
E54F JSR E6FF 
E552 BCS E576 
E554 LDA #0 
E556 STA $2EA 
E559 STA $2EB
* ustal adres procedury
* użytkownika,
* obsługującej operację OPEN;
* powrót
* z ustawionym carry oznacza
* błąd
E55C JSR E695
E55F BCS E547
* wywołaj procedurę OPEN
* użytkownika 
E561 JSR E6EA 
E564 LDA #$0B 
E566 STA ICCOMT
* ustal adres procedury
* użytkownika
* obsługującej operację OPEN 
E568 JSR E695
* ustaw adres procedury PUT BYTE 
E56B LDA ICAX3Z 
E56D STA ICPTLZ 
E56F LDA ICAX4Z 
E571 STA ICPTHZ
* procedura zakończenia obsługi
* IOCB-U 
E573 JMP E672
* procedura ustalająca parametry
* urządzenia zewnętrznego 
E576 JSR EEF9
* procedura zakończenia obsługi
* IOCB-U 
E579 JMP E670
    Myślę, że wiedza na temat sposobu uaktywniania kanału we/wy przez CIO została na tyle wzbogacona, iż pomysłowy programista cierpiący na nadmiar wolnego czasu może zasymulować operację OPEN, oszukując CIO. Tym, którzy tego schorzenia (wolnego czasu) nie posiadają, proponuję poprawne wywołanie procedury otwarcia kanału wejścia/wyjścia. Podprogram wywołuje się instrukcją JSR, co spowoduje otworzenie IOCB-U dla urządzenia DEVICE. Rejestr X powinien zawierać numer IOCB-U pomnożony przez 16, na przykład otwarcie pierwszego bloku kontroli we/wy wygląda tak:
LDX #ICBNUM (patrz etykieta
poniżej)
JSR IOCB_OPEN
...

READ   EQU 4 odczyt zbioru 
DIRECT EQU 6 odczyt katalogu
* dysku
WRITE  EQU 8 zapis zbioru
APPEND EQU 9 dopisanie na końcu
* zbioru
OPERAT EQU READ (może być;
* DIRECT, WRITE, APPEND)
LONG   EQU 0 długie przerwy dla
* urządzenia C:
SHORT  EQU 128 jak wyżej, ale
* przerwy krótkie
ICBNUM EQU $10 (może być: $20,
* $30 itp.) 
IOCB   EQU $340 
ICCOH  EQU IOCB+2 
ICBAL  EQU IOCB+4 
ICBAH  EQU IOCB+5 
ICPTL  EQU IOCB+6 
ICPTH  EOU IOCB+7 
ICBLL  EQU IOCB+8 
ICBLH  EQU IOCB+9 
ICAX1  EQU IOCB+10 
ICAX2  EQU IOCB+11 
CIOVEC EQU $E456

IOCB_OPEN EQU *

LDA #3
STA ICCOM,X
LDA DEVICE
STA ICBAH,X
LDA #OPERAT
STA ICAX1,X
LDA #SHORT   może być LONG
STA ICAX2,X
JMP CIOVEC
DEVICE DTA C'C!'
* początek procedury zamknięcia
* kanału we/wy.
* ustaw status operacji na l,
* czyli
* poprawne zakończenie 
E57C LDY #$01 
E57E STY ICSTAZ
* ustal adres procedury
* użytkownika,
* obsługującej operację CLOSE;
* powrót
* z ustawionym carry oznacza
* błąd.
E580 JSR E695
E583 BCS E588
* wywołaj procedurę CLOSE
* użytkownika 
E585 JSR E6EA
* ustaw status zamkniętego
* kanału WE/WY 
E588 LDA #$FF 
E58A STA ICHIDZ
* ustaw adres procedury
* zwracającej błąd:
* próba dokonania operacji we/wy
* bez wcześniejszego wykonania
* OPEN
E58C LDA #$E4 
E58E STA ICPTHZ 
E590 LDA #$DB 
E592 STA ICPTLZ
* procedura zakończenia obsługi
* IOCB-U 
E594 JMP E672
    Analogicznie, jak w przypadku operacji otwarcia kanału wejścia/wyjścia, ambitni programiści mogą w ramach "ćwiczeń praktycznych" spróbować zasymulować CIO, natomiast dla tych, którzy chcą korzystać z CIO w praktyce, podaję działającą procedurę zamknięcia IOCB-u. Podprogram wywołuje się instrukcją JSR z taką samą wartością rejestru X, jak przy operacji OPEN. Przykładowo, jeśli otworzony został pierwszy IOCB, czyli rejestr X zawierał wartość SIO, to jego zamknięcie powinno wyglądać tak:
LDX #ICBNUM ($10) 
JSR IOCB_CLOSE
...

* wartości etykiet: patrz
* procedura IOCB_OPEN

IOCB_CLOSE EQU *

LDA #l2 
STA ICCOM,X 
JMP CIOVEC
* procedury wykorzystywane
* przez operacje
* OPEN i CLOSE
* procedura zakończenia obsługi
* IOCB-U
* zapamiętaj status wykonanej
* operacji 
E670 STY ICSTAZ
* ustaw w ZIOCB stary adres
* (ważne przy
* przesyłaniu danych) 
E672 LDY ICAX5Z 
E674 LDA IOCB+4,Y 
E677 STA ICBALZ 
E679 LDA IOCB+5,Y 
E67C STA ICBAHZ
* flaga zezwolenia na operacje
* we/wy 
E67E LDX #0 
E680 STX $2E9
* skopiuj zawartość kanału
* we/wy ze strony
* zerowej do właściwego,
* systemowego IOCB-u 
E683 LDA ZIOCB,X 
E685 STA IOCB,Y 
E688 INX 
E689 INY 
E68A CPX #$0C 
E68C BCC E683
* ustaw rejestry;
* A - ostatni przesłany bajt,
* X - numer kanału pomnożony
* przez 16,
* Y - status operacji 
E68E LDA ICAX6Z 
E690 LDX ICAX5Z 
E692 LDY ICSTAZ 
E694 RTS
* procedura, ustalająca adres
* podprogramu
* obsługującego konkretna
* operację we/wy
* sprawdź, czy istnieje
* urządzenie o podanej
* nazwie 
E695 LDY ICHIDZ
E697 CPY #$22 
E699 BCC E69F
* błąd: próba dokonania
* operacji we/wy
* bez wcześniejszego wykonania
* OPEN
E69B LDY #$85
E69D BCS E6BA skok bezwzględny
* ustaw adres tablicy obsługi
* urządzenia
* zewnętrznego 
E69F LDA HATABS+1,Y 
E6A2 STA ICAX3Z 
E6A4 LDA HATABS+2,Y 
E6A7 STA ICAX4Z
* na podstawie tej tablicy
* ustal adres
* procedury obsługującej
*konkretną
* operację (OPEN, CLOSE, GET,
* PUT itd.) 
E6A9 LDY ICCOMT 
E6AB LDA $E72A,Y 
E6AE TAY
E6AF LDA (ICAX3Z),Y 
E6B1 TAX 
E6B2 INY
E6B3 LDA (ICAX3Z),Y 
E6B5 STA ICAX4Z 
E6B7 STX ICAX3Z
* sposób wywołania przez CIO
* ($E6EA) tego
* podprogramu użytkownika
* zmusza do ustawienia
* wartości adresu o l bajt
* mniejszego niż
* w rzeczywistości
* zgaś znacznik carry,
* ustalenie operacji
* we/wy zakończyło się pomyślnie 
E6B9 CLC E6BA RTS
* procedura wywołująca
* podprogram użytkownika
* obsługujący konkretną
* operację WE/WY 
E6EA LDY #$92 
E6EC JSR E6F4
* zapamiętaj status wykonanej
* przez użytkownika
* operacji; wynika stąd, że
* w przypadku
* błędu nie jest konieczne
* zapalenie
* znacznika ujemności (N), wystarczy
* odpowiednie ustawienie
* rejestru Y
E6EF STY ICSTAZ
E6F1 CPY #0
E6F3 RTS
E6F4 TAX
E6F5 LDA ICAX4Z
E6F7 PHA
E6F8 LDA ICAX3Z
E6FA PHA
E6FB TXA
* podprogram użytkownika
* wywołaj z numerem
* IOCB pomnożonym przez 16
* w rejestrze X 
E6FC LDX ICAX5Z 
E6FE RTS
* procedura obsługi podanej
* nazwy urządzenia
* ustal numer urządzenia na
* podstawie
* podanej nazwy, np. gdy nazwą
* jest
* D2:FILENAME.EXT, to numer
* jest równy 2 
E6FF SEC 
E700 LDY #$01 
E702 LDA (ICBALZ),Y 
E704 SBC #$31 
E706 BMI E70C 
E708 CMP #$09 
E70A BCC E70E
* gdy numer urządzenia nie
* mieści się
* w przedziale od l do 9 ustaw
* wartość l,
* np. gdy nazwą jest
* D:FILENAME.EXT, to
* numerem urządzenia jest l 
E70C LDA #0 
E70E STA ICDNOZ 
E710 INC ICDNOZ
* sprawdź, czy nazwa podanego
* urządzenia
* istnieje w tablicy
* zawierającej spis
* aktywnych urządzeń
* zewnętrznych 
E712 LDY #0 
E714 LDA (ICBALZ),Y 
E716 BEQ E724 
E718 LDY #$21 
E71A CMP HATABS,Y 
E71D BEQ E728 
E71F DEY 
E720 DEY 
E721 DEY 
E722 BPL E71A
* błąd: nazwa urządzenia
* zewnętrznego
* nie została zidentyfikowana 
E724 LDY #$82 
E726 SEC 
E727 RTS
* zapamiętaj numer urządzenia;
* na podstawie
* tej informacji użytkownik może
* zorientować się, jakie
* urządzenie zewnętrzne 
* jest obsługiwane przez dany
* IOCB;
* przykładowo, jeżeli
* w systemowej tablicy
* HATABS mamy kolejno wpisy;
* drukarka (P),
* magnetofon (C), edytor (E),
* ekran (S),
* klawiatura (K), to pierwszy
* bajt danego kanału
* we/wy przez cały czas
* operacji będzie
* odpowiednio równy; 0, 3, 6,
* 9, 12.
E728 TYA
E729 STA ICHIDZ
* zgaś znacznik carry,
* uaktywnienie kanału
* we/wy zakończyło się pomyślnie 
E72B CLC 
E72C RTS
    W następnym odcinku o CIO - kolejne operacje. Ponieważ szybkość transmisji przez CIO jest dla niektórych urządzeń zbyt wolna, zostanie podany szkic przykładu, tak oszukującego CIO, by dane były przesyłane szybciej.

Lesław Pasternak



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

Pixel 2002