Aman TL;DR olmasın, selamlar İlker
Kartuşla ilgili geliştirmeler konusunda uzun süredir bir şey yazmamışım.
Kartuşun çalışma mantığını tamamen değiştirecek bir API üstünde çalışıyorum.
Mevcutta kartuş üstünde çalışan menü programı biraz dumb. Sd card'ın dosya sisteminden epey bağımsız bir şekilde arduino'ya bağımlı bir hayat sürdürüyor. Sd kart içindeki dosya listesinin sayfalamasını bile arduino yapıyor.
Bu yapıyı değiştirerek C64 üstünde çalışan koda daha fazla sorumluluk verecek bir yapı yenisi.
Şu şekilde fonksiyonları kullanabilir oluyor c64 üstünde çalışan program.
IRQ_StartTalking ; Kartuşla iletişimi başlatır
IRQ_EndTalking ; Kartuşla iletişimi sonlandırır
IRQ_OpenFile ; Dosya açar
IRQ_CloseFile ; Dosya kapar
IRQ_DeleteFile ; Dosya siler
IRQ_ReadFile ; Dosyadan okuma yapar
IRQ_SeekFile ; Dosya üzerinde okunacak / yazılacak noktayı belirler (16 bit)
IRQ_LongSeekFile ; Dosya üzerinde okunacak / yazılacak noktayı belirler (32 bit)
IRQ_WriteFile ; Dosyaya yazar (her seferinde 32 byte)
IRQ_GetInfoForFile ; Dosya ile ilgili bilgileri getirir. FAT'teki DirEntry. (Dosya ile ilgili tarihler, boyutu vesaire)
IRQ_ReadDirectory ; Mevcut dizindeki klasör / dosya listesini getirir
IRQ_ChangeDirectory ; Klasör değiştirir
IRQ_DeleteDirectory ; Klasör siler
IRQ_CreateDirectory ; Klasör yaratır
IRQ_SeekEeprom ; Atmega328'in Eeprom'u üstünde yazılacak okunacak adresi belirler (1024 byte eprom var)
IRQ_ReadEeprom ; Eeprom üstünden okur
IRQ_WriteEeprom ; Eeprom üstüne yazar
Öncesinde kartuşa özel yazılmış programlar için IRQ interrupt'ları zinhar kullanım dışıydı. Yeni sistemde IRQ_StartTalking çağrılana dek istendiği gibi IRQ interrupt'larının kullanımı mümkün olacak. (Bunu muhtemelen wizofwor anlayacak en fazla
)
İlk etapta bu API'nin C64 tarafındaki kısmını yazdım, sonra döndüm bunların arduino tarafında karşılık gelen kodları yazdım. Tabii bunlar kısa zaman dilimi içinde olmuyor, arada bir günde bir saat uğraşıp falan.
Test & bugfix & development süreci gözümde büyüyordu. En sonunda aklıma c64 ile arduino arasındaki interaksiyonu test etmeden önce C64-> Arduino / Arduino -> C64 transfer kısmını seri bağlantı ile değiştirip PC'de test etme fikri geldi. Hali hazırda PC'den C64'e seri bağlantı üstünden program aktarımı için yazdığım programı biraz değiştirdim. Üstte bahsettiğim API kullanıcısı PC oldu. PC üstündeki yazdığım programla arduino içinde yazdığım rutinlerin işlevsel testini kolayca yapabildim.
İşin en zor kısmını sona bıraktım. Sırada C64'ten Arduino'ya komutları gönderen kısmın test & bugfix & development kısmı var.
Şu an sizlerin de elinde olan kartuşlarda C64 Arduino'ya bilgi göndermek istediğinde IRQ hattını belli zaman aralıklarıyla low yapıp, belli zaman aralıklarında da high yapıyor. Bunun modüle edilmesiyle bir byte gönderilebiliyor. 1 byte gönderimi ile de mevcut kartuş yazılımı işini idare ediyor. Biraz da kolaya kaçtığımdan ben bu işi Raster interrupt'ları ile yapmıştım. Mevcutta 1 byte'ın bu şekilde gönderimi aşağı yukarı 8-9 tarama gerektiriyor.
Daha sonra ara yükleme yapma / video oynatma gibi şeyleri geliştirirken yöntemi hızlandırmıştım.
Son durumda ise artık bu işin olabildiğince hızlı olması gerekiyor çünkü API'ye dosya yazma özelliği getirdim. Disk aktarımı vesaire gibi şeyler için yine de efektif olmasa da kartuşun burada belli bir hızı yakalaması gerekiyor. Kartuş için özel yazılmış bir oyun mesela save dosyasını yazmak isteyecektir. Mevcut yöntemlerle 1K - 2K gibi küçük boyutların transferi bile oldukça yavaş kalır.
Yeni yöntemde CIA timer interrupt'larını ve kartuşun daha önce exploit etmediğim bir özelliğini kullanıyorum.
CIA timer interrupt'larından bahsedeyim öncelikle. Timer interrupt'ları kullanılarak belli sayıda cycle bir sayaç gibi kullanılıp, sayaç sıfırlandığında bir interrupt oluşturulması sağlanabiliyor. Burada benim senaryom için dikkatli bakıldığında kullanılabilecek bir imkan var.
Öncelikle benim neye ihtiyacım var? Arduino'nun interrupt sinyalini dinleyip bunu anlamlandırabilmesine. 0 ya da 1, byte başlangıcı, bitişi vesaire şeklinde.
Interrupt oluştuktan sonra CPU'nun interrupt handler çalıştırmasına ihtiyacım var mı? İşte buna ihtiyacım yok aslında. CIA interrupt'larına özel bir durum değil, aynı şekilde VIC interrupt'ları için de aslında aynı şey söz konusu. CPU için verilen bir SEI komutu maskelenebilen interrupt'lara CPU'nun müdahalesini engelliyor. Ancak interrupt'ların maskelenmesi sadece cpu'yu ilgilendiren bir durum, çevre biriminin oluşturduğu interrupt'ın bir hardware karşılığı hala oluyor ve bu da benim işimi görüyor.
Amaçlarımdan biri de en alt seviyedeki API'nin mümkün oldukça kısa olması ve kaset buffer'ı gibi bir yerde saklanabilmesi. Interrupt handling hem interrupt'a giriş ve çıkıştan dolayı belli bir cycle tüketmeye zorluyor. Hem de bu şekilde yazılan kod busy waiting şeklindeki koda göre daha karmaşık oluyor. Komut / Parametre göndermenin hızlı olmasını istediğim için bu noktada veri gönderimi esnasında cpu'yu bloke etmiş olacağım.
Kartuşun exploit etmediğim bir özelliği demiştim. Bu da şu : Kartuşun çalışma prensibini bilenler hatırlayacaktır. 64K eprom'un içinde aslında aynı kodun 256 kopyası var. Arduino bu 256 parçadan gönderilmek istenen byte'a ait olan sayfayı/bank'ı seçerek C64'e hangi byte'ı göndereceğini ifade edebiliyor. 256 byte'lık bölge içinde bu bank numarasını ifade eden değer epey bir yerde geçiyor. Değişik eprom içeriklerini de düşünerek son byte'ı da bu şekilde bank numarası olacak şekilde rezerve etmiştim ben. Yani C64 tarafından kartuş rom'u aktifken $80FF / $81FF gibi giden adreslerde Arduino'nun seçtiği bank numarasını okumak mümkün.
Bu veri iletimi ve ardından arduino tarafında yapılan işlemin sonucunun C64'e bildirilmesi için kullanılabilecek bir kaç yöntem var.
1. Sıkı zamanlama ile iki tarafı senkron tutmak
2. NMI kullanmak
3. Başka bir yöntem kullanmak
Arduino tarafında yapılan bazı işlemlerin ne kadar süreceği önceden belli olamayacağı için ilk yöntem devre dışı. Bizim kartuş NMI üretebildiği için NMI kullanılabilir. Ancak bu da daha önce bahsettiğim kod boyutunun küçük olması hedefini biraz kastırıyor. Devreye $80FF'teki bank değerimiz giriyor.
1. Arduino komut kabulünden önce bank değerini 0 yapıyor.
2. C64 komutu argümanları ile gönderiyor
3. Arduino işi bittiğinde bank değerini hatalı işlemler için pozitif $0-$7F arası bir değere set ediyor. Başarılı işlemler için ise $80-FF arası bir değere.
Pek tabii C64 Arduino'nun meşguliyetini beklemesin bir şeyler yapsın denilirse tüm api'ye NMI kullanma özelliği de eklenebilir.
Şöyle de bir durum var: Şu an hala hızlı test yapabilmek için eski rutinleri Atmega328'in flash'ı üstünde tutuyorum ancak API rüştünü ispatladığında flash üstünde epey bir yer kazanacağım. Bu boşluğu da API'ye faydalı olabilecek yeni metodlar ekleyerek kullanmayı düşünüyorum. Faydalı olabileceğini düşündüğünüz rutinler varsa ekleyebilirim.
Şartlar şunlar
1. Implement edilmesi için ciddi flash gereksinimi olmayacak
2. Fazla ram kullanmayacak. Atmega 328'in ram'inden hali hazırda 300-400 byte bir yer boşta kalabiliyor.
Atmega328'in böyle bir API'de avantajı SD kart üstündeki yüksek hızda (C64'ün klasmanına göre) IO imkanı, yüksek depolama kapasitesi ve 1Mhz'e nazaran daha hızlı olması. (16 Mhz)
Tabii DMA gibi bir imkanımız yok, üstte bahsettiğim gibi C64->Arduino transfer kısmı yavaş, Arduino-> C64 transfer kısmı ise nispeten hızlı. API'ye eklenecek metod ciddi miktarda I/O ve Atmega328 üstünde cpu kullanımı yapmalı ki C64'e faydası dokunsun. Bir de tabii attığı taş ürküttüğü kurbağaya değecek bir metod olmalı. Sallıyorum 16 misli hızlı diye çarpma işlemini atmega'ya yaptırayım desek çarpılacak sayıları gönderme zamanında aslında 6502 üstünde çarpmak daha hızlı bile olabilir.
Benim bu anlamda aklıma gelen basit bir database özelliği. Binary tree kullanan basit bir database implementasyonu yapılabilir.
Kodları yazdıkça check-in'liyorum. Güncel kodlara aşağıdaki linkten ulaşabilirsiniz.
https://www.tepetaklak.com:8443/svn/Public/IRQHack64V2/