21 Aralık 2025 Pazar

VGA Metin Kipinde Yumuşak Kaydırma #3: Base Adres Değiştirme


Merhaba. Bu yazı, önceki yumuşak kaydırma (smooth scroll) serisinin beklenmedik bir devamı olacak. Serinin ikinci yazısında Start Address yazmacını tanıtıp, istenirse VGA sayfalarıyla birlikte bu yazmaç kullanılarak, bellek bloklarını kopyalamaya gerek kalmadan da kaydırma efektinin yapılabileceğinden bahsettim. Yazıyı hazırlarken izlediğim aşağıdaki video nedeniyle bu yaklaşımı da kısaca göstermeye karar verdim.



Öncelikle bu kanalın sahibi retroprogramming konusunda çok iyi işler yapıyor. İlgisi olan herkese kanalı takip etmesini tavsiye ederim. Benim yaklaşımımın bellek transferi yüzünden CPU'yu yoğun olarak kullandığını, buna karşın VGA belleğinin yalnızca ekranda görünen kadarını kullandığını ifade etmiştim. Yukarıdaki videoda bir ASCII art metin, bir sinüs fonksiyonuna bağlı hızda yukarı ve aşağı kaydırılıyor.

Ben bu yaklaşımı daha az görsel şekilde uyguladım. Birincisi bu kadar güzel görsel işler yapmayı beceremiyorum, ikincisi de elimde bu iş için hazır kod vardı, onu biraz değiştirdim. Zamanında bir diskmag için basit bir okuyucu yazmıştım. İki yana yaslı yazılar benim hep hoşuma gitmiştir. Blog'un sayfa düzeninden de belli oluyordur. Diskmag satır başına 80 karakteri geçmeyen metin dosyalarda yayınlanıyordu. Ben bunları tam ekranda okumak için iki yana yaslama algoritmasını uygulamıştım. Asıl okuyucuda fazladan başlık, altbilgi satırı ve bazı Escape karakter kodları vardı.

Kodu bu kez github gist'e koydum ve yazının en sonuna embedledim (bakalım, güzel görünmezse bir dahakine sadece bağlantı eklerim). justify_to_80() fonksiyonu, bir satırı seksen karaktere tamamlamaya yarıyor. Bunun için satırın karakter sayısı ve boşlukları sayılıyor ve ne kadar daha boşluk eklenmesi gerektiği bulunuyor. Eğer satırı seksen karaktere tamamlamak için gereken karakter sayısı, satırdaki boşluklardan fazlaysa (örn. satır 3-4 uzun kelimeden oluşuyorsa) o zaman each değişkeni her boşluğa eklenmesi gereken boşluk sayısını tutuyor (satır 33). Tam tersi, satır kısa kelimelerden oluşuyor ve seksene tamamlamak için az sayıda boşluk gerekiyorsa, o zaman extra değişkeni eklenmesi gereken boşluk sayısını tutuyor. Elbette her iki değişken de aynı anda sıfırdan farklı olabilir ama istatistiksel olarak genellikle each sıfır olup extra sıfırdan farklı oluyor.

each'i eklemek kolay çünkü zaten her boşluğa eklenecek (satır 42). extra ile eklenecek boşlukların homojen dağıtılması biraz daha karışık. Satırdaki her boşluk başına fazladan ne kadar boşluk eklenmesi gerektiği weight değişkeninde tutuluyor. Eksik karakter sayısı, boşlukların sayısına tam bölünemediği için weight bir double değişken. Değeri her boşluk karakterinde extra / spaces kadar arttırılıyor (satır 46). Bu değer her tam sayı geçişinde, örn 1.9'dan 2.1'e ulaşması durumunda bir boşluk karakteri ekleniyor. Örneğin aşağıdaki satırı ele alalım:

They were hidden from the road by a shallow ridge, but there was only sparse

Karakter sayısı 76, fakat strlen(), CR LF'i de saydığı için 77 döndürüyor. Zaten bu nedenle otuz ikinci satırda bu sayı 81'den çıkarılıyor. missing = 4, boşluk sayısı 14. Dolayısıyla each = 0, extra = 4. Otuz altıncı satırdaki döngüde karakterler tek tek işleniyor. Boşluk karakterine denk gelinirse (satır 40) döngüdeki diğer for, bu örnekte each = 0 olması nedeniyle etkisiz. sp1 = 0 olduğundan, weight değişkeni ilk adımda 0. İkinci boşluk karakterinde weight = 1 * (4 + 1) / 14 ≈ 0.36. sp1'den ötürü bu sayı doğrusal artacağından her boşlukta aşağıdaki değerler elde ediliyor:

0.00   0.36   0.71   1.07   1.43   1.79   2.14   2.50   2.86   3.21   3.57   3.93   4.29   4.64

Buradaki tam sayı geçişleri dört (0.71 -> 1.07), yedi (1.79 -> 2.14), on (2.86 -> 3.21) ve on üçüncü boşluklarda (3.93 -> 4.29) gerçekleşiyor.

Aslında gerek açıklamasına gerekse satır sayısına bakılırsa bu fonksiyon smooth scroll algoritmasından daha karmaşık.

vga_set_base_addr() fonksiyonu lineP değerini seksenle çarpıp (satır 62), bunun yüksek byte'ını Start Address High ve düşük byte'ını Start Address Low yazmaçlarına yazıyor. lineP ekranın en üstünde hangi satırın görüneceğini belirleyen bir sayaç.

waitbl()'i önceki yazıda detaylı açıklamıştım.

vgaprint(), verilen string'i video belleğine kopyalıyor. Burada justify_to_80()'e giren satırlar '\n' olmadan, girmeyenler (paragraf sonları) '\n' ile geldiklerinden, seksen altıncı satırdaki gibi bir workaround üretmek gerekti. for döngüsünde (satır 89) seksen karakter basılıyor. Satır, seksen karakterden azsa bile (paragraf sonundaki satır), boşluk karakteriyle ekranda önceden bulunan karakterlerin üzerine yazılıyor. Kodun ondördüncü satırında linecount'u yerel değişken yapmayı TODO'ya eklemiştim. Aslında bu değişkenin değeri main()'de arttırılırsa kolaylıkla yerel yapılabilir. Ancak şimdi main()'e de bakınca, bu kısımların biraz aceleye geldiği anlaşılacaktır.

Öncelikle main(), görece büyük. İlk while bloğu (satır 121) ve kaydırma işini yapan ikinci while bloğu (satır 137) iki ayrı fonksiyonda ele alınabilirdi. İkinci olarak girdi sanitizasyonu belki biraz daha çeşitlendirilebilirdi, ama dismag'deki hiçbir dosyanın 80 karakterden uzun olmadığını biliyorum. Asıl önemlisi dosyanın 409 satırdan uzun olmaması gerekliliği. Renkli VGA text mod belleği B800:0000 ile B800:7FFF arasındaki 32 KB. Buraya 80 karakterden en fazla 409 satır sığabilir. Dosya büyüklüğüne bu durumda güvenilemez, çünkü biz dosyaya boşluk karakteri ekleyeceğiz.

İlk while bloğundan önce dosyadan bir satır okunuyor (satır 119) ve bunun paragraf sonu olmadığı varsayılıyor. While'ın içinde bir sonraki satır okunuyor ve bunun boş satır veya dosya sonu olup olmadığı kontrol ediliyor. Buradaki amaç, paragraf sonundaki satırı olduğu gibi bırakmak. Aksi halde satır 80 karaktere tamamlanıp bir sonraki satır işleniyor. Kısacası her adımda bir sonraki satır da kontrol ediliyor.

İkinci while bloğundan ESC tuşuyla çıkılıyor. Switch / case yapısında klavye girdisi kontrol ediliyor. Yukarı ok basılmışsa, ekranın en üst satırında olması gereken satır 1 azaltılıyor (satır 142), benzer şekilde aşağı ok basılmışsa, en üst satır bir arttırılıyor. Elbette bunlar yapılırken dosyanın satır sayısı da kontrol ediliyor ki, metnin olduğu alan hep kullanıcının önünde kalsın, yukarıdan veya aşağıdan ucu kaçıp gitmesin. Burada kaydırma efektleri bir önceki kod ile tamamen aynı, hatta bellek kopyalanmadığı için daha bile basit. Farklı olarak, burada ok tuşlarıyla yapılan kaydırma SCROLLSTEPFINE, Pg Up ve Pg Dn tuşlarıyla yapılan kaydırma SCROLLSTEPCOARSE parametresine bağlı. Bu değerleri küçültmek kaydırmayı yavaşlatıyor, büyütmek efektin etkisini azaltarak kaydırmayı hızlandırıyor.

Pg Up ve Pg Dn tuşlarıyla yapılan kaydırmanın mantığı ok tuşlarıyla yapılan ile tamamen aynı. Fakat burada kaydırma etkisi katması için satır sayısı 1 ile 24 arasında tek tek arttırılıyor veya azaltılıyor.

Efektin videosunu da aşağıya ekledim ancak videonun etkisinden dolayı Pg Up ve Pg Dn ile kaydırırken yumuşak geçişler klipte düzgün görünmüyor.

Ve son olarak kaynak kod aşağıda:

Hiç yorum yok:

Yorum Gönder