FYI.

This story is over 5 years old.

Tecnologia

Gli integer a 32-bit e l'importanza dei vecchi computer

Cosa hanno in comune la psichedelia del livello 256 di Pac-Man, l'architettura di sistema e l'integer overflow.
I​mmagine: Pauli Rautakorpi/Wiki

È stato il numero 256 a rompere la versione arcade originale di Pac-Man. Essendo un gioco senza un'appropriata exit condition, Pac-Man contava sul fatto che i giocatori si sarebbero stancati di giocare prima del livello 256. Aveva anche senso la cosa, considerato che tutti i livelli dopo il 20 erano una copia del livello 20. Ma poche cose portano alla compulsione ossessiva come i videogiochi, persino nel 1980, e alcuni giocatori la presero come una sfida—un test di resistenza e concentrazione. Quelli che raggiunsero il 256 videro uno strano spettacolo, che gli informatici chiamerebbero "comportamento indefinito." Questo era il risultato:

Pubblicità

Immagine: YouTube

Hanno trovato una sorpresa ad aspettarli al livello 256," scrive Jamey Pittman nel Pac-Man Dossier. "Una sorpresa di cui nessuno sapeva niente—neanche gli sviluppatori alla Namco."

Il livello 256 è una landa psichedelica dentro Pac-Man. Metà dello schermo mostra il gioco normale, ma l'altra metà è un diluvio di simboli e caos. "Nonostante sia il giocatore sia i fantasmi possano navigare nella metà destra dello schermo," scrive Pittman, "il labirinto originale non ha più le sue mura. Al loro posto, Pac-Man deve girare tra una serie senza senso di zone aperte, tunnel, intersezioni a senso unico, muri spuri, e passaggi—tutti invisibili al giocatore—con i quattro fantasmi sempre alle calcagna."

La ragione tecnica è un integer overflow. Pittman prosegue:

Come si è creato questo livello difettoso? Il colpevole è la routine che serve a disegnare i simboli sul bordo in basso dello schermo. Ecco cosa succede: quando si raggiunge il livello 256, il contatore interno dei livelli aumenta di 255 (il contatore inizia da zero, non da uno) e la routine che disegna i simboli è chiamata in causa. La routine carica il valore attuale del contatore (255) in un registro CPU e incrementa quel registro di uno. Sfortunatamente, 255 è il numero più grande che possa entrare in un singolo byte che ha le dimensioni dei registri Z-80 CPU , così quando il valore aumenta, ciò che eccede viene scartato, e nel registro rimane uno zero invece del 256 che ci si aspetterebbe. Questo valore zero porta la routine a credere che si tratti di un livello iniziale, dal momento che il suo valore è meno di sette.

Pubblicità

La routine comincia a disegnare i simboli usando il registro confuso come contatore. Alla fine di ogni loop, il registro viene diminuito di una cifra e poi ricontrollato per vedere se è a zero (il segnale che fa smettere alla routine di disegnare simboli). Dal momento che il registro ha già uno zero in partenza, il primo decremento riavvolge il valore a 255. Continua a togliere al registro valori e a disegnare simboli finché il registro non torna di nuovo a zero, costringendo il loop a girare un totale di 256 volte. Questo significa che le celle di memoria fuori dai limiti della tabella dei simboli sono disegnate sullo schermo in collocazioni crescenti nella memoria video. Questo livello mezzo-guasto è stato chiamato lo "split screen" dai giocatori; gli sviluppatori invece lo chiamano "kill screen".

Per il processore Z-80, 256 è il punto di incontro tra hardware e software: un data type a byte singolo. (255 è il valore più grande che possa essere contenuto in 8 bit.) O, in modo più specifico, i limiti del data type. Quello che un hardware è fisicamente in grado di rappresentare.

Al livello base dei dati c'è l'integer. Questo è il valore numerico massimo che può essere immagazzinato e manipolato (direttamente) entro un dato sistema. Il singolo byte di X-80 è arrivato in un periodo in cui l'evoluzione dei computer era piuttosto rapida, e nello stesso periodo i primi processori a 32-bit (quattro byte) stavano entrando nel mercato. Questa grandezza rimane lo standard di oggi. Anche i nuovi processori a 64 bit sono progettati pensando a una retro-compatibilità con quelli a 32 bit.

Pubblicità

2.147.483.647

Il numero 2.147.483.647 è tra i numeri più importanti di tutta l'informatica (forse è proprio il più importante). Corrisponde a un data type conosciuto ai più come 32-bit signed integer. Normalmente vi si fa riferimento nei linguaggi di programmazione semplicemente con un "int," un valore del genere è limitato da una rappresentazione binaria a 32-bit, che è esattamente ciò che sembra: 32 uno e zeri vicini. Se riempissimo 31 di questi slot con degli '1', il 32esimo sarebbe riservato a un valore "sign bit," ovvero un valore che dice al sistema se il numero rappresentato è positivo o negativo (visto che non ci sono 'meno' nel binario)-la traslazione decimale sarebbe 2.147.483.647.

I computer hanno dei limiti, anche oggi. Molto spesso non sembra così, visto che anche gli smartphone del 2015 sono dei supercomputer se paragonati alle macchine di 15 anni fa. Molti linguaggi di programmazione di alto livello ora non chiedono nemmeno più ai programmatori di denominare i tipi di dati, ma li estrapolano dal contesto. Così è semplice perdere di vista il fatto che siano macchine.

I 32 bit hanno cominciato a spaccare nel 1979, con l'uscita del microprocessore Motorola 68000. La maggior parte delle sfide, in quel periodo, riguardavano il passaggio da 8 a 16 bit e sono durate poco, grazie all'impeto degli sviluppatori che volevano cavalcare l'onda dei PC. Il 68000 è entrato nel mercato come processore dei primi computer consumer-oriented, come l'Apple Lisa, Machintosh, i computer Commodore di Amiga. Probabilmente sono usati ancora oggi.

Pubblicità

Ma che significato hanno davvero questi bit?

Immagine:  W Nowicki/Wiki

Una CPU non è una macchina così misteriosa come può sembrare. Si tratta di cavi—ora come ora quei cavi sono incredibilmente piccoli, ma sono ancora lì a far girare elettroni nel nome dell'informatica. Dentro un microprocessore puoi trovare una piccola carta geografica di slot di memoria allocabili, che sono solamente degli insiemi di transistor; circuiti che fanno cose come aggiungere o spostare sequenze di bit (cavi e switch); register (spazi di memoria che immagazzinano dati così come sono mentre vengono computati); e, infine, bus che esistono per spostare allocazioni di memoria, istruzioni e veri e propri dati in giro per la CPU. Proprio come fa un bus.

Le entità fisiche dei bus e dei register, in particolare, sono finite, ovvero possono trasportare stringhe in unità da 32 bit e immagazzinare 32 zeri o uno. Tutto ciò che è più grande deve essere diviso e riorganizzato, ed è ciò che succede praticamente sempre.

Puoi provare a spedire 33 bit in un bus da 32 bit, ma puoi aspettarti che succedano due cose. In alcuni, il valore potrebbe andare in "wrap," che è più o meno ciò che succede all'odometro della macchina quando va in sovraccarico. La cifra più importante viene tagliata fuori e il nuovo valore è ben lontano dall'essere accurato. Qui c'è un esempio di Wikipedia di una situazione in cui ciò si può verificare, in cui due valori all'interno della rappresentazione a 32 bit vengono sommati e il risultato è un numero più grande di 32 bit: 000000000u + 1000000000u = 705032704u. Direi che è sbagliato.

L'alternativa a questo tipo di lavoro sul valore è abbastanza interessante. In questo caso anziché andare in wrap, il sovraccarico si satura. Questa è una particolare caratteristica conosciuta come saturazione aritmetica, dove i numeri sono limitati da un range specifico e nel caso un numero uscisse da quel range, cambierebbe subito per diventare il numero più grande possibile in quella certa aritmetica. Il processo si chiama "clamping." Quindi l'equazione di cui sopra diventerebbe: 4000000000u + 1000000000u = 2147483647u. Non è ancora giusta, ma è un po' meno sbagliato.

Mentre scrivo e mentre voi leggete questo, è in corso un conto alla rovescia piuttosto importante. L'orologio in questione è un tipo di computer data, conosciuto come time_t. Questo valore cambia ogni secondo e continua a diventare più alto dal 1 gennaio 1970. Permette ai sistemi basati su UNIX di computer velocemente ora e data, che è una funziona piuttosto comoda. Il valore time_t è implementato in qualunque forma di computer, ma ha un problema. Sì, time_t è un integer signed a 32bit. Ciò significa che a un certo punto (3:14:07 UTC di martedì 19 gennaio 2038, per essere precisi), saranno passati più di 2147483647 secondi.

Il tempo andrà in overflow e non c'è modo di risolvere questo problema. Cambiare time_t, o resettarlo, corrisponde a un sacco di codice che non sarebbe più compatibile con il nuovo, e ciò violerebbe uno dei principi fondamentali dell'informatica, che è la retrocompatibilità. Non credo verranno lanciati automaticamente dei missili nucleari, ma darà fastidio a diverse cose in maniera un po' più grave del Millenium Bug.

Tick tock.