6502 – čtení kláves a pořádek v kódu

Dnes nás čeká druhý krok při oživování počítačů. Už nás pozdravil, tak ještě aby nás poslouchal. A až bude poslouchat, tak si řekneme něco o štábní kultuře.

Když jsme začali experimentovat se SBC6502, tak jsem popisoval obvod ACIA. V počítači SBC6502 je k tomuto obvodu připojen terminál. Terminál ale není jen obrazovka, která vypisuje znaky. Je tam i klávesnice, kterou se s počítačem komunikuje.

Klávesnice je připojená úplně stejně jako obrazovka, jen komunikace probíhá opačným směrem, tj. do počítače. Když se podíváte do popisu obvodu ACIA, zjistíte dvě užitečné informace:

  • Přijatý znak si můžete přečíst z datového registru, pokud tedy byl nějaký znak přijatý.
  • O tom, jestli byl nějaký znak už přijatý, informuje bit 0 stavového registru.

Takže teoreticky by mělo stačit kontrolovat bit 0 stavového registru, a pokud bude nastavený, tak to znamená, že přišel nějaký znak z klávesnice. V takovém případě ho můžeme přečíst z datového registru.

Co s ním uděláme? Hmmm… co ho třeba zase vypsat? To by šlo:

Použil jsem první příklad s vypisováním znaků a dopsal právě kontrolu klávesnice a výpis znaků. Zkuste si tenhle příklad přeložit v ASM80.com a spustit v emulátoru SBC6502. Program vypíše hlášku a po ní čeká na stisknutí klávesy. Jakmile je nějaká stisknuta, pošle terminál její ASCII kód po sériovém rozhraní do počítače, tam jej zpracuje procesor a vypíše zpátky.

SBC6502 nepoužívá přerušení, které by systém upozornilo na to, že přišel znak a je možno ho zpracovat. Proto procesor musí pravidelně kontrolovat, jestli se už něco neděje, a během té kontroly nedělá nic jiného. V nejjednodušších single task systémech je to přijatelné řešení.

Složitější systémy mohou použít přerušení, buď od obvodů klávesnice, které signalizují, že přišel znak, nebo třeba přerušení od časovače (ZX Spectrum takhle testovalo klávesnici každou padesátinu sekundy při systémovém přerušení). V obsluze přerušení pak načtou znak a uloží ho do nějakého bufferu k dalšímu zpracování.

Organizace kódu

Protože vy, čtenáři, snad všichni znáte nějaké vyšší jazyky, tak nemusím moc složitě představovat koncepty modulů a lokálních proměnných. Ano, i tyhle věci v assembleru máme, ale není to tak úplně prosté…

Moduly

No, říkejme tomu tak. Ve skutečnosti se jedná jen o jednoduchý INCLUDE „jméno“, který na to místo načte obsah externího souboru.

Některé staré assemblery vůbec žádný include neměly. Ono by to třeba u ZX Spectra 48 s páskou nebylo moc pohodlné. Čímž neříkám, že takové kompilery nebyly, třeba HiSoft C měl #include, jak se na céčko sluší a patří, a při překladu jste spustili magnetofon, kde byly soubory ke slinkování, překladač si je prošel, načetl, přeložil… Ano, bylo to tak děsivé, jak to zní.

Většina modernějších assemblerů include samozřejmě má, jen se liší jeho syntax. Některé assemblery používají .INC, některé INCLUDE, některé .INCLUDE, takže nezbývá než si přečíst manuál k tomu kterému kousku. Já ve svém překladači používám tvar .INCLUDE název souboru.

Řekněme, že mi připadá jako dobrý nápad (a on to dobrý nápad je) přesunout tyhle rutiny pro výpis znaku a načtení znaku někam stranou, do nějaké společné (common) knihovny (library), kterou si důvtipně nazvu „comlib.a65“. V hlavním programu tak budu moci vesele tyhle rutiny používat, aniž by mi překážely ve zdrojáku, stačí jen, když je vhodně includuju.

Tím se nám zdroják rozštípnul na dva soubory: comlib.a65 (knihovna) a vlastní zdrojový kód.

Tenhle soubor pak elegantně načteme v hlavním programu:

Do comlib.a65 si klidně můžeme přihodit i rutinu PRIMM z minulé lekce. K tomu ale až na konci. Teď si musíme ukázat ještě jednu důležitou vlastnost assemblerů…

Assembler totiž sám o sobě nemá lokální jména. Jakmile jednou nadefinujete konstantu, návěští, něco, tak to je vidět v celém kódu. Což je docela problém, protože u složitějšího programu vám brzy dojde fantazie při pojmenovávání např. smyček. „LOOP1“, „LOOP2“, … to není moc elegantní.

Nemluvě o tom, že třeba použijete návěští „LOOP“ v nějaké knihovní funkci. V hlavním programu na to zapomenete (nebo o tom ani nevíte, protože tu knihovnu dělal někdo jiný), a překladač – logicky – zařve, že návěští bylo už použité. Co s tím?

Lokální návěští

V téhle situaci se hodí lokální návěští. Špatná zpráva je, že ne každý assembler je podporuje, a pokud ano, tak má svou konvenci, které bude pravděpodobně odlišná od všech ostatních konvencí všech ostatních assemblerů.

Některé mocnější assemblery zavádějí pseudokonstrukce „procedure“ a deklaraci „local“ apod., jiné se staví k problému z druhé strany a dovolují pro drobné smyčky a návěští používat jakási „pseudonávěští“ a odkazovat se na ně zápisem „skoč na předchozí pseudonávěští“, „skoč na následující pseudonávěští“, „skoč o dvě pseudonávěští zpátky“…

Já jsem v ASM80 zvolil cestu bloků. Blok začíná direktivou „.block“ a končí direktivou „.endblock“. Všechna návěští, co jsou v něm definována, jsou lokální. To znamená že v bloku na ně můžete odkazovat, mimo něj nejsou vidět. Pokud chcete, aby bylo návěští vidět i mimo blok, dejte před jeho název znak @ – ten se nestane součástí jména, jen říká, že toto návěští bude globální. „@SEROUT:“ říká „Definuj globální návěští se jménem SEROUT“.

Díky tomu můžu jako první řádek knihovny comlib napsat .block, na poslední .endblock, a vím, že pokud takovou knihovnu includuju, tak mi z ní nic „nevyteče“ ven, pokud explicitně neřeknu, co má být vidět zvenčí. Takže nová, šetrná verze comlib vypadá takto:

Díky uzavření, „zapouzdření“ zdrojáku můžu v hlavním programu použít klidně návěští SO_WAIT, a nedojde k chybě, protože to, které jsem použil v comlib.a65, bude vidět pouze v comlib.a65, nikde jinde. Stejně tak názvy jako ACIAData, ACIAStatus apod. Jediné, co bude vidět zvenčí, je SERIN, SEROUT a ACIAINIT.

Vylepšený PRIMM

Minule jsem tu ukazoval funkci PRIMM, která pomocí jednoduchého triku dokáže vytisknout konstantní řetězec znaků, zapsaný přímo v kódu, přímo za voláním JSR PRIMM. Kód funguje většinou dobře, ale jsou dva stavy, ve kterých fatálně selže.

První případ je, že je počet znaků větší než 255. Registr Y u posledního znaku „přeteče“ do nuly, rutina tím končí, ale bohužel se tím návratová adresa ocitne někde uprostřed textu a výsledek bude katastrofální. Řešení existuje – nechat přetočit Y, ale přitom si uloženou adresu zvýšit o 0100h, jak navrhnul v komentářích Roman Bórik.

Druhý problém nastane v případě, že ukazatel zásobníku SP je nízko. V takovém případě přeteče přes nulu, dostane se opět do vysokých hodnot FDh-FFh, a v takovém případě jednoduchý přepočet pomocí vzorce „SP + 104h“ selže, protože se ocitneme mimo zásobník, na adresách 0200h a vyšších, a nejen že načteme nesmysly, ale taky nesmysly uložíme. I tuto situaci by bylo možné ošetřit, ale v tomto případě to není asi úplně potřeba, pokud inicializujeme zásobník standardně, tj. na hodnotu FFh. Pokud se totiž za takové situace stane, že se zásobník protočí přes nulu, tak máme zásadnější problém někde jinde…

Líbil se vám článek? Podpořte autora na Patreonu
Příspěvek byl publikován v rubrice 6502, Obecně. Můžete si uložit jeho odkaz mezi své oblíbené záložky.

3 komentáře u 6502 – čtení kláves a pořádek v kódu

  1. Jan napsal:

    Proč se mi na stroják.cz neukazují obsahy tech velkých oken s listingy programů? (Windows 10). Je pro to potřeba něco doinstalovat?

  2. Ondrej Hlouzek napsal:

    Neskutcne dobry tutorial na programovani v assembleru (pardon vlastne v JSA). Klobouk dolu.

Napsat komentář

Vaše emailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *