Show HN: NAND kapılarıyla yapılmış programlanabilir bir bilgisayar
(github.com/ArhanChaudhary)- NAND, yalnızca clock ve NAND gate'lerden oluştuğu eğitimsel öncülüne dayanan, web üzerinde emüle edilen Turing-dengi 16-bit bir bilgisayardır ve CPU'dan IDE ile UI'a kadar her şeyi içerir
- Kendi yığını Jack-VM-Hack platformu temel alınarak oluşturulmuştur; CPU, makine dili, assembly, assembler, VM dili, VM translator, Jack dili ve compiler'ı birlikte sunar
- Örnek programlar arasında Average, Pong, 2048'in yanı sıra stack overflow ve stack smashing kullanan VM escape demosu ile basit makine öğrenimi kullanan GeneticAlgorithm da bulunur
- Jack, Java sözdizimine benzeyen zayıf tipli nesne yönelimli bir dil olsa da, iç yapıda yalnızca signed 16-bit integer'a dayanır; operatör önceliğinin olmaması, manuel bellek yönetimi ve tanımsız davranış gibi büyük kısıtlar içerir
- Proje, tüm hesaplamaların gerçekten fiziksel NAND gate'lerde çalıştığı anlamına gelmez; TypeScript compiler ve VM translator, Rust tabanlı NAND gate mantık simülatörü ve WebAssembly kullanan eğitimsel ve kuramsal bir uygulamadır
NAND'in sunduğu tam bilgisayar yığını
- NAND, Not A Nand-powered Device kısaltmasıyla tanıtılan, web tabanlı 16-bit bir bilgisayardır
- Clock ve NAND gate'lerle yapılmış Turing-dengi bir bilgisayarı emüle eder ve aşağıdaki bileşenleri kendi içinde barındırır
- CPU
- machine code language
- assembly language
- assembler
- virtual machine language
- virtual machine translator
- programming language
- compiler
- IDE
- user interface
- Temeli, Nand to Tetris course ve ilgili kitaplardaki Jack-VM-Hack platformudur
- NAND demo videosu sunulmaktadır
Örnek programlar ve demolar
-
Average
- Sayı girişi ve ortalama hesaplama yapan basit bir program
- control flow, aritmetik işlemler, I/O ve dinamik bellek tahsisini gösterir
- Nand to Tetris software suite ile sağlanan bir programdır
-
Pong
- Nesne yönelimli modeli gösteren bir Pong oyunu
- Yön tuşlarıyla paddle'ı sağa ve sola hareket ettirip topu sektirirsiniz
- Top her sektiğinde paddle küçülür ve top ekranın altına değdiğinde oyun biter
- Nand to Tetris software suite ile sağlanan bir programdır
-
2048
- Recursion ve karmaşık application logic'i gösteren bir 2048 oyunu
- 4x4 grid üzerinde yön tuşlarıyla sayılar hareket ettirilir ve aynı sayılar birleşir
- 2048 tile'ına ulaşıldığında kazanılır, ancak kaybedene kadar oynamaya devam edilebilir
-
Overflow
- Sonsuz recursion ile bilerek stack overflow oluşturup virtual machine escape gerçekleştirir
- Runtime'da stack overflow'u önleyen bir kontrol olmamasından yararlanır
- Stack pointer değeri 2048'i aşınca stack, amaçlanan bellek alanını terk ederek heap memory space'e taşar
- Boş RAM üzerinde çalıştırılırsa, program counter'ı 0'a ayarlayan instruction nedeniyle program ortada resetlenebilir
- GeneticAlgorithm çalıştırıldıktan hemen sonra yürütülürse, üzerine yazılmamış önceki RAM memory'si okunabilir
-
SecretPassword
- Runtime'ın stack smashing'i engellememesinden yararlanarak normalde erişilemeyen bir fonksiyonu çağırır
- Kullanıcı, RAM'deki bir memory address'i istediği bir değerle ezebilir
- Stack frame'in return address'i başka bir fonksiyonun adresiyle ezilirse, program içindeki rastgele kodların çalıştırılması mümkün olur
- Örnek değerler memory location
267ve overwrite value1743şeklindedir - Aynı tür zafiyet C'deki buffer overflow'da da bulunur
-
GeneticAlgorithm
- Basit makine öğrenimi kullanan bir creature simulation'dır
- Her dot, acceleration vector'lerden oluşan kendi “brain”ine sahiptir ve doğal seçilim yoluyla goal'e ulaşacak şekilde evrimleşir
- Goal'e daha yakın bir konumda ölen dot'ların bir sonraki neslin parent'ı olarak seçilme olasılığı daha yüksektir
- Reproduction sürecinde brain'in bazı bölümleri mutation geçirerek doğal evrimi simüle eder
- Genetic Algorithm demo videosu sunulmaktadır
GeneticAlgorithm'ın karşılaştığı donanım kısıtları
- GeneticAlgorithm, NAND'in çeşitli bileşenleri arasında geliştirilmesi en uzun süren tek programdır
- Performans nedeniyle, dot'ların evrimde kullandığı tek unsur ölürken goal'e ne kadar yakın olduklarıdır; doğal seçilim algoritmasının entropy'si düşüktür
- Bellek kullanımı nedeniyle dot sayısı ve brain boyutu üzerinde tatmin edici olmayan sınırlamalar vardır
- Teknik karmaşıklık nedeniyle, simulation sırasında obstacle'lar yeniden yerleştirilse bile dot'ların brain'inin goal'e ulaşacak kadar büyük olacağı garanti edilemez
- Brain size yalnızca program başlangıcında belirlenir
- Uygulama, çeşitli optimizasyonlarla NAND'in kısıtlarını aşar
- ROM instruction memory space sınırlıdır; kod çok fazla olursa compile edilemez
- Nihai GeneticAlgorithm, instruction memory space'in %99,2'sini kullanır
- RAM memory space sınırlıdır; bu yüzden heap memory usage optimize edilmelidir
- Nesiller arasında ekranın statik biçimde dolu görünmesinin nedeni, screen memory space'in sonraki nesil için temporary swap memory olarak kullanılmasıdır
- NAND'de floating point type yoktur ve temsil edilebilen integer aralığı -32768 ile 32767 arasındadır
- Fitness hesaplamasının hassasiyeti düşer ve integer overflow da hesaba katılmalıdır
- İlgili optimizasyonlar ve ek içgörüler GeneticAlgorithm codebase içinde belgelenmiştir
Jack ile NAND programlama
- Jack’te programın çalışmamasının en önemli nedenlerinden biri olarak operatör önceliğinin olmaması vurgulanıyor
4 * 2 + 3,(4 * 2) + 3şeklinde yazılmalıif (~x & y),if ((~x) & y)şeklinde yazılmalı- Parantez içermeyen belirsiz expression’ların evaluation value’su undefined’dır
- Jack, NAND’ın weakly typed object-oriented programming language’idir
- Açıklamaya göre “Java syntax’ına sahip C”ye daha yakındır
- NAND kendi complete tech stack’ine sahip olduğu için yalnızca Jack ile programlanabilir
- Varsayılan Jack OS, compile sırasında programla birlikte bundle edilir
- string, memory ve hardware ile interface sağlar
Keyboard.readLine,Keyboard.readInt,Output.printString,Output.printlngibi function’ları içerir
- Jack, üç primitive type destekler:
int,char,boolean- class aracılığıyla abstract data type tanımlanabilir
fieldvariable’ları, instance bazında attribute tanımlarfieldvariable’ları private scope’a sahiptir; dış erişim için method gerekir
functionvemethodfarklı şekillerde çağrılır- Mevcut object’in method’u aynı class içinde
do g();gibi çağrılabilir - function call’da başa class name eklenmelidir
- object method’u
do b.q();gibi object üzerinden çağrılır
- Mevcut object’in method’u aynı class içinde
Jack’in zayıf tiplemesi ve bellek yönetimi
- Jack, Java’dan farklı olarak strong type, down casting, polymorphism ve inheritance desteklemez
- İçeride gerçekte yalnızca tek bir type vardır: signed 16-bit integer
- compiler, assignment ve operation’larda type’ların karışmasına aldırmaz
chariçine65atanırsa'A'ile eşdeğer kabul edilebilirArraydeğişkenine5000atanıpa[100] = 77çalıştırılırsaRAM[5100] = 77olur- array entry’leri farklı data type’ları barındırabilir
- bellek yerleşimi uyuyorsa
Array, başka bir class instance’ı gibi kullanılabilir
- Jack, manuel bellek yönetimi kullanan bir dildir
- Artık gerekmeyen memory deallocate edilmezse memory leak oluşur
- heap overflow,
ERR6olarak görünür - Jack OS, array ve string’leri stack’te değil heap’te saklar
- Object’i temsil eden class’ların
disposemethod’una sahip olması best practice kabul edilir- Önce field variable’larının
disposemethod’u çağrılır - En sonda
do Memory.deAlloc(this);ile object instance’ının kendisi deallocate edilir
- Önce field variable’larının
- String literal’ları tekrar tekrar üretip yazdıran döngüler heap overflow oluşturabilir
- Her iterasyonda string dispose edilebilir
- veya string bir kez allocate edilip yeniden kullanılırsa sürekli yazdırma yapılabilir
Tanımsız davranışlar ve dikkat edilmesi gerekenler
-
Karşılaştırma operatörleri
a > bvea < bher zaman matematiksel olarak doğru değildir- VM implementasyonu
a > bifadesinia - b > 0biçimine dönüştürür a - boverflow yapabileceği için20000 > -20000,falseolurailebarasındaki mutlak fark 32767’den büyükse>ve<yanlış sonuç verebilir- Bu davranış, Nand to Tetris ile compatibility nedeniyle düzeltilmemiştir
-
-32768
-32768,-(-32768) = -32768olan tek değerdir- pozitif karşılığı olmadığı için unsoundness ve logic error üretebilir
Output.printInt, içerdeMath.abs’in positive number döndüreceğini varsayar; ancak-32768için bu gerçekleşmediğinden Jack OS hatalı çalışır
-
Çok az argümanla function çağrısı
- Parameter alan bir function, argümansız çağrılırsa undefined behavior oluşabilir
- Tersine, çok fazla argümanla yapılan function call geçerlidir ve extra argument’ler
argumentskeyword’ü ile indexlenebilir - argument count indicator yoktur
-
Uygunsuz type casting
Arraykullanılarak variable’lar başka bir type’a cast edilebilir- cast edilmiş variable üzerinde var olmayan bir instance method çağrılırsa bu undefined behavior’dır
- compiler bunu fark edecek kadar smart değildir
-
Stack frame ve internal register değiştirme
256~2047memory address aralığındaki stack frame’ler veya1~15aralığındaki internal register’lar değiştirilirse undefined behavior oluşabilir- Bunun, genelde
Memory.pokeyanlış kullanımı veya negative array indexing olmadan zor olduğu belirtiliyor
-
Kullanıcı VM dosyası yükleme
- NAND,
.jackfile’ları için program validation sağlasa da.vmfile’ları için sağlamaz .vmfile’larında nonexistent function çağrıları, unassigned variable referansları ve mantıksal olarak invalid memory operation’ları yapılabilir- Çoğu durumda virtual machine escape meydana gelir ve ekranda hiçbir şey görünmeyebilir
- NAND,
Donanım özellikleri ve bellek yerleşimi
- NAND’ın RAM’i 32.768 word’den oluşur ve her word bir 16-bit binary number tutar
- Hardware, screen için 8.192 memory address ayırır
- Her address’teki her bit, 512x256 ekranın karşılık gelen pixel’ine lineer olarak map edilir
- bit numaralandırması LSb 0 yöntemini kullanır
- Keyboard,
24576memory address’ine map edilmiştir- O anda basılı olan key bu konuma yansıtılır
- user input’u doğrudan bu address üzerinden işlemek yerine Jack OS’in
Keyboardclass’ını kullanmak önerilir
- Keyboard, ASCII character’ları ve special key’leri tanır
- new line =
128 - backspace =
129 - left/up/right/down arrow =
130~133 - home/end/page up/page down/insert/delete/ESC =
134~140 - F1~F12 =
141~152
- new line =
- Hardware, static class variable’ları için 240 memory address, global stack için ise 1.792 memory address ayırır
- Derin recursion yapılmadıkça bu sınırın genelde sorun yaratmadığı belirtiliyor
Jack OS'un ötesine geçip kendi OS'ini implement etmek
- Temelde Jack OS, derleme sırasında programla birlikte paketlenir ve string, bellek, donanım arayüzü sağlar
- Özel bir donanım arayüzüne sahip kendi OS implementasyonunuzu sağlayabilirsiniz
- IDE, Jack OS dosyasını normal program dosyasıyla aynı şekilde ele alır
- OS dosyası da silinebilir veya üzerine yazılabilir
- Kendi OS'inizi kullansanız bile derleme için mutlaka implemente etmeniz gereken çekirdek fonksiyonlar vardır
Sys.init:Main.maindeğil, VM implementasyonuna hardcoded edilmiş gerçek giriş noktasıdırMemory.alloc: class constructor'larının nesne oluştururken dahili olarak kullandığı heap bellek ayırıcısıdırString.newWithStr: string literal'lar için dahili constructor'dırMath.multiply: Jack ifadesix * yyerine dahili olarak çağrılırMath.divide: Jack ifadesix / yyerine dahili olarak çağrılır
- Sağlanan Jack OS'teki
Sys.init, belleği, matematik modülünü, ekranı ve output'u başlattıktan sonraMain.main()çağırır veSys.halt()çağırır
NAND'in iç çalışma şekli
- NAND computer, Harvard architecture kullanır
- Komut belleği olan ROM ile veri belleği olan RAM ayrıdır
- CPU bu ikisinin birlikte çalışmasını sağlar
- CPU bir accumulator machine'dir
- Kontrol akışında built-in register'lara büyük ölçüde bağımlıdır
- Buradaki accumulator,
Dregister'ıdır
- CPU instruction set'inde yalnızca iki opcode vardır
- instruction set görece basittir ama zengin işlevler sunar
- ALU, tek bir instruction içinde hesaplanabilecek expression'larla tanımlanır
- compiler ve virtual machine, NAND'e özgü kavramlar olmadığından kısaca ele alınır
- Bazı tuhaf syntax özellikleri, compiler implementasyonunu kolaylaştırmanın sonucudur
- compiler, LL(1) grammar üzerinde çalışan bir recursive descent parser'dır
- compiler VM code üretir ve VM, simple stack machine olarak kullanılır
- Her VM instruction'ı assembly ve machine code'a mapping edilir
- Implementasyon koduna core ve compiler implementation içinde bakılabilir
Jack dili ve OS Reference'ın özü
- Jack programı, bir class collection'ından oluşur
- class'lar ayrı dosyalarda tanımlanır
- Bir veya daha fazla class gerekir ve bunlardan biri
Mainolmalıdır - Jack OS'e göre giriş noktası,
Mainclass'ınınmainfunction'ıdır
- class'lar
field,static,constructor,method,functionbildirimleri içerebilirfieldvestaticdeclaration'larının sırası arbitrary'dir- subroutine declaration'larının sırası da arbitrary'dir
- type,
void,int,boolean,charveya bir class name olabilir
- syntax özellikleri
- whitespace ve comment'ler ignored edilir
&ve|bitwise operator'leridir ve short-circuit yapmaztrue,false,nullsırasıyla-1,0,0değerleridir- string constant'lar newline ya da quotation mark'ı doğrudan içeremez ve escape de desteklenmez
- quotation mark ve newline, OS içindeki
String.doubleQuote()veString.newLine()ile sağlanır - identifier'lar case-sensitive'dir
- Jack OS'in başlıca class'ları
Array: array oluşturma ve disposeKeyboard: tuş basımı, karakter, satır, integer girişiMath: abs, multiply, divide, sqrt, max, minMemory:peek,poke,alloc,deAllocOutput: metin ekranına çıktı ve imleç hareketiScreen: pixel, line, rectangle, circle çizimiString: string oluşturma, dispose, karakter erişimi, append, integer dönüşümüSys: halt, error, wait
- invalid state,
"ERR[N]"biçiminde bir error code gösterir ve program execution'ı sonlandırırERR3: sıfıra bölmeERR6: heap taşmasıERR15,ERR16: string index sınır dışındaERR17: string doluERR20: geçersiz imleç konumu
Gerçekten yalnızca NAND gate'lerinden oluşan bir proje değil
- FAQ, “everything made from NAND gates” açıklaması ve başlığının misleading olduğunu ama good faith ile yazıldığını kabul ediyor
- compiler ve virtual machine translator TypeScript ile yazılmış
- emulated kernel ve emulated hardware, gerçek bir bilgisayarın çalışma biçimini birebir temsil etmiyor
- Gerçek NAND gate logic simulator Rust ile yazılmış ve tüm codebase'in yalnızca küçük bir bölümünü oluşturuyor
- Rust kodu, tarayıcıda çalışması için WebAssembly'e derleniyor
- Bu nedenle tüm computation'ın NAND gate'lerinde çalıştığı varsayımının fiilen ortadan kalktığı belirtiliyor
- NAND, educational and theoretical bir proje işlevi görüyor
- Teoride aynı CPU logic'i, emulated hardware'in gerçek dünyadaki bir karşılığında da çalışabilir
- nand2tetris tabanlı FPGA hardware project örneği olarak https://gitlab.com/x653/nand2tetris-fpga/ veriliyor
Implementasyon kapsamı ve IDE sınırlamaları
- NAND, Nand to Tetris course ve ilgili kitabın specification'ını takip ediyor
- Geliştirici CPU, assembler, virtual machine translator ve compiler specification'ını bizzat implemente etmiş; platformu web'e taşırken kendi IDE ve UI'ını eklemiş
- Jack'te type belirtme zorunluluğunun nedeni, compiler'ın bir instance method'un hangi class'a ait olduğunu belirlemesidir
Stringtype'ı olarak tanımlanansiçins.appendChar(33), derleme sırasındaString.appendChar(s, 33)biçimine dönüştürülür
- IDE, implementasyonu basit tutmak için user experience'tan ödün veriyor
- syntax highlighting için
contenteditableve cursor positioning logic kullanılıyor - Sonuç olarak yavaş, gözle görülür biçimde buggy ve yaygın keybind'ler çalışmıyor
- syntax highlighting için
- Kodu derleyip çalıştırmak için “Start” düğmesine basmak yeterli
- OS, genellikle bellek başlatma ve servis kurulumunu 1 saniyeden biraz kısa sürede tamamlıyor
1 yorum
Hacker News yorumları
Harika bir yan proje ve README de gerçekten çok iyi. Ben Eater'ın 6502 Computer'ıyla (https://eater.net/) biraz uğraştıktan sonra Nand to Tetris'i takip etmeyi düşünüyordum.
Sadece bu materyalle birkaç üniversite dersi çıkarılabilir. İyi hazırlanmış bir kaynak.
Gerçekten çok iyi yapılmış. Çoğu programcının kariyeri boyunca görmediği soyutlama katmanlarını bizzat deneyimlemiş oluyor.
Harika iş. NAND to Tetris, üniversiteden mezun olduktan sonraki ilk işimi almama yardımcı olmuştu.
1990'ların başında UC Berkeley'deki bilgisayar donanımı yeterlilik sınavında buna benzer bir tasarım temel soruydu.
Daha özel olarak, yalnızca NAND kapılarıyla sıfırdan mikrokod tabanlı boru hatlı bir RISC işlemci tasarlama sorusuydu; gerçekten inşa etmek gerekmiyordu ama kâğıt üzerinde ayrıntılı bir tasarım teslim etmek gerekiyordu.
Gerçekten muazzam bir çalışma. Nand2Tetris dersini alırken ben de benzer bir sanal uygulama yapmayı istemiştim.
Bunu gerçekten başarmış olması etkileyici; artık bilgisayarların nasıl çalıştığını gerçekten çok iyi anlıyor olmalı.
Ama birinin, benim hayal ettiğimden bir basamak daha etkileyici bir işi çoktan yapmış olmasına şaşırdım.
Harika çalışma. Ben de yakın zamanda Nand2Tetris'e başladım ve önümüzdeki birkaç ay içinde dersin donanım kısmı olan 1. bölümü bitirmek istiyorum.
İlerlememi burada blogda yazdım: https://gurudas.dev/blog/2024/04/13/nand-to-tetris-2024-proj...
Yalan. NAND kapıları ve saat sinyali kullanmışsın.
Harika iş. Daha sonra derinlemesine bakmak için yer imlerine ekledim.
NAND-to-Tetris'i seviyorum ama sonuna kadar tamamlayamamıştım; bu projeyi incelemeyi sabırsızlıkla bekliyorum.
Merak ettim, toplam NAND kapısı sayısı kaç?