Online assembler pro osmibity

Jo, možná to je bláznivý nápad, ale mě baví. Představte si webové IDE, které obsahuje editor a assembler pro staré osmibity (8080, Z80, 6502 a možná i další). K tomu časem i emulátor… Už jsem o tom psal, tak teď přináším čerstvé zprávy z fronty…

Ne že by neexistovalo. Existuje online assembler pro Z80 se jménem ORG. Koukal jsem se, a zvolili řešení, které jsem já opustil – tedy editor v prohlížeči, překladač na serveru. Toto řešení má několik nevýhod.

Zaprvé: Nefunguje, když jste offline. Moje řešení „čistě client side“ je proti tomuto odolné (lépe řečeno může být, pomocí AppCache). Zadruhé je neskutečný vopich řešit případný include zdrojových souborů. Jediné schůdné řešení je, že všechny soubory máte na serveru ve svém účtu (tedy musíte mít účet) a webový prohlížeč funguje jen jako editor souborů na serveru.

Proto jsem zvolil řešení čistě JavaScriptové.

Uživatelské rozhraní jsem postavil na komponentách jQuery UI a na editoru CodeMirror, který je dostatečně jednoduchý a má i syntax highlighter pro Z80. Moc jsem nečaroval, zvolil jsem přístup „single page aplikace“.

Pro ukládání souborů využívám local storage. Nad ní jsem si napsal jednoduchou správu souborů. Ta umožňuje i pracvovat s adresáři, ale to nevyužívám. Pro mé potřeby (tedy zdrojové kódy pro osmibity) je doporučených 5MB prostoru per site dostatečných. Trochu prostoru ušetřím i tím, že soubory před uložením komprimuju algoritmem LZ a při čtení dekomprimuju, to vše zcela transparentně. Pro zvýšení komfortu má tato knihovna i možnost celý souborový systém zazálohovat a obnovit, a to i ve formátu ZIP. Můžete si tedy všechny soubory z prohlížeče stáhnout v jednom ZIPu. Kromě toho knihovna umožňuje i „připojit“ vzdálený souborový systém, což se bude hodit – až bude hotové ukládání souborů na server, budete si moct nalinkovat knihovnu, kterou zveřejnil někdo jiný.

Jádrem systému je dvouprůchodový assembler. Má modulární konstrukci, takže upravit ho pro jiný osmibit není problém.

Assembler je totiž syntakticky velmi jednoduchý: co řádek, to jedna instrukce. Každý řádek má navíc jasný formát:

Na řádku se tedy může vyskytovat samostatné návěští, instrukce i s parametrama, poznámka, nebo libovolná kombinace těchto tří položek. Staré assemblery měly striktní pravidla, např. že na prvním sloupci je vždy návěští; pokud řádek obsahuje instrukci, musí začínat až na druhém (či vyšším) sloupci. Tohle pravidlo jsem nakonec nezahrnul, můžete si tedy instrukce mastit od začátku řádku, ale doporučuju, abyste to nedělali. Staré assemblery taky vyžadovaly dvojtečky za jménem návěští. S výjimkou symbolické instrukce EQU, tam dvojtečka nebyla (ono to logiku mělo, EQU je přiřazení a to před ním není technicky vzato návěšť, ale pojmenování konstanty). Vyřešil jsem to šalamounsky: návěští nemusí mít dvojtečku.

Nojo, ale jak třeba vyřešit to, že assembler 8080 používal symbolické instrukce DB, DW, DS, zatímco Z80 používal pro totéž DEFB, DEFW, DEFM? Povolil jsem obě varianty s tím, že jsou to aliasy. Někdo používal ORG, jiný .ORG (s tečkou). Tady jsem taky nechal obě varianty ekvivalentní.

Oproti prehistorickým osmibitovým assemblerům jsem přidal include a makra. Jsou standardem u novějších assemblerů, tak proč je nemít, že?

Assembler vezme zdrojový kód a převede si ho interně na pole objektů. Na počátku je v objektech jen surový řádek. Pak následuje normalizace (odstranění mezer, vyhození prázdných řádků apod.), zpracování preprocesorem (řeší IF, makra, vkládání souborů a podobné) a parsování řádků. Parser rozdělí řádek na výše zmíněné části (návěští, instrukce, parametry a poznámka). Parser se postará i o rozdělení parametrů do pole (pokud je jich víc). V téhle fázi už se assembler dotazuje modulu pro konkrétní procesor, jestli danou instrukci zná.

Po téhle úvodní přípravě už může proběhnout první průchod překladu. V téhle fázi se bere instrukce po instrukci a převádí se na strojový kód. Je jasné, že některé hodnoty v tuto chvíli nejsou ještě známé, ale to nevadí – překladač si na ně nechá dostatek volného místa. Díky tomu už je jasné, kolik která instrukce zabere bajtů, takže lze postupně přiřazovat návěštím jejich hodnoty.

Druhý průchod pak jen prochází místa, kde je potřeba doplnit hodnotu nějakého výrazu. Teď už to není problém, protože všechny adresy známe z prvního průchodu. Pokud ji neznáme, je to chyba (a je na místě zařvat a skončit s překladem). Když se nic takového nestane, máme přeloženo. V paměti je teď pole objektů, kde u každého řádku přibyla spousta informací – od rozparsovaných částí původního řádku přes aktuální adresu, kde se daná instrukce bude vyskytovat, až po konkrétní bajty strojového kódu.

Mám tedy přeloženo, co dál? Zatím mám hotové generování .hex souborů a listingu. Postupně sem přidělám možnost rovnou si zkusit kód spustit v emulátoru, ale to je zatím v plánech.

Moduly pro jednotlivé procesory mají jen jednu jedinou funkci: dostanou instrukci a parametry. Buď ohlásí, že instrukci neznají, nebo vrátí strojový kód.

Jako první jsem napsal modul pro I8080/8085 včetně nedokumentovaných instrukcí. Tenhle assembler je jednoduchý v tom, že symbolické označení už v sobě nese veškeré informace o adresovacích režimech a potřebných parametrech. MOV má vždy dva registry, MVI má registr a konstantu a tak dál. Tam stačí z instrukce usoudit na „typ parametrů“, ty pak vyhodnotit a je hotovo.

Jako druhý jsem napsal modul pro 6502. Jeho instrukce nejsou takhle jednoznačné a každá může mít několik různých adresovacích módů. Naštěstí se z tvaru parametrů dá usoudit na to, který z nich to bude. Takže mám tabulku všech názvů instrukcí, kde je u každé uvedeno, jestli pro daný adresovací mód existuje (a pokud ano, tak jaký má kód). Zase jednoduché.

Teď píšu modul pro Z80. To je takový mix nejhorších vlastností obou předchozích (z hlediska assembleru). Třeba taková instrukce LD – mnoho různých adresních módů. Ne že by to bylo nemožné, ale je docela problém nalézt nějaký intuitivní a strojově snadný způsob, jak ten chaos popsat. Paradoxně je snazší popsat ukrutné (a nedokumentované) instrukce typu BIT 6, (IX+5) než LD… Ale nevzdávám to.

Líbil se vám článek? Podpořte autora na Patreonu

6 Comments

Got Something To Say:

Vaše emailová adresa nebude zveřejněna.

To je tak skvelé, že už sa neviem dočkať betatestu 😉
Ešte by sa hodilo implementovať špeciálnu makro-pseudo-inštrukciu ‚REPT n‘, tá sa hodí pri „lopatovaní“ pomocou ldi/ldd, alebo pri rozpise rutiny bez cyklov. Taktiež BINCLUDE súboru či url by nebolo odveci 🙂

    REPT mám v plánu taky, a nad BINCLUDE (INCBIN) přemýšlím. Buď ho dám takhle, nebo dám k dispozici možnost „vložit binární soubor do zdrojáku jako sérii DB“. Pravděpodobně půjdu druhou cestou, protože nakonec nějakou sadu nástrojů budu potřebovat (převod .hex na bin, generování .sna či .tap apod.), tak proč ne „převodník souboru na sérii DB“? BINCLUDE by s sebou přineslo nutnost řešit upload binárních souborů, ale zase uznávám, že by to nadělalo menší nepořádek ve zdrojácích. Jo a BTW – MON85 to při testu překládá bez problémů na první dobrou, jupí! 🙂

Lze sledovat vývoj kódu někde veřejně, třeba na Githubu? Třeba by se někdo přidal.

    Mám to zatím v private repo na Bitbucketu, je to alfa. Chci to dotáhnout do nějakého funkčního stádia a pak to otevřu. Některé části (local filesystem třeba) bych mohl dát ven dřív, assembler až poté, co ho zdokumentuju. Ale spíš než „open source“ bych to viděl na model „source available“ 🙂

banner

Copyright © 2017. Powered by WordPress & Romangie Theme.

banner