MSX Village forum

La Place des Développeurs MSXgl MSX Game Library

z80 Membre non connecté

Villageois

Rang

Avatar

Inscrit le : 17/05/2013 à 22h52

Messages: 956

Le 05/11/2023 à 13h14

Reprise du message précédent

Il est modeste, le "flip" des bits dans un octet est déjà en assembleur :siffle ;)

7 6 5 4 3 2 1 0 les bits sont dans "l'ordre"
0 1 2 3 4 5 6 7 les bits sont "flippés" Edité par z80 Le 05/11/2023 à 13h17


TurboR GT (1Mo), CANON V20! ( en kit, modif 2+ en cours) :top
Pas encore retrouvés: V9990, Grafx9000, SUNRISE IDE 2x CF, SUNRISE MOONSOUND, FM PAC, MUSIC MODULE, NMS8280, SD SNATCHER,... :\'(
   
aoineko Membre non connecté

Conseiller Municipal

Rang

Avatar

Groupe : Shoutbox

Inscrit le : 02/01/2011 à 21h17

Messages: 2654

Le 05/11/2023 à 16h02
Effectivement. :) C'est une ancienne fonction que j'avais zappé.

C'est sur que la même chose en C serait très lent.


On est toujours ignorant avant de savoir.
Github    
ericb59 Membre non connecté

Conseiller Municipal

Rang

Avatar

Groupe : compte ++ Groupe : Shoutbox

Inscrit le : 17/04/2012 à 10h25

Messages: 5464

Le 05/11/2023 à 16h52
Flip et Rotate sont des functions que j'avais aussi intégré dans Fusion-C 2.0.
J'ai fait en sorte qu'elles puissent s'appliquer à n'importe quel pattern 8x8 en RAM ou en VRAM.
J'ai pas regardé ton code, mais je suis certain qu'il est plus rapide que le mien, sans aucun doutes :siffle


banniere-ericb59e
Site web    
z80 Membre non connecté

Villageois

Rang

Avatar

Inscrit le : 17/05/2013 à 22h52

Messages: 956

Le 06/11/2023 à 11h41
aoineko :
Effectivement. :) C'est une ancienne fonction que j'avais zappé.

C'est sur que la même chose en C serait très lent.


Serais-tu intéressé par une version commentée de cette partie en assembleur? :siffle

Une explication détaillée de ce qu'il se passe aux niveaux des bits, ligne par ligne. :fou :top

Et puis honnêtement ça serait un bon exercice pour moi de participer à un projet GIT :p


TurboR GT (1Mo), CANON V20! ( en kit, modif 2+ en cours) :top
Pas encore retrouvés: V9990, Grafx9000, SUNRISE IDE 2x CF, SUNRISE MOONSOUND, FM PAC, MUSIC MODULE, NMS8280, SD SNATCHER,... :\'(
   
ericb59 Membre non connecté

Conseiller Municipal

Rang

Avatar

Groupe : compte ++ Groupe : Shoutbox

Inscrit le : 17/04/2012 à 10h25

Messages: 5464

Le 06/11/2023 à 12h38
Je serais curieux d'avoir les explication et le code d'un pro du Z80 comme notre ami Z80 :)

Juste pour comparaison voici les routines que j'ai utilisé dans fusion-c 2.0

Pattern Horizontal Flip
Code C :
 
//-----------------------------------------------------------------------------
// Pattern Horizontal Flip. : Flip horizontaly a 8x8 Pixels pattern
// *pattern : Address of the 8x8 Pattern
// *buffer  : address of a 8 Bytes buffer
void PatternFlipH (void *Pattern, void *buffer) __naked  __sdcccall(0)  
{
 Pattern,buffer;
    __asm
 
                ;--------------------------------------------
                ;Flip horizontaly 8x8 Pattern 
                ;input:
                ;DE = 8x8 Pattern
                ;HL = 8 bytes buffer
                ;
                ; Fast byte Fliping routine by John Metcaff
                ;
                Push ix
                ld ix,#0
                add ix,sp
                ld e,4(ix)
                ld d,5(ix)
                ld l,6(ix)
                ld h,7(ix)
                pop ix
                ld b,#8
            Hloop:
               push hl
               ld a,(de)
               ld l,a
               rlca
               rlca
               xor l
               and #0xAA
               xor l
               ld l,a
               rlca
               rlca
               rlca
               rrc l
               xor l 
               and #0x66 
               xor l
 
               pop hl
               ld (hl),a
               inc hl
               inc de
               djnz Hloop
 
               ret
 
    __endasm;
}
 



Pattern Vertical Flip
Code C :
 
 
//-----------------------------------------------------------------------------
// Pattern Vertical Flip. : Flip vertically a 8x8 Pixels pattern
// *pattern : Address of the 8x8 Pattern
// *buffer  : address of a 8 Bytes buffer
void PatternFlipV (void *Pattern, void *buffer) __naked __sdcccall(0)
{
 Pattern,buffer;
    __asm
 
                Push ix
                ld ix,#0
                add ix,sp
                ld e,4(ix)
                ld d,5(ix)
                ld l,6(ix)
                ld h,7(ix)
                pop ix
                ld bc,#7
                add hl,bc
                ld b,#8
            Vloop:
               ld a,(de)
               ld (hl),a
               inc de 
               dec hl
 
               djnz Vloop
 
               ret
 
    __endasm;
}
 


Pattern Rotation 90 degrés

Code C :
 
 
//-----------------------------------------------------------------------------
// Pattern Rotation : Rotate a 8x8 pixels pattern to 90° angles, right or left
// *pattern : Address of the 8x8 Pattern
// *buffer  : address of a 8 Bytes buffer
// direction : // 0 : Rotate to right    1: rotate to Left
 
void PatternRotation (void *Pattern, void *buffer, char rotation) __naked  __sdcccall(0)
{
    Pattern,buffer,rotation;
    __asm
           ;--------------------------------------------
            ;Rotate 8x8 Pattern 
            ;input:
            ;DE = 8x8 Pattern address  
            ;HL = 8 bytes buffer
            ;A   =  0 Clockwise (R)
            ;       1 equals Counter Clockwise (L)
            ;
            ;Output:
            ; Rotates a Pattern 90° left or right into a buffer
            ;
 
                Push ix
                ld ix,#0
                add ix,sp
                ld e,4(ix)
                ld d,5(ix)
                ld l,6(ix)
                ld h,7(ix)
                ld a,8(ix)
                pop ix
                ld c,#8
                and #1
 
                jr  z,rotbigloopR        ; If direction is right (=0)
 
            ; Left Rotation
            rotbigloopL:
               ld a,(de)
               inc de
               push hl
               ld b,#8
            rotloopL:
               rra
               rl (hl)
               inc hl
               djnz rotloopL
               pop hl
               dec c
               jr nz,rotbigloopL
               ret
 
            ; Right Rotation 
            rotbigloopR:
               ld a,(de)
               inc de
               push hl
               ld b,#8
            rotloopR:
               rla
               rr (hl)
               inc hl
               djnz rotloopR
               pop hl
               dec c
               jr nz,rotbigloopR
               ret
 
    __endasm;
}
 


banniere-ericb59e
Site web    
aoineko Membre non connecté

Conseiller Municipal

Rang

Avatar

Groupe : Shoutbox

Inscrit le : 02/01/2011 à 21h17

Messages: 2654

Le 06/11/2023 à 13h28
Les fonctions en assembleur semble bien optimisée, en dehors du passage des paramètres qui se fait via la pile alors qu'avec la nouvelle convention d’appel de SDCC ils pourraient être tous passé par registre.

Ca donnerait ça :
Code C :
void PatternFlipV (const void *Pattern, void *Buffer)
{
    Pattern; // -> HL
    Buffer;  // -> DE
    __asm
        ld bc,#7
        add hl,bc
        ld b,#8
    Vloop:
        ld a,(de)
        ld (hl),a
        inc de 
        dec hl
         djnz Vloop
    __endasm;
}


Les fonctions ne concernent également que les sprites 8x8.
On peut pourraient avoir des fonctions pour les sprites 16x16 qui appellent 4 fois les fonctions sur les 8x8, mais on perdrait un peu en performance. Edité par aoineko Le 06/11/2023 à 13h40


On est toujours ignorant avant de savoir.
Github    
z80 Membre non connecté

Villageois

Rang

Avatar

Inscrit le : 17/05/2013 à 22h52

Messages: 956

Le 06/11/2023 à 14h20
ericb59 :
Je serais curieux d'avoir les explication et le code d'un pro du Z80 comme notre ami Z80 :)

Juste pour comparaison voici les routines que j'ai utilisé dans fusion-c 2.0

Pattern Horizontal Flip
Code C :
 
               ld l,a
               rlca
               rlca
               xor l
               and #0xAA
               xor l
               ld l,a
               rlca
               rlca
               rlca
               rrc l
               xor l 
               and #0x66 
               xor l
}
 


Je note que vous avez tous les deux la même source internet ;)
C'est les commentaires pour cette section dont je parle dans un premier temps.

Dans MSXgl c'est dans MSXgl/engine/src/sprite_tool.c et c'est la fonction :
//-----------------------------------------------------------------------------
// Flip 8 bits value
u8 Sprite_Flip(u8 val) __PRESERVES(c, d, e, h, l, iyl, iyh) Edité par z80 Le 06/11/2023 à 14h22


TurboR GT (1Mo), CANON V20! ( en kit, modif 2+ en cours) :top
Pas encore retrouvés: V9990, Grafx9000, SUNRISE IDE 2x CF, SUNRISE MOONSOUND, FM PAC, MUSIC MODULE, NMS8280, SD SNATCHER,... :\'(
   
ericb59 Membre non connecté

Conseiller Municipal

Rang

Avatar

Groupe : compte ++ Groupe : Shoutbox

Inscrit le : 17/04/2012 à 10h25

Messages: 5464

Le 06/11/2023 à 18h04
aoineko :
en dehors du passage des paramètres qui se fait via la pile alors qu'avec la nouvelle convention d’appel de SDCC ils pourraient être tous passé par registre.


Oui effectivement, ces fonctions n'étant pas stratégiques pour moi, je ne les aient pas encore converties au nouveau passage de paramètres.


aoineko :
Les fonctions ne concernent également que les sprites 8x8.

J'ai créé des routines en C pour les patterns en 16x16, qui utilisent les routines assembleur ci-dessus.
D'ailleurs je me suis bien pris la tête pour le replacement des patterns 8x8 dans un sprite 16x16 qui fait une rotation ou un flip ! J'ai trouvé une solution mais il y en a sans doute une meilleures...

Rappel un sprite 16x16 est composé de 4 patterns 8x8 dans cet ordre
AC
BD



Je vous laisse juge ...

Socle commun
Code C :
 
#ifndef  __PATTERN_TRANSFORM_H__
#define  __PATTERN_TRANSFORM_H__ 1
// Used to replace 8x8 patterns in a 16x16 sprite
const char shift[4][4]={{16,0,24,8},{8,24,0,16},{16,24,0,8},{8,0,24,16}};
unsigned char __at(0xF41F) TempBuffer[32];
unsigned char __at(0xF41F+32) DoBuffer[32];
unsigned char rot;
unsigned char i;
#ifdef BASE_PATTERN_ADRESS
#define FUSION_PATTERN_ADDRESS  BASE_PATTERN_ADRESS
#else
#define FUSION_PATTERN_ADDRESS  Peekw( 0xF926 )
#endif
//
void    PatternRotation(void *Pattern, void *buffer, char rotation);    // 8x8 Pattern rotation. Used for the Rotation sprite pattern's function (
void    PatternFlipH(void *Pattern, void *buffer) __sdcccall(0);                      // Flips Horizontally a 8x8 pixels pattern
void    PatternFlipV(void *Pattern, void *buffer) __sdcccall(0);                      // Flips Vertically a 8x8 pixels pattern
void    Pattern16RotationVram(char pattern,  char DestPattern, char rotation);
void    Pattern16FlipVram(char pattern, char DestPattern, char direction);
void    Pattern8RotationVram(char pattern,  char DestPattern, char rotation);
void    Pattern8FlipVram(char pattern, char DestPattern,char direction);
void    Pattern8RotationRam(void *SrcAddr, void *DestAddr, char rotation);
void    Pattern8FlipRam(void *SrcAddr, void *DestAddr, char direction);
#endif
 




Pattern 16x16 Rotation en VRAM
Code C :
 
void Pattern16RotationVram(char pattern,  char DestPattern, char rotation)
{
   CopyVramToRam(FUSION_PATTERN_ADDRESS + (pattern << 3), &TempBuffer, 32); 
 
       for (int i = 0; i < 4; ++i)
       {
          PatternRotation (TempBuffer+i*8,    DoBuffer+shift[rotation][i], rotation);   
       }
 
       if (!DestPattern)
       {
       DestPattern=pattern;
       }
       CopyRamToVramNI(&DoBuffer, FUSION_PATTERN_ADDRESS + (DestPattern << 3), 32);     // Copy FlipBuffer to VRAM
       EnableInterrupt();
}
 


Pattern 16x16 Flip en VRAM
Code C :
 
void Pattern16FlipVram(char pattern,  char DestPattern, char direction)  
{
          CopyVramToRam(FUSION_PATTERN_ADDRESS + (pattern << 3), &TempBuffer, 32); 
          if (direction==0)
          {
            for (char i = 0; i < 4; ++i)
            {
                PatternFlipH (TempBuffer+i*8,  DoBuffer+shift[2][i]);   
            }
          }
          else
          {
            for (char i = 0; i < 4; ++i)
            {
               PatternFlipV (TempBuffer+i*8,  DoBuffer+shift[3][i]);  
            }
          }
 
          if (DestPattern!=0)
          {
              pattern=DestPattern;
          }
 
          CopyRamToVramNI(&DoBuffer, FUSION_PATTERN_ADDRESS + (pattern << 3), 32);     // Copy FlipBuffer to VRAM
          EnableInterrupt();
}
 

Edité par ericb59 Le 06/11/2023 à 18h07


banniere-ericb59e
Site web    
aoineko Membre non connecté

Conseiller Municipal

Rang

Avatar

Groupe : Shoutbox

Inscrit le : 02/01/2011 à 21h17

Messages: 2654

Le 06/11/2023 à 19h58
z80 :
Je note que vous avez tous les deux la même source internet ;)


C'est parce qu'on a le même informateur. Un certain Paul Google. :glass


On est toujours ignorant avant de savoir.
Github    
z80 Membre non connecté

Villageois

Rang

Avatar

Inscrit le : 17/05/2013 à 22h52

Messages: 956

Le 06/11/2023 à 20h42
aoineko :
z80 :
Je note que vous avez tous les deux la même source internet ;)


C'est parce qu'on a le même informateur. Un certain Paul Google. :glass


C'est bien à cette conclusion que j'étais arrivé en faisant moi même la même recherche :p

Il est vrai que l'optimisation en temps CPU est impressionnante parce que avec seulement le double de taille en octet du code assembleur on obtient une division du temps CPU d'un facteur 3,12!

Le mec précise ou du moins laisse entendre qu'après avoir écrit sa première version, il s'est posé la question de comment gagner "quelques" cycle CPU...
Et c'est son analyse statistique sur le nombre de décalages nécessaire à chaque bit pour atteindre sa position finale. Et ça c'est plutôt brillant. :top Edité par z80 Le 06/11/2023 à 20h43


TurboR GT (1Mo), CANON V20! ( en kit, modif 2+ en cours) :top
Pas encore retrouvés: V9990, Grafx9000, SUNRISE IDE 2x CF, SUNRISE MOONSOUND, FM PAC, MUSIC MODULE, NMS8280, SD SNATCHER,... :\'(
   
aoineko Membre non connecté

Conseiller Municipal

Rang

Avatar

Groupe : Shoutbox

Inscrit le : 02/01/2011 à 21h17

Messages: 2654

Le 07/11/2023 à 01h06
z80 :
Il est vrai que l'optimisation en temps CPU est impressionnante parce que avec seulement le double de taille en octet du code assembleur on obtient une division du temps CPU d'un facteur 3,12!


Son algorithme est vraiment super performant. J'adore le and #0x66 qui fait très magie noire. ^^

ericb59 :
J'ai pas regardé ton code, mais je suis certain qu'il est plus rapide que le mien, sans aucun doutes :siffle


Les fonctions en assembleur Flip et Rotate que tu utilises sont très bien. Je les d'ailleurs utilisées comme base pour les miennes. En fait, j'ai surtout retiré le surcout lié à la gestion du passage des paramètres par la pile et j'ai pu retirer une pair de push/pop qui faisait perdre 23 cycles à chaque itération sur les Flip. J'ai créé aussi des fonctions pour l'effet de Mask dont je suis assez fier (pas facile de gérer 3 pointers 16 bits en assembleur dans une boucle de façon performante). Il ne me reste plus que les fonctions Crop à convertir en assembleur (ça sera pour demain).
https://github.com/aoineko-fr/MSXgl/blob/main/engine/src/sprite_tool.c
Tu pourras évidemment récupérer tout ça pour Fusion-C. :top Edité par aoineko Le 07/11/2023 à 01h13


On est toujours ignorant avant de savoir.
Github    
z80 Membre non connecté

Villageois

Rang

Avatar

Inscrit le : 17/05/2013 à 22h52

Messages: 956

Le 07/11/2023 à 08h08
aoineko :
z80 :
Il est vrai que l'optimisation en temps CPU est impressionnante parce que avec seulement le double de taille en octet du code assembleur on obtient une division du temps CPU d'un facteur 3,12!


Son algorithme est vraiment super performant. J'adore le and #0x66 qui fait très magie noire. ^^


En en fait les AND 0xAA et 0X66, ça va, mais le trio:
xor l
and 0xAA
xor l

Là, c'est de la bombe BB! ;)
Vous comprendrez mieux quand vous aurez les commentaires.

Vous avez peut-être noté que sa version optimisée utilise TRES majoritairement des instructions dont l'OP Code tient sur un unique octet (optimisation des T cycles en réduisant les accès mémoires) sauf pour les AND ou il n'a pas le choix de deux octets.


TurboR GT (1Mo), CANON V20! ( en kit, modif 2+ en cours) :top
Pas encore retrouvés: V9990, Grafx9000, SUNRISE IDE 2x CF, SUNRISE MOONSOUND, FM PAC, MUSIC MODULE, NMS8280, SD SNATCHER,... :\'(
   
aoineko Membre non connecté

Conseiller Municipal

Rang

Avatar

Groupe : Shoutbox

Inscrit le : 02/01/2011 à 21h17

Messages: 2654

Le 08/11/2023 à 08h19
Voilà, j'ai fini de passer toutes les fonctions en assembleur !
J'en ai profité pour renommer mon module qui s'appelle maintenant : Sprite FX.
Y a peut-être encore quelques cycles à gagner ici et là, mais c'est déjà bien plus rapide que la version C qui était déjà relativement rapide. :)
Si les amateurs d'assembleurs voient des améliorations possible, je suis preneur : https://github.com/aoineko-fr/MSXgl/blob/main/engine/src/sprite_fx.c
Pour ces fonctions, je privilégie la vitesse à la taille ; enfin, jusqu'à un certain niveau. Je n'ai pas été jusqu'à "déroulé" (unroll) les boucles.


On est toujours ignorant avant de savoir.
Github    
aoineko Membre non connecté

Conseiller Municipal

Rang

Avatar

Groupe : Shoutbox

Inscrit le : 02/01/2011 à 21h17

Messages: 2654

Le 31/12/2023 à 16h02
@ericb59 un utilisateur de MSXgl a porté mon outil de conversion d'image MSXimg sur Linux: https://github.com/aoineko-fr/MSXgl/tree/main/tools/MSXtk/bin
Ca aura mis le temps, mais toute la suite d'outils est (enfin) compatible Linux. ^^
Pour macOS, il suffit de les recompiler j'imagine. Edité par aoineko Le 31/12/2023 à 16h02


On est toujours ignorant avant de savoir.
Github    
aoineko Membre non connecté

Conseiller Municipal

Rang

Avatar

Groupe : Shoutbox

Inscrit le : 02/01/2011 à 21h17

Messages: 2654

Le 16/01/2024 à 23h59
Hello,

Il y a 2 ans jour pour jour, sortais officiellement la première version de MSXgl, ma librairie pour créer des jeux MSX en langage C.

Apres la sortie des premiers jeux basés sur MSXgl en 2023, plein de nouvelles fonctionnalités et un gros travail de clean et de documentation, je considère que la librairie est maintenant suffisamment complète et stable pour passer son numéro de version en 1.0.0 ! :)

Merci à tous ceux qui m'ont aidé ici à mieux comprendre le fonctionnement du MSX et à pouvoir créer du code qui exploite au mieux ses capacités.

La librairie et tous les outils nécessaires pour créer un programme final en ROM ou en disquette, sont disponible sur le GitHub du projet.

Pour en savoir plus :
- Documentation Wiki
- Documentation du code source
- Serveur Discord (avec plein de développeurs sympas toujours prêt à aider)

Et pour la suite, j'ai encore plein de chose à ajouter (notamment de nouveaux formats de musique) ou à optimiser.
Y a aussi pas mal de projets en cours donc on verra en fonction des besoins des uns et des autres.
J'espère qu'il y a encore plein de nouveaux jeux MSX à venir !

:tea Edité par aoineko Le 17/01/2024 à 01h51


On est toujours ignorant avant de savoir.
Github    
Franck Membre non connecté

Maire-adjoint

Rang

Avatar

Association

Inscrit le : 02/10/2009 à 22h54

Messages: 3279

Le 17/01/2024 à 07h28
Gloire te soit rendue d’avoir développé un outil qui facilite la sortie de futures bonnes choses :top
   
Répondre
Vous n'êtes pas autorisé à écrire dans cette catégorie