RS232 pro ZX Spectrum 128k a Didaktik Gama 192k
Hardware
SIF je skvělý sériový interface pro ZX Spectrum, ale je to hardware navíc a někdy může být výhodnější použít sériový port, který už je součástí ZX Spectra 128k od výroby. Není to zcela bez problémů a o tom je tento článek.
ZX Spectrum 128+ toastrack (podle žeber chladiče na pravém boku) a šedá ZX Spectrum 128k +2 od Amstradu byly první počítače s tímto sériovým portem. Přinejmenším první ZX Spectra. Jejich RS232 je připojená k I/O portu AY-3-8912 a je realizována převodníky úrovní MC1488 a MC1489, které převádějí úrovně RS232 na TTL a zpět. Každý integrovaný obvod má v sobě 4 převodníky. Dva z každého z nich jsou využité pro RS232/MIDI konektor a dva pro KEYPAD konektor.
Didaktik Gama 192k je modifikací Didaktiku Gama 80k od CSS a kromě větší paměti kompatibilní se ZX Spectrum 128k, obsahuje i AY-3-8912 a k ní připojený MAX232 podobně, jako převodníky v originál ZX Spectrech.
Všechny tyto sériové porty jsou softwarové. To znamená, že počítač neobsahuje žádný hardware, který by provedl serializaci vysílaného bytu z osmi bitů na jejich správnou časovou posloupnost. To musí udělat software změnou stavů I/O portů na AY-3-8912 ve správný okamžik. Podobně i opačným směrem při přijímání sekvence bitů musí číst stavy ve správný okamžik a z těchto stavů složit byte. To je samozřejmě náročné na procesor, který během toho nemůže dělat něco jiného, jako je tomu u hardwarové RS232, která si toto řeší sama a CPU předá už hotový byte. V lepším případě skupinu bytů uložených v paměti čipu jako je např. 16C650 v SIFu.
Další problém softwarové RS232 bývá nižší rychlostní limit. Při rychlosti 57600bps na jeden bit / baud vychází přibližně 61.5 taktů, při 115200 polovina, nejkratší instrukce Z80 trvají 4 takty, často 7 a více taktů, nezbytný out (c),a
dokonce 12 taktů, kromě přijetí jednoho bytu je nutné dělat i další věci, uložit byte do paměti, nebo celý blok dat na externí úložiště a mezitím pozastavit vysílání dalších bytů z druhého počítače.
A ano, vím o skvělé práci Pavla Vymetálka a pro testování zkompilovaných rutin na reálném hardwaru jsem ho používal, ale můj cíl je maličko jiný. Rád bych lépe zdokumentoval možnosti hardwaru a trochu i softwaru, aby mohl mnohem snadněji RS232 ve svých programech použít kdokoli další.
Co o sériovém portu říká manuál?
Manuál k ZX Spectrum 128k je dostupný online na WoSu, není toho v něm o RS232 mnoho (strany 3 a 14). Zdá se, že celý manuál je orientovaný hlavně na popis odlišností od ZX Spectrum 48k.
K DG192k manuál nikdy neexistoval a i kdyby, vše, co se týká BASICu by mělo být shodné.
Mám papírový manuál k šedé +2 od Amstradu, ten je už na první pohled přepracovaný a kompletní včetně základů programování v 48k BASICu, byť graficky zjednodušený, ale co se týče RS232 je podobně žalostně strohý.
Manuály v podstatě říkají, že existují nějaké sériové tiskárny, na které lze z BASICu tisknout přes RS232 a jejich seznam si máme vyžádat od výrobce. Viděl někdy někdo ten seznam? Taktéž kabel lze objednat od výrobce. Dále se v manuálech zmiňuje možnost připojit MIDI zařízení, přičemž uvedený pinout je krajně podezřelý, protože uvádí signál RETURN na vývodu 1, kde je podle pinoutu o pár řádků výše GND. Zatímco pinout RS232 dává smysl, pinout MIDI zařízení považuji za něco jako pinout na druhé straně kabelu, ne pinout konektoru v ZX Spectru, který je označený RS232/MIDI. Pinout konektoru KEYPAD není uveden vůbec. Přitom je to taky RS232, jen podle mého měření na reálném hardwaru má zpřeházené signály, kde je na RS232 vstup, tam je na KEYPAD výstup a naopak.
pinout RS232 podle manuálu k šedé +2
pin | RS232/MIDI |
---|---|
1 | GND |
2 | TxD |
3 | RxD |
4 | DTR |
5 | CTS |
6 | +12V |
Vývod číslo 1 (GND) je opravdu vpravo a 6 (+12V) vlevo u zářezu pro páčku, tj. číslování na náčrtku konektoru je správně a náčrtek je orientovaný stejně jako snímek konektorů na zadní straně počítače, navzdory tomu, že se na internetu dají najít číslování vývodů telefonního konektoru BT631 přesně opačná. Lze snadno ověřit multimetrem.
Napájecí napětí 12V je na konektor připojeno přes 100Ω, nejspíš proto, že měnič uvnitř ZX Spectra není schopen dodat moc velký proud a při přetížení hrozí zničení tranzistoru v měniči.
napětí naměřená na konektorech šedé +2
Toto měření mi říká, aniž bych hlouběji zkoumal schéma a připojení ke konvertorům RS232 úrovní, kde je vstup a kde výstup. Výstup je tam, kde je nějaké napětí v mezích úrovní RS232 (tj. -3 až -15V pro log. 1, +3V až +15V pro log. 0). Naopak, kde se napětí blíži nule, ale vývod není propojený s GND, tam lze očekávat vstup ve stavu vysoké impedance.
pin | signál RS232 | na RS232 | na KEYPAD |
---|---|---|---|
1 | GND | 0V/GND | 0V/GND |
2 | TxD | 0.1V | -11.6V |
3 | RxD | -11.6V | 0.1V |
4 | DTR | 0.1V | -11.6V |
5 | CTS | -11.6V | 0.1V |
6 | +12V | 11.8V | 11.8V |
Pohledem do schématu (viz výřez výše) to lze snadno ověřit. MC1488 je převodník z TTL na RS232 a jsou na něj připojené výstupy. MC1489 je převodník z RS232 na TTL a jsou na něj připojené vstupy. Jen pozor na zpřeházené signály ve schématu, je opravdu nutné koukat na čísla.
To také znamená, že signály jsou na konektoru RS232/MIDI pojmenované obráceně, TxD (transmit data) má být na počítači výstup (vysílač), nikoli vstup, RxD (receive data) má být vstup (přijímač) atd... ZX Spectrum se zdá být označené jako koncové zařízení, ne jako počítač.
Proč jsou vstupy a výstupy na RS232/MIDI opačně vůči konektoru KEYPAD netuším. Špatné je, že kabel/redukci pro RS232/MIDI nepůjde přímo/vždy použít i pro KEYPAD.
TODO: lze skrze KEYPAD jiným ZX Spectrem ovládat BASIC druhého ZX Spectra? Jaký baudrate používá KEYPAD? Jaký protokol?
Kde mám sehnat kompatibilní konektor?
Typ konektoru manuál nezmiňuje. Naštěstí mi v diskuzi poradil z00m a následně jsem dohledal, že Sinclair pro sériový port použil konektory BT631W podobné telefonním, které v Británii používá British Telecom s označením BT631A, rozdíly mezi variantami konektoru jsou popsané zde. Špatné je, že BT631W je obtížně sehnatelný a když už, tak jsou potřeba zvláštní krimpovací kleště, podobně jako na RJ45 a další u nás mnohem běžnější konektory. Proto je lepší kupovat konektory už nakrimpované na kabel, podaří-li se je sehnat.
Naštěstí se mnohem snadněji dají sehnat telefonní kabely s konektorem BT631A. Ten do ZX Spectra nejde osadit přímo, brání tomu tvar plastu, který nemá zářez, kde by měl být a naopak má zářez, kde být nemusí. Ale to není velký problém, při troše šikovnosti lze potřebnou drážku do plastu vyhlodat nožem. Jen si musíte dát pozor, aby vám prodejce prodal kabel s opravdu všemi šesti dráty. Pro připojení telefonu, nebo modemu jich je potřeba méně, typicky jen dva, nebo čtyři.
Správně upravený konektor by měl jít zasunout lehce až na doraz včetně zacvaknutí jistící páčky. Určitě se ho nesnažte natlačit dovnitř větší silou, rámeček konektoru v ZX Spectru by mohl prasknout. Naopak, konektor by měl sedět i po okrájení přesně a neviklat se. Ten tenký proužek zbylý mezi zářezy je důležitý pro správný přítlak kontaktů.
RS232 obecněji na jiných počítačích
Abych mohl správně zapojit 9 pinový DSUB konektor v Didaktiku Gama 192k (dále jen DG192k) a abych mohl správně vyrobit redukci pro ZX Spectrum 128k ke které půjdou připojit standardní propojovací kabely, mrknul jsem jednak na Wikipedii, a druhak jsem zkusil proměřit pár sériových portů stejným způsobem, jako jsem to udělal se ZX Spectrem.
Měření jsem provedl víc, ale všechna se stejnými výsledky. Liší se pouze úroveň napětí na výstupech. USB redukce a notebooky mívají napětí nižší než plných +-12V. Stejně tak někdy je a někdy není připojen plech konektoru na GND. Důležité je, že kde má být výstup je výstup a kde vstup je vstup.
DSUB-9 pin | signál | směr | naměřeno stolní PC | naměřeno UC-232A | naměřeno notebook Dell |
---|---|---|---|---|---|
1 | DCD | vstup | 0.15V | 0V | 0V |
2 | RxD | vstup | 0.15V | 0V | 0V |
3 | TxD | výstup | -11.9V | -6.3V | -5.6V |
4 | DTR | výstup | -11.9V | -6.3V | -5.6V |
5 | GND | vstup | 0.15V | 0V | 0V |
6 | DSR | vstup | 0.15V | 0V | 0V |
7 | RTS | výstup | -11.9V | -6.3V | -5.6V |
8 | CTS | vstup | 0.15V | 0V | 0V |
9 | RI | vstup | 0.15V | 0V | 0V |
Signály DCD, DTR, DSR a RI nezbývá než ignorovat. Tolik signálů na ZX Spectru ani v DG192k k dispozici není a pro přenos dat s hardwarovým řízením toku naštěstí stačí kromě RxD a TxD signály RTS a CTS.
TODO: prozkoumat a doplnit jaké signály chce RS232 myš. jestli nebude chybět DTR posunuté na RTS.
RS232 ze ZX Spectra 128k na DSUB-9
Po všem zkoumání jsem skončil s následujícím propojením. Nejdůležitějším kriteriem byla úvaha vstup na výstup a výstup na vstup při použití překříženého RS232 kabelu mezi dvěma počítači, nebo kabelu 1:1 mezi počítačem a koncovým zařízením, jako je např. tiskárna, plotr, nebo modem. A to, pokud možno tak, aby bylo možné hardwarové řízení toku dat softwarovým přepínáním stavu signálu RTS, nikoli posíláním celých bytů Xon, Xoff.
BT631 pin | RS232/MIDI dle manuálu | RS232 přeznačené | AY-3-8912 signál | směr | barva | DSUB-9 pin |
---|---|---|---|---|---|---|
1 | GND | GND | GND | GND | modrá | 5 |
2 | TxD | RxD | I/O A7 | vstup | žlutá | 2 |
3 | RxD | TxD | I/O A3 | výstup | zelená | 3 |
4 | DTR | CTS | I/O A6 | vstup | červená | 8 |
5 | CTS | RTS | I/O A2 | výstup | černá | 7 |
6 | +12V | +12V | - | +12V | bílá | - |
Barva vodičů samozřejmě může být různá, ale v mém případě to tak v hotovém kabelu, ze kterého jsem vyráběl redukci, s nalisovaným konektorem BT631 zrovna vyšlo. Později jsem zjistil, že konektor na druhém konci kabelu má vodiče přesně v opačném pořadí. Je to tak normální u Britských telefonních kabelů? Zapisuji jen pro případ, že bych vyráběl další.
RS232 z Didaktiku Gama 192k na DSUB-9
Nejlépe bude to kvůli kompatibilitě udělat po vzoru ZX Spectra 128k (resp. RS232 ROM), není ani důvod to zapojit jinak. DG192k má na desce dva 3 pinové konektory označené jako SER-IN a SER-OUT s GND uprostřed a dvěma signály CH1 a CH2 na každém z nich. KEYPAD neřeší. Převodník úrovní MAX232CPE (IC13 na schématu) v DG192k má stejnak jen 2 kanály z RS232 na TTL a 2 kanály naopak, stačí k zapojení jednoho ze sériových portů.
AY-3-8912 signál | AY-3-8912 pin | MAX232 TTL pin | MAX232 RS232 pin | DG192k header | barva (foto) | směr | RS232 signál | DSUB-9 pin |
---|---|---|---|---|---|---|---|---|
I/O A7 | 7 | 9 | 8 | CH2 in | hnědá | vstup | RxD | 2 |
I/O A3 | 11 | 10 | 7 | CH2 out | hnědočerná | výstup | TxD | 3 |
GND | 6 | 15 | 15 | GND | bíločerná | GND | GND | 5 |
I/O A2 | 12 | 11 | 14 | CH1 out | bílooranžová | výstup | RTS | 7 |
I/O A6 | 8 | 12 | 13 | CH1 in | světle zelená | vstup | CTS | 8 |
A k nudné tabulce ještě přidávám snímky skutečné realizace včetně schéma související části zapojení v DG192k.
Umístění DSUB konektoru na pravém boku se mi zdá takto nejpraktičtější. Nejenom proto, že už jsem tam z doby před přestavbou na DG192k konektor přibastlený měl, jen ne pro sériový port, ale pro Kempston Joystick. Především proto, že tam je blízko konektorům na základní desce počítače a ničemu nepřekáží, jako by překážel na zadní stěně.
Podezříval jsem zapojení v Didaktiku Gama, že je špatně a sériový port se nechová stejně, jako v ZX Spectru 128k+/+2. To se později potvrdilo. Protože mi fungoval správně výstup, fungoval správně vstup, ale pouze pokud byla AY-3-8912 jako vstup nakonfigurovaná, nefungovalo čtení v režimu výstupu s hodnotou 255 na portu.
Fungovalo:
10 OUT 65533,7: OUT 49149,191 20 OUT 65533,14: OUT 49149,255 30 PRINT IN 65533
Nefungovalo a mělo fungovat, protože tohoto způsobu se na originál ZX Spectrum 128k využívá.
10 OUT 65533,7: OUT 49149,255 20 OUT 65533,14: OUT 49149,255 30 PRINT IN 65533
Vyřešil jsem to nakonec tak, že jsem rezistory R84 a R85 přemostil paralelně připojeným 68R a tím zmenšil odpor mezi AY a MAXem na cca 63 až 64Ω.
V tuto chvíli netuším, jestli to nebude mít na některý z čipů negativní efekt, ale pomohlo to. Alespoň prozatím.
RS232 ze ZX Spectrum 48k s AY-3-8912
Kromě ZX Spectrum 128k a Didaktiku Gama 192k jsem chtěl připojit i ZX Spectrum 48k. V diskuzi jsme spekulovali o tom, že to je triviální, ale dosud jsem od nikoho neslyšel, že mu to funguje, takže proto jsem to vyzkoušel sám.
K vyzkoušení jsem zvolil ZX Spectrum 48k+ o kterém vím, že funguje spolehlivě, s DivIDE a nainstalovaným ESXDOSem 0.8.7 s utilitou sercp. Měl jsem připojený i Jiiirův RTC, Jiiirův UPI a Jiiirův SimpleAY s AY-3-8912 v roztrojce. Na SimpleAY jsem připájel kolíčky nalámané z lišty kromě signálů i na +5V a GND, protože konvertor úrovní potřebuje napájení a vše propojil. Signály z AY jsem připojil přes rezistory 200Ω a signály do AY přes 12Ω. Ty hodnoty nemají žádný zvláštní význam, kromě toho, že jsem je měl při ruce a nechtěl jsem propojovat RS232 redukci s AY natvrdo pro případ, že by něco šlo do zkratu. Směrem z AY pravděpodobně postačí propojení vodičem bez přidaného odporu.
Propojení signálů bylo stejné jako je v tabulkách výše, ale pro snazší orientaci přidávám propojení odpovídající následujícím snímkům.
AY-3-8912 signál | AY-3-8912 pin | barva (foto) | směr | RS232 signál | konvertor WaveShare |
---|---|---|---|---|---|
I/O A7 | 7 | oranžová + bílá | vstup | RxD | 3 |
I/O A6 | 8 | žlutá + bílá | vstup | CTS | 5 |
I/O A3 | 11 | šedá + šedá | výstup | TxD | 4 |
I/O A2 | 12 | bílá + šedá | výstup | RTS | 6 |
GND | - | černá | - | GND | 2 |
+5V | - | červená | - | Vcc | 1 |
Fungovalo to na první zapojení. Přenesl jsem pomocí sercp pár souborů tam i zpět.
V tomto případě jsem nepoužil překřížený propojovací kabel, ale prostý prodlužovák. Tj. v podstatě jsem konvertor úrovní zapojoval jakoby rovnou do portu na zadní stěně PC. Na prostředním snímku to je vidět. Může se lišit podle použitého konvertoru. Mnou použitý konvertor má na sobě z druhé strany značku WaveShare a čip s těžko čitelným označením, pravděpodobně SP3232E.
V tuto chvíli netuším, jestli to nebude mít na některý z čipů negativní efekt, ale fungovalo to.
Vlastní konvertor RS232 - TTL
Protože konvertor úrovní koupený z eBay mi nevyhovoval a protože jsem měl v šuplíku nějaké MAX232, vyrobil jsem si vlastní, lépe přizpůsobené použití se ZX Spectrem a AY-3-8912. Asi nemá smysl se o něm rozepisovat detailněji. Ke stažení je tady (nebo v sekci download na konci stránky), balíček obsahuje kompletní projekt pro Eagle 6.4 včetně gerberů, náhledy v PNG a ve schématu detailní popis.
Kabel k propojení počítačů
Aby mohly dva počítače spolu komunikovat je nutné propojit přinejmenším signály TxD a RxD (výstup jednoho na vstup druhého) a samozřejmě GND, ke kterému se napětí signálu vztahuje. Totéž v opačném směru. Tím zajistíme, aby druhý počítač mohl i odpovídat. Ale to není vše. Počítač, který není dost rychlý, aby vždy spolehlivě dokázal přijímat data, kdykoli vysílající strana nějaká pošle, musí vysílajícímu nějak signalizovat připravenost přijímat. K tomu slouží signály RTS (ready to send) a CTS (clear to send). Wikipedia říká Logickou jedničkou na tomto vstupu protistrana signalizuje, že DTE může vysílat data
. Tzn. nastavíme-li RTS do logické 0, tak by druhá strana, která čte stav na CTS neměla začít vysílat. Tím lze pozastavovat datový tok v případě, že přijímač potřebuje na zpracování přijatých dat delší čas, než trvání jednoho, nebo dvou stop bitů. Což může být v případě ZX Spectra velmi často.
Poznámka: Citovaná věta z Wikipedia o logické jedničce možná není pravdivá, jak se ukázalo měřením pomocí osciloskopu a logického analyzátoru dále. Možná byla myšlena pro trochu jinou situaci, než jak ji chápu já? Nebo došlo jen k záměně logického stavu?
Z toho vyplývá, že pro propojení dvou rovnocenných počítačů (DTE-DTE) je nutné použít křížený kabel (nullmodem) a pro propojení počítače a periferie (DTE-DCE) kabel propojení přimo 1:1 (jsou-li na obou koncích DSUB-9 konektory tak 2 na 2, 3 na 3 atd...).
Pro propojení dvou ZX Specter a pravděpodobně i většiny PC postačí zjednodušená varianta. U plně zapojeného nullmodem kabelu prostě některé vodiče nebudou využité.
DSUB-9 pin | signál | signál | DSUB-9 pin |
---|---|---|---|
2 | RxD | TxD | 3 |
3 | TxD | RxD | 2 |
5 | GND | GND | 5 |
7 | RTS | CTS | 8 |
8 | CTS | RTS | 7 |
A na rozdíl od konektoru na redukci, na zadním panelu, nebo na šasi počítače se tentokrát jedná o konektory se zdířkami. Viz foto, po zvětšení je vidět i číslování vývodů v opačném pořadí, aby čísla odpovídala stejným číslům na protikusu.
Další možnosti připojení
Na sériových portech je hezké, že není nutné se omezovat pouze na připojení ZX Spectra ke starým počítačům s COM/RS232 porty od výroby, ale existuje přehršel nejrůznějších redukcí RS232-USB, USB-USART s TTL výstupy, nebo RS232 na TTL. Viz následující obrázky. Nebývají drahé. Jen pozor na to, že mnohé z těch velmi levných nepočítají se signály CTS a RTS, mají pouze RxD a TxD. Týká se to i některých kabelových "redukcí", které lze koupit v kamenných, resp. serióznějších tuzemských obchodech, nejenom na Fleabay.
Redukce z USB na USART s TTL úrovněmi, nebo redukce z RS232 úrovní na TTL by mělo být možné přímo připojit k AY-3-8912 a nahradit jimi konvertory úrovní MC1488/MC1489, nebo MAX232. Což může usnadnit použití i jiných interfaců s AY-3-8912. Jen opatrně na 3.3V redukce, jestli jsou 5V tolerantní. V každém případě, díky USB redukcím můžeme k ZX Spectru připojovat i notebooky, které by jinými způsoby byly připojitelné jen obtížně.
USB-RS232 redukce, které mi fungovaly
Malý USB dongle bez pouzdra s pinovou lištou a TTL výstupy, koupeno z Číny na eBay. Piny pro RTS a CTS bylo nutné dopájet.
ID 10c4:ea60 Cygnal Integrated Products, Inc. CP2102/CP2109 UART Bridge Controller [CP210x family]
USB kablík s DSUB 9 na straně RS232, pěkné robustní provedené, koupil jsem v tuzemském obchodě s výpočetní technikou pod označením Aten UC-232A (K-UC-232A).
ID 0557:2008 ATEN International Co., Ltd UC-232A Serial Port [pl2303]
USB kablík s DSUB 9 na straně RS232 koupený kdysi dávno v tuzemském obchodě, původní označení si nepamatuji a dohledat už nelze. Na šedém plastovém pouzdře konektoru je cedulka s modelem U232-P9.
ID 067b:2303 Prolific Technology, Inc. PL2303 Serial Port
USB-RS232 redukce, které mi nefungovaly
USB kablík s DSUB 9 na straně RS232 prodávaný spolu s 9 - 25 pin redukcí v průhledném modro azurovém provedení. Nepodařilo se mi u ní zprovoznit hardwarové pozastavování toku dat a to ani se ZX Spectrum ani s podomácku vyrobeným plotrem, kde přenos řídil AT Mega 32. Koupeno v tuzemském obchodě pod označením i-tec USB to serial adapter RS232.
ID 1a86:7523 QinHeng Electronics HL-340 USB-Serial adapter
Software a první pokusy s AY-3-8912
Máte-li zapojené kabely, redukce, konektory a připravené jiné nezbytnosti, je čas prozkoumat sériový port z pohledu softwaru. Přesněji, osahat si softwarem hardware a zjistit, jak se to vlastně chová než začneme tvořit něco komplikovanějšího na základě mylných předpokladů.
AY-3-8912 má kromě tří zvukových kanálů i jeden osmibitový port, tj. 8 vstupně výstupních linek, které lze číst a zapisovat na ně. Bohužel, pro potřeby RS232 jsou buď všechny nastavené jako vstup, nebo všechny jako výstup. Ale s RS232 potřebujeme vždy jeden vstupní bit a jeden výstupní bit zároveň. Například RxD jako vstup a RTS jako výstup, nebo naopak TxD jako výstup a CTS jako vstup. To s AY-3-8912, alespoň dle datasheetu, nelze realizovat jinak, než přepínat směry tam a zpět podle toho v jaké části časové posloupnosti sériové komunikace se právě nacházíme.
Datasheet k AY o práci s I/O porty říká (velmi volný překlad): Pro výstup dat z CPU do periferie připojené k I/O portu AY jsou potřeba následující kroky. Zvolit registr 7 a povolit výstup nastavením bitu 6 registru 7 do log. 1. Zvolit registr 14. Zapsat data do registru 14.
Přičemž datasheet z nějakého důvodu čísluje registry jinak. Já si dovolil použít hodnotu stejnou, jakou zapisujeme do portu, kterým se registr vybírá na ZX Spectru.
Analogicky k čtení: Je potřeba zvolit registr 7 a nastavit bit 6 registru 7 do log. 0. Zvolit registr 14. Přečíst z registru 14 data, tj. stav bitů I/O portu.
Datasheet dále poznamenává, že bity I/O portu ve výstupním režimu zůstávají v takovém stavu, jaká data na ně byla zapsána, dokud nejsou zapsána jiná data. A naopak, čtením I/O portu se přečtou taková data, jaké logické stavy byly na vstupech v okamžiku čtení i když se stavy mohou mezitím měnit. Nic neobvyklého, takto se chovají i jiné porty.
Při čtení I/O portu AY je všech 8 vstupů vybaveno pullup rezistory (hodnotu datasheet neuvádí), proto AY, není-li k ní něco připojeno, vrací na všech vstupech I/O portu log. 1 (255). Vstupy lze díky tomu snadno otestovat připojením na GND a příslušný bit by se měl změnit na log. 0.
Ale nebude nakonec problém s přepínáním režimu I/O portu jako vstup a výstup?
Ovládání AY-3-8912 z BASICu
AY-3-8912 v ZX Spectrech se ovládá na dvou portech, jeden slouží k výběru registru, nebo čtení obsahu portu a nachází se na adrese 65533, druhý slouží k zápisu do registru vč. I/O portu a nachází se na adrese 49149. Z následující tabulky je zřejmé, proč nelze AY adresovat pouze osmibitově. Nižší byte obou adres je shodný a není možné tak rozlišit do kterého z portů mají být data zapsána.
Pro své experimenty jsem si použil šedé ZX Spectrum 128k +2, protože jeho zapojení je přesně shodné se ZX Spectrum 128k+ toastrack a to lze považovat ve světě Sinclairů za defakto standard.
význam portu | dec | hex | bin |
---|---|---|---|
volba registru, nebo čtení registru | 65533 | 0xFFFD | 0b1111 1111 1111 1101 |
zápis obsahu registru | 49149 | 0xBFFD | 0b1011 1111 1111 1101 |
Na oba porty lze samozřejmě zapisovat i v BASICu. Pro nejjednodušší pokusy není nutné používat strojový kód. Viz příklad čtení I/O portu - všimněte si, že to je takřka doslovně do BASICu přepsaný postup z datasheetu.
5 REM read AY port 10 OUT 65533,7: OUT49149,191 20 OUT 65533,14 30 PRINT AT 0,0; IN 65533;" ": GO TO 30
Program na reálném ZX Spectru vrací 255, pokud není nic na RS232 připojeno. Pokud je, mohou být bity 7 a 6 v logické 0, podle toho, jaké napětí je přivedeno na vstupy RS232. Ve Fuse emulátoru verze 1.5.7 kdo ví proč program vrací trvale 191. Ani v jednom případě přitom nezáleží, jestli po volbě registru 14 na port 49149 zapíšu 0, nebo 255, nebo nic.
Zajímavé je, že když propojím ZX Spectrum s PC a spustím čtení portu v BASICu, tak bit 6 v log. 0 signalizuje připravenost PC na příjem dat. Nedělám-li na PC nic a nečtu port, pak BASIC zobrazuje 255, ale pokud na PC spustím cat /dev/ttyS0 (analogicky cat /dev/ttyUSB0 pro USB sériák), tak se zobrazí 191, tzn. změní se stav signálu CTS (RTS na straně PC). Viz relevantní výcuc z výše uvedených tabulek.
AY-3-8912 | směr | RS232 | DSUB-9 pin |
---|---|---|---|
I/O A7 | vstup | RxD | 2 |
I/O A6 | vstup | CTS | 8 |
Zkusme pro změnu na AY-3-8912 zapisovat. Ale protože stav nelze na PC číst tak snadno jako v BASICu, budu tentokrát měřit napětí výstupních signálů multimetrem. Měřit budu na vývodech 3 a 7 DSUB konektoru s kolíky. A opět, pro zjednodušení viz výcuc z výše uvedených tabulek.
AY-3-8912 | směr | RS232 | DSUB-9 pin |
---|---|---|---|
I/O A3 | výstup | TxD | 3 |
I/O A2 | výstup | RTS | 7 |
V následujícím příkladu se nastaví I/O port do výstupního režimu a pak se cyklicky přepínají stavy s intervalem asi 4s, tak akorát aby i digitální multimetr stihl zobrazovat napětí na příslušném výstupu.
10 REM write to AY port 20 LET d=200 30 OUT 65533,7: OUT 49149,255 40 BORDER 6: OUT 65533,14: OUT 49149,4+8: PAUSE 200 50 BORDER 7: OUT 65533,14: OUT 49149,0: PAUSE 200 60 GOTO 40
Po spuštění programu se bude pomalu střídat žlutý a bílý okraj obrazovky. Po dobu žlutého okraje byste měli naměřit na obou výstupech záporné napětí, pravděpodobně něco okolo -10V, nebo -11.5V, tzn. log 1 v úrovních RS232. Po dobu bílého okraje byste měli naměřit stejně velké napětí, ale kladné, tzn. v úrovních RS232 logickou 0.
Můžete samozřejmě zkusit zapisovat log. 1 jen na jeden z bitů (místo 4+8 jen 4, nebo 8) a tím si ověřit, že máte port, nebo redukci správně zapojené.
RS232 ve 128k BASICu
Udělejme malou odbočku. Hádáte-li, že BASIC pro ZX Spectrum 128k umí s RS232 pracovat, hádáte správně. Bohužel v manuálu není skoro nic. Vzpomínám, že jsem kdysi dávno měl propojená dvě ZX Spectra prostým překřížením signálů TxD s RxD a DTR s CTS dle pinoutu konektoru BT631W z manuálu. V manuálu se taky lze dočíst, jak nastavit baudrate příkazem FORMAT.
FORMAT "p"; baudrate
Možné hodnoty baudrate v manuálu uvedeny nejsou, pouze je tam zmínka, že 9600bps je rychlost výchozí, 19200bps je rychlost maximální. Prý s RS232 fungují příkazy LPRINT, LLIST a COPY, jsou tam dvě jednoduché ukázky jak poslat na tiskárnu pár znaků a to je vše. Nezbývá, než zkoumat vlastními silami.
10 PRINT "This program..."' 20 LLIST 30 LPRINT '"...prints out the character set, ie..."' 40 FOR n=32 to 255 50 LPRINT CHR$ n; 60 NEXT n
Domnívám se, že by měl fungovat i příkaz INPUT #3 pro příjem bytů z druhého ZX Spectra. Nutno ověřit. Je to už dávno, co jsem to zkoušel. Naštěstí se po internetu potuluje disassemblovaný 128k BASIC, kam lze nakouknout a s trochou úsilí pochopit, co to vlastně umí.
A kromě toho existuje i RS232 ROM, což je upravený BASIC 48k, rozšířený o možnost přenášet data přikazy LOAD a SAVE kromě magnetofonu přes EAR/MIC i přes oba RS232 porty v ZX 128k (i přes KEYPAD) a dokonce i přes sériák v ZX Interface 1 o kterém ale mé povídání není.
Jednoduchý pokus s AY ve strojovém kódu
Následující příklad je záměrně napsaný s ohledem na srozumitelnost. Vím, že by se dal zmenšit, ale ztratil by na přehlednosti. Zkuste nejprve nastavit sériový port na PC (s Linuxem) příkazem stty (viz manuál). Nastaví pouhých 300bps, ale nastaví hardwarové řízení toku dat signály RTS a CTS.
Zařízení /dev/ttyS0 označuje skutečný sériový port, třeba na základní desce, nebo na přídavné PCI/PCIe kartě. USB převodníky budou obvykle /dev/ttyUSB0. Případně místo nuly bude vyšší číslo, máte-li převodníků víc. Nahraďte si v příkladech sami.
stty -F /dev/ttyS0 300 cs8 clocal cread cstopb parenb parodd crtscts raw
Pak můžete zkompilovat a spustit následující kód. Je psaný v Prometheovi na ZX Spectru (a vyzkoušený v kompileru AS na Linuxu) a po spuštění by měl zobrazit dva červené čtverce v levém horním rohu obrazovky. Levý signalizuje změnou barvy na žlutou stav bitu 7 a pravý stav bitu 6 na I/O portu AY.
Stejný kód se zvýrazněním syntaxe zde.
cpu z80undoc org 50000 ; pseudoinstrukce - přelož a ulož na 50000 ; musí běžet v rychlé RAM START di ; nesmí být zpomalováno a přerušováno call READAY ei ; před návratem do BASICu přerušení povol ret READAY ld bc,65533 ; zvol registr 7 v AY ld a,7 out (c),a ld bc,49149 ; vypni zvuk a nastav I/O port jako výstup (bit 6 v log. 1) ld a,255 out (c),a ld bc,65533 ; zvol registr 14 v AY ld a,14 out (c),a ld bc,49149 ; nastav na I/O portu bit 2 do log. 0 0b11111011 ld a,251 ; tím se signalizuje na RTS připravenost přijímat data out (c),a ld b,10 ; počkej 10 cyklů WAIT djnz WAIT ld bc,65533 ; zvol registr 7 v AY ld a,7 out (c),a ld bc,49149 ; nastav I/O port jako vstup (bit 6 v log 0) ld a,191 ; 0b10111111 out (c),a ; touto instrukcí se RTS pulz ukoncuje ld bc,65533 ; zvol registr 14 v AY ld a,14 out (c),a ld hl,16384+6144 ; adresa prvního barvového atributu in a,(c) ld d,a bit 7,d ld a,6*8 ; žlutý PAPER pro 0 v bitu 7 jr z,READAY_1 ld a,2*8 ; červený PAPER pro 1 v bitu 7 READAY_1 ld (hl),a inc hl inc hl bit 6,d ld a,6*8 ; žlutý PAPER pro 0 v bitu 6 jr z,READAY_2 ld a,2*8 ; červený PAPER pro 1 v bitu 6 READAY_2 ld (hl),a inc hl inc hl call 8020 ret nc ; nc pokud byl stisknutý BREAK jr READAY
Teď zkuste pozorovat, co se stane, když na PC zkusíte data číst příkazem cat.
cat /dev/ttyS0
Viz vlevo ukázka kódu přes kompilací a spuštěním v Prometheovi a vpravo běžící program.
Změnil pravý čtverec barvu z červené na žlutou? Tak to je správně. Tím PC signalizuje ZX Spectru, že je připraveno přijímat data. Podobně naopak, ZX Spectrum signalizuje PC krátkým pulzem, že je připraveno přijímat data při každém průchodu cyklem, dokud není stisknutý BREAK. Pošlete-li data jedním z následujících příkazů, tak by se levý čtverec měl rozblikat podle přenášených bitů, zatímco po dobu přenosu pravý svítí žlutě. Proto je taky důležité, aby přenosová rychlost byla v tuto chvíli velmi nízká a tedy aby blikání bylo pozorovatelné.
cat textovy_soubor.txt > /dev/ttyS0 echo "nejaky zkusebni text" > /dev/ttyS0
Pokud zůstanou nějaká data v bufferu na PC a zkusíte příkazem stty změnit parametry portu, příkaz stty bude čekat, dokud se buffer nevyprázdní. Pak se výše uvedený příklad může taky hodit. Pomůže zahodit připravená data.
Vypozoroval jsem, že množství krátkých pulzů signalizujících připravenost přijímat data může pro PC fungovat jako by RTS bylo ve stavu připravenosti trvale. Aby bylo vidět např. logickým analyzátorem, že se tok dat opravdu pozastavuje, resp. PC reaguje vysláním jednoho bytu a pak počká na další pulz, měl by pulz RTS trvat přiměřeně dlouho a měly by mezi nimi být mezery v závislosti na nastaveném baudrate. Ale k tomu se dostanu později.
Trocha teorie o sériovém přenosu a časování
Rychlost sériového portu se udává v baudech, nikoli v bitech za sekundu. Jednotka se jmenuje podle pana Baudota, který stál u zrodu datových přenosů po telefonních linkách, resp. kódování abecedy pomocí Baudotova kódu ještě před vznikem dálnopisů (anglicky teletype, proto máme dodnes v *nixech TTY). Z Baudotova kódování se později vyvinulo ASCII a pulzy sériového přenosu zůstaly po panu Baudotovi pojmenované.
Proč baudy a ne bity? Protože přenos každého bytu je složený kromě přenášených datových bitů i ze start bitu a nejméně jednoho stop bitu. Každý z pulzů trvá stejný čas a počet těchto pulzů za sekundu se nazývá baudrate. Kdyby udávaná rychlost byla odvozená od přeneseného objemu dat měnila by se s počtem bitů, kterých může být klidně jen 5, nebo 7, měnila by se s počtem stop bitů, které mohou být 2, nebo dokonce 1.5 či mnohem víc, přijímací strana může počkat, protože stop bity považuje za klidový stav linky. A nakonec paritní bit, který může a nemusí být přenášen. Všechno závisí na tom, jak si to uživatel zvolí a na propojených strojích nastaví. Jediná podmínka je, že to musí být na obou stranách nastavené stejně. Počítače si většinou přenosovou rychlost neumí domluvit a když ano, tak to je s nejistotou a možnou ztrátou části přenášených znaků.
Viz obrázek záznamu logickým analyzátorem. Jsou na něm vidět 4 přenášené byty, v hexu 0xaa, 0x55, 0x00 a 0xff. Každý přenos začíná start bitem, kdy se linka na dobu jednoho baudu přepne z klidového stavu do opačné logické úrovně, tj. z log. 1 do log. 0. Podle start bitu přijímací strana pozná, kdy má začít číst bity a čte je v pravidelných časových intervalech,
Použitý logický analyzátor umí sériový přenos dekódovat a dekódovaný byte vypisuje v modrém rámečku nad záznamem, bity kódující informaci vyznačuje puntíkem. Díky tomu můžeme např. na první pohled vidět, že po každém bytu následují dva stop bity. Což sice zpomaluje přenos informace, ale dává přijímacímu stroji maličko víc času na zpracování přijatého bytu.
Díky puntíkům taky můžeme vidět, že se jako první přenáší LSB - nejméně významný bit 0 a jako poslední MSB - nejvýznamnější bit 7. První byte je binárně 10101010 (0xaa, dekadicky 170) a přitom při sériovém přenosu začíná log. 0. Analogicky u ostatních bytů.
Budete-li místo logickým analyzátorem měřit osciloskopen na straně RS232, ne TTL, pak viz druhý obrázek. Světle červeným pozadím je vyznačená přibližná úroveň napětí odpovídající log. 1, podobně jako na logických sondách, zeleně log. 0. Svislou červenou čarou optimální okamžik čtení hodnoty (s výjimkou start bitu, tam lze očekávat reakci na hranu signálu).
Přenosové rychlosti vs ZX Spectrum
Rychlost sériového přenosu může být takřka libovolná, zvládne-li ji hardware, ale za tu dlouhou dobu, co se sériový přenos používá, se hodnoty rychlostí ustálily na malé množině nejčastějších a dnešní zařízení, nebo software leckdy ani neumožní nastavit jiné. Např. příkaz stty dovolí nastavit tyto rychlosti - viz odkaz. I následující tabulka obsahuje několik běžně používaných rychlostí. Později se budu zabývat jen některými z nich.
baudrate | μs/baud | taktů ZXS 48k | taktů ZXS 128k |
---|---|---|---|
75 | 13333.333 | 46666.66 | 47292.00 |
300 | 3333.333 | 11666.67 | 11823.00 |
1200 | 833.333 | 2916.67 | 2955.75 |
4800 | 208.333 | 729.17 | 738.93 |
9600 | 104.167 | 364.58 | 369.47 |
19200 | 52.083 | 182.29 | 184.73 |
38400 | 26.041 | 91.15 | 92.37 |
57600 | 17.361 | 60.76 | 61.57 |
115200 | 8.681 | 30.38 | 30.79 |
V tabulce kromě času v μs uvádím i počet taktů procesoru Z80. ZX Spectrum 48k, stejně jako DG192k má 14MHz krystal, ze kterého je odvozena taktovací frekvence Z80 3.5MHz. V ZX Spectru 128k je krystal 35.4690MHz a odvozená taktovací frekvence Z80 je maličko vyšší 3.5469MHz. Toto bude důležité při časování přenosového programu. Všechny časově kritické operace je samozřejmě nutné spouštět v RAM nezpomalované ULA a se zakázaným přerušením, jinak program nejenom poběží pomaleji, ale hlavně se nedá spolehnout jakou přesně rychlostí v ten který okamžik proběhne.
počítač | takt procesoru | μs/takt |
---|---|---|
ZX Spectrum 48k gumák / 48k+ | 3500000Hz | 0,2857 |
ZX Spectrum 128k+ / +2 | 3546900Hz | 0,2819 |
Při znalosti, kolik taktů trvají jednotlivé instrukce Z80 se nejvyšší rychlost zdá být 115200bps nedosažitelná. Rychlost 57600bps dosáhnout určitě lze.
Instrukce na Z80 trvají celý počet taktů, ne zlomky, ale desetiny uváděné u počtu taktů by mohly mít vliv na rozhodování, jestli se přiklonit k vyšší, nebo nižší hodnotě. Chyba se při čtení 8 bitů po sobě nasčítá.
Rozdíl v taktech mezi ZX Spectrum 48k a ZX Spectrum 128k je přibližně 1.3%, totéž v rozdílech trvání jednoho bitu při různých baudrate. Dejme tomu, že optimem by proto mohl být počet taktů cca uprostřed mezi hodnotami pro ZXS 48k a 128k? Rozhodně by chyba v trvání bitu neměla být větší než malé jednotky %.
Nejrychlejší zápis na I/O porty AY v cyklu
Pojďme si vyzkoušet, jak dlouho trvají instrukce a jak nejrychleji je možné zapisovat na I/O port AY. Resp. jakou nejvyšší frekvenci lze dosáhnout změnou logických stavů, protože teorie je hezká, ale neuškodí si ji ověřit prakticky. V ZX Spectru existují kromě zpomalené paměti (oblast VRAM 16384 až 32767 a půlka stránek ZX Spectra 128k od 49152 do 65535) i takové pasti, jako třeba zpomalené porty. To se naštěstí netýká portů AY-3-8912.
Stejný kód se zvýrazněním syntaxe zde.
cpu z80undoc org 32768 START di ld bc,65533 ; zvol registr 7 ld a,7 out (c),a ld bc,49149 ; vypni zvuk, I/O port výstup (bit 6 v log. 1) ld a,255 out (c),a ld bc,65533 ; zvol registr 14 ld a,14 out (c),a ld bc,49149 ; a bude se zapisovat na I/O port LOOP xor a ; 4T out (c),a ; 12T jp PRESKOK ; 10T PRESKOK cpl ; 4T out (c),a ; 12T jp LOOP ; 10T
Program pouze nastaví porty AY, vezme do BC adresu datového portu a pak už jen donekonečna střídá zápis 0 a 255. Celá perioda by měla trvat 52 taktů procesoru, přibližně 52 * 0.2819 = 14.66μs, čemuž odpovídá frekvence 3546900/52 = 68209Hz.
Na výstupech šedé ZX Spectrum 128k +2 jsem naměřil 14.67μs a 68.18kHz analyzerem (resp. 68.2kHz multimetrem), takže hodnoty v rámci tolerance odpovídají předpokladu. Divné bylo jen to, že střída nebyla přesných 50%, ale něco málo přes 55%, log. 1 trvala 8.17μs a log. 0 6.5μs. Nevím, co je příčinou, jestli na to nemají vliv převodníky z TTL na RS232 a jejich rozhodovací úroveň napětí na vstupu z TTL. V každém případě délka pulzů mírně kolísá v řádu setin až desetin μs.
Více způsoby jsem měřil na ZX Spectrum 48k+ a zároveň na ZX Spectrum Sparrow Lite prototype 2, což je v podstatě taky 100% ZX Spectrum s originál ULA i Z80. Rozdíly mezi nimi byly nepatrné, na hranici chyby měření. Teoreticky by frekvence měla být 67.307kHz, protože cyklus trvá 52 taktů na 3.5MHz Z80, čemuž odpovída 14,86μs.
vypočteno | naměřeno Aneng AN8008 | naměřeno Siglent SDS1204X-E | naměřeno Saleae analyzer | |||||
---|---|---|---|---|---|---|---|---|
frekvence | 67.307kHz | 67.28kHz až 67.30kHz | 67.3074kHz | 67.23 až 67.42kHz | ||||
perioda | 14,86μs | neukazuje z frekv. 14,86μs | 14.86μs | 14.83μs až 14.88μs | ||||
střída | ideálně 50% | 49.7% | 49.65% | 49.72 až 49.86% |
Rozdíl mezi trváním log 0 a log 1 se nepotvrdil, pomalejší náběžná hrana z log 0 na log 1 nad 3V by snad mohla způsobit prodloužení log 0. Nutno prozkoumat s připojeným převodníkem. Rychlost překlopení výstupu je pro obě logické úrovně stejná, viz měření signálu BDIR na pinu 18, od náběžné hrany signálu trvá v obou případech zhruba stejných 280ns než se změní stav na výstupu.
Rozptyl hodnot logického analyzeru je nejspíš způsobený nízkým samplerate, naměřené hodnoty dost často oscilují mezi dvěma okolo předpokládané.
TODO: Porovnat s výsledky na DG192k. Mělo by být stejné, jako ZX Spectrum 48k+.
TODO: Porovnat trvání log 0 a log 1 přímo na AY proti RS232 úrovním. Liší se?
Sériový přenos na ZX Spectru
Teď se konečně dostáváme k něčemu praktickému. Když už máme ověřeno, že se lze na instrukce out (c),a spolehnout, je čas sestavit program, který serializuje a odesílá alespoň jeden byte, protože odesílaná data se dají mnohem lépe měřit než jejich příjem. Pro svůj pokus jsem si vybral 57600bps.
Cygnusovo vysílání dat 57600bps bez řízení toku
Prozkoumal jsem několik cizích zdrojáků a nakonec jsem zkusil napsat následující program. Zdál se mi přehledný, srozumitelný, instrukce jp trvá vždy stejný čas, nemusím řešit rozdíl v počtu taktů, když je podmínka splněna a když není. Nepřesnost trvání cyklu 62 taktů je tolerovatelná, v přenosu celého bytu udělá skoro 2.3μs a to je pořád výrazně méně než čtvrtina z trvání jednoho bitu 17.361μs. Později se ukázalo, že to je řešení naivní a nedokonalé. Ale data to přenáší a dodalo mi odvahy.
Kód odesílající jeden byte se zvýrazněním syntaxe zde.
Kód odesílající sekvenci dat se zvýrazněním syntaxe zde.
;================================================================================================== ; RS232 - transmitting through AY-3-8912 without data flow control ; ; 57600bps (17,3611μs) 61.57813T na ZX128k, 61T bude trvat 17.19811μs, chyba -0.9% (58146bps) ; 62T bude trvat 17.48005μs, chyba +0.7% (57208bps) ; 60.76389T na ZX48k, 61T bude trvat 17.42857μs, chyba +0,4% (57377bps) ; 62T bude trvat 17.71429μs, chyba +2.0% (56451bps) ;================================================================================================== cpu z80undoc org 32768 ; musí běžet v rychlé RAM START di ; nesmí být zpomalováno a přerušováno ld bc,65533 ; zvol registr 7 v AY ld a,7 out (c),a ld bc,49149 ; vypni zvuk a nastav I/O port jako výstup (bit 6 v log. 1) ld a,255 out (c),a ld bc,65533 ; zvol registr 14 v AY ld a,14 out (c),a SEND_STRING ld hl,DATA ; 10T data, která budeme přenášet ld bc,DATA_LENGTH ; 10T SEND_STRING_L push bc ; 10T push hl ; 10T ; celý cyklus 10+10+11+11+6+6+4+4+10=72T projeví se na prodloužení stop bitu po dokončení přenosu AYSER_SENDBYTE ld bc,49149 ; 10T registr 14 pro výstup zvolen, budu do něj zapisovat ld e,128 ; 7T E bude počítadlo, 8x rotovat, nastane carry ld a,247 ; 7T do A 0b11110111 = TxD do log.0, protože start bit out (c),a ; 12T zde začíná start bit a zde začíná záležet na taktech ld a,(hl) ; 7T vezmi byte, který má být vyslán ld d,a ; 4T ld l,247 ; 7T a teď můžu L změnit, před zavoláním AYSER_SENDBYTE ; bude muset být HL na zásobníku ; od outu sem 12+7+4+7 = 30T + k dalšímu outu bude 32T = 62T, trvání start bitu AYSER_SENDB_L rrc d ; 8T rotuj data skrz carry flag jp c,AYSER_SENDB_1 ; 10T ld a,l ; 4T do A 0 = TxD do log. 0 jp AYSER_SENDB_2 ; 10T AYSER_SENDB_1 ld a,b ; 4T do A B = TxD do log. 1 (B = 0b10111111) jp AYSER_SENDB_2 ; 10T ; od rrc d, bit = 1 8+10+4+10 = 32T ; od rrc d, bit = 0 8+10+4+10 = 32T AYSER_SENDB_2 out (c),a ; 12T rrc e ; 8T jp nc,AYSER_SENDB_L ; 10T ; celý cyklus trvá od AYSER_SENDB_L 8+10+4+10+12+8+10 = 62T, o takt víc, než by měl ld a,255 ; 7T protože log. 1 do stop bitu i do RTS nop ; 4T (použít raději rrc e 8T 3x?) nop ; 4T nop ; 4T nop ; 4T nop ; 4T nop ; 4T zpoždění celkem 24T v 6x nop ; od minulého outu sem 12+8+10+7+6*4 = 61T, trvání posledního bitu (MSB) out (c),a ; 12T zde začíná stop bit nop ; 4T zpoždění nop ; 4T zpoždění nop ; 4T zpoždění nop ; 4T zpoždění 4x nop = 16T pop hl ; 11T pop bc ; 11T inc hl ; 6T dec bc ; 6T ld a,b ; 4T or c ; 4T jp nz,SEND_STRING_L ; 10T ; od outu do jp včetně 12+4*4+11+11+6+6+4+4+10 = 80T ; začátek cyklu 44T prodlouží trvání stop bitu, celkem vychází 128T, nepatrně víc než 2 stop bity, ; naměřeno 36.6us ei ; 4T před návratem do BASICu přerušení povol ret ; 10T DATA db "Hello RS232 world! ZX Spectrum 128 is sending data." db 13,10 ; CR, LF DATA_LENGTH equ $ - DATA
Ověřil jsem, že přenos funguje analyzérem, proto je čas kód vylepšit a posílat celou sekvenci bytů. To už má praktické využití. Na prvním obrázku je oproti programu kód 0x55, protože jsem v tu chvíli chtěl vidět i trvání start bitu, není to způsobeno chybou v programu a opačným pořadím bitů.
Na screenshotu je vidět, že i bez řízení toku dat je stop bit maličko delší, ale nijak extrémně a stále lze zkrátit vynecháním zpomalovacích instrukcí jp. Přenos dat je pořád na poměry ZX Spectra velmi rychlý a pokud ZX Spectrum posílá data do PC, které je stíhá spolehlivě přijímat, není řízení toku dat vždy nezbytné. Je na uživateli, jak si kontrolu správnosti dat zařídí, když už ne lépe, tak by měl vždy alespoň porovnat počet přenesených bytů. Ať už je datový tok nějak řízen, nebo ne.
stty -F /dev/ttyS0 57600 cs8 clocal cread cstopb parenb parodd -crtscts -echo raw
K výpočtu odchylky jsem sečetl takty start bitu a datových bitů 62+7*62+61=557, z toho jsem spočítal čas trvání, vydělil 9 a vypočítal časovou odchylku odchylku trvání jednoho bitu. Vydělením této odchylky 1/100 času, kterého chci dosáhnout spočítám chybu v %.
rychlost | 57600bps | |
řízení toku | není | |
trvání bitu 128k | 17,4487μs - průměrně cca +0,5% | |
trvání bitu 48k | 17,6825μs - průměrně cca +1,85% |
Vysílání dat 57600bps podle Martina1
Další způsob, jak odesílat data rychlostí 57600bps poslal do diskuze na OldComp.cz Martin1. Jeho způsob je kratší a přesnější, pro všechny bity mu vychází 61T, přesně jak má. Resp. původně měl obráceně rotaci datového bytu, ale základní myšlenka byla v jádru správná. Upravil jsem ho do zkompilovatelné podoby vhodné k testování a změřil chování na reálném hardwaru.
Kód odesílající jeden byte se zvýrazněním syntaxe zde.
;================================================================================================== ; RS232 - transmitting through AY-3-8912 without data flow control ; ; 57600bps (17,3611μs) 61.57813T na ZX128k, 61T bude trvat 17.19811μs, chyba -0.9% (58146bps) ; 60.76389T na ZX48k, 61T bude trvat 17.42857μs, chyba +0,4% (57377bps) ;================================================================================================== cpu z80undoc org 32768 ; musí běžet v rychlé RAM START di ; nesmí být zpomalováno a přerušováno ld bc,65533 ; zvol registr 7 v AY ld a,7 out (c),a ld bc,49149 ; vypni zvuk a nastav I/O port jako výstup (bit 6 v log. 1) ld a,255 out (c),a ld bc,65533 ; zvol registr 14 v AY ld a,14 out (c),a TRANSMIT_57600 ld bc,49149 ; 10T port AY-3-8912 ld h,8 ; 7T H poslouží jako počítadlo 8 bitů ld a,01010101b ; 7T data k odeslání do A ld l,a ; 4T zkopíruj data z A do L rrc l ; 8T 4x rotuj data vpravo (LSB na pozici bitu 3) rrc l ; 8T dvě rotace před outem, aby vyšly takty ; start bit ld a, 11110111b ; 7T maska out (c),a ; 12T zapiš na port rrc l ; 8T a dvě rotace ze 4 po outu rrc l ; 8T ; datové bity TRANSMIT_LOOP ld a,0 ; 7T zpoždění ld a,0 ; 7T zpoždění rrc l ; 8T další bit na pozici bitu 3 ld a,11110111b ; 7T připrav masku or l ; 4T přidej masku ; od začátku start bitu 12+8+8+7+7+8+7+4 = 61T ; od minulého bitu 12+4+12+7+7+8+7+4 = 61T out (c),a ; 12T zapiš na port dec h ; 4T počítadlo bitů jr nz,TRANSMIT_LOOP ; 12/7T opakuj ; stop bit ld a,(ix+0) ; 19T zpoždění inc hl ; 6T zpoždění inc hl ; 6T zpoždění ld a,11111111b ; 7T ; od posledního outu v cyklu 12+4+7+19+6+6+7 = 61T out (c),a ; 12T zapiš na port ei ; 4T ret ; 10T prodlouží trvání stop bitu
Nastavení sériového portu v Linuxu bude pro tento kód vypadat následovně.
stty -F /dev/ttyS0 57600 cs8 clocal cread cstopb parenb parodd -crtscts -echo raw
Měřením jsem zjistil, že bity trvají cca 18.02μs v log. 1 a 16.42μs v log. 0, toto chování je stále stejné. Průměrně tak čas vychází zhruba na 17.22μs, což je opravdu o kousek míň, než cílových 17.361μs a blíží se vypočtené chybě -0.9% (měřením -0.8%). To je vlastně dobře, protože na ZX Spectru 48k s pomaleji taktovanou Z80 se chyba vyrovná a přenos bude naopak o maličko pomalejší, než by měl být. Obojí tak bude v tolerovatelných mezích.
rychlost | 57600bps | |
řízení toku | není | |
trvání bitu 128k | 17.1981μs - cca -0.94% | |
trvání bitu 48k | 17.4286μs - cca +0,38% |
TODO: Vyzkoušet na DG192 s pomalejším taktem Z80.
Vysílání dat 115200bps podle Martina1
Martin1 poslal na OldComp ještě jeden velmi zajímavý návrh, jak posílat data, tentokrát rychlostí 115200 baudů. Upravil jsem jeho "nástřel od boku" pro přenos sekvence bytů, opravil chybu v rotaci a dodatečně doplnil Martinův nápad použít exx místo push a pop. Výsledek funguje lépe než jsem si dovedl představit. Sice stále bez řízení toku dat, ale opravdu to přenáší rychlostí 115200bps, resp. jen nepatrně pomaleji s docela zanedbatelnou chybou na ZX Spectrum 128k a stále tolerovatelnou chybou na ZX Spectrum 48k. Délka stop bitu mezi byty přitom vychází jen málo přes dobu trvání 3.1 baudů v pomalejší variantě a přibližně 2.7 baudů v rychlejší variantě. To je na 3.5MHz Z80 velmi pěkný výsledek.
Kód s déle trvajícím stop bitem se zvýrazněním syntaxe zde.
Kód s použitím sekundární sady registrů se zvýrazněním syntaxe zde.
;================================================================================================== ; RS232 - transmitting through AY-3-8912 without data flow control ; ; 115200bps (8,68056μs) 30.78906T na ZX128, 31T bude trvat 8,7400μs, chyba +0.7% (114416bps) ; 30,38194T na ZX48k, 31T bude trvat 8,8571μs, chyba +2,0% (112903bps) ;================================================================================================== cpu z80undoc org 32768 TRANSMIT_115200 di ; nepřerušovat ld bc,65533 ; zvol registr 7 v AY ld a,7 out (c),a ld bc,49149 ; vypni zvuk a nastav I/O port jako výstup (bit 6 v log. 1) ld a,255 out (c),a ld bc,65533 ; zvol registr 14 v AY ld a,14 out (c),a ld hl,DATA ; adresa odkud ld de,DATA_LENGTH ; počet bytů k přenesení exx ld bc,49149 ; adresa datového portu do sekundárního BC' exx ; odsud se bude opakovat, násl. instrukce prodlouží stop bit TRANSMIT_LOOP ld a,(hl) ; 7T vezmi byte,který má být vyslán exx ; 4T sekundární sada registrů s připraveným BC, abych neměnil důležité HL rrca ; 4T předrotuj data 4x vpravo z bitu 0 do bitu 4 rrca ; 4T rrca ; 4T rrca ; 4T ld d,a ; 4T a uschovej A do D (na HL' nesahat, abych ho nemusel ukládat při návratu do BASICu) ; start bit ld a,11110111b ; 7T start bit out (c),a ; 12T zapiš na port (vždy log. 0 = start bit) rrc d ; 8T rotuj L, bit 0 na pozici bitu 3 (LSB) ld a,11110111b ; 7T priprav masku or d ; 4T vymaskuj, vše kromě významného bitu nastav na 1 out (c),a ; 12T zapiš na port, celkem 31T od předchozího out (c),a rrc d ; 8T bit 1 na pozici bitu 3 ld a,11110111b ; 7T or d ; 4T vymaskuj out (c),a ; 12T zapiš na port rrc d ; 8T bit 2 ld a,11110111b ; 7T or d ; 4T vymaskuj out (c),a ; 12T zapiš na port rrc d ; 8T bit 3 ld a,11110111b ; 7T or d ; 4T vymaskuj out (c),a ; 12T zapiš na port rrc d ; 8T bit 4 ld a,11110111b ; 7T or d ; 4T vymaskuj out (c),a ; 12T zapiš na port rrc d ; 8T bit 5 ld a,11110111b ; 7T or d ; 4T vymaskuj out (c),a ; 12T zapiš na port rrc d ; 8T bit 6 ld a,11110111b ; 7T or d ; 4T vymaskuj out (c),a ; 12T zapiš na port rrc d ; 8T bit 7 ld a,11110111b ; 7T or d ; 4T vymaskuj out (c),a ; 12 zapiš na port, poslední bit (MSB) rrc d ; 8T zpomalení nop ; 4T zpomalení ld a,11111111b ; 7T vymaskuj stop bit na 1 out (c),a ; 12T zapiš na port, zde začíná stop bit exx ; 4T vrať primární sadu registrů s adresou v HL a délkou v DE inc hl ; 6T další adresa v RAM dec de ; 6T o byte ke zpracování míň ld a,d ; 4T testuj, jestli byly zpracovány všechny or e ; 4T jp nz,TRANSMIT_LOOP ; 10T a opakuj, dokud nejsou přenesena všechna data ; opakování nastane zpoždění 12+4+6+6+4+4+10 = 46T ; plus na začátku cyklu dalších 7+4+4+4+4+4+4+7 = 38T ; celkem 84T délka stop bitu při přenosu sekvence, tj. asi 2.7 délky trvání jednoho bitu ei ret ; a to je vse, pratele! DATA db "Hello RS232 world! ZX Spectrum 128 is sending data." db 13,10 ; CR, LF DATA_LENGTH equ $ - DATA
Nastavení sériového portu v Linuxu bude pro tento kód vypadat následovně.
stty -F /dev/ttyS0 115200 cs8 clocal cread cstopb parenb parodd -crtscts -echo raw
Zkusil jsem přenést ze ZX Spectra i větší blok dat, konkrétně obsah ROM asi 20x a následně porovnat md5sum, i sha256sum a vše bylo správně, bez chyb. Zdá se, že se na přenos dá do značné míry spolehnout (alespoň na ZXS128k).
Zkoumáním logickým analyzerem je ale vidět, že náchylnost kombinace AY-3-8912 s RS232 převodníky setrvat déle v log. 1 (přepnout rychleji do log. 1?) začíná nabývat na významu. Nejkratší bit v log. 1 mezi dvěma log. 0 trvá přibližně 9.5μs, naopak log. 0 mezi dvěma log. 1 trvá přibližně 7.9μs. Obojí by mělo trvat 8.6806μs.
rychlost | 115200bps | |
řízení toku | není | |
trvání bitu 128k | 8,7400μs - cca +0.7% | |
trvání bitu 48k | 8,8571μs - cca +2,0% |
TODO: Změřit na DG192 s pomalejším taktem Z80. Vyzkoušeno bez měření analyzerem, přenos funguje spolehlivě.
Řízení toku dat při vysílání ze ZX Spectra
Řízení toku dat nemusí být potřeba při přenosu do rychlého PC a ukládání do souboru, nebo zobrazování v terminálu. Ale může být nezbytné při přenosu do zařízení, které data průběžně zpracovává. Například do plotru, který může zpracovávat jeden příkaz mnohonásobně delší dobu, než trvá přijetí třeba i desítek dalších příkazů.
Proto je nutné číst signál CTS a v tom se skrývá další výzva. AY neumí pracovat jako vstup a výstup zároveň a protože AY-3-8912 má jen jeden osmibitový I/O port, nebylo ani v ZX Spectru možné RS232 převodníky zapojit lépe, než jsou.
V režimu čtení jsou na vstupech AY pullup odpory, které zajišťují, že signál TxD zůstává v log. 1 a tím signalizuje klidový stav linky a signál RTS taky v log. 1 a tím signalizuje nepřipravenost přijímat data. V ZX Spectrum +2 si to konstruktéři pojistili přidáním dalších rezistorů.
Zkusil jsem ke kódu Martina1 přidat čekání na signál CTS s možností přerušení čekání stiskem mezerníku každé 256. čtení I/O portu. Funguje to. Ale přepnutí I/O portu AY z režimu vstupu na výstup trvá dlouho. Dle logického analyzeru trvá zhruba něco přes 50μs než po sestupné hraně CTS ZX Spectrum začne start bitem vysílaných dat. A ještě déle trvá mezera mezi byty, která i když je CTS na přijímací straně trvale v log. 0, tak celkem trvá okolo 82μs, což odpovídá přibližně 4.7 bitům.
Idea - viz časová posloupnost na náčrtku.
A realita viz screenshoty z logického analyzátoru. Zdá se, že to funguje, přenos se opravdu zastavuje, když není CTS v log. 0. Se skutečnou RS232 v PC mi to fungovalo lépe než TTL-USB převodníkem, kde jsem měl CTS z nějakého důvodu trvale v log. 0, ale to není chyba na straně ZX Spectra.
První dva obrázky ukazují stav před zjednodušením práce s AY-3-8912. Nejprve jsem slepě následoval instrukce z datasheetu a přepínal směr přenosu zápisem do registru 7. To zdržovalo stop bit. V poslední verzi nastavuji AY trvale jako výstup a zápisem hodnoty 255 v podstatě vypínám výstupní tranzistory I/O portu tak, aby nestahovaly bit k logické 0 a výstupní úroveň tak určuje pouze kombinace pullup odporů, kterou může RS232 - TTL převodník přetlačit.
Třetí obrázek pak ukazuje rychlost přenosu, resp. zkrácení stop bitů mezi byty po zjednodušení práce s AY. Klidový stav linky mezi byty začínající stop bity se tak zkrátil z 82μs na zhruba 41μs. Reálná přenosová rychlost mírně překračuje 5000 bytů/s (změřil jsem analyzerem na malém vzorku a ověřil přenosem po dobu dvě minuty, i tímto způsobem mi vyšlo 5070 bytů/s, přeneslo se 608417 bytů). To není špatný výsledek vzhledem k teoretickému limitu 5236 bytů/s pro 57600bps se dvěma stop bity.
Kód programu se zvýrazněním syntaxe je k nahlédnutí zde.
Zakomentovaná změna okraje může signalizovat žlutě ukončení programu po přenesení dat, nebo červeně po přerušení přenosu mezerníkem. Testování BREAK i s caps shiftem by program ještě víc zpomalilo, ponechal jsem pouze čtení mezerníku, na přerušení přenosu stačí a je to v podstatě stejné, jako při práci s páskou.
rychlost | 57600bps stopbit 145T (cca 40.9μs) | |
řízení toku | čeká na CTS, lze přerušit mezerníkem | |
trvání bitu 128k | 17.1981μs - cca -0.94% | |
trvání bitu 48k | 17.4286μs - cca +0,38% |
Přenos dat v RS232 ROM Paula Farrowa
Neodolal jsem zvědavosti a nakoukl jsem, jak to vyřešit Paul Farrow ve své RS232 ROM. Nejprve jsem autorovi zkusil napsat a poprosil ho o zdrojový kód, ale odpověděl mi až později. Mezitím jsem binární image jím poskytované RS232 ROM stihl disassemblovat. Což nebylo složité, ale ze strojového kódu nejsou vidět autorovy záměry a důvody. Vidím, co kód dělá, ale nevím, proč přesně to tak autor napsal.
V tuto chvíli mám disassemblováno vše, čím se RS232 ROM liší od standardní ZX Spectrum 48k ROM do té míry, že po kompilaci je binární kód identický. Tím jsem si ověřil, že ve zdrojovém kódu nemám chybu. Z něj jsem vybral jen tu část, která bezprostředně souvisí s RS232 přenosem. Dovolím si při tom ignorovat sériový přenos přes Interface 1, který jsem viděl naživo jen párkrát a rozhodně v našich krajích nepatří mezi běžně rozšířené interface. Na druhou stranu, je fajn, že i pro Interface 1 něco takového existuje.
Disassemblovaný a okomentovaný kód RS232 přenosu z RS232 ROM Paula Farrowa se zvýrazněním syntaxe, ale bez dalších úprav, je k nahlédnutí zde.
Zaujalo mě, jakým způsobem Paul Farrow pracuje s AY. V předchozí kapitole jsem zmínil naivní způsob přepínání mezi režimem čtení a zápisu I/O portu AY, protože jsem vycházel z popisu v katalogu, kdežto Paul Farrow neztrácel čas volbou registru 7 a zápisem na něj, ale četl port i když byl přepnutý pro zápis, jen do něj před čtením zapsal 255.
Jeho metodu jsem si ověřil na zjednodušeném programu, který nedělá nic víc, než že vyšle pulz na signálu RTS trvající zhruba stejně dlouho jako bit odpovídající přenosové rychlosti, v tomto případě cca 18μs. Po vyslání pulzu jsem četl port 4096x a každý přečtený byte zapsal do VRAM. Fungovalo to, aniž bych musel AY přepínat složitěji. Proto jsem podle toho zjednodušil přepínání i v předchozím programu.
Zdá se to být banalita, ale toto chování AY-3-8912 jsem nikde nenašel popsané a přitom je zcela zásadní pro rychlejší přenos dat po RS232 v ZX Spectru 128k s řízením toku dat.
Pro další zkoumání jsem oddělil kód Paula Farrowa vysílající data a upravil ho, aby byl samostatně použitelný a zkompilovatelný, přidal jsem k němu minimální smyčku pro přenos krátké sekvence bytů. Řízení datového toku s čtením signálu CTS jsem zachoval. Ale taky jsem neprováděl žádnou další optimalizaci.
Posčítáním trvání instrukcí v taktech jsem došel k 62T pro trvání start bitu i datových bitů. U stop bitu je to těžší, ale určitě nebude kratší než 120T. Celé to komplikuje test BREAku, který může trvat různý čas, podle toho jestli je stisknuta klávesa SPACE bez CAPS SHIFTu, nebo ne.
Logickým analyzerem jsem naměřil trvání log. 1 mezi nulami 18.3μs a log.0 mezi jedničkami 16.7μs, trvání stop bitu, resp. klidového stavu linky mezi byty se pohybovalo nejčastěji okolo 66.2μs, občas až k 73μs.
Správné fungování řízení toku dat jsem ještě nezkoušel.
Vysílání dat s pomocí 8255 a TTL-USB převodníku.
Didaktik Gama nabízí oproti továrnímu ZX Spectru zajímavou možnost připojit sériový převodník z úrovní TTL na USB přímo k I/O portům paralelního portu a tím zcela vynechat konverzi z TTL úrovní na napětí kompatibilní s RS232. V nejjednodušším případě stačí připojit dva vodiče. GND a TxD směrem z DG192k ven. Napájení převodníku obstará USB.
Vysílání dat 57600bps podle Busysofta s paritou
Kód pro použití s 8255, tj. Didaktiky, UPI, UR-4 a podobnými interface poslal na OldComp.cz Busysoft. S jeho dovolením zveřejňuji.
60 8051 f3 bytput di Odoslanie jedneho bajtu 61 8052 5f ld e,a 57600 Bd ... 1 bit = 61.57 T 62 8053 0609 ld b,9 stop bity 63 8055 10fe djnz #fe 64 8057 af xor a 65 8058 d35f out (#5f),a start bit 66 805a c35d80 jp $+3 w 67 805d c36080 jp $+3 w 68 8060 00 nop w 24 69 8061 0608 ld b,#08 70 8063 cb0b bbpp1 rrc e / 71 8065 9f sbc a,a 72 8066 e608 and #08 73 8068 d35f out (#5f),a \30 74 806a c36d80 jp $+3 w 75 806d 00 nop w 76 806e 00 nop w 18 77 806f 10f2 djnz bbpp1 78 8071 af xor a parita 79 8072 ab xor e 80 8073 3e08 ld a,#08 81 8075 e27980 jp po,bbpp2 82 8078 af xor a 83 8079 d35f bbpp2 out (#5f),a 84 807b c37e80 jp $+3 w 85 807e c38180 jp $+3 w 86 8081 c38480 jp $+3 w 87 8084 c38780 jp $+3 w 88 8087 00 nop w 44 89 8088 3e08 ld a,#08 stop bity - na zaciatku 90 808a d35f out (#5f),a 91 808c c9 ret
Busyho kód ale není přímo použitelný v Prometheu, nebo AS. V Prometheu budou vadit instrukce jp $+3, které je potřeba přepsat na jp $+2, protože Prometheus vyhodnocuje výraz jinak a $ velmi zjednodušeně bude znamenat nikoli adresu instrukce, ale adresu prvního bytu operandu. Zápis hexadecimálních čísel, adresy, kódy instrukcí a komentáře bez středníku znamenají jen hodně přepisování. Provedl jsem za vás.
;=================================================================================================== ; RS232 - transmitting through paralell interface with 8255 without data flow control ; ; 57600bps (17,3611μs) 61.57813T na ZX128k, 61T bude trvat 17.19811μs, chyba -0.9% (58146bps) ; 60.76389T na ZX48k, 61T bude trvat 17.42857μs, chyba +0,4% (57377bps) ;=================================================================================================== cpu z80undoc org 32768 bytput di ; 4T ld e,a ; 4T ld b,9 ; 7T BYTPUT_WAIT_0 djnz BYTPUT_WAIT_0 ; B * 13T + 9T xor a ; 4T out (95),a ; 11T zápis na port 8255 (start bit, log. 0) jp BYTPUT_WAIT_1 ; 10T zpoždění BYTPUT_WAIT_1 jp BYTPUT_WAIT_2 ; 10T zpoždění BYTPUT_WAIT_2 nop ; 4T zpoždění (celkem 24T) ld b,8 ; 7T BYTPUT_1 rrc e ; 8T sbc a,a ; 4T and 8 ; 7T ; k tomuto outu 11+10+10+4+7+8+4+7 = 61T out (95),a ; 11T zápis na port 8255 jp BYTPUT_WAIT_3 ; 10T zpoždění BYTPUT_WAIT_3 nop ; 4T zpoždění nop ; 4T zpoždění (celkem 18T) djnz BYTPUT_1 ; 13/9T xor a ; 4T parita xor e ; 4T ld a,8 ; 7T jp po,BYTPUT_2 ; 10T xor a ; 4T ; k tomuto outu 11+10+4+4+9+4+4+7+10+4 = 67T nenastane skok ; k tomuto outu 11+10+4+4+9+4+4+7+10 = 63T nastane skok BYTPUT_2 out (95),a ; 11T jp BYTPUT_WAIT_4 ; 10T zpoždění BYTPUT_WAIT_4 jp BYTPUT_WAIT_5 ; 10T zpoždění BYTPUT_WAIT_5 jp BYTPUT_WAIT_6 ; 10T zpoždění BYTPUT_WAIT_6 jp BYTPUT_WAIT_7 ; 10T zpoždění BYTPUT_WAIT_7 nop ; 4T zpoždění (celkem 44T) ld a,8 ; 7T out (95),a ; 11T ret ; 10T
TODO: Upravit do kompilovatelné podoby a změřit reálné chování na DG192k. Vyzkoušet i funkčnost se superlevným fake převodníkem, který jsem dostal na Bytefestu.
Busyho kód počítá s trváním instrukce out (N),a o takt méně než out (c),a, který využívá adresu v registru BC, při úpravě pro AY-3-8912 by bylo vhodné o ten jeden takt trvání kódu zkrátit.
Může být lepší se vyhnout použití portu C na 8255, aby program byl použitelný nejenom s DG192k, ale i s továrním a neupraveným Didaktik Gama 80k, který port C, resp. bit 0 z portu C, používá ke stránkování paměti.
Jak přijímat data?
Velmi zjednodušeně? Poslouchat RxD a po detekci start bitu v přesných časových intervalech načíst sérii bitů a z nich sestavit byte, rychle zapsat do RAM a co nejrychleji být připraven opět poslouchat na RxD jestli nepřichází další start bit. V praxi se situace samozřejmě komplikuje. Dat může přijít víc, než se do RAM vejde. Nebo dokonce může velmi snadno nastat situace, že po každém přijatém bytu nebude počítač schopen přijmout další dřív než předchozí zpracuje. Pak bude řízení toku dat nevyhnutelné.
Příjem dat 57600bps podle Martina1
I tady odvedl Martin1 většinu práce a nakonec jsme skončili následujícím kódem. Se zvýrazněním syntaxe k nahlédnutí zde.
Opět jsem doplnil smyčku pro příjem bloku dat, kterou jsme optimalizovali do současné podoby, doplnil jsem inicializaci AY-3-8912 a důkladně ověřil na reálném hardwaru. Tato varianta je bez řízení toku dat. To znamená, že ZX Spectrum musí být připraveno data přijímat a teprve potom může druhý počítač data poslat, jinak se část dat ztratí.
Pokud dojde k rozsynchronizování, nebo vysílající počítač vysílá pomaleji, program umí detekovat log. 0 na vstupu RxD v době, kdy očekává klidový stav linky (stop bit) před start bitem, oznámí to ukončením a červeným okrajem obrazovky. Ale je to detekce chyby značně nespolehlivá a spíš náhodná.
;=================================================================================================== ; RS232 - receiving data through AY-3-8912 without flow control - 2 stop bits ; ; 57600bps (17,3611μs) 61.57813T na ZX128k, 61T bude trvat 17.19811μs, chyba -0.9% (58146bps) ; 60.76389T na ZX48k, 61T bude trvat 17.42857μs, chyba +0,4% (57377bps) ;=================================================================================================== ; AY I/O ZX name dir. better name ; ; A2 CTS out RTS -> ; A3 RxD out TxD ; A6 DTR in CTS ; A7 TxD in RxD <- cpu z80undoc org 32768 START di ; 4T exx ; 4T sekundární sada push hl ; 11T exx ; 4T primární sada call AY_INIT ; dlouho ld hl,16384 ; 10T data buffer - sem se bude ukládat ld de,6912 ; 10T a uloží se 6912 bytů call RECEIVE57600 ; mělo by trvat zhruba 1.32s (6912*11/57600) ; ld hl,49152 ; 10T načtená data zkopíruj do VRAM, pokud nejsou ; ld de,16384 ; 10T ; ld bc,6912 ; 10T ; ldir ; 21*6911+16 = 145147T (40.9ms, tj. 2/50s) exx ; 4T sekundární sada pop hl ; 10T obnov HL', BASIC potřebuje exx ; 4T primární sada ei ; 4T ret ; 10T ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - RECEIVE57600 dec hl ; 6T bude zvýšeno během čekání ld bc,65533 ; 10T datový port AY-3-8912 do BC ; kontrola jestli nezačal přenos - pak by byla špatně synchronizace se start bitem RECEIVE_START in a,(c) ; 12T čti port, nastaví sign flag dle MSB jp p,RECEIVE_ERROR ; 10T chyba, RxD má být v log. 1 (klidový stav/stop bit) ; zpoždění detekcí 12+10 = 22T ; čekání na start bit RECEIVE_WFSB in a,(c) ; 12T čti port rlca ; 4T bit 7 do carry flagu jp c,RECEIVE_WFSB ; 10T opakuj do start bitu ; smyčka čekání 12+4+10 = 26T ; start bit začal, čeká se na bit 0 ; dlzka cakania je zvolena tak, aby bit 0 bol niekde vo svojej polovici ; od hrany startbitu uplynulo najmenej 15T, najviac 41T, berieme priemer 28T ; treba este cakat 1.5 x 61T - 28T = 63.5T (z toho 11T na dalsiu instrukciu IN) dec de ; 6T sniž počítadlo bytů, testovat se bude později inc hl ; 6T zvyš adresu v RAM, kam se byte uloží exx ; 4T sekundární sada ld bc,65533 ; 10T datový port AY-3-8912 do BC' (a zpoždění zároveň) inc hl ; 6T zpoždění dec hl ; 6T zpoždění ld a,0 ; 7T zpoždění ld h,7 ; 7T v cyklu bude čteno 7 bitů a 8. po skončení cyklu ; 6+6+4+10+6+6+7+7 = 52T ; teraz je bit 0 niekde v polovici, mozme citat 8 krat s odstupom 61T RECEIVE_LOOP in a,(c) ; 12T načti port rlca ; 4T bit 7 do carry flagu rr l ; 8T buduj bajt v L ld a,0 ; 7T zpoždění ld a,0 ; 7T zpoždění ld a,r ; 9T zpoždění dec h ; 4T počítadlo jp nz,RECEIVE_LOOP ; 10T čti další bity ; 12+4+8+7+7+9+4+10 = 61T opakuje-li se (bity 0 až 6) ; následuje čtení MSB, bit 7 in a,(c) ; 12T načti port (MSB je někde v půlce trvání, do stopbitu cca 31 ± 13T ± 2T odchylka přesnosti 3%) rlca ; 4T bit 7 do carry flagu ld a,l ; 4T téměř sestavený byte do A rra ; 4T narotuj MSB do bitu 7 v A ; 12+4+4+4 = 24T exx ; 4T primární sada ld (hl),a ; 7T ulož byte do RAM ld a,d ; 4T všechny byty přijaté? or e ; 4T zkontroluj jp nz,RECEIVE_WFSB ; 10T čekej na další byte ret ; 10T ; uložení bytu trvá 4+7+4+4+10 = 29T ; celkem 24+29=53T ; včetně detekce stavu linky, 75T s ověřením před start bitem ; jinak lze rovnou čekat na další startbit RECEIVE_ERROR exx ; 4T primární sada ld a,2 ; 7T DEBUG out (254),a ; 11T DEBUG ei ; 4T ret ; 10T ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - AY_INIT ld bc,65533 ; 10T BC = 65533 (11111111 11111101) ld a,7 ; 7T zvol registr 7 v AY out (c),a ; 12T 65533,7 ld a,b ; 4T A = 255, vypni zvuk a nastav I/O port jako výstup (bit 6 v log. 1) ld b,191 ; 7T BC = 49149 (10111111 11111101) out (c),a ; 12T 49149,255 ld b,a ; 4T BC = 65533 ld a,14 ; 7T zvol registr 14 v AY out (c),a ; 12T 65533,14 ld a,b ; 4T A = 255 ld b,191 ; 7T BC = 49149 out (c),a ; 12T 49149,255 = zapiš 255 a nastav pullupy na H, teď je možné port i číst ret ; 10T
Nastavení sériového portu v Linuxu pro tento kód může vypadat takto s jedním stop bitem
stty -F /dev/ttyS0 57600 cs8 clocal cread -cstopb -parenb -parodd -crtscts raw
Nebo takto, se dvěma stop bity.
stty -F /dev/ttyS0 57600 cs8 clocal cread -cstopb -parenb -parodd -crtscts raw
Kód stíhá přijímat data i s jedním stop bitem, ale druhý stop bit by mohl být potřeba, pokud do smyčky budete přidávat např. reakci na stisk klávesy, aby přenos dat mohl přerušit uživatel. V současné podobě je nutné do ZX Spectra poslat očekávaný počet bytů, jinak se smyčka neukončí.
Krátké video z testování jedné z předchozích verzí programu ke shlédnutí na YouTube.
rychlost | 57600bps | |
řízení toku | není, čeká zadaný počet bytů | |
trvání bitu 128k | 17.1981μs - cca -0.94% | |
trvání bitu 48k | 17.4286μs - cca +0,38% |
TODO: Zprovoznit i na Didaktiku Gama 192k.
Řízení toku dat při příjmu z PC.
Ačkoli předchozí program dokáže přijímat data docela spolehlivě, paměť pro ukládání dat není nekonečná a jakmile je potřeba s daty udělat víc, než jen uložit byte, 3.5MHz Z80 nestíhá. V takovém případě je potřeba pozastavit příjem dat a vysílajícímu počítači signálem RTS oznámit, že další data vysílat nemá.
Krátce jsem s několika sériovými porty experimentoval, abych viděl, jak dlouho trvá, než vysílající počítač zareaguje a kolik dat ještě pošle po změně stavu RTS. V ideálním případě začne vysílat se zpožděním jednoho bitu po nastavení RTS do log. 0 a po nastavení RTS do log. 1 další byte nezačne, nebo dokončí vysílání pokud, už vysílat začal, ale nepokračuje dalším bytem. Viz obrázky z osciloskopu.
Ačkoli byla přenosová rychlost během pořizování screenshotů velmi nízká - 300bps (přenos bytu trval zhruba 33ms), princip by měl být pro všechny rychlosti stejný.
Příjem dat v RS232 ROM Paula Farrowa
Lorem Ipsum.
nastavení COM ve Windows https://batchloaf.wordpress.com/2013/02/12/simple-trick-for-sending-characters-to-a-serial-port-in-windows/ https://www.computerhope.com/modehlp.htm https://ss64.com/nt/mode.html
Závěrem
TODO: Napsat.
Lorem Ipsum.
Download
- scan datasheetu AY-3-8912 od General Instrument - PDF 370kB
- konvertor úrovní TTL-RS232 na AY-3-8912 - projekt v Eagle 6.4 + soubory gerber
Související odkazy
- Pavel Vymetálek - utilita taptoser "Linuxové TAPkování a TRDování přes sériové porty"
- Pavel Vymetálek - utilita sercp pro kopírování mezi ZX Spectrem s ESXDOSem a jinými počítači
- RS232 ROM Paula Farrowa odkaz byl dočasně nefunkční, web přestal fungovat během psaní tohoto článku
- RS232 ROM Paula Farrowa poslední snapshot na Archive.org
- ESXDOS
- Další software Paula Farrowa tentokrát pro ZX81, byť s RS232 nesouvisí
- BT connectors used on Sinclair microcomputers - konektory BT použité v počítačích Sinclair
- diskuze na OldComp.cz o softwarové RS232 - mělo by se týkat převážně jen RS232 na AY-3-8912
- diskuze na OldComp.cz o Didaktiku Gama 192k - a mimo jiné i o RS232 v něm
- diskuze na OldComp.cz o RS232 pro ZX80/ZX81 - pro změnu hardwarová RS232 založená na obvodu AY-3-1015
- diskuze na OldComp.cz o RS232 na 8255 - původně o posílání dat skrze 8255 v Didaktiku Gama s čínským TTL USART - USB převodníkem
- použitý kompiler AS - protože se špatně vyhledává
Historie změn článku
- 2019-11-22 - napsáno, zveřejněno a ještě zcela nedopsáno, ale i tak už to je zatraceně dlouhé
- 2020-07-07 - doplněn test na ZX Spectrum 48k+ s AY
- 2020-08-31 - doplněn projekt vlastního konvertoru úrovní s MAX232