Merhaba,
Assembly ile alakalı birkaç örnek koddan oluşan bir konunun ilgi çekeceğini düşündüm. VisUAL''den dolayı da ARM Assembly ile yapmak istedim. Öncesinden belirtmeliyim ki bu bir örnek kod konusu, rehber değil. Assembly hakkında kısaca bilgi vereceğim ama "Assembly nedir, ne yapar, ne işe yarar" gibi sorulara cevap vermeyecektir. Zira bu, uzun bir rehber gerektiriyor. Sadece birkaç örnek kod paylaşacağım.
Bildiğiniz gibi bilgisayarlar, işlemcilerden oluşur. İşlemcilerin ise, kendilerine has dilleri vardır. Bu dile, kısaca 0 ve 1'lerden oluşan, işlemcinin Bitler vasıtasıyla iletişim kurduğu dil diyebiliriz. Makine dilinin nasıl kullanılacağı işlemci türüne, mimarisine göre değişir.
İşlemciyle ancak makine dilini kullanarak anlaşabilirsiniz. Bu dil, alıştığımız programlama dillerine göre oldukça farklıdır ve öğrenmesi zordur. Zaten temelinde bu zorluk ve bir noktada işin çok uzaması sebebiyle, günüzdeki programla dilleri var. (C, Java, Python gibi). Bu dilleri kullanırken compiler (derleyici) diye bir terim duymuşsunuzdur. İşte compiler, sizin Python ya da C gibi programlama dilinde yazdığınız kodu, işlemci mimarinize uygun makine diline çeviirir. Bu sayede işlemciniz, yazdığınız kodu anlar ve çalıştırır.
Assembly, makine diline oldukça yakın bir dildir. Neredeyse makine diline karşılık programlar yazabilirsiniz. Hatta breadboarda yapılmış 8 Bit bilgisayarlar bile uygun Assembly ile programlanabilir. Bu yakınlık sebebiyle de "Low level" dil olarak geçer. Low ve High level terimleri karşınıza çokça çıkmıştır. O nedenle şu diagramı bırakmak istiyorum:
Assembly dilinde yazılmış programlar bir Assembler tarafından derlenir. Her Assembler’ın belirli bir bilgisayar mimarisi için tasarlanmış kendi Assembly dili vardır. Bu nedenle X86 mimaride kullandığınız Assembly kodu ile ARM (RISC) mimaride işlem yapamazsınız. Bu nedenle de aynı makine dili gibi, taşınabilir değildir.
RISC ve X86 arasında temel farklar var. Zaten bu farklar, RISC'i X86'ya göre daha sade yapıyor. Ben de hem RISC hem de yeni trendin ARM olması sebebiyle, örneklerimi ARM diliyle vermeye karar verdim.
X86 ve ARM arasındaki farklar için yazımıza bakabilirsiniz:
VisUAL2
Bahsettiğim gibi, Assembly diline derlemek için Assembler'e ihtiyacımız var. ARM ile çalışacağımızdan, bu işi X86 makinelerimizde yapmak için, Assembler'e ek bir emülatöre de ihtiyacımız var.
VisUAL2, hem Assembler hem de emülatör görevini tek başına yerine getiren, beğendiğim güzel bir proje. GİtHub reposuna buradan ulaşabilirsiniz. Yine buradan indirebilirsiniz:
Kurulumu yapıp çalıştırdıktan sonra, karşınıza şöyle bir ekran çıkacak:
Üst menüden başlayalım:
Standart bir kod ile başlayalım:
Şimdi, kodu çalıştıralım ve sonuca bakalım:
Gördüğünüz gibi sağdaki register değerlerimiz değişmiş oldu.
Peki burada ne yaptık?
Yani aslında yaptığımız şey, alışık olduğmuz dilde şu şekilde ifade ediliyor:
Bu örneğin dışında,
Memory'e yazma ve Memory'den veri alma
İlk örneğimizde basit bir şekilde registerlara veri yazdık ve bunları yönettik. Bu örneğimizde belleğe nasıl değer yazacağımızı ve yazılı değeri nasıl okuyacağımızı göreceğiz.
Pointers
Belleği, apartmanınızın posta kutuları gibi düşünebilirsiniz. Nasıl her kutunun bir numarası varsa, bellekteki her alanın da bir adresi var. Bu adresler oldukça önemli zira adresi bilmeden veri yazamazsınız ya da var olan veriyi bulamazsınız. Adresleri de, diğer veriler gibi değişkenlerde tutuyoruz. Assembly'de, bellek adreslerini registerlara kaydederiz ve o register artık pointer haline gelir. (Çok benzer bir durum C'de de vardır. )
Pointer, bize belleğin adresini söyleyebileceği gibi, ilgili adresteki veriyi de söyleyebilir. Yani:
R1 registerında
LDR ve STR
Belleğe veri yazmak ve bellekten veri almak için, MOV ve ADD komutları gibi komutlara ihtiyacımız var. Bu iş için özelleşmiş iki komut bulunmaktadır:
Örnek bir kod ile ne yaptığımıza bakalım:
Ve, kodumuzu çalıştıralım. Bu sefer, Register bölümüne ek olarak, Memory bölümü de değişmiş olacak:
Kodun ilk 4 satırında, ilk örnekte de gördüğümüz MOV ve ADD ile basit işlemleri yaptık. 6. ve 7. satırda ise
9. ve 10. satırdaki
Symbols
Hatırlarsanız, programın sağ tarafında üçüncü bir bölüm daha vardı:
Symbolse örnek vermek için, az önce belleğe veri yazmak için kullandığımız kodu değiştirebiliriz:
Aslında çok anlaşılır bir olay. Bellek adresini, bir sembole atadık.
Branch and Control
Assembly dilinde
Çok derinlere dalmadan, meraklısı için keyifle okuyup, deneyebileceği, rehber/makale karışık bir yazı hazırlamak istedim. Ve bunun sonucunda bu yazı ortaya çıktı. Burada bazı noktaları sadeleştirerek anlatmaya çalıştım, bu nedenle içi boş kalmış olabilir ya da bu konuda bilgili biriyseniz, size tam da doğru gelmeyebilir. Bunu yorum olarak da belirtebilirsiniz. Yine bazı noktaları anlamak için programlamayla az çok haşır neşir olmuş olmak gerekebilir.
Assembly, özellikle işlemcilerle/mikroişlemcilerle çalışıyorsanız, C/C++ ile iç içeyseniz, insana farklı bir bakış açısı kazandırıyor. Ben tercihimi RISC'den dolayı ARM Assembly'den kullanmıştım ve onu öğrenmeye çalıştım.
Bu noktada, ARM Assembly için ileri çalışmalarda işinize yarayacak bir kaynak bırakmak istiyorum:
Umarım yazı hoşunuza gitmiştir. Okuduğunuz için teşekkürler.
Fikir ve yorumlarınızı belirtirseniz çok mutlu olurum.
Assembly ile alakalı birkaç örnek koddan oluşan bir konunun ilgi çekeceğini düşündüm. VisUAL''den dolayı da ARM Assembly ile yapmak istedim. Öncesinden belirtmeliyim ki bu bir örnek kod konusu, rehber değil. Assembly hakkında kısaca bilgi vereceğim ama "Assembly nedir, ne yapar, ne işe yarar" gibi sorulara cevap vermeyecektir. Zira bu, uzun bir rehber gerektiriyor. Sadece birkaç örnek kod paylaşacağım.
Bildiğiniz gibi bilgisayarlar, işlemcilerden oluşur. İşlemcilerin ise, kendilerine has dilleri vardır. Bu dile, kısaca 0 ve 1'lerden oluşan, işlemcinin Bitler vasıtasıyla iletişim kurduğu dil diyebiliriz. Makine dilinin nasıl kullanılacağı işlemci türüne, mimarisine göre değişir.
İşlemciyle ancak makine dilini kullanarak anlaşabilirsiniz. Bu dil, alıştığımız programlama dillerine göre oldukça farklıdır ve öğrenmesi zordur. Zaten temelinde bu zorluk ve bir noktada işin çok uzaması sebebiyle, günüzdeki programla dilleri var. (C, Java, Python gibi). Bu dilleri kullanırken compiler (derleyici) diye bir terim duymuşsunuzdur. İşte compiler, sizin Python ya da C gibi programlama dilinde yazdığınız kodu, işlemci mimarinize uygun makine diline çeviirir. Bu sayede işlemciniz, yazdığınız kodu anlar ve çalıştırır.
Assembly, makine diline oldukça yakın bir dildir. Neredeyse makine diline karşılık programlar yazabilirsiniz. Hatta breadboarda yapılmış 8 Bit bilgisayarlar bile uygun Assembly ile programlanabilir. Bu yakınlık sebebiyle de "Low level" dil olarak geçer. Low ve High level terimleri karşınıza çokça çıkmıştır. O nedenle şu diagramı bırakmak istiyorum:
Assembly dilinde yazılmış programlar bir Assembler tarafından derlenir. Her Assembler’ın belirli bir bilgisayar mimarisi için tasarlanmış kendi Assembly dili vardır. Bu nedenle X86 mimaride kullandığınız Assembly kodu ile ARM (RISC) mimaride işlem yapamazsınız. Bu nedenle de aynı makine dili gibi, taşınabilir değildir.
RISC ve X86 arasında temel farklar var. Zaten bu farklar, RISC'i X86'ya göre daha sade yapıyor. Ben de hem RISC hem de yeni trendin ARM olması sebebiyle, örneklerimi ARM diliyle vermeye karar verdim.
X86 ve ARM arasındaki farklar için yazımıza bakabilirsiniz:
ARM ve x86 Ä°Ålemci Mimarileri KarÅılaÅtırması
ARM ve x86 karÅılaÅtırması yaptıÄımız bu yazımızda komut setleri, mimari ve tüm temel farklılıklara bakıyoruz. Android, üç farklı türde iÅlemci mimarisi
www.technopat.net
VisUAL2
Bahsettiğim gibi, Assembly diline derlemek için Assembler'e ihtiyacımız var. ARM ile çalışacağımızdan, bu işi X86 makinelerimizde yapmak için, Assembler'e ek bir emülatöre de ihtiyacımız var.
VisUAL2, hem Assembler hem de emülatör görevini tek başına yerine getiren, beğendiğim güzel bir proje. GİtHub reposuna buradan ulaşabilirsiniz. Yine buradan indirebilirsiniz:
GitHub - tomcl/V2releases: A friendly ARM assembler and simulator for educational use
A friendly ARM assembler and simulator for educational use - tomcl/V2releases
github.com
Kurulumu yapıp çalıştırdıktan sonra, karşınıza şöyle bir ekran çıkacak:
Üst menüden başlayalım:
Run
Assembly kodunu çalıştırırReset
programı resetlerStep
tuşları adımlar arasında gitmenizi sağlar, bu sayede Clock cycle başına hangi satırın çalıştığını görebilirsiniz. Aynı zamanda program adım adım çalışır.Clock
programı çalıştırmak için ne kadar clock cycle gerektiğini bize gösterir. Clock cycle, aslında işlemcinin saat hızı ile bağlantılıdır ve size, programın ne kadar sürede çalışacağı ile ilgili bilgi verir
Registers
Registerler işlemci çalışması sırasında farklı amaçlar için kullanılan değişkenlerdir. Bellekteki verilere ulaşmak belirli bir zaman gerektirir, fakat registerler işlemci çekirdeğindedir ve fazladan zaman harcanmadan istenen işleme göre içerikleri kullanılabilmektedir. Ancak sınırlı sayıda bulunurlar. Registerler genel amaçlı kullanılabilecekleri gibi bazıları sadece özel görevleri üstlenmektedir. (Mesela burada, R13'ün özel bir görevi vardır, gördüğünüz gibi önceden tanımlanmıştır.)Memory
belleğimizSymbols
insanların anlayabileceği değişkenler
Standart bir kod ile başlayalım:
Kod:
MOV R1, #0x11
MOV R2, #0x5
MOV R3, #0x6
ADD R0, R1, #0x8
Şimdi, kodu çalıştıralım ve sonuca bakalım:
Gördüğünüz gibi sağdaki register değerlerimiz değişmiş oldu.
Peki burada ne yaptık?
MOV R1, #0x11
MOV komutu, boş bir registera değer yazmamıza ya da bir registerdeki değeri, eşit kapasitedeki başka bir registera kopyalamamıza olanak tanır. Burada, R1 registerına, 0X11 Hex değerini giriyoruz. 2. ve 3. satırlarda da farklı registerlara farklı değerler giriyoruz.- Syntax:
MOV <yazılacak register>, <yazılacak değer ya da kopyalanacak register>
- Syntax:
ADD R0, R1, #0x8
ADD komutu bir registera, başka iki registardaki değeri toplayarak yazmamıza, ya da bir registardaki değerle başka bir değeri toplayıp yazmamıza olanak tanır. Burada, R1 registarındaki değeri, 0x8 Hex değeriyle toplayıp, R0 registerına giriyoruz.- Syntak ADD <yazılacak register>, <toplanacak register>, <toplanacak deger ya da başka bir register>
Yani aslında yaptığımız şey, alışık olduğmuz dilde şu şekilde ifade ediliyor:
Kod:
a = 17
b = 5
c = 6
d = a + 8 (=25)
Bu örneğin dışında,
SUB
komutunu da ADD
gibi kullanabilirsiniz:SUB R5, R3, #0x06
gibi.
Memory'e yazma ve Memory'den veri alma
İlk örneğimizde basit bir şekilde registerlara veri yazdık ve bunları yönettik. Bu örneğimizde belleğe nasıl değer yazacağımızı ve yazılı değeri nasıl okuyacağımızı göreceğiz.
Pointers
Belleği, apartmanınızın posta kutuları gibi düşünebilirsiniz. Nasıl her kutunun bir numarası varsa, bellekteki her alanın da bir adresi var. Bu adresler oldukça önemli zira adresi bilmeden veri yazamazsınız ya da var olan veriyi bulamazsınız. Adresleri de, diğer veriler gibi değişkenlerde tutuyoruz. Assembly'de, bellek adreslerini registerlara kaydederiz ve o register artık pointer haline gelir. (Çok benzer bir durum C'de de vardır. )
Pointer, bize belleğin adresini söyleyebileceği gibi, ilgili adresteki veriyi de söyleyebilir. Yani:
R1 registerında
0x20000040
adresinin kayıtı olduğunu düşünelim. R1
, bize adresi dönecekken (aynı değişkenin değeri dönmesi gibi); [R1]
komutu bize, registera kayıtlı adresteki değeri döner.LDR ve STR
Belleğe veri yazmak ve bellekten veri almak için, MOV ve ADD komutları gibi komutlara ihtiyacımız var. Bu iş için özelleşmiş iki komut bulunmaktadır:
LDR
bellek adresini registera kaydetmemize olanak tanır.LDR R4, =0x20000040
LDR
, bellekteki değeri registera kaydetmemize olanak tanır.LDR R4, [R0]
--> ([RO]
pointerdır)
STR
registerdaki içeriği, belleğe kaydetmemize olanak tanır.STR R5, [R1]
--> ([R1]
pointerdır)
Örnek bir kod ile ne yaptığımıza bakalım:
Kod:
mov R0, #0x0B
mov R1, R0
add R2, R3, #0x4
add R3, R1, #0x0A
LDR R4, =0x20000040
LDR R5, =0x20000050
STR R2, [R4]
STR R3, [R5]
Ve, kodumuzu çalıştıralım. Bu sefer, Register bölümüne ek olarak, Memory bölümü de değişmiş olacak:
Kodun ilk 4 satırında, ilk örnekte de gördüğümüz MOV ve ADD ile basit işlemleri yaptık. 6. ve 7. satırda ise
LDR
komutuyla Bellek adreslerimizi ilgili registerlara atadık.9. ve 10. satırdaki
STR
kodlarıyla da, R2
ve R3
registerında kayıtlı değerleri, [R4]
ve [R5]
pointerları ile ilgili bellek adreslerine yazdık. Gördüğünüz gibi, STR komutunu kullanırken, Belleğe yazmak için registerı değil pointerı kullandık. Symbols
Hatırlarsanız, programın sağ tarafında üçüncü bir bölüm daha vardı:
Symbols
.Symbolse örnek vermek için, az önce belleğe veri yazmak için kullandığımız kodu değiştirebiliriz:
Kod:
Mem_Addr_1 equ 0x20000040
Mem_Addr_2 equ 0x20000050
mov R0, #0x0B
mov R1, R0
add R2, R3, #0x4
add R3, R1, #0x0A
LDR R4, =Mem_Addr_1
LDR R5, =Mem_Addr_2
STR R2, [R4]
STR R3, [R5]
Aslında çok anlaşılır bir olay. Bellek adresini, bir sembole atadık.
Branch and Control
Assembly dilinde
if
, while
, for
gibi operatörler yoktur. Bunların yerine conditional branching denen yöntem uygulanır, ancak bu başka bir yazının konusu olsun. Çok derinlere dalmadan, meraklısı için keyifle okuyup, deneyebileceği, rehber/makale karışık bir yazı hazırlamak istedim. Ve bunun sonucunda bu yazı ortaya çıktı. Burada bazı noktaları sadeleştirerek anlatmaya çalıştım, bu nedenle içi boş kalmış olabilir ya da bu konuda bilgili biriyseniz, size tam da doğru gelmeyebilir. Bunu yorum olarak da belirtebilirsiniz. Yine bazı noktaları anlamak için programlamayla az çok haşır neşir olmuş olmak gerekebilir.
Assembly, özellikle işlemcilerle/mikroişlemcilerle çalışıyorsanız, C/C++ ile iç içeyseniz, insana farklı bir bakış açısı kazandırıyor. Ben tercihimi RISC'den dolayı ARM Assembly'den kullanmıştım ve onu öğrenmeye çalıştım.
Bu noktada, ARM Assembly için ileri çalışmalarda işinize yarayacak bir kaynak bırakmak istiyorum:
Writing ARM Assembly (Part 1)
azeria-labs.com
Umarım yazı hoşunuza gitmiştir. Okuduğunuz için teşekkürler.
Fikir ve yorumlarınızı belirtirseniz çok mutlu olurum.
Son düzenleme: