Essai de rotozoom (archive d’un autre blog 2014)

Aujourd’hui on va tester un effet classique (sur des machines plus puissantes que notre pauvre TO8) et essayer d’optimiser un peu. On commence par trouver un tuto en google-isant avec rotozoom et on se retrouve ici: http://www.alrj.org/docs/2D/rotozoom/rotozoom.html A première vue c’est jouable sur du thomson. On va partir sur une pattern de 64×64 rendue sur un carré de 128×128. Le tout avec le mini C, du calcul en  virgule fixe 8:8 et le minimum d’assembleur pour commencer. Cela donne la partie active suivante:

u=0;
v=0;
texture=0xA000;
set_pt();
/* read on screen to texture… /
for(y=0;y<64;y++) {
for(x=0;x<64;x++) {
if (readpixel_BW(x,y)!=0) {
texture[(x)+(y)64]=1;
} else {
texture[(x)+(y)*64]=0;
}
}
}
scale=2;
for (alpha=0;alpha<64;alpha++) {
xx = cos(alpha)scale;
yy = sin(alpha)scale;
/* on calcule le vecteur balade */
for(y=0;y<128;y++) {
/* pour toutes les lignes horizontales de l'ecran /
_u = u;
_v = v;
/ on sauve les coordonnees du premier pixel de la ligne a afficher /
for(x=0;x<128;x++) {
/ affichage d'une ligne horizontale /
u = u + xx;
v = v + yy;
xxx=clip(u);
yyy=clip(v);
/ on se balade dans la texture /
putpixel(x, y, texture[xxx+yyy64]);
/* on affiche le point actuel de la texture a l'ecran /
}
u = _u - yy;
/ on se place sur le premier pixel de la prochaine ligne a afficher /
v = _v + xx;
/ en se deplacant selon un vecteur perpendiculaire au vecteur utilise pour tracer les lignes */
}
}

Et quelques primitives en 6809 comme:

cos(a) int a; 
{ {
#asm
LDB 5,U
LSLB
LDX #COSFIXED
LDD B,X
* return in X
#endasm
} }

Les fonction de lecture et d’écriture de point sont classiques. Cette version permet d’obtenir une frame … toutes les 8 secondes environ pour du mono… pas très interactif tout ça… On va donc passer à une boucle totalement en assembleur et en ne passant plus par un calcul de coordonnées écran pour putpixel(), avec également déroulage de la boucle sur un octet écran de 8 pixels… On crée donc la fonction C de rotozoom rapide

faster_roto(a)  
int a;
int scale;
{ {
#asm
* xx = cos(a)*scale;
* yy = sin(a)*scale;
LDB 5,U *
PSHS U ; we will kill U (the register, not 'you')
LSLB LDX #COSFIXED
LDX B,X
STX roto_xx
LDX #SINFIXED
LDX B,X
STX roto_yy
CLR roto_y
CLRA CLRB
STD roto_u
STD roto_v
* for(y=0;y<128;y++) {
ROTO_LOOPY
LDA roto_y
LBMI ROTO_END
* _u = u; * _v = v;
LDB #40 MUL
ADDD #$4000
STD roto_line
LDD rotou
STD roto_u
LDD rotov
STD roto_v
...

Le résultat est plus rapide, environ 1 frame par 2 secondes. C’est mieux mais pas encore assez rapide. On se rend aussi compte que la mise à l’échelle ne sera pas assez pratique car à 1 on manque de précision, il faudra envisager de passer en 8:16 nos calculs pour affiner. A partir du listing généra par c6809 (assembleur de PULS) il est possible de trouver le timing pour un pixel.

Ce qui nous donne 161 cycles par pixel (hors calcul de début de trame et hors blit). pour 128×128 pixels on a donc un peu moins de 2 millions de cycle par frame… ce qui colle bien avec la vitesse constatée (la fréquence d’horloge étant à 1MHz).

Pour obtenir quelque chose de rapide il n’y a pas 50 options… il faut réduire la taille et faire de gros pixels.

donc on change tout pour passer sur une bidouille avec un mode TO7 pour une résolution de 80×50 en 16 couleurs sans conflit.

L’image sera calculée sur 60×40 pour rester raisonnable en taille.

Une image « classique » de l’effet retaillée plus tard… et hop:

L’archive au format .HFE (pour TEO ou à déposer sur votre SD HxC)