ASSEMBLY GYAKORLAT 2. Irta Kaproncai Tamas [tomcat@szif.hu] -CIKLUSSZERVEZES- Vegyuk elo az utolso programunk, mely egy feher pontot jelenitett meg a grafikus kepernyo kozepen: MOV AX,13H INT 10H ; Grafikus kepernyo bekapcsolasa MOV AX,0A000H MOV ES,AX ; Kepernyo szegmens cimenek beallitasa ES-be MOV [ES:100*160+160],BYTE 15 ; A kep kozepere kiirodik a 15. szin: a feher MOV AH,0 INT 16H ; Varakozas egy billentyu lenyomasara RETN ; Visszalepes a DOS-hoz 5a. Most ne egy, hanem tobb pontot jelenitsunk meg a kepernyon, mondjuk az elso sor legyen feher! Megvizsgalva az eredemi munkat vegzo utasitast, mit kell modositanunk? MOV [ES:100*160+160],BYTE 15 Mindig ujabb memoriacimre kell kiirni a feher pontot, ezert a konstans cimet kicsereljuk egy regiszteres cimzesre. Hasznaljuk pl. DI-t: MOV [ES:DI],BYTE 15 A kepernyon a jobbra levo pixelhez ugy jutok, hogy eggyel novelem a memoria cim erteket. Tehat akkor ezen ket utasitas kore kell ciklust szerveznunk: MOV [ES:DI],BYTE 15 INC DI A legegyszerubb ciklus igy nez ki: ; ciklusszamlalonak valasztok egy regisztert pl. CX ; kezdeti ertekenek, azt adom ahanyszor az iteraciot ismetelni szeretnem MOV CX,320 ; 320 keppont van egy sorban ; ahol kezdodik a ciklusmag, azt egy cimkevel meg kell jelolnunk ; a cimket a sor elejere irjuk es kettosponttal zarjuk le. ; (Javaslom, hogy a cimke soraba ne irjunk utasitast!) ciklus1: ; kovetkezik a ciklus magja, most pl. MOV [ES:DI],BYTE 15 INC DI ; lezarjuk a ciklusunkat, 1. a ciklusszamlalot csokkentjuk DEC CX ; 2. amennyiben nem nulla, akkor visszaugrunk a ciklusmagot jelolo cimkere. ; a felteteles ugro utasitasok a FLAG regiszter allapota alapjan dontik el ; a tovabbhaladas iranyat (ugranak vagy se), a FLAG regiszter allapotat ; pedig az UTOLSO utasitas allitja be! (Ezert itt nem kerulhet mas utasitas ; a DEC es a JNZ koze!) JNZ ciklus1 A ciklus ELE meg kelleni fog egy utasitas, ami DI kezdeti erteket allitja be: MOV DI,0 ; a rajzolast a bal-felso sarokban kezdje a ciklusunk 5b. Akinek sikeresen megjelent a feher sor, fesse az egesz kepernyot feherre! Akinek meg nem fordult le a programja, nem baj ha nem erti az angol hibauzenetet, de nezze meg, hogy hanyadik sorban van a hiba. Pl. p.asm:5: symbol `A000H' undefined uzenetbol mindenki latja, hogy a program 5. soraban van a hiba, ott gepeltunk el valamit (a hexa szam bevezeto nullja hianyzik). A feladat megoldasa: MOV CX,320*200 ; 200 sor van ez 320*200 bajtot jelent 6a. Most a felsosorban jelenitsuk meg mind a 256 szint (1 szinhez 1 pixelt)! Most mit kell valtoztatnunk? MOV [ES:DI],BYTE 15 A konstans 15. szint is kicsereljuk egy 8 bites regiszterre, pl. AL-re! MOV [ES:DI],AL es a szineket is leptetem a cikluson belul: INC AL a ciklus elott inicializalom AL-t is: MOV AL,0 ; az elso szin a kepernyon a paletta 0. szine legyen! az iteraciot pedig most csak 256-szor kell vegrehajtani! MOV CX,256 6b. Modositsuk ugy a programot, hogy a ciklusszamalalot elmentjuk a verembe! Elofordulhat, hogy a ciklusmagjaban az osszes regiszterre szukseg van, akkor vagy hasznalhatnank memoriavaltozot, vagy elmenthetjuk a verembe a ciklus valtozo erteket. Nezzuk meg az utobbi esetet: A ciklus nyitasat erre kene kicserelni: MOV CX,256 ciklus1: PUSH CX A ciklus zarasat erre kene kicserelni: POP CX DEC CX JNZ ciklus1 Figyelem: mindig minden PUSH-nak legyen meg a POP parja, mert kulonben le fog fagyni a programunk! 7a. Most jelenitsuk meg ugyanezt az egy sort az osszes sorban! Ehhez mi kell? - egy duplaciklus! A belso ciklusnak jo lesz az elozo ciklusunk es meg kore szervezunk egy kulso ciklust: MOV DI,0 ; kuslo ciklus inditasa MOV CX,200 ; ennyi sor van a kepnyon kulso: PUSH CX MOV AL,0 ; minden sor a 0. szinnel kezdodjon! ; korabbi ciklusunk MOV CX,320 ciklus1: PUSH CX MOV [ES:DI],AL INC DI INC AL POP CX DEC CX JNZ ciklus1 ; Ezzel egy sor megvan a kepernyon. Vizsgaljuk meg mennyi is most DI erteke? ; Ahhoz, hogy a kovetkezo sor elejere mutasson DI, hozza kell meg valamennyit ; adni. MOV DI,320-256 ; teljes sor - a mar kirajzolt keppontok szama POP CX ; kulso ciklus bezarasa DEC CX JNZ kulso Ha valakinek elszall a programja, annak vagy a PUSH-POP utasitasok nincsenek megfeleloen parban, vagy a felteteles ugrrasa rossz cimkere ugrik. 7b. Apro modositas: most ne 256 szin latszon egy sorba, hanem csak 128! Ket helyen kell modositani: MOV CX,256/2 MOV DI,320-256/2 7c. Oldjuk meg a feladatot ugy, hogy csak egy sorban kelljen modositani! Ehhez, egy sor rajzolasa elott (tehat a belso ciklus elott) elmentjuk DI erteket (a verembe). A sor kirajzolasa utan (tehat a belso ciklus utan) elovesszuk DI-be (a verembol), hogy hol is kezdtunk el rajzolni. Akkor most mennyit kell DI-hez hozzadni? - egy sor hosszat (320-t). Fuggetlenul attol mennyit is rajzoltunk a kepernyore, konnyen a kovetkezo sor elejere tudunk irni a kovetkezo iteracioban. Vagyis pl. PUSH DI ; <- itt kell elmenteni DI erteket! MOV AL,0 ; minden sor a 0. szinnel kezdodjon! MOV CX,72 ; mindegy mennyi szint jelenit meg a belso ciklus. ; ... POP DI ADD DI,320 ; egy sor hossza bajtokban POP CX ; kulso ciklus bezarasa (Ne feledkezzunk el arrol, hogy a verembol mindig az utoljara betett ereteket tudjuk eloszor kivenni). 8a. Jelenitsunk meg egy piros teglalpot a karakteres kepernyo kozepen! A teglalap legyen mondjuk negyede a teljes kepnek, azaz 20*6 karakter! A megoldashoz hasznaljuk fel az elozo programot a dupla ciklus szervezesenel, es az ASMGYAK1.TXT-t a karakteres kepernyo kezelesehez! - benne lehetoseg onallo munkara... Kozosen dolgozzuk ki a megoldast a tablanal: A video uzemmod valtara most nincs szukseg, ES regiszter beallitasaval kezdunk: MOV AX, ? - 0B800H MOV ES,AX Kell-e valamit beallitani a duplaciklus elott? - Igen, DI kezdoerteket: MOV DI, ? - 9*160+60+1 Fent 9 karakter marad ki: (25-6)/2 Balra 30 karakter marad ki: (80-20)/2 A plusz 1 azert kell, mert a paros bajtokon a karakter ASCII kodja lenne es nekunk most a karakter szinet kell allitani, ami a paratlan bajtokon van. MOV CX, ? - kulso ciklus 6-szor fusson le! kulso: PUSH CX PUSH DI ; Elmentjuk DI-t, hogy konnyen a kov. sorra alljunk MOV CX, ? - 20 belso: PUSH CX MOV [ES:DI],BYTE ? ; - 44H (elol-hatul piros szin) ; Itt lehetne a legkonnyebben hibazni, mert most nem INC DI kell! ADD DI,2 ; karakteres kepernyon 2 bajttal odebb lesz a kov. szin POP CX DEC CX JNZ belso POP DI ADD DI,? - 160 bajt tartozik egy sorhoz POP CX DEC CX JNZ kulso 8b. Legyen nagyobb a teglalap, mondjuk a kep fele! Modositott sorok: MOV DI,6*160+40+1 ; fent-lent 6 sor, balra-jobbra 20 karakter marad ki MOV CX,12 ; 12 karakter magas a teglalp, 12 sort rajzolunk MOV CX,40 ; 40 karaktert szeles a teglalap 8c. Emeljuk ki a modositando konstansokat a program elejere! szelesseg EQU 40 magassag EQU 12 Akkor a korabbi konstansokat csereljuk ki igy: MOV DI,(25-magassag)/2*160 + (80-szelesseg)/2*2 + 1 MOV CX,magassag MOV CX,szelesseg Igy csak egy helyen, a program elejen eleg lesz modositanunk! Megjegyzesem ============= A DEC CX, JNZ utasitas par helyettesitheto a LOOP utasitassal. Tehat pl. DEC CX JNZ ciklus1 ekvivalens a LOOP ciklus1 utasitassal. De! DEC DX JNZ ciklus2 nem cserelheto le LOOP utasitassal, mert a LOOP szigoruan csak CX regiszterrel mukodik!!! Ehhez hasonloan a kovetkezo ket utasitas is helyettesitheto MOV [ES:DI],AL INC DI helyett egyszeruen: STOSB A kovetkezo feladatokat a ciklusszervezes onnalo gyakorlasahoz adom: ==================================================================== 9a. Jelenitsuk meg a ASCII tablat a kepernyo tetejen! - Ez melyik feladatra hasonlit? - A szinek megjelenitesere, az egyetlen hibazasi lehetoseg, hogy itt DI erteket 2-vel kell leptetgetni. 9b. A ASCII tabla a keprnyo kozepen egy teglalapba jelenjen meg! 9c. Ha elfogyott a 256 karakter fejezzuk be a teglalap rajzolasat! 10a. A teglalapunk kore rajzoljunk keretet is (pl. legyen kek szinu)! 10b. A keret ne szinnel legyen, hanem a duplavonalas jelekbol! 11a. Toltsuk fel a karakteres kepernyot egyre kisebb mas-mas szinu keretekkel! - Ezt tehetjuk ugy, hogy a nagyobb teglalap kozepebe egyre kisebb teglalapokat rajzolunk mas-mas szinnel. Ehhez 3 egymasba agyazott ciklus kell! - Tovabbi nehezseg, hogy a ciklus szamlalok nem konstans ertekerol indulnak. 12a. Rajzuljunk 8*8-s sakktablat, egy mezo legyen 1 karakter! 12b. Rajzoljunk 8*8-s sakktablat, egy mezo legyen 6*3 karakter! 13a. Rajzoljuk a kepernyore csigavonalat a karakteres kepernyon! - A kepernyore termeszetesen nem csak vizszintes iranyba lehet rajzolni, hanem fuggolegesen is. - Ha jobbra akartam az adott pixeltol rajzolni, akkor 1-t adtam hozza, ha ala szeretnek rajzolni, akkor egy teljes sor hosszat kell hozza adni! Megoldasok ========== 10a. A teglalapunk kore rajzoljunk keretet is (pl. legyen kek szinu)! Eloszor egy ciklussal megrajzoljuk a kek keret felso sorat. Aztan egy dupla ciklussal megrajzoljuk a piros teruletet + a belso ciklus elott es utan kiteszunk egy-egy kek karaktert. Vegul egy ujabb onallo ciklussal megrajzoljuk a kek keret also sorat. szelesseg EQU 40 magassag EQU 12 MOV AX,0B800H MOV ES,AX MOV DI,(25-magassag)/2*160 + (80-szelesseg)/2*2 - 1 ; csak azert -1 es nem +1, mert a keret miatt szelesebb ; a teglalap PUSH DI MOV CX,szelesseg+2 felso: PUSH CX MOV [ES:DI],BYTE 11H ADD DI,2 POP CX LOOP felso POP DI ADD DI,160 MOV CX,magassag kulso: PUSH CX PUSH DI MOV [ES:DI],BYTE 11H; ez a bal-oldali kek karakter ADD DI,2 MOV CX,szelesseg belso: PUSH CX MOV [ES:DI],BYTE 44H ADD DI,2 POP CX LOOP belso MOV [ES:DI],BYTE 11H ; ez a jobb-oldali kek karakter ADD DI,2 ; itt DI leptetese felesleges, csak megszokasbol irtam POP DI ADD DI,160 POP CX LOOP kulso ; Idemasoltam a felso sort huzo ciklust. Itt DI mentese mar felesleges lenne ; hiszen ugyis veget er a program. Ugyanigy CX erteket se lenne muszaj ; mindehol a verembe eltarolni, csak azert irtam megis mindehova oda, mert ; ez hibat nem okoz es mivel mashol meg kell, ezert nem baj, ha megszokjuk. PUSH DI MOV CX,szelesseg+2 also: PUSH CX MOV [ES:DI],BYTE 11H ADD DI,2 POP CX LOOP also POP DI ADD DI,160 MOV AH,0 INT 16H RETN 11a. Toltsuk fel a karakteres kepernyot egyre kisebb mas-mas szinu keretekkel! Az elozo programot is megoldhattuk volna ugy, hogy eloszor egy nagyobb kek teglalapot rajzoltunk volna es aztan meg bele egy kisebb pirosat. Most ezt a technikat fogjuk kovetni a megoldasnal. A teglalap rajzolo duplaciklusunk kore szervezunk egy harmadik ciklust. AL-ben tarolom az eppen rajzolando teglalap szinet BX-ben tarolom az eppen rajzolando teglalap szelesseget DX-ben tarolom az eppen rajzolando teglalap magassagat Kiemelem a szin konstansot is. szin EQU 44H ; a legkulso teglalap szine (piros elol-hatul) szelesseg EQU 80 ; a legkulso teglalap szelessege magassag EQU 25 ; a legkulso teglalap magassaga MOV AX,0B800H MOV ES,AX MOV DI,(25-magassag)/2*160 + (80-szelesseg)/2*2 + 1 MOV BX,szelesseg MOV DX,magassag MOV AL,szin MOV CX,(magassag+1)/2 ; felteszem, hogy a teglalap szelesebb mint magasabb harmadik: PUSH CX PUSH DI ; elmentem, hol kezdtem rajzolni az aktualis teglalapot MOV CX,DX ; aktualis magassag kulso: PUSH CX PUSH DI MOV CX,BX ; aktualis szelesseg belso: PUSH CX MOV [ES:DI],AL ADD DI,2 ; aktualis szinnel megjelenit egy karaktert POP CX LOOP belso POP DI ADD DI,160 POP CX LOOP kulso POP DI ; a elozo teglalapot itt kezdtem rajzolni ADD DI,160+2 ; a kovetkezo tegalalap egy sorral lejebb es ; egy karakterrel jobbra fog kezdodni ADD AL,11H ; az eloter es a hatter szinet is eggyel noveljuk SUB BX,2 ; a szelesseget kettovel csokkentjuk SUB DX,2 ; a magassagot kettovel csokkentjuk POP CX LOOP harmadik MOV AH,0 INT 16H RETN