Delenie a odmocnina

   Dnes sa budeme zaoberat dalsou matematickou operaciou, ktora nie je priamo zahrnuta v instrukcnom subore Z80. Je to delenie.
   Delenie cisla M (delenec) cislom N (delitel) nie je vlastne nic ine, ako zistovanie kolkokrat treba k nule pricitat cislo N aby vzniklo cislo M, alebo kolkokrat treba od cisla M odcitat N aby sme dospeli k nule. Vsimnime si tuto druhu myslienku a skusme ju realizovat.
   Nasobenie dvoch cisel v minulej lekci nam davalo 16-bitovy vysledok. Preto skusme tu nasu rutinku na delenie upravit tak, aby pracovala so 16-bitovym delencom. Z toho nam vyplyva, ze na odcitanie budeme musiet pouzit instrukcie ktora odcitavaju 16-bitove cisla. Tieto instrukcie odcitavaju cislo od registra HL, preto je tento register vyhodny na uchovavanie hodnoty delenca. Od registra HL sa daju odcitavat registre BC,DE,HL a SP. Registre SP a HL nepripadaju do uvahy, lebo SP je ukazovatel zasobnika a je potrebny pri zasobnikovych operaciach a keby sme od HL odcitali HL, potom by nam vysla nula a stratili by sme delenec.
   Zvolme si teda, ze delitel bude v registri DE. Register BC pouzijeme ako pocitadlo odcitani - aby sme vedeli, ze kolkokrat sa nam podarilo odcitat delitela od delenca.
   Samotne odcitanie budeme robit instrukciou SBC HL,DE. Lenze tato instrukcia okrem odcitania DE od HL este odcita od HL aj hodnotu priznaku CARRY. Preto musime tento priznak pred odcitanim vynulovat. Ako uz vieme, CARRY nuluju vsetky logicke instrukcie. Vyberme spomedzi nich taku, ktora ma co najmenej vedlajsich efektov - takou je napr. OR A alebo AND A.
   No a mozme sa venovat nasej rutinke. Na zaciatku vynulujeme pocitadlo odcitani - register BC. Instrukcia SBC HL,DE nam nastavi CARRY prave vtedy, ak to s tymi odcitaniami "presvihneme" - prekrocime nulu a dostaneme sme sa do zapornych hodnot. Ked CARRY nie je nastavene, znamena to ze este sme neprekrocili nulu a mozeme sa znovu pokusit o dalsie odcitanie. Na konci este musime spravit istu korekciu - poslednym odcitanim sme sa dostali do oblasti zapornych hodnot, preto toto posledne odcitanie uz nemozeme ratat do vysledku (napravime to instrukciou DEC BC) a do delenca musime vratit hodnotu, ktora bola pred tymto posledny odcitanim. Najlepsie to urobime tak, ze tu istu hodnotu, co sme "navyse" odcitali zase znovu pricitame, a tym vlastne dostavame okrem celociselnej casti podielu v registri BC aj v zvysok po deleni v registri HL.
   Zhrnutie: Nasa rutinka (nazvime ju "lomeno") ma na vstupe delenec v registri HL, delitel v DE a po jej vykonani je v BC celociselna cast vysledku a v HL zvysok po deleni.
   Nase "lomeno" bude teda vyzerat takto:

lomeno ld bc,#00 inicializacia pocitadla na nulu 
lom1 or vynulovanie CARRY [pre istotu] 
 sbc hl,de odcitanie delitela od delenca 
 inc bc po kazdom odcitani zvysime poc. 
 jr nc,lom1 opakujeme pokym to nie je zaporne 
 add hl,de oprava prekrocenia nuly 
 dec bc oprava hodnoty vysledku 
 ret  konec 

   A teraz sa venujme optimalizacii vzhladom na dlzku a cas vykonavania rutinky. Ked sa nad tou rutinkou zamyslime, zistime ze vysledky sa vobec nezmenia, ak opravu hodnoty vysledku spravime na zaciatku rutinky (hned po instrukcii LD BC,0). Lenze postupnost instrukii LD BC,0 a DEC BC mozeme nahradit jednou instrukciou LD BC,-1 (co je presne to iste ako LD BC,65535).
   Este si vsimnime, ze ked po odcitani skaceme na navestie "lom1" tak tam skaceme prave vtedy, ked CARRY je nulove. Cize namiesto na instrukciu OR A by sme mohli skakat az na SBC HL,DE. Tymto sa nam vykonavanie rutinky trochu zrychli. To OR A je tam potrebne, lebo ak ideme vykonat prve odcitane, nevieme ako je CARRY nastavene.
   Pokial nepotrebujeme poznat zvysok po deleni, mozeme opravu prekrocenia nuly (robenu istukciou ADD HL,DE) kludne vynechat, lebo na samotny podiel v registri BC nema ziadny vplyv.
   No a po tychto troch upravach sa nam nasa rutinka zmensila do takejto podoby:

lomeno ld bc,#ffff inicializacia pocitadla na -1 
 or zaciatocne vynulovanie CARRY 
lom1 sbc hl,de odcitanie delitela od delenca 
 inc bc po odcitani zvysime pocitadlo 
 jr nc,lom1 opakujeme pokym to nie je zaporne 
 ret  koniec 

   Jedna otazka na zamyslenie, ktorej odpoved necham na vas: Co by sa stalo, keby sme zavolali tuto rutinku s nulovym delitelom ?
   Tentoraz som si pre vas prichystal domacu ulohu, aku ste este nemali. Doteraz ste alebo riesili daky problem alebo ste mali navrhnut nejaku rutinku. Avsak tentoraz mate ulohu presne obratenu - predkladam vam uz hotovu rutinku a vasou ulohou je vysvetlit, ako pracuje a ake konkretne cinnosti v nej vykonavaju jednotlive instrukcie. Prezradim vam len tolko, ze tato rutinka na vstupe ocakava nejake cislo v registri HL a na vystupe je v registri A druha odmocnina tohto cisla.

sqr ld de,1 
 xor 
 dec 
loop sbc hl,de 
 inc de 
 inc de 
 inc 
 jr nc,loop 
 ret  

Vas Busy.

Nazad / back , predchadzajuca a dalsia lekcia