GERLA.CC

tutorial · gerla.cctutorial · gerla.cc

Il ciclo di vita di un giocoThe life cycle of a game

Un videogioco, sotto tutto il resto, è un anello che gira: parte, aspetta la pressione del tasto, dà tre vite, le porta via una a una, dice game over, salva il record e ricomincia. Vediamo com'è fatto quell'anello, fase per fase, sui due ambienti in gioco: Commodore 64 (6502) e Mega Drive (68000). Tutto in assembler, con il codice spiegato riga per riga dai commenti. A video game, underneath everything else, is a loop that spins: it boots, waits for the button, gives three lives, takes them away one by one, says game over, saves the record and starts again. Let's see how that loop is built, phase by phase, on the two environments at play: Commodore 64 (6502) and Mega Drive (68000). All in assembler, with the code explained line by line by the comments.

Nota: nel gioco vero, sul Mega Drive scrivo la logica in C. Qui però mostro l'ossatura in 68000 assembly, perché un tutorial sul loop rende di più a nudo: è esattamente quello che il compilatore produce, e che la macchina esegue davvero.

Note: in the real game, on the Mega Drive I write the logic in C. Here, though, I show the skeleton in 68000 assembly, because a tutorial on the loop reads better bare: it's exactly what the compiler produces, and what the machine actually runs.

AVVIOBOOT
accendi la macchina, prepara tuttopower on, set everything up
SCHERMATA HOMETITLE SCREEN
aspetta che premi FUOCO / STARTwaits for FIRE / START
premi il tastopress the button
PARTITAPLAY  ·  vite = 3, un frame alla voltalives = 3, one frame at a time
muoriyou die
UNA VITA IN MENOONE LIFE LESS
restano vite? sì → torna a PARTITA  ·  no → game over any lives left? yes → back to PLAY  ·  no → game over
GAME OVERGAME OVER
SALVA IL RECORDSAVE THE RECORD
e si ricominciaand round again
↩ SCHERMATA HOME↩ TITLE SCREEN
Commodore 64 · CPU 6502 · 1 MHz · 64 KBCommodore 64 · 6502 CPU · 1 MHz · 64 KB
Mega Drive · CPU 68000 · 7,6 MHz · ROM + SRAMMega Drive · 68000 CPU · 7.6 MHz · ROM + SRAM
01 · avvioboot

Accendere la macchinaPowering on

All'accensione la macchina esegue il primo indirizzo che le diciamo. Prima di ogni cosa mettiamo a tacere le interruzioni, sistemiamo lo schermo e i registri video, e azzeriamo le variabili: quante vite, che punteggio. Poi si salta alla schermata home. È il gesto d'apertura, si fa una volta sola.

At power-on the machine runs the first address we point it to. Before anything, we silence the interrupts, set up the screen and video registers, and zero the variables: how many lives, what score. Then we jump to the title screen. It's the opening move, done exactly once.

C64 · 6502

start: sei ; niente interruzioni durante il setup lda #$00 sta $d020 ; bordo nero sta $d021 ; sfondo nero jsr init_vic ; sprite, charset, schermo lda #3 sta lives ; tre vite lda #0 sta score ; punteggio a zero (16 bit) sta score+1 jmp title ; vai alla schermata home

Mega Drive · 68000

start: move.w #$2700,sr ; maschera le interruzioni lea $01000000,sp ; stack in cima alla RAM jsr init_vdp ; piani, sprite, palette move.w #3,lives ; tre vite clr.l score ; punteggio a zero (32 bit) bra title ; vai alla schermata home

Stessa idea, due dialetti: spegni il rumore, prepara il video, imposta vite e punteggio, vai al titolo.

Same idea, two dialects: silence the noise, prepare the video, set lives and score, go to the title.

02 · schermata hometitle screen

Aspettare che premi il tastoWaiting for the button

La schermata home fa una cosa sola: aspetta. Disegna «PREMI FUOCO» e poi gira in un piccolo anello leggendo il joystick a ogni frame, finché non premi. Il trucco è la pazienza: un giro dell'anello per ogni quadro video, così il controllo resta reattivo senza mangiarsi la macchina. Appena arriva il tasto, si salta alla partita.

The title screen does one thing: it waits. It draws "PRESS FIRE" and then spins in a small loop reading the joystick every frame, until you press. The trick is patience: one turn of the loop per video frame, so the control stays responsive without hogging the machine. The moment the button arrives, it jumps to the game.

C64 · 6502

title: jsr draw_title ; scrivi "PREMI FUOCO" .wait: jsr wait_vblank ; un giro = un frame lda $dc00 ; porta joystick 2 and #%00010000 ; isola il bit del fuoco bne .wait ; ancora su (non premuto)? aspetta jmp new_game ; premuto: si parte ; nota: sul C64 il bit del fuoco ; e' 0 quando premi (logica invertita)

Mega Drive · 68000

title: jsr draw_title ; scrivi "PRESS START" .wait: jsr wait_vblank ; un giro = un frame jsr read_pad ; tasti premuti in d0 btst #7,d0 ; il bit di START beq .wait ; non premuto? aspetta bra new_game ; premuto: si parte

Un ciclo che non fa nulla se non ascoltare. Il gioco vero comincia solo quando decidi tu.

A loop that does nothing but listen. The real game begins only when you decide.

03 · la partitathe game loop

Il cuore: un frame alla voltaThe heart: one frame at a time

Qui vive il gioco. Ogni giro dell'anello è un fotogramma: aspetti l'inizio del quadro (il vblank, il momento in cui il pennello video torna in cima), leggi i comandi, muovi il personaggio, muovi i nemici, controlli le collisioni. Se sei ancora vivo, riparte il giro. Sessanta volte al secondo, sempre uguale. Tutta l'azione che vedi è questo anello che gira in fretta.

This is where the game lives. Every turn of the loop is a frame: you wait for the start of the frame (the vblank, the moment the video beam returns to the top), read the inputs, move the player, move the enemies, check collisions. If you're still alive, the loop restarts. Sixty times a second, always the same. All the action you see is this loop spinning fast.

C64 · 6502

game: jsr wait_vblank ; aspetta l'inizio del frame jsr read_input ; leggi il joystick jsr move_player ; fisica: salti, gravita' jsr move_enemies ; i nemici avanzano jsr check_hits ; collisioni lda player_dead bne player_died ; morto? esci dall'anello jmp game ; vivo: prossimo frame

Mega Drive · 68000

game: jsr wait_vblank ; aspetta l'inizio del frame jsr read_input ; leggi il joypad jsr move_player ; fisica: salti, gravita' jsr move_enemies ; i nemici avanzano jsr check_hits ; collisioni tst.w player_dead bne player_died ; morto? esci dall'anello bra game ; vivo: prossimo frame

Il wait_vblank è il metronomo: senza, il gioco andrebbe a velocità diverse su macchine diverse. Con, tutto scorre a tempo.

The wait_vblank is the metronome: without it, the game would run at different speeds on different machines. With it, everything flows in time.

04 · fallimentofailure

Morire, e contare le viteDying, and counting lives

Quando muori, l'anello della partita si spezza e si arriva qui. La regola è semplice: togli una vita. Se ne restano, rimetti il personaggio in pista e torni a giocare. Se sei a zero, la strada porta al game over. Una sottrazione decide tutto.

When you die, the game loop breaks and you land here. The rule is simple: take away one life. If any are left, put the player back on the field and return to play. If you're at zero, the road leads to game over. One subtraction decides everything.

C64 · 6502

player_died: dec lives ; una vita in meno bne respawn ; ne restano? torna in pista jmp game_over ; zero vite: game over respawn: jsr reset_player ; rimetti il personaggio lda #0 sta player_dead jmp game ; e si riprende

Mega Drive · 68000

player_died: subq.w #1,lives ; una vita in meno bne respawn ; ne restano? torna in pista bra game_over ; zero vite: game over respawn: jsr reset_player ; rimetti il personaggio clr.w player_dead bra game ; e si riprende

Il dec/subq abbassa il contatore e, di colpo, imposta anche il segnale «è zero?». Il salto subito dopo legge quel segnale: niente confronti in più.

The dec/subq lowers the counter and, at once, also sets the "is it zero?" flag. The branch right after reads that flag: no extra comparisons.

05 · game over + recordgame over + record

Salvare il punteggio più altoSaving the high score

Finita la partita, si confronta il punteggio con il record. Se l'hai battuto, il record si aggiorna. Ed è qui che i due mondi si separano davvero. Sul Mega Drive la cartuccia può avere una SRAM con batteria: scrivi lì il record e resta anche a console spenta, per sempre. Sul C64 non c'è batteria: il record vive in RAM per la sessione, e se lo vuoi eterno lo salvi su disco. Stessa idea, due modi di ricordare.

When the game ends, the score is compared with the record. If you beat it, the record updates. And this is where the two worlds truly part ways. On the Mega Drive the cartridge can have a battery-backed SRAM: write the record there and it survives even with the console off, forever. On the C64 there's no battery: the record lives in RAM for the session, and if you want it eternal you save it to disk. Same idea, two ways to remember.

C64 · 6502

game_over: jsr draw_gameover ; --- record migliore? (16 bit) --- lda score+1 ; byte alto del punteggio cmp hiscore+1 bcc no_record ; sotto il record: niente bne set_record ; sopra: nuovo record lda score ; alto uguale: guarda il basso cmp hiscore bcc no_record set_record: lda score sta hiscore ; aggiorna il record... lda score+1 sta hiscore+1 ; jsr save_to_disk ; (se lo vuoi eterno) no_record: jmp wait_and_return

Mega Drive · 68000

game_over: jsr draw_gameover ; --- record migliore? (32 bit) --- move.l score,d0 cmp.l hiscore,d0 bls no_record ; sotto o uguale: niente move.l d0,hiscore ; aggiorna il record in RAM ; --- e nella SRAM con batteria: ; sopravvive allo spegnimento --- move.b #1,sram_enable move.l d0,sram_hiscore move.b #0,sram_enable no_record: bra wait_and_return

Il confronto a 16/32 bit si fa un pezzo alla volta, dal byte più pesante: se quello decide, il resto non serve nemmeno guardarlo.

The 16/32-bit comparison is done a piece at a time, from the heaviest byte: if that one decides, the rest isn't even worth looking at.

06 · ritornoreturn

E si ricominciaAnd round again

Ultimo anello: si aspetta un attimo, o un tasto, e si torna alla schermata home. Il cerchio si chiude e la macchina è di nuovo pronta ad aspettare il prossimo giocatore. È lo stesso jmp title del primo giorno: da qui, tutto ricomincia.

Last loop: wait a moment, or a button, and go back to the title screen. The circle closes and the machine is ready again to wait for the next player. It's the same jmp title from day one: from here, everything starts over.

C64 · 6502

wait_and_return: jsr wait_fire ; un attimo, o un tasto lda #3 sta lives ; nuove tre vite lda #0 sta score sta score+1 ; punteggio da capo jmp title ; torna alla home

Mega Drive · 68000

wait_and_return: jsr wait_fire ; un attimo, o un tasto move.w #3,lives ; nuove tre vite clr.l score ; punteggio da capo bra title ; torna alla home

Nota che il record NON si azzera: vite e punteggio ripartono, ma il primato resta. È la memoria del gioco.

Note that the record does NOT reset: lives and score start over, but the best stays. It's the game's memory.

in sintesiin short

Un anello dentro un anelloA loop inside a loop

Tutto qui: un anello grande (home → partita → game over → home) e dentro un anello piccolo che gira sessanta volte al secondo (la partita, frame per frame). Cambiano il dialetto e i dettagli della macchina, ma la forma è la stessa dal 1982 a oggi, e anche nel gioco HTML5 da cui sono partito. Impari questo, e hai imparato l'ossatura di qualunque gioco.

That's all: a big loop (title → play → game over → title) and inside it a small loop spinning sixty times a second (the game, frame by frame). The dialect and the machine details change, but the shape is the same from 1982 to today, and in the HTML5 game I started from too. Learn this, and you've learned the skeleton of any game.

← Tutti i tutorial← All tutorials Il diarioThe journal