HADES’ BASIC
Yeni bir konu ile herkese merhaba. Başlığa bakıp BASIC öğreteceğimizi düşünmeyin. Bildiğiniz gibi -bilmiyorsanız da öğrenmiş oluyorsunuz- C64 Guinness rekorlar kitabına giren bir ev bilgisayarıdır ve 20 senedir 2-3 revizyon dışında hiçbir değişikliğe uğramadı. Ama ne hikmetse 20 sene sonra bile hala deli gibi uğraşanlar var, ülkemizde BRONX'un 7D2, 7D3 gibi partileri haricinde hiçbir faaliyet olmasa da yurt dışında, Avrupa'da sürekli olarak partiler, yarışmalar düzenlenmektedir. (www.c64.sk adresinde haberleri bulabilirsiniz.)
Her neyse konuyu fazla dağıtmadan devam edelim. C64'ün çıktığı yıllarda ev bilgisayarları olarak birçok model vardı. Bazıları C64'ten gerçekten iyiydi. Ama 5-10 sene içinde çoğu piyasadan silindi. Kimisi pahalıydı, kimisi yeterince ilgi görmedi, kimisinin program desteği sorunu vardı vs. vs.. C64 aslında iyi bir makinaydı, zamanına göre çok güzel müzikleri ve grafikleri vardı. Bir düşünün, takvimler 1982'yi gösterirken bir bilgisayar piyasaya çıkıyor ve birkaç sene sonra o bilgisayar için yüzlerce oyun ve program üretilmiş durumda. Oyun müzikleri ve grafikleri insanı mest ediyordu. (O yıllarda PC -primitive calculator- ler siyah beyaz ekranda çalışan, EGA, CGA bilmemne modlu ekran seçen, iğrenç ötesi bip bip sesler çıkaran neredeyse sadece elektronik daktilo niyetine kullanılan makinalardı. Üstelik fiyatları da çok yüksekti.)
Konumuza geri dönersek C64'ün bu kadar tutulmasının sebebi neydi? Programlaması acaba çok mu kolaydı? Yoksa özel çipleri sayesinde birçok şeyin rahatça yapılabilir olması mıydı? Bence hem evet, hem hayır. Evet olan kısmı özel çipleri ve programlama teknikleri sayesinde bir zamanlar kimsenin -hatta C64'ü tasarlayan mühendislerin bile- aklına gelmeyen uçuk-kaçık demo efektlerinin yapılabilmesiydi. Hayır olan kısmı ise C64'te program yazmanın biraz zor olması daha doğrusu BASIC'in yetersiz olmasıdır. Bence programcılar C64 BASIC V2'yi yetersiz buldukları için assembler ile harikalar yarattılar. Gerçekten de C64'ün V2 BASIC'i çok yetersizdir. C64'ün BASIC'i Microsoft tarafından hazırlandığı için yetersizdir. Diğer ev bilgisayarlarında ekran rengini değiştirmek için BORDER, COLOR gibi komutlar varken C64'te bu iş için POKE 53280, renkkodu komutu kullanmanız gerekmektedir. Mesela C64'ün SPRITE dediğimiz grafik özelliğini kullanmanız için -yani ekranda sadece bir sprite göstermek için- en az 4 adet POKE komutu kullanmanız gerekiyor. Üstelik adresleri de bilmeniz gerekiyor. Önce sprite'ın şekil datalarını uygun adreslere POKE etmeniz gerekiyor. Yani istediğiniz adresleri kullanamazsınız. Sonra sprite'ın X ve Y koordinatları için 2 adet, sprite pointer'ı için 1 adet ve en son olarak sprite'ı görünür yapmak için 1 adet POKE komutuna ihtiyacınız vardır. C64'ün BASIC'i bu kadar yetersiz olmasına rağmen işletim sistemini tasarlayanlar o kadar esnek tasarlamışlar ki,
RAM'de bulunan VECTOR adresler sayesinde BASIC V2'nin yetersizliği sorun olmaktan çıkmıştır. Türkçe COMMODORE dergisinin MART 1986 tarihli ilk sayısında SIMONS' BASIC isimli bir programdan bahsediliyordu ve o programı yazan kişinin (DAVID SIMONS) henüz 14 yaşındayken yazmış olduğu bildiriliyordu. Bu program sayesinde C64'e 100'den fazla yeni BASIC komutu eklenmiş oluyordu.
Bu kadar edebiyattan sonra ne yapacağımızı anlatalım artık. SIMONS' BASIC kadar olmasa da C64'ümüze 3-5 tane komut ekleyeceğiz. Mümkün olduğu kadar basit bir şekilde anlatacağım. (Elimdeki 06-12-2000 tarihli bir dökümana göre -HADES' BASIC olarak isim vermişim- C64'e 20 adet yeni komut eklemişim)
C64'e komut eklemenin birkaç yolu vardır: 1 - SYS komutuna parametreler ekleyerek. Mesela ekran rengini değiştirmek için POKE 53280, renk yerine SYS 49152, renk kodu gibi geliştirilmiş bir SYS komutu kullanabilirsiniz. 2 - Bir zamanlar moda olan PINKY TURBO isimli teyp için turbo load / save programındaki gibi özel bir karakter tanımlayıp mesela renk değiştirme işlemi yapmak. (PINKY TURBO aktifken hafızaya turbosuz yüklenmiş fakat henüz çalıştırmamış olduğunuz bir programı "S" komutu ile kasete turbolu olarak kaydedebilir, "L" ile kasetten bilgisayara yükleyebilir, "V" ile de VERIFY işlemi yapabiliyordunuz. Burada "" karakteri yeni bir komut olarak işletim sistemine eklenmişti.) "C, renk kodu" gibi bir komutla renk değiştirme yapılabilir. 3 - Tamamen yeni bir komut ismi tanımlayıp işinizi yapmak. Mesele "POKE 53280, renk kodu" yerine "BORDER renk kodu" şeklinde bir komut tanımlamak. 4 - Benim bilmediğim başka bir şekilde komut eklemek :) Bu bölümde 3. maddedeki biçimde yeni komutlarımızı ekleyeceğiz.
3. yöntemde C64'e yeni BASIC eklemek için en basit yol $0308-$0309 adreslerinde bulunan IGONE olarak bilinen BASIC KARAKTER GÖNDERİMİ vektörünü değiştirmektir. Bu vektörü değiştirince işletim sistemi normalde gitmesi gereken $A7E4 adresi yerine sizin yeni komutları değerlendirecek rutininize gider. Eğer yeni komut varsa işletilir, yoksa işletim sistemi $A7E4 adresine sıçrayıp eski işine devam eder. Şimdi hep beraber ekran silme işini yapacak "CLS" yani CLear Screen ve birkaç tane komutu C64'e kazandıralım. Benim göstereceğim yöntemde komut sıkıştırma gibi bir özellik yoktur. Yani normal komutlarda olduğu gibi bir komut için tek bir bayt kullanılmıyor. Açıklamak gerekirse siz C64'te program yazarken PRINT komutunu kullandığınızda hafızaya P,R,I,N,T harfleri değil sadece $99 sayısı kaydedilir. Yani komutlar "TOKEN" olarak saklanır. Böylece hafızadan tasarruf edilir. RUN komutu ile program normal çalışır. LIST komutu verince hafızadaki her bayt tek tek değerlendirilir ve hangisinin komut kodu hangisinin normal text olduğu çözümlenir. Örnekte olduğu gibi $99 rakamı Basic yorumlayıcı tarafından PRINT şeklinde ekrana gönderilir. İşte bu sıkıştırma özelliği benim vereceğim komutlarda olmayacaktır. Fakat yeni komutlarımızda normal bir komutun harfleri geçiyorsa -mesela yeni komutlarımızdan olan BORDER komutunda OR komutu yer almaktadır- yeni komut B,O,R,D,E,R olarak değil B, OR komutunun kodu, D,E,R olarak hafızada tutulmaktadır.
Komut sıkıştırma özelliğini kullanmak için programda "ICRNCH", "IQPLOP" ve "IEVAL" isimleriyle bilinen "BASIC TEXT KODLAMA", "BASIC TEXT LIST" ve "BASIC TOKEN DEĞERLENDİRME" vektörlerini de değiştirip uygun programları yazmak gerekmektedir. Bu işle bir ara uğraştım ama devam ettirmedim. Aslında 3-5 komut için uğraşmaya değmez. Ama isteyen uğraşabilir. (İnternetten bulabileceğiniz SIMONS' BASIC programını inceleyebilirsiniz)
ÖNEMLİ NOT 1: Bu programı PC üzerinde VICE emülatörü ile kullanacaksanız VICE'da kartuş eklentisi olmamalıdır. Gerçek C64'te hatasız çalışan programı emülatör üzerinde çalıştırana kadar canım çıktı desem yeridir.
;--------------------------------------------;
; C64 İÇİN YENİ BASIC KOMUTLARI ;
; C64 TÜRKİYE SAYI 4 İÇİN HAZIRLANMIŞTIR ;
; 12-TEMMUZ-2003
HADES ;
; C64ASM V1.1 ILE YAZILMIŞTIR
;
;--------------------------------------------;
*
= $C000
comset =cls
lda
#<yeni
ldx
#>yeni
sta
$0308
stx
$0309
rts
;--------------------------------------------;
Buraya kadar olan kısımda programımızın başlangıcını $C000 yani 49152 olarak ayarladık ve yeni komutumuzu değerlendirecek kısmı "yeni" etiketi kullanarak vektörümüze yükledik. Böylece klavyeden bir komut girdiğimizde veya yazmış olduğumuz bir programı çalıştırınca önce bizim komut yorumlayıcımız devreye girecek, daha sonra biz kontrolü C64'ün kendi komut yorumlayıcısına bırakacağız.
;--------------------------------------------;
yeni
lda
$7a
ldx
$7b
sta
$fb
stx
$fc
;--------------------------------------------;
Yukarıda $0073 nolu adresteki CHRGET isimli rutinde bulunan ve program içindeki baytların adresinin tutulduğu iki adresin $7a ve $7b değerlerini okuyup daha sonra kullanmak için saklıyoruz
;--------------------------------------------;
lda
#<comset
ldx
#>comset
sta
$fd
stx
$fe
;--------------------------------------------;
Üstteki satırlarda ise yeni komutlarımızın isimlerinin bulunduğu bölgenin başlangıç adresini öğrenip $fd ve $fe adreslerine kopyalıyoruz.
;--------------------------------------------;
ldx
#$00
ldy
#$00
loop0
jsr
$0073
sta
$02
loop1
lda
($fd),y
cmp
#$02
bne
nextcom
iny
tya
cmp
comlen,x
bne
loop0
;--------------------------------------------;
Geldik en önemli kısma. Bu bölümde klavyeden girdiğimiz veya yazdığımız program içindeki komutların yeni komut mu yoksa eski komut mu olduğunu öğreniyoruz. Nasıl oluyor anlayalım.
X registerini yeni komutların sayıcısı olarak, Y registerini de yeni komutun harf sayıcısı olarak kullanmak üzere sıfırlıyoruz. JSR komutu ile program içindeki veya klavyeden girilen o anki karakteri okuyoruz ve saklıyoruz. Sonra bizim yeni komutların başlangıç adresinin tutulduğu $fd ve $fe adreslerini kullanarak komut tablomuzdan o anki karakteri alıyoruz ve daha önce sakladığımız değerle karşılaştırıyoruz. Karşılaştırma sonucu aynı ise karakter sayacını 1 arttırıp AKÜ'ye kopyalıyoruz. Aküdeki değeri X registerini kullanarak komut uzunlukları tablosundaki o anki değerle karşılaştırıyoruz. Eğer eşit değilse klavyeden girilen veya program içindeki bir sonraki baytı almak için "LOOP0" etiketli yere geri dönüyoruz. Eğer karşılaştırma sonucu eşitse bu kez "EXEC" rutini devreye giriyor. Fakat daha önce komut tablosu ile yapılan karşılaştırma sonucunda bir farklılık varsa bu kez "NEXTCOM" etiketli yere sıçrıyoruz ve bir sonraki komut için hesaplamalar yapıyoruz.
;--------------------------------------------;
exec
txa
asl
tax
lda
comexe+1,x
pha
lda
comexe,x
pha
rts
;--------------------------------------------;
Bu bölümde eğer yeni bir komut olduğu anlaşılmışsa bu komutun çalışması sağlanıyor. Bu ise komut sayacı olarak kullandığımız X registerini önce Akü'ye yükleyip 2 ile çarpıyoruz tekrar X'e kopyalıyoruz. Sonra komut çalıştırma adreslerinin tablosundan bulunan komutun çalışma adresi HIGH BYTE, LOW BYTE olarak yığına atılıyor. Böylece bilgisayar en sondaki "RTS" komutunu işlediği anda "PROGRAM COUNTER" de yeni komutun adresi bulunduğu için yeni komutu çalıştırıyor.
;--------------------------------------------;
nextcom
lda
comlen,x
clc
adc
$fd
sta
$fd
bcc
next0
inc
$fe
next0
inx
cpx
commax
bne
loop1
lda
$fb
ldx
$fc
sta
$7a
stx
$7b
jmp
$a7e4
;--------------------------------------------;
Bu bölümde ise komut tablosundaki bir sonraki yeni komutun adının adresi hesaplanarak bütün kontrol işlemine geri dönülüyor. Bunun için bir önceki komutun adının uzunluğu komut tablosunun başlangıç adresinin tutulduğu $fd adresindeki değerle toplanıyor ve geri yazılıyor. Sonra ise komut sayıcısı olan X registeri 1 arttırılıyor ve yeni komutların toplam sayısı olup olmadığı kontrol ediliyor. Eğer eşit değilse "LOOP1" etiketli yere geri dönüp karşılaştırma işlemlerini tekrar yapıyoruz.
Komut sayıcısı maximum komut sayısına eşit olduğu halde yeni komut bulunamadıysa artık kontrolü eski komut yorumlayıcısına bırakmak üzere daha önceden kopyalamış olduğumuz ve $fb ve $fc adreslerindeki değerleri $7a ve $7b adreslerine geri yazıyoruz. En son olarak JMP $A7E4 ile komut yorumlayıcısına benim işim bitti sıra sende diyoruz. Eğer standart komut değilse "SYNTAX ERROR" mesajı ekrana çıkar ve programın çalışması durur.
;--------------------------------------------; ; YENİ KOMUTLAR BURADAN BAŞLIYOR ; ;--------------------------------------------;
CLS komutunu normalde ekran silme için düşünmüştük. Fakat birkaç komut eklemeyle CLS komutunu hem ekran silme hem de ekrandan istediğimiz satırı silmek için kullanacağız. Sadece CLS yazarsak ekran tamamen silinir. Eğer CLS ekran satır numarası şeklinde verirsek sadece o satır silinir.
;--------------------------------------------;
cls
jsr
$73
; Komuttan sonra sayı var mı ?
beq
all
; Yoksa ekran tamamen silinsin
jsr
$b79e
; Evet var ve sayıyı al
cpx
#$01
; Sayı "1" den
bcc
ilerr
; küçükse hata mesajı yazdır.
cpx
#$1b
; "26" dan büyük veya eşitse
bcs
ilerr
; hata mesajı yazdır.
dex
; x registerini 1 azalt.
jsr
$e9ff
; ROM'daki satır silme rutinini çağır.
jmp
$a7ae
; Bir başka komut için devam et
all
jsr
$e544
; ROM'daki ekran silme rutinini çağır.
jmp
$a7ae
; Bir başka komut için devam et
ilerr
jmp
$b248
; "ILLEGAL QUANTITY ERROR" mesajı yazdır.
;--------------------------------------------;
border jsr
$b79b
; Komuttan sonraki ifadeyi değerlendir.
stx
$d020
; Sonucu VIC'in kenar rengi adresine yaz.
jmp
$a7ae
; Bir başka komut için devam et.
;--------------------------------------------;
ink
jsr
$b79b
; Komuttan sonraki ifadeyi değerlendir.
stx
$0286
; Sonucu karakter rengi adresine yaz.
jmp
$a7ae
; Bir başka komut için devam et.
;--------------------------------------------;
paper
jsr
$b79b
; Komuttan sonraki ifadeyi değerlendir.
stx
$d021
; Sonucu VIC'in sayfa rengi adresine yaz.
jmp
$a7ae
; Başka bir komut için devam et.
comexe .word
cls-1
.word
border-1
.word
ink-1
.word
paper-1
;--------------------------------------------;
cls
.text
"cls" ; "CLS"
;--------------------------------------------;
border .text
"b"
; "B"
.byte
$b0
; "OR" komutunun kodu
.text
"der" ; "DER"
;--------------------------------------------;
ink
.text
"ink" ; "INK"
;--------------------------------------------;
paper
.text
"paper"
; "PAPER"
;--------------------------------------------;
commax .byte
4
; max komut sayısı
comlen .byte
3,5,3,5
; komut uzunlukları
;=============================================
ÖNEMLİ NOT 2: Programa yeni bir komut eklediğinizde komutlar "comexe" bölümündeki sıra nasılsa "comset" bölümündeki sıra da aynı olmalıdır. Aynı şekilde "comlen" değerleri de doğru sırada olmalıdır.
ÖNEMLİ NOT 3: Yeni komutları SYS 49152 komutuyla aktif hale getirdikten sonra NEW komutu vermeniz gerekmektedir.

İşte yeni komutlar kulla-
narak
yazılan
ilk
pro-
gramımız ve ekran görün-
tüsü.
10 CLS : REM "PRINT CHR$(147)" YERİNE
20 FOR F=0 TO 15
30 BORDER F : REM "POKE 53280,F" YERİNE
40 NEXT : GOTO 20