V clanku jsou uvedeny konstanty 0×00010000 a 0×5800. To je trochu matouci, protoze prvni je binarni a druha hexadecimalni.
Pasmo rozpozna 0x5800 jako hexadecimalni hodnotu, nejen $5800 nebo #5800.
Binarni zapis je s predponou %, jak je uvedeno v kodu.
Z80 je trosku zvlastni procesor a spousta veci jde delat ruznymi cestami.
org $8000
start:
ld a,%00010000
ld ($5800),a
ret
Jde zapsat se stejnou delkou kodu (2+3+1=6) i takty (7+13+10=30) jako
org $8000
start:
ld hl,$5800
ld (hl),%00010000
ret
3+2+1 = 6 bajtu
10+10+10 = 30 taktu
Jen se pouzije jiny registr. Tohle dokaze byt poradna vyzva uz od par instrukci. Kdyz se budete snazit napsat neco jen trosku mene trivialniho, tak na prvni pohled nebude zrejme, ktera kombinace/varianta je lepsi.
Staci se podivat na vysledky Z80 size programming chalenge, kde i ostrileni programatori se nedostanou na optimalnich 15 bajtu.
http://www.retroprogramming.com/2014/12/z80-size-programming-challenge-1.html
A pokud se pokusite pouzit nejaky prekladac, napr. pro C tak vysledek bude opravdu strasny. Dones neexistuje poradny prekladac u ktereho si nebudete trhat vlasy pri pohledu na vysledny kod.
Z80 neumi efektivne pracovat s parametry ulozenymi na zasobnik, takze to uplne diskvalifikuje C. Pravdepodobne pouzije instrukce typu LD E,(IX+n), ktera ale zabira 3 bajty (jeden pro prefix, dalsi instrukce a treti hodnota n) a trva 19 taktu. Oproti instrukci LD E,(HL), ktera zabira jeden bajt a trva 7 taktu. Takze pri vhodne zvolenem algoritmu, kdy se budeme snazit cist data sekvencne za sebou a pouzijeme k tomu instrukci INC HL (1 bajt a 6 taktu) tak se dostaneme na 2 bajty a 13 taktu.
Ale vetsina prekladacu je jeste horsich budou se snazit neustale ukladat promenne do pameti a opakovane nacitat do registru. Proste bude zapominat co se pred chvili drzel v registrech, protoze uz dela jinou cast programu a nepredava si co si drzi v registrech, zda to bude muset uklidit nebo to muze naopak vyuzit.
Staci se podivat co za hruzu leze z jednoducheho programku co testuje zda retezec je pangram, kdy se pouziva 32 bitove pole pro pouzite znaky.
https://github.com/DW0RKiN/M4_FORTH/blob/master/Benchmark/Pangram.c
https://github.com/DW0RKiN/M4_FORTH/blob/master/Benchmark/Pangram.lst
Misto jednobajtove instrukce o sedmi taktech
or 32
napise sdcc
86 ;Pangram.c:10: c |= 32; // uppercase
004E DD 4E F8 [19] 87 ld c, -8 (ix)
0051 CB E9 [ 8] 88 set 5, c
0053 79 [ 4] 89 ld a, c
Preklad kodu zapsanemu ve FORTHu je nasobne rychlejsi a i ten byva stale 2x horsi nez co napisete primo v assembleru. Protoze Z80 se vlastne nehodi ani na ten FORTH. Chybi mu kratka a rychla instrukce ex SP, HL. Ktera by mu umoznila "zdvojit" zasobnik. Takze si musi druhy zasobnik emulovat pres to inc/dec a ld (HL),register pripadne ld register,(HL). Popripade jinak, protoze to zase existuje vice reseni.
PS: K tomu prekladaci "pasmo" bych podotknul, ze ma nespravne danou prioritu u unarniho znamenka minus. Ma ho misto nejvyssi nejnizsi, takze
ld HL, -100+500
je
ld HL,-600
Ke vsemu uklada pres EQU hodnoty pokazde jako unsigned word (16 bitu, vic neumi). Takze muze byt docela vyzva zapsat vyraz tak aby napriklad dokazal spravne porovnat >,<,>=,<= mezi signed hodnotami.
Napriklad potrebujete zapsat:
ld HL, m <= n
aby se to zkompilovalo jako
ld HL, 0x0000
nebo
ld HL, 0xFFFF
nebo chcete aby to spravne nasobilo nebo delilo signed hodnoty jako
ld HL,m/n
popripade zbytek
ld HL,m % n
Napsat floor divison uz je dost narocne jako vyraz pomoci toho co Pasmo umi.
Kde za m a n si dosadte pres makro nejakou konstantu 16 bitovou signed.
Popripade 32 bit/16 bit, pokud je vysledek 16 bitovy tak s omezenim Pasma na 16 bitu to zapsat sice jde, ale je to na celou obrazovku.
ld HL, 32bit_m/16bit_n
PPS: FUSE emulator ma sice debugger, ale ten je prakticky nepouzitelny, je tezke se vubec dostat k informacim jake zkratky muzete pouzit. Jak zmenit hodnoty registru atd. Ale na linuxu je bohuzel to nejlepsi co muzete mit pokud nechcete pouzivat wine.
Budete pouzivat jen:
breakpoint 0x6000
br 0x8000
ignore id count
ig id count
delete id
del id
Popripadne dvouklik na zasobnik vytvori breakpoint na jedno pouziti na adrese co v nem lezi.
A uz se nedostanete k:
Neco se snazi cist z adresy 0x6000:
breakpoint read $6000
br re $6000
Neco se snazi zapsat na adresu 0x6000:
breakpoint write $6000
br w $6000
break 0xa000 if z80:bc >= 0x8000 && z80:de < 0x1234
set z80:a 0xff
set z80:de 0x1234
7. 2. 2023, 03:49 editováno autorem komentáře
Díky za doplnění. Tu konstantu jsem v textu opravil na binární, aby to nemátlo.
A to, že je programování na Z80 výzva namísto tupého zápisu instrukcí (jako na RISCech, které mám jinak rád :-) to se mi právě líbí. I 6502 byla výzva, pokud se dělalo cokoli se šestnáctibitovými hodnotami, hlavně se signed. Z cc65 (céčka) taky lezou hrozné věci, protože 6502 neumí zásobníkové rámce (tady je na tom Z80 líp).
Jo, je to výzva :) Nicméně na moderních “Z80”, co tak koukám, na to vývojáři kašlou, například eZ80 je superskalární a běží na 50 MHz, takže se vesele používá C a fakt, že výsledek je řádově pomalejší, nikdo moc neřeší.
Staré dobré osmibity, to byl jiný level. Ach ta nostalgie, člověk by i slzu zamáčkl.
To je teda neuvěřitelnej doomsaying :) Je sice pravda, že kompiler typicky přistupuje k proměnným na zásobníku pomocí LD r, (ii+NN), což je pomalejší než indexace přes HL, ale na druhou stranu ne vždy se podaří data seřadit tak, aby opravdu stačilo hejbat indexem jen jednou. A HL je jediným plnohodnotným 16-bitovym akumulátorem který máte a někdy je prostě nemožné používat ho jen jako index, takže se musí někam uložit, pak zase obnovit atd.
V praxi se ruční alokace registrů vyplatí jen u časově kritických části programů, v okamžiku kdy řešíte nějakou logiku a potřebujete struktury bude i ručně psaný assembler vypadat tak, že se podprogramu ("metodě") předá pointer na strukturu v indexovém registru a na členy se přistupuje přes index.
A časově kritickou části je u Spectra téměř vždy výstup na obrazovku a to je přesně ta věc, na kterou třeba použijete už hotovou knihovnu, která je napsaná v asm.
Na 3.5MHz procesoru, kde nejkratší instrukce trvá 4T a nejdelší přes 20T a těch T mám za 1/50s jen 70800 je skoro každý kód časově kritický.
Proto se např. obrazovka maže pomocí rozvinuté smyčky instrukcí push místo ldir, proto je modifikace operandů instrukcí v podprogramech volajícím kódem docela běžná praxe, stejně tak nejrůznější přehazování hodnot mezi 8 bit registry ld R,R (místo pushování na zásobník) atd...
Ano, na ZXS se dá programovat i jinak než v ASM, ale jenom s ASM lze dosáhnout nejlepších výsledků.
Holt jiný svět. ZXS není PC, kde se časem i pamětí plýtvá.
Když jsme u toho plýtvání časem, jaká je asi produktivita někoho, kdo píše v C a kdo píše v asm ?
Jen potvrzuješ moje slova, že úzké hrdlo bude výstup na displej a na to se použije existující knihovna.
Mimochodem, memfill pomocí zásobníku je signaturou her napsaných až tak po roce 85. Knightlore si maže backbuffer prostým LD (HL),A a Manic Miner kopíruje backbuffer jednoduše LDIRem. Kupodivu to funguje.
Hobby vs práce
U hobby si můžu dovolit být méně efektivní, abych dosáhl výsledku, který mě bude víc těšit. (a třeba slepit Karlštejn ze zápalek... ukrutně neefektivní :))
Na 8bit Z80 s 16bit adresním prostorem je psaní v assembleru ještě docela dobře zvládnutelné, na modernějších CPU samozřejmě čím dál méně a jen když není vyhnutí.
Na osmibity existuje za ta desetiletí mnoho ustálených postupů jak věci dělat efektivně i v tom assembleru - zvuk, kreslit sprity, hýbat obrazem (scroll), číst klávesnici, číst soubory... leccos lze recyklovat a použít jinde.
Nepotvrzuji tvoje slova (zcela). Jen se shodujeme v tom, že grafické operace musí běhat rychle. Já k tomu ale říkám NEJENOM grafické operace. Každý ušetřený takt (nebo bajt) je dobrý.
Jasně, programátorské triky se vyvíjejí... ldir na mazání a přenosy se samozřejmě používat nepřestal (resp. ldi v částečně rozvinuté smyčce), je to jen o prioritách rychlost vs obsazená paměť.
Mně jde o to, že se tu všichni tváří jak počítají cykly v každé rutině. A to je prostě blbost.
Dodnes vychází na Spectrum něco kolem stovky titulů ročně, jestli ne víc a část z nich je napsaná v céčku a každý se může podívat jak to vypadá, když se vezme sdcc nebo z88dk a přihodí se knihovna pro práci s grafikou. A ten výsledek není špatný.
A pak jsou tu lidi, kteří viděli naposledy hisoft c compiler a vyprávěj tu moudra, jak JEDINĚ assembler.
A to říkám z pozice někoho, kdo po roce 2000 pár titulů pro ZX vydal (byť nic světoborného) a psal to vždy v čistém assembleru.