11.13. FPU Kullanımı

Garip bir şekilde bir çok assembly programlama dökümanıFPU'nun yada kayan noktalıünite (float point unit) nokvarlığından bahsetmiyor. İsterseniz biz biraz bu konuya açıklık getirelim.

11.13.1. FPU düzenlemesi

FPU 8 tane 80 bitlik yazmaçlardan oluşur. Bu yazmaçlar bir yığın gibi organize olmuşlardır , yani bir değeri push edersiniz yığına kaydedersiniz tekrar geri almak için pop işlemini uygularsınız.

Bu komutların assembly karşılıklarıpush ve pop değildir çünkü bunlar zaten önceden alınmıştır.

Push işlemini yapmak için

 fld,fild ve fbld
        
komutlarınıkullanabilirsiniz.Bunun dışında diğer bazıkomutlar ortak olarak kullanılan - pi sayısıgibi - sabitleri yığına atar.

Benzer şekilde pop işlemini gerçekleştirmek için

        fst,fstp,fist,fistp ve fbstp 
komutlarınıkullanabilirsiniz. Aslında bu komutlardan sonu p ile bitenler çıkarma(pop) işleminini gerçekleştirir diğerleri ise değerleri hafızadan silmeden başka bir yere kaydeder.

Bu yığının üst değeri ile 32-bit 64-bit yada 80-bit reel sayı,16-bit 32-bit veya 64-bit tamsayıyada 80-bit paketlenmişondalık sayıiçerir.(packed decimal)

Paketlenmişondalık sayıikili olarak kodlanmışondalık sayının özel bir halidir. Bu sistem FPU'nun içinde bulunan verilerin ASCII dönüşümünde kolaylık sağlamaktadır.Bu şekilde 18 tane basamak tanımlanabilmektedir.

FPU verilerin hafızada nasıl gösterildiğine bakmaksızın yazmaçlarda veriyi 80-bit reel sayıbiçiminde saklar.

Sayıların virgülden sonraki kısmıen azından 19 basamaklıdır. Bu yüzden sonuçlarımızı18 basamak hassasiyetinde ASCII olarak göstermek istesek bile doğru sonucu göstermeye devam edeceğiz.

Yığınımızın en üstünde matematik işlemleri de gerçekleştirebiliriz. Hatta orada bulunan sayının sinüsünü alabilir , üzerine çarpma ve bölme işlemi uygulayabiliriz.

Bu işlemleri FPU yazmaçlarıüzerine de uygulayabiliriz.

Intel komutlarının bu yığının en üstü için kullandığıifade st dir.Yazmaçlar için ise st(0)–st(7) şeklinde kullanılır.st ve st(0) aynıyazmacıifade eder.

Ne sebeble olursa olsun nasm programınıyapanlar bu komutları parantez olmadan st0-st7 şeklinde tercih etmişlerdir.Yığının en üstü st0 şeklinde ifade edilebilir ve sadece 'st' şeklinde kullanım geçerli değildir.

11.13.2. Paket ondalık biçimi

Paket ondalık biçimi 10 byte hafızadan oluşur ve 18 basamaklıdır. Hafızada tutulan sayıher zaman bir tam sayıdır.

İpucu: Sayının basamak değerlerini elde etmek için TOS(yığının en üst kısmı) ' u 10 ile çarpabilirsiniz.

En yüksek byte içinde bulunan en yüksek bit işaret bit'idir.Eğer 1 ise sayımız negatif ,değil ise sayımız pozitif sayıdır.Bu byte içinde diğer bitlerin önemi yoktur.

Geri kalan 9 byte ise 18 basamağıifade ermektir her bir byte içinde iki basamak vardır.

Daha etkili olan basamak yüksek bitlerde düşük değerli olan basamak ise düşük dört bit içinde bulunur.

Mesela -1234567 sayısışu şekilde saklanacaktır ( 16lı gösterimde ):

80 00 00 00 00 00 01 23 45 67

Intel ise little-endian kullandığından şu şekilde olur :

67 45 23 01 00 00 00 00 00 80

Bunu sakın unutmayın , yoksa ilerde nerede hata yaptğınızı bulmak için çok zaman harcasınız

Not: Bir kitap önerisi: ( eğer bulabilirseniz ) Richard Startz’ 8087/80287/80387 for the IBM PC &Compatibles (http://www.int80h.org/cgi-bin/isbn?isbn=013246604X).

Bu kitapta little-endian ve packed decimal ile ilgili bilgi bulabilirsiniz.

11.13.3. İğne deliği fotografisi üzerine kısa bir gezinti

Mantıklıbir yazılım yazmak için sadece programlama araçlarını değil program yazdığımız alanıda iyi bilmemiz gereklidir.

Bundan sonraki filtremiz bir iğne deliği kamerasıistediğimizde bize yardımcıolacak bir filtre olacak. Bu yüzden bu konu hakkında biraz bilgimiz olmalı

11.13.4. Kamera(Fotograf makinesi)

Bir kamerayıtarif edecek olursak basitçe ışığa duyarlıbir cisim içeren kapalıbir kutu ve bu kutunun dışarıaçılan küçük bir deliği olarak tanımlayabiliriz.

Bu kutu sağlam bir maddeden yada esnek bir materyalden olabilir.Fotograf makinasının içi oldukça karanlıktır.Işığın girebileceği tek nokta bu deliktir. Buradan gelen ışıklar fotograf makinesinin içinde görüntüyü oluştururlar.

Burada ışığa duyarlımateryal mesela film gibi cisimler vardır. Bu cisim sayesinde görüntü alınır.

Bu delik genelde lens yada lensler içerir ve bu bölüm objektif olarak adlandırılır.

11.13.5. İğne deliği

Tam olarak gerçekleri söyleyecek olursak lens illa ki gerekli değil.Orjinal kamarelar lens değil iğne deliği kullanır. Bugün bile kameraların nasıl çalıştığınıanlamak özel resimler elde etmek için iğne deliği kullanılır.

Aynıiğne deliğinden elde edilen bütün resimler aynınetlikte yada bulanıklıktadır.Bu delik için belli bir ideal büyüklük vardır büyük yada küçük olmasıoluşacak resmin bulanık olmasına neden olur.

11.13.6. Odak uzaklığı

İdeal deliğin boyutu odak uzaklığının kareköküne bağlıbir fonksiyondur.Film ile delik arasıuzaklık yine odak uzaklığının kareköküdür.

D = PC * sqrt(FL)

Yukarıda D ideal deliğin çapınıbelirtir. FL odak uzaklığıdır ve PC ise kamera için tanımlanmışsabittir.Jay Bender' e göre bu değer 0.04 olmalıdır ama Kenneth Connors bunu 0.37 olarak belirlenmiştir.Diğer değerler farklıamaçlar için yapılabilir. Bu değer gündüz ışığında geçerlidir diğer ışık ortamlarıdaha farklı sabitler gerektirecektir ve ancak deneysel olarak tesbit edilebilir.

11.13.7. F- sayısı

F sayısıne kadar ışığın filme ulaştığınıbelirlemede çok kullanışlıbir ölçü aracıdır.Bir ışık ölçer bunu tesbit edebilir, mesela f5.6 hassasyetinde bir filmi pozlamak için 1/1000 sn geçmesi gereklidir.

Kameranın nasıl bir kamera olduğunun önemi olmadan ister 35-mm ister 6x9 kamera olsun f sayısıbiliniyor ise pozlama zamanı bilebiliriz.F sayısınıhesaplamak kolaydır.

F = FL / D

Bir başka deyişle F sayısıOdak uzaklığının delik çapına bölmüdür.Buradan şu çıkarabilir : Eğer bu sayıyüksek ise odak uzaklığıfazladır ve delik boyutu küçüktür. Bu sayıbüyük olursa pozlama zamanıdaha fazla olacaktır.

Bundan başka delik yarıçapıve odak uzaklığıtek boyutlu değişken olduğu halde film ve deliğin alanıiki boyutlu değişkenlerdir. Bu demek oluyor ki eğer sizin A sayısında pozlama süreniz t ise B sayısında pozlama süreniz aşağıdaki şekilde hesaplanabilir.

t * (B / A)^2

formülünü kullanmalıyız

11.13.8. Normalize edilmişF sayısı

Modern kameralar deliğin boyutunu değiştirebileceği için bu yüzden f sayılarıbiraz daha esnek olarak yukarıda anlatıldığı biçimde olmayabilir.

Birden fazla f sayısına destek vermek için tipik olarak kameralar içinde değişik boyutlarda delikler açılmışmetal plakalar vardır.

Boyutlarıyukarıda verilen formüle göre standart f değerlerinden birini üretecek şekilde seçilir.Mesela Kodak Duaflex IV kamerasıböyle 8,11,16 f değeri üretecek şekilde 3 tane delik içermektedir.

Son zamanlarda üretilen kameraların f değerleri genel olarak 2.8,4,5.6,8,11,16,22 ve 32 dir. Bu sayılar rastgele seçilmiş değildir.Genel olarak 2 nin üssü şeklinde yada biraz yuvarlanmış hali olarak seçlmiştir.

11.13.9. F-değerleri

Tipik bir kamera normalize edilmişf-değerleri objektifin çevrilmesi ile değişecek şeklide dizayn edilmiştir.Doğal olarak f değeri seçmek için makine bu pozisyona ayarlanacaktır. Bu yüzden bu ayarlara pozisyonlarına f-değerleri (f-stops) denir.

Bütün f-değerleri bu noktalarda karekök ikinin üsleri şeklindedir.Bu değeri her bir artırmada pozlama için gereken ışık miktarıiki katına çıkacaktır.Aynışekilde iki kere bu sayıyı artırmak gereken ışık miktarını4 katına çıkarır.Üç kere artırmak 8 katına çıkarır gibi....

11.13.10. İğne deliği yazılımıdizayn etmek

Şimdi asıl iğne deliği yazılımımızın ne yapacağınıbelirlemeye hazırız.

11.13.11. Programın girdilerini işlemek

Asıl amacımız delikli bir kameranın yapımına yardımcıolmak olduğundan girdi olarak odak uzaklığınıkullanacağız.Bu yazılım olmadan da belirleyebileceğimiz birşeydi.Tam odak uzaklığıfilmin büyüklüğü ve fotografın standart resim mi , genişaçıresmi mi yoksa telefoto bir resim mi olacağıile belirlenecek. Şimdiye kadar yapmışolduğumuz programların çoğu tek bir karakter üzerinden çalışıyordu : hex programıbytelarıokuyup 16'lısayıdüzeninde yazıyordu , csv programıise karakterin geçmesine yada silinmesine karar veriyordu.

Bir diğer programımız ftuc ise aynıanda iki karakter okumamıza olanak sağlıyordu.

Fakat şimdi yapacağımız pinhole programısadece tek bir karakter üzerinde değil büyük gramatik manalar içeren cümleler üzerinde çalışacağız.

Mesela eğer programımız odak uzaklığı100 mm , 150 mm , 210 mm olan kameraların delik boyutunu hesaplayacak ise girdi şu şekilde olmalıdır :

100, 150, 210

Şimdi programımız sadece tek bir byte tan fazlasını okuyabilmelidir. Eğer okuma işlemi sırasında 1 sayısınıgörür ise ondalık bir sayının ilk basamağınıokuduğunu anlamalıbundan sonra gelen 0 ların ise aynısayının diğer basamaklarıolduğunu bilmeli ve buna göre sayıyıdoğru bir şekilde hafızaya kaydetmelidir.

İlk virgül işareti ile karşılaştığında artık sayının sona erdiğini ve başka basamak eklenmeyeceğini tesbit etmelidir. Sonra okumuşolduğu bu sayıyı100 değerine dönüştürmelidir. Aynışekilde sonra gelen değerlerin de 150 ve 200 ' ü ifade ettiğini tesbit etmelidir.

Girdi içerisinde hangi ayracın kullanılacağınıtesbit etmemiz gerekir : İlla ki girdiler arasında virgül mü olmak zorunda ? . Öyleyse virgülden bir başka karakter geldiğinde ne yapmamız gerekiyor ?

Kişisel olarak ben bunu basit tutmayıtercih ediyorum. Eğer birşey sayıise işle değil ise ihmal et. Çünkü girdim içinde yazmış olduğum çok açık bir şekilde ekstradan olan karakterlerin bilgisayar ekranında bana hata mesajıolarak gelmesini istemiyorum.

Bir de bu yöntemle bilgisayarın monotonluğunu biraz olsun kırabiliriz. Mesela aşağıdaki girdi programımızda sorunsuz çalışacaktır.

150 m uzaklık için acaba deliğin yarıçapıne kadar
          olmalı?

Bilgisayarın bana aşağıdaki hatalarıgöstermesine hiç lüzüm yok.

Syntax error: m Syntax error: uzaklık Syntax error: için Syntax error: acaba

vesaire vesaire ...

İkinci olarak # karakterini satırın sonuna koyduğumda o satırı yorum satırıolarak tanımlamak istiyorum.Bu fazla bir çaba gereketirmeden kodlanabilecek bir şey ve girdi dosyalarımı çalıştırılabilir script olarak değerlendirmeme imkan verir.

Diğer bir yandan girdimizin biriminin ne olmasıgerektiğini tesbit etmeliyiz.Şu anda biz milimetre tercih etmemizin nedeni birçok fotografçının odak uzaklığınıbu şekilde hesaplamasıdır

Son olarak sayıların virgülden sonraki kısmının olup olmayacağınıbelirlemeliyiz(Eğer böyle bir durum varsa virgülü de kontrol etmeliyiz.).

Bizim programımızda bunu yoksaydık.Çünkü 51mm odak ile 50 mm odak arasında bile fark edilemeyecek derecede küçük fark var iken 50.5 i de eklemek iyi bir fikir olmasa gerek. Bu bizim tercihimiz eğer farklıbir şekilde programlamak isterseniz tercihlerinizde özgürsünüz.

11.13.12. Seçenekler sunmak

Bir pinhole kamera yaparken bilmemiz gereken en önemli şey deliğin çapıdır.Net görüntüler almak istediğimizden bu deliğin boyutunu belirlemek için yukarıdaki formülü kullanacağız. Birkeç uzmandan farklıPC değerleri aldığımızdan bunlarıseçenek olarak sunabiliriz.

Geleneksel UNIX programlama sisteminde iki şekilde seçenek sunmak mümkündür artıbir de herhangi bir seçim yapılmadığında geçerli olan varsayılan değer vardır.

Birinci yöntem daimi seçenektir ve program her açıldığında bize sormadan otamatik olarak ayarlanır.

Daimi seçenek bir konfigrasyon dosyasında saklanabilir.Genel olarak bu dosyalar kullanıcıana dizininde bulunur.Bu dosyalar nokta ile başlayıp programın ismi ile devam eder. Bazen sonunda rc eki de gelebilir. Bizim programımız için bu dosya ismi ~/.pinhole yada ~/.pinholerc olabilir.

Konfigrasyon dosyasıçok sayıda değiştirilebilir parametreler içeren programların hemen hemen hepsinde bulunur.Ama programımız az sayıda parametre içeriyor ise diğer bir metod tercih edilir : çevre değişkenleri. Bizim yapacağımız programda bu çevre değişkenin ismi PINHOLE olacak.

Programlar yukarıda anlatılan yöntemlerden sadece bir tanesini tercih eder çünkü diğer durumda konfigrasyon dosyasıbir şey dese çevre değişkeni daha farklıbir değere sahip olsa program karışacaktır.

Bu yüzden biz sadece çevre değişkeni kullanmayıtercih ettik ve kullanacağımız çevre değişkenini PINHOLE olarak adlandırdık.

Diğer yöntem plansız bir tercih yapmaya olanak verir : " Genelde 0.039 kullanmayıtercih ediyorum ama bu sefer 0.03872 kullanacağım." Bir diğer deyişle sürekli seçimlerin üzerine yazar.

Bu tip seçimler genellikle komut satırından alınan ayarlar ile yapılır.

Son olarak program bir de varsayılan değere ihtiyaç duyacak. Kullanıcının hiçbir tercih yapmadığıdurumda bu değer geçerli olacak.Mesela kullanıcıneyi seçmesi gerektiğini bilmiyordur.Tercihen bu varsayılan değerimiz kullanıcılar tarafından en çok tercih edilen değer olmalıdır.Böylece en az sayıda kullanıcı bu değeri değiştirmek zorunda kalacaktır.

Bu şekilde açıklama karışık oluyor ise aşağıda maddeler halinde açıklayalım:

  1. Eğer program komut satırıile gönderilmişbir değer algılarsa bunu kabul etsin ve varsayılan değer yada çevre değişkenini görmezden gelsin.

  2. Diğer durumda eğer çevredeğişkeni var ise onu kabul etsin var sayılan değeri ihmal etsin.

  3. En son durumda eğer belirtilmişhiç bir şey yok ise varsayılan değeri kullansın.

Ayrıca PC seçenekleri nasıl alacağımızın biçimini de belirlemeliyiz.

İlk bakışta PINHOLE=0.04 çevre değişkeni için -p0.04 komut satırıargümanıolarak güzel görünüyor.

Ama gerçekte böyle bir şeye izin vermek güvenlik açısından riskli.Çünkü PC değerleri çok küçük değerler eğer birisi çokbüyük değerler için programıçalıştırırsa program göçebilir.

Yada programımıza biraz daha zaman ayırıp programın büyük sayılar ile de girdi alabilmesini sağlarız.Bunu ancak bilgisayar kullanmayıbilmeyen kullanıcılara yönelik yapılmışticari yazılım için uygulayabiliriz.

Yada müşteri her zaman haklıdır der programıolduğu gibi bırakırız.

Yada büyük sayıların girilmesini imkansız hale getirebiliriz. Tek yapmamız gereken 0. ön ekini kaldırmak.

Yani kullanıcı0.04 ayarlamak istiyor ise komut satırıile -p04 yazacak veya çevre değişkenini PINHOLE=04 olarak ayarlayacak.Böyle durumda kullanıcı-p999999 bile yazsa bu sayı 0.999999 olarak değerlendirildiğinden güvenli olacaktır.

Sonra kullanıcıBender sabitini mi yoksa Connors sabitini mi seçeceğini daha kolay belirtebilsin diye -b komut satırında -p04 olarak -c ise -p037 olarak algılanacak şekilde düzenleme yapacağız.

11.13.13. Çıktı

Şimdi programımızın hangi şekilde nasıl bir çıktıvereceğine karar verelim.

Girdimizde sayısıbelli olmayan odak uzaklığıdeğerleri olacağından geleneksel veri tabanıstilinde bir sonuç vermek mantıklıolacaktır.Bu stilde çıktıvermemiz için yapmamız gereken her bir sonuç farklısatırda olacak biçimde ve satırın içinde değerler arasıtab karakteri bulunacak biçimde yazdırmaktır.

Ekstradan bir seçenek olarak kullanıcıisterse çıktıyıCSV formatında gösterelim. Bu durumda her bir alanda hangi değişkenlerin var olduğunu belirten bir satır yazdırmalıyız ve tab karakterlerini virgül ile değiştirmeliyiz.

CSV formatıiçin komut satırından seçenek almamız gerekecek.Connor sabiti için kullanmışolduğumuz -c seçeneğini kullanamayız. Bazıgarip nedenlerden dolayıçoğu web sitesinin cvs dosyasınıMicrosoft Excel Spreadshet olarak kabul etmesinden dolayı (CSV Excel'den daha önce çıkmışolduğu halde)biz bunu -e ile ifade edelim.

Her bir satırda ilk olarak odak uzaklığınıyazdıralım. Bu biraz tekrar yapıyormuşuz gibi görüncek , özellikle interaktif modda iken kullanıcıodak uzaklığınıgirecek çıktıolarak yine odak uzaklığıgörünecek.

Ama kullanıcıbelki de bir satıra birden fazla odak uzaklığı yazabileceği için bu şekilde kullanmak daha mantıklıolacaktır. Ayrıca girdiler başka bir programdan gelebileceği için kullanıcının hiçbir girdi görmemesi ihtimali de var.Benzer şekilde çıktının da başka bir programa , dosyaya yada yazıcıgibi çevre birimlerine gitme ihtimali de var.

O yüzden her satırda girilen odak uzaklığınıyazdırmak çok daha mantıklıolacaktır.

Peki kullanıcıböyle bir girdi verirse :

00000000150

Açıkca tek yapmamız gereken baştaki 0 larısilmektir.

Kullanıcıların girdiği değerleri ikili sayıdüzeninde FPU ya kaydetmeyi ve buradan yazdırmayıdüşünüyorsanız kullanıcıaşağıdaki gibi bir girdi verdiğinde ne yapmalıyız :

17459765723452353453534535353530530534563507309676764423

Gördüğünüz gibi bizim FPU değişkenimiz en fazla 18 basamak sayıalabilicekti oysa burada kullanıcı18 den çok fazla basamak girdi. Peki bunu nasıl çözeriz ?

Başlangıçtaki 18 basamak sayıyıokuyabiliriz.Sonra bunu FPU içine kaydederiz. Sonra okumaya devam eder ve TOS içinde bulunan değeri 10 ile çarpar sonra bunun üzerine ekleriz.

Evet bunu yapabiliriz.Ama bunu yapmak çok saçma olacaktır.Çünkü böyle bir odak uzaklığına sahip kamera yapmak mümkün olmayacaktır. Zira dünyanın çevresi bile milimetre ile ifade edilecek olsa 11 basamaklıbir sayıoluyor.

Kullanıcıister oyun oynamak ister programıdenemek ister sistemi bozmak maksadıile kamera yapmaktan başka her ne maksatla olursa olsun böyle bir şey girdiğinde yapmamız gereken nedir ?

Tabiri caizse yüzüne tokadıpatlatmaktır.

17459765723452353453534535353530530534563507309676764423
          ??? ??? ??? ??? ???

Bunu başarmak için başlangıçtaki 0 değerlerini ihmal ettikten sonra 0 dışında bir sayıgelir ise bir sayaç değişkeni tanımlayıp buna başlangıç değeri olarak 0 veririz. Sonra aşağıdaki adımları uygularız.

  1. Basamağıçıktıya gönder

  2. Bu basamağıönbelleğe ekle ileride FPU içine kaydedeceğimiz zaman kullanacağız.

  3. Sayacı1 arttır.

Bu üç basamak içeren döngümüzde iki durmunu kontrolünü de yapmalıyız.

Ek olarak söylememiz gereken bir şey de sayıolmayan değerleri # karakteri olmadığızaman ihmal ediyoruz. Bu karakter hatırlarsanız yorum yazmak için kullanılıyordu. Çıktıişlemimizi bitirdikten sonra daha fazla girdi bekliyor isek buraya bakmalıyız.

Hala daha bahsetmediğimiz bir ihtimal var . Eğer kullanıcı bütün değerleri 0 olarak girerse bu durumda ekrana basmak için herhangi bir basamak olmayacak .

Bu durumu tesbit etmek için sayacımızın değerine bakarız eğer sayacımız ise ekrana 0 yazıp başka bir tokat da ona atarız.

0 ??? ??? ??? ??? ???

Artık mantıklıolan odak uzaklıklarınıtesbit edip ekrana yazdıktan sonra deliğin boyutunu hesaplayabiliriz.

İğne deliği kelimesinde iğne rastgele seçilmişbir kelime değildir.Ancak bir iğne tarafından açılabilecek kadar küçük bir delik demektir.

Bizim formülümüz milimetrik sonuçlar üretir. Eğer bu sayıyı 1000 ile çarpar isek sonucumuzu mikron cinsinden ifade edebiliriz.

Burada ayrıbir tuzakla karşıkarşıyayız : çok fazla hassasiyet.

Evet FPU yüksek hassasiyetli matematik için uygundur ama biz yüksek hassasiyetli matematikle uğraşmıyoruz biz fizikle uğraşıyoruz.(Özellikle optik)

Diyelim ki bir kamyonu kamera da görüntülemek istiyoruz.Diyelim ki onun kutusu 12m uzunluğunda odak uzaklığı12000 metre olacak eğer biz Bender sabitini kullanırsak 12000 sayısının karekökü ile 0.04 ü çarpar isek 4.381780460 milimetre elde ederiz. Bu da 4381.780460 mikron yapar.

Sonuç gerektiğinden fazla hasas. Bir kere bizim kamyonumuz tam olarak 12000 mm uzunluğunda değildir ki . Böyle hassasiyette uzunluk ölçmek te mümkün değildir. O yüzden bu sayıyıyuvarlamak en mantıklı yöntem olacaktır. 4.381780460 sayısını4.4 ile ifade edersek çok daha güzel olur.

Not: Yukarıda sadece 10 basamak hassasiyetinde kullandık. Bir de 18 basamak kullanır isek ne kadar mantıksız olacağınısiz kıyas edin.

Böylece sayımızda mana ifade eden basamak sayısınıazaltmamız gerekiyor.Bunu yapmanın bir yolu mikron olarak tamsayıdüzeninde gösterimdir. Virgülden sonraki kısımlar ihmal edildiğinden delik büyüklüğü 4382 mikron olacak.Bu sayıya bakarak 4400 mikrona yuvarlama yapabiliriz.

Ayrıca sayının ne kadar büyük olduğuna bakmaksızın sayıyı4 basamağa(Artık ne kadar lazımsa) kadar yuvarlamak istiyoruz.Fakat FPU sonuçta sayılarıikili olarak tutuğundan belli sayıda basamağa yuvarlamaya izin vermiyor.

Böyle olunca bizim yuvarama işlemi için bir algoritma geliştirmemiz gereklidir.

Burada bizim yöntemimiz.(Bu algoritmanın tam verimli bir algoritma değildir daha iyisini yaparsanız bize bildiriniz.)

  1. Sayacı0 dan başlat.

  2. Sayı10000 den büyük yada eşit olduğu sürece sayıyı10 a böl sayacıbir arttır.

  3. Sonucu yazdır.

  4. Sayac 0 dan büyük olduğu sürece 0 yazdır ve sayacı1 azalt.

    Not: 10000 eğer 4 basamağa yuvarlamak istiyorsanız mantıklıolacaktır. Diğer sayılar için 10^x sayısıile kontrol yapmanız gerekecektir. X burada basamak sayısınıifade ediyor.

Bundan sonra deliğin çapınımikron cinsinden 4 basamağa yuvarlanmışşekilde ekrana yazacağız.

Bu noktadan sonra odak uzaklığıve delik çapını biliyoruz.Artık f-sayısınıhesaplamak için yeterli bilgimiz var demektir.

f-sayısınıen son 4 basamağa yuvarlamışolduğumuz sayıiçin hesaplayacağız.. Daha mantıklıolmasıiçin en yakın normalize edilmişf - sayılarınıbulacağız.Yani en yakın karekök ikinin üslerini bulacağız.

Bunu yapmak için eldeki f sayınıkendisi ile çarptığımızda karesini elde edeceriz . Sonra 2 lik logaritma ile 2 üzeri hangi sayıya denk geldiğini hesaplarız.Bu şekilde hesaplamak kök 2 tabanında logaritma almaktan daha kolaydır. Sonra elde ettiğimiz sayıyıen yakın tam sayıya yuvarlamalıyız.Sonra elimizdeki sayıyı ikiniz üzerinde ifade edersek bunun kökünü aldığımızda normalize edilmişen yakın f-sayınınıelde ederiz.FPU da bunun için kısa bir yöntem vardır. fscale komutu sayıyıbüyütebiliriz. Aslında bu sayıyı bir sola kaydırmaktan başka bir işyapmıyor

Buraya kadar anlatılan şeyler biraz kafa yorucu geldi ise belki bunu kod halinde görmeniz anlamanıza daha yardımcıolacaktır. Hepsi sadece 9 komut :

fmul st0, st0 fld1 fld st1 fyl2x frndint fld1 fscale fsqrt fstp st1

İlk satır :

fmul st0, st0 
TOS(yığının en üstü) ta bulunan değerin karesini alır.

fld1
TOS'a bir koyar.

Sonraki satır

fld st1
ise karesi alınmışdeğeri geri TOS a alır.Bu noktada karesi alınmışsayıaynıanda st de ve st(2) dedir.st(1) ise içinde 1 tutar.(İleride neden bu değerin ikinci bir kopyasınıtuttuğumuz daha iyi anlaşılacaktır.)

Sonra

 fyl2x
komutu st nin st(1) ile çarpılmış halinin 2 lik tabanda logaritmasınıhesaplar. Bu da daha önce neden st(1)'e 1 yerleştirdiğimizi açıklıyor sanırım.

Yine bu noktada st sayımızın karesinin logaritmasını tutuyor.st(1) ise daha sonrasıiçin sakladığımız gerçek f-sayısının karesini tutuyor.

frndint
komutu TOS da bulunan değeri en yakın tam sayıya yuvarlar.
 fld1
komutu yine yığınımıza 1 gönderir. Sonra
 fscale
komutu ile TOS da bulunan biri st(1) de bulunan değer adedince sola kaydırırız ki bu da ikinin üzeri st(1) gibi sonuç verecektir.

Son olarak

 fsqrt
komutu sayının karekökünü alarak sonucu hesaplar.Böylece normalize edilmişf-değerlerini elde ederiz.

Artık elimizde TOS da bulunan normalize edilmişf-sayısıst(1) de bulunan iki tabanında logaritmasıalınmışve yuvarlanmışsayıve st(2) de bulunan gerçek f-sayısının karesi vardır.st(2) de bulunan değeri daha sonra kullanmak üzere saklıyoruz.

Ama st(1) artık ihtiyacımız yok.Bu yüzde son satırda kullanılan

 fstp st1
komutu st ' nin içinde bulunan değeri st(1) e aktracaktır.Sonra pop işlemi gerçekleştireceğinden son durumda st(1) de bulunan değer st ye aktarılacak ve st(2) de olan değer ise st(1)'e geçecektir.Bundan sonra st normalize edilmiş f-sayısınıst(1) ise gerçek f-sayısının karesini barındırır.

Şimdi normalize edilmişf -sayısınıçıktıvermeye hazırız.Normalize olmuşolduğundan dolayıbu sayıyı4 basamağa yuvarlamaya ihtiyaç yoktur.Bütün sayıyıekrana yazdırabiliriz.

Normalize edilmişf-sayısıkullanışlıdır çünkü küçük bir sayıdır ve fotometre tarafından bulunabilir.Aksi halde pozlama süresini hesaplamak için daha farklıbir metod uygulamamız gerekecekti.

Önceden vermişolduğumuz formülde değişik f-sayılarıiçin pozlama sürelerinin nasıl hesaplanacağınıgöstermiştik.

Şimdiye kadar görmüşolduğum bütün fotometreler f5.6 için pozlama süresini hesaplayabiliyordu.Bu yüzde bir f5.6 katsayısı tanımlamalıyız. Diğer biri ifade ile f5.6 için gereken pozlama süresini kaç ile çarpmalıyız ki kameramız için gerekli pozlama süresini tesbit edelim.

Yukarıdaki formülden anlıyoruz ki bu katsayılar f-sayısının (gerçek f sayısınormalize olan değil) 5.6 ya bölünmesinden sonra karesinin alınmasıile hesaplanabilir.

Matematiksel olarak iki sayının bir birine bölümünün karesi o sayıların karelerinin bölümüne eşittir.Hem de bu yöntem f-sayımızın karesi alınmışhalde st(1) de bulunduğundan daha mantıklı olacaktır.

Fakat 5.6 bir sabittir.Bu yüzden FPU yu böyle hassas işlemler üzerinde harcamak istemeyiz. Bunun yerine 5.6 nın karesi ne ise direk ona bölmesini söyleyebiliriz.Yada f-sayısınıalır 5.6 ya böler ondan sonra sonucun karesini alırız. İki yöntem de eşit gibi görünüyor ama değil.Yukarıda ifade ettiğimiz üzere 5.6 karekök ikinin 5. kuvveti. Yani bir irrsayonel sayı. Ama bu sayının karesi tam olarak 32 dir. 32 bizim için öyle kullanışlıdır ki bu sayıya bölmek için tek yapmamız gereken sayıyı5 defa sağa kaydırmaktan ibarettir. Çünkü 32 2 nin 5. kuvvetidir. Bunu da

fscale
komutu ile st(1) e -5 ile scale işlemi uygulayabiliriz. Bölmeden çok daha hızlı.

Şimdi neden gerçek f-sayının st(1) de saklandığınıdaha iyi anlamışsınızdır.f5.6 için gereken kat sayılarıbulmak programın en basit hesaplamalarından biri.Bu hesabıyaptıktan sonra 4 basamağa yuvarlama yapabiliriz.

Son olarak kullanışlıbir sayıdaha hesaplamamız gerekiyor : f5.6 dan bizim sayımıza kaç tane f-sayısıolduğu. Eğer bizim f -sayımız fotometrenin kapasitesini geçerse bize yardımcı olacaktır.Çünkü bizim objektifimiz çeşitli hızlarda çalışmaya müsaittir ve bu hızlar için f-sayılarınıkullanır.

Diyelim ki bizim f-sayımız f5.6 dan itibaren 5 tane sayıdaha içeriyor ve fotometre bize 1/1000 sn aralık kullanmamız sonucunu verdi. Bundan sonra yapmamız gereken objektif kapağını1/1000 olarak ayarlamak sonra objektifi 5 kere hareket ettirmek.

Bu hesaplamalar da oldukça kolaydır. Tek yapmamız gereken f5.6 katsayısının 2 tabanında logaritmasınıbulmak sonra onu en yakın sayıya yuvarlamak. Burada 4 basamak şeklinde yuvarlamak konusunda endişe etmeye hiç gerek yoktur. Çünkü sonuç büyük ihtimalle ya bir yada iki basamaklıbir sayıolacaktır.

11.13.14. FPU iyileştirmeleri

Assembly dilinde FPU kodunu optimize edebiliriz. Bu özellik C dili dahil diğer yüksek seviyeli dillerde mevcut değildir.

Bir C fonksiyonu virgüllü bir sayıhesaplayacağızaman gerekli değişkenleri ve sabitleri FPU yazmaçlarına yükler.Bundan sonra doğru sonucu hesaplamak için ne yapılmasıgerekiyorsa onlarıyapar.İyi bir C derleyicisi bu kısmıgüzel bir şekilde optimize edebilir.

Fonksiyon sonucunu TOS a döner. Bu dönme işlemi sırasında temizlik gerçekleştirilir ve bütün değişkenler ve sabitler FPU dan silinir.

Bunun dışında bir şey yapmasımümkün değildir. Ama biz yukarıda daha sonra kullanmak üzere f-sayımızın karesini yığında tutmayıtercih ettik.

Çünkü biliyorduk ki bu değeri ileride kullanacağız ve yığınımızda bu değeri tutmak için yeterli yer vardı.

Ama C derleyicisi için yığında olan bir değerin çok yakın zamanda kullanılacağınıbilmek mümkün değildir. Elbette C programcısı bunu bilebilir ama programcının değeri saklayabileceği tek değişkeni vardır.Yani FPU sanki C dilinde 64 bit yada 32 bit olan bir float gibi algılanacaktır.

Bu ayrıca demek oluyor ki TOS ta bulunan değer taşınmalısonra geri alınmalıdır.Ne yazık ki FPU işlemleri hafızaya erişim işlemlerinin en yavaşıdır.

Bu yüzden ne zaman Assembly dilinde FPU işlemleri gerçekleştirsek ara değerleri FPU da tutmayıtercih ederiz.

Bu fikri daha da genişletebiliriz.Bizim programımızda bazı sabitler var mesela(Bir tanesi PC adlıbir değer).

Kaç tane delik yarıçapıhesapladığımıza bakılmaksızın hep aynı değişkeni kullanıyoruz. Böyle olunca eğer değerimizi yığının içinde tutarsak programımızıhızlandırabiliriz.

Programın başlarında bu değerleri hesaplamaya çalışıyorduk. Bunun için girdideki her basamak için sayıyı10 a bölmemiz gerekiyordu.

Oysa çarpma işlemi bölmeden çok daha hızlıdır. Böyle olduğu için programımızın başında 1 sayısını10 ile bölüp 0.1 elde ederiz. Sonra bu değeri yığın içinde tutarak her bir basamakta 10 a bölmek yerine 0.1 ile çarparız.

0.1 sayısınıelle girebileceğimiz halde böyle yapmadık. Bunun için bir sebebimiz vardı: burada 0.1 sayısıvirgülden sonra 1 basamak ile ifade edilse de aslında bu sayının ikilik koda çevrildiğinde ne kadar yer kaplayacağınıbilmiyoruz. bu yüzden FPU bunun ikili değerini yüksek hassasiyetle hesaplar.

Bundan başka diğer sabitlerimiz de var. Deliğin çapını hesaplarken 1000 ile çarparak mikrona çeviriyoruz . Sonra yuvarlama sırasında karışlaştırma yaparken 10000 ile kontrol ediyoruz.Sonuçta bu sayılarıda yığında tutmamız gerekecek.Elbette 0.1 sayısınısayıları4 basamağa yuvarlarken tekrar kullanacağız.

Ayrıca ' -5 ' değerini de saklamamız gerekiyor.Yukarıda bahsettiğimiz gibi karesi alınmışf-sayısını32 ye bölmek yerine fscale kullanacaktık. Bu işlemi gerçekleştirmek için bu sayıya ihtyacımız olacak.Bu sayıyıkafamıza göre sonuncu olarak yüklemedik.Bu değeri sonucu olarak yüklediğimiz için yığının en üstünde bulunacak böylece f-sayısıdeğiştirileceği zaman -5 değeri st(1) e geçecektir.Bu da fscale fonksiyonumuzun çalışmasıiçin gerekli bir durumdur.

Böyle belirli değişkenleri hafızadan yüklemektense direk komuttan almak daha verimlidir. Biz de burada -5 için aynışeyi uyguluyoruz.

fld1 ;TOS = 1 fadd st0, st0 ;TOS = 2 fadd st0, st0 ;TOS = 4 fld1 ;TOS = 1 faddp st1, st0 ;TOS = 5 fchs ;TOS = -5

Optimizasyon kuralınışu şekilde özetleyebiliriz : Tekrar kullanılan değerleri yığın içinde tut.

İpucu : PostScript® yığın tabanlıbir programlama dilidir. Postscript ve FPU assembly dili hakkında yazılmışçok sayıda kitap vardır. Eğer kendinizi PostScript konusunda geliştirirseniz bu FPU konusunda gelişmenize katkıda bulunur.

11.13.15. PROGRAMIN KODU

;;;;;;; pinhole.asm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Find various parameters of a pinhole camera construction and use
;
; Started:	 9-Jun-2001
; Updated:	10-Jun-2001
;
; Copyright (c) 2001 G. Adam Stanislav
; All rights reserved.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
%include	'system.inc'
%define	BUFSIZE	2048
section	.data
align 4
ten	dd	10
thousand	dd	1000
tthou	dd	10000
fd.in	dd	stdin
fd.out	dd	stdout
envar	db	'PINHOLE='	; Exactly 8 bytes, or 2 dwords long
pinhole	db	'04,', 		; Bender's constant (0.04)
connors	db	'037', 0Ah	; Connors' constant
usg	db	'Usage: pinhole [-b] [-c] [-e] [-p <value>] [-o <outfile>] [-i <infile>]', 0Ah
usglen	equ	$-usg
iemsg	db	"pinhole: Can't open input file", 0Ah
iemlen	equ	$-iemsg
oemsg	db	"pinhole: Can't create output file", 0Ah
oemlen	equ	$-oemsg
pinmsg	db	"pinhole: The PINHOLE constant must not be 0", 0Ah
pinlen	equ	$-pinmsg
toobig	db	"pinhole: The PINHOLE constant may not exceed 18 decimal places", 0Ah
biglen	equ	$-toobig
huhmsg	db	9, '???'
separ	db	9, '???'
sep2	db	9, '???'
sep3	db	9, '???'
sep4	db	9, '???', 0Ah
huhlen	equ	$-huhmsg
header	db	'focal length in millimeters,pinhole diameter in microns,'
	db	'F-number,normalized F-number,F-5.6 multiplier,stops '
	db	'from F-5.6', 0Ah
headlen	equ	$-header
section .bss
ibuffer	resb	BUFSIZE
obuffer	resb	BUFSIZE
dbuffer	resb	20		; decimal input buffer
bbuffer	resb	10		; BCD buffer
section	.text
align 4
huh:
	call	write
	push	dword huhlen
	push	dword huhmsg
	push	dword [fd.out]
	sys.write
	add	esp, byte 12
	ret
align 4
perr:
	push	dword pinlen
	push	dword pinmsg
	push	dword stderr
	sys.write
	push	dword 4		; return failure
	sys.exit
align 4
consttoobig:
	push	dword biglen
	push	dword toobig
	push	dword stderr
	sys.write
	push	dword 5		; return failure
	sys.exit
align 4
ierr:
	push	dword iemlen
	push	dword iemsg
	push	dword stderr
	sys.write
	push	dword 1		; return failure
	sys.exit
align 4
oerr:
	push	dword oemlen
	push	dword oemsg
	push	dword stderr
	sys.write
	push	dword 2
	sys.exit
align 4
usage:
	push	dword usglen
	push	dword usg
	push	dword stderr
	sys.write
	push	dword 3
	sys.exit
align 4
global	_start
_start:
	add	esp, byte 8	; discard argc and argv[0]
	sub	esi, esi
.arg:
	pop	ecx
	or	ecx, ecx
	je	near .getenv		; no more arguments
	; ECX contains the pointer to an argument
	cmp	byte [ecx], '-'
	jne	usage
	inc	ecx
	mov	ax, [ecx]
	inc	ecx
.o:
	cmp	al, 'o'
	jne	.i
	; Make sure we are not asked for the output file twice
	cmp	dword [fd.out], stdout
	jne	usage
	; Find the path to output file - it is either at [ECX+1],
	; i.e., -ofile --
	; or in the next argument,
	; i.e., -o file
	or	ah, ah
	jne	.openoutput
	pop	ecx
	jecxz	usage
.openoutput:
	push	dword 420	; file mode (644 octal)
	push	dword 0200h | 0400h | 01h
	; O_CREAT | O_TRUNC | O_WRONLY
	push	ecx
	sys.open
	jc	near oerr
	add	esp, byte 12
	mov	[fd.out], eax
	jmp	short .arg
.i:
	cmp	al, 'i'
	jne	.p
	; Make sure we are not asked twice
	cmp	dword [fd.in], stdin
	jne	near usage
	; Find the path to the input file
	or	ah, ah
	jne	.openinput
	pop	ecx
	or	ecx, ecx
	je near usage
.openinput:
	push	dword 0		; O_RDONLY
	push	ecx
	sys.open
	jc	near ierr		; open failed
	add	esp, byte 8
	mov	[fd.in], eax
	jmp	.arg
.p:
	cmp	al, 'p'
	jne	.c
	or	ah, ah
	jne	.pcheck
	pop	ecx
	or	ecx, ecx
	je	near usage
	mov	ah, [ecx]
.pcheck:
	cmp	ah, '0'
	jl	near usage
	cmp	ah, '9'
	ja	near usage
	mov	esi, ecx
	jmp	.arg
.c:
	cmp	al, 'c'
	jne	.b
	or	ah, ah
	jne	near usage
	mov	esi, connors
	jmp	.arg
.b:
	cmp	al, 'b'
	jne	.e
	or	ah, ah
	jne	near usage
	mov	esi, pinhole
	jmp	.arg
.e:
	cmp	al, 'e'
	jne	near usage
	or	ah, ah
	jne	near usage
	mov	al, ','
	mov	[huhmsg], al
	mov	[separ], al
	mov	[sep2], al
	mov	[sep3], al
	mov	[sep4], al
	jmp	.arg
align 4
.getenv:
	; If ESI = 0, we did not have a -p argument,
	; and need to check the environment for "PINHOLE="
	or	esi, esi
	jne	.init
	sub	ecx, ecx
.nextenv:
	pop	esi
	or	esi, esi
	je	.default	; no PINHOLE envar found
	; check if this envar starts with 'PINHOLE='
	mov	edi, envar
	mov	cl, 2		; 'PINHOLE=' is 2 dwords long
rep	cmpsd
	jne	.nextenv
	; Check if it is followed by a digit
	mov	al, [esi]
	cmp	al, '0'
	jl	.default
	cmp	al, '9'
	jbe	.init
	; fall through
align 4
.default:
	; We got here because we had no -p argument,
	; and did not find the PINHOLE envar.
	mov	esi, pinhole
	; fall through
align 4
.init:
	sub	eax, eax
	sub	ebx, ebx
	sub	ecx, ecx
	sub	edx, edx
	mov	edi, dbuffer+1
	mov	byte [dbuffer], '0'
	; Convert the pinhole constant to real
.constloop:
	lodsb
	cmp	al, '9'
	ja	.setconst
	cmp	al, '0'
	je	.processconst
	jb	.setconst
	inc	dl
.processconst:
	inc	cl
	cmp	cl, 18
	ja	near consttoobig
	stosb
	jmp	short .constloop
align 4
.setconst:
	or	dl, dl
	je	near perr
	finit
	fild	dword [tthou]
	fld1
	fild	dword [ten]
	fdivp	st1, st0
	fild	dword [thousand]
	mov	edi, obuffer
	mov	ebp, ecx
	call	bcdload
.constdiv:
	fmul	st0, st2
	loop	.constdiv
	fld1
	fadd	st0, st0
	fadd	st0, st0
	fld1
	faddp	st1, st0
	fchs
	; If we are creating a CSV file,
	; print header
	cmp	byte [separ], ','
	jne	.bigloop
	push	dword headlen
	push	dword header
	push	dword [fd.out]
	sys.write
.bigloop:
	call	getchar
	jc	near done
	; Skip to the end of the line if you got '#'
	cmp	al, '#'
	jne	.num
	call	skiptoeol
	jmp	short .bigloop
.num:
	; See if you got a number
	cmp	al, '0'
	jl	.bigloop
	cmp	al, '9'
	ja	.bigloop
	; Yes, we have a number
	sub	ebp, ebp
	sub	edx, edx
.number:
	cmp	al, '0'
	je	.number0
	mov	dl, 1
.number0:
	or	dl, dl		; Skip leading 0's
	je	.nextnumber
	push	eax
	call	putchar
	pop	eax
	inc	ebp
	cmp	ebp, 19
	jae	.nextnumber
	mov	[dbuffer+ebp], al
.nextnumber:
	call	getchar
	jc	.work
	cmp	al, '#'
	je	.ungetc
	cmp	al, '0'
	jl	.work
	cmp	al, '9'
	ja	.work
	jmp	short .number
.ungetc:
	dec	esi
	inc	ebx
.work:
	; Now, do all the work
	or	dl, dl
	je	near .work0
	cmp	ebp, 19
	jae	near .toobig
	call	bcdload
	; Calculate pinhole diameter
	fld	st0	; save it
	fsqrt
	fmul	st0, st3
	fld	st0
	fmul	st5
	sub	ebp, ebp
	; Round off to 4 significant digits
.diameter:
	fcom	st0, st7
	fstsw	ax
	sahf
	jb	.printdiameter
	fmul	st0, st6
	inc	ebp
	jmp	short .diameter
.printdiameter:
	call	printnumber	; pinhole diameter
	; Calculate F-number
	fdivp	st1, st0
	fld	st0
	sub	ebp, ebp
.fnumber:
	fcom	st0, st6
	fstsw	ax
	sahf
	jb	.printfnumber
	fmul	st0, st5
	inc	ebp
	jmp	short .fnumber
.printfnumber:
	call	printnumber	; F number
	; Calculate normalized F-number
	fmul	st0, st0
	fld1
	fld	st1
	fyl2x
	frndint
	fld1
	fscale
	fsqrt
	fstp	st1
	sub	ebp, ebp
	call	printnumber
	; Calculate time multiplier from F-5.6
	fscale
	fld	st0
	; Round off to 4 significant digits
.fmul:
	fcom	st0, st6
	fstsw	ax
	sahf
	jb	.printfmul
	inc	ebp
	fmul	st0, st5
	jmp	short .fmul
.printfmul:
	call	printnumber	; F multiplier
	; Calculate F-stops from 5.6
	fld1
	fxch	st1
	fyl2x
	sub	ebp, ebp
	call	printnumber
	mov	al, 0Ah
	call	putchar
	jmp	.bigloop
.work0:
	mov	al, '0'
	call	putchar
align 4
.toobig:
	call	huh
	jmp	.bigloop
align 4
done:
	call	write		; flush output buffer
	; close files
	push	dword [fd.in]
	sys.close
	push	dword [fd.out]
	sys.close
	finit
	; return success
	push	dword 0
	sys.exit
align 4
skiptoeol:
	; Keep reading until you come to cr, lf, or eof
	call	getchar
	jc	done
	cmp	al, 0Ah
	jne	.cr
	ret
.cr:
	cmp	al, 0Dh
	jne	skiptoeol
	ret
align 4
getchar:
	or	ebx, ebx
	jne	.fetch
	call	read
.fetch:
	lodsb
	dec	ebx
	clc
	ret
read:
	jecxz	.read
	call	write
.read:
	push	dword BUFSIZE
	mov	esi, ibuffer
	push	esi
	push	dword [fd.in]
	sys.read
	add	esp, byte 12
	mov	ebx, eax
	or	eax, eax
	je	.empty
	sub	eax, eax
	ret
align 4
.empty:
	add	esp, byte 4
	stc
	ret
align 4
putchar:
	stosb
	inc	ecx
	cmp	ecx, BUFSIZE
	je	write
	ret
align 4
write:
	jecxz	.ret	; nothing to write
	sub	edi, ecx	; start of buffer
	push	ecx
	push	edi
	push	dword [fd.out]
	sys.write
	add	esp, byte 12
	sub	eax, eax
	sub	ecx, ecx	; buffer is empty now
.ret:
	ret
align 4
bcdload:
	; EBP contains the number of chars in dbuffer
	push	ecx
	push	esi
	push	edi
	lea	ecx, [ebp+1]
	lea	esi, [dbuffer+ebp-1]
	shr	ecx, 1
	std
	mov	edi, bbuffer
	sub	eax, eax
	mov	[edi], eax
	mov	[edi+4], eax
	mov	[edi+2], ax
.loop:
	lodsw
	sub	ax, 3030h
	shl	al, 4
	or	al, ah
	mov	[edi], al
	inc	edi
	loop	.loop
	fbld	[bbuffer]
	cld
	pop	edi
	pop	esi
	pop	ecx
	sub	eax, eax
	ret
align 4
printnumber:
	push	ebp
	mov	al, [separ]
	call	putchar
	; Print the integer at the TOS
	mov	ebp, bbuffer+9
	fbstp	[bbuffer]
	; Check the sign
	mov	al, [ebp]
	dec	ebp
	or	al, al
	jns	.leading
	; We got a negative number (should never happen)
	mov	al, '-'
	call	putchar
.leading:
	; Skip leading zeros
	mov	al, [ebp]
	dec	ebp
	or	al, al
	jne	.first
	cmp	ebp, bbuffer
	jae	.leading
	; We are here because the result was 0.
	; Print '0' and return
	mov	al, '0'
	jmp	putchar
.first:
	; We have found the first non-zero.
	; But it is still packed
	test	al, 0F0h
	jz	.second
	push	eax
	shr	al, 4
	add	al, '0'
	call	putchar
	pop	eax
	and	al, 0Fh
.second:
	add	al, '0'
	call	putchar
.next:
	cmp	ebp, bbuffer
	jb	.done
	mov	al, [ebp]
	push	eax
	shr	al, 4
	add	al, '0'
	call	putchar
	pop	eax
	and	al, 0Fh
	add	al, '0'
	call	putchar
	dec	ebp
	jmp	short .next
.done:
	pop	ebp
	or	ebp, ebp
	je	.ret
.zeros:
	mov	al, '0'
	call	putchar
	dec	ebp
	jne	.zeros
.ret:
	ret

Bu program daha önce yazmışolduğumuz filtrelerle aynıbiçimde ama küçük bir fark var :

Burada artık girişin bitmesi her şeyin bitmesi manasına gelmiyor ki biz diğer karakter tabanlıfiltrelerimizde böyle yapmıştık. Bu filtre ise karakterleri değil bir dili işliyor (sayılardan oluşan oldukça basit bir dil olsa bile ). Eğer girdi kalmamışise iki manaya gelebilir :

Bundan dolayıbizim

 getchar
ve
 read
rutinlerimizi bir
 carry
bayrağıile dönecek şekilde değiştirdik.Eğer yeni bir karakter okuyor isek bu bayrağı0 yapıyoruz eğer herhangi bir girdi kalmadıise bu bayrağı1 yapıyoruz.Elbette assembly dili bunu gerçekleştirecek sihirli özelliklere sahip.
        getchar
'ıdaha detaylıincelerseniz her zaman
        carry
bayrağına 0 döndüğünü göreceksiniz.

Programımız bundan sonra çıkmak için bu

 carry
bayrağına güvenir.Gerçekten de işe yaracaktır. Burada sihirli olan yer
 read
rutinidir. Sistemden yeni girdiler aldığında sadece
 getchar
'ıçağırır.
 getchar
ise girdiden bir karakter okur ve carry bayrağınısıfırlar.

Ama

read
sistemden artık girdi alamaz ise
getchar
rutinine dönmez böylece carry bayrağı1 olarak ayarlanır.

Peki nereye döner ? Program ne zaman call komutunu kullansa mikroişlemci dönüşadresini yığına kaydeder (FPU yığınından değil sistem yığınından bahsediyoruz.). Program ret komutunu kullandığında ise bu yığının en üstünde olan değer çıkarılır ve bu adrese atlama yapılır.

Ama biz ESP yazmacına 4 eklediğimizden (Bu yazmaç yığının adresini tutar).Artık bundan sonra mikroişlemcinin

 read
komutunu çağıran
 getchar
rutininin adresini hatırlaması mümkün değildir.
getchar
 read
komutunu çağırmadan önce yığına bir şey kaydetmediğinden yığının en başında artık
 getchar
' ıçağırmışolan adres bulunacaktır.

Bundan başka bcdload rutini Big-endian ile Little-Endian problemini çözer .

Bu rutin yazıhalindeki sayıyıgerçek sayıya dönüştürür.Yazı big-endian sırasına göre dizilmiştir ama paket ondalık sayıise little-endian dır.

Bu problemi çözmek için std komutunu kullandık. Sonra cld ile işlemi iptal ettik. Burada std aktifken çalışmasıdirection bayrağına bağlıherhangi bir çağrıyapmamak çok önemlidir.

Bunun dışında kalan her şey önceden anlatıldığıüzere çok açıktır.

Bir programcıatasözünün dediği gibi "Programlama bolca düşünce ve biraz da kodlamadır.". Başta bütün küçük ayrıntılarıdüşündükten sonra program artık nerdeyse kendisini yazar.

11.13.16. Programın kullanımı

Programda sayılar dışında her girişi ihmal ettiğimizden yazı formatında her şeyi girdiye verebiliriz. Bunu yapmamıza gerek yoktu ama yine ne imkan vardır.

Benim fikrime göre yazıformatında girişalmak diğer sınırlı ifadelerle işlem yapmaktan çok daha eğlenceli ve kullanıcı dostudur.

Diyelim ki biz 4x5 inch film için bir kamera tasarlıyoruz. Bu film için standart odak uzaklığı150mm olsun. Biz bu odak uzaklığımızı deliğin yarıçapıyuvarlak bir sayıolacak şekilde ayarlamak istiyoruz.Bilgisayara sadece sayılar vererek sormaktansa istediğiniz bir şekilde soru yöneltebilirsiniz.

% pinhole Bilgisayar, 150 odak uzaklığında deliğin boyu ne olması
        lazım? 150 490 306 362 2930 12 Peki 160 için? 160 506 316 362 3125 12 O zaman şunu 155 yapalım lütfen 155 498 311 362 3027 12 Hışimdi 157 dene o zaman. 157 501 313 362 3066 12 156? 156 500 312 362 3047 12 Çok güzel ! Teşekkür ederim ! ^D

Odak uzaklığımız 150 iken delik çapını490 mikron olarak bulduk yada 0.49 mm eğer odak uzaklığımızı156 olarak alırsak delik yarıçapı yaklaşık 1mm nin yarısıolacaktır.

11.13.17. Betik yazma

# karakterini yorum karakteri olarak seçtik.Böylece Pinhole yazılımınıbir betik dili olarak da algılayabiliriz.

Mesela shell betikleri yazıyorsanız şöyle başlayacaktır :

#! /bin/sh

...yada...

#!/bin/sh

#! sonra boşluk karakterleri konulabilir.

UNIX #! karakterleri ile başlayan bir program çalıştırılmaya çalışıldığında bunu bir betik olarak kabul eder.Bundan sonra komutları ilk satırın sonuna ekleyerek çalıştımayıdener.

Diyelim ki şimdi biz pinhole programımızı/usr/local/bin/dizini altına kurmuşuz.Bu şekilde kullanarak çeşitli odak uzaklıklarına karşılık gelen delik yarıçaplarıiçin betikeler yazabiliriz. Bir örnek olarak 120 lik film için :

#! /usr/local/bin/pinhole -b -i # Find the best pinhole diameter # for the 120 film ### Standart 80 ### Genişaçılı 30, 40, 50, 60, 70 ### Telefoto 100, 120, 140

120 lik film orta boyutlu olduğundan dosyayımedium olarak adlandırır isek :

% chmod 755 medium % ./medium

UNIX bu komutları

% /usr/local/bin/pinhole -b -i ./medium

olarak yorumlayacaktır. Sonuçlar ekrana baslıdığızaman çıktımız :

80 358 224 256 1562 11 30 219 137 128 586 9 40 253 158 181 781 10 50 283 177 181 977 10 60 310 194 181 1172 10 70 335 209 181 1367 10 100 400 250 256 1953 11 120 438 274 256 2344 11 140 473 296 256 2734 11

şeklinde olacaktır. Farklıkomut satırıayarlarıyine aynen geçerli olacaktır.

% ./medium -c

Kodu aşağıdaki şekilde değerlendirilecektir.

% /usr/local/bin/pinhole -b -i ./medium -c

Burada -b ve -c değerleri bir karışıklığa neden oluyor(Benner sabiti mi yoksa Connor sabiti mi kullanılacak ). Programımız sonraki ayarlarıeski ayarların üzerine yazacağından burada Connor sabiti kullanılır. Sonuç olarak :

80 331 242 256 1826 11 30 203 148 128 685 9 40 234 171 181 913 10 50 262 191 181 1141 10 60 287 209 181 1370 10 70 310 226 256 1598 11 100 370 270 256 2283 11 120 405 296 256 2739 11 140 438 320 362 3196 12

Eğer Bender sabiti kullanarak CSV dosyasıkaydetmek istiyorsak şu şekilde kullanabiliriz.

% ./medium -b -e >bender % cat bender focal length in millimeters,pinhole diameter in
        microns,F-number,normalized F-number,F-5.6 multiplier, 80,358,224,256,1562,11 30,219,137,128,586,9 40,253,158,181,781,10 50,283,177,181,977,10 60,310,194,181,1172,10 70,335,209,181,1367,10 100,400,250,256,1953,11 120,438,274,256,2344,11 140,473,296,256,2734,11 %

11.13.18. Uyarılar

MS-DOS yada WINDOWS ortamında yetişmişassembly programcıları kısayol kullanmaya eğimlidirler. Klavyenin -scan programlisting- değerlerini almak yada direk olarak grafik hafızasına yazım işlemi gerçekleştirmek MS-DOS altında hoşgörülmeyen ama yapılmasıgereken olarak değerlendirilen iki şeydir.

Peki sebep ? MS DOS ve PC BIOS bu işlemlerde yavaştırlar.

Belki aynıuygulamalarıUNIX ortamında da devam ettirmek isteyebilirsiniz.Mesela bir keresinde bir web sitesinde popüler bir UNIX klonu üzerinde klavyenin -scan programlisting - değerlerine nasıl ulaşılacağı yazılmıştı.

Bu genel olarak UNIX ortamında çok kötü bir fikirdir. Neden olduğunu açıklayalım.

11.13.19. UNIX Güvenlidir

UNIX güvenli modda çalışır .Sadece çekirdek ve aygıt sürücüleri donanıma direk olarak erişebilir. Belki özel olarak bazıUNIX klonları buna izin verse de gerçek UNIX işletim sistemleri izin vermez.Bir versiyonda mümkün olsa bile sonraki versiyonlarda düzeltilir.

11.13.20. UNIX bir soyutlamadır.

UNIX benzeri işletim sistemleri izin verse de UNIX ' in donanıma direk olarak ulaşmamıza izin vermemesinin bir başka nedeni de :

UNIX in bir soyutlama olmasıdır.

MS-DOS ve UNIX 'in tasarımındaki felsefeler tamamen farklıdır. MS-DOS tek kullanıcılıbir sistem olarak tasarlanmıştır. MS-DOS klavyesi ve ekranıdirek olarak bağlanmışbilgisayarlar üzerinde çalışır.Kullanıcının vereceği girdi büyük ihtimalle klavyeden olur . Yine en sonunda çıktıise ekrana gider.

Oysa UNIX te bu hiç bir zaman garanti değildir. Mesela UNIX altında kullanılan pipe(boru) redirection (yönlendirme) işlemleri oldukça yaygındır.

% program1 | program2 | program3 >file1

Eğer program2 yi yazmışsanız bilirsiniz ki program2 girdiyi klavyeden almıyor . Girdi program1'den gelirken çıktıise program3'e gönderiliyor. Burada berzer şekilde program3 ün çıktısıekrana değil file1 adlıdosyaya yazılıyor.

Dahasıda var! Bir şekilde girdilerin klavyeden geldiğini ve çıktıların ekrana gittiğini garanti eden bir terminale bağlansanız bile bu terminalin PC olduğunu garantileyemezsiniz.Belki de sizin beklediğiniz yerde grafik hafızasıolmayabilir. Yada üretmişolduğu klavye kodlarıPC tarzında olmayabilir belki Macintosh yada başka bir bilgisayar olabilir.

Belki kafanızısallayarak benim assembly kodum PC için tasarlanmıştınasıl Macintosh için çalışsın diyorsunuzdur. Ama biz programınızın Macintosh üzerinden çalışmasınıdeğil terminalin Macintosh üzerinde olmasınıkasdediyoruz.

UNIX sistemlerinde terminal direk olarak bilgisayar üzerinde bulunmak zorunda değildir. Bu terminal başka kıtadan hatta başka gezegenden bile olabilir. Telnet kullanan Kuzey Amerikadaki bir Macintosh kullanıcısının Avusturalyada bulunan bir UNIX makinaya terminal açmasımümkündür. Eğer bu sırada siz klavyeden -scan programlisting- okursanız yanlışsonuç alırsınız.

Bu anlattığımız şey diğer donanımlar için de geçerlidir : direk olarak erişiminiz olmayan bir dosyayıokumak , uydu ile bağlanmışuzay mekiğinin kamerasından resim almak gibi.

Bu yüzden UNIX'te verinin nereden gelip nereye gittiği konusunda hiç bir varsayım yapamayız.Sonuç olarak donanıma ulaşımısisteme bırakmak en mantıklısıolacaktır.

Not: Bu uyarılar kesin kurallar değillerdir. Bazıistisnalar mevcuttur. Mesela bir yazıeditörü yerel makinede çalışacağından performansıarttırmak adına direk olarak klavyeden -scan programlisting- değerlerini alabilir. Burada size ne yapmanız gerektiğini yada ne yapmamanız gerektiğini söylemiyoruz. Sadece dikkatsizlik sonucu oluşabilecek hatalar konusunda uyramak istiyoruz.Elbette yaratıcıinsanlar her zaman kurallarıçiğnerler eğer yaptığınız işi neden yaptığınızıbiliyorsanız hiç bir problem olmayacaktır.