La Place des Développeurs Projet GOS Et oui, encore trop d'ambitions ^^
aoineko
Membre non connecté
Conseiller Municipal
(@Eric, je me permets qq rappels de base au cas où d’autres seraient intéressés)
Accrochez vos ceintures, c’est parti !
Donc, le cœur du problème c’est la fonction qui décode le dessin d’un caractère pour le Screen mode 8, et l’envoi en VRAM pour l’afficher à l’écran ( PutChar_G7 dans ma lib).
Par défaut, les dessins d’un caractère font 8 points de large sur 8 points de haut et sont stockés sous forme binaire (1 : plein, 0 : transparent). Le dessin d’un caractère est donc composé de 8 octets représentant chaque ligne, et chacun des 8 bits de ces octets représentant 1 point dans la largeur.
Le Screen mode 8, lui, stock les images sous la forme : 1 point = 1 octet (les 256 valeurs possibles représentent la couleur du point au format Vert-Rouge-Bleu)
Le but de la fonction PutChar_G7() est donc de transformer chaque bit à 1 dans le dessin du caractère en un octet contenant la couleur du texte à afficher, et chaque bit à 0 en un octet contenant la couleur de fond.
Approche basique du problème :
Ce qui donne en C :
Cela fonctionne, mais c’est très lent pour plusieurs raisons :
- Beaucoup de calculs à faire pour chaque point du dessin du caractère
- Les boucles imbriquées coûtent cher en C (et même en assembleur)
- Envoyer les données en VRAM une-à-une n’est pas optimal (en fait, dans le cas présent, le CPU passe tellement de temps à décoder le dessin du caractère que les temps d’écriture dans la VRAM ne sont pas vraiment problématiques).
L’idée est donc de :
- Pré-calculer tout ce qui peut l’être
- Dérouler la deuxième boucle (celle qui passe en revue chaque bit d’une ligne)
Pour les pré-calculs, je vais pas rentrer dans le détail car c’est assez simple : toutes les valeurs qui ne dépendent pas du caractère à afficher devrait être calculé une seule fois qq part (dans une fonction d’initialisation par ex.) et stockées en RAM.
Là ou ça devient amusant, c’est le "déroulage" de la deuxième boucle.
Une approche basique serait de remplacer la boucle de 8 itérations par 8 tests consécutifs avec des valeurs de bit fixes (0, 1, 2, … 7).
C’est déjà mieux car on élimine la boucle imbriquée et les valeurs fixes sont bien mieux optimisées par le compilateur... mais on peut faire encore beaucoup mieux.
Au lieu de prendre les bits d’une ligne 1 par 1, prenons les 2 à 2.
Déjà, on va se retrouver avec 4 traitements à faire au lieu de 8 (ce qui est déjà mieux).
Mais surtout, avec 2 bits, on a 4 combinaisons possibles ([0,0], [0,1], [1,0], [1,1]) qu’on va pouvoir utiliser comme des valeurs de 0 à 3.
En utilisant ces valeurs comme index pour récupérer les données de couleur dans un tableau pré-rempli, on élimine tous les tests conditionnels et on réduit fortement les calculs.
Le tableau (à ré-initialiser dès qu’on change la couleur du texte) contient 4 valeurs de 16-bits représentants les 2 couleurs consécutives des 4 combinaisons possibles :
Et voilà !
Maintenant, on a une fonction 4~5 fois plus rapide.
Le léger surcoût sur la fonction de changement de couleur est largement compensé par le gain sur l’affichage d’un caractère (surtout qu’il est rare qu’on change de couleur très souvent dans un texte).
On peut aller encore plus loin si on veut :
- Y a plein de petits détails à optimiser
- La font (police de caractère) contenu dans la Main-ROM ayant une taille de 6 points de large sur 8 points de haut, on a besoin que de 3 traitements de 2-bits par ligne.
- On pourrait aussi traiter les lignes par 3 bits (dans ce cas, il faut précalculer et stocker 8 valeurs de 24-bits)
- On pourrait aussi le faire par 4 bits (16 valeurs de 32-bits… ce qui commence à faire beaucoup)
Voilà, j'espère que c'est plus clair.
Accrochez vos ceintures, c’est parti !
Donc, le cœur du problème c’est la fonction qui décode le dessin d’un caractère pour le Screen mode 8, et l’envoi en VRAM pour l’afficher à l’écran ( PutChar_G7 dans ma lib).
Par défaut, les dessins d’un caractère font 8 points de large sur 8 points de haut et sont stockés sous forme binaire (1 : plein, 0 : transparent). Le dessin d’un caractère est donc composé de 8 octets représentant chaque ligne, et chacun des 8 bits de ces octets représentant 1 point dans la largeur.
Le Screen mode 8, lui, stock les images sous la forme : 1 point = 1 octet (les 256 valeurs possibles représentent la couleur du point au format Vert-Rouge-Bleu)
Le but de la fonction PutChar_G7() est donc de transformer chaque bit à 1 dans le dessin du caractère en un octet contenant la couleur du texte à afficher, et chaque bit à 0 en un octet contenant la couleur de fond.
Approche basique du problème :
Code TEXT :
- Pour un caractère donné (prenons 'A' comme ex.) : - Trouver l’adresse de son dessin (le caractère 'A' a le code ASCII 65, son dessin est donc le 65-ième dans le tableau) - Pour chaque ligne du dessin (Y allant de 0 à 7) : - Récupérer l’octet de la ligne correspondant (L) - Pour chaque colonne du dessin (X allant de 0 à 7) : - Déterminer l’adresse où envoyer les données en VRAM (en fonction de la position où afficher 'A') - Si le X-ième bit de L vaut 1, envoyer l’octet de la couleur du texte en VRAM - Si le X-ième bit de L vaut 0, envoyer l’octet de la couleur de fond en VRAM (ou ne rien faire pour la transparence)
Ce qui donne en C :
Code C :
void PutChar_G7(u8 chr) { const u8* form = g_PrintData.FontAddr + chr * 8; // character form address u16 addr = (g_PrintData.CursorY * 256) + g_PrintData.CursorX; // vram destination address for(i8 y = 0; y < 8; y++) // lines { for(i8 x = 0; x < 8; x++) // columns { if(form[y] & (1 << (7 – x))) // check if the x-th bit is 0 g_PrintData.Buffer[i] = g_PrintData.TextColor; else g_PrintData.Buffer[i] = g_PrintData.BackgroundColor; } VDP_WriteVRAM(g_PrintData.Buffer, addr, 0, 8); // draw 8-bytes addr += 256; } }
Cela fonctionne, mais c’est très lent pour plusieurs raisons :
- Beaucoup de calculs à faire pour chaque point du dessin du caractère
- Les boucles imbriquées coûtent cher en C (et même en assembleur)
- Envoyer les données en VRAM une-à-une n’est pas optimal (en fait, dans le cas présent, le CPU passe tellement de temps à décoder le dessin du caractère que les temps d’écriture dans la VRAM ne sont pas vraiment problématiques).
L’idée est donc de :
- Pré-calculer tout ce qui peut l’être
- Dérouler la deuxième boucle (celle qui passe en revue chaque bit d’une ligne)
Pour les pré-calculs, je vais pas rentrer dans le détail car c’est assez simple : toutes les valeurs qui ne dépendent pas du caractère à afficher devrait être calculé une seule fois qq part (dans une fonction d’initialisation par ex.) et stockées en RAM.
Là ou ça devient amusant, c’est le "déroulage" de la deuxième boucle.
Une approche basique serait de remplacer la boucle de 8 itérations par 8 tests consécutifs avec des valeurs de bit fixes (0, 1, 2, … 7).
C’est déjà mieux car on élimine la boucle imbriquée et les valeurs fixes sont bien mieux optimisées par le compilateur... mais on peut faire encore beaucoup mieux.
Au lieu de prendre les bits d’une ligne 1 par 1, prenons les 2 à 2.
Déjà, on va se retrouver avec 4 traitements à faire au lieu de 8 (ce qui est déjà mieux).
Mais surtout, avec 2 bits, on a 4 combinaisons possibles ([0,0], [0,1], [1,0], [1,1]) qu’on va pouvoir utiliser comme des valeurs de 0 à 3.
En utilisant ces valeurs comme index pour récupérer les données de couleur dans un tableau pré-rempli, on élimine tous les tests conditionnels et on réduit fortement les calculs.
Le tableau (à ré-initialiser dès qu’on change la couleur du texte) contient 4 valeurs de 16-bits représentants les 2 couleurs consécutives des 4 combinaisons possibles :
Code C :
tab[0] = (bgColor << 8) + bgColor ; // [0,0] tab[1] = (textColor << 8) + bgColor ; // [0,1] tab[2] = (bgColor << 8) + textColor ; // [1,0] tab[3] = (textColor << 8) + textColor ; // [1,1]
Et voilà !
Maintenant, on a une fonction 4~5 fois plus rapide.
Le léger surcoût sur la fonction de changement de couleur est largement compensé par le gain sur l’affichage d’un caractère (surtout qu’il est rare qu’on change de couleur très souvent dans un texte).
On peut aller encore plus loin si on veut :
- Y a plein de petits détails à optimiser
- La font (police de caractère) contenu dans la Main-ROM ayant une taille de 6 points de large sur 8 points de haut, on a besoin que de 3 traitements de 2-bits par ligne.
- On pourrait aussi traiter les lignes par 3 bits (dans ce cas, il faut précalculer et stocker 8 valeurs de 24-bits)
- On pourrait aussi le faire par 4 bits (16 valeurs de 32-bits… ce qui commence à faire beaucoup)
Voilà, j'espère que c'est plus clair.
On est toujours ignorant avant de savoir.
ericb59
Membre non connecté
Conseiller Municipal
Alors j'ai bossé la dessus une bonne partie de la journée l'air de rien !
J'en ai mal aux yeux
Avec une méthode "traditionnelle" d'analyse des bits, j'étais tombé à 48 Cycles (Screen8, 50Hz).
Ton explication sur la méthode de scinder l'octet en 4 pairs de bits est très claire, et d'y associer un tableau de couleurs, c'est très malin en effet.
J'ai voulu faire ça à ma manière, mais je me suis cassé les dents pour faire rentrer des "INT" dans un tableau de "Char".
Hors de question d'utiliser un "Itoa" qui ralentirait le bouzin...
Alors j'ai repomper ton code
Et utilisé les pointeurs comme tu le fais. Les pointeurs c'est quelques chose que j'utilise de façon basique, et là, je dois dire, que je ne pige pas la logique, et la façon dont ca fonctionne. Mais ça le fait ... donc ...
Alors maintenant je suis à 32 Cycles.
Le traitement des données prend 16 Cycles, la copie en VRAM 16 Cycles.
Je ne sais pas comment tu fais pour arriver encore plus bas !
J'en ai mal aux yeux
Avec une méthode "traditionnelle" d'analyse des bits, j'étais tombé à 48 Cycles (Screen8, 50Hz).
Ton explication sur la méthode de scinder l'octet en 4 pairs de bits est très claire, et d'y associer un tableau de couleurs, c'est très malin en effet.
J'ai voulu faire ça à ma manière, mais je me suis cassé les dents pour faire rentrer des "INT" dans un tableau de "Char".
Hors de question d'utiliser un "Itoa" qui ralentirait le bouzin...
Alors j'ai repomper ton code
Et utilisé les pointeurs comme tu le fais. Les pointeurs c'est quelques chose que j'utilise de façon basique, et là, je dois dire, que je ne pige pas la logique, et la façon dont ca fonctionne. Mais ça le fait ... donc ...
Alors maintenant je suis à 32 Cycles.
Le traitement des données prend 16 Cycles, la copie en VRAM 16 Cycles.
Je ne sais pas comment tu fais pour arriver encore plus bas !
aoineko
Membre non connecté
Conseiller Municipal
Passer de 32 à 19 frames, ça va se jouer dans les détails... et là c'est très compliqué de te dire quoi changer sans avoir un outil de profiling (qui nous dirait où le CPU passe son temps).
Y a des optims globales : utiliser les FastCall, des variables uint8, éviter les boucles imbriquées, pre-incrémenter, dérouler les otir, etc.
Chaque ligne de code que j'écris est souvent pensé pour être la plus rapide possible.
Y a aussi un truc un peu tricky que j'ai choisi de garder mais qui risque de pas te plaire , c'est que je n'attends pas la libération du VDP avant d'envoyer mes commandes pour les prints.
J'ai calculé que le temps de traitement CPU est supérieur au temps d'envoie des données en VRAM donc pas besoin de check l'état du VDP entre les envois de caractère.
Par contre, j'avais un bug quand j'exécutais une grosse commande VDP juste avant un Print (le 1er caractère n'apparaissait pas entièrement).
Dans cas, je demande à l'utilisateur (moi pour le moment ) d'ajouter un VDP_WaitReady() à la main pour corriger le problème.
On pourrait l'ajouter au début du Print comme ça l'attente se fait par chaine de caractère et pas par caractère, mais perso je privilégie la rapidité et la flexibilité à la sécurité.
Tu es passé de 197 frames à 32... c'est déjà pas mal.
Y a des optims globales : utiliser les FastCall, des variables uint8, éviter les boucles imbriquées, pre-incrémenter, dérouler les otir, etc.
Chaque ligne de code que j'écris est souvent pensé pour être la plus rapide possible.
Y a aussi un truc un peu tricky que j'ai choisi de garder mais qui risque de pas te plaire , c'est que je n'attends pas la libération du VDP avant d'envoyer mes commandes pour les prints.
J'ai calculé que le temps de traitement CPU est supérieur au temps d'envoie des données en VRAM donc pas besoin de check l'état du VDP entre les envois de caractère.
Par contre, j'avais un bug quand j'exécutais une grosse commande VDP juste avant un Print (le 1er caractère n'apparaissait pas entièrement).
Dans cas, je demande à l'utilisateur (moi pour le moment ) d'ajouter un VDP_WaitReady() à la main pour corriger le problème.
On pourrait l'ajouter au début du Print comme ça l'attente se fait par chaine de caractère et pas par caractère, mais perso je privilégie la rapidité et la flexibilité à la sécurité.
Tu es passé de 197 frames à 32... c'est déjà pas mal.
On est toujours ignorant avant de savoir.
aoineko
Membre non connecté
Conseiller Municipal
Pour info j'ai rajouté le support du Screen 7 (512x212 en 2 bits-par-couleur).
C'est encore plus rapide (14 frames) car je gère les points 4 à 4 (pour manipuler des octets).
Par contre, il faut 16 bytes de données précalcl.
C'est commit sur GitHub si tu veux récupérer ça.
Après, même 14 frames pour afficher 7 lignes, ça reste extrêmement lent.
Encore une fois, la vraie solution serait de décoder les fonts en RAM ou en VRAM et de ne faire que des copies.
C'est encore plus rapide (14 frames) car je gère les points 4 à 4 (pour manipuler des octets).
Par contre, il faut 16 bytes de données précalcl.
C'est commit sur GitHub si tu veux récupérer ça.
Après, même 14 frames pour afficher 7 lignes, ça reste extrêmement lent.
Encore une fois, la vraie solution serait de décoder les fonts en RAM ou en VRAM et de ne faire que des copies.
On est toujours ignorant avant de savoir.
TurboSEB
Membre non connecté
Conseiller Municipal
C'est dingue ça, j'ai tout compris, enfin, dans le principe disont, même si je suis incapable synthétiser la chose, car j'ai déjà oublié le début c'est effectivement très intéressant
Bon si je comprends bien, ce "rafraîchissement bête et méchant" systématique de l'écran serait encore plus efficace si l'on pouvait optimiser le travail sur seulement la partie qui a changée, ça doit être predictible non !? Genre si c'est cette position là, on rafraîchi seulement cette ligne la Edité par TurboSEB Le 24/12/2020 à 03h59
Bon si je comprends bien, ce "rafraîchissement bête et méchant" systématique de l'écran serait encore plus efficace si l'on pouvait optimiser le travail sur seulement la partie qui a changée, ça doit être predictible non !? Genre si c'est cette position là, on rafraîchi seulement cette ligne la Edité par TurboSEB Le 24/12/2020 à 03h59
MSX 1&2 + Moniteurs+divers (environ 0.70Tonnes)
ericb59
Membre non connecté
Conseiller Municipal
@TurboSeb Malheureusement on pas de contrôle là dessus sur MSX
@aoineko : Oui, j’ai déjà fait une routine en plaçant la font en dessous de y=212 et utilisé des LMMM pour afficher le texte. C’est souvent comme ça qu’on procède depuis des millénaires
Après ça dépend des besoins en Ram ou VRAM.
Dans le Fusion-c 1.2 j’ai un exemple d’utilisation d’une font 4x4 En Ram qui s’affiche en screen2 avec des Pset.
La routine de Pset MSX1 est très très rapide, elle m’a été donnée par un généreux contributeur dont le nom m’echappe à’cet instant ... Edité par ericb59 Le 24/12/2020 à 07h01
@aoineko : Oui, j’ai déjà fait une routine en plaçant la font en dessous de y=212 et utilisé des LMMM pour afficher le texte. C’est souvent comme ça qu’on procède depuis des millénaires
Après ça dépend des besoins en Ram ou VRAM.
Dans le Fusion-c 1.2 j’ai un exemple d’utilisation d’une font 4x4 En Ram qui s’affiche en screen2 avec des Pset.
La routine de Pset MSX1 est très très rapide, elle m’a été donnée par un généreux contributeur dont le nom m’echappe à’cet instant ... Edité par ericb59 Le 24/12/2020 à 07h01
ericb59
Membre non connecté
Conseiller Municipal
@aoineki : J'ai modifié la façon de faire.
Plutôt que de stocker les couleurs Foreground/Background sous la forme d'un INT, et de devoir manipuler les pointeurs pour les mettre dans le tableau de "Char", j'utilise désormais un tableau de char double dimension pour stocker les couleurs.
Ce qui m'oblige à faire 8 assignations plutôt que 4, mais de façon simple:
Ce qui est beaucoup plus parlant pour moi
De plus, à un cycle prêt j'obtient la même vitesse (32 cycles au lieu de 31)
Un poil d'optimisation de plus, et je suis arrivé à 29 Cycles.
Tips : Dans certains cas, la boucle While est plus rapide que la boucle For Edité par ericb59 Le 24/12/2020 à 10h18
Plutôt que de stocker les couleurs Foreground/Background sous la forme d'un INT, et de devoir manipuler les pointeurs pour les mettre dans le tableau de "Char", j'utilise désormais un tableau de char double dimension pour stocker les couleurs.
Code TEXT :
char color[4][2];
Ce qui m'oblige à faire 8 assignations plutôt que 4, mais de façon simple:
Code C :
MyLetter.Mtemp[0]=MyLetter.color[n][0]; MyLetter.Mtemp[1]=MyLetter.color[n][1];
Ce qui est beaucoup plus parlant pour moi
De plus, à un cycle prêt j'obtient la même vitesse (32 cycles au lieu de 31)
Un poil d'optimisation de plus, et je suis arrivé à 29 Cycles.
Tips : Dans certains cas, la boucle While est plus rapide que la boucle For Edité par ericb59 Le 24/12/2020 à 10h18
aoineko
Membre non connecté
Conseiller Municipal
J'ai aussi testé les 2 et j'ai aussi gardé la copie octet par octet (cf. GitHub).
Par contre, c'est censé n'avoir aucun impact sur les perfs des prints.
A moins que tu set les couleurs avant chaque print ?
Par contre, c'est censé n'avoir aucun impact sur les perfs des prints.
A moins que tu set les couleurs avant chaque print ?
On est toujours ignorant avant de savoir.
igal
Membre non connecté
Conseiller Municipal
J'ai pas bien compris ce que vous essayez de dénouer mais faut dire que j'ai rdv chez l'ophtalmo qu'en février et pour le moment, j'ai beau essuyer mes verres progressifs, ca reste trouble même les lunettes au bout du nez
Bon...J'ai peur d'inventer la poudre mais sait on jamais
Si jamais toutes vos discutions tentent de résoudre le problème de priorité d'affichage des sprites (quel joueur cache l'autre), le plus simple est d'afficher "Toujours" les sprites avec la coordonnée "Y" la plus en bas de l'écran par dessus les autres!
Si c'est pas l'sujet, oubliez ce que vous avez lu....Ce n'est que le fruit de votre imagination
Bon...J'ai peur d'inventer la poudre mais sait on jamais
Si jamais toutes vos discutions tentent de résoudre le problème de priorité d'affichage des sprites (quel joueur cache l'autre), le plus simple est d'afficher "Toujours" les sprites avec la coordonnée "Y" la plus en bas de l'écran par dessus les autres!
Si c'est pas l'sujet, oubliez ce que vous avez lu....Ce n'est que le fruit de votre imagination
ericb59
Membre non connecté
Conseiller Municipal
Code C :
typedef struct { char letter; char letter_data; char x; char y; char Mtemp[8]; char color[4][2]; unsigned int letter_addr; unsigned int VramAddr; } GraphPrint; static GraphPrint MyLetter; void PrintCharBitmap(void) { char y,n; y=0; MyLetter.letter_addr=((MyLetter.letter)*8); MyLetter.VramAddr=(MyLetter.x)+(MyLetter.y)*256; while (y<8) { MyLetter.letter_data = font[MyLetter.letter_addr++]; n=MyLetter.letter_data >> 6; MyLetter.Mtemp[0]=MyLetter.color[n][0]; MyLetter.Mtemp[1]=MyLetter.color[n][1]; n=(MyLetter.letter_data >> 4) & 0b00000011; MyLetter.Mtemp[2]=MyLetter.color[n][0]; MyLetter.Mtemp[3]=MyLetter.color[n][1]; n=(MyLetter.letter_data >> 2) & 0b00000011; MyLetter.Mtemp[4]=MyLetter.color[n][0]; MyLetter.Mtemp[5]=MyLetter.color[n][1]; n=(MyLetter.letter_data) & 0b00000011; MyLetter.Mtemp[6]=MyLetter.color[n][0]; MyLetter.Mtemp[7]=MyLetter.color[n][1]; CopyRamToVram(&MyLetter.Mtemp[0], MyLetter.VramAddr, 8); MyLetter.VramAddr+=256; y++; } } void PrintBitmap(char *string, char x, char y, char ForeColor, char BackColor ) { char i=0; // Set the Precalculated color Array MyLetter.color[0][0]=BackColor; MyLetter.color[0][1]=BackColor; MyLetter.color[1][0]=BackColor; MyLetter.color[1][1]=ForeColor; MyLetter.color[2][0]=ForeColor; MyLetter.color[2][1]=BackColor; MyLetter.color[3][0]=ForeColor; MyLetter.color[3][1]=ForeColor; MyLetter.y=y; // Send Each Letter to the print Bitmap function while (string[i]) { MyLetter.letter=(string[i]-32); MyLetter.x=x+i*8; PrintCharBitmap(); i++; } }
Code C :
Edité par
ericb59
Le 24/12/2020 à 11h29
PrintBitmap(TEXT,0,20,180,1);
aoineko
Membre non connecté
Conseiller Municipal
igal :
Si jamais toutes vos discutions tentent de résoudre le problème de priorité d'affichage des sprites [...]
On essaye d'optimiser les fonctions d'affichage d'un caractère dans nos lib C respective.
(En vrai on s'amuse car au final tout ça n'est pas super utile dans le fond )
ericb59 :
[...]
Du coup, c'est normal que tu ais un surcout à chaque Print vu que de mon coté, je ne compte pas les temps de changement de couleur (qui sont fait en dehors de la fonction Print).
Faudrait voir le code ASM généré, mais j'ai pas l'impression que ton code de décodage des bits d'une ligne soit optimal (le fait que tu le fasses octet par octet en passant par des index).
Voici l'ASM de ma lib :
Code ASM :
;D:\Dev\Private\MSX\cmsx\src\print.c:339: u16* l = &g_PrintData.Buffer[8]; ;D:\Dev\Private\MSX\cmsx\src\print.c:340: *l = ((u16*)g_PrintData.Buffer)[f >> 6]; ld -3 (ix), a rlca rlca and a, #0x03 ld l, a ld h, #0x00 add hl, hl ld bc, #(_g_PrintData + 0x0015) add hl, bc ld c, (hl) inc hl ld b, (hl) ld ((_g_PrintData + 0x001d)), bc ;D:\Dev\Private\MSX\cmsx\src\print.c:341: *++l = ((u16*)g_PrintData.Buffer)[(f >> 4) & 0x03]; ld a, -3 (ix) rlca rlca rlca rlca and a, #0x0f and a, #0x03 ld l, a ld h, #0x00 add hl, hl ld bc, #(_g_PrintData + 0x0015) add hl, bc ld c, (hl) inc hl ld b, (hl) ld ((_g_PrintData + 0x001f)), bc ;D:\Dev\Private\MSX\cmsx\src\print.c:342: *++l = ((u16*)g_PrintData.Buffer)[(f >> 2) & 0x03]; ld c, -3 (ix) srl c srl c ld a, c and a, #0x03 ld l, a ld h, #0x00 add hl, hl ld bc, #(_g_PrintData + 0x0015) add hl, bc ld c, (hl) inc hl ld b, (hl) ld ((_g_PrintData + 0x0021)), bc ;D:\Dev\Private\MSX\cmsx\src\print.c:346: *++l = ((u16*)g_PrintData.Buffer)[f & 0x03]; ld a, -3 (ix) and a, #0x03 ld l, a ld h, #0x00 add hl, hl ld bc, #(_g_PrintData + 0x0015) add hl, bc ld c, (hl) inc hl ld b, (hl) ld ((_g_PrintData + 0x0023)), bc
Tu gagnerais peut-être aussi à mettre MyLetter.letter_addr et MyLetter.VramAddr dans des variables locales car tu laisses une chance au compilo de les garder dans les registres.
Dans l'état actuel, il va faire toujours lire/écrire en RAM, ce qui est plus lent (et ce ne sont pas des données utiles à conserver d'un Print à l'autre).
On est toujours ignorant avant de savoir.
ericb59
Membre non connecté
Conseiller Municipal
Citation :
Tu gagnerais peut-être aussi à mettre MyLetter.letter_addr et MyLetter.VramAddr dans des variables locales car tu laisses une chance au compilo de les garder dans les registres.
J'ai essayé ça ne change rien.
Je crois que c'est bon tel quel pour moi.
J'ai scindé la fonction de création du tableau de couleurs dans une fonction SetFontColor, ca ne me fait rien gagner non plus en terme de temps, mais c'est plus souple de cette façon.
Je viens de tester en mode Screen5, en faisant les quelques motifs qui s'imposaient...
J'ai exactement le même temps en Screen 5 qu'en screen 8 : 29 cycles
Comme pour le Bios, j'ai le même temps.
Ce qui me parait à peut prêt cohérent vu ma fonction CopyRamToVram, envoyer 4 octets ou 8 ca ne change pas grand chose...
aoineko
Membre non connecté
Conseiller Municipal
ericb59 :
Ce qui me parait à peut prêt cohérent vu ma fonction CopyRamToVram, envoyer 4 octets ou 8 ca ne change pas grand chose...
C'est surement pas loin de deux fois plus long d'envoyer 8 que 4 octets... mais comme c'est le CPU qui nous ralenti sur ce coup là, le temps d'écriture en VRAM importe peu.
On est toujours ignorant avant de savoir.
ericb59
Membre non connecté
Conseiller Municipal
Ma routine Check d’abord la vram, text si on est sur MSX1 ou pas, attends le VDP, Après tout ça 4 ou 8 octets, c’est kif-kif pour la routine à mon avis.
Répondre
Vous n'êtes pas autorisé à écrire dans cette catégorie