Artykuł z Informika I/1989, strony od 20 do 26.
"Młody Technik -- Informik" wydaje Instytut Wydawniczy "Nasza Księgarnia"

MIKROKOMPUTER JUNIOR -- pamięć dyskowa

WŁADYSŁAW STRUGAŁA

Każdego użytkownika mikrokomputera, jeśli tylko miał możliwość porównania komfortu pracy komputera wyposażonego w stację dysków i komputera z magnetofonem jako zewnętrzną pamięcią masową, nie trzeba przekonywać o zaletach pierwszego rozwiązania. Mikrokomputer szkolny Elwro 800-2 Junior ma możliwość pracy zarówno z magnetofonem, jak i ze stacją dysków elastycznych 5.25" -- dwa napędy (ang. Floppy Disk Drive, w skrócie FDD) po 720kB!

Junior produkowany jest w dwóch wersjach: nauczycielskiej i uczniowskiej. Zarówno pierwsza, jak i druga wersja mają możliwość współpracy ze stacją dysków. Standardowo jednak (ze względu na znaczny koszt stacji) tylko komputer nauczycielski może bezpośrednio z nią współpracować. Komputery uczniowskie natomiast, mogą korzystać ze stacji poprzez sieć komputerową Junet.

Struktura zapisu informacji na dyskach

Podstawowe parametry pamięci dyskowej mikrokomputera Elwro 800 Junior:
 1. Dwie strony dysku: 0 i 1.
 2. Każda strona ma 80 ścieżek.
 3. Każda ścieżka posiada 9 sektorów.
 4. Każdy sektor ma długość 512 bajtów.
 5. Tzw. rekord CP/M jest umowną jednostką długości zbioru. Jeden rekord to 128 bajtów. Jeden sektor zawiera 4 rekordy.

Sektor jest najmniejszą porcją informacji, którą fizycznie można odczytać lub zapisać na dysku. Na jednej ścieżce mieści się więc dokładnie 9×512 = 4608 bajtów, czyli 4.5kB informacji. Na jednej stronie mamy wobec tego 80×9=720 sektorów, czyli 80×4.5kB = 360kB, a na całym dysku 2×720 = 1440 sektorów, czyli 2×360kB = 720kB. Jest to pojemność maksymalna dysku. W rzeczywistości do dyspozycji użytkownika pozostaje mniej ze względu na wykorzystanie niektórych obszarów dysku na informacje systemowe, katalog oraz zaokrąglenia długości plików w górę do wielokrotności 2kB.

Dyskowy system operacyjny Juniora wymienia informacje ze stacją w porcjach zwanych blokami. [Ewentualnie jednostkami alokacji albo klastrami. JA] Jeden blok to 2kB. Blok zajmuje na dysku fizycznie 4 sektory. Na pojedynczej ścieżce mieści się wobec tego 21/4 bloku. Jeden blok może zajmować miejsce w całości na jednej ścieżce, jak również może być "rozrzucony" na dwóch ścieżkach (i na dwóch stronach!) dysku. Odpowiednich przeliczeń, gdzie fizycznie na dysku znajduje się określony blok, dokonuje w sposób niewidoczny dla użytkownika -- system operacyjny w czasie zapisu lub odczytu informacji.

Oczywiście użytkownik komputera nie musi pamiętać, w których blokach zapisany jest jego program czy dane. To także załatwia za niego system operacyjny. Do wczytania lub zapisu programu lub danych wystarcza nazwa (pliku, zbioru).

Dysk podzielony jest na kilka zarezerwowanych obszarów:
 1. Na ścieżce 0 (numeracja ścieżek od 0 do 79), na stronach 0 i 1 zapisywany jest system operacyjny CP/J.
 2. Na ścieżce 1 na stronie 0 oraz w sektorach od 1 do 7 (numeracja sektorów na ścieżce od 1 do 9) na ścieżce 1 na stronie 1 (16 sektorów czyli 4 bloki) zapisany jest tzw. katalog dysku (ang. directory). 3. Od sektora 8 na ścieżce 1 na stronie 1 rozpoczyna się obszar przeznaczony na dane użytkownika.

Każdy zbiór użytkownika opisany jest w katalogu dysku w tzw. tablicach FCB (ang. File Control Błock). [W CP/M tablicą FCB nazywamy blok 36 bajtów służący do obsługi otwartego pliku -- ponieważ pierwsze 32 bajty FCB odpowiadają zapisowi pozycji w katalogu, tutaj dla uproszczenia pozycje katalogu nazwano również FCB. JA] Struktura FCB opisana jest dokładnie w instrukcji komputera. Pojedynczy zbiór użytkownika może być opisany przez jedną lub więcej tablic FCB (zależy to od wielkości danego zbioru). Pojedyncza tablica FCB składa się z 32 bajtów. W skrócie jej struktura jest następująca:
 1. Bajt nr 0 -- numer użytkownika lub #E5, jeśli zbiór jest skasowany. Umożliwia identyfikację zbiorów poszczególnych użytkowników sieci Junet. [#E5 jest negatywem kodu #1A (^Z), oznaczającego w CP/M koniec zbioru tekstowego. Podczas formatowania sektory dysku są wypełniane bajtami #E5, również katalog, stąd po sformatowaniu wszystkie pozycje katalogu są uznawane za wolne. Normalnie w CP/M numer użytkownika jest z przedziału 0..15, czasem 0..31. Pliki zapisane przez użytkowników 32..63 Junet mogą być niewidoczne pod CP/M. JA]
 2. Bajty 1..11 -- nazwa zbioru (8 bajtów) z rozszerzeniem (3 bajty) (razem 11 bajtów).
 3. Bajt 12 -- numer tablicy FCB opisującej dany zbiór. Pierwsza tablica FCB zbioru zawiera tutaj nr 0. Jeśli zbiór jest dłuższy niż 16kB, wówczas opisany jest  także w kolejnych tablicach FCB. Bajt 12 w następnych tablicach zawiera wówczas kolejne liczby 1, 2 itd. Zbiór odługości 16..32kB opisany jest  w dwóch tablicach FCB, 32..48kB w trzech. Dłuższe zbiory raczej nie występują ze względu na ograniczenie pamięci operacyjnej komputera Junior. [Niemniej można je utworzyć np. programem dBase II. JA]
 4. Bajty 13..14 są zarezerwowane. [Niekiedy bajt 14 może zawierać wartość #81..#FF, która informuje ile z bajtów zapisanych w ostatnim rekordzie należy jeszcze do pliku (odpowiednio 1..127). Dzięki temu można określić długość pliku z dokładnością do jednego bajtu. Do długości pliku wyliczonej z ilości rekordów należy dodać zawartość bajtu 14 traktowanego jako liczba ze znakiem (-127..0). JA]
 5. Bajt 15 podaje długość zbioru (lub fragmentu zbioru) opisanego daną tablicą FCB w rekordach.
 6. Bajty 16.. 31 zawierają numery bloków, w których zapisany jest zbiór (lub fragment zbioru) opisany daną tablicą FCB. Każdy numer bloku zajmuje dwa bajty w tablicy. Jedna tablica FCB może więc opisywać zbiór (lub fragment zbioru) o wielkości maksimum 8 bloków, czyli 16kB.

Katalog dysku zajmuje 8kB (4 bloki -- numery od 0 do 3). Może więc pomieścić 256 tablic FCB. Jeżeli każdy zbiór opisany byłby jedną tablicą FCB i miałby długość maksymalnie 2kB, wówczas wykorzystując tylko 256×2kB = 512kB nominalnej pojemności dysku można go całkowicie wypełnić. Najkrótszy nawet zbiór (1 bajt) zajmuje na dysku 1 blok (2kB) i jedną tablicę FCB. Należy mieć to na uwadze i -- w miarę możliwości oczywiście -- nie "zaśmiecać" dysku bardzo krótkimi zbiorami. [Wynika stąd przeciętna wielkość pliku równa 2.74kB. (Pojemność dyskietki 720-9-8-1=702 kB podzielona przez ilość pozycji katalogu.) JA]

Mikrokomputer Junior ma dwa tryby pracy: ZX Spectrum i CP/J. W trybie ZX Spectrum ze stacji dysków może korzystać bezpośrednio jedynie komputer, który wyposażony jest w płytkę tzw. kontrolera (sterownika) stacji dysków (ang. Floppy Disc Controller, w skrócie FDC), i do którego jest ona podłączona (zwykle nauczycielski). Odczyt i zapis zbiorów dokonuje się wówczas przy pomocy poleceń o składni podobnej jak przy współpracy z magnetofonem tzn. LOAD, MERGE, SAVE. Po słowach tych należy jedynie umieścić gwiazdkę *, a nazwa zbioru nie może mieć więcej jak 8 znaków. [Podobnie zresztą jak w bardziej popularnym TOS A.2. JA]

Dodając po nazwie CODE informujemy komputer, że chodzi o zbiór bajtów, DATA -- zmienne i tablice (jak w przypadku magnetofonu). Na podstawie tych informacji system operacyjny w trybie ZX Spectrum dokonuje zapisu lub odczytu zbioru określonego typu (z odpowiednim rozszerzeniem nazwy). W tablicach FCB zbiory typu ZX Spectrum posiadają rozszerzenia nazwy PRG, COD, ARR, STR.

Pierwszy blok zbiorów typu ZX Spectrum zawiera zawsze na początku znane użytkownikom komputera ZX Spectrum nagłówki o identycznej jak w przypadku magnetofonu długości (17 bajtów) i strukturze. [Nie ma typu bloków ani sum kontrolnych, jak to jest w plikach .TAP. W ZXVGS do obsługi plików z takimi nagłówkami przeznaczone jest rezydentne rozszerzenie PRG.RZX, które również automatycznie dodaje odpowiednie rozszerzenia nazw plików. JA]

Jeżeli zapisujemy na dysk w trybie ZX Spectrum zbiór np. bajtów o długości dokładnie 2kB, to zostanie on fizycznie zapisany w dwóch blokach (bo 17 bajtów nagłówka + 2kB danych to już więcej niż 2kB). W pierwszym bloku zostanie zapisany najpierw nagłówek (17 bajtów), a następnie 2048-17=2031 bajtów danych. W drugim bloku umieszczone zostanie pozostałe 17 bajtów danych.

Normalnie użytkownik komputera nie ma możliwości "podglądania" informacji zapisanych np. na ścieżkach systemowych lub w katalogu dysku. Może jedynie zapisywać, czytać lub kasować zbiory o określonej nazwie, lub też czytać nazwy i niektóre parametry zbiorów zapisane w katalogu dysku (polecenia DIR -- w trybie ZX Spectrum i CP/J -- czy XDIR w trybie CP/J). Jednakże pisząc odpowiednie procedury w kodzie maszynowym można czytać lub zapisywać dowolne obszary na dyskach Juniora, a także na dyskach zapisanych w innych formatach (np. 360kB IBM)!

Struktura pamięci operacyjnej ELWRO 800-2 Junior

Chcąc wykonywać nietypowe operacje dyskowe należy poznać strukturę pamięci operacyjnej Juniora.

Komputer wyposażony jest w 64kB pamięci RAM oraz 24kB ROM.

Pamięć RAM zajmuje oczywiście pełny obszar przestrzeni adresowej procesora Z80. Pamięć ROM podzielona jest na dwa bloki: 16kB głównej pamięci ROM zajmującej adresy od #0000 do #3FFF, oraz dodatkowy ROM 8kB w obszarze od #0000 do #1FFF.

Jak więc widać w obszarze adresowym #0000..#1FFF egzystują jednocześnie RAM i oba ROMy, a w obszarze #2000..#3FFF -- RAM i ROM podstawowy. Aby uniknąć kolizji jednoczesnego dostępu do różnych pamięci, Junior wyposażony jest w odpowiedni mechanizm dynamicznego przełączania różnych banków (stron) pamięci operacyjnej. Oczywiście normalnie użytkownik nie musi wiedzieć, który z bloków pamięci jest aktywny. Znowu robi to za niego system operacyjny. Dla dalszych rozważań należy jednak wiedzieć kiedy i które obszary pamięci są aktywne.

W trybie ZX Spectrum struktura pamięci jest następująca:

Główna pamięć RAM #4000..#FFFF (jak w ZX Spectrum). Obszar RAM #2000..#3FFF wykorzystywany jest przez system operacyjny (niewidocznie dla użytkownika) przy operacjach ze stacją dysków oraz operacjach sieciowych. Natomiast RAM od #0000 do #1FFF w trybie ZX Spectrum nie jest wykorzystany.

Główny ROM 16kB zawiera zmodyfikowany system operacyjny ZX Spectrum. Zmienione są np. komunikaty błędów, wzorce znaków, procedury obsługi drukarki, procedury obsługi przerwań (całkowicie zmieniono procedurę przerwania niemaskowalnego, rozszerzono procedurę obsługi przerwania maskowalnego). [Przerwanie niemaskowalne może być generowane poprzez odebranie bajtu z Junet. JA] Wykorzystano też ROM w obszarze #386E..#3CFF, który w ZX Spectrum był nie używany. Zasadnicze procedury obsługi magnetofonu są identyczne jak w ZX Spectrum. Jedynie na ich początku system sprawdza, czy chodzi w danym momencie o transmisję w sieci Junet, na dysk czy też na magnetofon. Zawartość głównego ROMu Juniora można odczytywać przez np. PRINT PEEK adres oraz zdekodować korzystając z dowolnego deasemblera ZX Spectrum.

ROM dodatkowy 8kB zawiera przede wszystkim procedury obsługi stacji dysków, procedury obsługi sieci komputerowej, oraz inne jak np. sprawdzanie konfiguracji komputera (ze stacją czy bez), odczyt swojego numeru komputera (każdy Junior ma zakodowany swój numer), wydruk na ekranie komunikatu początkowego po włączeniu zasilania Juniora lub po instrukcji NEW, itd. Normalnie ROM ten jest niedostępny dla użytkownika tzn. nie można go odczytać np. przez PRINT PEEK adres.

W trybie CP/J aktywna jest cała pamięć RAM 64kB. W operacjach dyskowych system korzysta z procedur zawartych w ROM dodatkowym.

Mechanizm przełączania bloków pamięci

W Juniorze do przełączania bloków pamięci zarezerwowano jeden z możliwych adresów urządzeń wejścia-wyjścia. Wykonując instrukcję OUT (#F7),A, dokonywać można przełączania różnych bloków pamięci ROM i RAM. O tym, który blok pamięci ma zostać uaktywniony decyduje zawartość akumulatora A. Umiejętne wykorzystanie instrukcji OUT (#F7),A umożliwia także inne "sztuczki" jak np. możliwość sprawdzenia numeru komputera czy zmiana położenia ekranu w pamięci. [Według schematu, z bajtu zapisywanego do portu #F7 zapamiętywane są tylko bity 4..7, a bity 0..3 są ignorowane. JA]

Zawartości akumulatora i odpowiadające im warianty pamięci zawiera tabela I. [Poszczególne warianty są tworzone przez odpowiednio zaprogramowany EPROM 256B, stąd nieco niepasujący tryb CP/J. Jedynie bit 5 przełącza ekran niezależnie od zaprogramowania tego EPROM. JA]
TABELA I
(#F7) #0000..#1FFF #2000..#3FFF #4000..#FFFF ekran tryb
#08 (8)  ROM podst. ROM podst. RAM ZX Spectrum (#4000) ZX Spectrum
#28 (40)  ROM podst. ROM podst. CP/J (#E000)
#48 (72)  ROM dodatk. RAM ZX Spectrum (#4000)
#68 (104)  ROM dodatk. RAM CP/J (#E000)
#88 (136)  ROM podst. RAM ZX Spectrum (#4000)
#A8 (168)  RAM RAM CP/J (#E000) CP/J
#C8 (200)  ROM dodatk. ROM podst. ZX Spectrum (#4000)
#E8 (232)  ROM dodatk. ROM podst. CP/J (#E000)
#10 (16)  umożliwia odczyt numeru komputera

Krótki program w asemblerze Z80 umożliwiający odczytanie numeru komputera pokazano na wydruku 1. Po zasemblowaniu i umieszczeniu kodu wynikowego w pamięci, wykonując PRINT USR 60000 otrzymamy na ekranie numer komputera, na którym pracujemy. [Numer komputera odczytywany jest portem #FFFE, po ustawieniu bitu 4 w porcie #F7. Stan tego bitu nie wpływa na działanie pozostałych funkcji komputera. JA]

Wydruk 1.  Procedura odczytu numeru komputera

0010 ;
0020 ;                   ;"Odczyt numeru komputera"
0060
0070       ORG 60000     ;adres wywołania
0080 ;
0090 START DI            ;zablokuj przerwania
0100       LD A,10H      ;przygotowanie do odczytu
0110       OUT (0F7H),A  ;   numeru
0120       LD A,0FFH
0130       IN A,(0FEH)   ;A:= numer komputera
0140       LD B,0
0150       CPL           ;negacja akumulatora
0160       AND 3FH       ;weź tylko bity 0..5
0170       LD C,A        ;C - numer komputera
0180       LD A,8        ;ROM pod., RAM 16..64kB
0190       OUT (0F7H),A  ;przełącz pamięć
0200       EI            ;odblokuj przerwania
0210       RET           ;powrót - BC nr komp.
0220       END

Niestety nie jest możliwe włączenie pełnej pamięci RAM z obrazem ZX Spectrum tzn. pod adresem #4000. Można natomiast wykonując w ZX Spectrum BASIC OUT 247,40 przełączyć ekran ZX Spectrum na CP/J (oczywiście dotyczy to jedynie wyświetlania obrazu -- wszystkie procedury obsługi ekranu np. PRINT, CLS, itd., będą dalej działać tylko na ekranie ZX Spectrum).

Przełączanie bloków pamięci można oczywiście wykonać także innymi instrukcjami OUT procesora Z80, pamiętając jedynie, aby na mniej znaczącej części adresowej umieścić wartość #F7. Możliwe jest również wykonanie w ZX Spectrum BASIC instrukcji OUT 247,argument, jednakże należy w tym przypadku zwrócić baczną uwagę na argument operacji OUT! W zasadzie możliwe jest jedynie wykonanie OUT 247,40 (zmiana ekranu) i OUT 247,168 (pełny RAM), przy czym uprzednio należy umieścić w odpowiednim miejscu RAM procedurę, która na końcu będzie posiadała instrukcję (oczywiście już w kodzie wewnętrznym Z80) OUT 247,8. Po jej wykonaniu nastąpi powrót do sytuacji wyjściowej (ROM podstawowy, RAM #4000..#FFFF). W wydruku 2 przedstawiono krótki program w asemblerze Z80, który umożliwia kopiowanie pamięci RAM #2000..#3FFF do RAM #8000..#9FFF w niecodzienny sposób przez OUT 247,168. Program po asemblacji należy uruchomić instrukcją (np.) RANDOMIZE USR 60000. Po uruchomieniu nastąpi wpisanie do pamięci RAM od adresu #1E7A odpowiedniego kodu. Procedura OUT w ZX Spectrum BASIC znajduje się dokładnie pod adresem #1E7F i w tym właśnie miejscu w pamięci RAM znajdzie się początek procedury kopiującej.

Program tam umieszczony nie ulega zniszczeniu nawet po wykonaniu sprzętowego kasowania!

Uwaga!

Nieostrożne wykonanie OUT 247 (szczególnie w ZX Spectrum BASIC, lecz również w kodzie wewnętrznym) z reguły powoduje unieruchomienie komputera!

Wydruk 2.  Procedura kopiowania RAM przez OUT 247,168

0010 ;
0020 ;                   ;"kopiowanie RAM 2000 3FFF do RAM
0030 ;                   ;" 8000..9FFF"
0040 ;                   ;"kopiowanie przez OUT 247,168 w
0050 ;                   ; ZX Spectrum BASIC"
0060 ;
0070       ORG 60000	 ;adres wywołania 60000
0080 ;
0090 DLUG  EQU 20H       ;długość bloku kodu
0100 KOPY3 EQU 1E7AH     ;adres przeznaczenia bloku kodu
0110 ;
0120 KOPY1 DI            ;zablokuj przerwania
0130       LD HL.KOPY2   ;HL:=adres źródła danych
0140       LD DE,KOPY3   ;DE:=adres przeznaczenia danych
0150       LD BC,DLUG    ;BC:=długość bloku
0160       LD A,0A8H     ;włącz pełny RAM
0170       OUT (0F7H),A	 ;
0180       LDIR          ;przeładuj pamięć
0190       LD A 0C9H     ;A:=kod RET i wpisz do
0200       LD (38H),A    ; adresu INT oraz
0210       LD (66H),A    ; NMI	
0220       LD A,8        ;ROM pod., RAM 16..64kB
0230       OUT (0F7H),A	 ;przełącz pamięć
0240       EI            ;odblokuj przerwania
0250       RET           ;powrót do ZX Spectrum BASIC
0260 ;
0270 KOPY2 LD A,8        ;A - przygotow. do przełącz.
0280       EI            ;odblokuj przerwania
0290       OUT (0F7H),A  ;wróć do BASIC (ROM pod adres 1E7F)
0300 ;
0310 S1E7F DI            ;tu wejście po OUT 247,168 (BASIC)
0320       PUSH HL       ;zabezpiecz rejestry
0330       PUSH DE       ;
0340       PUSH BC       ;
0350       LD BC,2000H   ;BC:=dł.  bloku
0360       LD HL,2000H   ;HL:=adres bloku
0370       LD DE,8000H   ;DE:=adres przeznaczenia
0380       LDIR	         ;przeładuj RAM 2000..3FFFh do
                         ; 8000..9FFF hex
0390       POP BC	 ;odtwórz rejestry
0400       POP DE        ;
0410       POP HL        ;
0420       JR KOPY2	 ;skok do wyjścia
0430       END

Korzystając z mechanizmu przełączania bloków pamięci można przepisać odpowiednią procedurą w kodzie maszynowym zawartość ROMu dodatkowego lub też RAMu z obszarów #0000..#3FFF do RAMu w innym miejscu i następnie np. analizować procedury zawarte w ROMie dodatkowym.

Procedura w asemblerze Z80 pokazana na wydruku 3 przepisuje ROM dodatkowy do wolnej pamięci RAM.

Wydruk 3. Procedura kopiowania ROM dod. - RAM 8000..9FFFh

0000       ORG 60000	 ;adres proc. 60000 dec.
0010 ;
0020 ADR0  EQU 32768     ;adres przeznacz danych
0030 ADR1  EQU 0         ;adres źródła danych
0040 ;
0050 COPY  DI	         ;zablokuj przerwania
0060       LD A 48H	 ;akumulator :=48 hex
0070       OUT (OF7H),A	 ;włącz ROM dodatk.
0080       LD HL,ADR1	 ;HL:= adres źródła danych
0090       LD DE,ADR0	 ;DE:= adres przeznaczenia
0100       LD BC,2000H	 ;BC:= długość bloku danych
0110       LDIR          ;przepisz blok dł.8192 dec
0120       LD A,8        ;akumulator :=8
0130       OUT (0F7H),A	 ;ROM podst.,RAM 16..64kB
0140       EI            ;odblokuj przerwania
0150       RET           ;powrót
0160 ;
0170       END

Po wpisaniu powyższego programu za pomocą dowolnego asemblera dla ZX Spectrum, po wykonaniu asemblacji i umieszczeniu kodu wynikowego pod adresem 60000 uruchomić go można przez (np.) RANDOMIZE USR 60000. W obszarze 32768..40960 (#8000..#9FFF) znajdzie się kopia ROMu dodatkowego, którego zawartość można teraz analizować. Oczywiście program można umieścić w innym miejscu pamięci (inny argument dyrektywy ORG). Podobnie kopia ROMu także może być umieszczona w innym miejscu (ADR0). Podstawiając za ADR1 zamiast #0000 wartość #2000 możemy w ten sam sposób skopiować RAM z obszaru #2000..#3FFF.

W obszarze #2000..#3FFF pamięci RAM w trybie ZX Spectrum znajdują się bufory oraz zmienne systemowe używane przy operacjach dyskowych i sieciowych. Niewykorzystany (w trybie ZX Spectrum) obszar RAM #0000.. #1FFF można wykorzystać jako ramdysk. Także obszar #2000..#3FFF można wykorzystać jako ramdysk, jeśli nie używa się pamięci dyskowej i sieci komputerowej. Można do tego wykorzystać analogiczne procedury jak powyżej, jedynie w linii 60 należy zmienić argument instrukcji LD A,#A8 (pełny RAM), a także ustawić odpowiednie adresy źródła i przeznaczenia danych oraz potrzebną długość bloku. Można w ten sposób np. zapamiętywać i wywoływać ekran nie zajmując przy tym głównej pamięci RAM.

Pamięć RAM #2000..#3FFF w operacjach dyskowych

Wszystkie systemowe operacje dyskowe Juniora w trybie ZX Spectrum wykorzystują obszar RAM #2000..#3FFF. Wykaz niektórych zmiennych i bloków tego obszaru zawiera tabela II. Ponadto w zmiennej systemowej niewykorzystanej w ZX Spectrum pod adresem 23678 (#5CB0) bit 0 ustawiony informuje interpreter Junior BASIC o operacji dyskowej, a bit 1 o operacji sieciowej.
TABELA II
Adres(y)Opis
#2007Adres odczytu lub zapisu jednego sektora.
#2009..#2011Kody instrukcji i dane dla układu scalonego sterownika stacji NEC µPD 765.
#2012..#201AKody błędów sterownika dysków.
#2080..#28FFBufor, do którego wczytywane są bloki katalogu dysku (bloki 0..3). W danym momencie wpisany tam może być oczywiście tylko jeden blok (2kB).
#29EC..#29FCNagłówek zbioru typu ZX Spectrum.
#29FD..#32FCBufor, do którego wczytywane są z dysku (lub z którego zapisywane są na dysk) poszczególne bloki danych pliku. W pierwszych blokach plików typu ZX Spectrum pierwsze 17 bajtów zawiera nagłówek zbioru.
#3209Numer aktywnej stacji dysków.
#3800..#3EAFBufor, w którym system umieszcza przygotowany do wydruku na ekranie katalog dysku po wykonaniu polecenia DIR (w trybie ZX Spectrum).

Procedury dyskowe zawarte w pamięci ROM

Niektóre z wielu procedur zawartych w ROMie dodatkowym (8kB), które mogą być wykorzystane przez użytkowników we własnych programach w kodzie wewnętrznym Z80 zamieszczone są w tabeli III.
TABELA III
AdresOpis
#01EDWłączenie stacji. Numer napędu w rej. C (#01EE - numer w akumulatorze). Numer napędu zapamiętywany jest w rej. IY.
#01F8Wyłączenie napędu. Nie wymaga parametrów, gdyż numer stacji pamiętany jest w rejestrze IY. W czasie od włączenia (proc. #01ED) do wyłączenia stacji (proc. #01F8) nie wolno używać procedury #01ED.
#099AElementarna procedura komunikacji systemu ze scalo nym sterownikiem stacji NEC µPD 765. Po umieszczeniu w odpowiednim miejscu RAM kodu instrukcji oraz danych dla sterownika stacji (normalnie pod adresem #2009), wpisaniu w dwóch poprzednich komórkach (normalnie #2007) adresu zapisu lub odczytu sektora, należy w parze rejestrów BC podać adres (standardowo #2007) ww. parametrów i wywołać procedurę przez CALL #099A. W komórkach pamięci od #2011..#201A wpisywane są kody dotyczące statusu sterownika (najważniejsza jest zawartość #201A).
#0ACDPodstawowa procedura zapisu-odczytu jednego bloku (2kB). Przed jej użyciem należy umieścić na stosie procesora kod operacji np. ładowania bloku -- LD BC,0004: PUSH BC (zapis bloku -- LD BC,0006: PUSH BC), następnie w parze rejestrów BC należy umieścić numer bloku, a w parze DE adres zapisu-odczytu bloku.

Komunikacja systemu ze sterownikiem stacji

"Pośrednikiem" pomiędzy mikrokomputerem Junior a pamięcią dyskową jest układ scalonego sterownika stacji dysków elastycznych NEC µPD 765 (odpowiednik INTEL 8272) -- układ ten jest również stosowany w komputerach IBM PC XT/AT. Opis tego układu został zamieszczony w numerze 1/1988 czasopisma Mikroklan. Układ µPD 765 może pracować w dwóch trybach -- tzw. DMA (bezpośredni zapis-odczyt pamięci z pominięciem procesora komputera) oraz NON-DMA (procesor zapisuje i odczytuje dane ze sterownika). W Juniorze wybrano drugi tryb. [Bo tryb DMA wymaga użycia dodatkowych układów scalonych, albo co najmniej zmiany działania przerwań. JA]

Procesor Z80 komunikuje się ze sterownikiem za pośrednictwem trzech portów: #EF, #EE oraz #F1.

Przez OUT (#F1),A (A musi posiadać odpowiednią zawartość), procesor włącza i wyłącza jeden z dwóch napędów stacji (stacja Juniora jest podwójna). [W zasadzie dotyczy to silników, gdyż wybóru napędu dokonuje się programując kontroler. Zapamiętywane są bity 0..3, a 4..7 ignorowane. Bity 0 i 1 włączają silniki napędów, bit 2 jest podłączony do wejścia TC kontrolera, a bit 3 po zanegowaniu do wejścia RESET. JA]

Przez port #EF procesor wysyła do sterownika (OUT) kody poleceń i dane oraz odbiera dane odczytane z dysku (IN) i bajty statusu.

Przez port #EE procesor odczytuje (IN) ze sterownika jego główny rejestr stanu. W rejestrze stanu najważniejszy jest stan bitu 7, który informuje (po uaktywnieniu jednego z dwóch napędów przez OUT (#F1),A o gotowości sterownika i stacji do działania.

Przykłady niestandardowych procedur obsługi stacji

Wydruki 4 i 5 przedstawiają dwa przykłady procedur zapisu-odczytu pamięci dyskowej Juniora. Pierwsza z nich (wydruk 4) pozwala na odczytanie lub zapisanie dowolnego bloku na dysku (umożliwia np. bezpośredni odczyt katalogu dysku, odczyt pierwszych bloków typu ZX Spectrum, w których -- pierwsze 17 bajtów -- zawarty jest nagłówek zbioru), a także czytanie w trybie ZX Spectrum zawartości zbiorów typu "nie-Spectrum" (CP/J).

Wydruk 4.  Procedura odczytu-zapisu jednego bloku

0010 ;
0020 ;
0030       ORG 60000     ;adres wywoł.procedury
0040 ;
0050 BLOK  EQU 0ACDH     ;adres proc.zapisu-odczytu
0060 DYSNR EQU 3209H     ;adres zmien. - nr FDD
0070 ;
0080 RWBLK DI            ;zablokuj przerwania
0090       PUSH IY       ;przechowaj IY na stosie
0100       LD A,48H      ;A:=48 hex
0110       OUT (0F7H),A  ;włącz ROM dod. i RAM 8..64kB
0120       LD A,(NRDYS)  ;A:=numer FDD
0130       LD (DYSNR),A  ;i wpisz do 3209h
0140       LD BC (KODOP) ;BC:= kod op.4-odcz.,6-zapis
0150       PUSH BC       ;i umieść na stosie
0160       LD BC,(NRBLK) ;BC:=numer bloku
0170       LD DE,(ADRES) ;DE:=ad.zapisu-odczytu bloku
0180       CALL BLOK     ;czytaj-zapisz blok
0190       LD B,0        ;BC:=kod błędu 255 dobrze
0200       LD C,A        ;    0 - źle
0210       LD A,8        ;ROM podst.,RAM 16..64kB
0220       OUT (0F7H).A  ;
0230       POP IY        ;odtwórz rej. IY
0240       EI            ;odblokuj przerwania
0250       RET           ;wróć do BASIC ZX Spectrum
0260 ;
0270 NRDYS DEFB 0        ;nr FDD (0 lub 1)
0280 KODOP DEFW 4        ;kod oper. (4-load, 6-save)
0290 NRBLK DEFW 0        ;numer bloku
0300 ADRES DEFW 8000H    ;adres początku bloku w pamięci
0310 ;
0320       END

Druga (por. wydruk 5) umożliwia odczyt i zapis dowolnego sektora. Za jej pomocą można odczytywać normalnie ukryte dla użytkownika ścieżki systemowe (ścieżka 0 na stronach 0 i 1)! Ponadto pozwala na odczyt (i zapis) dysków zapisanych w innych formatach niż Junior!

Wydruk 5.  Procedura odczytu/zapisu jednego sektora

0010 ;
0020       ORG 60000     ;adres wywołania
0030 ;
0040 RWSEK DI            ;zablokuj przerwania
0050       PUSH IY       ;IY na stos
0060       LD A,48H      ;ROM dod., RAM 8..16kB
0070       OUT (0F7H),A  ;przełącz pamięć
0080       LD DE,2009H   ;DE:=adr. kodów ster.
0090       LD HL,USTAW   ;uPD 765 - tu ustaw.głow.
0100       LD BC,3       ;na ścieżkę
0110       LDIR          ;przepisz kody
0120       LD A,(NRDYS)  ;A:=numer FDD
0130       CALL 01EEH    ;włącz FDD
0140 WAIT  IN A,(0EEH)   ;odcz.rej. stat. sterów.
0150       BIT 7,A       ;czy stacja gotowa ?
0160       JR Z,WAIT     ;skok jeśli nie gotowa
0170       CALL OPOZN    ;opóźnienie
6180       LD BC,2007H   ;BC:=adr. kodów sterown.
0190       CALL 99AH     ;ustaw głowicę na ścieżkę
0200       LD DE,2007H   ;wpisz 2007..2010h kody dla
0210       LD HL,RDWR    ; sterownika
0220       LD BC,0BH     ;BC:=dług.bloku kodów
0230       LDIR          ;przepisz
0240       CALL OPOZN    ;opóźnienie
0250       LD BC,2007H   ;BC:=adres kodów sterownika
0260       CALL 99AH     ;zapisz/odczytaj sektor
0270       CALL OPOZN    ;opóźnienie
0280       CALL 1F8H     ;wyłącz FDD
0290       LD A,(201AH)  ;kod błędu do BC
0300       LD C,A        ;
0310       POP IY        ;odtwórz IY
0320       ID A,8        ;ROM podst., RAM 16..64kB
0330       OUT (0F7H),A  ;przełącz pamięć
0340       EI            ;odblokuj przerwania
0350       KET           ;wróć do BASIC ZX Spectrum
0360 ;
0370 OPOZN LD BC,80H     ;procedura opóźniająca
0380 LOOP  DJNZ LOOP     ;
0390       DEC C         ;
0400       JR NZ,LOOP    ;
0410       RET           ;powrót do głównej prac.
0420 ;
0430 USTAW DEFB 0FH      ;kod ster. - ustaw głowicę
0440 STDY1 DEFB 0        ;strona dysku, nr FDD
0450 SCIE1 DEFB 0        ;numer ścieżki (0..79)
0460 ;
0470 RDWR  DEFW 8000H    ;adres zapisu-odczytu
0480 CODE  DEFB 46H      ;kod ster.-tu odczyt sekt.
0490 STDY2 DEFB 0        ;strona dysku, nr FDD
0500 SCIE2 DEFB 0        ;nr ścieżki (0..79)
0510 STR   DEFB 0        ;strona (0..1)
0520 SEKT  DEFB 1        ;nr sektora (1..9)
0530       DEFB 2        ;512 bajtów/sektor
0540       DEFB 9        ;9 sektorów/ścieżkę
0550       DEFB 12H      ;odstęp między sektorami
0560       DEFB 0FFH     ;stała 255
0570 NRDYS DEFB 0        ;nr FDD (0 lub 1)
0580       END

W procedurze odczytu-zapisu bloku (wydruk 4) w komórce NRDYS (60040) należy umieścić numer napędu stacji (0 lub 1). W komórce KODOP (60041) należy wpisać 4, jeśli chcemy odczytać blok, lub też 6, jeśli chcemy zapisać blok. Dwie komórki NRBLK (60043) powinny zawierać numer bloku, który zamierzamy odczytać lub zapisać.

W komórkach ADRES wpisujemy adres początku obszaru pamięci, do którego zostaną wpisane dane odczytane z dysku przy odczycie bloku (lub z którego zostaną pobrane dane do zapisania na dysku przy zapisie bloku). W przykładzie podano adres #8000 (32768), a więc odczyt-zapis będzie dotyczył obszaru od 32768 do 34815.

Procedurę wywołuje się przez np. PRINT USR 60000.

Przy prawidłowym odczycie lub zapisie na ekranie powinniśmy otrzymać 255. Jeśli odczyt lub zapis nie był prawidłowy (np. dysk w innym formacie) na ekranie pojawi się 0.

Procedura z wydruku 5 umożliwia odczyt lub zapis dowolnego sektora na dysku formatu 720kB, jak również w innych formatach np. 360kB IBM. Wywołana z takimi jak podano w wydruku parametrami odczytuje pierwszy sektor ścieżki 0 na stronie 0 dysku umieszczonego w stacji 0 (czyli A) -- w formatach 720kB, 360kB i 180kB (jednostronne). Dane odczytane z dysku wpisuje do RAM od 32768 do 33279 (#8000..#81FF).

W celu odczytania innych sektorów należy zmienić poszczególne parametry dla sterownika µPD 765.

W komórce NRDYS można umieścić 0 lub 1 (napęd A lub B).

W komórkach STDY1 i STDY2 należy podać stronę i numer napędu: bity 0 i 1 -- nr napędu od 0 do 3 (w Juniorze 0 lub 1), bit 2 -- strona 0 lub 1.

W komórkach SCIE1 i SCIE2 należy wpisać numer ścieżki (od 0 do 79), przy czym nie muszą to być te same wartości. Jeśli do stacji włożymy dysk np. 360kB (każda strona zawiera tu po 40 ścieżek, a więc dwa razy mniej niż Junior) to chcąc odczytać jakiś sektor ze ścieżki np. 10, należy wpisać do SCIE1 wartość 20, a do SCIE2 wartość 10. Inaczej mówiąc należy ustawić głowicę stacji na odczyt ścieżki 20, a odczytać 10.

W komórce STR należy jeszcze raz podać numer strony dysku (0..1), a w komórce SEKT umieścić numer sektora 1..9.

Od adresu USTAW (#EAB6 = 60086) umieszczone są dane dla sterownika µPD 765: kod instrukcji "ustaw głowicę napędu na ścieżkę" (1 komórka -- #0F), strona i nr napędu (1 komórka), numer ścieżki (1 komórka).

Od adresu RDWR umieszczone są dane dla właściwej operacji zapisu lub odczytu sektora: adres odczytu (zapisu) sektora (2 komórki), kod operacji "odczytaj sektor" (46) (lub "zapisz sektor" -- 45), strona i nr napędu (1 komórka), numer ścieżki (1 komórka), strona (1 kom.), numer sektora (1 kom.). Dalej ilość 256-bajtowych bloków danych w jednym sektorze, liczba sektorów na ścieżkę, długość przerwy między sektorami, liczba 255, gdy sektor zawiera więcej niż 256 bajtów danych.

Dane od adresu SCIE2 do bajtu zawierającego #FF muszą być przy odczycie lub zapisie sektora identyczne z tzw. identyfikatorem sektora, który umieszczany jest na początku każdego sektora podczas formatowania dysku. Jeżeli dysk jest inaczej sformatowany np. jedna ścieżka zawiera 8 sektorów, to chcąc go odczytać należy zmienić niektóre dane dotyczące identyfikatora sektora (w tym przypadku liczbę sektorów na ścieżkę, a możliwe, że także i inne np. odstęp między sektorami, długość sektora).

Aby ułatwić posługiwanie się przedstawioną procedurą przy odczycie-zapisie dysków w dwóch formatach 720kB i 360kB można napisać dodatkowo program, który po określeniu numeru sektora (od 0 do 1439 dla dysku 720kB, lub od 0 do 719 dla 360kB) wyliczy numer strony, numer ścieżki i numer sektora na ścieżce. Dla łatwiejszego zrozumienia program napisany jest w ZX Spectrum BASIC (por. wydruk 6).

Wydruk 6.  Program czytania/zapisu sektorów w formatach 720 i 360 kB.

 10 LOAD *"CSEKTOR"CODE 60000,101
 20 CLS
 30 INPUT "DYSK 1/0";D: IF D<>0 AND D<>1 THEN GO TO 30
 40 INPUT "OPERACJA L/S";K$: IF K$<>"L" AND K$<>"S" THEN GO TO 40
 50 IF K$="L" THEN POKE L+91,70
 60 IF K$="S" THEN POKE L+91,69
 70 INPUT "FORMAT 720/360 kB" (1/2)";FORM: IF FORM<>l AND FORM< >2 THEN GO TO 70
 80 INPUT "SEKTOR 0..1439/0..719";NRSE: IF NRSE*FORM>1439 THEN GO TO 80
 90 REM ***   numer sektora na ścieżce (0..8)
100 LET SEK=NRSE-9*INT (NRSE/9)
110 REM ***   numer ścieżki
120 LET SCI=INT ((NRSE-SEK)/18)
130 REM ***   strona
140 LET STR=(NRSE-SEK)/9-SCI*2
150 REM ***   sektor 1..9
160 LET SEK=SEK+1
170 REM ***
180 LET L=60000
190 REM ***   strona i numer dysku
200 POKE L+87,D+4*STR: POKE L+92,PEEK (L+87)
210 REM ***   strona
220 POKE L+94,STR
230 REM ***   ścieżka (*2 dla 360kB)
240 POKE L+88.SCI*FORM: POKE L+93,SCI
250 REM ***   sektor
260 POKE L+95,SEK
270 REM ***   numer dysku
280 POKE L+100,D
290 REM ***
300 LET M=USR L: CLS : IF M<>0 THEN PRINT "BŁĄD ZAPISU-ODCZYTU": GO TO 30
310 CLS : PRINT "SEKTOR ";NRSE,"FORMAT ";F0RM: PRINT
320 LET ADRES=32768: FOR F=ADRES TO ADRES+511
330 PRINT F;"  ";PEEK F,CHR$ (PEEK F AND PEEK F>31)
340 NEXT F
350 GOTO 20


Uwaga:  w linii 10 zbiór "CSEKTOR" to kod wynikowy uzyskany
po zasemblowaniu procedury z wydruku 5.

Po uruchomieniu programu należy podać nr napędu, rodzaj operacji (Load--Save), format dysku oraz numer sektora.

W liniach 100..160 program przelicza numer sektora (0..1439 lub 0..719) na potrzebne dane tzn.: numer sektora na ścieżce, numer ścieżki i stronę. Następnie od linii 200 do 280 wpisuje wyliczone dane do odpowiednich komórek pamięci, gdzie stanowią one dane dla sterownika dysków. Po pomyślnym zakończeniu procedury maszynowej (linia 300) zmienna M = 0 i następuje wydruk na ekranie odczytanych (zapisanych) danych. W przeciwnym wypadku sygnalizowany jest błąd.

Program na wydruku 6 jest bardzo uproszczony. Można go rozbudować o wiele różnych funkcji, przede wszystkim, jeśli chodzi o wydruk (np. szesnastkowo). Można także przenieść np. przeliczenia sektorów do procedury maszynowej, itd.

Powyższy tekst został w całości napisany na mikrokomputerze Junior pracującym w trybie CP/J za pomocą edytora tekstów EDJ znajdującego się na dysku firmowym.

Procedury maszynowe napisane zostały przy użyciu asemblera EDITAS firmy Picturesque komputera ZX Spectrum (asembler przystosowano do współpracy ze stacją dysków Juniora). Następnie przy użyciu programu kopiującego napisanego w całości w asemblerze (znowu EDITAS) plik z niniejszym tekstem został przeniesiony na dysk w formacie IBM 360kB.

Po zamianie kodów polskich znaków diakrytycznych Juniora na kody "odpowiednie" dla IBM (patrz INFORMIK nr 2/1988 str. 11) -- to już wykonał krótki program w Pascalu na IBM AT -- tekst został "uformowany" pod kontrolą edytora WORDSTAR v.4.00 (na komputerze IBM AT) i wydrukowany na drukarce STAR NL 10.

Od redakcji: Ze względów poglądowych w tekście artykułu przyjęto konwencję zapisu liczb szesnastkowych (heksadecymalnych) jako ciągu znaków poprzedzonych znakiem #. W treści procedur zastosowany jest format zapisu żądany przez asembler, tzn. liczba heksadecymalna przedstawiana jest jako ciąg znaków zaczynający się od cyfry i kończący literą H. Pozostałe liczby są liczbami w systemie dziesiętnym.


Tekst artykułu wraz z wydrukami programów i tabelami został zeskanowany z oryginalnego egzemplarza gazety w dniu 2005-02-09, a następnie skorygowany, opatrzony komentarzami i dostosowany do HTML. Celem było udostępnienie w ośmiobitowym serwisie internetowym
8bit.yarek.pl. Zamieszczony w dobrej wierze, bez wiedzy i zgody autora, redakcji i wydawnictwa.
Jarek Adamski
Dnia 2005-10-22 komentarze zostały rozszerzone o wnioski wynikające z analizy schematów.