Kol talimatları. ARM işlemci komut setinin incelenmesi. Koşullu Komut Yürütme

Herkese merhaba!
Mesleğe göre, ben bir Java programcısıyım. Son aylarda çalışmak, Android NDK'nın geliştirilmesiyle ve buna bağlı olarak C'de yerel uygulamalar yazmamla tanışmamı sağladı. Burada Linux kitaplıklarını optimize etme sorunuyla karşılaştım. Birçoğunun kesinlikle ARM için optimize edilmediği ve işlemciye yoğun bir şekilde yüklendiği ortaya çıktı. Önceden, pratik olarak assembler'da programlamadım, bu yüzden ilk başta bu dili öğrenmeye başlamak zordu, ama yine de denemeye karar verdim. Bu makale, tabiri caizse, yeni başlayanlar için yeni başlayanlardan yazılmıştır. Daha önce çalıştığım temelleri açıklamaya çalışacağım, umarım birileri ilgilenir. Ayrıca, profesyonellerden yapıcı eleştiriler almaktan memnuniyet duyacağım.

Tanıtım
O halde, önce ARM'nin ne olduğunu bulalım. Wikipedia bu tanımı verir:

ARM mimarisi (Gelişmiş RISC Makinesi, Acorn RISC Makinesi, gelişmiş RISC makinesi), ARM Limited tarafından geliştirilen bir lisanslı 32-bit ve 64-bit mikroişlemci çekirdekleri ailesidir. Şirket, mimariyi üçüncü taraf üreticilere lisanslamaktan kazanç sağlayarak, yalnızca onlar için (derleyiciler, hata ayıklama araçları vb.)

Bilmeyen varsa, artık çoğu mobil cihaz, tabletler bu işlemci mimarisi üzerine geliştiriliyor. Bu ailenin ana avantajı, çeşitli gömülü sistemlerde sıklıkla kullanıldığı için düşük güç tüketimidir. Mimari zamanla gelişti ve ARMv7 3 profilleri tanımlandığından beri: 'A' (uygulama) - uygulamalar, 'R' (gerçek zamanlı) - gerçek zamanlı olarak, 'M' (mikrodenetleyici) - mikrodenetleyici. Bu teknolojinin gelişiminin tarihini ve diğer ilginç verileri Wikipedia'da veya internette Google'da dolaşarak okuyabilirsiniz. ARM, farklı çalışma modlarını destekler (Başparmak ve ARM, ayrıca, ARM ve Başparmak karışımı olan Thumb-2 son zamanlarda ortaya çıktı). Bu yazıda, 32 bitlik bir komut setinin yürütüldüğü ARM modunun kendisini ele alacağız.

Her ARM işlemcisi aşağıdaki bloklardan oluşturulmuştur:

  • 37 kayıt (geliştirme sırasında yalnızca 17'si görülebilir)
  • Aritmetik mantık birimi (ALU) - aritmetik ve mantık görevlerini gerçekleştirir
  • Varil kaydırıcı - veri bloklarını belirli sayıda bit ile taşımak için tasarlanmış bir cihaz
  • CP15 - ARM yardımcı işlemcilerini kontrol eden özel bir sistem
  • Talimat kod çözücü - bir talimatın bir dizi mikro işlemlere dönüştürülmesiyle ilgilenir
Bunlar, ARM'nin tüm bileşenleri değildir, ancak yapı işlemcileri ormanına derinlemesine girmek bu makalenin konusuna dahil değildir.
Boru hattı yürütme
ARM işlemcileri 3 aşamalı bir işlem hattı kullanır (ARM8'den beri 5 aşamalı bir işlem hattı uygulanmıştır). Örnek olarak ARM7TDMI işlemcisini kullanan basit bir boru hattını ele alalım. Her talimatın yürütülmesi üç adımdan oluşur:

1. Örnekleme aşaması (F)
Bu aşamada, talimatlar RAM'den işlemci boru hattına akar.
2. Kod çözme adımı (D)
Talimatların kodu çözülür ve türleri tanınır.
3. Yürütme aşaması (E)
Veri ALU'ya girer ve yürütülür ve elde edilen değer belirtilen kayıt defterine yazılır.

Ancak geliştirirken, örneğin load(LDR) veya store gibi birkaç yürütme döngüsü kullanan talimatların olduğunu dikkate almanız gerekir. Böyle bir durumda yürütme aşaması (E) aşamalara ayrılır (E1, E2, E3...).

koşullu yürütme
ARM assembler'ın en önemli işlevlerinden biri koşullu yürütmedir. Her komut koşullu olarak yürütülebilir ve bunun için son ekler kullanılır. Bir talimatın adına bir sonek eklenirse, çalıştırılmadan önce parametreler kontrol edilir. Parametreler koşulla eşleşmezse, talimat yürütülmez. son ekler:
MI - negatif sayı
PL - pozitif veya sıfır
AL - talimatı her zaman yürüt
Daha birçok koşullu yürütme eki vardır. Resmi belgelerdeki son ekleri ve örnekleri okuyun: ARM belgeleri
Şimdi düşünme zamanı...
ARM assembler sözdizimi temelleri
Montajcı ile daha önce çalışmış olanlar için bu madde aslında atlanabilir. Diğer herkes için bu dille çalışmanın temellerini anlatacağım. Bu nedenle her Assembly dili programı komutlardan oluşur. Talimat şu şekilde oluşturulur:
(etiket) (talimat|işlenenler) (@yorum)
Etiket isteğe bağlı bir parametredir. Bir talimat, işlemciye bir talimat için doğrudan bir anımsatıcıdır. Temel talimatlar ve kullanımları daha sonra tartışılacaktır. İşlenenler - sabitler, kayıt adresleri, RAM'deki adresler. Yorum, programın yürütülmesini etkilemeyen isteğe bağlı bir parametredir.
İsimleri kaydet
Aşağıdaki kayıt adlarına izin verilir:
1.r0-r15

3.v1-v8 (değişken kayıtları, r4 - r11)

4.sb ve SB (statik kayıt, r9)

5.sl ve SL (r10)

6.fp ve FP (r11)

7.ip ve IP (r12)

8.sp ve SP (r13)

9.lr ve LR (r14)

10.pc ve PC (program sayacı, r15).

Değişkenler ve sabitler
ARM assembler'da (neredeyse) diğer programlama dillerinde olduğu gibi değişkenler ve sabitler kullanılabilir. Aşağıdaki türlere ayrılırlar:
  • sayısal
  • zeka oyunu
  • Sicim
Sayısal değişkenler şu şekilde başlatılır:
bir SETA 100; 100 değeriyle sayısal bir "a" değişkeni oluşturulur.
Dize değişkenleri:
improb SETS "literal"; improb değişkeni "literal" değeriyle oluşturulur. DİKKAT! Değişken değeri 5120 karakteri aşamaz.
Boole değişkenleri sırasıyla TRUE ve FALSE değerlerini kullanır.
ARM montajcı talimatları örnekleri
Bu tabloda, daha fazla geliştirme için gerekli olacak ana talimatları topladım (en temel aşamada :):

Temel talimatların kullanımını güçlendirmek için bazı basit örnekler yazalım, ancak önce bir kol alet zincirine ihtiyacımız var. Linux üzerinde çalıştığım için şunu seçtim: frank.harvard.edu/~coldwell/toolchain (arm-unknown-linux-gnu toolchain). Linux'taki diğer herhangi bir program gibi, armutları bombalamak kadar kolay. Benim durumumda (Rus Fedora) sadece web sitesinden rpm paketleri kurmam gerekiyordu.
Şimdi en basit örneği yazmanın zamanı geldi. Program kesinlikle işe yaramaz olacak ama asıl olan çalışacak olması :) İşte size sunduğum kod:
start: @ Programın başlangıcını gösteren isteğe bağlı bir dize mov r0, #3 @ 3 değerini r0 mov r1, #2 kaydına yükleyin @ Aynısını r1 kaydıyla yapın, ancak şimdi 2 değeriyle r2, r1, r0 ekleyin @ r0 ve r1 değerlerini toplayın r2 mul r3, r1, r0 cevabını yazın @ r1 kaydının değerini r0 kaydının değeri ile çarpın r3 stopa cevabı yazın stop: b stop
.bin dosyasını alana kadar programı derliyoruz:
/usr/arm/bin/arm-unknown-linux-gnu-as -o arm.o arm.s /usr/arm/bin/arm-unknown-linux-gnu-ld -Ttext=0x0 -o ​​​​arm. elf arm .o /usr/arm/bin/arm-unknown-linux-gnu-objcopy -O ikili arm.elf arm.bin
(kod arm.s dosyasında ve benim durumumdaki araç zinciri /usr/arm/bin/ dizininde)
Her şey yolunda giderse, 3 dosyanız olacaktır: arm.s (gerçek kod), arm.o, arm.elf, arm.bin (gerçek yürütülebilir program). Programın çalışmasını test etmek için kendi kol cihazınızın olması gerekli değildir. QEMU'yu kurmak yeterlidir. Referans için:

QEMU, çeşitli platformların donanımını taklit etmek için ücretsiz ve açık kaynaklı bir programdır.

Intel x86 işlemcilerin ve G/Ç aygıtlarının öykünmesini içerir. 80386, 80486, Pentium, Pentium Pro, AMD64 ve diğer x86 uyumlu işlemcileri taklit edebilir; PowerPC, ARM, MIPS, SPARC, SPARC64, m68k - yalnızca kısmen.

Syllable, FreeBSD, FreeDOS, Linux, Windows 9x, Windows 2000, Mac OS X, QNX, Android ve daha fazlasında çalışır.

Yani, kolu taklit etmek için qemu-system-arm'a ihtiyacınız var. Bu paket yum'dadır, yani Fedora'nız varsa, bu zorluğu atlayabilir ve sadece şu komutu çalıştırabilirsiniz:
yum qemu-sistem-kolunu kurun

Ardından, arm.bin programımızı çalıştırabilmesi için ARM öykünücüsünü başlatmamız gerekiyor. Bunu yapmak için, QEMU için flash bellek olacak bir flash.bin dosyası oluşturacağız. Bunu yapmak çok kolay:
dd if=/dev/zero of=flash.bin bs=4096 count=4096 dd if=arm.bin of=flash.bin bs=4096 conv=notrunc
Şimdi QEMU'yu alınan flash bellekle yüklüyoruz:
qemu-system-arm -M connex -pflash flash.bin -nographic -serial /dev/null
Çıktıda şöyle bir şey elde edeceksiniz:

$ qemu-system-arm -M connex -pflash flash.bin -nographic -serial /dev/null
QEMU 0.15.1 monitör - daha fazla bilgi için "yardım" yazın
(kemu)

arm.bin programımız dört registerin değerlerini değiştirmek zorunda kaldı, bu yüzden doğru çalışıp çalışmadığını kontrol etmek için bu registerlere bakalım. Bu çok basit bir komutla yapılır: info registers
Çıktıda, 15 ARM kaydının tümünü göreceksiniz ve dördü değişen değerlere sahip olacak. Kontrol edin :) Kayıt değerleri, program çalıştırıldıktan sonra beklediğiniz ile aynıdır:
(qemu) bilgi kayıtları r00 = 00000003 r01 = 00000002 r02 = 00000005 r03 = 00000006 r04 = 00000000 R05 = 000000 R06 = 000000 R07 = 000000 R08 = 000000 R09 = 00000000 R10 = 000000 R11 = 00000000 R12 = 00000000 R13 =0000 R15=00000010 PSR=400001d3 -Z-- Bir svc32

not Bu yazımda ARM assembler'da programlamanın temellerini anlatmaya çalıştım. Umarım beğenmişsindir! Bu, bu dilin ormanını daha da araştırmak ve içinde programlar yazmak için yeterlidir. Her şey yolunda giderse, kendim öğrendiklerim hakkında daha fazla yazacağım. Montajcıda yeni olduğum için hatalar varsa lütfen tekme atmayın.

Raspberry Pi'nizin işletim sistemi olarak Raspbian dağıtımını kullanıyorsanız, (birleştirme dili kaynak kodunu ikili koda dönüştüren bir montajcı) ve ld (sonuçta çalıştırılabilir dosyayı oluşturan bir bağlayıcı) olmak üzere iki yardımcı programa ihtiyacınız olacaktır. Her iki yardımcı program da binutils yazılım paketindedir, bu nedenle zaten sisteminizde olabilirler. Elbette iyi bir metin düzenleyiciye de ihtiyacınız olacak; Program geliştirme için her zaman Vim'i kullanmanızı öneririm, ancak giriş için yüksek bir engeli vardır, bu nedenle Nano veya başka herhangi bir GUI metin düzenleyicisi de iyi çalışacaktır.

Başlamaya hazır? Aşağıdaki kodu kopyalayın ve myfirst.s dosyasına kaydedin:

Global _start _start: mov r7, #4 mov r0, #1 ldr r1, =string mov r2, #stringlen swi 0 mov r7, #1 swi 0 .data string: .ascii "Ciao!\n" stringlen = . -sicim

Bu program basitçe "Ciao!" dizesini yazdırır. x86 CPU'larla çalışmak için montaj dilini kullanma hakkında makaleler okuduysanız, kullanılan bazı talimatlar size tanıdık gelebilir. Ama yine de x86 ve ARM mimarisi komutları arasında kaynak kodun sözdiziminde de söylenebilecek birçok fark var, o yüzden detaylı olarak inceleyeceğiz.

Ancak bundan önce, yukarıdaki kodu bir araya getirmek ve ortaya çıkan nesne dosyasını yürütülebilir bir dosyaya bağlamak için aşağıdaki komutu kullanmanız gerektiği belirtilmelidir:

as -o ilkim.o ilkim.s && ld -o ilk ilkim.o

Artık oluşturulan programı ./myfirst komutuyla çalıştırabilirsiniz. Yürütülebilir dosyanın yaklaşık 900 baytlık çok mütevazı bir boyutta olduğunu fark etmiş olabilirsiniz - C programlama dilini ve puts() işlevini kullandıysanız, ikili dosyanın boyutu yaklaşık beş kat daha büyük olurdu!

Raspberry Pi için Kendi İşletim Sisteminizi Oluşturma

x86 Assembly dili programlama serisindeki önceki makaleleri okuduysanız, Linux veya başka bir işletim sisteminin yardımı olmadan ekranda bir mesaj görüntüleyen kendi işletim sisteminizi ilk çalıştırdığınız zamanı muhtemelen hatırlayacaksınız. Bundan sonra, basit bir komut satırı arayüzü ve programları diskten yüklemek ve çalıştırmak için bir mekanizma ekleyerek geleceğe bir yedek bırakarak geliştirdik. Bu çok ilginçti, ancak esas olarak BIOS üretici yazılımının yardımıyla çok zor olmayan bir işti - ekrana, klavyeye ve disket sürücüsüne erişim için basitleştirilmiş bir arayüz sağladı.

Raspberry Pi söz konusu olduğunda, artık kullanışlı BIOS işlevlerine sahip olmayacaksınız, bu nedenle aygıt sürücülerini kendiniz geliştirmek zorunda kalacaksınız; bu, ekranda çizim yapmak ve mekanizmayı uygulamakla karşılaştırıldığında başlı başına zor ve ilgi çekici bir iştir. kendi programlarını yürütmek. Aynı zamanda, ağda Raspberry Pi önyükleme işleminin ilk aşamalarını, GPIO pinlerine erişim mekanizmasının özelliklerini vb. ayrıntılı olarak açıklayan birkaç kılavuz vardır.

Bu tür en iyi belgelerden biri Cambridge Üniversitesi'nden Baking Pi'dir (www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/index.html). Esasen, LED'leri açmak, ekrandaki piksellere erişmek, klavye girişi almak vb. için montaj diliyle nasıl çalışılacağını açıklayan bir dizi kılavuzdur. Okurken Raspberry Pi donanımı hakkında çok şey öğreneceksiniz ve bu tek kartlı bilgisayarların orijinal modelleri için kılavuzlar yazılmıştır, bu nedenle A+, B+ ve Pi 2 gibi modellerle alakalı olacağının garantisi yoktur.

C programlama dilini tercih ediyorsanız, http://tinyurl.com/qa2s9bg adresinde bulunan Valvers kaynağından çapraz derleyici kurma ve basit bir işletim sistemi çekirdeği oluşturma işleminin açıklamasını içeren belgeye başvurmalısınız, ayrıca, yararlı OSDev kaynağının Wiki bölümünde, bir Raspberry Pi üzerinde temel bir işletim sistemi çekirdeğinin nasıl oluşturulacağı ve çalıştırılacağı hakkında bilgi için bkz. http://wiki.osdev.org/Raspberry_Pi_Bare_Bones.

Yukarıda belirtildiği gibi, bu durumdaki en büyük sorun, çeşitli Raspberry Pi donanım aygıtları için sürücü geliştirme ihtiyacıdır: USB denetleyicisi, SD kart yuvası vb. Sonuçta bahsedilen cihazların kodu bile on binlerce satır alabilir. Hala Raspberry Pi için kendi tam işlevsel işletim sisteminizi geliştirmek istiyorsanız, www.osdev.org adresindeki forumları ziyaret etmeli ve bu aygıtlar için daha önce herhangi birinin sürücü geliştirip geliştirmediğini sormalı ve mümkünse bunları çekirdeğine uyarlamalısınız. işletim sisteminiz, böylece büyük miktarda zaman tasarrufu sağlar.

her şey nasıl çalışıyor

Kodun ilk iki satırı CPU yönergeleri değil, birleştirici ve bağlayıcı yönergeleridir. Her programın _start adında iyi tanımlanmış bir giriş noktası olmalıdır ve bizim durumumuzda bu, kodun en başında sona erdi. Bu nedenle, bağlayıcıya kodun yürütülmesinin ilk talimattan başlaması gerektiğini ve herhangi bir ek eylem gerekmediğini bildiririz.

Aşağıdaki talimatla 4 sayısını r7 kaydına koyuyoruz. (Daha önce Assembly diliyle hiç çalışmadıysanız, yazmaçların doğrudan CPU'da bulunan bir bellek konumu olduğunu bilmelisiniz. Modern CPU'ların çoğu, milyonlarca veya milyarlarca RAM hücresine kıyasla az sayıda kayıt uygular, ancak kayıtlar çok daha hızlı çalıştıkları için vazgeçilmezdir.) ARM mimarisi yongaları geliştiricilere çok sayıda genel amaçlı kayıt sağlar: bir tasarımcı r0 ila r15 arasında 16 adede kadar kayıt kullanabilir ve bu kayıtlar herhangi bir tarihsel yerleşik kısıtlama ile ilişkili değildir. , bazı kayıtların belirli zamanlarda belirli amaçlar için kullanılabileceği mimari x86 örneğinde olduğu gibi.

Bu nedenle, mov komutu aynı adı taşıyan x86 mimarisi komutuna çok benzese de, her durumda, 4 sayısının yanında bir tamsayı değerinin bulunduğunu ve bir hafıza adresinin olmadığını belirten karma sembolüne dikkat etmelisiniz. . Bu durumda, dizgimizi yazdırmak için Linux çekirdeğinin yazma sistem çağrısını kullanmak istiyoruz; sistem çağrılarını kullanmak için çekirdeğin işini yapması için bağışlamadan önce registerları gerekli değerlerle doldurmanız gerekir. Sistem çağrı numarası, yazma sistemi çağrısının numarası 4 olmak üzere, r7 kaydına sığmalıdır.

Aşağıdaki mov komutuyla, "Ciao!" dizesinin, yani standart çıktı akışının tanımlayıcısının yazılacağı dosya tanımlayıcısını r0 kaydına yerleştiriyoruz. Bu durumda standart çıktı akışı kullanıldığından, standart tanımlayıcısı, yani 1 , kayıt defterine yerleştirilir. Daha sonra, çıktısını almak istediğimiz dizginin adresini ldr talimatını ("kayıta yükle" komutu) kullanarak r1 kaydına koymamız gerekir; aşağıdakilerin bir adres değil bir etiket olduğunu belirtmek için eşittir işaretine dikkat edin. Kodun sonunda yani data kısmında bu stringi ASCII karakter dizisi şeklinde ilan ediyoruz. "Yazma" sistem çağrısını başarılı bir şekilde kullanmak için, çalışan kök çekirdeğe çıktı dizesinin uzunluğunu da söylememiz gerekir, bu nedenle stringlen değerini r2 kaydına koyarız. (stringlen değeri, dizenin bitiş adresinin başlangıç ​​adresinden çıkarılmasıyla hesaplanır.)

Bu noktada tüm registerleri gerekli data ile doldurduk ve kontrolü Linux kerneline aktarmaya hazırız. Bunu yapmak için, işletim sisteminin çekirdek alanına bir geçiş gerçekleştiren adı "yazılım kesmesi" anlamına gelen swi komutunu kullanıyoruz (x86 mimarisindeki makalelerdeki int komutuyla hemen hemen aynı şekilde). İşletim sistemi çekirdeği r7 kaydının içeriğini inceler, içinde 4 tamsayı değerini bulur ve şu sonuca varır: "Yani, çağıran program bir dize yazdırmak istiyor." Bundan sonra, diğer kayıtların içeriğini inceler, bir dize yazdırır ve kontrolü programımıza döndürür.

Böylece ekranda "Ciao!" satırını görüyoruz, bundan sonra sadece programın yürütülmesini doğru bir şekilde tamamlamamız gerekiyor. Bu sorunu, çıkış sistemi çağrı numarasını r7 kaydına yerleştirerek ve ardından yazılım kesme talimatı numarası sıfırı arayarak çözüyoruz. Ve hepsi bu - işletim sistemi çekirdeği programımızın yürütülmesini tamamlıyor ve tekrar komut kabuğuna geçiyoruz.

Vim (solda), montaj dili kodu yazmak için mükemmel bir metin düzenleyicisidir - ARM mimarisi için derleme dili sözdizimi vurgulaması için bir dosya http://tinyurl.com/psdvjen adresinde mevcuttur.

Tavsiye: Assembly dili ile çalışırken yorumları gözden kaçırmamalısınız. Derginin sayfalarında kodu olabildiğince küçük tutmak için (ve ayrıca her bir talimatın amacını ayrıntılı olarak açıkladığımız için) bu makalede çok fazla yorum kullanmadık. Ancak kodu ilk bakışta bariz görünen karmaşık programlar geliştirirken, ARM mimarisi için Assembly dili sözdizimini kısmen unutup birkaç ay sonra geliştirmeye geri döndüğünüzde her zaman nasıl görüneceğini düşünmelisiniz. Kodda kullanılan tüm hileleri ve kısayolları unutabilirsiniz, bundan sonra kod tam bir anlamsız gibi görünecektir. Yukarıdakilere dayanarak, bazıları şu anda çok açık görünse bile, koda mümkün olduğunca çok yorum eklemelisiniz!

tersine mühendislik

Bir ikili dosyayı derleme dili koduna dönüştürmek de bazı durumlarda faydalı olabilir. Bu işlemin sonucu genellikle okunabilir etiket adları ve yorumları olmayan çok iyi biçimlendirilmiş bir kod değildir, ancak bu yine de derleyici tarafından kodunuz üzerinde gerçekleştirilen dönüşümleri incelemek için yararlı olabilir. myfirst ikili dosyasını sökmek için aşağıdaki komutu çalıştırmanız yeterlidir:

Objdump -d benim ilk

Bu komut, ikili dosyanın yürütülebilir kod bölümünü ayırmanıza izin verir (ancak ASCII metni içerdiğinden veri bölümünü değil). Demonte koda bakarsanız, içindeki talimatların orijinal koddaki talimatlarla hemen hemen aynı olduğunu fark edeceksiniz. Ayırıcılar çoğunlukla, bir virüs veya davranışını taklit etmek istediğiniz basit bir kapalı kaynak program gibi yalnızca ikili biçimde kullanılabilen bir programın davranışını incelemeniz gerektiğinde kullanılır. Aynı zamanda, incelenen programın yazarı tarafından uygulanan kısıtlamaları her zaman hatırlamalısınız! Bir program ikili dosyasını demonte etmek ve elde edilen kodu basitçe projenizin koduna kopyalamak elbette kötü bir fikirdir; Aynı zamanda, programın prensibini incelemek için ortaya çıkan kodu oldukça kullanabilirsiniz.

Altyordamlar, Döngüler ve Koşullu Talimatlar

Artık basit programları nasıl tasarlayacağımızı, birleştireceğimizi ve bağlayacağımızı bildiğimize göre, daha karmaşık bir şeye geçelim. Aşağıdaki program, dizgeleri çıktılamak için altyordamlar kullanır (onlar sayesinde, kod parçalarını yeniden kullanabiliriz ve kendimizi, kayıtları verilerle doldurmak için aynı tür işlemleri yapmaktan kurtarabiliriz). Bu program, kullanıcı "q" yazana kadar bir dize çıktısına izin veren bir ana olay döngüsü uygular. Kodu inceleyin ve talimatların amacını anlamaya çalışın (veya tahmin edin!), ancak bir şeyi anlamazsanız cesaretiniz kırılmasın, çünkü biraz sonra ona da ayrıntılı olarak bakacağız. ARM mimarisi için montaj dilindeki @ sembollerinin yorumları vurguladığını unutmayın.

global _start _start: ldr r1, =string1 mov r2, #string1len bl print_string döngü: mov r7, #3 @ read mov r0, #0 @ stdin ldr r1, =char mov r2, #2 @ iki karakter swi 0 ldr r1, =char ldrb r2, cmp r2, #113 @ "q" karakterinin ASCII kodu beq done ldr r1, =string2 mov r2, #string2len bl print_string b loop done: mov r7, #1 swi 0 print_string: mov r7, #4 mov r0, #1 swi 0 bx lr .data string1: .ascii "Çıkış için q girin!\n" string1len = . - string1 string2: .ascii "Bu q...\n değildi" string2len = . - string2 karakter: .word 0

Programımız, yazma sistemi çağrısının takip etmesi için uygun kayıtlara bir dizgenin başlangıcına bir işaretçi ve uzunluğu için bir değer yerleştirerek, hemen ardından kodda aşağıdaki print_string alt rutinine atlayarak başlar. Bu geçişi uygulamak için, adı "dal ve bağlantı" ("adresi koruyan dal") anlamına gelen bl talimatı kullanılır ve kendisi, geçerli adresi kodda saklar, bu da geri dönmenizi sağlar. daha sonra bx komutunu kullanarak ona. print_string alt yordamı, çekirdek alanına atlamadan ve ardından bx komutuyla kaydedilen kod adresine geri dönmeden önce, ilk programımızda olduğu gibi, yazma sistemi çağrısı için diğer kayıtları doldurur.

Çağıran koda dönersek, loop adında bir etiket bulabiliriz - etiketin adı zaten bir süre sonra ona geri döneceğimizi ima ediyor. Ama önce, kullanıcının klavyeyi kullanarak girdiği karakteri okumak için read (3 numaralı) adlı başka bir sistem çağrısı kullanıyoruz. Bu nedenle, bir dosyadan veri değil, kullanıcı girdisini okumamız gerektiğinden, 3 değerini r7 kaydına ve 0 değerini (standart giriş tutamacı) r0 kaydına koyarız.

Ardından, OS çekirdeği tarafından okunan ve yerleştirilen karakteri saklamak istediğimiz adresi r1 kaydına yerleştiririz - bizim durumumuzda bu, veri bölümünün sonunda açıklanan char bellek alanıdır. (Aslında, bir makine kelimesine ihtiyacımız var, yani iki karakter depolamak için bir hafıza alanına, çünkü aynı zamanda Enter anahtar kodunu da saklayacaktır. Montaj dili ile çalışırken, hafıza alanlarının taşma olasılığını her zaman hatırlamak önemlidir, çünkü size yardım etmeye hazır üst düzey mekanizmalar yok!).

Ana koda dönersek, kaydetmek istediğimiz iki karaktere karşılık gelen r2 kaydına 2 değerinin yerleştirildiğini ve ardından okuma işlemini gerçekleştirmek için OS çekirdek alanına geçiş yapıldığını göreceğiz. Kullanıcı bir karakter yazar ve Enter tuşuna basar. Şimdi ne tür bir karakter olduğunu kontrol etmemiz gerekiyor: hafıza alanının adresini (veri bölümündeki char) register r1'e koyuyoruz, ardından ldrb komutunu kullanarak işaret ettiği hafıza alanından bir bayt yüklemek için kullanıyoruz. Bu kayıttan değer.

Bu durumda köşeli parantezler, verilerin kayıt defterinde değil, bizi ilgilendiren bellek alanında saklandığını gösterir. Böylece register r2 artık data bölümünün char hafıza alanından tek bir karakter içeriyor ve bu tam olarak kullanıcının girdiği karakter. Bir sonraki görevimiz, r2 kaydının içeriğini ASCII tablosunun 113 karakteri olan "q" karakteriyle karşılaştırmak olacaktır (www.asciichart.com adresinde bulunan karakter tablosuna bakın). Şimdi karşılaştırma işlemini gerçekleştirmek için cmp komutunu kullanıyoruz ve ardından r2 kaydındaki değer 113 ise bitmiş etiketine atlamak için adı "dal ise eşitse" (eşitse dal) anlamına gelen beq komutunu kullanıyoruz. değil, ikinci satırımızı çıkarıyoruz ve ardından b komutuyla döngünün başına atlıyoruz.

Son olarak, done etiketinden sonra, ilk programda olduğu gibi, programın yürütülmesini sonlandırmak istediğimizi OS çekirdeğine söyleriz. Bu programı çalıştırmak için, ilk program için verilen talimatlara göre bir araya getirmeniz ve bağlamanız yeterlidir.

Bu nedenle, oldukça büyük miktarda bilgiyi en özlü biçimde ele aldık, ancak materyali kendi başınıza incelerseniz, yukarıdaki kodla deneyler yaparsanız daha iyi olur. Bir programlama diline aşina olmanın, başka birinin kodunu değiştirmeyi denemek ve etkisini görmekten daha iyi bir yolu yoktur. Artık döngüleri, karşılaştırmaları ve alt rutinleri kullanırken kullanıcı giriş ve çıkış verilerini okuyan basit ARM birleştirme dili programları geliştirebilirsiniz. Assembly diline bugünden önce rastlamadıysanız, umarım bu makale dili sizin için biraz daha anlaşılır hale getirmiş ve sadece birkaç yetenekli geliştiricinin erişebildiği mistik bir zanaat olduğu şeklindeki popüler klişeyi ortadan kaldırmaya yardımcı olmuştur.

Elbette, ARM mimarisi için Assembly dilinin kullanımı ile ilgili makalede verilen bilgiler buzdağının sadece görünen kısmı. Bu programlama dilini kullanmak her zaman çok sayıda nüansla ilişkilendirilir ve aşağıdaki makalelerden birinde onlar hakkında yazmamızı istiyorsanız, bize bildirin! Bu arada, ARM mimarisi CPU'larına sahip bilgisayarlarda çalışan Linux sistemleri için programların nasıl yazılacağına dair birçok materyal içeren mükemmel bir kaynak olan http://tinyurl.com/nsgzq89'u ziyaret etmenizi öneririz. Mutlu programlama!

"Assembler School" serisinden önceki makaleler:

Başta KOL oldukça sıra dışı bir montajcı (yeniden öğrenirseniz x86, MCS51 veya AVR). Ancak oldukça basit ve mantıklı bir organizasyonu var, bu yüzden hızlı bir şekilde emiliyor.

Montajcı için Rusça'da çok az belge var. 2 bağlantıya gitmenizi tavsiye edebilirim (belki daha fazlasını bulup bana söylersiniz? Minnettar olacağım.):
ARM ailesinin RISC işlemcilerinin mimarisi ve komut seti - http://www.gaw.ru/html.cgi/txt/doc/micros/arm/index.htm
ARM montajcısını anlama, GBA ASM serisinden, Mike H, çev. Aquila - http://wasm.ru/series.php?sid=21 .

Son bağlantı bana çok yardımcı oldu, sisi dağıttı =). İşe yarayabilecek ikinci şey, garip bir şekilde, C derleyicisidir. ARM için IAR Gömülü Workbench(bundan böyle sadece IAR EW KOLU). Gerçek şu ki, eski zamanlardan beri (ancak kendine saygı duyan tüm derleyiciler gibi) C-kodunu derleyici kodunda derleyebiliyor ve bu da IAR derleyicisi tarafından nesne koduna kolayca derleniyor. Bu nedenle, C'deki en basit işlevi yazıp, onu assembler'a derlemekten daha iyi bir şey yoktur ve hangi assembler komutunun ne yaptığı, argümanların nasıl iletildiği ve sonucun nasıl döndürüldüğü hemen anlaşılacaktır. Bir taşla iki kuş vurursunuz - Assembly dilini öğrenin ve aynı zamanda Assembly kodunu bir C projesine nasıl entegre edeceğinizi öğrenin. montajcıda.

İşte orijinal C işlevi (u16 işaretsiz kısa, u32 işaretsiz int, u8 işaretsiz karakter anlamına gelir):
// crc16.c dosyası
u16 CRC16 (geçersiz* veri belleği, u32 boyutu)
{
u16 tmpWord, crc16, idx;
u8bitCnt;
#define CRC_POLY 0x1021;

crc16 = 0;
idx=0;
while (boyut!=0)
{
/* x veya yüksek bayt crc16 ekleyin ve bayt girin */
tmpWord = (crc16>>8) ^ (*(((u8*)databuf)+idx));
/* sonucu crc16'nın yüksek baytına yaz */
tmpKelime<<= 8;
crc16=tmpWord+(0x00FF&crc16);
için (bitCnt=8;bitCnt!=0;bitCnt--)
{
/* CRC akümülatörünün en önemli bitini kontrol edin */
eğer (crc16 & 0x8000)
{
crc16<<= 1;
crc16 ^=CRC_POLY;
}
Başka
crc16<<= 1;
}
idx++;
boyut--;
}
dönüş crc16;
}

IAR EW ARM montajcı kodu oluşturmak çok kolaydır. crc16.c dosyasının seçeneklerinde (projeye eklendi) kutucuğu işaretledim Devralınan ayarları geçersiz kıl ve ardından Liste sekmesinde 3 onay kutusu koyun - çıktı birleştirici dosyası, Kaynağı dahil et ve Çağrı çerçevesi bilgilerini dahil et(muhtemelen son onay kutusu atlanabilse de - bir sürü gereksiz CFI-yönergeler). Derlemeden sonra project_folder \ ewp \ at91sam7x256_sram \ List \ crc16.s dosyası elde edildi. Bu dosya projeye bir C dosyası kadar kolay eklenebilir (iyi bir şekilde derlenecektir).

Tabii ki, C-derleyicisine C kodunun kesilmemiş bir versiyonunu kaydırdığımda, bana öyle bir montajcı listesi verdi ki hiçbir şey anlamadım. Ama biri hariç tüm C-operatörlerini fonksiyondan attığımda durum daha da netleşti. Sonra adım adım C-operatörlerini ekledim ve sonunda ne oldu:

; crc16.s dosyası
İSİM crc16
KAMU CRC16

CRC_POLY EQU 0x1021

BÖLÜM `.text`: KOD:NOROOT(2)
KOL

// u16 CRC16 (geçersiz* veri belleği, u32 boyutu)
;R0 - sonuç döndür, CRC16
;R1 - boyut parametresi
;R2 - databuf parametresi (R0 girişindeydi)
;R3, R12 - geçici kayıtlar

CRC16:
PUSH (R3, R12); rastgele R3 ve R13'ün kaydedilmesi gerektiğini öğrendim
; gerekli değil. Ama her ihtimale karşı kurtarmaya karar verdim
; olay.
MOVS R2,R0 ;şimdi R2==databuf
HAREKET R3,#+0
MOVS R0,R3 ;crc16 = 0
CRC16_LOOP:
CMP R1, #+0 ;tüm baytlar işlendi (boyut==0)?
BEQ CRC16_RETURN ; öyleyse çıkın
LSR R3, R0, #+8 ;R3 = crc16>>8
LDRB R12, ;R12 = *veri
EOR R3, R3, R12 ;R3 = *databuf ^ YÜKSEK (crc16)
LSL R3, R3, #+8 ;R3<<= 8 (tmpWord <<= 8)
VE R0, R0, #+255 ;crc16 &= 0x00FF
EKLE R0, R0, R3 ;crc16 = tmpWord + (0x00FF & crc16)
HAREKET R12, #+8 ;bitCnt = 8
CRC16_BIT_LOOP:
BEQ CRC16_NEXT_BYTE ;bitCnt == 0?
TST R0,#0x8000 ;Henüz tüm bitler işlenmedi.
BEQ CRC16_BIT15ZERO ;crc16'nın en önemli bitini kontrol edin.
LSL R0,R0,#+1 ;crc16<<= 1
MOV R3, #+(DÜŞÜK(CRC_POLY)) ;crc16 ^= CRC_POLY
ORR R3,R3,#+(YÜKSEK(CRC_POLY)<< 8) ;
EOR R0,R3,R0;
B CRC16_NEXT_BIT

CRC16_BIT15ZERO:
LSL R0,R0,#+1 ;crc16<<= 1
CRC16_NEXT_BIT:
SUBS R12,R12,#+1 ;bitCnt--
B CRC16_BIT_LOOP ;

CRC16_NEXT_BYTE:
EKLE R2,R2,#+1 ;databuf++
SUBS R1,R1,#+1 ;boyut--
B CRC16_LOOP ;tüm baytlar üzerinde döngü

CRC16_GERİ DÖNÜŞ:
POP (R3, R12) ; kayıtları geri yükle
BX LR ;alt program çıkışı, R0==crc16

IAR'ın C derleyicisi şaşırtıcı derecede iyi kod üretir. Çok iyi optimize edemedim. Yalnızca derleyicinin kullanmak istediği fazladan bir geçici kaydı attı (bir nedenden dolayı R3 ve R12 yeterli olmasına rağmen LR'yi fazladan bir geçici kayıt olarak aldı) ve ayrıca sayaçları kontrol eden ve bayrakları ayarlayan birkaç ekstra komutu kaldırdı (basitçe gerekli komutlara S son ekinin eklenmesi).

Bu bölüm, ARM7TDMI işlemcisinin komut setlerini açıklar.

4.1 Formatın kısa açıklaması

Bu bölüm, ARM ve Thumb komut setlerinin kısa bir açıklamasını sağlar.

Komut seti tablolarının anahtarı Tablo 1.1'de sunulmuştur.

ARM7TDMI işlemci, ARMv4T mimarisine dayanmaktadır. Her iki komut setinin daha eksiksiz bir açıklaması için "ARM Mimarisi Referans Kılavuzuna" bakın.

Tablo 1.1. Tabloların anahtarı

ARM komut seti formatları Şekil 1.5'te gösterilmiştir.

ARM komut seti formatları hakkında daha fazla bilgi için "ARM Mimari Referans Kılavuzu"na bakın.

Şekil 1.5. ARM komut seti formatları

Bazı talimat kodları tanımlanmamıştır, ancak bit 6'nın 1 olarak değiştirildiği bir çarpma talimatı gibi tanımsız bir talimat aramasına neden olmazlar. etkileri gelecekte değişebilir. Bu talimat kodlarının ARM7TDMI işlemcisinin bir parçası olarak yürütülmesinin sonucu tahmin edilemez.

4.2 ARM talimatlarının kısa açıklaması

ARM komut seti Tablo 1.2'de sunulmuştur.

Tablo 1.2. ARM talimatlarının kısa tanıtımı

Operasyonlar Montaj Sözdizimi
yönlendirme yönlendirme HAREKET(koşul)(S)Rd,
İleri DEĞİL MVN(koşul)(S)Rd,
Kayıt için SPSR'yi aktarın MRS (koşul) Rd, SPSR
Kayıt için CPSR'yi aktarma MRS (koşul) Rd, CPSR
SPSR Kayıt Transferi MSR(koşul) SPSR(alan), Rm
CPSR yönlendirme MSR(koşul) CPSR(alan), Rm
Bir sabiti SPSR bayraklarına iletme MSR (koşul) SPSR_f, #32bit_Imm
Bir sabiti CPSR bayraklarına iletme MSR (koşul) CPSR_f, #32bit_Imm
Aritmetik İlave EKLE (koşul)(S) Rd, Rn,
Taşıma ile ekleme ADC (koşul)(S) Rd, Rn,
Çıkarma ALT (koşul)(S) Rd, Rn,
Taşıma ile çıkarma SBC (koşul)(S) Rd, Rn,
çıkarma ters çıkarma RSB (koşul)(S) Rd, Rn,
Taşıma ile çıkarma ters çıkarma RSC (koşul)(S) Rd, Rn,
Çarpma işlemi MUL (koşul)(S) Rd, Rm, Rs
çarpmak-birikmek MLA (koşul)(S) Rd, Rm, Rs, Rn
Uzun işaretsiz sayıları çarpma UMUL
Çarpma - uzun değerlerin işaretsiz birikimi UMLAL (koşul)(S) RdLo, RdHi, Rm, Rs
çarpı imzalı uzun SMULL (koşul)(S) RdLo, RdHi, Rm, Rs
Çarpma - uzun değerlerin imzalı birikimi SMLAL (koşul)(S) RdLo, RdHi, Rm, Rs
Karşılaştırmak CMP (koşul)Rd,
Karşılaştırma olumsuz CMN(koşul)Rd,
zeka oyunu muayene TST (koşul)Rn,
denklik kontrolü TEQ(koşul)Rn,
Kayıt. VE VE (koşul)(S) Rd, Rn,
Hariç VEYA EOR (koşul)(S) Rd, Rn,
VEYA ORR (koşul)(S) Rd, Rn,
Sıfırlama biti BIC (koşul)(S) Rd, Rn, >
Geçiş Geçiş (koşul) etiket
Bir bağlantıyı takip etmek (koşul) etiket
Komut setini atlama ve değiştirme (koşul)Rn
Okuma sözler LDR (koşul)Rd,
LDR (koşul)T Rd,
bayt LDR (koşul) B Rd,
LDR (koşul) BT Rd,
imzalı bayt LDR (koşul)SB Rd,
yarım kelime LDR (koşul)H Rd,
işaretli yarı kelimeler LDR (koşul)SH Rd,
çoklu veri bloklarında işlemler -
  • ön artış ile
  • LDM (koşul)IB Rd(, !} {^}
  • ardından artış
  • LDM (koşul)IA Rd(, !} {^}
  • ön eksiltme ile
  • LDM (koşul)DB Rd(, !} {^}
  • ardından bir azalma
  • LDM (koşul)DA Rd(, !} {^}
  • yığın işlemi
  • LDM (koşul) Yol(, !}
  • yığın işlemi ve CPSR kurtarma
  • LDM (koşul) Yol(, !} ^
    kullanıcı kayıtları ile yığın işlemi LDM (koşul) Yol(, !} ^
    Kayıt sözler STR (koşul) Kd,
    kullanıcı modu tercihli kelimeler STR (koşul)T Rd,
    bayt STR (koşul) B Rd,
    kullanıcı modu tercihli bayt STR (koşul) BT Rd,
    yarım kelime STR (koşul)H Rd,
    birden fazla veri bloğu üzerinde işlemler -
  • ön artış ile
  • STM (koşul)IB Rd(, !} {^}
  • ardından artış
  • STM (koşul)IA Rd(, !} {^}
  • ön eksiltme ile
  • STM (koşul)DB Rd(, !} {^}
    o ardından bir azalma STM (koşul)DA Rd(, !} {^}
  • yığın işlemi
  • STM (koşul) Yol(, !}
  • kullanıcı kayıtları ile yığın işlemi
  • STM (koşul) Yol(, !} ^
    Değiş tokuş sözler SWP (koşul) Rd, Rm,
    bayt SWP (koşul) B Rd, Rm,
    yardımcı işlemci Veri üzerinde işlem CDP(koşul)p , , CRd, CRn, CRm,
    Yardımcı işlemciden ARM kaydına aktarım MRC(koşul)p , , Rd, CRn, CRm,
    ARM kaydından yardımcı işlemciye aktarım MCR(koşul)p , , Rd, CRn, CRm,
    Okuma LDC(koşul)p , CRd,
    Kayıt STC(koşul)p , CRd,
    Yazılım kesintisi SWI 24bit_Imm

    ARM modunda komut sistemi ile detaylı olarak tanışabilirsiniz.

    adresleme modları

    Adresleme modları, talimatların kullandığı değerleri oluşturmak için çeşitli komutlar tarafından kullanılan prosedürlerdir. ARM7TDMI işlemci 5 adresleme modunu destekler:

    • Mod 1 - Veri işleme talimatları için işlenenleri kaydırın.
    • Mod 2 - Kelimeyi veya işaretsiz baytı okuyun ve yazın.
    • Mod 3 - Yarım kelimeyi okuyun ve yazın veya işaret baytını yükleyin.
    • Mod 4 - Çoklu Okuma ve Yazma.
    • Mod 5 - Yardımcı işlemciyi okuyun ve yazın.

    Adresleme modları, tipleri ve anımsatıcı kodları ile birlikte Tablo 1.3'te sunulmaktadır.

    Tablo 1.3. adresleme modları

    adresleme modu Adresleme türü veya modu Anımsatıcı kod veya yığın türü
    mod 2 Ofset sabiti
    Ofset Kaydı
    Ofset terazi kaydı
    Önceden indekslenmiş ofset -
    Devamlı !
    Kayıt olmak !
    ölçek kaydı !
    !
    !
    !
    !
    -
    Devamlı , #+/-12bit_Ofset
    Kayıt olmak , +/-Rm
    ölçek kaydı
    Mod 2, ayrıcalıklı Ofset sabiti
    Ofset Kaydı
    Ofset terazi kaydı
    Ofset ve ardından indeksleme -
    Devamlı , #+/-12bit_Ofset
    Kayıt olmak , +/-Rm
    ölçek kaydı , +/-Rm, LSL #5bit_shift_imm
    , +/-Rm, LSR #5bit_shift_imm
    , +/-Rm, ASR #5bit_shift_imm
    , +/-Rm, ROR #5bit_shift_imm
    Mod 3 > Ofset sabiti
    !
    sonraki indeksleme , #+/-8bit_Offset
    Kayıt olmak
    Ön indeksleme !
    sonraki indeksleme , +/-Rm
    Mod 4, okuma IA, sonraki artış FD, tam azalan
    ED, boş azalan
    DA, sonraki azalma FA, tam artan
    DB ön azaltma EA, artan boş
    Mod 4, kayıt IA, sonraki artış FD, tam azalan
    IB, artış öncesi ED, boş azalan
    DA, sonraki azalma FA, tam artan
    DB ön azaltma EA, artan boş
    Mod 5, yardımcı işlemci veri aktarımı Ofset sabiti
    Ön indeksleme !
    sonraki indeksleme , #+/-(8bit_Offset*4)

    işlenen 2

    Bir işlenen, verilere veya bir çevre birimine atıfta bulunan bir talimatın bir parçasıdır. İşlenenler 2 tablo 1.4'te sunulmaktadır.

    Tablo 1.4. işlenen 2

    Alanlar tablo 1.5'te sunulmuştur.

    Tablo 1.5. alanlar

    Koşul alanları

    Koşul alanları Tablo 1.6'da sunulmuştur.

    Tablo 1.6. Koşul alanları

    Alan türü son ek Açıklama Şart
    Koşul (koşul) EQ eşittir Z=1
    NE Eşit değildir Z=0
    CS İşaretsiz büyük veya eşit C=1
    bilgi imzasız daha az C=0
    olumsuz N=1
    PL pozitif veya sıfır N=0
    VS taşma V=1
    VC taşma yok V=0
    MERHABA daha imzasız C=1, Z=0
    LS İşaretsiz küçük veya eşit C=0, Z=1
    G.E. Daha fazla veya eşit N=V (N=V=1 veya N=V=0)
    LT Daha az NV (N=1 ve V=0) veya (N=0 ve V=1)
    GT Daha Z=0, N=V (N=V=1 veya N=V=0)
    LE daha az veya eşit Z=0 veya NV (N=1 ve V=0) veya (N=0 ve V=1)
    AL Herzaman doğru bayraklar yoksayılır

    4.3 Thumb komut setinin kısa açıklaması

    Thumb komut seti formatları Şekil 1.6'da gösterilmektedir. ARM komut seti formatları hakkında daha fazla bilgi için ARM Mimari Referans Kılavuzuna bakın.


    Şekil 1.6. Başparmak talimat seti formatları

    Thumb komut seti Tablo 1.7'de gösterilmiştir.

    Tablo 1.7. Thumb komut setinin kısa açıklaması

    Operasyon Montaj Sözdizimi
    Yönlendirme (kopyalama) sabitler MOV Yolu, #8bit_Imm
    kıdemli ila genç MOV Yolu, Hs
    gençten kıdemliye MOV Hd, Rs
    kıdemliden kıdemliye MOV Hd, Hs
    Aritmetik ilave EKLE Rd, Rs, #3bit_Imm
    küçüğe küçüğe ekle EKLE Rd, Rs, Rn
    daha yaşlıyı daha genç eklemek EKLE Rd, Hs
    gençliği yaşlıya eklemek HD EKLE, Rs
    yaşlıya yaşlı ekle EKLE Hd, Hs
    sabit ile ekleme EKLE Rd, #8bit_Imm
    SP'ye değer katmak SP EKLE, #7bit_Imm SP EKLE, #-7bit_Imm
    taşıma ile ekleme ADC Yolu, Rs
    çıkarma ALT Rd, Rs, Rn SUB Rd, Rs, #3bit_Imm
    sabit çıkarma ALT Rd, #8bit_Imm
    taşıma ile çıkarma SBC Rd, Rs
    inversiyon işareti NEG Rd, Rs
    çarpma işlemi MUL Rd, Rs
    küçük ile genç karşılaştır CMP Rd, Rs
    genç ve kıdemli karşılaştırın CMP Yolu, Hs
    yaşlı ve genç karşılaştırın CMP HD, Rs
    daha yaşlı ve daha yaşlı karşılaştırın CMP Hd, Hs
    olumsuz karşılaştırmak CMN Yolu, Rs
    sabitle karşılaştır CMP Yolu, #8bit_Imm
    zeka oyunu VE VE Rd, Rs
    Hariç VEYA EOR Rd, Rs
    VEYA ORR Rd, Rs
    Sıfırlama biti BIC Rd, Rs
    İleri DEĞİL MVN Rd, Rs
    bit testi TST Yolu, Rs
    Shift/Döndür Mantıksal kaydırma sola LSL Rd, Rs, #5bit_shift_imm LSL Rd, Rs
    Mantıksal sağa kaydırma LSR Rd, Rs, #5bit_shift_imm LSR Rd, Rs
    Aritmetik sağa kaydırma ASR Rd, Rs, #5bit_shift_imm ASR Rd, Rs
    Sağ dönüş ROR Rd, Rs
    Geçiş koşullu atlamalar -
    BEQ etiketi
    BNE etiketi
    BCS etiketi
    BCC etiketi
    BMI etiketi
    BPL etiketi
    BVS etiketi
    BVC etiketi
  • C=1, Z=0
  • BHI etiketi
  • C=0, Z=1
  • BLS etiketi
  • N=1, V=1 veya N=0, V=0
  • BGE etiketi
  • N=1, V=0 veya N=0, V=1
  • BLT etiketi
  • Z=0 ve ((N veya V=1) veya (N veya V=0))
  • BGT etiketi
  • Z=1 veya ((N=1 veya V=0) veya (N=0 ve V=1))
  • BLE etiketi
    koşulsuz atlama B etiketi
    Uzun bağlantı bağlantısı BL etiketi
    İsteğe bağlı durum değişikliği -
  • adreste ml. Kayıt ol
  • BX R'ler
  • adresindeki st. Kayıt ol
  • BX Hs
    Okuma ofset sabiti ile -
  • sözler
  • LDR Yolu,
  • yarım kelime
  • LDRH Yolu,
  • bayt
  • LDRB Yolu,
    ofset kaydı ile -
  • sözler
  • LDR Yolu,
  • yarım kelime
  • LDRH Yolu,
  • yarım kelimeyi imzala
  • LDRSH Yolu,
    LDRB Yolu,
  • imzalı bayt
  • LDRSB Yolu,
    PC program sayacına göre LDR Yolu,
    yığın işaretçisi SP'ye göre LDR Yolu,
    adres -
  • bilgisayar aracılığıyla
  • EKLE Rd, PC, #10bit_Offset
  • SP kullanarak
  • EKLE Rd, SP, #10bit_Offset
    Çoklu okuma LDMIA Rb!,
    Kayıt ofset sabiti ile -
  • sözler
  • STR Yolu,
  • yarım kelime
  • yol yolu,
  • bayt
  • cadde yolu,
    ofset kaydı ile -
  • sözler
  • STR Yolu,
  • yarım kelime
  • yol yolu,
  • bayt
  • cadde yolu,
    SP ile ilgili STR Yolu,
    Çoklu giriş STMIA Rb!,
    Yığından itme/patlatma Kayıtları yığına itin İTMEK
    LR ve kayıtları yığına itin İTMEK
    Yığından pop kayıtları POP
    Pop kayıtları ve PC yığından POP
    Yazılım kesintisi - SWI 8bit_Imm

    Şu anda, oldukça basit mikro denetleyicileri bile programlamak için, kural olarak, C veya C++ dilinin alt kümeleri olan üst düzey diller kullanılmaktadır.

    Bununla birlikte, işlemcilerin mimarisini ve özelliklerini incelerken, yalnızca bu tür bir yaklaşım incelenen mimarinin özelliklerinin tanımlanmasını sağlayabileceğinden, montajcı dillerinin kullanılması tavsiye edilir. Bu nedenle, sonraki sunum Assembly dili kullanılarak gerçekleştirilir.

    ARM7 komutlarının değerlendirilmesine geçmeden önce, aşağıdaki özelliklerine dikkat etmek gerekir:

      İki komut seti desteği: 32-bit komutlu ARM ve 16-bit komutlu THUMB. Aşağıdaki 32 bitlik bir komut setidir, ARM kelimesi bu formata ait talimatlar ve ARM7 kelimesi - CPU'nun kendisi anlamına gelecektir.

      İki 32 bit adres formatı desteği: küçük endian işlemci ve küçük endian işlemci. İlk durumda, en anlamlı bit (En Anlamlı Bit - MSB), kelimenin en az anlamlı bitinde ve ikinci durumda en anlamlı bitinde bulunur. Bu, üst düzey dilleri kullanırken diğer 32 bit işlemci aileleriyle uyumluluk sağlar. Ancak, ARM çekirdekli bir dizi işlemci ailesinde, yalnızca çok az endian kullanılır (yani, MSB adresin en önemli bitidir), bu da işlemciyle çalışmayı büyük ölçüde basitleştirir. ARM7 için kullanılan derleyici her iki formatta da kodla çalıştığı için word formatının doğru ayarlandığından emin olmalısınız, aksi halde ortaya çıkan kod "tersine çevrilmiş" olacaktır.

      ALU'da kullanmadan önce "geçişte" işlenenlerden birinde çeşitli türlerde kaydırmalar yapabilme

      Herhangi bir komutun koşullu yürütülmesi için destek

      İşlem sonuçlarının bayraklarının değiştirilmesini yasaklama imkanı.

        1. Koşullu Komut Yürütme

    ARM komut setinin önemli özelliklerinden biri, herhangi bir komutun koşullu yürütülmesini desteklemesidir. Geleneksel mikrodenetleyicilerde, yalnızca koşullu komutlar koşullu atlamalardır ve belki de tek tek bitlerin durumunu kontrol etme veya değiştirme talimatları gibi bir dizi diğerleri. ARM komut setinde, talimat kodunun üst 4 biti her zaman CPSR kaydındaki durum bayraklarıyla karşılaştırılır. Değerleri eşleşmezse, şifre çözme aşamasındaki komut NOP (işlem yok) komutu ile değiştirilir.

    Bu, programın "kısa" geçişlerle bölümlerinin yürütme süresini önemli ölçüde azaltır. Bu nedenle, örneğin, gerçek katsayılı ikinci dereceden denklemleri ve negatif bir diskriminant ile keyfi kökleri çözerken, karekökü hesaplamadan önce diskriminantın işaretini değiştirmek ve sonucu cevabın sanal kısmına atamak gerekir.

    Bu problemin geleneksel çözümünde koşullu atlama komutunun girilmesi gerekmektedir. Bu komutun yürütülmesi en az 2 döngü sürer - yeni adres değerinin şifresinin çözülmesi ve program sayacına yüklenmesi ve komut hattını yüklemek için ek döngü sayısı. Bir pozitif diskriminant ile koşullu komut yürütmeyi kullanırken, işaret değiştirme komutu boş bir işlemle değiştirilir. Bu durumda, talimat ardışık düzeni temizlenmez ve kayıplar birden fazla saat döngüsü değildir. Koşullu talimatların NOP talimatı ile değiştirilmesinin geleneksel koşullu dal talimatlarının yürütülmesinden daha verimli olduğu eşik ve bununla ilişkili boru hattının yeniden doldurulması derinliğine eşittir, yani. üç.

    Bu özelliği uygulamak için, koşul bayraklarının test durumlarını tanımlayan on altı önekten herhangi birini, montajcı talimatlarının (ve C'nin de) temel anımsatıcı notasyonuna eklemeniz gerekir. Bu önekler Tabloda verilmiştir. 3. Buna göre her takım için 16 seçenek vardır. Örneğin, aşağıdaki komut:

    HAREKET R1, #0x008

    0x00800000 numarasının R1 kaydına yüklenmesinin, yalnızca son veri işleme komutunun sonucu "eşit" olduğunda veya 0 sonucu alındığında ve CPSR kaydının bayrağı (Z) buna göre ayarlanmışsa gerçekleştirileceği anlamına gelir.

    Tablo 3

    Komut önekleri

    Anlam

    Z yüklü

    Z düştü

    yüklü

    Büyüktür veya eşittir (işaretsiz)

    C sıfırlama

    Aşağıda (imzasız)

    N yüklü

    olumsuz sonuç

    N düştü

    Olumlu sonuç veya 0

    V yüklü

    taşma

    V düştü

    taşma yok

    yüklü ile,

    Z düştü

    Yukarıda (imzasız)

    Düşen ile,

    Z yüklü

    Küçüktür veya eşittir (işaretsiz)

    Büyüktür veya eşittir (imzalı)

    N, V'ye eşit değil

    Daha az (imzalı)

    Z sıfırlama VE

    (N eşittir V)

    Daha fazla (imzalı)

    Z ayarlanır VEYA

    (N, V'ye eşit değildir)

    Küçüktür veya eşittir (işaretli)

    (görmezden gelindi)

    koşulsuz yürütme

    Konunun devamı:
    internet

    Yeni bir taşıyıcı dağılımı hemen kurulmadığından, bir yarı iletken diyot, akım veya voltajdaki yeterince hızlı değişikliklere göre inerttir. Nasıl...