Blog:Programovanie v assembleri i8080 pre PMD 85 (9)
Z PMD 85 Infoserver
Obsah |
V predošlej kapitole sme si ukázali, ako vypisovať jednotlivé znaky, či už za pomoci rutín z Monitora alebo vlastnými rutinami. Čo sa týka vlastných rutín pre výpis znakov, tých spôsobov výpisu sa dá vymyslieť určite viac, to však nechám na vašu tvorivosť a fantáziu, ale možno si neskôr ešte niečo ukážeme.
Vypisovanie textov
Textom budeme rozumieť nejaký reťazec znakov uzavretý v úvodzovkách, ktorý budeme definovať za pomoci pseudoinštrukcie db. Dôležitou vecou pri vypisovaní reťazcov je vedieť, aký je daný reťazec dlhý, resp. ako je ukončený. To sa dá urobiť rôznymi spôsobmi:
- Dĺžka reťazca je v jednom byte pred samotným reťazcom.
- Reťazec je ukončený nulovým bytom (alebo iným kódom, ktorý nie je tlačiteľným znakom).
- Reťazec je ukončený znakom, ktorého kód má nastavený 7. bit (zo ZX Spectra sa vžil termín "s invertovaným" znakom, pretože sa v rôznych debuggeroch takýto byte zobrazoval ako invertovaný znak). Tento spôsob teda umožňuje používať iba znaky do kódu 127 (čo ale nevylučuje používať aj znaky s diakritikou, ak si vytvoríme font s vlastným kódovaním znakov), ale ušetrí sa jeden byte na každý reťazec.
Ktorý spôsob budete používať, záleží od vašej voľby, ale my si ukážeme všetky spôsoby a pre každý z týchto spôsobov si vytvoríme macro, ktoré nám opäť sprehľadní kód.
;------------------------------------------------------------------------------ cpu 8080 page 0 title "ASM Program 2" ;------------------------------------------------------------------------------ org 0 ; program začne od adresy 0 ;------------------------------------------------------------------------------ ; definícia adries rutín v Monitore ERASE equ 85A7h MONIT equ 8C40h ; systémové premenné COLOR equ 0C03Ah ; adresa VRAM VRAM equ 0C000h AV function row,col,VRAM+row*300h+col ;------------------------------------------------------------------------------ ; - dp - pripraví text txt tak, že jeho dĺžka je v prvom byte pred textom dp macro txt db strlen(txt) db txt endm ;------------------------------------------------------------------------------ ; - dz - pripraví text txt tak, že ho ukončí nulou dz macro txt db txt db 0 endm ;------------------------------------------------------------------------------ ; - dm - pripraví text txt tak, že posledné písmeno má svoj kód s nastaveným 7. bitom dm macro txt if strlen(txt) > 1 db substr(txt, 0, strlen(txt) - 1) endif if strlen(txt) > 0 db charfromstr(txt, strlen(txt) - 1) | 80h endif endm ;------------------------------------------------------------------------------ Start: di ; zakázanie prerušení lxi sp,8000h ; nastav zásobník na koniec RAM xra a ; nastav normálny výpis znakov sta COLOR call ERASE ; zmazanie obrazovky lxi h,AV(4,2) ; do HL adresu VRAM pre 4. riadok a 2. stĺpec lxi d,T_Text1 ; adresa 1. textu call PrtTxtPasAV ; vypíš text, ktorého dĺžka je v prvom byte lxi h,AV(6,8) ; do HL adresu VRAM pre 6. riadok a 8. stĺpec lxi d,T_Text2 ; adresa 2. textu call PrtTxt0AV ; vypíš text ukončený nulou lxi h,AV(8,0) ; do HL adresu VRAM pre 8. riadok a 0. stĺpec lxi d,T_Text3 ; adresa 3. textu call PrtTxtMAV ; vypíš text, ktorý má posledný znak "invertovaný" ; návrat do Monitora (Os ready) jmp MONIT ;------------------------------------------------------------------------------ ; Výpis reťazca "pascalovského" typu, kedy byte dĺžky reťazca predchádza samotný ; reťazec. ; I: DE=adresa textu (prvý byte je dĺžka), HL=adresa VRAM ; O: DE=adresa posledného znaku textu, B=0 ; M: DE, B, AF PrtTxtPasAV: shld PrtCharDiaPos+1 ; ulož adresu VRAM do inštrukcie ; LXI H,nn v rutine PrtCharDia ;------------------------------------------------------------------------------ ; Výpis reťazca "pascalovského" typu, kedy byte dĺžky reťazca predchádza samotný ; reťazec. ; I: DE=adresa textu (prvý byte je dĺžka) ; O: DE=adresa posledného znaku textu, B=0 ; M: DE, B, AF PrtTxtPas: ldax d ; prevezmi do A dĺžku reťazca mov b,a ; a ulož ju do B PrtTxtPasL: inx d ; posuň ukazateľ na znaky reťazca ldax d ; prevezmi znak do A call PrtCharDia ; vypíš znak dcr b ; zníž počítadlo znakov jnz PrtTxtPasL ; a opakuj pre celý reťazec ret ;------------------------------------------------------------------------------ ; Výpis reťazca ukončeného nulou. ; I: DE=adresa textu ukončeného nulou, HL=adresa VRAM ; O: DE=adresa ukončovacej nuly ; M: DE, AF PrtTxt0AV: shld PrtCharDiaPos+1 ; ulož adresu VRAM do inštrukcie ; LXI H,nn v rutine PrtCharDia ;------------------------------------------------------------------------------ ; Výpis reťazca ukončeného nulou. ; I: DE=adresa textu ukončeného nulou ; O: DE=adresa ukončovacej nuly ; M: DE, AF PrtTxt0: ldax d ; prevezmi znak do A ora a ; je to ukončovacia nula? rz ; áno, vráť sa call PrtCharDia ; vypíš znak inx d ; posuň ukazateľ na ďalší znak reťazca jmp PrtTxt0 ; opakuj pre celý reťazec ;------------------------------------------------------------------------------ ; Výpis reťazca, ktorý má v poslednom znaku nastavený 7. bit ("invertovaný" znak). ; I: DE=adresa textu ukončeného nulou, HL=adresa VRAM ; O: DE=adresa posledného znaku textu ; M: DE, AF PrtTxtMAV: shld PrtCharDiaPos+1 ; ulož adresu VRAM do inštrukcie ; LXI H,nn v rutine PrtCharDia ;------------------------------------------------------------------------------ ; Výpis reťazca, ktorý má v poslednom znaku nastavený 7. bit ("invertovaný" znak). ; I: DE=adresa textu ukončeného nulou ; O: DE=adresa posledného znaku textu ; M: DE, AF PrtTxtM: ldax d ; prevezmi znak do A ani 7Fh ; odmaskuj iba platné bity call PrtCharDia ; vypíš znak ldax d ; opäť vezmi znak do A ora a ; je to posledný znak s nastaveným 7. bitom? rm ; áno, vráť sa inx d ; posuň ukazateľ na ďalší znak reťazca jmp PrtTxtM ; opakuj pre celý reťazec ;------------------------------------------------------------------------------ ; Vypísanie znaku, ktorého kód je v A. ; Rutina využíva vlastný font, kde sú znaky vysoké 8 bodov (mikroriadkov). ; Riadok je ale vysoký 12 mikroriadkov a horné 4 mikroriadky slúžia pre ; diakritické znamienka. ; I: A=znak <32, 127> a <192, 255>, [PrtCharDiaPos+1]=adresa VRAM ; O: - ; M: AF PrtCharDia: cpi 32 ; ak je to riadiaci kód, návrat rc ; neplatný kód znaku, ihneď sa vráť cpi 127+1 ; ak je to znak zo základného ASCII jc PrtCharDiaN ; skoč ďalej s CY=1 cpi 192 ; kód znaku medzi <128, 191> rc ; nie je podporovaný, vráť sa PrtCharDiaN: push h ; odpamätaj registre push d push b push psw ; odpamätaj kód znaku aj CY jc PrtCharDiaN1 ; skoč, ak je to znak bez diakritiky ani 3Fh ; odmaskuj poradie znaku s diakritikou mov c,a ; v tabuľke a ulož do BC mvi b,0 lxi h,CsChar ; tabuľka zodp. znakov bez diakritiky dad b ; pripočítaj offset mov a,m ; a ulož do A základný znak PrtCharDiaN1: sui 32 ; uprav kód znaku na rozsah od 0 add a ; kód znaku x2 mov l,a ; ulož do HL mvi h,0 dad h ; x4 dad h ; x8; HL = (kód_znaku - 32) * 8 lxi b,Font ; BC=bázová adresa fontu dad b ; pripočítaj k offsetu znaku pop psw ; obnov kód znaku a CY push h ; adresu predlohy znaku na zásobník mvi l,NN ; bez diakritického znamienka jc PrtCharDiaN2 ; pre základne znaky ani 3Fh ; odmaskuj poradie znaku s diakritikou mov c,a ; v tabuľke a ulož do BC mvi b,0 lxi h,CsDiaChar ; nájdi v tabuľke nižší byte predlohy dad b ; diakritického znamienka mov l,m ; a ulož do L PrtCharDiaN2: mvi h,FontDia/256 ; vyšší byte adresy predlohy diakr. zn. xchg ; DE=predloha diakritického znamienka PrtCharDiaPos: lxi h,0 ; adresa VRAM PrtCharDiaC: mvi c,0 ; atribút do C mvi b,4 ; 4 mikroriadky nad znakom diakr. zn. PrtCharDiaK: ldax d ; vezmi byte predlohy diakr. zn. inx d ; posun na ďalší byte predlohy xra c ; pridaj atribút mov m,a ; ulož do VRAM mov a,l ; posun na nasledujúci mikroriadok adi 40h mov l,a jnc PrtCharDiaK0 ; skoč, ak nedošlo k pretečeniu inr h ; inak inkrementuj ešte H PrtCharDiaK0: dcr b ; opakuj pre celé diakritické znamienko jnz PrtCharDiaK pop d ; obnov predlohu základného znaku mvi b,8 ; výška znaku 8 mikroriadkov PrtCharDiaL: ldax d ; vezmi byte predlohy inx d ; posun na ďalší byte predlohy xra c ; pridaj atribút mov m,a ; ulož do VRAM mov a,l ; posun na nasledujúci mikroriadok adi 40h mov l,a jnc PrtCharDiaL0 ; skoč, ak nedošlo k pretečeniu inr h ; inak inkrementuj ešte H PrtCharDiaL0: dcr b ; opakuj pre celý znak jnz PrtCharDiaL lhld PrtCharDiaPos+1 ; aktuálna adresa VRAM inx h ; posun na ďalšiu znakovú pozíciu VRAM mov a,l ; opustili sme textový riadok? ani 3Fh cpi 48 ; 48 znakov na riadok jc PrtCharDiaE ; nie, návrat mov a,l ; áno, prejdi na ďalší textový riadok ani 0C0h ; CR - návrat na začiatok riadku mov l,a inr h ; LF - ďalší riadok (12 mikroriadkov) inr h inr h PrtCharDiaE: shld PrtCharDiaPos+1 ; ulož novú adresu VRAM pop b ; obnov registre pop d pop h ret ;------------------------------------------------------------------------------ ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ;------------------------------------------------------------------------------ ; tabuľka základných znakov, ktoré korešpondujú so znakmi s diakritikou podľa ; kódovania v KOI-8čs CsChar: db " a cder uiullonooarstu e yz " db " A CDER UIULLONOOARSTU E YZ " ; tabuľka nižších bytov adresy predlohy diakritického znamienka CsDiaChar: db NN,D1,NN,M1,M1,M1,D1,NN db P1,D1,K1,D2,M3,P1,M1,D1 db V1,P1,M1,M1,M3,D1,NN,D1 db NN,D1,M1,NN,NN,NN,NN,NN db NN,D2,NN,M2,M2,M2,D2,NN db P2,D2,K2,D2,M3,P2,M2,D2 db V2,P2,M2,M2,M2,D2,NN,D2 db NN,D2,M2,NN,NN,NN,NN,NN ;------------------------------------------------------------------------------ ; dáta fontu Font: binclude "../data/font.bin" ; dáta diakritických znamienok ; Potrebujeme, aby predlohy diakritických znamienok neprekročili 256 bytovú ; "stránku", teda aby mali všetky predlohy rovnaký vyšší byte adresy. if (($ / 256) <> (($ + 48) / 256)) align 256 ; zarovnanie na adresu MOD 256 endif FontDia: binclude "../data/font-dia.bin" ; definícia návestí na nižší byte adresy predlohy diakritických znamienok NN equ (FontDia+0)&255 ; nič D1 equ (FontDia+4)&255 ; dĺžeň nad malým písmenom M1 equ (FontDia+8)&255 ; mäkčeň nad malým písmenom M3 equ (FontDia+12)&255 ; mäkčeň pre malé/veľké L a malé T P1 equ (FontDia+16)&255 ; prehláska nad malým písmenom K1 equ (FontDia+20)&255 ; krúžok nad malým U V1 equ (FontDia+24)&255 ; vokáň nad malým O D2 equ (FontDia+28)&255 ; dĺžeň nad veľkým písmenom M2 equ (FontDia+32)&255 ; mäkčeň nad veľkým písmenom P2 equ (FontDia+36)&255 ; prehláska nad veľkým písmenom K2 equ (FontDia+40)&255 ; krúžok nad veľkým U V2 equ (FontDia+44)&255 ; vokáň nad veľkým O ;------------------------------------------------------------------------------ ; definícia vlastného charsetu (transformačnej tabuľky) kódovania znakov ; CP1250 -> KOI8čs charset 'á',0C1h charset 'č',0C3h charset 'ď',0C4h charset 'ě',0C5h charset 'ŕ',0C6h charset 'ü',0C8h charset 'í',0C9h charset 'ů',0CAh charset 'ĺ',0CBh charset 'ľ',0CCh charset 'ö',0CDh charset 'ň',0CEh charset 'ó',0CFh charset 'ô',0D0h charset 'ä',0D1h charset 'ř',0D2h charset 'š',0D3h charset 'ť',0D4h charset 'ú',0D5h charset 'é',0D7h charset 'ý',0D9h charset 'ž',0DAh charset 'Á',0E1h charset 'Č',0E3h charset 'Ď',0E4h charset 'Ě',0E5h charset 'Ŕ',0E6h charset 'Ü',0E8h charset 'Í',0E9h charset 'Ů',0EAh charset 'Ĺ',0EBh charset 'Ľ',0ECh charset 'Ö',0EDh charset 'Ň',0EEh charset 'Ó',0EFh charset 'Ô',0F0h charset 'Ä',0F1h charset 'Ř',0F2h charset 'Š',0F3h charset 'Ť',0F4h charset 'Ú',0F5h charset 'É',0F7h charset 'Ý',0F9h charset 'Ž',0FAh ;------------------------------------------------------------------------------ ; Texty (tento súbor je uložený v kódovaní CP1250) T_Text1: dp "Tento reťazec má dĺžku uvedenú v prvom byte." T_Text2: dz "Tento reťazec je ukončený nulou." T_Text3: dm "Retazec s nastavenym 7. bitom v poslednom znaku." ;------------------------------------------------------------------------------ ; zrušenie špeciálneho charsetu charset ;------------------------------------------------------------------------------ end ;------------------------------------------------------------------------------
Na začiatku sme si teda zadefinovali 3 makrá dp, dz a dm pre každý zo spôsobov definície reťazca. V makrách použité funkcie strlen, substr a charfromstr sú interné funkcie macroassemblera AS. Samotné makrá sú myslím dostatočne zrozumiteľné, takže nepotrebujú ďalšie vysvetlenie. Na konci sme si za pomoci týchto makier zadefinovali 3 rôzne reťazce rovnakým spôsobom, ako by sme to robili pomocou pseudoinštrukcie db. V tomto prípade sa ale reťazec pripraví príslušným spôsobom a je vidieť, že takto je kód prehľadnejší. Odporúčam pozrieť si listing prekladu (súbor s príponou .lst), kde uvidíte, ako sú makrá rozvinuté do kódu. Listing prekladu je veľmi užitočný hlavne pri debuggovaní, kde vidíme konkrétne adresy, na ktorých sa jednotlivé rutiny a dáta nachádzajú.
Pre výpis znaku používame našu rutinu PrtCharDia, ktorú sme si ale mierne upravili. Zmena je ale iba v tom, že cieľová adres VRAM už nevstupuje do rutiny v registri HL, ale je ju treba pripraviť zápisom na adresu PrtCharDiaPos+1 vo vnútri samotnej rutiny. Adresa VRAM sa potom podľa očakávania po vypísaní znaku inkrementuje a uloží sa opäť na adresu PrtCharDiaPos+1. Tým sme dosiahli, že rutina PrtCharDia nemení ani register HL, čo sa nám môže hodiť.
V predošlej kapitole som na to priamo nepoukázal, ale asi ste si všimli, že sme farbu textu v rutine PrtCharDia nastavovali zápisom atribútu na adresu PrtCharDiaC+1, teda prakticky sme modifikovali hodnotu číselného operandu priamo v inštrukcii mvi c,0. Teraz rovnakým spôsobom nastavujeme aj adresu VRAM. Na adrese PrtCharDiaPos je inštrukcia lxi h,0, kedy zápisom na adresu PrtCharDiaPos+1 modifikujeme operand v tejto inštrukcii. Táto technika samomodifikujúceho sa kódu sa používa veľmi často, pretože daná hodnota jednak nemusí byť niekde inde v pamäti a zároveň to skráti a aj zrýchli kód. Samozrejme, ak by sme písali kód, ktorý bude bežať v ROM, táto technika sa, pochopiteľne, nedá použiť.
Prvá rutina PrtTxtPas, resp. PrtTxtPasAV vypisuje reťazec zadefinovaný makrom dp. Adresa reťazca vstupuje do rutiny v registri DE. Na úvod sa do registra B pripraví dĺžka reťazca a potom sa postupne vyberajú a vypisujú jednotlivé znaky. Vo výpisovej slučke sa ukazateľ na znaky reťazca posunie pred vypísaním znaku, keďže pri prvej iterácii potrebujeme preskočiť byte dĺžky reťazca. Po každom vypísanom znaku sa zníži počítadlo znakov v registri B. Kým nie je počítadlo nulové, pokračuje sa vo vypisovaní znakov. Keďže rutina PrtCharDia nemení okrem AF žiadne iné registre, nemusíme to pri volaní tejto rutiny robiť my. Pri volaní rutiny PrtTxtPasAV musí byť cieľová adresa VRAM v registri HL, ale pri volaní rutiny PrtTxtPas sa pokračuje pri výpise od poslednej pozície.
Druhá rutina PrtTxt0, resp. PrtTxt0AV vypisuje reťazec zadefinovaný makrom dz. Adresa reťazca vstupuje do rutiny opäť v registri DE. Výpisová slučka sa líši tým, že hneď na začiatku sa prevezme znak do akumulátora a overuje sa, či je to už koniec reťazca, teda že je akumulátor nulový. Ak to tak je (nastaví sa príznak Z), urobí sa návrat z rutiny. Po výpise znaku sa inkrementuje ukazateľ na text a slučka sa opakuje. Pre obe verzie rutín platí to isté, čo v predošlom odstavci.
Tretia rutina PrtTxtM, resp. PrtTxtMAV vypisuje reťazec zadefinovaný makrom dm. Pred vypísaním znaku sa vynuluje prípadne nastavený 7. bit v kóde znaku. Až po vypísaní znaku sa overuje, či to bol posledný znak, teda či jeho kód mal nastavený 7. bit a to testovaním príznaku znamienka S.
V hlavnom programe sú už obvyklé veci a jednoducho sa iba zavolajú jednotlivé rutiny s predchádzajúcim nastavením používaných registrov.
Za zmienku ešte stojí, že na zmazanie obrazovky sme použili rutinu Monitora ERASE. Tá zmaže obrazovku vyplnením VRAM hodnotou systémovej premennej COLOR. Rutina ERASE mení všetky registre.
Nakoniec je potrebné vysvetliť definíciu vlastného charsetu. Keďže náš zdrojový text píšeme na PC, kedy je zdrojový súbor uložený v kódovaní CP1250 (ANSI Central European) a naša rutina vypisuje znaky v kódovaní KOI-88čs, potrebujeme, aby sa naše texty už pri kompilácii "transformovali" do správneho kódovania. Toto nám umožňuje pseudoinštrukcia charset. Pomocou nej definujeme dvojice hodnôt pre transformáciu jedného kódu znaku na iný. Prvá hodnota je znak zapísaný v PC editore v kódovaní CP1250 a druhá hodnota je kód daného znaku v KOI-8čs (viď tabuľka znakov v návode k PMD 85-3). Táto definícia zmeny charsetu musí byť, samozrejme, uvedená skôr, ako je zadefinovaný prvý text, ktorý používa znaky s diakritikou. Pre vypnutie "prekódovania" znakov sa použije pseudoinštrukcia charset bez parametrov. Táto pseudoinštrukcia môže mať aj iný počet parametrov, ale to si už preštudujte v návode ku makroassembleru AS.
Ďalšie spôsoby výpisu textu
Uvedené rutiny pre výpis textu vyžadovali, aby adresy textu a VRAM boli pripravené v registroch DE a HL. Ukážeme si ale aj iný spôsob predania parametra do rutiny a to tak, že bude uvedené za volaním rutiny.
... call PrtTxt0R ; vypíš text dw AV(10,1) ; cieľová adresa VRAM dw T_Text4 ; adresa textu ... ;------------------------------------------------------------------------------ ; Vypísanie textu ukončeného nulou. Adresa VRAM a adresa textu sú uvedené za ; volaním rutiny. ; I: adresa VRAM a textu za volaním rutiny ako dw ; O: - ; M: HL, DE, AF PrtTxt0R: pop h ; návratová adresa z rutiny do HL mov e,m ; do DE prevezmi adresu VRAM, ktorá inx h ; je uvedená za volaním rutiny mov d,m inx h xchg ; prehoď adresu VRAM do HL shld PrtCharDiaPos+1 ; a ulož do rutiny PrtCharDia xchg ; vráť do HL adresu za volaním rutiny mov e,m ; do DE teraz prevezmi adresu textu inx h mov d,m inx h push h ; ulož novú návratovú adresu na zásobník PrtTxt0RL ldax d ; prevezmi znak do A ora a ; je to ukončovacia nula? rz ; áno, vráť sa call PrtCharDia ; vypíš znak inx d ; posuň ukazateľ na ďalší znak reťazca jmp PrtTxt0RL ; opakuj pre celý reťazec ;------------------------------------------------------------------------------ T_Text4: db "Tento reťazec je ukončený nulou," db " jeho adresa, spolu s cieľovou adresou VRAM, sú" db " uvedené za volaním rutiny PrtTxt0VA" dz " - najprv adresa VRAM, potom adresa textu."
Ako sme si povedali v popise inštrukcií, pri vykonávaní inštrukcie call sa na zásobník uloží návratová adresa, čo je adresa inštrukcie za touto inštrukciou. V našom prípade tam ale pri volaní rutiny PrtTxt0R nie je žiadna inštrukcia, ale sú tam dve dvojbytové hodnoty. Preto si na začiatku rutiny PrtTxt0R vyberieme z vrcholu zásobníka "návratovú" adresu do HL a odtiaľ si postupne vyzdvihneme najprv adresu VRAM (ktorú uložíme do rutiny PrtCharDia) a potom adresu textu do DE. Následne už skutočnú návratovú adresu za týmito dvoma adresami uložíme späť na zásobník. Zbytok rutiny je už známy.
Keďže T_Text4 používa znaky s diakritikou, musí byť v bloku za definíciou charset, tak ako ostatné texty. Tu sme to pre skrátenie textu už neuviedli. Pri definícii textu T_Text4 si ešte všimnite, že až na poslednom riadku textu je uvedené makro dz a predošlé riadky sú definované pomocou pseudoinštrukcie db. Ukončenie reťazca nulou chceme až na jeho konci a keďže sme chceli pre sprehľadnenie zdrojového kódu rozdeliť tento dlhý text do viacerých riadkov, museli sme pre prvé 3 riadky použiť pseudoinštrukciu db.
Za volaním rutiny ale môže byť aj samotný text. Princíp bude rovnaký. Zo zásobníka si vyberieme návratovú adresu, kde budeme mať adresu VRAM a aj samotný text.
... call PrtTxt0RT ; vypíš text dw AV(15,3) ; cieľová adresa VRAM dz "Tento text je za volanim rutiny PrtTxt0RT." ... ;------------------------------------------------------------------------------ ; Vypísanie textu ukončeného nulou. Adresa VRAM a aj samotný text sú uvedené za ; volaním rutiny. ; I: Adresa VRAM a text za volaním rutiny ; O: - ; M: HL, DE, AF PrtTxt0RT: pop h ; návratová adresa z rutiny do HL mov e,m ; do DE prevezmi adresu VRAM, ktorá inx h ; je uvedená za volaním rutiny mov d,m inx h xchg ; prehoď adresu VRAM do HL shld PrtCharDiaPos+1 ; a ulož do rutiny PrtCharDia ; v DE je teraz adresa textu PrtTxt0RTL ldax d ; prevezmi znak do A ora a ; je to ukončovacia nula? jz JumpDE ; áno, skoč sa nepriamo vrátiť z rutiny call PrtCharDia ; vypíš znak inx d ; posuň ukazateľ na ďalší znak reťazca jmp PrtTxt0RL ; opakuj pre celý reťazec JumpDE: xchg ; prehoď adresu skoku z DE do HL JumpHL: pchl ; skoč na adresu v HL
Vyzdvihnutie cieľovej adresy VRAM je rovnaký, ako v predošlom prípade. Po uložení adresy VRAM do rutiny PrtCharDia nám ale v DE zostane ukazateľ na samotný text a rutina pokračuje ako obvykle vypisovaním jednotivých znakov. Líši sa reakcia na ukončovaciu nulu, kde namiesto okamžitého návratu pomocou inštrukcie rz, skočíme na nepriamy návrat z rutiny (jz JumpDE). Návrat pomocou inštrukcie rz ani nemôžeme urobiť, pretože na zásobníku naša návratová adresa nie je, na rozdiel od predošlého prípadu, kedy sme ju tam uložili. My máme návratovú adresu prakticky v registri DE a preto skokom na návestie JumpDE najprv prehodíme túto adresu do HL a nepriamo na ňu skočíme.
Tu ešte upriamim pozornosť na jednu maličkosť. Určite ste si všimli, že po dosiahnutí konca reťazca register DE ukazuje na ukončovaciu nulu reťazca a práve na túto adresu sa vlastne prevedie návrat z rutiny. Máte pravdu, exaktne vzaté, mali by sme sa vrátiť o byte neskôr, pretože až tam začína ďalšia "platná" inštrukcia. Keďže ale 0 je kód inštrukcie nop, nevadí to a nič nebezpečné sa nestane.
Nepriamy návrat z rutiny by sa dal urobiť aj iným spôsobom a to uložením registra DE na zásobník push de a obvyklým návratom inštrukciou ret, ale táto alternatíva trvá 21T, oproti 9T z nášho riešenia.
Text za volaním rutiny v našom prípade nepoužíva znaky s diakritikou, keďže definíciu charsetu máme až neskôr. Šlo by to samozrejme realizovať tak, že definíciu charsetu by sme mali hneď na začiatku zdrojového textu (napr. v mieste, kde sme si definovali makrá) a tak všetky texty v zdrojovom texte by už mohli byť s diakritikou.
Vypisovanie textov z tabuľky
Niekedy je vhodné mať všetky texty/reťazce na jednom mieste a mať ich "očíslované". To znamená, že pre vypísanie konkrétneho reťazca zavoláme príslušnú rutinu s číslom reťazca.
Ukážeme si dva spôsoby. Prvý spôsob bude spočívať vo vyhľadávaní koncov textov a tým nájdení toho požadovaného (rutina PrtTxtN1). Druhý spôsob bude postavený na tabuľke adries textov (rutina PrtTxtN2).
... ; vypísanie textu podľa čísla textu - verzia vyhľadania textu PrtTN1: lxi h,AV(1,0) ; počiatočná adresa VRAM mvi c,8 ; 8 reťazcov PrtTN1L: mov b,c ; číslo reťazca do B call PrtTxtN1 ; vypíš daný reťazec inr h ; prejdi na ďalší textový riadok inr h inr h dcr c ; opakuj pre všetky reťazce jnz PrtTN1L ; vypísanie textu podľa čísla textu - verzia s tabuľkou PrtTN2: lxi h,AV(11,0) ; počiatočná adresa VRAM mvi b,8 ; 8 reťazcov PrtTN2L: push b ; odpamätaj počítadlo na zásobník call PrtTxtN2 ; vypíš daný reťazec pop b ; obnov počítadlo inr h ; prejdi na ďalší textový riadok inr h inr h dcr b ; opakuj pre všetky reťazce jnz PrtTN2L ... ;------------------------------------------------------------------------------ ; Vypísanie textu podľa čísla textu - verzia vyhľadania textu. ; I: HL=adresa VRAM, B=číslo textu <1, N> ; O: - ; M: DE, B, AF PrtTxtN1: lxi d,T_TextBase ; bázová adresa textov PrtTxtN1B: dcr b ; zníž číslo textu jz PrtTxt0AV ; našli sme, skoč ho vypísať PrtTxtN1L: ldax d ; vezmi znak inx d ; posuň ukazateľ ora a ; koniec textu? jnz PrtTxtN1L ; nie, hľadaj jeho koniec jmp PrtTxtN1B ; opakuj, kým nenájdeš požadovaný text ;------------------------------------------------------------------------------ ; Vypísanie textu podľa čísla textu - verzia s tabuľkou adries textov. ; I: HL=adresa VRAM, B=číslo textu <1, N> ; O: - ; M: DE, BC, AF PrtTxtN2: xchg ; adresa VRAM dočasne do DE mov a,b ; číslo textu do A add a ; x2 - adresa v tabuľke má 2 byty mov c,a ; ulož offset do BC mvi b,0 lxi h,T_TxtBTab-2 ; adresa tabuľky adries textov dad b ; pripočítaj offset mov a,m ; vyber adresu textu do HL inx h mov h,m mov l,a xchg ; prehoď adresu VRAM a adresu textu jmp PrtTxt0AV ; a skoč vypísať text ;------------------------------------------------------------------------------ ; Tabuľka textov T_TextBase: T_TxtB1: dz "Prvý" T_TxtB2: dz "Druhý" T_TxtB3: dz "Tretí" T_TxtB4: dz "Štvrtý" T_TxtB5: dz "Piaty" T_TxtB6: dz "Šiesty" T_TxtB7: dz "Siedmy" T_TxtB8: dz "Ôsmy" ; Tabuľka adries textov T_TxtBTab: dw T_TxtB1,T_TxtB2,T_TxtB3,T_TxtB4 dw T_TxtB5,T_TxtB6,T_TxtB7,T_TxtB8
V prvej rutine PrtTxtN1 sa najprv pripraví do registra DE bázová adresa jednotlivých textov. Následne sa zníži číslo požadovaného textu v registri B a ak sa register B vynuloval, máme v registri DE adresu požadovaného textu a môžeme ho vypísať. Inak sa potom prechádza po jednotlivých znakoch aktuálneho textu a hľadá sa jeho koniec. Tým sa dostaneme na začiatok ďalšieho textu. Toto sa opakuje, kým sa teda nevynuluje register B.
Vo fragmente kódu od návestia PrtTN1 sa postupne vypíše všetkých 8 textov od ôsmeho po prvý. Číslo textu a zároveň počítadlo je v registri C, keďže rutina PrtTxtN1 register B vynuluje. Jednotlivé texty sa vypisujú na osobitných riadkoch, čo sa udeje už známou inkrementáciu vyššieho bytu adresy VRAM o 3.
V druhej rutine PrtTxtN2 sa číslo textu vynásobí dvoma, čím dostaneme offset do tabuľky adries jednotlivých textov T_TxtBTab. Do registra HL sa uloží adresa tejto tabuľky znížená o 2, keďže číslujeme texty od 1. Po pripočítaní offsetu k adrese tabuľky sa vyberie už samotná adresa textu do HL a po prehodení s adresou VRAM sa text vypíše.
Fragment kódu pre výpis všetkých textov touto rutinou je veľmi podobný predošlému, len počítadlo a teda aj číslo textu je priamo v registri B a preto sa musí odpamätávať na zásobníku.
Každá z týchto dvoch rutín má svoje výhody aj nevýhody. Prvá je časovo náročnejšia a čím viac je textov, tým dlhšie trvá nájdenie posledného textu. Nepotrebuje však osobitnú tabuľku adries textov, ktorá pri veľkom počte textov zaberá dvojnásobok ich počtu. Druhá rutina potrebuje pre nájdenie ktoréhokoľvek textu vždy rovnaký čas, ale vyžaduje už spomenutú tabuľku adries textov.
Na stiahnutie
Opäť je tu na stiahnutie celý projekt pre túto kapitolu a opäť sú všetky príklady v jednom zdrojovom súbore.
<<Vypisujeme znaky na obrazovku Vypisujeme čísla >>