Indice

Multithreading sui retrocomputer 6502/6510

ANIMAZIONI CON I PROTOTHREAD

Il codice su 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 "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:

  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:

RISULTATO

Move to COME SONO IMPLEMENTATI NELLA MIDRES LIBRARY?.