Z80 Assembly Rehberi: Amstrad CPC 464’ün Donanımına Hükmedin

guclusat

Tanınmış Üye
Süper Moderatör

Z80 Assembly Rehberi: Amstrad CPC 464’ün Donanımına Hükmedin

Bu seride, BASIC dilinin sınırlarını aşarak Z80 işlemcisinin dilini (Machine Code) öğreneceğiz. Adım adım donanım kayıtçılarından, bellek yönetimine ve firmware çağrılarına kadar her detayı inceleyeceğiz.

1. Bölüm: Z80 İşlemci Mimarisi ve Kayıtçılar (Registers)

Z80 Assembly yazarken verileri RAM'den işlemci içindeki "kutucuklara" yani kayıtçılara taşırsınız.
  • A (Akümülatör): Matematiksel ve mantıksal işlemlerin merkezidir.
  • B, C, D, E, H, L: Genel amaçlı 8-bitlik kayıtçılardır.
  • BC, DE, HL: Gerektiğinde 16-bitlik (çift) olarak kullanılırlar. HL, genellikle bellek adreslerini işaret etmek için tercih edilir.
  • F (Flags): Yapılan işlemin sonucunu (sıfır, elde, negatiflik) saklayan durum kayıtçısıdır.

2. Bölüm: Temel Komut Seti (Mnemonics)

Z80 ile iletişim kurmak için kullanılan temel "kelimeler":
  • LD (Load): Veri kopyalama komutudur. Örn: LD A, 10 (A'ya 10 değerini yükle).
  • INC / DEC: Değeri 1 artırır veya azaltır.
  • ADD / SUB: Toplama ve çıkarma işlemleri.
  • CALL / RET: Bir alt programı çağırır ve işi bitince geri döner.
  • JP (Jump): Belirli bir bellek adresine doğrudan atlar.

3. Bölüm: Amstrad CPC 464 Firmware Çağrıları

İşinizi kolaylaştırmak için Amstrad'ın ROM'unda (firmware) yer alan hazır fonksiyonları kullanabiliriz:
  • &BB5A (TXT OUTPUT): A kayıtçısındaki karakteri o anki imleç konumuna yazar.
  • &BB06 (KM WAIT CHAR): Kullanıcı bir tuşa basana kadar bekler.
  • &BC06 (SCR SET MODE): Ekran modunu (0, 1, 2) değiştirir.

4. Bölüm: İlk Programımız "Merhaba"

Kodun RAM üzerinde nerede çalışacağını belirleyerek ilk adımımızı atıyoruz:
Kod:
ORG &4000       ; Program başlangıç adresi (16384)

LD A, 'H'       ; A kayıtçısına 'H' karakterini al
CALL &BB5A      ; Amstrad Firmware ile ekrana yaz
RET             ; BASIC ortamına geri dön
Çalıştırma: BASIC ekranında CALL &4000 komutuyla kodu tetikleyebilirsiniz.

5. Bölüm: Kullanılacak Araçlar (Windows 11 Uyumu)

Modern sistemlerde bu kodları yazmak ve test etmek için:
  • Winape: En gelişmiş Windows emülatörüdür; içinde dahili assembler (kod derleyici) barındırır.
  • Maxam / DAMS: Doğrudan Amstrad üzerinde çalışan klasik assembler programlarıdır.
 
Z80 Assembly eğitim serimize, Amstrad CPC 464'ün en heyecan verici konularından biri olan Ekran Belleğine Doğrudan Erişim (Direct Screen Mapping) ile devam ediyoruz. BASIC dilindeki PLOT veya DRAW komutlarının yavaşlığından kurtulup, saniyede 50 kare (FPS) hızında grafikler üretmenin anahtarı buradadır.

6. Bölüm: Ekran Belleği (VRAM) Yapısı

Amstrad CPC 464'te ekran belleği standart olarak &C000 adresinden başlar ve &FFFF adresine kadar devam eder (toplam 16 KB). Ancak bu bellek yapısı, modern sistemler gibi doğrusal (linear) değildir.
  • Ekran Başlangıcı: &C000 adresi ekranın sol üst köşesindeki ilk piksel grubunu temsil eder.
  • Satır Atlamaları: Bir alt satıra geçmek için adrese sadece 1 eklemek yetmez; Amstrad'da satırlar bellek içinde karmaşık bir düzende (interlaced) yer alır.
  • Hız Avantajı: Firmware çağrılarını (CALL) kullanmak yerine doğrudan bu adreslere LD komutuyla veri yazmak, grafik hızını kat kat artırır.

7. Bölüm: Ekrana Doğrudan Piksel Yazma

Aşağıdaki kod örneği, ekranın en sol üst köşesine doğrudan bir veri bloğu yazarak hızlı bir görsel çıktı oluşturur:
Kod:
ORG &4000

LD HL, &C000       ; Ekran belleğinin başlangıç adresini HL'ye yükle
LD (HL), %11111111 ; HL'nin işaret ettiği adrese 8 piksellik (Mode 2) dolgu yaz
INC HL             ; Bir sonraki yatay adrese geç
LD (HL), %10101010 ; Yanındaki adrese desenli bir veri yaz
RET                ; BASIC'e dön

8. Bölüm: Ekran Modları ve Renk Derinliği

Assembly yazarken seçtiğiniz ekran modu, bir baytlık verinin ekranda kaç piksel görüneceğini belirler:
  • Mode 0 (160x200): 1 Bayt = 2 Piksel (16 Renk).
  • Mode 1 (320x200): 1 Bayt = 4 Piksel (4 Renk).
  • Mode 2 (640x200): 1 Bayt = 8 Piksel (2 Renk - Siyah/Beyaz).

9. Bölüm: Döngüler (Loops) ile Hızlı Temizlik

Ekranın tamamını anında temizlemek (Clear Screen) için Assembly döngülerini kullanırız:
Kod:
ORG &4000
    LD HL, &C000    ; Başlangıç
    LD BC, &3FFF    ; 16 KB'lık veri uzunluğu (Ekran boyutu)
CLEAN_LOOP:
    LD (HL), 0      ; Adresteki veriyi sıfırla (Siyah yap)
    INC HL          ; Sonraki adrese geç
    DEC BC          ; Sayacı azalt
    LD A, B         ; Sayaç bitti mi kontrol et
    OR C
    JR NZ, CLEAN_LOOP ; Bitmediyse döngüye devam et
    RET
 
Z80 Assembly serimizin bu bölümünde, Amstrad CPC 464'ün karakteristik seslerini borçlu olduğu AY-3-8912 ses çipine (PSG - Programmable Sound Generator) giriş yapıyoruz.

10. Bölüm: AY-3-8912 Ses Çipi ve Assembly Kontrolü

Amstrad CPC serisinde ses üretimi, doğrudan işlemciye bağlı olmayan, ancak özel bir giriş/çıkış (I/O) portu üzerinden haberleşilen AY-3-8912 çipi ile sağlanır. Bu çip 3 kanal sese, bir gürültü üretecine ve bir zarf (envelope) kontrolüne sahiptir.

Ses Çipi Kayıtçıları (Registers)

Ses çipinde toplam 16 adet kontrol kayıtçısı bulunur. En temel olanları şunlardır:
  • R0-R1: Kanal A Frekansı (Ses tonu)
  • R2-R3: Kanal B Frekansı
  • R4-R5: Kanal C Frekansı
  • R7: Mikser Kontrolü (Hangi kanalın aktif olacağını belirler)
  • R8-R10: Kanal Ses Seviyeleri (Amplitude)

11. Bölüm: Assembly ile Ses Çipine Veri Göndermek

Z80 işlemcisi ses çipine doğrudan LD komutuyla veri yazamaz. Bunun yerine Amstrad Firmware içindeki &BCC8 (SOUND OUT) fonksiyonunu kullanmak en kararlı yöntemdir.

Örnek: Basit Bir Ton Çıkışı AlmakAşağıdaki kod, Kanal A üzerinden sabit bir nota çalmanızı sağlar:
Kod:
ORG &4000

    LD A, 0          ; Kayıtçı 0'ı seç (Kanal A Frekans Düşük Bayt)
    LD C, &FF        ; Frekans değeri (Örn: 255)
    CALL &BCC8       ; Ses çipine gönder

    LD A, 7          ; Kayıtçı 7'yi seç (Mixer)
    LD C, %11111110  ; Sadece Kanal A'yı aktif et (0 = Aktif)
    CALL &BCC8

    LD A, 8          ; Kayıtçı 8'i seç (Kanal A Ses Seviyesi)
    LD C, 15         ; Maksimum ses seviyesi
    CALL &BCC8

    RET

12. Bölüm: Ses Efektleri ve Gürültü (Noise)

Retro oyunlardaki patlama ve vuruş sesleri, ses kanallarına "Noise" (Gürültü) eklenerek yapılır.
  • R6: Gürültü periyodunu ayarlar.
  • R7 (Mikser): Hangi kanalda gürültü, hangi kanalda ton olacağını bit düzeyinde kontrol eder.
Teknik İpucu: Eğer her şeyi doğru yaptığınız halde ses gelmiyorsa, Amstrad monitörünün veya harici hoparlörün ses seviyesini kontrol etmeyi unutmayın. Ayrıca Gate Array arızalarında ses çıkışının da etkilenebileceği (senkronizasyon kaynaklı) unutulmamalıdır.
 
Z80 Assembly serimizde, oyun ve uygulama geliştirmenin en kritik aşamalarından biri olan Giriş Birimleri (Klavye ve Joystick Kontrolü) ile devam ediyoruz. Amstrad CPC 464'te akıcı bir kontrol mekanizması kurmak, BASIC'teki yavaş INKEY$ komutundan kurtulmak demektir.

13. Bölüm: Klavye ve Joystick Kontrol Mekanizması

Amstrad CPC'de klavye ve joystick doğrudan işlemciye bağlı değildir. Tuş basımlarını okumak için PPI (8255) çipi üzerinden AY-3-8912 ses çipinin dahili portlarını kullanırız. Bu biraz karmaşık görünse de, sistem her tuşu bir satır ve sütun matrisine yerleştirmiştir.

Klavye Matrisi (Matrix)

Klavye 10 satırdan oluşur (0-9). Her satırda 8 farklı tuş veya yön bilgisi bulunur.
  • Satır 9: Joystick yönleri ve Space tuşu gibi kritik girişleri barındırır.
  • Satır 0-8: Harfler, rakamlar ve fonksiyon tuşlarını içerir.

14. Bölüm: Firmware Kullanarak Tuş Okuma

En güvenli ve kolay yöntem, Amstrad'ın kendi rutinlerini kullanmaktır.
  • &BB1E (KM TEST KEY): Belirli bir tuşun basılı olup olmadığını kontrol eder.
    • Giriş: A kayıtçısına kontrol edilecek tuşun kodunu (Key Scan Code) girin.
    • Çıkış: Eğer tuş basılıysa Z bayrağı (Zero Flag) sıfırlanır.
Örnek: Space Tuşunu Kontrol Etme
Kod:
ORG &4000

    LD A, 47        ; Space tuşunun tarama kodu 47'dir
    CALL &BB1E      ; Tuşu test et
    JR Z, NOT_PRESSED ; Eğer basılmadıysa atla
    
    ; --- Buraya tuşa basıldığında yapılacak işleri yazın ---
    LD A, '!'
    CALL &BB5A      ; Ekrana ünlem bas
    
NOT_PRESSED:
    RET

15. Bölüm: Doğrudan Donanım Erişimi (Joystick)

Hızın çok kritik olduğu oyunlarda, Firmware kullanmak yerine doğrudan &F4xx ve &F6xx portları üzerinden PPI çipine erişilir. Ancak başlangıç seviyesinde Firmware kullanmak sistem kararlılığı için daha iyidir.

Joystick Kodları (Satır 9):Joystick hareketleri klavye matrisinin 9. satırında şu bitlere denk gelir:
  • Bit 0: Yukarı (Up)
  • Bit 1: Aşağı (Down)
  • Bit 2: Sol (Left)
  • Bit 3: Sağ (Right)
  • Bit 4: Ateş (Fire / Space)

16. Bölüm: "Gecikme" (Delay) Döngüleri

Assembly çok hızlı çalıştığı için, bastığınız bir tuş saniyeler içinde binlerce kez okunabilir. Bunu engellemek için kodun arasına küçük bekletme döngüleri eklenir.
Kod:
DELAY:
    LD BC, &FFFF    ; Sayaç değerini en yükseğe kur
WAIT:
    DEC BC          ; Sayacı azalt
    LD A, B
    OR C            ; B ve C sıfır mı?
    JR NZ, WAIT     ; Değilse döngüye devam et
    RET
 
Z80 Assembly serimizin bu bölümünde, öğrendiğimiz tüm teknikleri (Görüntü, Ses, Kontrol) tek bir potada eriten Oyun Döngüsü (Game Loop) yapısını kuruyoruz. Bu yapı, modern oyun motorlarının bile temelini oluşturan, sürekli dönen bir mantık çerçevesidir.

17. Bölüm: Ana Oyun Döngüsü (The Game Loop)

Bir oyunun akıcı çalışması için işlemcinin saniyede 50 kez (Amstrad'ın ekran yenileme hızıyla senkronize) şu adımları tekrarlaması gerekir:
  1. Girişleri Oku: Oyuncu tuşa bastı mı?
  2. Mantığı Güncelle: Karakter hareket etsin mi, düşmana çarptı mı?
  3. Ekrana Çiz: Yeni pozisyonu ekrana bas.
  4. Bekle: Ekranın en üstüne dönmesini (VBLANK) bekle ki titreme olmasın.

18. Bölüm: Örnek Bir Mini Oyun İskeleti

Aşağıdaki kod, basit bir döngü içinde "Space" tuşuna basıldığında ses çıkaran ve ekranda bir karakteri hareket ettiren yapıyı simüle eder:
Kod:
ORG &4000

GAME_LOOP:
    ; --- 1. Senkronizasyon (Titremeyi Önleme) ---
    CALL &BD19      ; MC WAIT FLYBACK (Ekranın başa dönmesini bekle)

    ; --- 2. Klavye Kontrolü ---
    LD A, 47        ; Space tuş kodu
    CALL &BB1E      ; Tuş basılı mı?
    JR Z, NO_SOUND  ; Basılmadıysa atla

    ; --- 3. Ses Tetikleme ---
    CALL BEEP_SOUND ; Space basıldıysa bip sesi çıkar

NO_SOUND:
    ; --- 4. Mantık ve Çizim ---
    ; Buraya karakter hareket ve ekrana çizim kodları gelecek

    ; --- 5. Döngü Devamı ---
    JP GAME_LOOP    ; Başa dön ve sonsuza kadar devam et

BEEP_SOUND:
    ; Basit bir ses rutini (Önceki derslerden hatırla)
    LD A, 8: LD C, 15: CALL &BCC8 ; Kanal A Ses
    RET

19. Bölüm: Verimlilik İpucu - "Double Buffering" Mantığı

Amstrad CPC 464'te grafiklerinizin çok daha profesyonel görünmesi için "Double Buffering" tekniğini Assembly ile uygulayabilirsiniz:
  • Mantık: Bir ekran üzerinde çizim yaparken, oyuncu diğer ekranı görür.
  • Uygulama: Gate Array kayıtçıları üzerinden ekranın başlangıç adresini (&C000 ile &4000 arasında) anlık değiştirerek görüntünün pürüzsüz akmasını sağlayabilirsiniz.

20. Bölüm: Programı Kapatma ve BASIC'e Dönüş

Assembly programınızın içinde bir "Çıkış" tuşu (örneğin ESC) tanımlamak, cihazı resetlemeden kod üzerinde değişiklik yapmanıza olanak tanır.
  • ESC Kontrolü: Tarama kodu 66 olan ESC tuşunu döngü içinde kontrol edip, basıldığında RET komutuyla güvenli bir şekilde BASIC satırına dönebilirsiniz.
 
Z80 Assembly eğitim serimizin bu aşamasında, projeyi bir üst seviyeye taşıyacak olan Sprite (Karakter) Basma ve Animasyon mantığına giriş yapıyoruz. Amstrad CPC 464'ün grafik belleği üzerinde pikselleri nasıl manipüle edeceğimizi gördük; şimdi bu pikselleri anlamlı figürlere dönüştürme zamanı.

21. Bölüm: Sprite Verisi Hazırlama ve Belleğe Yerleştirme

Bir oyun karakteri (sprite), aslında bellek içinde yan yana duran bir dizi bayttan ibarettir.
  • Veri Tanımlama: Assembly içinde DEFB (Define Byte) komutunu kullanarak karakterinizin piksellerini tanımlarsınız.
  • Adresleme: Karakterin koordinatlarını (X, Y) ekran belleğindeki (&C000) karşılığına hesaplayarak yazdırmanız gerekir.

22. Bölüm: Ekrana Karakter Çizme Rutini

Aşağıdaki örnek kod, 8x8 piksellik basit bir kareyi (veya hazırladığınız bir karakteri) ekranın belirli bir noktasına basar:
Kod:
ORG &4000

DRAW_SPRITE:
    LD HL, &C000    ; Ekran başlangıcı
    LD DE, SPRITE_DATA
    LD B, 8         ; 8 satır yüksekliğinde
   
PLOT_LOOP:
    PUSH HL         ; Satır başlangıcını sakla
    LD A, (DE)      ; Sprite verisini al
    LD (HL), A      ; Ekrana yaz
    POP HL          ; Adresi geri al
   
    ; Bir alt satıra geçiş (Amstrad satır yapısı karmaşıktır)
    LD A, H
    ADD A, 8
    LD H, A
   
    INC DE          ; Sonraki veri baytına geç
    DJNZ PLOT_LOOP  ; 8 satır bitene kadar dön
    RET

SPRITE_DATA:
    DEFB %00111100  ; Karakterin piksel verileri
    DEFB %01111110
    DEFB %11111111
    DEFB %11111111
    DEFB %11111111
    DEFB %11111111
    DEFB %01111110
    DEFB %00111100

23. Bölüm: Hareket ve "Ghosting" (İz Bırakma) Sorunu

Bir karakteri hareket ettirirken karşılaşılan en büyük sorun, karakterin eski yerinde iz bırakmasıdır. Bunu çözmek için iki yöntem vardır:
  1. Önce Sil, Sonra Çiz: Karakteri yeni yerine çizmeden önce, eski yerini siyah (0) baytlarla doldurun.
  2. XOR Yöntemi: Karakteri XOR komutuyla ekrana basarsanız, aynı yere ikinci kez bastığınızda karakter otomatik olarak silinir.
 
Geri
Yukarı