GERLA.CC

tutorial · Mega Drivetutorial · Mega Drive

Assembler e C, perché tutti e due?Assembler and C, why both?

Il gioco per Mega Drive è scritto un po' in assembler e un po' in C. Perché due linguaggi per un gioco solo? Perché servono a cose diverse, e usati insieme fanno un lavoro migliore. Vediamo la differenza con qualche esempio di codice: per seguirlo bastano le figure e le note in verde. The Mega Drive game is written partly in assembler and partly in C. Why two languages for one game? Because they're good at different things, and used together they do a better job. Let's see the difference with a few code examples: the pictures and the green captions are enough to follow along.

01 · la CPUthe CPU

La CPU capisce un linguaggio soloThe CPU understands only one language

Dentro il Mega Drive c'è una CPU, il Motorola 68000: è il processore che esegue il gioco. Una CPU capisce soltanto il suo linguaggio macchina, fatto di istruzioni elementari: sposta un dato, sommalo, confrontalo, salta a un altro punto. L'assembler è il modo leggibile di scrivere quelle istruzioni, una per una. Per esempio, mettere un colore nella tavolozza (la palette) si fa così.

Inside the Mega Drive there's a CPU, the Motorola 68000: it's the processor that runs the game. A CPU understands only its machine language, made of elementary instructions: move a value, add it, compare it, jump somewhere else. Assembler is the readable way to write those instructions, one by one. For example, putting a color into the palette looks like this.

assembler · linguaggio della CPUassembler · the CPU's language

; scrivi il colore bianco nella palette move.w #$0EEE, d0 ; d0 = bianco move.w d0, (a1) ; scrivilo nel colore n.0

 

Due istruzioni per un solo colore. Preciso, ma scrivere un gioco intero a questo livello sarebbe lentissimo e facile da sbagliare. Serve uno strato più comodo.

Two instructions for a single color. Precise, but writing a whole game at this level would be slow and error-prone. A more convenient layer is needed.

02 · il compilatorethe compiler

Il C e il compilatoreC and the compiler

Ecco lo strato comodo: il C. Il C è un linguaggio più vicino al nostro modo di ragionare, dove le cose si dicono in modo corto e chiaro. La CPU però il C non lo esegue: serve un compilatore, un programma che traduce il C nelle tante istruzioni macchina che la CPU capisce. Il percorso è questo:

Here's the convenient layer: C. C is a language closer to how we think, where things are said short and clear. But the CPU doesn't run C: it needs a compiler that turns C into machine instructions. The path is this:

codice CC codecome si scrivehow it's written
compilatorecompilertraducetranslates
codice macchinamachine codele istruzionithe instructions
CPU 6800068000 CPUesegueruns it

A sinistra il C come si scrive, a destra il codice macchina che il compilatore produce.

On the left the C as it's written, on the right the machine code the compiler produces.

C · come si scriveC · how it's written

/* riempi di bianco tutti e 16 i colori */ for (int i = 0; i < 16; i++) { palette[i] = 0x0EEE; }

assembler · cosa genera il compilatoreassembler · what the compiler generates

moveq #0, d0 ; i = 0 .loop: move.w #$0EEE, (a0) ; palette[i] = bianco addq.w #2, a0 ; punta al colore dopo addq.w #1, d0 ; i = i + 1 cmp.w #16, d0 ; i < 16 ? blt .loop ; si': ripeti

Tre righe di C diventano sei istruzioni macchina, e le scrive il compilatore, senza sbagliare. Per questo la maggior parte del gioco è in C: più corto, più chiaro, più veloce da correggere.

Three lines of C become six machine instructions, and the compiler writes them, without mistakes. That's why most of the game is in C: shorter, clearer, faster to fix.

03 · la domandathe question

Allora perché non tutto in C?So why not everything in C?

Se il compilatore è così bravo, perché non scrivere tutto in C ed eliminare l'assembler? Perché ci sono tre punti in cui il compilatore non basta e l'assembler va scritto a mano. Vediamoli.

If the compiler is so good, why not write everything in C and drop the assembler? Because there are three points where the compiler isn't enough and assembler must be written by hand. Let's look at them.

03a · l'avvioboot

L'avvio, prima che il C possa girareBoot, before C can run

All'accensione la CPU parte da uno stato grezzo: la memoria non è preparata e, soprattutto, non esiste ancora lo stack, l'area di memoria che il C usa per le chiamate di funzione e le variabili locali. Senza stack, il C non può girare. Quindi il primissimo codice, quello che imposta lo stack e poi passa il controllo al programma, è per forza in assembler. Per convenzione questo file si chiama crt0.

At power-on the CPU starts in a raw state: memory isn't set up and, above all, there's no stack yet, the memory area C uses for function calls and local variables. Without a stack, C can't run. So the first bit of code, in assembler, prepares the ground and then hands over to C. By convention this file is called crt0. The boot sequence:

ACCENSIONEPOWER ON
la CPU legge la tabella dei vettorithe CPU reads the vector table
in cima alla ROMtop of the ROM
salta a _startjumps to _start
assembler · crt0
imposta lo stack, spegne le interruzionisets up the stack, disables interrupts
chiama main()calls main()
C · main()
init e game loop: il gioco parteinit and game loop: the game starts

crt0.s · assemblerassembler

_start: move.w #$2700, sr ; disabilita le interruzioni lea stack_top, sp ; imposta lo stack pointer jsr main ; passa il controllo al C .stop: bra .stop ; se main torna, resta qui

main.c · CC

int main(void) { /* da qui in poi lavoro comodo, in C: lo stack e' pronto grazie al crt0 */ init_game(); game_loop(); return 0; }

L'assembler prepara il terreno; poi entra il C. Senza quelle poche righe iniziali, il C non potrebbe nemmeno partire.

Assembler sets the stage; then C takes over. Without those few opening lines, C couldn't even start.

03b · i vettorithe vectors

La tabella dei vettoriThe vector table

Appena accesa, la CPU legge in cima alla ROM una tabella dei vettori: una lista di indirizzi. Il primo dice dov'è lo stack, il secondo da quale indirizzo iniziare, altri dicono quale routine chiamare quando arriva un'interruzione o un errore. Deve stare in una posizione e in un ordine precisi, decisi dall'hardware: si scrive a mano in assembler, perché il compilatore non saprebbe collocarla esattamente lì.

The moment it powers on, the CPU reads a vector table at the top of the ROM: a list of addresses in a fixed order set by the hardware. Here's how the ROM is laid out, and which parts are assembler and which are C.

↑ la CPU parte dall'indirizzo 0, in cima↑ the CPU starts at address 0, at the top
tabella dei vettorivector tableasm
crt0 · avviobootasm
codice del giocogame code (C → compilatocompiled)C
dati: grafica, livelli, musicadata: graphics, levels, music

assembler · la tabella dei vettoriassembler · the vector table

dc.l stack_top ; 0) indirizzo dello stack dc.l _start ; 1) da dove iniziare dc.l err, err ; errori di bus / indirizzo dc.l err, err ; istruzione illegale, ... ; (e cosi' via, in ordine fisso)

 

dc.l significa «qui va un valore a 32 bit». Ogni riga è una voce della tabella. L'hardware la legge sempre in questo ordine: spostare le voci manda in crash l'avvio.

dc.l means "a 32-bit value goes here". Each line is an entry in the table. The hardware always reads it in this order: move the entries and boot crashes.

03c · velocitàspeed

Quando conta la velocitàWhen speed matters

Il compilatore genera codice buono, ma in certi punti serve la massima velocità: per esempio copiare molti dati nella VRAM (la memoria video) entro il tempo di un frame, altrimenti si vede uno scatto. In questi punti critici l'assembler scritto a mano, scegliendo le istruzioni migliori, batte il compilatore. Sono poche righe, ma decisive per la fluidità.

The compiler generates good code, but in some spots maximum speed is needed: for example copying a lot of data into VRAM (video memory) within one frame, or a stutter shows. In these critical spots hand-written assembler, picking the best instructions, beats the compiler. Just a few lines, but decisive for smoothness.

assembler · scritto a mano, per la velocitàassembler · hand-written, for speed

; copia veloce verso la VRAM: ; 4 byte per istruzione, senza sprechi move.l (a0)+, (a1) ; blocco 1 move.l (a0)+, (a1) ; blocco 2 move.l (a0)+, (a1) ; blocco 3 move.l (a0)+, (a1) ; blocco 4

 

Qui il controllo dev'essere totale: le istruzioni sono scelte a mano per essere le più rapide. È l'unico caso in cui l'assembler batte il compilatore, sfruttando un accorgimento che il compilatore non applica.

Here control must be total: the instructions are hand-picked to be the fastest. It's the one case where assembler beats the compiler, using a trick the compiler doesn't apply.

04 · la logicathe logic

Tutto il resto: la logica, in CEverything else: the logic, in C

Tolti quei tre punti, tutta la logica di gioco (movimento del personaggio, salti, nemici, collisioni, punteggio) si scrive in C. È la parte che cambia più spesso, e in C è più leggibile e più veloce da modificare.

Apart from those three points, all the game logic (character movement, jumps, enemies, collisions, score) is written in C. It's the part that changes most often, and in C it's more readable and quicker to change.

game.c · il salto del personaggiothe character's jump

void aggiorna_giocatore(void) { if (premuto(FUOCO)) player.vy = -FORZA_SALTO; /* salta su */ player.vy += GRAVITA; /* accelera verso il basso */ player.y += player.vy; /* aggiorna la posizione */ if (player.y > PAVIMENTO) player.y = PAVIMENTO; /* fermati al suolo */ }

 

Si legge quasi come pseudocodice: se si preme fuoco, salta; la gravità accelera verso il basso; aggiorna la posizione; fermati al pavimento. In assembler puro sarebbe il triplo delle righe e più difficile da modificare.

It reads almost like pseudocode: if fire is pressed, jump; gravity accelerates downward; update position; stop at the floor. In pure assembler it would be three times the lines and harder to change.

in sintesiin short

Due strumenti, un lavoroTwo tools, one job

assembler · il basso livelloassembler · the low level

- l'avvio e lo stack (crt0) - la tabella dei vettori - le routine critiche per la velocita' (es. VRAM)

C · la logica di giocoC · the game logic

- movimento del personaggio - nemici, salti, collisioni - vite, punteggio, game over - tutto cio' che cambia spesso

Assembler e C non sono in competizione: si dividono i compiti. L'assembler gestisce l'avvio e i punti a basso livello; il C esprime la logica in modo chiaro. Insieme sono più efficienti di ciascuno da solo. In più, lo stesso codice C si può compilare anche sul computer di sviluppo: così si collauda prima di metterlo nella ROM.

Assembler and C aren't in competition: they split the work. Assembler handles boot and the low-level spots; C expresses the logic clearly. Together they're more efficient than either alone. On top of that, the same C code can also be compiled on the development machine: that way it's tested before going into the ROM.

← Tutti i tutorial← All tutorials I muri superati sul Mega DriveMega Drive walls overcome