[Programme C] Algorithme de traçage de pixel

Bonjour à tous !
Depuis bientôt 2 semaines, un vénérable membre de UTI nous a donné la voie pour commencer à programmer en C++ compilé pour le faire fonctionner avec l'émulateur de Goplat :
http://www.unitedti.org/forum/index.php ... stp=139981
Sur cette belle occasion, et histoire de mettre à l'épreuve ma logique, j'ai essayé de programmer une fonction pour afficher un pixel.
A première vue, ça parait simple, x et y, et clic clac pouf paf, on déplace le pointeur et on met la couleur... AAAAA, pas si vite petit ! Car quand on se penche dessus de plus près, on remarque que les pixels sont codés deux par deux dans un même octet... Je dois reprendre depuis le début, vous m'avez laché ?
Rappel :
Un octet est une séquence de 8 bits, en binaire cela donne : 0b00000000 ou encore 0b11111111, ou encore 0b01110110
puis en hexa : 0x00 ou encore 0xFF ou encore 76
en décimal : 0 ou encore 255 ou encore 118
De plus, on sait que l'écran de la nspire est codé entre les adresses mémoire 0xA4000100 et 0xA40096FF (cf Hackspire)
Or si l'on fait le calcul, en sachant que l'écran comporte 240*320 pixels, soit 76800 pixels, en hexa 12C00. Or 12C00 + 100 = 12D00 octets de décallage, ce qui est beaucoup trop par rapport à la limite 96FF de l'écran !!
En fait, chaque octet code deux pixels, c'est à dire que les bits de poids fort (les 4 premiers) vont coder le pixel de gauche, et les pixels de poids faible (les 4 derniers) le pixel de droite.
En effet : (240*(320/2)) = 38400, en hexa = 9600, et 9600 + 100 = 9700. Comme on commence à 0 et pas à 1 dans l'adressage mémoire, cela donne 96FF, ce qui convient.
Cette méthode était dure à gérer en C, on ne peux pas couper un octet en deux, il faut obligatoirement gérer les deux pixels en même temps, soit en les valorisant deux par deux, soit en utilisant un peu de logique (et tellement de logique que je suis assez fier de mon exploit, même si d'autres l'ont déjà fait avant
).
Enfin, une subtilité qui m'a fait bien des soucis est la couleur. En effet, il y a 16 niveaux de gris (0 à 15, puisque de 0b0000 à 0b1111 puisque de 0x0 à 0xF). Mais, logiquement, il y a un problème en termes d'algorithme. Comme on ne peut pas exploiter un demi octet seulement (ou alors je sais pas faire et je me suis compliqué la vie pour rien), on doit utiliser les opération AND/OR/XOR/NOT. J'utiliserais ce que j'appelle des masques. De plus, pour effacer l'écran on utilise une boucle qui va donner la valeur FF à chaque octet (dual-pixel si vous voulez) et là pas besoin de masque, puisque c'est partout pareil.
exemples :
# pour noir, - pour blanc
[tableborder=1]pixels.................... -. -. -. # # -. # # [/table]
[tableborder=1]valeur des octets... 255 240 .15. ..0.. ............................ ..FF. .F0. .0F. .00. [/table]
Alors maintenant, imaginez que vous voulez afficher UN et UN seul pixel ? Comment ça "facile" ?
Oh que non, car une autre contrainte est que vous devez conserver la valeur du précédent octet, tout en assignant au bon endroit notre demi octet.
Vous comprenez ? non ? Allez... imaginons un unique octet pour notre écran :
[tableborder=1]pixels.................... -. -. [/table]
[tableborder=1]valeur des octets... 255 ............................ ..FF. [/table]
Et là on veut assigner le deuxième pixel à # (noir), donc cela revient à valoriser l'octet à 240 :
[tableborder=1]pixels.................... -. # [/table]
[tableborder=1]valeur des octets... 240 ............................ ..F0. [/table]
Maintenant, on veut allumer le 1er pixel, mais on ne sait pas si le deuxième pixel est allumé, on donne la valeur 15 à l'octet :
[tableborder=1]pixels.................... # -. [/table]
[tableborder=1]valeur des octets... 15 ............................ ..0F. [/table]
Et là... catastrophe ! Il aurait fallut assigner la valeur 0 !
[tableborder=1]pixels.................... # # [/table]
[tableborder=1]valeur des octets... ..0.. ............................ ..00. [/table]
C'est donc là que l'opération AND rentre en jeux, mais avec d'autres opérandes, comme le décalage de bit et le masque pour éviter la perte de valeur, étant donné que le 1 est universel (il n'influence pas sur la valeur) pour cette opérande.
Allez, trêve de bavardage, voilà la solution, qui affiche un magnifique dégradé :
http://pastebin.com/f435c1bbb
et le résultat :

@+ !
Depuis bientôt 2 semaines, un vénérable membre de UTI nous a donné la voie pour commencer à programmer en C++ compilé pour le faire fonctionner avec l'émulateur de Goplat :
http://www.unitedti.org/forum/index.php ... stp=139981
Sur cette belle occasion, et histoire de mettre à l'épreuve ma logique, j'ai essayé de programmer une fonction pour afficher un pixel.
A première vue, ça parait simple, x et y, et clic clac pouf paf, on déplace le pointeur et on met la couleur... AAAAA, pas si vite petit ! Car quand on se penche dessus de plus près, on remarque que les pixels sont codés deux par deux dans un même octet... Je dois reprendre depuis le début, vous m'avez laché ?
Rappel :
Un octet est une séquence de 8 bits, en binaire cela donne : 0b00000000 ou encore 0b11111111, ou encore 0b01110110
puis en hexa : 0x00 ou encore 0xFF ou encore 76
en décimal : 0 ou encore 255 ou encore 118
De plus, on sait que l'écran de la nspire est codé entre les adresses mémoire 0xA4000100 et 0xA40096FF (cf Hackspire)
Or si l'on fait le calcul, en sachant que l'écran comporte 240*320 pixels, soit 76800 pixels, en hexa 12C00. Or 12C00 + 100 = 12D00 octets de décallage, ce qui est beaucoup trop par rapport à la limite 96FF de l'écran !!
En fait, chaque octet code deux pixels, c'est à dire que les bits de poids fort (les 4 premiers) vont coder le pixel de gauche, et les pixels de poids faible (les 4 derniers) le pixel de droite.
En effet : (240*(320/2)) = 38400, en hexa = 9600, et 9600 + 100 = 9700. Comme on commence à 0 et pas à 1 dans l'adressage mémoire, cela donne 96FF, ce qui convient.
Cette méthode était dure à gérer en C, on ne peux pas couper un octet en deux, il faut obligatoirement gérer les deux pixels en même temps, soit en les valorisant deux par deux, soit en utilisant un peu de logique (et tellement de logique que je suis assez fier de mon exploit, même si d'autres l'ont déjà fait avant

Enfin, une subtilité qui m'a fait bien des soucis est la couleur. En effet, il y a 16 niveaux de gris (0 à 15, puisque de 0b0000 à 0b1111 puisque de 0x0 à 0xF). Mais, logiquement, il y a un problème en termes d'algorithme. Comme on ne peut pas exploiter un demi octet seulement (ou alors je sais pas faire et je me suis compliqué la vie pour rien), on doit utiliser les opération AND/OR/XOR/NOT. J'utiliserais ce que j'appelle des masques. De plus, pour effacer l'écran on utilise une boucle qui va donner la valeur FF à chaque octet (dual-pixel si vous voulez) et là pas besoin de masque, puisque c'est partout pareil.
exemples :
# pour noir, - pour blanc
[tableborder=1]
[tableborder=1]
Alors maintenant, imaginez que vous voulez afficher UN et UN seul pixel ? Comment ça "facile" ?
Oh que non, car une autre contrainte est que vous devez conserver la valeur du précédent octet, tout en assignant au bon endroit notre demi octet.
Vous comprenez ? non ? Allez... imaginons un unique octet pour notre écran :
[tableborder=1]
[tableborder=1]
Et là on veut assigner le deuxième pixel à # (noir), donc cela revient à valoriser l'octet à 240 :
[tableborder=1]
[tableborder=1]
Maintenant, on veut allumer le 1er pixel, mais on ne sait pas si le deuxième pixel est allumé, on donne la valeur 15 à l'octet :
[tableborder=1]
[tableborder=1]
Et là... catastrophe ! Il aurait fallut assigner la valeur 0 !
[tableborder=1]
[tableborder=1]
C'est donc là que l'opération AND rentre en jeux, mais avec d'autres opérandes, comme le décalage de bit et le masque pour éviter la perte de valeur, étant donné que le 1 est universel (il n'influence pas sur la valeur) pour cette opérande.
Allez, trêve de bavardage, voilà la solution, qui affiche un magnifique dégradé :
http://pastebin.com/f435c1bbb
Show/Hide spoilerAfficher/Masquer le spoiler
En version non colorée et non commentée
- Code: Select all
asm ("mov sp,#0x12000000");
void main(void)
{
unsigned char ligne = 0xA0;
unsigned char *ptr;
unsigned int ini = 0xa4000100;
unsigned int x = 0;
unsigned int y = 0;
unsigned char color, colorf = 0;
unsigned int xf;
ptr = (unsigned char *) ini;
while (ptr (unsigned char *) (ini+240*ligne)) {
*ptr++= 0xFF;
}
while (y 240){
while (x 320){
while (color = 15){
xf = x1;
if (xf1==x){colorf = (color4)+0b1111;}
else {colorf = color+0b11110000;}
ptr = (unsigned char*) (ini+(xf+ligne*y));
*ptr = *ptrcolorf;
color++;
x++;
}
color = 0;
}
x = 0;
y++;
}
b:
goto b;
}
et le résultat :

@+ !