SINCLAIR'S SPIRIT

Déplacement au pixel.

Le cour précédent nous a permis de voir les automodifications et l'utilisation des flags pour nos tests.
Le resultat si vous y êtes parvenus est un smiley qui se balade sur tout l'écran en rebondissant sur le border.
Mais nous avons ici un problème.
Notre sprite se déplace à l'octet et donc au pixel en Y, mais à l'octet en X.
Hors 1 octet en X c'est 8 pixels d'un coup...
C'est assez violent et notre déplacement n'est donc pas identique en X et en Y.
Nous allons donc améliorer cela pour passer aussi au pixel en X

Création des sprites.

Pour passer au pixel en X il n'y a pas 50 solutions, il va nous falloir notre sprite avec autant de décalage en RAM.
Petit schéma explicatif:

smiley décalé

Comme nous devrons décaler le sprite de 8 pixels, nous allons donc déborder de notre largeur de 2 octets.
Le mieux est donc de passer directement à une largeur de 3 octets pour avoir une largeur constante et n'avoir donc qu'une seule routine d'affichage quelque soit le cas.

Nous avons maintenant deux choix:
- Dessiner toutes les étapes comme si dessus (mais sans les traits rouges de limite d'octet). - Faire un programme qui va nous copier le sprite en décallé automatiquement.

Dessiner toutes les étapes n'est pas franchement la chose la moins rapide. On peut même dire que ca nous évite de faire une routine et de réfléchir.
Oui mais cela prend de la place inutilement et lorsque nous devrons charger notre routine avec une cassette (oui oui), cela augmentera le temps de chargement.
Nous allons donc nous faire une routine qui va recopier le sprite en décallé 8 fois en ram.

Pour commencer, reprennons notre sprite sur desormais 3 octets.

                .spr
                            DB      %00000111,%11100000,0 ; line 0
                            DB      %00011000,%00011000,0 ; line 1
                            DB      %00100000,%00000100,0 ; line 2
                            DB      %01000000,%00000010,0 ; line 3
                            DB      %01000000,%00000010,0 ; line 4
                            DB      %10000100,%00100001,0 ; line 5
                            DB      %10000000,%00000001,0 ; line 6
                            DB      %10000000,%00000001,0 ; line 7
                            DB      %10000000,%00000001,0 ; line 8
                            DB      %10000000,%00000001,0 ; line 9
                            DB      %10001000,%00010001,0 ; line 10
                            DB      %01000100,%00100010,0 ; line 11
                            DB      %01000011,%11000010,0 ; line 12
                            DB      %00100000,%00000100,0 ; line 13
                            DB      %00011000,%00011000,0 ; line 14
                            DB      %00000111,%11100000,0 ; line 15

Voila qui est fait.
Commençons maintenant notre routine:

                            LD      HL,.spr         ;on pointe sur le début du gfx
                            LD      DE,.spr+(16*3)  ;le sprite suivant devra être 16*3 octets plus loin
                            LD      A,(HL)          ;on lit le premier octet.
                            SRL     A               ;décalage vers la droite avec inclusion de 0 dans le bit 7 et bit 0 mis dans la carry
                            LD      (DE),A          ;on envoi le nouvel octet
                            INC     DE              ;on pointe sur l'octet suivant
                            INC     HL              ;on passe à l'octet suivant
                            LD      A,(HL)          ;on lit le deuxième octet
                            RR      A               ;on decale vers la droite en incluant en bit 7 la carry et en mettant le bit 0 ensuite dans celle-ci.
                            LD      (DE),A
                            INC     DE
                            INC     HL
                            LD      A,(HL)          ;dernier octet
                            RR      A
                            LD      (DE),A
                            INC     DE
                            INC     HL

Maintenant que nous avons fait cela pour la première ligne, nous pouvons le faire pour les autres en incluant le tout dans une boucle.
Comme nous avons fait 1 ligne il faut faire 16 fois la boucle. Mais comme en plus nous avons 8 décalages a faire à partir du sprite de départ, il nous faut 16*8=128 répétitions

                            LD      HL,.spr         ;on pointe sur le début du gfx
                            LD      DE,.spr+(16*3)  ;le sprite suivant devra être 16*3 octets plus loin
                            LD      B,112           ;nombre de répétitions
                .DecaleSpr
                            LD      A,(HL)          ;on lit le premier octet.
                            SRL     A               ;décalage vers la droite avec inclusion de 0 dans le bit 7 et bit 0 mis dans la carry
                            LD      (DE),A          ;on envoi le nouvel octet
                            INC     DE              ;on pointe sur l'octet suivant
                            INC     HL              ;on passe à l'octet suivant
                            LD      A,(HL)          ;on lit le deuxième octet
                            RR      A               ;on decale vers la droite en incluant en bit 7 la carry et en mettant le bit 0 ensuite dans celle-ci.
                            LD      (DE),A
                            INC     DE
                            INC     HL
                            LD      A,(HL)          ;dernier octet
                            RR      A
                            LD      (DE),A
                            INC     DE
                            INC     HL
                            DJNZ    .DecaleSpr

Pour que vous compreniez mieux l'utilisation des instructions de décalage que j'ai utilisé, voici un schéma global pour une ligne (3 octets) de notre sprite:

décalage octets image

Faites attention à bien placer vos grpahismes du sprite à la toute fin du code si vous ne voulez pas écraser celui-ci avec la génération des sprites décalés

Notez aussi qu'il est toujours préférable de générer les données plutôt que de le faire à la main, pour la simple raison qu'un changement vous obligerait à tout refaire.
Ici simplement en changeant le sprite de base, tout se recréera tout seul.

Passons à la routine d'affichage maintenant car nous avons quelques changements à y apporter.

Calcul du sprite à afficher.

Pour commencer, remettons ici notre routine d'affichage telle que nous l'avions faite:

                .POSY           LD      A,.YPOS         
                                CALL    .CALCLGN

                                ;DE contient l'adresse de la ligne

                .POSX
                                LD      A,.XPOS
                                ADD     A,E             
                                LD      E,A             

                                

                                LD      HL,.Smile       
                                LD      B,16            
                .SPRITE
                                PUSH    BC              
                                LDI                     
                                LDI
                                LDI                     ;Je rajoute ici un LDI                    
                                DEC     E               
                                DEC     E               
                                DEC     E               ;Et un DEC E pour passer à 3 octets de large
                                CALL    .LGNINF         
                                POP     BC              
                                DJNZ    .SPRITE

Le calcul de X était assez simple.
Nous lisions une valeur et on l'ajoutait à l'adresse écran.

Cependant, notre routine va devoir changer.
En effet, prenons par exemple les valeur de 0 à 10...
0: on prendra le 1ier sprite et on sera en position x=0.
1: on prendra le 2ème sprite et on sera en position x=0.
2: on prendra le 3ème sprite et on sera en position x=0.
3: on prendra le 4ème sprite et on sera en position x=0.
4: on prendra le 5ème sprite et on sera en position x=0.
5: on prendra le 6ème sprite et on sera en position x=0.
6: on prendra le 7ème sprite et on sera en position x=0.
7: on prendra le 8ème sprite et on sera en position x=0.
8: on prendra le 1er sprite et on sera en position x=1.
9: on prendra le 2ème sprite et on sera en position x=1.
10: on prendra le 3ème sprite et on sera en position x=1.

Nous avons donc 8 valaurs pour lesquelles la position en X ne bouge pas et seul le sprite décalé change.
Ceci revient à dire que les valeur 0 à 7 correspondent au numéro du sprite tandis que les valeurs supérieures à 7 correspondent à la coordonnée X à ajouter à l'adresse de la ligne.
Aussi, nous allons dans un premier temps pouvoir ajouter la coordonnée X en supprimant les valeurs 0 à 7, soit les bits 0 à 2.

                .POSY           LD      A,.YPOS
                                CALL    .CALCLGN

                                ;DE contient l'adresse de la ligne

                .POSX           LD      A,.XPOS
                                LD      C,A         ;on sauvegarde la valeur
                                SRL     A
                                SRL     A
                                SRL     A           ;on enlève les 8 possibilités de sprites
                                ADD     A,E         ;on ajoute X à l'adresse
                                LD      E,A

Maintenant que notre décalage en X est fait, il nous reste à choisir lequel des 8 sprites nous allons afficher.

Comme chaque valeur correspond à un sprite, il suffit donc de prendre l'adresse du premier des sprites et de lui ajouter n fois la longueur de celui-ci.
Comme notre sprite fait 16*3 octets = 48, le premier sprite est donc en 0, le deuxième en 48; le troisième en 96 etc etc.
Soit: n le numéro du sprite et son adresse est égal à:
Adresse du sprite 0+(48*n)

Refléchissais bien, faites vous un schéma si nécessaire, vous verrez que c'est logique et simple.

Nous devons donc multiplier la valeur de nos trois premier bits de la coordonnée X par la longueur d'un sprite et ajouter à l'adresse du premier de ceux-ci.
Mais comment multiplier ?

Nous avons de la chance je vous le dis directement, notre multiplication est une puissance de 2 (comme 1,2,4,8,16,32,64,128,256...) et ca va bien nous arranger.
Revoyons brièvement notre cour de CE1 (ou CE2 je ne sais plus trop à notre époque quand nous avions vu cela) sur la multiplication.
Une multiplication n'est en soit qu'une addition d'un nombre par lui même.
Ex: 2*4 donne 4+4=8 ; 64*4 c'est 64+64+64+64=256
C'est exactement ce que nous allons exploiter.

Nous devons faire une multiplication par 48.
Prenons le chiffre 1:
1+1=2 : Addition 1
2+2=4 : Addition 2
4+4=8 : Addition 3
8+8=16 : Addition 4 ... Ici nous avons donc multiplié par 16 notre valeur de départ.

Changeons notre valeur de départ et prenons par exemple 4:
4+4=8 : Addition 1
8+8=16 : Addition 2
16+16=32 : Addition 3
32+32=64 : Addition 4 ... Nous avons fait le même nombre d'addition... Hors 64/4 donne ? Eh bien 16 !!! Notre multiplication reste la même pour le même nombre d'additions.

Aussi pour multiplier par 48:
1+1=2 : *2
2+2=4 : *4
4+4=8 : *8
8+8=16 :*16
16+16=32 : *32
32+32... Zut, on dépasse notre valeur.
Restons donc à 32 qui est notre multiplication maximale avant de dépasser 48. Posons-nous la question de savoir combien il manque à 32 pour atteindre 48: 16.
Une multiplication par 48 est donc une multiplication par 32 additionnée à une multiplication par 16 !

Reprennons:
1+1 : *2
2+2 : *4
4+4 : *8
8+8 : *16 ... On stocke cette valeur puis on continue
16+16 : *32
On additionne avec le resultat de la multiplication par 16 et hop, nous avons une multiplication par 48.

Notre Z80 possède plusieurs instructions d'addition qui pour les registres 8bits s'effectuent avec A pour resultat et pour les registres 16bits avec HL.
Comme multiplier 48*8 possibilité de sprite donne 384, nous avons donc besoin d'un registre 16 bits pour calculer.

L'instruction que nous allons utiliser est: LD HL,r16 : Celle-ci additionne à HL un registre 16bits et met le resultat dans HL.
Aussi pour multiplier un nombre par lui même et faire notre multiplication, nous pourrons simplement mettre ce chiffre dans HL et faire des LD HL,HL.

Reprenons notre routine et ajoutons notre calcul. Je rappelle avant tout que nous devrons ajouter le resultat de la multiplication à l'adresse ou se trouvent nos sprites

                .POSY           LD      A,.YPOS
                                CALL    .CALCLGN

                                ;DE contient l'adresse de la ligne

                .POSX           LD      A,.XPOS
                                LD      C,A             ;on sauvegarde la valeur
                                SRL     A
                                SRL     A
                                SRL     A               ;on enlève les 8 possibilités de sprites
                                ADD     A,E             ;on ajoute X à l'adresse
                                LD      E,A

                                LD      A,C             ;on récupère la valeur de X
                                AND     %00000111       ;on garde les 8 possibilités de sprites
                                LD      L,A             ;on copie A dans HL
                                LD      H,0             ;pensez à mettre H=0
                                ADD     HL,HL           ;*2
                                ADD     HL,HL           ;*4
                                ADD     HL,HL           ;*8
                                ADD     HL,HL           ;*16
                                LD      C,L             ;on sauvegarde le resultat de la multiplication par 16
                                LD      B,H             ;dans un registre 16bits bien evidement
                                ADD     HL,HL           ;*32
                                ADD     HL,BC           ;*48 par addition de *32+*16
                                LD      BC,.SPR         ;adresse des sprites en mémoire
                                ADD     HL,BC           ;addition de l'adresse des sprites avec notre valeur calculée
                                
                                LD      B,16
                .SPRITE
                                PUSH    BC
                                LDI
                                LDI
                                LDI
                                DEC     E
                                DEC     E
                                DEC     E
                                CALL    .LGNINF
                                POP     BC
                                DJNZ    .SPRITE

Voilà nous avons terminé et notre sprite se déplace joyeusement.

resultat

Vous devriez obtenir quelque chose de ce style.

X

CONNEXION




Inscription