Çokça karşılaştığımız Unix uygulamalarından bir tanesi de filtrelerdir. Filtre standart girişten almışolduğu veriyi bir şekilde işleyerek standart çıkışa sonucu yazar.
Bu bölümde basit bir filtre geliştireceğiz ve stdin ve stdout yapılarının nasıl çalıştığınıgöreceğiz.Bu filtre girilen verinin her bir byte'ını16 lık sayıtabanında aralara bir boşluk bırakarak ekrana yazdıracak.
%include 'system.inc' section .data hex db '0123456789ABCDEF' buffer db 0, 0, ' ' section .text global _start _start: ; read a byte from stdin push dword 1 push dword buffer push dword stdin sys.read add esp, byte 12 or eax, eax je .done ; convert it to hex movzx eax, byte [buffer] mov edx, eax shr dl, 4 mov dl, [hex+edx] mov [buffer], dl and al, 0Fh mov al, [hex+eax] mov [buffer+1], al ; print it push dword 3 push dword buffer push dword stdout sys.write add esp, byte 12 jmp short _start .done: push dword 0 sys.exit
Programın veri bölümünde hex olarak adlandırdığımız bir dizi oluşturduk.Bu dizi artan sırada 16 lık tabanın rakamlarını tutuyor.Ardından tanımlamışolduğumuz buffer hem girişhem çıkışişlemi için kullanılacak. Buffer'ın ilk iki biti başlangıçta 0 yapıldı.Bunu bizim 16 lık sayıtabanındaki sayının basamaklarınıyazmak için kullanacağız(Ayrıca ilk byte okuma sırasında da kullanılıyor).Üçüncü byte ise boştur.
Kod kısmı4 bölümden oluşur : bir byte okuma , byte içinde bulunan veriyi 16 lıtabana çevirme , sonucu yazdırma ve en sonunda programdan çıkma.
Bir byte okumak için sistemin read fonksiyonunu çağırarak stdin dosyasına erişiyoruz.Ardından aldığımız veriyi buffer içine kaydediyoruz.Sistem kaç byte okunduğunu EAX yazmacına döner.Eğer veri geliyorsa 1 olmalıdır , hiç girişyoksa 0 olmalıdır. O yüzden EAX da bulunan değeri kontrol etmeliyiz eğer 0 ise programıbitirmek için .done etiketine geçişyapmalıyız.
Not: Basit olmasımanasında şimdi ortaya çıkabilecek diğer hatalarıihmal ettik.
16'lık tabanda çeviri yapılırken önbellek(buffer) EAX içine atılır (Aslında AL'ye).Geri kalan bitler 0 olarak ayarlanır.Ayrıca bir kopyada EDX içine alındıçünkü her dört biti birbirinden ayıracağız.Sonucu önbelleğin ilk iki byte lık kısmında sakladık
Sisteme 2 byte 16 lık basamaklar biri ise boşluk karakteri olmak üzere 3 byte stdout a yazdırdıktan sonra bir sonraki okuyacağımız byte a geçtik.
Girişişlemi bitmişise programısonlandırma komutunu çalıştırıp 0 dönmesini sağladık. 0 programın başarılıbir şekilde sonlandığımanasına geliyor.
Sonra devam edecek olursak bu dosyayıhex.asm olarak kaydettik. Derleyip çalıştırmak için :
% nasm -f elf hex.asm % ld -s -o hex hex.o % ./hex Hello, World! 48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 0A Here I
come! 48 65 72 65 20 49 20 63 6F 6D 65 21 0A ^D %Not: MS-DOS sisteminden Unix sistemine geçişyapanlar neden yazılar 0D 0A yerine sadece 0A ile sonlanıyor diye sorabilirler.Çünkü Unix'te satır sonlarıcr/lf(enter , satır besleme) düzenini ile değil nl(yeni satır) düzeni ile yapılır ve sadece 0A ile gösterilir.
Bunu geliştirebilir miyiz ? Bu biraz karışık olacaktır çünkü bir satırıdönüştürdükten sonra satır başından devam edemeyiz. Her bir 0A dan sonra boşluk yerine yeni satır koyarak değiştirebiliriz.
%include 'system.inc' section .data hex db '0123456789ABCDEF' buffer db 0, 0, ' ' section .text global _start _start: mov cl, ' ' .loop: ; read a byte from stdin push dword 1 push dword buffer push dword stdin sys.read add esp, byte 12 or eax, eax je .done ; convert it to hex movzx eax, byte [buffer] mov [buffer+2], cl cmp al, 0Ah jne .hex mov [buffer+2], al .hex: mov edx, eax shr dl, 4 mov dl, [hex+edx] mov [buffer], dl and al, 0Fh mov al, [hex+eax] mov [buffer+1], al ; print it push dword 3 push dword buffer push dword stdout sys.write add esp, byte 12 jmp short .loop .done: push dword 0 sys.exit
Bu kodda boşluk karakterini CL yazmacında saklıyoruz. Bunu güvenli bir şekilde yapabiliriz. Çünkü Windows'tan farklıolarak Unix sistemleri yazmaçlar içindeki veriyi değiştirmez ve fonksiyonun sonuçlarını yazmaçlara(registers) dönmez.
Bu demek oluyor ki ;boşluk karakteri CL içinde bir defa saklamanız yeterlidir. Bunun için .loop adlıbir etiket ile bir sonraki byte için atlama yaptık tekrar programın başına _start ' a gitmedik . Çünkü aynıayarlamalarıtekrar etmeye gerek yoktu. Ayrıca .hex etiketi ile 3. karakterin boşluk karakteri yada yeni satır olabileceğini belirttik.
Bu değişiklikleri yaptıktan sonra çalıştırmak için :
% nasm -f elf hex.asm % ld -s -o hex hex.o % ./hex Hello, World! 48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 0A Here I come! 48 65 72 65 20 49 20 63 6F 6D 65 21 0A ^D %
Bu daha güzel görünüyor.Fakat bu kod oldukça verimsiz.Her bir byte için iki kere sistem çağrısıkullandık.