{{htmlmetatags>metatag-robots=()
metatag-title=(Animazioni con i protothread | Multithreading su retrocomputer 6502/6510)
metatag-keywords=(Multithreading,Commodore 64,Commodore VIC20,Atari,Commodore128,MIDRES Library,6502,6510)
metatag-description=(Come implementare il multithreading veloce ed efficiente su computer con risorse limitate.)
metatag-media-og:image=(:mt6502.png)
metatag-og:title=(Animazioni con i protothread | Multithreading su retrocomputer 6502/6510)
metatag-og:description=(Come implementare il multithreading veloce ed efficiente su computer con risorse limitate.)
}}
====== Multithreading sui retrocomputer 6502/6510 ======
===== ANIMAZIONI CON I PROTOTHREAD =====
Il codice su [[https://github.com/spotlessmind1975/midres/blob/master/src/tutorial_protothread2.c|tutorial_protothread2.c]] mostra come realizzare una animazione con diversi attori autonomi, ognuno gestito da un thread separato.
Non commenteremo ogni singola riga ma ci limiteremo a segnalare alcun punti fondamentali, da approfondire. In particolare questo tutorial sfrutta i tiles e il motore di rendering realizzato per il gioco [[:it:elevator|"The Elevator"]],al sorgente del quale si rimanda per approfondimenti e chiarimenti
MR_PT_CTX(walk, unsigned char floor; unsigned char frame; unsigned char pos; unsigned char to; mr_color color;);
Questa dichiarazione permette di riservare ben 5 variabili locali per ogni thread che si occuperà di spostare il personaggio. Queste variabili sono utili per rappresentare alcune informazioni essenziali, quali:
* il piano (''floor'') su cui dovremo disegnare il personaggio;
* il fotogramma (''frame'') del personaggio disegnato per l'ultima volta sullo schermo;
* la posizione (''pos'') dove il personaggio è stato disegnato per l'ultima volta;
* la destinazione (''to'') che il personaggio deve raggiungere;
* il colore (''color'') con cui disegnare il personaggio.
walk_protothread walkThread[FLOOR_COUNT];
Con questa istruzione allochiamo un thread per ogni piano dove camminerà il personaggio. Da notare che, a seguito di questa nuova definizione (''walk_protothread'') l'occupazione per ogni protothread passa da 2 byte a 7 byte per protothread. Quindi lo spazio di memoria occupato, complessivamente, dal multithreading di questo tutorial è di 35 byte.
MR_PT_THREAD_EXT(walk, walk_protothread) {
MR_PTI_BEGIN();
Da qui inizia il codice del protothread.
// ARRIVED?
while (MR_PTI_CTX()->pos < MR_PTI_CTX()->to) {
Qui si controlla la posizione raggiunta dal personaggio, rispetto a quella attesa. Notare come l'accesso alle variabili "locali" avvengano attraverso l'estensione del contesto.
// INCREASE PASSENGER FRAME
++MR_PTI_CTX()->frame;
Qui aggiorniamo il frame di animazione.
// EVEN FRAME?
if ((MR_PTI_CTX()->frame & 1) == 0) {
// INCREASE PASSENGER POSITION
++MR_PTI_CTX()->pos;
}
Spostiamo il personaggio in avanti di una posizione se e soltanto se il frame di animazione è pari. Questo significa che, ogni due fotogrammi, ci si sposta di un passo a destra.
// FRAME MAX REACHED?
if (MR_PTI_CTX()->frame > WALK_FRAME_COUNT) {
// RESET FRAME
MR_PTI_CTX()->frame = 0;
}
Qui garantiamo che l'animazione sia ciclica, dal primo all'ultimo frame.
// (yeld)
MR_PTI_YIELD();
Qui lasciamo il tempo allo scheduler di far andar avanti altri thread.
// DRAW PASSENGER
draw_passenger_walking(MR_PTI_CTX()->floor, MR_PTI_CTX()->pos, MR_PTI_CTX()->frame, MR_PTI_CTX()->color);
Disegniamo, infine, il personaggio.
}
MR_PTI_END();
Qui termina il protothread.
Nella funzione principale del tutorial, oltre alla parte prettamente grafica, sono inizializzate tutte le variabili di ogni protothread. In particolare, per ogni personaggio:
for (i = 0; i < FLOOR_COUNT; ++i) {
walkThread[i].floor = i;
Viene impostato il piano corrispondente alla posizione ordinale.
walkThread[i].pos = rand() & 0xf;
Viene impostata una posizione di partenza casuale, da 0 a 15.
walkThread[i].frame = 0;
Viene impostato il fotogramma di partenza.
walkThread[i].to = MR_SCREEN_WIDTH - 2;
Viene impostata la posizione di arrivo, che corrisponde al bordo dello schermo.
switch (i) {
case 0:
walkThread[i].color = MR_COLOR_RED;
break;
case 1:
walkThread[i].color = MR_COLOR_GREEN;
break;
case 2:
walkThread[i].color = MR_COLOR_BLUE;
break;
case 3:
walkThread[i].color = MR_COLOR_BLACK;
break;
case 4:
walkThread[i].color = MR_COLOR_PURPLE;
break;
}
}
Viene impostato il colore da utilizzare.
A questo punto, entra in scena il loop cooperativo:
while (1) {
mr_start_frame();
Questa funzione permette di impostare una posizione di sincronizzazione temporale con il disegno.
for (i = 0; i < FLOOR_COUNT; ++i) {
walk(&walkThread[i]);
}
Questo loop corrisponde allo scheduling dei protothreads.
mr_end_frame(4);
Questa funzione permette di impostare la posizione finale di sincronizzazione, garantendo che l'intero disegno sia eseguito entro 4 jiffies (4/60 di secondo circa).
===== COME COMPILARLO =====
E' possibile compilare il tutorial con questa linea di comando:
make tutorial=PROTOTHREAD2 target=... all
Le piattaforme supportate sono:
* Commodore VIC-20 espanso (''vic2024'');
* Commodore 64 (''c64'');
* Commodore 128 (''c128'');
* Commodore PLUS/4 (''plus4'');
* Atari (8 bit) (''atari'').
===== RISULTATO =====
{{ youtube>ipUotWD4WYo?medium }}
Move to [[:it:mt6502:midres|COME SONO IMPLEMENTATI NELLA MIDRES LIBRARY?]].