Je to zvláštne, nikdy som nezvykol písať takéto veci, ale asi ma to fakt baví alebo čo, lebo ináč si to neviem vysvetliť. Odniekiaľ sa proste berie pocit, že sa treba podeliť o skúsenosti…
Poďme teda na to.
Ako som v predošlom článku písal, pracujem na gamese roxblox. Nie je to o tom, že by som nad tym strávil nejako veľa času, ale postupne objavujem rôzne nové problémy, ktoré je potrebné riešiť. Zozačiatku to išlo super, ale čím ďalej, tým zložitejšie veci sa vyskytli a použiie Cčka na ich riešenie nie je vždy jednoduchšie ako použitie ASM (teda aspoň pre mňa).
Ono totiž CC65 štandardne všetky premenné, ktoré sa v programe používajú, ukladá do svojho softwarového stacku. To znamená, že aj jednoduchý programík na presun pár bytov z lokácie A na lokáciu B je riešený MRTE komplikovane.
Príklad z praxe… v hre mám kamene, ktoré majú rozmer 3×3 znaky. Tieto potrebujem vykreslovať do vramky. Nekreslím to priamo, ale mám k dispozícii vrambuffer, kde vykreslím čo treba a keď je to hotovo, len to skopírujem do vram všetko naraz. Toto kopírovanie je strašne pomalé, ukážem prečo… Zoberme si rutinu na kopčenie 3×3 grafiky do vrambuffera. V Cčku som to napísal takto:
Ako to funguje? Jednoducho! V cykle sa presúva obsah poľa tiles do vrambuffera po riadkoch, tzn. keď príde na 4. a 7. prvok, posunie sa o riadok nižšie. Optimálnejšie riešenie ma nenapadlo. Mimochodom, skúšal som skrátiť cyklus na 3 kroky (riadky) a zapísať za sebou 3 priradenia znakov v riadku, ale toto sa do ASM kódu premietlo naozaj doslovne, proste 3x po sebe ten istý kód.
Ok pozrime teraz na to, ako sa Cčkový kód preložil do ASM (bez optimalizácií):
Takýto nechutne dlhý kód presúva 9 bytov z jedného miesta na druhé!!!
Ono by sa mohlo zdať, že sa na to dá vyprdnúť: “veď je to ASM, aj tak to bude rýchle…” – nebude! Naozaj nie, mám to odskúšané 😉
Tu mi žiadna built-in optimalizácia nepomôže. Takéto kritické rutiny treba spraviť v ASM ručne. Ukážem ako na to.
V zásade ako prvý krok treba v configu (atari.cfg) skrátiť časť MEMORY ZP(zero page), aby sa dalo použiť nepriame adresovanie cez 0.stránku bez toho aby som sa musel obávať či si môj ASM kód s preloženým CC65 kódom nebudú vzájomne prepisovať údaje. Skrátil som teda ten size zo $7E na $6E, tým som si “alokoval” 16 bytov 0.stránky pre ASM subrutiny, to je viac než dosť. Zmenený riadok configu vyzerá takto:
ZP: start = $0082, size = $006E, type = rw, define = yes;
$82 + $6E = $F0 , takže od $F0 po $FF je môj priestor.
Rutinu napíšem do roxblox_data.asx z ktorej si labels prenášam do C (viď článok). Vstupné premenné vložím rovno do 0.stránky. Cčkový kód bude vyzerať nasledovne:
Dôložité je povedať kompilátoru aby neoptimalizoval inline assembler, pretože v prípade optimalizácie s využitím inline funkcií by to nechodilo. V úvode funkcie presúvam vstupné parametre do 0.stránky, ktorú mám zadefinovanú takto:
Je tu vidieť značný rozdiel v dĺžke kódu, ale hlavne v rýchlosti vykonania.
Takto vyzerá skompilované nové vnútro funkcie C:
Akosi nechápem, čo tam hľadá to ldx #$00, ale tak čo už, ja som to nepísal :D. Čo sa týka optimalizácie, úplne NEDOPORUČUJEM používať -Oi (oprimalizácia na inline funkcie), strašne to vie sprasiť môj kód a aj taká jednoduchá funkcia, ako je na prvom obrázku v tomto článku prestane fungovať. Optimalizáciu registrov som vyskúšal a zatiaľ som nenarazil na problém. Táto optimalizácia sa púšťa kľúčom -Or a dokážne naozaj viditeľne skrátiť výsledný kód oproti neoptimalizovanej kompilácii.
Pre úplnosť takto vyzerá kód s optimalizáciou -Or:
Dva nepotrebné výskyty ldx #$00 zmizli, jeden ostal.
…nikto nie je dokonalý