Opravil jsem nahodne nalezenou chybu pri "constant folding/propagation" kdy pri zpracovani logickych vyrazu d<, d<=, d>, d>=, d=, d<>, du<, du<=, du>, du>=, du=, du<> kdy jsem mel pozadovany vstup v konstantach se mi vracela booleanovska (jak se to pise spravne cesky?) hodnota typu true (-1) a nebo false (0) jako DOUBLE. Takze jsem mel na zasobniku 2 hodnoty misto jedne.
Overil jsem si to v online forthu ze to fakt vraci normalni velikost. Mel jsem automaticky ve fci ze kdyz to zacina "d" tak je to 32 bit hodnota kvuli d+, d- atd.
Zajimavejsi je, ze jak se snazim pridat vic podporu pro osmibitove cisla jsem se snazil trosku upravit bordel v tokenovych pravidlech.
Jak mam 3 iterace pruchodu temi pravidly tak se snazim minimalizovat rizika kolizi pravidel kdy jedno rozbiji druhe.
Idea je takova, ze pri prvnim pruchodu, ktery je pri prvnim vlozeni tokenu, kdy i postupne umazavam a menim predchozi pravidla to mensi hloubky bych nemel delat spojeni typu NECO + PARAMETR/DATA, ale jen PARAMETR/DATA + NECO. Abych nekradl vstup pro nasledujici slova. NECO_1+DATA a NECO_2, kdyz je to skoro na 100% optimalnejsi jako NECO_1 a DATA+NECO_2. S tim ze pokud je tech dat za sebou vice neco potrebuji tak si vezmu jen tolik kolik potrebuji a zbytek necham pro nasledujici optimalizace NECO+DATA ktere se delaji az v druhem pruchodu.
V pripade ze pri prvnim pruchodu vznikne nejake slovo (a klidne se i zmeni nejak posledni zlova v zasobniku tokenu), ktere by mohlo vyvolat dalsi optimalizace tak muzu uz prvni pruchod znovu zopakovat s tim novym slovem. Stale to bude jako prvni pruchod.
Takze prvni pruchod by mel dusledne udelat
NUM1 -> NUM1
NUM2 -> NUM1_NUM2
EQ (tohle je fce ktera chce 2 parametry tak by to mela dusledne vyhonotit) -> NUM3 (vyhodnoti se EQ(NUM1,NUM2)
a nebo
NUM1 -> NUM1
EQ -> NUM1_EQ (nezname oba parametry ale i jedna konstanta dokaze pomoct tak ji bereme)
IF -> NUM1_EQ_IF (if chce jeden parametr bool, tak se hodi mit tohle slovo a spojime ho uz ted)
a nebo
DUP -> DUP
NUM1 -> DUP NUM1
EQ -> DUP NUM1_EQ (nezname oba parametry ale i jedna konstanta dokaze pomoct tak ji bereme)
IF -> DUP NUM1_EQ_IF (if chce jeden parametr bool, tak se hodi mit tohle slovo a spojime ho uz ted)
a v druhem pruchodu teprve spojime DUP+DATA na
DUP -> DUP
NUM_EQ_IF -> DUP_NUM_EQ_IF
kde druhy pruchod by nasel pokud by mel na vstupu slova jako DUP+NUM na DUP_NUM.
Tohle pravidlo me dava jasne najevo kde mam co umistnit. Napriklad spojeni EQ+IF patri do prvniho pruchodu a nebude kolidovat s nicim protoze je jasne ze EQ neziskalo zadny parametr, jinak by to bylo NUM_EQ+IF, pripadne rovnou NUM+IF.
Tohle se snazim sjednotit a udelat pro vsechna nova slova c=,c<>,c<,c<=,cu>,cu>=,cu<,cu<=,cu>,cu>=. Protoze se ukazuje ze ne vsechny slozena slova mam udelana a tak ty pravidla nejsou jednotna a rozhazena vsude mozne, nekde i zbytecne duplicitne atd.
Konkretne jsem nemel vsude NUM+CONDITION takze jsem to obcas resil jako
NUM -> NUM
CONDITION -> NUM CONDITION
IF -> NUM_CONDITION_IF (nasel spojeni, ale musel se divat na 2 posledni ulozene tokeny a tohle pravidlo musi byt pred slovem CONDITION+IF, ktere sice nenaslo zadny parametr, ale aspon zna podminku a je to stale lepsi nez 2 nezavisla slova co jedno haze na zasobnik 16 bitovou bool hodnotu a druhe ji vyzvedava testuje a odstranuje, protoze pri spojeni me staci priznak a odstraneni)
a obcas
NUM -> NUM
CONDITION -> NUM_CONDITION (nasel spojeni)
IF -> NUM_CONDITION_IF (a nemusim resit poradi pravidel, protoze nenastane kolize)
Tak jsem se konecne dostal ve vypraveni k tomu ze delam PUSH_CLT (num_c<), PUSH_CLE (num_c<=), kde c je char, takze 8 bitu.
A tady jsem resil ze me nepomuze priznak preteceni protoze vysledek je bool, takze potrebuji mit pro TRUE nastaveny carry a pak udelat:
sbc HL, HL (popripade by slo i rychlejsi ale delsi sbc A,A + ld L,A + ld H,A)
s konstantou LD HL, 0 a podminkovymi skoky je to vetsinou delsi kod. Jedine kdyby to bylo ve fci a mohl bych pouzit podminkovy RET.
Takze mam kod typu "L < const" a resim to
L < const --> L - const < 0 --> carry if true
a protoze je to se znamenkem tak provedu upravu ze invertuji nejvyssi bit.
Kód:
ld A,L
xor 0x80
sub 0x80+const
sbc HL, HL
Tohle je nejsnazsi a optimalni reseni pokud je mi znamo.
Pri "L <= const" jsem to resil tak ze to prevedu na
"L <= const" --> "L < const+1" a pouziji prvni reseni s tim ze se zvlast resi kdy je const maximalni hodnota, protoze je to automaticky TRUE a ve vypoctu by to preteklo.
Ale co delat s tim ze mam otocene vstupy u
"L > const" pripadne "L >= const" protoze invertovat nejvyssi bit v L kdyz ho nedrzim v A je drahe, pripadne CCF stoji dalsi bajt a 4 takty...
A v te chvili se me zacala klicit myslenka, kterou jsem zacal testovat, protoze mam zkusenost ze kolikrat neco delam spatne.
Po XOR a zmene const pridanim 0x80 delam beznamenkove porovnani.
Takze jsem automaticky delal
UA < UB?
UA - UB --> carry true
ale plati neco co jsem vedel ale nikdy me nenapadlo to v tomhle pripade pouzit jak jsem zpomalenej...
sub 1 == add a,255 + ccf
sub 255 == add a,1 + ccf
Takhle jsem to pouzival ale nikdy jsem to nezobecnil...
"L >= const" --> (0x80+L) + (256-(0x80+const)) >= 256
Kód:
ld A,L
xor 0x80
add A, 0x80-const
sbc HL, HL
kdy se musime vyhnout (osetrit jinde) hodnotu 0x80 = -128. Protoze 256-(0x80+0x80) = 256-0 = 256 a 256 + A je vzdy carry, jenze ve vypoctu by se to zmenilo na A+0 a to naopak nikdy carry nevyvola.
Pro
"L > const" --> (0x80+L) + (255-(0x80+const)) >= 256
Kód:
ld A,L
xor 0x80
add A, 0x7F-const
sbc HL, HL
tady by to melo platit v celem rozsahu, protoze pro min(=0x80) je to add A,255 a pokud bylo v L min (0x80) tak po invertovani je to nula.
Pokud je konstanta max(=0x7F) tak je to add A, 0 a uz je jedno co je v A protoze nikdy carry nenastane a to je spravne protoze L nemuze byt vetsi nez max.
Kód:
dworkin@dw-A15:~/Programovani/ZX/Forth/M4$ ../check_word.sh 'PUSH(100) CLT'
ld A, L ; 1:4 100 c< L<100 --> (0x80+L)<(0x80+100)
xor 0x80 ; 2:7 100 c< min --> zero
sub 0xE4 ; 2:7 100 c< (0x80+L)<(0x80+100) --> (0x80+L)-(0x80+100)<0 --> true if carry
sbc HL, HL ; 2:15 100 c< HL = bool, variant: default
; seconds: 0 ;[ 7:33]
dworkin@dw-A15:~/Programovani/ZX/Forth/M4$ ../check_word.sh 'PUSH(100) CLE'
ld A, L ; 1:4 100 c<= L<=100 --> L<100+1 --> (0x80+L)<(0x81+100)
xor 0x80 ; 2:7 100 c<= min --> zero
sub 0xE5 ; 2:7 100 c<= (0x80+HL)<0xE5 --> L-0xE5<0 --> true if carry
sbc HL, HL ; 2:15 100 c<= HL = bool, variant: default
; seconds: 0 ;[ 7:33]
dworkin@dw-A15:~/Programovani/ZX/Forth/M4$ ../check_word.sh 'PUSH(100) CGE'
ld A, L ; 1:4 100 c>= L>=100 --> (0x80+L)+(256-(0x80+100))>=256 and 100 > min
xor 0x80 ; 2:7 100 c>= min --> zero
add A, 0x1C ; 2:7 100 c>= (0x80+L)+28>=256 --> true if carry
sbc HL, HL ; 2:15 100 c>= HL = bool, variant: default and 100 <> 0x80
; seconds: 0 ;[ 7:33]
dworkin@dw-A15:~/Programovani/ZX/Forth/M4$ ../check_word.sh 'PUSH(100) CGT'
ld A, L ; 1:4 100 c> L>100 --> (0x80+L)+(255-(0x80+100))>=256
xor 0x80 ; 2:7 100 c> min --> zero
add A, 0x1B ; 2:7 100 c> (0x80+L)+27>=256 --> true if carry
sbc HL, HL ; 2:15 100 c> HL = bool, variant: default and 100 <> 0x80
; seconds: 0 ;[ 7:33]
Kód:
dworkin@dw-A15:~/Programovani/ZX/Forth/M4$ ../check_word.sh 'PUSH(xx) CLT'
if ((0xFF & (xx))=0x80)
.warning The condition is always False!
ld HL, 0x0000 ; 3:10 xx c< HL = bool, variant: min
else
if (xx)
ld A, L ; 1:4 xx c< L<xx --> (0x80+L)<(128+xx)
xor 0x80 ; 2:7 xx c< min --> zero
sub 128+xx ; 2:7 xx c< (0x80+L)<(128+xx) --> (0x80+L)-(128+xx)<0 --> true if carry
sbc HL, HL ; 2:15 xx c< HL = bool, variant: variable
else
ld A, L ; 1:4 xx c< L<xx
add A, A ; 1:4 xx c< sign to carry
sbc HL, HL ; 2:15 xx c< HL = bool, variant: zero
endif
endif
; seconds: 0 ;[14:66]
dworkin@dw-A15:~/Programovani/ZX/Forth/M4$ ../check_word.sh 'PUSH(xx) CLE'
if ((0xFF & (xx))=0x7F)
.warning The condition is always True!
ld HL, 0xFFFF ; 3:10 xx c<= HL = bool, variant: max
else
if ((0xFF & (xx))=0xFF)
ld A, L ; 1:4 xx c<= L<=xx
add A, A ; 1:4 xx c<= sign to carry
sbc HL, HL ; 2:15 xx c<= HL = bool, variant: zero-1
else
ld A, L ; 1:4 xx c<= L<=xx --> (0x80+L)<(129+xx)
xor 0x80 ; 2:7 xx c<= min --> zero
sub 129+xx ; 2:7 xx c<= (0x80+L)<(129+xx) --> (0x80+L)-(129+xx)<0 --> true if carry
sbc HL, HL ; 2:15 xx c<= HL = bool, variant: variable
endif
endif
; seconds: 0 ;[14:66]
dworkin@dw-A15:~/Programovani/ZX/Forth/M4$ ../check_word.sh 'PUSH(xx) CGE'
if ((0xFF & (xx))=0x80)
.warning The condition is always True!
ld HL, 0xFFFF ; 3:10 xx c>= HL = bool, variant: min
else
if (xx)
ld A, L ; 1:4 xx c>= L>=xx --> (0x80+L)+(256-(0x80+xx))>=256 and xx > min
xor 0x80 ; 2:7 xx c>= min --> zero
add A, 128-xx ; 2:7 xx c>= (0x80+L)+128-xx>=256 --> true if carry
sbc HL, HL ; 2:15 xx c>= HL = bool, variant: variable
else
ld A, L ; 1:4 xx c>= L>=xx
sub 0x80 ; 2:7 xx c>= sign --> invert carry
sbc HL, HL ; 2:15 xx c>= HL = bool, variant: zero
endif
endif
; seconds: 0 ;[15:69]
dworkin@dw-A15:~/Programovani/ZX/Forth/M4$ ../check_word.sh 'PUSH(xx) CGT'
if ((0xFF & (xx))=0x7F)
.warning The condition is always False!
ld HL, 0x0000 ; 3:10 xx c> HL = bool, variant: max
else
if ((0xFF & (xx))=0xFF)
ld A, L ; 1:4 xx c> L>xx
sub 0x80 ; 2:7 xx c> sign --> invert carry
sbc HL, HL ; 2:15 xx c> HL = bool, variant: zero-1
else
ld A, L ; 1:4 xx c> L>xx --> (0x80+L)+(255-(0x80+xx))>=256
xor 0x80 ; 2:7 xx c> min --> zero
add A, 127-xx ; 2:7 xx c> (0x80+L)+127-xx>=256 --> true if carry
sbc HL, HL ; 2:15 xx c> HL = bool, variant: variable
endif
endif
; seconds: 0 ;[15:69]
Tohle ma pro me ale dusledky i pro 16 bitove porovnani, protoze to znamena ze je nedelam nekdy optimalne...
Misto "ld BC, const + or A + sbc HL, BC" to sice pocitam rychleji pomoci A osmibitove...
Kód:
dworkin@dw-A15:~/Programovani/ZX/Forth/M4$ ../check_word.sh 'PUSH(0x1234) ULT'
ld A, L ; 1:4 0x1234 u< HL<4660 --> L-0x34<0 --> true if carry
sub 0x34 ; 2:7 0x1234 u< HL<4660 --> L-0x34<0 --> true if carry
ld A, H ; 1:4 0x1234 u< HL<4660 --> H-0x12<0 --> true if carry
sbc A, 0x12 ; 2:7 0x1234 u< HL<4660 --> H-0x12<0 --> true if carry
sbc HL, HL ; 2:15 0x1234 u< HL = bool, variant: default
; seconds: 0 ;[ 8:37]
dworkin@dw-A15:~/Programovani/ZX/Forth/M4$ ../check_word.sh 'PUSH(0x1234) ULE'
ld A, L ; 1:4 0x1234 u<= HL<=4660 --> HL<4660+1
sub 0x35 ; 2:7 0x1234 u<= HL<4660+1 --> L-0x35<0 --> true if carry
ld A, H ; 1:4 0x1234 u<= HL<4660+1 --> H-0x12<0 --> true if carry
sbc A, 0x12 ; 2:7 0x1234 u<= HL<4660+1 --> H-0x12<0 --> true if carry
sbc HL, HL ; 2:15 0x1234 u<= HL = bool, variant: default
; seconds: 0 ;[ 8:37]
dworkin@dw-A15:~/Programovani/ZX/Forth/M4$ ../check_word.sh 'PUSH(0x1234) UGE'
ld A, 0x33 ; 2:7 0x1234 u>= HL>=4660 --> HL>4660-1
sub L ; 1:4 0x1234 u>= HL>4660-1 --> 0>0x33-L --> true if carry
ld A, 0x12 ; 2:7 0x1234 u>= HL>4660-1 --> 0>0x12-H --> true if carry
sbc A, H ; 1:4 0x1234 u>= HL>4660-1 --> 0>0x12-H --> true if carry
sbc HL, HL ; 2:15 0x1234 u>= HL = bool, variant: default
; seconds: 0 ;[ 8:37]
dworkin@dw-A15:~/Programovani/ZX/Forth/M4$ ../check_word.sh 'PUSH(0x1234) UGT'
ld A, 0x34 ; 2:7 0x1234 u> HL>4660 --> 0>0x34-L --> true if carry
sub L ; 1:4 0x1234 u> HL>4660 --> 0>0x34-L --> true if carry
ld A, 0x12 ; 2:7 0x1234 u> HL>4660 --> 0>0x12-H --> true if carry
sbc A, H ; 1:4 0x1234 u> HL>4660 --> 0>0x12-H --> true if carry
sbc HL, HL ; 2:15 0x1234 u> HL = bool, variant: default
; seconds: 0 ;[ 8:37]
..., ale tady se ukazuje ze pro UGE a UGT mohu pouzit
Kód:
ld BC, edit_const ; 3:10
add HL, BC ; 1:11
sbc HL, HL ; 2:15 0x1234 u> HL = bool, variant: default
; seconds: 0 ;[6:36]
6:36!!! (popripade 7:40 s ccf pro ULT a ULE pokud preferujete kratsi kod, a on je vlastne pro me optimalni protoze mam za to ze 1 bajt ma cenu 4 taktu a tady budeme o 3 takty pomalejsi a 1 bajt kratsi)
Ponauceni je ze pri porovnani cisla a konstanty nepouzivejte odcitani a podteceni ale pricitani a preteceni (navic to ma tu vyhodu, ze pri pricitani muzete zamenit hodnoty).PS: Na odcitani konstanty jsem bezne pouzival pricitani zaporne konstanty, ale u porovnani jsem se slepe drzel odcitani. Zvlastni jak hlava pracuje a drzi se zajetych koleji. Dalo by se rici ze tohle je naprosto trivialni zmena a pritom bylo na to prijit tak tezke.
PPS: Pro porovnani (a pobaveni) si jeste rypnu to C prekladace
Kód:
int lt_5(char num1) {
return (num1<5);
}
int gt_5(char num1) {
return (num1>5);
}
void main(){
lt_5(10);
gt_5(3);
}
Kód:
._lt_5
ld hl,2 ;const
add hl,sp
call l_gchar
ld a,l
xor 128
sub 133
ld hl,0 ;const
rl l
ret
._gt_5
ld hl,2 ;const
add hl,sp
call l_gchar
ld de,5
ex de,hl
call l_gt
ret
._main
ld hl,10 ;const
push hl
call _lt_5
ld hl,3 ;const
ex (sp),hl
call _gt_5
pop bc
ret
Je videt ze u "< 5" to resi stejne jen vola dalsi fci l_gchar kvuli... rozsireni 8 bitove hodnoty na 16 bitovou??? a u > 5 to rovnou vzdava a radsi vola fci l_gt a resi to jako 2 promenne.
Naposledy co jsem se dival jak resi ty fce tak tam mel neco jako bezpodminkovy skok na nasledujici instrukci atd.
Kód:
; true = 1
; false = 0
; l_lt de<hl --> de-hl<0
ld a,h ;8294
add a,080h ;8295
ld b,a ;8297
ld a,d ;8298
add a,080h ;8299
cp b ;829b
jp nz,082a4h ;829c
; h==d
ld a,e ;829f
cp l ;82a0
jp 082a4h ;82a1 !!!!!!!?????
;82a4
ld hl,00000h ;82a4
ret nc ;82a7
inc hl ;82a8
ret ;82a9
PPPS: A rovnou mohu ukazat jak resi unsigned int (on fakt pouziva vsude 16 bitove odcitani... Aspon ze to nevola pres fce i kdyz kdyby to opravil na to 16 bitove add tak mu zustane ten pop_push cirkus protoze C...
Kód:
int lt_100(unsigned int num1) {
return (num1<100);
}
int gt_100(unsigned int num1) {
return (num1>100);
}
void main(){
lt_100(10);
gt_100(3);
}
Kód:
._lt_100
pop bc
pop hl
push hl
push bc
ld de,100
and a
sbc hl,de
ld hl,0 ;const
rl l
ret
._gt_100
pop bc
pop de
push de
push bc
ld hl,100
and a
sbc hl,de
ld hl,0 ;const
rl l
ret
._main
ld hl,10 ;const
push hl
call _lt_100
ld hl,3 ;const
ex (sp),hl
call _gt_100
pop bc
ret
PPPPS: Takze objev super, ale dusledek je, ze me ceka spoustu dnu prace to opravit. A ani nevim co drive, zda dodelat vic tu podporu pro CHAR a nebo resit neoptimalni kod.