Genel Satıriçi Sembolik Makina Dili Kullanım Örnekleri
Önceki Linux için Satıriçi Sembolik Makina Dili (Inline Assembly) Sonraki
Genel Satıriçi Sembolik Makina Dili Kullanım Örnekleri
Aşağıdaki örnekler değişik terim belirteçlerinin kullanımını göstermektedir. Örnek verilebilecek çok fazla belirteç vardır fakat burada daha sık kullanılan belirteç türlerine yer verilmiştir.
"asm" ve register belirteci "r"
İlk önce 'r' belirtecinin "asm" de kullanılışına bir bakalım. Örneğimiz GCC'nin nasıl yazmaç ayırdığını ve değerleri nasıl güncelleştirdiğini göstermektedir.
int main(void)
{
        int x = 10, y;

        asm ("movl %1, %%eax;
             "movl %%eax, %0;"
                :"=r"(y)        /* y çıktı terimidir */
                :"r"(x)         /* x girdi terimidir */
                :"%eax");       /* %eax geri dönen yazmaç */
}
Bu örnekte x'in değeri "asm" içinde y'ye kopyalandı. x ve y "asm"den yazmaçların içinde geçtiler. Bunun için üretilecek Sembolik Makina Dili kodu şuna benzer:
    main:
        pushl %ebp
        movl %esp,%ebp
        subl $8,%esp
        movl $10,-4(%ebp)
        movl -4(%ebp),%edx      /* x=10 %edx te tutulur */
#APP    /* asm burada başlıyor */
        movl %edx, %eax         /* x %eax e taşınır*/
        movl %eax, %edx         /* y edx e taşınır ve güncelleştirilir */

#NO_APP /* asm burada bitiyor */
        movl %edx,-8(%ebp)      /* y nin yığıttaki değeri %edx in değeri ile
                                   güncelleştirilir.*/
'r' belirteci kullanıldığında GCC herhangi bir yazmacı ayırmakta serbesttir. Örneğimizde x'i tutmak için %edx'i seçmiştir. x'in değerini %edx'ten okuduktan sonra y için yine %edx'i seçmiştir.
y'nin değeri çıktı terimi bölümünde oldukça %edx'in güncelleştirilen değeri ;y'nin yığıttaki yeri, -8(%ebp)'de belirtilir. Eğer y girdi bölümünde tanımlanmış olsa idi, geçici y yazmacı (%edx) güncelleştirilmesine rağmen y'nin yığıttaki yeri güncelleştirilmezdi.
%eax geri dönen yazmaçlar listesinde oldukça GCC onu bilgi tutmak dışında kullanmayacaktır.
Çıktılar oluşmadan önce girdilerin yok olduğu farz edilerek, hem x girdisi hem y çıktısı aynı %edx yazmacında tutuldular. Ama şunu unutmayın eğer bir kaç komut işletecekseniz bunu yapamazsınız. Girdi ve çıktıların farklı yazmaçlarda tutulduğundan emin olmak için, & belirteç değiştiricisini kullanabiliriz.
Bununla ilgili bir örnek :
        int main(void)
{
        int x = 10, y;

        asm ("movl %1, %%eax;
             "movl %%eax, %0;"
                :"=&r"(y)       /* y çıktı terimidir ve
                                   & belirteç değiştiricisidir. */
                :"r"(x)         /* x girdi terimidir */
                :"%eax");       /* %eax geri dönen yazmaçtır*/
}
Ve burada bu örnek için üretilmiş Sembolik Makina Dili kodunu bulabilirsiniz, x ve y'nin "asm" de farklı yazmaçta tutulduğu görülmektedir.
    main:
        pushl %ebp
        movl %esp,%ebp
        subl $8,%esp
        movl $10,-4(%ebp)
        movl -4(%ebp),%ecx      /* x, girdi %ecx tedir */
#APP
        movl %ecx, %eax
        movl %eax, %edx         /* y, çıktı %edx tedir */

#NO_APP
        movl %edx,-8(%ebp)
Özel yazmaç belirteçlerinin kullanımı
Şimdi kişisel yazmaçları terimler için belirteç olarak kullanmaya bir bakalım. Aşağıdaki örneğimizde cpuid komutu girdiyi %eax yazmacından alıyor ve çıktıyı dört yazmaca veriyor: %eax, %ebx, %ecx, %edx. cpuid girdisi, op değişkeni, "asm"ye %eax cpuid'nin de beklediği gibi yazmacından giriyor. a, b, c ve d belirteçleri çıktıda dört yazmaçtaki değerleri kendilerinde toplamak için kullanılmıştır.
    asm ("cpuid"
                : "=a" (_eax),
                  "=b" (_ebx),
                  "=c" (_ecx),
                  "=d" (_edx)
                : "a" (op));
Aşağıda bunun için üretilmiş Sembolik Makina Dili kodunu görebilirsiniz (_eax ,_ebx vb ... değişkenlerin yığıtta bulunduğu varsayılmıştır):
        movl -20(%ebp),%eax     /* 'op' yi %eax te tut -- girdi */
#APP
        cpuid
#NO_APP
        movl %eax,-4(%ebp)      /* %eax i _eax te tutar -- çıktı */
        movl %ebx,-8(%ebp)      /* diğer yazmaçları kendi çıktı
        movl %ecx,-12(%ebp)        değişkenlerinde tutar */
        movl %edx,-16(%ebp)
Aşağıdaki yolda strcpy fonksiyonu "S" ve "D" belirteçleri kullanılarak uygulanabilir:
    asm ("cld\n
              rep\n
              movsb"
              : /* girdi yok */
              :"S"(src), "D"(dst), "c"(count));
Kaynak gösterge src %esi'ye "S" belirteci ve hedef gösterge dst'ye "D" belirteci kullanılarak yerleştirilmiştir. Sayaç değeri rep önekinin gerektirdiği gibi %ecx'e yerleştirilmiştir.
Ve burada da 32-bit kodları birleştirip 64-bit kod elde etmek için, %eax ve %edx olmak üzere iki yazmaç kullanan bir belirteç göreceksiniz:
#define rdtscll(val) \
     __asm__ __volatile__ ("rdtsc" : "=A" (val))
Üretilen Sembolik Makina Dili kodu şuna benzer (val 64 bit bellek alanına sahipse):
#APP
        rdtsc
#NO_APP
        movl %eax,-8(%ebp)      /* A belirtecinin sonucu olarak
        movl %edx,-4(%ebp)         %eax ve %edx çıktı gibi çalışır */
%edx:%eax'in içindeki değerler 64 bit çıktı gibi çalışır.
Karşılaştırma belirteçlerinin kullanımı
Burada da dört parametreli sistem çağrıları için kodları göreceğiz:
#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \
type name (type1 arg1, type2 arg2, type3 arg3, type4 arg4) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
        : "=a" (__res) \
        : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
          "d" ((long)(arg3)),"S" ((long)(arg4))); \
__syscall_return(type,__res); \
}
Üstteki örnekte sistem çağrısına dört argüman %ebx, %ecx, %edx ve %esi'ye b, c, d ve S belirteçleri kullanılarak bırakılmıştır. "=a" belirteci çıktıda kullanılmıştır. Bundan dolayı sistem çağrısının dönen değeri %eax, __res değişkenine aktarılır. Karşılaştırma belirteçlerinden "0"'ı girdi bölümünün ilk teriminin belirteci olarak kullanarak, sistem çağrısı numarası __NR_##name %eax'e atanır ve sistem çağrısın girdisi olarak işlem görür. Böylece %eax burada hem girdi hem de çıktı yazmacı olarak çalışır. Bunun için iki farklı yazmaç kullanılmadı. Ama şu unutulmamalıdır ki girdi (sistem çağrı numarası) çıkış üretilmeden önce kullanılmalıdır.
Bellek terimi belirtecinin kullanımı
Aşağıdaki küçük azaltma işlemini inceleyin:
    __asm__ __volatile__(
                "lock; decl %0"
                :"=m" (counter)
                :"m" (counter));
Bunun için üretilen Sembolik Makina Dili kodu şöyle olur:
    #APP
        lock
        decl -24(%ebp) /* sayaç bu bellek bölümünde değiştirilir. */
#NO_APP.
Burada sayaç için yazmaç belirteci kullanmadan önce bir düşünmelisiniz. Eğer kullanırsanız, önce sayacın içeriğini bir yazmaca atamalısınız. Azaltma işlemini yaptıktan sonra sonucu belleğine atamalısınız. Ama o zaman da lock kullanma ve kodu küçük tutma çabamızı boşa çıkarmış oluruz. Bu bellek belirtecinin gereğini kesin olarak göstermektedir.
Geri Dönen yazmaçların Kullanımı
Temel Bellek Kopyasının işlemesini inceleyin.
        asm ("movl $count, %%ecx;
              up: lodsl;
              stosl;
              loop up;"
                :                       /* çıktı yok */
                :"S"(src), "D"(dst)     /* girdi */
                :"%ecx", "%eax" );      /* geri dönen yazmaçlar */
lodsl %eax'i değiştirirken, lodsl ve stosl komutları onu dolaylı olarak kullanır. %ecx yazmacı doğrudan count'u çağırır. Biz %eax ve %ecx'i geri dönen yazmaçlar listesinde belirtmediğimiz sürece GCC bunun farkında olmayacaktır. Bunu yapmadığımız sürecce GCC %eax ve %ecx'in serbest olduğunu varsayar ve onları başka bilgiler tutmak için kullanabilir. Şunu bilmelisiniz ki %esi ve %edi "asm" tarafından kullanılıyor ve geri dönen yazmaçlar arasında belirtilmemişlerdir. Çünkü onlar zaten girdi terimlerinin listesinde bulunmaktadırlar. Ve son olarak, eğer dolaylı veya doğrudan kullanılmış bir yazmaç girdi veya çıktı listesinde bulunmuyorsa, onu geri dönen yazmaçlar listesine yazmalısınız.
Önceki Üst Ana Başlık Sonraki
Terim Belirteçleri Başlangıç Sonuç
Bir Linux Kitaplığı Sayfası