Gönderen Konu: Skate'in ZX Spectrum Maceraları - Vol 1  (Okunma sayısı 1708 defa)

0 Üye ve 2 Ziyaretçi konuyu incelemekte.

Çevrimdışı Skate

  • RAAT
  • Retro Meraklısı
  • *
  • İleti: 199
Skate'in ZX Spectrum Maceraları - Vol 1
« : 03 Mayıs 2022, 10:28:31 »
@Alcofribas, @hades'in açmış olduğu Z80 Makine diline meraklı olan? başlığı altında yaptığım paylaşımları başka bir başlığa taşımayı önermişti. Ancak henüz hazır olmadığım gerekçesiyle reddetmiştim. O başlıkta paylaştığım çalışmam da hem sorunlu, hem de optimize edilmemiş bir versiyon idi. Şimdi bir nebze hazırım.

Hollywood filmlerinin sıkça kullandığı filmi ortadan başlatıp, arada geçmişe gidip, en son başladığı noktayı yakalayıp devam etme şablonunu kullanmaya karar verdim. Çünkü an itibariyle filmin ortasındayım.

Ulaştığım noktayı hızlıca görmek isterseniz aşağıdaki linki kullanabilirsiniz.

http://speccy.akaydin.com/

Yeşil border alanı 256 noktayı çizme ve silme için harcanan CPU'yu temsil ediyor. Yaklaşık %50'lik bir CPU kullanımı var. Yani CPU'nun yarısı boşta. Bu rutin üzerinden ilerleyecek olursam şu anki haliyle sinüse bağlı hareket eden 50 FPS hızında 512 nokta basılabilir gibi duruyor. Tabii hatırlatıyorum, bu filmin ortası.

Ekte ekran görüntüsü, TAP dosyası ve kaynak kodu paylaşıyorum. Şu anda yorgun argın şehirlerarası yolculuktan gelmiş olduğum için sadece materyalleri paylaşıyorum. Biraz dinlendikten sonra fırsat buldukça ZX Spectrumla karşılaştığımız bir cafede ilk muzip bakışmamızdan şu ana kadar geçen süreci yazıya dökeceğim.

Çevrimdışı Skate

  • RAAT
  • Retro Meraklısı
  • *
  • İleti: 199
Ynt: Skate'in ZX Spectrum Maceraları - Vol 1
« Yanıtla #1 : 03 Mayıs 2022, 18:30:41 »
Amacım sizlerle Z80 maceramda yaşadıklarımı hızlıca paylaşmak olduğu için oldukça özet biçimde ilerlemeye çalışacağım. Yani ilk Z80 ile ne zaman tanıştım, ne zaman öğrenmeye başladım, hangi yollardan geçtim, bunları paylaşmak istiyorum.

I. Z80 İle İlk Tanışma
Parmak emmeden hallice yaşlardan beri 6502 tabanlı bilgisayarlar kullanan ve zamanla 6502/6510 Assembly öğrenmeye başlamış biri olarak Z80 uzun yıllar hiç hayatıma girmedi. 2000'li yıllara kadar Z80 benim sadece bir işlemci olarak varlığını bildiğim ve arada meraktan açıp dokümantasyondan opcodelarına ve registerlarına bakıp, "amma da çok registerı varmış, hem de 16-bit registerları bile var" dediğim, bundan bir gıdım fazlasını bilmediğim bir işlemciydi. 7DX Partileri sırasında Gameboy sık sık gündeme geliyordu, hatta Gameboy ile canlı müzik yapan bile çıkmıştı. O zaman yaptığımız sohbetlerden birinde 90'lı yıllarda oynamaktan çok keyif aldığım Gameboy'un içinde de Z80 olduğunu, ancak çok daha limitli, bazı özellikleri eksik bir Z80 olduğunu öğrenmiştim. Yani ne ZX Spectrum, ne Amstrad CPC, ilk merakı yaratan aslında Gameboy olmuştu. Tabii ki Gameboy henüz kuyruğun sonuna geçeceğinden bihaberdi.

Yıl 2011'e geldiğinde Amstrad CPC scene'inde Batman Group bombayı patlattı, "Batman Forever". Bana hediye olarak gelmiş bir Amstrad CPC'ye sahiptim ama hakkını veremiyordum, kenarda yatıyordu öylece. Yani elimde mevcut bulunan tek Z80 tabanlı platformda adamlar hatırı sayılır demo efektleri içeren bir demo patlattılar. Tabii hemen çevremdeki @Alcofribas, @Ref, @matahari, @ssg gibi Z80 tabalı platformlara aşık insanlardan da tepkiler gelmeye başladı. @Alcofribas her RAAT partisinde parti boyunca sık sık Batman Forever izleterek bizleri Batman'den soğuturken, Amstrad CPC'ye ve kendi adıma Z80'e ilgimizi artırmaya başladı, bizi alttan alttan işledi.

@matahari ve @ssg Amstrad CPC'de 256 byte ürünler de yayınlamışlardı o dönemlerde, @Ref ve @hades de 7DXlerde ZX Spectrum intro ve 256 byte ürünlerine imza atmışlardı. En son bir RAAT partisinde canıma tak etti... Hazır @matahari'yi de yakalamışken kendisinden biraz bize Z80 göstermesini istedim. Yanlış hatırlamıyorsam @Fero bir Z80 kodu ile uğraşıyordu. Sağ olsun @matahari gelip bize hızlandırılmış bir Z80 kursu verdi. O gün ile ilgili @Alcofribas'ın her zamanki misafirperverliği ve çok eğlendiğim haricindeki bir çok detay aklımdan uçup gitmiş ancak @matahari'nin ağzından çıkan teknik kelimelerden tek bir tanesinde bile kayıp olmadı, hepsini belleğime kazıdım.

2018'e geldiğimizde hala Z80'de tek satır kod yazmış değildim. @Ref o sene RAAT partisinde "an Unexpected Blizzard" isimli bir ZX Spectrum ürünü yayınladı, sadece yayınlamakla da kalmadı, üzerine yarı sunum yarı sohbet havasında bir de konuşma yaptı. Ben sonrasında konuşmanın uzunluğu ile ilgili kendisine çok takıldım, şakalar yaptım. Ama elbette ki yine benim için teknik açıdan çok faydalı oldu ve belleğime kazınan bilgiler arasına girdiler.

2019 Ocak Ayı'na geldiğimizde ilk kez kolları Z80 programlama için sıvadım. Amstrad CPC'nin donanım özellikleri ve Z80'i birlikte öğrenmeye karar verdim. Sırasıyla;

  • Amstrad CPC'nin grafik ekranı özlellikleri
  • Kesme rutinleri ve ekran başlangıcında tarama yakalatma
  • Müzik çaldırma

gibi şeyleri öğrendim. Jaruzi efekti diye tabir edebileceğim, sağdan soldan şeritler halinde çizilerek ekrana grafik basma gibi bir şeyler yaptıktan sonra dedim en temizi Amstrad CPC'de bir grup kuralım, yerli bir ürün çıkaralım CPC'ye. Türkiye'de Amstrad CPC ile ilgilenen tanıdığım herkes ile görüştüm, gruba davet ettim, sağ olsun herkes destek vereceğini de söyledi. Ancak bir süre benden pek bir aktivite çıkmayınca şimdilik o proje uykuda. Amaç aslında @Alcofribas'a RAAT partisinde bir Amstrad CPC ürünüyle sürpriz yapmaktı. Ama artık sürprizlik bir durumu kalmadığı ve günün birinde yapacak olursak yine de sürpriz olacağı için açıklamakta sakınca duymuyorum. Araya giren pandemi iyiden iyiye planlarımızı alt üst etti.

Amstrad CPC'de Z80 ile bir şeyler yaparken işlemciye hiç hakim değildim. Yani hep istediğimi yapmanın bir yolunu buluyordum ama hala kendi yazdığım kodlar bana yabancı görünüyordu. Benzerini geçmişte Motorola 68k, x86 ve ARM'da da yaşamıştım. 6502'ye olan aşırı ilgim diğer platformlara alışmamı güçleştiriyordu. Ama Z80 yapı olarak bana çok ters gelmemişti. Biliyordum ki zamanla hepsi oturacak. İşlerim de yoğunlaşınca o oturma süresini doğal olarak sağlamış oldum, bir kaç sene Z80 ile hiç ilgilenmedim. Ta ki geçtiğimiz ay olan Nisan 2022'nin son 10 gününe kadar.

Çevrimdışı Skate

  • RAAT
  • Retro Meraklısı
  • *
  • İleti: 199
Ynt: Skate'in ZX Spectrum Maceraları - Vol 1
« Yanıtla #2 : 03 Mayıs 2022, 18:52:28 »
II. ZX Spectrum'un 40. Yılı Şerefine

Bu forumda yakın zamanda yayınlanan, teknik olduğu kadar edebi olarak da yüksek değer içeren @matahari'nin yazıları tahmin ediyorum okuyan herkese ilham vermiştir. Beni zaten zor tutuyorlardı, o yüzden dedim artık bu işin vakti geldi. Ama yine de son bir kıvılcım iyi gider dedim. Bir baktım @hades başlık açmış.

Z80 Makine diline meraklı olan?

Hani ilkokuldaki sorunun cevabını bilmenin verdiği heyecanla parmak kaldırmakla yetinmeyip bir de parmağı sağa sola sallayan öğrenci vardır ya, o hislere büründüm. @hades yazdığı bazı rutinleri ve aynı işi farklı şekillerde yapma yöntemlerini paylaşıyordu. Doğrudan kaynak kodlar uçuşuyordu paylaşımlarında. Benim böyle başlamamam lazım dedim. Çünkü paylaşacağım kodlar birilerine kötü örnek olabilirdi. O yüzden bir şeyler yapıp, gelişme aşamalarımı paylaşıp, ilk hedefime ulaştığımda ise kötüden iyiye doğru sırasıyla paylaşmayı uygun gördüm. ZX Spectrum'un grafik ekranının byte dizilimi biraz farklı olduğu için ilk olarak ona hakim olmamı sağlayacak bir noktadan başlamaya ve bir plot rutini yazmaya karar verdim. Her ne kadar @Ref belki kendi kar yağdırma rutini ile kapışmaya çalıştığımı düşünse de amacım ZX Spectrum platformunda "ekrana nokta basmayı öğrenmek"ten ibaretti. Çünkü ekrana bir soft sprite basacaksam, polygon çizdireceksem önce nokta koyabiliyor olmam lazım. Bir diğer hedef ise ekrana grafik, logo basabilmekti.

Şu sıralar Commodore 64'de demo geliştirme araçlarımı tamamen Python dilinden scriptlerle yazmaya başladım. Aslında kullandığım Kick Assembler o kadar güzel özellikler içeriyor ki, Kick Assembler içinde Java dili yapısında bir scripting desteği ile dışarıdan hiç bir ek geliştirme aracına ihtiyaç duymadan işi çözebildiğim oluyor. Ama iş çapraz geliştirmede grafikleri hedef platforma uygun hale getiren converter araçlar yazmaya gelince, derleme sürelerini de çok olumsuz etkileyeceği için Python kullanmayı tercih ediyorum. Sadece Commodore 64 için de değil. Atari 800 XL, Commander X16, hepsinde araç zincirim bu şekilde kurulmuş durumda. ZX Spectrum da bu konuda bir istisna teşkil etmedi. Hemen ihtiyacımı görecek kadarıyla bir script yazıp yayınladım. Zamanla bu scripti geliştireceğim. Şimdilik 8 parlak renge göre ayarlanmış durumda. Gelişmeleri aşağıdaki github linkinden takip edebilirsiniz.

https://github.com/c64skate/zx-spectrum-bitmap-converter

Bu script ile artık PNG olarak oluşturduğum speccy grafiklerini ZX Spectrum formatına çevirip görüntüleyebilecektim. Ama hala geliştirme ortamım hazır değildi. Sıradaki işler IDE, derleyici, debugger seçimi, özetle geliştirme araç zincirinin belirlenmesi ve kurulması.

Çevrimdışı ssg

  • RAAT
  • Retromanik
  • *
  • İleti: 19
Ynt: Skate'in ZX Spectrum Maceraları - Vol 1
« Yanıtla #3 : 04 Mayıs 2022, 00:10:46 »
@Skate eline sağlık, @matahari'den sonra senin de bu konularda yazmana çok sevindim. ayrıca zirvede olduğun C64'teki konfor alanından çıkmana da şapka çıkartıyorum.

Çevrimdışı Skate

  • RAAT
  • Retro Meraklısı
  • *
  • İleti: 199
Ynt: Skate'in ZX Spectrum Maceraları - Vol 1
« Yanıtla #4 : 04 Mayıs 2022, 03:29:47 »
@Skate eline sağlık, @matahari'den sonra senin de bu konularda yazmana çok sevindim. ayrıca zirvede olduğun C64'teki konfor alanından çıkmana da şapka çıkartıyorum.

Çok teşekkürler Sedat. Yazıya devam ediyorum şu anda. Tecrübeli z80ciler olarak sizlerin okurken eğleneceğinizi tahmin ediyorum. Kulağımı epey tersten göstermişimdir. Ama keyifli olan da oydu, dediğin gibi konfor alanından çıkınca o ilk heyecanlar geri geliyor. 40 yıldır herkesin bildiği bir şeyi yeniden keşfedip sevinmek falan. :)

Çevrimdışı Skate

  • RAAT
  • Retro Meraklısı
  • *
  • İleti: 199
Ynt: Skate'in ZX Spectrum Maceraları - Vol 1
« Yanıtla #5 : 04 Mayıs 2022, 04:19:02 »
III. Çapraz Geliştirme Araçları

Bu kısım, geliştirmeye kazandırdığı rahatlık açısından önemli kısımlardan biri. Ama yıllarca çok limitli koşullarla, yeri geldiğinde derleyicinin kendi kodları ve hafızadaki kaynak kodların üzerine program derlemeye alışmış kişiler olarak hızlı karar almayı tercih ettim. Sonuç olarak yazdığım kodu derlememi sağlayacak herhangi bir derleyici temelde yeterliydi. Tabii bunu söylerken aklımda hep Commodore 64'de son senelerde hayranlıkla kullandığım Kick Assembler çıtayı yükseltiyordu. Kick Assembler'ın sağladığı olanaklar, bugüne kadar bazen saatlerimi, hatta günlerimi alan çeşitli işleri her derlemede parametrik olarak yapabilmemi sağlayan scripting özellikleri ve platforma özel olarak geliştirilmiş grafik modları, ses dosyaları şablonlarını tanıma gibi yetenekleri seçimi yaparken biraz daha iyi bir şeyler aramama neden oldu.

Commodore 64 için bir çok Visual Studio Code plugin'i olduğunu biliyor, gelişimlerini takip ediyorum. Ancak oturmuş, sorun yaşamadığım geliştirme platformumdan dolayı Visual Studio Code ve benzeri IDE alternatiflerini kullanmayı tercih etmemiştim. Şimdi yeni bir platform ile temiz bir sayfa açarken Visual Studio Code pluginleri tereddütsüz ilk tercihim oldu. Ama çok fazla sayıda olduklarını da tahmin ettim. Bu durumda iki şansım vardı, ya bir bilene danışacaktım ya da Google'a. Sonra Commodore 64'de kendi kullandığım geliştirme ortamımın o kadar da modern olmadığı geldi aklıma. Dedim iyisi mi ben bir bilenden önce Google Amca'ya sorayım, bakarsın 2022'de yeni bir platform / plugin çıkmıştır, ilk ben keşfederim. Yani böyle biraz da kendimi kandırarak hızlı bir araştırma yaptım. Ulaştığım Visual Studio Code içeren ilk referans sayfası şu oldu.

Z80 Development Toolchain

Bu sayfadan sadece isimleri seçtim.

  • Visual Studio Code plugini: Z80 Assembly by Imanolea
  • Visual Studio Code plugini: DeZog by Maziac
  • ZEsarUX Emulatörü
  • SjASMPlus Derleyicisi

Zaten hali hazırda kullandığım ZX Spectrum emülatörlerim vardı. Bunları belki geliştirme zincirinde uyumlulukları vardır diye seçtim. Ancak kurduğum versiyonlar sitede önerilenlerden çok daha ileri versiyonlardı. Anlaşılan gelişme sürecinde bazı uyumsuzluklar yaşanmış. Örneğin ZEsarUX emülatörünün remote debugger özelliği çalışsa da derlediğimde emülatörün çökmesine neden oluyordu. Ben de çözüm olarak hızla benzer pluginleri kullanan kod örnekleri aradım ve bir tane buldum. Bulduğum örnek kullandığım pluginlerden DeZog'u yazan Maziac'a aitti.

GitHub - maziac / z80-sample-program

Bu repodaki .vscode içerisinde yer alan dosyalar çok yol gösterici oldu. Böylece Asm Code Lens, Z80 Instruction Set, SNA File Viewer gibi diğer bazı pluginleri de öğrenmiş oldum. Gerçi bunların bir kısmını gerekli görmeyip kullanmadım. Yine de bu çapraz geliştirme platformumu kurmak adına son durağım oldu. Bu repoyu inceledikten 20 dakika sonra artık istediğim kodu yazıp derleyebiliyordum.

Burada anahtar kelimeler Visual Studio Code, DeZog (by Maziac), Z80 Assembly (by Imanolea), SjASMPlus. Bunlar yeterli ama işin lüksüne de kaçmanız mümkün. Ekte son durumda kullandığım geliştirme ortamından iki ekran görüntüsü örneği paylaşıyorum.

Böylece artık kod yazmaya hazır hale gelmiş oldum. Sanırım geliştirme platformunu ayağa kaldırmam bu gönderiyi hazırlamamdan kısa sürdü. Bu nedenle kesinlikle "en mükemmel, en modern çözüm" diye düşünmeyin. Ancak bir kaç eksiği olmasıyla birlikte işimi gayet iyi görüyor. Eksiklerinden de kısaca bahsedeyim.

  • Düzgün bir hafıza görüntüleyicisi yok. Konsoldan komutlar yazarak ekrana getirebildiğim hafıza blokları oldu ama günümüzün modern hafıza görüntüleyici örneklerine kıyasla biraz zayıf gibi. Tabii bu başka bir Visual Studio Code plugini ile de çözülüyor olabilir. Şimdilik basit işlerle uğraştığım için bu konuyu sonraya bıraktım.
  • SjASMPlus'ın desteklediği scripting dili LUA. İlk başta LUA'yı görünce sevinmiştim. Bir süre CoronaSDK ile kullanmıştım, sevmiştim de. Ancak sanırım burada kullanılan versiyon bayağı eski ya da eksik özelliklere sahip. Çok temel işlemleri bile yapamıyor. Kaynak kodumun sonunda şöyle bir kısım göreceksiniz.

Kod: [Seç]
; LUA common definitions and functions
    LUA ALLPASS
        OR, XOR, AND = 1, 3, 4

        function bitoper(a, b, oper)
            local r, m, s = 0, 2^31
            repeat
                s,a,b = a+b+m, a%m, b%m
                r,m = r + m*oper%(s-a-b), m/2
            until m < 1
            return r
        end
    ENDLUA

Bu kodu ben yazmadım, internetten olduğu gibi bulup aldım. Bit tabanlı işlemler için bunu kullandım. AND, OR, XOR içermiyormuş LUA, yani en azından SjASMPlus ile birlikte gelen versiyonu. LUA'yı araştırdığımda yeni versiyonda bu tarz desteklerin geldiğini gördüm. Ama şimdilik hiçbirini çalıştıramadım SjASMPlus bünyesinde. Geçici olarak böyle ucube fonksiyonlar kullanıyorum. Tabii proje küçük olunca derleme sürelerini etkilemiyor ama fonksiyonu gördükçe gözüm seğirmiyor dersem yalan söylemiş olurum.

Bu anlattığım dez avantajların henüz benim bazı özellikleri çözememiş olmamdan kaynaklanabileceğini hatırlatmak isterim. Zaman zaman "peeh, becerememişler" dedikten kısa süre sonra "peeh, meğerse ben becerememişim" demişliğim vardır. Bu yüzden şimdilik bunları potansiyel dezavantajlar olarak nitelendiriyorum. Hatta ilerleyen dönemde geliştiricileri ile irtibata geçip, bana eksik gelen kısımları tamamlamaları için destek de olabilirim. Nitekim Kick Assembler'a 65c02 işlemci desteğini bu şekilde ekletmiş ve Commander X16 için daha konforlu, ekstra opcodelar için yazdığım gereksiz makroları çöpe atarak devam etmiştim. Bu geliştirici araçlarında bir iki eksikten dolayı projeden hemen vazgeçmemek gerekiyor. Ancak öksüz kalmış projelerde mecburen alternatif arayışına gitmek durumunda kalıyoruz elbette ki.

Çevrimdışı Skate

  • RAAT
  • Retro Meraklısı
  • *
  • İleti: 199
Ynt: Skate'in ZX Spectrum Maceraları - Vol 1
« Yanıtla #6 : 04 Mayıs 2022, 05:00:02 »
IV. ZX Spectrum / Z80 Kullanarak İlk Efekti Programlama

"I. Z80 İle İlk Tanışma" bölümünde zaten Z80 ile flörtleştiğimden bahsetmiştim. Yani belli bir temel ancak oturmamış bilgim mevcuttu. En büyük sorun da 6502 ile mimari farklılıklarından kaynaklanıyordu. Örneğin bir değeri bir tablodan index ile okumak ve yazmak 6502'de çok kolay yapılan bir şeydir.

Kod: [Seç]
ldx $9000,y
lda $9100,x

Bu kod $9000'de yer alan tabloda y kadar ilerideki değeri x registerına atar ve ikinci satırda $9100'deki tabloda az önce okuduğumuz x değeri kadar ileriden okunan değer accumulatore aktarılır. Bunun z80'de bu kadar kolay bir karşılığı ne yazık ki bulunmuyor. Aynı işi yapan bir z80 örneği. x / y registerları olmadığı için örneğin b registerını kullanacağım.

Kod: [Seç]
; ldx $9000,y karşılığı, y yerine b kullanıp, sonucu da x yerine yine b'ye alacağız
ld hl, $9000
ld a, l
add a, b
ld l, a
ld b, (hl) ; sonucu yine b'ye alıyoruz, x yerine de b'yi kullanacağız

; lda $9100,x karşılığı, x yerine b kullanıyoruz
ld hl, $9100
ld a, l
add a, b
ld l, a
ld a, (hl)

Tabii bu optimize edilebilecek bir örnek, mesela;

Kod: [Seç]
ld hl, $9000
ld a, l
add a, b
ld l, a
ld l, (hl) ; sonucu l'ye alıyoruz
ld h, $91
ld a, (hl)

ya da tabloların $100'lük alanlara oturduğundan yola çıkarak daha da kısa biçimde;

Kod: [Seç]
ld h, $90
ld l, b
ld l, (hl)
ld h, $91
ld a, (hl)

Bu başka şekillerde de yazılabilir, işe diğer registerlar da girebilir, ix, iy de kullanılabilir, kendini modifiye eden (self modifying) kod da kullanılabilir. Ama öncelikle yaşadığım kaosu anlamanız için bu örneği paylaştım. 6502 sadece 3 adet 8 bit registerı olan (a, x, y) oldukça primitif bir işlemci. Yani z80'in 16 bit registerları, blok halinde hafıza kopyalamak için getirdiği özellikler, shadow registerlar incelendiğinde şu kodu yazmanın bu kadar uzun tutabileceğini düşünmüyor insan. Bunun bu şekilde uzun olmasındaki en büyük faktörlerden biri 8 bit ve 16 bit işlemlerin ayrı tutulmuş olması ve her registerın accumulator kadar yetenekli olmayışı. Örneğin;

Kod: [Seç]
ld hl, $9000
add hl, b

diyebilsek "ld a, l", "add a, b", "ld l, a" bu şekilde üç opcode harcamamız gerekmeyecek. Bunu diyemiyoruz çünkü "add" opcode'u ya 8 bit ile 8 bit ya da 16 bit ile 16 bit toplayabiliyor. Hay hay, zaten tablomuz $100'lük alanlara oturtulmuş durumda, bir byte ile topladığımızda low byte taşma yapmayacak. O zaman sadece low byte'ını toplayalım gitsin.

Kod: [Seç]
ld hl, $9000
add l, b

Voila! Değil ne yazık ki çünkü add 8 bit registerlarda sadece accumulator ile toplama yapabiliyor. Yani bu durumda hl'e b'nin değerini eklemek için iki şansımız kalıyor geriye. Birincisi yukarıda gördüğünüz gibi l'yi a'ya aktar, a ile topla, l'ye geri aktar yöntemi. Ya da alternatif olarak şunu kullanabiliriz.

Kod: [Seç]
ld hl, $9000
add hl, bc

bc yerine de kullanmamız da mümkün. Ama işte bunları 16 bit olarak kullandığımızda high bytelarının da boş olduğundan emin olmamız ya da boşaltmamız lazım. Bu nedenle ilk yazdığım versiyonlarda sık sık 16 bit kullanmaya çalışma, high bytelarında gelen değerleri de hesaba katarak tablolar üretme gibi boşa kürek çekip durdum. Bu konudaki mücadelemi adım adım paylaşıyor olacağım.

Çevrimdışı Skate

  • RAAT
  • Retro Meraklısı
  • *
  • İleti: 199
Ynt: Skate'in ZX Spectrum Maceraları - Vol 1
« Yanıtla #7 : 04 Mayıs 2022, 05:37:18 »
Bir yerlerden başlamak lazım...

Öncelikle ZX Spectrum'un hafıza yerleşiminin biraz farklı olmasından ötürü ilk yapmam gereken işi biliyordum. Y ekseninde tüm satır başlarına denk gelen hafıza adreslerini sıralı biçimde tablolamak. Bunun için JavaScript'den kısa bir tablo oluşturucu yazdım. Bunu yazma nedenim henüz SjASMPlus'ın özelliklerine hakim olmamam ve hızlıca ekranda pikselleri görmek istememdi.

Yazdığım scripti aşağıdaki URL'den görebilirsiniz.

https://jsfiddle.net/90L6heub/

Bu tabloyu da kullanarak yazdığım ilk örnek şu şekilde. Sonucu da eklerde bulunuyor. Ayrıca comment out ettiğim kodlar, derlenmesi için gereken diğer kısımlar da ekteki main_01.asm dosyasında mevcut.

Kod: [Seç]
    ORG $8000

main:
    ; Disable interrupts
    di
    ld sp,stackTop

    ; fill color memory
    ld hl,$5800

    ld e,l
    ld d,h
    inc de

    ld (hl),$07
    ld bc,$300
    ldir

    ; put pixel
    ld b, $aa
    ld a, 0
    ld de, readHere+1

loop1:
    ld (de), a
readHere:
    ld hl, (lines)
    ld (hl), b
    ld i, a
    ld a, 255
    xor b
    ld b, a
    ld a, i
    add a, 2
    cp a, 64*2
    jr C, loop1

loop2:
    halt
    jr loop2

    ALIGN $100
lines:
    defw $4000, $4100, $4200, $4300, $4400, $4500, $4600, $4700
    defw $4020, $4120, $4220, $4320, $4420, $4520, $4620, $4720
    defw $4040, $4140, $4240, $4340, $4440, $4540, $4640, $4740
    defw $4060, $4160, $4260, $4360, $4460, $4560, $4660, $4760
    defw $4080, $4180, $4280, $4380, $4480, $4580, $4680, $4780
    defw $40a0, $41a0, $42a0, $43a0, $44a0, $45a0, $46a0, $47a0
    defw $40c0, $41c0, $42c0, $43c0, $44c0, $45c0, $46c0, $47c0
    defw $40e0, $41e0, $42e0, $43e0, $44e0, $45e0, $46e0, $47e0
    defw $4800, $4900, $4a00, $4b00, $4c00, $4d00, $4e00, $4f00
    defw $4820, $4920, $4a20, $4b20, $4c20, $4d20, $4e20, $4f20
    defw $4840, $4940, $4a40, $4b40, $4c40, $4d40, $4e40, $4f40
    defw $4860, $4960, $4a60, $4b60, $4c60, $4d60, $4e60, $4f60
    defw $4880, $4980, $4a80, $4b80, $4c80, $4d80, $4e80, $4f80
    defw $48a0, $49a0, $4aa0, $4ba0, $4ca0, $4da0, $4ea0, $4fa0
    defw $48c0, $49c0, $4ac0, $4bc0, $4cc0, $4dc0, $4ec0, $4fc0
    defw $48e0, $49e0, $4ae0, $4be0, $4ce0, $4de0, $4ee0, $4fe0
    defw $5000, $5100, $5200, $5300, $5400, $5500, $5600, $5700
    defw $5020, $5120, $5220, $5320, $5420, $5520, $5620, $5720
    defw $5040, $5140, $5240, $5340, $5440, $5540, $5640, $5740
    defw $5060, $5160, $5260, $5360, $5460, $5560, $5660, $5760
    defw $5080, $5180, $5280, $5380, $5480, $5580, $5680, $5780
    defw $50a0, $51a0, $52a0, $53a0, $54a0, $55a0, $56a0, $57a0
    defw $50c0, $51c0, $52c0, $53c0, $54c0, $55c0, $56c0, $57c0
    defw $50e0, $51e0, $52e0, $53e0, $54e0, $55e0, $56e0, $57e0

Burada en çok dikkat çekmek istediğim nokta her Y satırının adres değerlerini okuyan kısma koyduğum "readHere" isimli etiketi kullanarak "readHere+1" adresine değer yazarak "ld hl, (lines)" adresinde lines tablosunu "şu kadar ileriden oku" demiş oluyorum. Bir önceki paylaşımımda örneklediğim yöntemler kafamda henüz tam olarak oturmamış durumda. Ben de opcodelarla boğuşmak yerine kodu modifiye ederek ilk sonucu görüyorum. Evet, ZX Spectrum'da ekrana bir şeyler çizdirilebiliyormuş, 2022 Nisan ayı sonlarına doğru bunu kanıtlamış olmak beni mutlu ediyor, derhal madalyamı bana takdim ediyorlar. :)

Bu arada "ld a, 0" yerine "xor a" gibi optimizasyonlardan da haberdarım, hatta @Ref ile yakın zamanda yaptığımız bir telefon görüşmesinde de geçmişti. Dediğim gibi bu aşama "sıfır optimizasyon, önce sonuç" şeklinde yazılmış bir kod.

Çevrimdışı Skate

  • RAAT
  • Retro Meraklısı
  • *
  • İleti: 199
Ynt: Skate'in ZX Spectrum Maceraları - Vol 1
« Yanıtla #8 : 04 Mayıs 2022, 05:56:23 »
İkinci versiyonda koda AND / OR işlemleri için gerekli tabloları ekliyorum. Burada amaç X'in alt 3 bitini, diğer bir deyişle 8 ile bölümünden kalanını bulup, daha sonra ona uygun bit değerini bulmakla zaman kaybetmemek. Doğrudan tablo 0-255 arası olabilecek tüm değerleri içermeli. ZX Spectrum'un çözünürlüğünün 256x192 olması da burada epey yardımımıza koşuyor. 9. bit derdimiz yok, tüm koordinat sistemi tablolardan hızlı okumalar yapmamıza uygun değer aralığında. Kodun tamamını paylaşmam uzun olacağından kodu ekte paylaşıp, burada sadece önemli noktalara değiniyorum.

Kod: [Seç]
mainLoop:

    ; put pixel
    ld hl, (plotX)
    ld de, orBit
    add hl, de
    ld b, (hl)

    ld de, (plotY)
    add de, de
    ld a, e
    ld de, readHere+1
    ld (de), a

    ld a, l
    sra a
    sra a
    sra a
    ld e, a
    ld d, 0

readHere:
    ld hl, (lines)
    add hl, de
    ld a, (hl)
    or b
    ld (hl), a

    ld hl, plotX
    inc (hl)

    ld a, (hl)
    and 3
    cp 2
    jp C, skip1

    ld hl, plotY
    inc (hl)
skip1:
    ld a, (hl)
    cp 128
    jp C, mainLoop

    halt

plotX:
    defw #0000

plotY:
    defw #0000

Dikkat ederseniz asla 16 bit değerlere ulaşamayacak olsalar da koordinatlar için 16 bitlik değerler tanımlamışım. Bu o aşamada 16 bitlik registerlarla yapacağım işlemlerin daha hızlı olabileceğini düşünmemden kaynaklanan içgüdüsel bir hareket idi.

Kod: [Seç]
    ALIGN $100
orBit:
    defb %10000000, %01000000, %00100000, %00010000, %00001000, %00000100, %00000010, %00000001
    defb %10000000, %01000000, %00100000, %00010000, %00001000, %00000100, %00000010, %00000001
    ...

    ALIGN $100
andBit:
    defb %01111111, %10111111, %11011111, %11101111, %11110111, %11111011, %11111101, %11111110
    defb %01111111, %10111111, %11011111, %11101111, %11110111, %11111011, %11111101, %11111110
    ...

Bu tablolardan şimdilik sadece OR'a ihtiyaç duyuyoruz. Hatta tek bir nokta silmemiz gerekmediği için kodun son ulaştığı noktaya kadar AND tablosu hiç kullanılmadı. Yine de alışkanlıktan oluşturmuşum bu versiyonda, yeri gelir gerekir diyerek.

Kod: [Seç]
basic_loader:
  db $00,$0a,$0e,$00,$20,$fd,"32767",$0e,$00,$00,$ff,$7f,$00,$0d     ; 10 CLEAR 32767
  db $00,$14,$07,$00,$20,$ef,$22,$22,$20,$af,$0d                     ; 20 LOAD "" CODE
  db $00,$1e,$0f,$00,$20,$f9,$c0,"32768",$0e,$00,$00,$00,$80,$00,$0d ; 30 RANDOMIZE USR 32768
basic_loader_end:

...

    SAVESNA "first.sna", main
    EMPTYTAP "first.tap"
    SAVETAP "first.tap",BASIC,"loader",basic_loader,basic_loader_end - basic_loader, 10
    SAVETAP "first.tap",code,"first",main,endOfCode-main,main

Bu kısımsa internetten bulduğum bir hazır bir basic loader kodu ve o kod ile SjASMPlus'ın özelliklerini kullanarak snapshot dosyasının yanısıra TAP dosyası da oluşturmak için eklediğim kısım. @hades'in açtığı bir başlıkta bu yöntemden daha iyisi, yani doğrudan tek parça halinde çalışan Basic+ASM kodu tartışıldı ama ben o işi önceliklendirmedim ve kod günümüze kadar bu şekilde, basic loader + ASM parçası şeklinde iki parçalı olarak geldi. Bu konu yapılacak işler listemde duruyor, henüz sıra gelmedi.

Çevrimdışı Skate

  • RAAT
  • Retro Meraklısı
  • *
  • İleti: 199
Ynt: Skate'in ZX Spectrum Maceraları - Vol 1
« Yanıtla #9 : 04 Mayıs 2022, 06:02:26 »
Üçüncü versiyona geldiğimizde ilk olarak plot rutinindeki bazı hataları gideriyoruz. Hala optimizasyon derdimiz yok, "srl a" komutları 8'er tstate yiyorlar, bu bana çok batıyor. Ama içimden diyorum ki "ben yine büyük ihtimalle bu kısmı tablolarla çözeceğim, şimdilik olduğu gibi kalsın". Sonuçta daha geniş bir alana çizim yapabilecek hale geliyor kod.

Kod: [Seç]
mainLoop:

    ; put pixel
    ld hl, (plotX)
    ld de, orBit
    add hl, de
    ld b, (hl)

    ld de, (plotY)
    add de, de
    ld a, e
    ld de, readHere+1
    ld (de), a

    ld a, l
    srl a
    srl a
    srl a
    ld e, a
    ld d, 0

readHere:
    ld hl, (lines)
    add hl, de
    ld a, (hl)
    or b
    ld (hl), a

    ld hl, plotX
    inc (hl)

    ld a, (hl)
    and 1
    cp 1
    jp C, skip1

    ld hl, plotY
    inc (hl)
    ld a, (hl)
    cp 128
    jp C, mainLoop
    halt
skip1:
    jp mainLoop

Çevrimdışı Skate

  • RAAT
  • Retro Meraklısı
  • *
  • İleti: 199
Ynt: Skate'in ZX Spectrum Maceraları - Vol 1
« Yanıtla #10 : 04 Mayıs 2022, 06:18:00 »
Dördüncü versiyonda artık ekranda sadece üç beş piksel görmek canımı sıkmaya başlıyor. Plot rutinini biraz kenara bırakıp, ekrana renk katmayı seçiyorum. Bunun için de daha önce GitHub linkini paylaşmış olduğum grafik dönüştürücü Python scriptimle kendi hazırladığım geçici yarım yamalak bir logoyu ekliyorum. En azından artık ekran o kadar boş değil. Scripti de bu aşamada eklere koyuyorum. Scriptte geçen şu kısma değinmek istiyorum.

Kod: [Seç]
# if char area is empty, add a default color as foreground color
if(len(palette) < 2):
    palette.append(7) # white color as empty area foreground color

Burada eğer o 8x8 blokta ikinci bir renk bulunamadıysa varsayılan olarak beyaz renk ekletiyorum. Bu logoyu aslında tam ekran bir grafikmiş gibi yükletip, noktaları bastığım alanın renklerini ayrıca ayarlamaya çalışmaktan kurtarıyor beni. Yani ideal, optimize çözümde logo sadece kapladığı alan kadar yükletilir, geri kalan alanın renkleri kod içinden doldurulur. Ama şu aşamada bunu hem pratik buluyorum, hem de logoyu tüm ekranı kaplayan, örneğin süslü bir çerçeve gibi bir grafikle değiştirmenin de kapısını açık bırakmış oluyorum.

Daha iyi yöntemler mutlaka vardır, en basitinden dosyalar sıkıştırılmış olarak tutulur, yüklendikten sonra açılır. Ancak şimdilik hafıza derdimiz olmadığı için logo verilerini kodun bitimine olduğu gibi açık halleriyle koyup, sonradan ekran ve renk adreslerine transfer ettirmeyi uygun görüyorum.

Kod: [Seç]
    ; Copy bitmap
    ld hl, bitmapStart
    ld de, #4000
    ld bc, bitmapEnd-bitmapStart
    ldir

    ; Copy colors
    ld hl, colorStart
    ld de, #5800
    ld bc, colorEnd-colorStart
    ldir

...

bitmapStart:
    INCBIN "assets/bitmap.bin"
bitmapEnd:

colorStart:
    INCBIN "assets/color.bin"
colorEnd:

Çevrimdışı Skate

  • RAAT
  • Retro Meraklısı
  • *
  • İleti: 199
Ynt: Skate'in ZX Spectrum Maceraları - Vol 1
« Yanıtla #11 : 04 Mayıs 2022, 06:44:26 »
Beşinci versiyonumuzda görüntüde çok bir değişim olmuyor. Ama ilk optimizasyonlar baş gösteriyor. Öncelikle 8*3 = 24 tstate harcayan "srl a" kısmı var. Biliyorum ki bu kısımlar geçici, çöpe gidecek. Ama sonuç olarak kısa bir süre sonra ekranda dolaşan noktalarımız olacak ve mevcut haliyle kodu optimize etmem lazım. Bu "srl a" içeren kısımda yaptığımız şey X koordinatı üzerinden karaker bloğu sayısını bulabilmek. Matematiksel olarak bakacak olursak yapmaya çalıştığımız şey 8'e bölüp kalanı önemsemeden sonucu almak. Çarpma bölme işlemimiz olmasa da 8 sayısı 2'nin katlarından biri olduğu için bit kaydırarak bu sonuca kolayca ulaşabiliyoruz. Commodore 64'de olsa;

Kod: [Seç]
lsr
lsr
lsr

İş bitti, 2'ye böl, 2'ye böl, 2'ye böl dedik ve toplamda 8'e bölerek karakter bloğumuza ulaştık. Commodore 64'de carry yani "elde" bayrağına bakan ve bakmayan iki alternatif var bit kaydırma opcodelarında. "lsr" elde bayrağına bakmıyor. "ror" dersek elde bayrağındaki değer en soldan giriyor. Bu durumda ihtiyacımız olan lsr, yani elde bayrağına bakmadan işlem yapması. Çünkü hem önceden elde bayrağı set edilmiş olabilir, hem de bitleri kaydırdıkça sağdan çıkan bitler de elde bayrağını set edebilir. Bu yüzden z80'de lsr'nin karşılığı sayılabilecek "srl a" kullanabiliyoruz. Ama 8 tstate yiyor. Halbuki "rrca" gibi 4 tstate yiyen çok daha güzel bir alternatifi var. Ne yazık ki "rrca" Commodore 64'deki "ror"un karşılığı, elde bayrağına bakıyor. Bu durumda aklıma gelen ilk çözümü uyguluyorum.

Kod: [Seç]
    and %11111000
    rrca
    rrca
    rrca

Baştan alt üç biti temizlersek "rrca" aynı "srl a" gibi davranacaktır. Tabii AND opcode'u 7 tstate yiyor bu haliyle (kodu yazdığımda 4 tstate yiyen register kullanan varyasyonunu fark etmemiş durumdaydım) ancak bit işlemi yapan kısımdan 4*3 = 12 tstate kazancımız olduğu için her türlü kardayız.

Şimdi daha kirli bir numaraya yelken açıyoruz.

Kod: [Seç]
    ld de, orBit
...

    ld e, a
    ;ld d, 0    ; orBit high byte is subtracted from lines table high bytes to get rid of this line

...

lines:
    defw $4000-(orBit&$ff00), $4100-(orBit&$ff00), $4200-(orBit&$ff00), $4300-(orBit&$ff00), $4400-(orBit&$ff00), $4500-(orBit&$ff00), $4600-(orBit&$ff00), $4700-(orBit&$ff00)
    defw $4020-(orBit&$ff00), $4120-(orBit&$ff00), $4220-(orBit&$ff00), $4320-(orBit&$ff00), $4420-(orBit&$ff00), $4520-(orBit&$ff00), $4620-(orBit&$ff00), $4720-(orBit&$ff00)
...

"ld de, N" 10 tstate yiyor. Ancak burada tek ihtiyacımız low byte'ı set etmek, "ld e, a" diyecek olursak 6 tstate kazancımız oluyor. Sıkıntı şu ki bu defanormal koşullarda d'nin değerini sıfırlamak gerekiyor. Ama çıkmadık candan ümit kesilmez misali d'nin önceki değerine bakıyoruz, sabit. "orBit" tablosunun high byte'ını almış durumda. Öyleyse d'yi sıfırlamadan, sonraki değer okuyacağımız "lines" tablosunun tüm değerlerinden "orBit"in başlangıç değerini  çıkaracak olursak iş çözülüyor.

Gördüğünüz gibi tablolar iyice okunmaz hal aldı, artık derleyicinin özelliklerini kurcalamanın zamanı geldi gibi.

Çevrimdışı Skate

  • RAAT
  • Retro Meraklısı
  • *
  • İleti: 199
Ynt: Skate'in ZX Spectrum Maceraları - Vol 1
« Yanıtla #12 : 04 Mayıs 2022, 06:52:04 »
Altıncı versiyonumuzda kodda değişen bir şey yok. Sadece tablolar artık elle yerleştirme gibi değil, üretiliyorlar. SjASMPlus'ın bazı özellikleri doğrudan yardımcı olabilirken daha kapsamlı işler için LUA script bloğu açmak gerekiyor. Değişen kısımlar şu şekilde.

Kod: [Seç]
    ALIGN $100
lines:

y = 0
    WHILE y < 192
        defw $4000 - (orBit & $ff00) + ((y / 64) & $ff) * $800 + (y % 8) * $100 + (((y & $3f) / 8) & $ff) * $20
y = y + 1
    ENDW

    ALIGN $100
orBit:
i = 0
    WHILE i < 32
        defb %10000000, %01000000, %00100000, %00010000, %00001000, %00000100, %00000010, %00000001
i = i + 1
    ENDW
   
    ALIGN $100
andBit:
i = 0
    WHILE i < 32
        defb %01111111, %10111111, %11011111, %11101111, %11110111, %11111011, %11111101, %11111110
i = i + 1
    ENDW

    ALIGN $100
sinX:
    LUA ALLPASS
        for i = 0, 255, 1 do
            _pc(' defb ' .. math.floor(128 + 127.5 * math.sin(i / 128 * math.pi)))
        end
    ENDLUA

sinY:
    LUA ALLPASS
        for i = 0, 255, 1 do
            _pc(' defb ' .. math.floor(96 + 31.5 * math.sin(i / 128 * math.pi)))
        end
    ENDLUA

Henüz kullanmasak da sinX ve sinY isimli iki tablo üretmiş durumdayız.

SjASMPlus hakkında daha detaylı bilgi için şu adresi kullanabilirsiniz.

SjASMPlus Documentation

Çevrimdışı Skate

  • RAAT
  • Retro Meraklısı
  • *
  • İleti: 199
Ynt: Skate'in ZX Spectrum Maceraları - Vol 1
« Yanıtla #13 : 04 Mayıs 2022, 07:01:03 »
Yedinci versiyonda efekt görünür olmaya başlıyor. Artık sinüs tablolarından okuduğumuz değerleri kullanıyoruz. Ama iki temel eksiğimiz var. Hem ekran taraması yakalatmıyoruz, sonsuz döngüde deli danalar gibi çizim yaptırıyoruz, hem de silme rutinimiz düz mantık alanın tamamını hiç bir optimizasyon olmadan, korkunç zaman harcayarak temizliyor. Daha sonra değişeceği garanti kısımlardan biri olduğu için "temporary clear area" diye belirtilmiş durumda kod içersinde.

Kod: [Seç]
mainLoop:

    ld hl, (counterY)
phaseY:
    ld de, sinY
    add hl, de
    ld a, (hl)
    ld (plotY), a

    ld hl, (counterX)
phaseX:
    ld de, sinX
    add hl, de
    ld l, (hl)
    ld h, 0

    ; put pixel
    ;ld hl, (plotX)
    ld de, orBit
    add hl, de
    ld b, (hl)

    ld a, l
    and %11111000
    rrca
    rrca
    rrca
    ld e, a
    ;ld d, 0    ; orBit high byte is subtracted from lines table high bytes to get rid of this line

    ld a, (plotY)
    add a, a
    ld hl, readHere+1
    ld (hl), a

readHere:
    ld hl, (lines)
    add hl, de
    ld a, (hl)
    or b
    ld (hl), a
    ; end of put pixel

    ld hl, counterY
    inc (hl)
    inc (hl)

    ld hl, counterX
    inc (hl)

    ld a, (hl)
    jr NZ, skip1

    ; temporary clear area
    ld hl,$4800
    ld e,l
    ld d,h
    inc de
    ld (hl), 0
    ld bc, $fff
    ldir

    ld hl, phaseY+1
    inc (hl)

    ld hl, phaseX+1
    inc (hl)
    inc (hl)

skip1:
    jp mainLoop

Çevrimdışı Skate

  • RAAT
  • Retro Meraklısı
  • *
  • İleti: 199
Ynt: Skate'in ZX Spectrum Maceraları - Vol 1
« Yanıtla #14 : 04 Mayıs 2022, 07:16:35 »
Sekizinciye geldik. ZX Spectrum'un dinamik stack yapısını da kullanarak Spectrum'a özel güzel bir hareket yapmanın zamanı geldi. Commodore 64'e karşı kazandığı ilk zafer diyebiliriz bu açıdan. Commodore 64'de aynısını kendini değiştiren kodlarla daha çok zaman harcayarak yapabiliyoruz. Burada ise tüm noktaları sildirmek için her noktanın çizileceği adres değerini stack'e push edip, sileceğimiz noktada stackten peş peşe çekip temizleme imkanımız var.

Dikkat! Bu tür yöntemler kullanırken stack'i başka şeylerin de kullanmadığından emin olun. Mesela arada "call" gibi komutlar kullanacak olursanız bunlar da stack'i etkileyecektir. Aslında kullanmanızda bir sakınca yok, kontrollü olduğu sürece onları da hesaba katarak, stack pointer ile oynayarak çözüme gidebilirsiniz. Ama şahsen önerim stack pointer ile çok fazla oynamamanız, stack'i aşağıda vereceğim şekilde kullandığınız durumlarda arada stack ile uğraşan başka şeylerin olmaması yönündedir. Dünyadaki en meşhur programlama sorunları çözme forumunun adının "Stack Overflow" olması tesadüfi değil.

Kod: [Seç]
readHere:
    ld hl, (lines)
    add hl, de
    ld a, (hl)
    or b
    ld (hl), a
    ; end of put pixel

    ; push pixel memory address to stack
    push hl

Burada dikkatinizi çekmek istediğim yalnızca son satır. "add hl, de" satırında artık pikseli koyacağımız hafıza adresi netleşmiş oluyor. Takibindeki satırlarda o adresin değerini okuyup, piksel'in bit değeriyle ORlayıp, yerine yazıyoruz. Bu işlemler sırasında "hl" register'ı değişmiyor. Bu noktada tek ihtiyacımız olan şey basit bir "push hl". Bu işlem 256 kere tekrarlandığı için stack boyutumuzu da 256*2 = 512 byte olarak belirlemiş durumdayız.

Döngü bitip sıra noktaları temizlemeye geldiğinde ise şunu yapıyoruz.

Kod: [Seç]
    ; clear plots
    ld a, 0
clearLoop:
    pop hl
    ld (hl), 0
    inc a
    jp NZ, clearLoop

Bu elbette ki böyle kalmayacak, sadece ilk deneme. Daha sonraki versiyonlarda bu döngüyü açacağız ve z80 öğrenme sürecimizde gözden kaçırdığımız bazı şeyleri fark edip daha da optimize edeceğiz.