11.7. UNIX® Filtreleri yazmak

Ç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.