Z80: Revizyonlar arasındaki fark
261. satır: | 261. satır: | ||
'''Açıklaması:''' <br> | '''Açıklaması:''' <br> | ||
'''LDIR komutu''': <br> | '''LDIR komutu''': <br> | ||
Bellekteki herhangi bir alani baska bir alana kopyalamak icin kullanilan dongusel komutlardan birisidir. (Bir digeri icin bkz. LDDR) | Bellekteki herhangi bir alani baska bir alana kopyalamak icin kullanilan dongusel komutlardan birisidir. (Bir digeri icin bkz. LDDR) <br> | ||
<br> | |||
'''Calisma sekli:''' <br> | '''Calisma sekli:''' <br> | ||
LDIR komutu yurutulmeden once 3 yazmac gerekli parametreler ile yuklenir. | LDIR komutu yurutulmeden once 3 yazmac gerekli parametreler ile yuklenir. | ||
HL ciftine, kopyalanmak istenen (kaynak) alanin baslangic adresi, | HL ciftine, kopyalanmak istenen (kaynak) alanin baslangic adresi, <br> | ||
DE ciftine hedef alanin baslangic adresi, | DE ciftine hedef alanin baslangic adresi, <br> | ||
BC ciftine ise baslangic adresinden itibaren kac byte kopyalanacagi yuklenir. | BC ciftine ise baslangic adresinden itibaren kac byte kopyalanacagi yuklenir. <br> | ||
Ardindan LDIR komutu verilerek kopyalama gerceklestirilir. | Ardindan LDIR komutu verilerek kopyalama gerceklestirilir. Gercekte LDIR komutu bir islemler dongusunu baslatir. Adim adim incelersek, LDIR komutu verildiginde, ilk olarak; <br> | ||
LD (DE),(HL) islemi ile DE'nin isaret ettigi adrese HL'nin isaret ettigi adresteki deger yuklenir. <br> | |||
Ardindan DE ve HL 'nin degerleri 1 arttirilir ve BC 'nin degerinden ise 1 eksiltilir. Dongu kontrol degiskeni olarak BC yazmaci kullanilir ve dongu BC'nin degeri 0 oluncaya kadar tekrar eder. Komutun calisma suresi kopyalanacak byte miktarina bagli olarak degisir. <br> | |||
Ardindan DE ve | Hades'in verdigi ornekte, hedef alan, kopyalanacak kaynak alanin sadece 1 byte ilerisinde oldugu icin, kaynak alanin ilk byte'inda tutulan deger (Hades'in orneginde 0) hemen bir ilerisindeki adrese yazilacak. Ardindan da HL ve DE yazmaclari birer arttirilacagi icin bir bu defa bir onceki adimda hedef alanda olan adres bu kez kaynak alana girecek, ve icerisinde tutulan deger (0) yine kendisinden sonraki adrese kopyalanacak. Bu da BC'deki deger yani $1000 (dec.4096) sifirlanana kadar, yani 4096 kere tekrarlanarak, 4K'lik bir RAM alanini 0'larla dolduracaktir. <br> | ||
Bu tarz bir kullanim ornegin ekran bellegi bolgesini (ZX Spectrum'da 16384 ten baslayan 6912 byte) sifirlamak sureti ile ekrani silmek amaci ile kullanilabilir. Tabi kullanim alani bununla sınırlı degildir. <br> | |||
LDIR genel olarak son derece kullanisli bir komuttur. <br> | |||
'''DJNZ (Decrease and Jump if Not Zero''' <br> | |||
DJNZ (Decrease and Jump if Not Zero | 255 defa'ya kadar tekrarlanmasi istenen islemleri bir donguye sokmak icin kullanilabilir. Dongu kontrol degiskeni olarak B yazmaci kullanilir. B yazmacindaki sayi 0'dan buyuk oldugu surece, DJNZ komutu isletildiginde, komutun isaret ettigi adrese atlanir. <br> | ||
255 defa'ya kadar tekrarlanmasi istenen islemleri bir donguye sokmak icin kullanilabilir. Dongu kontrol degiskeni olarak B yazmaci kullanilir. B yazmacindaki sayi 0'dan buyuk oldugu surece, DJNZ komutu isletildiginde, komutun isaret ettigi adrese atlanir. | |||
B yazmacindaki deger her DJNZ komutu calistirildiginda bir eksiltilir ve 0'a ulastiginda, DJNZ komutunun isaret ettigi adrese atlama yapilmaz. Onun yerine DJNZ komutundan bir sonraki komuta atlanir. | B yazmacindaki deger her DJNZ komutu calistirildiginda bir eksiltilir ve 0'a ulastiginda, DJNZ komutunun isaret ettigi adrese atlama yapilmaz. Onun yerine DJNZ komutundan bir sonraki komuta atlanir. | ||
Ornegin asagidaki rutin NOP komutunu 128 kere calistirir: | Ornegin asagidaki rutin NOP komutunu 128 kere calistirir: <br> | ||
LD B,128 | LD B,128 <br> | ||
islemyok: | islemyok: <br> | ||
NOP | NOP <br> | ||
DJNZ islemyok | DJNZ islemyok <br> | ||
RET | RET <br> | ||
== Kaynakça == | == Kaynakça == |
20.30, 19 Ocak 2015 tarihindeki hâli
Genel Bilgi
Z80, 8bitlik bir işlemci olup 8bitlik data hattına, 16 bitlik adres hattına ve çeşitli kontrol sinyallerine sahip bir işlemcidir. 64KByte adresleyebilir. Normal memory adreslemesinin yanında çeşitli IO komutları sayesinde 64K IO bölgesi adresleyebilir.
Yazmaçlar
('Register'lar)
Program Kontrol Yazmacı
PC : Program Counter. İşlemcinin a onda çalışmakta olduğu hafıza adresini gösterir.
Yığıt Yazmacı
SP : Stack Pointer. Yığın işlemleri için kullanılacak bölgenin başlangıç adresini gösterir. Kullanıcı tarafından değiştirilebilir.
Sayım Yazmaçları
B ve C yazmaçları genellikle sayım işlemi yapan komutlar tarafından kullanılır. Örn. DJNZ, LDIR.
Birikeç (Akümülatör)
A, Özel bir yazmaçtır. Birçok komut değerin önce A'ya yüklenmesini gerektirir.
Adres Yazmaçları
D, E, H, L ya da çift olarak kullanıldıklarında DE ve HL yazmaçları, genellikle bir adresi göstermek için kullanılırlar. DE genellikle DEstination (hedef) olarak kullanılır. HL ise adresin Hi(üst) ve Low(alt) byte sırasını daha kolay göstermesi için HL olarak isimlendirilmiştir.
8 bitlik registerlerden B ve C, D ve E, H ve L birlikte kullanılarak BC, DE, HL registerleri 16 bitlik olarak kullanılabilir. Yazmaç çiftleri alfabetik sıra ile yazılır ve kullanılırlar. Örn. CB=yanlış, BC=doğru.
Bayrak Yazmacı
Yapılan işlemin sonucuna göre değişen bitlere sahip F (Flag) yazmacı. Bu yazmaç ve bayraklar hakkında detaylı bilgi için Z80 Bayrakları başlığına bakınız.
Değiş-Tokuş Yazmaçları
Ayrıca Alternate register denilen AF', BC', DE' ve HL' registerleri vardır. Bu registerlere doğrudan erişim yoktur. EX AF,AF' komutu ile AF register çifti AF' register çiftiyle, EXX komutuyla ise BC, DE, HL register çiftleri BC', DE', HL' register çiftleriyle yer değiştirir. Daha doğrusu bu register çiftlerinde tutulan değerler yer değiştirir. Mesela LD BC,$4000 - LD DE,$5000 ve LD HL,$6000 olsun. EXX komutuyla $4000, $5000 ve $6000 değerleri Alternate registerlere aktarılır. Alternate registerlerdeki değerler asıl registerlere aktarılır. POP ve PUSH komutlarını kullanmaya gerek kalmadan Alternate registerleri geçici olarak saklama yeri gibi düşünebilirsiniz.
İndeks Yazmaçları
Z80'de iki adet index registeri bulunur ve 16bitlik yapıdadır. IX ve IY olarak adlandırılır. Gerekirse sanki 8 bitlik iki registerden oluşmuş gibi kullanılabilir. Alt 8 bitleri için LX, LY ve üst 8 bitleri için HX, HY olarak adlandırılan -undocumented opcodes- komutları bulunmaktadır.
İndeks yazmaçları IX ve IY aslında kullanım olarak HL yazmacına benzer. Fakat benzer komutların IX/IY ile kullanılan biçimleri 1 byte daha uzundur. Dolayısı ile bu indeks komutları, aynı işi yapan diğer komutlara göre daha yavaştır.
Diğer Yazmaçlar
- I Registeri : Interrupt kontrol registeridir
- R Registeri: Dinamik Ramler için kullanılan Refresh registeridir.
- Son olarak IFF1 (Interrupt Flip Flop 1) ve IFF2 (Interrupt Flip Flop 2) registerleri bulunmaktadır. Herhangi bir IRQ veya NMI oluştuğunda içerikleri değişmektedir.
Bayraklar
('Flag'ler)
Detaylı bilgi için bkz. Z80 Bayrakları
Yapılan işleme göre durum değiştiren bitlerdir.
7.bit - S - İşaret biti 6.bit - Z - Sıfır biti 5.bit - Kullanılmıyor. 4.bit - H - Yarı-Elde biti 3.bit - Kullanılmıyor. 2.bit - PV - Eşlik biti 1.bit - N - Çıkarma biti 0.bit - C - Elde biti
Giriş/Çıkış birimlerinin kontrolü
Z80 özellik olarak G/Ç komutlarına sahiptir. IN ve OUT komutlarıyla herhangi bir donanımla bilgi alışverişi yapabilir. Mesela ZX Spectrumun ekran belleği, donanımsal olarak IO adreslemeli bir yapıda olsaydı komutlarımız aşağıdaki şekilde olacaktı.
LD BC,$4000 ; 16384 ekran belleğinin başlangıç adresi. LD A,$FF ; Akümülatöre $FF (255) sayısını yerleştiriyoruz. OUT (C),A ; 16384 numaralı ekran belleği adresine aküdeki değeri yazıyoruz.
Normalde ZX Spectrumda ekran belleğine aşağıdaki şekilde yazarız.
LD A,$FF LD ($4000),A
veya
LD HL,$4000 LD (HL),$FF
Z80'nin G/Ç komutlarına sahip olması donanım tasarımı açısından büyük esneklik sağlar. Nitekim Spectrum'un joystickleri IN komutuyla okunur.
İşlem Komutları
(Opcode'lar)
Opcode : Operation Code. Yani bir işlemcinin yapacağı işlemi belirten koddur. İşlemci o anki opcode'a göre ne işlem yapacağını bilir. Z80'de komutların opcode'ları aşağıdaki şekilde olabilir.
(Opcode'lar hexadecimal formatta verilmiştir) (nn : $00..$FF arasında bir baytlık değer) (nnnn : $00 ... $FFFF arasında iki baytlık bir değer)
Z80 komutları 1, 2, 3 veya 4 bayt uzunluğunda olabilir.
Teorik olarak 256 komut var gibi gözüksede CB, DD, ED, FD opcode'ları birer önkod olup devamında 1, 2 veya 3 bayt daha olabilir. Ön koda sahip komutlarla beraber toplam komut sayısı 705 dir. Eğer kullanıcı kılavuzunda dökümante edilmemiş opcode'ları da sayarsak toplam komut sayısı 1278 olmaktadır.
Tek baytlık ve parametre almayan opcode'lar
00 : NOP 76 : HALT C9 : RET
Tek baytlık ve bir parametre alan opcode'lar
06 nn : LD B,nn DE nn : SBC A,nn
Tek baytlık ve iki parametre alan opcode'lar
21 nn nn : LD HL,nnnn FC nn nn : CALL M,nnnn
İki baytlık ve parametre almayan opcode'lar
ED B8 : LDIR DD E5 : PUSH IX
İki baytlık ve bir parametre alan opcode'lar
DD 7E nn : LD A,(IX+nn) DD AE nn : XOR (IX+nn)
İki baytlık ve iki parametre alan opcode'lar
DD 21 nn nn : LD IX,nnnn FD 22 nn nn : LD (nnnn),IY
Üç baytlık ve bir parametre alan opcode'lar
FD CB nn 06 : RLC (IY+nn) DD CB nn FE : SET 7,(IX+nn)
Ayrıca Bakınız
Kategori:Z80 İşlemci Komutları | Z80 Bayrakları | category:Z80 İşlemcisi Özellikleri
Sıkça Sorulan Sorular
Soru 1 (HADES): Yazdığım kodlarda IM 2'yi kullanıyorum ama Basic ekranına geri dönüş yapmayıp normal rutinde JR ile döngü yapıyorum. Hem Irq kullanıp hem Basic nasıl kullanılır? Aklıma IM 1 kullanmak geliyor ama nasıl olacak?
Cevap 1 (REF): Anladığım kadarıyla bir basic programı çalışırken, diğer taraftan asm ile müzik çaldırmak istiyorsun.
Spectrumda bir tek IM2'yi kullanabiliyoruz. IM1 ROM'a göre zıplıyor, dolayısı ile yapacak bişey yok.
IM2'yi kurması da gıcık aslında. Çünkü şöyle bir durum var. IM2 işaretçi adresinin hesaplanacağı adres şu şekilde hesaplanıyor (evet suyunun suyu şeklinde):
Adresin üst byte'ı I yazmacında tutuluyor. Fakat alt byte'ı takılı olan aygıt belirliyor. Eğer bir aygıt takılı değil ise, floating bus sebebi ile bu alt byte random olabiliyor. Dolayısı ile IM2 kurmak için 257 byte'lık bir alanı *aynı* byte'lar ile doldurmak gerekiyor ki, adres nereye düşerse düşsün pointer değişmesin.
Örnek. Diyelim ki I'yı 254 yaptınız. Bu şekilde IM2 pointer'ının okunacağı adres (yüksek byte'a göre) 65024 oldu. Fakat IM2 düşük byte 0-255 arasında rast gele bir değer. Dolayısı ile eğer düşük byte 0 olursa sizin kesme rutininizin bulunacağı adres 65024 ve 65025 byte'larından hesaplanacak. Eğer bu adreslerin ilkinin değeri "1", ikincisinin değer i"0" olursa, interrupt olduğunda, 255 adresine zıplanır. Fakat IM2 düşük byte başka bir rakam olsa, zıplanacak adres başka bir lokasyondan hesaplanacağı için, IM2 başka bir yere zıplar.
Ben asmp multicolor aracını kodlarken şöyle bir yöntem izledim. 65024'den itibaren 257 byte boyunca #FD ile doldurdum. I'ya 254 yükleyince her durumda IM2 vektörü #FDFD oluyor, yani 65021'e zıplanıyor. Bu adrese istediğin şekilde dallanabilirsin, ben timing için yaptığım için buraya RET girdim, böylece halt ile durduktan sonra IM2 olduğu gibi program senkronize olarak devam ediyor.
Dikkat, Interrupt rutini asla 32768'den aşağıda olmamalı!
Senin durumunda ise basic çalışsın istiyorsun, fakat IM1 servis rutinide çalışsın istiyorsun. Bu durumda servis rutininin yaptığı işi senin yapman gerek.
Neyse benim kodu asmp'den copy-paste ediyorum: Önce IM2'yi kuralım di ;interrupt'ı kapatalım
ld hl,65021
ld (hl),#C3 ;jump kodunu yerleştirelim, dikkat bu şekilde yapmak mecburi değil inc hl ;vektör tablosunu 253'le değil başka bir rakamla doldurursanız ld hl, #c0 ;istediğiniz yere zıplatırsınız zaten inc hl ld hl, #c7 ; yani #C3 #C0C7 (JP 47040)
ld hl,65024 ;şimdi aralığı FD ile dolduracağız.
ld de,65025
ld (hl),253
ld b,e
ld c,e
ldir
ld a,254 ;Artık IM2'yi kurabiliriz. ld i,a im 2 ;im2'ye geçtik
ei ;interruptlar açık Bu da basic'i çalışır durumda tutmanın bir yöntemi: org #C0C7 ;yukarıdaki örneğe göre kod 47040'dan başlamalı.
di ;alışkanlık ld (stackpointer),sp ;mecbur değilsen sp'yi yazmayabilirsin ld sp,stackpointer ;garanti olsun diye yerini de değiştiriyorum (18 byte'lık bir yer lazım)
Push HL ;basic'in kullandığı yazmaçları saklayalım Push BC Push DE Push AF
exx ;söylentiye göre basic için alternatif yazmaçları saklamaya gerek yok Push HL ;ama yine de koyalım bir kenara Push BC Push DE
CALL muzik ;müziğimizi çalalım
rst 56 ;ROM'daki servis rutini, klavyeyi okur, sayacı ilerletir
LD sp,stackpointer-14 ;basic'e dönmeden önce herşeyi geri yerleştirelim Pop DE Pop BC Pop HL
exx pop af pop de pop bc pop hl
ld sp,(stackpointer) ;herşeyi geri aldık
- IM1 ;Bu noktada IM1 işletsek, basic'e olduğu gibi geri dönülmüş olur.
ei ;interrupt'ı çalıştır. ret ;geri dön, dilerseniz RETI de kullanabilirsiniz.
stackpointer: defb 0
Bu koda göre IX/IY registerini kullanamazsınız, basic kullanıyor, istiyorsanız onları da kaydetmelisiniz.
Bu efektif yöntem mi bilmiyorum ama benim kullandığım yöntem bu. Özetlersek (değerler decimal):
1. IM2 için 257 byte uzunluğunda vektör tablosu oluştur. (tablo 220'lerden oluşursa adres 220*256+220= 56540 olacak) 2. I'yı bu tablonun başına point et. 3. vektör tablosundaki adrese rutinini koy 4. rutin başladığında basic değişkenlerini sakla (PUSH <hepsi>) 5. işini yap: (CALL x) 6. basic servis rutinini çağır (rst 56) 7. basic değişkenlerini geri çağır (POP <hepsi>) 8. geri dön (reti/ret)
Soru 2 (İLKER):
Örnek kodlardan biri ekrandaki resmi dikey olarak ters çeviriyor (Vertical Flip). Ancak ben bunu denemek için ekranda bir oyundan resim olsun istiyorum bunu yapabilir miyim?
Cevap 2 (MEMRAH): Soyledigini yapmanin bir suru yolu var. En basitlerinden bahsedeyim. ZX Spin kullandigini varsayarak, herhangi bir SCR dosyasini Spin'in emulasyon penceresine cekersen zaten icerigini spectrum'da 16384'ten itibaren ekran bellegine yukler. Elimizde bir tane SCR dosyası olsun. SCR dosyasini bir kenara (ornegin masa ustune) alıyoruz. Uzerinde calistigin kodu assemble et. Daha sonra eger 128 modunda isen EDIT menusunden SCREEN moduna gecmeni tavsiye ederim ki editor surekli ekrani silmeye kalkmasin. Daha sonra SCR dosyasini SPIN'in ana penceresi uzerine surukle ve birak... Resim ekranda... Daha sonra RANDOMIZE USR XXXXX ile kodu calistirabilirsin..
Diger yollar, oyunlarin TAP dosyasinin icine tape browser ile girip SCREEN$ dosyasini 16384 ile 16384+6912 arasina yuklemek, vs.
(istedigin oyunun TAP dosyasini indir, Spin'de Tape Browser'da ac, uzunlugu 6912 olan dosya ekran dosyasidir, bunu bul, LOAD "" CODE 16384,6912 yaz ve TAPE' i baslat)
Açıklamalı Örnek Kodlar
HADES ÖRNEK 01 - HAFIZADA 4K'LIK BİR BÖLGEYİ TEMİZLEYEN BİR RUTİN:
LD HL,$8000
LD DE,$8001
LD BC,$1000
LD (HL),0
LDIR
RET
$8000-$9000 arasını "0" ile dolduruyoruz.
Açıklaması:
LDIR komutu:
Bellekteki herhangi bir alani baska bir alana kopyalamak icin kullanilan dongusel komutlardan birisidir. (Bir digeri icin bkz. LDDR)
Calisma sekli:
LDIR komutu yurutulmeden once 3 yazmac gerekli parametreler ile yuklenir.
HL ciftine, kopyalanmak istenen (kaynak) alanin baslangic adresi,
DE ciftine hedef alanin baslangic adresi,
BC ciftine ise baslangic adresinden itibaren kac byte kopyalanacagi yuklenir.
Ardindan LDIR komutu verilerek kopyalama gerceklestirilir. Gercekte LDIR komutu bir islemler dongusunu baslatir. Adim adim incelersek, LDIR komutu verildiginde, ilk olarak;
LD (DE),(HL) islemi ile DE'nin isaret ettigi adrese HL'nin isaret ettigi adresteki deger yuklenir.
Ardindan DE ve HL 'nin degerleri 1 arttirilir ve BC 'nin degerinden ise 1 eksiltilir. Dongu kontrol degiskeni olarak BC yazmaci kullanilir ve dongu BC'nin degeri 0 oluncaya kadar tekrar eder. Komutun calisma suresi kopyalanacak byte miktarina bagli olarak degisir.
Hades'in verdigi ornekte, hedef alan, kopyalanacak kaynak alanin sadece 1 byte ilerisinde oldugu icin, kaynak alanin ilk byte'inda tutulan deger (Hades'in orneginde 0) hemen bir ilerisindeki adrese yazilacak. Ardindan da HL ve DE yazmaclari birer arttirilacagi icin bir bu defa bir onceki adimda hedef alanda olan adres bu kez kaynak alana girecek, ve icerisinde tutulan deger (0) yine kendisinden sonraki adrese kopyalanacak. Bu da BC'deki deger yani $1000 (dec.4096) sifirlanana kadar, yani 4096 kere tekrarlanarak, 4K'lik bir RAM alanini 0'larla dolduracaktir.
Bu tarz bir kullanim ornegin ekran bellegi bolgesini (ZX Spectrum'da 16384 ten baslayan 6912 byte) sifirlamak sureti ile ekrani silmek amaci ile kullanilabilir. Tabi kullanim alani bununla sınırlı degildir.
LDIR genel olarak son derece kullanisli bir komuttur.
DJNZ (Decrease and Jump if Not Zero
255 defa'ya kadar tekrarlanmasi istenen islemleri bir donguye sokmak icin kullanilabilir. Dongu kontrol degiskeni olarak B yazmaci kullanilir. B yazmacindaki sayi 0'dan buyuk oldugu surece, DJNZ komutu isletildiginde, komutun isaret ettigi adrese atlanir.
B yazmacindaki deger her DJNZ komutu calistirildiginda bir eksiltilir ve 0'a ulastiginda, DJNZ komutunun isaret ettigi adrese atlama yapilmaz. Onun yerine DJNZ komutundan bir sonraki komuta atlanir.
Ornegin asagidaki rutin NOP komutunu 128 kere calistirir:
LD B,128
islemyok:
NOP
DJNZ islemyok
RET