CODINGAMSLIVE ★ AMSLIVE n°15 - OPTIMUM SPIRITUM ★

AMSLIVE n°15 - AsmCoding Amslive

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 A

CE  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 BC

Remarquez, 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,LOOP

Tant que vous y êtes, notez qu'une
automodif du genre :

TOTO   LD   A,n
LD   (TOTO+),A

coû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,LOOP

Si 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 manoeuvre.

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 :

  1. sauvegarde intégrale du rectangle, la 1ère fois
  2. affichage du sprite.
  3. rétablissement de la bandelette à gauche
  4. sauvegarde d'une nouvelle bandelette à droite.
  5. 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),A

Pour 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 droite

Remarque : 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

★ ANNÉE: ???
★ AUTEUR: MADRAM

Page précédente : AMSLIVE n°14 - Sound Player 2

CPCrulez[Content Management System] v8.7-desktop/cache
Page créée en 855 millisecondes et consultée 1179 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.