The ULTIMATE HELP FOR ASSEMBLER by Shang Tsung Version 1.0.1 Last updated on 20th of June 1999. (Custom2) Ŀ SADRAJ Ĵ Poglavlje 1 Memorijski modeli, Registri opte namene, Tipovi podataka, Hexadecimalni brojevi, Binarni brojevi, Naredbe .MODEL, .ORG, MOV, INT; Interrapt 21h Ĵ Poglavlje 2 Interapt 16h, Naredbe CMP, JMP; J... naredbe Ĵ Poglavlje 3 Raunske operacije, Naredbe ADD, SUB, MUL, IMUL, DIV, IDIV, INC, DEC Rad sa procedurama Ĵ Poglavlje 4 Stringovi, Pointeri, Segment, Offset, Memorija, Segmentni registri, RET, Stek, COM i EXE fajl, Segmentacija, Novi red, Naredbe PUSH, PUSHA, PUSHAD, POP, POPA, POPAD Ĵ Poglavlje 5 Grafika, Interrapt 10h, EQU, Stack Registri, Video memorija Ĵ Poglavlje 6 Fajlovi, Rad sa fajlovima, FLAG-ovi, Indeksni registri, XOR, Opis PSP-a, LOOP The ULTIMATE HELP FOR ASSEMBLER by Shang Tsung Version 1.0.1 Predgovor Kako je UHFA personalizovan, originalni predgovor je izbrisan, a novi upravo itate. Autor (tj. Ja) ne odgovara na bilo koji nain za ispravnost napisanog teksta, kao ni za ispravnost programa koji se javljaju u njemu. Tekst je pisan tokom druge polovine 1998 godine i prvih 1-2 meseci 1999, sa ciljem da priblii polje programiranja u Asembleru za uenike Matematike gimnazije, no koristi se i na FTN-u, a sada i na PMF-u (koji je autor u meuvremenu upisao), kako bi studentima koji se izmeu ostalog bave i programiranjem u Asembleru napravio uvod u taj programski jezik (niskog nivoa). Kako sam tekst u poetku nije imao takve predispozicije, nain na koji je pisan i govor u njemu mogao bi se okarakterisati kao "slobodniji". No, kako je vreme odmicalo tekst je dobijao sve vie na znaaju za one koji se time bave, pa je uinjen pokuaj da se dobije neki "zvanian ton", te su odreene celine teksta relativno razliite iz toga aspekta, no nikako na utrb kvaliteta. Izmene koje su nainjene u ovo izdanju koje nosi oznaku "Personalized for PMF", su minorne. Ponovo je napisan predgovor, a pogovor u kome su najavljena naredna izdanja je obrisan, jer se naredna izdanja (za razliku od vremena pre 2,3 godine) ne oekuju. No, mislim da je i ovaj tekst sam po sebi dovoljan dobar pokreta za neko dalje izuavanje, ako se neko za to odlui. I poslednja izmena, dodata je jo jedna osoba u zahvalnici na kraju teksta, ijom zaslugom je ovaj fajl uopte i stigao do vas. Pozdrav, i puno sree (naroito studentima na PMF-u :) U Novom Sadu, 24. April 2001 Shang Tsung stsung@bigfoot.com 1 Memorijski modeli, Registri opte namene, Tipovi podataka, Hexadecimalni brojevi, Binarni brojevi, .MODEL, .ORG ,MOV, INT, Interrapt 21h Prvi zadatak: Napisati program u Assembler-u(sigurno ne u PASCAL-u?!!!) koji vraa kontrolu DOS-u. Da pojasnim.Program ne treba nita da radi, ali u svakom programu pisanom u assembler-u mora postojati deo koji e vratiti kontrolu DOS-u tj. operativnom sistemu, dakle deo kojim se u stvari naputa program. U ostalim jezicima vieg nivoa (C, PASCAL...) to se automatski dodaje programu iako mi to ne vidimo, ali ovde nije tako - u Assembler-u sve mora sam da uradi. E pa ponimo... *NAPOMENA : Fajl koji emo napisati se generie u .COM fajl. Program No. 1.1 ; PROGRAM prvenac (PRVI.ASM) ;iza take-zarez moe napisati ta god hoes - ignorie se ; Copyright(C) 1998. by Shang Tsung .MODEL tiny ; memorijski model .code ; segment instrukcija programa org 100h ; "Entry Point" - poetna adresa main: ; glavni program(nije vano ta pie-moe i vazbuka:) mov ax,4c00h ; u ax registar gurni 4c00h(h je heksadecimalno) int 21h ; poziv interaptu 21h end main ; kraj programa -.MODEL tiny Makro naredba ".MODEL" ima sledeu sintaksu : .MODEL ime_modela gde "ime_modela" specificira memorijski model koji e se koristiti. Znaj da postoje sledei memorijski modeli: TINY,SMALL,MEDIUM,COMPACT,LARGE,HUGE. Ŀ MODEL OPIS Ĵ TINY 64K za kod i podatke Kod i podaci su tipa NEAR Ĵ SMALL 64K za kod i 64K za podatke Kod i podaci su tipa NEAR Ĵ MEDIUM 1MB za kod i 64K za podatke Kod je tipa FAR, a podaci su tipa NEAR Ĵ COMPACT 64K za kod i 1MB za podatke Kod je tipa NEAR, a podaci su tipa FAR Ĵ LARGE 1MB za kod i 1MB za podatke stim to jedan niz ne moe biti dui od 64K. Ovo je bitno samo kada program u Assembler-u treba povezati sa programom u C-u, PASCAL-u ili nekim drugim programskim jezikom visokog nivoa. Kod i podaci su tipa FAR. Ĵ HUGE 1MB za kod i >64K za podatke, stim to jedan niz moe biti dui od 64K. Programeru u Assembleru memorijski modeli LARGE i HUGE se uopte ne razlikuju, sem ako ne treba povezati program u Assembler-u sa programskim jezikom visokog nivoa. Kod i podaci su tipa FAR. *Slovo K je skraenica za KILOBAJT (1024 bajta) Da se brzo osvrnem na NEAR i FAR. NEAR znai da se i kod i podaci mogu dosegnuti samo OFFSET-om tj. 16-o bitnim pointerom. FAR znai da se i kod i podaci mogu dosegnuti preko para SEGMENT:OFFSET tj. preko 20-o bitnih pointera. *O tome ta su pointeri, SEGMENT, i OFFSET bie rei neto kasnije. Nama je u ovom sluaju dovoljan MODEL tiny - 64K za kod i podatke. (ta e nam vie ?!!! ;) (Kako emo veinom generisati COM falove, samo emo moi koristiti model TINY) Memorijski model je potrebno specificirati samo ako se koristi pojednostavljeno deklarisanje segmenata (tipa. .CODE ili .DATA). *Ovo je potanko objanjeno u 4-om poglavlju : "Segmentacija". -.CODE Deklarisali smo segment u kome e se nalaziti kod, tj. instrukcije naeg programa. Ovo je pojednostavljeno deklarisanje segmenata. *O segmentima e biti rei u etvrtom poglavlju. -org 100h (ovo vai ako hoe da generie .COM fajl) Makro naredbom "org" modifikovali smo tzv. "Location counter" tj. broja lokacija u okviru segmenta u programu, na vrednost 100h. *Slovo "h" nakon broja oznaava da je broj predstavljen u heksadecimalnoj notaciji. Ovaj broja se pridruuje svakom novom segmentu koji deklariemo(.CODE u naem sluaju) i on je inicijalno nula. No, kako mi elimo da generiemo .COM fajl, na samom poetku broja lokacija moramo modifikovati u broj 100h, tako da deklarisani segment pone od navedene adrese a ne od 0. Ovo moramo uraditi jer kada je u pitanju .COM fajl, sve to on koristi mora biti u jednom segmentu(jedan segment u REAL modu moe biti najvie 64K dugaak), dakle i varijable, i same instrukcije koje ine telo programa, i stek, i PSP. E, upravo PSP, ta god to bilo, je razlog zbog koga menjamo broja lokacije. Naime, PSP COM fajla se mora smestiti na sam poetak segmenta memorije u koji se dotini fajl uitava. Kako PSP ima duinu od 256 bajta (ili 100h bajta), to COM fajl mora poeti uitavanje u memoriju od adrese 100h(ili 256). *Detaljni opisi segmenata i brojaa lokacije ima u etvrtom poglavlju, dok je PSP detaljno opisan u estom poglavlju. No, polako. -main: Na ovakav nain, deklarisali smo LABELU. Konkretno, ova labela je potrebna kompajleru da bi znao gde je poetak a gde kraj instrukcija programa. SAV KOD MORA BITI ISPOD OVE LABELE. *Ona ne mora da se zove "main" ( moe i "vazbuka" ;). Kada se labela deklarie, njoj se pridruuje vrednost brojaa lokacije na mestu na kom je navedena. Opti oblik deklarisanja labela uzima sledei oblik : ime_labele LABEL tip_labele Tip labele moe biti BYTE, WORD, DWORD(Double WORD), QWORD, TBYTE, NEAR FAR, ili ime neke strukture. Ako navedemo ime_labele praenu dvotakom, deklarisali smo NEAR labelu. Navoenje imena deklarisane labele u okviru programa je u sutini jedna olakica, jer ako na primer elimo da se preko odreene naredbe izvravanje instrukcija programa nastavi na nekom mestu(tj. da se "skoi"), na tom mestu emo definisati labelu i jednostavno ime labele navesti kao destinaciju skoka-umesto imena labela, pri kompajliranju nai e se upravo broj koji joj je pridruen. *Vie rei o ovome ima neto kasnije. -mov ax,4c00h -MOV (move) naredba obavlja pomeranje tj. kopiranje vrednosti iz jednog registra u drugi, iz registra na memorijsku lokaciju ili sa memorijske lokacije u registar. Ona zahteva 2 parametra : izvor i destinaciju. Prvo se navodi destinacija pa onda izvor. Primeri: mov ax,cx u registar AX kopira se sadraj registra CX mov dx,70 u registar DX upisuje se vrednost 70 mov [slovo],'a' na memorijsku lokaciju koju je zauzela promenljiva slovo, upisuje se slovo 'a' (tj.ASCII kod slova 'a') (Ovde je dodue potrebna specifikacija, ali da ne gnjavim ;) mov ax,bl BEEP !!! Malo sutra. AX je 16-o bitni a BL 8-o bitni registar (to je objanjeno malko kasnije). Znai naredbom mov ax,4c00h se u registar AX(akumulator) upisuje hexadecimalva vrednost 4c00. U sluaju da ne poznaje registre i hexadecimalne brojeve evo i o tome : Registri opte namene i tipovi podataka Registri uopte su sastavni deo procesora kome su potrebni da bi obavljao neke operacije. Sledei registri su 16-o bitni registri opte namene : Ŀ Registar Opis Ĵ AX acummulator (akumulator) BX base (bazni registar) CX count (registar broja) DX data (registar podataka) Svaki od ovih registara je podeljen, odnosno sastavljen iz dva dela i to : Ŀ Registar AX na AH i AL. Registar BX na BH i BL. Registar CX na CH i CL. Registar DX na DH i DL. Svaki od ovih registara je 8-o bitan. To izgleda otprilike ovako : Ŀ BH (High) BL (Low) (Viih 8 bita) (Niih 8 bita) Ĵ registar BX Svaki od ovih registara sastavljen je od niza bitova. To je upravo i predstavljeno njihovim nazivom : 8-o bitni registar ima 8 bita, 16-o bitni esnaest itd. Jedan bit je najmanja koliina informacije za raunar i moe imati vrednost 0 ili 1. *Primeti, da je u pitanju baratanje ciframa u binarnom brojnom sistemu to je objanjeno neto kasnije(no ovo bi ve trebao da zna). Postoji i naziv za grupacije od po 4 bita - NIBBLE. Ŀ High Nibble Low Nibble registra BH registra BH Ĵ BH BL Ĵ BX 80386 i noviji procesori raspolau i sledeim registrima : Ŀ Registar Opis Ĵ EAX Accumulator EBX Base ECX Counter EDX Data Svaki od ovih registara je 32-o bitni. >>> PAZI! <<< Registri EAX,EBX,ECX,EDX su u stvari Extended(produeni) registri registara AX,BX,CX,DX respektivno. To znai da je AX deo registra EAX tj. AX predstavlja donjih 16 bita registra EAX. Isto vai i za ostale 32-o bitne registre. To je sve slino npr. registru AL. Registar AL predstavlja donjih 8 bita registra AX to znai da je registar AX u sutini Extended registar registra AL. No za razliku od registra AX, ne moe se direktno optiti sa gornjih 16 bita 32-o bitnog registra (npr. EAX) kao to se sa AH moe optiti sa gornjih 8 bita registra AX. U sluaju da ne zna kako sve to izgleda prilaem grubu skicu registara AX, BH, i ECX. Registar AX izgleda ovako : *Broj 1011100001110101 je tu samo radi lepeg prikaza. U stvari tu e se nai broj koji mi upiemo koristei odgovarajue instrukcije. Broj je u binarnom formatu (drugaije ni ne moe :). Ŀ 1011100001110101 bit 0 (najnii bit) bit 1 bit 2 bit 3 bit 4 bit 5 bit 6 bit 7 bit 8 bit 9 bit 10 bit 11 bit 12 bit 13 bit 14 bit 15 (najvii bit) Registar BH izgleda otprilike ovako : Ŀ 00110001 BL Ĵ BH BX *Pazi! BH je samo deo BX registra a ne zaseban registar, ali sa njime moe optiti kao da jeste zaseban. Registar ECX izgleda ovako : bit 31(najvii bit) bit 0 10111000011101011011100001110101 Ĵ CH (8 bita) CL (8 bita) Ĵ CX (8+8=16 bita) Ĵ ECX Ĵ Ukupno 32 bita Tipovi podataka : U raunarskoj terminologiji : Ŀ 8 bita 1 bajt byte 16 bita 2 bajta word 32 bita tj. 4 bajta ine tip double word 64 bita 8 bajta quad word 80 bita 10 bajta ten-byte unit Ako te ba zanima, u programskim jezicima C i PASCAL to je ovako predstavljeno : Ŀ Assembler C PASCAL Ĵ byte char char, byte, shortint word int, enum integer, word double word long, float longint, single quad word double double, comp ten-byte unit long double extended Kasnije emo pri definisanju promenljivih koristiti sledee oznake koje predstavljaju ove tipove : Ŀ Oznaka TIP Zauzima (bajta) Ĵ DB byte 1 DW word 2 DD double word 4 DQ quad word 8 DT ten-byte unit 10 Kasnije, kada nam bude trebalo da definiemo neku promenljivu, to emo raditi na sledei nain : ime_promenljive tip_promenljive sadraj_promenljive Na primer: moja_promenljiva DW 200 dup (0) ime tip zauzmi 200 delova memorije tipa DW i popuni ih nulama, znai zauzee se ukupno 200*2=400 bajta memorije moja_promenljiva DT 10 dup(0) - e zauzeti 10*10=100 bajta moja_promenljiva DQ 2 dup(?) - e zauzeti 2*8=16 bajta memorije s' tim to se nita ne upisuje u te memorijske lokacije po inicijalizaciji moja_promenljiva DB 4 - Zauzima se jedan bajt memorije i u njega se upisuje vrednost 4 Ime promenljive nije obavezno navoditi. Ono je tu da bi mogli optiti sa promenljivom. Sasvim slobodno moe napisati i DB 100 dup (0) i 100 bajta memorije e biti rezervisano, no kako dotinim bajtovima pristupati? Sadraj promenljive takoe nije obavezno navoditi, i ako se ne navede kompajler podrazumeva "?". O ostalim registrima e biti vie rei drugi put. Kraj - Registri opte namene i tipovi podataka Ukoliko ve nisi upoznat sa hexadecimalnim brojevima, evo i o tome. Hexadecimalna notacija Hexadecimalni brojevi za osnovu imaju broj 16( za razliku od decimalnih kojima je osnova 10 ). E sad. Kako postoje brojevi (10) (11) ... (15) koji su u hexadecimalnoj notaciji jednocifreni koriste se slova abecede da ih zamene. Tako je : Ŀ Decimalno Hexadecimalno Ĵ 10 A 11 B 12 C 13 D 14 E 15 F Naravno, mogu se koristiti i mala slova. Konvertovanje decimalnih brojeva u hexadecimalne Imamo npr. broj 123 decimalno. Konvertovanje se vri na sledei nain : brojĿ osnova sistema u koji treba konvertovati dotini broj Ŀ 123:16=7 i ostatak 11 najnia cifra u broju Ŀ 7:16=0 i ostatak 7 sledea cifra u broju za jedan via od predhodne Znai 123 decimalno je 7B hexadecimalno. ( B hex. je 11 dec. ) Ovaj nain konvertovanja brojeva vai i ako eli da, eto broj 123 decimalno, konvertuje u brojni sistem sa osnovom npr. 4, samo to pri deljenju nee deliti brojem 16 nego brojem 4. Svi brojevi koji e biti predstavljeni kao da su u nekom registru, bie u binarnom formatu (osnova sistema je 2). Slino: Ŀ Decimalno Hexadecimalno Ĵ 1680 690 Ĵ 109383 1AB47 Ĵ 3237842 3167D2 E sada. Negativni brojevi se u raunaru malko drugaije predstavljaju nego u matematici. Elem. Indikator toga da li je broj negativan ili ne je najvii bit. Kako ? Ako je najvii bit postavljen na 1 onda je broj negativan dok je u suprotnom pozitivan. *Naravno, vrednost koju sadri registar moemo tretirati i kao UNSIGNED, tj. da nema predznak to znai da broj ne moe biti negativan, time to emo i najvii bit koristiti za skladitenje vrednosti. SIGNED je vrednost koja ima predznak tj. broj koji moe biti i negativan. Bit No. 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 Ŀ Br.690h 0 0 0 0 0 1 1 0 1 0 0 1 0 0 0 0 najvii bit Kako bi predstavili broj -690h ? To se radi na sledei nain : Od broja 690h ili 1680(dec) se prvo oduzme broj 1. Znai dobijamo ovakvu situaciju(binarnu) : Ŀ 0 0 0 0 0 1 1 0 1 0 0 0 1 1 1 1 I konano se NOT-uje ceo broj tj. izvrnu se bitovi(tamo gde je bit jedan stavi se nula i obrnuto). Ŀ 1 1 1 1 1 0 0 1 0 1 1 1 0 0 0 0 I eto, dobili smo broj -690h. U stvari, raunarski to nije -690h(to je u matematici) ve F970h. *Broj koji sam koristio u primeru je tipa WORD. Konvertovanje hexadecimalnih brojeva u decimalne To se obavlja na sledei nain. Imamo npr. broj 690h. 690 najnia cifra(index 0) cifra sa indexom 1 cifra sa indexom 2 cifra sa najveim indexom osnova sledea cifra index index ... 6 * 16^2 + 9 * 16^1 + 0 * 16^0 = 6 * 256 + 9 * 16 + 0 * 1 = 1536 + 144 + 0 = 1680 *Znak "^" oznaava stepen. Znai 16^2 je 16 na kvadrat. Prosto, zar ne ? U sluaju negativnih brojeva uradi sve obrnuto : prvo NOT-uje sve bitove, sabere sa 1 i uradi kao to je objanjeno. KRAJ - HEXADECIMALNA NOTACIJA Ako nisi upoznat sa binarnim brojevima, evo i o tome. BINARNA NOTACIJA Analogno Hexadecimalnoj notaciji. Pretvaranja se vre na isti nain s tom to je u binarnoj notaciji osnova sistema 2, iz ega sledi da su jedine mogue cifre 0 i 1. Primeri : Ŀ Decimalno Hexadecimalno Binarno Ĵ 13 D 1101 1 1 1 13218 33A2 11001110100010 -2323 F6ED 1111011011101101 KRAJ - BINARNA NOTACIJA Ako si do sada ve zaboravio o emu je uopte bila re, podseti se :) Znai, naredbom mov ax,4c00h smo u AX registar upisali vrednost 4c00 hexadecimalno. Na isti nain, ako eli da u registar AX stavi npr. vrednost 1000101 binarno to moe uiniti na sledei nain: mov ax,1000101b. Ako ti ak zatreba da vrednosti tretira kao oktalne (osnova sistema 8) koristi mov ax,223o No, da nastavimo. Isti rezultat tj. posledicu(odnosno vraanje kontrole DOS-u) emo postii i sa : mov ah,4ch. Stvar je u tome da je AX registar (kao i svi registri opte namene) koji je 16-to bitni podeljen na 2 komada od po 8 bita. Ŀ AX: AH AL To su AH(high) i AL(low), svaki sa po 8 bita. U 8-mo bitni registar se moe upisati broj koji nije vei od 255 (Ovo ti je ista kombinatorika : imamo 8 mesta i na svako od tih mesta moe da se nae 0 ili 1. Znai broj razliitih brojeva je dakle 2 na 8-i stepen to je 256). Znai, niz brojeva koji se mogu upisati u 8-o bitni registar pripada intervalu [0,255]. Primeti da se ovde i najvii bit koristi za skladitenje broja. Znai, na ovaj nain ne mogu se skladititi negativni brojevi. Ŀ Npr : 1 0 1 1 0 1 1 0 U ovom sluaju upisan je broj 10110110 to je 182 decimalno tj. B6 hexadecimalno. Ŀ 1 1 1 1 1 1 1 1 Broj 11111111 je 255 decimalno tj. FF hexadecimalno. U naem sluaju, pri upisivanju vrednosti 4c00h u registar AX, AH registar dobije vrednost 4ch a AL vrednost 0. To izgleda ovako : Ŀ 0 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 Ĵ AH = 4ch AL = 0h Ali, zbog ega mi uopte stavljamo vrednost 4ch u registar AH ? Elem, kako je u sledeem pasusu objanjeno, to 4ch u stvari predstavlja broj funkcije interapta 21h. Interapt 21h je MS-DOS interrapt koji ima mnogo funkcija koje rade najrazliitije stvari. Ako pogleda opis funkcije 4ch interapta 21h u nekom Help-u (npr. PC-Help, Norton Guides...) videe da tamo pie neto nalik na ovo : " Terminate process with return code ". Dakle, ova funkcija obezbeuje prekid procesa tj. programa i vraanje u operativni sistem. E tu dolazimo do onoga 4c00h umesto 4ch. Kao to sam ve napomenuo 4c00h e u registar AH upisati 4ch tj. broj funkcije a u registar AL nulu. E, ta nula predstavlja tzv. "Return Code". To se moe iskoristiti u BATCH fajlovima kada se proverava da li je dolo do greke u izvravanju programa. Na primer jedan BATCH fajl (npr. START.BAT) moe ovako da izgleda : @echo off moj_pr if errorlevel==1 goto greska echo Sve je proslo OK goto kraj :greska echo Dogodila se greska!!! :kraj Dakle ovaj BATCH fajl e startovati program "moj_pr" i zatim, kada se izvravanje programa okona, proverava se "Return Code" i ako je on vei ili jednak jedinici ispisae se : "Dogodila se greska!!!", dok e se u suprotnom ispisati : "Sve je proslo OK". Sada moe i sam pokuati da napravi tako neto. Na primer, program ; Program PRIMER ; Copyright(C) 1998. by Shang Tsung .MODEL tiny .code org 100h main: mov ah,4ch ; ili mov ax,4c01h mov al,1 ; int 21h end main e kao Return Code proslediti broj 1. *Ovde bi mogao da primeti da je broj hexadecimalnih cifara koji moe da stane u jedan 8-o bitni registar 1 ili 2. Stvar je u tome to je FF hex. 255 dec. odnosno, poslednji dvocifreni hexadecimalan broj (FF) predstavlja nita drugo do broj 11111111 binarno. Ako si ba radoznao, ovo je ovako jer je osnova hexadecimalnog sistema (16) stepen broja 2, to je osnova binarnog brojnog sistema. Ali da ne gnjavim. -int 21h Naredbom INT broj pozivamo interrapt iji smo broj naveli. Ovde je u pitanju poziv interrapt-u 21h. Kad ga pozovemo on pogleda u AH i poto tamo nae 4ch izvrava funkciju 4ch koja daklem vraa kontrolu DOS-u tj. naputa program. Za svaki interrapt broj funkcije se nalazi u registru AH(sem ako taj interrapt nema samo jednu funkciju, odnosno, kad je dovoljno samo pozvati taj interrapt). Naravno, neka funkcija nekog interrapt-a moe zahtevati da se odreeni podaci nalaze i u nekim drugim registrima. Iz tog razloga potrebno je imati precizan opis zahteva odreenih funkcija interrapt-a koji nam je potreban. -END MAIN Opti oblik: END ime_labele "END" naredba zavrava odreeni blok naredbi. U ovom sluaju ona mora biti tu da bi kompajler znao gde je poetak programa, tj. odakle treba da pone izvravanje programa(tzv."Entry point" tj. inicijalna IP vrednost). Kako se nama kod nalazi ispod labele "main", prosleujemo njeno ime naredbi "END". Za COM program, sve se mora nalaziti ispod ove labele. OK. Sada napii u nekom text editoru prethodni program i primer i kompajliraj ih. U sluaju da ne zna kako da ih kompajlira, uradi sledee: tasm ime_fajla.ASM tlink /t/x ime_fajla.OBJ ( Ukoliko eli da kreira .EXE fajl uradi sledee : tlink /x ime_fajla.OBJ ) U naem sluaju generisaemo .COM fajl. *Opcija /x govori linkeru da ne kreira .MAP fajl, a opcija /t da kreira COM fajl. *Za kompajliranje Turbo Assembler-om, potrebni su ti fajlovi TASM.EXE, TLINK.EXE, RTM.EXE i DPMI16BI.OVL. Izvri program PRVI.COM. ta se dogodilo? Nita. E upravo se to i trebalo dogoditi. Program je uradio ono to je trebao - vratio je kontrolu DOS-u. Pokuaj da nita ne napie izmeu labele "main:" i "end main" pa takav program kompajlira. Rezultat e biti COM fajl duine 0. Moe ak takav program i da startuje, ali ovo ba nije preporuljivo - program e najverovatnije da zaglavi sistem. Postoje i drugi naini za okonavanje programa, na primer korienjem interrapt-a 20h. Interrapt zahteva da se u segmentnom registru CS nae segmentna adresa PSP-a procesa odnosno programa koji treba okonati tj. prekinuti. No, poto je jo uvek rano da objanjavam ta su segmentni registri kao i da opisujem PSP, ostaviemo to za kasnije. 2 Interapt 16h, CMP, JMP, J... Sada emo napisati neto sloeniji program: Program No. 2.1 ; PROGRAM ispis (ISPIS.ASM) ; Copyright(C) 1998. by Shang Tsung .MODEL tiny .CODE org 100h main: mov ah,0h ; upie nulu u registar AH int 16h ; Poziva interapt 16h(interapt za tastaturu) cmp al,27 ; poredi broj 27 sa sadrinom registra AL je kraj ; ako su jednaki skae na labelu "KRAJ" mov dl,al ; kopira u DL vrednost iz AL mov ah,2h ; funkcija 2h interrapt-a 21h int 21h ; poziv interrapt-u 21h jmp main ; skae na poetak(labela "MAIN") kraj: ; NEAR labela mov ah,4ch ; vraanje kontrole DOS-u. Program int 21h ; e da zaglavi ako nema ovoga end main ; Kraj programa Program radi sledee: ita znak sa tastature i ispisuje ga na ekranu. Ako je pritisnut taster ESC naputa se program. JOJ. Ajmo od poetka. Funkcijom 0h interrapt-a 16h se ita znak sa tastature i radi sledee: -u registar AH se upisuje SCAN kod pritisnutog tastera -u registar AL se upisuje ASCII kod pritisnutog tastera *NAPOMENA: Ovim se nita ne ispisuje na ekranu. *Nadam se da zna ta je ASCII kod :) *Slian rezultat se dobija preko funkcije 1h interrapt-a 21h, s'tim to se tada karakter ispisuje na ekran. *Interrapt 16h je BIOS-ov interrapt, to znai da mu nije potreban OS(Operative System - Operativni Sistem) da bi radio. -mov ah,0h int 16h Sa ove dve naredbe smo dakle pozvali funkciju 0h interapta 16h koja radi sve ono navedeno. Recimo da smo pritisnuli taster "a". (ASCII kod slova a je 97) Sada u registru AH imamo SCAN kod slova a. E ba sam pogledao - u pitanju je broj 1Eh ili 30 decimalno. No, SCAN kodovi nam za sada nisu preterano bitni. Ono to je vano jeste da u registru AL imamo ASCII kod slova "a" tj. broj 97. - cmp al,27 je kraj - CMP operand,vrednost Komanda CMP poredi(compare) operand sa vrednou i rezultat upisuje u flag-ove odnosno flag registar. Dakle ta se radi ? Poredi se sadraj registra AL brojem 27. Inace, 27 je ASCII kod tastera ESC. I, ako je u registru AL vrednost 27 izvr[avanje programa se nastavlja od labele "kraj". Posle poreenja, FLAG-ovi e biti postavljeni na jedan odreen nain koji emo rastumaiti "JE" naredbom. *Vie rei o FLAG-ovima ima u estom poglavlju ali NE URI!!! Naredba JE labela znai ako su jednaki skoi na poziciju koja je definisana labelom. -JE ( jump if equal - skoi ako su jednaki ) Kako su nam naredbe kao to su JMP, JE i sl. esto potrebne, navedimo koje sve postoje. * Signed znai da broj moe biti i negativan tj. da se najvii bit u registrima koji se porede koristi za znak a ne za skladitenje broja. Skraenica u tabeli - (S). Ŀ J... OPIS(English) OPIS(Srpski) Ĵ JA Jump if Above Skoi ako je vee JAE Jump if Above or Equal Skoi ako je vee ili jednako JB Jump if Below Skoi ako je manje JBE Jump if Below or Equal Skoi ako je manje ili jednako JC Jump if Carry Skoi ako je CF postavljen JCXZ Jump if CX Zero Skoi ako je CX nula JE Jump if Equal Skoi ako je jednako JG Jump if Greater (signed) Skoi ako je vee(S) JGE Jump if Greater or Equal (signed) Skoi ako je vee ili jednako(S) JL Jump if Less (signed) Skoi ako je manje (S) JLE Jump if Less or Equal (signed) Skoi ako je manje ili jednako(S) JMP Unconditional Jump Uvek skae JNA Jump if Not Above Skoi ako nije vee JNAE Jump if Not Above or Equal Skoi ako nije vee ili jednako JNB Jump if Not Below Skoi ako nije manje JNBE Jump if Not Below or Equal Skoi ako nije manje ili jednako JNC Jump if Not Carry Skoi ako CF nije postavljen JNE Jump if Not Equal Skoi ako nije jednako JNG Jump if Not Greater (signed) Skoi ako nije vee (S) JNGE Jump if Not Greater or Equal(signed)Skoi ako nije vee ili jed...(S) JNL Jump if Not Less (signed) Skoi ako nije manje (S) JNLE Jump if Not Less or Equal(signed)Skoi ako nije manje ili jednako(S) JNO Jump if Not Overflow (signed) Skoi ako OF nije postavljen(S) JNP Jump if No Parity Skoi ako PF nije postavljen JNS Jump if Not Signed (signed) Skoi ako SF nije postavljen JNZ Jump if Not Zero Skoi ako ZF nije postavljen JO Jump if Overflow (signed) Skoi ako je OF postavljen (S) JP Jump if Parity Skoi ako je PF postavljen JPE Jump if Parity Even Skoi ako je PF postavljen JPO Jump if Parity Odd Skoi ako PF nije postavljen JS Jump if Signed (signed) Skoi ako je SF postavljen JZ Jump if Zero Skoi ako je ZF postavljen Ne panii. Da odmah objasnim. Sve ono : ako je PF postavljen, ako ZF nije postavljen... e pa ti PF, ZF, SF, OF... su flag-ovi (flegovi) i o njima ima vie rei u 6-om poglavlju, no za sada nas preterano ne interesuju. I sada, kada na primer uradi cmp al,27 je kraj posle izvravanja prvog reda, flag-ovi e se promeniti na odreen nain, i kada se izvri drugi red tj. "JE kraj", uradie se ba ono to treba tj. skoie se na labelu "kraj" ako je u AL registru bila vrednost 27. Ovde mora da pazi na neto. Poto se podrazumeva da J... naredbe primaju OFFSET(tj. lokaciju u okviru programa (definisanu labelom)) u odnosu na rastojanje J... naredbe i labele koji je 8-o bitan, oblast skoka mora biti u intervalu od -128 i +127 bajta od pozicije J... naredbe koja se izvrava jer J... naredbe skau odreen broj bajtova OD TRENUTNOG OFFSET-a(odnosno od pozicije same J... naredbe), a ne od poetka fajla(naravno u istom segmentu). Znai, ako na OFFSET-u 45 u fajlu imamo naredbu "JE labela", a labela se nalazi na OFFSET-u 50, nee se skakati NA LOKACIJU 50, VE ZA 50-45-(duina naredbe JE=2) =3 BAJTA, znai na lokaciju [pozicija JE naredbe]+[duina JE naredbe]+3 = 45+2+3 = 50. Dakle, nee biti JE 50 (naravno u mainskom jeziku) ve JE 3 No ako se eli skoiti na neku veu daljinu(dakle na lokaciju koja izlazi iz pomenutog intervala), najomiljeniji trik svakog programera je da navede re JUMPS u okviru programa. Ovo je samo naredba kompajleru da omogui J... naredbama koje izlaze iz malopre pomenutog intervala da dosegnu eljenu lokaciju. Dakle ako ti se dogodi da ti kompajler prijavi greku tipa **Error** ????.ASM(??) Relative jump out of range by ????h bytes odnosno, da postoji neki skok koji izlazi iz intervala [-127,128], samo navedi re JUMPS pre samog programa i kompajler e se pobrinuti za sve. To je vrlo zanimljivo uraeno. Ako navedemo re JUMPS, i napiemo naredbu "JE ime_labele", a OFFSET dotine labele izlazi iz pomenutog intervala, kompajler e nau naredbu pretvoriti u dve naredbe to se moe predstaviti na sledei nain : JNE Obii_JMP JMP ime_labele Obii_JMP: Kako JMP naredba prima 16-o bitni OFFSET, naredba "JMP ime_labele" e raditi. Ludo, zar ne ? No, tako je to sa 8086 arhitekturom procesora. Ali, ako se ukljue npr. 486 instrukcije, sve je to malko drugaije ali da se ne bismo previe udaljavali od teme, neka ostane na ovome. Da nastavimo gde smo stali. Dakle, ako smo pritisnuli taster ESC u AL registru e se nai broj 27 tj. ASCII kod tastera ESC. I sad, ako je AL=27 onda skai na labelu "kraj", nakon ega sledi ve opisana rutina povratka u DOS. Ako AL i broj 27 nisu jednaki onda se akcija nastavlja. - mov dl,al mov ah,2h int 21h Dakle prvo se u registar DL kopira sadraj registra AL. Funkcija 2h interrapt-a 21h tampa karakter iji se ASCII kod nalazi u registru DL na ekran. Zbog toga u DL kopiramo sadraj registra AL tj. ASCII kod pritisnutog tastera. Sad pozivamo dakle funkciju 2h interapta 21h. Ona pokupi sadraj registra DL i ako je tamo npr. 97 tampa na ekran slovo "a". Prosto ko pasuljina :). - jmp main Naredba JMP(jump) obezbeuje skok na npr. neku labelu koji je bezuslovan tj. uvek e se skoiti. Kao to sam ve rekao, JMP naredba, za razliku od ostalih J... naredbi prima 16-o bitni OFFSET, to znai da je u stanju da skoi na lokaciju koja je (po default-u) u intervalu [-32768,32767]. Samim time, ova naredba e zauzeti 3 bajta memorije(jedan bajt za identifikaciju naredbe, i dva bajta za vrednost). Ako je lokacija na koju treba skoiti ipak u intervalu [-127,128] nije loe upotrebiti SHORT skok na tu labelu. To se realizuje na sledei nain : JMP SHORT ime_labele Ovako e se proslediti 8-o bitna vrednost skoka, te e samim time naredba zauzeti 2, a ne 3 bajta memorije. Ajde da i ovo pomenem : Ako je potrebno skoiti na lokaciju koja je u drugom SEGMENT-u, treba koristiti FAR skok. To se ovako radi : JMP FAR PTR ime_labele_u_nekom_drugom_segmentu No, da te ne bih sada gnjavio o segmentima(toooooooo dolazi kasnije ;) ostanimo zasad na ovome. Naredba JMP main nam dakle obezbeuje skok na sam poetak programa tj. labelu "MAIN". Dakle kada na ve opisan nain kompajlira ovaj program i izvri .COM fajl "ISPIS.COM", moi e da unosi karaktere sa tastature koji e odmah biti ispisani na ekranu pa e stei utisak da pie u obinoj komandnoj liniji. Tu e primetiti da se ako pritisne npr. taster BACKSPACE koji slui za brisanje, nee dogoditi ba ono to treba tj. nee se obrisati karakter koji predhodi poziciji kursora, ve e se samo kursor postaviti na njegovu poziciju - ovo je u potpunosti normalno, MS-DOS je onaj koji brie karakter. Ako pritisne ESC program se zavrava. ISPII PROGRAM ISPIS.ASM I KOMPAJLIRAJ GA!!! Toliko u ovom poglavlju. 3 ADD, SUB, MUL, IMUL, DIV, IDIV, INC, DEC, Rad sa Procedurama U ovom poglavlju u (pomalo nabrzaka) da navedem nekoliko naredbi koje obavljaju osnovne raunske operacije kao i opisati rad sa procedurama. ADD (ADD - Sabiranje) Sintaksa: ADD dest,sour Modifikuje flag-ove : AF CF OF PF SF ZF Sabira dest i sour i rezultat upisuje u dest. Primer: ADD ah,1 U AH registar dodaje broj 1. Znai ako je u AH bilo 67 sada je 68. Meutim, ako je u AH bilo 255, nova vrednost nije 256 nego 0. Zato - mislim da je oigledno :) ADD dl,cl U DL registar dodaje vrednost CL registra. ADD bl,ax BEEP. Malo sutra. Poto je BL 8-bitni a AX 16-bitni doi e do greke. NE MOGU se obavljati ovakve operacije ako tipovi oba operanda nisu isti. ADD [bx],al BAJTU u DATA segmentu ija se OFFSET adresa nalazi BX, dodaje se sadraj registra AL. ADD [bx],ax WORDU u DATA segmentu ija se OFFSET adresa nalazi BX, dodaje se sadraj registra AX. SUB (Substract - Oduzimanje) Sintaksa: SUB dest,sour Modifikuje flag-ove : AF CF OF PF SF ZF Oduzima se dest or sour-a i rezultat se upisuje u dest. Primeri : SUB ax,bx ; dest=dest-sour tj. AX=AX-BX Ako se u registru AX nalazila vrednost 60 a u BX 21, nova vrednost u AX registru je 60-21 odnosno 39. SUB dl,bl ; DL=DL-BL SUB [bx],al ; BAJT na memorijskoj lokaciji u DATA segmentu ija se OFFSET adresa nalazi u registru BX se umanjuje za vrednost koja je sadraja u registru AL. MUL (Unsigned Multiply - Mnoenje bez predznaka) Sintaksa : MUL src Modifikuje flag-ove : CF OF Registar AX se mnoi sa src-om. U pitanju je UNSIGNED mnoenje tj. brojevi ne mogu biti negativni to znai da se najvii bit koristi za skladitenje broja. Ako je src 8-o bitna(BYTE) vrednost, onda se src mnoi vrednou registra AL, a rezultat se smeta u registar AX. Ako je src 16-o bitna(WORD) vrednost, onda se src mnoi vrednou registra AX, a DX:AX e sadrati rezultat. *DX:AX je 32-o bitni broj. U DX se nalazi HIGH WORD(gornjih 16 bita) a u AX LOW WORD(donjih 16 bita) rezultata. (neto vie o ovome ima u 6-om poglavlju) Ako je src 32-o bitna(DOUBLE WORD) vrednost, onda se src mnoi sa vrednou registra EAX a EDX:EAX sadri rezultat. *EDX:EAX je 64-o bitni broj. U EDX se nalazi HIGH DWORD (DOUBLE WORD) a u EAX LOW DWORD broja. Primeri: MUL dx ; Vrednost u AX se mnoi vrednou koja se nalazi u DX ; i rezultat se smeta u registre DX i AX. ; Ako je u AX bio broj 12950, a u DX broj 123800, nakon ; dobijanja rezultata 1603210000 to je u binarnom obliku ; 1011111100011110000101100010000 ; High WORD Low WORD ; ; DX registar je primio gornjih 16 bita rezultata tj. ; broj 101111110001111 binarno, ili 24463 decimalno, ; a AX registar je primio donjih 16 bita rezultata tj. ; broj 101100010000 binarno, ili 2832 decimalno. MUL al ; AX=AL*AL MUL ebx ; EDX:EAX=EAX*EBX MUL byte ptr[bx] ; BAJT u DATA segmentu sa memorijske lokacije iji se ; OFFSET nalazi u registru BX, mnoi se sadrajem ; registra AL, i rezultat se upisuje u registar AX MUL word ptr[bx] ; WORD u DATA segmentu sa memorijske lokacije iji se ; OFFSET nalazi u registru BX, mnoi se sadrajem ; registra AX, i rezultat se upisuje u DX:AX *Mnoenje pri kome se upotrebljavaju 32-o bitni registri(EAX,EBX,...) je mogue samo na 386 ili boljim procesorima. IMUL (Signed Multiply - Mnoenje sa predznakom) Sintaksa : IMUL src IMUL src,immed (286+) IMUL dest,src,immed (286+) IMUL dest,src (386+) Modifikuje flag-ove : CF OF Ovde je u pitanju SIGNED mnoenje tj. brojevi mogu biti i negativni. Postupak je isti kao i kod naredbe MUL, s tim to kao to vidi, procesori 286 i noviji mogu da ak i specificiraju dest, kao i da src mnoe sa immed(Immediate tj. konstanta : npr. 78). Primeri : IMUL bl ; AX=AL*BL IMUL bx ; DX:AX=AX*BX IMUL ax,4 ; AX=AX*4 IMUL ax,dx,78 ; AX=DX*78 IMUL dx,bx ; DX=DX*BX DIV (Unsigned Divide - Deljenje bez predznaka) Sintaksa : DIV src Modifikuje flag-ove : (AF CF OF PF SF ZF ???) Radi se o UNSIGNED deljenju registra AX src-om. Ako je src 8-o bitna vrednost, onda se vrednost registra AX deli src-om i kolinik smeta u registar AL, a ostatak u registar AH. Ako je src 16-o bitna vrednost, onda se DX:AX deli src-om i kolinik smeta u registar AX, a ostatak u registar DX. Ako je src 32-o bitna vrednost, onda se EDX:EAX deli src-om i kolinik smeta u registar EAX, a ostatak u registar EDX. Primeri : DIV cx ; 32-o bitna vrednost koja se nalazi u DX:AX, deli se ; vrednou u registru CX i kolinik se smeta u AX a ; ostatak u registar DX. ; Ako je vrednost u registru DX 1, vrednost u AX registru ; nula, a u CX registru sa nalazi broj 100, nakon naredbe ; DIV podelie se vrednost 10000000000000000 binarno (ili ; 65536 decimalno) brojem 100 i kolinik tj. broj 655 e ; se smestiti u registar AX, dok e se u registru DX nai ; ostatak deljenja tj. broj 36. DIV dl ; Deli se vrednost u registru AX vrednou u registru DL ; i kolinik se smeta u AL, dok se ostatak smeta u AH. DIV byte ptr[bx] ; Vrednost u registru AX se deli BAJT-om sa memorijske ; lokacije ija se OFFSET adresa nalazi u registru BX u ; DATA segmentu i kolinik se smeta u AL, a ostatak u AH. DIV word ptr[bx] ; Vrednost u registru AX se deli WORD-om sa memorijske ; lokacije ija se OFFSET adresa nalazi u registru BX u ; DATA segmentu i kolinik se smeta u AX, a ostatak u DX. DIV ebx ; 64-o bitni broj ijih se gornjih 32 bita nalaze u ; registru EDX a donjih 32 bita u registru EAX, deli ; se vrednou koja se nalazi u registru EBX i kolinik ; se smeta u registar EAX a ostatak u registar EDX Ako kojim sluajem doe do pokuaja deljenja nulom, izvravanje programa se prekida i ispisuje se poruka : "Your program caused a divide overflow error. If the problem persists, contact your program vendor." *Nadam se da ti je ve poznato da je deljenje nulom nedefinisano. Ista ova greka dogodie se i ako kolinik ili ostatak pri deljenju premauju najveu vrednost. Na primer, ako je u registru AX vrednost 60000, a u registru BL vrednost 1, pri deljenju AX sa BL ( div bl ), dobijamo kolinik od 60000 i ostatak 0. Ali, kako kolinik treba da bude upisan u registar AL koji je 8-o bitni doi e do greke jer broj 60000 ne moe da stane u 8-o bitni registar (podsetiu te da je najvea vrednost koja se moe upisati u 8-o bitni registar 255). *Deljenje pri kome se upotrebljavaju 32-o bitni registri(EAX,EBX,...) mogue je samo na 386 ili boljim procesorima. IDIV (Signed Divide - Deljenje sa predznakom) Sintaksa: IDIV src Modifikuje flag-ove : (AF CF OF PF SF ZF ???) Radi se o SIGNED deljenju registra AX src-om. Ako je src 8-o bitna vrednost onda se vrednost registra AX deli src-om i kolinik se smeta u registar AL, a ostatak u registar AH. Ako je src 16-o bitna vrednost onda se DX:AX deli src-om i kolinik se smeta u registar AX, a ostatak u registar DX. Primeri : IDIV al ; Vrednost u AX registru deli se sadrajem registra AL ; i kolinik se smeta u registar AL, a ostatak u AH. ; Ako je u registru AX broj 1234 odnosno 10011010010 u ; binarnoj brojnoj osnovi, to znai da se u registru ; AL nalazi broj 11010010 binarno tj. -46 (potseam te da ; su u pitanju SIGNED brojevi. Kako je najvii bit AL ; registra jedinica, to je broj u AL negativan), jer AL ; registar predstavlja donjih 8 bita registra AX. Nakon ; operacije deljenja u registru AL nai e se kolinik ; tj. broj -26, a u registru AH ostatak tj. broj 38. IDIV dx ; DX:AX IDIV DX - kolinik u AX, ostatak u DX IDIV ebx ; EDX:EAX IDIV EBX - kolinik u EAX, ostatak u EDX INC (Increment - Uveavanje) Sintaksa : INC operand Modifikuje flag-ove : AF OF PF SF ZF Operand se uveava za 1. Primeri: inc ax ; AX=AX+1 ; Ukoliko se u AX registru nalazila vrednost ; 65535 ili 1111111111111111 binarno, nakon ove ; operacije u AX registru se ne nalazi broj 65536 ; ve nula. inc dl ; DL=DL+1 inc byte ptr[bx] ; Inkrementuje se BYTE u DATA segmentu na memorijskoj ; adresi iji se OFFSET nalazi u registru BX inc ecx ; ECX=ECX+1 DEC (Decrement - Umanjivanje) Sintaksa: DEC operand Modifikuje flag-ove : AF OF PF SF ZF Operand se umanjuje za 1. Primeri : dec cl ; CL=CL-1 ; Ako se u CL registru nalazila nula, nakon ove ; operacije nova vrednost CL registra je -1(ako se ; broj gleda sa preznakom) tj. 255(bez predznaka). dec bx ; BX=BX-1 dec byte ptr[bx] ; Dekrementuje se BYTE u DATA segmentu na memorijskoj ; adresi iji se OFFSET nalazi u registru BX dec ecx ; ECX=ECX-1 Rad sa Procedurama Sintaksa: Ime_procedure PROC [NEAR ili FAR] ; ;telo procedure tj. ono to radi sama procedura ; ret ;povratak na mesto poziva ( nije obavezno ) ime_procedure ENDP ; ENDP (END Procedure) Pozivanje procedure: CALL ime_procedure NEAR ili FAR nakon rezervisane rei PROC, oznaavaju da li e se procedura pozivati samo preko OFFSET-a ili parom SEGMENT:OFFSET, to e rezultovati 3 bajta dugakim pozivom NEAR procedure i 5 bajta dugakim pozivom FAR procedure. Ukoliko je u pitanju FAR procedura, RET sa kraja procedure e automatski postati RETF naredba, dok e, sluaju NEAR procedure postati RETN naredba. Ako se ne navede tip procedure(tj. NEAR ili FAR), kompajler e joj to automatski dodeliti u zavisnosti od memorijskog modela koji se koristi. *Default za memorijski model TINY je NEAR. Napomena : Sa CALL naredbom se moe pozvati i obina labela. To je korisno ako se elimo vratiti na mesto poziva te labele sa RET, jer sa J... naredbama tako neto nije mogue, jer one ne ostavljaju adresu (tj. CS:IP, odnosno, ako je u pitanju NEAR poziv, samo IP) mesta sa koga se pozvane na steku kao to to radi naredba CALL. RETN(RETurn Near) naredba uzima vrednost tipa WORD sa steka, SP(Stack Pointer) registar uveava za 2 i u IP(Instruction Pointer) registar upisuje vrednost skinutu sa steka. Kako CS:IP pokazuju na instrukciju koja treba da se izvri, zakljuak je jednostavan. RETF(RETurn Far) naredba uzima vrednost tipa DOUBLE WORD sa steka, SP registar uveava za 4 i modifikuje IP i CS skinutim vrednostima. *O tome ta je stek...kasnije. ZADATAK : Na osnovu prethodnog programa i instrukcija uraditi sledee: Napisati program POGISPIS.ASM kojim se uitavaju karakteri sa tastature i ispisuju na ekran ali prethodno sabrani sa 1(tj. njihova ASCII vrednost sabrana sa 1). Ako je pritisnut taster z(ASCII kod 122) ili Z(ASCII kod 90) ispisati karakter a(ASCII kod 97) odnosno A(ASCII 65). Ako je pritisnut taster ESC izlazi se iz programa. Dakle ono to program treba da radi jeste sledee: Kada se startuje program ja njemu ukucavam redom zakLoZq a on meni na ekran umesto toga napie sledee ablMpAr i ako pritisnem ESC vrati kontrolu DOS-u. Nije nimalo teko. Ideja je u sledecem: 1.Saekati na pritisak tastera i time dobiti ASCII kod u AL 2.Proveriti da li je pritisnut ESC i ako jeste uraditi ono to treba 3.Proveriti da li je pritisnuto Z i ako jeste uraditi ono to treba 4.Proveriti da li je pritisnuto z i ako jeste uraditi ono to treba 5.Itampati na ekran karakter iji je ASCII kod za jedan vei od unetog karaktera Provere za ESC,'Z', i 'z' uraditi u 3 procedure. NEKOLIKO NAPOMENA: 1.Kod imena labela ili procedura ne razlikuju se velika i mala slova. 2.Ne smeju postojati dve labele sa istim imenima 3.Ne smeju postojati dve procedure sa istim imenima 4.Ne smeju postojati procedura i labela sa istim imenima VANO!!! Na osnovu dosadanjeg znanja pokuaj da sam napie taj program. Nemoj odmah gledati reenje jer tako nikada nee nauiti!!! Cilj svega ovoga je da se osamostali a to nee uspeti gledajui reenja. Sreno! REENjE: Program No. 3.1 ; PROGRAM Pogrean_ispis (POGISPIS.ASM) ; Copyright(C) 1998. by Shang Tsung .MODEL tiny .CODE org 100h main: jmp pocetak ; idi na labelu pocetak (da ne bi upao ; u proceduru) provera_esc PROC cmp al,27 ; proveravamo da li je bio pritisnut ESC je kraj ; ako jeste skaemo na labelu "kraj" ret provera_esc ENDP provera_veliko_z PROC cmp al,90 ; proveravamo da li smo pritisnuli 'Z' jne nije_veliko_Z ; ako nismo, vraamo se na mesto poziva mov al,64 ; u AL postavljamo broj 64 nije_veliko_Z: ret provera_veliko_z ENDP provera_malo_z PROC cmp al,122 ; proveravamo da li smo pritisnuli 'z' jne nije_malo_z ; ako nismo, vraamo se na mesto poziva mov al,96 ; u AL postavljamo broj 96 nije_malo_z: ret provera_malo_z ENDP pocetak: mov ah,0h ; ekamo na akciju int 16h ; korisnika CALL provera_esc ; pozivaju se procedure CALL provera_malo_z ; za proveru uslova CALL provera_veliko_Z ; zadatka INC al ; sabiramo ASCII vrednost sa 1 mov dl,al ; u DL kopiramo sadraj AL mov ah,2h ; tampamo karakter iji int 21h ; se ASCII kod nalazi u DL jmp pocetak ; sledei karakter kraj: mov ah,4ch ; povratak u DOS int 21h ; END main OBJANJENJA(ako su neophodna?!!!): U prvoj proceduri se obavlja sledei posao: 1.Proverava se sadraj registra AL i ako je on 27 ide se na labelu kraj koja u stvari oznaava kraj programa. U drugoj proceduri se obavlja sledei posao: 1.Proverava se sadraj Al i ako je on jednak 90 taj broj se menja u broj 64. Menja se u 64 jer ce se dalje u glavnom programu taj broj prvo inkrementovati i onda tampati. Kako je 65 ASCII kod slova 'A' zakljuak je jednostavan. U treoj proceduri se obavlja sledei posao: 1.Sve isto kao i u prethodnoj proceduri samo je sada re o malom slovu 'z'(i malom slovu 'a'(ASCII 97)). Glavni program: Uitava se karakter i pozivaju procedure za proveru uslova. Zatim se vrednost u AL registru inkrementuje, tampa se ASCII ekvivalent na ekran i ponovo skae na poetak programa. Jedna napomena: Na samom poetku programa posle "main:" se nalazi "jmp pocetak". To je neophodno jer bez toga, program e odmah upasti u prvu proceduru. *Jedna olakica. Ne mora napamet znati sve ASCII kod nekog karaktera da bi ga uneo u registar. Sasvim je dovoljno navesti karakter meu jednostrukim navodnicima i kompajler e se za sve pobrinuti. Dakle, umesto "mov ah,97", gde broj 97 predstavlja ASCII kod slova 'a', moe se napisati i "mov ah,'a'". Ako eli da u AH registar upie vrednost za jedan manju od ASCII vrednosti slova 'a', moe napisati "mov ah,'a'-1". Nadam se da si ovaj zadatak uspeo da rei. to 'no kau bio je prost ko ona vojnika pasuljina. A da sada napiemo jedan koristan programi : Zadatak : Napisati progam koji e na ekran ispisati sadraj AL registra u decimalnoj brojnoj notaciji. Nekoliko rei o zadatku. Da bismo na ekran ispisali sadraj 8-o bitnog registra u decimalnoj notaciji, to oigledno treba raditi cifru po cifru. E, kako je, za 8-o bitni registar, najvei mogui broj trocifren, reenje bi moglo da izgleda otprilike ovako : Podelimo vrednost u AX(?) brojem 100(kako DIV naredba deli akumulator vrednou, u AH emo jednostavno postaviti nulu a u AL broj koji treba itampati) i time dobijamo prvu cifru u vidu kolinika deljenja u registru AL. Zatim, ostatak deljenja iz registra AH prebacimo u AL, postavimo nulu u AH i podelimo novu vrednost u registru AX brojem 10. Time emo u AL dobiti drugu, a u AH treu cifru. I eto, to je cela mudrost. Naravno, da bi to malo lepe napravili(tanije da nam broj 9 ne tampa kao 009), moemo ubaciti i rutinu koja nee tampati nule ispred broja (ovo se lako postie definisanjem npr. indikatora koji e biti razliit od 0 ako je neka cifra itampana). to se samog tampanja karaktera tie, jednostavno dodamo broj 48 (ASCII kod karaktera '0') u registar u kome se nalazi jednocifren broj, i jednostavno taj karakter itampamo na ekran. E, pa sreno! Program No. 3.2 ; PROGRAM stampanje_vrednosti_registra_AL__decimalno (SVRALD.ASM) ; Copyright(C) 1998. by Shang Tsung .MODEL tiny .CODE ORG 100h main: mov bh,0 ; Indikator ( ako je u BH nula, nismo itampali ; nikakav karakter, inae jesmo ) mov ah,0 ; u AH postavljamo nulu mov al,201 ; u AL postavljamo broj koga treba itampati mov bl,100 ; Delilac div bl ; Delimo AX sa BL tj. 201 sa 100 i ; u AL dobijamo kolinik tj. 2 a u AH ; ostatak tj. 1. mov dl,al ; u DL postavljamo prvu cifru add dl,48 ; konvertujemo cifru u njen ASCII ekvivalent mov dh,ah ; u DH uvamo ostatak malopreanje operacije mov ah,2 ; u AH stavljamo broj funkcije interrapt-a 21h cmp dl,'0' ; proveravamo da li je u pitanju nula, i ako je prva_cifra_je_nula ; jeste skaemo na labelu "prva_cifra_je_nula" mov bh,1 ; inae indikator postavljamo na jedan int 21h ; ispisujemo prvu cifru na ekran prva_cifra_je_nula: mov ah,0 ; u AH postavljamo nulu mov al,dh ; u AL vraamo sauvani ostatak deljenja mov bl,10 ; novi delilac div bl ; delimo ostatak sa BL tj. 1 sa 10 i ; u AL dobijamo kolinik tj. 0, a u AH ostatak ; tj. 1 mov dl,al ; u DL upisujemo drugu cifru add dl,48 ; konvertujemo tu cifru u njen ASCII ekvivalent mov dh,ah ; u DH uvamo ostatak deljenja mov ah,2 ; U AL upisujemo broj funkcije interrapt-a 21h cmp dl,'0' ; proveravamo da li je u pitanju nula i ako jne stampaj_drugu_cifru ; nije skaemo na labelu stampaj_drugu_cifru cmp bh,0 ; proveravamo indikator, i ako nije nula jne stampaj_drugu_cifru ; tampamo drugu cifru jmp stampaj_trecu_cifru ; inae prelazimo na treu cifru stampaj_drugu_cifru: ; naredna naredba nije potrebna jer emo treu cifru svakako itampati mov bh,1 ; menjamo indikator int 21h ; ispisujemo drugu cifru na ekran stampaj_trecu_cifru: mov dl,dh ; u DL upisujemo treu cifru add dl,48 ; konvertujemo tu cifru u njen ASCII ekvivalent ; moe i ovako : "add dl,'0'" int 21h ; ispisujemo treu cifru na ekran mov ah,4ch ; povratak u DOS int 21h end main *NAPOMENA : tampanje karaktera se moglo malo lepe obaviti, korienjem procedure. No, ja sam to ovako uradio ne bi li bilo oiglednije ta se u stvaru deava. Predlaem ti da pokua da to malo jednostavnije (i krae :) napie. Da objasnimo. - mov bh,0 Znai, izabrali smo registar BH za indikator i ako je u BH nula, onda nita nismo itampali na ekran, dok u suprotnom jesmo. - Dalje, u AX smo upisali vrednost koju treba itampati ( tj. u AH nulu a u AL pravu vrednost ). Ovo smo uradili zbog opisa naredbe DIV. Kao to si ve video, DIV naredba deli vrednost AKUMULATOR-a tj. AX vrednou koju mi zadamo i smeta kolinik deljenja u AL a ostatak deljenja u AH. - Dalje, u BL smo postavili delilac tj. broj 100 kojim smo zatim podelili vrednost u registru AX tj. 201, i time u registru AL dobili kolinik tj. 2, a u registru AH ostatak tj. 1. - Zatim, u registar DL upisujemo prvu cifru broja, tj. kolinik predhodne operacije i konvertujemo vrednost u DL u njen ASCII ekvivalent. U DH uvamo ostatak predhodne operacije. - Zatim, proveravamo da li je prva cifra nula, i ako jeste preskaemo njeno tampanje. Inae, indikator postavljamo na jedinicu i tampamo cifru. - Dalje, u AH postavljamo 0 a u AL ostatak malopreanje operacije i sve to delimo brojem 10, ime u AL dobijamo drugu, a u AH treu cifru. - Dalje, pripremamo se za tampu druge cifre, i pre samog tampanja proveravamo da li je u pitanju cifra 0. Ako nije prelazimo na tampanje, a ako jeste, proveravamo da li smo ve ispisali neku cifru na ekran, tj. proveravamo da li je BH razliit od nule. Ako jeste razliit, to znai da smo ve itampali neku cifru, to opet znai da ovu nulu treba itampati. U suprotnom prelazimo na tampanje tree cifre. - Primeti, da nema provera da li je cifra nula ili ne kada je u pitanju tampanje tree cifre. Ovo ima jednostavno objanjenje. Ako je npr. trebalo itampati broj 0, nita se nee itampati kao prva ili druga cifra, ali kao trea hoe, tj. na ekran se tampa jedna cifra 0. Znai, koja god da je trea cifra u pitanju, nju treba itampati. Ovaj program se moe i neto jednostavnije napisati ali ne znanjem kojim trenutno raspolaemo, te neka ostane na ovome. Da ubacimo malo matematike u sve ovo. Zadatak : Itampati sve proste prirodne brojeve iz intervala [2,255] na ekran. Par rei o zadatku. Treba napraviti dve petlje. Prva treba da vrti sve brojeve od 2 do 255, dok druga treba da vrti brojeve koji treba da predstavljaju delilac tj. od 2 do broja koji se proverava(bez samog broja). I sad, ako se nae neki delilac tj. ako je ostatak pri deljenju broja koji se proverava i delioca nula, preskae se ostatak proveravanja(broj je sloen) dok se u suprotnom akcija nastavlja sve dok delilac ne bude jednak samom broju. Ako doe do toga da delilac bude jednak samom broju znai da nismo nali ni jedan delilac broja to opet znai da je broj prost. Ovde pazi da ne proverava da li je broj deljiv samim sobom ! Dalje se jednostavno ( korienjem predhodnog programa ) na ekran tampa taj broj. Sreno ! Program No. 3.3 ; PROGRAM prosti_brojevi (PRBR.ASM) ; Copyright(C) 1998. by Shang Tsung .MODEL tiny .CODE ORG 100h main: mov ah,0 ; U AX stavljamo poetni mov al,2 ; broj (isto to i mov ax,0) mov cl,al ; U CL e nam biti trenutni broj ; koji se proverava sledeci_broj: mov bl,2 ; U BL e biti delilac sledeci_delilac: cmp bl,cl ; proveravamo da li je delilac jednak samom je jeste_prost ; broju, i ako jeste - dobili smo prost broj div bl ; Delimo vrednost u AX deliocem cmp ah,0 ; Proveravamo da li je ostatak deljenja 0 je nije_prost ; ako jeste, znai da broj nije prost mov ah,0 ; U AH postavljamo nulu mov al,cl ; U AL vraamo broj koji se proverava inc bl ; poveavamo delilac jmp sledeci_delilac ; idemo dalje jeste_prost: call ispisi_vrednost_registra_CL ; ispisujemo prost broj na ekran nije_prost: cmp cl,255 ; proveravamo da li smo stigli do kraja je kraj ; intervala i ako jesmo, vraamo se u DOS mov ah,0 ; U AH postavljamo 0 add cl,1 ; Pomeramo se na sledei broj mov al,cl ; U AL upisujemo taj broj jmp sledeci_broj ; proveravamo taj broj kraj: mov ah,4ch ; povratak u DOS int 21h ; ispisi_vrednost_registra_CL PROC ; procedura je ista kao i predhodni program s' tim to treba napraviti ; nekoliko ispravki : ; 1. Umesto "mov al,201" sada nam treba "mov al,cl". ; 2. Nakon tampanja broja potrebno je odtampati razmak ; 3. Na kraju procedure potrebno je staviti RET (ovo valjda ve zna) ispisi_vrednost_registra_CL ENDP end main Da objasnimo. - Prvo, interval. Zadao sam ti interval brojeva od broja 2. To sam uradio jer broj 1 (to nadam se ve zna :) nije ni prost ni sloen, te ga ne treba ni tampati. Kraj intervala je broj 255 iz razloga toga, to u 8-o bitni registar ne moe upisati broj koji je vei od 255( ako bi pokuao da inkrementuje(INC) vrednost registra npr. AL koji trenutno sadri broj 255, nee dobiti broj 256 nego nulu - proveri). - U AX(u AH e biti 0) e se nalaziti broj koji se proverava a u CL taj isti broj. Ovo treba ovako uraditi jer se pri deljenju menja sadraj registra AX pa bi izgubili broj koji se proverava. U BL e se nalaziti delilac. - Dalje, proveravamo da li je delilac jednak broju koji se proverava. Ako jeste, znai da je u pitanju prost broj i skaemo na labelu "jeste_prost" nakon koje pozivamo proceduru za tampanje. U suprotnom delimo sadraj registra AX deliocem, i proveravamo ostatak u AH. Ako je ostatak jednak nuli, znai da broj nije prost i skaemo na labelu "nije_prost". U suprotnom vraamo vrednosti u AX kakve su bile pre operacije deljenja i uveavamo delilac. Dalje skaemo na labelu "sledeci_delilac" i tako sve dok delilac i broj ne budu jednaki. Ne sme se pri proveri dogoditi da delilac bude jednak broju koji se proverava, jer e tako svi brojevi biti sloeni jer svaki je broj deljiv samim sobom. - Ako je broj prost pozivamo proceduru "ispisi_vrednost_registra_CL" koja, na ve opisani nain, na ekran tampa sadraj registra CL. - Ako broj nije bio prost, skoili smo na labelu "nije_prost". Tu smo odmah izvrili proveru da li smo doli do kraja intervala, i ako jesmo skaemo na labelu "kraj" nakon koje sledi ve opisan nain povratka u DOS. U suprotnom akcija se nastavlja. Vraamo staru vrednost registra AX uveanu za jedan i potom prelazimo na labelu "sledeci_broj" koja e proveriti da li je taj sledei broj prost i tako do kraja intervala. - Ovo sve moe i malo bre da se uradi. Nije potrebno ispitivati parne brojeve, tj. umesto da ispitujemo skup {2,3,4,5,6,...,224,225} dovoljno je ispitati skup {2,3,5,7,9,...,251,253,255}. Parni brojevi sigurno nisu prosti (sa izuzetkom broja 2). Neto slino se moglo uraditi i to se delioca tie. No, da ti ne bih zagoravao ivot, ja sam to ovako uradio. Nadam se da nije bilo previe teko. Predlaem da ovo isto uradi i na veem intervalu, to znai da e sam morati napisati proceduru/program koji e na ekran tampati decimalnu vrednost nekog 16-o bitnog registra npr. AX. Pokuaj, nije teko. 4 Stringovi, Pointeri, Segment, Offset, Memorija, Segmentni registri, RET, Stek, COM i EXE fajl, Segmentacija, Novi red, Naredbe PUSH, PUSHA, PUSHAD, POP, POPA, POPAD Zadatak : Napisati program koji na ekran ispisuje 'Hello, world!'. OK. Polako. Nemoj da ti padne na pamet da to radi slovo po slovo. Zato komplikovano kada moe mnogo lake? Elem. Funkcija 9h interapta 21h ima zadatak da tampa string na koji pokazuje DS:DX na ekran. JOJ! "Na koji pokazuje DS:DX ?!!!!!!!!!" Odmah emo to objasniti. Kada kaemo DS:DX treba da POKAZUJE je neto, takav "izraz" nazivamo POINTER-om. Elem, kada uitamo neki string u memoriju, on zauzme odreenu koliinu iste. Dakle treba nekako ukazati na deo memorije gde je smeten string, tanije na adresu prvog karaktera stringa. Kako ? E. Ono DS od malopre je segmentni registar koji ukazuje na DATA SEGMENT memorije(segment podataka) a DX je opti registar koji u ovom sluaju oznaava OFFSET(prirataj) memorije. To u memoriji izgleda otprilike ovako: Adresa npr.10000-> 10001-> a 10002-> o 10003-> $ Deo memorije Zajedno, DS:DX u ovom sluaju treba da pokazuju na memorijsku lokaciju 10000. UH!!! Pre nego to nastavimo da ti pojasnim svu tu priu o segmentima. Da li si se ikada pitao zbog ega je RAM tj. konvecionalna memorija uvek ogranien na 640K+visoka zona memorije i bez obzira to ti moda ima i 64MB RAM-a dogodi se da te pozdravi poruka tipa "Out of memory" ? Elem vrlo je jednostavno. Pri nastajanju samog DOS-a i uopte softvera tamo negde 80-tih godina, gledalo se da se to vie prostora tj. memorije utedi. I tu je postavljen jedan limit. Naime, granica konvecionalne RAM memorije postavljena je na, tada nedosenih, 640K. To je uinjeno tako to se RAM mapira dvadesetobitno. To je upravo ono "segment:offset". Naime, i segment i offset su 16-o bitne vrednosti. I sad, kada eli da neto radi sa tom memorijskom lokacijom na koju pokazuje "segment:offset", kalkulacija se izvri na sledei nain : vrednost segmentne adrese koja se nalazi u segmentnom registru se iftuje (pomeri) ulevo za 4 bita i to se onda doda offsetu. Na taj nain je dobijen 20-o bitni broj tj. adresa memorijske lokacije. Jednostavnom proverom se utvruje da se 20-o bitnim brojem moe mapirati najvie 2^20 bajta memorije tj. 1048576 bajta to ini 1 megabajt. To je upravo ono : 640K konvecionalne + 384K visoke memorije. I to je cela pria. Da bi zadrali kompatibilnost sa starijim programima mapiranje memorije se nije menjalo pa se danas jo uvek koristi 20-o bitno. *Ovo sve vai u REAL modu. U PROTECTED modu to je malko drugaije. Da rezimiramo: DS:DX= "segment:offset" -20-o bitna adresa koja e pokazati na lokaciju od koje je u ovom sluaju smeten na string Primer : DS=10 (1010 binarno) DX=50 (110010 binarno) bit 15 bit 0 Registar DS: 0000000000001010 bit 15 bit 0 Registar DX: 0000000000110010 Posle iftovanja tj. pomeranja ulevo za 4 bita segmentne adrese u DS dobijamo neto nalik na ovo : Ŀ 00000000000010100000 I kad treba da ovoj papazjaniji dodamo sadraj registra DX : Ŀ 00000000000010100000 Ŀ 0000000000110010 + Ŀ DS:DX 00000000000011010010 I eto, to je cela mudrost. Dobili smo znai broj 11010010b to je 210 decimalno. No, nije potrebno sve ovo razmatrati na binarnoj osnovi. Kako je iftovanje ulevo za 4 bita nita drugo do mnoenje sa 2*2*2*2 (ili 2na4), dakle sa 16, kalkulacija je mogla biti i ovakva : 10*16+50=210 - DS:DX sadraj DX registra sadraj DS registra Ovime se lako zakljuuje da se ista memorijska lokacija moe predstaviti i drugom kombinacijom SEGMENT-A i OFFSET-A. Prosto. Poto postoje izvesne razlike izmeu COM i EXE fajlova kada je u pitanju ovaj program(to je tampanje stringa, u sluaju da si zaboravio :), osvrnuu se na oba sluaja. Napiimo prvo .COM program koji tampa na ekran 'Hello, world!'. Program No. 4.1 ; PROGRAM Hello (HELLO.ASM) ; Copyright(C) 1998. by Shang Tsung .model tiny .code org 100h main: jmp pocetak poruka db "Hello, world!$" ;definisanje promenljive tj. stringa pocetak: mov dx,offset poruka ;offset tj.prirataj promenljive poruka mov ah,9h int 21h ret ;povratak u DOS end main Da razjasnimo. Posle "main:" sreemo zanimljivu sintaksu: jmp pocetak poruka db "Hello, world!$" pocetak: Naime, "jmp pocetak" mora biti tu jer ne moe se jednostavno izvriti kao naredba neka promenljiva koja se na njenu alost ba tu nalazi - procesor ima da izvri bog te pita koju naredbu. Proveri - nemoj staviti obilazak promenljive pa e videti uda(ovo ba i nije preporuljivo - verovatno e morati da resetuje raunar, no nemoj rei da te nisam upozorio :). Nema brige, ovime se ipak promenljiva registruje i zauzima se memorijski prostor za poruku. Moda ne bi bilo loe promenljive definisati na samom kraju programa, kako veina programera i radi. Znai, predhodni program je mogao i ovako da izgleda : ; PROGRAM Hello (HELLO.ASM) ; Copyright(C) 1998. by Shang Tsung .model tiny .code org 100h main: mov dx,offset poruka ;offset tj.prirataj promenljive poruka mov ah,9h int 21h ret ;povratak u DOS poruka db "Hello, world!$" ;definisanje promenljive tj.stringa end main Ovde nije trebalo preskakati promenljivu, jer izvravanje programa u stvari nikada nee ni stii do nje(promenljiva e se ipak inicijalizovati mada to nije oigledno) zbog RET koje joj predhodi. RET e nas u ovom sluaju vratiti u DOS na nain ije objanjenje sledi. No, ja u u narednim programima promenljive definisati nekad na poetku programa, nekad na kraju, arenila radi ;). -poruka db "Hello, world!$" Znai definisali smo promenljivu "poruka" koja je tipa bajt(db). Zato bajt ? Pa jednostavno: string se sastoji iz NIZA karaktera od kojih svaki zauzima po jedan bajt. Ima ukupno 255 (256) moguih karaktera (slova). Zapamti da string nije promenljiva, nego NIZ. Pod navodnicima se deklarie telo stringa. E tu ima neto zanimljivo : znak '$'. ta e on tu? Elem, funkcija 9h interapt-a 21h koju koristimo da itampamo string tampa sve od memorijske lokacije DS:DX dok ne naie na znak '$'. Naravno, sam znak '$' se ne tampa. Znai, ako ti treba da tampa znak '$' batali ovu funkciju. To smo znaci raistili. Idemo dalje. - mov dx,offset poruka mov ah,9h int 21h Znai, u DX smo upisali OFFSET(prirataj) promenljive poruka. Ali, ta je sa segmentom ? E, tu dolazimo do one malopre pomenute razlike. Kao to je ve reeno u prvom poglavlju, memorijski model TINY tretira kod i podatke kao NEAR tj. 16-o bitno. U COM fajlu svi segmenti se nalaze tj. integriu u jedan jedini segment. Znai, bez obzira koliko ti segmenata navodio u programu, ako eli da kreira COM fajl mora paziti da duina svih segmenata zajedno ne prelazi granicu od 64K. *Ovo je potanko objanjeno u delu "Segmentacija". No prvo nekoliko rei o Segmentnim Registrima... Segmentni registri Ŀ Registar Opis Ĵ CS code segment Ĵ DS data segment Ĵ SS stack segment Ĵ ES extra segment Svi ovi registri su 16-o bitni. Noviji raunari raspolau i sledeim segmentnim registrima: Ŀ Registar Opis Ĵ FS Ĵ 386 i noviji GS procesori Ovi registri su takoe 16-o bitni. Segmentni registri nam ukazuju na SEGMENT memorije. Jedan segment memorije moe biti duine od najvie 64K. CS sadri segmentnu adresu KODA tj. izvrnih instrukcija samog programa. CS:IP je 20-o bitni pokaziva koji pokazuje procesoru instrukciju u okviru programa koja treba da se izvri. IP je podsetiu te, "Instruction Pointer". Sve ovo naravno vai ako se radi u REAL modu. Na PROTECTED mod sistemu kakav je na primer Windows 95, CS:EIP pokazuje na trenutnu instrukciju u okviru programa ali CS ne sadri segmentnu adresu nego je pokaziva na tabelu ...:) DS je DATA segment tj. segment podataka. On, kao to mu i samo ime kae sadri segmentnu adresu dela memorije u kojoj su definisane promenljive. SS je STACK segment i on sadri segmentnu adresu STEKA. >>> Adresu STEK segmenta je bolje ne dirati !!! <<< ES je ekstra segment koji se ne koristi za neto odreeno - on je tu za programere i njihove programe. Registri FS i GS na novijim raunarima su prilino korisni programerima, jer se moe pristupati podacima iz vie razliitih segmenata memorije nego to se to moglo na starijim raunarima. *Sve ovo je potanko objanjenu u delu "Segmentacija". Elem, kako se na program generie u COM fajl, pri uitavanju programa u memoriju tj. pri startovanju programa MS-DOS e sve segmentne registre (CS,DS,SS,ES) inicijalno postaviti da pokazuju na poetak PSP-a naeg programa, nakon kojega se nalazi sam program. Znai, praktino u DS se ve nalazi segment promenljive poruka. Divno! ***PAZI!!! Ovo vai SAMO za COM fajlove.*** NAPOMENA : Kao to vidi, SS tj. STEK SEGMENT registar pokazuje isti segment kao i CS registar. Sam poetak steka se smeta na sam kraj segmenta. Tako, ako ima COM fajl koji je duine bliske duini od 64K tj. 65536 bajta, ima ozbiljan problem - lako se moe desiti da pri korienju steka doe do gubitka podataka. OPET PAZI!!! Ovo vai samo za COM fajlove. *OPIS STEKA JE NETO KASNIJE !!! To objanjava injenicu da smo promenljivu definisali u okviru koda (drugaije ni ne moe, jer ako pogleda opis memorijskog modela TINY koji je objanjen u prvom poglavlju, videe da se 64K koristi i za kod i za podatke tj.promenljive. Pokuaj i sam : definii promenljivu van koda i linker e odmah prijaviti greku : "Fatal : Cannot generate COM file : data below initial CS:IP defined".) *Neto vie o segmentima ima u delu ovog poglavlja : "Segmentacija". Znai DS:DX nam sada pokazuje na poetak stringa 'Hello, world!$'. Ostatak je ve poznat. Pozivamo funkciju 9h interapta 21h koja e na ekran itampati 'Hello, world!'. - RET (tanije u pitanju je RETN (RETurn Near)) Ova jednostavna komanda nas u ovom sluaju vraa u DOS. Ali mi smo rekli da nas mov ah,4ch int 21h vraa u DOS!!! Jeste, ali da objasnim. Kada smo startovali fajl ( npr. C:\ASM>HELLO ) na steku je ostavljena vrednost tipa WORD koja je jednaka 0 tj. 2 bajta vrednosti 0. RETN naredba sa vrha steka uzima WORD vrednost, smeta je u IP, i poveava SP registar za 2. Kako je u ovom sluaju u pitanju vrednost 0, IP dobija vrednost 0 i sledea instrukcija koja e se izvriti (CS:IP pokazuje na tu instrukciju) je sa samog poetka CODE segment-a tj. sa samog poetka PSP-a tj. naredba INT 20h koja okonava program na iji CODE segment pokazuje registar CS a to je upravo na program. :)))) Keeee?!!!! Samo nemoj da panii. U narednim poglavljima je potanko objanjeno ta je PSP kao i neto vie o IP registru. Za sada samo znaj da je na poetku CODE segmenta (COM fajla) instrukcija "int 20h"(naravno u mainskom jeziku). *Ako na kraj procedure navede naredbu RET, u zavisnosti od tipa procedure koji moe biti NEAR ili FAR, kompajler e generisati naredbe RETN odnosno RETF. Ako je definie van procedure, uzima se oblik karakteristian za taj memorijski model. U sluaju da ne zna ta je stek, brzo u se na to osvrnuti. Stek je deo memorije sa kojim se radi po LIFO(last in, first out) principu. Stek moe zamisliti kao au u koju ubacuje novie. Ako na primer ubaci 5 novia, da bi doao do prvog mora izvaditi poslednja etiri. Kada ostavi neku vrednost na stek (koja moe biti tipa WORD ili DOUBLE WORD (DWORD), registar SP(pokaziva na poslednju vrednost na steku) se prvo UMANJUJE za 2 odnosno 4 i tek onda se na stek upisuje vrednost. Vrednost se naravno upisuje na lokaciju SS:SP. Primeti da sam naglasio da se pri stavljanju vrednosti na stek registar SP umanjuje. Stvar je u tome to se upis na stek obavlja "unazad" kroz memoriju, odnosno ako je lokacija 10000 poetak steka(to znai da SP registar sadri vrednost 10001) i onda stavimo na stek vrednost tipa WORD, SP e se umanjiti za 2 i lokacije 10000 i 9999 e primiti vrednost. Ako opet stavimo WORD vrednost na stek, SP e se umanjiti jo za 2 a lokacije 9998 i 9997 e primiti vrednost itd. Pri skidanju vrednosti sa steka sve ide obrnuto. Ĵ SS:SP pokazuje na > 5. novi ovu adresu Ĵ 4. novi Ĵ Veliina jednog > 3. novi podatka na steku moe Ĵ biti 2 ili 4 bajta 2. novi Ĵ poetak steka > 1. novi Naredba RETN radi sledee - sa steka uzima adresu tipa WORD i upisuje je u IP tj. skae na tu adresu. U naem sluaju skae se na poetak PSP-a tj. neposredno nakon skoka izvrava se poziv interrapt-u 20h. Tu mora da pazi da se na steku, sem te adrese vie nita ne nalazi inae e da doe do kurlusa jer ima da se skoi bog te pita gde. Pokuaj i sam. U programu POGISPIS.ASM umesto mov ax,4c00h int 21h stavi ret E, povratak u DOS se nee odviti u ovom sluaju. Zato? Elem, pri proveri da li je pritisnut taster ESC obavio se skok u proceduru provera_ESC i na steku je ostavljena adresa mesta poziva. Ĵ mesto poziva procedure provera_ESC Ĵ --------------- 0 --------------- I sad, recimo da je bio pritisnut taster ESC. Onda e se odmah obaviti skok na labelu "kraj". Kako se taj skok obavlja instrukcijom JMP, stanje na steku ostaje nepromenjeno. I, kako se posle labele kraj nalazi RET(to e kompajler implementovati kao RETN), vratie se na prvu adresu odozgo sa steka tj. vratie se na mesto poziva procedure "provera_ESC" i program e nastaviti da radi kako treba : Itampae se karakter ija je ASCII vrednost 28 i ponovo e sve poeti od poetka. Dakle, neemo moi da napustimo program. Zbog toga sam ja koristio mov ax,4c00h int 21h Naravno, 'RET' se ne koristi samo da bi se vratilo u DOS(povratak u DOS korienjem ove naredbe je programerski trik). Vec smo je koristili da bismo se vratili na mesto poziva procedure. Kao to sam ve objasnio, kada pozovemo proceduru, na steku e se nai povratna adresa koja ukazuje na instrukciju koja sledi poziv procedure. I onda kada uradimo 'RET' fino se vratimo na mesto odakle smo pozvali proceduru. Sjajno! No, ako eli da bude 100% siguran da e se program okonati kad mu ti to kae, ti koristi intarrapt-e. E, sad. Vratiemo se ponovo na onu razliku izmeu COM i EXE fajla. Ekvivalentan program od malopre u EXE formatu izgleda ovako: Program No. 4.1.1 ; PROGRAM Hello EXE verzija (HELLOE.ASM) ; Copyright(C) 1998. by Shang Tsung .model tiny .stack 200h ;ova linija nije u sutini potrebna .code main: jmp pocetak poruka db "Hello, world!$" pocetak: mov ax,seg poruka mov ds,ax mov dx,offset poruka mov ah,9h int 21h mov ax,4c00h int 21h end main Ovde sreemo par novih stvari. -.stack 200h Definisali smo stek segment koji ima 200h bajtova. Ako ne navedemo ovu naredbu tj. inicijalizaciju stek segmenta, moe svata da se dogodi jer tim inom, na program nema svoj stek segment to je potencijalno opasno, jer svaki program bi trebao da ima svoj stek. -mov ax,seg poruka mov ds,ax Ovde dolazimo do malopreanje prie o segmentima. Naime u EXE fajlu mora se u DS(data segment) registar upisati segmentna adresa memorijske lokacije od koje poinje dotini string jer inicijalno MS-DOS nee DS registar postaviti da pokazuje na na CODE segmenta u kome je deklarisan string. "SEG" je instrukcija makro assembler-a koja e, u naem sluaju, pri kompajliranju generisati kod koji e u registar AX upisati segmentnu adresu promenljive "poruka". *Kako se u ovom sluaju promenljiva "poruka" nalazi u CODE segment-u, moemo saznati segmentnu adresu korienjem CS registra. Ali zar nije moglo : "mov ds,seg poruka". BEEP. Malo sutra! Stvar je u tome to se u Segmentni Registar ne moe upisati takozvana "Immediate Value" tj. direktna vrednost(konstanta) kao na primer mov ds,2000 Kako je direktiva "SEG" takoe "Immediate value" tj. obian broj to ni mov ds,seg poruka nee raditi. Ali, u segmentni registar se mogu kopirati podaci iz registara kao i podaci neke indeksne vrednosti. Na primer: mov ds,ax mov ds,[Memorijska_promenljiva_tipa_word+DI] Pazi!!! Kako je Segmentni registar 16-o bitni to i vrednost koja se kopira u njega mora biti 16-o bitna. Pokua li i sam ovako neto: mov ds,ah kompajler e odmah prijaviti greku : "Argument to operation or instruction has illegal size". Znaci, prvo u AX unesemo segment promenljive poruka i zatim u DS stavimo AX. Ostatak je vie manje poznat. NAPOMENA: Primeti da sam umesto 'RET' na kraju ipak stavio mov ax,4c00h ; ili mov ah,4ch , kako hoe int 21h Elem, 'ret' se vrlo zanimljivo ponaa kada je u pitanju EXE fajl pa nee tako jednostavno kao COM fajl da se vrati u DOS. ak ta vie, ima da zaglavi raunar. Razlog ovoga je taj to MS-DOS pri uitavanju EXE fajla malko drugaije rasporedi segmente te se skokom na adresu CS:0 nee skoiti na poetak PSP-a programa. Tanije PSP EXE fajla poinje od adrese iji je segmentni deo za 16 manji od adrese CODE segmenta, za razliku od COM fajla kod kojega je segmentna adresa PSP-a indetina segmentnoj adresi CODE segment-a. Znai PSP EXE fajla poinje od adrese (CS-16):0. *O PSP-u e biti rei u 6-om poglavlju. ta da se radi. Ja lino preferiram COM fajlove jer su, kao to e primetiti mnogo krai od EXE fajlova iz oiglednih razloga, no iole komplikovani programi su obino vei od 64K te COM fajl nije mogue generisati. Ako postoji potreba da se utvrdi memorijska lokacija od koje poinje PSP, moe se upotrebiti funkcija 62h interrapt-a 21h (funkcija radi samo ako je verzija DOS-a od 3.0 pa navie). Nakon poziva interrapt-u u registru BX e se nai segmentna adresa PSP-a. Sada ispii i kompajliraj obe verzije programa HELLO. Jedan mali osvrt na EXE verziju. Sledea dva primera su takoe vaea: ; PROGRAM Hello EXE verzija_1 ; PROGRAM Hello EXE verzija_2 ; Copyright(C) 1998. by Shang Tsung ; Copyright(C) 1998. by Shang Tsung .model small .model tiny .stack 200h .stack 200h .data .code poruka db "Hello, world!$" poruka db "Hello, world!$" .code main: main: mov ax,seg poruka mov ax,seg poruka mov ds,ax mov ds,ax mov dx,offset poruka mov dx,offset poruka mov ah,9h mov ah,9h int 21h int 21h mov ax,4c00h mov ax,4c00h int 21h int 21h end main end main ovde je promenljiva poruka definisana u okviru CODE segmenta Ovde smo promenljivu poruka definisali u okviru DATA segmenta. PAZI !!! Sa ".DATA" deklarie se DATA segment. Ovo je pojednostavljeni nain deklarisanja segmenata, no neto kasnije sve e to biti potanko objanjeno. Kako je u pitanju EXE fajl, sadraji segmentnih registara CS i DS e biti razliiti po startovanju izvrne verzije programa. ( Da napomenem : Pri startovanju EXE fajla DS e primiti vrednost koja nee biti SEGMENT promenljive "poruka" Primeti da je kao memorijski model upotrebljen model SMALL. Ako pogleda opis memorijskog model SMALL u prvom poglavlju, videe da je za taj memorijski model karakteristino da je rezervisano 64K za kod (kod segment) i 64K za podatke (data segment). No, kako naem programu zaista ne treba toliko memorije, i model tiny e raditi. Kada smo mi definisali promenljivu "poruka" nakon ".DATA", ta e se promenljiva nai u DATA segmentu. Ovo zna da bude prilino zbunjujue te, ako ni ovo ni deo "Segmentacija" koji nam uskoro predstoji ne kapira, jednostavno zaboravi - nije toliko bitno za poetnika. to se drugog programa tie, promenljiva "poruka" je tu definisana nakon ".CODE" ali pre "main:", znai u okviru CODE segmenta. Dakle, umesto mov ax,seg poruka mov ds,ax slobodno smo mogli da napiemo i mov ax,cs mov ds,ax Isto tako, umesto memorijskog modela TINY smo slobodno mogli da stavimo SMALL i da to jo i kombinujemo sa mov ax,cs mov ds,ax a da rezultati programa ostanu isti - itampae se "Hello, world!". Postavlja se pitanje, zato u ovom drugom programu, nije potrebno preskakati inicijalizaciju stringa ? Elem, stvar je u tome to je u ovom sluaju promenljiva "poruka" deklarisana pre labele "main", dakle pre "Entry Point" adrese. Kako je "Entry Point" adresa u stvari adresa od koje poinje izvravanje programa, to se inicijalizacija promenljive nikad nee ni izvriti. Ovako neto nee raditi u COM fajlu, jer u COM fajlu "Entry Point" adresa je uvek 100h, kako je objanjeno u prvom poglavlju, dok u EXE fajlu ova adresa moe da varira(to je objanjeno neto kasnije). Promozgaj malo o ovome - nije ba lako odmah ukapirati. Ako eli da definie neki tvoj segment, to nee moi uraditi sa ".ime_segmenta". Proveri i sam. Pokuaj da definie segment npr. .vazbuka (tj. .Zvazbuka). Da li e to raditi ? avola e raditi. Stvar je u tome to sa ".ime_segmenta" deklarie predefinisane segmente kojima je ovo pojednostavljena upotreba. No ako ti ikad padne na pamet da tako neto uradi, objasniu ti postupak definisanja novog segmenta(samo osnovu). Program No. 4.2 ;PROGRAM definisanje_novog_segmenta (novi_seg.ASM) ;Program se generie u .EXE fajl ; Copyright(C) 1998. by Shang Tsung zvazbuka SEGMENT ; definisali smo segment poruka db "Hello, world!$" ; promenljiva main: mov ax,seg poruka mov ds,ax mov ah,9 mov dx,offset poruka int 21h mov ah,4ch int 21h zvazbuka ENDS end main *Ovde bi dodue trebalo navesti jo jednu makro naredbu : ASSUME, ali da ne urimo sa teorijom. Elem. U ovom programu smo definisali segment ZVAZBUKA i promenljivu poruka smo definisali u okviru njega. Ispod main labele je ve poznat kod za tampanje stringa koji u stvari predstavlja promenljiva poruka. Primeti da su ovde i instrukcije tj. samo tampanje stringa takoe u "zvazbuka" segment-u. Na kraju programa pre 'end main' se nalazi red : zvazbuka ENDS (ili samo ENDS) To znai da se segment zvazbuka tu zavrava. -ENDS znai END Segment. Program se nee moi generisati u .COM fajl jer u COM fajlu sve, i kod i podaci tj. varijable, moraju biti u okviru jednog segmenta i poetna adresa mora biti 100h. Ekvivalentan program u COM formatu bi ovako izgledao : Program No. 4.3 ; PROGRAM definisanje_novog_segmenta_COM (novi_s_c.ASM) ; Program se generie u .COM fajl ; Copyright(C) 1998. by Shang Tsung zvazbuka SEGMENT org 100h main: mov ah,9 mov dx,offset poruka int 21h mov ah,4ch int 21h poruka db "Hello, world!$" zvazbuka ENDS end main Sada slobodno pokuaj da ispie i izvri predhodne programe. URA!!! RADI!!! Samo neto da napomenem. COM verzija "Hello, world" programa moe i ovako da izgleda : ; PROGRAM Hello COM verzija_2 ; Copyright(C) 1998. by Shang Tsung .model tiny .data poruka db "Hello, world !$" .code org 100h main: mov ah,9h mov dx,offset poruka int 21h ret end main E, ako ovako napie jedan COM program, nije se u sutini nita promenilo. Kako je u pitanju COM fajl, doi e do kombinovanja segmenata (ovo je potanko objanjeno u delu "Segmentacija") u jedan jedini segment i svi segmentni registri e pri startovanju programa opet inicijalno pokazivati na taj segment. Proveri. Kompajliraj predhodni program i potom promenljivu deklarii nakon "RET" i uporedi COM fajlove. Bie indetini. Tu mora paziti da memorijski model ostane TINY kao i za sve druge COM fajlove. Ako promeni memorijski model, OFFSET promenljive "poruka" e se poremetiti jer je specifirano da je promenljiva "poruka" u DATA segmentu (dakle ne u CODE segment-u) a neto nalik na mov ax,seg poruka mov ds,ax e proizvesti greku : "Cannot generate COM file : segment-relocatable items present in module..." tako da ne moemo baratati SEGMENT-om promenljive i ... ode mast u propast ;). *Ovo ne radi jer, kako je ve reeno, sve to jedan COM fajl koristi mora da se nalazi u jednom segmentu. Da smo promenljivu "poruka" definisali posle npr. "ret", promena memorijskog modela nee uticati na OFFSET promenljive, jer je ona definisana u okviru CODE segmenta. Proveri. No, za COM fajl uvek, kao memorijski model, treba uzeti model TINY. SEGMENTACIJA Segmentacija uopte je prilino komplikovana, i mnogi novopeeni ;) programeri u Assembler-u nalaze je velikom preprekom jer jednostavno ne mogu da ukapiraju kako sve to radi. to se mene tie, ako ne bude ba shvatio sve ove nebuloze, nije preterano vano ali kako se bude penjao na lestvici Assembler-a, pre ili kasnije morae se suoiti sa tim. No, ako za sada ne moe da ukapira sve ovo, zanemari i nastavi dalje. Ja u objasniti jo par stvari po pitanju segmentacije i uopte segmenata. Do sada smo, za kod segment uzimali skraenicu ".CODE". E, dolo je vreme da malko dublje pogledamo u sve to. Kada napie ".CODE", i memorijski model je TINY to je u sutini isto kao da si napisao _TEXT SEGMENT WORD PUBLIC 'CODE' AAAAARGH!!!!!!!!!! De, de, nije tako strano ;). E, ovde dolazimo do samog poetka prie o segmentima. WORD, PUBLIC, 'CODE' i sl. su parametri koji odreuju kakav e neki segment biti. Proi emo kroz sve te parametre neto kasnije. Deklarisanje segmenta uzima sledei oblik : Upravo to Rezervisana re ime_segmenta SEGMENT {READONLY} {align} {combine} {use} {'class'} .................. .................. parametri ime_segmenta ENDS -Ime segmenta je potrebno da bi kompajler kombinovao segmente, da bi znao da li je potreban "segment override" tj. da li je potrebno specificirati koji segmentni registar sadri adresu segmenta i da bi znao samu segmentnu adresu. Ako ime segmenta nije jedinstveno, svako naredno pojavljivanje imena koje je upotrebljeno za deklarisanje segmenta, takoe mora predstavljati segment. U takvom sluaju assembler kombinuje te segmente u jedan kontinualan blok. Svaki segment ima svoj broja lokacije. Kada definiemo novi segment, njemu se dodeljuje poseban broja lokacije koji je inicijalno nula. Ako definiemo segmente sa istim imenima, svaki sledei takav segment predstavlja jednostavno nastavak preanjeg, samo na drugom mestu u okviru programa, i broja lokacije se nee resetovati ve e nastaviti od predhodne vrednosti. Broja lokacije je potreban da bi se znali OFFSET-i labela, promenljivih..., i on u COM fajlu se mora modifikovati naredbom "ORG 100h" jer inicijalna vrednost brojaa lokacije ne treba biti 0 nego 256(100h) da bi se zaobiao PSP COM programa koji zauzima prvih 256 bajta CODE segmenta. *PSP je objanjen u 6-om poglavlju. Segmenti mogu samo izgledati rascepkani u okviru programa, ali e assembler sve te delie jednog segmenta spojiti u jedan jedinstveni segment. Verovatno ti je ovo sve zbunjujue, pa bi moda bilo bolje da navedemo par primera. Primer No. 1 ; PROGRAM segmenti_primer1 (SPR1.ASM) ; Copyright(C) 1998. by Shang Tsung zvazbuka SEGMENT ; definiemo zvazbuka segment main: mov ah,2h ; broj funkcije mov dl,'a' ; karakter zvazbuka ENDS milojko SEGMENT mov ah,1h ; u AH upisujemo vrednost 1 milojko ENDS zvazbuka SEGMENT int 21h ; poziv interrapt-u 21h mov ah,4ch ; u AH upisujemo vrednost 4ch zvazbuka ENDS milojko SEGMENT mov ah,0 ; u AH upisujemo vrednost 0 milojko ENDS zvazbuka SEGMENT int 21h ; povratak u DOS zvazbuka ENDS end main *Ovde je izostavljena makro naredba "ASSUME" koja e biti objanjena neto kasnije. Koliko god da udno izgleda, ovaj program na ekran tampa slovo 'a'. ?!!!!!!!!!!!!!! Da poemo od poetka. Prvo smo definisali novi segment - "Zvazbuka". zvazbuka SEGMENT main: mov ah,2h mov dl,'a' zvazbuka ENDS Labela 'main' ukazuje na poetak koda tj. tzv.(ta je skraenica Bog te :) "Entry point". Pri startovanju izvrne verzije programa odatle poinje samo izvravanje. To da ova labela odreuje poetak izvravanja naredbi programa odreuje naredba "END main". U okviru OVOG DELA "zvazbuka" segment-a smo obavili 2 radnje - 1. U AH registar smo upisali broj funkcije interrapt-a 21h koja je (kao to nam je ve dobro poznato) zaduena za tampanje karaktera iji se ASCII kod nalazi u DL na ekran. 2. U DL smo upisali ASCII kod karaktera 'a'. I tu se OVAJ DEO "zvazbuka" segment-a zavrava. Zatim smo definisali novi segment - "milojko" :). milojko SEGMENT mov ah,1h ; u AH upisujemo vrednost 1 milojko ENDS U okviru ovog segment-a smo obavili samo jedno - upisivanje vrednosti 1 u registar AH. Nakon toga smo PONOVO "definisali", odnosno naveli segment "zvazbuka". zvazbuka SEGMENT int 21h ; poziv interrapt-u 21h mov ah,4ch ; u AH upisujemo vrednost 4ch zvazbuka ENDS U okviru ovog dela "zvazbuka" segment-a smo obavili poziv interrapt-u 21h i upisivanje vrednosti 4ch u registar AH. Zatim smo ponovo naveli "milojko" segment. milojko SEGMENT mov ah,0 ; u AH upisujemo vrednost 0 milojko ENDS U okviru ovog dela "milojko" segment-a smo obavili upis vrednosti 0 u AH. I na kraju, ponovo smo naveli "zvazbuka" segment u okviru kojega smo samo pozvali interrapt 21h. zvazbuka SEGMENT int 21h ; povratak u DOS zvazbuka ENDS I to radi. Kako ? Elem. Assembler je pri kompajliranju povezao sve segmente sa istim imenima i jedan kontinualan segment. Znai prvo je povezao sve segmente sa imenom "zvazbuka" i time izgradio segment sledee sadrine : mov ah,2h mov dl,'a' int 21h mov ah,4ch int 21h Gle uda. Ovo pare koda zaista tampa slovo 'a' na ekran i vraa kontrolu DOS-u. Ali ta je sa "milojko" segment-om? "Milojko" segment se smestio nakon "zvazbuka" segment-a, i, nakon povezivanja izgleda ovako mov ah,1h mov ah,0 Ako bi pogledao kako izgleda izvrna verzija ovog programa, primetio bi na postoje 4 bajta izmeu ova 2 segmenta koja su, najverovatnije, nule. Kako e uskoro biti objanjeno, dolo je do "PARA" poravnavanja segmenata, tj. segment "milojko" se naao na adresi koja je deljiva sa 16. No, da ne urimo sa teorijom. Ono to je vano jeste : prvo se izvrava "zvazbuka" segment, a tek potom "milojko" segment. No, kako je na samom kraju "zvazbuka" segment-a rutina koja prekida program i vraa kontrolu DOS-u, to se 2 instrukcije u okviru "milojko" segmenta nee nikada ni izvriti. Izgleda aavo, ali mene lea vie ne bole. ;) Sada slobodno ispii predhodni primer i kompajliraj ga. Ovako kako je napisan, program e se moi generisati samo u EXE fajl, ali ako odmah posle deklarisanja "zvazbuka" segment-a navede "org 100h" tj. modifikuje "Entry Point", moi e se generisati i u COM fajl. Jo neto. to se tie "Location counter"-a tj. brojaa lokacije, na poetku "zvazbuka" segment-a (pri prvom pojavljivanju) on je 0. Zatim slede naredbe "mov ah,2h" dugaka 2 bajta i "mov dl,'a'" dugaka takoe 2 bajta, iz ega sledi da je na kraju prvog pojavljivanja "zvazbuka" segment-a, broja lokacije asociran sa "zvazbuka" segment-om jednak 4. Zatim sledi "milojko" segment i sa njim se asocira novi broja podataka, koji e (pri prvom pojavljivanju "milojko" segment-a) biti 0. Zatim sledi naredba "mov ah,1h" koja zauzima 2 bajta, te e na kraju prvog pojavljivanja "milojko" segment-a njegov broja lokacija biti 2. Zatim se po drugi put pojavljuje "zvazbuka" segment u kome su opet dve instrukcije : "int 21h" duine 2 bajta i "mov ah,4ch" duine opet 2 bajta. Znai na kraju drugog pojavljivanja "zvazbuka" segment-a, broja lokacije asociran sa njim jednak je broju 8. Nakon toga, po drugi put se pojavljuje "milojko" segment koji sadri jednu instrukciju "mov ah,0" duine opet dva bajta (ovo je neverovatno ;), te je broja lokacije asociran sa "milojko" segment-om jednak 4. I na samom kraju imamo po trei put naveden "zvazbuka" segment u kome se nalazi samo jedna instrukcija : "int 21h" koja opet zauzima 2 bajta. Znai broja lokacije asociran sa "zvazbuka" segment-om sada iznosi 10. I eto, to je cela mudrost. Sve ovo znai, da e asemblovanje drugog pojavljivanja "zvazbuka" segment-a poeti od offset-a 4, a ne 0. Naravno ako si naveo "ORG 100h" pre labele "main:", pri prvom pojavljivanju "zvazbuka" segment-a broja lokacija nee biti 0 nego 100h(256 dec) to znai da e na kraju biti 10Ah tj. 266. Ukoliko nikako ne moe da ukapira ovu papazjaniju, slobodno preskoi ovaj deo o segmentima. Dodue, kada malo ovlada assembler-om, pre ili kasnije, morae se ponovo suoiti sa ovim. Redosled uitavanja segmenata je onim redom kojim su i navedeni. Znai u naem primeru "zvazbuka" segment e se uitati u memoriju pre "milojko" segmenta. Ovo se moe i promeniti tako da se "milojko" segment uita u memoriju pre "zvazbuka" segment-a ali u tom sluaju nee se moi generisati COM fajl. Stvar je u tome da je "Entry Point", tj. adresa od koje poinje izvravanje fajla(koja je definisana labelom "main") u COM fajlu uvek 100h, a ako bi uitali prvo "milojko" segment to se nee dogoditi jer e se dve instrukcije u okviru "milojko" segment-a nalaziti iznad labele "main", dok u EXE fajlu "Entry Point" adresa moe da varira, jer EXE fajl za razliku od COM fajla ima zaglavlje tj. blok na samom poetku fajla u kome su skladitene razne informacije meu kojima se nalazi i "Entry Point" adresa. Navoenjem direktive ".ALPHA", assembler e sortirati segmente prema abecedi. - Postoji 6 razliitih operanada koje direktiva SEGMENT moe da primi. To su : ALIGN operand, COMBINE operand, CLASS operand, READONLY operand, USES operand i SIZE operand. Idemo redom. -ALIGN operand ALIGN operand moe biti jedna od sledeih direktiva : BYTE, WORD, DWORD, PARA ili PAGE. ALIGN operand predstavlja instrukciju kompajleru, linkeru i samom DOS-u da uita segment poinjui od memorijske lokacije ija je adresa deljiva brojem 1, 2, 4, 16, ili 256 respektivno. BYTE align je poravnavanje (boundary) sa lokacijom ija je adresa deljiva brojem 1, WORD align brojem 2, DWORD align brojem 4, PARA align brojem 16, i PAGE align brojem 256. Navodjenje ALIGN operanda nije obavezno i, ako ono izostane, po default-u uzima se PARA align-ovanje. Znai, ako bi za 2 segmenta definisali BYTE align, oni bi se u memoriju uitali otprilike ovako : Ŀ SEGMENT 1 Ĵ< Nema neiskoritenog prostora SEGMENT 2 Ali ako je u pitanju PARA align, dogodie se sledee : Ŀ SEGMENT 1 Ĵ < najvie 15 bajta neiskoritenog Ĵ prostora SEGMENT 2 Kako segmentni registri 80x86 procesora uvek pokazuju na PARA adrese, veina segmenata su PARA align-ovani. Zbog toga, za segmente koje bude definisao uvek navedi PARA align sem ako nema dobar razlog da uradi drugaije. No kako je PARA u stvari default align metod, ALIGN direktivu nije uopte potrebno ni navoditi. -COMBINE operand COMBINE tip odreuje redosled kojim se segmenti sa istim imenima upisuju u .OBJ fajl koji kreira kompajler i samim time redosled uitavanja segmenata u memoriju. Da bi specificirali koji COMBINE tip elimo, koristimo jednu od sledeih mogunosti : PUBLIC, STACK, COMMON, MEMORY, ili AT. MEMORY je sinonim za PUBLIC i tu je radi kompatibilnosti, no treba uvek koristiti PUBLIC umesto MEMORY. COMMON i AT direktive su relativno komplikovane i, opet relativno, nepotrebne(bar nama) te ih neu ni objanjavati. STACK direktivu treba koristiti za stek segment a PUBLIC direktivu za skoro sve ostalo. PUBLIC i STACK direktiva u sutini rade isto - povezuju sve segmente sa istim imenom u jedan kontinualan segment. Razlika je u toma kako MS-DOS inicijalizuje SS i SP registre. Svaki program(EXE) bi trebao da ima STEK segment (ili e linker izdati upozorenje), dok bi svi ostali segmenti trebali biti tipa PUBLIC. DOS e automatski postaviti SS(Stack Segment) registar da pokazuje na segment koji je deklarisan sa STACK combine tipom pri uitavanju programa u memoriju. Ako se COMBINE operand ne navede, podrazumeva se PRIVATE, tj. ne dolazi do povezivanja segmenata. Ovde se ve postavlja jedno pitanje. Zato bi neko uopte rasparavao segmente na ovakav nain? Elem, to moe biti veoma korisno. Ako na primer u segmentu "PODACI" uvamo sve promenljive koje na program koristi, a sam program se sastoji iz niza procedura, nije uopte loe navoditi pare "PODACI" segment-a ispred svake procedure i u okviru tog dela "PODACI" segment-a definisati promenljive koje su potrebne toj proceduri. Tako je mnogo lake kontrolisati promenljive koje program koristi, i bilo bi prilino nezgodno da su sve promenljive navedene na jednom mestu pogotovo ako ih ima mnogo (ko bi se tu snaao ? :). -CLASS operand Poslednji operand deklarisanja segmenta je obino CLASS operand. On odreuje nain povezivanja segmenata razliitih imena. Ovaj operand se sastoji iz simbola koji se nalazi izmeu jednostrukih navodnika. Generalno, koriste se sledei simboli : 'CODE' - za segmente koji sadre izvrni kod programa, 'DATA' - za segmente koji sadre promenljive, konstante i tabele, 'CONST' - za segmente koji sadre konstante i tabele i 'STACK' - za stek segment. Pri povezivanju segmenata, assembler povee prvo delove segmenata sa istim imenima (kao to je ve objanjeno) i potom na to nadovee segmente koji pripadaju istoj klasi s tim to se segmenti sa razliitim imenima ipak ALIGN-uju. Na primer : Primer No. 2 ; PROGRAM segmenti_primer2 (SPR2.ASM) ; Copyright(C) 1998. by Shang Tsung zvazbuka SEGMENT PUBLIC 'CODE' org 100h main: mov ah,2h mov dl,'a' zvazbuka ENDS milojko SEGMENT PUBLIC 'DATA' mov ah,1h milojko ENDS zvazbuka SEGMENT PUBLIC 'CODE' int 21h mov ah,4ch zvazbuka ENDS milojko SEGMENT PUBLIC 'DATA' mov ah,0 milojko ENDS zvazbuka_2 SEGMENT PUBLIC 'CODE' int 21h zvazbuka_2 ENDS end main *U sutini potrebno je (u ovom programu nije) i navesti makro "ASSUME" koji ovde nije naveden. Ovaj program e takoe da itampa slovo 'a' na ekran. E, ovde mora da pazi na neto. Kako je krajnji segment (zvazbuka_2) takoe u klasi 'CODE' kao i segment "zvazbuka", poto se poveu svi delovi "zvazbuka" segment-a, ne nastavlja se "milojko" segment-om (jer on pripada 'DATA' klasi), ve upravo "zvazbuka_2" segment-om. No, kako se imena segmenata razlikuju, to su u sutini odvojeni segmenti i jedina slinost je u tome to se segmenti koji pripadaju istoj klasi nastavljaju jedan na drugi. Ono na ta treba paziti je ALIGN operand. Kako ga ovde nismo naveli, podrazumeva se PARA. Kako segmenti "zvazbuka" i "zvazbuka_2" imaju razliita imena, to ih kompajler i tretira kao razliite, te e "zvazbuka_2" segment PARA align-ovati, to znai da e izmeu poslednje naredbe "zvazbuka" segment-a (mov ah,4ch) i prve naredbe "zvazbuka_2" segment-a postojati praznina koja e najverovatnije (bar je tako kod mene) biti ispunjena nulama. Nakon to izvri poslednju instrukciju u okviru "zvazbuka" segment-a, nekoliko puta e se izvriti instrukcija koja je u mainskom jeziku predstavljena sa dve nule, to u prevodu znai mov [bx+si],al Znai na lokaciju [bx+si] u segmentu iju segmentnu adresu sadri DS na OFFSET-u bx+si, nekoliko puta e se upisati vrednost registra AL. To moe biti potencijalno opasno jer niko ne zna ta se na toj memorijskoj lokaciji nalazi ( Lino sam proterao takav program kroz debager, i video sam da je malopre pomenuta naredba modifikovala sam kod programa). Iz tog razloga, ne bi bilo loe navesti BYTE align za segment "zvazbuka_2". Ovde se krije jo jedna zamka. Kada smo definisali "zvazbuka_2" segment, bez obzira to je on takoe u klasi 'CODE' kao i "zvazbuka" segment, njemu se pridruio NOVI broja lokacije to znai da je pri deklaraciji "zvazbuka_2" segment-a, njegov broja lokacije jednak nuli. Ovo znai da, ako bismo na primer naveli promenljivu "promenljiva1" na samom poetku "zvazbuka" segment-a i promenljivu "promenljiva2" na samom poetku "zvazbuka_2" segment-a, OFFSET promenljive "promenljiva1" e biti 100h (jer je broja lokacije prethodno modifikovan naredbom "ORG") a OFFSET promenljive "promenljiva2" nula. Iz tog razloga, kada bi recimo trebalo tampati string koji se nalazi u segmentu "zvazbuka_2" iz segmenta "zvazbuka", DS registar moramo modifikovati ne bi li sadrao segmentnu adresu "zvazbuka_2" segment-a. - READONLY operand Ovaj operand, ako se navede kao prvi parametar, uini segment READ-ONLU, tj. generisae greku pri pokuaju upisivanja u segment. No kako se ove greke mogu uloviti samo u toku kompajliranja, lako se moe prevariti assembler i ipak eprkati po segmentu. No, definisanjem ovog operanda mogu se uloviti greke u programu koje bi se inae previdele. - USES operand Kada pie program koristei prednosti 80386 ili novijeg procesora, odnosno set naredbi u mainskom jeziku koje su uvedene tek kod tog tipa procesora (to znai da ih stariji procesori ne prepoznaju), assembler generie jedan kod za 16-o bitne, i totalno drugaiji kod za 32-o bitne segmente. Ako pie program koji treba da se izvrava u REAL modu pod DOS-om, mora koristiti 16-o bitne segmente. 32-o bitni segmenti su dostupni samo programima koji rade u PROTECTED modu. Assembler meutim, esto postavi kao default 32-o bitni reim rada ukljui li 386 i navie instrukcije u program(to se radi preko .386 i slino, to je objanjeno neto kasnije). Ako elimo da radimo u odreenom reimu, to eksplicitno moramo rei assembler-u. To se radi preko direktiva USE16, USE32 ili FLAT. Za veinu DOS aplikacija koristiemo USE16 operand. Ova instrukcija govori kompajleru da su segmenti 16-o bitni. Ako ikada ukljui 386 ili neke vie instrukcije u svoj program, treba ukljuiti ovu direktivu da kompajler ne bi proizveo pogrean kod(sem ako ne deklarie pojednostavljene segment-e (tipa .CODE ili .DATA)). Da bi program radio u 32-o bitnom reimu treba koristiti USE32 i FLAT operande, no kako se u ovom izdanju neemo baviti PROTECTED modom (di e PROTECTED mod u samom poetku ?!!!), to ih neu ni objanjavati. UH!!! Da rezimiramo. Napiimo deklaracije CODE, DATA, CONST i STACK segmenta koji su 16-o bitni. KOD SEGMENT PARA PUBLIC USE16 'CODE' KOD ENDS PODACI SEGMENT PARA PUBLIC USE16 'DATA' PODACI ENDS KONSTANTE SEGMENT PARA PUBLIC USE16 'CONST' KONSTANTE ENDS STEK SEGMENT PARA STACK USE16 'STACK' STEK ENDS *Ovde sam segmente namerno imenovao po naki ;) Prosto. Pregledajmo predefinisane segmente. To su u stvari pojednostavljene deklaracije segmenata. Tako smo i do sada radili - jednostavno smo pisali ".CODE" bez razmiljanja ta sve to predstavlja. E, pa da vidimo o emu se radi. Ŀ TINY MEMORIJSKI MODEL Ĵ Directive Segment Alignment Combine Class Ĵ .CODE _TEXT WORD PUBLIC 'CODE' .FARDATA FAR_DATA PARA PRIVATE 'FAR_DATA' .FARDATA? FAR_BSS PARA PRIVATE 'FAR_BSS' .DATA _DATA WORD PUBLIC 'DATA' .CONST CONST WORD PUBLIC 'CONST' .DATA? _BSS WORD PUBLIC 'BSS' .STACK STACK PARA STACK 'STACK' Ĵ SMALL MEMORIJSKI MODEL Ĵ Directive Segment Alignment Combine Class Ĵ .CODE _TEXT WORD PUBLIC 'CODE' .FARDATA FAR_DATA PARA PRIVATE 'FAR_DATA' .FARDATA? FAR_BSS PARA PRIVATE 'FAR_BSS' .DATA _DATA WORD PUBLIC 'DATA' .CONST CONST WORD PUBLIC 'CONST' .DATA? _BSS WORD PUBLIC 'BSS' .STACK STACK PARA STACK 'STACK' Ĵ COMPACT MEMORIJSKI MODEL Ĵ Directive Segment Alignment Combine Class Ĵ .CODE _TEXT WORD PUBLIC 'CODE' .FARDATA FAR_DATA PARA PRIVATE 'FAR_DATA' .FARDATA? FAR_BSS PARA PRIVATE 'FAR_BSS' .DATA _DATA WORD PUBLIC 'DATA' .CONST CONST WORD PUBLIC 'CONST' .DATA? _BSS WORD PUBLIC 'BSS' .STACK STACK PARA STACK 'STACK' Ĵ MEDIUM MEMORIJSKI MODEL Ĵ Directive Segment Alignment Combine Class Ĵ .CODE name_TEXT WORD PUBLIC 'CODE' .FARDATA FAR_DATA PARA PRIVATE 'FAR_DATA' .FARDATA? FAR_BSS PARA PRIVATE 'FAR_BSS' .DATA _DATA WORD PUBLIC 'DATA' .CONST CONST WORD PUBLIC 'CONST' .DATA? _BSS WORD PUBLIC 'BSS' .STACK STACK PARA STACK 'STACK' Ĵ LARGE ili HUGE MEMORIJSKI MODELI Ĵ Directive Segment Alignment Combine Class Ĵ .CODE name_TEXT WORD PUBLIC 'CODE' .FARDATA FAR_DATA PARA PRIVATE 'FAR_DATA' .FARDATA? FAR_BSS PARA PRIVATE 'FAR_BSS' .DATA _DATA WORD PUBLIC 'DATA' .CONST CONST WORD PUBLIC 'CONST' .DATA? _BSS WORD PUBLIC 'BSS' .STACK STACK PARA STACK 'STACK' Ĵ TPASCAL MEMORIJSKI MODEL Ĵ Directive Segment Alignment Combine Class Ĵ .CODE CODE BYTE PUBLIC .DATA DATA WORD PUBLIC Ovde dolazimo do same sri korienja memorijskih modela. Kao to je verovatno ve oigledno, specificiranje memorijskog modela je od znaaja jedino ako u okviru programa definiemo segmente koristei njihove "skraenice" tj. ".CODE", ".DATA" i sl. Kada smo u malopreanjim primerima sami definisali svoje segmente, memorijski model nisno ni navodili jer je to u potpunosti nepotrebno. U predhodnoj tabeli je navedeno kako koja "skraenica" predstavlja segment u odreenom memorijskom modelu. Na kraju je naveden jedan memorijski model koji nisam pominjao - TPASCAL. Re je o memorijskom modelu koji se koristi kada je potrebno povezati(link) program odnosno proceduru napisanu u assembler-u sa programom pisanim u Turbo Pascal-u. Nadam se da ti je slika svega ovoga malko istija. Nije ba teko, ali je prilino zapetljano. 80x86 procesor generalno opti sa podacima relativno, u odnosu na DS registar. Isto tako, sve naredbe u CODE segmentu (J... naredbe, CALL ...) tretiraju se u odnosu na CS registar tj. trenutni kod segment. Tu se postavlja jedno pitanje. Kako assembler zna koji je segment CODE segment ili DATA segment ili STACK segment ? Tu dolazimo do poente : DATA segment je "DATA" jer DS registar pokazuje na njega. Isto tako CODE segment je "CODE" jer CS pokazuje na njega tj. sadri njegovu segmentnu adresu. Kako sadraj DS registra moe biti promenjen u toku izvravanja programa, bilo koji segment moe biti DATA segment. Kada deklarie DATA segment u okviru programa(ne pojednostavljenom upotrebom), ne samo da mora procesoru da kae da je to DATA segment (to se realizuje upisivanjem segmentne adrese segmenta u DS registar), ve mora i samom kompajleru da kae gde i kada je taj segment DATA segment. Mnogopominjana direktiva ASSUME daje ovakve podatke kompajleru. ASSUME direktiva ima sledeu sintaksu: ASSUME {CS:seg} {DS:seg} {ES:seg} {FS:seg} {GS:seg} {SS:seg} Bar jedan od ovih parametara mora biti ukljuen u zaglavlje naredbe. "Seg" je ili ime nekog definisanog segmenta ili re NOTHING. Direktiva ASSUME ne modifikuje segmentne registre. Ona jednostavno govori assembler-u da pretpostavi(ASSUME) da odreeni segmentni registar pokazuje na odreeni segment u okviru programa(tj. sadri njegovu segmentnu adresu) i da sve naredbe koje se odnose na taj segment organizuje relativno u odnosu na njega. U sutini, ASSUME direktiva govori assembleru da smo, na ovaj ili onaj nain, u odreeni segmentni registar upisali segmentnu adresu nekog segmenta u okviru naeg programa. Ako je potrebno ukloniti tu pretpostavku za odreeni segmentni registar, umesto imena registra potrebno je navesti re "NOTHING". Prosto kao pasulj. Navedimo primer koji e ovo ilustrovati: Primer No.3 ; PROGRAM segmenti_primer3 (SPR3.ASM) ; Copyright(C) 1998. by Shang Tsung DATA SEGMENT PARA PUBLIC 'DATA' ; deklaracija DATA segmenta varijabla1 db 12 DATA ENDS DATA_2 SEGMENT PARA PUBLIC 'DATA' ; deklaracija DATA_2 segmenta varijabla2 db 89 DATA_2 ENDS CODE SEGMENT PARA PUBLIC 'CODE' ; deklaracija CODE segmenta ASSUME cs:CODE, ds:DATA, es:DATA_2 ; ASSUME da CS pokazuje na CODE ; segment, DS na DATA segment a ES na ; DATA_2 segment main: ; "Entry Point" mov ax,seg DATA ; u DS upisujemo segmentnu adresu mov ds,ax ; DATA segmenta mov ax,seg DATA_2 ; u ES upisujemo segmentnu adresu mov es,ax ; DATA_2 segmenta mov cl,[varijabla1] ; u CL upisujemo vrednost varijabla1 call ispisi_vrednost_registra_CL ; ispisujemo vrednost registra CL mov cl,[varijabla2] ; u CL upisujemo vrednost varijabla2 call ispisi_vrednost_registra_CL ; ispisujemo vrednost registra CL mov [varijabla1],3 ; menjamo vrednost varijabla1 u 3 mov [varijabla2],41 ; menjamo vrednost varijabla1 u 41 mov cl,[varijabla1] ; u CL upisujemo vrednost varijabla1 call ispisi_vrednost_registra_CL ; ispisujemo vrednost registra CL mov cl,[varijabla2] ; u CL upisujemo vrednost varijabla2 call ispisi_vrednost_registra_CL ; ispisujemo vrednost registra CL mov ah,4ch ; povratak u DOS int 21h ; ;***************************************************** ; procedura tampa vrednost CL registra na ekran(decimalno) ispisi_vrednost_registra_CL PROC ; Procedura je navedena u treem poglavlju :) ispisi_vrednost_registra_CL ENDP CODE ENDS end main ; kraj programa Da malko razjasnimo. Na samom poetku programa deklarisali smo dva segmenta klase 'DATA'. To su "DATA" i "DATA_2". U okviru svakog od njih, definisali smo po jednu varijablu i inicijalno u nju upisali neku vrednost. Potom dolazi deklaracija "CODE" segmenta. I odmah na poetku, rekli smo assembleru kako su rasporeeni segmentni registri : CS pokazuje na "CODE" segment, DS na "DATA" segment a ES na "DATA_2" segment. Ovde je potrebno staviti da CS pokazuje na CODE segment, jer pri pozivu procedure "ispisi_vrednost_registra_CL" bi se dogodila greka kada to ne bi uinili. Kompajler bi jednostavno prijavio greku "**Error** spr3.asm(38) Near jump or call to different CS" jer ne zna u kom se segmentu nalazi dotina procedura(koja je definisana u "CODE" segment-u). J... naredbe, CALL i sl. po default-u "skau" na offset u CODE segment-u i CS registar sadri segmentnu adresu tog segmenta. Bez "ASSUME cs:CODE", dotina procedura se mogla nalaziti i u nekom drugom segmentu, to znai da argument CALL naredbe treba modifikovati relativno u odnosu na taj segment tj. u odnosu na to koji segmentni registar sadri segmentnu adresu dotinog segmenta. Dalje, DS je ASSUME-ovan da pokazuje na "DATA" segment a ES na "DATA_2" segment, jer emo na samom poetku programa u te registre upisati segmentne adrese tih segmenata. Ako bismo izostavili te ASSUME-ove kompajler bi generisao sledeu greku : "Can't address with currently ASSUMEd segment registers". Nakon podeavanja segmentnih registara, u registar CL postavljamo vrednost varijabla1 i tampamo tu vrednost na ekran. Procedura "ispisi_vrednost_registra_CL" je opisana u treem poglavlju. Nakon toga, u CL postavljamo vrednost varijabla2 i tampamo tu vrednost. Sada bi na ekranu trebali da vidimo itampane inicijalne vredosti varijabli. Nakon ovoga menjamo vrednosti varijablama i ponovo ih tampamo da bi smo se uverili u rezultat. Kada u CL registar upisujemo vrednost varijabla2, kako je ASSUME-ovano da je varijabla2 u segmentu na koji pokazuje ES, umesto mov cl,[varijabla2] pri kompajliranju, kompajler e generisati sledeu naredbu mov cl,es:[varijabla2]. Ovde se vrednost registra CL upisuje na memorijsku lokaciju iji je OFFSET predstavljen labelom "varijabla2" i ija se segmentna adresa nalazi u registru ES. Ostatak je vie-manje poznat. *U ovom programu sam namerno izostavio STEK segment. U ovom delu poglavlja (SEGMENTACIJA) pojavljuju se neke stvari koje nisu jo uvek potanko objanjene. Stvar je u tome to je oblast "Segmentacija" ubaena u ovo poglavlje tek nakon to je ceo help napisan. Tako, ako ti neki pojam nije poznat, ti preskoi "Segmentaciju" pa se kasnije, kada shvati ta odreeni pojmovi predstavljaju, vrati na ovu oblast. Nadam se da ti je sve ovo jasno. Ako nije, batali segmentaciju - bie dana za megdana. ;) Da otkrijem jo par stvari pre nego to preemo na neto drugo. Kako da kursor pree u novi red ? E, tu vidi treba znati da je novi red, u stvari, kombinacija dva ASCII karaktera (bar u MS-DOS-u :). To su karakteri 13 i 10 (ASCII vrednosti 13 i 10 naravno). Karakter 10 je tzv. LINEFEED (LF) a karakter 13 je takozvani CARRIAGE RETURN (CR) to je nita drugo do ASCII kod koji proizvodi sam ENTER. Iz ova dva karaktera se sastoji prelazak u novi red. ta se u stvari deava? Kada se itampa karakter 13 on u stvari inicira kursor da se pomeri na poetak trenutnog reda. Karakter 10 inicira kursor tako to ga pomera za jedan red na dole. Ako se kursor nalazi u poslednjem redu na ekranu, ceo ekran se skroluje za jedan red na gore. Pokuaj i sam, itampaj samo karakter 13 ili 10 pa e videti ta e da se desi. Ali kako da ih itampamo ? Evo ovako: poruka db "Hello, world",13,10,"Zdravo, svete$" Znai samo navede ASCII vrednosti pri deklaraciji promenljive(ili niza:) i sve je OK. to se redosleda tie, moram primetiti da svi programi na koje sam ja nabasao koriste kombinaciju prvo karakter 13 pa tek onda 10, te i tebi preporuujem da tako uradi. Sada slobodno pokuaj da itampa ovakav string. Mislim da bih trebao da objasnim jednu instrukciju koju sam verovatno trebao mnogo pre da objasnim (kada sam opisivao stek). -PUSH Push naredba Push-ne (gurne) na stek vrednost npr. registra, s' tim to prvo umanji vrednost SP registra (Stack Pointer) za 2(odnosno 4) i na memorijsku lokaciju SS:SP upie dotinu vrednost. Primeri: push ax ; Na stek stavljamo vrednost registra AX push dx push ax bx cx dx ; Na stek stavljamo vrednosti registara AX, BX, CX, DX tim redom. *Vrednost koja se PUSH-uje je tipa WORD sem ako ne PUSH-uje 32-o bitni registar (npr. EAX) kada je vrednost tipa DWORD i samim time se SP registar ne umanjuje za 2 nego za 4 bajta. -POP POP naredba POP-uje tj. vraa predhodno sauvane vrednosti sa steka sa memorijske lokacije SS:SP i potom uveava vrednost SP registra za 2(odnosno 4). Primeri: pop ax pop dx pop ax bx cx dx Ove dve naredbe su veoma vane kada se eli sauvati vrednost nekog registra. UPOZORENjE !!! Kao to sam ve objasnio, stek radi po LIFO principu. Znai, ako na primer uradi neto nalik na ovo : ----------------------- mov ax,50 ; u AX upisujemo broj 50 mov cx,80 ; u CX upisujemo broj 80 push ax cx ; ostavljamo registre AX i CX na steku mov cx,2 ; menjamo vrednost registra CX pop ax cx ; vraamo vrednosti sa steka u registre AX i CX --------------------- dogodie se sledea stvar : - U AX registar smo stavili broj 50 a u CX registar broj 80. - PUSH-nuli smo na stek prvo registar AX pa onda registar CX Situacija na steku izgleda ovako : Ĵ 80 ( sadraj registra CX ) Ĵ 50 ( sadraj registra AX ) Ĵ ---------------------------- Pare steka AX = 50 CX = 80 - Vrednost u registru CX smo promenuli u broj 2 - POP-nuli smo sa steka prvo registar AX pa onda registar CX E, u tom grmu lei zec. Kada ti napie POP DX to ne znai "Pronai kada sam PUSH-nuo na stek DX pa mi ga vrati", nego znai "Uzmi WORD vrednost sa vrha steka i stavi je u DX". Znai, kada smo mi uradili POP ax cx dogodilo se sledee : Polazna situacija : U CX imamo 2 U AX imamo 50 Stek : Ĵ 80 Ĵ 50 Ĵ ---------------------------- Pare steka Sa vrha steka smo uzeli vrednost i stavili je u registar AX. Novonastala situacija : U CX imamo 2 U AX imamo 80 Stek : Ĵ 50 Ĵ ---------------------------- Pare steka I konano smo sa vrha steka pokupili vrednost i stavili je u CX Novonastala situacija : U CX imamo 50 U AX imamo 80 Stek : Ĵ ---------------------------- Pare steka I to je to. Umesto da sauvamo vrednosti registara AX i CX mi smo ih - ZAMENILI !!! Iz tog razloga pazi ta se u kojem trenutku nalazi na steku. Ako ne pazi, program ima da najverovatnije zaglavi sistem. Kada ti bude zatrebalo da sauva sve registre na stek korisne su sledee naredbe : PUSHA (ne postoji u 8086 instrukcionom setu) PUSHAD (386+) PUSHA naredba nee PUSH-nuti na stek registre kojima raspolau noviji procesori (EAX, EBX, ESP ...) dok naredba PUSHAD hoe. Jednostavno, naredbnom PUSHAD na stek se gurnu sledei registri : (E)AX, (E)CX, (E)DX, (E)BX, (E)SP, (E)BP, (E)SI, (E)DI tim redom. Vrednost sauvanog SP registra(Stack Pointer - pokaziva na poslednji element na steku) je vrednost pre naredbe PUSHAD (ili PUSHA). POP-ovanje registara se vri naredbama POPA (ne postoji u 8086 instrukcionom setu) POPAD (386+) Ponovo NAGLAAVAM !!! Mora paziti ta sve stavlja na stek da registri ne bi primili pogrene vrednosti. Da bi koristio naredbe PUSHAD(PUSHA) i POPAD(POPA) i uopte bilo koju naredbu koja postoji tek od odreene generacije procesora, pre navoenja te naredbe mora odabrati tip procesora sa kojim radi na sledei nain : .MODEL tiny .486 ; tip procesora .code org 100h ---------- Ako to ne uradi kompajler e prijaviti greku : "Illegal instruction for currently selected processor(s)". to se ostalih tipova procesora tie, evo i liste : .8086, .8087, .186, .286, .287, .286P, .386, .387, .386P, .486, .486P .586, .586P. Slovo "P" nakon tipa procesora znai da su dozvoljene "privilegovane" instrukcije, koje su bitne onima koji rade neki operativni sistem, drajver ili neto slino. Direktivu ".broj" gde broj predstavlja generaciju procesora moe navesti bilo gde u okviru programa jer je to samo instrukcija kompajleru da moe da koristi i set instrukcija koje odgovaraju specificiranoj generaciji procesora. Znai moe da uradi ak i neto ovakvo : mov ax,0 PUSH ax .486 PUSHAD .8086 mov dx,1 .486 POPAD .8086 POP ax Ludo, zar ne ? Toliko u ovom poglavlju. 5 Grafika, Interrapt 10h, EQU, Stack Registri, Video memorija Ajde da ubacimo malo grafike u sve ovo. Zadatak 1: Itampati na poziciji (10,10) na ekranu string 'Evo me mama!'. String treba da je ute boje i ispisan u grafikom okruenju 320 X 200 sa 256 boja. Na pritisak tastera, ekran se vraa u normalan reim i vraa se kontrola DOS-u. AAAARGH!!! Da pojasnimo. Potrebni koraci za izvrenje ovog zadatka su sledei: 1.Odabrati pravilan grafiki mod(320 X 200 sa 256 boja u ovom sluaju) 2.Itampati string 3.Saekati pritisak bilo kog tastera 4.Vratiti ekran u normalan reim i vratiti kontrolu DOS-u UH. Ajmo korak po korak. 1.korak: Odabrati pravilan grafiki mod(320 X 200 sa 256 boja u ovom sluaju) Spisak grafikih modova dat je u sledeoj tabeli : Ŀ Mod Rezolucija Opis Ĵ 00 40x25 Tekst Crno/Belo (CGA,EGA,MCGA,VGA) 01 40x25 Tekst 16 boja (CGA,EGA,MCGA,VGA) 02 80x25 16 shades of gray text (CGA,EGA,MCGA,VGA) 03 80x25 Tekst 16 boja (CGA,EGA,MCGA,VGA) 04 320x200 Grafika 4 boje (CGA,EGA,MCGA,VGA) 05 320x200 Grafika 4 boje (CGA,EGA,MCGA,VGA) 06 640x200 Grafika Crno/Belo (CGA,EGA,MCGA,VGA) 07 80x25 Tekst monohromatsko (MDA,HERC,EGA,VGA) 08 160x200 Grafika 16 boja (PCjr) 09 320x200 Grafika 16 boja (PCjr) 0A 640x200 Grafika 4 boje (PCjr) 0B Rezervisano (EGA BIOS funkcija 11) 0C Rezervisano (EGA BIOS funkcija 11) 0D 320x200 Grafika 16 boja (EGA,VGA) 0E 640x200 Grafika 16 boja (EGA,VGA) 0F 640x350 Grafika monohromatsko (EGA,VGA) 10 640x350 Grafika 16 boja (EGA or VGA with 128K) 640x350 Grafika 4 boje (64K EGA) 11 640x480 Grafika Crno/Belo (MCGA,VGA) 12 640x480 Grafika 16 boja (VGA) 13 320x200 Grafika 256 boja (MCGA,VGA) - funkcija 0h interapta 10h prepravlja bajt na lokaciji 40h:49h i bit 7 bajta na lokaciji 40h:87h. - ukoliko je najvii bit u AL jedinica(pri prelasku iz jednog u drugi video mod), ekran se nee iistiti. Na primer, ako elimo da preemo u video mod 3h bez ienja ekrana uradiemo neto nalik na ovo : mov ah,0 mov al,10000000b+3h int 10h *to se ostalih video modova tie, oni variraju od jedne do druge grafike kartice te ih neemo ni navoditi. Navedeni video modovi su standardni, i svaka grafika kartica bi trebalo da ih podrava. *320 X 200 je broj taaka na ekranu i naziva se REZOLUCIJA. Svaka taka naziva se PIKSEL (PIXEL). To znai da imamo 200 vodoravnih redova sa po 320 taaka na ekranu. Ukupan broj taaka tj. piksela : 320*200=64000. Elem. Interapt 10h je BIOS-ov grafiki interapt i njega emo koristiti. Funkcija 0h ovog interapt-a aktivira odreeni video mod. Naime u AL treba upisati grafiki mod koji nam je potreban. Kako nama treba 320*200 sa 256 boja vidimo da u AL treba staviti broj 13h. Znai to bi izgledalo ovako: mov ah,0h ; Ovo moe i krae: mov al,13h ; mov ax,13h int 10h Dakle, odabrali smo grafiki mod. 2.korak: Itampati string. Za ovu rabotu nam je potrebna funkcija 13h interapta 10h. Ova funkcija tampa string na ekran, ali je sam proces neto komplikovaniji od funkcije 9h interapta 21h. Elem, potrebno je: - u AH upisati broj funkcije tj. 13h - u AL nain pisanja: -0 : string je sastavljen od karaktera, atribut(itaj:boja) je u BL, kursor se ne pomera -1 : string je sastavljen od karaktera, atribut je u BL, kursor se pomera -3 : string je sastavljen od karaktera i atributa, kursor se ne pomera -4 : string je sastavljen od karaktera i atributa, kursor se pomera Nama je ovde potreban nain 0 ili 1. - u BH broj video strane Elem, za broj video strane obino se koristi 0 ali da se ipak osvrnemo na ostale video strane. Ŀ Mod Strane Adapteri Ĵ 00 0-7 CGA,EGA,MCGA,VGA 01 0-7 CGA,EGA,MCGA,VGA 02 0-3 CGA 0-7 EGA,MCGA,VGA 03 0-3 CGA 0-7 EGA,MCGA,VGA 07 0-7 EGA,VGA - MDA 0D 0-7 EGA,VGA 0E 0-4 EGA,VGA 0F 0-1 EGA,VGA 10 0-1 EGA,VGA Pojasniu ti sada ovu priu o video stranama. Elem, jedna video strana obuhvata 64K video memorije. Kako ti je verovatno poznato, dananje standardne grafike kartice imaju najmanje 1MB memorije. I ta sada ? E, tu dolazi pojam video strane. Kao to sam ve rekao, jedna video strana obuhvata 64K video memorije. Video strana 0 obuhvata prvih 64K video memorije, video strana 1 obuhvata sledeih 64K video memorije, i tako sve dok ima video memorije. Nama je to apsolutno nepotrebno kada radimo sa npr. koordinatama taaka na ekranu i slino. Zbog toga se (skoro) uvek uzima video strana 0. No pojam je potrebno poznavati, jer nikad se ne zna kada nam moe ustrebati. Idemo dalje. - u BL atribut(boja) Za na sluaj uzeemo boju 14(uta). - u CX duinu stringa U ovom sluaju to je 12. - u DH koordinatu reda(Y koordinata) U ovom sluaju 10. - u DL koordinatu kolone(X koordinata) U ovom sluaju 10. - ES:BP postaviti tako da pokazuju na string koji treba odtampati Ovo se realizuje na isti nain kao i DS:DX iz predhodnog poglavlja. Da ne bude nikakve zabune jos u malo objasniti. U AL treba staviti nain pisanja. Prema ve opisanom, ako u AL upiemo 0, string e se itampati ali se kursor nee pomeriti(u ovom sluaju to je pozicija(10,10)), za razliku od sluaja kad bi u AL upisali 1. Kako nije ni bitno da li e se kursor pomeriti ili nee to ti stavi to god hoe. Znai, neemo pomerati kursor, string se sastoji samo od karaktera a atribut tj. boja slova e se nalaziti u BL. -Broj video strane u BH Kao to sam ve rekao, samo stavi u BH nulu i miran si. - U BL atribut tj. boja karaktera Znai u BL stavi broj koji predstavlja boju karaktera. Naa (uta) boja je broj 14. *Opisi boja su neto kasnije, u delu "Video Memorija". - U CX duinu stringa Jednostavno. U CX stavi duinu stringa i to je to. Ovde moemo napraviti i jednu olakicu - da ne bismo svaki put brojali koliko karaktera ima neki string. Prvo da objasnim jo jednu naredbu - EQU. EQU je naredba makro assembler-a koja jednostavno dodeljuje vrednost nekoj konstanti. Na primer : duzina_stringa EQU 12 Ovim se nita ne postie, tj. ovako definisan indikator je samo konstanta koja uopte ne zauzima memoriju. Korist je (nadam se) oigledna. Ako na vie mesta u programu imamo naredbu tipa mov cx,12 gde ovo 12 predstavlja duinu stringa, nije loe definisati konstantu duzina_stringa EQU 12 a u okviru programa koristiti mov cx,duzina_stringa Pri kompajliranju, umesto "duzina_stringa" nai e se broj 12. Tako, ako promenimo duinu stringa, ne moramo traiti svuda po programu gde se koristi ta duina, ve emo samo modifikovati konstantu. Praktino. Ime konstante, naravno, ne sme biti ve definisano bilo kao druga konstanta, bilo kao neto drugo. Postoji jo jedna, vrlo zanimljiva instrukcija makro assembler-a - '$'. ta ona predstavlja? Karakter '$' ili dolar :), predstavlja broja trenutne lokacije u fajlu. *Broja lokacije je obraen u delu "Segmentacija" u 4-om poglavlju To znai, ako bismo napisali neto nalik na string DB "Evo me, mama !" duzina_stringa EQU $-string konstanta "duzina_stringa" e sadrati duinu naeg stringa. Karakter '$' predstavlja OFFSET lokacije nakon stringa, a "string" predstavlja OFFSET poetka stringa. Razlika ta dva je, gle uda, duina stringa. iribu, iriba :). I eto, konstanta "duzina_stringa" zaista i predstavlja ono to joj samo ime kae - duinu stringa. I sada, kad navedemo mov cx,duzina_stringa uradiemo upravo ono to je i trebalo - u registar CX emo upisati duinu stringa. - U DH i DL koordinate Prosto. Kao obian dvodimenzionalni koordinatni sistem (etvrti kvadrant, samo Y vrednosti nisu negativne (stara dobra matematika ;)). Znai vodoravno su koordinate po X(DL) a uspravno po Y(DH). *Gornji levi ugao ekrana ima koordinate 0,0 0,0 X osa ekrana recimo 50 Y o s a e k r a n a npr.24 (50,24) - ES:BP da pokazuju na string Pa kao i u predhodnom poglavlju, samo je tamo bilo DS:DX a ovde ES:BP Kada smo ve pomenuli BP da opiemo i Stek Registre. STEK REGISTRI Ŀ Stek registar Opis Ĵ SP Stek pointer Ĵ BP Base pointer Noviji raunari raspolau i sledeim stek registrima : Ŀ Stek registar Opis Ĵ ESP Stek pointer Ĵ EBP Base pointer Registar SP, tj. STEK POINTER pokazuje tj. sadri OFFSET poslednjeg elementa na steku. Ovaj registar nije ba zgodno menjati jer se time moe izgubiti pozicija na steku. *U okviru predhodnog poglavlja je potanko objanjen kako stek tako i ta se tano dogaa sa SP registrom pri operacijama sa stekom. Ponoviu da je ESP Extended registar registra SP, to znai da donjih 16 bita registra ESP predstavlja nita drugo do registar SP. Ako se BP registar koristi za indeksiranje memorije, tj. ako se uradi neto nalik na mov [bp],4 na offset-u iju vrednost sadri registar BP u STEK Segmentu e se upisati broj 4. Ako bi koristio registre SI,DI ili BX za indeksiranje memorije, podrazumevae se da dotini registri sadre OFFSET u DATA segmentu. 3.korak: Saekati pritisak bilo kog tastera. Dovoljno je samo pozvati funkciju 0h interapta 16h i problem je reen. 4.korak: Vratiti ekran u normalan (textualan) mod i vratiti kontrolu DOS-u. ta je uopte normalan (textualan) mod. E, pa to je mod 80*25 sa 16 boja. Naravno, ovo je na jednom standardnom PC-ju. No dovoljno je pogledati u opisu funkcije 0h interapta 10h i tamo nai odgovarajui mod. Znai, na ve opisani nain odabrati video mod 3. Vraanje kontrole DOS-u: ret. Na osnovu svih objanjenja pokuaj sam da napie ovaj program. Ponavljam da, ako samo bude gledao reenja nikada se nee osamostaliti i poeti da pie sopstvene programe. Ako ba ne bude ilo, a ti malo pogledaj u reenje pa pokuaj ponovo. Nije nimalo teko!!! SRENO!!! REENjE: Program No. 5.1 ;PROGRAM tampanje_grafikog_stringa (STRINGGR.ASM) ; Copyright(C) 1998. by Shang Tsung .model tiny .code org 100h main: jmp pocetak poruka db "Evo me mama!" ; Na ve objanjen nain, naredna instrukcija se takoe ; moe ukljuiti u program ;duzina_poruke EQU $-poruka pocetak: mov ax,13h ; Grafiki mod int 10h ; 320*200 256color mov ah,13h ; funkcija 13h mov al,0 ; nain pisanja mov bh,0 ; video strana mov bl,14 ; atribut tj. boja slova mov cx,12 ; duina stringa ili ; "mov cx,duzina_poruke" mov dh,10 ; koordinata reda mov dl,10 ; koordinata kolone mov bp,offset poruka ; ES:BP pokazuju na string poruka int 10h ; tampamo string mov ah,0 ; Pauza dok korisnik int 16h ; ne pritisne neki taster mov ax,3 ; Normalan video int 10h ; mod ret ; Povratak u DOS end main Kompajliraj i izvri program stringGR.asm. Pokuaj sa EQU naredbom i bez nje i videe da se rezultujui izvrni fajlovi nee uopte razlikovati. Ponoviu jo jednom: Segmentni registri : ES,SS,DS,CS u tiny modu u COM fajlu inicijalno pokazuju na CODE segment. Ponekad nam je potrebno da kursor pomerimo na neku poziciju na ekranu, da bismo na toj poziciji neto uradili. F Za tako neto, koristi se funkcija 2h interrapt-a 10h. Funkcija zahteva sledee ulazne podatke : -U AH registar broj funkcije tj. 2h -U BH registar broj video strane -U DH registar broj reda tj. Y koordinatu nove pozicije kursora -U DL registar broj kolone tj. X koordinata nove pozicije kursora EF Primer 1: Napisati program koji e na ekran, u normalnom (textualnom) video modu, na poziciji XY(60,4) itampati string "Evo me mama !". Reenje: ; PROGRAM Primer No.5.1 ; Copyright(C) 1998. by Shang Tsung .MODEL tiny .CODE ORG 100h main: jmp poc string db "Evo me mama !$" poc: mov ah,2 ; broj funkcije mov bh,0 ; broj video strane mov dh,4 ; Y koordinata mov dl,60 ; X koordinata int 10h mov ah,9h ; mov dx,offset string ; tampamo string int 21h ; ret ; povratak u DOS end main ; Kraj programa Ukoliko nam je potrebno da iitamo trenutnu poziciju kursora, koristiemo funkciju 3h interrapt-a 10h. F Funkcija zahteva sledee ulazne podatke : - U AH registru broj funkcije tj. 3h - U BH registru broj video strane Posle poziva interrapt-u dobijamo sledee rezultate : - U CH registru poetni SCAN line kursora - U CL registru krajnji SCAN line kursora - U DH red tj. Y koordinatu pozicije kursora - U DL kolonu tj. X koordinatu pozicije kursora EF Da odmah neto objasnim. Poetni i krajnji SCAN line kursora odreuju oblik samog kusrora. Poetni SCAN line predstavlja vrh kursora, a krajnji SCAN line predstavlja dno kursora. SCAN line je "Zero based" tj. poetna vrednost mu kree od 0, a ne od 1. Znai, korienjem ove funkcije saznajemo sve to se moe saznati o kursoru. Ako bi eleli da promenimo oblik kursora, u pomo nam pritie funkcija 1h interrapt-a 10h. F Funkcija zahteva sledee ulazne podatke : - U AH registar broj funkcije tj. 1h - U CH registar poetni SCAN line kursora - U CL registar krajnji SCAN line kursora *Ako eli da onemogui (tj. sakrije) kursor u CX upii 2000h. EF Primer 2: Napiimo program koji e menjati oblik kursora od najveeg ( CH=0, CL=16) do najmanjeg (CH=15, CL=16), svaki put ekajui na pritisak tastera. Reenje : ; PROGRAM Primer No. 5.2 ; Copyright(C) 1998. by Shang Tsung .MODEL tiny .CODE ORG 100h main: mov ch,0 ; poetna vrednost vrha kursora opet: mov ah,0 ; ekamo na int 16h ; pritisak tastera mov cl,16 ; SCAN line dna kursora mov ah,1 ; broj funkcije inc ch ; poveavamo SCAN line vrha kursora int 10h ; Pretvaramo kursor cmp ch,16 ; proveravamo da li smo jne opet ; sve obrnuli mov ah,0 ; ekamo na pritisak int 16h ; nekog tastera ret ; povratak u DOS end main ; Kraj programa Jednostavno. P.S. Ovo dodue zna udno da se ponaa. A sada, da upoznamo jo jednu funkciju interrapt-a 10h - 6h. Funkcija 6h interrapt-a 10h ima za zadatak da skroluje prozor na ekranu na gore. F Funkcija zahteva sledee ulazne elemante : - U AH registar broj funkcije tj. 6h - U AL registar broj - za koliko linija treba skrolovati Ako se u AL nee 0 ili broj vei od broja linija na ekranu, ekran se prazni - U BH registar atribut koji treba upotrebiti na praznoj liniji (boja-grafiki mod). Za obian mod to je broj 7. -U CH registar red tj. Y koordinatu gornjeg levog ugla prozora koji se skroluje -U CL registar kolonu tj. X koordinatu gornjeg levog ugla prozora koji se skroluje -U DH registar red tj. Y koordinatu donjeg desnog ugla prozora koji se skroluje -U DL registar kolonu tj. X koordinatu donjeg desnog ugla prozora koji se skroluje EF Znai, ako imamo na primer sledeu situaciju na ekranu : Ŀ Sve ono sto smo uradili ne valja. Ono sto smo uradili nam ide od ruke. Ostatak ekrana i onda uradimo neto nalik na ovo : mov ah,6h mov al,1 mov bh,0 mov ch,0 mov cl,4 mov dh,1 mov dl,22 int 10h dogodie se sledee : Ŀ Sve sto smo uradili nam ne valja. Ono ide od ruke. Ostatak ekrana Kako postoji skrolovanje na gore, tako postoji i skrolovanje na dole. Za takvu rabotu nam je potrebna funkcija 7h interrapt-a 10h. F Funkcija zahteva sledee ulazne elemante : - U AH registar broj funkcije tj. 7h - U AL registar broj - za koliko linija treba skrolovati Ako se u AL nee 0 ili broj vei od broja linija na ekranu, ekran se prazni - U BH registar atribut koji treba upotrebiti na praznoj liniji (boja-grafiki mod). Za obian mod to je broj 7. -U CH registar red tj. Y koordinatu gornjeg levog ugla prozora koji se skroluje -U CL registar kolonu tj. X koordinatu gornjeg levog ugla prozora koji se skroluje -U DH registar red tj. Y koordinatu donjeg desnog ugla prozora koji se skroluje -U DL registar kolonu tj. X koordinatu donjeg desnog ugla prozora koji se skroluje EF Zadatak 2: Napisati program koji isti ekran, puni ga slovima na sledei nain : -u prvom redu sva slova a -u drugom redu sva blova b - - - - - - - - - - - - - - -u 24 redu sva slova x -u 25-om redu e biti kursor i potom isti prozor sa koordinatama (10,8,30,20). Po izlasku iz programa na poziciji (30,10) ispisati string "Gotovo !", oistiti ekran, i kursor, u zavisnosti od trenutnog stanja poveati za 1 SCAN line. Sreno ! Program No. 5.2 ; PROGRAM demo_funkcije_interrapt-a_10h (DEMO_I10.ASM) ; Copyright(C) 1998. by Shang Tsung .MODEL tiny .CODE org 100h main: call ClrScr ; Brisemo ekran mov dl,'a'-1 ; Poetni karakter -1 mov ah,2 ; Funkcija 2h interrapt-a 21h sledeci_karakter: inc dl ; poveavamo ASCII vrednost karaktera mov cl,0 ; broja karaktera ponovo: int 21h ; tampamo karakter inc cl ; uveavamo broja cmp cl,80 ; U modu 3h imamo 80 kolona na ekranu jne ponovo cmp dl,'x' ; proveravamo da li smo itampali sva slova jne sledeci_karakter call Taster mov ax,600h ; U AH 6, u AL 0 mov bh,0 ; Video strana 0 mov ch,8 ; Gornja ivica mov cl,10 ; Leva ivica mov dh,20 ; Donja ivica mov dl,30 ; Desna ivica int 10h ; istimo prozor sa koordinatama ; (10,8,30,20) call Taster ; ekamo na pritisak tastera mov ah,13h ; Funkcija 13h interrapt-a 10h mov al,1 ; Kursor e se pomerati mov cx,duzina_poruke ; U CX upisujemo duinu poruke mov bp,offset poruka ; BP e sadrati offset poruke mov dh,10 ; Y koordinata poetka poruke mov dl,30 ; X koordinata poetka poruke mov bl,7 ; Atribut (boja) mov bh,0 ; Video strana int 10h ; tampamo string na poziciji (30,10) call Taster ; ekamo na pritisak tastera call ClrScr ; istimo ekran mov ah,3h ; broj funkcije mov bh,0 ; broj video strane int 10h ; dobijamo podatke o kursoru dec ch ; poveavamo oblik kursora za 1 SCAN LINE mov ah,1h ; Menjamo oblik int 10h ; kursoru call taster ; ekamo na pritisak tastera ret ; povratak u DOS ClrScr PROC NEAR mov ax,3 ; Jednostavan (i prljav :) nain da se int 10h ; iisti ekram - prelazak u textualni ret ; video mod 3h. Ovim se obezbeujemo u ; sluaju da je program startovan iz ; nekog drugog video moda. ENDP Taster PROC mov ah,0 ; Funkcija 0 interrapt-a 16h int 16h ret ENDP poruka db "Gotovo !" ; Telo poruke duzina_poruke EQU $-poruka end main Mislim da su objanjenja suvina. *Za tvoju informaciju, naredba inc al tj. inc (8-o bitni ili 32-o bitni registar) e zauzeti 2 bajta, dok e naredba inc ax tj. inc (16-o bitni registar) zauzeti samo 1 bajt. Ako nam bude zatrebalo da iitamo karakter i atribut tog karaktera koji se nalazi na trenutnoj poziciji kursora, koristiemo funkciju 8h interrapt-a 10h. F Funkcija zahteva sledee ulazne podatke : - U AH registru broj funkcije tj. 8h - U BH registru broj video strane Posle poziva interrapt-u dobijamo sledee : - U AH registru atribut proitanog karaktera - U AL registru ASCII kod proitanog karaktera EF Ako nam bude zatrebalo da ispiemo karakter sa atributom na trenutnoj poziciji kursora, koristiemo funkciju 9h interrapt-a 10h. F Funkcija zahteva sedee ulazne podatke : - U AH registar broj funkcije tj. 9h - U AL registar ASCII kod karaktera - U BH registar video strana - U BL registar atribut (jedna od 16 boja) karaktera (text), tj. boja (grafika) - U CX registar broj karaktera koliko treba ispisati *NAPOMENA : Kursor se pri tampanju karaktera na ovaj nain NE POMERA ! EF Zadatak 3 : Napisati progam CIPIRIPI.ASM. Program radi sledee : Kree od gornjeg levog ugla ekrana i redom ita karaktere. Ako naleti na karaktere : a,e,o,u,y ili A,E,O,U,Y menja ih u karakter i tj. I. Atributi pozicije treba da ostanu isti. Ekran proveriti do pozicije na kojoj se kursor nalazi po startovanju programa. Po zavretku programa odtampati string "Gotovo !" i vratiti kontrolu DOS-u. Sreno ! REENjE : Program No. 5.3 ; PROGRAM Cipiripi (CIPIRIPI.ASM) ; Copyright(C) 1998. by Shang Tsung .MODEL tiny .CODE ORG 100h main: jmp pocetak ; Obilazimo poruku i procedure poruka db "Gotovo !$" ; Telo poruke citaj_poziciju PROC mov ah,3 ; itamo trenutnu mov bh,0 ; poziciju kursora na ekranu int 10h ; (rezultat se smeta u DX) ret citaj_poziciju ENDP pocetak: call citaj_poziciju ; itamo trenutnu poziciju kursora push dx ; uvamo trenutnu poziciju kursora na steku mov ah,2 ; postavljamo kursor xor dx,dx ; u gornji levi int 10h ; ugao ekrana ( u BH se ve nalazi 0 ) sledeca_pozicija: mov ah,8 ; broj funkcije mov bh,0 ; video strana int 10h ; itamo karakter sa trenutne pozicije kursora ; U AH dobijamo atribut a u AL ASCII kod ; karaktera cmp al,'a' ; je malo_slovo ; cmp al,'e' ; je malo_slovo ; P cmp al,'o' ; R je malo_slovo ; O cmp al,'u' ; V je malo_slovo ; E cmp al,'y' ; R je malo_slovo ; A cmp al,'A' ; je veliko_slovo ; U cmp al,'E' ; S je veliko_slovo ; L cmp al,'O' ; O je veliko_slovo ; V cmp al,'U' ; A je veliko_slovo ; cmp al,'Y' ; je veliko_slovo ; jmp neko_je_drugo_slovo ; ako nije ni jedno od tih slova u pitanju je ; neki drugi karakter malo_slovo: mov al,'i' ; u AL slovo jmp zameni_slovo ; nije u pitanju neko drugo slovo, ovime ; samo obilazimo sledei blok naredbi veliko_slovo: mov al,'I' ; u AL slovo zameni_slovo: mov bl,ah ; u BL upisujemo atribut mov ah,9 ; u AH broj funkcije mov bh,0 ; video strana mov cx,1 ; koliko da se ispie karaktera int 10h ; ispisujemo karakter neko_je_drugo_slovo: call citaj_poziciju ; itamo trenutnu poziciju kursora cmp dl,79 ; proveravamo da li smo stigli do kraja reda jne nije_novi_red ; ako nismo idemo na labelu "nije_novi_red" mov dl,-1 ; kolona (ovo e se poveati za jedan) inc dh ; sledea vrsta nije_novi_red: inc dl ; poveavamo broj kolone mov ah,2 ; pomeramo kursor int 10h ; na novu poziciju mov bx,dx ; u BX upisujemo trenutnu poziciju kursora pop dx ; vraamo poetnu poziciju sa steka u DX push dx ; vraamo poetnu poziciju na stek cmp dx,bx ; proveravamo da li smo stigli do poetne ; pozicije kursora jne sledeca_pozicija ; ako nismo idemo dalje pop dx ; istimo stek mov ah,9h ; tampamo mov dx,offset poruka ; poruku int 21h ; ret ; povratak u DOS end main Nadam se da nije bilo teko. Poto smo malo bolje upoznali interrapt 10h da se vratimo na mod 13h. Mod 13h je, kao to smo na poetku poglavlja videli, grafiki mod 320 X 200 sa 256 boja. Znai imamo 64000 piksela na celom ekranu. Ajde onda da pokuamo da, bar za poetak, obojimo ekran u neku boju radei to piksel po piksel. Da bismo tako neto uradili, trebalo bi da se upoznamo sa jo nekoliko funkcija interrapt-a 10h. F Ako elimo da ispiemo neki piksel na ekranu, koristimo funkciju 0ch interrapt-a 10h. Funkcija zahteva sledee ulazne podatke : - U AH registru broj funkcije tj. 0ch - U AL registru boju piksela - U BH registru broj video strane - U CX registru koordinatu kolone ( X koordinata ) - U DX registru koordinato reda ( Y koordinata ) *Koordinate su "Zero based" tj. gornji levi ugao ima koordinate (0,0). *Poznato je da ova funkcija zna da uniti sadraj registra AX. EF Znajui ovo, napiimo jedan programi : Zadatak 4: Program e ekati na pritisak tastera, i poto taster bude pritisnut bojiti ekran u jednu od boja piksel po piksel. Boje kreu od 0 i zavravaju se brojem 255. Ako je pritisnut taster ESC ekran se vraa u normalan reim i vraa se kontrola DOS-u. Sreno ! Program No. 5.4 ; PROGRAM bojenje_ekrana (BE13H.ASM) ; Copyright(C) 1998. by Shang Tsung .MODEL tiny .CODE ORG 100h main: jmp pocetak boja db -1 ; Promenljiva koja e sadrati ; trenutnu boju (ovo e se poveati za 1) pocetak: mov ax,13h ; Aktiviramo grafiki int 10h ; mod 13h sledeca_boja: inc [boja] ; boja ekrana mov dx,-1 ; koordinata reda ( Y koordinata ) sledeci_red: inc dx ; poveavamo broj koordinate reda mov cx,0 ; postavljamo koordinatu kolone na 0 cmp dx,200 ; proveravamo da li smo obojili ceo ekran je gotovo ; ako jesmo prelazimo na sledeu boju sledeca_kolona: mov ah,0ch ; Broj funkcije interrapt-a 10h mov al,[boja] ; Boja piksela ; Ovaj red moe takoe ukljuiti u program. ; Time e dobiti jedan zanimljiv efekat. ; inc [boja] mov bh,0 ; Video strana int 10h ; Oboji taj piksel inc cx ; Prelazimo na sledeu kolonu cmp cx,320 ; Ako smo stigli do poslednje kolone je sledeci_red ; prei u sledei red jmp sledeca_kolona ; u suprotnom oboji sledeu kolonu ; tj. piksel gotovo: mov ah,0 ; ekamo na pritisak int 16h ; nekog tastera cmp al,27 ; ako pritisnuti taster nije bio ESC jne sledeca_boja ; prelazimo na sledeu boju mov ax,3 ; Vraamo ekran u int 10h ; normalan mod mov ax,4c00h ; povratak u DOS int 21h end main ; Kraj programa Mislim da je suvino objanjavati. Sada bih hteo da se osvrnem na pojam video memorije. Kao to smo rekli, u grafikom modu 13h to je 320 X 200 sa 256 boja, na ekranu imamo 64000 taaka. Malopre, smo korienjem interrapt-a bojili te take tj. piksele jednu po jednu. Ali, zato komplikovano kada moe jednostavnije (i bre :) Elem. U memoriji se od SEGMENT-a 0A000h i OFFSET-a 0, nalazi VIDEO MEMORIJA u kojoj su u stvari upisani atributi (itaj : boje) svih piksela na ekranu. Svaki od tih atributa u memoriji zauzima po jedan bajt. Znai, od lokacije 0a000h:0 pa sledeih 64000 bajta su smeteni atributi svih piksela na ekranu i to sledeim redom : Od SEGMENT-a 0a000h i OFFSET-a 0 pa sledeih 320 bajta je prvi red piksela. Na to se nadovezuje drugi red i tako sve do poslednjeg reda i do poslednjeg piksela. *NAPOMENA : Ovo vai za mod 13h. I sada, ako bi eleli da obojimo ekran u neku boju, to moemo uraditi i tako to emo direktno u memoriju upisati vrednosti atributa piksela. Direktno u memoriju pristupaemo na sledei nain (vie rei o tome ima u 6-om poglavlju :) : mov DS:[BX],al mada ja preferiram korienje indeksnih registara u te svrhe mov DS:[DI],byte ptr al ; "byte ptr" ovde nije potrebno mada moe i drugaije. Samo da neto ne zaboravim. Kada napiemo "byte PTR" misli se sledee : Ako imamo na primer mov byte ptr DS:[DI], 'a' u prevodu to znai - Na lokaciju DS:[DI] duine jedan bajt upii ASCII vrednost karaktera 'a'. Ako bi se na primer desila sledea situacija : mov DS:[DI],9 kompajler ne bi znao da li da tretira tu memorijsku lokaciju kao bajt ili word ili neto tree. Ako je bude tretirao kao word, na lokaciju DS:[DI] e se upisati vrednost 9, a na sledeu lokaciju 0. (Pri upisivanju podataka u memoriju, prvo se upisuje najnii bajt, pa sledei itd.) A ta ako mi nismo hteli da predhodna naredba deluje na sledeu lokaciju ? E, doi e do kurlusa. Zbog toga, ako elimo da predhodna naredba ima efekat samo na bajt na lokaciji DS:[DI] napisaemo mov byte ptr DS:[DI], 9 Ovaj tzv. "override" nema svrhe koristiti kada je jedan od parametara registar, jer registri imaju striktnu duinu (dakle 1 bajt, ili 2 bajta, ili 4 bajta). To je cela mudrost. Samo da ne bude zabune. Ja ovde, pri upisivanju u memoriju koristim mov byte ptr DS:[DI], 9 mada to se u sutini ovako zapisuje : mov byte ptr [DI], 9. Zato? Elem, kada napiemo mov byte ptr [DI], 9 podrazumeva se da se ta memorijska lokacija nalazi u DATA SEGMENT-u tj. da se u DS nalazi segmentna adresa te memorijske lokacije. No u sledeim programima ja u ipak koristiti sintaksu mov byte ptr DS:[DI], 9 ne bi li bilo oiglednije ta se u stvari dogaa. No da se vratimo na zadatak. To se moe realizovati sledeim programom. *U ovom programu u umesto DS koristiti ekstra segment - ES, jer u promenljivoj boja pristupati sa mov al,[boja] a ako bi DS pokazivao na video memoriju, sa [boja] bi operisao sa video memorijom tj. iako se u DATA SEGMENT-u definisao prostor za promenljivu boja, podatke o njoj bismo itali iz video memorije a ne DATA SEGMENT-a to je naravno pogreno. To se naravno moe obii sa na primer mov al,cs:[boja] (da te podsetim, svi segmenti registri u COM fajlu inicijalno pokazuju na poetak koda) ali da ne komplikujemo. Program No. 5.5 ; PROGRAM bojenje_ekrana_korienjem_video_memorije (B13H_MEM.ASM) ; Copyright(C) 1998. by Shang Tsung .MODEL tiny .CODE ORG 100h main: jmp pocetak boja db -1 ; Promenljiva koja e sadrati trenutnu ; boju pocetak: mov ax,13h ; Ulazimo u grafiki int 10h ; mod 13h mov dx,0a000h ; SEGMENT video memorije mov es,dx ; upisujemo u registar ES sledeca_boja: inc [boja] ; boja ekrana mov bx,0 ; poetni pixel tj. OFFSET video memorije ponovo: mov al,[boja] ; u AL registar upisujemo boju ; i sledeu naredbu moe uvrstiti u program ; tako e dobiti jedan zanimljiv efekat ; inc [boja] mov es:[bx],al ; na memorijsku lokaciju ES:BX ; postavljamo atribut inc bx ; pomeramo se na sledei pixel cmp bx,64000 ; proveravamo da li smo ispunili ekran jne ponovo ; ako nismo prelazimo na sledei pixel mov ah,0 ; ekamo na pritisak int 16h ; nekog tastera cmp al,27 ; ako nije pritisnut taster ESC jne sledeca_boja ; prelazimo na sledeu boju mov ax,3 ; Vraamo ekran u int 10h ; textualni mod ret ; Vraamo se u DOS end main ; Kraj programa *Pri operacijama sa memorijom na ovaj nain : mov ds:[bx],al od registara opte namene, moe se primeniti samo registar BX. (Vie rei o tome ima u 6-om poglavlju : Indeksni registri) ISPII I KOMPAJLIRAJ PRETHODNI PROGRAM !!! Oigledno je da je bojenje ekrana direktnim upisivanjem u memoriju mnogo bre od korienja interrapt-a (i jednostavnije ;). A sada napiimo program koji e u grafikom modu 13h ekran podeliti na kvadrate tj. napraviti reetku. U sutini, treba crtati vodoravne i uspravne linije na ekranu, s tim to treba da postoji razmak izmeu takvih linija. Uzmimo da je taj razmak 5 piksela. Uzmimo takoe boju 14(uta) za boju reetke. Na osnovu prethodnih programa i instrukcija pokuaj sam da napie ovaj program. Sreno! Program No. 5.6 ; PROGRAM crtanje_reetke_na_ekranu (RESETKA.ASM) ; Copyright(C) 1998. by Shang Tsung .MODEL tiny .CODE ORG 100h main: mov ax,13h ; Ulazimo u grafiki int 10h ; mod 13h ; prvo emo iscrtati vodoravne linije mov dx,0 ; koordinata vrste ; sledeu liniju moe uvrstiti u program ako eli da dobije ; jedan zanimljiv efekat tj. malo areniju reetku ; mov al,0 sledeca_linija: mov cx,0 ; koordinata kolone sledeci_pixel_vrsta: mov ah,0ch ; broj funkcije mov al,14 ; boja pixela ; ako sledeu liniju uvrsti umesto predhodne dobie jedan ; zanimljiv efekat tj. malo areniju reetku ; inc al mov bh,0 ; video strana int 10h ; bojimo pixel inc cx ; pomeramo se za jedno mesto u desno cmp cx,320 ; Proveravamo da li smo doli jb sledeci_pixel_vrsta ; do kraja vrste add dx,5 ; koordinata vrste sledee linije cmp dx,200 ; Proveravamo da li smo doli do jb sledeca_linija ; kraja ekrana ; Sada emo iscrtati uspravne linije mov cx,0 ; koordinata kolone sledeca_kolona: mov dx,0 ; koordinata vrste sledeci_pixel_kolona: mov ah,0ch ; broj funkcije mov al,14 ; boja pixela ; ako sledeu liniju uvrsti umesto predhodne dobie jedan ; zanimljiv efekat tj. malo areniju reetku ; inc al mov bh,0 ; video strana int 10h ; bojimo pixel inc dx ; pomeramo se za jedno mesto na dole cmp dx,200 ; proveravamo da li smo doli jb sledeci_pixel_kolona ; do kraja kolone add cx,5 ; koordinata kolone sledee linije cmp cx,320 ; proveravamo da li smo doli jb sledeca_kolona ; do kraja ekrana mov ah,0 ; ekamo na pritisak int 16h ; nekog tastera mov ax,3h ; Vraamo ekran u int 10h ; normalan reim ret ; povratak u DOS end main ; kraj programa Nadam se da nije bilo teko. Fino bi bilo predhodni program uraditi korienjem video memorije. Pa da i to uradimo... Program No. 5.7 ; PROGRAM crtanje_reetke_na_ekranu_korienjem_video_memorije (RESETO_V.ASM) ; Copyright(C) 1998. by Shang Tsung .MODEL tiny .CODE ORG 100h main: mov ax,13h ; ulazimo u grafiki int 10h ; mod 13h mov bx,0 ; Opti registar koji e nam pomoi da ; se etamo kroz video memoriju mov dx,0 ; broja linija mov ax,0a000h ; U DS registar stavljamo mov ds,ax ; segment video memorije sledeca_vrsta: mov cx,0 ; broja pixela sledeci_pixel_vrste: mov al,14 ; upisujemo atribut tj. boju pixela u mov ds:[bx],al ; video memoriju inc bx ; pomeramo se na sledei pixel inc cx ; uveavamo broja pixela cmp cx,320 ; proveravamo da li smo doli do kraja ekrana jb sledeci_pixel_vrste add bx,1280 ; pomeramo pokaziva na pixel koji je za 5 ; redova nii od prethodnog poetnog pixela add dx,5 ; uveavamo broja linija cmp dx,200 ; proveravamo da li smo doli do kraja ekrana jb sledeca_vrsta mov bx,0 ; vraamo se u gornji levi ugao ekrana mov dx,0 ; broja linija sledeca_kolona: mov cx,0 ; broja pixela sledeci_pixel_kolone: mov al,14 ; upisujemo atribut tj. boju pixela u mov ds:[bx],al ; video memoriju ; ako u predhodne dve linije umesto registra AL stavi 16-o bitan registar ; npr. AX , praktino e dobiti trodimenzionalni model reetke. ; U stvari e se obojiti 2 pixela - prvi stvarnom bojom, a drugi bojom 0 ; (jer podaci o pixelima su duine jednog bajta a mi korienjem ; 16-o bitnog registra prepravljamo podatke za dva pixela ) ; tj. sledei pixel e izgubiti boju to e rezultovati prividnom ; trodimenzionalnom slikom reetke. add bx,320 ; pomeramo pokaziva na sledei pixel u ; uspravnoj liniji inc cx ; uveavamo broja pixela cmp cx,200 ; proveravamo da li smo doli do kraja ekrana jb sledeci_pixel_kolone add dx,5 ; uveavamo broja linija mov bx,0 ; ponovo pokazujemo na gornji levi ugao ekrana add bx,dx ; pomeramo pokaziva na sledeu kolonu cmp dx,320 ; proveravamo da li smo doli do kraja ekrana jb sledeca_kolona mov ah,0 ; ekamo na pritisak int 16h ; nekog tastera mov ax,3h ; Vraamo ekran u int 10h ; normalan reim ret ; povratak u DOS end main ; kraj programa Mislim da su objanjenja suvina. U napomeni pre zadatka u kojem smo bojili ekran korienjem video memorije stajalo je : "Ovo vai za mod 13h.". Znai, ako bismo hteli da uradimo neto slino bojenju ekrana u nekoj drugoj rezoluciji, treba da znamo novi SEGMENT od koga e poinjati video memorija za taj grafiki mod kao i veliinu te memorije. E, pa da za poetak objasnim postupak u normalnom textualnom modu tj.modu 3h. Kao to je ve reeno, mod 3h predstavlja 80 X 25 pozicija na ekranu sa 16 boja. Znai ukupno imamo 80*25 pozicija(da ne kaem taaka) na ekranu, to mu doe 2000 pozicija. Tu je potrebno naglasiti i da je re o TEKSTUALNOM video modu, a ne o grafikom. Organizacija ekrana u ovakvom modu je neto drugaija od moda 13h. Elem, organizacija je ovakva. Od SEGMENT-a 0B800h i OFFSET-a 0 pa sledeih 4000 bajta nalazi se video memorija moda 3h. Dodue, nije sa svim adapterima ovako. Na primer, na monohromatskim adapterima ta adresa je 0B000h. Ako postoji potreba da se utvrdi koji je segment video memorije, u memoriji na adresi 0000h:0449h se nalazi vrednost koja, ako je 7 oznaava da se radi o monohromatsom adapteru, a ako nije onda se skoro sigurno moe zakljuiti da se radi o kolor adapteru te se stoga slobodno moe uzeti, kao adresa video memorije adresa 0B800h:0000h. Inae, na lokaciji 0000h:0449h (ili 0040h:0049h) to predstavlja deo BIOS memorije, nalazi se vrednost tipa BYTE koja predstavlja nita drugo do video mod koji je trenutno aktivan. Ako pogleda opis video moda 7 u tabeli, videe da je u pitanju monohromatski textualni mod, koji je karakteristian za monohromatske adaptere, mada je i na kolor adapteru mogue prei u ovaj video mod. No, u svakom sluaju, adresa video memorije i na kolor i na monohromatskom adapteru u modu 7h poinje od lokacije 0B000h:0000h. No, mi govorimo o organizaciji video memorije u modu 3h. Prvi bajt video memorije predstavlja ASCII kod karaktera koji se nalazi u gornjem levom uglu ekrana, drugi bajt predstavlja atribute te pozicije, trei bajt predstavlja ASCII kod sledeeg karaktera na ekranu, etvrti bajt predstavlja atribute te pozicije itd. To i objanjava injenicu da imamo 2000 pozicija na ekranu, a 4000 bajta video memorije - 2000 bajta za ASCII karaktere i 2000 bajta za atribute. Da samo objasnimo organizaciju bajta koji predstavlja atribute. Ŀ Bit No. 7 6 5 4 3 2 1 0 Plav Ŀ Zelen Atributi Crven ispisa Naglaen Plav Ŀ Zelen Atributi Crven podloge Treptanje Iz ovoga vidimo da je mogue kreirati 16 boja za tekst (2 na 4-ti) i 8 boja za podlogu (jer podloga nema atribut Naglaen). Sedmi bit obezbeuje treptanje teksta. Da bi napravili odreenu boju treba pravilno pomeati osnovne boje. Pravila meanja data su u sledeoj tabeli. *Kad kaem da je atribut 0010 to znai : Bit za Naglaeno - 0, bit za crvenu - 0, bit za zelenu - 1, bit za plavu - 0. Naravno, boje pozadine nee imati atribut naglaeno te najvii bit treba ignorisati. Ŀ Br. BOJA ATRIBUT Ĵ 1. Crna 0000 2. Plava 0001 3. Zelena 0010 4. Cijan 0011 5. Crvena 0100 6. Magenta 0101 7. Smea 0110 8. Svetlo siva 0111 9. Tamno siva 1000 10. Svetlo plava 1001 11. Svetlo zelena 1010 12. Svetlo cijan 1011 13. Svetlo crvena 1100 14. Svetlo magenta 1101 15. uta 1110 16. Bela 1111 *Primeti da je atribut u stvari, kao broj jednak rednom broju boje umanjenom za jedan(binarno). Zato - sam prokljuvi ;). Uradimo sada jedan mali programi. Zadatak 5 : Napisati program koji e u video modu 3h tj. normalnom reimu rada, ekran napuniti slovima 's'. Slova treba da su naglaene ute boje, dok pozadina treba da bude iste plave boje. Sreno ! Program No. 5.8 ; PROGRAM demo_upis_u_vide_memoriju_mod_3h (DUVM3H.ASM) ; Copyright(C) 1998. by Shang Tsung .MODEL tiny .CODE ORG 100h main: mov ax,0b800h ; Adresa video memorije moda 3h mov ds,ax ; U DS upisujemo adresu video memorije mov bx,0 ; OFFSET poetka video memorije mov al,'s' ; Karakter u AL mov ah,00011110b ; Atribut u AH sledeci_karakter: mov ds:[bx],ax ; Upisujemo karakter i njegov atribut ; u video memoriju add bx,2 ; Pomeramo se za 2 mesta cmp bx,4000 ; Proveravamo da li smo doli do kraja video ; memorije tj. ekrana jbe sledeci_karakter ; ako je OFFSET manji ili jednak 4000 ; obraujemo sledeu memorijsku lokaciju mov ah,0 ; ekamo na pritisak int 16h ; nekog tastera ret ; Povratak u DOS end main Nadam se da nije bilo teko. A sada, jo jedan zadatak. Zadatak 6 : Napisati program CIPIRIP2.ASM. Program treba da radi sve to i program CIPIRIPI.ASM, ali ovoga puta korienjem video memorije. Ovoga puta neka nema ogranienja tj. bez obzira na poloaj kursora, neka se ceo ekran proveri za slovima. Kako upisujemo direktno u video memoriju, kursor se nee pomeriti. Pretpostaviti da je aktivan video mod 3h u trenutku startovanja programa. Sreno ! Program No. 5.9 ; PROGRAM CIPIRIPI_video_memorija (CIPIRIP2.ASM) ; Copyright(C) 1998. by Shang Tsung .MODEL tiny .CODE ORG 100h main: jmp pocetak poruka db "Gotovo !$" ; zavrna poruka pocetak: mov ax,0b800h ; U DS registar upisujemo segmentnu mov ds,ax ; adresu video memorije mov bx,0 ; u BX registar upisujemo OFFSET poetka ; video memorije sledeci_karakter: cmp byte ptr [bx], 'a' ; je malo_slovo_i ; cmp byte ptr [bx], 'e' ; je malo_slovo_i ; P cmp byte ptr [bx], 'o' ; R je malo_slovo_i ; O cmp byte ptr [bx], 'u' ; V je malo_slovo_i ; E cmp byte ptr [bx], 'y' ; R je malo_slovo_i ; A cmp byte ptr [bx], 'A' ; je veliko_slovo_I ; U cmp byte ptr [bx], 'E' ; S je veliko_slovo_I ; L cmp byte ptr [bx], 'O' ; O je veliko_slovo_I ; V cmp byte ptr [bx], 'U' ; A je veliko_slovo_I ; cmp byte ptr [bx], 'Y' ; je veliko_slovo_I ; jmp neki_je_drugi_karakter malo_slovo_i: ; Upisujemu direktno u video memoriju mov byte ptr [bx], 'i' ; karakter 'i' umesto jednog od karaktera ; koje treba zameniti jmp neki_je_drugi_karakter ; Nije u pitanju drugi karakter, ve isto ; da iskoristimo ve postojeu labelu za ; obilaenje sledee naredbe veliko_slovo_I: ; Upisujemu direktno u video memoriju mov byte ptr [bx], 'I' ; karakter 'I' umesto jednog od karaktera ; koje treba zameniti neki_je_drugi_karakter: add bx,2 ; pomeramo pokaziva za 2 tj. preskaemo ; atribut i prelazimo na sledei karakter cmp bx,4000 ; veliina video memorije jbe sledeci_karakter ; ako je manje ili jednako idemo dalje mov ax,cs ; U AX upisujemo sadraj CS mov ds,ax ; u DS upisujemo AX ; Ovo radimo stoga to nam je u DS segment ; video memorije a ne naeg programa. Kako ; je u CS segment naeg programa ovime ; vraamo sadraj segmenta u DS. To nam je ; potrebno jer funkcija 9h interrapt-a 21h ; zahteva da DS:DX pokazuje na string koji ; treba itampati. Ovo se moe lepe uraditi ; sa ; PUSH CS ; POP DS mov ah,9h ; mov dx,offset poruka ; tampamo poruku int 21h ; ret ; Povratak u DOS end main Jednostavno, zar ne ? Primeti da sam pri proveri koristio : cmp byte ptr [bx], 'a' Ovde se kriju dve cake. Prva - nisam koristio DS:[BX], i druga - umesto samo [bx] koristio sam byte ptr [bx]. Objanjenja slede. Nisam koristio DS:[BX] jer se to podrazumeva (kao to je ve bilo reeno). Kada kaem cmp byte ptr [bx], 'a' podrazumeva se lokacija ds:[bx] u DATA SEGMENT-U, a na poetku programa mi smo promenuli sadraj tog segmenta iz ega sledi da je tekui DATA segment nita drugo do sama video memorija. No na kraju programa, zarad tampanja poruke, morali smo to da vratimo. Ovo smo naravno mogli i malo drugaije. Naime, kao to je ve bilo objanjeno, nismo uopte morali da menjamo sadraj DS, ve smo sasvim komotno mogli da upotrebimo ES. No ovako sam pokuao da ti objasnim da, ako promeni sadraj DS i onda pokua da iskoristi npr. funkciju 9h interrapt-a 21h koja jednostavno zahteva da se segmentna adresa stringa nalazi u DS, mora da u DS vrati staru vrednost. to se druge cake tie, jednostavno nisam hteo da raunar kojim sluajem tretira lokaciju kao word, to bi znailo da e porediti DVA bajta iz video memorije sa ASCII vrednou slova, to ne najverovatnije niim rezultovalo, jer male su anse da bajt koji predstavlja atribut bude 0 (to bi znailo da su boja karaktera i boja pozadine isti to znai da se taj karakter uopte ne vidi na ekranu), tako da se nikakve promene u video memoriji nee dogoditi. Kada pokua neto kao cmp [bx], 'a' kompajler izdaje sledee upozorenje : "*Warning* cipirip2.ASM(15) Argument needs type override". To je zbog toga to nema "byte ptr" kao to je ve objanjeno. Primetie da je ovaj program mnogo bri i efikasniji od programa CIPIRIPI.ASM, jer je direktan upis u video memoriju uvek bri od koritenja interrapt-a. to se tie ostalih video modova i ostalih pikanterija vezanih za pojam grafike, ostavljam tvojoj radoznalosti i kreativnosti da se doseti jo nekih zanimljivih programa. O nekim ozbiljnijim stvarima koje se tiu ove oblasti govoriemo u nekom drugom izdanju. Toliko o grafici u ovom izdanju UHFA-a. 6 Fajlovi, Rad sa fajlovima, FLAG-ovi, Indeksni registri, XOR, PSP, LOOP Naravno tu su i fajlovi i rad sa njima. OK. Za poetak pokazaemo kako se fajl otvara i zatvara preko HANDLE-ova, to je standardan nain za rad sa fajlovima. Postoji i FCB tehnika, ali ona je zastarela te se vie ne koristi iako je jo uvek podrana od MS-DOS-a. Otvaranje fajla korienjem Handle-a Rutina za otvaranje fajla : Da bi smo otvorili neki fajl koristiemo funkciju 3dh interapta 21h. Funkcija zahteva sledee ulazne elemente : - U AH registar broj funkcije tj. 3Dh - U AL registar Access mode 00 - samo za itanje (read only) 01 - samo za pisanje (write only) 02 - za itanje i pisanje (read/write) - DS:DX treba da pokazuju na ASCIIZ ime fajla Posle poziva funkcije : - U AX registru e se nai Handle fajla ako Carry Flag (CF) nije postavljen - Ako je CF postavljen u AX registru e se nai kod greke Ovde mora da pazi na jednu stvar. Ako treba otvoriti fajl sa atributom Read Only, to e moi da uradi samo ako u AL upie 0, inae e se generisati greka. Da razjasnimo. - U AL se dakle upisuje Access mode. Ako elimo da fajl samo itamo staviemo 00, ako elimo samo da piemo u fajl staviemo 01, a ako elimo i da itamo i da piemo u fajl staviemo 02. Naravno, generisae se greka ako pokuamo da otvorimo fajl sa atributom ReadOnly modom 01 ili 02. - DS:DX treba da pokazuju na ASCIIZ ime fajla Odmah da neto razjasnimo. ASCIIZ oznaava string koji ima nulu na kraju. Definisanje takvog stringa se obavlja na sledei nain : ime_fajla db "c:\autoexec.bat",0 Naravno moe i ovako : ime_fajla db "autoexec.bat",0 ako u trenutnom direktorijumu postoji takav fajl. -DS:DX znai treba da pokazuju na takav string Posle poziva interrapt-u ukoliko doe do greke Carry Flag e postati 1 i u AX registru e se nai kod greke. Ako do greke ne bude dolo u AX registru e se nai Handle fajla. Preko tog Handle-a se ubudue ostvaruje pristup tom fajlu. Handle u stvari ukazuje na fajl na disku. Pomenuo sam neki Carry Flag. ta je to uopte ? Elem. Flag Registar se sastoji iz Flag-ova koji su duine jedan bit i koriste se za razne provere i operacije. Flag registar izgleda ovako : Ŀ 11 10 F E D C B A 9 8 7 6 5 4 3 2 1 0 CF Carry 1 Flag PF Parity 0 Flag AF Auxiliary Flag 0 ZF Zero Flag SF Sign Flag TF Trap Flag IF Interrupt Flag DF Direction Flag OF Overflow Flag IOPL I/O Privilege Level Flag NT Nested Task Flag 0 RF Resume Flag VM Virtual Mode Flag Nabrzaka u objasniti najvanije FLAG-ove. - Carry Flag Mnoge funkcije kao npr. za otvaranje fajla e, ako doe do greke, postaviti Carry Flag na jedinicu pa se moe proveriti da li je dolo do greke u radu. Naredbe za proveru su na primer JC ili JNC. Isto tako, pri iftovanjem i rotiranjem bitova ta god to bilo, CARRY Flag obino sadri poslednji bit koji je iftovan ili rotiran. - Zero Flag Postavljen je uvek kada je rezultat neke operacije 0. - Sign Flag Postavljen je ako je najvii bit rezultata neke operacije 1. Drugim reima, postavljen je ako je rezultat negativan. - Direction Flag Ako je postavljen, koristi se Auto-Decrement pri radu sa stringovima, u suprotnom koristi se Auto-Increment. - Overflow Flag Postavljen je ako je dolo do aritmetikog prekoraenja. Zatvaranje fajla korienjem Handle-a Rutina za zatvaranje fajla : Funkcija 3Eh interapta 21h ima za zadatak da zatvori fajl koji je otvoren putem Handle-a. Funkcija zahteva sledee ulazne elemente: U AH registru broj funkcije tj. 3Eh U BX registar Handle fajla koji treba zatvoriti. Posle poziva interrapt-u, ukoliko doe do greke Carry Flag e biti postavljen a u AX registru e se nai kod greke. Na osnovu prethodnih instrukcija pokuajmo sastaviti program koji e otvoriti fajl samo za itanje, proveriti da li se sve odvilo po planu, potom ga zatvoriti i vratiti kontrolu DOS-u uz prigodne poruke. Program No. 6.1 ; PROGRAM Demo_Otvaranje_i_zatvaranje_fajla (FILE_IO.ASM) ; Copyright(C) 1998. by Shang Tsung .MODEL tiny .CODE ORG 100h main: jmp pocetak Handle dw 0 ime_fajla db "test.txt",0 nema_fajla db "Nema fajla TEST.TXT !$" greska_zatvaranje db "Ne mogu da zatvorim fajl test.txt !$" sve_ok db "Sve je proslo u najboljem redu.$" pocetak: mov ah,3dh ; moe i krae mov al,0 ; mov ax,3d00h :-) mov dx,offset ime_fajla int 21h ; Otvaramo fajl samo za itanje JC greska_u_otvaranju ; ako ne moe da otvori fajl ; skae na labelu "greska" mov [Handle],ax ; uvamo Handle fajla mov ah,3eh ; mov bx,[handle] ; Zatvaranje fajla int 21h ; JC greska_u_zatvaranju ; ako ne moe da zatvori fajl ; skae na dotinu labelu mov ah,9h ; mov dx,offset sve_ok ; tampamo poruku o uspehu int 21h ; jmp kraj greska_u_otvaranju: mov ah,9h ; mov dx,offset nema_fajla ; tampamo poruku o greci int 21h ; JMP short kraj greska_u_zatvaranju: mov ah,9h ; mov dx,offset greska_zatvaranje ; tampamo poruku o greci int 21h ; kraj: mov ax,4c00h ; povratak u DOS int 21h ; end main Da pojasnimo. Na samom poetku pri inicijalizaciji promenljivih sreemo sledei red: - Handle dw 0 Ovime se rezervie mesto u memoriji tipa WORD (DW) koje e primiti Handle otvorenog fajla. Promenljiva je tipa WORD (16 bit-a) jer je i Handle tipa WORD. Nula znai da se odmah pri inicijalizaciji upie nula. Ako je otvaranje fajla prolo uspeno u promenljivu Handle se upisuje Handle fajla test.txt : mov [handle],ax Ovakve zagrade oznaavaju da je re o memorijskoj lokaciji i u ovom sluaju nisu neophodne. - JC labela ( JUMP IF CARRY - SKOI AKO JE CARRY FLAG POSTAVLjEN) *Pogledaj opis J... naredbi u drugom poglavlju. Ako je u toku izvravanja programa dolo do greke, Carry Flag e biti postavljen na jedinicu i JC naredba e obaviti skok na predvieno mesto, tj. program e se okonati uz prigodnu poruku. Znai, poto smo sauvali Handle fajla test.txt pristupamo njegovom zatvaranju to mislim da ne treba komentarisati. ISPII PROGRAM FILE_IO.ASM I KOMPAJLIRAJ GA !!! Sve radi ? Naravno. Idemo dalje. itanje iz fajla Za ovu rabotu nam je potrebna funkcija 3Fh interapta 21h. Funkcija zahteva sledee ulazne elemente: - u AH registar broj funkcije tj. 3Fh - u BX registar Handle fajla - u CX registar broj bajtova koliko treba proitati - DS:DX treba da pokazuju na deo memorije (bafer) u koji treba uitati podatke Posle poziva interrapt-u ako doe do greke, bie postavljen Carry Flag i u AX registru e se nai kod greke. U suprotnom u AX registru e se nai broj bajtova koliko je zaista proitano. Ako su AX i CX razliiti, to znai da se dolo do kraja fajla i da su uitani svi podaci. Ako je AX nula, onda se nita novo nije iitalo iz fajla i dogodie se EOF (End Of File) pre nego to pone itanje. Zadatak : Napisati program koji otvara fajl TEST.TXT samo za itanje, ita iz fajla blokove od po 16 bajta u memoriju i potom te podatke tampa na ekran. Poto se ceo fajl iita itampati poruku "Kraj fajla!", zatvoriti fajl i vratiti kontrolu DOS-u. Ukoliko u toku izvravanja programa doe do neke greke, tampati prigodnu poruku. Na osnovu dosadanjeg znanja pokuaj sam da napie ovaj program. REENjE : Program No. 6.2 ; PROGRAM dump (DUMP.ASM) ; Copyright(C) 1998. by Shang Tsung .MODEL tiny .CODE ORG 100h main: jmp pocetak Handle dw 0 ime_fajla db "test.txt",0 nema_fajla db 13,10,"Nema fajla TEST.TXT !$" greska_zatvaranje db 13,10,"Ne mogu da zatvorim fajl TEST.TXT !$" sve_ok db 13,10,"Sve je proslo u najboljem redu.$" kraj_fajla db 13,10,"Kraj fajla!$" Buffer db 17 dup (0) pocetak: mov ax,3d00h ; U AH broj funkcije, u AL mod mov dx,offset ime_fajla ; U DX offset lokacije od koje ; poinje ime fajla int 21h ; Otvaramo fajl JC greska ; Ako nismo uspeli skaemo na ; labelu "greska" mov [handle],ax ; uvamo Handle mov cx,16 ; Maksimalan broj karaktera za ; uitavanje mov bx,[handle] ; Koji fajl ? mov dx,offset buffer ; Gde da se upiu podaci ponovo: mov ah,3fh ; Broj funkcije int 21h ; itamo podatke iz fajla cmp ax,0 ; Proveravamo da li je broj proitanih je gotovo ; bajtova jednak nuli. Ako jeste, ; doli smo do kraja fajla mov di,ax ; U DI upisujemo broj proitanih ; bajtova mov [buffer+di],'$' ; Na kraj bafera (na poziciju 17) ; upisujemo znak "$" mov ah,9h ; tampamo podatke na ekran int 21h jmp ponovo ; Idemo dalje greska: ; Greska u otvaranju fajla mov ah,9h mov dx,offset nema_fajla int 21h ; Ispisujemo poruku o greci JMP kraj gotovo: ; DUMP-ovanje je zavreno mov ah,9 mov dx,offset kraj_fajla int 21h ; tampamo poruku o zavretku mov ah,3eh mov bx,[handle] int 21h ; Zatvaranje fajla JC greska_zatvaranje_fajla ; ako nismo uspeli da zatvorimo fajl mov ah,9h mov dx,offset sve_ok int 21h ; tampamo poruku da je sve OK jmp kraj greska_zatvaranje_fajla: ; tampamo poruku o greci pri ; zatvaranju fajla mov ah,9h mov dx,offset greska_zatvaranje int 21h kraj: mov ax,4c00h ; Kraj programa int 21h ; end main AARGH!!!!!!! Da krenemo od poetka. Kao to si primetio, pri inicijalizaciji stringova koristio sam sledeu sintaksu : sve_ok db 13,10,"Sve je proslo u najboljem redu.$" Ovim sam obezbedio da kursor prvo pree u novi red pa da se tek potom itampa string. Razlog je oigledan. Dalje, u bloku promenljivih sreemo zanimljivu sintaksu : Buffer db 17 dup (0) Elem, ovim smo rezervisali 17 delova memorije tipa BAJT (DB) i svaku od njih popunili nulom. Primeri: temp dd 200 dup (0) ; definisali smo 200 delova memorije ; tipa DOUBLE WORD, to znai da e ; promenljiva temp zauzeti 800 bajta zvazbuka dw 10 dup (?) ; Ovako se nita nee upisivati ; po inicijalizaciji *Ovo bi ve trebao da zna(objanjeno je u prvom poglavlju zaboga). Dalje u programu sreemo jo jednu novinu: mov di,ax mov [buffer+di],'$' Dakle dolo je vreme da kaemo neto i o Indeksnim registrima. INDEKSNI REGISTRI Ŀ Indeksni Registar Opis Ĵ SI Source index Ĵ DI Destination index Ĵ IP Instruction pointer Svi ovi registri su 16-o bitni. Noviji raunari raspolau i sledeim indeksnim registrima : Ŀ Indeksni Registar Opis Ĵ ESI Source index Ĵ EDI Destination index Svi ovi registri su 32-o bitni. Ovi registri se koriste u operacijama sa memorijom, odnosno u mogunosti smo da pomou njih indeksiramo memoriju. Ako bi pokuao da to uradi na sledei nain mov [buffer+ax],'$' kompajler e odmah prijaviti greku : "Illegal indexing mode". Meutim mov [buffer+bx],'$' e raditi ?!!! Stvar je u tome da registri opte namene nisu ba meusobno ravnopravni. U operacijama sa memorijom, kao npr. na ovaj nain, samo se moe koristiti registar BX. No, ja za pokazivae na poziciju u nekom nizu, kao u ovom sluaju, uvek koristim indexne registre. Da se brzo osvrnem na IP registar. IP registar je Instruction Pointer, i on pokazuje odnosno sadri OFFSET naredbe koju procesor treba da izvri. Najoigledniji primer je u radu sa procedurama. Kada sa CALL pozove proceduru ona(CALL) na stek PUSH-ne prvo CS pa IP ako se radi o FAR pozivu, tj. samo IP kada se radi o NEAR pozivu. Znaci na stek se PUSH-ne adresa instrukcije koja treba da se izvri posle naredbe CALL. I sad, kada se sa CALL skoi u neku proceduru i u toj proceduri uradi RET, (kao sto je vec bilo objanjeno RET e POP-nuti sa steka IP i CS (tj. samo IP)) i kako procesor izvrava ono na ta pokazuje CS:IP bukvalno se izvrava naredba koja sledi pozivu naredbe CALL. Prosto. Jedna napomena : Sa IP registrom ne moe tek tako da opti(taman posla da moe ;) tako da : mov ax,IP ili PUSH IP nisu ispravne instrukcije jednog programa pisanog u Assembler-u. Pokua li da tako neto napie kompajler e prijaviti greku : "Undefined symbol: IP" Bilo bi prilino opasno kada bismo tek tako mogli da eprkamo sa IP. Na taj nain, samo odreenim komandama je mogue optiti sa registrom IP. Toliko o tome. Da se vratimo na posao. U naem sluaju zahvaljujui DI registru imamo mogunost da se etamo kroz memoriju. Znai u DI smo stavili vrednost u AX koja predstavlja broj proitanih ( i upisanih ) bajtova iz fajla. Zato to radimo ? Pa jednostavno. Poto elimo da iskoristimo blagodeti funkcije 9h interapta 21h, na kraj proitanog stringa treba da dopiemo znak "$". To realizujemo sa : mov [buffer+di],'$' Kako buffer oznaava poetak memorijske lokacije, sa buffer+di obavljamo upis znaka "$" na kraj proitanog stringa. To i objanjava injenicu da se za promenljivu buffer rezervisalo 17 a ne 16 bajta. Ti slobodno pokuaj da iz programa izbrie ova 2 reda, pa e videti ta e se dogoditi kada se na kraju ne bude proitalo svih 16 bajta. Ostatak programa je, mislim, suvino opisivati. *Primeti da za tampanje sadraja fajla na ekran koristim funkciju 9h interrapt-a 21h, to ba i nije pametno, jer velika je verovatnoa da e se u fajlu nai poneki znak '$' to e blokirati ispis. To je moglo i drugaije ali da ti ne bih komplikovao ivot ja sam ga uradio ovako. Ti ako eli, slobodno iskoristi npr. funkciju 13h interrapt-a 10h ili ak, karakter po karakter. ISPII PROGRAM DUMP.ASM I KOMPAJLIRAJ GA !!! A ? ta kae ? Tvoj sopstveni program - ekvivalent naredbe type(ne ba tako "savren" ;). Super! A sada da kreiramo novi fajl. Kreiranje novog fajla Funkcija 3ch interapta 21h je zaduena za kreiranje novog fajla. Funkcija zahteva sledee ulazne podatke : -u registar AH broj funkcije tj. 3ch -u CL registar atribut fajla : bit 0: read-only atribut bit 1: hidden atribut bit 2: system atribut bit 3: volume label bit 4: sub directory bit 5: Archive atribut bit 6 i 7: rezervisani - DS:DX treba da pokazuju na ASCIIZ ime novog fajla *Kada kaem bit 2 : system atribut to znai da je bit 2 postavljen tj. ima vrednost 1. Posle poziva interrapt-u, ukoliko se dogodi greka, Carry Flag e biti postavljen a u AX registru e se nai kod greke. U suprotnom u AX e se nai Handle novootvorenog fajla. Ako fajl ve postoji, bie prebrisan ... zato OPREZ ! A kako da upisujemo podatke u fajl ? Evo i o tome. Upis podataka u fajl Funkcija 40h interapta 21h ima zadatak da pie podatke u fajl. Funkcija zahteva sledee ulazne podatke: - u registar AH broj funkcije tj. 40h - u registar BX Handle fajla u koji treba upisivati podatke - u CX registar broj bajtova koliko treba upisati - DS:DX treba da pokazuje na deo memorije odakle treba itati podatke te ih potom upisivati u fajl Posle poziva interrapt-u 21h u AX registru e se nai broj bajtova koliko je zaista upisano. Ako taj broj nije jednak broju u CX doi e do greke. Prosto. Sada smo spremni za zadatak. Zadatak : Napisati program CP.ASM tako da on predstavlja ekvivalent DOS naredbe COPY sa par izuzetaka : Ime fajla koji se kopira je TEST.TXT, kopiranje se vri u isti direktorijum u fajl pod imenom test2.txt. Sreno! REENjE: Program No. 6.3 ; PROGRAM copy (CP.ASM) ; Copyright(C) 1998. by Shang Tsung .MODEL tiny .CODE org 100h main: jmp pocetak handle1 dw 0 handle2 dw 0 buffer db 17 dup (0) ime_fajla_1 db "test.txt",0 ime_fajla_2 db "test2.txt",0 greska1 db 13,10,"Nema fajla TEST.TXT !$" greska2 db 13,10,"Ne mogu da kreiram fajl TEST2.TXT !$" sve_ok db 13,10,"Sve je proslo u najboljem redu.$" pocetak: mov ax,3d00h mov dx,offset ime_fajla_1 int 21h ; otvaramo fajl JC greska_1 ; u sluaju greke skaemo na ; labelu "greska_1" mov [handle1],ax ; uvamo handle mov ah,3ch mov cl,0 ; bez atributa mov dx,offset ime_fajla_2 int 21h ; kreiramo fajl JC greska_2 mov [handle2],ax ; uvamo handle kreiranog fajla mov cx,16 ; Maksimalan broj bajtova za ; itanje/pisanje mov bx,[handle1] ; u BX upisujemo handle prvog fajla mov dx,offset buffer ; ponovo: mov ah,3fh int 21h ; itamo podatke iz fajla cmp ax,0 ; Ako nije nita proitano je gotovo ; doli smo do kraja fajla push cx bx ; uvamo CX i BX registre na steku mov cx,ax ; broj bajtova za upisati mov ah,40h mov bx,[handle2] int 21h ; upisujemo proitane podatke u fajl pop bx cx ; vraamo CX i BX registre sa steka jmp ponovo ; itamo sledei blok podataka greska_1: ; ispisujemo poruku o greci mov ah,9 mov dx,offset greska1 int 21h jmp kraj greska_2: ; ispisujemo poruku o greci mov ah,9 mov dx,offset greska2 int 21h mov ax,3eh mov bx,[handle1] int 21h ; Zatvaramo prvi fajl jmp kraj gotovo: mov ah,9 mov dx,offset sve_ok int 21h ; tampamo poruku o uspehu mov ax,3eh mov bx,[handle1] int 21h ; Zatvaramo prvi fajl mov ah,3eh mov bx,[handle2] int 21h ; Zatvaramo drugi fajl kraj: mov ax,4c00h int 21h ; Kraj programa end main Dakle, ako smo fajl kreirali, treba da znamo i kako da ga obriemo. Brisanje fajla Funkcija 41h interapta 21h radi upravo to. Funkcija zahteva sledee ulazne podatke : - u registru AH broj funkcije tj. 41h - DS:DX treba da pokazuje na ASCIIZ ime fajla koji treba obrisati Posle poziva interrapt-u 21h ako se dogodi greka Carry Flag e biti postavljen a u registru AX e se nai kod greke : 2 - fajl nije naen 3 - put(PATH) nije naen 5 - pristup zabranjen U suprotnom Carry Flag je 0. Kreirajmo sada mali program koji e obrisati fajl TEST.TXT uz prigodne poruke u sluaju greke. Program No. 6.4 ; PROGRAM brisi_test_txt (BTT.ASM) ; Copyright(C) 1998. by Shang Tsung .MODEL tiny .CODE org 100h main: jmp short pocetak ime_fajla db "test.txt",0 greska02 db "Fajl nije nadjen.$" greska03 db "Path nije nadjen.$" greska05 db "Pristup zabranjen.$" obrisan db "Fajl je obrisan.$" pocetak: mov ah,41h mov dx,offset ime_fajla int 21h ; Briemo fajl JC greska mov ah,9 mov dx,offset obrisan int 21h ; tampamo poruku o uspehu jmp kraj greska: cmp ax,2 ; Proveravamo da li je u pitanju ; greka 2(file not found) je greska_02 cmp ax,3 ; Proveravamo da li je u pitanju ; greka 3(path not found) je greska_03 cmp ax,5 ; Proveravamo da li je u pitanju ; greka 5(access denied) je greska_05 greska_02: mov ah,9 mov dx,offset greska02 int 21h ; tampamo poruku o greci jmp kraj greska_03: mov ah,9 mov dx,offset greska03 int 21h ; tampamo poruku o greci jmp kraj greska_05: mov ah,9 mov dx,offset greska05 int 21h ; tampamo poruku o greci kraj: mov ax,4c00h ; Kraj programa int 21h end main Mislim da su objanjenja suvina. No da nastavimo. Kako da se kreemo unutar fajla tj. kako da pomeramo File Pointer ? Pomeranje pointera na poziciju u fajlu Za ovakvu rabotu nam je potrebna funkcija 42h interrapt-a 21h. Funkcija zahteva sledee ulazne podatke : - U registru AH broj funkcije tj. 42h - U registru BX Handle fajla kojem treba pomeriti pokaziva - CX:DX 32-o bitni pointer na novu poziciju u fajlu - U registru AL polaznu poziciju 0 - od poetka fajla 1 - od trenutne pozicije u fajlu 2 - od kraja fajla Posle poziva interrapt-u, ako je dolo do greke CF e biti postavljen a u AX registru e se nai kod greke. U suprotnom CF nee biti postavljen a DX:AX e biti 32-o bitni pointer kojim odreuje trenutnu poziciju u fajlu. Da malko bolje objasnim. Zamisli da imamo fajl sledee sadrine : "Ovo je test fajl. Nemojte ga ni po koju cenu menjati." *Bez navodnika naravno ;). I sada, korienjem funkcije 3Fh interrapt-a 21h isitamo u memorijsku lokaciju pod imenom BUFF prvih 19 bajta iz fajla. U poetku, posle otvaranja fajla, File Pointer je pokazivao na sam poetak tj. slovo 'O'. Posle itanja 19 bajta iz fajla, Pointer se pomerio i sada pokazuje na slovo 'N' na poetku sledeeg reda. ekaj malo, ali u prvom redu ima samo 17 karaktera ? Jesta, ali nemoj da zaboravi karaktere CR i LF koji se nalaze na kraju prve linije, to objanjava injenicu da posle "Ovo je test fajl." imamo novi red. Znai u prvom redu imamo 19 a ne 17 karaktera. I sada, ako hoemo ponovo da iitamo prvi red u fajlu, pokaziva moramo vratiti na poetak to se realizuje malopre opisanom funkcijom 42h interrapt-a 21h. Na primer : --------------------- mov ah,3fh ; itamo mov bx,[handle] ; prvih 19 bajta iz fajla mov cx,19 ; iji je Handle u BX mov dx,offset buff ; i podatke upisujemo u promenljivu int 21h ; tj. niz buff. mov ah,42h ; Vraamo se na na poetak fajla mov bx,[handle] ; iji je Handle u BX xor cx,cx ; isto to i mov cx,0 xor dx,dx ; isto to i mov dx,0 mov al,0 ; int 21h ; mov ah,3fh ; Opet itamo mov bx,[handle] ; prvih 19 bajta iz fajla mov cx,19 ; iji je Handle u BX mov dx,offset buff ; i podatke upisujemo u promenljivu int 21h ; tj. niz buff. ------------------------------- Reeno je da je DX:AX 32-o bitni broj, koji oznaava poziciju u fajlu. Kada posle poziva interrapt-u dobijemo DX:AX kao 32-o bitnu poziciju u fajlu, taj broj treba i da sauvamo. Kako ? Elem, 32-o bitni broj je u stvari DOUBLE WORD (DD). Znai, bie nam potrebna promenljiva tipa DD u koju emo sauvati poziciju u fajlu. To se realizuje na sledei nain : U bloku promenljivih definiimo promenljivu POZICIJA: pozicija DD 0 Ovim smo definisali 32-o bitnu promenljivu tipa DD koja je odmah po inicijalizaciji dobila vrednost 0. I sada, recimo da elimo da saznamo trenutnu poziciju u fajlu : - - - - - - - - - - - - - - - - - mov ah,42h ; funkcija 42h mov bx,[handle] ; handle fajla xor cx,cx ; isto to i mov cx,0 xor dx,dx ; isto to i mov dx,0 mov al,1 ; od trenutne pozicije u fajlu int 21h Znai, pomeramo File Pointer za 0 bajta od trenutne pozicije u fajlu tj. File Pointer se uopte ne menja. No, ono to je vano jeste da nam (sem ako nije dolo do greke) DX:AX sadri trenutnu poziciju File Pointera u fajlu. I sad, da sauvamo tu poziciju u promenljivu pozicija tipa DD. mov word ptr [pozicija],ax mov word ptr [pozicija+2],dx AARGH!!! No, no, polako. Da objasnimo. Kako je promenljiva pozicija tipa DD (32-o bitna), njoj nije ba lako direktno pristupati, s obzirom da su registri preteno 16-o bitni. No, kako je DD u stvari DOUBLE WORD, a WORD je 16-o bitan, moemo mu pristupati deo po deo. Kada pokuavamo da saznamo trenutnu poziciju u fajlu, dobijamo DX:AX 32-o bitni broj. To znai da je u registru DX High WORD 32-o bitnog broja a u registru AX Low WORD 32-o bitnog broja. To izgleda otprilike ovako : Ŀ 32 bit 01100000011010011000101110100101 High WORD Low WORD Znai, kada elimo da sauvamo vrednost DX:AX u promenljivu pozicija, posebno emo sauvati High WORD a posebno Low WORD. E sad. Kako su 32 bita u stvari 4 bajta, to Low WORD zauzima 2, i high WORD zauzima 2 bajta. I sad. mov word ptr [pozicija],ax Na lokaciju pozicija, koju tretiraj kao WORD ( word ptr ), to je nita drugo do Low WORD, unesi vrednost registra AX. Potom mov word ptr [pozicija+2],dx e na lokaciju za 2 bajta viu [pozicija+2] ( +2 jer je WORD dugaak 2 bajta ) koju takoe tretiraj kao WORD, unesi vrednost registra DX. [pozicija+2] e pokazati na lokaciju za 2 bajta viu od [pozicija], a kako je [pozicija] Low WORD, to je [pozicija+2] nita drugo do High WORD. I eto, uspeli smo. U promenljivi pozicija sada imamo 32-o bitnu vrednost DX:AX. Prosto, da ne moe biti prostije. *Kao to sam ve jednom napomenuo, pri upisivanju podataka u memoriju prvo se upisuje najnii bajt pa potom sledei itd. Ako bismo na primer imali DWORD broj 467589 tj. 72285h i elimo da ga upiemo u memoriju, rezultat u memoriji je ovakav 85h, 22h, 07h, 00h Prva 2 bajta predstavljaju LOW WORD, a druga HIGH WORD. Prvi bajt predstavlja LOW BYTE LOW WORD-a, drugi HIGH BYTE LOW WORD-a... U predhodnim primerima se pojavilo neto novo - XOR. Pa da objasnimo. - Naredba XOR operand1,operand2 vri XOR-ovanje oba operanda bit po bit, i rezultat upisuje u operand1, tj. poredi se najnii bit operanda1 sa najniim bitom operanda2 po XOR logici i rezultat upisuje u operand 1. Akcija se potom odvija za bit 1, bit 2 i tako dalje. Ŀ XOR Logika Ĵ XOR 0 1 Ĵ 0 0 1 Ĵ 1 1 0 Na primer, imamo u registru AX broj 1000111011011010b a u registru BX 1000011010111101b. Posle XOR ax,bx dogaa se sledee : Najnii bit registra AX (0) XOR Najnii bit registra BX (1) Rezultat : 0 XOR 1 je 1 i najnii bit u registru AX se menja u 1. Akcija se dalje nastavlja za sve bitove. Ŀ Ĵ1000111011011010 Dest X O 0 0 0 0 1 0 0 0 0 1 1 0 0 1 1 1 Nova vrednost Dest-a R Ĵ1000011010111101 Source Elem. Kada uradimo neto kao XOR cx,cx CX e dobiti vrednost 0. Zato ? Pa jednostavno. Kako su operandi indentini, pri XOR-ovanju poredie se indentini bitovi, a iz tabele se vidi da je 1 XOR 1 nula, i 0 XOR 0 takoe nula. Znai svaki bit registra CX e postati 0, a to je znai nita drugo do 0. Ali, zato bismo to uopte radili kada moemo jednostavno da uradimo mov cx,0 ??? E, pa zavisi da li ti ba treba da ti izvrni fajl bude to krai. U izvrnom fajlu naredba "MOV cx,0" e zauzeti 3 bajta, a naredba "XOR cx,cx" jedan bajt manje. Ova naredba je naroito korisna kada npr. treba promeniti bitove 2,4 i 5 npr. registra AL u suprotne vrednosti(ako je bit 2 jedinica promeniti u nulu i obrnuto), a da ostali bitovi ostanu nepromenjeni. bit 7 bit 0 Ŀ AL : 1 0 0 1 1 0 1 0 Kako ? Pa jednostavno : XOR al,00110100b Ŀ AL : 1 0 0 1 1 0 1 0 XOR Ŀ 0 0 1 1 0 1 0 0 Ŀ AL : 1 0 1 0 1 1 1 0 Prosto. Bilo kako bilo, bar smo se upoznali sa jednom logikom operacijom. to se ostalih tie , otom potom :). Zadatak : Na osnovu prethodnih instrukcija napisati program koji e na osnovu fajla TEST.TXT kreirati fajl TEST2.TXT u koji e kopirati fajl TEST.TXT unazad. Znai, ako je fajl TEST.TXT ovakav: Ovo je test fajl. Nemojte ga ni po koju cenu menjati. fajl TEST2.TXT bi trebao otprilike da bude ovakav : .itajnem unec ujok op in ag etjomeN .ljaf tset ej ovO Da ti ne bih zagoravao ivot, prihvatimo da fajl TEST.TXT ne moe biti dui od 2K pa stoga i kreiraj promenljivu tj. niz BUFF duine 2K u koju e uitati fajl TEST.TXT. Sreno! Program No. 6.5 ; PROGRAM rikverc (RIKVERC.ASM) ; Copyright(C) 1998. by Shang Tsung .MODEL tiny .CODE ORG 100h main: jmp pocetak DUZINA_BUFFERA EQU 2048 input_file db "test.txt",0 output_file db "test2.txt",0 Handle_input dw 0 Handle_output dw 0 buff db DUZINA_BUFFERA dup (0) greska_1 db "Ne mogu da otvorim TEST.TXT !$" greska_2 db "Ne mogu da kreiram TEST2.TXT !$" greska_3 db "Ne mogu da zatvorim TEST.TXT !$" greska_4 db "Ne mogu da zatvorim TEST2.TXT !$" greska_5 db "Greska u citanju fajla TEST.TXT !$" pocetak: mov ax,3D00h ; mov dx,offset input_file ; Otvaramo fajl TEST.TXT int 21h ; JC greska_otvaranje_input ; Provera greke mov [handle_input],ax ; uvanje Handle-a mov ah,3ch ; xor cx,cx ; Kreiramo fajl TEST2.TXT mov dx,offset output_file ; int 21h ; JC greska_otvaranje_output ; Provera greke mov [handle_output],ax ; uvanje Handle-a citaj: mov ah,3Fh ; mov bx,[handle_input] ; itamo 2 kilobajta mov cx,DUZINA_BUFFERA ; iz fajla mov dx,offset buff ; TEST.TXT int 21h ; JC greska_citanje ; Provera greke cmp ax,0 ; Proveravamo da li su je kraj ; isitani svi podaci mov si,0 ; pokazuje na poetak buff-a mov di,ax ; pokazuje na kraj+1 buff-a ponovo: mov bh,[buff+si] ; mov bl,[buff+di-1] ; Zamenjuje karaktere mov [buff+si],bl ; u buff-u mov [buff+di-1],bh ; inc si ; Pomera pokaziva dec di ; Vraa pokaziva cmp si,di ; proverava da li su se JB ponovo ; pokazivai sreli mov cx,ax ; Broj karaktera koje treba ; ispisati mov ah,40h ; mov bx,[handle_output] ; Ispis mov dx,offset buff ; int 21h ; jmp citaj ; ita se sledeih 2 kilobajta greska_otvaranje_input: mov ah,9h ; mov dx,offset greska_1 ; tampanje poruke o int 21h ; greci jmp totalan_kraj greska_otvaranje_output: mov ah,9h ; mov dx,offset greska_2 ; tampamo poruku o greci int 21h ; mov ah,3Eh ; Zatvaranje prvog fajla mov bx,[handle_input] ; Ako ne uspe - koga briga? ;) int 21h ; jmp totalan_kraj greska_citanje: mov ah,9 ; mov dx,offset greska_5 ; tampamo poruku o greci int 21h ; mov ah,3Eh ; Zatvaranje input-a mov bx,[handle_input] ; Ako ne uspe - koga briga ;) int 21h ; mov ah,3eh ; mov bx,[handle_output] ; Zatvaranje output-a int 21h ; Ako ne uspe - koga briga ;) jmp totalan_kraj greska_zatvaranje_input: mov ah,9h ; mov dx,offset greska_3 ; tampamo poruku o greci int 21h ; mov ah,3eh ; Zatvaranje Output-a mov bx,[handle_output] ; Ako ne uspe - koga briga? ;) int 21h ; jmp totalan_kraj greska_zatvaranje_output: mov ah,9h ; mov dx,offset greska_4 ; tampamo poruku o greci int 21h ; jmp totalan_kraj kraj: mov ah,3Eh ; mov bx,[handle_input] ; Zatvaranje Input-a int 21h ; JC greska_zatvaranje_input ; Provera greke mov ah,3eh ; mov bx,[handle_output] ; Zatvaranje Output-a int 21h ; JC greska_zatvaranje_output ; Provera greke totalan_kraj: mov ax,4c00h ; Izlaz iz programa int 21h end main ; Kraj programa Ovaj program e izvrnuti blokove od po 2K u fajlu, znai nee bukvalno izvrnuti fajl(ako bi fajl bio vei od 2K). Uz predhodni program je ila i reenica "Da ti ne bih zagoravao ivot...". E, sada emo da ga malo zagoramo. Jo u prvom poglavlju sam pominjao PSP - Program Segment Prefix. E pa da i to objasnimo. PSP je kao to mu i samo ime kae prefiks programa, tj. blok od 256 bajta koji predhodi svakom programu (EXE,COM) koji startujemo. U okviru PSP-a postoji obilje informacija koje su potrebne DOS-u da bi radio sa programom normalno, kao i mnoge informacije koje se tiu samog programa. Primeti da su svi programi koje smo do sada pisali (.COM programi ) imali jednu ( dobro vie ;) stvar(i). To je : " org 100h ". Kao to je bilo objanjeno u prvom poglavlju, to predstavlja adresu od koje poinje uitavanje programa u memoriju tj. tzv."ENTRY POINT". Ukoliko nisi primetio, 100h je 256 decimalno. Znai svaki na program je poinjao od adrese 256 u tekuem segmentu iz prostog razloga to su prvih 256 bajta rezervisani za PSP. Pokua li da umesto broja 100h stavi neki drugi, linker e odmah prijaviti greku : "Fatal: Cannot generate COM file : invalid initial entry point address". Jednostavno, adresa MORA biti 100h(kada je u pitanju COM fajl), jer PSP programa mora biti slepljen uz sam program. Evo, kreiraj mali program u Assembler-u koji e tampati prvih recimo 1024 bajta(1K) CODE segmenta. To se realizuje veoma lako, korienjem npr. indeksnog registra kao pokazivaa i funkcije 2h interrapt-a 21h u jednoj petlji programa, te taj program neu ovde navoditi. Poto program itampa prvih 1024 bajta (uz mogua pitanja i slina udna ponaanja, to se moe obii tako to prvo proveri koji je karakter u pitanju, pa ako je manji od 32(ASCII) umesto njega odtampa npr. taku - tako je najsigurnije) primetie da se od 256-og karaktera pojavljuje niz znakova koji (ako proveri) u stvari predstavljaju tvoj sopstveni .COM fajl. Prvih 256 bajta e biti PSP a ostatak tvoj program i neko ubre koje se tu zateklo. Procesor to izvrava redom, i kada ne bi naiao na RET ili neku drugu vrstu povratka u DOS, pokuae da izvri to ubre koje mu moe narediti bog te pita ta, i PAFFF - sistem pada. No malo smo se udaljili od teme. Re je o PSP-u. Pa da predstavimo i mapu PSP-a : *OFFSET i VELIINA su hexadecimalni brojevi Ŀ OFFSETVELIINA OPIS Ĵ 00 02 "INT 20h instruction" Instrukcijski bajtovi naredbe INT 20H na mainskom jeziku tj. karakteri 205 i 32 ASCII. Ovom naredbom se izlazi iz programa. Ĵ 02 02 "Top of memory" Vrh memorije tj. adresa sledeeg slobodnog MCB-a. Ĵ 04 01 "Reserved" Rezervisano Ĵ 05 05 "Long call to function dispatcher" Ostalo od CP/M(stari operativni sistem) kompatibilnosti. Ulogu funkcijskog dispeera preuzeo INT 21H. Ĵ 06 02 "Available Memory" Broj slobodnih bajtova u programskom CODE segmentu. Ĵ 0A 04 "Program terminate address" Segment i offset povratne adrese iz programa. Na ovu adresu se prelazi po zavretku programa. Ĵ 0E 04 "Control-Break exit address" Segment i offset adrese na koju se "skae" po pritisku na Control-Break i Control-C. Ĵ 12 04 "Critical error exit address" Segment i offset adrese na koju se "skae" po pojavi kritine greske. Ĵ 16 02 "Parent PSP" Segmentna adresa "starijeg - roditelja" procesa ( procesa koji je pozvao ovaj ). U sluaju da proces nema "roditelja" ovde se nalazi segmentna adresa PSP-a samog procesa. Ĵ 18 14 "File handle table" Sadri 20 bajtova, koji su pokazivai na sistemsku FILE tabelu. Prvih 5 bajtova su ve postavljeni, a otvoreni su za STDIN, STDOUT, STDERR, AUXIO, i LSTOUT. Za svaki otvoreni fajl, rezervise se novi bajt, time sto se u njega upise pokaziva na sistemsku FILE tabelu. Neiskoriteni bajt je oznaen sa 0FFh. Ĵ 2C 02 "Environment address" Segmentna adresa ENVIRONMENT varijabli. Ĵ 2E 04 "Stack switch storage" Lokacija za privremeno smestanje steka prilikom operacija DOS-a sa internim stekom. Ĵ 32 02 "Handle count" Maksimalan broj elemenata File Handle table. Standardna vrednost je 20. Ĵ 34 04 "Handle table address" Offset i segment na File Handle Table. Standardno pokazuje na samu tabelu u okviru PSP-a, ali ako korisnik poeli da povea broj fajlova koje sme da otvori, moe sam da napravi File Handle Table, i da ovaj pokaziva usmeri na nju, i da stavku Handle Count podesi na eljeni broj fajlova. Ĵ 38 18 "Reserved" Rezervisano Ĵ 50 03 "Function dispatcher interrupt" Sadri kod kojim se poziva INT 21H. Ĵ 53 02 "Reserved" Rezervisano Ĵ 55 07 "File control block extension" Prosirenje File Control Block-a. MicroSoft umesto rada sa FCB-ovima preporuuje rad sa hendlovima. Ĵ 5C 10 "File control block number one" Neotvoreni FCB #1. Prilikom otvaranja, mogue unitenje FCB #2, i sadraja komandne linije. Preporuen rad sa hendlovima, umesto sa FCB-ovima. Ĵ 6C 10 "File control block number two" FCB #2. Ĵ 7C 04 "Reserved" Rezervisano Ĵ 80 80 "Default disk transfer area" Standardan bafer za rad sa diskom kod FCB-a. Mogue unistenje sadraja komandne linije. Ĵ 80 01 "Command line length" Ukupna duina komandne linije, bez simbola za redirekciju. Ĵ 81 7F "Command line buffer" Komandna linija, onako kako je otkucana. No, no, polako. Neu se zadravati da svako pare PSP-a potanko objasnim ve u samo na informativnom nivou objasniti ta znai ova papazjanija. Naravno, potanko u objasniti stvari koje nas se tiu i koje nisu previe komplikovane. Detaljniji opisi e se najverovatnije nai u sledeem izdanju UHFA. Idemo od poetka. 1. Na offset-u 0 PSP-a imamo 2 bajta koja nam predstavljaju poziv interrapt-u 20h u mainskom jeziku. Kako sam poziv ovog interrapt-a u .COM fajlu u stvari okonava proces pokuaj da napravi mali .COM program koji e da skoi na tu memorijsku lokaciju. To se obavlja na sledei nain : Primer 6.1 ; PRIMER korienje_PSP_1 (KPSP1.ASM) ; Copyright(C) 1998. by Shang Tsung .MODEL tiny .CODE ORG 100h main: mov ax,0 JMP ax end main Ovaj mali program e jednostavno odmah po startovanju skoiti na poetak PSP-a tj. izvrie se instrukcija poziva interrapt-u 20h to e rezultovati niim drugim do povratkom u stari dobri DOS ;). Ovde konano dolazimo i do same sri naredbe RET (bar kada je u pitanju COM fajl), odnosno do razloga korienja RET(tj. RETN) naredbe za okonavanje programa. Kao to je bilo objanjeno, RET nas vraa u DOS(i opet: kada je u pitanju COM fajl), tako to sa steka uzme vrednost tipa WORD tj. 2 bajta (ta vrednost je 0)i tu vrednost smesti u IP. Kako procesor izvrava instrukcije koje se nalaze na memorijskoj adresi CS:IP, nakon poziva naredbe RET, u IP e se nai 0, i procesor e izvriti naredbu CS:0 to je nita drugo do sam poetak PSP-a, a tu se nalazi instrukcija poziva interrapt-a 20h. Eto, sve je dobro to se dobro svri. 2. "Top of memory" tj. adresa sledeeg slobodnog MCB-a. Neu se zadravati oko ovoga, samo da zna da je MCB u stvari skraenica za "Memory Control Block". 3. Rezervisano. 4."Long call to function dispatcher" Ostalo od CP/M(stari operativni sistem) kompatibilnosti. Ulogu funkcijskog dispeera preuzeo INT 21H. Kao to je i objanjeno. Ostatak starog operativnog sistema. 5."Available Memory" Sadri broj slobodnih bajtova u programskom CODE segmentu. ta vie da se kae ?!!! 6."Program terminate address" Sadri segment i offset povratne adrese iz programa. Na ovu adresu se prelazi po zavretku programa. 7."Control-Break exit address" Sadri segment i offset adrese na koju se "skae" po pritisku na tastere Control-Break ili Control-C. Dakle, ako u toku izvravanja programa pritisnemo CTRL+BREAK, skoie se na adresu koja se nalazi u ovom polju PSP-a. Dakle, ako eli da sprei nasilno okonavanje programa pritiskom na CTRL+BREAK, potrebno je promeniti adresu na koju se skae, pa je najbolje tu upisati adresu neke labele u naem programu nakon koje e se samo nai naredba "IRET". Ajde da i to objasnim. Naredba "IRET" je slina RETF naredbi. Isto je u pitanju FAR skok kao i u sluaju naredbe RETF, ali IRET e, nakon to POP-ne IP i CS sa steka, popnuti i FLAG-ove. Ovu naredbu(IRET) je potrebno navesti, jer pre skoka na tu nau labelu, na stek e se PUSH-nuti FLAG-ovi,CS i IP tim redom. 8."Critical error exit address" Sadri segment i offset adrese na koju se "skae" po pojavi kritine greke. 9."Parent PSP" Segmentna adresa "starijeg - roditelja" procesa ( procesa koji je pozvao ovaj ). U sluaju da proces nema "roditelja" ovde se nalazi segmentna adresa PSP-a samog procesa. Znai, ako bismo startovali iz naeg programa neki drugi program, u ovom polju PSP-a tog drugog programa e se nai segmentna adresa programa iz kojeg smo startovali taj drugi program tj. segmentna adresa naeg programa. 10."File handle table" Sadri 20 bajtova, koji su pokazivai na sistemsku FILE tabelu. Prvih 5 bajtova su ve postavljeni, a otvoreni su za STDIN, STDOUT, STDERR, AUXIO, i LSTOUT. Za svaki otvoreni fajl, rezervise se novi bajt, time to se u njega upie pokaziva na sistemsku FILE tabelu. Neiskoriteni bajt je oznaen sa FFh. Da ne komplikujemo. U ovom polju PSP-a se nalaze pokazivai na sistemsku FILE tabelu tj. tabelu u kojoj se nalaze pokazivai na sve otvorene fajlove. Najvei broj otvorenih fajlova je kontrolisan stavkom FILES u CONFIG.SYS-u. STDIN predstavlja standardni ureaj (koji DOS tretira kao fajl) za ulaz(tastatura), STDOUT predstavlja standardni ureaj za izlaz (monitor) ... 11."Environment address" Segmentna adresa ENVIRONMENT varijabli. Dok si u komandnoj liniji otkucaj sledee : C:\ASM>set Bie ti prezentovana lista ENVIROMENT varijabli koje se koriste za razne stvari, no ovo bi sve ve trebao da zna (to je osnovno poznavanje DOS-a). Dakle u ovom polju PSP-a se nalazi segmentna adresa tih varijabli. Na ovaj nain moe da sazna ali i da promeni PATH, PROMPT, pa ak i COMSPEC varijablu (eprikanje sa COMSPEC varijablom nije ba preporuljivo - ona sadri adresu komandnog interpretera COMMAND.COM-a). No, pri startovanju programa, nainie se kopija ENVIROMENT varijabli, i adresa koja e se nalaziti u ovom bloku PSP-a pokazivae na tu KOPIJU. Tako, ako promeni neku varijablu iz ove KOPIJE ENVIROMENT varijabli, to nee uticati na ENVIROMENT varijable procesa iz koga si startovao program. 12."Stack switch storage" Lokacija za privremeno smestanje steka prilikom operacija DOS-a sa internim stekom. 13."Handle count" Maksimalan broj elemenata File Handle table. Standardna vrednost je 20. Kao to je i malopre objanjeno, u CONFIG.SYS-u se vre ovakva podeavanja. 14."Handle table address" Offset i segment na File Handle Table. Standardno pokazuje na samu tabelu u okviru PSP-a, ali ako korisnik poeli da povea broj fajlova koje sme da otvori, moe sam da napravi File Handle Table, i da ovaj pokaziva usmeri na nju, i da stavku Handle Count podesi na eljeni broj fajlova. 15."Reserved" Rezervisano 16."Function dispatcher interrupt" Sadri kod kojim se poziva INT 21H tj. karaktere 205 i 33 ASCII, koji predstavljaju poziv interrapt-u 21h u mainskom jeziku. to se treeg karaktera tie, u pitanju je karakter 203 ASCII koji predstavlja instrukciju "RETF" u mainskom jeziku. Naredba RETF je FAR verzija naredbe RET. Ona sa steka POP-ne IP i CS, dakle dve WORD vrednosti. 17."Reserved" Rezervisano 18."File control block extension" Prosirenje File Control Block-a(FCB). MicroSoft umesto rada sa FCB-ovima preporuuje rad sa hendlovima. 19."File control block number one" Neotvoreni FCB #1. Prilikom otvaranja, mogue unitenje FCB #2, i sadraja komandne linije. Preporuen rad sa hendlovima, umesto sa FCB-ovima. 20."File control block number two" Neotvoreni FCB #2. 21."Reserved" Rezervisano 22."Default disk transfer area" Standardan bafer za rad sa diskom kod FCB-a. Mogue unistenje sadraja komandne linije. 23."Command line length" Ukupna duina komandne linije, bez simbola za redirekciju. E, ovo nam je ve vano. Dakle na offset-u 80h imamo bajt koji nam govori kolika je duina komandne linije. Ako smo program startovali sa ime_prg parametar1 parametar2 parametar3 u ovom bajtu e se nai vrednost 33. Primeti da se i razmaci raunaju. Ovo je naravno bez simbola za redirekciju ( <,>,| ). *Ovde treba dodati, da isti ovaj prostor, dakle od offset-a 80h pa sledeih 128 bajta, predstavlja i DTA(Disk Transfer Area). Ako bismo izvrili funkciju 4Eh interrapt-a 21h, koja ima za zadatak da trai fajl sa odreenim imenom i atributima, ona e u DTA vratiti rezultate pretrage, to znai da e se sve od offset-a 80h PSP-a prebrisati tim podacima, to dovodi do gubitka komandne linije. 24."Command line buffer" Komandna linija, onako kako je otkucana. Znai, od offset-a 81h pa sledeih [80h](vrednost sa offset-a 80h) bajta imamo komandnu liniju. Na kraju komandne linije je karakter CR (13 ASCII). Evo, napiimo program koji e da nam itampa komandnu liniju, a u sluaju da komandne linije nema (bajt na offset-u 80h je 0) odtampati prigodnu poruku. Program No. 6.6 ; PROGRAM komandna_linija (COMMLINE.ASM) ; Copyright(C) 1998. by Shang Tsung .MODEL tiny .CODE ORG 100h main: mov bx,80h ; offset u PSP-u cmp byte ptr ds:[bx],0 ; proveravamo ima li komandne linije je nema_komandne_linije ; ako nema skai gde treba mov dx,offset linija ; tampamo mov ah,9h ; jednostavni int 21h ; prefiks mov cl,ds:[bx] ; u CL postavljamo duinu komandne ; linije inc bx ; prelazimo na komandnu liniju mov ah,2h ; funkcija 2h interrapt-a 21h sledeci_karakter: mov dl,ds:[bx] ; u DL stavljamo karakter za tampanje int 21h ; tampamo karakter inc bx ; pomeramo se na sledei bajt dec cl ; smanjujemo broja cmp cl,0 ; ako nismo doli do kraja jne sledeci_karakter ; prei na sledei karakter ret ; povratak u DOS nema_komandne_linije: mov dx,offset nema_linije ; mov ah,9h ; tampamo poruku int 21h ; ret ; povratak u DOS nema_linije db "Nema komandne linije.$" ; Definicije linija db "Komandna linija :$" ; poruka end main ; Kraj programa Prosto ko pasulj. Kao to si verovatno primetio, u ovom programu sam malko drugaije definisao promenljive tj. na neto drugaijem mestu, no, ve sam rekao da je i ovako mogue definisati promenljive(i preporuljivo). Sada kompajliraj predhodni program i unesi neto nalik na C:\ASM>COMMLINE ovo je test programa i trebalo bi da se dogodi sledee Komandna linija : ovo je test programa *NAPOMENA: Prvom parametru e predhoditi razmak(SPACE). Ali, ako startuje program sa npr. C:\ASM>COMMLINE/ovo je test programa rezultat e biti : Komandna linija :/ovo je test programa Korist od ovoga je oigledna - sada moemo da nekom naem programu prosledimo neke parametre. Na primer programu RIKVERC.COM (naravno, prvo moramo da malo preradimo RIKVERC.ASM) moemo proslediti ime fajla koji treba da se izvrne. Pa da tako neto i pokuamo. Pre nego to se bacimo na posao da objasnim jo jednu stvaricu. Interrapt 29h ima za zadatak da na ekran tampa karakter iji se ASCII kod nalazi u AL. Ali, zato ja ovo napominjem kada smo ve upoznali druge rutine koje rade isti ovaj posao (funkcija 2 interrapt-a 21h) ? E, jedina razlika je u tome to se tampanje na ekran korienjem interrapt-a 29h obavlja mnogo bre(i jednostavnije) nego ostalim rutinama. Brzo u ti objasniti jo jednu naredbu za koju mislim da e ti dobro doi. LOOP Sintaksa : LOOP labela Naredba LOOP e umanjiti vrednost registra CX za 1 i zatim skoiti na labelu ako je registar CX razliit od nule. Primer : mov cx,10 ; Koliko puta da skoi mov dl,'a' ; ta da se tampa mov ah,2h ; Funkcija 2h interrapt-a 21h skaci: int 21h ; pozovi interrapt 21h loop skaci Ovo pare koda e na ekran 10 puta itampati slovo 'a'. Znajui sve ovo napiimo sledei program. Program No.7 : Napisati program slian programu DUMP.ASM. Program treba da pokupi ime fajla kojeg treba itampati na ekran putem komandne linije, i da zatim, korienjem malopre pomenutog interrapt-a, taj fajl itampa na ekran. Program No.6.7 ; PROGRAM dump_advanced (DUMPADV.ASM) ; Copyright(C) 1998. by Shang Tsung .MODEL tiny .CODE org 100h main: mov di,80h ; Na ovoj poziciji imamo duinu ; komandne linije cmp byte ptr [di],0 ; Ako je 0, znai nema komandne je nema_parametara_komandne_linije ; linije inc di ; pomeramo se na sadraj komandne ; linije provera_sledeceg_karaktera: inc di ; sledei karakter cmp byte ptr [di], ' ' ; proveravamo da li je u pitanju je nasli_smo_kraj_imena_fajla ; razmak cmp byte ptr [di],13 ; proveravamo da li je u pitanju je nasli_smo_kraj_imena_fajla ; CR karakter jmp provera_sledeceg_karaktera ; proveravamo sledei karakter nasli_smo_kraj_imena_fajla: mov byte ptr [di],0 ; postavljamo nulu na kraj imena ; fajla mov ax,3d00h ; U AH 3dh, u AL 0 - Otvaramo fajl ; u read-only modu mov dx,82h ; offset poetka imena fajla int 21h ; otvaramo fajl jc greska_u_imenu_fajla ; u sluaju greke tampamo poruku mov [handle],ax ; uvamo Handle fajla sledeci_blok: mov ah,3fh ; itamo iz fajla mov bx,[handle] ; u BX handle fajla mov cx,20000 ; maximalan broj bajtova za itanje ; u jednom koraku mov dx,offset buffer ; Offset buffer-a int 21h cmp ax,0 ; proveravamo da li smo stigli do je kraj_fajla ; kraja fajla mov cx,ax ; u CX stavljamo broj bajtova mov di,offset buffer ; u DI offset buffer-a sledeci_karakter: mov al,[di] ; u AL stavljamo karakter koji ; treba itampati int 29h ; tampamo karakter inc di ; pomeramo se na sledei karakter loop sledeci_karakter jmp sledeci_blok ; itamo sledei blok od ; 20000 bajta (ako ga ima) kraj_fajla: mov ah,3eh ; mov bx,[handle] ; Zatvaramo fajl int 21h ; ret ; povratak u DOS nema_parametara_komandne_linije: mov ah,9h ; tampamo sintaksu mov dx,offset sintaxa ; tj. kako da se koristi int 21h ; na program ret ; povratak u DOS greska_u_imenu_fajla: mov ah,9h ; tampamo poruku mov dx,offset nema_fajla ; o int 21h ; greci ret ; povratak u DOS handle dw 0 ; handle fajla nema_fajla db "Ne mogu da nadjem fajl!$" sintaxa db "Sintaksa : DUMPADV ime_fajla$" buffer: ; odavde poinje buffer end main Ovde bih mogao pruiti nekoliko objanjenja. Kao to si primetio, pri traenju kraja imena fajla, da bi stavio 0 na kraj, jer funkcija 3dh interrapt-a 21h zahteva da DS:DX pokazuje na ASCIIZ ime fajla, upotrebio sam 2 poreenja. Prvo sa brojem 32 koje predstavlja ASCII vrednost karaktera SPACE, i potom sa brojem 13 to je ASCII vrednost tastera ENTER. Zato smo ovo uradili ? Jednostavno. Kao to je ve bilo objanjeno, od offset-a 81h PSP-a, nalazi se komandna linija onako kako je otkucana, praena karakterom 13 tj. enterom. I sad, ako bi korisnik uneo vie od jednog parametra a prvi parametar je zaista ime fajla, taj prvi parametar se nee zavravati karakterom 13 nego karakterom 32. Zbog toga sam ja ukljuio obe mogunosti. I sada, ako bi korisnik ukucao neto nalik na C:>dumpadv test.txt blablabla a fajl TEST.TXT zaista postoji, u PSP-u bi se, nakon imena fajla, naao karakter 32, koji emo onda mi pretvoriti u karakter 0. Ali, ako je korisnik uneo samo ime fajla C:>dumpadv test.txt nakom imena fajla u PSP-u, nai e se karakter 13. Zbog toga se moraju oba sluaja proveriti, da ne bi dolo do greke. *Dodue, i ime fajla moe sadrati znak 32 ASCII tj. razmak. U takvom sluaju, predhodni program nee uspeti da izvri zadatak. To u stvari nije pravilno, ali neki programi mogu da kreiraju takve fajlove - sa razmakom u imenu, ali je onda prilino teko takvom fajlu pristupati. Ostatak je vie-manje poznat sem po pitanju jedne stvari - buffer-a. Kao to si primetio, za buffer uopte nisam odvojio mesto u fajlu tipa buffer db 20000 dup (0) ve sam jednostavno definisao labelu buffer na samom kraju fajla. Zato ? Da te podsetim, kako radimo u TINY modu, i u pitanju je naravno COM fajl, blok memorije od 64K je rezervisan za na program po uitavanju. Taj blok memorije je u stvari CODE segment. Kako je na program (izvrna verzija) duine tek neto preko 100 bajta, ostatak CODE segment-a je formalno prazan, tj. ne sadri nikakve vane podatke - tu e se nalaziti neko ubre zaostalo od predhodnih programa ili neto slino. Ŀ PSP Ĵ Na program Ĵ Ostatak CODE segment-a Ĵ STEK CODE SEGMENT Znai deo CODE segment-a duine od skoro 64K se ne koristi. E, pa zato ne bismo to iskoristili ? Kada smo u DX stavili offset buffer-a tj. istoimene labele, u DX smo u sutini stavili broj 100h+duina_naeg_fajla, to oigledno predstavlja offset prvog bajta CODE segment-a nakon koda tj. instrukcija naeg programa. I super - i vuk sit, i sve ovce na broju - dobili smo na bafer bez rezervisanja istog u okviru samog fajla. Ovime je i sam fajl krai. Naravno, mora paziti na duinu bafera, da sluajno ne bude predugaak pa prebrie sam stek koji se smeta na sam kraj CODE segmant-a. Ostatak programa je, mislim, suvino objanjavati. U narednim programima ja neu ukljuiti kod koji e upati imena fajlova iz komandne linije, a ti ako to eli, sam to i napii. Vratimo se na temu poglavlja - rad sa fajlovima. Uradimo jo jednu stvar sa fajlom. Kako bi saznali ili promenili atribute nekom fajlu ? itanje i Menjanje Atributa Za ovakvu rabotu nam je potrebna funkcija 43h interrapt-a 21h. Funkcija zahteva sledee ulazne podatke: -U registru AH broj funkcije tj. 43h -U registru AL ta da radi 0 - isitae atribute fajla i vratiti ih u CX 1 - promenie atribute fajla na osnovi sadraja CX -U CX koje atribute da postavi Ŀ CX: 543210 read-only atribut hidden atribut system atribut ne upotrebljavaju se u ovoj funkciji archive atribut -DS:DX treba da pokazuje na ASCIIZ ime fajla Posle poziva interrapt-u : Ako je dolo do greke CF je postavljen i u AX e se nai kod greke. U suprotnom, ako je u AL bila nula u CX e se nai atributi fajla. Na primer, promenimo naem fajlu TEST.TXT atribute u read-only, i hidden. Program No. 6.8 ; PROGRAM menjaj_atribute_fajlu (MAF.ASM) ; Copyright(C) 1998. by Shang Tsung .MODEL tiny .CODE ORG 100h main: jmp pocetak ime_fajla db "test.txt",0 greska db "Dogodila se greska !$" pocetak: mov ax,4301h ; U AH broj 43h , u AL broj 1 mov dx,offset ime_fajla mov cx,11b ; Postaviemo poslednja 2 bita int 21h JNC kraj mov ah,9h ; mov dx,offset greska ; Poruka o greci int 21h ; kraj: ret ; Kraj programa end main Mislim da su objanjenja suvina. Poto sam ve nekoliko puta pominjao greke koje se mogu dogoditi u toku izvravanja programa, evo i liste greaka : Ŀ GREKA OPIS GREKE Ĵ 01 Invalid function number 02 File not found 03 Path not found 04 Too many open files (no handles left) 05 Access denied 06 Invalid handle 07 Memory control blocks destroyed 08 Insufficient memory 09 Invalid memory block address 0A Invalid environment 0B Invalid format 0C Invalid access mode (open mode is invalid) 0D Invalid data 0E Reserved 0F Invalid drive specified 10 Attempt to remove current directory 11 Not same device 12 No more files 13 Attempt to write on a write-protected diskette 14 Unknown unit 15 Drive not ready 16 Unknown command 17 CRC error 18 Bad request structure length 19 Seek error 1A Unknown media type 1B Sector not found 1C Printer out of paper 1D Write fault 1E Read fault 1F General failure 20 Sharing violation 21 Lock violation 22 Invalid disk change 23 FCB unavailable 24 Sharing buffer overflow 25 Reserved 26 Unable to complete file operation (DOS 4.x) 27-31 Reserved 32 Network request not supported 33 Remote computer not listening 34 Duplicate name on network 35 Network name not found 36 Network busy 37 Network device no longer exists 38 NetBIOS command limit exceeded 39 Network adapter error 3A Incorrect network response 3B Unexpected network error 3C Incompatible remote adapter 3D Print queue full 3E No space for print file 3F Print file deleted 40 Network name deleted 41 Access denied 42 Network device type incorrect 43 Network name not found 44 Network name limit exceeded 45 NetBIOS session limit exceeded 46 Temporarily paused 47 Network request not accepted 48 Print or disk redirection is paused 49-4F Reserved 50 File already exists 51 Reserved 52 Cannot make directory entry 53 Fail on INT 24 54 Too many redirections 55 Duplicate redirection 56 Invalid password 57 Invalid parameter 58 Network device fault 59 Function not supported by network (DOS 4.x) 5A Required system component not installed (DOS 4.x) UH. Ovo je bilo dugako. Ovo poglavlje je bilo prilino obilno informacijama i novitetima pa u stoga da ti zadam par zadataka. Zadatak 1 : Napisati program KODIRAJ.ASM koji e fajl TEST.TXT ifrovati korienjem sledeeg algoritma : 1. karakter u fajlu sabrati sa 1 (ASCII) 2. karakter u fajlu sabrati sa -1 3. karakter u fajlu sabrati sa 2 4. karakter u fajlu sabrati sa -2 5. karakter u fajlu sabrati sa 3 6. karakter u fajlu sabrati sa -3 7. karakter u fajlu sabrati sa 4 8. karakter u fajlu sabrati sa -4 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 499. karakter u fajlu sabrati sa 250 500. karakter u fajlu sabrati sa -250 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 509. karakter u fajlu sabrati sa 255 510. karakter u fajlu sabrati sa -255 511. karakter u fajlu sabrati sa 0 512. karakter u fajlu sabrati sa 0 513. karakter u fajlu sabrati sa 1 514. karakter u fajlu sabrati sa -1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1999. karakter u fajlu sabrati sa 250 2000. karakter u fajlu sabrati sa -250 Postupak ponoviti za sve blokove ukljuujui i poslednji blok koji naravno moe sadrati manje od 2000 bajta. Koristiti 8-o bitne registre pri operaciji sabiranja. Na taj nain e se posle izvesnog vremena poeti ponavljati sabirci od poetka ( jer u 8-o bitni registar se ne moe upisati broj vei od 255. Ako je u AL bio npr. broj 255, i onda uradimo INC al, nova vrednost registra AL nee biti 256, nego 0, kao to je i prikazano ). Nakon ifrovanja, blokovi od po 100 bajta treba da se izvrnu (RIKVERC.ASM), s tim da se, ako bude ostao blok koji sadri manje od 100 bajtova, ti bajtovi ne diraju. Znai, ako je fajl duine 250 bajta, posle kodiranja gore navedenim algoritmom, prvih 100 bajta e se izvrnuti, kao i sledeih 100 bajta, dok e ostatak od 50 bajta ostati nepromenjen(bar po pitanju izvrtanja). Buffer u koji e uitavati podatke neka bude 2000 bajta. Takoe napisati i program DKODIRAJ.ASM koji e takav fajl da dekodira. U sluaju greke tampati prigodnu poruku. *Pokuaj program da napie tako to e program primati kao parametar ime fajla (ovde to nije uraeno). Zadatak 2 : Napisati program SYSTEM.ASM koji e u fajl SYSTEM.RPT ispisati konfiguraciju sistema, tj. sadraje fajlova AUTOEXEC.BAT, i CONFIG.SYS uz prigodnu poruku o greci. Npr. sadraj fajla SYSTEM.RPT je Vas AUTOEXEC.BAT izgleda ovako : - - - - - - - - - - Nemate CONFIG.SYS fajl !. Na kraju, dodeliti atribute system, hidden i read-only fajlu SYSTEM.RPT. Ukoliko fajl SYSTEM.RPT ve postoji, uz prigodnu poruku izai iz programa ne radei nita. Uzeti da se oba fajla nalaze na C:\. Ne znam da li da ponovim jo jednom. POKUAJ DA OVE PROGRAME SAMOSTALNO NAPIE. NIKADA SE NEE OSAMOSTALITI AKO STALNO GLEDA REENjA. Sreno !!! :) REENjA : Reenje Zadatka 1 ; PROGRAM zadatak_1_poglavlje_6__KODIRAJ (KODIRAJ.ASM) ; Copyright(C) 1998. by Shang Tsung .486 ; Ovde ukljuujemo 486 instrukcije ; jer (zbog veliine fajla) odreene ; J... naredbe izlaze iz intervala ; [-128,127] a 486 instrukcije mogu ; proslediti vee intervale takvim ; naredbama. ; Ovo se moglo obii i instrukcijom ; JUMPS pre samog koda, pa e se sam ; kompajler pobrinuti za to na nain ; koji je ve opisan. CODE SEGMENT PARA PUBLIC USE16 'CODE' ASSUME cs:code, ds:code, es:code ; Ovde je upotrebljena kompletna ; deklaracija segmenta. Ako nisi shvatio ; svu priu iz 4-og poglavlja, umesto ; ova dva reda unesi ; .MODEL tiny ; .CODE ; i ukloni "CODE ENDS" sa kraja progr ORG 100h ; Modifikujemo "Location Counter" main: mov ax,3d02h ; Otvaramo fajl mov dx,offset ime_fajla ; i za itanje i za upis int 21h ; JC greska ; Provera greke mov [handle],ax ; uvamo Handle fajla sledeci_blok: mov cx,DUZINA_BUFFERA ; Maksimalan broj karaktera koliko itamo mov bx,[handle] ; U BX e se nalaziti Handle fajla mov dx,offset buffer ; Proitani podaci upisuju se u bafer mov ah,3fh ; itamo blok podataka iz fajla int 21h ; u memoriju mov [procitano],ax ; uvamo broj proitanih bajtova cmp [procitano],0 ; Ako je proitano 0 bajta znai da smo je kraj ; stigli do kraja fajla i okonavamo ; program. ; Korak 1 : Kodiranje podataka prema algoritmu ; Ovde znamo da je proitan bar jedan bajt iz fajla mov cx,[procitano] ; broj proitanih karaktera upisujemo u CX mov al,1 ; Poetna vrednost sabirka, odnosno ; umanjioca mov di,0 ; DI e nam sluiti za indeksiranje niza ; odnosno kao pointer na odreeni karakter ; u nizu. Inicijalna vrednost je 0. kodiraj_sledeci_karakter: add [buffer+di],al ; Sabiramo karakter u nizu brojem prema ; algoritmu (sabiraju se karakteri sa ; indeksima 0,2,4,6 ...) cmp cx,1 ; proveravamo da li smo doli do kraja ; niza (u sluaju da je broj proitanih ; bajtova neparan). Ako jesmo, prelazimo je kodiranje_gotovo ; na sledei korak. sub [buffer+di+1],al ; Oduzimamo karakter u nizu brojem prema ; algoritmu inc al ; Podeavamo sabirak, odnosno umanjilac add di,2 ; pomeramo index u nizu na sledea 2 ; karaktera sub cx,2 ; Smanjujemo broja za 2 cmp cx,0 ; Proveravamo da li smo doli do kraja JA kodiraj_sledeci_karakter ; proitanog niza. Ako jesmo, prelazimo ; na sledei korak. ; izvrtanje blokova od po 100 bajta kodiranje_gotovo: mov ax,[procitano] ; u AX upisujemo broj proitanih bajtova cmp ax,100 ; Proveravamo da li preostali blok (ceo jb manje_od_100 ; blok pri prvom prolazu) ima 100 bajta. ; Ako nema, ne diramo ga. mov si,0 ; SI koristimo za indeksiranje niza sa ; njegove "leve" strane mov di,100 ; DI koristimo za indeksiranje niza sa ; njegove "desne" strane rikverc: push si di ; uvamo SI i DI registre na steku izvrni_karaktere: mov bh,[buffer+si] ; U BH upisujemo ASCII kod karaktera sa ; "leve" strane niza mov bl,[buffer+di-1] ; U BL upisujemo ASCII kod karaktera sa ; "desne" strane niza mov [buffer+si],bl ; Na mesto karaktera sa "leve" strane niza ; upisujemo karakter sa "desne" strane ; niza mov [buffer+di-1],bh ; Na mesto karaktera sa "desne" strane ; niza upisujemo karakter sa "leve" strane ; niza inc si ; Uveavamo pokaziva na "levu" stranu ; niza dec di ; Umanjujemo pokaziva na "desnu" stranu ; niza cmp si,di ; Proveravamo da li su se "leva" i "desna" ; strana niza susrele JB izvrni_karaktere ; Ako nisu, izvremo sledei par karaktera sub ax,100 ; Umanjujemo duinu niza za 100 bajta pop di si ; Vraamo stare pokazivae na niz add si,100 ; Pomeramo se na sledeih add di,100 ; 100 bajta u nizu cmp di,[procitano] ; Proveravamo da li je indeks koji ; pokazuje na "desnu" stranu niza preao ja manje_od_100 ; njegov kraj. Ako jeste, zavrili smo ; obrtanje blokova. U suprotnom akcija ; se nastavlja. jmp rikverc ; Izvremo sledei blok od 100 bajta manje_od_100: mov ax,4200h ; Vraamo pointer u fajlu na poetak mov bx,[handle] ; prethodno proitanog bloka da bi smo mov dx,word ptr [pozicija] ; preko tog dela fajl upisali kodirane mov cx,word ptr [pozicija+2] ; podatke int 21h ; mov cx,[procitano] ; Broj proitanih bajta upisujemo u CX mov ah,40h ; Upisujemo preko starih vrednosti u mov bx,[handle] ; fajlu kodirane podatke mov dx,offset buffer ; int 21h ; JC greska ; Proveravamo da li je dolo do greke mov ax,4201h ; Pomeramo se od trenutne pozicije u fajlu mov bx,[handle] ; za 0 bajta i time u DX:CX dobijamo xor cx,cx ; trenutnu vrednost file pointera xor dx,dx ; int 21h ; mov word ptr [pozicija],ax ; uvamo vredost trenutnog file pointera mov word ptr [pozicija+2],dx ; u varijablu jmp sledeci_blok ; itamo sledei blok podataka greska: mov ah,9h ; tampamo jednostavnu mov dx,offset poruka_greska ; poruku o greci int 21h ; jmp totalan_kraj ; Nasilno zavravamo program jer se ; dogodila greka u njegvom izvravanju kraj: mov ah,3eh ; mov bx,[handle] ; Zatvaramo fajl int 21h ; JC greska ; Proveravamo da li je dolo do greke totalan_kraj: mov ax,4c00h ; Izlaz iz programa int 21h ; ; VARIJABLE PROGRAMA DUZINA_BUFFERA EQU 2000 ime_fajla db "test.txt",0 ; Ima fajla koji kodiramo handle dw 0 ; Varijabla u kojoj uvamo Handle fajla procitano dw 0 ; Varijabla koja sadri broj proitanih ; bajtova pozicija dd 0 ; Varijabla koja sadri lokaciju u okviru ; fajla od koje je uitan blok podataka poruka_greska db "Dogodila se greska!$" ; Poruka o greci buffer db DUZINA_BUFFERA dup (?); Bafer u koji uitavamo podatke CODE ENDS ; Kraj CODE segmenta end main ; Kraj programa. Program koji e da dekodira fajl... U sutini isti kao i predhodni, sa samo par izmena. ; PROGRAM zadatak_1_poglavlje_6__DEKODIRAJ (DKODIRAJ.ASM) ; Copyright(C) 1998. by Shang Tsung JUMPS ; Kompajler se brine o J.. naredbama koje ; izlaze iz intervala [-128,127] .MODEL tiny .CODE ORG 100h ; Modifikujemo "Location Counter" main: mov ax,3d02h ; Otvaramo fajl mov dx,offset ime_fajla ; i za itanje i za upis int 21h ; JC greska ; Provera greke mov [handle],ax ; uvamo Handle fajla sledeci_blok: mov cx,DUZINA_BUFFERA ; Maksimalan broj karaktera koliko itamo mov bx,[handle] ; U BX e se nalaziti Handle fajla mov dx,offset buffer ; Proitani podaci upisuju se u bafer mov ah,3fh ; itamo blok podataka iz fajla int 21h ; u memoriju mov [procitano],ax ; uvamo broj proitanih bajtova cmp [procitano],0 ; Ako je proitano 0 bajta znai da smo je kraj ; stigli do kraja fajla i okonavamo ; program. ; Korak 1 : Sada prvo izvremo blokove od po 100 bajta mov ax,[procitano] ; u AX upisujemo broj proitanih bajtova cmp ax,100 ; Proveravamo da li preostali blok (ceo jb manje_od_100 ; blok pri prvom prolazu) ima 100 bajta. ; Ako nema, ne diramo ga. mov si,0 ; SI koristimo za indeksiranje niza sa ; njegove "leve" strane mov di,100 ; DI koristimo za indeksiranje niza sa ; njegove "desne" strane rikverc: push si di ; uvamo SI i DI registre na steku izvrni_karaktere: mov bh,[buffer+si] ; U BH upisujemo ASCII kod karaktera sa ; "leve" strane niza mov bl,[buffer+di-1] ; U BL upisujemo ASCII kod karaktera sa ; "desne" strane niza mov [buffer+si],bl ; Na mesto karaktera sa "leve" strane niza ; upisujemo karakter sa "desne" strane ; niza mov [buffer+di-1],bh ; Na mesto karaktera sa "desne" strane ; niza upisujemo karakter sa "leve" strane ; niza inc si ; Uveavamo pokaziva na "levu" stranu ; niza dec di ; Umanjujemo pokaziva na "desnu" stranu ; niza cmp si,di ; Proveravamo da li su se "leva" i "desna" ; strana niza susrele JB izvrni_karaktere ; Ako nisu, izvremo sledei par karaktera sub ax,100 ; Umanjujemo duinu niza za 100 bajta pop di si ; Vraamo stare pokazivae na niz add si,100 ; Pomeramo se na sledeih add di,100 ; 100 bajta u nizu cmp di,[procitano] ; Proveravamo da li je indeks koji ; pokazuje na "desnu" stranu niza preao ja manje_od_100 ; njegov kraj. Ako jeste, zavrili smo ; obrtanje blokova. U suprotnom akcija ; se nastavlja. jmp rikverc ; Izvremo sledei blok od 100 bajta manje_od_100: ; Korak 2 : I na kraju dekodiramo podatke prema algoritmu mov cx,[procitano] ; broj proitanih karaktera upisujemo u CX mov al,1 ; Poetna vrednost sabirka, odnosno ; umanjioca mov di,0 ; DI e nam sluiti za indeksiranje niza ; odnosno kao pointer na odreeni karakter ; u nizu. Inicijalna vrednost je 0. kodiraj_sledeci_karakter: sub [buffer+di],al ; Sabiramo karakter u nizu brojem prema ; algoritmu (sabiraju se karakteri sa ; indeksima 0,2,4,6 ...) cmp cx,1 ; proveravamo da li smo doli do kraja ; niza (u sluaju da je broj proitanih ; bajtova neparan). Ako jesmo, prelazimo je kodiranje_gotovo ; na sledei korak. add [buffer+di+1],al ; Oduzimamo karakter u nizu brojem prema ; algoritmu inc al ; Podeavamo sabirak, odnosno umanjilac add di,2 ; pomeramo index u nizu na sledea 2 ; karaktera sub cx,2 ; Smanjujemo broja za 2 cmp cx,0 ; Proveravamo da li smo doli do kraja JA kodiraj_sledeci_karakter ; proitanog niza. Ako jesmo, prelazimo ; na sledei korak. kodiranje_gotovo: mov ax,4200h ; Vraamo pointer u fajlu na poetak mov bx,[handle] ; prethodno proitanog bloka da bi smo mov dx,word ptr [pozicija] ; preko tog dela fajl upisali kodirane mov cx,word ptr [pozicija+2] ; podatke int 21h ; mov cx,[procitano] ; Broj proitanih bajta upisujemo u CX mov ah,40h ; Upisujemo preko starih vrednosti u mov bx,[handle] ; fajlu kodirane podatke mov dx,offset buffer ; int 21h ; JC greska ; Proveravamo da li je dolo do greke mov ax,4201h ; Pomeramo se od trenutne pozicije u fajlu mov bx,[handle] ; za 0 bajta i time u DX:CX dobijamo xor cx,cx ; trenutnu vrednost file pointera xor dx,dx ; int 21h ; mov word ptr [pozicija],ax ; uvamo vredost trenutnog file pointera mov word ptr [pozicija+2],dx ; u varijablu jmp sledeci_blok ; itamo sledei blok podataka greska: mov ah,9h ; tampamo jednostavnu mov dx,offset poruka_greska ; poruku o greci int 21h ; jmp totalan_kraj ; Nasilno zavravamo program jer se ; dogodila greka u njegvom izvravanju kraj: mov ah,3eh ; mov bx,[handle] ; Zatvaramo fajl int 21h ; JC greska ; Proveravamo da li je dolo do greke totalan_kraj: mov ax,4c00h ; Izlaz iz programa int 21h ; ; VARIJABLE PROGRAMA DUZINA_BUFFERA EQU 2000 ime_fajla db "test.txt",0 ; Ima fajla koji kodiramo handle dw 0 ; Varijabla u kojoj uvamo Handle fajla procitano dw 0 ; Varijabla koja sadri broj proitanih ; bajtova pozicija dd 0 ; Varijabla koja sadri lokaciju u okviru ; fajla od koje je uitan blok podataka poruka_greska db "Dogodila se greska!$" ; Poruka o greci buffer db DUZINA_BUFFERA dup (?); Bafer u koji uitavamo podatke end main ; Kraj programa. Nadam se da ti nije bilo teko da sam uradi ovaj program. Iako ovaj program moda zasluuje nekoliko objanjenja, ja ih neu navesti. Morae da zagreje stolicu i sam ukapira kako ovo radi. Reenje Zadatka 2 ; PROGRAM zadatak_2_poglavlje_6_backup_sistemske_konfiguracije (SYSTEM.ASM) ; Copyright(C) 1998. by Shang Tsung .MODEL tiny .486 ; Ukljuujemo 486 instrukcije .CODE ORG 100h MAIN: mov ax,3d00h ; Proveravamo da li fajl SYSTEM.RPT mov dx,offset ime_output ; ve postoji, tako to pokuavamo da int 21h ; ga otvorimo. Ako uspemo, znai JNC greska_1 ; postoji. U suprotnom, tampamo ; prigodnu poruku. mov ah,3ch ; Kreiramo fajl pod imenom mov cl,0 ; SYSTEM.RPT mov dx,offset ime_output ; int 21h ; JC greska_2 ; U sluaju greke tampamo prigodnu ; poruku mov [handle_output],ax ; uvamo Handle novokreiranog fajla mov [indikator_fajla],AUTOEXEC ; Indikator fajla mov ax,3d00h ; Otvaramo fajl AUTOEXEC.BAT samo za mov dx,offset ime_1 ; itanje int 21h ; JC nema_inputa ; ako ne moemo pozivamo proceduru ; "nema_inputa" mov [handle_input],ax ; uvamo Handle otvorenog fajla call upisi_u_output ; Upisivanje sadraja input fajla u ; output fajl fajl_config_sys: mov ah,3eh ; Zatvaramo AUTOEXEC.BAT mov bx,[handle_input] ; i prelazimo na CONFIG.SYS int 21h ; nije_bilo_autoexeca: mov [indikator_fajla],CONFIG ; indikator fajla mov ax,3d00h ; Otvaramo fajl CONFIG.SYS samo za mov dx,offset ime_2 ; itanje int 21h ; JC nema_inputa ; ako ne moemo pozivamo proceduru ; nema_inputa mov [handle_input],ax ; uvamo Handle otvorenog fajla call upisi_u_output ; Upisivanje sadraja input fajla u ; output fajl greska_1: mov bx,ax ; Zatvaramo fajl SYSTEM.RPT mov ah,3eh ; koji smo otvorili pri proveri int 21h ; da li on uopte postoji mov ah,9 ; Na ekran tampamo poruku da mov dx,offset fajl_vec_postoji ; fajl SYSTEM.RPT ve postoji int 21h ; jmp totalan_kraj ; Nasilno okonavamo izvravanje ; fajla greska_2: mov ah,9 ; tampamo na ekran poruku mov dx,offset greska_kreiranje ; da nismo uspeli int 21h ; da kreiramo fajl SYSTEM.RPT jmp totalan_kraj ; Nasilno okonavamo izvravanje ; fajla kraj: mov ah,3eh ; Zatvaramo input fajl, koji god mov bx,[handle_input] ; on bio int 21h ; mov ah,3eh ; Zatvaramo output fajl SYSTEM.RPT mov bx,[handle_output] ; int 21h ; mov ax,4301h ; Dodeljujemo atribute mov cx,111b ; READ ONLY, HIDDEN, i SYSTEM mov dx,offset ime_output ; fajlu SYSTEM.RPT int 21h ; totalan_kraj: mov ax,4c00h ; Povratak u DOS int 21h ; ;********************************************************************* nema_inputa PROC cmp [indikator_fajla],CONFIG ; Proveravamo koji je fajl u pitanju je config_fajl mov ah,40h ; Ispisuje mov bx,[handle_output] ; 'Nemate AUTOEXEC.BAT fajl !' mov cx,DUZINA_NEMA_AUTOEXEC ; u output mov dx,offset nema_fajla_1 ; int 21h ; jmp gotovo config_fajl: mov ah,40h ; Ispisuje mov bx,[handle_output] ; 'Nemate CONFIG.SYS fajl !' mov cx,DUZINA_NEMA_CONFIG ; u output mov dx,offset nema_fajla_2 ; int 21h ; gotovo: cmp [indikator_fajla],AUTOEXEC ; Proveravamo koji je fajl u pitanju, je nije_bilo_autoexeca ; i potom radimo ono to treba jmp kraj ; nema_inputa ENDP ;********************************************************** upisi_u_output PROC cmp [indikator_fajla],AUTOEXEC ; Proveravamo koji je je autoexec_fajl ; fajl u pitanju mov ah,40h ; Ispisuje mov bx,[handle_output] ; 'Vas CONFIG.SYS izgleda ovako :' mov dx,offset vas_config ; u output mov cx,DUZINA_VAS_CONFIG ; int 21h ; jmp sledeci_blok autoexec_fajl: mov ah,40h ; Ispisuje mov bx,[handle_output] ; 'Vas AUTOEXEC.BAT izgleda ovako :' mov dx,offset vas_autoexec ; u output mov cx,DUZINA_VAS_AUTOEXEC ; int 21h ; sledeci_blok: mov ah,3fh ; mov cx,DUZINA_BUFFERA ; itamo 100 bajtova mov bx,[handle_input] ; iz input-a mov dx,offset buffer ; int 21h ; mov [procitano],ax ; uvamo broj proitanoh bajtova cmp ax,0 ; Proveravamo da li je sve je kraj_fajla ; isitano mov ah,40h ; mov bx,[handle_output] ; U output mov dx,offset buffer ; upisujemo mov cx,[procitano] ; proitane podatke int 21h ; jmp sledeci_blok ; itamo sledei blok iz input-a kraj_fajla: cmp [indikator_fajla],1 ; Proveravamo koji je fajl u pitanju. je fajl_config_sys ; Ako je AUTOEXEC.BAT onda treba ; isitati i CONFIG.SYS, ako nije - jmp kraj ; kraj. upisi_u_output ENDP ;************************************************************** ; PROGRAMSKE VARIJABLE DUZINA_BUFFERA EQU 100 AUTOEXEC EQU 1 CONFIG EQU 2 handle_output dw 0 handle_input dw 0 procitano dw 0 indikator_fajla db 0 ime_1 db "C:\AUTOEXEC.BAT",0 ime_2 db "C:\CONFIG.SYS",0 ime_output db "SYSTEM.RPT",0 nema_fajla_1 db "Nemate AUTOEXEC.BAT fajl !",13,10 DUZINA_NEMA_AUTOEXEC EQU $-nema_fajla_1 nema_fajla_2 db 13,10,"Nemate CONFIG.SYS fajl !" DUZINA_NEMA_CONFIG EQU $-nema_fajla_2 vas_autoexec db "Vas AUTOEXEC.BAT izgleda ovako :",13,10,13,10 DUZINA_VAS_AUTOEXEC EQU $-vas_autoexec vas_config db 13,10,"Vas CONFIG.SYS izgleda ovako :",13,10,13,10 DUZINA_VAS_CONFIG EQU $-vas_config fajl_vec_postoji db "Fajl SYSTEM.RPT vec postoji !$" greska_kreiranje db "Ne mogu da kreiram fajl SYSTEM.RPT !$" buffer db DUZINA_BUFFERA dup (?) end main Nadam se da ti nije bilo teko da sam uradi ovaj program. Kao i u predhodnom zadatku - ni ovde ne nameravam nita da objanjavam. Samo ti je potreban mozak. To bi bilo sve o radu sa fajlovima u ovom izdanju UHFA. THE END **************************************************************************** * Zahvaljujem se : * * * * prof. Milanu Vidakoviu na korisnim savetima i prilozima u cilju da * * ovo izdanje UHFA bude to kvalitetnije * * * * I posebna zahvalnica * * * * prof. Radivoju Stojkoviu na svoj pomoi i podrci * * * **************************************************************************** Novi Sad, 1998-1999 by Shang Tsung