Dnes si pohovorime (presnejsie popiseme) o jednej typickej ulohe, ktora sa vyskytuje pri programovani nielen strojovom kode, ale aj vo vyssich programovacich jazykoch. Touto ulohou je porovnanie dvoch retazcov. Vo vyssich jazykoch je na provnavanie retazcov alebo nejaky prikaz ci operator, alebo aspon nejaka standartna kniznicna funkcia. Ale v strojovom kode nic take nemame, preto si to musime sami poctivo naprogramovat.
Este pred tym, ako prejeme k samotnemu algoritmu a k programu,
musime si najskor definovat strukturu udajovych prvkov. Ludsky
povedane, musi nam byt jasne ze ake retazce vlastne budeme
porovnavat. Lebo tazko sa nam bude pisat program na spracovanie
niecoho, ked ani nevieme ako to vlastne vyzera... Zvolme si, ze
nase sa budu skladat z jednotlivych bajtov. Mohli by sa skladat
aj z inych poloziek (napr. z dvojbajtovych hodnot), ale to by uz
bolo ovela zlozitejsie. Majme teda v pameti dva retazce - prvy
je na adrese add1, druhy na adrese add2. To ze je retazec na
adrese XYZ znamena, ze na tejto adrese je jeho zaciatocny alebo
prvy bajt. Druhy bajt je na adrese XYZ+1, treti na XYZ+2 a tak
postupne az po posledny. Nech su tieto nase retazce rovnako dlhe
a nech maju dlzku len. Tymto sme si presne definovali
strukturu udajov a mozme si vymysliet nejaky rafinovany
porovnavaci algoritmus.
So samotnym algoritmom sa trapit nemusime, pretoze ho uz davno
vymysleli mudre hlavy (hlavicky-makovicky). Len si ho
pripomenieme. Najprv sa porovnaju prve bajty v retazcoch. Ak su
rozne, vysledok celeho porovnavania retazcov bude taky isty ako
vysledok porovnania tychto dvoch bajtov a algoritmus konci. Ale
ak su tieto dva bajty rovnake, celu cinnost dokola opakujeme s
nasledujucimi (druhymi, tretimi,atd.) bajtami v retazcoch az kym
alebo nenarazime na rozne bajty alebo neprideme na koniec aspon
jedneho retazca. V nasom pripade su oba retazce rovnako dlhe,
cize prideme na konce oboch retazcov naraz. V tomto pripade
vyhlasime oba retazce za zhodne a rovnake.
Tak, mame strukturu udajov a algoritmus a konecne mozeme
prikrocit k napisaniu samotneho programu. Pre samotne porovnanie
dvoch bajtov by sa nam hodila instrukcia, ktora porovnava dva
bajty v pameti. Ale taka zial neexistuje. Preto sa musime
uspokojit s instrukciou, pri ktorej je jeden bajt v pameti a
druhy v akumulatore. Je to cp (hl). Tato instrukcia adresu
bajtu v pameti chce v registri HL, preto si aj my do tohoto
registra umiestnime adresu prveho retazca. Druhy bajt musi byt v
akumulatore, preto ho sem musime nejak dostat. No a na to mame
hned celu plejadu instrukcii. Pre nas najvhodnejsia bude
instrukcia ld a,(de). Z tohto hned vidime, ze vhodne miesto
pre adresu druheho retazca bude register DE. Ked budeme mat
bajty porovnane, musime sa rozhodnut - alebo su rozne a program
ukoncime (vyriesi to instrukcia ret nz), alebo budeme
pokracovat porovnavanim dalsich bajtov. Aby sme vedeli, kolko
bajtov este mame porovnat, potrebujeme na zaciatku prace vediet
dlzku retazcov. Este mame volny registrovy par BC, preto ho
vyuzijme a do neho umiestnime tuto dlzku. Register BC nas bude
pocas nasho porovnavania informovat, ze kolko bajtov ostava este
do konca retazcov.
Nas program bude (po prvom priblizeni) vyzerat takto:
ld | hl,add1 | adresa prveho retazca | |
ld | de,add2 | adresa druheho retazca | |
ld | bc,len | dlzka retazcov | |
loop | ld | a,(de) | vezme bajt z druheho retazca |
cp | (hl) | porovna s bajtom z prveho retazca | |
ret | nz | pri rozdiele - vysledok je jasny | |
inc | hl | dalsi bajt prveho retazca | |
inc | de | dalsi bajt druheho retazca | |
dec | bc | do konca ostava o jeden bajt menej | |
ld | a,b | \kontrola, ci register BC | |
or | c | /je uz nulovy | |
jr | nz,loop | ak nie tak porovnavame dalej | |
ret | ak ano tak sme vycerpali celu dlzku | ||
retazcov, nenasli sme ziadny rozdiel | |||
a mozme teda vyhlasit retazce za | |||
zhodne. |
Nas program vracia priznaky (alebo aj prizraky - ako chcete)
nastavene ako instrukcia cp pre jednotlive bajty: ZERO=1 ak su
retazce zhodne alebo ZERO=0 ak su rozdielne. Ak su rozdielne tak
sa este CARRY nastavi nasledovne: CARRY=1 ak je prvy retazec
vecsi a CARRY=0 ak je vecsi ten druhy.
Ta kontrola ci je register BC nulovy je znama a velmi casto
pouzivana programatorska finta a mnohi ju uz poznaju. Ale pre
tych, ktori ju este nepoznaju si ju teraz vysvetlime. Instrukcia
dec bc nam sice zmensi register BC o jednotku, ale nenastavuje
ziadne priznaky. Preto si musime nulovost BC otestovat sami.
Registrovy par BC obsahuje nulu prave vtedy, ked nulu obsahuje
aj register B aj register C. Tato "definicia" nam tak trosku
pripomina definiciu bitovej logickej funkcie OR - vysledok je
nulovy prave vtedy, ak oba vstupne argumenty su nulove. Tato
podobnost vobec nie je nahodna a my ju teraz velmi zakerne
zneuzijeme - v nasej kontrole pouzijeme instrukciu, ktora robi
prave tuto funkciu. Ako prvy argument jej podstrcime register B
(cez akumulator) a druhym argumentom bude zase register C.
Register B sme museli dat do akumulatora preto, lebo instrukcia
or pracujuca s registrami B a C neexistuje, vzdy jeden z
operandov musi byt akumulator. No a instrukcia or nam na
rozdiel od dec bc uz nastavuje priznak ZERO, takze vlaste uz
vieme, ci je v BC nula a mozeme spravit podmieneny skok.
Na zaver tejto lekcie vam dam jednu malu domacu ulohu. Ked si
pozorne prezriete tabulky, zistite, ze su tam instrukcie na
blokove prehladavanie. Skuste porozmyslat, ci by sa tieto
instrukcie nedali nejako vyuzit v nasom programe. Mala pomocka:
insrukcia cpi sa da velmi pekne pouzit, skuste prist na to,
ako. Spravne riesene sa dozviete v buducej tretej lekcii.
Vas Busy.