Samomodifikujuce sa programy I.

   Dnes by som vas chcel zasvetit do jednej z najtajomnejsich veci, s ktorymi sa moze stretnut pri programovani v strojovom kode. Ano, su to samomodifikujuce sa programy. V lekcii 15 sme sa toho dotkli tym, ze sme ich oznacili ako programy, ktore sa "za jazdy" menia, a preto nemozu bezat v romke.

   Spominate si este na druhu rutinku z minulej lekcie ? Sluzila na vypocet N-tej mocniny cisla 2 pre N = 0 az 7. Pre obcestvenie pameti vam ju znovu napisem:

rut2 cpl   Invertovanie bitov v registri A 
 and #07  Vymaskovanie nepotrebnych bitov 
 ld c,a Vysledok ulozime do registra C 
 ld b,#00 Parovy register BC obsahuje hodnotu 7-N 
 ld a,#01  Inicializacna hodnota registra A 
 ld hl,bbb Do HL vlozime adresu zaciatku postupnosti 
 add hl,bc a pripocitame k nej BC cim ziskame 
 jp (hl) adresu na ktoru napokon skocime. 
bbb add a,a   
 add a,a  Postupnost siedmych instrukcii add a,a 
 add a,a  Kolko z nich sa vykona, 
 add a,a  zavisi od toho, 
 add a,a  do ktoreho miesta 
 add a,a  tejto postupnosti sa skoci. 
 add a,a   
 ret    

   Okrem samotnej postupnosti instrukcii ADD A,A v rutinke pomerne vela miesta zabera vypocet adresy skoku pre instrukciu JP (HL). Nedal by sa tento vypocet nejak eliminovat, nemohli by sme ho usetrit ?

   Pozorne si vsimnite instrukciu nepodmieneneho relativneho skoku JR ... . Tato instrukcia okrem operacneho kodu obsahuje este jeden jednobajtovy argument, ktory hovori o tom, kolko nasledujucich bajtov sa ma preskocit. Argument sa chape ako cislo v doplnkovom kode a preto moze byt aj zaporny. Vtedy instrukcia skoci o prislusny pocet bajtov nazad.

   Nepripomina vam to nic ? V minulej lekcii sme spominali, ze potrebujeme preskocit potrebny pocet instrukcii ADD A,A. Nedalo by sa na toto vyuzit prave JR ... ?

   Ako ste si uz urcite v tomto seriali lekcii zvykli, urcite by som sa nepytal, keby sa nedalo. Vsimnite si pozorne co robi kusok rutinky oznaceny hviezdickami. Preskoci prave tolko bajtov, aka hodnota je na jeho zaciatku v registri A. Instrukcia JR ... robi presne to iste, ale s tym rozdielom, ze pocet bajtov nie je v ziadnom registri, ale je zapisany priamo v pameti ako priamy operand instrukcie. Nedalo by sa to nejak vyuzit ?

   Urcite dalo, ale je tu ten rozdiel. Na jednej strane potrebujeme mat pocet preskakovanych bajtov zadany v registri, pretoze je to hodnota ktora sa moze menit, ale na druhej strane okrem JP (HL), JP (IX), JP (IY) neexistuje uz ziadna instrukcia vetvenia programu, ktorej velkost alebo adresa skoku by bola priamo dana nejakym registrom. Ako z tejto neprijemnej situacie von ?

   Existuju sice podmienene skoky (medzi ne v podstate patri aj DJNZ), ale u nich mozeme ovplyvnit len ci maju alebo nemaju skocit, ale velkost samotneho skoku je dana vzdy napevno priamym operandom. Dala by sa napriklad vyuzit instrukcia RET, co je vlastne zhruba to iste ako POP HL a JP (HL), ale riesenie s nim by vychadzalo este zlozitejsie ako v nasej uvedenej rutinke, pretoze okrem samotneho vypoctu adresy skoku by este navyse bolo treba pracovat aj so zasobnikom.

   Pametate sa este, ako sme v 15.lekcii v komprimacnej rutinke potrebovali ulozit na urcitu adresu do pameti minipocitadlo v registri A ? Vyskytol sa pri tom problem, ze v case, ked sme mali k dispozicii tuto adresu, sme este nevedeli hodnotu minipocitadla. Vyriesili sme to tak, ze tam kde sme vedeli adresu (mali sme ju v registri DE) sme pouzili instrukciu LD (pocet+1),DE a potom na mieste, kde sme potrebovali ulozit minipocitadlo, bola instrukcia LD (#5555),A pricom navestie "pocet" sa nachadzalo prave pri tejto instrukcii. Islo tam o to, ze adresa #5555 v instrukcii LD (#5555),A sa prepisala na hodnotu, ktoru sme potrebovali.

   Prave tento princip pouziva aj nasa nova - zjednodusena rutinka. Hodnota 7-N je presne ta hodnota, ktoru potrebuje mat instrukcia JR ... vo svojom operande, aby preskocila 7-N bajtov a aby sa vykonalo prave N instrukcii ADD A,A.

rut3 cpl   Invertovanie bitov v registri A 
 and #07  Vymaskovanie nepotrebnych bitov 
 ld (ccc+1),a Vysledok ulozime do parametra skoku 
 ld a,#01  Inicializacna hodnota registra A 
ccc jr #00  Skok na potrebne miesto 
 add a,a   
 add a,a  Postupnost siedmych instrukcii add a,a 
 add a,a  Kolko z nich sa vykona, 
 add a,a  zavisi od toho, 
 add a,a  do ktoreho miesta 
 add a,a  tejto postupnosti sa skoci. 
 add a,a   
 ret    

   Tato nasa nova rutinka ma uz len 18 bajtov, pre N=0 trva 52, pre N = 7 trva 80 taktov, co je v priemere 66 taktov. A ako vedlajsi efekt pri optimalizacii sa nam aj podarilo odstranit aj urcitu nevyhodu rutinky - rutinka teraz okrem samotneho registra A nemeni a ani nepotrebuje ziadne ine uzivatelske registre procesora.

Vas Busy.

Nazad / back , predchadzajuca a dalsia lekcia