Temel Seviye Mikroişlemci Tasarımı

30/07/2024

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:

Microprocessor

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:

  • AR (Address Register): Bellek adresini tutmak için kullanılan 16 bitlik kaydedici.
  • PC (Program Counter): Sıradaki komutun adresini gösteren 16 bitlik kaydedici.
  • DR (Data Register): İşlem yapılacak verinin geçici olarak saklanması için kullanılan 16 bitlik kaydedici.
  • IR (Instruction Register): MSB 8 biti komutu belirlemek, LSB 16 biti bellek adreslemek, işlem yapılacak veriyi barındırmak veya işlem yapılacak kaydediciyi seçmek amacıyla kullanılan, toplam 24 bitlik kaydedici.

İşlemler

Girişte bahsedildiği gibi mikroişlemci, genel amaçlı bir mikroişlemciden beklenen temel işlemlerin birçoğunu gerçekleştirir. Bunlar:

  • Mantıksal İşlemler: AND, OR, XOR, sağa ve sola kaydırma, sağa ve sola dairesel kaydırma ve sayının tersini alma işlemleri.
  • Aritmetik İşlemler: Toplama, eldeli toplama, çıkarma, borçlu çıkarma, çarpma, bölme, 1 artırma, 1 eksiltme ve karşılaştırma işlemleri.
  • Kontrol İşlemleri: JMP (Jump), JIZ (Jump If Zero), JNZ (Jump If Not Zero), JIA (Jump If Above), JIB (Jump If Below), JIE (Jump If Equal) olmak üzere program akışını kontrol eden işlemler ve mikroişlemcinin durdurulduğu HLT işlemi.
  • Trasnfer İşlemleri: Kaydediciler ve bellek arasında verilerin kopyalanabildiği ve kaydediciler ile bayrakların sıfırlanabildiği veri transfer işlemleri.

Adresleme Çeşitleri

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:

  • Direct Addressing: İşlem yapılacak verinin bulunduğu bellek adresinin komut içinde verildiği adresleme biçimi.
  • Indirect Addressing: İşlem yapılacak verinin bulunduğu bellek hücresinin adresini barındıran başka bir bellek hücresinin adresinin komut içinde verildiği adresleme biçimi.
  • Immediate Addressing: İşlem yapılacak verinin komut içinde verildiği adresleme biçimi.
  • Register Addressing: İşlem yapılacak veriyi barındıran genel amaçlı kaydedicinin komutta belirtildiği adresleme biçimi.
  • Register Indirect Addressing: İşlem yapılacak verinin bulunduğu bellek hücresinin adresini barındıran genel amaçlı kaydedicinin komutta belirtildiği adresleme biçimi.

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.

Programlama

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.

Karşılaştırma sonucu
DurumZeroCarry
A = B11
A < B10
A > B01

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:

Faktöriyel programı
Bellek Hücresi Komut Makine Kodu
0MOV AX, 5be0005
1CMP AX, 3a30003
2JIB 10d1000a
3MOV CX, AXc30008
4DEC CXdd0002
5MUL AX, CX870002
6DEC CXdd0002
7CMP CX, 1a50001
8JIE 10d4000a
9JMP 5c50005
10HLTe2ffff

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:

Dizi sıralama programı
Bellek Hücresi Komut Makine Kodu
0MOV AX, 120be0078
1MOV [100], AXb90064
2MOV AX, 100be0064
3MOV [101], AXb90065
4MOV AX, 125be007d
5MOV [102], AXb90066
6MOV AX, 90be005a
7MOV [103], AXb90067
8MOV AX, 80be0050
9MOV [104], AXb90068
10MOV AX, 100be0064
11MOV [50], AXb90032
12MOV AX, 104be0068
13MOV [51], AXb90033
14MOV BX, [50]b00032
15INC BXdc0001
16MOV CX, [50]b10032
17CLR EXd60004
18MOV [52], CXbb0034
19MOV AX, [[52]]aa0034
20CMP AX, [BX]a80001
21JIA 27ce001b
22CMP BX,[51]9f0033
23JIE 34d40022
24INC BXdc0001
25INC CXdc0002
26JMP 18c50012
27MOV [52], BXba0034
28MOV DX, [[52]]ad0034
29MOV [[52]], AXb40034
30MOV [52], CXbb0034
31MOV [[52]], DXb70034
32INC EXdc0004
33JMP 22c50016
34CMP EX, 0a70000
35JIA 14ce000e
36HLTe2ffff

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.