Questo agosto per una serie di eventi mi sono trovato a stare (quasi) senza internet per 2 settimane, questo mi ha dato modo di riprendere un paio di progetti che avevo messo da parte, uno di questi era l’implementazione del chip8.
È una piccola CPU piuttosto semplice (la versione più semplice ha sole 36 istruzioni) ed è considerata l’Hello World per i progetti di emulazione.
Anche se semplice da molti spunti per lavorare con aspetti della programmazione che non si utilizzano spesso, in particolare la bit manipulation , il tipo di variabili da utilizzare (consiglio vivamente un linguaggio tipato) e la lettura di un documento delle specifiche.
Quest’ultimo aspetto non è da sottovalutare, vi garantisco che sbaglierete l’implementazione di almeno un’istruzione perchè leggerete male la guida.
Per la parte grafica ho utilizzato raylib, davvero semplice rispetto a SDL2.
Il chip8 ha uno schermo che è soli 64x32 pixel quindi è anche divertente trovare un modo per simularlo con raylib.
Un paio di consigli generali (che ovviamente potete ignorare):
Non cercate di complicare il progetto, il chip8 è semplice e non serve overengineering o chissà quale tecnica.
Non serve allocazione dinamica o design pattern stile observer/observed (per display o keypad), provare per credere.
Scrivere un paio di test vi aiuta un sacco, non dico di fare TDD, ma un test per istruzione aiuta molto.
Per una volta i test sono stati anche divertenti da scrivere, ho dovuto ragionare su come mettere dei valori esadecimali in memoria per far si che venissero le operazioni che volevo io con i valori che dicevo io, controllare la gestione dello stack e dei registri della cpu, insomma davvero figo.
Qui trovata la guida che ho usato, il sito è così semplice e senza fronzoli che il browser lo carica sempre dalla cache .
Se qualcuno si vuole cimentare in questo esercizio se ne può parlare qui sotto, magari anche per qualche problema di implementazione o simile .
Vi lascio anche il mio sorgente su github, è scritto in un linguaggio non molto conosciuto ma è molto semplice da leggere, assomiglia abbastanza a golang.
Il mio emulatore non è completo, manca l’audio (non l’ho fatto per pigrizia) ed un’emulazione precisa del clock (stessa ragione), vi consiglierei di ignorarle in un primo momento e di aggiungerle dopo che avete un progetto funzionante.
Com’è sto Odin? Io l’ho studiato e non mi ha colpito, sembra Golang senza GC sinceramente e non ne sono fan. Sono più tipo da Rust io, per farti capire. E infatti ora ti becchi il mio emulatore. Io ho usato wgpu (l’implementazione WebGPU di Firefox) per la grafica e uno shader semplice. Per l’audio ho usato semplicemente una lib rodio che fa le sue cose magiche a cui non sono per nulla interessato, l’importante è che faccia beep per me.
In asm c’è l’assembler
In bin c’è il parser degli opcode
In cpu c’è il simulatore della sola CPU
In emu praticamente incollo cpu a wgpu e rodio
Per testare ho usato questa suite, non ti accorgi di tutti i bug che hai fino a quando non testi con quella roba.
Per provarlo cargo run --bin ch8emu -r -- <ROM> (così non si confonde con l’assembler e buildate in release mode)
So far so good, come hai detto tu è molto simile a GO ma ci sono delle differenze oltre al garbage collector, ha un sistema di tipi migliore ad esempio.
Supporta errors as values e le enum possono avere payload, con queste due feature si può programmare con tecniche da linguaggio di alto livello, per dire puoi fare pattern matching come in rust, ocaml, etc…
Ci sono altre cose molto più avanzate, tipo gli allocator, il context e costrutti per la data-oriented programming, ma non li ho ancora provati (praticamente è il primo codice che scrivo in odin) quindi non ti so dare un parere personale, posso solo dire che ne ho sentito parlare bene.
Alla fine è molto un gusto personale, ad esempio per me è l’opposto, ho provato a studiare rust ma non mi convince.
Grazie per la suite di test, ora la faccio runnare un secondo e vedo come va .
PS: il tuo emulatore fa sembrare il mio un giocattolino ahahahah
Rust è Haskell che vive nel mondo reale, ma senza GC. E io amo Haskell. Se compila sei quasi sicuro che funzioni, non lascia nulla scoperto, non ha sintassi ad-hoc strane per degli oggetti builtin (<-chan, ->chan, range e se continuo a parlare male di Golang oggi mi faccio bannare). Il compilatore ti prende a schiaffi e ti consola allo stesso tempo, non ti lascia scrivere codice dubbio, il tooling è pazzesco. A me non piace parlare in termini di gusti, sinceramente, ma in termini di feature per determinati task. L’unica cosa che ti prende a calci nei denti è il borrow checker (e gli effetti collaterali), una volta che ne hai capito il senso e come funziona non hai più dubbi.
Comunque sì, ho semplificato molto con Odin, però le feature per il data-oriented (SOA per esempio) le trovo superflue, non capisco come possano fare la differenza, ma va be’, è grasso che cola.
L’overload delle funzioni non mi è mai piaciuto, credo che porti più confusione (e mangling brutto) che altro.
Le enum (sia plain che tagged union o “enum con payload”, come le chiami tu) per me non sono una feature, sono la base per un linguaggio moderno, è scandaloso che Golang non le abbia. Il pattern matching è un effetto collaterale.
Gli allocator ce li ha anche Rust, se ti interessa, però sì, non tutti ti danno questa flessiblità.
I context sono il primo passo verso l’inferno. Side effects e UB. Sarà stupendo debuggare quando usi un context.
Error as value è uno standard ormai, anche C++ sta passando a std::expected perché le eccezioni hanno bisogno di unwinding, sono UB e più core hai più ti ammazzano la concorrenza.
Tornando in topic: non usare una matrice di bool per il canvas, divertiti con un bitarray
Pure qua stiamo andando OT , purtroppo è difficile prevedere dove andranno le discussioni.
Se devo andare un pò più nel dettaglio sul perchè non mi piace rust è che devi prevedere troppo ed essere molto specifico, pure qui bastonami pure se dico cazzate non sono (e non lo sarò a breve) un programmatore rust.
Rust è Haskell che vive nel mondo reale
È proprio questo che non mi convince, se haskell non si è dimostrato utilizzabile (adottabile su larga scale) non vedo perchè debba esserlo rust, pure il se “compila sei quasi sicuro che funziona” è molto lontano dal “se compila funziona”.
Nelle codebase che ho visto si ricorre spesso ad operazioni “unsafe”, ci sono anche nel tuo chip8, questo va troppo in contrasto con la prima affermazione.
Prendi pure il borrow checker, “ti prende a calci sui denti” ma è una delle parti centrali del linguaggio e la maggior parte degli utenti non la conosce bene, mettici pure arc, mutex, lifetimes ed anche gli esperti hanno difficoltà (Nota che non ho bisogno di essere un’esperto per sostenerlo).
Anche per gli allocator, sono stati aggiunti in seguito (quindi potrebbe essere la prima di molte), una cosa non prevista e che non sarà mai integrata alla perfezione nel linguaggio proprio perchè è venuta dopo .
Un linguaggio lo giudico molto di più per quello che ci si è fatto e non dalle feature, go e c non hanno tagged union ma ci sono molti più tool scritti in uno di questi due linguaggi che in rust.
Si capisce subito che non conosci Rust, c’è troppo da smontare punto per punto tanto che riscriverei il Rust Book.
Quando ho detto che ho studiato Odin ero serio, non l’ho guardato di sfuggita.
La statitistica dei tool non so dove l’hai presa, ma credo che sia un tuo parziale discutibile, a parte per C, un linguaggio di 50 anni che non ho mai messo nell’equazione per ovvi motivi.
Ragazzi, tutto interessante, ma qui il focus è il progetto CHIP8. La discussione adesso sta andando molto OT, non solo rispetto alla discussione del progetto CHIP8 ma proprio rispetto all’obiettivo di questo forum.
Le opinioni personali sono inevitabili, ovviamente, ma sono utili solo quando argomentate, eventualmente portando codice o esempi, e soprattutto quando non vanno ad intaccare l’argomento principale della discussione. L’inizio della discussione era interessante, adesso è palesemente degenerata, quindi direi in futuro solo commenti relativi al progetto CHIP8.
Bel progetto @antofma97, e grazie anche a @shurizzle per la condivisione della sua implementazione. Se qualcun altro lo ha implementato in altri linguaggi può linkare a seguire.
Si possono fare thread appositi per parlare di linguaggi nello specifico e nel confrontare i vari linguaggi. Sono discussioni molto complesse e piene di opioni, come è giusto che sia, e dunque meritano il loro spazio separato.
Grazie per i contributi a tutti e buon proseguimento.