Bir assembly dili programının nasıl çalıştığını anlamak için bilgisayarların nasıl organize edildiğini, çok düşük bir seviyede nasıl çalışıyor göründüklerini anlamak gerekir. En basit düzeyde, bilgisayarların üç ana parçası vardır:
- Veri ve talimatları tutan ana bellek veya RAM,
- talimatları uygulayarak verileri işleyen bir işlemci ve
- giriş ve çıkış (bazen I/O olarak kısaltılır), bilgisayarın dış dünya ile iletişim kurmasını ve verileri daha sonra geri alabilmesi için ana belleğin dışında saklamasını sağlar.
Ana Bellek
Çoğu bilgisayarda bellek baytlara bölünmüştür. Her bayt 8 bit içerir. Bellekteki her baytın, baytın bellekte nerede olduğunu belirten bir sayı olan bir adresi de vardır. Bellekteki ilk baytın adresi 0'dır, bir sonrakinin adresi 1'dir ve bu böyle devam eder. Belleği baytlara bölmek onu bayt adreslenebilir yapar çünkü her bayt benzersiz bir adres alır. Bayt belleklerin adresleri, bir baytın tek bir bitine atıfta bulunmak için kullanılamaz. Bir bayt, adreslenebilen en küçük bellek parçasıdır.
Bir adres bellekteki belirli bir baytı ifade etse de, işlemciler arka arkaya birkaç bayt bellek kullanılmasına izin verir. Bu özelliğin en yaygın kullanımı, genellikle bir tamsayı olan bir sayıyı temsil etmek için arka arkaya 2 veya 4 bayt kullanmaktır. Bazen tek baytlar da tamsayıları temsil etmek için kullanılır, ancak yalnızca 8 bit uzunluğunda olduklarından, yalnızca 28 veya 256 farklı olası değeri tutabilirler. Bir satırda 2 veya 4 bayt kullanmak, farklı olası değerlerin sayısını sırasıyla 216 , 65536 veya 232 , 4294967296'ya yükseltir.
Bir program harf, sayı veya başka herhangi bir şeyi temsil etmek için bir bayt veya bir dizi baytı arka arkaya kullandığında, bu baytlar nesne olarak adlandırılır çünkü hepsi aynı şeyin parçasıdır. Nesnelerin hepsi aynı bellek baytlarında saklansa da, baytların nasıl anlaşılması gerektiğini söyleyen bir 'tür' varmış gibi ele alınır: tamsayı, karakter veya başka bir tür (tamsayı olmayan bir değer gibi). Makine kodu, talimatlar olarak yorumlanan bir tür olarak da düşünülebilir. Tür kavramı çok ama çok önemlidir çünkü nesneye ne yapılıp ne yapılamayacağını ve nesnenin baytlarının nasıl yorumlanacağını tanımlar. Örneğin, pozitif bir sayı nesnesinde negatif bir sayı saklamak geçerli değildir ve bir tamsayıda bir kesir saklamak geçerli değildir.
Çok baytlı bir nesneye işaret eden (onun adresi olan) bir adres, o nesnenin ilk baytının adresidir - en düşük adrese sahip bayt. Bir kenara not edilmesi gereken önemli bir nokta, bir nesnenin türünün ne olduğunu - hatta boyutunu - adresine bakarak söyleyemeyeceğinizdir. Aslında, bir nesnenin ne tür olduğunu ona bakarak bile söyleyemezsiniz. Bir assembly dili programının hangi bellek adreslerinin hangi nesneleri tuttuğunu ve bu nesnelerin ne kadar büyük olduğunu takip etmesi gerekir. Bunu yapan bir program tür güvenlidir çünkü nesnelere yalnızca türlerine göre yapılması güvenli olan şeyleri yapar. Bunu yapmayan bir program muhtemelen düzgün çalışmayacaktır. Çoğu programın aslında bir nesnenin türünün ne olduğunu açıkça saklamadığını, sadece nesnelere tutarlı bir şekilde eriştiğini unutmayın - aynı nesne her zaman aynı tür olarak ele alınır.
İşlemci
İşlemci, ana bellekte makine kodu olarak saklanan talimatları çalıştırır (yürütür). Depolama için belleğe erişebilmenin yanı sıra, çoğu işlemci o anda üzerinde çalışılan nesneleri tutmak için birkaç küçük, hızlı, sabit boyutlu alana sahiptir. Bu alanlara register adı verilir. İşlemciler genellikle üç tür talimat yürütür, ancak bazı talimatlar bu türlerin bir kombinasyonu olabilir. Aşağıda x86 assembly dilindeki her türün bazı örnekleri verilmiştir.
Bellek okuma veya yazma talimatları
Aşağıdaki x86 assembly dili komutu, 4096 (onaltılık olarak 0x1000) adresindeki bayttan 2 baytlık bir nesneyi 'ax' adlı 16 bitlik bir yazmaca okur (yükler):
mov ax, [1000h]
Bu assembly dilinde, bir sayının (veya bir register adının) etrafındaki köşeli parantezler, sayının kullanılması gereken verinin adresi olarak kullanılması gerektiği anlamına gelir. Veriye işaret etmek için bir adresin kullanılmasına dolaylama denir. Bir sonraki örnekte, köşeli parantezler olmadan, başka bir yazmaç olan bx'e aslında 20 değeri yüklenir.
mov bx, 20
Dolaylama kullanılmadığından, gerçek değerin kendisi yazmacın içine yerleştirilmiştir.
İşlenenler (anımsatıcıdan sonra gelenler) ters sırada görünürse, bellekten bir şey yükleyen bir komut bunun yerine belleğe yazar:
mov [1000h], ax
Burada, 1000h adresindeki bellek ax değerini alır. Bu örnek bir öncekinden hemen sonra çalıştırılırsa, 1000h ve 1001h adreslerindeki 2 bayt, 20 değerine sahip 2 baytlık bir tamsayı olacaktır.
Matematiksel veya mantıksal işlemler gerçekleştiren talimatlar
Bazı talimatlar çıkarma gibi işlemler veya değil gibi mantıksal işlemler yapar:
Bu makalenin başlarındaki makine kodu örneği assembly dilinde bu şekilde olacaktır:
ax ekle, 42
Burada, 42 ve ax toplanır ve sonuç tekrar ax içinde saklanır. x86 assembly'de bir bellek erişimini ve matematiksel işlemi bu şekilde birleştirmek de mümkündür:
ax ekle, [1000h]
Bu komut, 1000h'de saklanan 2 baytlık tamsayının değerini ax'ye ekler ve cevabı ax'de saklar.
veya ax, bx
Bu komut ax ve bx kayıtlarının içeriğinin or değerini hesaplar ve sonucu ax'e geri kaydeder.
Bir sonraki talimatın ne olacağına karar veren talimatlar
Genellikle talimatlar bellekte göründükleri sırayla, yani assembly kodunda yazıldıkları sırayla yürütülür. İşlemci bunları birbiri ardına çalıştırır. Ancak, işlemcilerin karmaşık şeyler yapabilmesi için, kendilerine verilen verilerin ne olduğuna bağlı olarak farklı talimatları yürütmeleri gerekir. İşlemcilerin bir şeyin sonucuna bağlı olarak farklı talimatlar yürütme yeteneğine dallanma denir. Bir sonraki talimatın ne olması gerektiğine karar veren talimatlara dallanma talimatları denir.
Bu örnekte, birinin belirli bir kenar uzunluğuna sahip bir kareyi boyamak için ihtiyaç duyacağı boya miktarını hesaplamak istediğini varsayalım. Ancak, ölçek ekonomisi nedeniyle boya mağazası bu kişiye 100 x 100'lük bir kareyi boyamak için gereken boya miktarından daha azını satmayacaktır.
Boyamak istedikleri karenin uzunluğuna bağlı olarak almaları gereken boya miktarını hesaplamak için bu adımlar dizisini buluyorlar:
- kenar uzunluğundan 100 çıkarın
- cevap sıfırdan küçükse, kenar uzunluğunu 100 olarak ayarlayın
- kenar uzunluğunu kendisiyle çarpın
Bu algoritma, ax'in kenar uzunluğu olduğu aşağıdaki kodla ifade edilebilir.
mov bx, ax alt bx, 100 jge devam et mov ax, 100 devam et: mul ax
Bu örnek birkaç yeni şeyi tanıtmaktadır, ancak ilk iki talimat tanıdıktır. Bunlar ax değerini bx'e kopyalar ve ardından bx'ten 100 çıkarır.
Bu örnekteki yeni şeylerden biri, genel olarak assembly dillerinde bulunan bir kavram olan etiket olarak adlandırılır. Etiketler programcının istediği herhangi bir şey olabilir (assembler'ın kafasını karıştıracak bir komut adı olmadığı sürece). Bu örnekte etiket 'continue' şeklindedir. Bu etiket assembler tarafından bir komutun adresi olarak yorumlanır. Bu durumda, mult ax'in adresidir.
Bir diğer yeni kavram ise bayraklardır. X86 işlemcilerde, birçok komut işlemcide ne yapılacağına karar vermek için bir sonraki komut tarafından kullanılabilecek 'bayraklar' belirler. Bu durumda, bx 100'den küçükse, sub sonucun sıfırdan küçük olduğunu söyleyen bir bayrak ayarlayacaktır.
Bir sonraki komut 'Jump if Greater than or Equal to' ifadesinin kısaltması olan jge'dir. Bu bir dallanma komutudur. İşlemcideki bayraklar sonucun sıfırdan büyük veya sıfıra eşit olduğunu belirtirse, işlemci bir sonraki komuta gitmek yerine devam etiketindeki komuta, yani mul ax'a atlar.
Bu örnek iyi çalışıyor, ancak çoğu programcının yazacağı gibi değil. Çıkarma komutu bayrağı doğru şekilde ayarlar, ancak üzerinde çalıştığı değeri de değiştirir, bu da ax'in bx'e kopyalanmasını gerektirir. Çoğu assembly dili, geçtikleri argümanların hiçbirini değiştirmeyen, ancak yine de bayrakları doğru şekilde ayarlayan karşılaştırma komutlarına izin verir ve x86 assembly bir istisna değildir.
cmp ax, 100 jge devam et mov ax, 100 devam et: mul ax
Şimdi, ax'den 100 çıkarmak, bu sayının sıfırdan küçük olup olmadığına bakmak ve ax'ye geri atamak yerine, ax değişmeden bırakılır. Bayraklar hala aynı şekilde ayarlanır ve atlama hala aynı durumlarda yapılır.
Giriş ve Çıkış
Giriş ve çıkış bilgi işlemin temel bir parçası olsa da, assembly dilinde bunların yapılmasının tek bir yolu yoktur. Bunun nedeni, G/Ç'nin çalışma şeklinin sadece ne tür bir işlemciye sahip olduğuna değil, bilgisayarın kurulumuna ve çalıştırdığı işletim sistemine bağlı olmasıdır. Örnekler bölümünde Merhaba Dünya örneği MS-DOS işletim sistemi çağrılarını, ondan sonraki örnek ise BIOS çağrılarını kullanmaktadır.
Assembly dilinde I/O yapmak mümkündür. Aslında, assembly dili genellikle bir bilgisayarın yapabildiği her şeyi ifade edebilir. Bununla birlikte, assembly dilinde her zaman aynı şeyi yapan toplama ve dallanma talimatları olsa da, assembly dilinde her zaman G/Ç yapan hiçbir talimat yoktur.
Dikkat edilmesi gereken önemli nokta, G/Ç'nin çalışma şeklinin herhangi bir assembly dilinin parçası olmadığıdır çünkü işlemcinin çalışma şeklinin bir parçası değildir.