ZX Spectrum a loadery – 1

Každý Spectrista jistě aspoň jednou viděl při nahrávání efekt, u něhož žasnul: Jak to dělají? Tak pojďte a poslyšte… Bude to dlouhé a možná se i něco dozvíte.

ZX Spectrum nahrávalo programy na kazetu a z kazety čistě softwarově, nemělo na to žádný speciální obvod, jako třeba PMD. Na té nejvyšší úrovni se programy nahrávaly jako dvojice „hlavička + data“, kde v hlavičce byly uloženy informace o souboru (jméno, typ, délka apod.) a v datech vlastní obsah.

Hlavička má délku 17 bajtů a následující strukturu:

Pozice Délka Popis
0 1 Typ (0=program, 1=number array, 2=character array, 3=code)
1 10 Jméno (zprava doplněno mezerami na 10 znaků)
11 2 Délka datového bloku
13 2 Parametr 1. Např. u programů je to parametr LINE, u code adresa počátku dat
15 2 Parametr 2. Např. u programů je to začátek proměnných

Data žádnou takovou strukturu nemají, jsou to prostě jen data.

Sestupme o úroveň níž:  každý blok byl uvozen jedním příznakovým bajtem ($00 pro hlavičku, $ff pro data) a ukončen kontrolním součtem (všechny bajty, včetně příznakového, XORovány dohromady). Hlavička tedy měla ve skutečnosti 19 bajtů – začínala 0 a končila kontrolním součtem, ovšem tyto bajty „zkonzumoval“ systém a vám se do rukou nedostaly.

Tady končí ta úroveň, na kterou jste se mohli dostat voláním rutin z ROMky. Níž jsou už jen

Jedničky a nuly

Data byla na pásek zapisována jako posloupnost pulsů. O jejich generování, včetně správného časování, se staral procesor.

Každý záznam byl uvozen takzvaným pilotním tónem. Ten byl tvořen obdélníkovým signálem, tj. pravidelně se střídaly stavy ON a OFF, a to tak, že 2168 T (T je takt procesoru) byl výstup MIC ve stavu ON, 2168T ve stavu OFF. Zaváděcí tón připravil vstupní obvody u kazeťáku na správnou úroveň hlasitosti (pokud tedy nějaké takové byly). Před hlavičkou trval 5 sekund, před datovým blokem dvě sekundy. O délce zaváděcího tónu rozhoduje příznakový bajt, všechny hodnoty menší než 128 mají „dlouhý“, 128 a větší mají krátký.

Po pilotním tónu následoval synchronizační puls. Ten sloužil k tomu, aby nahrávací rutina poznala, že končí pilotní tón a začala zpracovávat data. Synchronizační puls měl 667T ON a 735T OFF.

Po synchronizačním pulsu následovaly už bajty dat. Nejprve flag byte, pak obsah, nakonec kontrolní součet. Bajty byly posílány po bitech od nejvyššího. Každý bit byl představován pulsem (tedy přechodem do ON a přechodem do OFF), lišila se ale délka. U log. 0 to bylo 855T ON a 855T OFF, u log. 1 dvojnásobek, tedy 1710T ON a 1710T OFF.

Při čtení dat (a o to nám tu půjde) pak procesor měřil dobu, za jakou se vstupní signál změní (tj. čas mezi dvěma přechody, tzv. „hranami“). Záznam tak nebyl citlivý na polaritu (jako např. u zmíněného PMD v první verzi), zato procesor neměl už moc času na to dělat něco jiného, protože vlastně jen poslouchal vstup EAR, počítal průchody smyčkou, dokud se signál nezměnil, a k tomu kontroloval stisk klávesy SPACE (ten přerušil nahrávání, jistě se pamatujete).

Čas pro assembler

Teď nastala ta chvíle, kdy přijde výpis ROMky (komentáře beru z českého vydání komentovaného výpisu ROM):

V tomto článku není prostor na podrobné probírání toho, jak algoritmus funguje. Rychle si ale projdeme některá fakta.

Rutina zakazuje přerušení. Je to logické, závisí totiž na přesném načasování. To je taky důvod, proč nebudou fungovat loadery uložené v pomalé RAM ($4000-$7FFF).

V registru C je uložen poslední stav vstupu EAR, ale posunutý o 1 bit doprava, a v nejnižších třech bitech barva borderu. U leaderu to je 02 (červená) / 05 (cyan), u dat pak 01 (modrá) / 06 (žlutá). Proč o 1 bit doprava? Při načítání je provedena sekvence LD A, $7F a IN A, ($FE). Hodnota 7F je vyslána na horních 8 bitů adresy, tj. při čtení klávesnice vybere řádek B – N – M – Symbol Shift – Space. Stav těchto kláves je v nejnižších pěti bitech (0-4), v bitu 6 je stav EAR. Pomocí instrukce RRC se nejnižší bit (=stav klávesy SPACE) posune do příznaku CY a EAR na pozici 5. bitu. Pokud je CY nulový, znamená to, že byl stisknut mezerník a nahrávání končí.

Rutina začíná detekcí signálu, který by odpovídal pilotnímu tónu (LD_LEADER). Pokud je načteno 255 takových pulsů, má se zato, že je to pilotní tón a čeká se na kratší puls, synchronizační. Jakmile přijde (LD_SYNC), můžeme načítat data.

LD_MARKER načte 1 bajt do registru L. Začíná s hodnotou 01, což nahrazuje počítadlo. Postupně se plní bity zprava instrukcí RL L, nejvyšší pak přechází do CY. Dokud je CY=0, načítají se další, jakmile je CY=1, znamená to, že byla načtena kompletní osmice.

Klíčové rutiny jsou LD_EDGE_n (kde n je 1 nebo 2). LD_EDGE_1 nejprve počká určitý časový úsek (465T) a pak zjišťuje, zda se změnila hodnota na vstupu EAR proti poslední uložené (v registru C, viz výše). Pokud ne, smyčka se opakuje. Při každém průběhu je zároveň zvýšena hodnota v registru B. Jakmile se dostane na nulu, znamená to „timeout“, tedy že nepřišla hrana v očekávaném časovém limitu.

Pokud byla hrana nalezena, neguje se obsah registru C. To má za následek jednak změnu uložené hodnoty EAR, ale také změnu barvy borderu.

LD_EDGE_2 vlastně provede dva LD_EDGE_1 za sebou.

Výstup LD_EDGE je tedy následující:

  • CY = 0, Z = 1 – V časovém intervalu nepřišla změna EAR („timeout“)
  • CY = 0, Z = 0 – Stisknuta klávesa SPACE (BREAK)
  • CY = 1 – hrana byla nalezena, v B je aktuální hodnota počítadla.

Počítadlo v B čítá, jak jsem už psal, směrem nahoru. Při každém průchodu smyčkou, která zabere 58 taktů procesoru, je B zvýšen o jedničku. Příklad: při čtení bitu je volána rutina LD_EDGE_2, hledají se tedy dvě hrany. Počítadlo v B je nastaveno na $B0. To znamená, že timeout přijde po $4F průbězích cyklem ($FF-$B0). To představuje 2 x 465T čekací smyčky + 79 * 58T = 5512T. Po takové době tedy rutina zahlásí výpadek signálu.

Výsledná hodnota počítadla je porovnána s hodnotou $CB. Pokud je menší, je to vyhodnoceno jako dva krátké pulsy log. 0, pokud větší, je to log. 1. Hodnota $CB znamená, že smyčka proběhla 27x ($CB-$B0), tedy že dvě hrany přišly za čas menší než 2496T. Připomeňme si: U log. 0 trvají oba impulsy 1710T, u log. 1 je to 3420T. Hodnota mezi těmito dvěma časy je 2565, tedy plusmínus to, co vyšlo i mně. Je to míň, protože nějaké cykly zabere režie (volání podprogramu, vyhodnocování apod., viz LD_8_BITS).

Hackujeme loader

Nebylo to složité, že ne? Tak, teď si řekneme, jak udělat nějaké ty triky…

Zaprvé: čtecí rutina není úplně „T-pünktlich“, takže pár T sem či tam nepředstavuje žádný větší problém. Pokud chceme jednoduchý efekt, můžeme ho přidat bez nějakých složitějších úprav.

Efekty s borderem

Můžeme třeba obarvit pruhy, pokud se nám nelíbí dvoubarevné. Co třeba duha? Přepíšeme jednoduše konec rutiny LD_EDGE:

Co se tu děje? Místo negace obsahu registru C zvyšujeme hodnotu o 1 a negujeme hodnotu bitu 5. Díky maskování s hodnotou $27 se nám nestane, že při přičítání „přeteče“ hodnota a ovlivní bit EAR. Bude se stále měnit v rozsahu 0-7 a vytvoří v borderu duhový efekt.

Poznámka: Pokud si to budete zkoušet, nezapomeňte nahrávací rutinu umístit do horních 32kB RAM!

Když na konec, před instrukci SCF, přidám ještě dvojici instrukcí XOR A; OUT ($FE), A promění se pruhy v borderu na krátké čárky na černém pozadí.

Sem se vejdou všechny efekty, které nějak pracují s barvou borderu. Buď tím, že jej mění, nebo tím, že jej využívají. Co třeba efekt, který použil Busy u loaderu pro Song In Lines 3 (zakulacené rohy). Vypadá to efektně, co?

Přitom to není těžké… Čtyři čtverce v rozích obsahují jednoduchý vzorek („zakulacení“). Pixely, které jsou rovny 1 (mají barvu INK) se budou tvářit jako by byly součástí borderu a budou zobrazovat pruhy. Nekoukal jsem se, jak to Busy dělá, ale vsadil bych se, že principiálně nějak takto:

Efekty s borderem jsou většinou prosté a dostatečně rychlé, takže je sem můžeme napasovat a nestarat se o změnu časování. Většinou se vejdeme do tolerance.

Jednoduché efekty s načteným obsahem

Během nahrávání stihneme určitě i jednodušší operace s načtenými daty, buď na úrovni bitů, nebo na úrovni bajtů. Loader u dema Digisynth dokázal v reálném čase provádět rozbalování dat pomocí Huffmanova dekompresního algoritmu (Huffman se k tomu hodí docela dobře, stačí mít uložený dekomprimační strom a podle načteného bitu jím procházet). Pro zájemce jsem připravil rekonstrukci tohoto loaderu. My se ale podíváme na jiný případ, a tím bude známý Mad Load – rutina, která nahrává obrázky po čtverečcích v nějakém pořadí. Na videu je jeho vylepšená verze.

Mad Load používal velmi jednoduchý formát dat. Po příznakovém bajtu následovaly data pro jednotlivé čtverce. Každý zabral 11 bajtů – nižší a vyšší bajt adresy na obrazovce, kde má být čtverec uložen, pak 8 bajtů obrazové paměti a 1 bajt atributu. Poté následoval další čtverec…

František Fuka nahrávací rutinu mírně upravil – z LD_8_BITS udělal vlastně podprogram, který mu načte 1 bajt do registru L. Tento podprogram využívá v podprogramu pro načtení jednoho čtverce (MAD_SQUARE). Načítání čtverců je voláno stále dokola, dokud magnetofon dává data, a když přestane, vrátí se zpět. Nekontroluje se žádný součet, nic.

Zde je zdroják Mad Loaderu. Okomentoval jsem pouze části, které se od standardního liší.

… a v tomto bodě bych pro tentokrát skončil.

Příště nás čekají ty složitější efekty, třeba nejrůznější počítadla bajtů, zbývajícího času a další legrácky, pro které je potřeba víc taktů procesoru, a musíme tedy upravovat časovací smyčky a algoritmy rozsekat tak, aby se vešly do času, co máme k dispozici. Zatím přijměte tedy toto stručné „úvodní nakoukání do tajemství loaderů“, zkuste si zaexperimentovat sami a pokud nějaký hezký loader vytvoříte, pochlubte se!

Příspěvek byl publikován v rubrice Hardware, Software se štítky , , , , . Můžete si uložit jeho odkaz mezi své oblíbené záložky.
  • Eye

    Existuje někde tzx toho původního loaderu DigiSynthu? Moc by mě to zajímalo. Pamatuji si, že jsem v cracknuté, na disk převedené verzi našel zbytky toho loaderu – tuším, že mě tam zaujalo jen to, že nahrával data do 128K paměti v jednom bloku (prostě přestránkoval během nahrávání), to depackování během nahrávání jsem nezaregistroval, ale jak říkám, byly to zbytky loaderu.

  • Eye

    Jinak samozřejmě díky za připomenutí staré coderské disciplíny, která bohužel nutně od počátku devadesátek zanikla s nástupem disketovek – o dnešku už ani nemluvě. Některé legendární loadery jsem viděl až z tzx na WoS (Technician Ted), protože u nás pochopitelně kolovaly jen cracknuté verze (necracknuté tolikrát nešly kopírovat). Během nahrávání se daly dělat i šílené věci, viz The Israeli Team Demo: http://www.worldofspectrum.org/infoseekid.cgi?id=0007491 Ale vlastně máme i pár let starý případ: http://www.pouet.net/prod.php?which=53846

    • Tak zrovna Technician Teda (aka Chip Factory) jsem měl v té originální verzi s počítadlem času a chodícími panáčky. BTW, legenda praví, že na konci nahrávání byla pozice nožiček jednotlivých panáčků použita k vytvoření dekódovacího klíče pro samotný program…

      • Eye

        Aha, tak jestli to šlo kopírovat, mohlo se to objevit i v originální verzi. Ta legenda se dá ověřit, ale bylo by to rafinované.

  • Zdeněk Starý

    Když jsem poprvé viděl loader u hry Joe Blade II, nevěřil jsem. Během nahrávání se dá hrát jednoduchá verze Pac Mana.
    http://www.worldofspectrum.org/infoseekid.cgi?id=0002623

  • Softhouse

    Skvelý článok. Loaderom som sa nikdy veľmi nevenoval,hoci koncepcia mi bola celkom jasná. Opäť som si rozšíril obzor 🙂

    • Softhouse? TEN Softhouse Peter Turányi? 🙂 Díky za všechno, hlavně za literaturu…

      • edo_nick

        🙂 Niet zač. Bolo mi cťou.