Bölüm 4: QFTASM ve Cogol
Mimariye Genel Bakış
Kısacası, bilgisayarımızın 16 bitlik bir asenkron RISC Harvard mimarisi vardır. Bir işlemci el ile oluşturulurken, bir RISC ( azaltılmış komut seti bilgisayarı ) mimarisi pratikte bir gerekliliktir. Bizim durumumuzda bu, opot sayısının az olduğu ve daha da önemlisi tüm talimatların çok benzer bir şekilde işlendiği anlamına gelir.
Başvuru için, Wireworld bilgisayarı , sadece talimatların yapıldığı ve hesaplamaları özel kayıtlar yazarak / okuyarak gerçekleştirildiği , taşımayla tetiklenen bir mimari kullandı MOV
. Her ne kadar bu paradigma uygulaması kolay bir mimariye yol açsa da sonuç sınırda kullanılamaz niteliktedir: tüm aritmetik / mantık / şartlı işlemler üç talimat gerektirir . Bizim için çok daha az ezoterik bir mimari yaratmak istediğimiz açıktı.
Kullanışlılığı arttırırken işlemcimizi basit tutmak için, birkaç önemli tasarım kararı aldık:
- Kayıt yok. RAM'deki her adres eşit olarak ele alınır ve herhangi bir işlem için herhangi bir argüman olarak kullanılabilir. Bir anlamda, bu, tüm RAM'lerin kayıtlar gibi ele alınabileceği anlamına gelir. Bu, özel bir yükleme / saklama talimatı olmadığı anlamına gelir.
- Benzer bir damarda, hafıza haritalama. Yazılan veya okunan her şey birleşik bir adresleme şemasını paylaşır. Bu, program sayacının (PC) adres 0 olduğu ve normal talimatlarla kontrol akış talimatları arasındaki tek farkın kontrol akış talimatlarının 0 adresini kullandığı anlamına gelir.
- Veri iletimde seri, depoda paralel. Bilgisayarımızın "elektron" temelli yapısı nedeniyle, verilerin seri küçük endian (ilk olarak en az anlamlı bit) biçiminde iletilmesi durumunda toplama ve çıkarma işlemlerinin uygulanması oldukça kolaydır. Dahası, seri veri, zamana göre hem gerçekten geniş hem de hantal olan hantal veri yollarına olan ihtiyacı ortadan kaldırır (verinin bir arada kalması için, veriyolunun tüm "şeritlerinin" aynı seyahat gecikmesini yaşaması gerekir).
- Harvard mimarisi, program belleği (ROM) ve veri belleği (RAM) arasında bölünme anlamına gelir. Bu, işlemcinin esnekliğini azaltmasına rağmen, boyut optimizasyonuna yardımcı olur: programın uzunluğu, ihtiyacımız olan RAM miktarından çok daha büyüktür, bu nedenle programı ROM'a ayırabiliriz ve ardından ROM'u sıkıştırmaya odaklanabiliriz. , salt okunur olduğunda çok daha kolaydır.
- 16 bit veri genişliği. Bu standart Tetris panelinden (10 blok) daha geniş olan ikisinin en küçük gücüdür. Bu bize -32768 ila +32767 veri aralığını ve maksimum 65536 komut uzunluğunu verir. (2 ^ 8 = 256 talimat, bir oyuncak işlemcinin yapmak isteyebileceği en basit şeyler için yeterlidir ancak Tetris için yeterli değildir.)
- Asenkron tasarım. Bilgisayarın zamanlamasını dikte eden merkezi bir saate (veya eşdeğer olarak birkaç saate) sahip olmak yerine, tüm verilere bilgisayar etrafında akarken verilere paralel olarak hareket eden bir "saat sinyali" eşlik eder. Bazı yollar diğerlerinden daha kısa olabilir ve bu durum merkezi olarak saatli bir tasarım için zorluklar doğursa da, asenkronize bir tasarım değişken zamanlı işlemlerle kolayca başa çıkabilir.
- Tüm talimatlar eşit büyüklüktedir. Her komutun, 3 işlenenli (değer değeri hedefi) 1 opod içerdiği bir mimarinin en esnek seçenek olduğunu düşündük. Bu, ikili veri işlemlerini ve koşullu hareketleri kapsar.
- Basit adresleme modu sistemi. Çeşitli adresleme modlarına sahip olmak, diziler veya özyineleme gibi şeyleri desteklemek için çok kullanışlıdır. Nispeten basit bir sistemle birkaç önemli adresleme modu uygulamayı başardık.
Genel bakış yazımızda mimarimizin bir örneği bulunmaktadır.
İşlevsellik ve ALU İşlemleri
Buradan, işlemcimizin hangi işlevselliğe sahip olması gerektiğini belirleme meselesi vardı. Her komutun çok yönlülüğünün yanı sıra uygulama kolaylığına da dikkat edildi.
Koşullu Hareketler
Koşullu hamleler çok önemlidir ve hem küçük hem de büyük ölçekli kontrol akışı olarak işlev görür. "Küçük ölçekli", belirli bir veri hareketinin yürütülmesini kontrol etme kabiliyetini belirtirken "büyük ölçekli", kontrol akışını herhangi bir rasgele kod parçasına transfer etmek için koşullu bir atlama işlemi olarak kullanılmasını ifade eder. Özel bir atlama işlemi yoktur, çünkü bellek haritalaması nedeniyle koşullu bir hareket hem verileri normal RAM'e kopyalayabilir hem de bir hedef adresini PC'ye kopyalayabilir. Benzer bir nedenden dolayı hem koşulsuz hareketleri hem de koşulsuz atlamalardan vazgeçmeyi seçtik: ikisi de TRUE'ya kodlanmış bir koşulla koşullu bir hareket olarak uygulanabilir.
İki farklı koşullu hareket türü seçtik: "sıfır değilse hareket et" ( MNZ
) ve "sıfırdan düşük hareket et" ( MLZ
). İşlevsel olarak, MNZ
verilerdeki herhangi bir bitin 1 olup olmadığını kontrol etmek için MLZ
miktarlar, işaret bitinin 1 olup olmadığını kontrol etmek için miktarlar sırasıyla denklemler ve karşılaştırmalar için kullanışlıdır. Nedeni, örneğin, "sıfır ise hareket" olarak diğerlerine göre, bu iki seçti ( MEZ
( "sıfırdan büyük ise hareket") ya da MGZ
oldu) MEZ
ise, boş bir sinyalden DOĞRU sinyali oluşturma gerektirecektir MGZ
gerektiren, daha karmaşık bir kontroldür işaret biti 0 iken en az bir bit 1 olacaktır.
Aritmetik
İşlemci tasarımına rehberlik etmesi açısından en önemli talimatlar, temel aritmetik işlemlerdir. Daha önce de belirttiğim gibi, toplama / çıkarma işlemlerinin kolaylığı ile belirlenen birleştirme seçimi ile küçük-endian seri verileri kullanıyoruz. İlk olarak en az anlamlı bitin gelmesiyle, aritmetik birimler taşıma bitini kolayca takip edebilir.
2'nin tamamlayıcı gösterimini negatif sayılar için kullanmayı seçtik, çünkü bu toplama ve çıkarma işlemlerini daha tutarlı hale getiriyor. Wireworld bilgisayarının 1'in tamamlayıcısını kullandığı dikkat çekiyor.
Toplama ve çıkarma, bilgisayarımızın yerel aritmetik desteğinin kapsamıdır (daha sonra tartışılacak olan bit değişimlerinin yanı sıra). Çarpma gibi diğer işlemler mimarimiz tarafından gerçekleştirilemeyecek kadar karmaşıktır ve yazılımda gerçekleştirilmelidir.
Bitsel İşlemler
Bizim işlemci vardır AND
, OR
ve XOR
beklediğiniz ne talimatlar. Bir NOT
talimattan ziyade , bir "ve-değil" ( ANT
) talimatına sahip olduk . Öğretimdeki zorluk NOT
yine, hücresel otomatlarda zor olan bir sinyal eksikliğinden sinyal oluşturması gerektiğidir. ANT
İlk bağımsız değişken biti 1 ise, ve ikinci bağımsız değişken bit Böylece, 0 olduğu takdirde yönerge 1 döndürür NOT x
eşdeğerdir ANT -1 x
(aynı zamanda XOR -1 x
). Ayrıca, ANT
çok yönlüdür ve maskelemede temel avantajı vardır: Tetris programında onu tetrominoları silmek için kullanırız.
Bit kaydırma
Bit kaydırma işlemleri, ALU tarafından yönetilen en karmaşık işlemlerdir. İki veri girişi alırlar: kaydırılacak bir değer ve kaydırılacak bir miktar. Karmaşıklıklarına rağmen (değişken kayma miktarı nedeniyle), bu işlemler Tetris'e dahil olan birçok "grafiksel" işlem dahil olmak üzere birçok önemli görev için çok önemlidir. Bit kaymaları ayrıca verimli çarpma / bölme algoritmalarının temelini oluşturur.
İşlemcimiz, "sola kaydırma" ( SL
), "sağa kaydırma mantığı" ( SRL
) ve "sağa kaydırma aritmetiği" ( SRA
) olmak üzere üç bit kaydırma işlemine sahiptir . İlk iki bit kaydırma ( SL
ve SRL
) yeni bitleri tüm sıfırlarla doldurur (yani, sağa kaydırılan negatif bir sayının artık negatif olmayacağı anlamına gelir). Vardiyanın ikinci argümanı 0 ila 15 aralığının dışındaysa, sonuç beklediğiniz gibi tüm sıfırlardır. Son bit kayması için, SRA
bit kayması girişin işaretini korur ve bu nedenle iki ile gerçek bir bölünme görevi görür.
Talimat Boru Hattı
Artık mimarinin bazı detaylarından bahsetmenin tam zamanı. Her bir CPU döngüsü aşağıdaki beş adımdan oluşur:
1. Mevcut talimatı ROM'dan alın
PC'nin mevcut değeri ROM'dan ilgili talimatı almak için kullanılır. Her komutun bir opcode ve üç operandı vardır. Her işlenen bir veri sözcüğü ve bir adresleme modundan oluşur. Bu parçalar ROM'dan okunurken birbirlerinden ayrılırlar.
Opcode, 11 tanesi atanmış 16 benzersiz opcode'u destekleyen 4 bit'tir:
0000 MNZ Move if Not Zero
0001 MLZ Move if Less than Zero
0010 ADD ADDition
0011 SUB SUBtraction
0100 AND bitwise AND
0101 OR bitwise OR
0110 XOR bitwise eXclusive OR
0111 ANT bitwise And-NoT
1000 SL Shift Left
1001 SRL Shift Right Logical
1010 SRA Shift Right Arithmetic
1011 unassigned
1100 unassigned
1101 unassigned
1110 unassigned
1111 unassigned
2. Önceki talimatın sonucunu (gerekirse) RAM’e yazınız.
Önceki komutun durumuna bağlı olarak (koşullu bir hareket için ilk argümanın değeri gibi), bir yazma yapılır. Yazmanın adresi, önceki talimatın üçüncü işleneni tarafından belirlenir.
Yazının talimat alındıktan sonra gerçekleştiğini not etmek önemlidir. Bu , dal hedefinden hemen sonraki talimatın (PC'ye yapılan herhangi bir işlem) dal hedefindeki ilk komut yerine gerçekleştirildiği dal gecikme yuvasının oluşturulmasına yol açar .
Bazı durumlarda (koşulsuz atlamalar gibi), dal gecikme yuvası optimize edilebilir. Diğer durumlarda yapamaz ve daldan sonraki komutlar boş bırakılmalıdır. Ayrıca, bu tür gecikme yuvası, oluşan bilgisayar artışını hesaba katmak için dalların, asıl hedef talimatından 1 adresi daha az olan bir dal hedefi kullanması gerektiği anlamına gelir.
Kısacası, önceki komutun çıktısı bir sonraki komut alındıktan sonra RAM'e yazıldığından, koşullu atlamaların onlardan sonra boş bir komut alması gerekir, aksi takdirde atlama için PC düzgün bir şekilde güncellenmez.
3. Mevcut komutun argümanlarına ilişkin verileri RAM'den okuyun.
Daha önce de belirtildiği gibi, üç işlenenden her biri hem veri sözcüğü hem de adresleme modundan oluşur. Veri sözcüğü, 16 bit, RAM ile aynı genişliktedir. Adresleme modu 2 bittir.
Adresleme modları, bunun gibi bir işlemci için önemli bir karmaşıklık kaynağı olabilir, çünkü birçok gerçek dünyadaki adresleme modu çok adımlı hesaplamalar içerir (ofset ekleme gibi). Aynı zamanda, çok yönlü adresleme modları işlemcinin kullanılabilirliğinde önemli bir rol oynar.
Kodlanmış sayıları operand olarak kullanma ve data adreslerini operand olarak kullanma kavramlarını birleştirmeye çalıştık. Bu, karşı tabanlı adresleme modlarının oluşturulmasına yol açtı: bir işlenenin adresleme modu, verilerin bir RAM okuma döngüsü etrafında kaç kez gönderilmesi gerektiğini gösteren bir sayıdır. Bu, doğrudan, doğrudan, dolaylı ve çift dolaylı adreslemeyi içerir.
00 Immediate: A hard-coded value. (no RAM reads)
01 Direct: Read data from this RAM address. (one RAM read)
10 Indirect: Read data from the address given at this address. (two RAM reads)
11 Double-indirect: Read data from the address given at the address given by this address. (three RAM reads)
Bu yeniden düzenleme yapıldıktan sonra, talimatın üç işleneni farklı rollere sahiptir. İlk işlenen genellikle bir ikili işleci için ilk argümandır, ancak geçerli komut bir koşullu hareket olduğunda koşul olarak da işlev görür. İkinci işlenen, bir ikili işleç için ikinci argüman olarak görev yapar. Üçüncü işlenen, komutun sonucu için hedef adres işlevi görür.
İlk iki komut veri olarak hizmet ettiğinden, üçüncü adres ise, adresleme modları hangi pozisyonda kullanıldıklarına bağlı olarak biraz farklı yorumlara sahiptir. Örneğin, doğrudan mod sabit RAM adresinden veri okumak için kullanılır (örneğin bir RAM okuma gereklidir), ancak acil durum modu sabit bir RAM adresine veri yazmak için kullanılır (RAM okuma gerekli olmadığından).
4. Sonucu hesaplayın
Opcode ve ilk iki işlenen, ikili bir işlem gerçekleştirmek için ALU’ya gönderilir. Aritmetik, bitsel ve vardiyalı işlemler için bu, ilgili işlemi yapmak anlamına gelir. Koşullu hamleler için, bu sadece ikinci işlenenin döndürülmesi anlamına gelir.
Opcode ve first operand, sonucu belleğe yazıp yazmamayı belirleyen koşulu hesaplamak için kullanılır. Koşullu hareketler söz konusu olduğunda, bu, işlenendeki herhangi bir bitin 1 (for MNZ
) olup olmadığını belirlemek veya işaret bitinin 1 (for MLZ
) olup olmadığını belirlemek anlamına gelir . Opcode koşullu bir hareket değilse, yazma işlemi her zaman gerçekleştirilir (koşul her zaman doğrudur).
5. Program sayacını arttırın
Son olarak, program sayacı okunur, artırılır ve yazılır.
PC artışının talimat okuma ve talimat yazma arasındaki konumu nedeniyle, bu, PC'yi 1 artıran bir komutun çalışmaması anlamına gelir. Bilgisayarı kendisine kopyalayan bir talimat, bir sonraki talimatın arka arkaya iki kez yürütülmesine neden olur. Ancak, talimat satırına dikkat etmiyorsanız, arka arkaya çoklu PC komutları, sonsuz döngü dahil olmak üzere karmaşık etkilere neden olabilir.
Tetris Assembly için görev
İşlemcimiz için QFTASM adlı yeni bir montaj dili oluşturduk. Bu montaj dili, bilgisayarın ROM'undaki makine koduyla 1'e 1'e karşılık gelir.
Herhangi bir QFTASM programı, her satırda bir tane olmak üzere bir dizi talimat olarak yazılır. Her satır şöyle biçimlendirilir:
[line numbering] [opcode] [arg1] [arg2] [arg3]; [optional comment]
Opcode Listesi
Daha önce de tartışıldığı gibi, her biri üç işlenen bulunan bilgisayar tarafından desteklenen on bir kod vardır:
MNZ [test] [value] [dest] – Move if Not Zero; sets [dest] to [value] if [test] is not zero.
MLZ [test] [value] [dest] – Move if Less than Zero; sets [dest] to [value] if [test] is less than zero.
ADD [val1] [val2] [dest] – ADDition; store [val1] + [val2] in [dest].
SUB [val1] [val2] [dest] – SUBtraction; store [val1] - [val2] in [dest].
AND [val1] [val2] [dest] – bitwise AND; store [val1] & [val2] in [dest].
OR [val1] [val2] [dest] – bitwise OR; store [val1] | [val2] in [dest].
XOR [val1] [val2] [dest] – bitwise XOR; store [val1] ^ [val2] in [dest].
ANT [val1] [val2] [dest] – bitwise And-NoT; store [val1] & (![val2]) in [dest].
SL [val1] [val2] [dest] – Shift Left; store [val1] << [val2] in [dest].
SRL [val1] [val2] [dest] – Shift Right Logical; store [val1] >>> [val2] in [dest]. Doesn't preserve sign.
SRA [val1] [val2] [dest] – Shift Right Arithmetic; store [val1] >> [val2] in [dest], while preserving sign.
Adresleme Modları
İşlenenlerin her biri hem veri değeri hem de adresleme hareketi içeriyor. Veri değeri -32768 ila 32767 aralığında bir ondalık sayı ile tanımlanır. Adresleme modu, veri değerine bir harflik bir ön ek ile tanımlanır.
mode name prefix
0 immediate (none)
1 direct A
2 indirect B
3 double-indirect C
Örnek kod
Beş satırda Fibonacci dizisi:
0. MLZ -1 1 1; initial value
1. MLZ -1 A2 3; start loop, shift data
2. MLZ -1 A1 2; shift data
3. MLZ -1 0 0; end loop
4. ADD A2 A3 1; branch delay slot, compute next term
Bu kod, Fibonacci dizisini, geçerli terimi içeren RAM adresi 1 ile hesaplar. 28657'den sonra hızla taşar.
Gri kod:
0. MLZ -1 5 1; initial value for RAM address to write to
1. SUB A1 5 2; start loop, determine what binary number to covert to Gray code
2. SRL A2 1 3; shift right by 1
3. XOR A2 A3 A1; XOR and store Gray code in destination address
4. SUB B1 42 4; take the Gray code and subtract 42 (101010)
5. MNZ A4 0 0; if the result is not zero (Gray code != 101010) repeat loop
6. ADD A1 1 1; branch delay slot, increment destination address
Bu program Gray kodunu hesaplar ve kodu 5. adreste başlayan kısmi adreslerde saklar. Bu program dolaylı adresleme ve koşullu bir atlama gibi birçok önemli özelliği kullanır. Sonuçtaki Gray kodu bir kez durduğunda durur 101010
, bu 56. adresdeki 51 girişi için olur.
Çevrimiçi tercüman
El'endia Starman burada çok faydalı bir çevrimiçi tercüman yarattı . Kodda ilerleyebilir, kesme noktalarını ayarlayabilir, RAM'e manuel yazma yapabilir ve RAM'i bir ekran olarak görselleştirebilirsiniz.
Cogol
Mimari ve montaj dili tanımlandıktan sonra, projenin "yazılım" tarafındaki bir sonraki adım Tetris için uygun olan daha yüksek bir dil oluşturulmasıydı. Böylece Cogol'u yarattım . Bu ad hem "COBOL" kelimesi hem de "C of Life of Game" in kısaltmasıdır, ancak Cogol'un bilgisayarımızın gerçek bir bilgisayara ne olduğunu C de belirtmekte fayda vardır.
Cogol, meclis dilinin hemen üstünde bir düzeyde var. Genel olarak, bir Cogol programındaki çoğu satır tek bir montaj satırına karşılık gelir, ancak dilin bazı önemli özellikleri vardır:
- Temel özellikler arasında atamalar ve daha okunaklı sözdizime sahip operatörler ile adlandırılmış değişkenler bulunur. Örneğin,
ADD A1 A2 3
olur z = x + y;
adresleri üzerine derleyici haritalama değişkenlerle.
- Gibi döngüleri
if(){}
, while(){}
ve do{}while();
bu nedenle derleyici dallanma işleme.
- Tetris kartı için kullanılan tek boyutlu diziler (işaretçi aritmetik ile).
- Alt yordamlar ve bir çağrı yığını. Bunlar, büyük kod parçalarının çoğaltılmasını önlemek ve özyinelemeyi desteklemek için kullanışlıdır.
Derleyici (sıfırdan yazdığım) çok basit / basit, fakat kısa derlenmiş bir program uzunluğu elde etmek için birkaç dil yapısını el ile optimize etmeye çalıştım.
Aşağıda, çeşitli dil özelliklerinin nasıl çalıştığına ilişkin bazı kısa bilgiler verilmiştir:
dizgeciklere
Kaynak kod, bir karakter içinde bitişik olmasına izin verilen basit kurallar kullanılarak doğrusal olarak (tek geçişli) belirtilir. Geçerli belirtecin son karakterine bitişik olmayan bir karakterle karşılaşıldığında, geçerli belirteç tamamlanmış sayılır ve yeni karakter yeni bir belirteç başlatır. Bazı karakterler ( {
veya gibi ,
) diğer karakterlere bitişik olamaz ve bu nedenle kendi belirteçleridir. Diğerlerinin ( >
veya veya benzeri =
) yalnızca sınıflarındaki diğer karakterlere bitişik olmalarına izin verilir ve bu nedenle >>>
, gibi ==
veya >=
benzeri olmayan simgeler oluşturabilir =2
. Boşluk karakterleri, belirteçler arasında bir sınırı zorlar, ancak sonuçta kendileri yer almazlar. Belirlemek için en zor karakter-
çünkü hem çıkarma hem de olumsuz olumsuzlamayı temsil edebilir ve bu nedenle bazı özel durumlar gerektirir.
ayrıştırma
Ayrıştırma da tek geçişli bir şekilde yapılır. Derleyici, farklı dil yapılarının her birini idare etmek için yöntemlere sahiptir ve çeşitli derleyici yöntemleri tarafından tüketildiklerinde belirteçler global belirteç listesinden çıkarılır. Derleyici hiç beklemediği bir belirteç görürse, bir sözdizimi hatası oluşturur.
Global Bellek Tahsisi
Derleyici, her global değişkene (word veya array) kendi belirlenmiş RAM adreslerini atar. my
Derleyicinin bunun için yer ayırmasını bilmesi için anahtar kelimeyi kullanarak tüm değişkenleri bildirmek gerekir. Adlandırılmış global değişkenlerden çok daha soğuk, sıfır adres hafıza yönetimidir. Pek çok talimat (özellikle şartlandırıcılar ve birçok dizi erişimi), ara hesaplamaları saklamak için geçici "çizik" adresleri gerektirir. Derleme işlemi sırasında derleyici, çizik adreslerini gerektiği şekilde tahsis eder ve tahsis eder. Derleyici daha fazla sıfırlama adresine ihtiyaç duyuyorsa, daha fazla RAM'i sıfırlama adresi olarak ayırır. Her bir kazı kazan adresi birçok kez kullanılacak olsa da, bir programın sadece birkaç kazıma adresi gerektirdiğine inanıyorum.
IF-ELSE
tablolar
if-else
İfadelerin sözdizimi standart C formudur:
other code
if (cond) {
first body
} else {
second body
}
other code
QFTASM'ye dönüştürüldüğünde, kod şöyle düzenlenir:
other code
condition test
conditional jump
first body
unconditional jump
second body (conditional jump target)
other code (unconditional jump target)
İlk vücut yürütülürse, ikinci vücut atlanır. İlk gövde atlanırsa, ikinci gövde yürütülür.
Montajda, bir koşullandırma testi genellikle sadece bir çıkarma işlemidir ve sonucun işareti atlamanın yapılıp yapılmayacağını veya gövdeyi çalıştırıp çalıştırmayacağını belirler. Bir MLZ
komut, >
ya da gibi eşitsizlikleri ele almak için kullanılır <=
. Farklılık sıfır olmadığında (ve dolayısıyla argümanlar eşit olmadığında) vücutta atladığı MNZ
için bir komut kullanılır ==
. Çoklu ifade şartlamaları şu anda desteklenmiyor.
Eğer else
durum gözardı, koşulsuz atlama da ihmal edilir ve QFTASM kodu şöyle:
other code
condition test
conditional jump
body
other code (conditional jump target)
WHILE
tablolar
while
İfadelerin sözdizimi de standart C formudur:
other code
while (cond) {
body
}
other code
QFTASM'ye dönüştürüldüğünde, kod şöyle düzenlenir:
other code
unconditional jump
body (conditional jump target)
condition test (unconditional jump target)
conditional jump
other code
Koşul testi ve koşullu atlama, bloğun sonundadır; bu, bloğun her bir yürütülmesinden sonra yeniden yürütüldüğü anlamına gelir. Koşul yanlış döndüğünde, vücut tekrar edilmez ve döngü sona erer. Döngü yürütme başlangıcında, kontrol akışı döngü gövdesi üzerinden koşul koduna atlar, bu nedenle koşul ilk kez yanlışsa vücut asla çalıştırılmaz.
Bir MLZ
komut, >
ya da gibi eşitsizlikleri ele almak için kullanılır <=
. if
İfadelerden farklı olarak , bir MNZ
talimat kullanılır !=
, çünkü fark sıfır olmadığında (ve dolayısıyla argümanlar eşit olmadığında) vücuda atlar.
DO-WHILE
tablolar
Arasındaki tek fark while
ve do-while
bir olduğunu do-while
her zaman en az bir kez yürütülür böylece döngü gövdesi başlangıçta atlanır değildir. do-while
Döngünün hiçbir zaman atlanması gerekmediğini bildiğimde, genellikle birkaç satırlık montaj kodunu kaydetmek için ifadeler kullanırım .
Diziler
Tek boyutlu diziler, bitişik bellek blokları olarak uygulanır. Tüm diziler beyanlarına göre sabit uzunlukludur. Diziler şöyle bildirildi:
my alpha[3]; # empty array
my beta[11] = {3,2,7,8}; # first four elements are pre-loaded with those values
Dizi için, bu, 15-18 adreslerinin dizi için nasıl ayrıldığını gösteren olası bir RAM eşlemesidir:
15: alpha
16: alpha[0]
17: alpha[1]
18: alpha[2]
Etiketli adres alpha
, bulunduğu yere bir işaretçi ile doldurulur alpha[0]
, bu nedenle, buradaki adres 15, 16 değerini içerir. alpha
Değişken, bu diziyi bir yığın olarak kullanmak istiyorsanız, muhtemelen bir yığın işaretçisi olarak Cogol kodunun içinde kullanılabilir. .
Bir dizinin elemanlarına erişim standart array[index]
notasyon ile yapılır . Eğer değer index
sabitse, bu referans otomatik olarak o elementin mutlak adresi ile doldurulur. Aksi halde, istenen mutlak adresi bulmak için bazı göstergeler aritmetik (sadece ekleme) yapar. Bunun gibi dizin oluşturmayı yuvalamak da mümkündür alpha[beta[1]]
.
Alt Rutinler ve Arama
Alt yordamlar, birden fazla bağlamdan çağrılabilen, kodun çoğaltılmasını önleyen ve özyinelemeli programların oluşturulmasını sağlayan kod bloklarıdır. Fibonacci sayıları (temelde en yavaş algoritma) üretmek için özyinelemeli bir alt yordam içeren bir program:
# recursively calculate the 10th Fibonacci number
call display = fib(10).sum;
sub fib(cur,sum) {
if (cur <= 2) {
sum = 1;
return;
}
cur--;
call sum = fib(cur).sum;
cur--;
call sum += fib(cur).sum;
}
Anahtar kelimeyle sub
bir alt yordam bildirilir ve programın herhangi bir yerine bir alt yordam yerleştirilebilir. Her alt yordam, bağımsız değişkenler listesinin bir parçası olarak bildirilen birden fazla yerel değişkene sahip olabilir. Bu argümanlara varsayılan değerler de verilebilir.
Özyinelemeli çağrıları işlemek için bir alt yordamın yerel değişkenleri yığında depolanır. RAM'deki son statik değişken, çağrı yığını işaretçisidir ve bundan sonraki tüm hafıza, çağrı yığını olarak işlev görür. Bir alt yordam çağrıldığında, çağrı yığını üzerinde tüm yerel değişkenleri ve dönüş (ROM) adresini içeren yeni bir çerçeve oluşturdu. Programdaki her alt yordam, bir işaretçi olarak görev yapması için tek bir statik RAM adresi verilir. Bu işaretçi, çağrı yığındaki alt yordamın "geçerli" çağrısının konumunu verir. Bir yerel değişkene referans vermek, bu statik göstergenin değerini ve bu belirli yerel değişkenin adresini vermek için bir ofset kullanarak yapılır. Çağrı yığında da yer alan, statik işaretçinin önceki değeridir. İşte'
RAM map:
0: pc
1: display
2: scratch0
3: fib
4: scratch1
5: scratch2
6: scratch3
7: call
fib map:
0: return
1: previous_call
2: cur
3: sum
Alt rutinler hakkında ilginç olan şeylerden biri, herhangi bir özel değer vermemeleridir. Aksine, alt rutininin yerel değişkenlerinin tümü, alt rutin gerçekleştirildikten sonra okunabilir, böylece bir alt rutin çağrısından çeşitli veriler çıkarılabilir. Bu, işaretçiyi belirli bir alt rutin çağrısı için saklamak suretiyle gerçekleştirilir; bu, daha sonra yerel değişkenlerin herhangi birini (yakın zamanda dağıtılan) yığın çerçevesinden kurtarmak için kullanılabilir.
Her biri call
anahtar sözcüğü kullanarak bir alt yordam çağırmanın birden çok yolu vardır :
call fib(10); # subroutine is executed, no return vaue is stored
call pointer = fib(10); # execute subroutine and return a pointer
display = pointer.sum; # access a local variable and assign it to a global variable
call display = fib(10).sum; # immediately store a return value
call display += fib(10).sum; # other types of assignment operators can also be used with a return value
Bir alt rutin çağrı için herhangi bir sayıda değer argüman olarak verilebilir. Sağlanmayan herhangi bir argüman, varsa varsayılan değeriyle doldurulur. Sağlanmayan ve varsayılan değeri olmayan bir argüman silinmez (yönergeleri / zamanı kaydetmek için) bu nedenle, alt rutinin başlangıcında herhangi bir değere potansiyel olarak sahip olabilir.
İşaretçiler, alt yordamın birden çok yerel değişkenine erişmenin bir yoludur, ancak işaretçinin yalnızca geçici olduğunu not etmek önemlidir: başka bir alt yordam çağrısı yapıldığında işaretçinin işaret ettiği veriler yok edilir.
Hata Ayıklama Etiketleri
{...}
Bir Cogol programındaki herhangi bir kod bloğu, çok kelimeli açıklayıcı bir etiketten önce gelebilir. Bu etiket derlenmiş montaj koduna bir yorum olarak eklenmiştir ve belirli kod parçalarını bulmayı kolaylaştırdığından hata ayıklamak için çok yararlı olabilir.
Şube Gecikme Yuvası Optimizasyonu
Derlenmiş kodun hızını arttırmak için Cogol derleyici, QFTASM kodu üzerinden son bir geçiş olarak gerçekten temel bir gecikme yuvası optimizasyonu gerçekleştirir. Boş dal gecikme yuvasına sahip olan koşulsuz herhangi bir atlama için, gecikme yuvası atlama hedefindeki ilk komut ile doldurulabilir ve atlama hedefi bir sonraki talimata bir noktaya kadar artırılır. Bu, genellikle koşulsuz bir sıçrama yapıldığında her bir çevrimi korur.
Cogol'da Tetris kodunun yazılması
Son Tetris programı Cogol'da yazılmıştır ve kaynak kodu burada mevcuttur . Derlenmiş QFTASM kodu burada mevcuttur . Kolaylık sağlamak için burada bir kalıcı bağlantı sağlanmıştır: QFTASM'da Tetris . Amaç derleme kodunu (Cogol kodunu değil) golf oynamak olduğu için, sonuçta elde edilen Cogol kodu hantaldır. Programın pek çok kısmı normal olarak alt rutinlerde yer alacak, ancak bu alt rutinler kodun çoğaltılmasının,call
ifadeleri. Son kod, ana koda ek olarak yalnızca bir alt rutine sahiptir. Ek olarak, birçok dizi çıkarıldı ve eşdeğerde uzun bir bireysel değişkenler listesiyle veya programdaki çok sayıda kodlanmış sayı ile değiştirildi. Son derlenen QFTASM kodu 300 talimatın altında olmasına rağmen Cogol kaynağının kendisinden biraz daha uzun olsa da.