Rozdział 6
OBSŁUGA NOWYCH URZĄDZEŃ
System operacyjny Atari był projektowany tak, aby umożliwić
maksymalną elastyczność. Oczywiste było, że powstaną w
przyszłości nowe urządzenia. Z tego powodu tabela procedur
obsługi urządzeń zewnętrznych została umieszczona w pamięci RAM
i przewidziano możliwość jej rozszerzania do jedenastu wpisów.
Projektanci Atari założyli ponadto, że powstaną w przyszłości
urządzenia peryferyjne korzystające z szyny równoległej
komputera i nazwali je "nowymi urządzeniami" (new device). Do
ich obsługi przewidziano liczne procedury systemu operacyjnego
oraz wiele rejestrów w obszarze zmiennych systemowych. Jedynym
- na razie - takim urządzeniem jest twardy dysk Supra 20 MB.
Dla procedur nowych urządzeń przewidziana jest struktura
tzw. listy liniowej. Polega ona na tym, że każdy element listy
wskazuje na element następny. Dołączenie do środka listy nowego
elementu (np. między elementy n i n+1) wymaga skopiowania
wskaźnika z elementu n (wskazującego na n+1) do nowego
elementu, a następnie ustawienia wskaźnika elementu n na nowy
element.
Trzeba jeszcze powiedzieć, do czego służy ta struktura.
Otóż zakłada się, że procedury obsługi nowych urządzeń będą
składały się z elementów zorganizowanych w listę liniową.
Kolejne elementy mogą zostać umieszczone w dowolnym miejscu
pamięci, a ich następstwo jest ustalane właśnie przez listę
liniową.
Chciałbym jeszcze przeprosić za ewentualne nieścisłości,
które mogą znaleźć się w tym rozdziale. Niestety trudno jest
pisać o współpracy komputera z urządzeniami, które są nieznane
lub - co gorsza - jeszcze nie istnieją.
6.1. Instalowanie nowego urządzenia
Podczas startu systemu (RESET) wywoływana jest procedura
LINKSOM, której zadaniem jest ustawienie elementów listy
liniowej. Jej przebieg jest zupełnie odmienny przy starcie
zimnym i gorącym. Dlatego najpierw sprawdzana jest zawartość
wskaźnika WARMST i na tej podstawie wybierany jest odpowiedni
wariant procedury.
0100 ;LINK SOMething
0110 ;
0120 CHCKFF = $CB56
0130 CHLINK = $03FB
0140 CKEY = $03E9
0150 DCBINI = $E7BE
0160 DVSTAT = $02EA
0170 DVTMOT = $02EC
0180 INITLD = $E7DE
0190 LINKCD = $E89E
0200 LINKWM = $E894
0210 MEMLO = $02E7
0220 MEMTOP = $02E5
0230 REVNUM = $02ED
0240 TEMP1 = $0312
0250 TEMP2 = $0313
0260 WARMST = $08
0270 ZCHAIN = $4A
0280 ;
0290 *= $E739
0300 ;
0310 LDA WARMST
0320 BEQ CDS
0330 LDA # <CKEY
0340 STA ZCHAIN
0350 LDA # >CKEY
0360 STA ZCHAIN+1
0370 NEXT LDY #$12
0380 CLC
0390 LDA (ZCHAIN),Y
0400 TAX
0410 INY
0420 ADC (ZCHAIN),Y
0430 BEQ EXIT
0440 LDA (ZCHAIN),Y
0450 STA ZCHAIN+1
0460 STX ZCHAIN
0470 JSR CHCKFF
0480 BNE EXIT
0490 JSR LINKWM
0500 BCS EXIT
0510 BCC NEXT
0520 CDS LDA #$00
0530 STA CHLINK
0540 STA CHLINK+1
0550 LDA #$4F
0560 BNE GDV
0570 END LDA #$00
0580 TAY
0590 JSR DCBINI
0600 BPL MEM
0610 EXIT RTS
0620 MEM CLC
0630 LDA MEMLO
0640 ADC DVSTAT
0650 STA TEMP1
0660 LDA MEMLO+1
0670 ADC DVSTAT+1
0680 STA TEMP2
0690 SEC
0700 LDA MEMTOP
0710 SBC TEMP1
0720 LDA MEMTOP+1
0730 SBC TEMP2
0740 BCS ADR
0750 LOOP LDA #$4E
0760 GDV TAY
0770 JSR DCBINI
0780 JMP END
0790 ADR LDA DVTMOT
0800 LDX MEMLO
0810 STX DVTMOT
0820 LDX MEMLO+1
0830 STX REVNUM
0840 JSR INITLD
0850 BMI LOOP
0860 SEC
0870 JSR LINKCD
0880 BCS LOOP
0890 BCC END
Przy zimnym starcie systemu zerowany jest rejestr CHAIN i,
po umieszczeniu w akumulatorze i rejestrze Y wartości $4F,
wywoływana jest procedura DCBINI. Ma ona stwierdzić obecność
urządzenia i jego gotowość do pracy. Następnie przez kolejne
wywołania DCBINI odczytywane są elementy procedury urządzenia.
Czynność ta jest powtarzana, dopóki kolejne wywołanie DCBINI z
wartością $00 w akumulatorze i rejestrze Y nie da wyniku
negatywnego.
Sam proces odczytu jest skomplikowany. W wyniku wywołania
DCBINI z wartościami $00 w rejestrze DVSTAT znajduje się
wielkość elementu (liczba bajtów). Jest ona dodawana do dolnej
granicy wolnej pamięci MEMLO i porównywana z górną granicą
MEMTOP. Jeśli element nie mieści się w pamięci, to urządzenie
jest o tym informowane przez wywołanie DCBINI z wartością $4E,
a następnie pętla jest powtarzana.
Jeżeli rozmiar elementu pozwala na umieszczenie go w
pamięci, to wywoływana jest procedura INITLD, która
przygotowuje i wykonuje odczyt elementu. Błędny odczyt powoduje
wywołanie DCBINI z wartością $4E i kolejne powtórzenie pętli,
Poprawnie odczytany element jest dołączany do listy liniowej
przez procedurę LINK.
Odmienny jest przebieg procedury LINKSOM przy gorącym
starcie. Kolejno sprawdzane jest następstwo elementów listy, aż
do napotkania wskaźnika zerowego, który oznacza koniec listy.
Każdy element jest przy tym sprawdzany przez procedurę CHCKFF.
Negatywny wynik powoduje opuszczenie procedury. Jeśli zaś wynik
kontroli jest pozytywny, to procedura LINK umieszcza element na
końcu listy. Nieprawidłowy przebieg LINK kończy procedurę, a w
przeciwnym razie poszukiwany jest następny element.
Procedura DCBINI przepisuje do bloku DCB parametry z tabeli
TSIOIN, a w rejestrach DAUX1 i DAUX2 umieszcza wartości
przekazane jej odpowiednio w akumulatorze i rejestrze Y. Na
końcu wykonywany jest bezpośredni skok do procedury SIOINT,
która obsługuje transmisję. Wynikiem wykonanej operacji są
cztery bajty umieszczone w rejestrze DVSTAT. Oznaczają one
rozmiar elementu oraz adres dla SIO. Ostatni bajt jest
niewykorzystany (może zawierać numer wersji).
0100 ;DCB INItiation
0110 ;
0120 DAUX1 = $030A
0130 DAUX2 = $030B
0140 DDEVIC = $0300
0150 JSIOINT = $E459
0160 TSIOIN = $E7D4
0170 ;
0180 *= $E7BE
0190 ;
0200 PHA
0210 LDX #$09
0220 NEXT LDA TSIOIN,X
0230 STA DDEVIC,X
0240 DEX
0250 BPL NEXT
0260 STY DAUX2
0270 PLA
0280 STA DAUX1
0290 JMP JSIOINT
Wartości znajdujące się w tabeli TSIOIN oznaczają parametry
dla DCB w kolejności: DDEVIC ($4F), DUNIT ($01), DCMND ($40 -
GET DATA), DSTAT ($40 - odczyt), DBUFA ($02EA - DVSTAT), DTIMLO
($1E) i DBYT ($04).
0100 ;Table SIO INitiation
0110 ;
0120 *= $E7D4
0130 ;
0140 .BYTE $4F,$01,$40,$40,$EA
0150 .BYTE $02,$1E,$00,$04,$00
Procedurą kontrolującą poprawność elementu jest CHCKFF.
Oblicza ona sumę 18 bajtów poczynając od bajtu wskazanego przez
rejestr ZCHAIN. Suma ta powinna mieć wartość $FF, w
akumulatorze jest wtedy $00. Jeśli jest inaczej to akumulator
zawiera różnicę między obliczoną wartością a $FF.
0100 ;CHeCK for $FF
0110 ;
0120 ZCHAIN = $4A
0130 ;
0140 *= $CB56
0150 ;
0160 LDY #$11
0170 LDA #$00
0180 CLC
0190 LOOP ADC (ZCHAIN),Y
0200 DEY
0210 BPL LOOP
0220 ADC #$00
0230 EOR #$FF
0240 RTS
6.1.1. Odczyt elementu
W celu odczytania elementu struktury wywoływana jest
procedura INITLD. Jej zadaniem jest przygotowanie transmisji.
0100 ;INITiation LoaDer
0110 ;
0120 DVTMOT = $02EC
0130 GBYTEA = $02CF
0140 GETBYT = $E816
0150 LOADAD = $02D1
0160 LOADER = $C745
0170 TEMP1 = $312
0180 TEMP2 = $313
0190 TEMP3 = $315
0200 ZLOADA = $02D3
0210 ;
0220 *= $E7DE
0230 ;
0240 STA TEMP2
0250 LDX #$00
0260 STX TEMP1
0270 DEX
0280 STX TEMP3
0290 LDA DVTMOT
0300 ROR A
0310 BCC LDH
0320 INC DVTMOT
0330 BNE LDH
0340 INC DVTMOT+1
0350 LDH LDA DVTMOT
0360 STA LOADAD
0370 LDA DVTMOT+1
0380 STA LOADAD+1
0390 LDA # <GETBYT
0400 STA GBYTEA
0410 LDA # >GETBYT
0420 STA GBYTEA+1
0430 LDA #$80
0440 STA ZLOADA
0450 JMP LOADER
Na początku zawartość akumulatora jest zapisywana do
pomocniczego rejestru TEMP2, a rejestry TEMP1 i TEMP3 otrzymują
odpowiednio wartości $00 i $FF. Obliczony uprzednio adres
wczytywania jest przenoszony z DVTMOT do rejestru LOADAD (LOAD
ADdress), a wektor GBYTEA (Get BYTE Address) jest ustawiany na
adres procedury GETBYT. Następnie znacznik ZLOADA otrzymuje
wartość $80 i wykonywany jest bezpośredni skok do procedury
LOADER.
0100 ;LOADER routine
0110 ;
0120 GBYTEA = $02CF
0130 HIBYTE = $0288
0140 LCOUNT = $0233
0150 LOADAD = $02D1
0160 NEWVEC = $C8E4
0170 RECLEN = $0245
0180 RUNADR = $02C9
0190 ;
0200 *= C745
0210 ;
0220 LDX #$05
0230 NEXT LDA #$00
0240 STA RUNADR,X
0250 DEX
0260 BPL NEXT
0270 LOOP1 LDA #$00
0280 STA LCOUNT
0290 JSR GBYTAC
0300 LDY #$9C
0310 BCS EXIT
0320 STA HIBYTE
0330 JSR GBYTAC
0340 LDY #$9C
0350 BCS EXIT
0360 STA RECLEN
0370 LDA HIBYTE
0380 CMP #$0B
0390 BEQ HENDRT
0400 ROL A
0410 TAX
0420 LDA NEWVEC,X
0430 STA RUNADR
0440 LDA NEWVEC+1,X
0450 STA RUNADR+1
0460 LOOP2 LDA RECLEN
0470 CMP LCOUNT
0480 BEQ LOOP1
0490 JSR GBYTAC
0500 LDY #$9C
0510 BCS EXIT
0520 JSR RUNADC
0530 INC LCOUNT
0540 BNE LOOP2
0550 EXIT RTS
0560 ;
0570 ;Handle END Record Type
0580 ;
0590 HENDRT JSR GBYTAC
0600 LDY #$9C
0610 BCS END
0620 STA RUNADR
0630 JSR GBYTAC
0640 LDY #$9C
0650 BCS END
0660 STA RUNADR+1
0670 LDA RECLEN
0680 CMP #$01
0690 BEQ SUC
0700 BCC ERR
0710 CLC
0720 LDA RUNADR
0730 ADC LOADAD
0740 TAY
0750 LDA RUNADR+1
0760 ADC LOADAD+1
0770 SET STY RUNADR
0780 STA RUNADR+1
0790 SUC LDY #$01
0800 END RTS
0810 ERR LDY #$00
0820 LDA #$00
0830 BEQ SET
0840 GBYTAC JMP (GBYTEA)
0850 RUNADC JMP (RUNADR)
Na początku zerowane jest sześć bajtów poczynając od adresu
RUNADR (są to rejestry RUNADR, HIUSED i ZHIUSE). Po ustawieniu
licznika LCOUNT (Loader COUNTer) na zero rozpoczyna się pętla
odczytu. Najpierw dwukrotnie wywoływana jest procedura GETBYT,
a odczytane przez nią wartości są umieszczane kolejno w
rejestrach HIBYTE (HIgh BYTE) i RECLEN (RECord LENgth).
Zasygnalizowanie błędu po zakończeniu GETBYT zawsze powoduje
umieszczenie w rejestrze Y kodu błędu $9C (żadne ze źródeł nie
podaje znaczenia tego kodu) i przerwanie procedury LOADER.
Przy pierwszym wywołaniu procedury GETBYT z LOADER rejestr
TEMP3 zawiera wartość $FF. Powoduje to wpisanie do TEMP3
wartości $80 i wywołanie procedury GTNXBL i odczyt bloku 128
bajtów. Pierwszy bajt bloku jest umieszczany w akumulatorze i
przekazywany do procedury LOADER. Każde następne wywołanie
GETBYT powoduje zwrócenie kolejnego bajtu bloku, aż do jego
wyczerpania. Wtedy znów wywoływana jest procedura GTNXBL.
Pierwszy bajt bloku (przepisany do HIBYTE) służy jako
indeks tabeli wektorów NEWVEC, natomiast drugi (RECLEN) określa
długość odczytanego bloku. Jeżeli RECLEN jest równy licznikowi
LCOUNT, to pętla jest powtarzana. Jej opuszczenie następuje po
odczytaniu wartości HIBYTE równej $0B. Następuje wtedy
przejście do etykiety HENDRT.
Przy RECLEN różnym od LCOUNT uruchamiana jest pętla
wewnętrzna, która kończy się po zrównaniu tych wartości.
Podczas niej kolejne bajty odczytane przez GETBYT są
przekazywane do procedury, której adres znajduje się w
rejestrze RUNADR. Po jej zakończeniu licznik LCOUNT jest
zwiększany i pętla się powtarza.
0100 ;GET BYTe
0110 ;
0120 CSCB = $03FD
0130 GTNXBL = $E833
0140 TEMP3 = $0315
0150 ;
0160 *= $E816
0170 ;
0180 LDX TEMP3
0190 INX
0200 STX TEMP3
0210 BEQ GPL
0220 NEXT LDX TEMP3
0230 LDA (CSCB-$80),X
0240 CLC
0250 RTS
0260 GPL LDA #$80
0270 STA TEMP3
0280 JSR GTNXBL
0290 BPL NEXT
0300 SEC
0310 RTS
Po przerwaniu pętli procedura GETBYT jest wywoływana
jeszcze dwa razy. Odczytane bajty umieszczane są w rejestrze
RUNADR. Gdy RECLEN ma wartość 1, procedura się kończy, a gdy 0
- to zerowany jest RUNADR. W innym przypadku do zawartości
RUNADR dodawana jest jeszcze zawartość LOADAD.
0100 ;GeT NeXt BLock
0110 ;
0120 DAUX1 = $030A
0130 DDEVIC = $0300
0140 JSIOINT = $E459
0150 SIOTAB = $E851
0160 TEMP1 = $0312
0170 TEMP2 = $0313
0180 ;
0190 *= $E833
0200 ;
0210 LDX #$0B
0220 NEXT LDA SIOTAB,X
0230 STA DDEVIC,X
0240 DEX
0250 BPL NEXT
0260 LDX TEMP1
0270 STX DAUX1
0280 INX
0290 STX TEMP1
0300 LDA TEMP2
0310 STA DDEVIC
0320 JMP JSIOINT
Procedura GTNXBL wywoływana przez GETBYT przepisuje do DCB
parametry transmisji z tabeli SIOTAB. Następnie do rejestru
DAUX1 określającego numer rekordu wpisuje wartość pobraną z
TEMP1, który z kolei jest zwiększany. Na końcu z rejestru TEMP2
przepisuje kod urządzenia umieszczony tam przez procedurę
INITLD i wykonuje skok do SIOINT.
0100 ;SIO TABle
0110 ;
0120 *= $E851
0130 ;
0140 .BYTE $00,$01,$26,$40
0150 .BYTE $FD,$03,$1E,$00
0160 .BYTE $80,$00,$00,$00
Wartości znajdujące się w tabeli SIOTAB oznaczają parametry
dla DCB w kolejności: DDEVIC ($00), DUNIT ($01), DCMND ($26 -
NOTE SECTOR), DSTAT ($40 - odczyt), DBUFA ($03FD - CSCB),
DTIMLO ($1E), DBYT ($80 - 128 bajtów) oraz DAUX1 i DAUX2 ($00).
6.1.2. Przetwarzanie elementu
Każdy bajt odczytany przez GETBYT jest przekazywany do
procedury wskazanej wektorem RUNADR. Wektor ten jest pobierany
przez procedurę LOADER z tabeli adresowej NEWVEC według
wartości HIBYTE. Warto zwrócić uwagę na fakt, że układ tej
tabeli jest bardzo zbliżony do układu tabeli COMTAB, która jest
wykorzystywana przez CIO (zob. rozdział 2.1. - str. 15).
0100 ;NEW device VECtor
0110 ;
0120 ADD28E = $C86D
0130 ADDGET = $C8B5
0140 ADDWRD = $C892
0150 HENDRT = $C795
0160 PUTCHR = $C7D5
0170 ;
0180 *= $C8E4
0190 ;
0200 .WORD PUTCHR
0210 .WORD PUTCHR
0220 .WORD ADDWRD
0230 .WORD ADDWRD
0240 .WORD ADDWRD
0250 .WORD ADDWRD
0260 .WORD ADD28E
0270 .WORD ADD28E
0280 .WORD ADDGET
0290 .WORD ADDGET
0300 .WORD PUTCHR
0310 .WORD HENDRT
Teraz zostaną kolejno opisane procedury umieszczone w
powyższej tabeli. Trzeba przy tym pamiętać, że w każdej z nich
daną wejściową jest bajt odczytany przez GETBYT i umieszczony w
akumulatorze.
Procedura PUTCHR jest wywoływana, gdy rejestr HIBYTE ma
wartość $00, $01 lub $0A. Jej przebieg jest zależny od stanu
licznika LCOUNT. Gdy jest on równy zero, to zawartość
akumulatora jest tylko zapisywana do młodszego bajtu rejestrów
RELADR i NEWADR, a procedura się kończy. Gdy LCOUNT jest równy
1, to bajt z akumulatora jest umieszczany w starszym bajcie
tych rejestrów i następuje przejście do drugiej fazy PUTCHR.
0100 ;PUT CHaRacter
0110 ;
0120 HIBYTE = $0288
0130 HIUSED = $02CB
0140 LCOUNT = $0233
0150 LOADAD = $02D1
0160 LTEMP = $36
0170 MEMTOP = $02E5
0180 NEWADR = $028E
0190 RECLEN = $0245
0200 RELADR = $024A
0210 ;
0220 *= $C7D5
0230 ;
0240 LDY LCOUNT
0250 CPY #$01
0260 BEQ ADD
0270 BCS RELTXT
0280 STA RELADR
0290 STA NEWADR
0300 BCC EXIT
0310 ADD STA RELADR+1
0320 STA NEWADR+1
0330 LDX #$00
0340 LDA HIBYTE
0350 BEQ ADL
0360 CMP #$0A
0370 BEQ ADN
0380 LDX #$02
0390 ADL CLC
0400 LDA RELADR
0410 ADC LOADAD,X
0420 STA NEWADR
0430 LDA RELADR+1
0440 ADC LOADAD+1,X
0450 STA NEWADR+1
0460 ADN CLC
0470 LDA NEWADR
0480 ADC RECLEN
0490 PHA
0500 LDA #$00
0510 ADC NEWADR+1
0520 TAY
0530 PLA
0540 SEC
0550 SBC #$02
0560 BCS BPS
0570 DEY
0580 BPS PHA
0590 TYA
0600 CMP HIUSED+1,X
0610 PLA
0620 BCC LOD
0630 BNE SAV
0640 CMP HIUSED,X
0650 BCC LOD
0660 SAV STA HIUSED,X
0670 PHA
0680 TYA
0690 STA HIUSED+1,X
0700 PLA
0710 LOD LDX HIBYTE
0720 CPX #$01
0730 BEQ EXIT
0740 CPY MEMTOP+1
0750 BCC EXIT
0760 BNE ERR
0770 CMP MEMTOP
0780 BCC EXIT
0790 ERR PLA
0800 PLA
0810 LDY #$9D
0820 EXIT RTS
0830 ;
0840 ;RELocate TeXT in memory
0850 ;
0860 RELTXT SEC
0870 PHA
0880 LDA LCOUNT
0890 SBC #$02
0900 CLC
0910 ADC NEWADR
0920 STA LTEMP
0930 LDA #$00
0940 ADC NEWADR+1
0950 STA LTEMP+1
0960 PLA
0970 LDY #$00
0980 STA (LTEMP),Y
0990 JMP EXIT
Dalsze działanie zależy od wartości HIBYTE. Przy HIBYTE
równym zero do RELADR jest dodawane LOADAD, a przy równym jeden
ZLOADA i rezultat jest umieszczany w NEWADR. Wartość $0A w
HIBYTE powoduje ominięcie tego etapu. Następnie zawartość
NEWADR jest zwiększana o długość rekordu RECLEN i zmniejszana o
2. Uzyskany wynik jest porównywany z zawartością rejestru
HIUSED (HIgh USED address) i gdy jest większy, to zostaje do
niego przepisany. Na końcu wykonywane jest jeszcze porównanie
otrzymanego adresu z wartością MEMTOP. Jeżeli adres jest
większy, to w rejestrze Y sygnalizowany jest błąd $9D.
Wystąpienie błędu każdorazowo powoduje zdjęcie ze stosu dwóch
bajtów i powrót z procedury PUTCHR do miejsca wywołania INITLD.
Zupełnie odmienny jest przebieg procedury, gdy licznik
LCOUNT ma wartość większą od 1. Do adresu zawartego w NEWADR
dodawany jest stan LCOUNT zmniejszony o 2 i uzyskany wynik jest
wpisywany do rejestru przejściowego LTEMP (Loader TEMPorary
register). Przekazany w akumulatorze znak jest teraz
umieszczany pod adresem wskazywanym przez LTEMP i procedura się
kończy.
Wartości HIBYTE z zakresu od $02 do $05 powodują wywołanie
procedury ADDWRD. Najpierw dodawane są zawartości akumulatora i
NEWADR, a wynik umieszczany jest w LTEMP. Następnie do bajtu
wskazywanego przez LTEMP dodawana jest zawartość rejestru
LOADAD (dla HIBYTE mniejszego od 4) lub ZLOADA (dla HIBYTE
większego od 3).
0100 ;ADDition for Write/ReaD
0110 ;
0120 HIBYTE = $0288
0130 LOADAD = $02D1
0140 LTEMP = $36
0150 NEWADR = $028E
0160 ;
0170 *= $C892
0180 ;
0190 LDX #$00
0200 LDY HIBYTE
0210 CPY #$04
0220 BCC BPS
0230 LDX #$02
0240 BPS CLC
0250 ADC NEWADR
0260 STA LTEMP
0270 LDA #$00
0280 ADC NEWADR+1
0290 STA LTEMP+1
0300 LDY #$00
0310 LDA (LTEMP),Y
0320 CLC
0330 ADC LOADAD,X
0340 STA (LTEMP),Y
0350 RTS
Przy HIBYTE równym $06 lub $07 wywoływana jest procedura
ADD28E. Wykonywane jest przez nią także dodawanie zawartości
LOADAD według LTEMP, jak w ADDWRD, lecz teraz operacja dotyczy
dwóch bajtów: wskazywanego przez wektor LTEMP i następnego.
0100 ;ADDition $028E
0110 ;
0120 LOADAD = $02D1
0130 LTEMP = $36
0140 NEWADR = $028E
0150 ;
0160 *= $C86D
0170 ;
0180 CLC
0190 ADC NEWADR
0200 STA LTEMP
0210 LDA #$00
0220 ADC NEWADR+1
0230 STA LTEMP+1
0240 LDY #$00
0250 LDA (LTEMP),Y
0260 CLC
0270 ADC LOADAD
0280 STA (LTEMP),Y
0290 INC LTEMP
0300 BNE BPS
0310 INC LTEMP+1
0320 BPS LDA (LTEMP),Y
0330 ADC LOADAD+1
0340 STA (LTEMP),Y
0350 RTS
Przebieg procedury ADDGET wywoływanej dla zawartości HIBYTE
równej $08 lub $09 jest zależny od parzystości licznika LCOUNT,
co jest rozpoznawane według bitu 0.
0100 ;ADDition for GET
0110 ;
0120 HIBYTE = $0288
0130 LCOUNT = $0233
0140 LOADAD = $02D1
0150 LTEMP = $36
0160 NEWADR = $028E
0170 ;
0180 *= $C8B5
0190 ;
0200 PHA
0210 LDA LCOUNT
0220 ROR A
0230 PLA
0240 BCS ADD
0250 CLC
0260 ADC NEWADR
0270 STA LTEMP
0280 LDA #$00
0290 ADC NEWADR+1
0300 STA LTEMP+1
0310 LDY #$00
0320 LDA (LTEMP),Y
0330 STA HIBYTE
0340 EXIT RTS
0350 ADD CLC
0360 ADC LOADAD
0370 LDA #$00
0380 ADC LOADAD+1
0390 ADC HIBYTE
0400 LDY #$00
0410 STA (LTEMP),Y
0420 BEQ EXIT
Przy parzystej wartości LCOUNT (bit 0 skasowany) zawartość
akumulatora jest dodawana do NEWADR, a uzyskany wynik jest
traktowany jako wektor. Bajt wskazany przez ten wektor jest
odczytywany i umieszczany w rejestrze HIBYTE.
Nieparzysta wartość LCOUNT (bit 0 ustawiony) powoduje
zsumowanie zawartości akumulatora oraz rejestrów LOADAD i
HIBYTE. Uzyskany rezultat jest umieszczany w komórce wskazanej
wektorem LTEMP.
6.1.3. Dołączenie elementu do listy
Każdy odczytany element struktury musi zostać umieszczony
we właściwym miejscu listy liniowej. Operacja ta jest
przeprowadzana przez procedurę LINK. Procedura ta posiada trzy
punkty początkowe. Jeden z nich (LINKCD) jest wykorzystywany
przy zimnym starcie systemu, drugi (LINKWM) przy starcie
gorącym, a trzeci (LINK) jest normalnym adresem wywołania
procedury.
0100 ;LINK routine
0110 ;
0120 CALVEC = $E900
0130 CHCKFF = $CB56
0140 DVTMOT = $02EC
0150 MEMLO = $02E7
0160 SRCHLS = $E85D
0170 TEMP1 = $0312
0180 TEMP2 = $0313
0190 UNLINK = $E915
0200 ZCHAIN = $4A
0210 ;
0220 *= $E894
0230 ;
0240 ;LINK WarM start
0250 LINKWM SEC
0260 PHP
0270 BCS INIHND
0280 ;LINK
0290 LINK STA DVTMOT+1
0300 STY DVTMOT
0310 ;
0320 ;LINK ColD start
0330 LINKCD PHP
0340 LDA #$00
0350 TAY
0360 JSR SRCHLS
0370 BCS ERR
0380 LDY #$12
0390 LDA DVTMOT
0400 STA (ZCHAIN),Y
0410 TAX
0420 INY
0430 LDA DVMOT+1
0440 STA (ZCHAIN),Y
0450 STX ZCHAIN
0460 STA ZCHAIN+1
0470 LDA #$00
0480 STA (ZCHAIN),Y
0490 DEY
0500 STA (ZCHAIN),Y
0510 ;INItialize HaNDler
0520 INIHND JSR CALVEC
0530 BCC SUCC
0540 LDA DVTMOT+1
0550 LDY DVTMOT
0560 JSR UNLINK
0570 ERR PLP
0580 SEC
0590 RTS
0600 SUCC PLP
0610 BCS UPDMLO
0620 LDA #$00
0630 LDY #$10
0640 STA (ZCHAIN),Y
0650 INY
0660 STA (ZCHAIN),Y
0670 UPDMLO CLC
0680 LDY #$10
0690 LDA MEMLO
0700 ADC (ZCHAIN),Y
0710 STA MEMLO
0720 INY
0730 LDA MEMLO+1
0740 ADC (ZCHAIN),Y
0750 STA MEMLO+1
0760 LDY #$0F
0770 LDA #$00
0780 STA (ZCHAIN),Y
0790 JSR CHCKFF
0800 LDY #$0F
0810 STA (ZCHAIN),Y
0820 CLC
0830 RTS
Rozpoczęcie procedury od LINKWM powoduje opuszczenie całej
pierwszej fazy procedury. Po ustawieniu bitu Carry i
zapamiętaniu na stosie rejestru statusu procesora wykonywany
jest skok do drugiej części oznaczonej etykietą INIHND.
Po rozpoczęciu od etykiety LINKCD akumulator i rejestr Y są
zerowane i wywoływana jest procedura SRCHLS. Wyszukuje ona
ostatni element struktury i umieszcza jego adres w rejestrze
ZCHAIN. Gdy taki element nie został znaleziony, to po
odtworzeniu ze stosu rejestru statusu i ustawieniu bitu Carry
procedura LINK jest przerywana. Następnie wskaźnik tego
elementu jest ustawiany tak, aby wskazywał na nowy element.
Adres nowego elementu jest umieszczany w rejestrze ZCHAIN, a
jego wskaźnik jest zerowany.
Teraz element jest poprzez wywołanie procedury CALVEC
inicjowany. W przypadku niepowodzenia ponownie pobierany jest
jego adres z DVTMOT i wywoływana jest procedura UNLINK, która
usuwa element z listy. Dzięki temu lista zawiera tylko
prawidłowo zainicjowane struktury.
Potem odtwarzany jest ze stosu rejestr statusu procesora.
Jeżeli ma on skasowany bit Carry, to zerowane są bajty 16 i 17
struktury wskazywanej przez wektor ZCHAIN. Oznacza to, że nowy
element mieści się poniżej dolnej granicy wolnej pamięci
(MEMLO). Następnie zawartości bajtów 16 i 17 są dodawane do
wartości MEMLO, aby zabezpieczyć nowy element przed ingerencją
programu użytkownika.
Na zakończenie wywoływana jest procedura CHCKFF, która
oblicza sumę kontrolną i zwraca wynik odjęcia jej od $FF. Wynik
ten jest umieszczany w 15 bajcie elementu. Teraz po wywołaniu
CHCKFF bit Carry będzie skasowany, a w akumulatorze znajdzie
się zero.
0100 ;SeaRCH LiSt
0110 ;
0120 CHCKFF = $CB56
0130 CKEY = $03E9
0140 TEMP1 = $0312
0150 TEMP2 = $0313
0160 ZCHAIN = $4A
0170 ;
0180 *= $E85D
0190 ;
0200 STY TEMP1
0210 STA TEMP2
0220 LDA # <CKEY
0230 STA ZCHAIN
0240 LDA # >CKEY
0250 STA ZCHAIN+1
0260 LOOP LDY #$12
0270 LDA (ZCHAIN),Y
0280 TAX
0290 INY
0300 LDA (ZCHAIN),Y
0310 CMP TEMP2
0320 BNE FIND
0330 CPX TEMP1
0340 BNE FIND
0350 CLC
0360 RTS
0370 FIND CMP #$00
0380 BNE FOUND
0390 CPX #$00
0400 BNE FOUND
0410 ERR SEC
0420 RTS
0430 FOUND STX ZCHAIN
0440 STA ZCHAIN+1
0450 JSR CHCKFF
0460 BNE ERR
0470 BEQ LOOP
Procedura SRCHLS przeszukuje listę liniową w celu
odnalezienia elementu, którego wskaźnik ma parametry
umieszczone przed wywołaniem SRCHLS w akumulatorze i rejestrze
Y. Przekazane wartości są najpierw zapamiętywane w rejestrach
TEMP1 i TEMP2. Następnie sprawdzany jest wskaźnik elementu. Gdy
jest to poszukiwany element, procedura kończy się ze skasowanym
bitem Carry.
W przeciwnym przypadku sprawdza się, czy jest to ostatni
element listy (wskaźnik równy zero). Jeśli tak, to procedura
kończy się z ustawionym bitem Carry. Jeżeli nie jest to jeszcze
koniec listy, to po przepisaniu adresu do rejestru ZCHAIN
wywoływana jest procedura CHCKFF. Pozytywny wynik sprawdzenia
sumy kontrolnej powoduje przejście do początku pętli i badanie
następnego elementu. Błąd kończy procedurę SRCHLS z ustawionym
bitem Carry.
0100 ;CALl VECtor
0110 ;
0120 TEMP1 = $0312
0130 TEMP2 = $0313
0140 ZCHAIN = $4A
0150 ;
0160 *= $E900
0170 ;
0180 CLC
0190 LDA ZCHAIN
0200 ADC #$0C
0210 STA TEMP1
0220 LDA ZCHAIN+1
0230 ADC #$00
0240 STA TEMP2
0250 JMP (TEMP1)
Inicjowanie elementu struktury odbywa się poprzez procedurę
CALVEC. Odczytuje ona 12 i 13 bajt elementu oraz umieszcza je w
rejestrach TEMP1 i TEMP2. Następnie według tego wektora
wykonywany jest skok do procedury inicjującej. Wyraźnie widać
tu analogię z tabelami adresowymi CIO, w których od dwunastego
bajtu znajduje się instrukcja skoku do procedury inicjowania
urządzenia.
Gdy zachodzi konieczność usunięcia elementu z listy
liniowej (np. po niepoprawnej inicjalizacji), to wywoływana
jest procedura UNLINK. Adres elementu do usunięcia jest
przekazywany do niej w akumulatorze i rejestrze Y. Z tymi
wartościami wywoływana jest najpierw procedura SRCHLS, która
wyszukuje element do usunięcia.
Teraz wskaźnik elementu poprzedzającego jest ustawiany na
element następny i w ten sposób zbędny element jest już
usunięty z listy. Dokładniej metodę wykonania tej operacji
można prześledzić na wydruku procedury. Jeżeli usunięcie
elementu odbyło się poprawnie, to procedura kończy się ze
skasowanym bitem Carry, w innym wypadku Carry jest ustawiany.
0100 ;UNLINK routine
0110 ;
0120 CHCKFF = $CB56
0130 COLDST = $0244
0140 SRCHLS = $E85D
0150 ZCHAIN = $4A
0160 ;
0170 ;
0180 *= $E915
0190 ;
0200 JSR SRCHLS
0210 BCS EXIT
0220 TAY
0230 LDA ZCHAIN
0240 PHA
0250 LDA ZCHAIN+1
0260 PHA
0270 STX ZCHAIN
0280 STY ZCHAIN+1
0290 LDA COLDST
0300 BNE COLD
0310 LDY #$10
0320 CLC
0330 LDA (ZCHAIN),Y
0340 INY
0350 ADC (ZCHAIN),Y
0360 BNE END
0370 JSR CHCKFF
0380 BNE END
0390 COLD LDY #$12
0400 LDA (ZCHAIN),Y
0410 TAX
0420 INY
0430 LDA (ZCHAIN),Y
0440 TAY
0450 PLA
0460 STA ZCHAIN+1
0470 PLA
0480 STA ZCHAIN
0490 TYA
0500 LDY #$13
0510 STA (ZCHAIN),Y
0520 DEY
0530 TXA
0540 STA (ZCHAIN),Y
0550 CLC
0560 RTS
0570 END PLA
0580 PLA
0590 EXIT SEC
0600 RTS
6.2. Komunikacja z nowym urządzeniem
Pierwszą operacją konieczną do współpracy z urządzeniem
przez CIO jest otwarcie kanału IOCB dla transmisji. Gdy
otwierająca kanał procedura CIOOPN stwierdzi w rejestrze HNDLOD
wartość różną od zera lub nie znajdzie wpisu urządzenia w
tabeli HATABS, to wywoływana jest procedura SPCHND
przygotowująca komunikację z nowym urządzeniem.
0100 ;SPeCial HaNDler
0110 ;
0120 DCBINI = $E7BE
0130 DVSTAT = $02EA
0140 ICAX3 = $034C
0150 ICAX4 = $034D
0160 ICAX5Z = $2E
0170 ICBAZ = $24
0180 ICDNOZ = $21
0190 ICHIDZ = $20
0200 ICPTZ = $26
0210 PUTBYT = $EF26
0220 ;
0230 *= $EEF9
0240 ;
0250 LDY #$00
0260 LDA (ICBAZ),Y
0270 LDY ICDNOZ
0280 JSR DCBINI
0290 BPL NERR
0300 LDY #$82
0310 RTS
0320 NERR LDA #$7F
0330 STA ICHIDZ
0340 LDA # <[PUTBYT-1]
0350 STA ICPTZ
0360 LDA # >[PUTBYT-1]
0370 STA ICPTZ+1
0380 LDA DVSTAT+2
0390 LDX ICAX5Z
0400 STA ICAX4,X
0410 LDY #$00
0420 LDA (ICBAZ),Y
0430 STA ICAX3,X
0440 LDY #$01
0450 RTS
Najpierw wywołuje ona procedurę DCBINI, która sprawdza
istnienie urządzenia. Jej negatywny wynik powoduje wpisanie do
rejestru Y kodu błędu $82 i przerwanie procedury SPCHND. Po
otrzymaniu od urządzenia odpowiedzi, indeks w HATABS (ICHIDZ)
jest ustawiany na wartość $7F, która sygnalizuje, że jest to
nowe urządzenie. Jako wektor procedury obsługi wpisywany jest
adres procedury PUTBYT. Następnie do ICAX4 przepisywany jest
adres z DVSTAT+2, a do ICAX3 właściwa nazwa urządzenia z
bufora.
Każde następne żądanie komunikacji z nowym urządzeniem musi
być poprzedzone wpisaniem do rejestru HNDLOD wartości różnej od
zera (rejestr ICHIDZ zawiera $7F). Po stwierdzeniu takich
wartości CIOMAIN wywołuje procedurę PRPLNK, która przygotowuje
urządzenie do transmisji.
0100 ;PRePare LiNK
0110 ;
0120 DFVHND = $E716
0130 ICAX3 = $034C
0140 ICAX4 = $034D
0150 ICAX5Z = $2E
0160 ICCHID = $0340
0170 ICCOMT = $17
0180 ICHIDZ = $20
0190 INIOPN = $E55C
0200 INITLD = $E7DE
0210 LINKCD = $E89E
0220 NEXDER = $E510
0230 ;
0240 *= $CA29
0250 ;
0260 LDX ICAX5Z
0270 LDA ICAX4,X
0280 JSR INITLD
0290 BCS EXIT
0300 CLC
0310 JSR LINKCD
0320 BCS EXIT
0330 LDX ICAX5Z
0340 LDA ICAX3,X
0350 JSR FDVHND
0360 BCS EXIT
0370 LDX ICAX5Z
0380 STA ICCHID,X
0390 STA ICHIDZ
0400 LDA #$03
0410 STA ICCOMT
0420 JMP INIOPN
0430 EXIT JMP NEXDER
Najpierw do akumulatora przenoszony jest bajt z ICAX4 i
wywoływana jest procedura INITLD, która inicjuje odpowiedni
element. Następnie procedura LINK umieszcza ten element w
liście liniowej. Teraz z rejestru ICAX3 jest pobierany właściwy
kod urządzenia i procedura FDVHND (część DEVNUM) odszukuje w
tabeli HATABS wektor tabeli adresowej tego urządzenia.
Znaleziony indeks w HATABS jest przepisywany do rejestrów
ICCHID i ICHIDZ. Wykrycie błędu podczas PRPLNK powoduje skok do
procedury CIOMAIN, która sygnalizuje błąd NON EXISTENT DEVICE
(urządzenie nie istnieje). W innym razie, po ustaleniu kodu
operacji na $03 (OPEN), procedura kończy się skokiem do INIOPN
(wewnątrz procedury CIOOPN).
Po otwarciu kanału IOCB dla nowego urządzenia w rejestrze
ICPTZ znajduje się adres procedury PUTBYT. Spełnia ona zadania
zbliżone do CIO i przy porównaniu obu tych procedur bardzo
łatwo wykryć podobieństwa.
Na początku sprawdzana jest poprawność numeru IOCB
(identycznie jak w CIOMAIN) oraz wartość znacznika HNDLOD.
Pozytywny wynik powoduje przepisanie odpowiedniego IOCB do
ZIOCB i wywołanie procedury PRPLNK. W przeciwnym przypadku do
rejestru Y wpisywany jest kod błędu i procedura się kończy.
0100 ;PUT BYTe routine
0110 ;
0120 HNDLOD = $02E9
0130 ICAX5Z = $2E
0140 ICCHID = $0340
0150 ICHIDZ = $20
0160 ICPTZ = $26
0170 PRPLNK = $CA29
0180 ;
0190 *= $EF26
0200 ;
0210 PHA
0220 TXA
0230 PHA
0240 AND #$0F
0250 BNE BIN
0260 CPX #$80
0270 BPL BIN
0280 LDA HNDLOD
0290 BNE SUC
0300 LDY #$82
0310 ERR PLA
0320 PLA
0330 CPY #$00
0340 RTS
0350 BIN LDY #$86
0360 BMI ERR
0370 SUC STX ICAX5Z
0380 LDY #$00
0390 NEXT LDA ICCHID,X
0400 STA ICHIDZ,Y
0410 INX
0420 INY
0430 CPY #$0C
0440 BMI NEXT
0450 JSR PRPLNK
0460 BMI ERR
0470 PLA
0480 TAX
0490 PLA
0500 TAY
0510 LDA ICPTZ+1
0520 PHA
0530 LDA ICPTZ
0540 PHA
0550 TYA
0560 LDY #$92
0570 RTS
Po poprawnym zakończeniu PRPLNK wektor z rejestru ICPTZ
jest przepisywany na stos. Po wykonaniu rozkazu RTS program
jest kontynuowany od umieszczonego na stosie adresu. W CIO
analogiczną operację wykonują procedury GOHAND i CIOJMP.
System operacyjny Atari zawiera jeszcze tabelę adresową,
która może być wykorzystana przez nowe urządzenie. Ma ona
strukturę wymaganą przez CIO i może być wykorzystana po
wpisaniu jej adresu do HATABS.
0100 ;CALl TABle
0110 ;
0120 NEWINIT = $C90C
0130 NWDVC1 = $C991
0140 NWDVC3 = $C996
0150 NWDVC5 = $C99B
0160 NWDVC7 = $C9A0
0170 NWDVC9 = $C9A5
0180 NWDVCB = $C9AA
0190 ;
0200 *= $E48F
0210 ;
0220 .WORD NWDVC1-1
0230 .WORD NWDVC3-1
0240 .WORD NWDVC5-1
0250 .WORD NWDVC7-1
0260 .WORD NWDVC9-1
0270 .WORD NWDVCA-1
0280 JMP NEWINIT
0290 .BYTE $00
Zawarte w tej tabeli adresy wskazują na tabelę wywołań
procedur. Wszystkie operacje są rozpoczynane przez procedurę
CHKNWP, a ich rodzaj jest rozpoznawany według zawartości
rejestru Y.
0100 ;NEW DeVice Call
0110 ;
0120 CHKNWP = $C9DC
0130 ;
0140 *= $C991
0150 ;
0160 NWDVC1 LDY #$01
0170 JMP CHKNWP
0180 NWDVC3 LDY #$03
0190 JMP CHKNWP
0200 NWDVC5 LDY #$05
0210 JMP CHKNWP
0220 NWDVC7 LDY #$07
0230 JMP CHKNWP
0240 NWDVC9 LDY #$09
0250 JMP CHKNWP
0260 NWDVCB LDY #$0B
0270 JMP CHKNWP
Procedura ta odszukuje poprzez wywołanie GETLOW aktywne
urządzenie o najmniejszym numerze i przekazuje mu sterowanie
poprzez wywołanie NEWPER. Czynności te powtarzane są w pętli,
aż do obsłużenia wszystkich urządzeń. Ponadto znaczną część
procedury zajmują operacje przechowania niezbędnych parametrów
i ich późniejszego odtworzenia.
0100 ;CHecK NeW device Port
0110 ;
0120 CRITIC = $42
0130 GETLOW = $C9AF
0140 NEWPER = $C9CA
0150 PDVREG = $D1FF
0160 PDVRS = $0248
0170 PPTMPA = $024C
0180 PPTMPX = $024D
0190 ;
0200 *= $C9DC
0210 ;
0220 STA PPTMPA
0230 STX PPTMPX
0240 LDA CRITIC
0250 PHA
0260 LDA #$01
0270 STA CRITIC
0280 LDX #$08
0290 LOOP JSR GETLOW
0300 BEQ NEXDV
0310 TXA
0320 PHA
0330 TYA
0340 PHA
0350 JSR NEWPER
0360 BCC NEXT
0370 STA PPTMPA
0380 PLA
0390 PLA
0400 JMP END
0410 NEXDV LDY #$82
0420 END LDA #$00
0430 STA PDVRS
0440 STA PDVREG
0450 PLA
0460 STA CRITIC
0470 LDA PPTMPA
0480 STY PPTMPX
0490 LDY PPTMPX
0500 RTS
0510 NEXT PLA
0520 TAY
0530 PLA
0540 TAX
0550 BCC LOOP
Działanie procedury NEWPER jest identyczne, jak
znajdujących się w podsystemie CIO procedur GOHAND i CIOJMP
(zob. str. 24). Jednakże adres wywoływanej procedury jest
pobierany z pamięci ROM nowego urządzenia zależnie od operacji.
Zawartość rejestru Y określa kolejno: PDVOPV (Parallel DeVice
OPen Vector), PDVCLV (Parallel Device CLose Vector), PDVGBV
(Parallel Device Get Byte Vector), PDVPBV (Parallel Device Put
Byte Vector), PDVSTV (Parallel Device STatus Vector) i PDVSPV
(Parallel Device SPecial Vector).
0100 ;NEW device Port ERror
0110 ;
0120 PDVOPV = $D80D
0130 PPTMPA = $024C
0140 PPTMPX = $024D
0150 ;
0160 *= $C9CA
0170 ;
0180 LDA PDVOPV,Y
0190 PHA
0200 DEY
0210 LDA PDVOPV,Y
0220 PHA
0230 LDA PPTMPA
0240 LDX PPTMPX
0250 LDY #$92
0260 RTS
|