{{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?]].