Revenir au plan du site


Transformation de sprites hard à la volée à partir de données uniques


Contrairement aux bornes d'arcade et consoles, les sprites hard de l'ASIC ne s'affiche que dans un seul sens. Il n'est pas possible de les tourner ou de les inverser sur un axe.

À minima deux solutions sont possibles :
- stocker tous les sprites nécéssaires
- créer des routines d'affichage qui s'occupent des transformations

La rotation de 180° est la plus triviale, on décode dans l'ordre et on écrit dans l'ordre inverse complet. Il faut donc commencer à écrire dans le dernier octet du sprite, au poids faible 255.
decrunchSpriteRotation180
; HL = source alignée sur 128 octets
; DE = destination dans l'ASIC
ld b,hi(tableDecale4)
ld e,255
.loop
repeat 8
ld a,(hl) : inc l : ld (de),a : dec e
ld c,a : ld a,(bc) : ld (de),a : if x==7 : ret z : endif : dec e
rend
jr .loop
ret

Pour une inversion en miroir, soit on inverse la lecture de la source, soit on inverse l'écriture dans la destination, sur chaque ligne. En modifiant la routine de décrunch présentée dans la page sur la [ compression delta ] , on aurait un code comme ceci :
decrunchSpriteMiroirHorizontal
; HL = source alignée sur 128 octets
; DE = destination dans l'ASIC
ld b,hi(tableDecale4)
ld e,15 ; on part de la FIN de la ligne destination
.loop
repeat 8,x
ld a,(hl) : inc l : ld (de),a : dec e
ld c,a : ld a,(bc) : ld (de),a
if x<7 : dec e : else ; on décrémente SAUF sur le dernier pixel écrit
ld a,e : add 31 : ld e,a ; on revient au point de départ, +16 pour être à la fin de la ligne suivante :)
endif
rend
; la dernière comparaison change, on ne teste plus le retour à zéro mais le dépassement de 255
jr nc,.loop
ret

Le pendant du miroir horizontal est le miroir vertical

decrunchSpriteMiroirVertical
; HL = source alignée sur 128 octets
; DE = destination dans l'ASIC
ld b,hi(tableDecale4)
ld e,240 ; on part du début de la dernière ligne destination
.loop
repeat 8,x
ld a,(hl) : inc l : ld (de),a : inc e
ld c,a : ld a,(bc) : ld (de),a
if x<7 : inc e : else ; on incrémente SAUF sur le dernier pixel écrit
ld a,e : sub 31 : ld e,a ; on revient au point de départ, -16 pour être au début de la ligne précédente :)
endif
rend
; on teste le débordement inférieur à zéro
jr nc,.loop
ret

Les deux dernières rotations sont les plus coûteuses ( presque 2400 nops contre moins de 1650 pour les autres )

Voici la rotation 90° horaire.
decrunchSpriteRotation90
; HL = source alignée sur 128 octets
; DE = destination dans l'ASIC
ld b,hi(tableDecale4)
ld e,15 ; on part de la FIN de la ligne destination
.loop
repeat 8,x
ld a,(hl) : ld c,a : inc l : ld (de),a : ld a,e : add 16 : ld e,a ; pixel du dessous
ld a,(bc) : ld (de),a
if x<7 : ld a,e : add 16 : ld e,a : else ; on passe au pixel du dessous sauf le dernier de la colonne
ld a,e : sub 1+240 : ld e,a ; on revient au point de départ, -1 pour être sur le pixel de la colonne précédente
endif
rend
; la dernière comparaison change, on teste le débordement inférieur à zéro
jr nc,.loop
ret

Et la rotation à 270° pour terminer.
decrunchSpriteRotation270
; HL = source alignée sur 128 octets
; DE = destination dans l'ASIC
ld b,hi(tableDecale4)
ld e,240 ; on part du coin en bas à gauche
.loop
repeat 8,x
ld a,(hl) : ld c,a : inc l : ld (de),a : ld a,e : sub 16 : ld e,a ; pixel du dessous
ld a,(bc) : ld (de),a
if x<7 : ld a,e : sub 16 : ld e,a : else ; on passe au pixel du dessus sauf le dernier de la colonne
ld a,e : add 1+240 : ld e,a ; on revient au point de départ, +1 pour être sur le pixel de la colonne suivante
endif
rend
; on teste le débordement à 255
jr nc,.loop
ret

Allez, on met tout ça en pratique avec l'éternel programme de test :) Il vous faudra télécharger le [ mini sprite ] pour l'assembler
startingindex 0
buildsna
SNASET CPC_TYPE,4 ; 6128+
bankset 0
org #38
ei : ret
org #100
run #100

;*** RMR2 tags ***
ASICOFF equ 0 : ROM0000 equ 0 : ROM4000 equ %01000 : ROM8000 equ %10000 : ASICON equ %11000
ROM0 equ 0 : ROM1 equ 1 : ROM2 equ 2 : ROM3 equ 3 : ROM4 equ 4 : ROM5 equ 5 : ROM6 equ 6 : ROM7 equ 7
macro RMR2 tags : ld a,{tags}+%10100000 : ld b,#7F : out (c),a : mend

ld sp,#100 ; la pile avant le code
ld bc,#7F80+%1100 : out (c),c ; MODE 0

UnlockAsic
ld bc,#BCFF
out (c),c
out (c),0
ld hl,%1001000011101010
.loop
out (c),c
ld a,h:rlca:ld h,l:ld l,a
srl c:res 3,c
and #88
or c
ld c,a
cp #4D
jr nz,.loop
ld a,#CD
out (c),a : out (c),a
RMR2 ASICON
ld hl,#FFF : ld (#6422),hl
ei

; activer 6 sprites en ratio mode 1 zoomé
ld a,%1110
ld hl,#6004 : ld b,6 : ld de,8
.setHsp ld (hl),a : add hl,de : djnz .setHsp

; il faut positionner nos sprites à minima à l'initialisation
ld hl,0 : ld bc,80
ld (#6000+8*0),hl : add hl,bc
ld (#6000+8*1),hl : add hl,bc
ld (#6000+8*2),hl : add hl,bc
ld (#6000+8*3),hl : add hl,bc
ld (#6000+8*4),hl : add hl,bc
ld (#6000+8*5),hl

ld hl,fragola : ld de,#4000 : call decrunchSprite
ld hl,fragola : ld d,#41 : call decrunchSpriteMiroirHorizontal
ld hl,fragola : ld d,#42 : call decrunchSpriteMiroirVertical
ld hl,fragola : ld d,#43 : call decrunchSpriteRotation90
ld hl,fragola : ld d,#44 : call decrunchSpriteRotation180
ld hl,fragola : ld d,#45 : call decrunchSpriteRotation270

jr $

align 128 : fragola incbin 'lemon.bin'

decrunchSpriteRotation180
; HL = source alignée sur 128 octets
; DE = destination dans l'ASIC
ld b,hi(tableDecale4)
ld e,255
.loop
repeat 8,x
ld a,(hl) : inc l : ld (de),a : dec e
ld c,a : ld a,(bc) : ld (de),a : if x==7 : ret z : endif : dec e
rend
jr .loop

decrunchSpriteMiroirHorizontal
; HL = source alignée sur 128 octets
; DE = destination dans l'ASIC
ld b,hi(tableDecale4)
ld e,15 ; on part de la FIN de la ligne destination
.loop
repeat 8,x
ld a,(hl) : inc l : ld (de),a : dec e
ld c,a : ld a,(bc) : ld (de),a
if x<7 : dec e : else ; on décrémente SAUF sur le dernier pixel écrit
ld a,e : add 31 : ld e,a ; on revient au point de départ, +16 pour être à la fin de la ligne suivante :)
endif
rend
; la dernière comparaison change, on ne teste plus le retour à zéro mais le dépassement de 255
jr nc,.loop
ret

decrunchSpriteMiroirVertical
; HL = source alignée sur 128 octets
; DE = destination dans l'ASIC
ld b,hi(tableDecale4)
ld e,240 ; on part du début de la dernière ligne destination
.loop
repeat 8,x
ld a,(hl) : inc l : ld (de),a : inc e
ld c,a : ld a,(bc) : ld (de),a
if x<7 : inc e : else ; on incrémente SAUF sur le dernier pixel écrit
ld a,e : sub 31 : ld e,a ; on revient au point de départ, -16 pour être au début de la ligne précédente :)
endif
rend
; on teste le débordement inférieur à zéro
jr nc,.loop
ret

decrunchSpriteRotation90
; HL = source alignée sur 128 octets
; DE = destination dans l'ASIC
ld b,hi(tableDecale4)
ld e,15 ; on part de la FIN de la ligne destination
.loop
repeat 8,x
ld a,(hl) : ld c,a : inc l : ld (de),a : ld a,e : add 16 : ld e,a ; pixel du dessous
ld a,(bc) : ld (de),a
if x<7 : ld a,e : add 16 : ld e,a : else ; on passe au pixel du dessous sauf le dernier de la colonne
ld a,e : sub 1+240 : ld e,a ; on revient au point de départ, -1 pour être sur le pixel de la colonne précédente
endif
rend
; la dernière comparaison change, on teste le débordement inférieur à zéro
jr nc,.loop
ret

decrunchSpriteRotation270
; HL = source alignée sur 128 octets
; DE = destination dans l'ASIC
ld b,hi(tableDecale4)
ld e,240 ; on part du coin en bas à gauche
.loop
repeat 8,x
ld a,(hl) : ld c,a : inc l : ld (de),a : ld a,e : sub 16 : ld e,a ; pixel du dessous
ld a,(bc) : ld (de),a
if x<7 : ld a,e : sub 16 : ld e,a : else ; on passe au pixel du dessus sauf le dernier de la colonne
ld a,e : add 1+240 : ld e,a ; on revient au point de départ, +1 pour être sur le pixel de la colonne suivante
endif
rend
; on teste le débordement à 255
jr nc,.loop
ret

startingindex 0
align 256
tableDecale4
repeat 256,x
defb x>>4
rend

decrunchSprite
; HL = source alignée sur 128 octets
; DE = destination dans l'ASIC
ld b,hi(tableDecale4)
.loop
repeat 8
ld a,(hl) : inc l : ld (de),a : inc e
ld c,a : ld a,(bc) : ld (de),a : inc e
rend
jr nz,.loop
ret

Vous devriez voir apparaitre dans l'ordre, le sprite d'origine, les miroirs et enfin les 3 rotations