CODINGSDCC TUT'S PAR STEPHBB75 ★ Dessiner en mode 1 ★

Sdcc - 04 - Dessiner en Mode 1Coding Sdcc Tut's Par Stephbb75

Moi j'aime bien dessiner en mode 1 sur CPC, soit le nombre de couleurs est limité mais le rendu est un peut mieux que le mode 0, voici donc des routines pour faire des trais en mode 1 (une partie du code est repris du site cpcmania).
A la fin, je vous fournie aussi les fonctions pour dessiner en mode 0 et en mode 2.

Je ne vais pas vous expliquer comment est architecturé le CPC pour la mémoire vidéo et comment cela fonctionne, pour cela y'a des explication par exemple sur cpcrulez ici ou encore la et la puis sur le met.

Nous avons besoin de quoi alors, en plus de savoir allumer un pixel de l'écran, ce qui est la base, on vas devoir tracer des lignes, j'ai utilisé l'algorithme de Bresenham pour le tracé de ligne (qui est Bresenham ? cherche sur le net...).
J'ai récupéré sur le net plusieurs implémentation des line de Bresenham, je les est testé et j'ai opté pour une fonction que vous trouverez ici.

Comment allumé un pixel en mode 1 :
L'écran commence à l'adresse mémoire 0xC000 et a une longueur de 16Ko (16384 octets exactement) donc il fini en 0xFFFF. Sachant comment fonctionne le CPC (vous ne savez pas ?) chaque pixel n'est pas codé sur un octet, en fait sur 1 octet on trouve 4 pixels de codé en mode 1. Bien sur cela aurais été trop simple que sur cette octet on trouver les 4 pixels les un derrière les autre, non, ils sont imbrique ...

Voici la fonction pour afficher un pixel en mode 1 en fonction de sa couleur :

  • ATTENTION, la fonction ne teste pas les limites, donc si vous tenter de d'afficher un point en dehors des 320 x 200 vous risquez de taper n' importe où dans la mémoire, et bien sur de modifier une valeur qui n'a rien à voire, et au final de faire planter l'ordi, la on sait ce que l'on fait, sinon il faut ajouter un test pour les limite en X et Y !
  • L'origine est inversée vis-à-vis du BASIC, le point 0,0 est en haut à gauche !
  • Aucun test du mode n'est fait, si vous passé en mode 0 et utilisé cette fonction, cela ne donne pas le résultat voulu !!

void PutPixelMode1(int nX, unsigned char nY, unsigned char nColor)
{
int nPixel = nX % 4;
unsigned char *pAddress = (unsigned char *)((unsigned int)(0xC000 + ((nY / 8) * 80) + ((nY % 8) * 2048) + (nX / 4)));

if(nPixel == 0)
{
*pAddress &= 119;

if(nColor & 1)
*pAddress |= 128;
if(nColor & 2)
*pAddress |= 8;
}
else if(nPixel == 1)
{
*pAddress &= 187;

if(nColor & 1)
*pAddress |= 64;
if(nColor & 2)
*pAddress |= 4;
}
else if(nPixel == 2)
{
*pAddress &= 221;

if(nColor & 1)
*pAddress |= 32;
if(nColor & 2)
*pAddress |= 2;
}
else //nPixel == 3
{
*pAddress &= 238;

if(nColor & 1)
*pAddress |= 16;
if(nColor & 2)
*pAddress |= 1;
}
}

Quelques explication :

Les paramètres de la fonction :
void PutPixelMode1(int nX, unsigned char nY, unsigned char nColor)
"int nX" : la coordonnée x, de 0 à 320
"unsigned char nY" : la coordonnée y, de 0 à 200
"unsigned char nColor" : La couleur, de 0 à 4

cette ligne "unsigned char *pAddress = (unsigned char *)((unsigned int)(0xC000 + ((nY / 8) * 80) + ((nY % 8) * 2048) + (nX / 4)));" permet de calculer l'adresse de l'octet ou se trouve le pixel.
la ligne "nPixel = nX % 4;" permet de savoir ou se trouve le pixel dans l'octet.
les if suivant servent en fonction du pixel dans l'octet de mettre un masque pour ne changer que ce pixel.

Tracer une ligne avec l'algorithme de Bresenham :
La je ne vais pas m'étendre sur cela, je vous fournis une fonction, j'en est testé plusieurs, celle ci est la plus "rapide" (pour le moment)

void lineBresenham(int x1,int y1,int x2,int y2, unsigned char nColor) {
int cx, cy,
ix, iy,
dx, dy,
ddx= x2-x1, ddy= y2-y1;

if (!ddx) { //vertical line special case
if (ddy > 0) {
cy= y1;
do PutPixelMode1(x1, cy++, nColor);
while (cy <= y2);
return;
} else {
cy= y2;
do PutPixelMode1(x1, cy++, nColor);
while (cy <= y1);
return;
}
}
if (!ddy) { //horizontal line special case
if (ddx > 0) {
cx= x1;
do PutPixelMode1(cx, y1, nColor);
while (++cx <= x2);
return;
} else {
cx= x2;
do PutPixelMode1(cx, y1, nColor);
while (++cx <= x1);
return;
}
}
if (ddy < 0) { iy= -1; ddy= -ddy; }//pointing up
else iy= 1;
if (ddx < 0) { ix= -1; ddx= -ddx; }//pointing left
else ix= 1;
dx= dy= ddx*ddy;
cy= y1, cx= x1;
if (ddx < ddy) { // < 45 degrees, a tall line
do {
dx-=ddy;
do {
PutPixelMode1(cx, cy, nColor);
cy+=iy, dy-=ddx;
} while (dy >=dx);
cx+=ix;
} while (dx > 0);
} else { // >= 45 degrees, a wide line
do {
dy-=ddx;
do {
PutPixelMode1(cx, cy, nColor);
cx+=ix, dx-=ddy;
} while (dx >=dy);
cy+=iy;
} while (dy > 0);
}
}

Tracer un cercle :

Voici la fonction pour tracer un cercle, Je ne m'étend pas dessus non plus, une recherche sur le net vous donneras toutes les explications, juste à savoir que l'on ne calcule qu'un quart de cercle et on dessine le reste par translation, cette méthode pour afficher un cercle n'est pas la meilleur, effectivement vous verrez en exécutant que si vous faite des cercle concentrique vous aurez des trous entre les cercle, mais elle est bien suffisent pour le moment et surtout, les autre type de tracer de cercle sont bien plus long...

void Cercle(int rayon, int x_centre, int y_centre, unsigned char nColor) {
int x =0;
unsigned char y = rayon;
int d = rayon - 1;
while ( y >= x )
{
PutPixelMode1( x+x_centre, y+y_centre, nColor );
PutPixelMode1( y+x_centre, x+y_centre, nColor );
PutPixelMode1( -x+x_centre, y+y_centre, nColor );
PutPixelMode1( -y+x_centre, x+y_centre, nColor );
PutPixelMode1( x+x_centre, -y+y_centre, nColor );
PutPixelMode1( y+x_centre, -x+y_centre, nColor );
PutPixelMode1( -x+x_centre, -y+y_centre, nColor );
PutPixelMode1( -y+x_centre, -x+y_centre, nColor );
if ( d >= 2*(x-1) )
{
d = d - (2*x);
x = x + 1;
}
else if ( d <= 2*(x-y) )
{
d = d + (2*y) - 1;
y = y-1;
}
else
{
d = d + 2*(y-x-1);
y = y-1;
x = x+1;
}
}

}

Le main maintenant
Rien de bien spéciale pour le main, on vas dessiner des carrés décalé de 1 pixel ... et des cercles ...

void main() {
int i;

//SCR_SET_MODE 1
__asm
ld a, #1
call #0xBC0E
__endasm;

for(i=0; i<25; i++)
{
lineBresenham(108+i,48+i, 211+i,48+i,i%4);
lineBresenham(211+i,48+i, 211+i,151+i,i%4);
lineBresenham(211+i,151+i, 108+i,151+i,i%4);
lineBresenham(108+i,151+i, 108+i,48+i,i%4);

// trace ligne oblique
//lineBresenham(108+i,48+i, 211+i,151+i,i%4);
//lineBresenham(211+i,151+i, 108+i,48+i,i%4);
}

//KM_WAIT_CHAR
__asm
call #0xBB06
__endasm;

// ON EFFACE TOUT
__asm
call #0xBBDB
__endasm;

for (i=0; i< 50; i++)
{
Cercle(30+i, 160, 100, 1);
}

//KM_WAIT_CHAR
__asm
call #0xBB06
__endasm;
}

Mais c'est quoi ce code la, ld a, #1 ce n'est pas du C ?
Et non, c'est de l'assembleur, c'est plus rapide de faire comme cela que de le faire en C tout de même... et cela appel les fonctions du frimweare
SCR_SET_MODE 1 permet de passer en mode 1, en fait ld a, #1 c'est pour choisir le mode, donc la valeur peut être 0, 1, 2 ...
KM_WAIT_CHAR permet d'attendre l'appuie sur une touche.
ON EFFACE TOUT bas heuuuu cela devrais faire du café non ? :-).
Le 1er FOR c'est pour dessiner 25 carrés soit 100 lignes
Les lignes en commentaire dans le for permettent de faire des diagonales dans le carré, juste pour vérifier que l'on peut faire autre choses sue des lignes verticale et horizontale.
Le 2ème for c'est pour tracer des cercles, on en fait 50 centré sur le centre de l'écran.

Voici ce que cela donne à l'écran


L'animation est fidèle en temps vis à vis d'un CPC 


Vous trouverais dans se ZIP le fichier C et de quoi compiler le tout.

Dessiner dans les autres modes
Je vous fournie ici les deux fonctions pour afficher un pixel en mode 0 et mode 2, il n'y a pas d'autre différence pour le reste du code, sauf appeler les bonne fonctions ;-).


void PutPixelMode0(int nX, unsigned char nY, unsigned char nColor)
{
    int nPixel = nX % 2;
    unsigned char *pAddress = (unsigned char *)((unsigned int)(0xC000 + ((nY / 8) * 80) + ((nY % 8) * 2048) + (nX / 2)));

    if(nPixel == 0)
    {
        *pAddress &= 85;

        if(nColor & 1)
        *pAddress |= 128;

        if(nColor & 2)
        *pAddress |= 8;

        if(nColor & 4)
        *pAddress |= 32;

        if(nColor & 8)
        *pAddress |= 2;
    }
    else
    {
        *pAddress &= 170;

        if(nColor & 1)
        *pAddress |= 64;

        if(nColor & 2)
        *pAddress |= 4;

        if(nColor & 4)
        *pAddress |= 16;

        if(nColor & 8)
        *pAddress |= 1;
    }
}


void PutPixelMode2(int nX, unsigned char nY, unsigned char nColor)
{
    int nPixel = nX % 8;
    unsigned char *pAddress = (unsigned char *)((unsigned int)(0xC000 + ((nY / 8) * 80) + ((nY % 8) * 2048) + (nX / 8)));

    if(nPixel == 0)
    {
        *pAddress &= 127;
        if(nColor & 1)
            *pAddress |= 128;
    }

    if(nPixel == 1)
    {
        *pAddress &= 191;
        if(nColor & 1)
            *pAddress |= 64;
    }

    if(nPixel == 2)
    {
        *pAddress &= 223;
        if(nColor & 1)
            *pAddress |= 32;
    }

    if(nPixel == 3)
    {
        *pAddress &= 239;
        if(nColor & 1)
            *pAddress |= 16;
    }

    if(nPixel == 4)
    {
        *pAddress &= 247;
        if(nColor & 1)
            *pAddress |= 8;
    }

    if(nPixel == 5)
    {
        *pAddress &= 251;
        if(nColor & 1)
            *pAddress |= 4;
    }

    if(nPixel == 6)
    {
        *pAddress &= 253;
        if(nColor & 1)
            *pAddress |= 2;
    }

    if(nPixel == 7)
    {
        *pAddress &= 254;
        if(nColor & 1)
            *pAddress |= 1;
    }
}

stephbb75

Page précédente : Sdcc - 03 - Fichier Bat Pour Compiler Avec Sdcc

CPCrulez[Content Management System] v8.7-desktop
Page créée en 547 millisecondes et consultée 1374 fois

L'Amstrad CPC est une machine 8 bits à base d'un Z80 à 4MHz. Le premier de la gamme fut le CPC 464 en 1984, équipé d'un lecteur de cassettes intégré il se plaçait en concurrent  du Commodore C64 beaucoup plus compliqué à utiliser et plus cher. Ce fut un réel succès et sorti cette même années le CPC 664 équipé d'un lecteur de disquettes trois pouces intégré. Sa vie fut de courte durée puisqu'en 1985 il fut remplacé par le CPC 6128 qui était plus compact, plus soigné et surtout qui avait 128Ko de RAM au lieu de 64Ko.