Conway'in Yaşam Oyunda bir Tetris oyunu oynayın


994

İşte teorik bir soru - önemsiz olanı bile değil, her durumda kolay bir cevap vermeyen bir soru.

Conway'in Yaşam Oyununda, Yaşam Oyununun diğer herhangi bir Yaşam Oyunu kural sistemini simüle etmesine izin veren metapiksel gibi yapılar da vardır. Ayrıca, Hayat Oyununun Turing'in tamamlandığı bilinmektedir.

Göreviniz Conway'in Tetris oyununun oynamasına izin verecek yaşam oyununun kurallarını kullanarak hücresel bir otomat oluşturmaktır.

Programınız, kesmeyi (örneğin bir parçayı sola veya sağa hareket ettirmek, düşürmek, döndürmek veya rasgele ızgaraya yerleştirmek için yeni bir parça oluşturmak için) belirli bir nesilde otomatiğin durumunu manuel olarak değiştirerek girdi alır. Bekleme süresi olarak belirli bir nesiller sayısı ve sonucu otomatın bir yerinde gösteriliyor. Görüntülenen sonuç, gözle görülür bir Tetris ızgarasına benzemelidir.

Programınız, sırayla (daha yüksek kriterler için tiebreaker olarak hareket eden düşük kriterler ile) sıralanacaktır:

  • Sınırlayıcı kutu boyutu - verilen çözümü tamamen içeren en küçük alana sahip dikdörtgen kutu kazanır.

  • Girdide daha küçük değişiklikler - bir kesinti kazanma işlemi için manuel olarak ayarlanması gereken en az hücre (odanızdaki en kötü durumda)

  • En hızlı işlem - simülasyondaki bir onay işaretini ilerleten en az nesiller kazanır.

  • İlk canlı hücre sayısı - daha küçük sayı kazanır.

  • İlk mesaj gönderen - önceki mesaj kazanır.


95
“Gösterilebilir şekilde çalışan bir örnek”, birkaç saat içinde akan bir şey mi, yoksa evrenin sıcağı ölümüne kadar sürecek olsa bile, doğru olduğu kanıtlanabilecek bir şey mi?
Peter Taylor

34
Böyle bir şeyin mümkün ve oynanabilir olduğuna eminim. Sadece çok az sayıda insan, muhtemelen dünyadaki daha ezoterik "montaj dillerinden" hangisini programlayabilecek uzmanlığa sahip.
Justin L.

58
Bu zorluk üzerinde çalışılıyor! Sohbet odası | İlerleme | Blog
mbomb007

49
Bu sabah 5:10 (9:10 UTC) itibariyle, bu soru PPCG tarihinde 100 oy alan ve bir cevap almadan ilk oyu! Herkese tebrikler.
Joe Z,

76
Bunu çözmeye çalışıyorum ... Şimdi, yattığımda her yerde paraşütler görüyorum, dev bir karmaşa içinde çarpışıyorlar. Uykularım, nabzı kesen pentadekatlonların yolumu tıkadığı ve Herschels'in beni emmek için geliştiği kabuslarla doludur. John Conway, ... benim için dua edin
dim

Yanıtlar:


938

Bu bir arayış olarak başladı ancak bir odyssey olarak sona erdi.

Tetris İşlemci Görevi, 2.940.928 x 10.295.296

Desen dosyası, tüm görkemiyle, burada tarayıcıda görüntülenebilir burada bulunabilir .

Bu proje, son 1 ve 1/2 yıl boyunca birçok kullanıcının çabalarının sonucudur. Takımın kompozisyonu zaman içinde değişmiş olsa da, katılımcılar yazılı olarak aşağıdaki gibidir:

Ayrıca 7H3_H4CK3R, Conor O'Brien ve bu zorluğun çözümü için çaba harcayan diğer birçok kullanıcılara teşekkürlerimizi sunmak istiyoruz .

Bu işbirliğinin eşi benzeri görülmemiş kapsamı nedeniyle, bu cevap, ekibin üyeleri tarafından yazılan birden fazla cevaba bölünmüştür. Her üye, kabaca projeye en çok dâhil oldukları alanlara karşılık gelen belirli alt başlıklar hakkında yazacaktır.

Lütfen takımın tüm üyelerine herhangi bir öneri veya ödül dağıtın.

İçindekiler

  1. genel bakış
  2. Metapikeller ve VarLife
  3. Donanım
  4. QFTASM ve Cogol
  5. Meclis, Çeviri ve Gelecek
  6. Yeni Dil ve Derleyici

Ayrıca , çözümümüzün bir parçası olarak yazdığımız tüm kodları koyduğumuz GitHub organizasyonumuzu da inceleyin . Sorular geliştirme sohbet odamıza yönlendirilebilir .


Bölüm 1: Genel Bakış

Bu projenin altında yatan fikir soyutlamadır . Doğrudan Yaşam'da bir Tetris oyunu geliştirmek yerine, soyutlamayı yavaş yavaş bir dizi adımda hızlandırdık. Her katmanda, Yaşamın zorluklarından daha uzaklaşır ve diğerlerinin programlaması kolay olan bir bilgisayarın yapımına daha da yaklaşırız.

İlk önce, OTCA metapixlerini bilgisayar temeli olarak kullandık . Bu metapikseller herhangi bir "yaşam benzeri" kuralı taklit edebilir. Wireworld ve Wireworld bilgisayarı bu proje için önemli bir ilham kaynağı oldu, bu yüzden metapiksellerle benzer bir yapı oluşturmaya çalıştık. Wireworld'ü OTCA metapixleri ile taklit etmek mümkün olmamakla birlikte, farklı metapiksellere farklı kurallar atamak ve tellere benzer şekilde çalışan metapiksel düzenlemeler oluşturmak mümkündür.

Bir sonraki adım, bilgisayarın temelini oluşturmak için çeşitli temel mantık kapıları inşa etmekti. Zaten bu aşamada gerçek dünya işlemci tasarımına benzer konseptlerle uğraşıyoruz. İşte bir OR geçidi örneği, bu görüntüdeki her bir hücre aslında bir OTCA metapixel'ı. “Elektronların” (her biri tek bir veri parçasını temsil eder) kapıya girip çıktığını görebilirsiniz. Bilgisayarımızda kullandığımız farklı metapiksel türlerinin tümünü de görebilirsiniz: siyah arka plan olarak B / S, mavi olarak B1 / S, yeşil olarak B2 / S ve kırmızı olarak B12 / S1.

görüntü

Buradan işlemcimiz için bir mimari geliştirdik. Hem ezoterik hem de mümkün olduğunca kolay uygulanabilir bir mimari tasarlamak için büyük çaba harcadık. Wireworld bilgisayarı basit bir taşımacılıkla tetiklenen mimariyi kullanırken, bu proje birden fazla opcodes ve adresleme modları ile tamamlanmış çok daha esnek bir RISC mimarisi kullanıyor. İşlemcimizin yapımını yönlendiren QFTASM (Tetris'in Görevi) adı verilen bir montaj dili oluşturduk.

Bilgisayarımız aynı zamanda asenkrondir, yani bilgisayarı kontrol eden global bir saat yoktur. Aksine, verilere bilgisayarın etrafında akarken bir saat sinyali eşlik eder; bu, yalnızca yerel olarak odaklanmamız gerekir, ancak bilgisayarın genel zamanlamalarına değil.

İşlemci mimarimizin bir örneği:

görüntü

Buradan sadece Tetris'i bilgisayara uygulama meselesi. Bunu başarmak için, yüksek seviyeli dili QFTASM'a derlemek için birçok yöntem üzerinde çalıştık. Geliştirilmekte olan ikinci, daha gelişmiş bir dil olan Cogol adında temel bir dile sahibiz ve son olarak yapım aşamasında bir GCC arka ucuna sahibiz. Mevcut Tetris programı Cogol'dan yazılmış / derlenmiştir.

Son Tetris QFTASM kodu oluşturulduktan sonra, son adımlar bu koddan ilgili ROM'a ve daha sonra metapiksellerden temel Yaşam Oyuna kadar bir araya getirilerek yapımımızı tamamladı.

Tetris Koşu

Bilgisayarla uğraşmadan Tetris oynamak isteyenler için , QFTASM yorumlayıcısında Tetris kaynak kodunu çalıştırabilirsiniz . Oyunun tamamını görüntülemek için RAM ekran adreslerini 3-32 olarak ayarlayın. İşte rahatınız için bir kalıcı bağlantı: QFTASM'deki Tetris .

Oyun özellikleri:

  • 7 tetromino
  • Hareket, dönme, yumuşak damlalar
  • Çizgi temizler ve puanlama
  • Önizleme parçası
  • Oyuncu girişleri rastgele enjekte eder

Görüntüle

Bilgisayarımız Tetris kartını hafızasında bir ızgara olarak temsil ediyor. 10-31 arasındaki adresler panoyu, 5-8 numaralı adresler önizleme parçasını görüntüler ve 3. adres, puanı içerir.

Giriş

Oyuna giriş, RAM adres 1 içeriğinin manuel olarak düzenlenmesiyle gerçekleştirilir. QFTASM yorumlayıcısını kullanarak, bu adres 1'e doğrudan yazma yapmak anlamına gelir. Her hamle sadece bir bit RAM'in düzenlenmesini gerektirir ve bu giriş kaydı giriş olayı okunduktan sonra otomatik olarak silinir.

value     motion
   1      counterclockwise rotation
   2      left
   4      down (soft drop)
   8      right
  16      clockwise rotation

Skor sistemi

Tek bir seferde birden fazla çizgiyi temizlemek için bonus kazanırsınız.

1 row    =  1 point
2 rows   =  2 points
3 rows   =  4 points
4 rows   =  8 points

14
@ Christopher2EZ4RTZ Bu genel bakış yazısı, pek çok proje üyesinin (genel bakış yazısının asıl yazımı dahil) yaptığı çalışmaları ayrıntılarıyla anlatmaktadır. Bu nedenle, CW olması uygundur. Aynı zamanda bir kişinin iki direğe sahip olmasından da kaçınmaya çalışıyorduk, çünkü bu onların haksız bir miktar rep almasına neden olacaktı, çünkü rep'leri eşit tutmaya çalışıyoruz.
Mego

28
Her şeyden önce +1, çünkü bu delicesine harika bir başarı (özellikle sadece tetris yerine, yaşam oyununda bir bilgisayar kurduğunuzdan). İkincisi, bilgisayar ne kadar hızlı ve tetris oyunu ne kadar hızlı? Uzaktan bile oynanabilir mi? (tekrar: bu harika)
Sokratik Anka,

18
Bu ... bu tamamen delilik. Hemen tüm cevapları + 1'leyin.
scottinet

28
Küçük ödülleri dağıtmak isteyen herkes için cevaplar üzerine bir uyarı: her seferinde (500'e ulaşana kadar) ödül miktarınızı ikiye katlamanız gerekir; bu nedenle, bu tutar 500 rep olmadığı sürece, tek bir kişi her cevabı aynı miktarda veremez.
Martin Ender

23
Bu, çok az şey anlarken yaşadığım en harika şey.
Mühendis Toast

678

Bölüm 2: OTCA Metapixel ve VarLife

OTCA Metapiksel

OTCA metapixel
( Kaynak )

OTCA Metapixel herhangi Yaşam benzeri hücresel otomatlar simüle etmek için kullanılabilir Conway'in Hayat oyununda bir yapıdır. LifeWiki'nin (yukarıda bağlantılı) dediği gibi

OTCA metapixel Brice Due tarafından yapılan bir 2048 x 2048 dönem 35328 birim hücredir ... Herhangi bir Yaşam benzeri hücresel otomat taklit yeteneği ve yakınlaştırıldığında, ON olduğu gerçeği de dahil olmak üzere birçok avantajı vardır. ve OFF hücreleri ayırt etmek kolaydır ...

Ne Yaşam benzeri hücresel otomata burada demektir hücreler doğarlar ve hücreler kendi sekiz komşu hücrelerin birçok hayattadır şekline göre hayatta olduğunu aslında. Bu kuralların sözdizimi aşağıdaki gibidir: bir B ardından doğuma neden olacak canlı komşu sayıları, ardından bir eğik çizgi, ardından S, ardından hücreyi canlı tutacak canlı komşu sayıları takip eder. Biraz endişe, bu yüzden bir örnek yardımcı olacağını düşünüyorum. Kanonik Yaşam Oyunu, üç canlı komşusu olan herhangi bir ölü hücrenin canlı olacağını ve iki veya üç canlı komşu olan herhangi bir canlı hücrenin canlı kalacağını söyleyen B3 / S23 kuralı ile temsil edilebilir. Aksi takdirde, hücre ölür.

2048 x 2048 hücre olmasına rağmen, OTCA metapixel gerçekte 2058 x 2058 hücre sınırlayıcı bir kutuya sahiptir, bunun nedeni, çapraz komşularıyla her yöne beş hücre ile çakışmasıdır . Üst üste binen hücreler, üzerinde bulunan metacells'e komşuları işaret etmek için yayılan planörlere müdahale eder - böylece diğer metapixlere karışmazlar veya süresiz olarak uçmazlar. Doğum ve hayatta kalma kuralları, metapikselin sol tarafındaki hücrelerin özel bir bölümünde, iki sütun boyunca belirli pozisyonlarda yiyeceğin varlığı veya yokluğu ile (biri doğum için, diğeri hayatta kalmak için) kodlanır. Komşu hücrelerin durumunu tespit etmeye gelince, işte böyle:

Bir 9-LWSS akımı daha sonra hücrenin etrafında saat yönünde ilerleyerek, bir balbit reaksiyonunu tetikleyen her bir bitişik 'on' hücre için bir LWSS kaybediyor. Eksik LWSS sayısı, ön LWSS'nin konumunu, başka bir LWSS'yi ters yönden çarparak tespit ederek sayılır. Bu çarpışma, doğum / hayatta kalma durumunun olmadığını gösteren yiyiciler varsa, bir veya iki balbit reaksiyonunu tetikleyen planörleri serbest bırakır.

OTCA metapikselinin her bir yönünün daha ayrıntılı bir diyagramı orijinal web sitesinde bulunabilir: Nasıl Çalışır? .

VarLife

Herhangi bir hücrenin yaşam benzeri kurallara göre hareket etmesini sağlayabileceğiniz çevrimiçi bir Yaşam benzeri kural simülatörü oluşturdum ve buna "Yaşamın Varyasyonları" adını verdim. Daha kısa olması için bu isim "VarLife" olarak kısaltıldı. İşte bir ekran görüntüsü (buraya link: http://play.starmaninnovations.com/varlife/BeeHkfCpNR ):

VarLife ekran görüntüsü

Önemli özellikleri:

  • Hücreleri canlı / ölü arasında değiştirin ve tahtayı farklı kurallarla boyayın.
  • Simülasyonu başlatma ve durdurma ve bir seferde bir adım yapma yeteneği. Ayrıca, saniye başına keneler ve onay kutusu başına milisaniye cinsinden belirlenen hızda, belirli sayıda adımı mümkün olduğu kadar hızlı veya daha yavaş yapmak da mümkündür.
  • Tüm canlı hücreleri temizleyin veya kartı tamamen boş bir duruma getirin.
  • Hücre ve tahta boyutlarını değiştirebilir ve ayrıca yatay ve / veya dikey olarak toroidal sarmayı sağlar.
  • Permalink'ler (URL’deki tüm bilgileri kodlayanlar) ve kısa URL’ler (çünkü bazen çok fazla bilgi vardır, ancak yine de güzeldirler).
  • B / S özellikleri, renkleri ve isteğe bağlı rastgelelik ile kural kümeleri.
  • Ve son fakat kesinlikle en az değil, gif oluşturma!

Gif-render özelliği benim en sevdiğim şeydi, çünkü uygulamak için çok fazla işim vardı, bu yüzden sabah 7'de nihayet kırdığımda gerçekten tatmin ediciydi ve VarLife yapılarını başkalarıyla paylaşmayı çok kolaylaştırdı. .

Temel VarLife Devresi

Sonuçta, VarLife bilgisayarı sadece dört hücre tipine ihtiyaç duyuyor! Sekiz devlet, ölü / yaşayan devletlerin sayımında bulunur. Onlar:

  • B / S hücreleri asla canlı olamayacağından, tüm bileşenler arasında tampon görevi gören B / S (siyah / beyaz).
  • Sinyalleri yaymak için kullanılan ana hücre tipi olan B1 / S (mavi / mavi).
  • Esas olarak sinyal kontrolü için kullanılan B2 / S (yeşil / sarı), geri yayılmamasını sağlar.
  • B12 / S1 (kırmızı / turuncu), sinyalleri çaprazlamak ve bir miktar veriyi depolamak gibi birkaç özel durumda kullanılır.

VarLife'ı zaten kodlanmış olan kurallarla açmak için bu kısa URL'yi kullanın: http://play.starmaninnovations.com/varlife/BeeHkfCpNR .

telleri

Farklı özelliklere sahip birkaç farklı tel tasarımı vardır.

Bu, yeşil şeritlerle çevrelenmiş mavi bir şerit olan VarLife'ın en kolay ve en temel telidir.

temel tel
Kısa url: http://play.starmaninnovations.com/varlife/WcsGmjLiBF

Bu tel tek yönlüdür. Yani, ters yönde hareket etmeye çalışan herhangi bir sinyali öldürür. Aynı zamanda temel telden daha dar bir hücredir.

tek yönlü tel
Kısa URL: http://play.starmaninnovations.com/varlife/ARWgUgPTEJ

Köşegen teller de var, ancak pek kullanılmıyorlar.

çapraz tel
Kısa url: http://play.starmaninnovations.com/varlife/kJotsdSXIj

kapıları

Her bir kapıyı inşa etmenin aslında birçok yolu var, bu yüzden her türden sadece bir örnek göstereceğim. Bu ilk gif, sırasıyla AND, XOR ve OR kapılarını gösterir. Buradaki temel fikir, yeşil bir hücrenin bir AND gibi davrandığını, mavi bir hücrenin XOR gibi davrandığını ve kırmızı bir hücrenin bir OR gibi davrandığını ve etrafındaki diğer tüm hücrelerin akışını düzgün bir şekilde kontrol etmek için orada olduklarıdır.

AND, XOR, VEYA lojik kapılar
Kısa url: http://play.starmaninnovations.com/varlife/EGTlKktmeI

"ANT geçidi" olarak kısaltılmış olan AND-NOT geçidi, hayati bir bileşen olduğu ortaya çıktı. A'dan bir sinyal alan ve yalnızca B'den hiçbir sinyal yoksa, bu nedenle "A AND NOT B" olan bir kapıdır.

AND-NOT geçidi
Kısa url: http://play.starmaninnovations.com/varlife/RsZBiNqIUy

Tam olarak bir geçit olmasa da , bir tel geçiş döşemesi hala çok önemlidir ve olması çok faydalıdır.

tel geçişi
Kısa url: http://play.starmaninnovations.com/varlife/OXMsPyaNTC

Bu arada, burada NOT geçidi yok. Bunun nedeni, gelen bir sinyal olmadan, mevcut bilgisayar donanımının gerektirdiği zamanlamalardaki çeşitlilikle iyi çalışmayan sabit bir çıktı üretilmesi gerekir. Zaten onsuz gayet iyi anlaştık.

Ayrıca, birçok bileşen, 11'den 11 sınırlayıcı kutuya (bir kiremit ) içine döşenmek üzere sinyallerin alındığı ve kiremitten ayrılmak için kiremit girme sinyallerinin alınması için tasarlandı. Bu, bileşenleri sıralamak veya zamanlamak için kabloları ayarlamaktan endişe etmeden gerektiğinden tokatlanmasını kolaylaştırır.

Devre bileşenlerini keşfetme sürecinde keşfedilen / inşa edilen diğer kapıları görmek için, bu blog gönderisine PhiNotPi: Building Blocks: Logic Gates .

Gecikme Bileşenleri

Bilgisayar donanımını tasarlama sürecinde KZhang, aşağıda gösterilen çoklu gecikme bileşenlerini tasarladı.

4 tıklama gecikmesi: Kısa URL: http://play.starmaninnovations.com/varlife/gebOMIXxdh
4 kene gecikmesi

5 kene gecikmesi: Kısa url: http://play.starmaninnovations.com/varlife/JItNjJvnUB
5 kene gecikmesi

8 tik gecikmesi (üç farklı giriş noktası): Kısa URL: http://play.starmaninnovations.com/varlife/nSTRaVEDvA
8 kene gecikmesi

11-tick gecikmesi: Kısa url: http://play.starmaninnovations.com/varlife/kfoADussXA
11 kene gecikmesi

12 tik gecikme: Kısa url: http://play.starmaninnovations.com/varlife/bkamAfUfud
12 kene gecikmesi

14 tik gecikmesi: Kısa url: http://play.starmaninnovations.com/varlife/TkwzYIBWln
14 kene gecikmesi

(İle karşılaştırarak doğrulanmış 15-kene gecikmesi bu ): Kısa url: http://play.starmaninnovations.com/varlife/jmgpehYlpT
15 kene gecikmesi

VarLife'daki temel devre bileşenleri için bu kadar! Bilgisayarın ana devresi için KZhang donanım donanımına bakınız !


4
VarLife bu projenin en etkileyici bölümlerinden biridir; Wireworld'ün olağanüstü olması ile karşılaştırıldığında çok yönlülük ve basitlik . OTCA Metapixel gereğinden çok daha büyük görünüyor, ancak golf oynamak için herhangi bir girişimde bulunuldu mu?
primo

@primo: Dave Greene bunun üzerinde çalışıyor, öyle görünüyor. chat.stackexchange.com/transcript/message/40106098#40106098
El'endia Starman 21:17

6
Evet, bu hafta sonu 512x512 HashLife dostu bir metacell'in kalbinde iyi bir ilerleme kaydedildi ( conwaylife.com/forums/viewtopic.php?f=&p=51287#p51287 ). Metacell, bir “piksel” alanının ne kadar büyük olduğunu, uzaklaştığınızda hücrenin durumunu belirtmek istediğine bağlı olarak biraz daha küçük yapılabilir. Golly'nin HashLife algoritması bilgisayarı çok daha hızlı çalıştırabildiğinden, kesinlikle 2 ^ N boyutlu bir karoda durmaya değer görünüyor.
Dave Greene

2
Teller ve kapılar daha az "israf" şeklinde uygulanamaz mı? Bir elektron bir planör veya bir uzay gemisi (yöne bağlı olarak) ile temsil edilir. Onları yönlendiren (ve gerekirse birinden diğerine değişen) düzenlemeler ve planörlerle çalışan bazı kapılar gördüm. Evet, daha fazla yer kaplarlar, tasarım daha karmaşıktır ve zamanlamanın kesin olması gerekir. Ancak bu temel yapı taşlarına sahip olduğunuzda, bir araya getirmek için yeterince kolay olmalılar ve OTCA kullanılarak uygulanan VarLife'dan daha az yer kaplayacaklardı. O da daha hızlı koşardı.
Heimdall

@Heimdall Her ne kadar iyi çalışsa da, Tetris oynarken iyi görünmezdi.
MilkyWay90

649

Bölüm 3: Donanım

Mantık kapıları bilgimiz ve işlemcinin genel yapısı sayesinde, bilgisayarın tüm bileşenlerini tasarlamaya başlayabiliriz.

Demultiplexer

Demultiplexer veya demux, ROM, RAM ve ALU için çok önemli bir bileşendir. Bir giriş sinyalini, verilen bazı seçici verilere dayanarak, birçok çıkış sinyalinden birine yönlendirir. 3 ana bölümden oluşur: paralel-dönüştürücüye seri, bir sinyal denetleyicisi ve bir saat sinyali ayırıcı.

Seri seçici verilerini "paralel" olarak dönüştürerek başlıyoruz. Bu, verilerin stratejik olarak bölünmesi ve geciktirilmesiyle yapılır, böylece en soldaki veri bitinin saat sinyalini en soldaki 11x11 karede kesiştiği, bir sonraki veri bitinin saat sinyalini sonraki 11x11 karesinde kesiştiği vs. Her veri biti, her 11x11 karede gösterilecek olsa da, her veri biti, saat sinyaliyle sadece bir kez kesişecektir.

Paralel dönüştürücüye seri

Ardından, paralel verilerin önceden ayarlanmış bir adresle eşleşip eşleşmediğini kontrol edeceğiz. Bunu saat ve paralel veri üzerinde AND ve ANT geçitlerini kullanarak yapıyoruz. Ancak, paralel verilerin de tekrar karşılaştırılabilmesi için çıktılandığından emin olmalıyız. Bunlar bulduğum kapılar.

Sinyal Kontrol Kapıları

Son olarak, sadece saat sinyalini böldük, bir grup sinyal denetleyicisini yığıyoruz (her adres / çıkış için bir tane) ve bir çoklayıcımız var!

Çoklayıcı

ROM

ROM'un bir adresi bir girdi olarak alması ve bu adresdeki talimatı çıktı olarak göndermesi gerekir. Saat sinyalini talimatlardan birine yönlendirmek için bir çoklayıcı kullanarak başlıyoruz. Daha sonra, bazı kablo geçişlerini ve OR kapılarını kullanarak bir sinyal üretmemiz gerekiyor. Kablo geçişleri, saat sinyalinin talimatın 58 bitinin tümünü aşağı doğru hareket etmesini sağlar ve ayrıca üretilen bir sinyalin (şu anda paralel olarak) çıkacak ROM boyunca aşağı hareket etmesini sağlar.

ROM bit

Sonra paralel sinyali seri verilere dönüştürmemiz gerekiyor ve ROM tamamlandı.

Seri dönüştürücüye paralel

ROM

ROM, şu anda Golly’de bir derleme kodu çalıştırılarak derleme kodunu panonuzdan ROM’a çevirecek şekilde üretilir.

SRL, SL, SRA

Bu üç mantık geçidi, bit kaydırmaları için kullanılır ve tipik AND, VEYA, XOR, vb. Öğelerinizden daha karmaşıktır. Bu geçitlerin çalışmasını sağlamak için önce saat sinyalini "kaymaya" yol açmak için uygun bir süre geciktiririz. verilerde. Bu kapılara verilen ikinci argüman kaç bit kaydırılacağını belirler.

SL ve SRL için bizim

  1. En önemli 12 bitin açık olmadığından emin olun (aksi halde çıktı yalnızca 0 olur) ve
  2. Verileri, en az 4 önemli bit üzerinden doğru miktarda erteleyin.

Bu, bir grup AND / ANT kapıları ve bir çoklayıcıyla yapılabilir.

SRL

SRA biraz farklı, çünkü vardiya sırasında işaret biti kopyalamamız gerekiyor. Bunu, saat sinyalini işaret biti ile ANDing yaparak yapıyoruz ve ardından bu çıktısını tel ayırıcılar ve OR geçitleriyle birkaç kez kopyalıyoruz.

SRA

Set-Reset (SR) mandalı

İşlemcinin işlevselliğinin pek çok kısmı veri saklama olanağına dayanır. 2 kırmızı B12 / S1 hücresi kullanarak bunu yapabiliriz. İki hücre birbirini açık tutabilir ve birlikte kalabilirler. Bazı ekstra set, reset ve okuma devreleri kullanarak basit bir SR mandalı yapabiliriz.

SR mandalı

Synchronizer

Seri verileri paralel verilere dönüştürerek, ardından bir grup SR mandalını ayarlayarak tüm bir veri kelimesini saklayabiliriz. Daha sonra verileri tekrar çıkarmak için tüm mandalları okuyup sıfırlayabilir ve verileri buna göre geciktirebiliriz. Bu, bir başkasını beklerken bir (veya daha fazla) veri sözcüğü depolamamıza olanak sağlar ve farklı zamanlarda gelen iki veri kelimesinin senkronize edilmesine izin verir.

Synchronizer

Sayacı Oku

Bu cihaz RAM'den kaç kez adreslenmesi gerektiğini izler. Bunu SR mandalına benzer bir cihaz kullanarak yapar: bir T flip flop. T flip flop her girdi aldığında durumu değiştirir: eğer açıksa kapanır ve bunun tersi de geçerlidir. T flip flop açıktan kapalı çevrildiğinde, 2 bitlik bir sayaç oluşturmak için başka bir T flip flop'a beslenebilen bir çıkış darbesi gönderir.

İki bit sayacı

Okuma Sayacını yapmak için, sayacı iki ANT kapılı uygun adresleme moduna ayarlamamız ve saat sinyalini nereye yönlendireceğinize karar vermek için sayacın çıkış sinyalini kullanmamız gerekir: ALU veya RAM.

Sayacı Oku

Kuyruğu Oku

Okuma kuyruğunun hangi okuma sayacının RAM'e bir girdi gönderdiğini takip etmesi gerekir, böylece RAM'in çıkışını doğru konuma gönderebilir. Bunu yapmak için, bazı SR mandalları kullanıyoruz: her giriş için bir mandal. Bir okuma sayacından RAM'e bir sinyal gönderildiğinde, saat sinyali bölünür ve sayacın SR mandalını ayarlar. RAM çıkışı SR mandalı ile ANDed olur ve RAM'den gelen saat sinyali SR mandalını sıfırlar.

Kuyruğu Oku

ALU

ALU, bir sinyal gönderilecek yeri takip etmek için bir SR mandalı kullanması nedeniyle okuma sırasına benzer şekilde çalışır. İlk önce, talimatın opcode'una karşılık gelen mantık devresinin SR mandalı, bir çoklayıcı kullanılarak ayarlanır. Daha sonra, birinci ve ikinci argümanın değerleri SR mandalı ile ANDed olur ve ardından mantık devrelerine geçirilir. Saat sinyali mandalı geçerken sıfırlar, böylece ALU tekrar kullanılabilir. (Devrenin çoğu kapalı durumda ve bir ton gecikme yönetimi kullanılıyor, bu yüzden biraz karışıklık gibi gözüküyor)

ALU

Veri deposu

RAM bu projenin en karmaşık kısmıydı. Veri depolayan her bir SR mandalı üzerinde çok özel kontrol için gerekli. Okumak için, adres çoklayıcıya gönderilir ve RAM birimlerine gönderilir. RAM birimleri, paralel olarak depoladıkları verileri seriye dönüştürür ve çıktısını alır. Yazmak için, adres farklı bir çoklayıcıya gönderilir, yazılacak veriler seriden paralele dönüştürülür ve RAM üniteleri sinyali RAM boyunca iletir.

Her 22x22 metapixel RAM ünitesi aşağıdaki temel yapıya sahiptir:

RAM birimi

Bütün RAM'i bir araya getirip şöyle bir şey elde ediyoruz:

Veri deposu

Her şeyi bir araya getirmek

Bu bileşenlerin hepsini ve Genel Bakış bölümünde açıklanan genel bilgisayar mimarisini kullanarak çalışan bir bilgisayar oluşturabiliriz!

İndirme: - Bitmiş Tetris bilgisayar - ROM oluşturma komut dosyası, boş bilgisayar ve asal bulma bilgisayar

Bilgisayar


49
Sadece bu yazıdaki resimlerin, her ne sebeple olursa olsun bence çok güzel olduğunu söylemek isterim. : P 1
HyperNeutrino

7
Bu şimdiye kadar gördüğüm en şaşırtıcı şeydi ....
Yapabilseydim

3
@tfbninja Yapabilirsin, buna ödül denir ve 200 itibar kazanabilirsin.
Fabian Röling

10
Bu işlemci Spectre ve Meltdown saldırısına karşı savunmasız mı? :)
Ferrybig

5
@ Ferrybig şube tahmini yok, bu yüzden şüpheliyim.
JAD

621

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, MNZverilerdeki herhangi bir bitin 1 olup olmadığını kontrol etmek için MLZmiktarlar, 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 MGZoldu) MEZise, boş bir sinyalden DOĞRU sinyali oluşturma gerektirecektir MGZgerektiren, 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, ORve XORbeklediğiniz ne talimatlar. Bir NOTtalimattan ziyade , bir "ve-değil" ( ANT) talimatına sahip olduk . Öğretimdeki zorluk NOTyine, 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 xeş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 ( SLve 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, SRAbit 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 3olur 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. myDerleyicinin 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 MLZkomut, >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ığı MNZiçin bir komut kullanılır ==. Çoklu ifade şartlamaları şu anda desteklenmiyor.

Eğer elsedurum 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 MLZkomut, >ya da gibi eşitsizlikleri ele almak için kullanılır <=. ifİfadelerden farklı olarak , bir MNZtalimat 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 whileve do-whilebir olduğunu do-whileher zaman en az bir kez yürütülür böylece döngü gövdesi başlangıçta atlanır değildir. do-whileDö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. alphaDeğ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 indexsabitse, 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 subbir 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 callanahtar 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,callifadeleri. 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.


22
Assembly dili talimatlarının seçiminin substrat donanımınız tarafından tanımlanmasını seviyorum (MEZ yok çünkü iki sahiden bir doğruluk yapmak zor). Harika bir okuma.
AlexC

1
Bunun =sadece kendi yanında durabileceğini söyledin ama bir tane var !=.
Fabian Röling

@Fabian ve a+=
Oliphaunt

@Oliphaunt Evet, açıklamam tam olarak doğru değildi, daha çok belirli bir karakter sınıfının birbirine bitişik olabileceği karakter sınıfı bir şeydi.
PhiNotPi

606

Bölüm 5: Meclis, Çeviri ve Gelecek

Derleyiciden derleme programımızla, Varlife bilgisayarı için bir ROM toplama ve her şeyi büyük bir GoL düzenine dönüştürme zamanı!

montaj

Derleme programının bir ROM'a montajı, geleneksel programlamayla aynı şekilde yapılır: her komut bir ikili eşdeğerine çevrilir ve bunlar daha sonra çalıştırılabilir olarak adlandırdığımız büyük bir ikili bloğa birleştirilir. Bizim için tek fark, ikili blobun Varlife devrelerine çevrilmesi ve bilgisayara bağlanması gerektiğidir.

K Zhang , derleme ve çeviriyi yapan Golly için Python betiği CreateROM.py'yi yazdı . Oldukça basittir: panodan bir montaj programı alır, onu bir ikiliye birleştirir ve bu ikiliyi devreye alır. İşte betiğe dahil edilmiş basit bir ilkellik test cihazına bir örnek:

#0. MLZ -1 3 3;
#1. MLZ -1 7 6; preloadCallStack
#2. MLZ -1 2 1; beginDoWhile0_infinite_loop
#3. MLZ -1 1 4; beginDoWhile1_trials
#4. ADD A4 2 4;
#5. MLZ -1 A3 5; beginDoWhile2_repeated_subtraction
#6. SUB A5 A4 5;
#7. SUB 0 A5 2;
#8. MLZ A2 5 0;
#9. MLZ 0 0 0; endDoWhile2_repeated_subtraction
#10. MLZ A5 3 0;
#11. MNZ 0 0 0; endDoWhile1_trials
#12. SUB A4 A3 2;
#13. MNZ A2 15 0; beginIf3_prime_found
#14. MNZ 0 0 0;
#15. MLZ -1 A3 1; endIf3_prime_found
#16. ADD A3 2 3;
#17. MLZ -1 3 0;
#18. MLZ -1 1 4; endDoWhile0_infinite_loop

Bu, aşağıdaki ikili dosyayı üretir:

0000000000000001000000000000000000010011111111111111110001
0000000000000000000000000000000000110011111111111111110001
0000000000000000110000000000000000100100000000000000110010
0000000000000000010100000000000000110011111111111111110001
0000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000011110100000000000000100000
0000000000000000100100000000000000110100000000000001000011
0000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000110100000000000001010001
0000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000001010100000000000000100001
0000000000000000100100000000000001010000000000000000000011
0000000000000001010100000000000001000100000000000001010011
0000000000000001010100000000000000110011111111111111110001
0000000000000001000000000000000000100100000000000001000010
0000000000000001000000000000000000010011111111111111110001
0000000000000000010000000000000000100011111111111111110001
0000000000000001100000000000000001110011111111111111110001
0000000000000000110000000000000000110011111111111111110001

Varlife devrelerine çevrildiğinde şöyle görünür:

ROM

closeup ROM

ROM daha sonra Varlife'da tamamen işleyen bir program oluşturan bilgisayara bağlanır. Ama henüz bitmedi ...

Yaşam Oyunu Çevirisi

Bütün bu zaman boyunca, Hayat Oyununun temelinin üzerinde çeşitli soyutlama katmanları üzerinde çalıştık. Ama şimdi, soyutlama perdesini geri çekip çalışmalarımızı Yaşam Oyunu düzenine çevirmenin zamanı geldi. Daha önce de belirtildiği gibi, OTCA Metapixel'i Varlife için temel olarak kullanıyoruz. Böylece, son adım Varlife'daki her bir hücreyi Game of Life'taki bir metapixel'a dönüştürmektir.

Neyse ki, Golly farklı kural kümelerindeki desenleri OTCA Metapixel aracılığıyla Game of Life kalıplarına dönüştürebilen bir komut dosyası ( metafier.py ) ile birlikte geliyor . Ne yazık ki, yalnızca tek bir global kural kümesi olan kalıpları dönüştürmek için tasarlanmıştır, bu nedenle Varlife'da çalışmaz. Bu sorunu ele alan değiştirilmiş bir sürüm yazdım , böylece her metapiksel için kural Varlife için hücre bazında oluşturuldu.

Böylece, bilgisayarımız (Tetris ROM ile birlikte) 1.436 x 5.082 sınırlayıcı bir kutuya sahiptir. Bu kutudaki 7,297,752 hücreden 6,075,811 boş alan olup gerçek bir popülasyon sayısı 1,221,941'dir. Bu hücrelerin her birinin, 2048x2048 sınırlayıcı bir kutuya ve 64.693 (ON metapixel için) veya 23.920 (OFF metapixel için) popülasyonuna sahip bir OTCA metapixel'a çevrilmesi gerekir. Bu, nihai ürünün 29,228,828,720 ila 79,048,585,231 arasındaki bir nüfusa sahip olan 2,940,928 x 10,407,936 (artı metapiklin sınırları için birkaç bin fazla) sınırlayıcı kutusunun olacağı anlamına gelir. Canlı hücre başına 1 bit ile, tüm bilgisayarı ve ROM'u temsil etmek için gerekli olan 27 ila 74 GiB arasındadır.

Bu hesaplamaları buraya yazdım çünkü senaryoyu başlatmadan önce çalıştırmayı ihmal ettim ve bilgisayarımda çok hızlı bir şekilde bellek kalmadı. Bir paniklemiş sonra killkomuta ben metafier komut dosyasına bir değişiklik yaptı. Her 10 metapix satırı, desen diske kaydedilir (gzipli bir RLE dosyası olarak) ve ızgara temizlenir. Bu, çeviriye ekstra çalışma zamanı ekler ve daha fazla disk alanı kullanır, ancak bellek kullanımını kabul edilebilir sınırlar içinde tutar. Golly, desenin konumunu içeren genişletilmiş bir RLE formatı kullandığından, bu desenin yüklenmesine daha fazla karmaşıklık kazandırmaz - sadece aynı desendeki tüm desen dosyalarını açın.

K Zhang bu çalışmadan yola çıktı ve büyük desenler için RLE'den daha verimli olan MacroCell dosya formatını kullanan daha verimli bir metafier betiği yarattı . Bu betik oldukça hızlı çalışıyor (orijinal metafier betiği için birkaç saatle karşılaştırıldığında birkaç saniye), çok daha küçük çıktılar alıyor (1,7 GB'a karşı 121 KB) ve tüm bilgisayarı ve ROM'u büyük bir miktar kullanmadan tek bir dokunuşta düştü Hafıza MacroCell dosyalarının desenleri tanımlayan ağaçları kodlaması gerçeğinden faydalanır. Özel bir şablon dosyası kullanılarak, metapiksler ağaca önceden yüklenmiştir ve komşu algılama için bazı hesaplamalar ve modifikasyonlardan sonra, Varlife deseni kolayca eklenebilir.

Tüm bilgisayarın ve ROM'un Oyun Yaşamındaki kalıp dosyasını burada bulabilirsiniz .


Projenin Geleceği

Şimdi Tetris'i yaptık, tamamız değil mi? Yakınında bile değil. Çalıştığımız bu proje için birkaç hedefimiz daha var:

  • çamur balığı ve Kritixi Lithos, QFTASM'a uygun daha üst seviyede bir dil üzerinde çalışmaya devam ediyor.
  • El'endia Starman, çevrimiçi QFTASM tercümanına yükseltmeler üzerinde çalışıyor.
  • quartata, bağımsız bir C ve C ++ kodunun (ve potansiyel olarak Fortran, D veya Objective-C gibi diğer dillerin) GCC aracılığıyla QFTASM'ye derlenmesini sağlayacak olan bir GCC arka ucunda çalışıyor. Bu, standart bir kütüphane olmasa da, daha gelişmiş bir dilde daha bilinen bir dilde oluşturulmasını sağlayacaktır.
  • Daha fazla ilerleme kaydetmeden önce üstesinden gelmemiz gereken en büyük engellerden biri, aletlerimizin pozisyondan bağımsız kod (örn. Göreceli atlamalar) veremediği gerçeğidir. PIC olmadan, hiçbir bağlantı yapamayız ve bu nedenle mevcut kütüphanelerle bağlantı kurabilmenin avantajlarını kaçırıyoruz. Doğru PIC yapmanın bir yolunu bulmak için çalışıyoruz.
  • QFT bilgisayarı için yazmak istediğimiz bir sonraki programı görüşüyoruz. Şu anda, Pong iyi bir hedefe benziyor.

2
Sadece gelecekteki alt bölüme bakmak, göreceli bir sıçrama değil ADD PC offset PCmi? Afedersiniz, eğer bu yanlışsa, montaj programlama asla benim forte'im olmadı.
MBraedley 14:17

3
@Timmmm Evet, ama çok yavaş. (Ayrıca HashLife kullanmanız gerekir).
Bir spaghetto

75
Bunun için yazacağınız bir sonraki program Conway'in Yaşam Oyunu olmalıdır.
ACK_stoverflow

13
@ACK_stoverflow Bu bir noktada yapılacak.
Mego

13
Videonuz çalışıyor mu?
PyRulez

583

Bölüm 6: QFTASM'a Yeni Derleyici

Her ne kadar Cogol ilkel Tetris uygulaması için yeterli olsa da, genel amaçlı programlama için kolay okunabilir bir seviyede çok basit ve çok düşük seviyededir. Eylül 2016'da yeni bir dil üzerinde çalışmaya başladık. Gerçek yaşamın yanı sıra anlaşılması zor olması nedeniyle de dildeki ilerleme yavaştı.

Basit bir tip sistem, özyinelemeyi destekleyen alt yordamlar ve satır içi operatörleri içeren Python'a benzer bir sözdizimi olan düşük seviyeli bir dil oluşturduk. Metinden QFTASM'ye kadar olan derleyici 4 adımda oluşturuldu: belirteç, gramer ağacı, yüksek seviye bir derleyici ve düşük seviye bir derleyici.

Belirteç

Geliştiricinin yerleşik tokeniser kütüphanesini kullanarak Python'u kullanmaya başlaması, bu adımın oldukça basit olması anlamına geliyordu. Sıyırma yorumları da dahil olmak üzere varsayılan çıktıda yalnızca birkaç değişiklik yapılması gerekiyordu (ancak #includes değil ).

Dilbilgisi ağacı

Dilbilgisi ağacı, herhangi bir kaynak kodunu değiştirmek zorunda kalmadan kolayca genişletilebilecek şekilde yaratıldı.

Ağaç yapısı, ağacı oluşturan düğümlerin yapısını ve diğer düğüm ve belirteçlerle nasıl oluştuğunu içeren bir XML dosyasında depolanır.

Dilbilgisi, isteğe bağlı olanları olduğu kadar tekrarlanan düğümleri de desteklemek için gerekliydi. Bu, jetonların nasıl okunacağını açıklamak için meta etiketler getirerek gerçekleştirildi.

Üretilen belirteçler daha sonra dilbilgisi kurallarına göre ayrıştırılır, böylelikle çıktı subs ve generic_variablesbenzeri diğer dilbilgisi öğelerini ve belirteçlerini içeren bir dilbilgisi öğeleri ağacı oluşturur .

Yüksek seviye kod içine derleme

Dilin her bir özelliğinin yüksek seviyeli yapılarda derlenebilmesi gerekir. Bunlar arasında assign(a, 12) ve call_subroutine(is_prime, call_variable=12, return_variable=temp_var). Bu segmentte, elemanların döşenmesi gibi özellikler yürütülür. Bunlar operators olarak tanımlanmıştır ve her bir operatör gibi +veya %kullanıldığında satır içi olmaları bakımından özeldir . Bu nedenle, normal kodlardan daha kısıtlıdırlar - kendi operatörlerini veya tanımlanmış olana dayanan operatörleri kullanamazlar.

Satır içi işlemi sırasında, iç değişkenler çağrılanlarla değiştirilir. Bu aslında döner

operator(int a + int b) -> int c
    return __ADD__(a, b)
int i = 3+3

içine

int i = __ADD__(3, 3)

Ancak bu davranış, girdi değişkeni ve çıktı değişkeni bellekteki aynı konuma işaret ederse zararlı ve hataya açık olabilir. 'Daha güvenli' davranışını kullanmak için, unsafeanahtar kelime, derleme işlemini, ek değişkenler oluşturulacak ve satır içi veya gerektiği gibi kopyalanacak şekilde ayarlar.

Çizilme değişkenleri ve karmaşık işlemler

Bu gibi matematiksel işlemler a += (b + c) * 4, ilave bellek hücreleri kullanılmadan hesaplanamaz. Üst düzey derleyici, işlemleri farklı bölümlere ayırarak bununla ilgilenir:

scratch_1 = b + c
scratch_1 = scratch_1 * 4
a = a + scratch_1

Bu, hesaplamaların ara bilgisini depolamak için kullanılan çizik değişkenleri kavramını tanıtır. Gerektiğinde tahsis edilir ve bitirildikten sonra genel havuza tahsis edilir. Bu, kullanım için gerekli kazı kazan hafıza konumlarının sayısını azaltır. Kazı kazan değişkenleri küresel kabul edilir.

Her bir alt rutinin, alt rutinin kullandığı tüm değişkenlerin yanı sıra türlerini de referans almak için kendi VariableStore'u vardır. Derlemenin sonunda, mağazanın başlangıcından itibaren göreli ofsetlere çevrilir ve daha sonra RAM'deki gerçek adresleri verilir.

RAM Yapısı

Program counter
Subroutine locals
Operator locals (reused throughout)
Scratch variables
Result variable
Stack pointer
Stack
...

Düşük seviye derleme

Düşük seviyeli derleyici sahip tek şey başa sub, call_sub, return, assign, ifve while. Bu, QFTASM talimatlarına daha kolay bir şekilde çevrilebilecek görevlerin azaltılmış bir listesi.

sub

Bu, adlandırılmış bir alt yordamın başlangıcını ve sonunu bulur. Düşük seviyeli derleyici, etiketler ekler ve mainalt rutin durumunda, bir çıkış komutu ekler (ROM'un sonuna atla).

if ve while

Hem whileve ifdüşük seviye tercümanlar oldukça basittir: onların koşullarına ve onlara bağlı atlama işaretçileri olsun. whiledöngüler biraz derlendiğinden farklıdırlar.

...
condition
jump to check
code
condition
if condtion: jump to code
...

call_sub ve return

Çoğu mimariden farklı olarak, derlediğimiz bilgisayar bir yığından itmek ve çıkarmak için donanım desteğine sahip değil. Bu, istifden hem itme hem de çıkarmanın iki talimat alması anlamına gelir. Patlama durumunda, yığın göstergesini azaltır ve değeri bir adrese kopyalarız. İtme durumunda, bir adresi bir adresden geçerli yığın göstergesindeki adrese kopyalayıp artırıyoruz.

Bir alt yordam için tüm yerliler derleme zamanında belirlenen RAM'de sabit bir yerde saklanır. Özyineleme çalışması yapmak için, bir işlevin tüm yerlileri bir çağrının başlangıcındaki yığına yerleştirilir. Daha sonra alt yordamın argümanları yerel mağazadaki konumlarına kopyalanır. Dönüş adresinin değeri yığına konur ve alt rutin çalıştırılır.

Bir returncümleyle karşılaşıldığında, yığının tepesi açılır ve program sayacı bu değere ayarlanır. Çağıran alt rutinin yerlileri için değerler yığından çıkarılır ve önceki konumlarına getirilir.

assign

Değişkenli atamalar derlenmesi en kolay olanlardır: değişken ve değer alırlar ve tek satırda derlenirler: MLZ -1 VALUE VARIABLE

Atlama hedefi atama

Son olarak, derleyici talimatlara iliştirilmiş etiketler için atlama hedeflerini belirler. Etiketlerin mutlak konumu belirlenir ve daha sonra bu etiketlere referanslar bu değerlerle değiştirilir. Etiketlerin kendileri koddan çıkarılır ve son olarak derlenen koda talimat numaraları eklenir.

Örnek adım adım derleme

Şimdi tüm aşamalardan geçtikten sonra, adım adım gerçek bir program için gerçek bir derleme sürecinden geçelim.

#include stdint

sub main
    int a = 8
    int b = 12
    int c = a * b

Tamam, yeterince basit. O, o program sonunda açık olmalı a = 8, b = 12, c = 96. İlk olarak, ilgili kısımları dahil edelim stdint.txt:

operator (int a + int b) -> int
    return __ADD__(a, b)

operator (int a - int b) -> int
    return __SUB__(a, b)

operator (int a < int b) -> bool
    bool rtn = 0
    rtn = __MLZ__(a-b, 1)
    return rtn

unsafe operator (int a * int b) -> int
    int rtn = 0
    for (int i = 0; i < b; i+=1)
        rtn += a
    return rtn

sub main
    int a = 8
    int b = 12
    int c = a * b

Tamam, biraz daha karmaşık. Belirteçe geçelim ve ne çıkacağına bakalım. Bu aşamada, herhangi bir yapıya sahip olmayan sadece doğrusal bir jeton akışına sahip olacağız.

NAME NAME operator
LPAR OP (
NAME NAME int
NAME NAME a
PLUS OP +
NAME NAME int
NAME NAME b
RPAR OP )
OP OP ->
NAME NAME int
NEWLINE NEWLINE
INDENT INDENT     
NAME NAME return
NAME NAME __ADD__
LPAR OP (
NAME NAME a
COMMA OP ,
NAME NAME b
RPAR OP )
...

Şimdi tüm belirteçler dilbilgisi çözümleyiciden geçirilir ve bölümlerin her birinin adını içeren bir ağaç çıkarır. Bu, kod tarafından okunan üst düzey yapıyı gösterir.

GrammarTree file
 'stmts': [GrammarTree stmts_0
  '_block_name': 'inline'
  'inline': GrammarTree inline
   '_block_name': 'two_op'
   'type_var': GrammarTree type_var
    '_block_name': 'type'
    'type': 'int'
    'name': 'a'
    '_global': False

   'operator': GrammarTree operator
    '_block_name': '+'

   'type_var_2': GrammarTree type_var
    '_block_name': 'type'
    'type': 'int'
    'name': 'b'
    '_global': False
   'rtn_type': 'int'
   'stmts': GrammarTree stmts
    ...

Bu gramer ağacı, üst düzey derleyici tarafından ayrıştırılacak bilgileri ayarlar. Bir değişkenin yapı tipleri ve nitelikleri gibi bilgileri içerir. Dilbilgisi ağacı daha sonra bu bilgiyi alır ve alt rutinler için gerekli olan değişkenleri atar. Ağaç ayrıca tüm satırları ekler.

('sub', 'start', 'main')
('assign', int main_a, 8)
('assign', int main_b, 12)
('assign', int op(*:rtn), 0)
('assign', int op(*:i), 0)
('assign', global bool scratch_2, 0)
('call_sub', '__SUB__', [int op(*:i), int main_b], global int scratch_3)
('call_sub', '__MLZ__', [global int scratch_3, 1], global bool scratch_2)
('while', 'start', 1, 'for')
('call_sub', '__ADD__', [int op(*:rtn), int main_a], int op(*:rtn))
('call_sub', '__ADD__', [int op(*:i), 1], int op(*:i))
('assign', global bool scratch_2, 0)
('call_sub', '__SUB__', [int op(*:i), int main_b], global int scratch_3)
('call_sub', '__MLZ__', [global int scratch_3, 1], global bool scratch_2)
('while', 'end', 1, global bool scratch_2)
('assign', int main_c, int op(*:rtn))
('sub', 'end', 'main')

Daha sonra, düşük seviye derleyici bu yüksek seviye gösterimi QFTASM koduna dönüştürmelidir. Değişkenler RAM'deki gibi konumlara atanır:

int program_counter
int op(*:i)
int main_a
int op(*:rtn)
int main_c
int main_b
global int scratch_1
global bool scratch_2
global int scratch_3
global int scratch_4
global int <result>
global int <stack>

Basit talimatlar daha sonra derlenir. Son olarak, çalıştırılabilir QFTASM koduyla sonuçlanan talimat numaraları eklenir.

0. MLZ 0 0 0;
1. MLZ -1 12 11;
2. MLZ -1 8 2;
3. MLZ -1 12 5;
4. MLZ -1 0 3;
5. MLZ -1 0 1;
6. MLZ -1 0 7;
7. SUB A1 A5 8;
8. MLZ A8 1 7;
9. MLZ -1 15 0;
10. MLZ 0 0 0;
11. ADD A3 A2 3;
12. ADD A1 1 1;
13. MLZ -1 0 7;
14. SUB A1 A5 8;
15. MLZ A8 1 7;
16. MNZ A7 10 0;
17. MLZ 0 0 0;
18. MLZ -1 A3 4;
19. MLZ -1 -2 0;
20. MLZ 0 0 0;

Sözdizimi

Şimdi çıplak bir dile sahip olduğumuza göre, aslında küçük bir program yazmalıyız. Python gibi girintiler kullanıyoruz, mantıksal blokları bölüyoruz ve akışı kontrol ediyoruz. Bu, boşlukların programlarımız için önemli olduğu anlamına gelir. Her tam program, C benzeri dillerde işlev maingibi davranan bir alt yordam vardır main(). Fonksiyon programın başında çalıştırılır.

Değişkenler ve türleri

İlk defa değişkenler tanımlandığında, kendileriyle ilişkili bir tür olması gerekir. Halihazırda tanımlanmış olan tipler intve boolderleyici değil, tanımlanmış dizilerin sözdizimindedir.

Kütüphaneler ve operatörler

stdint.txtTemel operatörleri tanımlayan bir kütüphane mevcuttur. Bu dahil edilmezse, basit operatörler bile tanımlanmayacaktır. Bu kütüphaneyi ile kullanabiliriz #include stdint. stdintgibi operatörler tanımlar +, >>ve hatta *ve %doğrudan QFTASM işlem kodları olan, ikisi de.

Dil ayrıca QFTASM kodlarının doğrudan çağrılmasını sağlar __OPCODENAME__.

Ek stdintolarak tanımlanır

operator (int a + int b) -> int
    return __ADD__(a, b)

Hangi +iki ints verilen operatörün ne yaptığını tanımlar .


1
Hayatın Conway oyun bir WireWorld benzeri CA oluşturup bu devreyi yerine yeniden daha kullanarak yeni işlemci oluşturmak için karar verildi neden sorabilir miyim, / evrensel bir bilgisayara cgol mevcut böyle güçlendirme bunu ?
eaglgenes101

4
@ eaglgenes101 Yeni başlayanlar için çoğumuzun diğer kullanışlı evrensel bilgisayarların varlığından haberdar olduğumuzu sanmıyorum. Çoklu karışık kurallara sahip bir wireworld benzeri CA fikri, metacell'lerle uğraşmanın bir sonucu olarak ortaya çıktı (inanıyorum - Phi bu fikri ortaya çıkaran oydu). Oradan yarattıklarımıza mantıklı bir ilerleme oldu.
Mego
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.