Merhabalar,
C64'ü DMA ile freeze/defreeze yapmakla ilgili kafa yormaktayım. Bazı fikirler oluştu, yorum ve öneri almak istiyorum. Ayrıca bir kaç ufak tefek konu var aklıma takılan sormak istediğim.
Öncelikle Final III, AR vb. kartuşların nasıl freeze yaptıklarını incelemek için Vice üzerinde monitor kullanarak yapabildiğim kadar inceleme yaptım, bazı fikirler oluştu. Ancak bu sistemler, DMA kullanmadıkları için genelde tümü NMI interrupt yaratarak, NMI vectorlerinin olduğu bölgeye de kendilerini mapleyerek freeze yazılımını çalıştırıyorlar. Freeze yazılımları da temelde registerlar (A,X,Y,SP), memory configurasyonu ($00,$01) gibi anlık önemli bilgileri kopyalarak işe başlıyor. Ayrıca, interrupt yaratılarak bu koda sıçrandığı için, 6510 zaten Processo Status Flags ve PC (Kodun interrupt sırasında çalışmak olduğu adres) bilgisini stack'e atmış oluyor. Bu bilgiler de stackten kolayca alınabiliyor. Bunun haricinde daha birçok detay var tabiki, timerların kontrolü, interrupt oluştuğu andaki raster satır no vb vb başka bilgiler de snapshot ediliyor.
Ben nette özellikle AR için bir kaç disassembled source bulabildim. Ama malesef freezer bölümü yoktu kaynak kodlarda. İlk olarak bu konuda birşeyler bilen varsa paylaşırlarsa sevinirim.
Gelelim bizim DMA Freeze olayına. Doberman'da orjinal tasarımda NMI hattını bağlamamıştım. Nasıl freeze yaparım diye düşünürken aklıma ilk gelen NMI hattını da bağlayarak, AR vb nasıl freeze yapıyorsa benzer bir yöntem kullanmaktı. Fakat bugün aklıma başka yöntem geldi.
Freeze edilecek program oyun vb zaten kendi içinde mutlaka bir şekilde interrupt kullanıyordur diye düşündüm. Şimdilik IRQ kullanmayan programları hedef listemizden çıkardığımızı varsayalım.
1. Herhangi bir anda DMA başlatılacak, $FFFE/$FFFF IRQ Vectörleri DMA ile okunacak. Burada iki olasılık var. Oyun ya KERNAL'ı kapatmıştır, yada kapatmamıştır.
a) Eğer kapatmış ise, bu vectorlerin işaret ettiği adres RAM olmalı. Bu adresi not et.
b) Eğer KERNAL açık ise IRQ RAM'de $314/$315'in işaret ettiği yerde başlayacak. DMA ile bu adresi oku ve not et.
NOT: Burada bir açıklama. DMA ile KERNAL açık mı değil mi nasıl anlayacağız? Eğer $FFFE/$FFFF vectörler, ROM'daki vectorlerden farklı ise kesin KERNAL kapalı. Eğer vectörler aynı ise o zaman $FF48 adresini JMP ($314) koduna kadar olan kısmı kontrol et. Eğer ROM ile aynı ise ya KERNAL açık yada kapalı olsa bile orjinal ROM la aynı işi yapıyor ve $314 de gösterilen adrese sıçranıyor demek...
2. Not ettiğin adresten itibaren küçük bir bölümü DMA ile kendi hafızana yedekle, çünkü birazdan o bölgeye kod enjekte edeceğiz. Bu arada bu not ettiğimiz adres eminiz ki RAM'de bir yerlerde ve ilk interrupt geldiğinde çalışacak.
3. Not ettiğin adresten itibaren çok basit bir kodu DMA ile yerleştir. Bu kod 0,1 adreslerinin durumunu, A,X,Y,SP,PC,Flags, D012 Raster line no vb yi, gene DMA ile okunabilir bir RAM bölgesine yazsın. Örneğin hemen bu kodun bittiği yere.
4. DMA'yı serbest bırak ve beklemeye başla...

Şimdi bu noktadan sonra oyunun içindeki ilk orjinal IRQ geldiği anda oyunun kendi interrupt handler'i değil, bizim yerleştirdiğimiz trojan kod çalışacak. Normalde DMA ile okuyamadığımız A,X,Y,SP,PC,Flags bilgilerini RAM'de DMA ile okunabilir bir yere yazacak. Bizde bunları bir güzel DMA ile alacağız. Sonrası kolay. Gene trojan kod bizim için 01 adresi ile oynayarak tüm RAM'i açacak. Gene DMA ile RAM'i alacağız. Sonra gene trojan kod IO bölgesi ve Color RAM'i açacak. Gene DMA ile D000-DFFF arasını yedekleyeceğiz.
De-Freeze de gene teorik olarak kolay. RAM ve IO'yu yerli yerine koy. Örneğin $0400 e basit bir defreeze kodu at. Bu kod da A,X,Y,SP,Flags vb yi restore etsin, D012'yi beklemeye başlasın. istenilen raster satırına gelinince bir basit JMP ile orjinal IRQ kodunun adresine sıçra. (Madde 1 de not ettiğimiz adres)
Soru1: Sizce bu seneryo da gözümden kaçan birşeyler var mı? İlk fırsatta denemeyi düşünüyorum.
Soru2: C64 de aslında hiçbir şekilde tam olarak snapshot almak mümkün değil. Çünkü bazı registerlar okuma ve yazma durumlarında farklı işlevlere sahip. Özellikle CIA lerde bu durum bolca var. Bir başka örnek VIC. D012 okunduğu zaman o anki raster satırını veriyor. Ama buraya yazdığınız zaman Raster Interrupt'ın hangi satırda olmasını istediğinizi belirtiyorsunuz. Yani aslında kurulmuş olan raster interrupt satır noyu okumanın yolu yok. (Ancak raster interrupt oluşmasını bekleyip D012 yi okumak falan olabilir.) AR vb de dahil olmak üzere alınan snaphotlarda bazen müziğin belli bir süre gelmediği vb söyleniyor. Bu gibi durumlara karşı diğer freezer nasıl çözümler üretiyor, bilgisi yada dökümanı olan var mı?
Son olarak NMI Freeze yöntemine göre DMA Freeze'in benim gördüğüm bir kaç avantajını belirteceğim.
- Aslında oyunun içinde varolmayan bir interrupt yaratılmadığı için sanki daha doğal ve sağlıklı.

(Ne demekse ben bile güldüm yazarken.)
- NMI Freeze karşı alınan önlemlerden etkilenmemiş olacak. (NMI IRQ Disable etme yöntemi var. Disable edilmiş olsa bile eğer oyun içinde kendi kullandığı bir interrupt varsa freeze edilebilir oluyor teorik olarak.)
Fikir ve yorumlarınızı paylaşırsanız sevinirim...