CodeGolf: emulare parzialmente la vecchia CPU 8086

Pubblicità

DispatchCode

Utente Èlite
Messaggi
2,504
Reazioni
2,048
Punteggio
144
Rieccomi dopo un pò di tempo! Qualcuno appassionato di golf? :)


450px-Golfer_swing.jpg

Anche questa volta voglio proporre qualcosa, e no, non si tratta di una partita a golf. :lol:



Scherzi a parte, qualche tempo fa ho notato questo CodeGolf che ho provveduto a salvare tra i segnalibri: https://codegolf.stackexchange.com/questions/4732/emulate-an-intel-8086-cpu

Riporto schematicamente ciò che dovrà essere implementato; link utili, il binario da eseguire, e il codice sorgente, li trovate al link qui sopra.

  • Only the following opcodes need to be implemented:
    • mov, push, pop, xchg
    • add, adc, sub, sbb, cmp, and, or, xor
    • inc, dec
    • call, ret, jmp
    • jb, jz, jbe, js, jnb, jnz, jnbe, jns
    • stc, clc
    • hlt, nop
  • As a result of this, you only need to calculate the carry, zero and sign flags
  • Don't implement segments. Assume cs = ds = ss = 0.
  • No prefixes
  • No kinds of interrupts or port IO
  • No string functions
  • No two-byte opcodes (0F..)
  • No floating point arithmetic
  • (obviously) no 32-bit things, sse, mmx, ... whatever has not yet been invented in 1979
  • You do not have to count cycles or do any timing

Start with ip = 0 and sp = 100h.

Input: Your emulator should take a binary program in any kind of format you like as input (read from file, predefined array, ...) and load it into memory at address 0.

Output: The video RAM starts at address 8000h, every byte is one (ASCII-)character. Emulate a 80x25 screen to console. Treat zero bytes like spaces.

Spero di vedere qualche partecipante o almeno qualche domanda, considerando che è un contest a parer mio interessante/stimolante. :D

Aspetterò qualche soluzione o qualche giorno prima di linkare la mia; lascio solo lo screen del mio output sotto spoiler:
68747470733a2f2f692e6962622e636f2f38385a32634d4b2f636f6465676f6c662e706e67
 
Roba che facevo quaranta anni fa, da buon hacker, quando passavo le notti nei laboratori del dipartimento di fisica invece che preparare gli ultimi esami. Ovviamente tutto in standard C. Vedo comunque che lo spirito rimane nella comunità, vedere fare queste cose in JS e Python è impressionante. Beh, in fondo non è così difficile, solo tedioso. Io scrissi un intero emulatore per una calcolatrice HP programmabile, non ricordo il modello. Chi me lo fece fare... Quello più divertente fu emulatore BASIC... con variabili a un numero infinito di cifre decimali... pazzo...
 
Roba che facevo quaranta anni fa, da buon hacker, quando passavo le notti nei laboratori del dipartimento di fisica invece che preparare gli ultimi esami. Ovviamente tutto in standard C. Vedo comunque che lo spirito rimane nella comunità, vedere fare queste cose in JS e Python è impressionante. Beh, in fondo non è così difficile, solo tedioso. Io scrissi un intero emulatore per una calcolatrice HP programmabile, non ricordo il modello. Chi me lo fece fare... Quello più divertente fu emulatore BASIC... con variabili a un numero infinito di cifre decimali... pazzo...

Richiede principalmente tempo per andare a guardarsi le documentazioni. Nel caso di specie è quasi sufficiente guardare solo l'elenco di opcodes e la struttura delle istruzioni. Non dovendo gestire segmenti e altro, come la memoria video (magari supportare VGA etc), non è particolarmente complesso, ma dipende poi da quanto si programma (secondo me).
 
Inizio a pubblicare il mio: https://github.com/DispatchCode/NaTE

Brevemente, ild.c è il progetto che sto piano piano portando avanti; si tratta in questo caso di una versione molto semplificata e adattata al 16bit. Di fatto si occupa di disassemblare l'istruzione scomponendola nelle sue parti.

cpu.c inizializza la CPU, creando anche la "ram" e inizializzando i registri.

cpu_exec.c esegue una singola istruzione disassemblata in precedenza, ed è di fatto l'emulazione vera e propria.

Questo due ultimi file sono parte di un progetto datato che ho messo in standby, per così dire, e molto più vasto.
 
Mi permetto di riesumare un vecchio topic perchè ho un aggiornamento.

Siccome sto iniziando a studiare un pò Rust nel tempo libero, ho pensato di scriverlo anche in Rust. Se qualcuno fosse interessato qui trova il codice: https://github.com/DispatchCode/NaTE-rust :)

Sotto spoiler metto uno screenshot:

rust_nate.webp

Mi manca un pò la libertà che lascia il C... ma allo stesso tempo trovo Rust interessante, quindi proseguirò ad utilizzarlo sicuramente! :)
 
Siccome sto iniziando a studiare un pò Rust nel tempo libero

Hihihi ormai sei stato risucchiato dal lato oscuro :D

Mi manca un pò la libertà che lascia il C... ma allo stesso tempo trovo Rust interessante, quindi proseguirò ad utilizzarlo sicuramente! :)

La libertà di sviluppare usando vagonate di anti-pattern! A me fanno incazzare quelli che vanno su reddit o stackoverflow e sparano a zero contro Rust. Specialmente i programmatori C++. Evidentemente si sentono detronizzati o non riescono ad adeguarsi alle rigide regole del modello di memoria di Rust....e s'incazzano!

Ma in tutta onestà, ho trovato Rust una boccata d'aria fresca in un mondo ormai stagnante. Le difficoltà dovute al dover acquisire nuove abitudini di programmazione, te le ritrovi ripagate per mille.
 
Hihihi ormai sei stato risucchiato dal lato oscuro :D

Ho paura di si... :D

La libertà di sviluppare usando vagonate di anti-pattern! A me fanno incazzare quelli che vanno su reddit o stackoverflow e sparano a zero contro Rust. Specialmente i programmatori C++. Evidentemente si sentono detronizzati o non riescono ad adeguarsi alle rigide regole del modello di memoria di Rust....e s'incazzano!

Ciò che mi ha creato qualche "difficoltà" è stata appunto la rigidità nel compiere alcune operazioni.
Faccio un esempio:

C:
memcpy(&instr->disp, (data + index), instr->disp_len);

Qui ho data che è un puntatore a char e instr->disp che è un uint16. Io sono certo che quel "len" valga 1 o 2, quindi copio 1-2 byte dalla locazione puntata da data+index in disp.

In Rust, non conoscendolo ancora decentemente, ho dovuto ripiegare su:

Codice:
fn read_val(data : &Vec<u8>, index : usize, val_size : usize) -> u16{
    match val_size {
        1 => { data[index] as u16 },
        2 => { ((data[index + 1] as u16) << 8) | (data[index]) as u16},
        _ => { 0 }
    }
}

Altra situazione? La somma tra due u8/u16 che va in overflow, piuttosto che su, ad esempio, u8 e u16... che non si può fare avendo tipo differente. :D
Utilizzando C (ma anche tanti altri linguaggi, in realtà) puoi fare di tutto.

Comunque sto apprezzando altri aspetti di Rust; se devo trovare ciò che secondo me è ancora un punto critico, sono le documentazioni di alcuni (o molti) crate. Lo sto utilizzando anche per un altro "programmino" dove ho iniziato ad usare una GUI, ed ho avute non poche difficoltà inizialmente (e ora che tornerò a metterci mano immagino proseguiranno).

Ma in tutta onestà, ho trovato Rust una boccata d'aria fresca in un mondo ormai stagnante. Le difficoltà dovute al dover acquisire nuove abitudini di programmazione, te le ritrovi ripagate per mille.

Su questo concordo.
Inoltre ci si trova a dover pensare in un modo un pò diverso rispetto all'approccio che si ha con altri linguaggi (e paradigmi).
 
C:
memcpy(&instr->disp, (data + index), instr->disp_len);

Beh però questo è l'equivalente di un triplo salto mortale :D

Il compilatore non avrebbe mai modo di capire che sta succedendo e cosa potrebbe andare storto. E Rust è pensato proprio per evitare questa confusione. Alla fin fine si tratta di dare al compilatore le informazioni necessarie per capire la possibile evoluzione dello stato del programma. Il compilatore vuole sapere, in ogni istante, qual è il dominio di ognuna delle variabili. E si spinge fino al punto, in casi particolari dove non riesce a capirlo da solo, a farti specificare pure il lifetime di una variabile.


dove ho iniziato ad usare una GUI

Azul?
 
Beh però questo è l'equivalente di un triplo salto mortale :D

Il compilatore non avrebbe mai modo di capire che sta succedendo e cosa potrebbe andare storto. E Rust è pensato proprio per evitare questa confusione. Alla fin fine si tratta di dare al compilatore le informazioni necessarie per capire la possibile evoluzione dello stato del programma. Il compilatore vuole sapere, in ogni istante, qual è il dominio di ognuna delle variabili. E si spinge fino al punto, in casi particolari dove non riesce a capirlo da solo, a farti specificare pure il lifetime di una variabile.

Si, è un triplo salto mortale... ma è comodo :D
Non sono un male tutti i check che vengono fatti, alcuni sono un pò "rognosi" se non si è abituati, ma alla fine credo che i benefici si vedano poi in progetti più corposi dove tutte le situazioni "ambigue" sono causa di bug, magari a causa di un particolare evento o disattenzioni di chi sviluppa.

Tu hai studiato/studi Rust per puro interesse o lo utilizzi per lavoro?


No, quello l'ho visto e per applicazioni che fanno uso di grafica credo proprio che lo proverò.
Voglio disegnare su una finestra un'immagine - che genero - e su consiglio ho scelto ImGui. A questo ho abbinato poi glium e gfx.
 
Tu hai studiato/studi Rust per puro interesse o lo utilizzi per lavoro?

Eh magari. In Italia siamo fermi a C# e VB.NET :asd: ...un giorno forse ci faranno lavorare su Go...entro il 2080 qualche progetto sarà realizzato in Rust :D


No, quello l'ho visto e per applicazioni che fanno uso di grafica credo proprio che lo proverò.
Voglio disegnare su una finestra un'immagine - che genero - e su consiglio ho scelto ImGui. A questo ho abbinato poi glium e gfx.

Ottima scelta ImGui. Che tra l'altro è la libreria a cui si sono ispirati i creatori di Azul, che però hanno deciso di utilizzare il WebRender di Mozilla, piuttosto che impelagarsi nei meandri delle API Windows, Linux e compagnia.
 
Pubblicità
Pubblicità
Indietro
Top