Po delsi dobe sem zase neco napisi. Pridaval jsem podporu 32 bitovych cisel a dostal se do casti kdy pracuji s konstantama. Proste kdyz vlozim cislo a s nim a dalsim pro prekladac uz neznamym cislem v zasobniku provedu matematickou operaci. Kupodivu nejnarocnejsi je operace zjisteni zda jsou cisla shodne nebo ruzne, protoze u vseho ostatniho to bud spocteme a nebo nam pomuze priznak carry. Tady ale ne, musime se spolehnout na priznak nuly a ten se neda prakticky prenaset mezi bajty. S tim jak neni Z80 ortgonalni byla tohle docela velka vyzva a zaroven zabava.
Protoze u techto operaci se mnohokrat opakuje kod u ruznych slov, nebo kombinaci slov, jako je:
123456789 =
123456789 = if
123456789 = while
2dup 123456789 =
2dup 123456789 = if
2dup 123456789 = while
123456789 <>
123456789 <> if
123456789 <> while
2dup 123456789 <>
2dup 123456789 <> if
2dup 123456789 <> while
jsem se rozhodl ze se pokusim udelat nejakou makro funkci, ktera by zakladni shodny kod sama vygenerova, a zbytek co se lisi se obalil pak zvlast.
M4 Forth uklada 32 bitove cislo v zasobniku tak, ze HL obsahuje nizsich 16 bajtu a DE obsahuje vyssich 16 bajtu. Coz je mimochodem presne naopak nez je v standartu, ale pro nektere funkce to bylo vyhodnejsi tak jsem to tak zvolil. A Z80 nema podporu pro 32 bitove cisla takze endianitu si stejne musite resit softwarove.
A protoze podporuji operace typu "2dup constant = if" tak si v te makro funkci nesmim znicit hodnoty v registru. Vcetne HL. Takze jsem v podstate odkazany na akumulator A, protoze to skoro vzdy vychazi rychleji nez schovavat HL na zasobnik.
Zakladni kod vypada nejak takto
Kód:
../check_word.sh '_2DUP_PUSHDOT_DNE_IF(0x45352515)'
;[21:75/23,41,59,75] 2dup 0x45352515 D<> if ( d1 -- d1 ) 0x45352515 <> DEHL
ld A, 0x15 ; 2:7 2dup 0x45352515 D<> if
cp L ; 1:4 2dup 0x45352515 D<> if x[1] = 0x15
jr nz, $+18 ; 2:7/12 2dup 0x45352515 D<> if
ld A, 0x25 ; 2:7 2dup 0x45352515 D<> if
cp H ; 1:4 2dup 0x45352515 D<> if x[2] = 0x25
jr nz, $+13 ; 2:7/12 2dup 0x45352515 D<> if
ld A, 0x35 ; 2:7 2dup 0x45352515 D<> if
cp E ; 1:4 2dup 0x45352515 D<> if x[3] = 0x35
jr nz, $+8 ; 2:7/12 2dup 0x45352515 D<> if
ld A, 0x45 ; 2:7 2dup 0x45352515 D<> if
xor D ; 1:4 2dup 0x45352515 D<> if x[4] = 0x45
jp z, else101 ; 3:10 2dup 0x45352515 D<> if
;[21:75]
Pokud jsou bajty shodne tak kod vypada nejak takto
Kód:
../check_word.sh '_2DUP_PUSHDOT_DNE_IF(0x33333333)'
;[15:54/20,31,42,54] 2dup 0x33333333 D<> if ( d1 -- d1 ) 0x33333333 <> DEHL
ld A, L ; 1:4 2dup 0x33333333 D<> if the beginning of identical values
cp H ; 1:4 2dup 0x33333333 D<> if x[1] = x[2]
jr nz, $+13 ; 2:7/12 2dup 0x33333333 D<> if
cp D ; 1:4 2dup 0x33333333 D<> if x[2] = x[3] continuation of identical values
jr nz, $+10 ; 2:7/12 2dup 0x33333333 D<> if
cp E ; 1:4 2dup 0x33333333 D<> if x[3] = x[4] continuation of identical values
jr nz, $+7 ; 2:7/12 2dup 0x33333333 D<> if
xor 0x33 ; 2:7 2dup 0x33333333 D<> if x[4] = 0x33 termination of identical values
jp z, else101 ; 3:10 2dup 0x33333333 D<> if
;[15:54]
Pokud cislo obsahuje nejake nulove bajty tak je to stale snadne, proste se jen daji nakonec, a predchozi testovani musi vynulovat akumulator.
Kód:
../check_word.sh '_2DUP_PUSHDOT_DNE_IF(0x45002500)'
;[13:47/23,47,47,47] 2dup 0x45002500 D<> if ( d1 -- d1 ) 0x45002500 <> DEHL
ld A, 0x25 ; 2:7 2dup 0x45002500 D<> if
cp H ; 1:4 2dup 0x45002500 D<> if x[1] = 0x25
jr nz, $+10 ; 2:7/12 2dup 0x45002500 D<> if
ld A, 0x45 ; 2:7 2dup 0x45002500 D<> if
xor D ; 1:4 2dup 0x45002500 D<> if x[2] = 0x45
or E ; 1:4 2dup 0x45002500 D<> if x[3] = 0
or L ; 1:4 2dup 0x45002500 D<> if x[4] = 0
jp z, else101 ; 3:10 2dup 0x45002500 D<> if
;[13:47]
Vylepseni oproti 16 bitove verzi co jsem uz resil je v tom, ze me doslo ze ty jednotlive bajty mohu chapat jako nejakou posloupnost, kdy mezi nema mohou existovat i nejake vztahy.
Napriklad kazde dalsi cislo je dvounasobkem predchoziho, zde konkretne vidite zradnost preteceni, kdy polovina cisla 0x2A muze byt jak 0x15 tak 0x95. V tomto kodu pouzivam jednoduse pro overeni uz overene hodnoty.
Kód:
../check_word.sh '_2DUP_PUSHDOT_DNE_IF(0xA8542A95)'
;[18:66/23,38,53,66] 2dup 0xA8542A95 D<> if ( d1 -- d1 ) 0xA8542A95 <> DEHL
ld A, 0x95 ; 2:7 2dup 0xA8542A95 D<> if
cp L ; 1:4 2dup 0xA8542A95 D<> if x[1] = 0x95
jr nz, $+15 ; 2:7/12 2dup 0xA8542A95 D<> if
add A, L ; 1:4 2dup 0xA8542A95 D<> if
cp H ; 1:4 2dup 0xA8542A95 D<> if x[2] = x[1] + x[1]
jr nz, $+11 ; 2:7/12 2dup 0xA8542A95 D<> if
add A, H ; 1:4 2dup 0xA8542A95 D<> if
cp E ; 1:4 2dup 0xA8542A95 D<> if x[3] = x[2] + x[2]
jr nz, $+7 ; 2:7/12 2dup 0xA8542A95 D<> if
add A, E ; 1:4 2dup 0xA8542A95 D<> if
xor D ; 1:4 2dup 0xA8542A95 D<> if x[4] = x[3] + x[3]
jp z, else101 ; 3:10 2dup 0xA8542A95 D<> if
;[18:66]
Nebo jiny matematicky vztah, nebo jejich kombinace
Kód:
../check_word.sh '_2DUP_PUSHDOT_DNE_IF(0x3f1f2010)'
;[18:66/23,38,53,66] 2dup 0x3f1f2010 D<> if ( d1 -- d1 ) 0x3F1F2010 <> DEHL
ld A, 0x10 ; 2:7 2dup 0x3f1f2010 D<> if
cp L ; 1:4 2dup 0x3f1f2010 D<> if x[1] = 0x10
jr nz, $+15 ; 2:7/12 2dup 0x3f1f2010 D<> if
add A, L ; 1:4 2dup 0x3f1f2010 D<> if
cp H ; 1:4 2dup 0x3f1f2010 D<> if x[2] = x[1] + x[1]
jr nz, $+11 ; 2:7/12 2dup 0x3f1f2010 D<> if
dec A ; 1:4 2dup 0x3f1f2010 D<> if
cp E ; 1:4 2dup 0x3f1f2010 D<> if x[3] = x[2] - 1
jr nz, $+7 ; 2:7/12 2dup 0x3f1f2010 D<> if
add A, H ; 1:4 2dup 0x3f1f2010 D<> if
xor D ; 1:4 2dup 0x3f1f2010 D<> if x[4] = x[3] + x[2]
jp z, else101 ; 3:10 2dup 0x3f1f2010 D<> if
;[18:66]
U bajtu s hodnotu 0xFF jsem prisel na to, ze ten vztah nemusim prevadet na nulu, ale na hodnotu 0xFF a misto OR pouzivat instrukci AND. Tohle je jedna z mala veci, kdy mohu spojit test bajtu bezpecne dohromady. Dalsim trikem ktery jsem objevil je, ze predchozi vztah, ktery jsem nuloval a pak pomoci "dec A" nastavoval na 0xFF mohu udelat jeste efektivnejsi kdyz misto
Kód:
ld A, 0x45
xor D
dec A
pouziji
Kód:
ld A, (0x45 ^ 0xFF)
xor D
Vysledny kod vypada podobne jak s nulama takto, jen o jeden bajt delsi pro ukoncujici instrukci "inc A". 0xFF bajty jsou vzdy na konci, popripade hned pred nulovymi bajty.
Kód:
../check_word.sh '_2DUP_PUSHDOT_DNE_IF(0x45FF25FF)'
;[14:51/23,51,51,51] 2dup 0x45FF25FF D<> if ( d1 -- d1 ) 0x45FF25FF <> DEHL
ld A, 0x25 ; 2:7 2dup 0x45FF25FF D<> if
cp H ; 1:4 2dup 0x45FF25FF D<> if x[1] = 0x25
jr nz, $+11 ; 2:7/12 2dup 0x45FF25FF D<> if
ld A, D ; 1:4 2dup 0x45FF25FF D<> if
xor 0xBA ; 2:7 2dup 0x45FF25FF D<> if x[2] = 0x45 = 0xFF ^ 0xBA
and E ; 1:4 2dup 0x45FF25FF D<> if x[3] = 0xFF
and L ; 1:4 2dup 0x45FF25FF D<> if x[4] = 0xFF
inc A ; 1:4 2dup 0x45FF25FF D<> if
jp z, else101 ; 3:10 2dup 0x45FF25FF D<> if
;[14:51]
Drobne optimalizace jsou pokud pred 0xFF je 0xFE
Kód:
./check_word.sh '_2DUP_PUSHDOT_DNE_IF(0xFEFF25FF)'
;[13:48/23,48,48,48] 2dup 0xFEFF25FF D<> if ( d1 -- d1 ) 0xFEFF25FF <> DEHL
ld A, 0x25 ; 2:7 2dup 0xFEFF25FF D<> if
cp H ; 1:4 2dup 0xFEFF25FF D<> if x[1] = 0x25
jr nz, $+10 ; 2:7/12 2dup 0xFEFF25FF D<> if
ld A, D ; 1:4 2dup 0xFEFF25FF D<> if
inc A ; 1:4 2dup 0xFEFF25FF D<> if x[2] + 1 = 0xFF
and E ; 1:4 2dup 0xFEFF25FF D<> if x[3] = 0xFF
and L ; 1:4 2dup 0xFEFF25FF D<> if x[4] = 0xFF
inc A ; 1:4 2dup 0xFEFF25FF D<> if
jp z, else101 ; 3:10 2dup 0xFEFF25FF D<> if
;[13:48]
Posledni odlisnou cislici jsou jednicky, ty mohou byt obcas rychlejsi kdyz se s nema zachazi odlisne. Zde jen podobne vylepseni jako u 254
Kód:
../check_word.sh '_2DUP_PUSHDOT_DNE_IF(0xFEFF01FF)'
;[12:45/20,45,45,45] 2dup 0xFEFF01FF D<> if ( d1 -- d1 ) 0xFEFF01FF <> DEHL
ld A, H ; 1:4 2dup 0xFEFF01FF D<> if
dec A ; 1:4 2dup 0xFEFF01FF D<> if x[1] = 1
jr nz, $+10 ; 2:7/12 2dup 0xFEFF01FF D<> if
ld A, D ; 1:4 2dup 0xFEFF01FF D<> if
inc A ; 1:4 2dup 0xFEFF01FF D<> if x[2] + 1 = 0xFF
and E ; 1:4 2dup 0xFEFF01FF D<> if x[3] = 0xFF
and L ; 1:4 2dup 0xFEFF01FF D<> if x[4] = 0xFF
inc A ; 1:4 2dup 0xFEFF01FF D<> if
jp z, else101 ; 3:10 2dup 0xFEFF01FF D<> if
;[12:45]
A tady je uz videt ze pomoci pouziti volneho registru C, dokazi spojit bajty v jeden test
Kód:
../check_word.sh '_2DUP_PUSHDOT_DNE_IF(0x00443333)'
;[15:54/20,34,54,54] 2dup 0x00443333 D<> if ( d1 -- d1 ) 0x00443333 <> DEHL
ld A, L ; 1:4 2dup 0x00443333 D<> if the beginning of identical values
cp H ; 1:4 2dup 0x00443333 D<> if x[1] = x[2]
jr nz, $+13 ; 2:7/12 2dup 0x00443333 D<> if
cp 0x33 ; 2:7 2dup 0x00443333 D<> if x[2] = 0x33 termination of identical values
jr nz, $+9 ; 2:7/12 2dup 0x00443333 D<> if
ld A, 0x44 ; 2:7 2dup 0x00443333 D<> if
xor E ; 1:4 2dup 0x00443333 D<> if x[3] = 0x44
or D ; 1:4 2dup 0x00443333 D<> if x[4] = 0
jp z, else101 ; 3:10 2dup 0x00443333 D<> if
;[15:54]
../check_word.sh '_2DUP_PUSHDOT_DNE_IF(0x00013333)'
;[13:48/20,48,48,48] 2dup 0x00013333 D<> if ( d1 -- d1 ) 0x00013333 <> DEHL
ld A, L ; 1:4 2dup 0x00013333 D<> if the beginning of identical values
cp H ; 1:4 2dup 0x00013333 D<> if x[1] = x[2]
jr nz, $+11 ; 2:7/12 2dup 0x00013333 D<> if
xor 0x33 ; 2:7 2dup 0x00013333 D<> if x[2] = 0x33 termination of identical values
ld C, E ; 1:4 2dup 0x00013333 D<> if
dec C ; 1:4 2dup 0x00013333 D<> if
or C ; 1:4 2dup 0x00013333 D<> if x[3] = 1
or D ; 1:4 2dup 0x00013333 D<> if x[4] = 0
jp z, else101 ; 3:10 2dup 0x00013333 D<> if
;[13:48]
U tech testu jsem dospel k zaveru, ze pokud jsou v nich vic jak jeden xor na test, tak je ten kod vadny. Musel jsem si to overit v excelu, kde jsem si zkratil cislo na 3 bity a dival se co to udela. Problem je ze kobinaci chyb u vice cisel bude test ukazovat obcas spatne vysledky.
Proste pokud mam v cisle jednicku a pokusim se to spojit tak ze
Kód:
...
inc A
xor A
dec A
...
Tak to pri soubehu chyb selze.
Zpetne pouzivani jeste neoverenych hodnot funguje jen u pripadu kdy mame cisla 4,3,2,1
a budou se testovat jako
Kód:
ld A,D
dec A
cp E x[3] = x[4]-1
jr nz,...
dec A
cp H x[2] = x[3]-1
jr nz,...
dec A
cp L x[1] = x[2]-1
jr nz,...
dec A x[4] = 4
Protoze tady nakonec opravdu tu x[4] nezavisle overime na nulu. Ale tento kod neni rychlejsi nez generovany
Kód:
../check_word.sh '_2DUP_PUSHDOT_DNE_IF(0x04030201)'
;[17:63/23,38,63,63] 2dup 0x04030201 D<> if ( d1 -- d1 ) 0x04030201 <> DEHL
ld A, 0x03 ; 2:7 2dup 0x04030201 D<> if
cp E ; 1:4 2dup 0x04030201 D<> if x[1] = 0x03
jr nz, $+14 ; 2:7/12 2dup 0x04030201 D<> if
dec A ; 1:4 2dup 0x04030201 D<> if
cp H ; 1:4 2dup 0x04030201 D<> if x[2] = x[1] - 1
jr nz, $+10 ; 2:7/12 2dup 0x04030201 D<> if
add A, H ; 1:4 2dup 0x04030201 D<> if
xor D ; 1:4 2dup 0x04030201 D<> if x[3] = x[2] + x[2]
ld C, L ; 1:4 2dup 0x04030201 D<> if
dec C ; 1:4 2dup 0x04030201 D<> if
or C ; 1:4 2dup 0x04030201 D<> if x[4] = 1
jp z, else101 ; 3:10 2dup 0x04030201 D<> if
;[17:63]
Puvodne jsem ten kod psal tak, ze jsem si tu posloupnost 4 cisel seradil a nepouzival odcitani, ale pak mi doslo ze cislo 0x3F201F10 ma kratsi zapis jako x, 2*x, 2*x-1, 2*x-1+2*x. Po case jsem dospel k reseni kdy delam variace te rady, jen ignoruji bajty 255 a 0 (nuly mam vnitrne jako 256, kvuli razeni).
Jak napsat v makru variace?
No rozepsal jsem si to na "papir" a je to snadne. Udelame vic maker. Mame cisla 1,2,3,4. Posledni jen vola generovani kodu (pokud je lepsi co mame tak ho nahradi) a pokud je cislo na pozici 3 mensi jak 255 tak ho prohodi s cislem na pozici 4 (a s nema prohodi i jmena registru). Pokud prohazoval tak znovu vola generovani kodu (pokud je lepsi co mame tak ho nahradi).
Dalsi makro vola posledni makro, pak pokud je cislo na pozici 3 mensi jak 255 tak to zameni s cislem na pozici 2 a pokud prohazoval tak znovu vola posledni makro a udela opet prohozeni 3 a 2 pozice a znovu vola posleni makro. Posledni makro totiz vzdy vymeni cislo na 2 pozici tak to pekne navazuje.
Prvni (main) makro nastavi pocatecni hodnoty kodu na nejhorsi a pak vola prostredni makro. Pokud je cislo na 4 pozici mensi nez 255 tak prohodi hodnotu na 4 pozici s hodnotou na 3 pozici a znovu vola prostredni makro, tohle jednou zopakuje a nakonec prohazuje cislo na pozici 4 s cislem na pozici 1 (takove preteceni misto cisla na 5. pozici) a vola naposledy prostredni makro.
Posledni makro generuje 2 kody.
Prostredni generuje az 6 kodu.
Prvni generuje az 24 kodu, coz je variace 4 hodnot kdy zalezi na poradi. Vlastne je to podobne nejakemu kodovani kdy nasledujici cislice se vzdy lisi jen jednim bitem. Zapomel jsem nazev, protoze jsem sklerotik. Takze jsem vedel ze to nejak pujde kdyz se do toho opru.
Vaše zpráva obsahuje 18813 znaků. Maximální povolený počet znaků je 17000.