Öncelikle dedim indireyim şu SidReloc'u kullanayım, güzel sevdiğim müziklerden birini kullanayım.
Adresi şurası :
http://www.linusakesson.net/software/sidreloc/Program source formundaymış. Hani lan windows binary? Yok kendin yapıcan. Visual studio'da unix/linux vesaire c source'unu compile etmek ölümdür, ben yine de fantaziye girmeyeyim diye açtım Visual Studio'yu. Proje oluştur, ayarla, build et... 104 tane hata.. dahası da varmış, fazla olmasın diye göstermemiş sağolsun. stdint.h , err.h, getopt.h sıkıntılı.. biraz uğraşayım dedim header'larını hallettim derken kütüphane olmayan yerlerde de sıkıntı var. Eytere beah deyip baktım başka ne kullanabilirim diye. Hayır iki dakkada sanal makine altına linux kurarım ya da gider cygwin yahut mingw gibi şeylerle de compile ederim bunu da inat ettim. Eclipse'in c derleyicisi fena değil yazmış birileri bi yerde, şansımı deneyeyim dedim. Bir tek err.h için şamar attı bu, onu da bir yerlerden buldum sadece header imiş. Macro'lar falan var içinde. Derlendi meret. (İsteyen olursa windows executable var elimizde

)
Başladım denemeye,
sidreloc -p E0 Cybernoid.sid CybernoidE0.sid
Neither the source nor the destination relocation range may overlap with the zer
o-page.
Olmadı... Denediğim çoğu sid bunu dedi, derken Rob Hubbard abimize bakayım dedim o neler yapmış.
Spellbound'u denedim..
Aha,
Spellbound, Rob Hubbard, 1986 MAD/Mastertronic, $e000-$effe, 13 subtunes
Relocating from $e000-$ffff to $e000-$ffff
Lan dedim ben relocator'ı ne demeye kullanıyorum, zaten müzik tam da kernal'ın olduğu yere yükleniyor, orada çalışıyor. Bingo dedim kendi kendime. (Ancak bkz. yazının sonları)
Şimdi psid formatına göre Sid dosyası içinde $7E'den itibaren header bitip sid içeriği başlıyor.
CCS64 emülatörü altındaki KERNAL.ROM'u yedekledim, yeni bir kopya yaptım içini 00'lar ile doldurdum. Kernal rom'u tam 8k (8192 bayt). $7E'den itibaren sid'i kopyalayıp boşalttığım kernal.rom'un başına yapıştırdım.
Dosya içinde $1C00'a yani $FC00'a init ve play'i çağıracak kod parçamızı yazacağız.
Bu arada reset vektörü haricindeki vektörleri de RTI yani return from interrupt opcode'unu çalıştıran bir adrese göndermek istiyorum. Sonuçta emülatör altında çalışıyoruz, interrupt oluşursa rastgele bir yere gitmesin. Aslında ben interrupt'ları disable edersem bir yere gidemez ama neme lazım müzik açar falan.
Sırasıyla
$FFFA - NMI seris rutini
$FFFC - Cold reset rutini
$FFFE - Irq servis rutini
Vektörlerini değiştiriyorum. $FB00 adresine gelecek şekilde RTI instruction'ı koydum, vektörleri sırasıyla şu şekilde set ediyorum.
$FFFA : 00 FB 00 FC 00 FB
Sıra geldi, init ve play'i çağıracak rutinimize. Burada parçayı doğru hızda duyabilmek için sid'in play rutinini, benim elimdeki sid için $E012 adresinde, saniyede 50 defa çağırmam lazım. Yapacağım donanım içinde raster interrupt'ları kullanamayacağım için mecburen döngülerle bunu sağlamalıyım. c64 saniyede 1 milyon cycle çalıştırıyor dersek, bunu 50'ye bölüp her iki play çağrısı arasında kaç cycle beklemem gerektiğini bulabilirim.
1.000.000 / 50 = 20.000 cycle. Harcamak için epey bir cycle. Çok fazla doğru sonuç elde etmeye çalışmayacağım çünkü 20000 rakamı da doğru bir rakam değil zaten, biraz hızlı biraz yavaş olması çok da fark etmez benim için şu aşamada. Gerçek donanım üzerinde zaten doğru hızda çalışacak. (Zamanlamayı pic ile sağlayacağız)
Bu kadar çok cycle harcayabilmek için en az iki döngü kurmak lazım, ya da tek döngü içinde masraflı bir iş yapmak lazım.
Heyecan olsun masraflı bir iş yapalım diyorum. INC Absolute, DEC Absolute işlemleri 6 cycle yiyormuş. $FB02 adresini 7 defa arttırıp 7 defa azaltarak tek döngü içinde 7 * 12 = 84 cycle yerim. Bir adet register azaltma işlemi yapmam lazım örn. DEY, iki cycle o, bir tane karşılaştırma yapmam lazım BNE o da 3 cycle. (Yaptığımız dallanma aynı sayfa içine olacağı için, başka sayfaya olaydı 2 eklemek gerekecekti) Yani toplamda 84 + 2 + 3 = 89 cycle harcayacağız tek döngüde. 20000 / 89 = 224 defa dönersek aşağı yukarı 20000 cycle'ı yiyoruz.
Kodu yazdık, Assemble to memory deyip, kodun assemble edilmiş halini sid dosyası içinde başlangıç adresimiz olan $FC00 adresine kopyalayalım. Herhangi bir relokasyona gerek yok zira bu sid dosyasını emülatörün kernal.rom'u ile değiştireceğimiz için zaten E000'dan itibaren yüklenecek. Assemble ettiğimiz kod tam da başlangıcını verdiğimiz yerden başlayacak. Gerçi kodu hangi sayfaya yerleştirirsek yerleştirelim çalışır zira içinde iki adet sabit adres (init ve play adresleri) ve iki adet relatif adres geçiyor (loop'ların relatif adresleri) Kod tek sayfaya (256 byte) sığdığı için bu sayfa dışına dallanma imkanı yok, dolayısıyla hangi sayfaya yerleştirirsek yerleştirelim çalışır. Neyse bizim durumumuzda böyle bir şey söz konusu değil zaten.
Bir şeyleri eksik yaptığıma dair bir his var içimde, gelelim denemeye. Bakalım çalışacak mı.
Aha, harbiden de çalışmadı. Bir sıkıntı var.
Anlaşılan o ki biz kernal'i rom diye verip peşine ram gibi kullanmaya kalktık... Bizim kodumuz pek ram gibi kullanmasa da Rob Hubbard'ın müziği E000'dan başlamak üzere oranın ram olduğu varsayımıyla çalışıyor. Epey bir uğraştım 6510'un 01 adresindeki processor port register'ı ile ancak Hubbard'ın sid'ini çalıştırmayı başaramadım. Alakasız yerlere erişip yazdığım kodu da eziyor olabilir ya da ben denemelerim esnasında bir şeyi gözden kaçırdım.
Neyse Kernal Kernal diyerek gittim yattım,
Ertesi gün, bir hikmeti vardır bu kernal'in diye biraz daha inceledim. Temelde yaptığı şuymuş : Interrupt'ları disable et, stack pointer'ını ilk değerine getir, falan filan. Aynı kodu ben de koydum başa, yok yine başarılı olmadı.
Sonradan aklıma geldi, lan ben niye ille de kernal altından su yürütmeye çalışıyorum dedim. Bulsana bir tane $1000 adresli sid, reset vectörünü yiyince kopyala kendini müzik sonrasındaki bir adrese oradan çalışmaya devam et. $01 adresindeki processor port'u ile de iştigal etmeye gerek yok.
Açtım hvsc arşivini, girdim MUSICIANS klasörüne kafada Z harfi var, Zyron babadan bir şeyler bakayım bari dedim. Şansa çoğu da $1000'e yüklenen sid'lerden. Baktım beğendiğim bir tanesini seçtim. Kodu açılışta kendini $1000'den itibaren kopyalayacak şekle çevirdim, bunun init adresi $1000, play adresi $1003.
Evet ilk deneme CCS65'ü modifiye kernal ile açıyorum... başarısız... Tune indeksi yanlış gönderiyor olabilir miyim acaba? #$01'i #$00 yaptım. Tekrar denedim... Voila! Çalıştı! Azıcık olması gerekenden biraz yavaş. Demekki saniyenin 50'de birinde öyle hunharca harcayacağımız 20000 küsür cycle'ımız yokmuş. Kesin VIC girmiştir araya, neyse bizim donanımımızda VIC olmayacak, SID ile başbaşa kalacağız.
Kod şu bu arada
*=$FC00
LDX #$FF ; Default stack pointer'ı
SEI ; Interrupt'ları disable et. Açmıycaz bir daha
TXS ; X register'ını stack pointer'ına gönder
CLD ; Decimal modu kapat
LDA #$37 ; Processor port için default değer, c64 default açılışı gibi
STA $01 ; Processor port'a yaz.
LDA #$00 ; Belleği $E000'dan $1000'a
STA $FC ; transfer etmek için hazırlık
LDA #$E0 ; $FB/$FC = Kaynak
STA $FD ; $E000 adresini yazıyoruz bu iki ZP belleğe
LDA #$00 ; Belleği $E000'dan $1000'a
STA $FA ; transfer etmek için hazırlık
LDA #$10 ; $FA/$FB = Hedef
STA $FB ; $1000 adresini yazıyoruz bu iki ZP belleğe
LDX #$00 ; $20 sayfa kopyalayacağız. X'i sıfırlıyoruz.
TO:
LDY #$00 ; Her bir sayfa transferi için Y'yi kullanıyoruz.
; El mecbur, indexed indirect kullanacağız çünkü
TX:
LDA ($FC), Y ; $E000 + (X * $100) + Y'den A'e oku
STA ($FA), Y ; $1000 + (X * $100) + Y'ye yaz
INY ; Aynı sayfadaki bir sonraki adrese
BNE TX; Sayfa dolmadıysa devam
INC $FD ; Bir sayfa doldu, diğer sayfaya
INC $FB ; Bir sayfa doldu, diğer sayfaya
INX ; Sayfa sayısını takip ediyorduk, arttıralım.
CPX #$20 ; $20 sayfa oldu mu?
BNE TO; Olmadıysa yeni bir sayfa daha.
JMP $2C30; Kod $FC00'a derlensin diye ayarladık ama bu ne alaka?
THIS: ; Kodu kopyaladıktan sonra kernal rom'u altında execute etmekten
; kurtulup daha güvenli bir yerde çalışmaya devam etmek lazım.
; o bakımdan. Macro falan da yazılabilirdi, uğraşmak istemedim.
; Kod bir alt satıra dallanıyor burada ama başka bir bellek bölgesinde.
PLAY:
LDA #$00; Müzik init
JSR $1000 ;
OUTERLOOP:
JSR $1003 ; Play rutinini çağır
LDY #$E0 ; 224 defa dönelim demiştik, bu o.
LOOP:
INC $FB02 ; İşlemci ziyan etme kısmı.
INC $FB02
INC $FB02
INC $FB02
INC $FB02
INC $FB02
INC $FB02
DEC $FB02
DEC $FB02
DEC $FB02
DEC $FB02
DEC $FB02
DEC $FB02
DEC $FB02
DEY
BNE LOOP
JMP $2C35 ; Kod burada PLAY'e dallanıyor. RAM'a kopyalanmış yere.
2. ve 3. maddeler tamam... Ancak kernal ile ilgili sağlam dersler aldım. Bir kere sid psid bile olsa kernal ile ya da basic rom'u ile iştigal ediyorsa sıçtık. Hele burayı hem rom gibi hem de ram gibi kullanmaya kalkıyorsa katmerli sıçtık. Neyse bunları çok sonra düşünmek zorunda kalacağım inşallah.
Resim falan da koyacaktım ama işler yolunda gitmedi, kod 3-4 defa yazıldı falan, artık bir sonraki sefere.
Mesajın ekinde oluşturduğum kernal rom mevcut, emülatörünüzde deneyebilirsiniz.