ยŦยк
Üyelik tarihi: Jan 2007
Mesajlar: 11.262
| Tabikki Setcc nin gücünü gösteren Örnek bir kodda yazmak mümkün, mesela asadakki gibi cok fazla kosulun oldugu program parcasinda, C++:
if(r1==r2) r1++;
else if(r2==r1) r2++;
else r1=r2;
if(r1==r2) r1--;
if(r2==r1) r2--;
else if(r2==(r1-1)) r3=r2;
else r2=r3;
Assembler x86:
if0:
Cmp ax, bx
Jne else_if0
Inc ax
Jmp if1
else_if0:
Cmp bx, ax
Jne else0
Inc bx
Jmp if1
else0:
Mov ax, bx
if1:
Cmp r1, r2
Jne if2
Dec ax
if2:
Cmp bx, ax
Jne else_if1
Dec bx
else_if1:
Mov cx, bx
Dec cx
Cmp ax, cx
Jne else1
Mov cx, bx
Jmp cnt
else1:
Mov bx, cx
cnt:
;...
Burda Cmov ve Set komutlarini kullanarak yüksek bir seviyede performans elde ediyoruz, cünkü sadece 4 kere zipliyoruz. Yukarida ise 8 kere.
xor ecx, ecx
cmp eax, edx
jne else_if1
inc eax
jmp if_1
else_if1:
cmp edx, eax
sete cl
cmovne eax, edx
add eax, ecx
if_1:
cmp eax, edx
sete cl
sub eax, ecx
cmp edx, eax
jne else_if2
dec edx
jmp end_if
else_if2:
mov ecx, eax
dec ecx
cmp edx, ecx
xor ecx, ecx
cmove ecx, edx
cmovne edx, ecx
end_if:
Yukaridaki Cmov ve Set ile yazilmis olan kod x86 icin yazilmis koddan cok daha hizli(Tabikki Pentium Pro icin), ziplama komutlari kullanilmadigi icin, islemcinin bir sonraki komutu tespit etme imkani daha yüksek. Bunun disinda ziplama olmadigi icin koddaki CS:EIP register ciftlerinin icerigide degistirilmiyor. Unutmayin Set komutu 8386 dan Cmov komutu ise Pentium Pro dan itibaren gecirlidir.
Eger Assembler i ögrenmek istiyorsaniz, BIOS int. lerini ögrenmelisiniz. Ama eger Dos icin program yapmak(yazmak) istiyorsaniz Dos cagrilarini, Windows icin program yapmak(yazmak) istiyorsaniz Windows API isini ögrenmelisiniz. Ama BIOS int. lerini kesinlikle ögrenmeniz gerekmekte, bunlar size er yada gec lazim olacaktirlar.(Meselaa bir isletim sistemi yazmak istediginizde!)
Yüksek sayilarla calismak
--------------------------------------------------------------------------------
Bir cok dilde 64-Bitlik veri tiplerine raslamak dogal, oysa x86 islemcilerinin 64 Bitlik registerleri bulunmuyor, peki nasil oluyor bu? Öncelikle ilk akla gelen cevap 64-Bit lik bir sayinin 32 Bitinin bir resistere, diger 32 Bittin baska baska bir resistere yüklenmesi. Bu VC++ deki __int64 ile tanimlanabilen veri türü ile ayni islemin yapilmasi anlamina geliyor. Mesela:
mov eax, 0x00ffff00
mov edx, 0x000000ff
Seklinde 0x000000ff00ffff00
(1095233437440) sayisini eax:edx in icine kaydediyoruz. Mesela basit bir toplama islemini su sekilde yapiyoruz,
add eax, eax
adc edx, edx
Burda 1095233437440 sayisini kendisi ile topladik(yada iki ile carptik). Adc komutu add dan farkli olarak önce sayiyi topluyor sonrada üstüne CF in degerini ekliyor. Akla gelen ikinci yöntem ise FPU, bu sayede C/C++ da double adi verilen sayilarla carisma olanagimiz oluyor. 3 üncü ve en iyi tekniklerden biride MMX. MMX 8 tane 64-Bitlik register le geliyor.(MM0…MM7) Ama bu registerler yeni degiller, aksine eski 80-Bitlik FPU registerlerini(ST0…ST7) kullaniyorlar. Ama yaninda bu sayilari islemek icin bircok yeni komutla beraber geliyor MMX, ve bu sekilde sadece bir register icinde bir sayi ile calismak zorunda degiliz. Herhangibir registerin icine 8 tane sayi koyup, 8 ile ayni anda calismamiz bile mümkün. Ama yinede MMX inde eksikleri var, ama bunlar 3Dnow! SSE ve SSE2 ile ortadan kalkmis durumdalar. Yinede size su anda MMX ile calismanizi tavsiye ederim, cünkü hem 3DNow, hem SSE hemde SSE2 MMX i destekliyorlar. Ama su anda SSE2 sadece Pentium 4 tarafindan destekleniyor. TIP: Sayilarin hesaplanmasinda Windows un hesap makinesi, gelismis bölümünü kullanabilirsiniz. Gercektende cok faydali, hem hex, hem binary hemde normal sayilarin hesaplanmasinda.
DOS ve Windows
Ilk programlamaya basliyanlar icin kusursuz bir ögrenim ortami olusturan DOS ayni zamanda bazi isleri gercektende zorlastiriyor. Mesela dosda sayilari ekrana yazmak icin ASCII formatina cevirmeniz gerekiyor ki, bu bir sayinin her basamaginin tek tek cevrilmesi anlamina geliyor. DOS icat edildigi siralarda 8086 bulundugundan dolayi sadece 1MB lik adresleme kapastesine sahip DOS, ve bu DOS un en son sürümü 6.0 ile bile bugün öyle. Bunun disinda Windows 3.x de 16-Bitlik bir isletim sistemi ama 16MB lik adresleme destegi var. Yani Windows 3.x de adresler 8286 ya göre hesaplaniyorlar:
DOS(8086)
adreslemesi=16*Segment+Offset
Windows 3x(8286)
adreslemesi=256*Segment+Offset
Bunun disinda Windows 3.x su anki Windows versiyonlarina göre cok daha farkli bir coklu-kanal programlama teknigi kullaniyor. Buna göre sadece o an secili olan program calisiyor, diger kanallar arkada beklemede kaliyorlar. TSS 16-Bitlik ve cok kanalli programlama mantigi cok basit bir sekilde calisiyor Windows 3.x de. Buna göre TSS 44 Bytes büyüklügünde. Windows 95 ile gelen coklu kanal sisteminde ise her program belli bir aralikla calistiriliyor. Kanallar degistirilirken(Yani sira diger programa geldiginde) TSS(Task Switch Segment) ye programin her registeri ve Flaglari yüklüyor. 32-Bit TSS 104 Bytes büyüklügünde. Windows EDI, ESI, EBX ve EBP registerlerini kullaniyor ve bu degerleri degistirmemenizi bekliyor. Bu yüzden bu registerler kullanilmadan önce yedeklemeniz gerekiyor, aksi halde sistemi cöktürebilirsiniz. Hernekadar 8386 da 4GB gibi bir bellek alani gösterilebilsede, Windows altinda bu sadece 2GB büyüklügünde. Geri kalan 2GB Windows tarafindan ayrilmis, ve kullanimi yasak olarak duruyor. Coklu kanallilik olunca her programin adresleme kismida Windows a yükleniyor. Yani programlarin biribirlerinin degiskenlerini göstermemeleri, vs. Kisimlarida Windows hallediyor. Windows XP ise gercek bir 32-Bit isletim sistemi olma özelligi ile geliyor. Bundan önceki Windows serisi Dos üzerinde aciliyordu, ve daha sonra 32-Bit moda geciyorlardi. Bu yüzden aslinda Windows 9x serisininde 16/32-Bit bir isletim sistemi oldugu söylenebilir. 32-Bit Moda sizde programlariniz icinde DOS dan gecebilirsiniz, bunun icin programiniza su kücük satiri eklemeniz yeterli:
push cr0
; Daha sonra Real-Mode geri dönmek icin.
smsw ax
or ax,0x01 ; Protected –Mode
lmsw ax
; ...
pop cr0
; Geri Real-Mode dayiz simdik
Her ne kadar bir programin icinde Mode degistirmek fazla gerekli bir durum olmasada, burda kisa bir aciklamada bulunayim dedim. SMSW ile CR0 registerinin ilk 16-Bit tini ax e kaydedip, 1 inci biti degistererek Protected mode girmek icin gerekli degeri ax e yüklüyoruz. Daha sonrada LMSW ile ax i CR0 registerine yazarak Protected Mode geciyoruz.
Protected Mode: Windows 9x ve yeni sürümlerinin kullandigi, isletim sistemi Mode u. Bu mod un özelligi degisik programlarin birbirilerine zarar vermelerini engelleyebilmesi. Real- Mode: 8086 ile beraber gelen isletim sistemi Mode u. Pogramlamayi ögrenmek icin kusursuz bir ortam bence. Virtual 8086 Mode: Protected Mode altinda baslatilan fakat 8086 icin yazilmis programlarin calistiklari Mode.
CPUID
Pentium ile birlikte, programinizin üstünde calistigi islemci hakkinda bilgi alabilecegi bir komut geldi, CPUID. CPUID eax registerinin icinde bir fonksiyon numarasi bekler, böylelikle hangi bilgileri göndermesi gerektigini anlar. Bu bilgiler EDX icinde geri dönerler. Daha sonra TEST komutu ile geri dönen bilgilerde istenilen özelligin olup olmadigi anlasilabilir.
Mesela:
mov eax, 0x01
cpuid eax
test edx, 0x0800000
;(23 uncu bit dolu ise MMX vardir.) jnz mmx_var
EKLER
ADC
----------------------------------------
add with carry
ADC O1, O2
ADC iki elemani(O1 ve O2) toplar ve bu sonuca Carry Flags sida ekler.
Örnek:
stc
mov al, 3
mov ah, 2
adc al, ah ; al= ah+al+cf=6
ADD
--------------------------------------
ADD O1, O2
ADD O1 ile O2 yi toplayip O1 in icine yazar.
Tip:
Eger bir registere 1 eklemek
istiyorsaniz INC i kullanin.
DIV
----------------------------------------
division
DIV O1
Bu komut bölme icindir. Eger O1 8 Bit ise AX deki sayi bölünür, kalan AH nin icine ve sonuc AL nin icine yazilir.
Eger O1 16 Bit ise DX:AX icindeki sayi bölünür, kalan DX in icne sonuc AX in icine yazilir. Eger O1 32- Bit ise EDX:EAX deki sayi bölünür, kalan EDX e sonuc EAX e yazilir.
Tip:
Eger bir sayiyi 2 nin katlarina bölmek istiyorsaniz;
SHR komutunu kullanabilirsiniz.
mov ax, 24
mov bl, 8
div bl
yerine;
mov ax, 24
shr ax, 3 (2 üstü 3)
yazarsaniz kodun boyu ve hizi degisecektir.
IDIV
---------------------------------------
interger division
IDIV O1
Bo komut DIV ile hemen hemen aynidir, fark olarak isaretli sayilarlada islem yapabilir.
IMUL
---------------------------------------
interger multiply
IMUL O1, [O2, [O3]]
IMUL iki virgüllü sayiyi carpar.
Ilk olarak eger iki tane adress yada register le kullanirsaniz;
Örnek:
IMUL ax, dx
sonuc ax= ax*dx
Eger ama 3 tane adress yada register kullanirsaniz; Örnek: IMUL ax, dx, cx O zaman ax= dx*cx seklindedir.
MUL
---------------------------------------
multiply
MUL O1
MUL O1 i Akkumulator(AH, AL, AX, EAX) ile carpar.
Tip:
Eger bir sayiyi 2 nin katlariyla carpacaksaniz:
mov al, 5
mov bl, 4
mul bl
yerine;
mov al, 5
shl al, 2
seklinde yazarsaniz programinizin boyutunu ve hizini degistirmis olursunuz.
SBB
---------------------------------------
subtraction with borrow SBB O1, O2 SBB O2 ile Carry- Flag i toplayip sonuctan O1 i cikarir.
SUB
---------------------------------------
subtract
SUB O1, O2
O2 yi O1 den cikarir ve sonucu O1 e yazar.
Tip:
Bir sayidan 1 cikarmak istiyorsaniz,
Örnek:
sub ax, 1
yerine:
dec ax
yazmaniz kodunuzun boyunu ve hizini degisterecektir.
XADD
---------------------------------------
exchange and add
XADD O1, O2
XADD iki elemanin önce iceriklerini degistirir sonrada ADD komutunun yaptigini yapar.
Örnek:
mov al, 3
mob bl, 5
xadd al, bl ; Al= 8, Bl=3
KARSILASTIRMA KOMUTLARI
CMP
Compare
CMP birinci ile ikinci girilen degerleri karsilastirir ve sonucu FLAG lara yazar.
Örnek:
mov ax, 2
cmp ax, 2 ; ZF=1
CMPXCHG
Compare and exchange
Cmpxchg komutu kendisine gelen ilk degeri al, ax veya eax ile(Kendisine gelen ilk degeri boyutuna göre) karsilastirir. Eger degerler esitseler kendisine gelen ilk degere ikinci degeri yükler ve ZF ki doldurur. Örnek:
mov eax, 3
mov edx, 3
lea ecx, [eax+edx*8]
cmpxchg edx, ecx ; edx= ecx
Bunun disinda cmpxchg8b ile 64-Bit lik sayilar üstündede bu islemi yapabilirsiniz. Yalniz cmpxchg8b biraz daha degisik calisiyor, sadece bir tane adres gelmesini bekliyor kendisine, daha sonrada bu adresteki degeri EDX:EAX icindeki deger ile karsilastiriyor. Eger esitlerse ECX:EBX icindeki degeri bu adrese yaziyor.
Örnek:
Cmpxchg8b qword ptr[ebp]
TEST
Test
Test kendisine gelen degeri, ile kendisine gelen ikinci deger arasinda bir “and” islemi yapar. Ama sonucu and komuttundaki gibi kaydetmek yerine sadece FLAG lari degistirir.
FONKSIYON KOMUTLARI:
CALL
Call bir fonksiyonu cagirmak icin kullanilir. Eger bir near-call yapilirsa sadece o anki programin eip(ip) registeri yedeklenir, böylece fonksiyon bittikten sonra programda kalinan yerden devam edilebilir. Eger bir far call yapilirsa hem cs hemde eip(ip) yedeklenir.
INT
Interrupt
Int kendisine gelen 8-Bitlik(0 ile 255 arasinda sayilar) adresdeki aliciyi cagirir.
Örnek:
int 0x21 ; Dos interrupt unu cagirma.
Bunun disinda birde into vardir. Bu komut ise eger OF=1 ise bir int islemi gerceklestirir. Ama bir deger göndermeniz gerekmemektedir, otomatik olarak interrupt 0x04 ü cagirir.
RET
Ret Call ile cagrilan bir fonksiyondan tekrar dönmek icin kullanilir. Eger bir near call yapilmissa eip yi tekrar eski haline getirir. Eger bir far call yapilmissa hem cs yi hemde eip yi eski haline getirir.
MANTIKSAL OPERATORLER
Assembler
C/C++/Java
And
&&
Not !
Or
||
Xor
^
Shl
<<
Shr
>> |