Assembler' a Giriş - II
Kesmeler (Interrupts)
Kesmeler özellikle gerçek modda çok önemli olan bir konudur. Ancak korumalı modda da en düşük seviyede kesmeler kullanılmaktadır. Kesmeler programcılar açısından kısaca sistemin bize sağladığı fonksiyonlardır. Sistem açısından ise, kullanılan işletim sistemi ile bilgisayar arasındaki kontrollü çalışmayı sağlama ve gerektiği kadar paylaşımı oluşturmadır. Buradaki paylaşım sistemin ve işletim sistemimin kaynaklarının paylaştırılması. Örneğin bir tuşa basılıp basılmadığını ve hangi tuşa basıldığını öğrenmek için sistem bize sadece klavye konusunda bir kesme vermiştir. Bunu kullanarak program içerisinden hangi tuşa basıldığını öğrenebiliriz. Yada hangi ekran modunda olduğumuzu yine kesmeler vasıtasıyla öğrenebiliriz. Kesmelerin olmadığı bir program düşünürsek o zaman zor ve çok yüksek bir bilgi birikimi gerektiren "donanım programlaması" yapılması gerekir.
Kesmelerin gerçek modda "kesme tablosu" olarak adlandırılan 0:0 adresniden başlayan bir tablosu vardır. Bu tabloda kesmeler 4 byte aralıkla birbirlerini takip ederler. Bunun nedeni gerçek modda 64kb ' lık segment - offset sistemidir. 16 bitlik segment adresi önce, 16 bitlik offset adreside sonra takip eder. Bu kesme vektörleri değiştirilerek "geridönüm" (callback) yapmak şartıyla yönlendirilebilirler (Hook). Bundan yararlanarak ortaya TSR (terminate and stay resident -sonlan ve kal-) ortaya çıkmıştır. Sistemin istenilen bir fonksiyonu alınarak filitre edilebilir ve o fonksiyon kullanıldığında istenilen program parçacığı çalıştırılabilir. Ayrıca sistemin debug için kesme 1 ve kesme 3 olarak ayırdığı iki kesmede mevcuttur. EFLAG bayrak registerininin TR biti (trap biti = kapan veya debug biti) set edilince CPU her komuttan sonra kesme 1 ' i çağırır. Böylece debug etme işlemi yapılmış olur.
Bayraklar ( Flaglar)
Bayrak komutları ve olayları deyince aklımıza hep sayıları oluşturan bit'ler gelmeli. Basit olarak iki sayının karşılaştırılmasında ortaya o sayının aynı olduğu, aynı olmadığı, büyük veya küçük olması yada sıfır olması gibi durumlar çıkar. Bu durumları öğrenmenin tek yolu o duruma göre şekil alan bayrak register (f) bitlerinin durumunu öğrenmemizdir. Bir test veya bir matematiksel işlem olduğu durumlarda sonuclar daima bayrak registerin ilgili bitlerine yansır.
Bayrak register gerçek modda 16 bit, korumalı modda ise 32 bitliktir. Yani gerçek modda toplam 16 bit tane sebeb-sonuç biti ayrılmış aynı şekilde korumalı modda 32 tane sebeb-sonuç biti ayrılmıştır.32 bitlik bayrak registere EFlag adı verilir ve aynı 16 bitlik şekli gibi sadece ilk 16 biti kullanılır.(Bazı bitler dökümante edilmemiştir). EFlag registerin sonraki 16 biti ileride yapılacak CPU güncellemeri için ayrılmıştır.En başlıca olanları ZF (zero flag), CF (carry flag) ,SF, AF, gibidir. Örneğin bir işlem sonucu 0 ise ZF set olur yani 1 olur.
Bayraklar işlevleri açısından 3 gruba ayrılılar. Status flaglar (durum bayrağı), Control flaglar (kontrol bayrağı) ve, System flaglar (sistem bayrağı). Durum bayrakları programın içindeki komutsal, mantıksal ve matematiksel işlemlerin sonuçlarını verir. Genelde kullanılan bayraklar bunlardır. Kontrol bayrağı sadece 1 tanedir ve ileride göreceğimiz repz komutunun aşağı yada yukarı işlem yapma şeklini ayarlar. Sistem bayrakları ise sistemle ilgili işlemler için kullanılır. Genelde bunları işletim sistemi yada kullanacağımız debugger gibi yüksek düzeyli bir program değiştirir.
Bayrak registerin tam yapısı aşağıdaki gibidir:
0 1 2 3 4 5 6 7 8 9 A B C D E F 10 11-31
CF ? PF ? AF ZF SF TF IF DF OV ? IO ? NTF RF V86 -
Komut Noktası registeri (IP)
Bu register çalışmaktan olan sistemin yada programın o anda hangi adreste olduğunu komutun başlangıç noktasından itibaren belirtir. Örneğin bir programı debug ederken her F8 (trace) tuşuna basınca o komut işletip bir alt komuta geçer. İşte o anda IP register o komutun işlemesi ile sonraki komutun başına gelmiş olur. Böyle kod zinciri sağlanmış olur. Bunu CPU otomatik olarak kendisi düzenler. Yine moda göre uzunluğu ve ismi değişir, gerçek mod daha önce anlattığım gibi segment:offset sistemine sahip olduğu için IP register 16 bit olur. Yani sadece offseti gösterir. Ancak korumalı modda bütün 32bit olan registerin başına aldığı 'E' (Extended) harfini alarak EIP olur. Bu registeri direkt olarak MOV EAX,EIP şeklinde elde edemiyoruz. Çünkü bu registerin değişmesi yada farklı bir değer alması sistemin çalışma bütünlüğünü yok eder ve sistemde hatalarına neden olur. Bunu bir su yoluna benzeterek örnekleyebilirim. Örneğin bir nehrin suyunun rasgele başka bir yere yönlendirildiğini düşünelim. O zaman istenmeyen su baskınları ve ekinleri- evlerin zarar görmesine hatta ölümlere neden olacaktır. Yinede sistem debug API' leri ile veya aşağıdaki gibi basit bir yöntemle o andaki IP noktası bulunabilir.
call delta
delta:
pop eax
sub eax,5
Burada ileride göreceğimiz CALL komutunu bulunduğu yeri yığın içine atmasından yararlanıyoruz. SUB EAX,5 ile o zamana kadar işletilen kodun uzunluğunu çıkarıyoruz ve sonuçta elimize baştaki komutun çağrıldığı IP noktası kalıyor.
IO Portları
Donamın bize temel işlemler ve sistemi bütün kaynakların kullanmak için kaynak paylaşımını ve erişimini sağlayan erişim kanallaı vermiştir. Aslında sistemdeki ek aygıtlar (ekran kartı, ses kartı..) hepsi sistemler bu portlar vasıtasıyla haberleşmekte ve işlemleri yerine getirmektedir. Bunlara ilaveten daha önce bahsettiğim kesmeler tamamlayıcı bir roldedir. Donamın portları normal programlarda kullanılması hem karışık hemde zor olduğu için genelde çok yüksek düzeyli uygulamalarda direkt olarak kullanılırlar. DOS altında korumalı modda 8 ve 16 bitlik değerlerle istenildiği gibi kullanılabilir ancak korumalı modda en yüksek hassasiyet değerinde (ring 0) olma zorunluğu vardır. Gerçek modda daha doğrusu DOS ortamında bir *.com programı dışarıdan hiç kesme, adres kullanmadan kendi başına bir sistem programı oluşturabilir. Ancak korumalı modda bu pek mümkün değildir. Bu işletim sisteminin istediği özelliklerden kaynaklanır. Bir *.com dosyası salt makina kodudur ve uygun bir programlama ile sistemin her yerinde çalışabilir (örneğin virusler) Ancak bir windows programı IO programlarının kullanılma kısıtlılığı ve PE dosya yapısı nedeniyle bunu pekde başaramaz. Burada IO portlarının derin bir işlevlik gücü ortaya çıkıyor.
IO portları için 8,16 ve 32 bitlik özel opkodlar tahsis edilmiştir. Genelde asıl port numarasından önce status port denilen ve ön değerlerin gönderilip - alınan veya kontrol edilen port(lar) vardır. Hatta bazı donanım sistemlerinde bu portları 6-7 taneye kadar çıkarak konbine çalışmayı gerektirir.
Örnek vermek gerekirse sistenm CMOS hafızasını okumak için 70h portu kullanılır. Burada CMOS'un 15 ve 16. byteları okunarak hafıza uzunluğu öğreniliyor.
mov ax,1516
out 70h,al
in al,71h
mov byte ptr [hafıza_low],al
mov al,ah
out 70h,al
in al,71h
mov byte ptr [hafıza_high],al
ret
Bu örneği portlar kullanılmı hakkında daha iyi fikir edinilmesi için verdim. Normalde sistem kesmeleri, API kütüphaneleri ile bu işlem rahatça elde edilebilir. Fakat burada söyle bir fark var. Sistem kesmeleri (interruptlar) ve Windows API kütüphaneleri vectörleri kullanıcı tarafından ele geçirilerek (hook ) istenildiği gibi kısıtlama yada değiştirilme olanağına sahiptir. Ancak IO portları için böyle bir ele geçirme (hook) mümkün değildir. Bunlar tamamen donanıma ait portlardır.
Assembler komutları
Bu kadar genel ve ön bilgi sonrası artık komutları tanımaya başlayabiliriz. Assembler komutları genel amaçlı komutlar, mantıksal komutlar, sistem komutları, gibi bölümlere ayrılmaktadır.
Daha önce söz ettiğimiz MOV, LEA komutları registerleri yükleme, adresleme veya değerini bir yere yazdırma için kullanılır. Genel kullanılış şekli : MOV register, değer veya MOV [adres],register şeklindedir. Bu komut birden fazla registeri bir arada kullanmayada olanak verir. Örneğin : MOV register, [register+register] gibi.
mov register,[adres]
mov [adres],register
mov [adres+register],register
mov register, [adres+register]
mov register, [register+register]
gibi burada sadece registerler arasında toplama değil çıkartma ve çarpma işlemide yapılabilimektedir.
LEA komutu hem registere değer yüklemek için hemde doğrusal bir hafıza adresinin alınması için kullanılır.
lea register, adres
lea register, [adres]
lea register, register*4
lea register, register+10h
gibi kullanılır. İleride uygulamalı örneklerde MOV ile LEA arasındaki daha iyi farkı göreceksiniz.
Ayrıca LES, LDS, LFS, LSS, LGS gibi genelde gerçek modda ve yüksek düzeyli korumalı mod mimarisinde kullanılan register yükleme komutlarıda vardır. Bunlar interrupt ve segment ayarlamalarında kullanılır. Korumalı modda Segment seçici görevini yerine getirir. Şu andaki sistemlerde çok fazla kullanım alanları yoktur.
Buradan sonra çok kullanılan önemli komutları inceleyeceğiz. Bazı komutlar çalıştıktan sonra birden fazla komutu etkileyebilir. Bunların mümkün olduğunca açıklayacağım. Ayrıca mümkün olduğunca her komut için örnek vereceğim.
Registerleri değiştiren, aktaran, ekleyen başka komutlarda vardır.
XCHG KOMUTU
XCHG komutu iki registerin yada registerler bir yerin yer değişimi sağlar. Bu komut 4-5 tane ayrı komut yapılacak bir işleömi tek başına gerçekleştirir. Kullanılışıda çok basittir.
XCHG REG1, REG2
XCHG REG, [ADDR]
XCHG [ADDR],REG
gibi kullanılır. Buradaki tek şart yer değişecek register veya adresin aynı bit uzunluğuna sahip olmasıdır.
XLAT KOMUTU
XLAT komutu esas olarak BX (veya EBX) registeri esas alarak çalışır. Buna göre BX registerle gösterilen bir adresteki bilgiyi almak için AL registeri BX register ile toplar ve gösterdiği noktadaki bilgiyi (8 bitlik) alıp AL register yükler. Bu işlemde sadece AL registerin değeri değişir.
Örneğin:
yazi db 'ABCDEFGH',0
...
mov al,3 ; AL = 3
lea ebx, yazi ; EBX = yazi [ABCDEFG]
xlat ; EBX+AL = [- - *---] AL= 'C' olur
XLAT komutunu bazı zamanlar XLATB olarakda geçer ve bunlar farklı değil ikiside aynıdır.
MOVSX ve MOVZX komutları
Bu komutlarda diğer komutlar gibi kodu kısatmak ve daha hızlı işlem yapmak içindir. MOVZX komutu bir adresdeki 8 bitlik sayıyı 8 bit registere yükler ama 8 bit dışındaki bitleri resetler (sıfırlar) Böylece elimizde 8 bitlik tertemiz bir değer olur. MOVSX komutuda benzer olarak registeri yükler ve diğer bitleri temizler ancak bu işlem 16 bitten 8 bite veya 32 bitten 8 bite olmaktadır.
deger2 db 10
....
mov eax,12345678h ; eax = 12345678
movzx eax, byte ptr [deger2] ; deger2 = 10, eax =10 oldu
Yuıkarıdaki örnekte önceden EAX registere 12345678 gibi 32 bitlik bir değer girdik. [deger2] adresinin 8 bit olduğuna dikkat edin. MOVZX komutndan sonra EAX register sadece 10 değerini alacaktır. Eğer MOVZX kullanmadan normal bir AL register yüklemesi yapsaydık EAX registerin sadece AL kısmı değişecek ve EAX 12345610 değerini alacaktı.
Kombine Veri komutları (LODS, MOVS, CMPS, STOS, SCAS)
Bu komutlar birden fazla registerle, birden fazla kere işlem yapmayı amaçlayan güçlü ve komutlardır. Bunlar tekil olarak yada REPZ, REPNZ, REPNE gibi bu komutlara özel tahsis edilmiş yardımcı komutlar ile beraber çalışır. Hepsi yanına yapacağı bit sayısına göre ek alırlar Bu komut kullacağı bit değerine göre yanına simge alır. Ancak bu bizim tarafımızdan tahsis edilen bir özellik değildir. Bu standart komut setinin ayrı ayrı ayırdığı opkodlardır. Bu opkodlar konunun sonunda göreceğimiz REP komutu ile kullanılışında her defasında yapılacak işlem miktarınıda ayarlar.
B = byte (8 bit) bazında işlem
W= word (16 bit) bazında işlem
D= dword (32 bit) bazda işlem
Öncelikle anlaşılabilmesi için tekil kullanımlarını sonrada sayaçlı kullanımlarını inceleyeceğiz.
LODS komutu
Bu komut yanına aldığı eke göre 8 ile 32 bit arasında işlem gücü kazanır. Temel görevi SI (yada ESI) registerin gösterdiği noktadaki değeri alıp bit değerine göre A (AL, AX, EAX) registere yüklemektir. Ayrıca LODS komutu değeri yükledikten sonra SI registerin değerini kullanılan bit değerine göre arttırarak sonraki veriye göre ayarlamış olur. Bu byte için 1, word için 2 ve dword için 4 byte otomatik olarak toplanacak demektir. Daha önce gördüğünüz XLAT komutuna benziyor olmasna karşın LODSB çok farklıdır. Aşağıda verilen ve diğer örneklerde verilen bit değerleri gerçek modda veya korumalı modda aynıdır. Kullanım ihtiyacına göre hepsi kullanılabilir.
LODSB = 1 byte işlem için (8 bit)
LODSW = 2 byte işlem için (16 bit)
LODSD = 4 byte işlem için (32 bit)
Örnek:
ornekyazi db 'ASM GENEL TUTOR',0
.....
lea esi,ornekyazi ; ornekyazi noktasının başlangıç adresini ESI registere yükledik.
lodsb ; al registere ilk harf olan 'A' harfi yüklendi. ESI register byte olarak kullandığımız için 1 arttı.
Eğer ilk iki harfi alıp ax registere yüklemek isteseydik lodsw kullanacaktık. Yine ilk 4 byte için lodsd kullanacaktık.
MOVS komutu
Bu komutta LODS komut gibi yanına aldığı B,W,D takıları ile ayrı bir opkod halini alır ve buna göre bitte işlem yapar. MOVS komutu DI (veya EDI), SI (veya ESI) ve registereleri ile beraber çalışır. SI kaynaktır yani aktarılacak verini adresini gösterir. DI ise hedeftir gideceği yeri gösterir. Komut sonrası her iki registerde kullanılan bit değerine göre ekleme olur ve yeni yeri gösterirler.
buf1 db 'YAZI',0
buf2 db 0,0,0,0
......
lea esi,buf1 ; buf1 = 'YAZI'
lea edi,buf2 ; buf2= 000000000
movsd
; buf1 = 'YAZI ' ve buf2 = 'YAZI
Bu işlem sonunda buf1 adresindeki veri buf2 bölgesine aktarılır. buf2 bölgesinde bir değişme olmaz, aynı kalır. Burada eğer MOVSB kullanılmış olsaydı sadece ile harf aktarılacaktı. Eğer MOVSW kullanılmış olsa o zaman ilk iki veri aktarılacaktı. Bunlar kullanılan bit değerlerine bağlı.
CMPS Komutu
CMPS komutu ileride göreceğimiz karşılaştırma komutunu kombine bir şeklidir. Bu komut yine tekil olarak diğerleri gibi yanına aldığı ek ile orantılı olarak bit sayısı kadar işlem yapar Yine bu komut SI (veya ESI) ve DI (veya EDI) registerler ile birlikte çalışır.Bu komut tüm karşılaştırma ve test komutları gibi bayrak registeri etkiler. Bu komutu kısaca SI register ile gösterilen bölgeyi DI ile gösterilen bölge ile karşılaştırır diye açıklayabiliriz. Bu işlem tamamen hafıza bölgesi ile ilgilidir. Bu komut ileride göreceğimiz karşılaştıma komutları gibi registerler karşılaştırması yapmaz. Karşılaştırma sonucunu ZF (zero flag) ile alırız. Eğer iki bölgede verdiğimiz bir değeri kadar karşılaştırılması sonucu aynı ise ZF set (1) olur. Eğer yanlış ise ZF reset (0) olur. Bunu nasıl kullabncağımızı ileri şartlı atmala komutlarında göreceğiz. Şimdilik aşağıdaki örnek bunun nasıl kullanıldığı konusunda bir fikir verebilir.
buf1 db 01,02,03,04
buf2 db 02,03,01,04
....
lea esi, buf1 ; asıl bölge
lea edi, buf2 ; karşılaştırılan bölge
cmpsd ; dword (32 bit ) olarak karşılaştırma
jz dogru ; eğer aynı ise "dogru " etiketli yere atlar. Değilse devam eder.
....
STOS Komutu
Bu komut A register (AL, AX EAX) ile DI register (veya EDI) ile beraber çalışır. STOS komutu diğerler gibi yanlarına aldıkları işaret ile opkod eşlenir ve bu eşleşmeye göre işlemde bit seviyesi kullanır. STOS komutu A registerdeki değeri DI registerin gösterdiği bölgeye aktarır.
buf1 db 00,00,00,00
......
lea edi, buf1
mov al,89h
stosb
Bu işlem sonunda stosb kullandığımız için 1 byte (8 bit) bir yazma olacak ve buf1 db 89h,0,0,0 şeklinde olacaktır. Eğer STOSW komutu kullanmış olsaydı 2 byte yazacaktı ve dolayısıyla bu AX register olacaktı. Aynı şeklide STOSD kullansaydık 4 byte olacaktı ve bu EAX register olacaktı.
SCAS Komutu
SCAS komutu aramak - taramak kelimesinin kısaltılmışıdır ve yaptığı işlemde taramadır. Tek başına pek bir işlevi yoktur. Asıl olarak aşağıda göreceğiniz gibi REP tekrarlatıcısı ile güçlü bir arama - tarama işlevi kazanır.
REP komutunu LODS, MOVS, CMPS, STOS ve SCAS ile kullanılışları
REP komutu yukarıda gördüğümüz 4 sınıf komut ile kombine çalışarak onların tek başına yaptıkları işi grup halinde ve istenilen sayıda yapılmasını sağlar. Burada önemli olan sayıdır ve sayıyı CX (veya ECX) register ile belirtiriz. Diğer kullanımlar her komut için yukarıda anlattığım gibi ilgili registerlere ve kullanılan alanlara bağlıdır. REP komutuvir döngü oluşturur ve verilen komutu belirlenen CX (veya ECX) kadar yapar.
REP komutu REPZ, REPE, REPNE olarak döngüyü kontrol altına alma olanağıda vardır. REPZ ile CX (yada ECX) 0 oluncaya kadar devam et anlamındadır. REPE yine CX register eşit oluncaya kadar ve, REPNE ise eşit olmayıncaya kadar anlamındadır. REPZ genelde çok kullanılır ama DF bayrağı değiştirilerek tersine işlem ile diğerleride rahatça kullanılabilir.
MOVS ve REP kullanılışı
MOVSB daha önce anlattığım gibi kombine veri aktarma görevini yerinr getiriyordu. REP komut ile SI ile belirtilen hafıza bölgesinden DI ile belirtilen hazıfa bölgesine CX kadar aktarma yapar. Örneğin:
lea esi,buf1
lea edi,buf2
mov ecx,100h
repz
movsb
....
buf1 bölgesinden buf2 bölgesine 100h byte şeklinde aktarma yapılıyor. Eğer MOVSB yerine MOVSW kullanlırsa aktarma Word (16 bitlik) olacak ve burada kullandığımız ECX = 100h * 2 olacak. Çünkü her sayımda 1 değil 2 veri aktarmış olacağız. Aynı şekilde MOVSD kullanırsak 32 bitlik bir aktarma komutu kullandığımız için 100h * 4 olacaktır.
CMPS ve REP kullanılışı
CMPS komutuda MOVS komutu gibi REP komutu ile beraber kullanılınca döngü içerisinde kontrol sağlar. Bu komutun en büyük avantajı çok uzun veri yada yazıları rahatlıkla karşılaştırılabilmesidir. Normalde bu komut olmasa döngü içinde tek tek karşılaştıran bir program yazmak gerekir. Ancak REP CMPS kombinasyonu ile güçlü bir kontrol mekanizması oluşturulabilir.
str1 db 'www.bsca.tk',0
str2 db 'BSCA2002',0
.....
lea esi,str1
lea edi,str2
mov ecx,8
repz
cmpsb
jz ayni
:::::::::
Burada str1 bölgesi ile str2 bölgesi karşılaştırılıyor ve karşılaştırma uzunluğu 8 byte. Eğer aynı ise JZ AYNI ile başka yere atlanılıyor eğer değilse devam ediliyor. Buradaki karşılaştırma sonucunda ZF (Zero flag = sifir bayrağı) etkilendiği için istenildiği gibi düzenlenebilir.
STOS ve REP kullanılışı
STOS komutu doldurma komutu olduğunu daha önce anlattım. Bu komut DI (veya EDI) komutu ve A (AL, AX, EAX) ile beraber kombine çalışır.DI ile belirtilen hazıfıza bölgesini A register ile doldurulur. Burada doldurma yine CX (veya ECX) sayısı kadardır. Yine MOVS komutunda anlattığım gibi kullanılan registerin biti ve komutun çalışma biti dosldurma alanını büyütür. Eğer ECX=100h verip STOSW kullanırsak 100h * 2 kadar olacaktır.
buf1 db 0,0,0,0,0,0,0,0,0,0,0,0,0
....
lea edi, buf1
mov ecx, 5
mov al,99h
repz
stosw
Burada buf1 bölgesini 99 sayısı ile dolduruyoruz. Normalde ECX ile 5 kere tekrarlanacak olarak verildiği halde STOSW kullandığımız için 5 * 2 = 10 bytelık bir doldurma olacaktır.
LODS ve REP kullanılışı
LODS ile REP komutu genelde döngü içerisinde kullanılır. Tek başına kullanılıması SI (veya ESI) register ve CX (veya ECx) register ile sağlanır ve yine diğerlerinde olduğu gibi kullanılan bit uzunluğuna göre opkod alır. Kullanılan bit uzunluğunca A register (AL, AX, EAX) SI registerdeki veriyi alır.
buf1 db 'ASM TUTORS-2'
....
lea esi, buf1
mov ecx,5
repz
lodsb
bu işlem sonucunda ESI register ECX register kadar veriyi buf1 adresinden alıp AL registere aktarır.
STOS ve REP kullanılışı
STOS komutu daha önce anlattığım gibi arama - tarama komutudur. Bu komut DI (veya EDI) register ile belirtilen bölgede CX (veya ECX) kadar A registerdeki (AL, AX, EAX) değeri arar. 8, 16 ve 32 bitlik bir veriyi istediğimiz hafıza alanında arama konusunda bir hayli hızlıdır. Yine diğerlerinde olduğu gibi yanına aldığı ek ile opkodu şekil alır ve dolayısıyla arama değerinin biti buna göre ayarlanır. STOSB byte, STOSW word, ve STOSD dword araması yapar. Bu komutta aranılan değer bulunduğu takdirde döngü sonlanır ve DI register aranılan değerin olduğu hafıza adresini gösterir.
buf1 db 'aranılan harfler CY oldun',0
.....
lea edi buf1
mov ecx,10
mov ax,'CY'
repz
scasb
Bu işlem sonunda buf bölgesi aranacak ve 'CY' harflerinin olduğu adreste duracak. Sonuçta bize DI register ile adres vermiş olacak.
1 - MATEMATİK İŞLEM KOMUTLARI
Toplama işlemi
Bütün register arasında standart toplama, çıkarma, çarpma ve bölme işlemleri rahatça yapılabilir. Ayrıca daha ileri işlemler için sonraki bölümlerde anlatacağım FPU (matematik işlemci komutları) vardır ve bunlarla daha üst dereceli hesaplamalar yapılabilmektedir. Şimdi burada standart dört işlemi inceleyeceğiz. Burada genelde al, ax, eax, ebx gibi registerle örnekleme yaptım. Ancak diğer registerle ilede aynı işlemleri rahatça yapabilirsiniz. ESP gibi registerle işlem yaparken dikkatli olmanız gerekir çünkü ESP register sistemin yığın adresini belirttiği için değiştiğinde program içinde kilitlemelere neden olacaktır. Ayrıca işlemler sonucunda kalan sayılar konusunda EDX register yetkili kılınmıştır.
Toplama yapmak için ya iki farklı değişkeni birbirleriyle toplarız yada kendisiyle kendisini toplarız. Registerler arasında toplama veya adresler arasında toplama işlemi için ADD komutunu kullanıyoruz. ADD komutu 8,16,32 bit olarak işlem yapılabileceği gibi MMX ve FPU komutları ile 64 bitlik işlemde rahatça yapılabilir. Ayrıca bazı registerler için gerekli olabilecek 48 bitlik bir ara değerde vardır.
Bu komut register+register, register+değer, değer+register, register+adres gibi değişik çok yönlü kullanılıma sahiptir. Eğer AX registerimiz 100 olsa ve BX registerimiz 50 olsa bunların toplamı 150 olacaktır. Bunu gerçeklerştirmek için sadece:
add ax,bx
dememiz yeterli. Burada önemli husus ilk verilen register daima toplanan ikinci register eklenendir. Eğer burada add bx,ax olarak kullanmış olsaydık bx registerin değerine ax registerin değeri eklenecekti. İkinci eklenen registerin değeri değişmez.
Register+değer şeklinde kullanımda ise kullanılacak register ile istenilen bir değer toplanır. Örneğin:
add ebx,12345678h
gibi. Burada ebx registeri ile 12345678h değeri toplanıyor. EBX registerin o andaki değeri ne ise artı 12345678 değeride ekleniyor. Burada verebileceğim tiyo matematikte bildiğimiz gibi sayıdan önceki 0 (sıfır) değersizdir. Diğer işlemlerde olduğu gibi matematiksel işlemlerde sayıdan önce sıfır veya sıfırlar eklemek gereksizdir. Ayrıca sıfır ile yapılan işlemler bildiğimizde matematiksel kurallar geçerli olur.
Eğer bir adresteki değer ile bir register toplanacaksa :
add word ptr [adres],ax
şeklinde kullanılabilir. Burada toplanılan register ile adreslenen yerin aynı bit uzunluğunda olmasına dikkat edin. Mesela 16 bitlik bitlik bir ax register ile 8 bitlik bit alan toplanmaya çalışılırsa işlem 8 bit olacaktır. Burada toplama işlemi [adres] olarak simgelenen yere olur ve toplanan register değişmez.
Eğer bir register ile adres toplanacaksa:
add eax,dword ptr [adres]
şeklinde kullanılır. Bu işlemde [adres] olarak belirtilen hafıza adresindeki değer değişmez, o değer register ile toplanır. Yani önceki örneğin tersidir.
Birde bayrak registerle beraber özel bir toplama komutuda mevcuttur. Bu ADC komutudur ve taşıyıcı (CF) bayrak set (1) ise toplama anlamına gelir. Bu bazı matematiksel özel durumlarda kullanılır ve bir registerin değerini en yüksek noktasını geçmesi sonucunda ortaya çıkan taşma durumunu anlamakta yardımcı olur. Örneğin:
mov ax,0ffffh ; ax reg = fff0 alabileceği en büyük değer 0ffff' 'dir
add ax,10h ; 0fff0 ile 10h toplanıyor ve 16 bitlik en büyük değer ulaşıyor. Buından sonrası başa dönüş yani 0
adc dx,0 ; burada dx register 1 olacak çünkü ax registerde taşma oldu carry flag (CF) set (1) durumunda
Yukarıdaki örnekte görüldüğü gibi ax registerin değeri 16 bitlik bir registerin alabileceği en yüksek matematiksel değer yükseltidi. Bunun sonucu CF set olduğu için dx registerle 0 toplamasına rağmen dx register 1 olacaktır. Bunu programlarını içinde kullanarak daha iyi öğrenbilirsiniz. ADC bütün registerler için geçerli ve yukarıda anlattığım toplama şekillerinde de aynen kullanılabilir. Önemli olan registerin değerini en yüksek değere ulaşıp ulaşmamasıdır.
Çıkarma İşlemi
Çıkarma işleminin toplama işleminden tek farkı komutun SUB olması ve işlevini çıkarma olmasıdır. Aynı toplama komutu gibi register arası, register-adres, adres-register şeklinde çıkarma işlemi yapılır. Toplama işleminde örneklediğim ADC komutu yerine SBB komutu kullanılır ve sonuc toplamada olduğu gibi değer en yüksek değerde ise CF bayrağı set olur ve kullanılabilir durum oluşur.
Çarpma işlemi
Çarpma işlevide registerler arasında , adresler arasında, ve kendi arasında uygulanabilir. Çarpma işlemi MUL ve IMUL komutları ile gerçekleşmektedir. İkisini amacı çarpim olduğu halde MUL ve IMUL arasında farklılıklar vardır. IMUL komutu 2 veya çarpıma olanak vermekte ve kullanım şekli daha geniştir. IMUL komutu 32 bit işlemler için idealdir. Öncelikle MUL komutunu görelim.
MUL komutunda çarpılan daima AX (kullanım bitine göre AL ve EAX registerde) asıl çarpılandır. MUL komunun yanına çarpımı yapacak register yazılır.
mov bx,20
mul bx
şeklinde kullanılınca geçmemesine rağmen burada çarpılan ax registerin o andaki değeri ile bx register (burada 20 değerinde) çarpılır.
MUL komutu için CPU AX ve DX registeri yetkili kılmştır. Eğer bir çarpma işlemi sonucu ax registerin alabileceği en büyük değeri geçiyorsa geçme sayısı DX register aktarılır. Eğer geçme yoksa DX registerde bir değişme olmaz.
Böyle bir duruma örnek :
mov ax,100h
mov bx,1000
mul bx
IMUL komutu ise toplama ve çıkartma konularında görüldüğü gibi çoklu register ve büyük değerlere olanak vermektedir. Yanlız MUL komutunda olduğu gibi taşma olduğunda DX register aktarılma yapılmaz.
Örneğin:
mov ax,0ffffh
mov bx,0ffffh
imul ax,bx
gibi kullanılır.
IMUL ile üçlü çarpimda mümkündür.
mov eax, 0ffffh
mov ebx,0ffffh
imul eax,ebx,768 ; [AX*BX]*768 = 02fff0000
Burada önce EAX*EBX işlemi yapılır ardından çıkan sonuç ile 768 sayısı (bu sayı istediğimiz bir sayı olabilir 768 burada sadece örnek olarak) çarpılır. Burada işlem büyük olduğu için 32 bit registelerle örnek verdim. Büyük değerlerde daima 32 bit registerler kullanın. Sonucu tam alabilmek için bu gerekli.
Bölme işlemi
Bölme komutuda DIV ve IDIV olmak üzere kapsamlı olarak iki tanedir. Çarpmada olduğu gibi DIV komutu taşma durumunda DX (veya EDX) komutu CPU tarafından yetkili kılınmıştır. Bölme işleminde DX register kalan sayıları verme konusunda görevlidir. Diğer işlemlerde olduğu gibi bölme işleminde de matematik kuralları geçerlidir ve matematikte "sıfıra bölüm" anlamsız olarak tanımlanmıştır. CPU' da sıfıra bölüm için özel bir kesme ayırmıştır.
DIV komutu aynı MUL konutu gibidir.
mov ax,104h
mov bx,10h
div bx
;ax= 10 , dx =4
yukarıdaki örnekte görüldüğü gibi 104h/10h olur. 104h içerisinde 16 tane 10h vardır ve 04 sayısıda kalandır. Bu işlem sonucunda AX register 10h ve DX registerde 04 kalan sayısını alır. Burada örnek olarak 16 bir registerler ve sayılarla verdim. 32 bit registerler ile daha yüksek işlem olanağı olur.
IDIV komutu aynı IMUL konutunda olduğu gibi daha yüksek işlemler için idealdir. IDIV komutunu DIV komutunu yerine kullanıldığı zaman aynı işlemi yapar. Ancak IMUL' da olduğu üçlü bölüm yoktur. Konu bölüm olunca bunun pek fazla gereği olmadığı görülüyor zaten.
2 - YIĞIN KOMUTLARI
Yığın konusundan daha önce bahsetmiştim. Hafızada saklaması gereken verileri yığın komutları ile sistemin bize tahsis ettiği adreste saklarız. Bu döngülerde, çoklu işlemlerde, matematiksel işlemlerde çok önemlidir. Ayrıca Windows' un API sistemi yığın üzerinden bilgi alışverişi sağlancak şekilde tasarlanmıştır. Bunun için yığın ve kmutları çok önemlidir.
Yığın işlemini ana merkezinde SP (veya 32 bit hali ESP) vardır. SP ,stack pointer olarak açılır ve yığın noktası anlamına gelir. Yani bu register bize yığın noktasının doğrusal adresini verir. Gerçek modda bu işleme yardımcı olarak SS registerde kullanılır. SS, Stack segment anlamına gelmekte ve Korumalı mod programlamasında gerek olmadığı için kullanılmamaktadır.
Bir değeri, adresi, registeri yığın noktasına attığımız zaman yığın noktası registerinden (SP veya ESP) moda göre düşme olur. Eğer Windows altında bir değer saklıyorsan o zaman SP registerden 16 bitlik bir değer olarak 2 byte gerileyecektir. Örneğin:
SP register burada 0FFFEh adresini gösteriyor.
push ax ; 0FFFE - 2 = 0FFFCh
push bx ; 0FFFC - 2 = 0FFFAh
oldu. Bu örneği söyle düşünebilirsiniz, bir depomuz var. Bu depomuzun sadece 1 giriş kapısı var. Biz buradan kutuları numaralarına göre önden başlayıp arkaya doğru sıralıyoruz. Tabi olarak deponun alanı metrekare olarak düşüyor. Depo SP registerimiz olsa ve kutularda değerlerimiz olsa sanırım daha iyi anlaşılır.
Yukarıdaki örnekte görüldüğü gibi PUSH komutu değeri, registeri, yada bir adresteki değeri alıp saklamaya yarar. POP komutu ise bu değeri alıp istediğimiz yere yada registere aktarmamız sağlar. Assembler da saklanan değerin illaki saklandığı yere çıkmak gibi bir zorunluğu yoktur. Örneğin eax registeri PUSH EAX olarak sakladıysak bunu yine POP EAX olarak illaki EAX register çıkmak zorunda değilir. POP EBX yada POP [ADDR] olarakda çıkabiliriz.
Saklanan değerlerin sondan başlıyarak ilk değere doğru geri dönmesi esastır. Eğer sırasıyla 1,3,5 değerlerini sakladıysak, bize geri dönüşümü 5,3,1 şeklinde olacaktır.
PUSH komutu yığın noktasına saklama görevini üstlenir. PUSH komutu register, sayısal değer veya bir adres olabilir. Aşağıda görülen örnekler ayrı ayrıdır:
PUSH EAX
PUSH [ESI]
PUSH 9943512
PUSH OFFSET [410002]
Burada son örnekte görülen OFFSET aslında derleyiciler için kullanılan bir ektir. Normalde böyle bir ek assembler komutları içinde yoktur. Sadece DOS' da segment sistemi olduğu ve segmentler registerin çalışma alanlarını değiştirdiği için CS: , DS:, ES: gibi ön ekler kullanılır.
Geri alma komutu ise POP komutudur .Bu komut PUSH komutunda olan olayların tersini gerçekleştirir. Yani yığın noktası registerinden (SP vea ESP) değerin boyutu düşülür ve o noktadaki değer istenilen registere, adrese yüklenir. PUSH ve POP komutları döngüler içinde yoğun kullanım alanı bulurlar. Örnegin :
mov ecx,10
dongu:
push ecx
mov ecx,dword ptr [sayac]
inc ecx
mov dword ptr [sayac],ecx
pop ecx
loopnz dongu
....
Bu örnekte iki tane ECX register değeri olduğu halde işlem PUSH ve POP komutları sayesinde değerler ayrı ayrı saklanıp - alnarak tamamlanmaktadır.
POP komutuda PUSH komutu gibi çeşitli şekillerde kullanılabilir: (Hepsi ayrı ayrı örneklenmiştir.)
POP EAX
POP [ESI]
POP 9943512
POP OFFSET [410002]
Kesmeler (Interrupts)
Kesmeler özellikle gerçek modda çok önemli olan bir konudur. Ancak korumalı modda da en düşük seviyede kesmeler kullanılmaktadır. Kesmeler programcılar açısından kısaca sistemin bize sağladığı fonksiyonlardır. Sistem açısından ise, kullanılan işletim sistemi ile bilgisayar arasındaki kontrollü çalışmayı sağlama ve gerektiği kadar paylaşımı oluşturmadır. Buradaki paylaşım sistemin ve işletim sistemimin kaynaklarının paylaştırılması. Örneğin bir tuşa basılıp basılmadığını ve hangi tuşa basıldığını öğrenmek için sistem bize sadece klavye konusunda bir kesme vermiştir. Bunu kullanarak program içerisinden hangi tuşa basıldığını öğrenebiliriz. Yada hangi ekran modunda olduğumuzu yine kesmeler vasıtasıyla öğrenebiliriz. Kesmelerin olmadığı bir program düşünürsek o zaman zor ve çok yüksek bir bilgi birikimi gerektiren "donanım programlaması" yapılması gerekir.
Kesmelerin gerçek modda "kesme tablosu" olarak adlandırılan 0:0 adresniden başlayan bir tablosu vardır. Bu tabloda kesmeler 4 byte aralıkla birbirlerini takip ederler. Bunun nedeni gerçek modda 64kb ' lık segment - offset sistemidir. 16 bitlik segment adresi önce, 16 bitlik offset adreside sonra takip eder. Bu kesme vektörleri değiştirilerek "geridönüm" (callback) yapmak şartıyla yönlendirilebilirler (Hook). Bundan yararlanarak ortaya TSR (terminate and stay resident -sonlan ve kal-) ortaya çıkmıştır. Sistemin istenilen bir fonksiyonu alınarak filitre edilebilir ve o fonksiyon kullanıldığında istenilen program parçacığı çalıştırılabilir. Ayrıca sistemin debug için kesme 1 ve kesme 3 olarak ayırdığı iki kesmede mevcuttur. EFLAG bayrak registerininin TR biti (trap biti = kapan veya debug biti) set edilince CPU her komuttan sonra kesme 1 ' i çağırır. Böylece debug etme işlemi yapılmış olur.
Bayraklar ( Flaglar)
Bayrak komutları ve olayları deyince aklımıza hep sayıları oluşturan bit'ler gelmeli. Basit olarak iki sayının karşılaştırılmasında ortaya o sayının aynı olduğu, aynı olmadığı, büyük veya küçük olması yada sıfır olması gibi durumlar çıkar. Bu durumları öğrenmenin tek yolu o duruma göre şekil alan bayrak register (f) bitlerinin durumunu öğrenmemizdir. Bir test veya bir matematiksel işlem olduğu durumlarda sonuclar daima bayrak registerin ilgili bitlerine yansır.
Bayrak register gerçek modda 16 bit, korumalı modda ise 32 bitliktir. Yani gerçek modda toplam 16 bit tane sebeb-sonuç biti ayrılmış aynı şekilde korumalı modda 32 tane sebeb-sonuç biti ayrılmıştır.32 bitlik bayrak registere EFlag adı verilir ve aynı 16 bitlik şekli gibi sadece ilk 16 biti kullanılır.(Bazı bitler dökümante edilmemiştir). EFlag registerin sonraki 16 biti ileride yapılacak CPU güncellemeri için ayrılmıştır.En başlıca olanları ZF (zero flag), CF (carry flag) ,SF, AF, gibidir. Örneğin bir işlem sonucu 0 ise ZF set olur yani 1 olur.
Bayraklar işlevleri açısından 3 gruba ayrılılar. Status flaglar (durum bayrağı), Control flaglar (kontrol bayrağı) ve, System flaglar (sistem bayrağı). Durum bayrakları programın içindeki komutsal, mantıksal ve matematiksel işlemlerin sonuçlarını verir. Genelde kullanılan bayraklar bunlardır. Kontrol bayrağı sadece 1 tanedir ve ileride göreceğimiz repz komutunun aşağı yada yukarı işlem yapma şeklini ayarlar. Sistem bayrakları ise sistemle ilgili işlemler için kullanılır. Genelde bunları işletim sistemi yada kullanacağımız debugger gibi yüksek düzeyli bir program değiştirir.
Bayrak registerin tam yapısı aşağıdaki gibidir:
0 1 2 3 4 5 6 7 8 9 A B C D E F 10 11-31
CF ? PF ? AF ZF SF TF IF DF OV ? IO ? NTF RF V86 -
Komut Noktası registeri (IP)
Bu register çalışmaktan olan sistemin yada programın o anda hangi adreste olduğunu komutun başlangıç noktasından itibaren belirtir. Örneğin bir programı debug ederken her F8 (trace) tuşuna basınca o komut işletip bir alt komuta geçer. İşte o anda IP register o komutun işlemesi ile sonraki komutun başına gelmiş olur. Böyle kod zinciri sağlanmış olur. Bunu CPU otomatik olarak kendisi düzenler. Yine moda göre uzunluğu ve ismi değişir, gerçek mod daha önce anlattığım gibi segment:offset sistemine sahip olduğu için IP register 16 bit olur. Yani sadece offseti gösterir. Ancak korumalı modda bütün 32bit olan registerin başına aldığı 'E' (Extended) harfini alarak EIP olur. Bu registeri direkt olarak MOV EAX,EIP şeklinde elde edemiyoruz. Çünkü bu registerin değişmesi yada farklı bir değer alması sistemin çalışma bütünlüğünü yok eder ve sistemde hatalarına neden olur. Bunu bir su yoluna benzeterek örnekleyebilirim. Örneğin bir nehrin suyunun rasgele başka bir yere yönlendirildiğini düşünelim. O zaman istenmeyen su baskınları ve ekinleri- evlerin zarar görmesine hatta ölümlere neden olacaktır. Yinede sistem debug API' leri ile veya aşağıdaki gibi basit bir yöntemle o andaki IP noktası bulunabilir.
call delta
delta:
pop eax
sub eax,5
Burada ileride göreceğimiz CALL komutunu bulunduğu yeri yığın içine atmasından yararlanıyoruz. SUB EAX,5 ile o zamana kadar işletilen kodun uzunluğunu çıkarıyoruz ve sonuçta elimize baştaki komutun çağrıldığı IP noktası kalıyor.
IO Portları
Donamın bize temel işlemler ve sistemi bütün kaynakların kullanmak için kaynak paylaşımını ve erişimini sağlayan erişim kanallaı vermiştir. Aslında sistemdeki ek aygıtlar (ekran kartı, ses kartı..) hepsi sistemler bu portlar vasıtasıyla haberleşmekte ve işlemleri yerine getirmektedir. Bunlara ilaveten daha önce bahsettiğim kesmeler tamamlayıcı bir roldedir. Donamın portları normal programlarda kullanılması hem karışık hemde zor olduğu için genelde çok yüksek düzeyli uygulamalarda direkt olarak kullanılırlar. DOS altında korumalı modda 8 ve 16 bitlik değerlerle istenildiği gibi kullanılabilir ancak korumalı modda en yüksek hassasiyet değerinde (ring 0) olma zorunluğu vardır. Gerçek modda daha doğrusu DOS ortamında bir *.com programı dışarıdan hiç kesme, adres kullanmadan kendi başına bir sistem programı oluşturabilir. Ancak korumalı modda bu pek mümkün değildir. Bu işletim sisteminin istediği özelliklerden kaynaklanır. Bir *.com dosyası salt makina kodudur ve uygun bir programlama ile sistemin her yerinde çalışabilir (örneğin virusler) Ancak bir windows programı IO programlarının kullanılma kısıtlılığı ve PE dosya yapısı nedeniyle bunu pekde başaramaz. Burada IO portlarının derin bir işlevlik gücü ortaya çıkıyor.
IO portları için 8,16 ve 32 bitlik özel opkodlar tahsis edilmiştir. Genelde asıl port numarasından önce status port denilen ve ön değerlerin gönderilip - alınan veya kontrol edilen port(lar) vardır. Hatta bazı donanım sistemlerinde bu portları 6-7 taneye kadar çıkarak konbine çalışmayı gerektirir.
Örnek vermek gerekirse sistenm CMOS hafızasını okumak için 70h portu kullanılır. Burada CMOS'un 15 ve 16. byteları okunarak hafıza uzunluğu öğreniliyor.
mov ax,1516
out 70h,al
in al,71h
mov byte ptr [hafıza_low],al
mov al,ah
out 70h,al
in al,71h
mov byte ptr [hafıza_high],al
ret
Bu örneği portlar kullanılmı hakkında daha iyi fikir edinilmesi için verdim. Normalde sistem kesmeleri, API kütüphaneleri ile bu işlem rahatça elde edilebilir. Fakat burada söyle bir fark var. Sistem kesmeleri (interruptlar) ve Windows API kütüphaneleri vectörleri kullanıcı tarafından ele geçirilerek (hook ) istenildiği gibi kısıtlama yada değiştirilme olanağına sahiptir. Ancak IO portları için böyle bir ele geçirme (hook) mümkün değildir. Bunlar tamamen donanıma ait portlardır.
Assembler komutları
Bu kadar genel ve ön bilgi sonrası artık komutları tanımaya başlayabiliriz. Assembler komutları genel amaçlı komutlar, mantıksal komutlar, sistem komutları, gibi bölümlere ayrılmaktadır.
Daha önce söz ettiğimiz MOV, LEA komutları registerleri yükleme, adresleme veya değerini bir yere yazdırma için kullanılır. Genel kullanılış şekli : MOV register, değer veya MOV [adres],register şeklindedir. Bu komut birden fazla registeri bir arada kullanmayada olanak verir. Örneğin : MOV register, [register+register] gibi.
mov register,[adres]
mov [adres],register
mov [adres+register],register
mov register, [adres+register]
mov register, [register+register]
gibi burada sadece registerler arasında toplama değil çıkartma ve çarpma işlemide yapılabilimektedir.
LEA komutu hem registere değer yüklemek için hemde doğrusal bir hafıza adresinin alınması için kullanılır.
lea register, adres
lea register, [adres]
lea register, register*4
lea register, register+10h
gibi kullanılır. İleride uygulamalı örneklerde MOV ile LEA arasındaki daha iyi farkı göreceksiniz.
Ayrıca LES, LDS, LFS, LSS, LGS gibi genelde gerçek modda ve yüksek düzeyli korumalı mod mimarisinde kullanılan register yükleme komutlarıda vardır. Bunlar interrupt ve segment ayarlamalarında kullanılır. Korumalı modda Segment seçici görevini yerine getirir. Şu andaki sistemlerde çok fazla kullanım alanları yoktur.
Buradan sonra çok kullanılan önemli komutları inceleyeceğiz. Bazı komutlar çalıştıktan sonra birden fazla komutu etkileyebilir. Bunların mümkün olduğunca açıklayacağım. Ayrıca mümkün olduğunca her komut için örnek vereceğim.
Registerleri değiştiren, aktaran, ekleyen başka komutlarda vardır.
XCHG KOMUTU
XCHG komutu iki registerin yada registerler bir yerin yer değişimi sağlar. Bu komut 4-5 tane ayrı komut yapılacak bir işleömi tek başına gerçekleştirir. Kullanılışıda çok basittir.
XCHG REG1, REG2
XCHG REG, [ADDR]
XCHG [ADDR],REG
gibi kullanılır. Buradaki tek şart yer değişecek register veya adresin aynı bit uzunluğuna sahip olmasıdır.
XLAT KOMUTU
XLAT komutu esas olarak BX (veya EBX) registeri esas alarak çalışır. Buna göre BX registerle gösterilen bir adresteki bilgiyi almak için AL registeri BX register ile toplar ve gösterdiği noktadaki bilgiyi (8 bitlik) alıp AL register yükler. Bu işlemde sadece AL registerin değeri değişir.
Örneğin:
yazi db 'ABCDEFGH',0
...
mov al,3 ; AL = 3
lea ebx, yazi ; EBX = yazi [ABCDEFG]
xlat ; EBX+AL = [- - *---] AL= 'C' olur
XLAT komutunu bazı zamanlar XLATB olarakda geçer ve bunlar farklı değil ikiside aynıdır.
MOVSX ve MOVZX komutları
Bu komutlarda diğer komutlar gibi kodu kısatmak ve daha hızlı işlem yapmak içindir. MOVZX komutu bir adresdeki 8 bitlik sayıyı 8 bit registere yükler ama 8 bit dışındaki bitleri resetler (sıfırlar) Böylece elimizde 8 bitlik tertemiz bir değer olur. MOVSX komutuda benzer olarak registeri yükler ve diğer bitleri temizler ancak bu işlem 16 bitten 8 bite veya 32 bitten 8 bite olmaktadır.
deger2 db 10
....
mov eax,12345678h ; eax = 12345678
movzx eax, byte ptr [deger2] ; deger2 = 10, eax =10 oldu
Yuıkarıdaki örnekte önceden EAX registere 12345678 gibi 32 bitlik bir değer girdik. [deger2] adresinin 8 bit olduğuna dikkat edin. MOVZX komutndan sonra EAX register sadece 10 değerini alacaktır. Eğer MOVZX kullanmadan normal bir AL register yüklemesi yapsaydık EAX registerin sadece AL kısmı değişecek ve EAX 12345610 değerini alacaktı.
Kombine Veri komutları (LODS, MOVS, CMPS, STOS, SCAS)
Bu komutlar birden fazla registerle, birden fazla kere işlem yapmayı amaçlayan güçlü ve komutlardır. Bunlar tekil olarak yada REPZ, REPNZ, REPNE gibi bu komutlara özel tahsis edilmiş yardımcı komutlar ile beraber çalışır. Hepsi yanına yapacağı bit sayısına göre ek alırlar Bu komut kullacağı bit değerine göre yanına simge alır. Ancak bu bizim tarafımızdan tahsis edilen bir özellik değildir. Bu standart komut setinin ayrı ayrı ayırdığı opkodlardır. Bu opkodlar konunun sonunda göreceğimiz REP komutu ile kullanılışında her defasında yapılacak işlem miktarınıda ayarlar.
B = byte (8 bit) bazında işlem
W= word (16 bit) bazında işlem
D= dword (32 bit) bazda işlem
Öncelikle anlaşılabilmesi için tekil kullanımlarını sonrada sayaçlı kullanımlarını inceleyeceğiz.
LODS komutu
Bu komut yanına aldığı eke göre 8 ile 32 bit arasında işlem gücü kazanır. Temel görevi SI (yada ESI) registerin gösterdiği noktadaki değeri alıp bit değerine göre A (AL, AX, EAX) registere yüklemektir. Ayrıca LODS komutu değeri yükledikten sonra SI registerin değerini kullanılan bit değerine göre arttırarak sonraki veriye göre ayarlamış olur. Bu byte için 1, word için 2 ve dword için 4 byte otomatik olarak toplanacak demektir. Daha önce gördüğünüz XLAT komutuna benziyor olmasna karşın LODSB çok farklıdır. Aşağıda verilen ve diğer örneklerde verilen bit değerleri gerçek modda veya korumalı modda aynıdır. Kullanım ihtiyacına göre hepsi kullanılabilir.
LODSB = 1 byte işlem için (8 bit)
LODSW = 2 byte işlem için (16 bit)
LODSD = 4 byte işlem için (32 bit)
Örnek:
ornekyazi db 'ASM GENEL TUTOR',0
.....
lea esi,ornekyazi ; ornekyazi noktasının başlangıç adresini ESI registere yükledik.
lodsb ; al registere ilk harf olan 'A' harfi yüklendi. ESI register byte olarak kullandığımız için 1 arttı.
Eğer ilk iki harfi alıp ax registere yüklemek isteseydik lodsw kullanacaktık. Yine ilk 4 byte için lodsd kullanacaktık.
MOVS komutu
Bu komutta LODS komut gibi yanına aldığı B,W,D takıları ile ayrı bir opkod halini alır ve buna göre bitte işlem yapar. MOVS komutu DI (veya EDI), SI (veya ESI) ve registereleri ile beraber çalışır. SI kaynaktır yani aktarılacak verini adresini gösterir. DI ise hedeftir gideceği yeri gösterir. Komut sonrası her iki registerde kullanılan bit değerine göre ekleme olur ve yeni yeri gösterirler.
buf1 db 'YAZI',0
buf2 db 0,0,0,0
......
lea esi,buf1 ; buf1 = 'YAZI'
lea edi,buf2 ; buf2= 000000000
movsd
; buf1 = 'YAZI ' ve buf2 = 'YAZI
Bu işlem sonunda buf1 adresindeki veri buf2 bölgesine aktarılır. buf2 bölgesinde bir değişme olmaz, aynı kalır. Burada eğer MOVSB kullanılmış olsaydı sadece ile harf aktarılacaktı. Eğer MOVSW kullanılmış olsa o zaman ilk iki veri aktarılacaktı. Bunlar kullanılan bit değerlerine bağlı.
CMPS Komutu
CMPS komutu ileride göreceğimiz karşılaştırma komutunu kombine bir şeklidir. Bu komut yine tekil olarak diğerleri gibi yanına aldığı ek ile orantılı olarak bit sayısı kadar işlem yapar Yine bu komut SI (veya ESI) ve DI (veya EDI) registerler ile birlikte çalışır.Bu komut tüm karşılaştırma ve test komutları gibi bayrak registeri etkiler. Bu komutu kısaca SI register ile gösterilen bölgeyi DI ile gösterilen bölge ile karşılaştırır diye açıklayabiliriz. Bu işlem tamamen hafıza bölgesi ile ilgilidir. Bu komut ileride göreceğimiz karşılaştıma komutları gibi registerler karşılaştırması yapmaz. Karşılaştırma sonucunu ZF (zero flag) ile alırız. Eğer iki bölgede verdiğimiz bir değeri kadar karşılaştırılması sonucu aynı ise ZF set (1) olur. Eğer yanlış ise ZF reset (0) olur. Bunu nasıl kullabncağımızı ileri şartlı atmala komutlarında göreceğiz. Şimdilik aşağıdaki örnek bunun nasıl kullanıldığı konusunda bir fikir verebilir.
buf1 db 01,02,03,04
buf2 db 02,03,01,04
....
lea esi, buf1 ; asıl bölge
lea edi, buf2 ; karşılaştırılan bölge
cmpsd ; dword (32 bit ) olarak karşılaştırma
jz dogru ; eğer aynı ise "dogru " etiketli yere atlar. Değilse devam eder.
....
STOS Komutu
Bu komut A register (AL, AX EAX) ile DI register (veya EDI) ile beraber çalışır. STOS komutu diğerler gibi yanlarına aldıkları işaret ile opkod eşlenir ve bu eşleşmeye göre işlemde bit seviyesi kullanır. STOS komutu A registerdeki değeri DI registerin gösterdiği bölgeye aktarır.
buf1 db 00,00,00,00
......
lea edi, buf1
mov al,89h
stosb
Bu işlem sonunda stosb kullandığımız için 1 byte (8 bit) bir yazma olacak ve buf1 db 89h,0,0,0 şeklinde olacaktır. Eğer STOSW komutu kullanmış olsaydı 2 byte yazacaktı ve dolayısıyla bu AX register olacaktı. Aynı şeklide STOSD kullansaydık 4 byte olacaktı ve bu EAX register olacaktı.
SCAS Komutu
SCAS komutu aramak - taramak kelimesinin kısaltılmışıdır ve yaptığı işlemde taramadır. Tek başına pek bir işlevi yoktur. Asıl olarak aşağıda göreceğiniz gibi REP tekrarlatıcısı ile güçlü bir arama - tarama işlevi kazanır.
REP komutunu LODS, MOVS, CMPS, STOS ve SCAS ile kullanılışları
REP komutu yukarıda gördüğümüz 4 sınıf komut ile kombine çalışarak onların tek başına yaptıkları işi grup halinde ve istenilen sayıda yapılmasını sağlar. Burada önemli olan sayıdır ve sayıyı CX (veya ECX) register ile belirtiriz. Diğer kullanımlar her komut için yukarıda anlattığım gibi ilgili registerlere ve kullanılan alanlara bağlıdır. REP komutuvir döngü oluşturur ve verilen komutu belirlenen CX (veya ECX) kadar yapar.
REP komutu REPZ, REPE, REPNE olarak döngüyü kontrol altına alma olanağıda vardır. REPZ ile CX (yada ECX) 0 oluncaya kadar devam et anlamındadır. REPE yine CX register eşit oluncaya kadar ve, REPNE ise eşit olmayıncaya kadar anlamındadır. REPZ genelde çok kullanılır ama DF bayrağı değiştirilerek tersine işlem ile diğerleride rahatça kullanılabilir.
MOVS ve REP kullanılışı
MOVSB daha önce anlattığım gibi kombine veri aktarma görevini yerinr getiriyordu. REP komut ile SI ile belirtilen hafıza bölgesinden DI ile belirtilen hazıfa bölgesine CX kadar aktarma yapar. Örneğin:
lea esi,buf1
lea edi,buf2
mov ecx,100h
repz
movsb
....
buf1 bölgesinden buf2 bölgesine 100h byte şeklinde aktarma yapılıyor. Eğer MOVSB yerine MOVSW kullanlırsa aktarma Word (16 bitlik) olacak ve burada kullandığımız ECX = 100h * 2 olacak. Çünkü her sayımda 1 değil 2 veri aktarmış olacağız. Aynı şekilde MOVSD kullanırsak 32 bitlik bir aktarma komutu kullandığımız için 100h * 4 olacaktır.
CMPS ve REP kullanılışı
CMPS komutuda MOVS komutu gibi REP komutu ile beraber kullanılınca döngü içerisinde kontrol sağlar. Bu komutun en büyük avantajı çok uzun veri yada yazıları rahatlıkla karşılaştırılabilmesidir. Normalde bu komut olmasa döngü içinde tek tek karşılaştıran bir program yazmak gerekir. Ancak REP CMPS kombinasyonu ile güçlü bir kontrol mekanizması oluşturulabilir.
str1 db 'www.bsca.tk',0
str2 db 'BSCA2002',0
.....
lea esi,str1
lea edi,str2
mov ecx,8
repz
cmpsb
jz ayni
:::::::::
Burada str1 bölgesi ile str2 bölgesi karşılaştırılıyor ve karşılaştırma uzunluğu 8 byte. Eğer aynı ise JZ AYNI ile başka yere atlanılıyor eğer değilse devam ediliyor. Buradaki karşılaştırma sonucunda ZF (Zero flag = sifir bayrağı) etkilendiği için istenildiği gibi düzenlenebilir.
STOS ve REP kullanılışı
STOS komutu doldurma komutu olduğunu daha önce anlattım. Bu komut DI (veya EDI) komutu ve A (AL, AX, EAX) ile beraber kombine çalışır.DI ile belirtilen hazıfıza bölgesini A register ile doldurulur. Burada doldurma yine CX (veya ECX) sayısı kadardır. Yine MOVS komutunda anlattığım gibi kullanılan registerin biti ve komutun çalışma biti dosldurma alanını büyütür. Eğer ECX=100h verip STOSW kullanırsak 100h * 2 kadar olacaktır.
buf1 db 0,0,0,0,0,0,0,0,0,0,0,0,0
....
lea edi, buf1
mov ecx, 5
mov al,99h
repz
stosw
Burada buf1 bölgesini 99 sayısı ile dolduruyoruz. Normalde ECX ile 5 kere tekrarlanacak olarak verildiği halde STOSW kullandığımız için 5 * 2 = 10 bytelık bir doldurma olacaktır.
LODS ve REP kullanılışı
LODS ile REP komutu genelde döngü içerisinde kullanılır. Tek başına kullanılıması SI (veya ESI) register ve CX (veya ECx) register ile sağlanır ve yine diğerlerinde olduğu gibi kullanılan bit uzunluğuna göre opkod alır. Kullanılan bit uzunluğunca A register (AL, AX, EAX) SI registerdeki veriyi alır.
buf1 db 'ASM TUTORS-2'
....
lea esi, buf1
mov ecx,5
repz
lodsb
bu işlem sonucunda ESI register ECX register kadar veriyi buf1 adresinden alıp AL registere aktarır.
STOS ve REP kullanılışı
STOS komutu daha önce anlattığım gibi arama - tarama komutudur. Bu komut DI (veya EDI) register ile belirtilen bölgede CX (veya ECX) kadar A registerdeki (AL, AX, EAX) değeri arar. 8, 16 ve 32 bitlik bir veriyi istediğimiz hafıza alanında arama konusunda bir hayli hızlıdır. Yine diğerlerinde olduğu gibi yanına aldığı ek ile opkodu şekil alır ve dolayısıyla arama değerinin biti buna göre ayarlanır. STOSB byte, STOSW word, ve STOSD dword araması yapar. Bu komutta aranılan değer bulunduğu takdirde döngü sonlanır ve DI register aranılan değerin olduğu hafıza adresini gösterir.
buf1 db 'aranılan harfler CY oldun',0
.....
lea edi buf1
mov ecx,10
mov ax,'CY'
repz
scasb
Bu işlem sonunda buf bölgesi aranacak ve 'CY' harflerinin olduğu adreste duracak. Sonuçta bize DI register ile adres vermiş olacak.
1 - MATEMATİK İŞLEM KOMUTLARI
Toplama işlemi
Bütün register arasında standart toplama, çıkarma, çarpma ve bölme işlemleri rahatça yapılabilir. Ayrıca daha ileri işlemler için sonraki bölümlerde anlatacağım FPU (matematik işlemci komutları) vardır ve bunlarla daha üst dereceli hesaplamalar yapılabilmektedir. Şimdi burada standart dört işlemi inceleyeceğiz. Burada genelde al, ax, eax, ebx gibi registerle örnekleme yaptım. Ancak diğer registerle ilede aynı işlemleri rahatça yapabilirsiniz. ESP gibi registerle işlem yaparken dikkatli olmanız gerekir çünkü ESP register sistemin yığın adresini belirttiği için değiştiğinde program içinde kilitlemelere neden olacaktır. Ayrıca işlemler sonucunda kalan sayılar konusunda EDX register yetkili kılınmıştır.
Toplama yapmak için ya iki farklı değişkeni birbirleriyle toplarız yada kendisiyle kendisini toplarız. Registerler arasında toplama veya adresler arasında toplama işlemi için ADD komutunu kullanıyoruz. ADD komutu 8,16,32 bit olarak işlem yapılabileceği gibi MMX ve FPU komutları ile 64 bitlik işlemde rahatça yapılabilir. Ayrıca bazı registerler için gerekli olabilecek 48 bitlik bir ara değerde vardır.
Bu komut register+register, register+değer, değer+register, register+adres gibi değişik çok yönlü kullanılıma sahiptir. Eğer AX registerimiz 100 olsa ve BX registerimiz 50 olsa bunların toplamı 150 olacaktır. Bunu gerçeklerştirmek için sadece:
add ax,bx
dememiz yeterli. Burada önemli husus ilk verilen register daima toplanan ikinci register eklenendir. Eğer burada add bx,ax olarak kullanmış olsaydık bx registerin değerine ax registerin değeri eklenecekti. İkinci eklenen registerin değeri değişmez.
Register+değer şeklinde kullanımda ise kullanılacak register ile istenilen bir değer toplanır. Örneğin:
add ebx,12345678h
gibi. Burada ebx registeri ile 12345678h değeri toplanıyor. EBX registerin o andaki değeri ne ise artı 12345678 değeride ekleniyor. Burada verebileceğim tiyo matematikte bildiğimiz gibi sayıdan önceki 0 (sıfır) değersizdir. Diğer işlemlerde olduğu gibi matematiksel işlemlerde sayıdan önce sıfır veya sıfırlar eklemek gereksizdir. Ayrıca sıfır ile yapılan işlemler bildiğimizde matematiksel kurallar geçerli olur.
Eğer bir adresteki değer ile bir register toplanacaksa :
add word ptr [adres],ax
şeklinde kullanılabilir. Burada toplanılan register ile adreslenen yerin aynı bit uzunluğunda olmasına dikkat edin. Mesela 16 bitlik bitlik bir ax register ile 8 bitlik bit alan toplanmaya çalışılırsa işlem 8 bit olacaktır. Burada toplama işlemi [adres] olarak simgelenen yere olur ve toplanan register değişmez.
Eğer bir register ile adres toplanacaksa:
add eax,dword ptr [adres]
şeklinde kullanılır. Bu işlemde [adres] olarak belirtilen hafıza adresindeki değer değişmez, o değer register ile toplanır. Yani önceki örneğin tersidir.
Birde bayrak registerle beraber özel bir toplama komutuda mevcuttur. Bu ADC komutudur ve taşıyıcı (CF) bayrak set (1) ise toplama anlamına gelir. Bu bazı matematiksel özel durumlarda kullanılır ve bir registerin değerini en yüksek noktasını geçmesi sonucunda ortaya çıkan taşma durumunu anlamakta yardımcı olur. Örneğin:
mov ax,0ffffh ; ax reg = fff0 alabileceği en büyük değer 0ffff' 'dir
add ax,10h ; 0fff0 ile 10h toplanıyor ve 16 bitlik en büyük değer ulaşıyor. Buından sonrası başa dönüş yani 0
adc dx,0 ; burada dx register 1 olacak çünkü ax registerde taşma oldu carry flag (CF) set (1) durumunda
Yukarıdaki örnekte görüldüğü gibi ax registerin değeri 16 bitlik bir registerin alabileceği en yüksek matematiksel değer yükseltidi. Bunun sonucu CF set olduğu için dx registerle 0 toplamasına rağmen dx register 1 olacaktır. Bunu programlarını içinde kullanarak daha iyi öğrenbilirsiniz. ADC bütün registerler için geçerli ve yukarıda anlattığım toplama şekillerinde de aynen kullanılabilir. Önemli olan registerin değerini en yüksek değere ulaşıp ulaşmamasıdır.
Çıkarma İşlemi
Çıkarma işleminin toplama işleminden tek farkı komutun SUB olması ve işlevini çıkarma olmasıdır. Aynı toplama komutu gibi register arası, register-adres, adres-register şeklinde çıkarma işlemi yapılır. Toplama işleminde örneklediğim ADC komutu yerine SBB komutu kullanılır ve sonuc toplamada olduğu gibi değer en yüksek değerde ise CF bayrağı set olur ve kullanılabilir durum oluşur.
Çarpma işlemi
Çarpma işlevide registerler arasında , adresler arasında, ve kendi arasında uygulanabilir. Çarpma işlemi MUL ve IMUL komutları ile gerçekleşmektedir. İkisini amacı çarpim olduğu halde MUL ve IMUL arasında farklılıklar vardır. IMUL komutu 2 veya çarpıma olanak vermekte ve kullanım şekli daha geniştir. IMUL komutu 32 bit işlemler için idealdir. Öncelikle MUL komutunu görelim.
MUL komutunda çarpılan daima AX (kullanım bitine göre AL ve EAX registerde) asıl çarpılandır. MUL komunun yanına çarpımı yapacak register yazılır.
mov bx,20
mul bx
şeklinde kullanılınca geçmemesine rağmen burada çarpılan ax registerin o andaki değeri ile bx register (burada 20 değerinde) çarpılır.
MUL komutu için CPU AX ve DX registeri yetkili kılmştır. Eğer bir çarpma işlemi sonucu ax registerin alabileceği en büyük değeri geçiyorsa geçme sayısı DX register aktarılır. Eğer geçme yoksa DX registerde bir değişme olmaz.
Böyle bir duruma örnek :
mov ax,100h
mov bx,1000
mul bx
IMUL komutu ise toplama ve çıkartma konularında görüldüğü gibi çoklu register ve büyük değerlere olanak vermektedir. Yanlız MUL komutunda olduğu gibi taşma olduğunda DX register aktarılma yapılmaz.
Örneğin:
mov ax,0ffffh
mov bx,0ffffh
imul ax,bx
gibi kullanılır.
IMUL ile üçlü çarpimda mümkündür.
mov eax, 0ffffh
mov ebx,0ffffh
imul eax,ebx,768 ; [AX*BX]*768 = 02fff0000
Burada önce EAX*EBX işlemi yapılır ardından çıkan sonuç ile 768 sayısı (bu sayı istediğimiz bir sayı olabilir 768 burada sadece örnek olarak) çarpılır. Burada işlem büyük olduğu için 32 bit registelerle örnek verdim. Büyük değerlerde daima 32 bit registerler kullanın. Sonucu tam alabilmek için bu gerekli.
Bölme işlemi
Bölme komutuda DIV ve IDIV olmak üzere kapsamlı olarak iki tanedir. Çarpmada olduğu gibi DIV komutu taşma durumunda DX (veya EDX) komutu CPU tarafından yetkili kılınmıştır. Bölme işleminde DX register kalan sayıları verme konusunda görevlidir. Diğer işlemlerde olduğu gibi bölme işleminde de matematik kuralları geçerlidir ve matematikte "sıfıra bölüm" anlamsız olarak tanımlanmıştır. CPU' da sıfıra bölüm için özel bir kesme ayırmıştır.
DIV komutu aynı MUL konutu gibidir.
mov ax,104h
mov bx,10h
div bx
;ax= 10 , dx =4
yukarıdaki örnekte görüldüğü gibi 104h/10h olur. 104h içerisinde 16 tane 10h vardır ve 04 sayısıda kalandır. Bu işlem sonucunda AX register 10h ve DX registerde 04 kalan sayısını alır. Burada örnek olarak 16 bir registerler ve sayılarla verdim. 32 bit registerler ile daha yüksek işlem olanağı olur.
IDIV komutu aynı IMUL konutunda olduğu gibi daha yüksek işlemler için idealdir. IDIV komutunu DIV komutunu yerine kullanıldığı zaman aynı işlemi yapar. Ancak IMUL' da olduğu üçlü bölüm yoktur. Konu bölüm olunca bunun pek fazla gereği olmadığı görülüyor zaten.
2 - YIĞIN KOMUTLARI
Yığın konusundan daha önce bahsetmiştim. Hafızada saklaması gereken verileri yığın komutları ile sistemin bize tahsis ettiği adreste saklarız. Bu döngülerde, çoklu işlemlerde, matematiksel işlemlerde çok önemlidir. Ayrıca Windows' un API sistemi yığın üzerinden bilgi alışverişi sağlancak şekilde tasarlanmıştır. Bunun için yığın ve kmutları çok önemlidir.
Yığın işlemini ana merkezinde SP (veya 32 bit hali ESP) vardır. SP ,stack pointer olarak açılır ve yığın noktası anlamına gelir. Yani bu register bize yığın noktasının doğrusal adresini verir. Gerçek modda bu işleme yardımcı olarak SS registerde kullanılır. SS, Stack segment anlamına gelmekte ve Korumalı mod programlamasında gerek olmadığı için kullanılmamaktadır.
Bir değeri, adresi, registeri yığın noktasına attığımız zaman yığın noktası registerinden (SP veya ESP) moda göre düşme olur. Eğer Windows altında bir değer saklıyorsan o zaman SP registerden 16 bitlik bir değer olarak 2 byte gerileyecektir. Örneğin:
SP register burada 0FFFEh adresini gösteriyor.
push ax ; 0FFFE - 2 = 0FFFCh
push bx ; 0FFFC - 2 = 0FFFAh
oldu. Bu örneği söyle düşünebilirsiniz, bir depomuz var. Bu depomuzun sadece 1 giriş kapısı var. Biz buradan kutuları numaralarına göre önden başlayıp arkaya doğru sıralıyoruz. Tabi olarak deponun alanı metrekare olarak düşüyor. Depo SP registerimiz olsa ve kutularda değerlerimiz olsa sanırım daha iyi anlaşılır.
Yukarıdaki örnekte görüldüğü gibi PUSH komutu değeri, registeri, yada bir adresteki değeri alıp saklamaya yarar. POP komutu ise bu değeri alıp istediğimiz yere yada registere aktarmamız sağlar. Assembler da saklanan değerin illaki saklandığı yere çıkmak gibi bir zorunluğu yoktur. Örneğin eax registeri PUSH EAX olarak sakladıysak bunu yine POP EAX olarak illaki EAX register çıkmak zorunda değilir. POP EBX yada POP [ADDR] olarakda çıkabiliriz.
Saklanan değerlerin sondan başlıyarak ilk değere doğru geri dönmesi esastır. Eğer sırasıyla 1,3,5 değerlerini sakladıysak, bize geri dönüşümü 5,3,1 şeklinde olacaktır.
PUSH komutu yığın noktasına saklama görevini üstlenir. PUSH komutu register, sayısal değer veya bir adres olabilir. Aşağıda görülen örnekler ayrı ayrıdır:
PUSH EAX
PUSH [ESI]
PUSH 9943512
PUSH OFFSET [410002]
Burada son örnekte görülen OFFSET aslında derleyiciler için kullanılan bir ektir. Normalde böyle bir ek assembler komutları içinde yoktur. Sadece DOS' da segment sistemi olduğu ve segmentler registerin çalışma alanlarını değiştirdiği için CS: , DS:, ES: gibi ön ekler kullanılır.
Geri alma komutu ise POP komutudur .Bu komut PUSH komutunda olan olayların tersini gerçekleştirir. Yani yığın noktası registerinden (SP vea ESP) değerin boyutu düşülür ve o noktadaki değer istenilen registere, adrese yüklenir. PUSH ve POP komutları döngüler içinde yoğun kullanım alanı bulurlar. Örnegin :
mov ecx,10
dongu:
push ecx
mov ecx,dword ptr [sayac]
inc ecx
mov dword ptr [sayac],ecx
pop ecx
loopnz dongu
....
Bu örnekte iki tane ECX register değeri olduğu halde işlem PUSH ve POP komutları sayesinde değerler ayrı ayrı saklanıp - alnarak tamamlanmaktadır.
POP komutuda PUSH komutu gibi çeşitli şekillerde kullanılabilir: (Hepsi ayrı ayrı örneklenmiştir.)
POP EAX
POP [ESI]
POP 9943512
POP OFFSET [410002]