jeudi 9 février 2012

Tutorial : Les sprites sur Coleco

La mémoire Colecovision :

Sur Coleco il y à en gros 3 zones de mémoire :
La RAM : 1 ko au total, mais beaucoup moins (600 octets ?) avec la librairie de Daniel Bienvenue 
La ROM : 32 ko, ou on va stocker les graphismes, la musiques, les données que l'on a besoin mais qui n'évoluent pas.
La VRAM : la mémoire vidéo qui est composée ainsi :


#define chrtab  0x1800 /* écran en linéaire */
#define chrgen  0x0000 /* table des caractères */
#define coltab  0x2000 /* couleur des caractères */
#define sprtab  0x3800 /* sprite_pattern_table */
#define sprgen  0x1b00 /* sprite_attribute_table */
#define buffer  0x1c00 /* écran 2 en linéaire */

Ce qui nous intéresse pour les sprites c'est : 

sprtab : zone de mémoire qui va contenir les dessins (composé de patterns) des sprites
sprgen : zone de mémoire qui va contenir la définition des sprites (x,y,pattern,colour)

Dessiner des sprites :

On utilise l'outil de Daniel Bienvenu ICVGM303.exe qui permet de dessiner des dessins de sprites de 16x16 pixels. Un dessin de sprite de 16x16 est composé en fait de 4 patterns.
Un pattern est une zone de 8x8 pixels.

Donc, le 1er dessin commence au pattern 0 et est composé des patterns 0,1,2,3
Le second dessin commence au pattern 4 et est composé des patterns 4,5,6,7
et ainsi de suite.


Injecter les sprites en mémoire :

Avec l'outil de Daniel, il faut générer un fichier C, compressé en RLE (case à cocher). Ce qui nous intéresse c'est la zone :

byte SPATTERNRLE[] = {
  0x88, 0x00, 0x06, 0x01,...};

C'est la définition des patterns de sprites. IMPORTANT : injecté dans notre programme on doit ABSOLUMENT ajouter un const.

const byte SPATTERNRLE[] = {
  0x88, 0x00, 0x06, 0x01,...};

Sinon à terme il y auras des plantages, car les patterns seront n'importe ou en mémoire, et pas en ROM.

Maintenant, il faut injecter ce tableau en VRAM avec la commande :

rle2vram(SPATTERNRLE,sprtab);

NMI :

Dans le programme en C, il faut absolument créer une fonction

void nmi()
{
}

Mème si elle est vide, cette fonction va s’exécuter 50 fois par seconde sur une Coleco PAL et 60 fois par seconde sur une Coleco NTSC. On va voir ensuite à quoi ça va nous servir.


Manipuler les sprites en RAM :

Dans la librairie de Daniel les sprites ont déjà leur objet définit à savoir :

typedef struct
{
        byte y;
        byte x;
        byte pattern;
        byte colour;
} sprite_t;

sprite_t sprites[32];

il n’y à donc pas besoin de redéclarer le tableau !! Par contre il est conseillé de rendre invisible les sprites.

Void initSprites()
{
         byte i ;
         for (i=0 ;i<32 ;i++) sprites[i].y = 204 ;
}

En fait, au dessus d’un y > 192, le sprite est invisible. Attention : Eviter y=203, en effet cette valeur signale à la Coleco qu'il n'y à plus de sprites à traiter après celui-ci.

Ensuite, on définit comme on veux les sprites :

sprites[0].p = 8 ; // Le sprite 0 aura le dessin n°2 (Dessin 0 = 0 ;Dessin 1 = 4 ; Dessin 2 = 8)
sprites[0].colour = 3 ; // Avec un code couleur 3
sprites[0].x = 0 ;
sprites[0].y = 100 ;

Puis on le fais bouger

while(1)
{
         sprites[0].x ++ ;
}

Problème, avec ça on ne vois rien bouger … NORMAL !!

Transférer les informations de la RAM vers la VRAM :

Pour afficher les sprites à l’écran, il va falloir transférer le tableau sprites[] vers la VRAM. A savoir la commande :

put_vram (0x1b00,sprites,128) ; // copie le tableau sprites vers la sprgen 4 attributs (x,y,colour,p) * 32 sprites = 128 octets

Sauf, qu’il faut faire cela, à chaque changement dans le tableau en RAM . Et donc pour faire simple, il suffit de faire cela, 50 ou 60 fois pas seconde.
Bref, on claque ça dans la NMI et on est tranquille !!


void nmi() 
{
put_vram (0x1b00,sprites,128) ;
}

Le flickering :

La Coleco à une sale contrainte c’est qu’elle ne peut pas afficher plus de 4 sprites en simultané sur une ligne horizontale.
Si on veux afficher les sprites suivants sur la même ligne :

0 1 2 3 4 5

seul les sprites 0 1 2 3 seront affiché, en fait, les sprites avec les n° les plus petits sont prioritaires !

En gros si tu veux faire afficher plus de 4 sprites par ligne, il va falloir changer la priorité des sprites. J’utilise une technique, qui permet d’afficher au moins 8 sprites par lignes
  
D'abord une routine qui ramène le n° d'un sprite libre (y==204), on recherche à chaque fois à partir d'une position de début différente. Ramène -1 si il n'y à plus de sprites de libre.

#define INACTIF 204
byte odd ; // variable globale a mettre en dehors du main
byte currentFlicker;
sprite_t bsprites[32]; // seconde Zone de sprites en RAM

char getFreeSprite()
{
    byte i;
    odd+=8;
    if (odd>=32) odd = 0;

    for (i=odd;i<32;i++)
       if (sprites[i].y==INACTIF)
          return i; 

    for (i=0;i<odd;i++)
       if (sprites[i].y==INACTIF)
         return i;

   return -1;
}

Dans la Nmi on va copier en VRAM, une fois les sprites dans l'ordre
0......31


Et une fois

16,17....31 0,1,2,...15

Ca suffit dans 99% des cas pour afficher correctement 8 sprites par lignes. Sauf si les 8 sprites ont pour numéro 1,2,3,4,5,6,7,8, c'est pour cela que le getFreeSprite() choisit une position de début différente à chaque tour pour trouver un sprite de libre. Ca ne règle pas tout les cas, mais cela m'a toujours suffit.
void nmi()
{
         // Si currentFlicker = 2 alors on copie les sprites de 0 à 31, priorité normale
         if (currentFlicker==0) {put_vram (0x1b00,sprites,128); currentFlicker=1;}
         else  // Sinon les sprites 16 à 31 sont copié à partir de 0 et le reste à partir de 16, on intervertit les priorités.
         {
                   memcpyb(bsprites,sprites+16,64);
                   memcpyb(bsprites+16,sprites,64);
                   put_vram (0x1b00,bsprites,128);           
                     currentFlicker = 0;
         }
}


Voilà, j'essayerais de faire un autre tuto pour déterminer quand écrire dans la VRAM sans risque, chose plutôt délicate à appréhender sur Coleco.
A bientôt !

1 commentaire: