Delphi Can

Orjinalini görmek için tıklayınız: Delphi ve Assembly
Şu anda (Arşiv) modunu görüntülemektesiniz. Orjinal Sürümü Görüntüle internal link
DELHPI VE ASSEMBLY
 
Merhaba,

Bir delphi inline assembly eğitim makale serisi başlatmak istiyorum. Makale serisi x64 Windows programlama üzerine olacak.
 
1: DELPHI İLE ASSEMBLY’E GİRİŞ
 
Her şeyden önce projeye Windows x64 platformunu eklemeliyiz.
 
Kodlarımız asm ve end; blokları arasında yer almalı.
 

procedure Test;
asm
 
end;

 
Aşğıdaki gibi bir kullanımda assembly kodu yazmamız mümkün olmaz:
 

procedure Test;
begin
 
end;

 
Hemen bir sayı döndüren fonksiyon tasarlayalım. Projeye bir unit ekleyelim ve şu şekilde kodlayalım:
 

unit Unit2;
 
interface
 
function Test: int64;
 
implementation
 
function Test:int64;
asm
    mov rax, 1234
end;
 
end.

 
Bu unit’i vcl uygulaması yapıyorsanız formun unit’ine ekleyelim. Ben konsol uygulamasına ekledim:
 

program Project1;
 
{$APPTYPE CONSOLE}
 
{$R *.res}
 
uses
  System.SysUtils,
  Unit2 in 'Unit2.pas';
 
var
  sayi: int64;
begin
  try
      sayi:=Test;
      writeln(inttostr(sayi));
      Readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

 
Yukarıda da gördüğünüz gibi sayi adında bir 64 bit integer değişken tanımladım ve Test fonksiyonunu bu değişkene atadım. Daha sonra da konsola writeln ile yazdırdım. Konsolda 1234 yazını gördüm. Peki bu nasıl oldu? Test fonksiyonu nasıl 1234 değerini döndürdü?
 
Hemen test fonksiyonumuzu inceleyelim:
 
mov rax, 1234
 
mov assembly dilinde bulunan atama komutudur. rax ise genel amaçlı kullanılan registerdir. Fonksiyonlar değerleri rax registeri üzerinden döndürür. Bizim return ya da result ile değer döndürmemize gerek yok.
 
Bu arada komutları büyük ya da küçük yazmanızın bir önemi yok.
 
Şimdi biraz değişiklik yapalım:
 

function Test: int64;
asm
    mov rax, 1234
    inc rax
end;

 
Tekrar programı çalıştırdığımızda bu sefer konsol ekranında 1235 yazdığını göreceksiniz. mov ile rax’e 1234 değerini atadıktan sonra inc komutuyla rax’deki değeri 1 arttırdık. Evet inc komutu Delphi’deki gibi sayıya bir ekler. Tam tersi de dec komutudur. dec komutu sayıyı bir eksiltir.
 

function Test: int64;
asm
    mov rax, 1234
    dec rax
end;

 
Çalıştırdığınızda konsolda 1233 yazacaktır.
 
Fonksiyonumuzu aşağıdaki şekilde değiştirelim:
 

function Test(a:int64):int64;
asm
    mov rax, a
end;

 
Bu fonksiyonda bir parametre kullandık. Ve doğrudan bu parametreyi rax registerine atadık ve yine rax ile fonksiyonun değeri döndürüldü.
 

      sayi:=Test(5);
      writeln(inttostr(sayi));
      Readln;

 
Bu kod ile konsolda 5 yazısını gördüm.
 
Parametreleri doğrudan assembly içinde kullanabiliyoruz. Ancak başka ve daha iyi bir yöntem daha var.
 
Öncelikle registerlerden biraz söz edelim. X64 işlemcilerdeki bazı genel amaçlı registerler şöyledir:
rax, rbx, rcx, rdx, r8, r9, r10, r11. Biz (rbx hariç) genelde bunları kullanacağız. rbx registerine bulaşmak istemememin nedeni daha önceden başka bir fonksiyon ile rbx registerine atanan verinin daha sonra başka bir fonksiyon tarafından kullanılacak olması. rbx’i stack’e kaydedip daha sonra stack’ten tekrar yüklemekle uğraşmak istemiyorum. Stack olayını ilerleyen makalelerde anlatacağım.
 
Windows x64’te parametreler fonksiyonlara soldan sağa sırasıyla rcx, rdx, r8 ve r9 registerlerine otomatik olarak aktarılır. Yukarıdaki fonksiyonda a parametresi rcx registerine otomatik atanmıştı. Bu fonksiyonunun daha güzel versiyonu şöyle olacak:

 
function Test(a:int64):int64;
asm
    mov rax, rcx
end;

 
Ekrana tekrar parametre a’nın değerini yazdırdı.
 
Şimdi bir toplama fonksiyonu yazalım. Unit’imizi şu şekilde değiştirelim:
 

unit Unit2;
 
interface
 
function Topla(a,b:int64):int64;
 
implementation
 
function Topla(a,b:int64):int64;
asm
    mov rax, rcx
    add rax, rdx
end;
 
end.

 
Dpr dosyamız da şöyle olmalı:
 

program Project1;
 
{$APPTYPE CONSOLE}
 
{$R *.res}
 
uses
  System.SysUtils,
  Unit2 in 'Unit2.pas';
 
var
  sayi:integer;
begin
  try
      sayi:=Topla(5,6);
      writeln('Topmlam='+inttostr(sayi));
      Readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

 
Konsolda 11 yazıldığını göreceksiniz.
 
mov rax, rcx kodu ile rax registerine rcx registerinde yer alan a’nın değerini atadık. Bu satırdan itibaren rax’in aldığı değer a’nın değeridir.
 
add rax, rdx satırı ile de rax registerine rdx registerinde yer alan b’nin değerini atadık. Bu satırdan itibaren rax’in aldığı değer a’nın ve b’nin değerleri toplamıdır. Otomatik olarak fonksiyon rax değerini döndürecektir.
 
Bir de çıkartma fonksiyonu yazalım:
 

function Cikar(a,b:int64):int64;
asm
    mov rax, rcx
    sub rax, rdx
end;

 
sub komutu ilk yazılan operanddan ikincisini çıkaracaktır. İşlemin sonucu ilk operandda olacaktır.
 
Unit’imizin son hali:
 

unit Unit2;
 
interface
 
function Topla(a,b:int64):int64;
function Cikar(a,b:int64):int64;
 
implementation
 
function Topla(a,b:int64):int64;
asm
    mov rax, rcx
    add rax, rdx
end;
 
function Cikar(a,b:int64):int64;
asm
    mov rax, rcx
    sub rax, rdx
end;
 
end.

 
Dpr dosyamızın son hali:
 

program Project1;
 
{$APPTYPE CONSOLE}
 
{$R *.res}
 
uses
  System.SysUtils,
  Unit2 in 'Unit2.pas';
 
var
  sayi:integer;
begin
  try
      sayi:=Topla(5,6);
      writeln('Topmlam='+inttostr(sayi));
      sayi:=Cikar(5,6);
      writeln('Fark='+inttostr(sayi));
      Readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

 
Bir sonraki makalede registerler üzerine biraz daha açıklama yapacağım.

Bu makale serisi özgün olup ilk defa Delphican sitesinde yayımlıyorum.

Faydalı olması ümidiyle.
 
Hakan DİMDİK
Teşekkürler
@PROGRAMADOR35; emeğinize sağlık.

Lütfen paylaşımlarınızda başlığın tamamını büyük harfle yazmayın.(Forum kurallarına aykırı)
Bundan önceki paylaşımda ben düzeltmiştim. Bunu ve sonrakileri düzenlerseniz iyi olur.
DELPHI VE ASSEMBLY
 
2: X64 REGISTERLER

Registerleri ana hatlarıyla anlatacağım. Burada zorlanacağınız bazı konular olabilir ama hiç kafanıza takmayın. En azından aklınızda registerlerla ilgili bir şeyler kalsın diye bu konuyu anlatma gereği duydum. Asıl amacımız konuları kod yazarak öğrenmek.

X64 registerlerinin başlıcaları şu şekildedir:

  • Genel Amaçlı Registerler:  rax, rbx, rcx, rdx
  • r8, r9, r10, r11, r12, r13, r14, r15 registerleri
  • Indeks Registerleri: rsi, rdi
  • Özel Amaçlı Stack İle İlgili Registerler: rbp, rsp, rip
  • FLAGS registeri
  • Segment Registerleri: cs, ds, ss, es



Bu registerler en fazla 64 bit (8 byte) veri kaydedebilir. 8 byte’tan daha düşük verilerin kaydedilmesi için isimlerinde değişiklik yapılması gerekir. İsim değişse bile yine de aynı registeri kullanmaktayız.
 
mmx, xmm-, ymm- ve zmm- registerlerine bu çalışmamızda yer vermeyeceğiz. Belki bir başka ders altında bunlara değinebiliriz.


RAX, RBX, RCX VE RDX REGİSTERLERİ
 
Bu dört register genel amaçlı registerlerdendir.
 
Rax registerine accumulator, rbx registerine base, rcx registerine counter ve rdx registerine data registeri denmektedir. Programcıların kod yazma alışkanlıklarına göre bu şekilde isim verilmiştir.
 
Bu registerlerin şimdi alabileceği verilere göre isimlendirmelerini görelim:


64 bit Register    -   Düşük Anlamlı 32 bit   -   Düşük Anlamlı 16 bit

rax                         eax                               ax
rbx                         ebx                               bx
rcx                         ecx                               cx
rdx                         edx                               dx


Yüksek Anlamlı 8 Bit    -   Düşük Anlamlı 8 Bit

ah                                  al
bh                                  bl
ch                                  cl
dh                                  dl

64 bitlik bir register'de soldan sağa doğru gittiğimizde ilk byte olarak al ile karşılacağız. bir solundaki ikinci bit ise ah olacak. al ve ah birleşiminden iki bytelık (16 bitlik) ax oluşacak. ax ve solundaki iki bytelık kısım birleşip 4 bytelık (32 bit) eax kısmını oluşturacak. Yine eax ve solundaki 4 bytelık kısım birleşerek rax'i oluşturacak.

Biraz daha açıklayalım:

rax registerinin düşük anlamlı olan al kısmı 1 bytelik veri alır ve 1 bytelık verinin alabileceği en yüksek değer 255’tir. Bu 2'lik sayı sisteminde 11111111 ile 16’lık sayı sisteminde FF ile ifade edilir. Bir bytelık kısmın en büyük değerinin 255 olmasının nedeni anlaşılacağı üzere byte'ın 8 bitten oluşması ve alınabilecek en büyük değerin tüm bitlerin 1'lerle dolu olmasıyla ilgilidir. 2'lik sistemde 11111111 sayısı 255 eder.

Daha önce de belirttiğimiz gibi al’nin anlamı rax registerinin en düşük byte’ı (en düşük 8 biti yada düşük anlamlı byte’ı) anlamına gelir.  ah kısmı ise al’den sonra gelen ilk bytelık kısımdır. Bunun da alabileceği en büyük değer 255’dir. Her byte’ın alabileceği değer en fazla 255 olacaktır. ah kısmına ax’in yüksek anlamlı byte’ı ya da bitleri diyebiliriz.

rax registerinin al ve ah kısımlarının birleşiminden ax kısmı oluşmaktadır. Ax kısmı her iki registerin birleşiminden oluştuğu için toplamda 16 bitlik yani 2 bytelık veri alabilir. 2 bytelık verinin alabileceği en küçük değer 0 ve en büyük değer 65535’tir. Bu değer ikilik sayı sisteminde 11111111 11111111 ve 16’lık sayı sisteminde FFFF’ye işaret eder.

Yine 2 bytelık ax registerinin iki katı büyüklüğünde 4 bytelık eax registeri bulunmaktadır ve onun da iki katı büyüklüğünde 8 bytelık (64 bitlik) rax registeri bulunmaktadır. 32 bit işletim sistemlerindeki en büyük genel amaçlı değişken e- öneki ile başlayan değişkenlerdir. Bu değişkenler 64 bit işletim sistemlerinde r- önekini alır ve boyutu da iki katı fazladır.

Yukarıdaki açıklamalar rbx, rcx ve rdx için de geçerlidir:

64 bit       32 bit      16 bit      8 bit h       8 bit l
rax     <-   eax   <-    ax     <-   ah       <-   al
rbx     <-   ebx   <-    bx     <-   bh       <-   bl
rcx     <-   ecx   <-    cx     <-   ch       <-   cl
rdx     <-   edx   <-    dx     <-   dh       <-   dl

Genel amaçlı 64 bitlik ve 32 bitlik registerlerin yüksek anlamlı kısımları ayrı bir register gibi kullanılamamaktadır.

r8,r9,r10,r11 REGİSTERLERİ
 
Aslında bu registerler r8’den r15’e kadardır. Ancak, serbestçe kullanacağımız registerler r8-r11 arası registerlerdir. r8-r11 arası registerler de tıpkı rax, rbx, rcx, ve rdx registerleri gibi genel amaçlı registerlerdir. Ancak düşük bytelara erişim biraz farklıdır:
 

64 bit Register    -   Düşük Anlamlı 32 bit   -   Düşük Anlamlı 16 bit   -   Düşük Anlamlı 8 bit
 
r8                          r8d                             r8w                               r8b
r9                          r9d                             r9w                               r9b
r10                         r10d                           r10w                               r10b
r11                         r11d                           r11w                               r11b

RSİ VE RDİ REGİSTERLERİ (INDEKS REGİSTERLERİ)
 
RSI ve RDI registerleri indeks registerleridir. Bu registerlere ingilizce “source index” ve “destination index” registerleri denmektedir. Bu isimlerin verilmesinin sebebi bazı assembly fonksiyonlarının bu registerleri otomatik olarak kullanmasıdır. Dolayısıyla biz rsi ve rdi’ye değerlerimizi atayıp fonksiyonu çağırınca fonksiyon işlemi gerçekleştirecektir. İlerde daha ayrıntılı bir şekilde bu değişkenleri ele alacağız.

RBP, RSP VE RIP REGİSTERLERİ
 
Bu registerler özel amaçlı registerlerdir.
 
RIP (Instruction Pointer) çalışan makine komutunun o andaki RAM’deki adresini içerir. Bu registerle fazla işimiz olmayacak.
 
RSP (Stack Pointer) halihazırda çalışan uygulamada stackin o andaki adresini göstermektedir. Stack pointer RAM’deki stack alanında aşağı doğru (alt adrese doğru) ilerler. Ram’deki stack alanına veri eklendikçe stack pointer da aşağı doğru iner. Bulunduğu yer stack’in tepe noktasıdır.
 
rbp frame pointer olarak adlandırılmaktadır. Rbp halihazırdaki fonksiyon, method ya da prosedürünün başlangıç stack adresidir. Rsp ise o fonksiyon, method ya da prosedürde stack’in tepe noktasıdır. Fonksiyon, method ya da prosedürde local değişken arttıkça rsp’nin değeri değişmesine rağmen rbp’nin değeri o fonksiyon, method ya da prosedürde değişmeyecektir.
 
Yerel değişkenler rbp registerinin altında sıralanırlar. En altta rsp registeri yer alır. Stack’in aşağıya doğru ilerlediğini aklımızda tutalım.

FLAGS REGİSTERİ
 
Bitleri önem arzeden yazmaçtır. Bu registerdeki önemli bitlere isim verilmiştir. Assembly komutları bazı durumlarda bu bayrakların değişmesine neden olurlar. Bu bayraklar bitler olduğundan 1 ya da 0 değerini alır. Yapılan işlemin sonucunu kontrol etmede bu bayraklar örnem arzetmektedir. Jump komutlarının çoğu bu bayraklara göre yapılmaktadır.
 
JZ komutu Zero flag biti 1 ise çalışan bir komuttur. Şu aşamada sadece genel bilgiler veriyorum. Daha sonra örnek kodlarla daha iyi anlaşılacaktır. Bu konuya da şimdilik çok fazla değinmeye gerek yok.
Teşekkürler @PROGRAMADOR35
DELPHI VE ASSEMBLY

3: PROSEDÜR VE FONKSİYONLAR HAKKINDA

Yeni bir Windows VCL Application oluşturalım ve x64 platformu aktif edelim. Kod görünümü şu şekilde olacaktır:


unit Unit3;
interface
uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
type
  TForm3 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form3: TForm3;
implementation
{$R *.dfm}
procedure TForm3.FormCreate(Sender: TObject);
begin

end;
end.

Bir fonksiyon oluşturarak kodumuzu şu şekilde düzenleyelim.

unit Unit3;
interface
uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
type
  TForm3 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form3: TForm3;
implementation
{$R *.dfm}
function birarttir(sayi:integer):integer;
asm
    .noframe
    inc rcx
    mov rax, rcx
end;
procedure TForm3.FormCreate(Sender: TObject);
begin
    ShowMessage(inttostr(birarttir(5)));
end;
end.

Bu kodda oluşturduğumuz fonksiyonu adım adım inceleyelim:

function birarttir(sayi:integer):integer;

Bu kısım bizim fonksiyonumuzun baş kısmı. Fonksiyon tanımlamamızı burada yaptık. Fonksiyonumuzun ismi birarttir. Fonksiyonun döndürdüğü değer integer. Bu fonksiyon sayi isminde bir integer tipinde parametre alıyor.

Alt satırda asm anahtar kelimesini kullanarak asm bloğumuzu açtık. Burada fonksiyonumuzun bir assembly fonksiyonu olduğunu da belirtmiş olduk.

Bir alt satırda .noframe yazdık. Peki bu .noframe ne anlama geliyor? Daha önceden de belirttiğim gibi fonksiyon ve prosedürlerde ilk dört parametre registerlere aktarılır. Yani ram’in stack alanının kullanılması gerekmez. Parametreler sırasıyla rcx, rdx, r8 ve r9 parametrelerine aktarılır. Dörtten fazla parametre alan fonksiyon ya da prosedür söz konusu ise program stack alanı kullanmak zorundadır. Biz .noframe diyerek stack alan kullanmadığımızı derleyiciye bildirmiş oluyoruz. Bu ayrıca yerel değişken kullanmadığımız anlamına da gelmektedir. Çünkü yerel değişkenler de stack’te yer almaktadır. Eğer yerel değişken kullanacaksak .noframe’in hiçbir anlamı yoktur. Kodu incelediğinizde yerel değişkenimizin olmadığını ve fonksiyonumuzda sadece bir parametremizin olduğunu görüyoruz. Parametresi en fazla dört olan ve yerel değişken almayan fonksiyonlara leaf fonksiyon denilmektedir. Leaf fonksiyon kullanımı programımıza hız katacaktır. Bu nedenle registerler kullanılabilecek iken mümkün mertebe 4’ten fazla parametre kullanmamalı ve yerel değişken tanımlamamalıyız.

Bizim sayi parametremiz otomatik olarak rcx registerinde yer alıyor. Daha sonra inc rcx diyerek rcx’i 1 arttırıyoruz. Akabinde de mov rax, rcx diyerek rcx’deki değeri rax’e atıyoruz. Peki neden? Çünkü fonksiyonlar dönen değerleri rax’ten döndürür.

dönen değer 64 bit ise rax, 32 bit ise eax, 16 bit ise ax ve 8 bit ise al registerlerinden dönen değere ulaşılabilir. Biz burada 32 bit integer’e 64 bit olan rax registerinden ulaştık. Bu da mümkün. Çünkü rax’e 6 değerini atayabiliriz. Ve dönen değeri ister rax’ten ister eax’ten alırız. Ancak normal kullanım hata yapmamak adına boyuta göre almaktır.

Şimdi şu prosedürü inceleyelim:


procedure TForm3.FormCreate(Sender: TObject);
begin
    ShowMessage(inttostr(birarttir(5)));
end;


Parantezlerin en iç tarafından başlayalım. birarttir(5) diyerek bize rax ile birarttir fonksiyonu 6’yı döndürecektir. Daha sonra inttostr ile 6’yı string ‘6’ya döndürdük ve ShowMessage ile de ekranda gösterdik.

Şimdi unitimizi şu şekilde değiştirelim:


unit Unit3;
interface
uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
type
  TForm3 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form3: TForm3;
implementation
{$R *.dfm}
function dortsayitopla(a,b,c,d:integer):integer;
asm
    .noframe
    mov eax, ecx
    add eax, edx
    add eax, r8d
    add eax, r9d
end;
procedure TForm3.FormCreate(Sender: TObject);
begin
    ShowMessage(inttostr(dortsayitopla(5,6,7,8)));
end;
end.

Bu fonksiyon içinde a,b,c,d değişkenleri adlarının yerine sırasıyla rcx, rdx, r8 ve r9 registerlerini doğrudan kullanabiliriz. Biz burda 4 byte, yani 32 bit integer değişkeni kullandığımız için sırasıyla ecx, edx, r8d, r9d registerlerini kullandık.

Fonksiyonumuzu inceleyelim: .noframe diyerek leaf fonksiyon oluşturduğumuzu derleyiciye bildirdik. Dönen değer eax’ten döneceği için eax üzerinden tüm işlemleri gerçekleştirdik. Öncelikle mov eax, ecx diyerek ilk parametremizi eax’e atadık. Daha sonra add eax, edx diyerek eax’teki değere edx’deki değeri ekledik. Bu aşamada eax’deki değer 5+6 =11’dir. add eax, r8d diyerek eax’e r8d’deki 32 bitlik 7 değerini içeren integeri ekledik. 11+7=18. Daha sonra da add eax, r9d diyerek 18+8 işlemini gerçekleştirmiş olduk. eax 26 değerini almış olup dönen değeri ister rax’ten, ister eax’ten alabiliriz.

Etiketler (labels):

Asm fonksiyon ve prosedürleri içinde etiket düzenleyebiliriz. Bu delphi’deki label’lardır. Ancak burada dikkat edilmesi gereken husus labelların local düzeyde tanımlanması gerektiğidir. Etiketimizin başına @ ya da @@ koyarak labelları local düzeyde tanımlayabiliriz. Label’ın local olması demek o fonksiyon ya da prosedür içinde geçerli olması demektir.

Herhangi bir satırda label tanımlamamız mümkündür. Dortsayitopla fonksiyonumuzu şu şekilde yazabilirdik:


function dortsayitopla(a,b,c,d:integer):integer;
asm
    .noframe
@satir1:    mov eax, ecx
@satir2:    add eax, edx
@satir3:    add eax, r8d
@satir4:    add eax, r9d
end;

ya da şu şekilde de olabilirdi:


function dortsayitopla(a,b,c,d:integer):integer;
asm
    .noframe
@satir1:
    mov eax, ecx
@satir2:
    add eax, edx
@satir3:
    add eax, r8d
@satir4:
    add eax, r9d
end;

Labelların başında @ ve sonunda : olduğuna dikkat edelim. Tabi dortsayitopla fonksiyonunda label yazmanın pek bir önemi yok ama döngü içeren fonksiyonlar söz konusu olduğunda labelların önemi anlaşılacaktır.

1’den n’e kadar sayıların toplamını bulan fonksiyonu yazalım:


function nsayitopla(n:uint64):uint64;
asm
    .noframe
    mov rax, rcx
    dec rcx
@dongu:
    add rax, rcx
    loop @dongu  //rcx'i bir eksiltip rcx sıfır olana dek @dongu etiketine gidiyoruz
end;
procedure TForm3.FormCreate(Sender: TObject);
begin
    ShowMessage(inttostr(nsayitopla(6)));
end;

Şimdi ne yaptığımızı inceleyelim: function nsayitopla(n:uint64):uint64; tanımlamasına baktığımızda uint64 sayı türünü görüyoruz. Büyük sayılarda sorun yaşamamak adına uint64 kullandık. Tabi uint64 tipinde negatif sayı kullanmayacağız. .noframe diyerek leaf fonksiyon olduğumuzu belirttik. mov rax, rcx diyerek rax’e rcx içindeki değeri atadık. Örneğimizde rcx’teki 6 değerini rax’e atamış olduk. dec rcx diyerek rcx’deki değeri bir azalttık ve rcx artık 5. @dongu: etiketini yerleştirdik. add rax, rcx diyerek rax’teki 6 değeri ile rcx’teki 5 değerini topladık. Artık rax’imiz 11 oldu. Şimdi kilit nokta geliyor: loop @dongu. Burada ne oldu. loop bir assembly komutudur ve rcx’deki değere göre kodda atlama yapar. Burada şunu diyoruz rcx’deki değer sıfır değilse @dongu’ye atla. Yani kodumuz @dongu etiketinin olduğu yerden devam eder çalışmaya. Bu döngü rcx değeri sıfır olana dek tekrarlanır. Bu arada rcx değerini hiç eksiltmediğimizi farketmiş olmalısınız. loop komutu her çalıştığında rcx değerini otomatik olarak bir azaltır. nsayitopla(6) fonksiyonu 6+5+4+3+2+1= 21 değerini döndürecektir. Bu fonksiyonu delphi kodu ile yazmış olsaydık değişken tanımlayacaktık. Assembly ile hiç değişken tanımlamadan halletmiş olduk.

Bir sonraki dersimizde karşılaştırma ve jump komutları üzerinde kod yazarak duracağız.

Şimdiye kadar öğrenmiş olduğumuz assembly komutları: mov, add, sub, inc, dec, loop. Daha bunlar bir şey değil. İzlemeye devam edin.

Hakan DİMDİK
Teşekkürler Hakan bey. Keyifli ve faydalı bir seri.
Devamını bekleriz.
(12-01-2021, Saat: 20:58)frmman Adlı Kullanıcıdan Alıntı: [ -> ]Teşekkürler Hakan bey. Keyifli ve faydalı bir seri.
Devamını bekleriz.

Ben teşekkür ederim. Beğenmenize sevindim.