ZX-Net a Q-LAN

Jak tu síť pojmenovat?
Sinclairova konkurence to měla s názvem sítě jednoduché. Pro stroje od Acornu existoval EcoNet, ať už to byly Acorn System 2-5, Atom nebo Proton (BBC), a není divu, že se stejná síť objevila i pro Archimedes a RiscOS stroje (s výjimkou A3010 a Electronu, pro který ale existoval EcoNet interface od jiného výrobce).

Na Apple ][ to bylo složitější, protože pro něj existují i sítě jiných výrobců (jako Mastery development), ale na Apple II GS díky podpoře na zabudovaném sériáku a na ostatních modelech díky Workstation Card fungoval stejný AppleTalk (a sdílení souborů přes Apple Share) jako na Macintoshi (takže Macintosh mohl Applu ][ sloužit jako fileserver, dokonce bylo možné z něj na Apple IIe a Apple II GS bootovat).

U Sinclaira se síť vyskytuje na ZX Spectru, pod názvem ZX-Net.
Na Sinclairu QL, i když jde o stejnou síť (podobný hardware, stejně stavěný protokol), se jmenuje Q-LAN.

Kupodivu se tato síť nevyskytuje na ZX-81, i když by si o to tento malý počítač přímo říkal.
Nerealizovatelné to není, rutiny máme dostupné v disassemblované ZX Interface 1 ROM, a samotný hardware sítě je poměrně jednoduchý.
Jen to asi ještě nikomu nestálo za to (a je škoda, že ani Sinclairovi, ZX-81 v síti by mu usnadnily vstup do škol a získal by výhodu pro boj s Acornem o BBC).
Pravda, naroubovat obsluhu do ROM by asi bylo těžké a bylo by nutné použít ovladače v RAM.
ZX-81 totiž nemá kanály (rozlišuje jen výstup na obrazovku a na tiskárnu prostřednictlívm PRINT/LIST a LPRINT/LLIST), navíc je v ROM málo místa (víme přeci, že kvůli podpoře ZX Printeru musely být obětovány příkazy DRAW, UNDRAW, READ, DATA a RESTORE) a do třetice je nyní na ZX Printer příkazech mapována obsluha ZXPandu.

Teď už to ale uživatelé ZX-81 stejně řeší jinak – ZeddyNetem.
(Pokud jsem to pochopil dobře, zatím jsou jen dva funkční exempláře ZeddyNetu, ale už pro něj existují programy ipconfig, telnet, sendmail, IRC, network filemanager NFM a html browser ZeddyFox. A POP3 klient, který má ale problém s pomalostí zpracování mailových hlaviček a server ho proto odpojuje.)

Tak se vraťme k platformám, které ZX-Net/Q-LAN podporují.

Základ, který zvládá ZX Interface 1 a “holá” QL ROM je společný.
Rozvíjení šlo ale na obou strojích různými směry – Disciple obchází neexistenci multitaskingu tím, že monitoruje stav sítě pod přerušením, a zavádí i přístupová práva (rozlišuje učitelskou, asistentskou a žákovskou stanici). na QL přidává Toolkit sdílení souborů, samozřejmě jiné, než má Disciple, a jeho fileserver běží v multitaskingu jako samostatná úloha.

Teoreticky by tedy alespoň na základní úrovni (to, co umí ZX Interafce 1 na ZX Spectru a NETI_/NETO_ zařízení na QL) se měly umět oba stroje dorozumět.
Zkuste si to. Asi vám to nepůjde.

Tak nejprve LOAD a SAVE souboru.
Podle mých pokusů, když dá te SAVE Basicu z QL na ZX, ohlásí ZX “Wrong file type”.
Pokud dáte SAVE ze ZX na QL, neohlásí QL nic a stále čeká na příjem.
Je to jasné – Basic obou počítačů se liší.
Stejný důvod, proč na ZX nejde načíst Basic ze ZX-81 (odlišná klíčová slova a jiné příkazy, odlišné kódování znaků – Spectrum má ASCII, zatímco ZX-81 má vlastní nestandard přejatý ze ZX-80).

No tak na jednom stroji zkuste OPEN a PRINT#, na druhém OPEN a INPUT#.
OPEN #3,neto_1
LIST #3
CLOSE #3

Výsledek bude zase ten, že to spolu nebude komunikovat.
Proč ale?

Inu, ZX je malý osmibit. A QL, ať si kdo chce co chce říká, ať si kdejaký amigista slzy utírá, je prostě 68k stroj, příbuzný UNiXových mašin (i když jeho shell je “jen” Basic).
Takže ZX Spectrum jako konce řádků používá kód 13 (jako to ve světě 68k počítačů dělá jen klasický Mac OS X), zatímco QL používá “UNiXový” kód 10.

Jak to obejít?
Přijímat informace z QL pomocí INKEY$#, takto:
10 CLEAR #
20 CLEAR 24999
30 OPEN #4;”n”;1
40 LET m=25000
50 LET a$=INKEY$#4
55 IF a$=”” THEN GO TO 50
60 POKE m,CODE a$: LET m=m+1
70 GO TO 50
80 REM End of File
90 PRINT m-25000;” bytes received.”

Protože použití INKEY$# je pomalé, dobrá duše si to napsala částečně ve strojáku. Opišme si to od ní:
10 CLEAR #
20 CLEAR 24949
30 FOR i=24950 TO 24982
40 READ m
50 POKE i,m
60 NEXT i
70 RANDOMIZE USR 24950
80 LET m=PEEK 24983+256*PEEK 24984
90 PRINT m-25000;” bytes received.”
100 DATA 62, 1, 50, 214, 92, 207, 45, 33, 168
110 DATA 97, 34, 151, 97, 221, 34, 81, 92, 205
120 DATA 230, 21, 183, 40, 250, 42, 151
130 DATA 97, 119, 35, 34, 151, 97, 24, 240

Ve strojáku se skrývá toto:
ld a,1 : ld (23766),a : rst 8 : defb 45
ld hl,25000 : ld (24983),hl : ld (23633),ix
loop call 5606: or a : jr z,loop
ld hl,(24983) : ld (hl),a : inc hl : ld (24983),hl : jr loop

;… zde je proměnná 24983

Služba 45, volaná pomocí RST 8, je Open Network Channel v ROMce ZX Interface 1, ale o tom si ještě povíme později.
Na adrese 5605 v ZX ROM je rutina INPUT-AD.
Zbytek je v podstatě jasný.

Než si prozradíme dál, co se dá se sítí dělat, podíváme se, jak je zařízena.

Tedy, počítače se zapojují v řadě (krajní mají nezapojené konektory) pomocí kabelů s 3,5 mm jack konektory, kabely mohou být až 3 metry dlouhé.
Počítačů v síti je až 64, s adresou 1-64.
Adresa 0 je vyhrazena pro broadcasting (takže ve skutečnosti se používá 65 adres).
Rychlost přenosu je asi 100 kbit/s – jedničkový a nulový bit jsou různě dlouhé.
Data jsou rozdělena do paketů o délce až 255 bajtů (obvykle 255 byte, poslední paket v sérii může být kratší), každý paket má 8bytovou hlavičku, sériové čislo, a paket i hlavička mají kontrolní součet (uložený v hlavičce – samotný datový paket jsou čistá data). Poslední paket v sérii je oznašen příslušným příznakem.
Při přijetí hlavičky nebo paketu vrací příjemce potvrzení o přijetí (bajt 01), pokud odpověď do určité doby nepřijde, pošle se paket znovu. Při broadcastu se potvrzení neodesílá (kdo nezvládne broadcastové vysílání zachytit, má smůlu).
Délka paketu a počet paketů v sérii umožňuje zaslat v jedné sérii asi 16 megabajtů.

Hlavička vypadá takto:
byte 00, 1 bajt – cílová adresa (0-64)
byte 01, 2 bajt – zdrojová adresa (1-64)
byte 02, 2 bajty – číslo paketu (0-65535)
byte 04, 1 bajt – typ paketu (00 normální, 01 poslední)
byte 05, 1 bajt – délka dat 1-255
byte 06, 1 bajt – kontrolní součet dat
byte 07, 1 bajt – kontrolní součet hlavičky (bajty 00-06)
a následují data (až 255 bajtů).

Paket nebo hlavička začíná nulovým bitem a končí jedničkovým, jednotlivé bajty hlavičky nebo dat jsou uvozeny naopak jedničkovým bitem a ukončeny nulovým.
Scout je samostatný bajt, který je jako paket uvozen nylovým bitem a ukončen jedničkovým.

Vlastní průběh vysílání je tento:
Síť se po náhodně dlouhou dobu (2-3 milisekundy) testuje na inaktivitu.
Pokud je klid, pošle se desetibitový “scout” – nulový bit, bajt obsahující číslo stanice a jedničkový bit.
Pokud klid není, čeká se opět náhodnou dobu (jde o to, že pokud by počítače při kolizi na síti čekaly vždy stejnou dobu, tak by při dalším pokusu muselo zase ke kolizi dojít).

Pak se odešle hlavička – nulový bit jako začátek paketu, pak jednička před prvním bajtem, první bajt, nula na konec prvního bajtu, zase jednička, druhý bajt, nula, … a nakonec po nule na konci posledního bajtu následuje jedničkový bit uzavírající paket.
Pokud nejde o broadcast, počká se na potvrzovací bajt 01. Pokud nepřijde do timeoutu (podle některých údajů 1 milisekunda), otestuje se znovu aktivita sítě a pošle scout a hlavička.
Scout a hlavička trvají asi 1,6 milisekundy, opakování při nepotvrzení je po asi 8 milisekundách.

Před odesláním dat se opět otestuje aktivita sítě, pokud je volno, pošlou se data stejně, jako se posílala hlavička.
Opět se počká, pokud nejde o broadcast, na potvrzovací bajt 01.
Doba odeslání 255 bajtů dat je asi 37 milisekund.

Příjem je pak podobný:
Počkej na aktivitu na síti, chytni scout, počkej dobu v něm uvedenou.
Načti hlavičku (zkontroluj, zda je určena pro tebe), spočítej kontrolní součet a porovnej se součtem v hlavičce.
Pokud nejde o broadcast, a checksum souhlasí, vrať potvrzovací bajt (01).
Počkej na aktivitu na síti, počkej dobu uvedenou ve scoutu.
Přečti data, spočítej kontrolní součet a porovnej se součtem v hlavičce.
Pokud nejde o broadcast, a checksum souhlasí, vrať potvrzovací bajt (01).

Proč by nás vlastně měl protokol zajímat?

Jeho znalost je důležitá hlavně proto, že je pak možné realizovat tunelování Q-LAN/ZX-Netu přes ethernet/Internet.
Do sítě se dá zapojit 64 Specter nebo QLek – pokud jsou blízko sebe.
Co když ale jedno z nich je někde daleko?
Řešit problém tak, že budeme QL učit TCP/IP, sice jde, ale to vyžaduje jiný přístup, třeba iWiFi Nano modul na sériáku, jako je to na ZX Spectru u SIFu. A ten se samozřejmě obsluhuje softwarově jinak.

Naopak mít krabičku, která na jednom konci “čichá” lokální síť ZX-Net/Q-LAN, na druhém má ethernet, znamená jen naučit ji, jaký rozsah adres má ponechat na lokální síti a pakety pro které adresy má poslat na tu či onu IP adresu – kde čeká podobná krabička, která pakety přišlé z ethernetu pošle do ZX-Net/Q-LAN sítě.
Nemusí ani přenášeným datům rozumět, jen podle hlaviček směřuje pakety.
Se 64 takovými krabičkami a jejich vhodným nastavením pak nemusí být žádné dva počítače v jedné lokální síti, a přesto se všechny, byť rozptýlené po světě, domluví jako v jedné síti (pokud se stihnou z té dálky včas vracet potvrzovací bajty).

Využití na ZX Spectru, pokud se omezíme na ZX Interface 1, si žádá opustit bezpečné vody Basicu a vrhnout se do strojového kódu (kde třeba síťová hra může využívat podobné triky, jako Disciple, a nemusí, narozdíl od Basicu, při čekání na spojení zahálet).
Služby Interface 1 ROM se volají pomocí RST 8 a DEFB s číslem služby.
Pro síť jsou specifické tyto:
(Počítají s tím, že jsou vytvořeny nové systémové proměnné Interface 1, proto je dobré před jejich použitím zavolat službu 49, která systémové proměnné vytvoří. IY má ukazovat na 5C3A.)

27 – Console input – čeká na klávesu, kód načte do A.
28 – Console output – vytiskne na obrazovku (kanál “S”) znak v A.
29 – RS232 input – čeká na znak ze sériáku (8 data bitů, 1 stop bit, baudrate podle systémové proměnné BAUD), načte do A.
30 – RS232 output – vyšle znak v A na sériák (8 data bitů, 2 stop bity, bez parity, baudrate podle systémové proměnné BAUD).
31 – ZX Printer output – pošle znak v A na ZX Printer.
32 – Keyboard test – vrací SCF, pokud je v klávesnicovém bufferu kód znaku.
45 – Open Network channel – otevře kanál ke stanici, jejíž adresa je na D-STR1. Lokální adresa je uložena v NTSTAT. V kanálových informacích (CHANS) se vytvoří prostor pro síťový buffer, obsah paměti po STKEND se tím posune nahoru. Adresa kanálových informací je vrácena v IX.
46 – Close Network channel – uzavře sí’tový kanál, jehož adresa je v IX. Pokud buffer obsahuje data k odeslání (NCOBL>0), před uzavřením se odešlou.
47 – Get packet – IX obsahuje adresu kanálu, do kterého se paket ze sítě načte. NCNUMB obsahuje pořadové číslo požadovaného paketu. Pokud se ztratilo potvrzení posledního přijatého paketu, potvrdí oba (poslední přijatý i aktuální). Pokud nečeká na broadcast, vrací se s timeoutem po 12 milisekundách čekání, při přijetí paketu trvá dalších 37 milisekund. Při příjmu broadcastu (NCIRIS=0) se nevrací, dokud packet nepřijde. Vrací SCF při chybě (paket nepřišel nebo byl přijat s chybou). Hlavička a data se ukládají od NCIRIS a NCB, NCNUMB se  při přijetí inkrementuje.
48 – Send packet – IX obsahuje adresu síťového kanálu, do kterého pošle paket. Počet bajtů v buffferu k odeslání určuje proměnná NCOBL (musí být větší než 0). Obsah A se uloží do NCTYPE. NCNUMB se inkrementuje a rutina se nevrací, dokud paket neodešle a (pokud nejde o broadcast, kdy NTDEST=0) nepřijme i potvrzení.
49 – Create system variables – vytváří systémové proměnné:
23734 FLAGS3 – flagy
23735 VECTOR – adresa pro rozšíření Basicu o nové příkazy
23737 SBRT – stránkovací rutina ROM
23747 BAUD – baudrate, BAUD=(3500000/(26 x baudrate))-2
23749 NTSTAT – vlastní síťová adresa 1-64
23750 IOBORD – barva borderu použitá během I/O operací.
23751 SER_FL – dvojbajtový prostor pro RS232
23753 SECTOR – dvojbajtový prostor pro Microdrive
23755 CHADD_ – dočasné úložiště CH_ADD
23757 NTRESP – úložiště pro odpověď ze sítě
23758 NTDEST – začátek síťového bufferu, obsahuje číslo cílové stanice 0-64
23759 NTSRCE – číslo zdrojové stanice 1-64
23760 NTNUMB – číslo síťového paketu 0-65535
23762 NTTYPE – kód typu v hlavičce
23763 NTLEN – délka dat v paketu 0-255
23764 NTDCS – kontrolní součet datového bloku
23765 NTHCS – kontrolní součet hlavičky
23766 D_STR1 – začátek 8 bajtů identifikace souboru, dva bajty číslo mechaniky (1-8)
23768 S_STR1 – pokračování identifikace souboru, číslo kanálu 1-15
23769 L_STR1 – typ zařízení (‘M’, ‘N’, ‘T’, ‘B’, …)
23770 N_STR1 – délka jména souboru
23772 – začátek jména souboru
23774 D_STR2 – druhých 8 bajtů identifikace souboru pro použití s MOVE a LOAD
23782 HD_00 – začátek prostoru pro SAVE, LOAD, VERIFY and MERGE. Kód typu dat
23783 HD_0B – délka dat 0-65535
23785 HD_0D – počáteční adresa dat 0-65535
23787 HD_0F – délka programu 0-65535
23789 H_11 – číslo řádku
23791 COPIES – počet kopií pro příští SAVE
23792 začátek kanálových informací a souborových map pro microdrive

Informace pro síťový kanál (při volání kanálových služeb je adresa ukazována registerm IX) jsou:

0 – adresa 8
2 – adresa 9
4 – ‘N’
5 – adresa výstupní rutiny v ROM
7 – adresa vstupní rutiny v ROM
9 – adresa 276
11 NCIRIS – číslo cílové stanice
12 NCSELF – vlastní adresa
13 NCNUMB – číslo paketu
15 NCTYPE – typ paketu (0 data, 1 poslední blok)
16 NCOBL – počet bajtů v datovém paketu
17 NCDCS – kontrolní součet dat
18 NCHCS – kontrolní součet hlavičky
19 NCBL – poloha posledního bajtu načteného z bufferu
20 NCB – počet bajtů ve vstupním bufferu
21 – 255 bajtů pro data

Jak software ZX Interface 1, tak základní služby zařízení NETI_/NETO_ na Sinclairu QL nepoužívají jméno souboru.
Jak rozšíření Disciple ROM, tak Toolkit a sdíleníá souborů na Sinclairu QL už názvy souborů používají – ovšem každý po svém.

Defaultně (bez použití příkazu NET) má QL přiřazenu adresu 1.
Pokud ale příkaz NET proběhl, jak zjistit, jakou má QL adresu?
Na ZX Interafce 1 to šlo zjistit pomocí PEEK ze systímové proměnné, o které je známo, kde leží.
Na QL je to složitější – u ROM od nejstarší po MG jsou systémové proměnné uložené ve druhé VRAM, na fixním místě, ale Minerva ROM a SMSQ tuto VRAM uvolňuje a navíc každá instance multitaskujícího Basicu má svoje vlastní proměnné, takže ta hledaná pak může ležet kdekoliv (nebo naopak – adresu systémových proměnných zde lze zjistit snadno pomocí k tomu určené funkce, která ale ve starších ROMkách neexistuje).
Asi takto:

9000 DEFine FuNction My_Station_Number
9010   LOCal v$
9020   v$ = VER$
9030   IF v$ = ‘JSL’ or v$ = ‘HBA1’ THEN
9040     REMark Minerva or SMSQ
9050     RETurn PEEK(VER$(-2)+55)
9060   ELSE
9070     REMark other systems
9080     RETurn PEEK(163895)
9090   END IF
9100 END Define My_Station_Number

Síťovou adresu pak zjistíme snadno – pomocí PRINT My_Station_Number.

Kromě broadcastingu (vysílání a příjem na adrese 0) funguje na QL i příjem z vlastní adresy – v takovém případě přijme QL vysílání z libovolné stanice, která na něj vysílá.
To řeší situaci, kdy znám svou síťovou adresu, vím, že mi někdo bude posílat soubor, ale nevím, kdo.
(Nemám ověřeno, zda to takhle funguje i na ZX Interface 1).

V okamžiku, kdy na QL používáme Toolkita FileServer (příkaz FSERVE), místo NETI_ a NETO_ nastupuje nové zařízení (většinou uvozující cestu k souboru) n<x>_, kde x je adresa stanice.
A s ním spousta legrace.

Kromě nahrávání, ukládání a kopírování souborů ze zařízení na jiném počítači (například lze poslat textový soubor uložený na disku jednoho stroje na paralelní tiskárnu připojenou k jinému, a to vše z třetího stroje pomocí povelu COPY n2_mdv1_soubor_txt TO n3_par).

Používat lze opravdu všechna zařízení, tedy i výstup na obrazovku nebo konzoli.
OPEN #3,n2_scr_448x200a32x16:LIST #3:CLOSE #3
Tento příkaz vylistuje váš program v Basicu do okna otevřeného na obrazovce počítače s adresou 2.
To už je základ posílání zpráv a odpovědí na ně.
OPEN #3,n3_con_448x200a32x16:INPUT #3,’Tady stanice 1, pujdes na obed? ‘;a$:PRINT a$:CLOSE #3

Ještě že jsme tím oknem nezaplácli celou obrazovku.
Ale kdyby ano?
OPEN #3,n2_con_512x256a0x0:PAPER #3:CLS #3:PAUSE:CLOSE #3
Ano, prázdná obrazovka nepotěší.

Lze se ale pomstít vytížením fileserveru (pokud je spuštěný, v tomto případě i spolu s RAMdikem – na něm totiž není vidět disková aktivita, zatímco na Microdrivu nebo floppy ano). Jde vlastně o DDoS útok na síti Q-LAN.
REPeat loop
  COPY RAM1_soubor_txt TO n1_RAM1_soubor_txt:DELETE n1_RAM1_soubor_txt
END REPeat loop

Dobrá zpráva je, že pokud nepustíte FSERVE (nebo pokud běžící fileserver killnete pomocí RJOB), nikdo se vám ani na QL, ani na žádné ze zařízení k němu připojených nedostane.

Ale pokračujme.
Vezměme si program, který posílá data na tiskárnu, a nabízí třeba volbu mezi PAR_, SER1_ a SER2_.
Jenže my chceme použít “síťovou tiskárnu” připojenou přes paralelní port ke stroji číslo 4.
Podobně, jako lze microdrivové operace přesměrovat na disketu nebo harddisk, lze i nastavit alias pro síťovou cestu. Takže si “síťovou tiskárnu” nastavíme jako SER1_ a kdykoliv se nějaký tisk pošle na SER1_, neobjeví se na fyzickém sériáku, ale pošle se na nadefinovanou síťovou cestu (paralelní port čtvrté stanice).
NFS_USE SER,n4_PAR

Existuje i program NETPAL, který umožňuje ovládat QL na dálku (například pokud máte více QL, ale jen jeden monitor). využívá ale vždy některý z toolkitů, které mají příkaz pro spuštění řetězcové proměnné jako Basicového příkazu (QUEUE% z DIY Toolkitu nebo TYPE-IN z Turbo Toolkitu).
Zápis do konzolového kanálu zdá se pro vkládání Basicových příkazů nelze použít, výstup do něj jde totiž na obrazovku.

No a poslední věc – když už by někdo měl více QLek, asi by je neovládal jen tak z klávesnice, ale chtěl by je nejspíš použít pro paralelní výpočty.
A to samé by mohl chtít i se ZX Spectrem.

Běžné programovací jazyky na to nejsou zařízeny a rozdělení práce mezi jednotlivé počítače, její distribuce a sběr výsledků jsou na programátorovi.
Ideální pro rozložení na více procesorů jsou déletrvající části úloh, ideálně iterace (cykly). Samozřejmě takové, které se dají rozdělit na několik částí – tedy, česky řečeno, místo, aby jeden počítač prováděl FOR A=1 to 10000, provedou čtyři počítače FOR A=1 TO 2500, FOR A=2501 TO 5000, FOR A=5001 TO 7000 a FOR A=7001 TO 10000. Což by teoreticky mělo trvat asi tak čtvrtinu času.
Podmínkou je, aby šel cyklus opravdu takto rozdělit na čtyři oddělené části – aby výsledek z jedné iterace (jednoho průchodu cyklu) nebyl potřeba jako vstup v další iteraci.

Nicméně soudím, že i kdyby někomu ležela doma čtyři Spectra se ZX Interface 1, asi na nich nebude počítat fraktály.
Zatímco sejdou-li se čtyři spectristi, a zapojí si síť, síťová hra by se jim hodila.
Ale i když jsem koukal na WOS, zda někde nenajdu “requires ZX Interface 1 network”, žádnou takovou hru tam neměli.