17 Ekim 2012 Çarşamba

CCNA


Merhaba,
Diğer yazılara göre çok kısa bir yazı olacak fakat uzunca bir süredir çok sıkı çalıştığımdan elle tutulur bir şey yazamıyordum. Dün sonunda sınavı geçerek CCNA oldum.

Sevgilerimle,

5 Eylül 2012 Çarşamba

OSI TCP/IP

Normalde bu yazıda TCP/IP'nin katmanlı yapısından bahsetmeyecektim. Çok daha ilgisiz birşeylerden bahsedecektim ki işler her bakımdan tam anlamıyla sarpasardı. Kafamda olan PCB yapımıyla ilgili birşeylerden bahsetmekti ancak bunu anlatmak için kullanacağım devre doğru düzgün çalışmadı; diğer taraftan aynı devreyi kendim SMT elemanlarla kurup SMT lehimlemeye giriş yapacaktım hatta bir zaman sonra belki buradan kendim de anlatabilirdim. Tabi devre çalışmayınca olmadı, devreyi düzeltmek için simülatörde yada Protoboard'da zaman da ayıramadım. Diğer taraftan da ağ işlerine bulaşmak zorunda kaldım. Dolayısıyla hazır kafamdaki bilgiler tazeyken bununla ilgili yazacağım. Bu kısım aslında düşük seviye olduğu kadar yüksek seviye programlama ve API'leri de içeren bir bölüm olacak. Hadi bakalım.

TCP/IP yada şu an adı bile kalmamış IPX/SPX gibi hangi protokolü kullanıyor olursanız olun yaptığınız işler en yüksek seviyede kullanıcı etkileşiminden en düşük seviye bit kodlamasına ve ortama yayımlanmasına kadar bir çok ara işlem geçirir. Yani Facebook'ta birine "merhaba" şeklinde mesaj yazdıktan sonra o "merhaba" karakter katarının doğrudan bakır kablo üzerinden ASCII kodlanmış şekliyle gitmesini kimse beklemesin. Kullanıcının gönderdiği ve aldığı veriler birden çok defa işlemden geçirilir ve her işlem adımına katman denir. Daha önce duymuş olanlar için OSI katmanları yada TCP/IP katmanları aslında işlem adımlarını sanallaştıran katmanlar. Ben IPX/SPX yada NetBEUI gibi kullanımdan kalkmış protokoller yerine daha kolay kafada canlandırılması açısından TCP/IP üzerinden örnekler vereceğim.

Klavyeden girilen "merhaba" karakterinin bakır kabloya ulaşmasına kadar bir çok aşama olduğuna değindim. Eski (hatta ilk) TCP/IP yapısına göre bu katmanlar dört tane. Hatta daha heyecan verici olan aslında bu dört katmanlı modele "DoD model" yani "Department of Defense Model" deniyor. Savunma Bakanlığı modeli. Internetin ilk geliştirildiği zamanlarda adının ARPANET ve geliştiricisinin de DARPA olduğunu hatırlatayım. Açılımları Advanced Research Projects Agency Network ve Defense Advanced Research Projects Agency. Bilenler zaten biliyor ama kısaca; ABD savunma bakanlığı bir haberleşme düğümü çökse dahi veri iletimine devam edebilecek bir sistem kurma işini DARPA'ya veriyor. DARPA, Internet'in atası ARPANET'i geliştiriyor ve sistem sonradan zamanın büyük bilgisayar şirketleri (Hey gidi Digital Equipment) ve üniversitelerin kullanımına açılıp sivilleştiriliyor. DARPA'nın ilk zamanlarda yaptığı dört katmanlı dökümantasyon bu nedenle DoD model olarak biliniyor.

Nedir bu dört katman:
  1. Physical
  2. Internet
  3. Transport
  4. Application
Bunların ne işe yaradıklarını OSI katmanlarına değindikten sonra anlatacağım. OSI zamanla bu dört katmanı yetersiz görüyor ki dördüncü katmanı üç alt katmana ve birinci katmanı da iki ayrı katmana daha ayırıyor. Adlar biraz değişiyor:
  1. Physical
  2. Data Link
  3. Network
  4. Transport
  5. Session
  6. Presentation
  7. Application
Baş harflerini terstern sıralarsak "All Programmers Seems To Need Data Processing". (Bu arada Data Link katmanı da kendi içinde 2 alt katmana ayrılıyor ancak orjinal OSI dökümantasyonu yedi katman olduğundan böyle bırakılmış.)

Kullanıcı verisi en alt katmanda başlıyor. Her katmanda bir üst katmana geçerken belli veriler ekleniyor. Application'a daha yakın olan katmanları kullanıcı programları üstlenirken, orta katmanlarda işleri işletim sistemi devralıyor ve physical'a yaklaştıkça işletim sistemi de bayrağı donanıma devrediyor. Her bir katmandan diğerine geçişe paketlenme (encapsulation) yada direk türkçeye yamultulmuş şekliyle enkapsülasyon adı veriliyor.

İlk katmanda kullanıcı verisi olduğu haliyle düşünülebilir yada tarayıcıya gelen HTML kodları, FTP komutları gibi pek az yabancı olunan yapılar ilk katmana ait bilgilerdir. Normal kullanıcı verisi HTML ile kodlanarak biçimlendirilir ve sunucu tarafından kullanıcıya gönderilir. HTML kodlarını doğrudan metin olarak göstermek yada biçimlendirmek tamamen kullanıcının programına kalmış bir seçenektir.

Kullanıcı verisine, Presentation katmanında verinin içeriği ve bu içeriğin nasıl gösterileceğiyle ilgili bilgiler eklenir. Bu bilgiler bir veri başlığı (header) ve gerekirse sona eklenen CRC gibi bir hata denetleme mekanizmasından oluşur. Bu katmanla ilgili en bilindik örnek e-posta içeriklerine ait MIME bilgileridir. Bu MIME bilgisi sayesinde e-postalarda resim yada sıkıştırılmış veri gönderilebilir ve karşı tarafa bunu şu programla aç şeklinde bildirilebilir. Büyük olasılıkla web sayfalarındaki resimler de bu şekilde bir bilgiyle karşı tarafa resim, MIDI vb. içerik; resmin biçimi (.gif, .jpg, .png) ile ilgili bilgiler gidiyordur. (Bu kısımdan tamamen emin değilim.) XML bu katmanda çalışan bir protokol.

Bundan sonra gönderilen veriler için bir oturum açılacaksa Session katmanına iş düşüyor. Bu kısım her zaman gerekli olmasa da bazı durumlarda, örneğin SSL ile şifrelenmiş veri alıp yollarken güvenli bir oturum açılması gerektiğinde kullanılıyor.

Yukarıdaki kısımlar genellikle kullanıcı programlarını ve seçimlik üst seviye işletim sistemi API'lerini kapsıyor. Ancak bundan sonra anlatacağım kısımlar daha önemli kısımlar.

Üç katman sonrasında veriye hala veri adı veriliyor. Başka ne olabilirdi diye sormayın çünkü zamanla verinin adı diğer katmanlarda değişecek. Internetteki port kavramı burada devreye giriyor. Aynı sunucuda bulunan bir web sayfası açıp bir de e-posta'larımı kontrol ettiğimi varsayalım. Bu durumda haberleşeceğim sunucu aynı ancak verilerin karışmaması için farklı haberleşme kanalları açılması gerekiyor. Benzer şekilde web sunucusunu açtım ama sonra aynı sitedeki bir linki farklı bir sekmede açarak incelemeye devam ettim. Gönderilen veri aynı ancak birden fazla kanaldan gönderilmesi gerekiyor yoksa diğer sekmeyle karışır. Bunun için sunucu bana bütün bu verileri farklı portlardan gönderiyor. Ben web sunucusuna 80'inci porttan ne istediğimi bildiryorum. HTTP1.0'in kendi 3 tane komutu var. GET, POST, HEAD. Örneğin bir web sunucusuna telnet programıyla 80. porttan bağlanıp GET yazarsanız sunucu size anasayfayı HTML kodlarıyla gönderir. Telnet programı HTML kodlarını yorumlayamadığından olduğu gibi gösterir. "GET deneme.html" yazarsanız bu sefer de deneme.html dosyasının kodlarını gösterecektir. Tabi sunucu tek bir porttan çalışır (HTTP için 80) ama istemci bunu farklı portlardan kabul eder. Yani web sunucusu 80. portundan anasayfayı istemcinin 12930. portuna yollarken, deneme.html dosyasının kodlarını yine 80. portundan ancak bu sefer istemcinin herhangi başka (mesela 22844 olsun) portuna yollar. Web tarayıcısı farklı portlardan gelen verileri farklı sayfalar olarak yorumlayabilir ve ayrı sekmelerde gösterebilir.

Bu veriye port eklenmesi işini verileri paketleyerek yapar. Şekilde bunu nasıl yaptığı anlatılıyor. Application katmanından gelen kullanıcı verisi Transport katmanına geldiğinde başına bir başlık ekleniyor. Bu başlıkta port bilgileri var. Port bilgileri eklenen veri sonrasında bir alt katmana gidiyor, burada IP başlığı ekleniyor. Her katmanda eklenen bilgileri yeri geldikçe daha ayrıntılı açıklayacağım. Yalnız unutmadan şunu ekleyeyim, verilerin adlarının değiştiğinden bahsetmiştim. Kaynak ve hedef port bilgisi eklenerek paketlenmiş veriye artık Segment adı veriliyor.



Transport katmanın önemli bir özelliğide verinin doğru ulaşıp ulaşmadığını kontrol etmek. Yukarıdaki şekilde UDP anlatılıyor. UDP pek verinin doğru ulaşıp ulaşmadığıyla ilgilenmez. UDP'de veriyi yollarsınız, sonrasında başka veri yollarsınız, böylece sürer gider. Karşı tarafın veriyi alıp almadığıyla pek ilgilenmezsiniz. Ancak UDP'den başka bu katmanda çalışan diğer protokol olan TCP verinin alınıp alınmadığıyla ilgilidir. Bir kere TCP karşı tarafla bir oturum açar. Bu oturum açma işine "Three way handshake" deniyor. TCP başlığında sadece port bilgisi bulunmaz (UDP'de de bulunmaz ancak UDP başlığı her zaman TCP başlığından daha basittir.) bazı bayraklar ve sıra numaraları bulunur. Bu bayraklardan biri SYN diğeri de ACK'dir. Bir TCP oturumu açılacağı zaman istemci sunucuya SYN bayrağı set edilmiş bir paket yollar. Sunucu eğer karşılık verecekse ACK bayrağı set edilmiş bir paket bir de SYN bayrağı set edilmiş bir paket yollar. (Paketler iki ayrı paket olabileceği gibi tek bir paket de olabilir.) Sonra istemci de karşı tarafın SYN isteğini ACK ile onaylar ve komut satırında "netstat -a" yazdığımızda ESTABLISHED olarak görünen bağlantılar oluşur. Her bağlantı işletim sisteminde belli bir miktar kaynak kullanır. Eğer siz bir sunucuya farklı farklı yerlerden bir sürü SYN yollarsanız o zaman sunucunun bir yerden sonra kaynakları sadece SYN'lerden dolayı tükenebilir. Bu SYN FLOOD saldırısı olarak bilinir. Bu nedenle aynı anda açılabilecek en fazla SYN sayısı ve birim zamanda kabul edilecek SYN paketlerinin sayısı sınırlandırılır.

TCP paketinde bu bayraklardan başka sıra numarasını bildiren 32 bitlik bir alan daha vardır. Eğer farklı paketler sunucudan farklı yollar üzerinden gelirse, aslında sonradan gelmesi gereken bir paket önce gelebilir yada önce gelmesi gereken bir paket gecikebilir. Bu durumun önüne geçmek için paketler sıra numaralarıyla gönderilirler. Yine örneğin bir paket 01 sıra numarasıyla, ve hemen ardından paket 4196000 numarasıyla gönderilirse işletim sistemi bu paketi belleğe sığdırıcam diye çok fazla bellek ayırmaya kalkabilir. Ya bellek biter yada segmentation fault yada benzeri bir durum oluşabilir. Bu saldırının adı da Fragments Attack'tir.

Hem TCP hem de UDP'de segmentin doğru alınıp alınmadığıyla ilgili bir CRC bulunur. Hoş UDP ile yanlış segment alınsa yapılabilecek tek şey onu çöpe atmaktır ancak TCP'de bu segment'in yeniden gönderilmesi istenebilir. TCP'de bir de pencere büyüklüğü (Window Size) adında bir parametre bulunur. Bunun değerini çoğunlukla işletim sistemi belirler ancak oturum açılırken ortak bir pencere büyüklüğü kararlaştırılabilir. Yani işletim sisteminin belirlediği değeri karşı taraf beğenmeyebilir. TCP, Window Size ile belirlenen kadar veri alındığında veriyi alan "Tamam sorunsuz geldi sonrakileri de yolla" anlamında bir ACK paketi yollar. Eğer gönderici belirli bir zaman içerisinde ACK paketini almazsa önce gönderdiğini tekrar yollar. Eğer veriler yolda bozulmuşsa yolda bir sıkıntı var demektir ve yada belki sunucuda aşırı trafik olduğundan herkese 60KB yollamaktansa herkese 10KB'lik veriler yollamayı tercih ediyorsa, karşılıklı olarak bu pencere büyüklüğü veri iletişimi sırasında gerçek zamanlı olarak değiştirilebilir.

Port, bayraklar ve pencere büyüklüğü gibi parametreler eklendikten sonra segment network katmanına gelir. Bu katmanda IP adresleri ve bununla ilgili bilgiler eklenir. IP protokolü verilerin doğru iletilip iletilmediğiyle ilgilenmez. IP protokolü verileri gönderilmesi gereken yere yollar ancak verilerin doğru gidip gitmediği, ulaşıp ulaşmadığının kontrolü bir üst katman olan Transport katmanında yapılır. Bu bağlamda katmanlaşma verilerle ilgili işlerin bölüşülmesini kolaylaştırır. Eğer Network katmanı Transport katmanının denetleme yaptığından habersiz kendisi de bir denetleme yapmaya kalksaydı bu durumda performans düşecekti. Transport katmanında bir denetleme, bir de gerekiyorsa Session katmanında bir denetleme yeterli olmaktadır. Segment IP başlığı da eklenerek paketlendiğinde artık adı Paket olur. Yani adını sıkça duyduğumuz IP paketi.

Paket'ten sonraki durak Data Link katmanıdır. Data Link katmanının da ikiye bölündüğünden bahsetmiştim. Biri Logical Link Control (Mantıksal Bağlantı kontrolü - LLC) diğeri de Media Access Control (ortama erişim kontrolü - MAC) adını alır. LLC işletim sisteminin son durağı, MAC ise donanımın başladığı yerdir. Siz ethernet fişini taktığınızda kartın üzerinde yada modemde yanan yeşil ışık bu katmanda gerçekleşen bir "handshake" sonucunda yanar. Bu katmanda artık iş IP'den de çıkmaktadır. Bağlantı kablolu yada kablosuz Ethernet olabilir ancak Ethernet verileri ADSL modem'den PPP ile paketlenerek çıkıyor olabilir. ADSL modem, ADSL erişim merkezine bağlanırken artık Ethernet kurallarıyla değil PPP kurallarıyla konuşur. (ADSL aslında PPP paketlerini Ethernet ile paketleyerek yollar. ADSL modemde PPPoE'yi hatırlayın. Yani PPP over Ethernet) Bu farklı bir ortamdır dolayısıyla Ethernetten farklı protokolleri ve kuralları vardır. Telefonu kullanan modemler de PPP kullanırlar. Eğer modem harici modemse ve Seri porttan bağlanıyorsa araya SLIP protokolü girecektir. Eğer kiralık hat kullanıyorsanız (Leased Line) ATM yada Frame Relay gibi ikinci katman teknolojileri girecektir. Bunların hepsi içinde IP protokolünü paketleyerek iletebilir. Bu durumda örneğin IP paketi SLIP ile paketlenip modeme iletilir, modem SLIP katmanını atar IP paketini alır, PPP ile yeniden paketler ve servis sağlayıcıya yollar.

LLC katmanında donanımsal yada yazılımsal hata kontrolü gerçekleşir. Seri iletişimi kullananlar bileceklerdir, Seri porttan bir veri yollandığı zaman asıl veriye başlama ve durma bitlerinin eklenmesiyle bir yapı elde edilir. 'A' karakteri yollanırken 6 bit de kullanılabilir 8 bit de. Başlama ve durma bitleri de eklendiğinde seri kabloya aktarılan veri 7 bitle 10 bit arasında farklılık gösterebilir. Buna bir Frame yada çerçeve adı verilir. Benzer şekilde IP paketi de ikinci katmana geçtiği zaman artık Frame adını alır. Frame olarak paketlenirken başına Ethernet için MAC adresleri sonuna da CRC eklenir. LLC katmanında gelen verinin CRC'si donanımsal yada yazılımsal olarak kontrol edilir. Doğruysa kabul edilir, değilse dikkate alınmaz. Elbette ki böyle bir durumda Transport katmanı gerekirse olaya müdahale edip hatayı düzeltecektir.

MAC katmanında donanım adreslemesi ve veri yollayacakların ortama ne şekilde erişeceklerinin kontrolü yapılır.

Son fiziksel katmana gelindiğinde artık Frame'ler bitlere dönüşür. Yine ortama salınacak bitler için belli dönüşümler yapılır. Frame'ler içerisindeki bitler olduğu gibi ASCII kodlarıyla salınmaz. Yani osiloskobu bağlayıp gidip gelen verileri kolaylıkla okuyamayız. Bitler dış ortamda ister istemez gürültüden etkilenir. CPU'ya iletilecek 5V sinyalde %1'lik bir düşüş kabul edilebilir ancak dış ortamda yeri geldiğinde sinyal gerilimi gürültüden %30-%40 kadar etkilenebilir. Sinyallerin nasıl modüle edildikleri de önemlidir. En basitinden 1'leri artı gerilim ve 0'ları toprakla gösterirsek Genlik modülasyonu yapmış oluruz. Sıfırları taşıyıcı sinyal üzerinde değişim olmaması, birleri de taşıyıcı sinyalde değişim olmasıyla gösterirsek Frekans modülasyonu yapmış oluruz. Bu kodlamaların her birinde sıkıntı saat sinyallemesinde kayma olursa ortaya çıkar. Yüksek bant genişliği, dolayısıyla yüksek frekans gereken yerlerde ardarda 111 mi yolladık yoksa 1111 mi yolladık tam olarak ayırt edilemeyebilir. Bununla başedebilmek için saat sinyali gönderici tarafından ayrıca yollanmalıdır ki bu da gereksiz kablo kalabalığı yaratır.

Ethernet bunun yerine Manchester Encoding adı verilen, veriyi gönderirken aynı zamanda saat sinyalinin de gönderilebilmesine olanak sağlayan bir teknoloji kullanır. Bu kodlamada sıfırlar (+) sinyalden toprağa yada (-) sinyale geçiş, birlerse (-) sinyalden yada topraktan (+) sinyale geçiş olarak kodlanır. Üst üste üç sıfır yollanacaksa ilk sıfır için sinyalin tutulma zamanında bir gerilim düşmesi olur, sonraki tutulma zamanının tam arasında gerilim yukarı çıkar (bu aynı zamanda saat sinyali yerine de geçer) ve sonraki tutulma zamanında yine gerilim düşer (sıfır). Bir sonraki sıfır için aynı durum tekrarlanır. Bunun wikipedia'daki resim çok daha iyi anlatıyor. Bu kodlamanın dışında Ethernet bir de 4B/5B adı verilen bir kodlamayda dört bitlik veriyi beş bitlik veriye çıkarır. Genelde 11111 yada 00000 gibi veriler kullanılmaz. Bunun yerine 4 bitlik veriler bit geçişleri çok olan örn. 10010, 01010 gibi 5 bitlik verilerle eşleştirilir. Bundan başka bazı 5 bitlik sinyaller komut sinyalleridir.

Yedi katmanı da geçip bitlere dönüşen veri ortama (fiber kablo, bakır kablo, kablosuz ethernet için mikrodalga ışınımı vb.) salınır. Veriyi alan taraf bitleri frame'lere dönüştürür. Frame kendisine gelmişse ve hatasızsa frame'in başlığını atıp paketi işletim sistemine verir. İşletim sistemi paket doğru IP'ye gelmişse IP başlığını sıyırıp Segment'teki port bilgisine göre veriyi kullanıcı programına aktarır. Kullanıcı programı veriyi yorumlayarak olması gerektiği şekilde gösterir.

Son olarak yukarıda "frame kendisine gelmişse" gibi bir ifade kullandım. Normalde Ethernet ortamında özellikle kullanıcılar birbirlerine Hub ile bağlıysalar ortama gönderilen paketler bütün bilgisayarlara ulaşır. MAC adresi kullanıcının MAC adresine eşitse işletim sistemi bunu alır ancak işletim sistemini yerine kendi yazacağımız Ethernet kartı sürücüsü ve bunu kullanan bir kullanıcı programıyla gelen bütün frame'lerin kabul edilerek içeriğinin görüntülenmesi sağlanabilir. Piyasada Sniffer adıyla bu tür programlar bulunmaktadır. (Şu ana kadar gördüğüm en iyi Sniffer Wireshark adında bir program). Ancak Hub'ların zamanla ortadan kalkıp yerini Switch'lerin almasıyla bu programlar da gelen bütün paketleri alamamaktadırlar ancak kablosuz ortamlar için aynı durum geçerli değil. Wireshark kablolu ortamda bile gidip gelen Broadcast paketlerini gösterebiliyor. Diğer yandan amatörce ayarlanmış switchleri de belli saldırı araçlarıyla Hub gibi davrandırabilmek mümkündür. Bunun nasıl yapılacağı bu yazının konusunu aşacağından burada değinmeyeceğim.

8 Ağustos 2012 Çarşamba

Programlamabilir Kesme Denetleyicisi Gerçekten Programlanabilir mi?


Programlanabilir kesme denetleyicisi Türkçe windows'un çevirisinden alınma. Kulağa ileri okuma eniyileştirme gibi bir tını veriyor. İngilizcesi programmable interrupt controller yani PIC. Yazarken asıl mikrodenetleyici olan PIC serisiyle karıştırmamaya özen göstereceğim.

Programlanabilir kesme denetleyicisi, standartları neredeyse milattan öncesinden kalmış gibi gelen IBM PC uyumlu bilgisayarlarda mikroişlemci dışındaki aygıtlardan gelen kesme isteklerine cevap verebilmek için kullanılan 8259A çipine verilen ad. Bugün üretilen hiçbir anakart bu entegreyi kullanmıyor. Bunun iki nedeni var; birincisi zaten anakartların üzerinde1990'lardan beri kesme denetleyicisi için ayrı, DMA için ayrı, klavye denetleyicisi için ayrı ayrı entegreler bulunmuyor. Bunun yerine bunların hepsini kapsayan Southbridge çipseti içerisinde bütün entegrelerin karşılıkları var. İkincisi de 2000'lerde kesme çakışması yada başka bir deyişle IRQ çakışması hepimizi üzen bir sorundu. Eğer anakartınızda çok fazla genişleme kartı takılıysa mutlaka başınıza geliyordu çünkü toplamda 15 tane IRQ, zamanla gelişen bilgisayar teknolojisinin artan IRQ ihtiyacına yetmiyordu. Üstelik bir kaç seneye kadar artık masaüstü bilgisayarlarında bile birden fazla işlemci kullanılması kaçınılmaz görünüyordu ama 8259A ile bunu desteklemenin imkanı yoktu. Dolayısıyla Intel gelişmiş kesme denetleyicisini yani APIC'i çıkardı. APIC, 240 tane IRQ'yu destekleyecek şekilde üretilmişti ve mikroişlemcilerin birlikte çalışırken, cache bellek güncellemesi sırasında diğerinin çalışmasını durdurmasına olanak veren işlemciler arası kesmeyi (inter-processor interrupt -- IPI) destekliyordu. Ben genel olarak PIC'i anlatacağım kadar bildiğim kadarıyla APIC'in farklarından da bahsedeceğim.

Yalnız hepsinden önce birbirine benzeyen üç tane terimin bence açıklığa kavuşması gerekiyor:
  1. IRQ (Interrupt Request -- Kesme İsteği): Mikroişlemcinin dışındaki aygıtlar bir durum değişikliği yaşadıkları zaman "yahu birşeyler oldu, çalışmana bi ara ver de benimle ilgilen, ne olduğunu anla" anlamında mikroişlemciyi dürterler. Buna IRQ denir. En açıklayıcı örnek klavyeden bir tuşa basıldığında klavye denetleyicisi PIC'e bir kesme isteği iletir ki basılan tuş sistem tarafından okunsun, kullanılmayacaksa bile BIOS'un basılan tuşları tuttuğu tampon belleğine aktarılabilsin. Henüz kesme isteği PIC'dedir. PIC bunu mikroişlemciye iletir.
  2. INT (Interrupt -- Kesme): Mikroişlemciye PIC aracılığıyla iletilen kesme isteği eğer mikroişlemci daha önemli bir şeyle meşgul değilse bir kesme oluşturması gerekir. Kesme oluşursa adı gereği yapılan işlem kesilir, hangi kesme çağırılacaksa onun numarasına karşılık gelen kesme vektörü tablodan okunur ve CS:(E)IP'e yüklenir. 
  3. ISR (Interrupt Service Routine -- Kesme Hizmet Programı): Bir kesme gerçekleştiğinde devreye giren kesme koduna ISR denir. CS:(E)IP'ye yüklenen adresin başlangıcındaki koddan IRET (Interrupt Return) opkoduna kadar olan bloktur.

Kesme vektörleri Intel x86 işlemcilerinde gerçek modda (real mode) belleğin en başında bulunurlar. Bunlar aslında uzak göstericilerden (far pointer) başka birşey değildir. Örneğin resimde sıfırıncı kesme (sıfıra bölme kesmesi) gerçekleştiği anda mikroişlemci CS yazmacına (code segment) 00A7h değerini ve IP yazmacına (instruction pointer) 1068h değerini yükleyecektir.

Bu arada bir gereksiz bilgi daha: Aslında sıfırıncı kesme yalnızca sıfıra bölme hatası değil işlem sonucunda bulunan bölümün, bu bölümün depolanması için ayrılmış alana sığmaması sonucu da oluşur. DIV ve IDIV komutları bölen 8 bitlikse AX'deki değeri böler, bölümü AL'ye kalanı da AH'ye depolar; 16 bitlikse DX:AX'deki değeri böler ve bölümü AX'e kalanı da DX'e depolar. Örneğin aşağıdaki kod parçası sıfıra bölme olmamasına rağmen sıfırıncı kesmeyi tetikler:

mov dx, 0FFF
mov ax, dx
mov bx, 0001
idiv bx

Bunu yazmakla 'int 0' kodunu çağırmak yada yukarıdaki resim için 'jmp 00A7:1068' yazmak aynı şeydir. Turbo Pascal'ın CRT.TPU birimi, birim zamanda saniye üzerinden bir döngüye girip bir sayacı arttırıyor sonra da bulduğu değer üzerinden bölme işlemi yapıyordu. Pascal'ın CRT birimini kullanan programlar çok hızlı bilgisayarlarda (333 Mhz falan) çalışmamaya başladılar, sıfıra bölme hatası verip çıkıyorlardı. Çünkü bölmenin sonucu yazmaca sığmıyordu. (Sene 90'ların sonu) Sonradan bir CRT yaması çıktı da düzelmişti.

Bir gereksiz bilgi de, elbette ki yukarıdaki resim Windows XP altında alındığından, orada bir işlemcinin gerçek çalışma modundan bahsedemeyiz. Korumalı modda kesme vektörleri yerine kesme betimleyici tabloda (interrupt descriptor table -- IDT) bu vektörler tutuluyor.

Konudan yeterince uzaklaştıktan sonra tekrar PIC'e dönelim. Normalde IBM/PC için hangi IRQ'nun hangi INT tarafından karşılanacağı kabaca bellidir:
  • IRQ0: Programmable Interval Timer (PIT) Zamanlayıcı kesmesi. İstenirse PIT programlanarak belli zaman aralıklarında kesme üretmesi sağlanabilir. Eskiden anakarta bağlı hoparlörden bunu kullanarak ses çıkarırdık.
  • IRQ1: Klavye denetleyicisi. Daha önce de söyledim, her tuşa basıldığında bu kesme çağırılır.
  • IRQ2: Bu kullanılan bir kesme değildir. Aslında 8259A entegresi 8 tane kesmeye kadar bakabiliyordu ama bir tane daha 8259A buna bağlanarak (slave PIC) 15 tane kesmeye bakması sağlanıyordu. İkincil PIC birinciye bu IRQ2 bacağından bağlandığından gerçek anlamda bir IRQ2 bulunmamaktadır.
  • Geri kalan IRQ'lar seri ve paralel portlar, disk denetleyicisi ve ses kartı tarafından kullanılır. Bunları kullanmadığımdan hakkında pek bilgim yok.
IRQ0 oluştuğunda INT 08h çağırılır, IRQ1 oluştuğunda INT 09h çağırılır. İlk IRQ7'ye kadar bu sırada gider. İkincil PIC'in baktığı IRQ8--IRQ15 arasıysa INT 70h -- INT 77h arasındadır. Örneğin BIOS açılışta zamanlayıcıyı saniyenin 18.2'sinde IRQ0 oluşturacak şekilde programlar. (PIT'in zamanlayıcı taşması (Timer Overflow) bacağı PIC'in IRQ0 bacağına bağlıdır.) Her saniye 18.2 kere INT 08h çağırılır. BIOS aynı zamanda INT 08h'ya ait ISR'yi de belleğe ekler. ISR her çağırıldığında 0000:046Ch adresindeki DWORD'u bir arttırıp INT 1Ch'yi çağırır. DOS altında düzenli olarak çağırılan bir TSR yazarken INT 08h'nın kodunu değiştirmek sistemin kararlılığını etkileyeceğinden pek önerilmez, bunun yerine programın INT 1Ch'ya asılması önerilir.

Yalnız kutsal kitap Ralf Brown Interrupt List'e gözatınca INT 08h'nın aynı zamanda çifte hata (double exception) için de çağırıldığını görürüz. Benzer şekilde INT 09h matematik işlemci hatalarında "exception handler" olarak CPU tarafından INT 00h'ya benzer biçimde çağırılmaktadır. Özellikle IRQ5'i karşılaması gereken INT 0Dh aynı zamanda kullanıcılara çok tanıdık gelen genel koruma hatası (general protection fault -- GPF) ve yine IRQ6'yı karşılaması gereken INT 0Eh de aynı zamanda sayfa hatası (page fault) için çalışmaktadır. Şimdi Windows 95'in verdiği mavi ekranda "bir istisna 0E oluştu" ifadesinin ne anlama geldiği daha açıkça anlaşılabiliyordur sanırım.

Çok kötü birşeyler olmuş (Image credit)


Daha önce hizalama hatasından bahsederken aslında bu kesmenin de BIOS kesmeleriyle çakıştığını söylemiştim. Peki bu nasıl olabiliyor?

Birincisi sonradan eklenen INT 08h ile INT 11h arasındaki mikroişlemci istisnaları dikkat edilirse çoğunlukla korumalı moda ait hatalarda tetikleniyor. Dolayısıyla kodu, gerçek mod kodu olan INT 10h ve INT 11h kesmelerini zaten korumalı mod altında kullanmayacağımızdan yada INT 10h ile INT 11h gerçek modda tetiklenecek bir hata olmadığından gerçek moddayken güvendeyiz. Ama gerçek moddan korumalı moda geçtiğimizde bir IRQ5 gerçekleştiğinde ben gerçekte koruma hatası mı alıyorum yoksa sabit disk bir kesme mi istiyor bunu ayırt edebilmem gerçekten çok zor olacaktır. İşte programlanabilir kesme denetleyicisinin programlanabilir olduğu yer burası. Siz kesme denetleyicisine G/Ç portlarından ulaşarak yeniden programlayabilirsiniz. Kesme denetleyicisinin portları birinci denetleyici için 20h ikinci denetleyici için 0A0h'dır.
Bu portlardan denetleyiciyi programlayan kodu aşağıda veriyorum. Kod benim değil, zamanında Utrecht Üniversitesi'nin korumalı mod e-posta listesinde paylaşılmış basit bir kod:

mov    al, 00010001b ; Input Control Word (ICW) 1
out    20h, al       ; Birinci PIC
out    0A0h, al      ; Ikinci PIC

mov    al, 20h       ; IRQ0'i karsilayacak INT
out    21h, al
mov    al, 28h       ; IRQ8'i karsilayacak INT
out    0A1h, al
 
mov    al, 00000100b ; ICW3
out    21h, al       ; Ikinci PIC hangi bacaga bagli?
mov    al, 2
out    0A0h, al

mov    al, 00000001b ; ICW4
out    21h, al
out    0A1h, al

mov    al, 11111011b ; Output Control Word (OCW)1: bir olan IRQ'lari maskele
out    21h, al
mov    al, 11111111b ; hepsini maskele
out    0A1h, al

Benzeri kodlar http://wiki.osdev.org/8259_PIC adresinde de bulunabilir.

PIC'i programlarken dikkat edilmesi gereken ICW1 20h portuna yazılırken ICW2, ICW3 ve ICW4 komutları ICW1'in yazılması bittikten hemen sonra 21h portuna yazılması gerekiyor.

ICW1'in ilk dört biti 0001 olması gerekiyor. Üçüncü bit PIC'i kenar tetiklemeli (edge-triggered) moda alıyor, ikinci bit kesme vektörlerinin 32 bit olduğunu bildiriyor, birinci bit PIC'in birlikte çalıştığı başka bir PIC daha bulunduğunu bildiriyor. Sıfırıncı bitse ICW4'ün verileceğini bildiriyor.

ICW2, birinci ve ikinci PIC'lerin oluşturacağı IRQ'ları mikroişlemcide hangi kesmenin karşılayacağını bildiriyor.

ICW3, birinci PIC'in hangi bacağına bağlı olduğunu bildiriyor. (IRQ2) İkinci PIC içinse ICW3 bu PIC'e bir numara vermemizi sağlıyor. Bu numara birden fazla PIC ardarda bağlanırsa hepsini numaralandırabilmeye olanak tanıyor. IBM PC uyumlularında yalnızca iki tane olduğundan çok da önemli değil.

ICW4'ün sıfırıncı biti 8086 modunda kesmeler üretmesini sağlıyor.

Kodun son parçasında OCW aracılığıyla birinci PIC'de IRQ2 haricindeki IRQ'lar iptal ediliyor ve ikinci PIC'de de bütün IRQ'lar iptal ediliyor. Diğer bitlerin değerleri ve anlamları için yine Ralf Brown kesme listesine bakılabilir.

Elbette ki bu kod çalışırken herhangi bir kesme gerçekleşse bile dikkate alınmaması gerekiyor bu nedenle bu kodun hemen öncesinde bir CLI komutu çalıştırılmalı ve sonrasında gerekmiyorsa (tabi ISR'ler hazırlandıktan sonra) kesmeler yeniden STI ile geri getirilmelidir.

Kesme denetleyici entegreye bakınca 8 bitlik bir veriyolu bağlantısının olduğu görülür. Bir IRQ gerçekleştiği zaman kesme denetleyicisi mikroişlemcinin INTR (Interrupt Request) bacağını set edip veriyoluna kesmenin numarasını sürer. Bundan sonrasında mikroişlemcinin keyfi olursa o kesmeyi çağırır ve INTA bacağını resetler (INTA active low olması lazım) yada kesme isteğine cevap vermez.

Son olarak sinyallemenin daha rahat anlaşılması için google görsellerde "Cascaded 8259" aratıldığında bağlantının nasıl yapıldığını anlatan güzel görseller bulunabiliyor. Ben, başkasının çizimleri olduğundan buraya eklemek istemedim.

18 Temmuz 2012 Çarşamba

Peki Neden Trap Flag?

Aslında yazının tam başlığı "Peki Neden Trap Flag? veya FLAGS Yazmacı ve Debuggerlara Genel Bir Bakış" olmalıydı. Son birkaç yazıyı donanımla ilgili yazmışken biraz da yazılımla ilgili birşeyler yazmak aklımdaydı. Bu seferki yazı yazılımla ilgili olacak ağırlıklı olarak.

Önce neden trap flag sorusuna cevap vereyim: Çünkü programlamada en çok kullanılan zero flag adı blogspot'ta alınmıştı. Ayrıca bundan 6-7 sene öncesinde kendi debugger'ımı yazmak aklımdaydı. Hatta bence en zaman alıcı kısım olan disassembler rutinleri hemen hemen hazırdı. Haliyle bir debugger'ın en çok kullanması gereken trap flag aynı zamanda blog adı oldu.

http://css.csail.mit.edu/6.858/2012/readings/i386/s02_03.htm
Neden böyle düşündüğümü bilmiyorum ama FLAGS yazmacı bence x86'ların en eğlenceli yazmacı. 80286'larla birlikte Virtual 86 modunu desteklemek için bir kaç bit eklendi ve 32 bitlik 80386'larla FLAGS yazmacı da 32 bite çıkarıldı ve adı EFLAGS oldu. Benim değineceğim daha çok ilk 16 bitlik FLAGS kısmı. Zaten assembly'e elini bulaştıran herkes az çok Zero Flag (ZF) ile yada Carry Flag (CF) ile uğraşmıştır.


  • Carry Flag (CF): İşlem sonucunda bir elde yada bir taşma varsa set edilir. 8086'da iki 32 bitlik sayıyı toplarken önce düşük anlamlı 16 bit ADD ile toplanır, sonraki yüksek anlamlı 16 bit ADC (add with carry) ile toplanır. ADC eğer ilk ADD işlemi carry oluşturduysa sonuca bir daha ekler. Carry aynı zamanda kaydırma (shift) ve döndürme (rotate) işlemlerinde de kullanılır. Bir tamsayı tek mi çift mi anlamak için sayı sağa kaydırılır. Carry flag set edildiyse tektir, edilmediyse çifttir. Set durumunda CY (Carry), reset durumunda NC (No Carry) adını alır. İçeriği doğrudan CLC, STC ve CMC komutlarıyla değiştirilebilen ender flag'lardandır.
  • Parity Flag (PF): İşlem sonucunda sonuçta tek sayıda bit varsa set edilir, çift sayıda bit varsa resetlenir. Yoksa tam tersi miydi? Hatırlayamadım. Pek kullandığım bir bit değil. Çift bit için PE (Parity Even), tek bit için PO (Parity Odd) adını alır.
  • Auxiliary Carry (AF): CF'ın bir nibble'da (4bit) oluşan eldeler için olanı gibi düşünülebilir. Doğrudan kullanılmasa da AAA, DAM gibi komutlar bu bit yardımıyla BCD düzenlemesi yaparlar. Set durumunda AC (Auxiliary Carry), reset durumunda NA (No Auxiliary Carry) adını alır.
  • Zero Flag (ZF): Yapılan işlemin sonucu sıfırsa bu set edilir. Bu aslında doğrudan kullanmaya kalktığımızda çok da bir işe yaramayacağı açık. CMP ve TEST komutları sırasıyla mikroişlemcide sonucun operandları değil sadece FLAGS'i etkilediği bir çıkarma ve VE (AND) işlemi gerçekleştirir. CMP AL, 23h çalıştığında sonucunda FLAGS'ın değeri SUB AL, 23h çalıştırmakla aynı olur ancak AL'nin içeriğine dokunulmaz. Bu da eğer AL'nin değeri 23h ise ZF set edilir. Bundan sonra JZ yada JNZ komutlarıyla AL'nin içeriğine göre dallanma gerçekleştirilebilir. Set durumunda ZR (Zero) reset durumunda NZ (Non-Zero) adını alır.
  • Sign Flag (SF): Yapılan işlemin en yüksek anlamlı biti olan işaret biti bu bitin içeriğine kopyalanır. Çıkarma işleminin sonucunun negatif olup olmadığını kontrol edebileceğiniz gibi CMP işleminden sonra kullanıldığında sayının büyük yada küçük olup olmadığı kontrol edilebilir. JG ve JL komutları bu flag'ın içeriğine bakar. Reset edildiğinde PL (Plus), set edildiğinde NG (Negative) olur. 
  • Trap Flag (TF): Dediğim gibi bir debugger için en önemli flag. Eğer set edilmişse işlemci her bir komutun çalıştırılması biter bitmez Single Step diye bilinen 1 numaralı kesmeyi çağırır. Ekrana bir numaralı kesmenin vektörünü yazan bir program yazıp bunu hem debugger'da hem de komut satırında çalıştırın. Farkı görün. Debugger'lar bu kesmeye asılırlar. Set edildiğinde EI (Enable Interrupt); reset edildiğinde DI (Disable Interrupt) değerindedir. 
  • Interrupt Flag (IF): IRQ kesmelerini kapatır yada açar. Eğer bir kesme vektörünü değiştirmek, kesme denetleyicisini programlamak vb. kritik bir iş yapıyorsanız bunu resetlemeniz tavsiye edilir ki tam siz vektörü değiştirirken kullanıcı klavyeden bir tuşa basıp bir siz daha tam işinizi bitirirememişken bir kesme oluşturmasın. NMI (Non Maskable Interrupt) haricindeki bütün kesmeler iptal olur. Bunu reset etmek için CLI set etmek için de STI komutları bulunur. Resetleyince DI (Disabled Interrupts), set edince EI (Enabled Interrupts) değerini alır.
  • Direction Flag (DF): MOVS, STOS, LODS gibi komutlar çalıştırıldıktan sonra indis yazmaçlarının (SI ve DI) değerlerinin arttırılacağı yada azaltılacağı seçilir. Yani bellek bloğu işlemleri bellekte ileri doğru yada geriye doğru yapılır. Hangisi reset'ti hangisi set'ti unuttum ama bir değeri UP diğer değeri de DN'dir (Down). STD ve CLD komutlarıyla değeri değiştirilebilir.
  • Overflow Flag (OF): Yapılan işlemin sonucunda bir overflow varsa set edilir yoksa reset'lenir. Hakkında tek bildiğim işaretsiz sayılarda yapılan karşılaştırmalarla ilgili olduğuydu. Set edildiği zaman değeri OV (Overflow), reset edildiği zaman ne oluyordu onu da unuttum.

Buradan sonra asıl görevini açıklayacağım trap flag.


- DOS'un debug.exe'si benzeri basit bir debugger nasıl yazılır? 

Öncelikle eğer .exe dosyaları debugger'da açmak istiyorsanız nasıl belleğe yüklendiklerini bilmek gerekiyor, ki bunu ben de çok az biliyorum. .com dosyaları ele alalım. Onların belleğe yüklenmesi kolay. Herhangi bir segmentin 0100h adresine yükleniyorlar. Segmentte 0000h ile 0100h adresleri arasında .com dosyanın içerisinde bulunmayan, DOS'un exec() fonksiyonunun ürettiği bir kısım var, adı program segment prefix (PSP). Bu kısmı ayrıntısıyla hatırlamak zor ancak 0000h adresinde 0CDh ve 20h byte'ları var. Bu çalıştırılabilir bir kod ve assembly karşılığı INT 20h. DOS'un eski usül (CP/M zamanlarından kalma) kodu sonlandırma fonksiyonu. Diğer DOS kodunu sonlandırma yolları AH'nin değeri 00h yada 4Ch iken INT 21h'yı çağırmak. İlk iki yolu DOS'un ilk versiyonu bile destekliyor. Son yol ise DOS2.0 ile birlikte destekleniyor ve DOS'a bir geri dönüş değeri (Errorlevel) bildirebilmek için tek yol. PSP'nin 80h ile 0FFh byte'ları arasında programın çağırıldığı komut yer alıyor. Komut karakterlerinin nasıl tutulduğunu anlamak için örnek resme bakmak yeterli.


PSP'nin CP/M zamanlarında kalma olduğunu söylemiştim. JMP 0000h gibi bir komut .com dosyayı sonlandırmak için yeterli aslında. Program ilk defa belleğe yüklendiğinde aslında stack'da da bazı değerler oluyor. Bunları tam bilmemekle birlikte bunlardan ilki her zaman 0000h değeri. Dolayısıyla RET komutu da .com dosyayı pekala sonlandırıyor. Bunun nedeninden tam olarak emin olmamakla birlikte bir seçenek yine CP/M'e uyumluluk için bırakılmış olabileceği. Diğer uzak bir olasılıksa UNIX uyumlulularında her program aslında çekirdeğin çağırdığı bir fonksiyon olarak ele alınıyor. POSIX standardı bile olabilir. Bu nedenle DOS'u bilmem ama UNIX için C kodu yazarken main()'i mutlaka int olarak tanımlamak gerekiyor. void tanımlamak DOS'da sorun çıkarmazken UNIX'de sorun çıkarabilir. Herneyse eğer program bir fonksiyonsa onu sonlandırmak için RET çağırmaktan daha doğal birşey olamaz.

Bu arada bir program çalıştığında DOS ona dallanmadan önceki son CS:IP değerlerini INT 22h'nin vektörüne yazıyor ancak programı INT 22h'yi çağırarak sonlandırmak, programın çalıştığı bellek bloklarının işletim sistemine geri verilememesine açtığı dosya ve sistemden aldığı diğer kaynakların açık kalarak sisteme iade edilememesine neden olacaktır. Eğer exec()'i işletim sistemi değil başka bir program çağırmışsa o zaman kesme vektörü o programın CS:IP'sini gösterecektir. INT 23h CTRL+C yada CTRL+Break tuş kombinasyonu basıldığında aktif hale gelir. Bir tuşa basılır basılmaz klavye IRQ'sunun kesme hizmet programı basılan tuşu (yada ASCII kodunu) klavye belleğine (keyboard buffer) kopyalar ve çalışmayı o anda ödünç aldığı (kestiği) programa geri döner. DOS kesme hizmet programını değiştirip geliştirebilir. Komut satırındayken CTRL+C için eğer basılan tuş buysa klavye belleğine kopyalamayıp yeni bir komut satırı açar yada o anda bir program çalışıyorsa o programı sonlandırabilir. Bir program da yazılma amacına göre bu kesmeyi değiştirip (hatta yeri geldiğinde klavye kesme hizmet programını da değiştirebilir) CTRL+C'ye basıldığında başka birşeylerin yapılmasını sağlayabilmelidir. Tam ayrıntısını bilmemekle birlikte bir program çalışırken CTRL+C'ye basıldığında DOS, kesme hizmet programından sonra INT23h'ü çağırır. (Sanırım geri dönüş değerlerinin DOS çekirdeği mi yoksa ayrı bir program mı olduğunu kontrol ediyor.) Eğer program INT23h'ü değiştirmişse kendi istediklerini yapabilir. Son olarak herhangi önemli bir DOS hatası oluşursa yine CTRL+C'de olana benzer biçimde INT24h çağrısı yapılır. Bütün bu kesmelerin dökümantasyonu dünyaca ünlü Ralf Brown's Interrupt List'de bulunabilir. Ben bile bunları yazarken arada bu listeye bakıyorum.

Debugger'da .com dosya için uyumluluk için bile olsa uygun PSP oluşturmak gerekli. Bu INT 21h'in büyük olasılıkla AH=26h altfonksiyonunu (yada belki AH=4Bh ama bence 26h'dır kesin) çağırarak halledilebilir. Kodu belleğe yükledikten sonra kullanıcıdan debug.exe gibi komutlarını alıyoruz. Eğer g (Go) vermişse direk çalıştırıyoruz yani kodu yüklediğimiz segmentin 0100h offsetine dallanıyoruz. Eğer p (Proceed yani Step/Trace Over) yada t (Trace yani Step/Trace Into) çağırıldıysa işte o zaman trap flag'i set edip kodun son kaldığı yere dallanmak gerekiyor. Bu durumda program çalışırken bütün yazmaçların değerlerini yedeklemeli ve komut verildiğinde dallanmadan önce bu değerleri geri yüklemeli. Bu arada Intel'in Pentium'lara eklediği LOADALL adında dökümante etmediği bir komutu vardı sanırım, POPA'ya benzeyen. Trap flag en son set edilmeli yada trap flag'i set eden bir komut çalıştırılacak kodun başına eklenip o kodun uzunluğu kadar geriye de dallanılabilir. Tabi uygun bir INT 01h yordamı da yazıp vektörünü girmek gerekiyor. Bütün işleri yapan tek bir INT 01h kullanılacaksa program başlar başlamaz set edilebilir ama t için ayrı p için ayrı INT 01h yazmak bana daha tercih edilebilir geliyor. debug.exe'de INT 01h'in hizmet programı, komutun çalışması sonlanınca yazmaçların değerlerini ve sıradaki kodu hem makina kodu olarak hem de disassemble edilmiş karşılığını ekrana yazıyor. Debugger'ın disassembler rutinleri bu aşamada gerekli (bir de u (Unassemble) komutu verildiğinde).

Bir de debugger'in asıl daha önemli bir kesmesi var ki o da INT 3. Bütün debugger'lar bu kesmeye de asılırlar (hook). INT 3'ün önemli özelliği makina kodu olarak hem 0CDh 03h ile hem de 0CCh ile çağırılabilir olması. Tabii ki tercih edilen yol 0CCh ile çağırmak. Tek byte opkod programcıya tek byte'ı değiştirerek oraya kırılma noktası (break point) eklenebilmesini sağlıyor. Aslında kesmenin başka da önemli bir özelliği yok çünkü aynı işi iki byte'da da yapabilmek olanaklı ve debugger olmadıkça INT 3'ün içeriği bir IRET'den ibaret (en azından öyle olması gerekir ama ne yapacağı işletim sistemine kalmış). INT 01h'ya benzer şekilde bütün işi INT 3'ün vektörünü yazmak olan bir programımız olsaydı, bu da debugger'da farklı kendi kendine farklı sonuçlar verecekti.

Debugger'lar bu kadarla bitmiyor, örneğin derleyiciyle çalıştırılabilir kodun içerisinde sembolleri saklayacak biçimde derledikten sonra bunları okuyabilir olması gerek. Sanırım debug.exe'de bu özellik bulunmuyor. Bundan başka breakpoint'lere değindim ama bunların belli koşul gerçekleştiğinde çalışanları, "conditional breakpoint"'ler de güzel bir özellik. debug.exe de bundan da yok. Assembler ile kodun içerisinde çalışırken kodları değiştirebilme gibi özellikler zaten olmazsa olmaz. Sadece trap flag ile iş bitmiyor. Bu yazıyı genel bir fikir vermesi için yazdım. İş debugger'i kodlamaya geldiğinde bu kadar basit olmayacağı açıktır.

12 Temmuz 2012 Perşembe

PCAD ile Baskı Devre Çizelim

Bu yazıda PCAD'le baskı devre çizimine değineceğim ve basit bir baskı devreyi çizerek göstereceğim. Yazı biraz uzun ve dolayısıyla sıkıcı olabilir. 90'ların sonunda benim görebildiğim kadarıyla amatör piyasaya Boardmaker programı hakimdi. Sonuçta o zamanlar hiçbirşey bilmeyen 15 yaşında çiçeği burnunda ergen elektronikçi adayıydım ve esnaf (çoğunlukla Pala Elektronik) ne derse onu yapıyorduk. Internet henüz tam anlamıyla yaygınlaşmadığından ve olan kaynaklara da henüz ulaşmayı bilmediğimden fazla bir seçeneğimiz kalmıyordu. Boardmaker, tek disketlik ve yanlış hatırlamıyorsam sadece baskı devre çizimi yapabildiğimiz (devre şeması çizemiyordu) bir programdı. Kitabıyla birlikte satılıyordu. Kitabını alıp bir haftasonunda bilgisayarın başında deneye deneye bitirdim ama beni tatmin etmemişti. Gel zaman git zaman seçenekler çoğaldıkça nasıl olduğunu hatırlamıyorum ama PCAD ile tanıştım. Windows altında çalışan, zengin bir dijital devre elemanı özellikle de mikroişlemci kütüphanesi bulunan (Intel 80286 bile vardı daha ne olsun) ve devre yollarını autoroute özelliğiyle kendi çizen muhteşem bir programdı. Yıllar boyu o kadar alıştım ki hala 2000 sürümünü (15.10.17) kullanıyorum ve çok memnunum. Bu kadar gevezelikten sonra programı anlatayım.



Programı açar açmaz yukarıdaki gibi bir pencere geliyor. Kullanışlı ve önemli özellikleri kenara yazdım. Yanlış hatırlamıyorsam 255 katmana kadar destekliyor ve siz programı açar açmaz 9 yada 10 katman kendisi ekliyor. Bunlardan önemli olanlar (diğerlerini ben de bilmiyorum aslında) :
  • Top: Bakır yolların olduğu katman
  • Bottom: Çift taraflı baskı devrelerde alt katman
  • Top Silk: Bakır yolların olmadığı tarafta devre elemanlarının tanımları (R2, C3, Q1 gibi) yazar ya işte o.
  • Bottom Silk: Top silk'in çift taraflı karşılığı. SMT kullanmayacaksanız çok da gerekli değil.
  • Board: Baskı devrenin sınırlarını belirleyen katman.

Program ilk defa kurulduğunda henüz kütüphaneler ekli olmuyor. Kütüphanelerde kullanacağımız devre elemanlarının hazır ölçülmüş biçilmiş şekilleri olduğundan sizi tek tek elemanı ölçüp ona göre delik koyma zahmetinden kurtarıyor. Devre elemanlarına tıklayınca liste bomboşsa kütüphaneler henüz ekli değildir. Yukarıda Library menüsünden Setup'u seçip kütüphaneleri eklemek gerekiyor. Bazı kütüphaneleri hiç kullanmadımsa da hepsini eklemeyi tercih ediyorum. Az bilinen garip bir devre elemanıyla karşılaşınca hepsini gözden geçirmek için tekrar eklemeye gerek kalmıyor.

Alt katman devredışı
Kütüphaneleri ekledikten sonra program kullanıma hazır duruma geliyor. Bundan sonra herhangi bir çizime başlamadan önce mutlaka yapılması gereken iki şey var. Birincisi katmanları belirlemek. Eğer çift taraflı bir devre geliştirmeyecekseniz Bottom'u (hatta Bottom'la ilgili bütün katmanları) kapatmakta yarar var. Options menüsünden Layers'i seçerek yada Top'un yanındaki kırmızı kutucuğa tıklayarak katmanların belirlendiği arabirimde önce Layers altında bottom'a sonra da sağda Disable düğmesine basarak alt katmanı kapatıyorum.




Bu arabirimi kullanarak Sets sekmesinde bu katmanlara ek olarak kendi katmanınızı oluşturabilirsiniz ancak bence gerek yok.

Örnek için şu adresten bulduğum basit sayılabilecek devre şemasını kullanacağım. 

Katmanları belirledikten sonra yapılması gereken ikinci iş yapılacak plaket boyutlarına karar vermek. Ben devre kartını aldıktan sonra kesme zahmetine girmemek için kafamda yaklaşık bir boyut belirliyorum. (Bu devre şu kadar alanda yapılabilir gibi...) Sonra gidip ona en yakın boyutlardaki bakırlı plakayı alıyorum ve çizimi o plakaya göre yapıyorum. Örneğin bu devreyi 5cm x 5cm'lik plakada yapalım. PCAD inç ve mil birimlerini kullandığından ölçüleri çevirmek gerek. (1 inç = 2.54cm, 1 inç = 1000 mil). Dolayısıyla 2 inç x 2 inç yani 2000 mil x 2000 mil'lik bir alan gerekli. Ekrandaki noktalar yeşil değil de beyaz olana kadar gözkararı biraz yaklaştırıyorum. Yeşil noktalar varsa sizin çözünürlüğünüz (Aşağıda ABS düğmesinin yanında mil cinsinden belirtilen değer) görüntülenen alana sığmadığından nokta sayısı azaltılır. Yakınlaştıkça beyaz noktalar görünmeye başlar. Beyaz noktaların arası sizin seçtiğiniz çözünürlüktedir.

Aşağıda Board katmanını seçip, sonra bu alanı soldaki çizgi gerecini seçip bir kareyle belirliyorum. Çizerken sağ altta çizginin uzunluğu belirtiliyor. Önce bir köşeye tıklıyorum, imleç şekil değiştiriyor, sonra diğer köşeye tıklıyorum, çizginin uzunluğu istediğim kadar olmadıysa aynı yönde biraz daha tıklayıp 2000 olana kadar çiziyorum. Sonrasında dik açıyla diğer kenarı köşesine tıklayarak belirliyorum. Dört kenarın tamamını belirleyip kare (yada istediğiniz herhangi bir şekil) oluşturduktan sonra sağ tıklayarak çizimi bitiriyorum. Board katmanının renginden bir kare oluşması gerekiyor.

Bundan sonra View menüsünden Extent diyip çizimi ekrana sığdırıyorum ve Top katmanını seçip devre elemanlarını eklemeye başlayabilirim. İlk zamanlarda devre elemanlarının nerede oldukları yada olmayan eleman için ne kullanmak gerektiği biraz sıkıcı olabilir. Önemli kütüphaneler ve içeriklerini kısaca belirtmeye çalıştım:
  1. CONNECT.LIB: DB9, DB25, dişi ve erkek slotlar (ISA, PCI gibi) için bağlantılar. 
  2. DIODE.LIB: Diyotlar
  3. PCBCONN.LIB: Connect.lib'dekine benzer bağlantılar ama daha fazla çeşit var.
  4. PCBMAIN.LIB: En çok kullanılan kütüphane. Kondansatörler, entegreler, dirençler, jumperlar ve bazı genel diyot ve transistörler bu kütüphanede bulunur.
  5. PCBSMT.LIB: SMT için gerekli devre elemanları.
  6. TRANS.LIB: Transistörler
Burada devre elemanlarını seçerken Browse düğmesine basıp şeklini görmek de olası. Eğer Browse'ye basınca şekil çıkmıyorsa o eleman baskı devre çizimi için değil devre şeması çizimi içindir. (Daha önce de belirttim PCAD'in bir de devre şeması çizimi için modülü var ancak ben onu kullanmıyorum.) Çizimi yaparken şemanın hemen altındaki delikli pertinaks çizimine sadık kalmayacağım ama elbetteki sonuçlar benzeyecektir. Pil beslemesi ve hoparlör plaketin üzerinde olmayacağından ben onlar için iki delik (PAD) atıp tel lehimlemeyi tercih edeceğim ancak potansiyometre plaketin üzerinde olacak. Butonu koymayacağım. Bir de devreyi basmadan önce protoboard'da yapıp, çalışıp çalışmadığından emin olduğumu varsayarak elemanların elimde olduklarını yani bacak aralarını bildiğimi varsayacağım. Örneğin bazı elektrolitik kondansatörlerin iki bacağı arası 100 milken diğerlerinin arası 200 mil olabiliyor. Bacakları uzatarak yada bükerek lehimleyince sorun olmaz ama tam uzaklığı neyse onu kullanmak devreye daha profesyonel bir görünüm veriyor.

Önce besleme ve hoparlör için 2şer delik (pad) ekledim.

Devre elemanlarını eklerken hangilerinin kullanılacağı biraz deneyim gerektiriyor. Örneğin standart dirençler için RES400 kullanınca tam oturuyorlar. Biraz daha tel bırakmak gerekirse RES500 kullanmak gerekli. Devre elemanları için sırasıyla devre elemanları düğmesine tıklayıp, boş bir yere tıklamak, yerleştirilecek elemanın bulunduğu kütüphaneyi seçmek (örn. PCBMAIN.LIB) ve o kütüphanenin içinden elemanı (örn. RES400) seçmek gerekiyor. Sonra kaç tane RES400 kullanılacaksa (bu örnekte üç) tıklaya tıklaya yaklaşık yerlerine koyuyorsunuz. Sonrasında sağ tuşla tıklayıp normal fare imlecine dönüyorsunuz. Benim yerleştireceğim dirençler dikeydi fakat PCAD onları yatay koydu. Hemen önce en sol üstteki fare imleçli düğmeye basıp yerleştirdiğim direncin üzerine tıklıyorum. Direnç seçiliyor, sonra klavyeden 'R' tuşuna basarak döndürüyorum. Yada fare düğmesine bastıktan sonra masaüstündeki ikonları seçer gibi dirençleri seçip çoklu seçmek ve 'R' ye basarak hepsini birden çevirmek olanaklı. Çevirdikten sonra fare imleciyle yaklaşık yerlerine sürükleyip aşağıdaki duruma getirdim.

Benzeri şekilde tüm elemanları PCBMAIN.LIB kütüphanesinden; elektrolitik kondansatör için CAP200RP, normal kondansatör için CAP200, LEDler için LED100 ve 555 için DIP8 seçerek yerleştirdim. Ancak bir tek potansiyometre için satın almış olduğuma uygun bir eleman bulamadım. Böyle durumlarda potansiyometreyi kumpasla yada protoboard'la ölçerek (protoboard'un delikleri arası 100 mil'dir) elemanın bacakları arasındaki mesafeyi bulup ona göre pad'ler yerleştirmek gerekir. Ayrıca kendi başına duran delikler anlamsız olacağından (en azından devreye bir sene sonra tekrar bakınca kesin anlamsız gelecektir.) Top Silk katmanında potansiyometrenin sınırlarını belirlemek için bir dikdörtgen atmak mantıklı olabilir.


Ben, tek başına bir dikdörtgen de çok açıklayıcı olmayacağından yazı aracını kullanarak 'POT1M' yazısını ekledim. Yazı aracını kullanmak basit. Üzerinde 'A' olan düğmeye bastıktan sonra yazının yaklaşık olarak yerleştirileceği yere tıklayıp çıkan pencerede yazıyı ekledikten sonra 'Place' demek yeterli. Yerini beğenmediğiniz yazıyı yine devre elemanına yaptığım gibi fare imleciyle seçip sürükleyebilir yada döndürebilirsiniz.

Tüm elemanları yerleştirdikten sonra geriye bağlantıları yapmak kalıyor. Bağlantıların doğrudan baskı devre yollarıyla bir ilgisi yok ancak yolları yerleştirirken bağlantıları kısa tutmakta her zaman yarar var. Uzun yolların diğer yolları kesme olasılığı artıyor. Bağlantılar ters 'N' biçimli araçla yapılıyor. Yapılacak iş ters 'N' bağlantı aracını seçmek ve bağlantı yapacağınız deliğin birini diğerine sürükleyerek elemanların bacaklarını birbirlerine bağlamak. Bağlarken ilk defa bir bağlantı yaparsanız size NET'in adını soracak. Bunlara ne ad verdiğinizin bir önemi yok ama özellikle artı besleme ve toprağın NET00000 ile NET00001 olması size sonradan kolaylık sağlayacaktır. Ben ilk önce + beslemeyi üstteki LED'in + bacağına bağlayarak NET00000 adını buna verdim sonra da GND'yi LED'in eksi bacağına bağlayıp buna da NET00001 dedim. Bir kere bağlantıya bir ad verdikten sonra bundan sonra aynı yere bağlanan her bağlantının adı da aynı olacak. Yani bundan sonra GND baskı devrenin her yerinde NET00001 olacak. Tüm bağlantılar bittiğinde özellikle 555'in civarında karışık bir görünüm olacak. Net'lerin adları burada önem kazanıyor. Devreyi kontrol etmek için sol üstteki fare imleci moduna geçip - beslemenin bulunduğu deliği seçip sonra sağ tıklıyorum. Çıkan menüde Highlight Net dediğim zaman o Net'e bağlı olan bütün delikler farklı renkte gösteriliyor. Hata kontrolü için yararlı. Seçimi geri kaldırmak için Unhighlight Net demek gerekli.

Kondansatörler, LED ve 555'in birinci bacağı eksiye bağlı olmalıydı. Demek ki sorun yok.

Aslında buraya kadar herşey tamamlandı ama tavsiye edeceğim bazı noktalar var. Gördüğünüz delikleri çapı 60mil yani 1.5mm. Eğer matkap ucunuz biraz titreşiyorsa bile bu delikleri delmek imkansız hale gelebiliyor. Matkap ucu sallanarak lehimlenecek yerleri de götürebiliyor. Benim bu duruma karşı önerim delikleri büyütmek.

Pad'e sağ tıklayınca çıkan menüden Properties'i seçip Pad Styles düğmesine basın. Herhangi birine tıklayıp Copy diyin ve istediğiniz bir adda kopyalayın. Örnekte ben P:BIR adını seçtim. Sonra Modify (Simple)'ı seçecek Width/Height değerlerini değiştirin. Çok büyük değerler deliklerin çevresinde kalan bakırların birbirine değmesine yol açabilir. Benim önerim Width (En) için 70mil Height (Boy) için de 160 mil Shape (Biçim) olarak da Oval iyidir. Değişikleri yapıp OK dedikten sonra Pad style olarak yeni tanımladığımız Pad'i seçip tekrar OK diyince artık geniş Pad yerleştirilmiş oluyor. Diğer Pad'ler için yine Properties'e gelip çıkan arabirimde Pad Style olarak yeni oluşturulmuş Pad'i seçip OK deyince diğer Pad de büyütülür. Elbette bunları tek tek yapmak sıkıcı olacaktır. Fare imlecindeyken CTRL'ye basılı tutarak hoparlöre ve potansiyometreye ait delikleri seçip hepsini bir seferde değiştirmek olası. Ancak böyle yapınca potansiyometreye ait deliklerin uzun tarafları birbirlerine yakın olacaktır. Daha düzgün olması için bir Pad daha tanımlayacağız. Bu seferkinin eni 160mil boyu 70 mil olacak. Yani eskisinin 90 derece dönmüşü olacak. Herhangi bir deliğe tıklayıp aynı şekilde özelliklerden varolan bir delik biçimini kopyalayıp en ve boy değerlerini değiştirmek yeterli.

Bundan sonra devre elemanlarının bacaklarını da bu deliklere uyduralım. Benzer elemanlar CTRL ile toplu seçilerek değiştirilebilir. Örneğin RES400'lerin tümünü seçip sağ tuşla tıklıyorum ve Properties'i seçiyorum. Çıkan arabirimde Pattern Pads sekmesine gidiyorum ve solda A ve B olarak gördüğüm iki tür deliği de seçiyorum. Normalde dikkat edilirse dirençlerin bir bacağı kare diğer bacağı yuvarlaktı. A kare olan bacak, B yuvarlak olan bacak. Genelde bu türden bir farklılaşma kutupları fark eden devre elemanlarında birinci bacağı belirtmekte kullanılır. Örneğin elektrolitik kondansatörün + bacağını yada entegrelerin birinci bacaklarını ayırdetmek için kare delik koymak mantıklıdır. Dirençte böyle bir farklılaşma olmadığından ben her ikisine de oval delik koyacağım. Her iki deliği de (A ve B) seçtikten sonra Pad Style olarak P:IKI'yi (fark etmez P:BIR de seçilebilir ama P:IKI bana daha güzel göründü.) seçiyorum ve alttaki Apply düğmesine basıyorum. Sadece A'yı değiştirirseniz sadece kare olan bacaklar değişir. Benzer biçimde LED'leri seçip her iki deliğini P:IKI yapıyorum. Burada dikkat edilmesi gereken LED'lerin bacakları birbirine yakın olduğundan P:BIR yapılırsa bakırlı yollar birbirine değecektir. CAP200 ve CAP200RP'yi birlikte seçip Properties'i seçince Pattern Pads sekmesine tıklanmıyor. Bunun nedeni daha önce dediğim gibi aslında her ikisi de kondansatör olsa bile ikisinin türünün farklı olması. Dolayısıyla aynı işlemi CAP200 için ayrı CAP200RP için ayrı yapmam gerekli. Son olarak entegre kaldı. Bunun için yine Pattern Pads de sekiz bacağını birden seçip Pad Style olarak P:IKI seçilebilir. Eğer birinci bacağın Pad'i dikdörtgen olması istenirse tekrar bir Pad tanımlayıp boyutları aynı fakat shape olarak Rectangle seçmek gerekli. Yazıyı çok uzatmamak için ayrıntısına değinmeyeceğim. Ben olduğu gibi bıraktım.

Pad'leri değiştirdikten sonra baskı devrenin görünümü.
Bu aşamada baskı devrede sadece bağlantıları tanımladım ama bakırlı yolları çizmedim. Bu biçimiyle diske kaydediyorum ancak kaydederken ASCII olarak kaydedicem. Binary olarak kaydetmemin bir zararı yok ama Autorouter'ı kullanırken zaten kendisi ASCII'ye çevireyim mi diye soracak.

Bakırlı yollar, sol üstte ikinci satırda (fare simgesinin altında) Route Manual aracıyla elle çizilebilir. Yapmam gereken Route Manual'i seçtikten sonra herhangi bir deliğe tıklamak. Tıkladıktan sonra o Net'in rengi değişecek (Highlight) sonra o Net'e bağlı başka bir deliğe tıklayıp yolu götürecem; sağ tuşla tıkladığımda da bakırlı yol çizilmiş olacak. Yol üst katmanda olduğundan, kırmızı görünecek. Diğer taraftan üzülerek belirtmeliyim ki bilgisayarın kendi algoritmasıyla çizdiği yol, en azından benim çizdiğim bütün yollardan genel olarak daha iyi oluyor ve otomatik yol çizimi basit bir devre için bile en az bir saatlik işten kurtarıyor. Otomatik yol çizimi için Route menüsü altında Autorouters'ı seçip gelen arabirimde Autorouter olarak Quick Route'u seçiyorum. Quick Route PCAD'in kendi yol çizme programı. İstenirse SPECCTRA adlı yazılım yüklenerek yol çizimi SPECCTRA'ya da yaptırılabilir. Öncelikle Quick Route'u seçip Start diyorum. Devrenin çizilmişi çıkıyor.


Bu haliyle bende +'dan LED'e giden bir yolu çizemeyip mavi renkli bağlantı olarak bıraktı. Bunun nedeni hoparlör için koyduğum Pad'lerin birbirine yakın olmalarından dolayı arasından yol geçememesi. (Ben de sonradan fark ettim). PCAD ile yol çizdirince çizilmiş yolların olduğu dosya yeniden kaydediliyor ve başına bir R harfi ekleniyor (Route anlamında). Yapılacak şey hoparlör için bıraktığımız Pad'lerin arasını açmak. Bunun iki yolu var, hiç yol olmayan 'R'siz ilk dosyaya döner, deliğin arasını açarım yada yolların olduğu dosyada deliğin arasını açar tekrar yol çizdiririm (yada elle çizerim). R'li dosyaya baktığımda kılavuz noktaların yine yeşile döndüğünü görüyorum çünkü Autorouter, Routing Grid diye bir parametre alıyor ve 25mil olarak seçili. Bunun anlamı en yakın iki yol birbirine 25 mil'den daha yakın olamaz demek. Otomatik olarak kılavuz noktalar 25mil'e indirildi ama yeterli zoom olmadığından yeşil noktalar görünüyor. İstenirse en alt satırda 100mil'e geri alınabilir. Ben ilk dosyada sağdaki deliği biraz daha sağa aldım ve tümünü yeniden çizdirdim. Başka bir seçenek de iki delik daha ekleyip baskı devrenin üst katından köprüyle birleşmeyen yolları birleştirmek.


Şu haliyle herşey hazır ama yine yapılmasını tavsiye edeceğim ufak şeyler var. Birincisi her ne kadar Autorouter kullanmak elle çizmekten daha iyi olsa da Autorouter bir algoritma olduğundan bazı şeyleri kendisi akıl edemiyor. Bir numaralı RES400'ün yukarıdaki bacağını ele alalım. Bir tane entegrenin üçüncü bacağına giden düz bir yol çıkıyor ve entegrenin altıncı bacağına giden yolun dibinden geçiyor, bir de ayrı bir yol hoparlör deliklerine giderken aynı yerden 45 derece açıyla çıkıyor. 45 derece açıyla çıkan yol anlamsız onun yerine alttaki yolla T biçiminde birleşse daha iyi olacak. Ayrıca başka bir bakır yolun o kadar yanından geçeceğine iki numaralı direncin iki bacağı arasında kocaman boşluk var. Daha yukarıdan geçse baskı devreyi basarken biraz rahatlık sağlar. Özellikle Positive20 kullanırken eksik pozlandırmada birbirine çok yakın geçen yolların arası erimiyor. Veya üçüncü dirençten gelen yol entegrenin altıncı yerine yedinci bacağına bağlansa ortalık biraz daha ferahlar. Bu nedenle Autorouter'in çizdiği yollar her zaman elle düzeltmeye ihtiyaç duyuyor. Bunu yapmak oldukça kolay. Yerini beğenmediğim devre yolunu fare imleciyle tıklayıp seçtikten Delete tuşuyla siliyorum. Silinen yolun yerini yine mavi bağlantı alıyor. Bütün beğenmediğim yolları silip Routing Manual ile sildiğim yolları elle düzeltiyorum.

Bir diğer sıkıntı da yolların çok ince olması. Aslında Net'lerin özellikleriyle (Attribute) oynayarak bir çok özelliğini değiştirmek olası. Width özelliği de bakırlı yolların kalınlığını belirleyen özellik ancak bu birçok durumda özelliği değişen Net'in bakırlı yola dönüşememesine neden oluyor. Yollar için Autoroute arabiriminde 10mil gibi bir kalınlık veriyor ki bu da çeyrek milimetre anlamına geliyor. Belki CNC tezgahta işlenebilir ama elle yapmak için çok ince, üstelik yine üretimde Positive20 kullanılacaksa sıkıntılı. Maalesef PCAD yolların kalınlığını bir seferde değiştirme gibi bir olanak sunmuyor. Yol kalınlığı değişince çoğunlukla Routing Grid parametresinde de değişiklik yapmak gerekiyor. Yol kalınlıklarını değiştirmek için iki yöntem var. Birincisi Autoroute bittikten sonra bakırlı yola çift tıklamak (yada sağ tuşla tıklayıp Properties'den) ve çıkan arabirim penceresinden yol kalınlığını değiştirmek. Yine bunu bütün yollar için ayrı ayrı yapmak gerekiyor. Ben genelde plaket üzerinde yer varsa, yollar için 40mil'i tercih ediyorum ancak yer yoksa 25mil'e kadar azalttığım da oluyor.

Çizimdeki bazı yolları biraz kenara kaydırdım yada birbirinden uzaklaştırdım. Daha sonra entegrenin altındaki yolları 25mil diğer yolları 40mil olacak biçimde değiştirdim. Küçük küçük birden fazla değişiklik yaptığım için ekran görüntüsü koymadım sadece baskı devrenin yollar kalınlaştırıldıktan sonraki halini ekledim.




Yol kalınlıklarını değiştirmek için iki yol olduğundan bahsetmiştim fakat bunun için daha önce bahsettiğim SPECCTRA gerekiyor. SPECCTRA ayrı bir baskı devre programı. PCAD'in dışarıdan bu programı çağırmak için arabirimi bulunuyor. SPECCTRA kuruluysa Autorouter olarak seçilir. İlk defa kurulduğunda SPECCTRA'nın yolunun Autorouters arabirim penceresinde ayarlanması gerekiyor. Penceredeki Command Line düğmesine bastıktan sonra programın yolunu girmek yeterli. System Log ve Extra Options'da bir hata olduğundan düzgün biçimde ayarlanamıyor. Onu olduğu gibi bıraktım. OK diyip bunları geçtikten sonra Do Wizard düğmesine basıp çıkan pencerede Auto Create DO File demek gerekiyor. DO dosyası SPECCTRA'nın bakırlı yolları oluştururken kullanacağı parametreleri içeriyor. Ben bu pencereye OK diyip sonra Autorouter penceresinde Edit as Text düğmesine basarak parametreleri düzenlemeyi tercih ediyorum. Benim ilgilendiğim iki tane parametre var. Birincisi Grid Wire. Bu PCAD'in Routing Grid parametresiyle aynı, yani yanyana iki yol arasındaki en kısa mesafenin uzunluğu. rule pcb (width 10.0) parametresi de ikinci parametre; bakırlı yolların kalınlığını belirliyor. Bu değeri arttırınca bakırlı yolları biraz daha yaklaştırmak gerekiyor dolayısıyla Grid Wire'ı azaltmak gerekiyor. Width olarak 25..40 arası, Grid Wire'da 20..25 arası fena değil. DO dosyasını düzenledikten sonra Save'e basıp kaydediyorum ve Start'a basıyorum. Bazı durumlarda SPECCTRA çalışmayabiliyor. Bu SPECCTRA'nın yada PCAD arabiriminin uzun dosya adlarını düzgün desteklememesinden kaynaklanıyor. SPECCTRA, DSN uzantılı bir dosyada hata veriyorsa sekiz karakterden uzun dosya adlarını kontrol etmek gerekiyor hatta dosyayı kök dizinine taşıyıp oradan çalıştırmak kesin sonuç veriyor. SPECCTRA düzgün çalışınca Autorouter'lar arasında seçim soruyor. Ben genelde Interactive Router'ı tercih ediyorum ama aralarında belirgin bir fark göremedim. SPECCTRA işlemini yapıp sonlanıyor ve dosyanın bitmiş biçimi PCAD'e yollanıyor.

SPECCTRA'nın Autorouter'ı bazı yönlerden PCAD'den daha güçlü. Özellikle tek katmanlı baskı devrelerde genellikle parametrelerini düzenleyince PCAD'den daha iyi yol çiziyor ama PCAD çift katmanlı baskı devrelerde çok daha güçlü. Aslında çift katman baskı devrelerde PCAD'in stratejisi yatay yolları alttan dikey yolları da üstten geçirmek. Böylece her katmanda mümkün olduğu kadar birbirine paralel yollar kalıyor. (Bu arada bunları yazarken birşey daha fark ettim. Üst taraftaki LED'i 90 derece çevirince yollar daha kolay belirleniyor. Bundan sonraki ekran görüntülerinde çevirilmiş LED'i kullanacağım.) SPECCTRA daha başarılı olsa da PCAD'deki gibi elle müdahale edilmesi gereken anlamsızca çizilmiş yollar kalabiliyor. SPECCTRA'ya yolları çizdirip elle biraz üzerinden geçtikten sonra aşağıdaki baskı devreyi elde ettim.


Herşey hazır gibi. Bundan sonra son küçük değişiklikler kaldı. Birincisi, devrenin girişine hangi ucun artı hangisinin eksi ve besleme geriliminin ne kadar olduğunu yazmak. Bu, devreye sonradan baktığınız zaman nereye kaç volt vermek yada multimetreyle nereden kaç volt okumak gerektiğini bilmek için çok gerekli. Onun haricinde ben devrenin boş bir köşesine adımı ve devreyi çizdiğim tarihi yazmayı seviyorum. Yıllar sonra bakıp ne zaman nasıl birşey tasarladığımı görmek hoşuma gidiyor. Bunları eklemek için yazı ekleme gerecini kullanmak gerek. Bir de bence en eğlenceli olan kısım baskı devreye bakır alanlar eklemek. Demek istediğim özellikle alıcı yada verici gibi devrelerde vardır. Baskı devre üzerindeki bakırlı alanlar sadece yollar geçecek şekilde yukarıdaki resimde olduğu gibi açılmaz. Onun yerine yolların sadece kenarları bakırdan ayrılır, boş alanlarda toprağa yada hiçbiryere bağlı olmayan bakırlar bırakılır. Bu hem bana güzel geliyor hem de devreyi demir3klorüre atınca erime zamanını azaltıyor. Benim gibi sabırsızsanız tercih edilebilir. Bunu yapmak için sol tarafta yazıyla çizgi arasında Place Copper Pour gereci var. Onu seçiyorum. Top katmanındayken en başta baskı devrenin sınırlarını çizgilerle belirlediğim gibi plaketin kenarlarından biraz daha içeriye bir yere Copper Pour ile benzer bir kare çizip sınırları belirliyorum. Sağ tuşla tıklayınca anlamsız bir kare oluşacak. Sonra fare imleciyle kareye çift tıklayıp Properties'den Connectivity sekmesinde NET00001'i (yani toprak) ve Style sekmesinden de Poured ile Pattern'de istediğim bir deseni seçiyorum (bence çaprazlı güzel). Backoff parametresi kalacak bakırla yollar arasında ne kadar mesafe olacağı belirleniyor. Çok yakın olmamasında yarar var. Island Removal sekmesinde istenirse belli bir alandan küçük kalmış, içerilerde kalan yada yollardan ötürü seçilen Net'e bağlantısı yapılamamış adacıklar oluşmaması sağlanabiliyor. OK dedikten sonra boş alanlardaki bakırlar bırakılıyor. İstenirse hiçbir yere bağlı olmayan bakır alanlar da bırakılabilir. Şekilde adacıklara bağlı mavi bağlantılar yollardan dolayı hiçbiryere bağlanamamış adacıklara bağlı. Bunların oluşması sorun değil.

Herşey hazır.

Şimdi bunun çıktısını almak lazım. File menüsünden Print'i seçince maalesef herşey hallolmuyor. Setup Print Jobs'a basıp hangi katmanda nelerin kağıda basılacağını seçmek gerekiyor. En üstteki Print Job Name'e herhangi bir ad yazın. Bakırlı katman için Top katmanını seçin. Baskı devrenin sınırlarını da eklemek için CTRL'ye basılı tutarak Board katmanı da seçilmelidir. En sağdaki Display Options'da Pads ve Pad/Via Holes seçili bulunmalıdır. Çift katmanlı devreler için Vias seçeneği de seçilmesi gerekir. Bu haliyle Add düğmesine tıklanır ve bir baskı işi oluşturulur. Kağıda yada PNP kağıdına baskı alınacaksa bu hali yeterlidir. Asetata baskı alıp Positive20 ile çalışılacaksa Display Options'da Mirror'u da seçmek gerekebilir. Ayrıca bakırsız taraf için devre elemanlarının sembolleri, değerleri hatta şekilleri yani Top Silk katmanı basılacaksa ayrı bir çıktı almak gerekir. Bu nedenle başka bir Print Job Name daha verilir ve bu sefer Top Silk, Board katmanları seçilir. Ref Des, Type, Value vb. gerekli değerler seçilir. Çift taraflı devrenin alt katmanı için de ayrı iş tanımlamak gerekir. Close düğmesine bastıktan sonra Print Preview ile baskı kontrol edilebilir yada Generate Printouts ile seçilen işler varsayılan yazıcıya yollanır. (Başka yazıcıyı Print Setup ile değiştirin.) File menüsünde görüleceği üzere PCAD'in Gerber yada Drill çıktısı da var yani CNC tezgaha doğrudan çıktı yollamak da mümkün.

Biraz uzun bir yazı oldu. Bir CAD programının bildiğim kadarını etraflıca anlatmaya çalıştım artık ne kadar başarabildimse. Bundan sonra bir ara zamanım olursa çizilmiş baskı devrenin nasıl üretileceğini anlatacağım.

6 Temmuz 2012 Cuma

Bir başka PIC

Sayıca en fazla satın aldığım devre elemanlarını sıraladığımda muhtemelen birinci sırada her işe koşulan BC548 sonrasında LEDler ve daha önemlisi üçüncü sırada PIC16F84A gelir. Başlıkta kastettiğim aslında PC'lerin programlanabilir kesme denetleyicileri (Programmable Interrupt Controller) yerine Microchip firmasının ürettiği ve açılımı Peripheral Interface Controller olan mikrodenetleyici serisi. PC'lerin PIC'lerine dair birşeyler daha sonra da ekleyeceğim.

PIC serisi mikrodenetleyicilerin adını birçok kimse duymuştur. Mikrodenetleyiciler, kabaca sade yapıya sahip mikroişlemcilerin aksine üzerlerinde paralel ve seri I/O portları, ADC/DAC gibi arabirimleri hazır olarak gelen ve entegrenin içerisinde Timer, RAM/ROM gibi ıvır zıvırların da olduğu işlemciler. Bana hep mikroişlemciyle PLC arasında kalmış entegreler olarak gelmiştir. Siz programınızı geliştirip EEPROM'a aktarıyorsunuz. Devreye takıp elektriği verince kodu çalışmaya başlıyor.

16F84A denetleyicisi yurtdışında artık tükenmek üzere olan bir denetleyici. Bunun yerini ucuza maledilen hem de daha yetenekli ve pin uyumlu 16F628 modeli alıyor [ http://www.finitesite.com/d3jsys/16F628.html ] ancak 16F84A için internette hala deli gibi amatör tasarımlar bulabilmek olanaklı.

16F serisiyle tanışmam yaklaşık on sene öncesine rastlasa da son bir senedir aktif olarak kullanıyorum. Aslında bunun nedeni ilk aldığımdaki programlayıcının çalışmayıp beni deli etmesiydi. Bu nedenle programlayıcılara ileride biraz değineceğim. Bundan önce tesadüfen gördüğüm ve yanda resmini eklediğim hazır kitten bahsedeyim. Bunu piyasadaki dükkanlarda bulabilirsiniz. Ben üzerinde çok fazla tersine mühendislik gerçekleştirdiğim için kendim tasarlamış kadar hakimim ancak ticari bir ürün olduğundan detaylarının tamamını vermeyeceğim.

Uzun bir aradan sonradan programlayıcısı yüzünden beni kendine küstüren 16F84 denetleyicisiyle barışmaya karar verip bu kiti aldım. Her kit alışımdaki gibi heyecanla eve geldim. Devre elemanlarını yerleştirip lehimlemeye başladım. (Kitin hazır lehimlenmişi de satılıyor ancak ben kendim kurduklarımı tercih ediyorum.) Herşey bittiğinde kesin lehimlerken yaktım dediğim transistörlerin hiçbirini yakmadığımı, devrenin sorunsuz çalıştığını görünce önce sevindim. Devreyi çalıştırdım; zamanı kurup geri sayımı başlattım; süre dolduğunda röle çekti. Hepsi bu kadar. (Ya ne olacaktı?) Her kiti yaptıktan sonra, satın alıp eve gelirkenki heyecanın tam tersi orantılı bir hayalkırıklığı yaşıyorum, çünkü yapılacakları yaptım herşey bitti. Oynayacak bir şey yok çünkü amaç belli.

Bir kaç gün sonra bari kodu inceleyeyim diyip kendi programlayıcımı yapmaya koyuldum. Elimde iki farklı programlayıcı şeması vardı. Biri JDM Programmer olarak bilinen ve benim de zamanında kendisini yada çok benzerini satın aldığım model, diğeri de görece çok daha basit ve beslemesi dışarıda olduğundan gereksiz kablo kalabalığı yaratan başka bir model. Birinci modelde sanıyorum elektrik beslemesi, seri port üzerinden gelen akımın bir kondansatörde depolanıp 5V'a yakın bir zener üzerinden entegreye verilmesiyle sağlanıyor, böylece dışarıdan bir besleme gerekmiyor.

İkinci model basit olduğundan önce onu protoboard'a kurdum. Besleme katını yapmaya üşendiğimden beslemeyi doğrudan adaptörden aldım. Devreyi bağladıktan sonra donanım denetlemesinde programlayıcıyı görüyordu ancak programlarken on sene önce olanlar tekrar oluyordu. Neyse diyip ikinci programlayıcıyı kurdum. Sonuç yine aynı. Derken birşeyi fark ettim; ilk denemede adaptörü 9V'a ayarlamışım ama entegrenin besleme gerilimi 5V. Dolayısıyla büyük olasılıkla PIC'i daha okuyamadan yaktım. Bu arada PIC'lerde Code Protect (CP) diye bir özellik var. CP'yi set ederek yazdığınızda kodunuz okunamıyor. Büyük olasılıkla benim kod da öyleydi ama artık yanmış zaten ne önemi vardı ki.

Bu sırada kullandığım yazılımdan şüphelendiğimden programlayıcıyla birlikte üç ayrı yazılımı denedim. Bu yazılımlar sizin yazıp derleyiciyle ikili koda dönüştürdüğünüz dosyaları alıp seri port üzerinden entegrenin EEPROM'una aktarıyorlar. Seri portu kullandıklarından bir sıkıntı daha var: Windows NT tabanlı sistemlerde donanıma doğrudan erişemediklerinden çok yavaş çalışıyorlar ve hatta çalışmıyorlar. Bu nedenle bütün bu işleri yapabilmek için biryerlerden P2 sistem bulup (anakartında ISA slotu bile var.) Windows 98 kurdum. Bu üç programların adları şöyle:
  1. IC-Prog: Daha önceden de bunu kullanmıştım. Basit bir kullanımı var üstelik dökümanlarında JDM programmer'i tanıyabildiğini söylüyor.
  2. WinPIC: Piyasadaki bir çok programlama devresini tanıyor ve yaygın olarak kullanılıyor.
  3. pgm84: JDM Programmer'in tasarlayan adamın pascal'da geliştirdiği açık kaynak kodlu yazılım. Programın komut satırı parametrelerini anlamak için kaynak kodunu incelemem gerekti ama başarılı buldum. [ http://www.jdm.homepage.dk/newpic.htm adresinde bulunabilir ancak ben tavsiye etmiyorum. ]
Entegreleri yaktığım için gidip boş bir tane daha 16F84A satın aldım. JDM programmer hala protoboard'da kuruluydu. pgm84 ile sağdan soldan bulduğum basit bir hex dosyasını yazdım. Sonuç: Başarılı. Ancak hala IC-Prog'da sorunlar çıkıyordu. Bundan sonra JDM'in elemanlarını alıp tekrar poşetine kaldırdım. İlk tasarıma geri dönüp, besleme katını düzgünce yaptım. Üstelik bu olaydan sonra kullandığım bütün 16F84 devreleri için aynı yada çok benzeri bir besleme katı kullandım. Programlayıcıyı bitirip IC-Prog ile denedim; sorunsuz çalıştı. Bahsettiğim programlayıcının şemasını aşağıya da ekledim. Yalnız şemada bir YANLIŞLIK yapılmış, buna dikkat edin: LM7805'in GND bacağı 3 değil 2, yani ortadaki bacak; Vout da 2. değil 3. bacak.



LM7805'in doğru pin şemasını şuradan bulabilirsiniz. Google resimlerde aratınca zaten binlercesi çıkıyor.

JDM'in programlayıcı devresi, yukarıda verdiğim bağlantıda var. Doğrudan bağlantı şu şekilde: http://www.jdm.homepage.dk/newpic22.gif

Bundan sonra 16F84A'nın assembly'sini öğrenip birşeyler geliştirebilirdim. Bozduğum zamanlayıcıyla başlamak zaten kafamdaydı. Ancak zamanlayıcıyı nasıl tamir ettiğimden daha sonra bahsedeyim.


1 Temmuz 2012 Pazar

Byte, Word ve Alignment Fault

Intel'in çığır açan 8080 ve 8086 işlemcileri hatta onun öncülü gelişmiş ALU 4004 işlemcisi piyasaya sürüldüğünde kimse olayın bu yazıları yazdığımız Pentium'lara varacağını tam olarak öngörememişti. O zamanlar henüz 4004'ler için bir nibble yani 4 bit bir word'u oluşturmak için yeterliydi. 8008 ve 8080'lerle birlikte word 8bit'e yani bir byte'a ve 8086 ile birlikte de bir word 16 bite çıktı.

Word yani CPU Word kelime anlamı olarak mikroişlemcinin bir kerede işleyebileceği en büyük veri miktarı. Yani bir 8086 işlemcinin bir word'unun 16 bit olması demek veri yolunun (data bus) 16 bit olması, yazmaçlarının (register) 16 bit olması ve bir işlemci çevriminde örneğin mov word ptr ax, [7F00h] komutuyla adresleme yoluna (address bus) 7F00h'yı sürmesi, MEMR bacağını set etmesi (daha doğrusu nMEMR'yi resetlemesi) ve veriyoluna o adresteki word'un okunarak o word içindeki bitlerin AX'e tutturulması anlamına geliyor. İşlemci 7F00h ve 7F01h'yı bir çevrimde okuyabiliyor. Ancak yine de mov byte ptr al,[7F01h] ile 7F01h adresindeki byte'a da erişebiliyorduk.

Zaman geçtikçe word kavramı kendi anlamını yitirdi yada byte kavramının altında kaldı demek daha doğru olur. 80386'lara eklenen 32 bitlik genişleme yazmaçlarla birlikte word 32bit'e çıkarıldı. Pentium'larda word hala 32 bitti ancak MMX'lerle birlikte garip bazı genişlemeler sayesinde word 64bitmiş gibi işlemler yaptırabiliyorduk. Veri yolu hala 32 bit olsa da CISC mimarisi sayesinde bellek okuma/yazma süreleri birden fazla işlemci çevrimine (cycle) dağıtılarak bir komutla 64 bitlik verilerin işlenebilmesi olanaklı oluyordu. Aslında bir bakıma word hala 32 bit olsa da 64 bitmiş gibi gösteriliyordu.

Önce nispeten yakın zamanda gelen x64 yada x86_64 mimarisiyle birlikte AMD gerçek anlamda word'u 64 bite çıkardı. Elbette adı üzerinde, bu mimariler varolan 32bitlik x86 mimarisine eklenen bir yama biçimindeki genişlemelerdi. Bana kalırsa artık geriye doğru uyumluluk kavramı uyumluluğun getirdiği avantajlar yanında sürekli eski komut setini desteklemenin getirdiği maliyeti kaldıramıyordu. Yani geriye doğru uyumluluğu yok sayarak geliştirilecek yenikomut setli yeni mimari hızlanmanın önünü açabilecekti. Bu da zaten IA64 yani Itanium mimarisine karşılık geliyor. x86_64'un hala masaüstü makinalarında olmasının nedeni bütün kodları yada en azından bütün derleyicileri bir anda IA64'e göre yazmanın zorluğundan kaynaklanıyor. Ancak yüksek performans piyasasında IA64 işlemciler şimdiden x86'ların yerini almaya başlıyor.

Peki neden bu kadar hardcore işlemci mimarisinden girdim? Çünkü taa 80386 zamanında işlemci word'u 32 bit olsa bile hala aldığımız RAM'leri megabayt üzerinden alıyorduk. Sene oldu 2012 ama hala RAM'ler word üzerinden değil de byte üzerinden satılıyor. Aslında aldığımız 32MB RAM işlemci için boş 8Megaword'luk boş alan içerdiğini düşününce insan biraz kazıklanmış hissetmiyor değil. Diğer taraftan hemen yukarıda 7F01h adresindeki byte'a 8086'larda iki türlü erişilebileceğinden bahsetmiştim. Adreslemeyi byte byte yaptığımız halde bir anda 2 byte'a birden erişmek Intel mühendislerine biraz gereksiz gelmiş olacak ki bellek erişimini hızlandırmak adına yanılmıyorsam Pentium'lardan itibaren adresleme veri yolunun en düşük anlamlı 3 bitini iptal ettiler. Yani Pentium'larda A0, A1 ve A2 artık bulunmuyordu. Sizin 8086'nızda çalıştırdığınız masum mov byte ptr al, [7F01h] komutu Pentium'larla birlikte artık mov dword ptr eax, [7F00h] olarak çalışıyor ve sonrasında gereksiz kısımlar atılarak gereken kısımlar AL'ye yazılıyordu. Bu arada dword yani "double word"ün altını çizmek lazım. Seneler içerisinde word kavramı da karışıklık olmasın diye 16bit'te sabitlenmiş ve 32bitlik word, double word olarak adlandırılmaya başlanmıştı. 64bit'eyse qword diyecektik. Tabi genişletilmiş adresleme yolu sayesinde korumalı modda 00007F00h adresini kullanıyorduk.

Buraya kadar herşey aslında normal gibi ancak zurnanın zırt dediği yer işlemciye mov dword ptr eax, [7F01h] verdiğimiz zaman ortaya çıkıyor. Bu durumda işlemci şunu yapmaya çalışacak:






Ancak bunu yaparken artık 7F01h'ya doğrudan erişemiyor. Dolayısıyla 7F00h'dan okuyup bunu EAX'in gerekli kısımlarına yazacak. Sonra diğer çevrimde 7F04h'ya erişip oradan gerekli olan verileri okuyacak ve yine EAX'in gereken kısımlarına yazacak. Dolayısıyla normalde bir çevrimde tamamlanması gereken okuma iki çevrimde tamamlanacak. Elbette korumalı modda araya girecek denetlemeleri ve kesmeleri saymıyorum. Ancak erişim 4ün katı olan yada başka bir deyişle sonunda üç tane sıfır olan bellek adresine yapılacak olsaydı tek bir çevrimde okuma yada yazma olanaklı olacaktı. Bu da performansı düşüren bir durum. Günümüzde derleyiciler size sormaya gerek bile duymadan bellekte ayırdığınız bütün verileri dördün katlarına gelecek biçimde hizalarlar. Zaten bu olaya hizalama denmesinin nedeni de bu.

Intel mühendisleri bununla da kalmadılar. Eğer EFLAGS'ın AC (Alignment Check) ve CR0'ın AM (Alignment Mask) bitlerini set ettiğiniz zaman size Alignment Fault adındaki istisnayı (exception) atarlar. 80286'nın exception belgelerine baktığınız zaman alıştığımız sıfıra bölme, tuzak kapısı, NMI vb. haricinde 8-15 arasını ayrılmış olarak görüyoruz. 16 yani 10h'dan sonrasını BIOS Video, Disk vb. kesme hizmet programlarının olduğunu biliyoruz. Ancak daha sonradan eklenen bu Alignment Fault kesmesi 17 yani 11h'yı kullanıyor. Bu durum biraz kafa karıştırıcı.

İşletim sistemleri performans açısından bu kesmeye asılıp performans denetlemesi yapabilir ve bu şekilde kullanıcıyı uyarabilir hatta mavi ekran bile çıkartabilirler. Daha önce de dediğim gibi normalde basit bir malloc() fonksiyonu çağırıldığında işletim sistemi dördün katı olan bir adres sağlar. Derleyici de ayırılan dizinin elemanlarını dördün katlarında bulunacak biçimde adreslemeye çalışır. Yine de bazen derleyicinin yapabileceği bir şey kalmayabilir: Örneğin 5 byte'lık bir struct tanımladıktan sonra bu struct üzerinden malloc() çağırırsanız. Böyle durumlarda icc kullanarak derleme yaptığınız zaman UNIX sistemler kullanıcıyı program çalışması esnasında uyarıyorlar.

Son olarak bütün bunları bir yere bağlamak gerekirse; SuperLU kütüphanesini hem x86_64 mimarili Intel Xeon işlemcide hem de Itanium2 işlemcili makinalarda derleyip çalıştırdım. Her ikiside sorunsuz derlendiği halde kurulum testlerinde Itanium makinada hizalama uyarısı verdi:

pstest(17541): unaligned access to 0x600000000003733c, ip=0x4000000000099230

Dikkat edilirse erişilen adres dördün katı ancak erişim yapan makina 64bit. Yani erişilen adresin sekizin katı olması gerekiyor. Aynı hatayı x86_64 makinada vermiyor. Üstelik büyük boyutlu denemelerde programın çalışma süresi yaklaşık dörtte bir daha uzun sürüyor. Programları 64bit makinalar için yazarken, araştırınca Intel icc için basit derleyici komutlarıyla nasıl qword'luk hizalama yaptırabileceğinizi belirtiyor ancak sözkonusu program SuperLU gibi büyük ve başkasının yazdığı bir program olduğunda düzeltme yapmak can sıkıcı uzunlukta olabilir.