Serwis Edukacyjny
w I-LO w Tarnowie
obrazek

Materiały dla uczniów liceum

  Wyjście       Spis treści       Wstecz       Dalej  

Autor artykułu: mgr Jerzy Wałaszek

©2024 mgr Jerzy Wałaszek
I LO w Tarnowie

ROZDZIAŁ 8

System operacyjny

Podrozdziały

Wprowadzenie

Każdy komputer domowy ATARI wyposażony jest w 10 KB kartridż z systemem operacyjnym. Waga tego kartridża jest często niedostrzegana. Bez niego masz sprzęt z dużym potencjałem, lecz absolutnie nic więcej! Taka sytuacja nie jest unikalna tylko dla systemu komputerowego ATARI; spotyka się ją we wszystkich komputerach. Komputer jest przecież tylko zbiorem różnych modułów sprzętowych. Użytkownik musi zarządzać tymi zasobami, aby wykonać jakiekolwiek zadanie. Gdyby wszyscy programiści musieli zawsze rozpoczynać każdy program od początku, mielibyśmy o wiele mniej oprogramowania niż mamy dzisiaj. Rozwiązaniem, które ewoluowało przez lata, jest wbudowanie w komputer programu zarządzającego zasobami dostępnymi dla systemu i czyniącego brzemię zaprogramowania sterowania nimi lżejszym. Program ten jest znany pod różnymi nazwami: system operacyjny (ang. Operating System), główny program zarządzający (ang. Master Control Program), system wykonawczy (ang. System Executive), monitor systemu (ang. System Monitor), itp. W komputerze domowym ATARI program ten nazwany został systemem operacyjnym (ang. Operating System) lub w skrócie OS.

Pierwszym zadaniem, z którym styka się student systemu operacyjnego, jest dokładne skatalogowanie zasobów dostępnych dla OS. Są to:

Wykorzystując te zasoby, OS potrafi współpracować i sterować szerokim wachlarzem zewnętrznych urządzeń, łącznie z odbiornikiem telewizyjnym lub monitorem, klawiaturą, głośnikiem konsoli, przełącznikami konsoli, dżojstikami, wiosełkami, magnetofonem kasetowym, stacją dysków, drukarkami, interfejsem RS-232 i modemem.

Reszta tego podrozdziału krótko wymienia główne elementy OS. Elementy te są szczegółowo opisane w kolejnych podrozdziałach.


Na początek:  podrozdziału   strony 

Monitor

Monitor OS jest tą częścią systemu operacyjnego w ROM, która obsługuje obie sekwencje włączania i RESETU SYSTEMU. Sekwencje te pozwalają OS przejąć wstępną kontrolę nad komputerem i zapewnić, że wszystko zostanie poprawnie zainicjowane przed przekazaniem częściowej kontroli do programu aplikacji. Obie sekwencje są podobne w działaniu i faktycznie współdzielą dużą część tego samego kodu.

Procedura zimnego startu (ang. Coldstart) jest wywoływana albo przez włączenie komputera, albo przez skok pod wektor systemowy COLDSV ($E477). Oto kilka ważnych zagadnień do zapamiętania, które dotyczą sekwencji startowej:

  1. Wymazana zostaje cała pamięć RAM z wyjątkiem komórek $0000-$000F.
  2. Następuje próba załadowania programu z kasety i dysku. Znacznik BOOT? ($0009) wskazuje sukces lub porażkę tego ładowania. Bit 0 = 1 przy sukcesie startu z kasety; Bit 1 = 1 przy sukcesie startu z dysku.
  3. Znacznik COLDST ($0244) informuje Monitor, że jest on w trakcie sekwencji uruchamiania. COLDST=0 oznacza naciśnięcie klawisza [SYSTEM RESET], natomiast COLDST≠0 oznacza początkowy start po włączeniu zasilania. Znacznik COLDST można użyć w celu uzyskania pewnego stopnia bezpieczeństwa. Jeśli COLDST otrzyma niezerową wartość podczas wykonywania programu, to naciśnięcie klawisza [SYSTEM RESET] zainicjuje sekwencję włączania. Uniemożliwi to użytkownikowi przejęcie kontroli nad komputerem podczas pracy programu.

Naciśnięcie klawisza [SYSTEM RESET] powoduje reset systemu, zwany również ciepłym startem (ang. Warmstart). Powinieneś zapamiętać poniższe kluczowe fakty na temat resetu systemu:

  1. Wektory systemowe RAM są ładowane z pamięci ROM podczas zarówno sekwencji zimnego startu jak i resetu systemu. Jeśli chcesz "wykraść" jakiś wektor, musisz odpowiednio zatroszczyć się o reset systemu. Jak to się robi opisuje następny podrozdział "Zarządzanie pamięcią".
  2. Zmienne systemowe MEMLO, MEMTOP, APPMHI, RAMSIZ i RAMTOP są ustawiane na nowo podczas resetu systemu. Jeśli chcesz zmienić te wskaźniki RAM w celu rezerwacji miejsca w pamięci na moduły asemblerowe wywoływane z poziomu języka BASIC, musisz również zatroszczyć się o odpowiednią obsługę resetu systemu. Rys.8-3 udostępnia przykład realizacji tego zadania.

Poniżej znajduje się kilka szczegółowych schematów blokowych sekwencji zimnego (ang. Power-up) i ciepłego startu (ang. Reset).

Rys.8-1.1 Inicjalizacja systemu

Rys.8-1.2 Inicjalizacja systemu

Rys.8-1.3 Inicjalizacja systemu

Rys.8-1.4 Inicjalizacja systemu


Na początek:  podrozdziału   strony 

Zarządzanie pamięcią

To, iż system operacyjny napisano dla mikroprocesora 6502, dyktuje kilka ogólnych rozwiązań w zarządzaniu pamięcią. 6502 posiada trzy specjalne obszary w przestrzeni adresowej. Kluczowe znaczenie ma strona zerowa (komórki o adresach od $00 do $FF), ponieważ używanie danych przechowywanych na stronie zerowej daje w wyniku bardziej zwarty i szybszy kod. W rzeczywistości istnieją nawet instrukcje, które bezwzględnie wymagają do swojej pracy komórek ze strony zerowej. Strona pierwsza (komórki o adresach od $100 do $1FF) jest również specjalna, ponieważ mikroprocesor 6502 wykorzystuje ją na swój stos. Adresy $FFFA - $FFFF są też specjalne, ponieważ zarezerwowane zostały na wektory sprzętowego resetu i przerwań.

Dlatego pierwszym zadaniem zarządzania pamięcią jest przydzielenie obszaru pamięci ROM systemu operacyjnego w górnej części przestrzeni adresowej.  OS znajduje się w komórkach ROM o adresach od $D800 do $FFFF. Tuż pod tym obszarem jest przestrzeń zarezerwowana na rejestry sprzętowe w układach ANTIC, CTIA/GTIA i POKEY. Ich adresy obejmują zakres od $D000 do $DFFF.

Na drugim końcu przestrzeni adresowej OS rezerwuje połowę strony zerowej do własnego użytku. Strony dwa, trzy, cztery i pięć są również zarezerwowane na potrzeby OS. Z punktu widzenia programisty obszar użytecznej pamięci rozciąga się od $0600 do $BFFF.

Gdy system zostanie włączony, jednym z pierwszych działań podejmowanych przez OS jest określenie ilości dostępnej pamięci RAM. Wykonuje się to przez sprawdzanie pierwszego bajtu każdego 4 KB bloku pamięci poczynając od adresu $1000. Zawartość tego bajtu zostaje odczytana, zanegowana i następuje próba zapisu tej zanegowanej wartość z powrotem do pierwszego bajtu. Jeśli ta próba się powiedzie, to tymczasowy licznik rozmiaru pamięci jest zwiększany o 4 K. Proces jest kontynuowany aż do znalezienia komórki, której nie da się zmienić. Dwie zmienne, RAMTOP i RAMSIZ zawierają liczbę obecnych stron pamięci RAM. Obrócz tych komórek procedury zarządzania pamięcią w OS utrzymują wskaźniki MEMLO, MEMTOP i APPMHI. Związek pomiędzy nimi pokazuje rys.8-2.

MEMLO jest 2-bajtowym wskaźnikiem używanym przez OS do wskazywania w pamięci początku obszaru, w którym może rozpocząć się program aplikacji. Możesz modyfikować MEMLO, aby stworzyć zarezerwowane obszary na procedury w języku maszynowym, które mogą być wywoływane z poziomu języka BASIC. Język BASIC wykorzystuje MEMLO do określenia początku programu (zobacz do rozdziału "Basic ATARI", gdzie przedyskutowano strukturę programów w języku BASIC). Jeśli wartość MEMLO jest zmieniana na wyższy adres, to musi to być zrobione przed oddaniem kontroli kartridżowi BASIC. Jest to dosyć kłopotliwe zadanie, ponieważ wartość MEMLO jest resetowana przez zimny i gorący start.

Jeśli program aplikacji pracuje w środowisku ze stacją dyskietek, to narzędzie AUTORUN.SYS może zostać użyte do zmiany MEMLO w celu rezerwacji miejsca. Jednakże DOS jest również inicjowany w czasie resetu systemu poprzez wektor DOSINI ($000C), który zawiera adres kodu inicjujacego dyskowy system operacyjny jako część inicjalizacji systemu monitora. DOSINI jest również jedynym miejscem, w którym możesz "przechwycić" sekwencję resetu systemowego. Ponieważ inicjalizacja DOS musi wystąpić bez względu na to, co jest robione ze wskaźnikiem MEMLO, musisz pozwolić na wykonanie normalnej inicjalizacji przed "wykradzeniem" wektora DOSINI. Można tego dokonać przez przeniesienie zawartości DOSINI do 2-bajtowego argumentu instrukcji JSR w AUTORUN.SYS. Tuż po tej instrukcji wstaw kod ustawiający nową wartość w MEMLO. Zakończ kod instrukcją RTS. Następnie w wektorze DOSINI należy umieścić adres tej instrukcji JSR. Gdy wystąpi reset systemu, zostanie wykonana nowa sekwencja kodu i pierwsza instrukcja JSR STARE_DOSINI zainicjuje DOS. Następnie będzie wykonana reszta kodu, która ustawi MEMLO na jego nową wartość i instrukcja RTS połączy ten nowy kod z resztą sekwencji inicjalizacji. Rys.8-3 przedstawia przykład pokazujący, jak to zrobić.

Powyższą technikę można również zastosować z MEMTOP, wskaźnikiem góry pamięci użytkownika. Wskaźnik ten zawiera najwyższy adres w pamięci RAM dostępny dla programu aplikacji. Adres ten różni się od maksymalnego adresu fizycznej pamięci RAM, ponieważ OS przydziela nieco pamięci RAM na samym jej szczycie dla swojej listy wyświetlania i danych ekranowych. Osobne miejsce na moduły w języku asemblera można ustawić przez obniżenie MEMTOP z wartości ustawianej przez zimny i ciepły start. Wykorzystanie MEMTOP zamiast MEMLO do rezerwacji miejsca stwarza jednak jeden problem. Wartość MEMTOP zależy zarówno od ilości RAM w systemie jak i od trybu graficznego obrazu. Trudno jest przewidzieć jego wartość przed rzeczywistym sprawdzeniem tego wskaźnika, chyba że zakładasz określoną konfigurację systemu. Ta niepewność co do ostatecznego położenia kodu maszynowego zmusza programistę do używania tylko kodu przemieszczalnego.

APPMHI wskazuje najniższy adres, do którego może rozciągać się pamięć RAM ekranu. Właściwe ustawienie APPMHI zapewnia, iż sterownik ekranu nie nadpisze części twojego programu lub danych.

RAMSIZ, podobnie jak MEMTOP, może również być wykorzystane do rezerwacji miejsca na procedury lub dane użytkownika. Ponieważ RAMSIZ jest wartością 1-bajtową zawierającą liczbę dostępnych stron RAM (tj, grup 256 bajtów), obniżenie jej o 1 zarezerwuje 256 komórek. Zaletą używania RAMSIZ zamiast MEMTOP jest to, iż obszar zarezerwowany przez obniżenie RAMSIZ znajduje się ponad pamięcią ekranu, natomiast obszar zarezerwowany przez obniżenie wartości MEMTOP pozostaje poniżej pamięci ekranu.

Rys.8-2. Wskaźniki OS i BASIC (DOS obecny)

10 ; RESET WSKAŹNIKA MEMLO
20 ;
30 START     =     $600
40 DOSINI    =     $0C
50 MEMLO     =     $2E7
60 NEWMEM    =     $3000 ;TO JEST NOWA WARTOŚĆ DLA MEMLO
65 ;
70 ;TA PROCEDURA REZERWUJE MIEJSCE NA PROCEDURY ASEMBLEROWE
90 ;PRZEZ RESETOWANIE WSKAŹNIKA MEMLO. URUCHAMIANA JEST JAKO
0100 ;PLIK AUTORUN.SYS. RESETUJE RÓWNIEŻ MEMLO PO CIEPŁYM STARCIE.
0120 ;MEMLO JEST USTAWIANE NA WARTOŚĆ NEWMEM.
0130 ;
0140 ; TA CZĘŚĆ JEST STAŁA, TZN. MUSI BYĆ REZYDENTNA.
0150 ; WEKTOR SYSTEMOWY ZOSTAJE SKRADZIONY
0160 ; I UMIESZCZONY W CZĘŚCI ADRESOWEJ INSTRUKCJI JSR TROJAN
0170 ; GDY ZOSTANIE NACIŚNIĘTY PRZYCISK [RESET] IS VEKTOR DOSINI WSKAZUJE
0180 ; NA INITDOS, NASTĘPNIE JSR TROJAN WYWOŁUJE INICJALIZACJĘ DOS,
0185 ; RESETUJE MEMLO INA NOWĄ WARTOŚĆ I ZWRACA STEROWANIE
0190 ; DO MONITORA SYSTEMU.
0200      *=  START
0210 INITDOS
0220     JSR TROJAN ; WYKONAJ INICJALIZACJĘ DOS
0230 SETMEMLO
0240     LDA #NEWMEM&255
0250     STA MEMLO
0260     LDA #NEWMEM/256
0270     STA MEMLO+1
0280 TROJAN
0290     RTS
0300 ; TA CZĘŚĆ JEST WYKONYWANA TYLKO PRZY ZIMNYM STARCIE,
0310 ; PO CZYM MOŻNA JĄ WYKASOWAĆ.
0320 ; PROCEDURA UMIESZCZA ZAWARTOŚĆ WEKTORA DOSINI W INSTRUKCJI
0330 ; JSR TROJAN. NASTĘPNIE ZASTĘPUJE WEKTOR DOSINI WSKAZANIEM
0340 ; NA ADRES INITDOS.
0350 GRABDOSI
0400     LDA DOSINI       ; ZAPAMIĘTAJ DOSINI
0410     STA INITDOS+1
0420     LDA DOSINI+1
0430     STA INITDOS+2
0440     LDA #INITDOS&255 ; USTAW DOSINI
0450     STA DOSINI
0460     LDA #INITDOS/256
0470     STA DOSINI+1
0480     JMP SETMEMLO     ; USTAW MEMLO
0530     *=  $2E2
0540     .WORD GRABDOSI   ; USTAW ADRES STARTOWY
0550     .END

Rys.8-3. Resetowanie MEMLO


Na początek:  podrozdziału   strony 

Struktura przetwarzania przerwań

Możliwość wybiórczej odpowiedzi na specjalne zdarzenia sprzętowe i programowe (tj. przerwania) dostarcza olbrzymiej elastyczności każdemu systemowi komputerowemu. Jak w każdym systemie opartym na mikroprocesorze 6502 istnieją dwa rodzaje żądań przerwań na poziomie procesora: przerwania maskowane (IRQ) i niemaskowane (NMI). Wyższy poziom kontroli nad przerwaniami udostępniają układy ANTIC, POKEY i PIA. Każdy z tych układów odpowiada za obsługę pewnej liczby zdarzeń, które mogą spowodować przerwanie. Jeśli określone przerwanie zostało włączone na poziomie tych trzech układów nadzorczych, to pozwalają one następnie na przejście żądania przerwania do mikroprocesora 6502. ANTIC zajmuje się żądaniami przerwań niemaskowanych NMI, a POKEY i PIA obsługują żądania przerwań maskowanych IRQ.

Dostępne są następujące funkcje przerwań:

Nazwa (wektor) Typ Funkcja Używane przez
LISTA WYŚWIETLANIA (VDSLST)
RESET SYSTEMU (brak)
WYGASZANIE PIONOWE (VVBLKI,VVBLKD)
GOTOWOŚĆ WEJŚCIA SZEREGOWEGO (VSERIN)
GOTOWOŚĆ WYJŚCIA SZEREGOWEGO (VSEROR)
ZAKOŃCZENIE PRZESYŁANIA SZEREGOWEGO (VSEROC)
TIMER 1 POKEY (VTIMR1)
TIMER 2 POKEY (VTIMR2)
TIMER 4 POKEY (VTIMR4)(*)
KLAWIATURA (VKEYBD)
KLAWISZ BREAK (BRKKY)(*)
DALSZA TRANSMISJA PO MAGISTRALI SZEREGOWEJ (VPRCED)
PRZERWANIE Z MAGISTRALI SZEREGOWEJ (VINTER)
NMI
NMI
NMI
IRQ
IRQ
IRQ
IRQ
IRQ
IRQ
IRQ
IRQ
IRQ
IRQ
Taktowanie grafiki
Inicjalizacja systemu
Wyświetlanie grafiki
Wejście szeregowe
Wyjście szeregowe
Wyjście szeregowe
Timer sprzętowy
Timer sprzętowy
Timer sprzętowy
Naciśnięcie klawisza
Klawisz [BREAK]
Kontynuacja pracy urządzenia
Przerwanie z urządzenia
Użytkownika
OS
OS, użytkownika
OS
OS
OS
Użytkownika
Użytkownika
Użytkownika
OS
OS
Nieużywane
Nieużywane

* To żądanie przerwania IRQ posiada swój wektor tylko wersji B systemu operacyjnego ATARI OS

Rozdział 6 instrukcji systemu operacyjnego zawiera bardziej szczegółowe informacje na temat przerwań. Należy podejmować ekstremalne środki ostrożności przy pracy z przerwaniami. Na przykład, jeśli przypadkowo zablokujesz przerwania IRQ klawiatury, to komputer będzie ignorował wszystkie klawisze za wyjątkiem klawisza [BREAK]. Chociaż może to być czasami użyteczne, to jednak utrudnia debugowanie twojego programu!

Sterownik przerwań IRQ

System operacyjny posiada sterownik przerwań IRQ, który przetwarza ich różne rodzaje. Sterownik ma wektory w RAM dla wszystkich przerwań IRQ (Zauważ, iż przerwanie IRQ od klawisza [BREAK] nie ma wektora w oryginalnej wersji OS). Wektory są ustawiane na swoje wartości początkowe zarówno przez zimny jak i ciepły start. Adresy tych wektorów podaje następny podrozdział.

Funkcje wektorów IRQ są następujące:

VIMIRQ – Wektor bezpośredni IRQ (ang. Immediate IRQ vector). Wszystkie przerwania IRQ przechodzą przez wektor pod tym adresem. VIMIRQ normalnie wskazuje na systemowy sterownik przerwań IRQ. Możesz podmienić ten wektor na taki, który będzie wskazywał na twoją procedurę przetwarzania przerwań IRQ.

VSEROR – Wektor IRQ Żądania Danych Dla Wyjścia Szeregowego POKEY (ang. Pokey Serial Output Needed IRQ vector). Normalnie wskazuje na kod dostarczający następny bajt z bufora do szeregowego portu wyjściowego.

VSERIN – Wektor IRQ Gotowości Odczytu z Wejścia Szeregowego POKEY (ang. Pokey Serial Input Ready IRQ vector). Wskazuje na kod, który umieszcza w buforze bajt z wejścia portu szeregowego.

VSEROC – Wektor IRQ Zakończenia Transmisji Szeregowej POKEY (ang. Pokey Serial Output Complete IRQ vector). Normalnie wektor ten wskazuje kod, który ustawia znacznik zakończenia transmisji po wysłaniu bajtu z sumą kontrolną.

VTIMR1 – Wektor IRQ Timera 1 POKEY (ang. Pokey Timer 1 IRQ vector). Inicjowany na wskazywanie sekwencji instrukcji PLA, RTI.

VTIMR2 – Wektor IRQ Timera 2 POKEY (ang. Pokey Timer 2 IRQ vector). Inicjowany na wskazywanie sekwencji instrukcji PLA, RTI.

VTIMR4 – Wektor IRQ Timera 4 POKEY (ang. Pokey Timer 4 IRQ vector). Inicjowany na wskazywanie sekwencji instrukcji PLA, RTI.

VKEYBD – Wektor IRQ Klawiatury (ang. Keyboard IRQ vector). Przerwanie to jest powodowane przez naciśnięcie dowolnego klawisza za wyjątkiem [BREAK]. VKEYBD można wykorzystywać do wczesnego przetwarzania kodów klawiszy zanim zostaną zamienione na ATASCII przez OS. VKEYBD normalnie wskazuje na procedurę obsługi klawiaturowego IRQ w OS.

BRKKY – Wektor klawisza [BREAK] (ang. [BREAK] key vector). W wersji B systemu operacyjnego to przerwanie IRQ posiada własny wektor. Inicjowany na wskazywanie sekwencji instrukcji PLA, RTI.

VPRCED – Wektor przerwania IRQ do rozpoczęcia transmisji przez urządzenie peryferyjne (ang. Peripheral Proceed IRQ vector). Linia rozpoczęcia dostępna jest dla urządzeń peryferyjnych na magistrali szeregowej. Przerwanie nie jest obecnie wykorzystywane i normalnie wektor ten wskazuje sekwencję instrukcji PLA, RTI.

VINTER – Wektor przerwania IRQ z urządzenia peryferyjnego (ang. Peripheral interrupt IRQ vector). Linia przerwania jest również dostępna na magistrali szeregowej. VINTER normalnie wskazuje sekwencję instrukcji PLA, RTI.

VBREAK – Wektor iIRQ nstrukcji BRK mikroprocesora 6502 (ang. 6502 BRK instruction IRQ vector). Przerwanie to występuje zawsze po napotkaniu przez mikroprocesor instrukcji BRK (przerwanie programowe). VBREAK może być wykorzystany do ustawiania punktów przerwań dla debuggera, chociaż normalnie wskazuje sekwencję instrukcji PLA, RTI.

Przerwania IRQ są włączane i wytłaczane grupowo odpowiednio przez instrukcje CLI i SEI mikroprocesora 6502. Przerwania posiadają również indywidualne bity włączania/wyłączania w układzie POKEY.

Rejestr IRQEN zawiera większość bitów sterujących włączaniem/wyłączaniem przerwań IRQ i jest rejestrem tylko do zapisu.

OS utrzymuje kopię rejestru IRQEN w pamięci RAM w POKMSK ($0010), lecz IRQEN nie jest uaktualniany z POKMSK podczas wygaszania pionowego. Każde przerwanie jest włączane przez ustawienie na jeden odpowiedniego bitu w IRQEN. Zero jest umieszczane w bicie w IRQEN w celu wyczyszczenia stanu przerwania w odpowiednim bicie w IRQST. Możesz zauważyć, iż bit 3 w IRQST (zakończenie transmisji szeregowej) nie jest zerowany przez ten proces. Ten bit jest po prostu bitem stanu, który odzwierciedla bieżący stan rejestru transmisji szeregowej.

Rejestry PACTL i PBCTL są wykorzystywane do włączania przerwań IRQ obsługiwanych przez układ PIA i do testowania ich stanu. Bit 0 każdego z tych rejestrów włącza przerwanie z danego portu. Bit 7 reprezentuje stan przerwania. Bit ten jest zerowany po odczycie rejestru PACTL lub PBCTL.

Używanie przerwań IRQ

Dostępność wektorów IRQ oznacza, iż możesz przystosować dużą część systemowych operacji wejścia/wyjścia do swoich potrzeb. Bieżąco system operacyjny nie wspiera wykonywania operacji we/wy współbieżnie z innym przetwarzaniem. Jednakże przez zmianę trzech wektorów przerwań z szeregowego we/wy jest możliwe ponowne napisanie części podsystemu we/wy, aby możliwe było przetwarzanie współbieżne.

Trzy przerwania timerów mogą być użyte w każdej operacji wymagającej precyzyjnej kontroli czasu. Timery te normalnie są używane w sytuacji, gdy 60-hercowe timery programowe są zbyt wolne. Więcej informacji na ten temat znajdziesz w podrozdziale "Przetwarzanie w czasie rzeczywistym".

Wiele aplikacji wymaga, aby programy były zabezpieczone przed błędami wejściowymi użytkownika. Kilka wektorów IRQ można użyć do zapewnienia takiej poszerzonej ochrony. Poniższy przykład programu używa wektora IRQ  VKEYBD do wyłączenia klawisza [CONTROL]. Procedura również maskuje klawisz [BREAK] przez podmianę wektora VIMIRQ i ignorowanie przerwania z klawisza [BREAK]. Chociaż procedurę napisano dla oryginalnej wersji OS, to będzie działała również z wersją B.

Dwa przerwania IRQ są obsługiwane przez układ PIA, VPRCED i VINTER. System operacyjny ich nie używa i można je zagospodarować w celu uzyskania większej kontroli nad urządzeniami zewnętrznymi.

10 POKMSK =    0010
20 KBCODE =    $D209
30 VKEYBD =    $0208
40 IRQEN  =    $D20E
45 IRQST  =    IRQEN
46 VMIRQ  =    $0216
60        *=   $600
80 START  SEI  ;        WYŁĄCZ PRZERWANIA IRQ
90        LDA  VMIRQ   ;PODMIEŃ WEKTORY IRQ
0100      STA  NBRK+1  ;NASZYMI WŁASNYMI
0110      LDA  VMIRQ+1 ;WSZYSTKIE IRQS
0120      STA  NBRK+2  ;PRZEJDĄ DO NBRK
0130      LDA  #IRQ&255
0140      STA  VMIRQ
0150      LDA  #IRQ/256
0160      STA  VMIRQ+1
0170      CLI  ;WŁĄCZ PRZERWANIA IRQ
0200      LDA  VKEYBD  ;USTAW IRQ KLAWISZA
0210      STA  JUMP+1  ;NA REP
0220      LDA  VKEYBD+1
0230      STA  JUMP+2
0240      LDA  #REP&255 ;USTAW WEKTOR IRQ KLAWISZA
0250      STA  VKEYBD   ;DOLNY BAJT WEKTORA
0260      LDA  #REP/256
0270      STA  VKEYBD+1
0280      RTS
0290      *=$639
0300 REP  LDA  KBCODE ;WSZYSTKIE PRZERWANIA IRQ KLAWISZY IDĄ TUTAJ
0310      AND  #$80   ;SPRAWDŹ, CZY NACIŚNIĘTO KLAWISZ CONTROL
0320      BEQ  JUMP   ;JEŚLI NIE, IDŹ DALEJ
0330      PLA  ;INACZEJ IGNORUJ KLAWISZ CONTROL
0340      RTI
0360 JUMP JMP  ;TO WYWOŁA STARE IRQ KLAWISZA
0375 IRQ  PHA  ;WSZYSTKIE IRQ PRZYCHODZĄ TUTAJ
0380      LDA  IRQST ; SPRAWDŹ KLAWISZ [BREAK]
0390      BPL  BREAK ; JEŚLI [BREAK] NACIŚNIETY, PRZEJDŹ DALEJ
0405      PLA  ; INACZEJ WYWOŁAJ STARY WEKTOR IRQ
0410 NBRK JMP  NBRK ; WYWOŁAJ STARY WEKTOR IRQ
0430 BREAK LDA  #$7F ; TUTAJ PO [BREAK]
0440      STA  IRQST ; KASUJ [BREAK]
0450      LDA  POKMSK
0460      STA  IRQEN
0462      PLA
0464      RTI ; WRÓĆ JAKBY NIE BYŁO [BREAK]
0470      *=  $02E2
0480      .WORD START

Rys.8-4. Ochrona programów przed błędami wejściowymi użytkownika

Sterownik przerwań niemaskowanych NMI

System operacyjny posiada sterownik przerwań NMI do obsługi przerwań niemaskowanych. W przeciwieństwie do przerwań IRQ przerwania NMI nie mogą być "maskowane" (wyłączane) na poziomie mikroprocesora 6502. Wszystkie przerwania NMI z wyjątkiem resetu systemu mogą zostać zablokowane przez układ ANTIC.

Dwa przerwania NMI, przerwanie z listy wyświetlania (DLI) oraz przerwanie przy wygaszaniu pionowym (VBLANK), posiadają wektory w pamięci RAM i można je wykorzystać. W rzeczywistości VBLANK można przechwycić w dwóch miejscach jako natychmiastowe i końcowe VBLANK. Wektory NMI są następujące:

Nazwa Wektor
RESET SYSTEMU
PRZERWANIE Z LISTY WYŚWIETLANIA
WYGASZANIE PIONOWE
   POCZĄTKOWE
   KOŃCOWE
brak
VDSLST ($0200)

VVBLKI ($0222)
VVBLKD ($0224)

Przerwanie NMI przy resecie systemu nie posiada wektora w pamięci RAM. Reset systemu zawsze skutkuje skokiem do procedury ciepłego startu w monitorze. Jednakże podczas procesu ciepłego startu wykorzystywany jest wektor w RAM DOSINI, co pozwala przechwycić reset systemu (zobacz do podrozdziału "Monitor").

Wektor DLI nie jest używany przez OS. Zobacz do rozdziału 5, "Przerwania z listy wyświetlania".

Przetwarzanie przerwania przy wygaszaniu pionowym

Funkcja przerwań przy wygaszaniu pionowym jest bardzo cennym zasobem dla programisty. Przerwania te są niemaskowane i występują w regularnych odstępach czasu zależnych od używanego standardu telewizyjnego (co 1/60 sekundy w systemie NTSC i co 1/50 sekundy w systemie PAL). Ważne jest również to, iż przerwania występują podczas okresu czasu, gdy ekran został wygaszony, zatem zmiany wykonane w tym okresie nie będą natychmiast widoczne na ekranie. To prowadzi do szerokiego wachlarza zastosowań.

Gdy system operacyjny rozpozna przerwanie od wygaszania pionowego, umieszcza on zawartość rejestrów A, X i Y na stosie. Następnie system wykonuje skok poprzez natychmiastowy wektor wygaszania pionowego (VVBLKI) umieszczony pod adresem $0222. Wektor ten normalnie prowadzi do systemowej procedury obsługi przerwania od wygaszania pionowego pod adresem ROM $E45F. OS używa tej procedury do zwiększania stanu zegara czasu rzeczywistego, zmniejszania timerów systemowych, do wykonania zmiany kolorów w trybie przyciągania uwagi (oszczędzanie monitora), do skopiowania rejestrów w RAM i do uaktualnienia wartości z kontrolerów gier. Procedura kończy się skokiem poprzez wektor końcowego wygaszania pionowego (VVBLKD) umieszczony pod adresem $0224. Wektor ten jest inicjowany na wskazywanie adresu w ROM $E462, gdzie znajduje się procedura kończenia przerwania przy wygaszaniu pionowym. Rys.8-5 ilustruje ten proces.


Rys.8-5. Wykonanie przerwania od wygaszania pionowego

Te dwa wektory zostały umieszczone w pamięci RAM, aby umożliwić programistom przechwycenie procedury obsługi przerwania od wygaszania pionowego i użycie tego przerwania 60Hz do własnych celów. Procedura użycia ich jest prosta. Najpierw zdecyduj, czy twoja procedura obsługi przerwania VBI ma być początkowa lub końcowa. W wielu przypadkach nie ma to znaczenia. Jednakże czasem ma znaczenie. Pierwszy taki przypadek występuje, gdy twoja procedura VBI czyta lub zapisuje do rejestrów, które mają kopie w RAM obsługiwane przez procedurę VBI w OS. Może być konieczny zapis do rejestrów sprzętowych po tym, jak procedura VBI w OS wykonała swój zapis do nich. Ten, który zapisuje ostatni, zapisuje najlepiej!

Drugi przypadek występuje, gdy twoja procedura VBI pochłania zbyt wiele czasu przetwarzania. Wtedy procedura VBI w OS może zostać opóźniona poza koniec okresu wygaszania pionowego. To z kolei może spowodować zmianę niektórych rejestrów graficznych, gdy strumień elektronów przebiega po powierzchni ekranu. Wynik może być nieprzewidywalny. Jeśli tak jest, to twoja procedura VBI powinna być umieszczona przy końcu VBI. Ograniczeniem dla początkowej procedury VBI jest około 2000 taktów maszynowych, natomiast dla końcowej procedury VBI granica ta wynosi około 20.000 taktów. Jednakże wiele z tych 20,000 taktów maszynowych jest wykonywane, gdy strumień elektronu rysuje już obraz po ekranie, zatem operacje graficzne nie powinny być wykonywane w bardzo długich procedurach końcowych VBI. Co więcej wykonywanie przerwań z listy wyświetlania opóźnia się w czasie. Pamiętaj, przetwarzanie VBI obetnie czas przeznaczony na wykonywanie głównego kodu.

Trzeci przypadek powstaje, gdy twoje własne VBI musi mieszać się z krytycznymi czasowo operacjami we/wy, takimi jak dostęp do kasety lub do dysku. Początkowa procedura VBI posiada dwa etapy: krytyczny i niekrytyczny. Podczas operacji we/wy krytycznych czasowo początkowa procedura VBI w OS kończy się po etapie pierwszym. Jeśli chcesz, aby twoja procedura VBI była wykonywana przy każdym okresie wygaszania pionowego, to musi być zdefiniowana jako początkowe VBI. Bądź jednak czujny w tej sytuacji, ponieważ może to stworzyć problemy interferencji z krytycznymi czasowo operacjami we/wy.

Gdy już zdecydujesz, czy twoja procedura VBI ma być początkową lub końcową, musisz ją umieścić w pamięci (strona szósta jest dobrym miejscem), połączyć ją z właściwą procedurą VBI i zmodyfikować odpowiedni wektor w RAM, aby na nią wskazywał. Procedurę początkową zakończ skokiem pod adres $E45F. Procedurę końcową VBI zakończ skokiem pod adres $E462. Jeśli chcesz zupełnie pominąć systemową procedurę VBI (zyskując w ten sposób więcej czasu na przetwarzanie), zakończ swoją początkową procedurę VBI skokiem pod adres $E462.

Typowy problem z przerwaniami na 8-bitowych mikrokomputerach powstaje, gdy próbujesz zmieniać wektor do przerwania. Wektory są obiektami 2-bajtowymi; ich zmiana zabiera dwie instrukcje zapisu do pamięci. Istnieje mała szansa, iż przerwanie wystąpi po zmianie pierwszego bajtu, lecz przed załadowaniem drugiego bajtu. Może to doprowadzić do zawieszenia się systemu. Rozwiązaniem tego problemu jest systemowa procedura SETVBV pod adresem $E45C. Załaduj rejestr Y mikroprocesora 6502 dolnym bajtem adresu, rejestr X górnym bajtem adresu, a akumulator liczbą 6 dla początkowej VBI lub 7 dla końcowej VBI. Następnie wykonaj instrukcję JSR SETVBV i przerwanie zostanie bezpiecznie włączone. Nowa procedura VBI zacznie się wykonywać w ciągi 1/60 sekundy.

Duża ilość różnorodnych operacji może być wykonywana przy pomocy przerwań 60-hercowych. Po pierwsze operacje ekranowe mogą być wykonywane podczas wygaszania pionowego w celu zapewnienia, iż zmiany te nie będą widoczne na ekranie. Po drugie można wykonywać regularne operacje ekranowe z dużą prędkością. Może to być bardzo ważne w rytmicznych animacjach, gdzie zmiany muszą być wykonywane w tempie niezależnym od innego przetwarzania.

Inną funkcją przerwań od wygaszania pionowego są efekty dźwiękowe w czasie rzeczywistym. Rejestry dźwiękowe w ATARI 400/800 pozwalają kontrolować częstotliwość, głośność i zniekształcenie, lecz nie czas trwania dźwięku. Długość dźwięku można kontrolować przy pomocy VBI przez ustawienie w procedurze wywołującej parametru związanego z długością, który następnie procedura VBI zmniejsza o 1 aż do zera. Ta technika może być użyta do sterowania głośnością dźwięku nadając mu odpowiednią obwiednię. Precyzyjna kontrola częstotliwości i zniekształcenia jest również możliwa poprzez rozszerzenie tej techniki. W efekcie można otrzymać bardzo zawiłe efekty dźwiękowe. Ponieważ czasowa rozdzielczość VBI wynosi tylko 1/60 sekundy, przerwania VBI nie nadają się do sterowania jedynie głośnością kanałów dźwiękowych.

Przerwania VBl są również użyteczne do obsługi danych wejściowych od użytkownika. Zwykle te dane wymagają niewiele przetwarzania, lecz ciągłej uwagi. VBI pozwala sprawdzać dane użytkownika co 1/60 sekundy bez niepokojenia programu głównego. Jest to idealne rozwiązanie dla utrzymania ciągłości obliczeniowej bez ignorowania użytkownika.

Na koniec VBI pozwalają na prostą formę przetwarzania współbieżnego. Program pierwszoplanowy pracuje pod kontrolą VBI, natomiast program w tle pracuje w kodzie głównym. Jak z każdym przerwaniem należy utrzymać dokładną separację przetwarzanych danych w obu programach. Jednakże pożytek z przerwania pionowego wygaszania jest tak duży, iż warte to jest zachodu.


Na początek:  podrozdziału   strony 

Wektory systemowe

Jedną z miar potęgi systemu operacyjnego jest jego przystosowalność. Jak łatwo jest użytkownikowi skorzystać z procedur OS lub zmodyfikować i przystosować procedury systemowe?

W tym względzie system operacyjny komputera domowego ATARI osiąga wysokie noty. W praktycznie każdym przypadku, w którym dostęp do procedur systemowych byłby korzystny, OS posiada "haczyki", gdzie możesz dołączyć się do procedur systemowych lub je zastąpić swoimi własnymi.

Ta elastyczność udostępniana jest przez kombinację kilku różnych mechanizmów. Pierwszym z nich jest tablica w ROM zawierająca instrukcje skoków JMP do najważniejszych procedur OS. W przyszłych wersjach OS położenie tej tablicy skoków nie ulegnie zmianie, chociaż jej wartości prawdopodobnie tak. Dlatego, jeśli twoje oprogramowanie uzyskuje dostęp do głównych procedur OS poprzez tą tablicę, to będzie ono również pracowało na przyszłych wersjach OS. Jeśli natomiast twoje oprogramowanie nie używa tych wektorów w ROM, lecz zamiast tego skacze bezpośrednio do OS ROM, to prawie na pewno ulegnie awarii po uruchomieniu w przyszłej wersji OS.

Drugim mechanizmem jest ciąg wektorów adresowych w RAM, które prowadzą do wielu procedur przetwarzających przerwania. Aby zmienić sposób obsługi określonego przerwania, należy jedynie zmienić pojedynczy wektor, tak aby wskazywał na nowy kod. OS inicjuje te wektory w sekwencji uruchamiania komputera. Ponownie, chociaż inicjowana zawartość tych wektorów może się zmieniać, to jednak ich położenie nie.

Trzecim mechanizmem jest tablica sterowników urządzeń, w której przechowywane są wektory do specyficznych sterowników (np. stacji dysków, drukarki, ...). Więcej informacji na ten temat znajdziesz dalej w tym rozdziale.

Nazwa Adres Użycie
DISKIV
DSKINV
CIOV
SIOV
SETVBV
SYSVBV
XITVBV
SIOINV
SENDEV
INTINV
CIOINV
BLKBDV
WARMSV
COLDSV
RBLOKV
CSOPIV
$E450
$E453
$E456
$E459
$E45C
$E45F
$E462
$E465
$E468
$E46B
$E46E
$E471
$E474
$E477
$E47A
$E47D
Inicjalizacja sterownika dysku
Wektor sterownika dysku
Wektor centralnej procedury we/wy
Wektor procedury szeregowego we/wy
Wektor procedury ustawiania timerów systemowych
Systemowe przetwarzanie wygaszania pionowego
Wyjście z przetwarzania wygaszania pionowego
Inicjalizacja szeregowego we/wy
Procedura uaktywnienia przesyłu po magistrali szeregowej
Procedura sterownika przerwań
Inicjalizacja centralnego we/wy
Wektor trybu Blackboard (Memopad)
Punkt wejścia do ciepłego startu (RESET SYSTEMU)
Punkt wejścia do zimnego startu (włączanie zasilania)
Wektor procedury odczytu bloku z kasety
Wektor otwarcia kasety do odczytu

Rys.8-6 Wektory skoków w ROM

Ponieważ ta tablica w ROM jest w rzeczywistości zbudowana z 3-bajtowych instrukcji skoku JMP, to przykładowe użycie wektora w ROM wygląda następująco:

JSR CIOV

Wektory w pamięci RAM

Nazwa Adres Wartość Użycie
-- Adresy na stronie drugiej --
VDSLST
VPRCED
VINTER
VBREAK
VKEYBD
VSERIN
VSEROR
VSEROC
VTIMR1
VTIMR2
VTIMR4
VIMIRQ
VVBLKI
VVBLKD
CDTMA1
CDTMA2
BRKKY
$0200
$0202
$0204
$0206
$0208
$020A
$020C
$020E
$0210
$0212
$0214
$0216
$0222
$0224
$0226
$0228
$0236
$E7B3
$E7B3
$E7B3
$E7B3
$FFBE
$EB11
$EA90
$EAD1
$E7B3
$E7B3
$E7B3
$E6F6
$E7D1
$E93E
$xxxx
$xxxx
$E754
Wektor przerwania NMI od listy wyświetlania
Wektor IRQ linii kontynuacji -- obecnie nieużywany
Wektor IRQ linii przerwania -- obecnie nieużywany
Wektor IRQ przerwania programowego instrukcją BRK
Wektor IRQ klawiatury
Wektor IRQ gotowości wejścia szeregowego
Wektor IRQ gotowości wyjścia szeregowego
Wektor IRQ zakończenia nadawania szeregowego
Wektor IRQ Timera 1 układu POKEY
Wektor IRQ Timera 2 układu POKEY
Wektor IRQ Timera 4 układu POKEY
Wektor do sterownika IRQ
Wektor NMI początku wygaszania pionowego
Wektor końca wygaszania pionowego
Adres JSR Timera systemowego 1
Adres JSR Timera systemowego 2
Wektor klawisza BREAK (** tylko wersja B **)
-- Adresy na stronie zerowej --
CASINI
DOSINI
DOSVEC
RUNVEC
INIVEC
$0002
$000C
$000A
$02E0
$02E2
$xxxx
$xxxx
$xxxx
$xxxx
$xxxx
Wektor inicjalizacyjny programu startowego z kasety
Wektor inicjalizacji dysku
Wektor uruchamiania oprogramowania dyskowego
Wektor ładowania i uruchamiania pliku DUP
Wektor inicjalizacji ładowania i uruchamiania pliku DUP

Literka x oznacza, iż zawartość tego adresu może ulegać zmianie

Rys.8-7 Wektory w pamięci RAM

W przeciwieństwie do tablicy skoków w ROM wektory w RAM są prawdziwymi 2-bajtowymi wektorami adresowymi. Typowy ciąg wywołania jednego z wektorów w RAM może wyglądać następująco:

      JSR CALL
CALL  JMP (DOSINI)

Na początek:  podrozdziału   strony 

Scentralizowany podsystem we/wy

Jednym z najbardziej wymagających problemów stojących przed projektantem systemu operacyjnego jest określenie sposobu obsługi dużej liczby różnorodnych urządzeń we/wy, które mogą zostać podłączone do tego systemu. Oto kilka ogólnych, filozoficznych wskazówek efektywnej obsługi we/wy:

System operacyjny komputera domowego ATARI 400/800 (OS) został zaprojektowany dokładnie wg powyższych wskazówek. OS ATARI wykorzystuje sterowany tablicą scentralizowany podsystem we/wy. Z punktu widzenia OS we/wy jest zorganizowane wokół tzw. IOCB (ang. Input/Output Control Block – Blok sterujący we/wy). IOCB jest ustandaryzowaną tablicą, która określa jedną kompletną operację wejścia lub wyjścia. Poprzez blok IOCB można wykonać dowolną z ośmiu operacji we/wy. Poprzez zmianę zawartości elementu IOCB użytkownik może zmienić naturę operacji we/wy, a nawet fizyczne urządzenie, które jest celem we/wy.

W ten sposób użytkownik może z łatwością wykonywać operacje we/wy na zupełnie różnych urządzeniach takich jak stacja dysków i drukarka bez zawracania sobie głowy szczegółami sprzętowymi. Większość operacji we/wy wymaga jedynie od użytkownika wpisania do bloku IOCB odpowiednich danych kontrolnych, a następnie przekazanie kontroli do podsystemu we/wy.

Podsystem we/wy tworzą dwa rodzaje elementów: systemowe procedury we/wy i systemowe bloki kontrolne we/wy. Systemowe procedury we/wy zawierają centralną procedurę we/wy (CIO), sterowniki urządzeń (E:, K:, S:, P:, C:, D:, R:) oraz procedurę szeregowego we/wy (SIO).

Tablica adresów sterowników (HATABS) odgrywa główną rolę w połączeniu CIO z poszczególnymi sterownikami urządzeń. Systemowe bloki kontroli we/wy zawierają dane sterujące, które skierowane są do podsystemu we/wy. Interfejs użytkownika jest taki sam dla wszystkich urządzeń (np. rozkazy wysłania wiersza znaków na drukarkę P: lub na ekran edytora E: są bardzo podobne).

Zrozumienie struktury podsystemu we/wy pozwoli ci go lepiej wykorzystywać we własnych aplikacjach. Na rys.8-8 pokazano związek pomiędzy systemowymi procedurami we/wy a systemowymi blokami kontrolnymi we/wy.

Rys.8-8 Podsystem we/wy

Bloki sterujące systemu we/wy

Jest cztery rodzaje bloków sterujących:

Bloki sterujące we/wy są wykorzystywane do przesyłania informacji o funkcji we/wy, która ma zostać wykonana. Bloki kontrolne dostarczają systemowym procedurom we/wy informacji kontrolnej do wykonania danej funkcji we/wy. Opis szczegółowej struktury tych czterech typów bloków sterujących znajdziesz w instrukcji do systemu operacyjnego.

Do realizowania komunikacji pomiędzy programami użytkownika a CIO wykorzystywane jest osiem bloków IOCB. Rys.8-9 przedstawia zawartość bloku IOCB dla niektórych typowych funkcji we/wy. Bloki IOCB są następujące:

Nazwa Adres,długość
IOCB0 [$340,16
IOCB1 [$350,16]
IOCB2 [$360,16]
IOCB3 [$370,16]
IOCB4 [$380,16]
IOCB5 [$390,16]
IOCB6 [$3A0,16]
IOCB7 [$3B0,16]

Drugi rodzaj bloku sterującego, ZIOCB [$0020,16], jest używany do wymiany danych sterującyĉh we/wy pomiędzy CIO a sterownikami urządzeń. Po wywołaniu CIO używa wartości zawartej w rejestrze X jako indeksu do wybranego bloku IOCB, który ma zostać użyty. Następnie CIO przenosi dane sterujące z wybranego bloku IOCB do bloku ZIOCB do użytku przez odpowiedni sterownik urządzenia. Blok ZIOCB ma dla ciebie małe znaczenie, chyba że właśnie piszesz kod nowego sterownika urządzenia lub zastępujesz bieżący sterownik innym.

Sterowniki urządzeń wymagające transmisji we/wy poprzez magistralę szeregową ładują następnie informacje sterujące do bloku DCB [$0300,12]. SIO wykorzysta informację w DCB i zwróci w DCB informację o statusie ostatniego wykorzystania bloku przez sterownik urządzenia. Rys.8-10 ilustruje niektóre typowe funkcje we/wy oraz zawartość powiązanych z nimi bloków DCB.

Rezydentny sterownik dysków nie podpada pod normalną sekwencję wywołań CIO. Zamiast tego używa się bloku DCB do bezpośredniej komunikacji z tym sterownikiem. Więcej informacji znajdziesz w rozdziale 9 "Dyskowy system operacyjny".

Ostatnim rodzajem bloku sterującego spotykanego w podsystemie we/wy jest bufor ramki rozkazu (ang. Command Frame Buffer, CFB).  Jest to 4-bajtowa tablica pod adresem $023A, którą wykorzystuje procedura SIO podczas wykonywania operacji na magistrali szeregowej.  Te cztery bajty zawierają kod urządzenia, kod rozkazu oraz bajty pomocnicze rozkazu 1 i 2. Przesyłany bufor danych jest zdefiniowany przez dwa wskaźniki BUFRLO [$0032,2] i BFENLO [$0034,2]. Ogólnie nie jest zalecane używanie systemu operacyjnego na tak niskim poziomie. Inne parametry muszą być ustawiane i należy podjąć szczególne starania, aby został wywołany właściwy ciąg podprogramów. CIO i SIO zostały zaprojektowane, tak aby łatwo można je było wywoływać z programu użytkownika. Używaj ich — lecz trzymaj się z dlala od bufora ramki rozkazu!

Blok sterujący IOCB

WYWOŁANIE ICHID ICDNO ICCOM ICSTA ICBAL ICBAH ICPTL ICPTH ICBLL ICBLH ICAX1 ICAX2
OTWARCIE PLIKU DO ODCZYTU
OTWARCIE PLIKU DO ZAPISU
ODCZYT BAJTÓW
ZAPIS BAJTÓW
ODCZYT REKORDU
ZAPIS REKORDU
ZAMKNIĘCIE PLIKU
STATUS
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
3
3
7
$B
5
9
$C
$D
(1)
(1)
(1)
(1)
(1)
(1)
(1)
(1)
$80
$80
00
00
00
00
X
X
06
06
06
06
06
06
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
$80
$80
$80
$80
X
X
X
X
00
00
00
00
X
X
4
8
X
X
X
X
X
X
0
(2)
X
X
X
X
X
X
Nota 1 Status rozkazu we/wy jest umieszczany tutaj oraz w rejestrze Y przy powrocie z CIO.
Nota 2 Bajty pomocnicze bloków IOCB są wykorzystywane przez niektóre sterowniki do oznaczania specjalnych trybów.
X = oznacza brak znaczenia, lecz nie należy zmieniać bieżącej wartości.
OGÓLNA UWAGA: Powyższe definicje zakładają:
 
*=$600
IOBUFF .RES 80              USER I/O BUFFER
FILE   .BYTE 'D:MYPROG.BAS' USER  FILENAME

Rys.8-9 Blok sterujący wejścia/wyjścia (IOCB)

Blok sterujący DCB

FUNKCJA NAZWA ADRES STACJA DYSKÓW 810/815 ZAPIS NA
DRUKARKĘ 820
ODCZYT SEKTORA ZAPIS SEKTORA ZAPIS FORMAT
810 815 810 815
Numer magistrali szeregowej
Numer urządzenia
Bajt rozkazu
Status
Adres bufora

Przekroczenie czasu operacji
Długość bufora
 
DDEVIC
DUNIT
DCOMND
DSTATS
DBUFLO
DBUFHI
DTIMLO
DBYTLO
DBYTHI
DAUX1
DAUX2
[$0300]
[$0301]
[$0302]
[$0303]
[$0304]
[$0305]
[$0306]
[$0308]
[$0309]
[$030A]
[$030B]
$30
1-4
$52
$40
U
U
$30
$80
$00
2*
2*
$30
1-8
$52
$40
U
U
$30
00
01
2*
2*
$30
1-4
$57
$80
U
U
$30
$80
$00
2*
2*
$30
1-8
$57
$80
U
U
$30
00
01
2*
2*
$30
1-4
$50
$80
U
U
$31
$80/00
$00/01
- 2*
- 2*
$30
1-4
$21
$40
U
U
$130
-
-
-
-
$30
1
$57
$80
U
U
5
$40
$00
1*
1*
1* = Te bajty określają tryb drukarki (zobacz do instrukcji 820l).
2* = DAUX1 + DAUX2 określają sektor do ODCZYTU, ZAPISU lub WERYFIKACJI.
U  = Oznacza adres ustawiony przez użytkownika.
 -  = Oznacza wartość ignorowaną

Rys.8-10 Blok sterujący urządzenia (DCB)

Centralna systemowa procedura we/wy

Główną funkcją CIO jest pobranie danych sterujących z IOCB i upewnienie się, iż trafią one do właściwego sterownika urządzenia, a następnie przekazanie sterowania do tego sterownika. Cio działa również jako kolejka dla większości operacji we/wy w systemie. Większość funkcji systemowych we/wy wykorzystuje CIO jako wspólny punkt wejścia, a wszystkie sterowniki wychodzą do systemu poprzez CIO. Na przykład BASIC wywoła CIO przy wykonywaniu polecenia OPEN lub GRAPHICS. CIO wspiera następujące funkcje

OPEN
CLOSE
GET CHARS
READ RECORD
PUT CHARS
WRITE RECORD
STATUS
SPECIAL
Otwieranie urządzenia/pliku
Zamykanie urządzenia/pliku
Odczyt N znaków
Odczyt następnego rekordu
Zapis N znaków
Zapis następnego rekordu
Pobranie stanu urządzenia
Działanie specyficzne dla sterownika (np. NOTE dla FMS)

Możesz wykonywać własne wysołania CIO. Sekwencja wywołania CIO jest następująca:

               ;Użytkownik ustawił już IOCB
LDX #IOCBNUM   ;Ustaw indeks IOCB (IOCB * 16)
JSR CIOV       ;Wektor systemowej procedury do CIO
BMI ERROR      ;Jeśli zostanie wykonany skok, to CIO zwróciło
               ;kod błędu w rejestrze Y

Jak pokazano w powyższym wywołaniu, jeden z bloków IOCB jest używany do przekazywania danych sterujących do CIO. Możesz do tego celu użyć dowolnego z ośmiu bloków IOCB. CIO oczekuje indeksu tego bloku IOCB w rejestrze X. Zapamiętaj, iż wartość ta musi być równa numerowi bloku IOCB pomnożonemu przez 16. Powodem jest to iż CIO wykorzystuje ten indeks do adresowania zawartości różnych IOCB, a każdy z nich ma długość 16 bajtów. Przy powrocie bit znaku w rejestrze stanu mikroprocesora 6502 jest odpowiednio ustawiany do oznaczenia sukcesu lub błędu w operacji we/wy. Jeśli bit N jest wyzerowany, to operacja we/wy powiodła się, a rejestr Y będzie zawierał wartość 1. Jeśli bit N jest ustawiony, to żądanie we/wy dało w wyniku błąd i w takim wypadku rejestr Y będzie zawierał kod tego błędu. Typowym sposobem testowania wyniku operacji we/wy jest użycie instrukcji skoku warunkowego przy minusie (przy ustawionym bicie znaku N) do procedury obsługi błędu. Wartość sukcesu/błędu jest również zwracana w bajcie ICSTA bloku IOCB (zobacz na definicję IOCB). Rozdział 5 instrukcji systemu operacyjnego zawiera przykładowy fragment programu, który wywołuje CIO do otwarcia pliku na dysku, odczytania kilku rekordów, a następnie zamknięcia tego pliku.

CIO kopiuje dane sterujące z wybranego bloku IOCB do bloku ZIOCB na stronie zerowej. Następnie CIO określa punkt wejścia sterownika urządzenia i wykonuje skok poprzez wektor do odpowiedniej procedury sterownika. Rysunek 8-11 przedstawia przebieg systemowej procedury CIO.


Rys.8-11.1 Procedura CIO


Rys.8-11.2 Procedura CIO

Tablica adresów sterowników

CIO oblicza adres punktu wejścia do sterownika urządzenia w sposób pośredni. Przede wszystkim wywołanie OPEN musi poprzedzać wszystkie inne funkcje we/wy związane z danym urządzeniem. Podczas przetwarzania rozkazu OPEN CIO pobiera specyfikację urządzenia dla otwieranego pliku. Specyfikacja ta jest łańcuchem ATASCII wskazywanym przez część adresu bufora w IOCB. Pierwszym elementem tego łańcucha musi być jednoliterowy identyfikator urządzenia (np. 'D' dla stacji dysków, 'P' dla drukarki, ...). Następnie CIO szuka tego znaku w tablicy punktów wejścia sterowników HATABS, która jest w pamięci pod adresami od $031A do $033B (Rys.8-12 pokazuje układ HATABS). CIO rozpoczyna poszukiwania od spodu HATABS i kontynuuje je w górę aż zostanie znaleziona zgodność z identyfikatorem urządzenia. Poszukiwanie jest wykonywane w tym kierunku, aby ułatwić dodawanie lub modyfikację sterowników urządzeń. Podczas wykonywania kodu inicjalizującego tablica HATABS jest kopiowana z ROM do RAM. Urządzenia, które są później inicjowane (np. stacja dysków lub moduł RS-232) dodają informację o swoich sterownikach na spód tej tabeli. W tabeli jest miejsce na 14 elementów, z których 5 zostaje ustawionych podczas inicjalizacji systemu. Jeśliby później został dodany na spodzie tabeli jakiś nowy sterownik drukarki, to CIO znalazłoby go przed znalezieniem sterownika skopiowanego z ROM. Pozwala to nowym sterownikom zastępować stare.

Po zlokalizowaniu identyfikatora urządzenia CIO wie, że następne dwa bajty są adresem wskazującym tablicę punktów wejścia do procedur sterownika tego urządzenia. Jest to tablica adresów procedur, które obsługują każdą z funkcji CIO. Rys.8-13 podaje układ typowej tablicy punktów wejścia.

Aby określić punkt wejścia sterownika, do którego należy skoczyć poprzez wektor, CIO wykorzystuje ICCOM, czyli bajt rozkazu w bloku IOCB, jako indeks w tablicy wejść. Tablice punktów wejścia wszystkich rezydentnych sterowników można znaleźć na listingu systemu operacyjnego. Względne położenie każdego z wektorów w tablicy wejść ma takie same znaczenie we wszystkich tych tablicach. Na przykład pierwszy wpis we wszystkich tablicach punktów wejścia jest wektorem do procedury OPEN sterownika urządzenia.

Możesz skorzystać z elastyczności tablicy HATABS w celu dodania kilku nowych cech do systemu operacyjnego. Rys.8-14 pokazuje przykładowe dodanie sterownika urządzenia pustego NULL. Sterownik urządzenia pustego robi to, co sugeruje jego nazwa: nic. Może to być użyteczne przy wyszukiwaniu błędów w programach. Zamiast czekać na 50.000 dostępów do dysku, aby znaleźć jakiś błąd, wyjście może zostać skierowanie na sterownik NULL. Przy jego pomocy kłopotliwe miejsca w programie można szybciej zidentyfikować.

            01 ; TABLICA ADRESÓW STEROWNIKÓW
E430        02 PRINTV  =     $E430
E440        03 CASETV  =     $E440
E400        04 EDITRV  =     $E400
E410        05 SCRENV  =     $E410
E420        06 KEYBDV  =     $E420
            07 ;
0000        08         *=   $031A
            09 ;
            10 HATABS
031A 50     20         .BYTE   'P'     DRUKARKA
031B 30E4   30         .WORD    PRINTV    ADRES TABLICY PUNKTÓW WEJŚCIA
031D 43     40         .BYTE   'C'     MAGNETOFON
031E 40E4   50         .WORD   CASETV     ADRES TABLICY PUNKTÓW WEJŚCIA
0320 45     60         .BYTE   'E'     EDYTOR EKRANOWY
0321 00E4   70         .WORD   EDITRV     ADRES TABLICY PUNKTÓW WEJŚCIA
0323 53     80         .BYTE   'S'     STEROWNIK EKRANU
0324 10E4   90         .WORD   SCRENV     ADRES TABLICY PUNKTÓW WEJŚCIA
0326 4B     0100       .BYTE   'K'     KLAWIATURA
0327 20E4   0110       .WORD   KEYBDV     ADRES TABLICY PUNKTÓW WEJŚCIA
0329 00     0120       .BYTE   0       WOLNY ELEMENT 1 (DOS)
032A 00 00  0130       .BYTE   0,0
032C 00     0140       .BYTE   0       WOLNY ELEMENT 2 (MODUŁ 850)
032D 00 00  0150       .BYTE   0,0
032F 00     0160       .BYTE   0       WOLNY ELEMENT 3
0330 00 00  0170       .BYTE   0,0
0332 60     0180       .BYTE   0       WOLNY ELEMENT 4
0333 00 00  0190       .BYTE   0,0
0335 00     0200       .BYTE   0       WOLNY ELEMENT 5
0336 00 00  0210       .BYTE   0,0
0338 00     0220       .BYTE   0       WOLNY ELEMENT 6
0339 00 00  0230       .BYTE   0,0
033B 00     0240       .BYTE   0       WOLNY ELEMENT 7

Rys.8-12 Tablica adresów sterowników (HATABS)

                *= $PRINTV
E430 9E EE      .WORD   PHOPEN-1      OTWARCIE URZĄDZENIA
E432 DB EE      .WORD   PHCLOS-1      ZAMKNIĘCIE URZĄDZENIA
E434 9D EE      .WORD   BADST-1       ODCZYT Z URZĄDZENIA NIEZAIMPLEMENTOWANY
E436 A6 EE      .WORD   PHWRIT-1      ZAPIS NA URZĄDZENIU
E438 80 EE      .WORD   PHSTAT-1      STAN URZĄDZENIA
E43A 9D EE      .WORD   BADST-1       FUNKCJA SPECJALNA NIEZAIMPLEMENTOWANA
E43C 4C 78 EE    JMP    PHINIT        INICJALIZACJA URZĄDZENIA

Rys.8-13 Tablica punktów wejścia do sterownika drukarki

0000        10           *=    $600
031A        20 HATABS    =     $031A
0600 A000   40 START     LDY    #0
0602 B91A03 60 LOOP      LDA   HATABS,Y
0605 C900   70           CMP    #0         ;   WOLNY ELEMENT?
0607 F009   80           BEQ    FOUND
0609 C8     90           INY
060A C8     0100         INY
060B C8     0110         INY               ;   WSKAŻ NASTĘPNY ELEMENT HATABS
060C C022   0120         CPY    #34        ;   KONIEC HATABS?
060E D0F2   0130         BNE    LOOP       ;   NIE ... KONTYNUUJ
0610 38     0140         SEC               ;   TAK ... INFORMUJ O BŁĘDZIE
0611 60     0150         RTS
            0160 ;
0612 A94E   0180 FOUND   LDA    #'N        ;   USTAW NAZWĘ URZĄDZENIA
0614 991A03 0190         STA    HATABS,Y
0617 C8     0200         INY
0618 A924   0210         LDA    #NULLTAB&255
061A 991A03 0220         STA    HATABS,Y   ;   ADRES STEROWNIKA
061D C8     0230         INY
061E A906   0240         LDA    #NULLTAB/256
0620 991A03 0250         STA    HATABS,Y
0623 60     0260         RTS
            0270
0624 3206   0290 NULLTAB .WORD  RTHAND-1   ;   OPEN
0626 3206   0300         .WORD  RTHAND-1   ;   CLOSE
0628 3406   0310         .WORD  NOFUNC-1   ;   READ
062A 3206   0320         .WORD  RTHAND-1   ;   WRITE
062C 3206   0330         .WORD  RTHAND-1   ;   STATUS
062E 3406   0340         .WORD  NOFUNC-1   ;   SPECIAL
0630 4C3306 0350         JMP    RTHAND     ;   INITILIZATION
            0360 ;
0633 A001   0380 RTHAND  LDY    #1         ;   FUNKCJA WE/WY WYKONANA Z SUKCESEM
0635 60     0400 NOFUNC  RTS               ;   FUNCTION NIEZAIMPLEMENTOWANA

Rys.8-14 Sterownik urządzenia pustego (NULL)

Sterowniki urządzeń

Sterowniki urządzeń można podzielić na rezydentne i nierezydentne. Sterowniki rezydentne są obecne w pamięci ROM systemu operacyjnego i można je wywoływać poprzez CIO zawsze, gdy dany sterownik posiada swój element w tablicy HATABS. Sterowniki nierezydentne należy najpierw załadować do pamięci RAM i umieścić o nich informację w HATABS, zanim będzie można je wywołać z CIO. Sterownikami rezydentnymi są:

Chociaż sterowniki nierezydentne nie znajdują się w OS ROM, to jednak system operacyjny może je dodać podczas włączania lub systemowego resetu. Możesz nawet dodać swój własny sterownik urządzenia podczas wykonywania programu. Rys.8-14 prezentuje przykład dodawania sterownika do systemu operacyjnego.

Sterowniki urządzeń wykorzystują dane sterujące we/wy przekazane przez CIO w bloku ZIOCB. Dane te są używane do wykonywania funkcji we/wy takich jak OPEN, CLOSE, PUT i GET. Nie wszystkie sterowniki urządzeń wspierają wszystkie rozkazy we/wy (np. próba przesłania znaku za pomocą PUT do klawiatury skończy się błędem 146 – funkcja niezaimplementowana). Rozdział 5 instrukcji do systemu operacyjnego zawiera listę funkcji obsługiwanych przez każdy ze sterowników urządzeń, jak również pełne szczegóły operacyjne tych sterowników.

Procedura szeregowego we/wy

SIO obsługuje komunikację po magistrali szeregowej pomiędzy sterownikami urządzeń szeregowych w komputerze a urządzeniami na magistrali szeregowej. Komunikuje się ona z wywołującym ją kodem poprzez blok sterujący urządzenia (ang. Device Control Block, DCB). SIO używa danych sterujących we/wy w bloku DCB do wysyłania i odbierania rozkazów i danych po magistrali szeregowej. Sekwencja wywołania jest następująca:

          ;program wywołujący ustawił DCB w celu wykonania funkcji
JSR SIOV  ;wektor systemowy do SIO
BMI ERROR ;ustawiony bit N oznacza błąd w wykonaniu operacji we/wy

Blok DCB zawiera informację kontrolną we/wy dla SIO i musi zostać ustawiony przed wywołaniem SIO. Rys.8-10 pokazuje zawartość bloku DCB dla kilku typowych operacji we/wy.

Aby wysyłać rozkazy do SIO, musisz zrozumieć strukturę bloku DCB, która została opisana w rozdziale 9 instrukcji systemu operacyjnego. Rys.8-15 demonstruje prostą procedurę w języku asemblera, która wyprowadza wiersz tekstu na drukarkę przez ustawienie DCB i wywołanie SIO.

0000        05            *= $3000      DOWOLNIE WYBRANY PUNKT STARTU
            10 ;TA PROCEDURA DRUKUJE WIERSZ NA DRUKARCE PRZEZ WYWOŁANIE SIO POD ADRESEM E459
            20 SIOV       =       $E459   WEKTOR SIO
009B        30 CR         =       $9B     ZNAK KOŃCA WIERSZA
0040        40 PRNTID     =       $40     NUMER IDENTYFIKACYJNY DRUKARKI NA MAGISTRALI SZEREGOWEJ
004E        45 MODE       =       $4E     TRYB NORMALNY
001C        50 PTIMOT     =       $001C   ADRES NA PRZEKROCZENIE CZASU
0300        60 DDEVIC     =       $300    NUMER IDENTYFIKACYJNY URZĄDZENIA NA MAGISTRALI SZEREGOWEJ
0301        70 DUNIT      =       $301    NUMER JEDNOSTKI SZEREGOWEJ
0302        80 DCOMND     =       $302    ROZKAZ SIO
0303        90 DSTATS     =       $303    KIERUNEK DANYCH SIO
0304        0100 DBUFLO   =       $304    DOLNY BAJT ADRESU BUFORA
0305        0110 DBUFHI   =       $305    GÓRNY BAJT ADRESU BUFORA
0306        0120 DTIMLO   =       $306    LIMIT CZASU SIO
0307        0130 DTIMHI   =       $307
0308        0140 DBYTLO   =       $308    DŁUGOŚĆ BUFORA
0309        0150 DBYTHI   =       $309
030A        0160 DAUX1    =       $30A    BAJT POMOCNICZY---TRYB PRACY DRUKARKI
030B        0170 DAUX2    =       $30B    BAJT POMOCNICZY---NIEUŻYWANY
            0180 ;
3000 455841 0190 MESS     .BYTE   "EXAMPLE 12",CR
3001 4D504C
3005 452031
3009 329B
            0200 ;
300B A940   0220          LDA     #PRNTID     USTAW IDENTYFIKATOR MAGISTRALI
300D 8D0003 0230          STA     DDEVIC
3010 A901   0240          LDA     #1          USTAW NUMER JEDNOSTKI
3012 8D0103 0250          STA     DUNIT
3015 A94E   0260          LDA     #MODE
3017 8D0A03 0270          STA     DAUX1       NORMALNY TRYB DRUKARKI
301A A901   0275          LDA     #1
301C 8D0B03 0280          STA     DAUX2       NIEUŻYWANE
301F 8D0703 0290          STA     DTIMHI      LIMIT CZASU<256 SEKUND
3022 A51C   0300          LDA     PTIMOT      USTAW LIMIT CZASU SIO DLA DRUKARKI
3024 8D0603 0310          STA     DTIMLO
3027 A900   0320          LDA     #MESS&255
3029 8D0403 0330          STA     DBUFLO      USTAW MESS JAKO BUFOR
302C A930   0340          LDA     #MESS/256
302E 8D0503 0350          STA     DBUFHI
3031 A980   0360          LDA     #$80        USTAW KIERUNEK DANYCH SIO
3033 8D0303 0370          STA     DSTATS      DO ODBIORU PRZEZ URZĄDZENIE PERYFERYJNE
3036 A957   0380          LDA     #'W         ROZKAZ ZAPISU SIO
3038 8D0203 0390          STA     DCOMND
303B 2059E4 0410          JSR     SIOV        WYWOŁAJ SIO
303E 3001   0420          BMI     ERROR
3040 00     0430 GOOD     BRK
3041 00     0440 ERROR    BRK 
Rys.8-15 Wywołanie CIO w celu wydruku wiersza na drukarce

Przerwania SIO

SIO wykorzystuje trzy przerwania IRQ do sterowania komunikacją po magistrali szeregowej z urządzeniami podłączonymi do tej magistrali:

IRQ Adres, długość Funkcja
VSERIR
VSEROR
VSEROC
[$020A,2]
[$020C,2]
[$020E,2]
GOTOWOŚĆ DANYCH ODEBRANYCH SZEREGOWO
POTRZEBNE DANE DO WYSŁANIA SZEREGOWEGO
TRANSMISJA ZAKOŃCZONA

Całe wykonywanie programu zostaje wstrzymane, gdy SIO używa magistrali szeregowej do komunikacji. Nawet jeśli nic innego nie dzieje się podczas przesyłu po magistrali szeregowej, to rzeczywiste operacje we/wy są same w sobie sterowane przerwaniami. Metoda komunikacji pomiędzy SIO a sterownikami przerwań jest znana jako metoda semaforowa. Główny kod czeka w pętli, aż sterowniki przerwań zasygnalizują mu, że wykonały operację. Na przykład, podczas wyprowadzania danych SIO umieszcza bajt do przesłania w rejestrze przesuwającym z wyjściem szeregowym, który znajduje się w układzie POKEY. Następnie SIO wchodzi w pętlę i obserwuje pewien znacznik, który będzie ustawiony, gdy żądana operacja we/wy zostanie ukończona. W tym czasie POKEY przesyła na zewnątrz bity po linii szeregowej. Gdy bajt ten będzie wysłany, zostanie wygenerowane przerwanie IRQ – Potrzebne Dane Do Wysłania Szeregowego (ang. Serial Output Needed IRQ). Przerwanie to powoduje skok poprzez wektor do procedury, która ładuje następny bajt z bufora do rejestru przesuwającego z wyjściem szeregowym. Proces ten jest kontynuowany, aż zostanie przesłany cały bufor. Po zatroszczeniu się o wartości sum kontrolnych sterownik przerwania ustawia następnie znacznik zakończenia transmisji. W międzyczasie SIO cierpliwie czekało w pętli na ustawienie tego znacznika. Gdy zobaczy, że znacznik został ustawiony, SIO wróci z powrotem do wywołującej je procedury.

Wykonanie SIO przy wprowadzaniu danych jest podobne. POKEY generuje IRQ (VSERIR), aby poinformować SIO, że odebrano bajt w rejestrze przesuwającym z wejściem szeregowym (SERIN). Sterownik przerwania VSERIR następnie umieszcza ten bajt w buforze i sprawdza, czy został osiągnięty koniec bufora. Jeśli tak, to ustawia znacznik zakończenia transmisji.

W powyższym wyjaśnieniu mogłeś zauważyć, że SIO marnuje nieco czasu czekając bezczynnie na wysłanie lub odbiór informacji z magistrali przez układ POKEY. Ponieważ wektory dla trzech procedur obsługujących przerwania IRQ SIO znajdują się w pamięci RAM, można ich użyć dla twoich własnych sterowników w celu poprawienia wydajności operacji we/wy wykonywanych przez system. W rzeczy samej w ten właśnie sposób moduł interfejsu ATARI 850 jest w stanie wykonywać współbieżne operacje we/wy. Sterownik ten przechwytuje wektory IRQ SIO i kieruje je na swoje własne procedury IRQ, gdy pracuje w trybie współbieżnym we/wy. Pozwala to wywołującemu programowi kontynuować pracę, podczas gdy moduł interfejsu wysyła rozkazy i dane poprzez linię szeregową.

Używanie CIO z poziomu języka BASIC

Większość funkcji CIO (OPEN, CLOSE, itp.) jest dostępna poprzez wywołania z poziomu języka BASIC przy użyciu rozkazów OPEN, GET i PUT. Jednakże w języku BASIC brakuje jednego zbioru funkcji CIO – możliwości wykonywania blokowych operacji we/wy o rozmiarze większym niż jeden bajt naraz (GETCHRS i PUTCHRS).

Możliwość wczytania lub zapisu bufora znaków jest potężną zaletą. Na przykład, procedura asemblerowa może zostać załadowana bezpośrednio do pamięci z pliku dyskowego. Albo obraz graficzny w wysokiej rozdzielczości może zostać załadowany bezpośrednio do obszaru pamięci ekranu. Typową metodą usprawniania wydajności programu w języku BASIC jest dostarczenie mu programu w języku maszynowym. który obsługuje pewne funkcje wykonywane wolno przez BASIC. Niestety, znalezienie miejsca w pamięci RAM dla takiej procedury może stanowić kłopotliwy problem. Jednym z rozwiązań jest umieszczenie procedury w obszarze zarezerwowanym dla łańcucha znaków, a następnie wywołanie procedury za pomocą USR ADR(łańcuch). Ponieważ adres łańcucha języka BASIC może się przesuwać podczas edycji programu, to procedura w języku asemblera musi być relokowalna. Dlatego niezmodyfikowane adresowanie pamięci odwołujące się do adresów wewnątrz łańcucha nie będzie działać.

Podprogram z rys.8-16 unika używania łańcuchów przez załadowanie procedury do strony 6 pamięci RAM. W ten sposób procedura w języku asemblera nie musi być relokowalna. Dane sterujące są wstawiane poleceniem POKE do bloku IOCB w celu odczytania procedury w języku asemblera bezpośrednio do pamięci RAM pod adres, pod którym została ona skompilowana przez asembler. Podprogram w języku BASIC z rys.8-16 można również użyć do zapisu danych bezpośrednio z pamięci, gdzie użytkownik określa adres oraz długość bufora danych.

30  REM TEN PROGRAM ŁADUJE NA STRONĘ 6 ZAWARTOŚĆ PLIKU D:TEST
100 DIM FILE$(20),CIO$(7):CIO$="hhh*LVd"
106 REM CIO$ TO PLA,PLA,PLA,TAX,JMP $E456 (CIOV)
110 FILE$="D:TEST":REM NAZWA PLIKU
120 CMD=7:STADR=1536:GOSUB 30000
130 IF ERROR=1 THEN ? "TRANSFER COMPLETE":STOP
135 ? "ERROR # ";ERROR;" OCCURRED AT LINE # ";PEEK(186)+256*PEEK(187)
200 END
300 REM PODPROGRAM USTAWIANIA CIO
310 REM
30000 REM
30001 REM
30002 REM TA PROCEDURA ŁADUJE LUB ZAPISUJE OBSZAR PAMIĘCI Z BASICU
30003 REM PRZEZ USTAWIENIE BLOKU IOCB I BEZPOŚREDNIE WYWOŁANIE CIO
30004 REM
30006 REM NA WEJŚCIU CMD=7  OZNACZA ŁADOWANIE PAMIĘCI
30008 REM            CMD=11 OZNACZA ZAPIS PAMIĘCI
30009 REM            STADR= ADRES BLOKU PAMIĘCI DO ZAŁADOWANIA LUB ZAPISU
30010 REM            BYTES= LICZBA BAJTÓW DO ZAŁADOWANIA LUB ZAPISU
30011 REM            IOCB=  BLOK IOCB DO UŻYCIA
30012 REM            FILE$= NAZWA PLIKU DOCELOWEGO
30013 REM 
30014 REM NA WYJŚCIU ERROR=1 OZNACZA WYKONANIE POLECENIA Z SUKCESEM
30018 REM            ERROR<>1 OZNACZA NUMER BŁĘDU
30019 REM
30020 REM ELEMENTY BLOKU IOCB
30022 REM
30024 IOCBX=IOCB*16:ICCOM=834+IOCBX:ICSTA=835+IOCBX
30026 ICBAL=836+IOCBX:ICBAH=837+IOCBX
30028 ICBLL=840+IOCBX:ICBLH=841+IOCBX
30029 REM
30030 AUX1=4:IF CMD=11 THEN AUX1=8
30035 TRAP 30900:OPEN #IOCB,AUX1,0,FILE$
30040 TEMP=STADR:GOSUB 30500
30090 POKE ICBAL,LOW:POKE ICBAH,HIGH
30100 TEMP=BYTES:GOSUB 30500
30130 POKE ICBLL,LOW:POKE ICBLH,HIGH
30140 POKE ICCOM,CMD:ERROR=USR(ADR(CIO$),IOCBX)
30150 ERROR=PEEK(ICSTA):RETURN
30200 REM
30300 REM PROCEDURA ZWRACA GÓRNY I DOLNY BAJT LICZBY 16-BITOWEJ
30400 REM
30500 HIGH=INT(TEMP/256):LOW=INT(TEMP-HIGH*256):RETURN
30550 REM
30600 REM USTAW TUTAJ PUŁAPKĘ NA BŁĄD PODCZAS WYKONYWANIA PROCEDURY
30900 ERROR=PEEK(195)
30920 CLOSE #IOCB:RETURN

Rys.8-16 Bezpośrednie wywołanie CIO z poziomu języka BASIC


Na początek:  podrozdziału   strony 

Przetwarzanie w czasie rzeczywistym

Większość czasu przy programowaniu mamy wygodę ignorowania ograniczeń czasowych. Zwykle nie przejmujemy się, ile czasu program będzie wykonywany, lub nie troszczymy się o dokładne wartości czasów wykonywania podprogramów. Jednakże czasami wartości czasów wykonania grają istotną rolę w wydajności programu , a wtedy wchodzimy w świat programowania w czasie rzeczywistym (ang. Real Time Programming). Takie przypadki często powstają w systemie komputerowym ATARI. W dużo większym stopniu niż inne małe komputery system ten opiera się na programowaniu w czasie rzeczywistym. Podstawa czasu, na której opiera się działanie wewnętrznych układów, została precyzyjnie wybrana, aby komputer mógł pracować w dokładnej synchronizacji z określonym sygnałem – mianowicie z sygnałem telewizyjnym.

W celu otrzymania czystej grafiki i efektów specjalnych układy komputera domowego ATARI są podporządkowane lokalnemu sygnałowi telewizyjnemu. Niestety używane jest wiele "standardów" sygnałów telewizyjnych w różnych krajach. W stanach Zjednoczonych standardem jest system NTSC: 60 ramek na sekundę, 262 poziome linie na ramkę i 228 taktów kolorów na linię. Mówimy o 262 liniach, ponieważ komputer domowy ATARI generuje sygnał bez przeplotu linii; rzeczywisty standard odwołuje się do 525 linii, z których połowa pokazywana jest w każdej ramce. Niektóre kraje Europejskie używają standardu PAL: 50 ramek na sekundę, 312 linii na ramkę. W wyniku odmierzanie czasu jest inne w NTSC i inne w PAL. Zobacz do Rozdziału 2 – ANTIC i lista wyświetlania, gdzie dokładniej przedyskutowano sygnał telewizyjny. Uwagi w tym podrozdziale odnoszą się do systemu NTSC.

Synchronizacja z sygnałem telewizyjnym

Mikroprocesor 6502 jest synchronizowany z sygnałem telewizyjnym na dwa sposoby: przy pomocy synchronizacji zgrubnej i dokładnej. Synchronizacja zgrubna jest osiągana przez użycie tego samego sygnału, który synchronizuje odbiorniki telewizyjne z nadawanym sygnałem telewizyjnym, do wywołania przerwania systemowego. Sygnał ten nazywany jest sygnałem wygaszania pionowego i w odbiornikach telewizyjnych jest on wskazówką, aby wyłączyć strumień elektronów i rozpocząć od góry ekranu przygotowując się na następną ramkę obrazu. Ten sam sygnał jest prezentowany komputerowi jako przerwanie niemaskowane. Programiście udostępnia to regularnie pojawiające się przerwanie, które można użyć do wszystkiego od odmierzania czasu trwania dźwięku do prostej metody programowania wielowątkowego.

Bardziej precyzyjną korelacją pomiędzy przetwarzaniem mikroprocesora 6502 a sygnałem telewizyjnym osiągnięto przez wybór częstotliwości 1,79 MHz na częstotliwość zegarową systemu. Dało to w wyniku bezpośredni związek pomiędzy czasem wykonywania instrukcji maszynowej a odległością, jaką pokonuje strumień elektronów na ekranie. Na przykład, podczas czasu wykonania najkrótszej instrukcji mikroprocesora 6502 (2 takty) strumień elektronów przesuwa się o cztery takty koloru lub o jeden znak trybu graficznego 0 po ekranie. Ten precyzyjny związek czasowy pozwala zdolnemu programiście tworzyć efekty graficzne w środku pojedynczej linii skanowania. Jednakże należy uważać. Bezpośredni dostęp do pamięci przez układ ANTIC sprawia, iż to wewnątrzliniowe odmierzanie czasu staje się bardzo niepewne i będzie się zmieniało w zależności od wybranego trybu graficznego oraz innych czynników. W praktyce oznacza to, iż każda zmiana wewnątrz linii musi być testowana i traktowana jako specjalny przypadek.

Sprzętowe timery

Cztery timery zliczające w dół są wbudowane w układ POKEY. Funkcjonują one jako "sprzętowe podprogramy" wielokrotnego użytku. Najczęstsze ich zastosowania wiążą się z kanałami dźwiękowymi przy tworzeniu efektów dźwiękowych (zobacz do Rozdziału 7 – Dźwięk). Można ich również używać jako bezpośrednich timerów zliczających w dół, ponieważ generują one przerwanie IRQ. Każdy timer skojarzony jest z rejestrem częstotliwości, który przechowuje wartość początkową timera. Gdy nastąpi wpis do sprzętowego rejestru STIMER, ta początkowa wartość jest ładowana do timera i rozpoczyna się odliczanie w dół. Gdy timer zliczy do zera, następuje wygenerowanie żądania przerwania IRQ. Ważne jest, iż tylko timery 1, 2 i 4 posiadają wektory przerwań do przetwarzania. Należy podjąć następujące kroki, aby uaktywnić dowolny z timerów:

Jedną z komplikacji przy pracy z tymi timerami jest to, iż odpowiedź na nie mikroprocesora 6502 będzie uprzedzona i prawdopodobnie opóźniona przez bezpośredni dostęp do pamięci układu ANTIC, przerwania z list wyświetlania lub przetwarzanie wygaszania pionowego.

Programowe timery

Jest 6 programowych timerów systemowych:

Nazwa Adres Wektor lub znacznik
RTCLOK
CDTMV1
CDTMV2
CDTMV3
CDTMV4
CDTMV5
[$0012,3]
[$0218,2]
[$021A,2]
[$021C,2]
[$021E,2]
[$0220,2]
żaden
CDTMA1 [$0226,2]
CDTMA2 [$0228,2]
CDTMF3 [$022A,1]
CDTMF4 [$022C,1]
CDTMF5 [$022E,1]

Wszystkie timery systemowe są zmniejszane o 1 jako część procesu wygaszania pionowego (VBLANK). Jeśli proces VBLANK jest wyłączony lub przechwycony, to timery te nie będą uaktualniane.

Zegar czasu rzeczywistego (RTCLOK) i timer systemowy 1 (CDTMV1) są uaktualniane podczas początkowej procedury VBLANK w etapie1. RTCLOK zlicza w górę od 0 i jest wartością 3-bajtową. Gdy RTCLOK osiągnie swoją wartość maksymalną (16.777.216), to zostanie zresetowany na zero. RTCLOK można używać jako zegara czasu rzeczywistego, co pokazuje rys.8-17.

Ponieważ timery systemowe są uaktualniane jako część procesu VBLANK, to należy zastosować specjalne kroki ostrożności, aby je poprawnie ustawić. Do tego celu używa się procedury systemowej o nazwie SETVBV [$E45C]. Jej wywołanie wygląda następująco:

REJESTRY :
X zawiera górny bajt wartości timera
Y zawiera dolny bajt wartości timera
A zawiera numer timera 1-5

Przykład:

LDA #1      ;Ustaw timer systemowy 1
LDY #0
LDX #2      ;Wartość wynosi $200 (512) okresów VBLANK
JSR SETVBV  ;Wywołaj procedurę systemową, aby ustawić timer

Timery systemowe 1-5 są 2-bajtowymi licznikami. Można je ustawiać przy pomocy procedury SETVBV. System operacyjny zmniejsza ich zawartość podczas przerwania od wygaszania pionowego VBLANK. Timer 1 jest zmniejszany podczas początkowej procedury VBLANK w etapie 1. Timery 2-5 są zmniejszane w etapie 2. System operacyjny wykonuje różne działania, gdy różne timery osiągną wartość 0.

Timery systemowe 1 i 2 posiadają skojarzone z nimi wektory. Gdy timer 1 lub 2 osiąga 0, system operacyjny symuluje rozkaz JSR poprzez wektor dla danego timera. Rys.8-7 podaje wektory dla tych dwóch timerów.

Timery systemowe 3-5 posiadają znaczniki, które są normalnie ustawione (tj. mają wartość różną od zera). Gdy którykolwiek z tych trzech timerów zliczy do zera, system operacyjny wyzeruje jego znacznik. Możesz testować stan znacznika i podjąć odpowiednie działania.

Timery 1-5 są timerami programowymi ogólnego przeznaczenia, które można używać w różnorodnych aplikacjach. Na przykład, timer 1 jest używany przez SIO do odmierzania czasu operacji na magistrali szeregowej. Jeśli timer ten zliczy do zera przed zakończeniem danej operacji na magistrali, zostanie zwrócony błąd przekroczenia czasu (ang. timeout error). Timer 1 jest ustawiany na różne wartości w zależności od urządzenia, z którym jest prowadzona komunikacja. Zapewnia to obszerną ilość czasu dla urządzenia na odpowiedź na żądanie we/wy, a jednocześnie komputer nie będzie na nią czekał w nieskończoność, jeśli dane urządzenie nie jest obecne. Sterownik magnetofonu kasetowego używa timera 3 do ustawiania długości czasu odczytu i zapisu nagłówków taśmowych. Rys.8-18 pokazuje przykład użycia timera 2 do odmierzania czasu dźwięku metronomu.

Timery programowe używane są zwykle wtedy, gdy związana z nimi skala czasu jest większa od jednego okresu wygaszania pionowego VBLANK. Dla czasów krótszych należy zastosować timery sprzętowe lub jakąś inną metodę.

1 POKE 752,1
3 ? "+":REM WYMAŻ EKRAN (+=ESC-CTRL-CLR)
4 ? "HOUR";:INPUT HOUR:? "MINUTE";:INPUT MIN:? "SECOND";:INPUT SEC
5 CMD=1:GOSUB 45
6 ? "+";HOUR;" : ";MIN;" : ";SEC:? " ": ? " "
7 CMD=2:GOSUB 45
9 ? "";HOUR;":";MIN;":";SEC;" ":GOTO 7

10 REM TO JEST DEMONSTRACJA ZEGARA CZASU RZECZYWISTEGO
20 REM PROCEDURA TA PRZYJMUJE CZAS W GODZINACH, MINUTACJ I SEKUNDACH
30 REM USTAWIA ZEGAR CZASU RZECZYWISTEGO NA ZERO
40 REM BIEŻĄCA WARTOŚĆ RTCLOCK JEST UŻYWANA DO DODANIA CZASU POCZĄTKOWEGO, ABY OTRZYMAĆ
42 REM BIEŻĄCY CZAS W GODZ,MIN,SEK
45 HIGH=1536:MED=1537:LOW=1538
50 REM
60 REM ******PUNKT WEJŚCIA******
65 REM
70 ON CMD GOTO 100,200
95 REM
96 REM ****INICJALIZUJ ZEGAR*****
97 REM
100 POKE 20,0:POKE 19,0:POKE 18,0
105 DIM CLOCK$(50)
106 CLOCK$=" "
107 GOSUB 300
110 IHOUR=HOUR:IMIN=MIN:ISEC=SEC:RETURN
197 REM
198 REM *******ODCZYTAJ ZEGAR*****
199 REM
200 REM
201 A=USR(ADR(CLOCK$))
210 TIME=C(((PEEK(HIGH)*256)+PEEK(MED))*256)+PEEK(LOW))/59.923334
220 HOUR=INT(TIME/3600):TIME=TIME-(HOUR*3600)
230 MIN=INT(TIME/60):SEC=INT(TIME-(MIN*60))
235 SEC=SEC+ISEC:IF SEC>60 THEN SEC=SEC-60:MIN=MIN+1
236 MIN=MIN+IMIN:IF MIN>60 THEN MIN=MIN-60:HOUR=HOUR+1
237 HOUR=HOUR+IHOUR
240 HOUR=HOUR-(INT(HOUR/24))*24
250 RETURN
300 FOR J=1 TO 38:READ Z:CLOCK$(J,J)=CHR$(Z):NEXT J:RETURN
310 DATA 104,165,18,141,0,6,165,19,141,1,6,165
320 DATA 20,141,2,6,165,18,205,0,6,208,234
330 DATA 165,19,205,1,6,208,227,165,20,205,2,6,208,220,96
Rys.8-17 Zegar czasu rzeczywistego
1 REM TO JEST PROGRAM KONTROLUJĄCY TEMPO METRONOMU
2 REM
3 REM
5 PRINT "+":REM                       CZYŚĆ EKRAN
10 X=10:REM                           WARTOŚĆ POCZĄTKOWA TEMPA
20 FOR J=1 TO 10:NEXT J:REM           PROGRAMOWA PĘTLA OPÓŹNIAJĄCA
50 IF STICK(0)=14 THEN X=X+1 :REM     JOYSTICK DO PRZODU OZNACZA ZWIĘKSZENIE TEMPA
51 IF STICK(0)=13 THEN X=X-1 :REM     JOYSTICK DO TYŁU OZNACZA ZWOLNIENIE TEMPA METRONOMU
52 IF X<1 THEN X=1:REM                NIGDY NIE SCHODŹ PONIŻEJ JEDEN
53 IF X>255 THEN X=255:REM            LUB POWYŻEJ 255
54 REM                                WYPISZ TAKTY/MINUTĘ
56 ? "";INT(3600/X);" BEATS/MINUTES       "
60 POKE 0,X:REM                       ADRES $0000 ZAWIERA TEMPO DLA
70 NEXT I :REM                        PONIŻSZEJ PROCEDURY W ASEMBLERZE
Rys.8-18 Procedura metronomu w języku BASIC
40            *=$600
50 ;PROCEDURA METRONOMU...UŻYWA $0000 DO PRZEKAZYWANIA TEMPA METRONOMU
60 ;
70 AUDF1       =       $D200   REJESTR CZĘSTOTLIWOŚCI AUDIO
80 AUDC1       =       $D201   REJESTR STERUJĄCY AUDIO
90 FREQ        =       $08     WARTOŚĆ AUDF1
0100 VOLUME    =       $AF     WARTOŚĆ AUDC1
0110 OFF       =       $A0     WYŁĄCZ GŁOŚNOŚĆ
0120 SETVBV    =       $E45C   PROCEDURA USTAWIANIA WARTOŚCI TIMERA
0130 XITVBV    =       $E462
0140 CDTMV2    =       $021A   TIMER 2
0150 CDTMA2    =       $0228   WEKTOR TIMERA 2
0160 ZTIMER    =       $0000   WARTOŚĆ TIMERA VBLANK NA STRONIE ZEROWEJ
0170 ;
0180 START    LDA     #10
0190          STA     ZTIMER
0200 ;     USTAW WEKTOR TIMERA
0220 ;
0230 INIT     LDA     #CNTINT&255
0240          STA     CDTMA2
0250          LDA     #CNTINT/256
0260          STA     CDTMA2+1
0270 ;
0280 ;     USTAW WARTOŚĆ TIMERA PO WEKTORZE
0290 ;
0300          LDY     ZTIMER  USTAW TIMER DWA NA ZLICZANIE
0310          JSR     SETIME
0320          RTS
0340 ;      ZLICZANIE W DÓŁ METRONOMU WSKAZUJE TUTAJ
0380 ;      USTAW KANAŁ AUDIO NA KLIK METRONOMU
0390
0400 CNTINT   LDA     #VOLUME
0410          STA     AUDC1
0420          LDA     #FREQ
0430          STA     AUDF1
0435          LDY     #$FF     OPÓŹNIENIE
0440 DELAY    DEY
0442          BNE     DELAY
0450          STY     AUDC1
0460          JMP     INIT
0480 ;
0490 ;      PODPROGRAM DO USTAWIANIA TIMERA
0500 ;
0520 SETIME   LDX     #0              LICZBA OKRESÓW VBLANK < 256
0530          LDA     #2              USTAW TIMER 2
0540          JSR     SETVBV          PROCEDURA SYSTEMOWA USTAWIANIA TIMERA
0550          RTS
0560          *=$2E2
0570          .WORD   START
0580          .END
Rys.8-19 Procedura metronomu w języku asemblera

Na początek:  podrozdziału   strony 

Pakiet zmiennoprzecinkowy

Pakiet zmiennoprzecinkowy (ang. Floating Point Package, FPP) jest zbiorem procedur, które rozszerzają możliwości arytmetyczne systemu operacyjnego. Procedury te są zebrane w osobnym układzie pamięci ROM, który jest udostępniony jako część 10KB SYSTEMU OPERACYJNEGO ATARI. Pakiet FPP znajduje się pod adresem szesnastkowym $D800 - $DFFF. Nie zmieniono go w wersji B systemu operacyjnego. Poniżej opisano szczegółowo wewnętrzną reprezentację liczb, dostępne procedury i ich właściwą sekwencję wywoływania. Dodano również przykładowy program w asemblerze do zilustrowania sposobu dostępu do pakietu FPP z programów użytkownika.

Wewnętrzna reprezentacja

Czytelników pragnących zapoznać się ze sposobami reprezentacji liczb w komputerach zapraszam do osobnego artykułu "Binarne Kodowanie Liczb".

Wewnętrznie pakiet FPP konfiguruje liczby jako wartości 6-bajtowe. Każda liczba składa się z 1-bajtowego wykładnika i 5-bajtowej mantysy w formacie BCD (ang. Binary Coded Decimal). Tę reprezentację wybrano, aby zminimalizować błędy zaokrągleń, które mogą się pojawiać w niektórych procedurach arytmetycznych.

Bit znakowy bajtu wykładnika dostarcza znaku mantysy, 0 dla dodatniej, 1 dla ujemnej. Najmniej znaczące 7 bitów wykładnika dostarczają wykładnika potęgi liczby 100 w notacji z nadmiarem 64. W notacji tej wartość 64 jest dodawana do wykładnika zanim zostanie on umieszczony w bajcie wykładnika. Pozwala to wyrazić pełny zakres wykładników, dodatnich i ujemnych, bez potrzeby stosowania bitu znakowego.

Mantysa jest zawsze znormalizowana w taki sposób, iż jej najbardziej znaczący bajt ma wartość niezerową. Jednakże, ponieważ mantysa jest w formacie BCD, a wykładnik reprezentuje potęgi 100 a nie 10, to wynikowa precyzja może posiadać 9 lub dziesięć cyfr dokładnych. Przecinek dziesiętny jest ustawiany na prawo od pierwszego bajtu mantysy, zatem wykładnik mniejszy od 64 (szesnastkowo 40) oznacza liczbę mniejszą od 1.

PRZYKŁADY (wartości w formacie są podane szesnastkowo)

Liczba: 0,02 = 2 × 100-1
Format: 3F 02 00 00 00 00 (wykładnik= 40-1)

Liczba: -0,02 = -2 × 100-1
Format: BF 02 00 00 00 00 (wykładnik= 80+40-1)

Liczba: 37,0 = 37 × 1000
Format: 40 37 00 00 00 00 (wykładnik= 40+0)

Liczba: -460312 = -46,0312 × 1002
Format: C2 46 03 12 00 00 (wykładnik= 80+40+2)

Liczba zero jest traktowana jako przypadek specjalny i tworzy ją zerowy wykładnik i zerowa mantysa. Test na zero można wykonać sprawdzając albo wykładnik, albo pierwszy bajt mantysy.

Schemat ten pozwala reprezentować dynamiczny zakres liczb od 10-98 do 10+98.

Wykorzystanie pamięci

Przy implementacji pakietu FPP wykorzystywane są dwa obszary pamięci RAM:

$00D4 - $00FF na stronie zerowej
$057E - $05FF na stronie piątej

Obszary te są używane na parametry sterujące jak również do symulacji kilku rejestrów zmiennoprzecinkowych. Dwa pseudorejestry należące do głównego kręgu zainteresowań noszą nazwy FR0 i FR1 (adresy odpowiednio $00D4-$00D9 i $00E0-$00E5). Każdy z tych pseudorejestrów ma długość 6 bajtów i jest w stanie przechowywać liczbę w reprezentacji zmiennoprzecinkowej. 2-bajtowy wskaźnik jest używany do wskazywania liczby zmiennoprzecinkowej. Nazywa się on FLPTR i przebywa pod adresem $00FC.

Do konwersji pomiędzy liczbami zmiennoprzecinkowymi a łańcuchami ATASCII należy udostępnić bufory na łańcuchy tekstowe. Bufor wyjściowy nosi nazwę LBUFF i jest 128 bajtowym blokiem o adresie od $0580 do $05FF. Bufor wejściowy jest określany przez 2-bajtowy wskaźnik INBUFF pod adresem $00F3. Również jednobajtowy indeks CIX o adresie $00F2 jest używany jako przesunięcie w buforze wskazywanym przez INBUFF.

Typowa sekwencja rozkazów przy używaniu pakietu zmiennoprzecinkowego z poziomu języka asemblera wygląda następująco: najpierw łańcuch ATASCII reprezentujący jedną z liczb, które mają być użyte w procedurze arytmetycznej, zostaje umieszczony w jakimś buforze w dowolnym miejscu w pamięci. Następnie wskaźnik INBUFF zostaje ustawiony na początek tego łańcucha. Również wartość indeksu CIX powinna wynosić zero. Liczba jest wtedy gotowa do konwersji na jej zmiennoprzecinkową reprezentację, zatem zostaje wywołana procedura AFP. Spowoduje to umieszczenie liczby zmiennoprzecinkowej w rejestrze FR0, skąd można jej użyć w dowolnej operacji FPP. Po wykonaniu operacji matematycznych zmiennoprzecinkowy wynik będzie w rejestrze FR0. Wywołanie procedury FASC zamieni tą liczbę na łańcuch ATASCII umieszczony w LBUFF. Przykład tego procesu znajdziesz na rys.8-21.

Aby użyć wartości 16-bitowych z pakietem FPP, umieść te dwa bajty liczby w dwóch najniższych bajtach rejestru FR0 ($D4 i $D5) i wykonaj rozkaz JSR IFP, który zamieni tę liczbę całkowitą na jej reprezentację zmiennoprzecinkową i pozostawi wynik w FR0. Podprogram FPI wykonuje operację odwrotną.

Tabelka poniżej wymienia dostępne funkcje, ich adresy w ROM, używane pseudorejestry oraz przybliżony, maksymalny czas obliczeń.

PROCEDURY ZMIENNOPRZECINKOWE

NAZWA ADRES FUNKCJA WYNIK MAKSYMALNY
CZAS
WYKONANIA
(mikrosekundy)
AFP
FASC
IFP
FPI
FSUB
FADD
FMUL
FDIV
FLDOR
FLDOP
FLD1R
FLD1P
FSTOR
FSTOP
FMOVE
PLYEVL
EXP
EXP1O
LOG
LOG10
ZFR0
AF1
D800
D8E6
D9AA
D9D2
DA60
DA66
DADB
DB28
DD89
DD8D
DD98
DD9C
DDA7
DDA8
DDB6
DD40
DDC0
DDCC
DECD
DED1
DA44
DA46
ATASCII na liczbę zmiennoprzecinkową
Liczba zmiennoprzecinkowa na ATASCII
Liczba całkowita na zmiennoprzecinkową
Liczba zmiennoprzecinkowa na całkowitą
FR0-FR1 Odejmowanie
FR0+FR1 Dodawanie
FR0*FR1 Mnożenie
FR0/FR1 Dzielenie
Ładowanie liczby zmiennoprzecinkowej przy pomocy X,Y
Ładowanie liczby zmiennoprzecinkowej przy pomocy FLPTR
Ładowanie liczby zmiennoprzecinkowej przy pomocy X,Y
Ładowanie liczby zmiennoprzecinkowej przy pomocy FLPTR
Zapis liczby zmiennoprzecinkowej przy pomocy X,Y
Zapis liczby zmiennoprzecinkowej przy pomocy FLPTR
Przeniesienie FR0
Obliczenia wielomianowe
Potęgowanie - eFR0
Potęgowanie - 10FR0
Logarytm naturalny
Logarytm dziesiętny
Zerowanie
Zerowanie rejestru w X
FR0
LBUFF
FR0
FR0
FR0
FR0
FR0
FR0
FR0
FR0
FR1
FR1
FR0
FR0
FR1
FR0
FR0
FR0
FR0
FR0
FR0
zmienny
3500
950
1330
2400
740
710
12000
10000
70
60
70
60
70
70
60
88300
115900
108800
136000
125400
80
80

Rys.8-20 Procedury zmiennoprzecinkowe

0000        20         *= $4000  ; DOWOLNIE WYBRANY PUNKT STARTOWY
DDB6        30   FMOVE  = $DDB6
DA60        40   FSUB   = $DA60
0482        50   FTEMP  = $0482
DDA7        60   FSTOR  = $DDA7
D8E6        70   FASC   = $D8E6
00F3        80   INBUFF = $00F3
D800        85   AFP    = $D800
00F2        90   CIX    = $00F2
0580        100  LBUFF  = $0580
009B        120  CR     = $9B
0009        130  PUTREC = $09
0005        140  GETREC = $05
E456        150  CIOV   = $E456
0342        160  ICCOM  = $0342
0344        170  ICBAL  = $0344
0348        180  ICBLL  = $0348
            190  ;
            200  ; DEMONSTRACJA LICZB ZMIENNOPRZECINKOWYCH
            210  ; ODCZYTUJE DWIE LICZBY Z EDYTORA EKRANOWEGO,
            215  ; ZAMIENIA JE NA REPREZENTACJE ZMIENNOPRZECINKOWE,
            220  ; ODEJMUJE PIERWSZĄ OD DRUGIEJ,
            225  ; UMIESZCZA WYNIK W FTEMP,
            230  ; KTÓRY JEST REJESTREM ZDEFINIOWANYM PRZEZ UŻYTKOWNIKA,
            240  ; I WYŚWIETLA WYNIK.
4000 205340 260  START   JSR GETNUM      ; POBIERZ PIERWSZĄ LICZBĘ Z E:
4003 20B6DD 270          JSR FMOVE       ; PRZENIEŚ LICZBĘ Z FR0 DO FR1
4006 205340 280          JSR GETNUM      ; POBIERZ DRUGĄ LICZBĘ Z E:
4009 2060DA 290          JSR FSUB        ; FR0 <-- FR0-FR1
400C 900A   300          BCC NOERR       ; OMIŃ, JEŚLI NIE MA BŁĘDU
400E A981   340          LDA #ERRMSG&255 ; JEŚLI JEST, WYŚWIETL WIADOMOŚĆ
4010 8D4403 350          STA ICBAL
4013 A940   360          LDA #ERRMSG/256
4015 4C3940 370          JMP CONTIN
4018 A282   390  NOERR   LDX #FTEMP&255  ; UMIEŚĆ WYNIK W FTEMP
401A A004   400          LDY #FTEMP/256
401C 20A7DD 410          JSR FSTOR
            420  ;
            430  ; ZAMIEŃ LICZBĘ NA ŁAŃCUCH ATACSII.
            440  ; ZNAJDŹ KONIEC ŁAŃCUCHA,
            445  ; ZMIEŃ LICZBĘ UJEMNĄ NA DODATNIĄ,
            450  ; I DODAJ ZNAK POWROTU KARETKI.
401F 20E6D8 470          JSR FASC        ; LICZBA ZMIENNOPRZECINKOWA NA ATASCII, WYNIK W LBUFF
4022 A0FF   480          LDY #$FF
4024 C8     490  MLOOP   INY
4025 B1F3   500          LDA (INBUFF),Y  ; ZAŁADUJ NASTĘPNY BAJT
4027 10FB   510          BPL MLOOP       ; JEŚLI DODATNI, KONTYNUUJ
4029 297F   520          AND #$7F        ; JEŚLI NIE, WYMASKUJ BIT ZNAKU
402B 91F3   530          STA (INBUFF),Y
402D C8     540          INY
402E A99B   550          LDA #CR         ; ZAPISZ ZNAK POWROTU KARETKI
4030 91F3   560          STA (INBUFF),Y
            570  ;
            580  ; WYŚWIETL WYNIK
4032 A5F3   600          LDA INBUFF      ; POBIERZ ADRES BUFORA
4034 8D4403 610          STA ICBAL
4037 A5F4   620          LDA INBUFF+1
4039 8D4503 630  CONTIN  STA ICBAL+1
403C A909   640          LDA #PUTREC     ; ROZKAZ DO ZAPISU REKORDU
403E 8D4203 650          STA ICCOM
4041 A928   660          LDA #40         ; USTAW DŁUGOŚĆ BUFORA NA 40
4043 8D4803 670          STA ICBLL
4046 A900   690          LDA #0
4048 8D4903 700          STA ICBLL+1
404B A200   710          LDX #0          ; IOCB 0 NA EDYTOR EKRANOWY
404D 2056E4 720          JSR CIOV        ; WYWOŁAJ CIO
4050 4C0040 730          JMP START       ; JESZCZE RAZ
            740  ;
            750  ; POBIERZ ŁAŃCUCH ATASCII Z E:
            755  ; ZAMIEŃ GO NA LICZBĘ ZMIENNOPRZECINKOWĄ, WYNIK W FR0
4053 A905   780  GETNUM  LDA #GETREC     ; POBIERZ REKORD (ZAKOŃCZONY ZNAKIEM CR)
4055 8D4203 790          STA ICCOM
4058 A980   800          LDA #LBUFF&255  ; USTAW ADRES BUFORA RÓWNY LBUFF
405A 8D4403 810          STA ICBAL
405D A905   820          LDA #LBUFF/256
405F 8D4503 830          STA ICBAL+1
4062 A928   840          LDA #40         ; USTAW DŁUGOŚĆ BUFORA NA 40
4064 8D4803 850          STA ICBLL
4067 A900   860          LDA #0
4069 8D4903 870          STA ICBLL+1
406C A200   880          LDX #0          ; IOCB 0 NA EDYTOR EKRANOWY
406E 2056E4 890          JSR CIOV        ; WYWOŁAJ CIO
4071 A980   900          LDA #LBUFF&255  ; ZAPISZ ADRES BUFORA W INBUFF
4073 85F3   910          STA INBUFF
4075 A905   920          LDA #LBUFF/256
4077 85F4   930          STA INBUFF+1
4079 A900   940          LDA #0          ; USTAW INDEKS BUFORA NA 0
407B 85F2   950          STA CIX
407D 2000D8 960          JSR AFP         ; WYWOŁAJ ATASCII NA LICZBĘ ZMIENNOPRZECINKOWĄ
4080 60     970          RTS
4081 45     980  ERRMSG  .BYTE     "ERROR",CR
4082 52
4083 52
4084 4F
4085 52
4086 9B
            1000 ; ADRES POCZĄTKU PROCEDURY
4087        1020         * = $2E0
02E0 0040   1030         .WORD START
02E2        1040         .END

Rys.8-21 Przykład zmiennoprzecinkowy


Na początek:  podrozdziału   strony 

Zespół Przedmiotowy
Chemii-Fizyki-Informatyki

w I Liceum Ogólnokształcącym
im. Kazimierza Brodzińskiego
w Tarnowie
ul. Piłsudskiego 4
©2024 mgr Jerzy Wałaszek

Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone
pod warunkiem podania źródła oraz niepobierania za to pieniędzy.

Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl

Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.

Informacje dodatkowe.