Après s'être bien échauffé les cordes vocales, nous allons passer en revue quelques trucs d'optimisation asm. Même si les solutions proposées ne s'appliqueront que rarement telles quelles, ça donnera éventuellement des idées (généralement les histoires de bits donnent toujours des idées).COMPLETEMENT DECALE Quand on doit lire des données bit par bit, une méthode est d'extraire un octet, et d'utiliser une rotation bien sentie. Pour savoir quand prendre un nouvel octet, un autre registre fait office de compteur (de 7 à 0). Une alternative consiste à forcer le bit 0 à 1. Une première lecture se fera par "RLA : SET 0,A ", les suivantes par SLA A simplement (pour insérer des bits 0), et on rechargera quand le SLA renverra le flag Z, Il existe une instruction cachée qui remplacera avantageusement "RLA : SET 0,A" ; il s'agit de SLL A, de code #CB #37 (on prend les mêmes opcodes que SLA, mais on ajoute #10 au 2eme). Voila, s il y a d'autres instructions dont vous ne voyez pas l'utilité, demandez ! M'enfin, le plus efficace reste de copier 8 fois la routine ! Un même octet peut contenir une valeur 7 bits et un flag, c'est son droit le plus strict. On rencontrera par exemple ce cas avec des nombres signés, quand on a prévu 2 routines spécifiques. Le "flag" en question sera le signe, et les 7 bits représenteront une valeur absolue. Pensez alors à mettre le flag dans le bit 0, de telle façon qu'après un décalage, la valeur se retrouve bien positionnée. Pensez aussi à mettre un préservatif le cas échéant ! Illustration : SRL A JR C,NEGATIF ;lci commence la routine "POSITIF", qui peut directement utiliser ACE N'EST PAS A UN VIEUX SIGNE QU'ON APPREND A FAIRE DES GRISES MATHS (NDSNN : Madram, tu passeras à mon bureau, c'est le pire calembour que tu aies jamais fait !) Ceci dit, lorsqu'on souhaite manipuler des nombres signés, il vaut mieux traiter en bloc tous les nombres d'un même signe. C'est ce qui se passe quand, dans un starfield plongeant, on traite à part les 4 quadrants de l'écran. BOUCLE 16 BITS Mes bons amis, ci-suit la méthode la plus rentable pour effectuer un compteur 16 bits. Soit BC=#1F32, on sépare (cruellement, certes) B et C LOOP; Ici figure vos instructions. ; Je serais vous, je ne m'y prendrais pas comme ça ; Quoique, s'y j'étais vraiment vous, je ferais pareil, ; puisque je serais vous, et vous, vous faite comme ça. DEC C ; Ï JR NZ,LOOP DJNZ LOOP On se rend compte que B doit être incrémenté (car il indique le nombre de fois qu'on effectue une boucle sur C), et que C=0 implique en fait C=256, Ainsi, si on désire vraiment #1F32 itérations, on chargera BC avec #2032. #1F00 resterait #1F00, la transformation pouvant s'écrire DEC C :INC BCRemarquez, cette "transformation" est automatiquement réalisée quand on passe en négatif. On peut donc aussi faire : LD BC,-#1F32 LOOP ; ; INC C JR NZ,LOOP INC B JR NZ,LOOPTant que vous y êtes, notez qu'une automodif du genre :
TOTO LD A,n LD (TOTO+),Acoûte moins que la paire PUSH/POP. Pour un additif alimentaire sur les boucles, consultez Quasar ! COMPARAISON SOUTENUE Si vous avez à comparer 2 nombres 16 bits, pour détecter une adresse de fin par exemple, commencez par le poids qui contient la valeur la moins souvent atteinte. Mettons que HL vaille initialement #4000, et qu'on veuille boucler tant qu'il est inférieur à #7E03. On fera : LOOP ; ; INC HL LD A,H ; H ne vaudra que 4 fois #7E, CP #7E ;quand HL=#7E00,#7EO1,#7EO2 et #7E03 JR NZ,LOOP LD A,L CP #03 JR NZ,LOOPSi on voulait comparer avec #7E50, on testerait L en premier, car il égalera #50 lors de #3E itérations (quand HL=#4050, #4150, ...), tandis que H égalera #7E pendant #50 pas. LA SAUVEGARDE DU FOND Je m'y connais, je l'ai touché depuis longtemps ! Bien sûr, le plus rapide est de ne pas le sauvegarder du tout, en l'ayant stocke intégralement auparavant. Mais un manque de mémoire peut empêcher cette manœuvre. Imaginez un sprite de 7 octets de large sur 11 de haut, se déplaçant horizontalement octet par octet à droite. On a stocké un premier rectangle 7x11, puis affiché le sprite. Dans le nouveau rectangle de fond à sauvegarder, une grande partie se trouve déjà dans la précédente sauvegarde. En fait, toutes les bandes verticales, sauf la dernière (la plus à droite). De plus, rétablir tout le fond serait peu astucieux, puisqu'il se retrouvera détruit. Il suffit de réafficher la bandelette de gauche, non recouverte par le sprite lors de la prochaine itération (dans le cas d'un sprite "compact", non masqué). On résume : - sauvegarde intégrale du rectangle, la 1ère fois
- affichage du sprite.
- rétablissement de la bandelette à gauche
- sauvegarde d'une nouvelle bandelette à droite.
- on boucle à 2 !
Afin d'éviter que le rectangle de sauvegarde ne se déplace en mémoire "parallèlement" au sprite, on travaillera modulo 8. Pour un déplacement multi-directionnel, je vous laisse extrapoler. MASQUAGE Ce sujet m'a été commandé par la Pecodocave, petite commission d'organisation du carnaval de Venise. Rappel : le masquage consiste à gérer la "transparence" d'un sprite, qu'on puisse voir le fond à travers ses trous ! En pratique, dans un octet contenant x pixels, il faudra modifier une partie en laissant les autres intacts. Les exemples suivants se basent sur le mode 0, où te pixel de gauche est codé aXbXcXdX (dcba donne l'encre), et celui de droite XaXbXcXd. LD A,(HL) ; on lit le fond AND #AA; #AA=&x10101010, on conserve le pixel gauche OR B ; pixel droite, B de la forme OaObOcOd LD (HL),A; le tour est jouéDans une routine, on a tout intérêt à stocker #AA dans un registre. Pour changer le pixel gauche, on pourrait faire la même chose avec un AND #55 ; mais si C contient déjà #AA, rusons ! LD A,(HL) OR C ; on conserve le pixel droite AND D ; D de la forme a1b1c1d1 LD (HL),APour les pixels les plus soumis au masquage (généralement les bords d'un sprite), choisissez de préférence les encres 0 et 15, puisque qu'alors un seul opérateur logique suffira : AND #AA : mise à 0 du pixel droite AND #55 : mise à 0 du pixel gauche OR #AA : mise à 15 du pixel gauche OR #55 : mise à 15 du pixel droiteRemarque : le graphiste n'a pas à se coltiner ces "contraintes". Une conversion d'encres est toujours possible après coup, à moindre frais. LUTINS MALICIEUX (NDSNN : Tu sais ce qu'il te dit, le lutin !) Si vous comptez déplacer un sprite au pixel près, veillez à NE PAS choisir un nombre "rond" de pixels en largeur. En mode 0, par exemple, un nombre impair assurera toujours la même largeur EN OCTETS. DERRIERE LES FAGOTS Un écran de 32 mots de large permet décrémenter la position x en ne modifiant que le poids faible (INC L à la place de INC HL). Ceci est détaillé en long, en large, et en anglais dans Demoniak 6. Il existe d'autres solutions. Par exemple, fixer la largeur à 43 regroupe les passages "critiques" (quand le poids faible passe de #FF à #00) le long d'une ligne suffisamment pentue pour laisser une grande surface "libre". Si vous avez du mal à visualiser, essayez : FOR i=0 to &3f:POKE &c000+i*&100,255 : NEXT : FOR i=32 to 50:OUT &bc00,1:OUT &bd00,i:CALL &BB06;NEXT NINC L CHROME Même dans un écran peu arrangeant, on essaiera d'éviter les INC HL au profit des INC L. Par exemple, si on commence sur une adresse paire, on alternera INC L et INC HL. Avec une adresse impaire aussi, mais en commençant par INC HL ! ALTERNANCE En rupture ligne à ligne, on a souvent à modifier les registres 12 et 13. Plutôt que de les changer toujours dans le même ordre (12, 13, 12, 13, ...), alternez (12, 13, 13, 12, 12, 13 ...). On gagne ainsi une sélection de registre sur deux. Il faut bien-sûr "dédoubler" l'intérieur de la boucle. Ceci reste valable pour les rasters sur plusieurs encres, ou les samples sur plusieurs voies ! CONSEIL GENERAL 1 Evitez, dans la mesure du possible, toute "table intermédiaire". Si une donnée doit subir plusieurs traitements successifs, essayez de les réaliser d'une traite. Même si ça oblige à jongler lourdement avec les registres, on gagne à ne pas écrire une donnée intermédiaire pour la relire ensuite. CONSEIL GENERAL 3 (Le conseil général 2 n'était pas très bon) Dans une boucle, tout test ne dépendant que d'un paramètre qui n'évolue pas au cours de la boucle est à proscrire. Par exemple, une routine d'affichage de caractère s'aiguillera sur 3 routines distinctes suivant le mode ; mais on n'aura pas une seule routine qui teste dans la boucle principale le mode courant ! QUOI D'AUTRE ? Si vous le souhaitez, nous étudierons d'autres astuces, sur des sujets précis (tel ou tel effet graphique, recherche de chaînes, cueillette de plantes, ...). D'ici là, au revoir. AMSLIVE n°15 ★ AMSTRAD CPC ★ A voir aussi sur CPCrulez , les sujets suivants pourront vous intéresser... |
CPCrulez[Content Management System] v8.7-desktop/c Page créée en 274 millisecondes et consultée 2005 foisL'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. |
|
|