tutorial · commodore 64tutorial · commodore 64
Come scorre la stradaHow the road scrolls
In Ruote Fumanti la strada scende dall'alto verso il basso, pixel per pixel, cinquanta volte al secondo, e non trema mai. Sembra la cosa più semplice del gioco. Su un Commodore 64 è quella che ha richiesto più ingegno, perché la macchina non ha niente di pronto per farlo: né una memoria video in cui disegnare liberamente, né un comando che «sposti l'immagine». Questo è il modo in cui ci si arriva, con i pezzi veri del programma. In Ruote Fumanti the road slides down from top to bottom, pixel by pixel, fifty times a second, and it never shakes. It looks like the simplest thing in the game. On a Commodore 64 it's the one that took the most ingenuity, because the machine has nothing ready-made for it: no video memory to draw into freely, no command that "moves the picture". This is how you get there, with the real pieces of the program.
la strada in corsa, registrata in VICE a cinquanta fotogrammi al secondo.the road at speed, recorded in VICE at fifty frames a second.
Il codice è assembler 6510, la lingua del processore del C64, e non serve saperlo leggere: ogni pezzo è spiegato riga per riga subito dopo. I commenti dentro il codice, quelli dopo il punto e virgola, sono gli originali, e sono in parte in inglese e in parte in italiano come li ho scritti.
The code is 6510 assembly, the C64 processor's language, and you don't need to be able to read it: every piece is explained line by line right after. The comments inside the code, after the semicolon, are the originals, partly in English and partly in Italian, as I wrote them.
Il C64 non disegna pixel: incolla mattonelleThe C64 doesn't draw pixels: it lays tiles
Lo schermo è una griglia di 40 colonne per 25 righe. In ogni casella non si mette un'immagine, ma il numero di un carattere: un disegnino di 8 per 8 pixel preso da una tavola di 256. La strada, l'erba, i cordoli e l'acqua sono tutti caratteri ridisegnati apposta.
The screen is a grid of 40 columns by 25 rows. Each cell holds not a picture but the number of a character: a little 8 by 8 pixel drawing taken from a table of 256. The road, the grass, the kerbs and the water are all characters redrawn for the purpose.
È un sistema velocissimo, perché per cambiare quello che si vede basta scrivere un numero in una casella. Ma per un gioco di guida ha un difetto grosso: le caselle sono fisse. Un carattere sta nella riga 5 o nella riga 6, non a metà. Se la strada si spostasse solo di caselle intere scatterebbe di 8 pixel alla volta, e a bassa velocità si vedrebbe benissimo.
It's a very fast system, because changing what you see only takes writing a number into a cell. But for a driving game it has a big flaw: cells are fixed. A character sits in row 5 or row 6, not in between. If the road only moved by whole cells it would jump 8 pixels at a time, and at low speed that would be plain to see.
Il trucco dei sette pixelThe seven-pixel trick
Il chip video, il VIC-II, ha un registro chiamato YSCROLL: vale da 0 a 7 e sposta tutta l'immagine verso il basso di quel numero di pixel. Lo scorrimento allora si fa in due tempi. Per i primi sette pixel non si tocca nessuna casella: si aumenta YSCROLL e l'intero schermo scivola giù. Quando si arriverebbe all'ottavo, si riporta YSCROLL indietro di 8 e nello stesso istante si ricopia tutta la strada una riga più in basso, mettendo una riga nuova in cima.
The video chip, the VIC-II, has a register called YSCROLL: it runs from 0 to 7 and shifts the whole picture down by that many pixels. So scrolling happens in two beats. For the first seven pixels no cell is touched: YSCROLL goes up and the entire screen slides down. When you'd reach the eighth, you set YSCROLL back by 8 and at the same instant copy the whole road one row lower, putting a new row at the top.
otto fotogrammi: nei primi sette cambia solo un registro, all'ottavo la strada si ricopia di una riga e il registro torna a zero. La riga scesa di una casella risale di otto pixel, e l'occhio non vede niente.eight frames: in the first seven only a register changes, on the eighth the road is copied one row down and the register goes back to zero. The row that dropped a cell climbs back eight pixels, and the eye sees nothing.
Chi guarda non vede il salto, perché le due cose si compensano esattamente. Restano due problemi, e il resto del tutorial è la loro soluzione. Il primo: ricopiare la strada richiede tempo, e durante la copia lo schermo sarebbe mezzo vecchio e mezzo nuovo. Il secondo: con YSCROLL a 7, in cima comparirebbe una fascia di pixel vuoti. Questo si risolve subito, perché il C64 ha una modalità a 24 righe invece di 25, che copre col bordo mezza riga in alto e mezza in basso: la riga che sta entrando resta nascosta dietro il bordo.
Whoever's watching doesn't see the jump, because the two things cancel out exactly. Two problems remain, and the rest of the tutorial is their solution. First: copying the road takes time, and during the copy the screen would be half old and half new. Second: with YSCROLL at 7, a band of empty pixels would show at the top. That one is solved at once, because the C64 has a 24-row mode instead of 25, which covers half a row at the top and half at the bottom with the border: the incoming row stays hidden behind the border.
Quanto scorre in un fotogrammaHow far it scrolls in a frame
Prima di tutto bisogna sapere di quanti pixel far scendere la strada, e dipende dalla velocità.
First of all you need to know how many pixels to move the road down, and that depends on speed.
Il processore del C64 non sa moltiplicare: sa solo raddoppiare un numero (asl, «sposta a sinistra») e sommare (adc). Allora «sei volte la velocità» diventa due volte più quattro volte. lda fis_speed_hi legge la velocità in miglia orarie; i due asl tmp / rol tmp2 la raddoppiano e poi la raddoppiano ancora, e il valore da «due volte» viene messo da parte in count; le due somme mettono insieme due volte e quattro volte.
The C64's processor can't multiply: it can only double a number (asl, "shift left") and add (adc). So "six times the speed" becomes twice plus four times. lda fis_speed_hi reads the speed in miles per hour; the two asl tmp / rol tmp2 double it and double it again, and the "twice" value is set aside in count; the two additions put twice and four times together.
Il risultato non è in pixel ma in duecentocinquantaseiesimi di pixel: il byte alto, step_px, sono i pixel interi, il byte basso è la frazione. E la frazione non si butta: si somma a fis_frac, che la conserva da un fotogramma all'altro, e quando le frazioni accumulate fanno un pixel intero quel pixel viene aggiunto. È questo che rende lo scorrimento regolare anche a velocità «strane»: a 50 miglia orarie la strada scende di 1,17 pixel a fotogramma, cioè di norma 1 e circa un fotogramma su sei 2, sempre con la stessa cadenza. Senza l'accumulo scenderebbe di 1 pixel e basta, e andare a 50 o a 80 sembrerebbe uguale.
The result isn't in pixels but in 256ths of a pixel: the high byte, step_px, is whole pixels, the low byte is the fraction. And the fraction isn't thrown away: it's added to fis_frac, which keeps it from frame to frame, and when the accumulated fractions make a whole pixel that pixel gets added. That's what keeps the scroll even at "odd" speeds: at 50 mph the road moves 1.17 pixels a frame, which is usually 1 and about one frame in six 2, always at the same cadence. Without the accumulator it would move 1 pixel and that's it, and 50 or 80 would look the same.
Sette pixel, poi la copiaSeven pixels, then the copy
Adesso che si sa di quanto scendere, questo è il pezzo che decide se basta YSCROLL o se serve copiare.
Now that we know how far to move, this is the piece that decides whether YSCROLL is enough or a copy is needed.
lda nextfine / adc step_px prende la posizione fine attuale, il valore di YSCROLL da 0 a 7, e ci somma i pixel di questo fotogramma. cmp #8 / bcs .coarse: se il totale arriva a 8 o più serve una copia, altrimenti sta nextfine salva il nuovo YSCROLL e rts esce. Nella maggior parte dei fotogrammi finisce qui: sette istruzioni, e la strada è già scesa.
lda nextfine / adc step_px takes the current fine position, the YSCROLL value from 0 to 7, and adds this frame's pixels. cmp #8 / bcs .coarse: if the total reaches 8 or more a copy is needed, otherwise sta nextfine saves the new YSCROLL and rts returns. Most frames end right here: seven instructions, and the road has already moved.
Sotto .coarse si conta quante righe intere sono passate, una oppure due se il totale arriva a 16; in corsa, anche a 220 miglia orarie, i pixel per fotogramma sono al massimo sei, quindi di norma è una riga sola. and #7 è il «riporta YSCROLL indietro di 8»: tiene solo il resto, quindi se si era a 6 e si aggiungono 5 il totale è 11, si copia una riga e YSCROLL diventa 3. lda front / eor #1 / sta nextfront è il trucco del prossimo capitolo: front dice quale dei due schermi si sta guardando, ed eor #1 sceglie l'altro.
Under .coarse you count how many whole rows have gone by, one, or two if the total reaches 16; while racing, even at 220 mph, pixels per frame top out at six, so normally it's a single row. and #7 is the "set YSCROLL back by 8": it keeps only the remainder, so if you were at 6 and add 5 the total is 11, one row is copied and YSCROLL becomes 3. lda front / eor #1 / sta nextfront is the trick in the next chapter: front says which of the two screens is being shown, and eor #1 picks the other.
Due schermi, come un blocchetto animatoTwo screens, like a flip book
Il problema della copia a metà si risolve così: gli schermi sono due. Il C64 permette di dire al chip video in quale zona della memoria sta lo schermo, e Ruote Fumanti ne tiene due, uno a $4000 e uno a $4400. Uno è quello che si vede, l'altro è nascosto. Il programma scrive sempre in quello nascosto, con calma, e quando il disegno è finito dice al chip video di mostrare l'altro. È il principio del blocchetto animato: non si cancella il foglio che si sta guardando, si prepara il successivo e poi si gira pagina.
The half-copied screen problem is solved like this: there are two screens. The C64 lets you tell the video chip which area of memory the screen lives in, and Ruote Fumanti keeps two, one at $4000 and one at $4400. One is visible, the other hidden. The program always writes into the hidden one, taking its time, and when the drawing is done it tells the video chip to show the other. It's the flip-book principle: you don't rub out the page you're looking at, you prepare the next one and then turn the page.
Questo non è codice che gira: è una ricetta per l'assemblatore, il programma che traduce l'assembler in codice macchina. !for vuol dire «ripeti»: per ognuna delle 24 righe e ognuna delle 25 colonne di strada scrivi due istruzioni. lda $4000+... legge il carattere nella riga .r dello schermo A, sta $4400+(.r+1)*40... lo scrive nello schermo B una riga più sotto. PANEL_COLUMNS salta le prime undici colonne, dove sta il pannello, che non scorre.
This isn't code that runs: it's a recipe for the assembler, the program that turns assembly into machine code. !for means "repeat": for each of the 24 rows and each of the 25 road columns, write two instructions. lda $4000+... reads the character in row .r of screen A, sta $4400+(.r+1)*40... writes it into screen B one row lower. PANEL_COLUMNS skips the first eleven columns, where the panel sits, which doesn't scroll.
Il risultato sono 600 coppie di istruzioni una dopo l'altra, senza nessun ciclo. Oggi si scriverebbe un ciclo con un contatore, molto più corto. Qui però conta il tempo, non la lunghezza: sul 6510 spostare un byte con l'indirizzo indicizzato costa 9 cicli di processore, contro gli 8 con l'indirizzo scritto per esteso, e il ciclo stesso ne costa altri. Il commento originale, nel sorgente, lo dice così: «The scroll copy, unrolled. Indexed addressing costs nine cycles a byte and a loop on top; plain absolute costs eight and nothing on top.»
The result is 600 pairs of instructions one after another, with no loop at all. Today you'd write a loop with a counter, much shorter. Here, though, time matters, not length: on the 6510 moving a byte with indexed addressing costs 9 processor cycles, against 8 with the address spelled out, and the loop costs more on top. The original comment in the source puts it like this: "The scroll copy, unrolled. Indexed addressing costs nine cycles a byte and a loop on top; plain absolute costs eight and nothing on top."
In numeri: 600 caratteri per 8 cicli fanno 4800 cicli. Un fotogramma PAL ne ha 19656, cioè 312 righe video per 63 cicli ciascuna, quindi la copia da sola si prende quasi un quarto del fotogramma. A tutta velocità, con poco più di cinque pixel a fotogramma, capita circa due fotogrammi su tre.
In numbers: 600 characters at 8 cycles make 4800 cycles. A PAL frame has 19656, that is 312 video lines at 63 cycles each, so the copy alone takes almost a quarter of the frame. At full speed, with just over five pixels a frame, it happens about two frames in three.
Una cosa che la copia non fa: non copia i colori. Nel C64 i colori stanno in un'altra memoria, e lì non c'è uno schermo di riserva. Per questo la strada è disegnata con quattro colori soli, e il colore proprio di ogni casella è lo stesso dappertutto: così i colori non vanno spostati, e scorrono gratis. L'unica eccezione è la bandiera a scacchi del traguardo, che ha un pezzetto di codice suo per il bianco e nero.
One thing the copy does not do: it doesn't copy colours. On the C64 colours live in separate memory, and there's no spare screen there. That's why the road is drawn with just four colours, and each cell's own colour is the same everywhere: that way colours don't need moving, and they scroll for free. The only exception is the chequered finish flag, which has its own little piece of code for the black and white.
La pista non esiste, si componeThe track doesn't exist, it's composed
Dopo la copia, in cima allo schermo nascosto manca una riga. Una tappa è lunga 2304 righe, cioè 18432 pixel, e tenerla in memoria già disegnata costerebbe troppo: per una sola pista servivano dieci chilobyte, e le tappe sono otto. Allora la strada viene composta al momento, come facevano i cabinati da sala.
After the copy, a row is missing at the top of the hidden screen. A stage is 2304 rows long, that's 18432 pixels, and keeping it in memory already drawn would cost too much: a single track took ten kilobytes, and there are eight stages. So the road is composed on the spot, the way arcade cabinets did it.
Funziona come un mosaico a tre livelli. La mappa della tappa è una lista di blocchi: ogni blocco è un pezzo di strada di 32 righe, con un numero che dice che forma ha, dritta, stretta, con il guado. Ogni blocco è una griglia di mattonelle larghe due caratteri e alte due. Ogni mattonella dice quali quattro caratteri disegnare. L'ultima riga del commento è la formula per trovare tutto a partire dal numero di riga: block = q>>5 vuol dire «dividi per 32», (q>>1)&15 trova la riga di mattonelle dentro il blocco, e q&1 dice se si sta disegnando la metà alta o quella bassa della mattonella. Di nuovo spostamenti di bit al posto di divisioni, perché dividere il processore non lo sa fare.
It works like a three-level mosaic. The stage map is a list of blocks: each block is a 32-row piece of road, with a number saying what shape it has, straight, narrow, with a ford. Each block is a grid of tiles two characters wide and two high. Each tile says which four characters to draw. The last line of the comment is the formula for finding everything from the row number: block = q>>5 means "divide by 32", (q>>1)&15 finds the tile row inside the block, and q&1 says whether you're drawing the top or bottom half of the tile. Bit shifts in place of divisions again, because the processor can't divide.
Così tutte e otto le tappe costano una tabellina di 128 byte più questa routine. E c'è un vantaggio che con lo spazio non c'entra: la fisica legge la strada dagli stessi caratteri disegnati sullo schermo. Quello che vedi sotto le ruote è esattamente quello su cui stai guidando, perché è la stessa cosa. Il pezzo che chiama la routine, subito dopo la copia:
So all eight stages cost a little 128-byte table plus this routine. And there's an advantage that has nothing to do with space: the physics reads the road from the same characters drawn on the screen. What you see under your wheels is exactly what you're driving on, because it's the same thing. The piece that calls the routine, right after the copy:
screen_row_lo / screen_row_hi sono una tabella con l'indirizzo di ogni riga dello schermo, così non serve moltiplicare per 40. ldx nextfront / adc #4: se lo schermo di destinazione è il secondo, il suo indirizzo è quattro «pagine» più in là, $4400 invece di $4000. jsr decode_row compone la riga e la scrive lì, e dec native_rows_due / bne .incoming ripete se le righe da comporre erano due.
screen_row_lo / screen_row_hi are a table holding the address of each screen row, so there's no need to multiply by 40. ldx nextfront / adc #4: if the destination screen is the second one, its address is four "pages" further on, $4400 instead of $4000. jsr decode_row composes the row and writes it there, and dec native_rows_due / bne .incoming repeats if there were two rows to compose.
Girare pagina al momento giustoTurning the page at the right moment
Nello schermo nascosto adesso c'è il fotogramma nuovo, e c'è il nuovo valore di YSCROLL. Resta la parte più delicata: farli vedere insieme, e nel momento in cui il chip video non sta disegnando. Il VIC-II disegna l'immagine dall'alto in basso, una riga alla volta: se si cambia schermo mentre è a metà, la parte alta viene dal vecchio e quella bassa dal nuovo, e si vede uno strappo. Per evitarlo il C64 può chiedere al chip video di interrompere il programma quando arriva a una certa riga: sono gli interrupt raster.
The hidden screen now holds the new frame, and there's the new YSCROLL value. The most delicate part remains: showing them together, at a moment when the video chip isn't drawing. The VIC-II draws the picture top to bottom, one line at a time: switch screens halfway and the top comes from the old one and the bottom from the new, and you see a tear. To avoid it the C64 can ask the video chip to interrupt the program when it reaches a given line: these are raster interrupts.
un fotogramma PAL steso in orizzontale, da riga 0 a riga 311. Il lavoro comincia appena dopo la consegna, nel bordo in basso, e di norma finisce a metà della parte visibile.a PAL frame laid out horizontally, from line 0 to line 311. Work starts right after delivery, in the bottom border, and normally finishes halfway through the visible area.
Ruote Fumanti usa un'interruzione alla riga 304, sotto la parte visibile, dove il chip sta disegnando il bordo. È lì che si consegna il fotogramma.
Ruote Fumanti uses an interrupt at line 304, below the visible area, where the chip is drawing the border. That's where the frame is delivered.
lda ready / beq .miss: il programma principale alza ready solo quando ha finito tutto il lavoro del fotogramma. Se non l'ha finito non si consegna niente, si tiene il fotogramma precedente e si conta un fotogramma perso. Meglio ripetere un'immagine intera che mostrarne una a metà. lda nextfront / sta front: lo schermo preparato diventa quello da mostrare. I quattro asl e l'ora #8 costruiscono il valore del registro $d018, la cui metà alta dice al chip dove sta lo schermo e quella bassa dove stanno i caratteri: con front a 0 esce $08, schermo a $4000; con front a 1 esce $18, schermo a $4400. lda nextfine / sta fine: il nuovo YSCROLL.
lda ready / beq .miss: the main program raises ready only once it has finished all of the frame's work. If it hasn't, nothing is delivered, the previous frame is kept and one frame is counted as dropped. Better to repeat a whole picture than to show half of one. lda nextfront / sta front: the prepared screen becomes the one to show. The four asl and the ora #8 build the value for the $d018 register, whose upper half tells the chip where the screen is and whose lower half where the characters are: with front at 0 you get $08, screen at $4000; with front at 1 you get $18, screen at $4400. lda nextfine / sta fine: the new YSCROLL.
I valori finiscono davvero nel chip un attimo dopo, in cima al quadro successivo, con un'altra interruzione:
The values actually reach the chip a moment later, at the top of the next frame, with another interrupt:
lda fine / ora #$10 / sta $d011: in un solo registro ci vanno YSCROLL, nei tre bit bassi, e le impostazioni del video. Qui vale la pena essere precisi, perché è un registro che confonde tutti: il bit che ora #$10 accende è il quarto, e vuol dire «schermo acceso»; la modalità a 24 righe è il bit subito sotto, il terzo, e si ottiene lasciandolo a zero, che è proprio quello che fa scrivere $10 invece del solito $18. Il commento originale descrive l'effetto: è la finestra a 24 righe che nasconde la riga in entrata, ed è questo che rende lo scorrimento senza giunture. sta $d018 è il cambio di schermo. Tutto accade nel bordo, prima che il chip cominci a disegnare la strada: chi guarda vede sempre un'immagine completa.
lda fine / ora #$10 / sta $d011: a single register holds YSCROLL, in the three low bits, and the video settings. It's worth being precise here, because this register confuses everyone: the bit that ora #$10 sets is bit four, and it means "display on"; the 24-row mode is the bit just below, bit three, and you get it by leaving it at zero, which is exactly what writing $10 instead of the usual $18 does. The original comment describes the effect: it's the 24-row window that hides the incoming row, and that's what makes the scroll seamless. sta $d018 is the screen switch. It all happens in the border, before the chip starts drawing the road: whoever's watching always sees a complete picture.
Quaranta righe regalateForty free lines
C'è un dettaglio nella consegna che vale la pena raccontare, perché è stato trovato misurando e non ragionando.
There's a detail in the delivery worth telling, because it was found by measuring, not by reasoning.
tick è il via al programma principale: «comincia il prossimo fotogramma». All'inizio il via veniva dato in cima al quadro, alla riga 32. Ma la consegna avviene alla riga 304, e fra la 304 e la 32, passando dal fondo e dal bordo in alto, ci sono quaranta righe in cui il processore restava fermo ad aspettare. Sono righe preziose, perché mentre il chip video disegna la parte visibile ogni tanto «ruba» tempo al processore per leggere i caratteri, mentre nel bordo non ruba niente.
tick is the go signal for the main program: "start the next frame". At first the go was given at the top of the picture, at line 32. But delivery happens at line 304, and between 304 and 32, going round the bottom and the top border, there are forty lines in which the processor sat waiting. They're precious lines, because while the video chip draws the visible area it now and then "steals" time from the processor to read the characters, whereas in the border it steals nothing.
Spostare quelle due istruzioni, lda #1 e sta tick, dalla riga 32 a subito dopo la consegna ha dato al lavoro circa 2500 cicli in più a ogni fotogramma. Misurato su tutte e otto le tappe a ingressi fissi, i fotogrammi persi sono scesi dall'1,93% allo 0,11%. Due istruzioni spostate, e il gioco ha smesso di perdere colpi.
Moving those two instructions, lda #1 and sta tick, from line 32 to right after delivery gave the work about 2500 more cycles every frame. Measured over all eight stages with fixed inputs, dropped frames went from 1.93% to 0.11%. Two instructions moved, and the game stopped stuttering.
morale: nessun ragionamento avrebbe trovato quelle quaranta righe. Le ha trovate un numero, misurato sempre allo stesso modo, prima e dopo.
moral: no amount of reasoning would have found those forty lines. A number found them, measured the same way every time, before and after.
Perché il pannello non ballaWhy the panel doesn't jiggle
YSCROLL sposta tutto lo schermo, non solo la strada. Se punteggio, velocità e tempo fossero scritti con i caratteri, ballerebbero su e giù di sette pixel insieme alla strada. Per questo le scritte del pannello non sono caratteri ma sprite, che YSCROLL non tocca.
YSCROLL moves the whole screen, not just the road. If score, speed and time were written with characters, they'd bob up and down seven pixels along with the road. That's why the panel's lettering isn't characters but sprites, which YSCROLL doesn't touch.
Solo che gli sprite sono otto, e servono anche per le auto: il pannello ne usa due soli. Ogni volta che il chip video arriva a una nuova fascia del pannello, un'altra interruzione sposta quei due sprite più in basso e cambia le cifre che mostrano. Il fondo del pannello invece è fatto di caratteri, ma scelti apposta, pieni o a strisce verticali per le cornici: sono uguali in ogni riga, quindi quando scendono di qualche pixel non se ne accorge nessuno.
Except there are only eight sprites, and the cars need them too: the panel uses just two. Each time the video chip reaches a new band of the panel, another interrupt moves those two sprites further down and changes the digits they show. The panel background, on the other hand, is made of characters, but chosen on purpose, solid or with vertical stripes for the frames: they're identical on every row, so when they drop a few pixels nobody notices.
Un fotogramma di Ruote FumantiOne frame of Ruote Fumanti
- riga 304, bordo in bassoline 304, bottom border se è pronto, si consegna: schermo e YSCROLL nuovi, e subito il via al lavoroif it's ready, deliver: new screen and YSCROLL, and go straight to work
- programma principalemain program fisica, e con la velocità i pixel da scorrere, frazioni compresephysics, and from speed the pixels to scroll, fractions included
- meno di otto pixelunder eight pixels si aggiorna YSCROLL e bastajust update YSCROLL
- otto o piùeight or more copia nello schermo nascosto e riga nuova composta in cimacopy into the hidden screen and compose a new top row
- riga 32, in cimaline 32, at the top il chip riceve schermo e YSCROLL, prima di disegnare la stradathe chip gets screen and YSCROLL, before drawing the road
- durante il disegnowhile drawing le interruzioni del pannello spostano i due sprite delle scrittepanel interrupts move the two lettering sprites
Misurato in VICE con ingressi fissi su 9600 fotogrammi e otto tappe: lo 0,1% di fotogrammi persi, e sei tappe su otto che non ne perdono nemmeno uno.
Measured in VICE with fixed inputs over 9600 frames and eight stages: 0.1% dropped frames, and six stages out of eight that don't drop a single one.