Bilgisayar mimarisi henüz kısa süredir ilgilendiğim bir alan ve bu alanda hayata geçirdiğim ilk projeyi sizlere sunmanın mutluluğunu yaşıyorum.
Projenin dosyalarına ve aşağıda anlatacağım iki hazır programa GitHub linkinden erişebilirsiniz. İşte o proje:
Mikroişlemciler özel amaçlı ve genel amaçlı olmak üzere iki başlık altında incelenebilir. Özel amaçlı mikroişlemciler belirli özel bir işlemi yerine getirmek için tasarlanmış tümleşik devrelerdir. Genel amaçlı mikroişlemciler ise geniş bir yelpazede görevleri yerine getirebilmek için daha esnek biçimde programlanabilme kabiliyetine sahip mikroişlemcilerdir.
Bu projede tasarladığım mikroişlemci, genel amaçlı bir mikroişlemcinin gerçekleştirmesi beklenen aritmetik, mantıksal, transfer ve kontrol komutlarının çoğunu temel seviyede gerçekleştirebilir.
Bu tasarım, programcı tarafından kullanılabilen 5 adet genel amaçlı ve program çalışırken mikroişlemci tarafından kullanılan 4 adet özel amaçlı kaydedici içerir. Genel amaçlı kaydediciler; AX, BX, CX, DX ve EX isminde 16 bitlik kaydedicilerdir. Özel amaçlı kaydedicileri ise aşağıda özellikleri ile sıralandığı gibidir:
Girişte bahsedildiği gibi mikroişlemci, genel amaçlı bir mikroişlemciden beklenen temel işlemlerin birçoğunu gerçekleştirir. Bunlar:
Mikroişlemci, verinin esnek biçimde ele alınabilmesini sağlamak amacıyla çoğu komutta farklı adresleme çeşidini kabul eder. Bu adresleme çeşitleri:
AND, OR, XOR, toplama, eldeli toplama, çıkarma, borçlu çıkarma, çarpma, bölme ve karşılaştırma komutları yukarıda bahsi geçen tüm adresleme biçimlerini kabul eder.
Kontrol komutları atlanacak komutun adresini direct, indirect veya register adresleme yöntemlerinden biri ile alabilir.
Kaydırma ve dairesel kaydırma, bir artırma ve eksiltme komutları ile kaydedicilerin sıfırlandığı veya tersinin alındığı işlemler kaydediciler üzerinde gerçekleşir ve yalnızca işlemin hangi kaydedicide gerçekleşeceğinin belirtilmesi gerekir.
Veri transfer komutları bellek adresine yükleme yaparken direct ve indirect adresleme biçimlerini kullanır. Bir kaydediciye yükleme yapılması sırasında immadiate, kaydediciler arasında kopyalama yapılırken register adresleme kullanılır.
Son olarak bayrakları sıfırlama ve tersini alma komutları ile HLT komutu bir adresleme biçimine ihtiyaç duymayan komutlardır.
Tüm bu adresleme biçimleri ile IR kaydedicisinin 8 bitlik komut alanından toplam 226 farklı sinyal alınmış olur.
Mikroişlemcinin programlanmasından bahsedecek olursak. Buradan komut setine ve kullanılan adresleme biçimlerine göre belleğe girilmesi gereken makine kodlarına ulaşabilirsiniz. [address] ve [number] yazan kısımlara işlem yapılacak bellek adresi veya verinin hexadecimal olarak girilmesi gerekiyor. [xxxx] yazan kısımlarda ise ne yazıldığının bir önemi yok.
| Durum | Zero | Carry |
|---|---|---|
| A = B | 1 | 1 |
| A < B | 1 | 0 |
| A > B | 0 | 1 |
Programlama örneğinden bahsetmeden önce iki konuya değinmekte fayda var. Birincisi iki sayı karşılıştırıldığında sonuç zero ve carry bayrakları üzerinden gösterilir ve koşullu sıçrama yapan komutlar bu iki bayrağın durumunu kontrol eder. İki sayıyı karşılaştırdığımızda (bunlar A ve B olsun) bayrakların durumu karşılaştırma sonucu tablosunda gösterildiği gibi şekillenir
İkinci konu ise çarpma işleminin sonucunda 32 bitlik bir sayı ve bölme işleminin sonucunda 16 bitlik sonuç ve kalan elde ederiz. Kaydediciler 16 bitlik olduğu için bu işlemlerin sonucunda iki genel amaçlı kaydedici kullanılır. Komut setinin olduğu sayfada da görülebileceği üzere bu işlemlerin sonucunda işlem yapılan kaydedici ile alfabetik olarak ondan sonra gelen kaydedici kullanılır.
Gelelim örnek programlara. Özellikle koşullu kontrol komutlarının da nasıl kullanıldığını gösterecek iki basit program hazırladım.
Belleğe girilen bir sayının faktöriyelini hesaplayan program örneği:
| Bellek Hücresi | Komut | Makine Kodu |
|---|---|---|
| 0 | MOV AX, 5 | be0005 |
| 1 | CMP AX, 3 | a30003 |
| 2 | JIB 10 | d1000a |
| 3 | MOV CX, AX | c30008 |
| 4 | DEC CX | dd0002 |
| 5 | MUL AX, CX | 870002 |
| 6 | DEC CX | dd0002 |
| 7 | CMP CX, 1 | a50001 |
| 8 | JIE 10 | d4000a |
| 9 | JMP 5 | c50005 |
| 10 | HLT | e2ffff |
Adım adım inceleyecek olursak. Belleğin ilk hücresine girdiğimiz komutta AX kaydedicisine faktöriyelini almak istediğimiz sayıyı yüklüyoruz. Bu örnek için bu sayı 5.
1 numaralı hücredeki komutta ise bu sayıyı 3 ile karşılaştırıyoruz ve sonraki komutta sayı 3’ten küçükse programı sonlandırmak için 10 numaralı hücreye atlıyoruz ki boşuna işlem yapmayalım. Çünkü sonuç hali hazırda AX’te yazıyor.
3. ve 4. hücrelerdeki komutlarda sayı 3’ten küçük değilse CX kaydedicisini sürekli AX ile çarpacağımız sayaç olarak ayarlıyoruz. Burada yukarıda bahsettiğim sebepten ötürü özellikle BX’i seçmiyoruz.
5. ve 6. hücrelerde çarpma işlemini yapıp CX’deki sayıyı 1 eksiltiyoruz.
7. ve 8. hücrelerde CX’deki değer 1’e ulaştığında programın sonlanmasını sağlıyoruz. Diğer durumda 9. hücre çalışıyor ve CX’in yeni değeri ile AX’i tekrar çarpmak için programı 5. hücreye yönlendiriyor.
Tüm sürecin ardından başta AX’e girdiğimiz sayının faktöriyeli yine AX’te bizi bekliyor olacak.
Makine kodunu belleğe tek tek elinizle girebileceğiniz gibi proje dosyaları arasında göreceğiniz factorial ve sorting programlarının olduğu formatta uzantısız metin dosyaları oluşturabilir ve logisim’de belleğe sağ tıklayıp load image seçeneği ile programı kolayca yükleyebilirsiniz.
Daha fazla uzatmadan diğer programdan da kısaca bahsedelim. Belleğe girilen sayı dizisini küçükten büyüğe sıralayan program:
| Bellek Hücresi | Komut | Makine Kodu |
|---|---|---|
| 0 | MOV AX, 120 | be0078 |
| 1 | MOV [100], AX | b90064 |
| 2 | MOV AX, 100 | be0064 |
| 3 | MOV [101], AX | b90065 |
| 4 | MOV AX, 125 | be007d |
| 5 | MOV [102], AX | b90066 |
| 6 | MOV AX, 90 | be005a |
| 7 | MOV [103], AX | b90067 |
| 8 | MOV AX, 80 | be0050 |
| 9 | MOV [104], AX | b90068 |
| 10 | MOV AX, 100 | be0064 |
| 11 | MOV [50], AX | b90032 |
| 12 | MOV AX, 104 | be0068 |
| 13 | MOV [51], AX | b90033 |
| 14 | MOV BX, [50] | b00032 |
| 15 | INC BX | dc0001 |
| 16 | MOV CX, [50] | b10032 |
| 17 | CLR EX | d60004 |
| 18 | MOV [52], CX | bb0034 |
| 19 | MOV AX, [[52]] | aa0034 |
| 20 | CMP AX, [BX] | a80001 |
| 21 | JIA 27 | ce001b |
| 22 | CMP BX,[51] | 9f0033 |
| 23 | JIE 34 | d40022 |
| 24 | INC BX | dc0001 |
| 25 | INC CX | dc0002 |
| 26 | JMP 18 | c50012 |
| 27 | MOV [52], BX | ba0034 |
| 28 | MOV DX, [[52]] | ad0034 |
| 29 | MOV [[52]], AX | b40034 |
| 30 | MOV [52], CX | bb0034 |
| 31 | MOV [[52]], DX | b70034 |
| 32 | INC EX | dc0004 |
| 33 | JMP 22 | c50016 |
| 34 | CMP EX, 0 | a70000 |
| 35 | JIA 14 | ce000e |
| 36 | HLT | e2ffff |
0 ve 9 numaralı hücrelerin arasında bulunan komutlarla belleğin 100 ve 104 arasındaki adreslerine 5 adet sayı yüklüyoruz.
10 ve 13 numaralı hücrelerin arasında 50 ve 51. adreslere sayı dizimizin ilk ve son adreslerini yüklüyoruz.
14, 15 ve 16 numaralı hücrelerin sonunda sayı dizisinin ilk elemanın adresini CX’e arkasından gelen elemanın adresini BX’e yüklemiş oluyoruz. Daha sonra bunlar 1’er artırılarak, karşılaştırılacak sıralı elemanların adreslerini gösteriyor olacak.
17. hücrede ise dizi üzerinde turlarken sayıların yerini değiştirip değiştirmediğimizi takip etmek için EX kaydedicisini temizliyoruz.
18 ve 19. hücrelerde CX’in gösterdiği adresteki sayıyı AX kaydedicisine alıyoruz ve 20. hücrede bu sayı ile BX’in gösterdiği adresteki sayıyı karşılaştırıyoruz.
21. hücrede eğer AX’teki sayı büyükse 27. hücreye atlıyoruz ve 27 ve 33 numaralı hücreler arasında bellekteki iki sayının yerini değiştirip değişiklik yaptığımıza dair EX’i artırarak 22. hücreye kaldığımız yere geri dönüyoruz.
22 ve 23. hücrelerde eğer BX sayı dizisinin son elemanının adresini gösteriyorsa 34. hücreye giderek EX’i 0 ile karşılaştırarak bu turda hiç yer değiştirme yapıp yapmadığımızı kontrol ediyoruz.
Eğer EX 0’dan büyükse 14. hücreye dönerek BX, CX ve EX kaydedicilerini ayarlayarak dizi üzerinde bir tur daha atıyoruz. EX 0’a eşit olduğunda dizinin sıralanmış olduğunu anlıyoruz ve 36. hücrede HLT komutu ile mikroişlemcinin çalışmasına son veriyoruz.
Program farklı uzunluktaki dizileri sıralayacak biçimde tasarlandı ancak dikkat edilmesi gereken iki nokta var. Bunlardan ilki sayı dizisinin uzunluğu 50’yi geçerse dizinin ilk ve son adreslerinin kaydedildiği hücrelerin değişmesi gerekir. İkincisi ise dizinin uzunluğu 5’ten farklı olduğunda (özellikle de uzun olduğunda) komutların bellekteki yerleri değişebileceği için sıçramaların yapıldığı adresler de değişecektir.
Benim gerçekten tamamlamaktan keyif aldığım bir proje oldu. Umarım siz de okumaktan keyif almışsınızdır. Buraya kadar okuduğunuz için teşekkür ederim. Başka projelerde görüşmek üzere.