Logicielsmoto.com

Nous sommes le 18 Avr 2024, 12:14

Heures au format UTC + 1 heure




Poster un nouveau sujet Répondre au sujet  [ 168 messages ]  Aller à la page Précédente  1, 2, 3, 4, 5 ... 12  Suivante
Auteur Message
MessagePosté: 27 Juil 2020, 07:23 
Hors ligne
M. DCMOTO

Inscription: 06 Juin 2004, 08:23
Messages: 681
Localisation: Provence (France)
Neotenien a écrit:
mais évidemment, comme ça utilisait les caractères, c'était donc beaucoup plus rapide que les dessins de pixels.

Les ordinateurs 8 bits Thomson n'ont pas de processeur graphique permettant l'affichage en mode caractère. Tout est affiché pixel par pixel. Le mode caractère n'est donc pas plus rapide. La vitesse d'affichage dépend de l'optimisation du programme.


Haut
 Profil  
Répondre en citant le message  
MessagePosté: 28 Juil 2020, 20:37 
Hors ligne

Inscription: 21 Fév 2020, 11:38
Messages: 366
Merci pour les conseils

Samuel Devulder a écrit:
Pour le transparent il ne faut pas "bloquer" une couleur, mais plutôt utiliser un masque séparé. C'est bien plus rapide. Ensuite "MOD 8" est très lent (ca utilise une division) ! Au lieu de ca, utilises les opérations bits à bits comme "AND 7" qui vaut la même chose de façon infiniment plus rapide. Similairement "DIV 8" est lent.. et est équivalent à une décalage à droite de 3 bits (le bascal a peut-être une primitive SHR, sinon ca serait un truc bien utile en asm), bien plus rapide.
Citation:
Il n'y a pas d'instruction de décalage de bit en Pascal base, de toute façons ceci est juste pour calculer la position de sprites.. Et je peux utiliser 2 autres variables pour cela, pour réduire le nombre de cycles... C'est ce que je ferai en ASM... et adapter cette fonction avec l'ajout de boucles... Je verrai si c'est inférieur à 7 secondes en Pascal compilé...

Pour la transparence avec le masque, je ne vois pas ce que tu veux dire et comment ça peut considérablement diminuer la vitesse, j'ai créé un petit algo en ASM pour afficher 2 pxl avec transparence, ça prend moins de 50 cycles.

Citation:
Mais plus globalement "4*(K MOD 8) + 600*(K DIV 8)" est une expression complexe qui même is on optimise chaque bout du "+" serait lente. Mais comme K ne varie pas sur de grosses valeurs, c'est un candidat idéal au pré-calcul! Idée: tu te crée un tableau precalc[0..49] dont la valeur precalc[k] contient les différentes valeurs de "4*(k MOD 8) + 600*(k DIV 8)" calculés une fois pour toutes au début du programme. Ca ira super, super vite!

Je ferai comme ça! Et du coup je comparerai avec la version assembleur... Je pense qu'effectivement la lenteur peut venir de là.

Merci pour les conseils!


Haut
 Profil  
Répondre en citant le message  
MessagePosté: 29 Juil 2020, 17:13 
Hors ligne

Inscription: 21 Fév 2020, 11:38
Messages: 366
Les joie des tests en Basic pour l'affichage de sprites en mode bm 16c...

J'ai réussi à créer un algo qui peut afficher environ 50 sprites 8x16 (cxl) en 55seconde en Basic, quand il n'y a pas de tests de transparence (voir ci dessous), et quand j'en ajoute un, dont une sous routine qui fait le calcul de la valeur de 2 pts (1 octet) avec ceci
Code:
200 IF (B AND 240) < 240 THEN A = (A AND 15) + (B AND 240)
201 IF (B AND 15) < 15 THEN A = (A AND 240) + (B AND 15)
204 RETURN

Ca met 2'44 soit 3 fois le temps sans transparence!! wow, l'interpréteur basic sur ce genre d'opération est hyper lent! Explication : A contient les 2 pixels présents à l'écran, B ceux du sprite. Je teste chacune des couleurs de pixel du sprite est à 15, dans ce cas, je choisis la couleur d'écran. Dans cette algorithme, je traite en rafale tous les pixels de mémoire forme avant de traiter tous ceux de mémoire couleurs (ça ne fera que 2 bascules du régistre E7C3 pour chaque sprite et pas NbPixel/2, c'est toujours ça de gagné en cycle d'horloge.

Pour la routine d'affichage de 50 sprites SANS TRANSPARENCE, c'est en utilisant les poke qui travaillent plus vite qu'avec les pset (PSET semble une routine hyper rapide, parce que c'est en assembleur alors que si on fait les peek et poke en basic, pour faire le même traitement). Donc trouver une routine en Basic qui travaille plus vite que les PSET... Ya plus qu'à voir ce que ça donne en Pascal compilé (alors que je n'ai pas encore créé de programme d'affichage de sprites sans transparence en Pascal)... Je vous tiens au courant.


Haut
 Profil  
Répondre en citant le message  
MessagePosté: 29 Juil 2020, 18:21 
Hors ligne

Inscription: 21 Aoû 2006, 09:06
Messages: 1804
Localisation: Brest
Question: A et B sont elles des variables entières ? (DEFINT A-Z pour rendre toutes les variables entières 16bits). Si non, ce sera des float sur 32bits qui sont de base plus lents et surtout qui passeront leur temps à être converti d'entier vers float et l'inverse.

Donc 1er truc: ajoute DEFINT A-Z après les CLEAR de réservation de mémoire pour voir si ca accélère quelque chose.

Ensuite on voit que le code calcule plusieurs fois B AND 240 et A AND 15. Fais le calcul une seule fois, mets le résultat dans une variable temporaire, tu gagnera en vitesse.

Ensuite le "+" entre 2 entiers fait des tests de débordement. C'est lent. Ce que tu veux faire en fait est un OR bit à bit.
Code:
..
10 DEFINT A-Z
..
200 Z=B AND 240:IF Z<240  THEN A = (A AND 15) OR Z
201 Z=B AND 15:IF Z < 15 THEN A = (A AND 240) OR Z
204 RETURN
Mais là encore tu te tapes les test des IF ce qui est lent. Je te propose d'essayer de virer ces tests avec une table précalculée. En effet regardons ce qu'on a si on note B=xy en hexa.
Code:
B  | A
---+------------------------------
xF | A=(A and 0F) or (B and F0)
Fy | A=(A and F0) or (B and 0F)
xy | A=(A and 00) or (B and FF)
              |_____NOT_____|
On voit que les 00,F0,0F et FF finaux sont reliés par une notion de NOT et ne dépendent que de la "tronche" de B. Ceci peut être exploité comme suit:
Code:
20 DIM MB(255) ' toutes les cases sont à 0, seules nous reste à remplir les cas xF et Fy
21 'PRECALC masque MB
22 FOR B=0 TO 15:MB(240+B)=240:MB(B*16+15)=15:NEXT ' résume le tableau
...
200 Z=MB(Z):A=(A AND Z) OR (B AND NOT Z):RETURN
La routine en ligne 200 est réduite à une seule ligne à présent. :)

Nota: cette version se porte très facilement en pascal. Et en ASM, si on remarque que B AND NOT Z = NOT (B OR Z):
Code:
MB rmb 256       ; à initialiser comme il faut en tenant compte du +128 ci-après
ROUT200
   LDU   #MB+128 ; astuce pour indexer comme si B était non-signé
   ANDA  B,U     ; A = A AND Z
   ORB   B,U
   NOTB          ; B = NOT(B OR Z)
   STB   ,-S     ; ici ca serait plus rapide d'avoir un accès direct-page à une variable tampon
   ORA   ,S+
   RTS

_________________
Good morning, that's a nice Tnetennba


Haut
 Profil  
Répondre en citant le message  
MessagePosté: 29 Juil 2020, 23:36 
Hors ligne

Inscription: 21 Fév 2020, 11:38
Messages: 366
Sujet: Nouveaux jeux en préparation (BASIC/Pascal Base Ass ?)

Samuel Devulder a écrit:
Question: A et B sont elles des variables entières ? (DEFINT A-Z pour rendre toutes les variables entières 16bits). Si non, ce sera des float sur 32bits qui sont de base plus lents et surtout qui passeront leur temps à être converti d'entier vers float et l'inverse.


En effet, on gagne 25% de temps de calcul en BASIC (différents tests effectués)

Citation:
Ensuite le "+" entre 2 entiers fait des tests de débordement. C'est lent. Ce que tu veux faire en fait est un OR bit à bit.
Code:
..
10 DEFINT A-Z
..
200 Z=B AND 240:IF Z<240  THEN A = (A AND 15) OR Z
201 Z=B AND 15:IF Z < 15 THEN A = (A AND 240) OR Z
204 RETURN
Mais là encore tu te tapes les test des IF ce qui est lent. Je te propose d'essayer de virer ces tests avec une table précalculée. En effet regardons ce qu'on a si on note B=xy en hexa.
Code:
B  | A
---+------------------------------
xF | A=(A and 0F) or (B and F0)
Fy | A=(A and F0) or (B and 0F)
xy | A=(A and 00) or (B and FF)
              |_____NOT_____|
On voit que les 00,F0,0F et FF finaux sont reliés par une notion de NOT et ne dépendent que de la "tronche" de B. Ceci peut être exploité comme suit:
Code:
[/quote]
Ok jusqu'ici je pige... En fat dans différent algorithme en C, PHP etc, j'utilise même un truc du genre
T = A AND 0F; U = A AND F0; A = (T==0F)*(T OR  (B AND F0) OR (U == 0F)*(U OR (B AND 0F) etc...
Mais ça ferait énormément de calcul...

C'est la suite que  je ne comprend pas...
[quote]
20 DIM MB(255) ' toutes les cases sont à 0, seules nous reste à remplir les cas xF et Fy
21 'PRECALC masque MB
22 FOR B=0 TO 15:MB(240+B)=240:MB(B*16+15)=15:NEXT ' résume le tableau
...
200 Z=MB(Z):A=(A AND Z) OR (B AND NOT Z):RETURN
La routine en ligne 200 est réduite à une seule ligne à présent. :)

Si je comprend bien on crée un masque de 256 valeurs avec seules les valeurs pertinente (15 ou 240) pour tous les cas ou B AND 15 == 15 et B AND 240 == 240 et 0 pour le reste ? C'est vraiment astucieux!! J'aurais pas trouvé tout seul. As tu déjà vu ce cas avant ? As tu énormément d'expérience sur le sujet ?
J'ai relevé une erreur dans la création du tableau, les indices vont de 240 à 254 pour le masque à 240 (MB[255] = 255 ), idem pour les valeur = 15 : ça va de 15 à 239 (et pas 255), il faut donc faire varier B de 0 à 14.
Code:
22 FOR B=0 TO 14:MB(240+B)=240:MB(B*16+15)=15:NEXT ' résume le tableau

Pour 255, le masque sera = 255.

Bon heureusement que ça n'utilise que 256 octets, parce qu'avec tous les sprites existant dasn Buble Bobble Image

Citation:
Nota: cette version se porte très facilement en pascal. Et en ASM, si on remarque que B AND NOT Z = NOT (B OR Z):
Code:
MB rmb 256       ; à initialiser comme il faut en tenant compte du +128 ci-après
ROUT200
   LDU   #MB+128 ; astuce pour indexer comme si B était non-signé
   ANDA  B,U     ; A = A AND Z
   ORB   B,U
   NOTB          ; B = NOT(B OR Z)
   STB   ,-S     ; ici ca serait plus rapide d'avoir un accès direct-page à une variable tampon
   ORA   ,S+
   RTS

En fait, je comptais déjà utiliser l'accès direct page pour les sprites de la variable B

En tous cas un grand merci pour l'aide!! J'aurais préférer trouver tout ça tout seul, c'est frustrant... Pour le moment j'en suis à des phase de tests de vitesses, et c'est sûr que ton aide me fait gagner du temps pour l'optimisation.

En Pascal Base, il semble que le temps de traitement global soit /10 comparé au Basic (voire moins avec les DEFINT, en Pascal Base tout est de type Integer, pas de Byte, float, double etc)


Haut
 Profil  
Répondre en citant le message  
MessagePosté: 30 Juil 2020, 00:31 
Hors ligne

Inscription: 21 Aoû 2006, 09:06
Messages: 1804
Localisation: Brest
On dirait qu'il y a des problèmes dans les citations :-/

Heu sinon non j'ai jamais fais spécifiquement de routine de sprite pour le mode BM4.. mais j'ai une bonne pratique de l'asm et des optims en tout genres (moins que préhisto cependant.) Une bonne connaissance des opérations en logique bit à bit est aussi pratique pour voir si on ne peut pas calculer une formule complexe efficacement en étant astucieux comme sortir le NOT.. ah tiens je vois un bug:
Citation:
B AND NOT Z = NOT (B OR Z)

C'est une erreur c'est = NOT(NOT(B) OR Z), ce qui fait en asm:
Code:
code:ROUT200
   LDU   #MB+128 ; astuce pour indexer comme si B était non-signé
   LEAU   B,U    ; (réparation) Peut être plus rapide si on utilise X au lieu de U et qu'on fait ABX
   ANDA  ,U      ; A = A AND Z
   NOTB          ; (réparation)
   ORB    ,U
   NOTB          ; B = NOT(NOT(B) OR Z)
   STB   ,-S     ; ici ca serait plus rapide d'avoir un accès direct-page à une variable tampon
   ORA   ,S+
   RTS

_________________
Good morning, that's a nice Tnetennba


Haut
 Profil  
Répondre en citant le message  
MessagePosté: 30 Juil 2020, 18:04 
Hors ligne

Inscription: 21 Fév 2020, 11:38
Messages: 366
Ok Samuel, je verrai tout ça, Lors du message précédent, j'ai aussi noté une erreur dans la déclaration du tableua MB (il faut faire varier B de 0 à 14!! l’indice 255 étant inexistant pour notre cas...)

Sinon, je me suis créé aujourd’hui un logiciel JavaScript pour faciliter le dessins de sprites Thomson (il est opérationnel et il y a des fonctions à ajouter comme les miroirs, l'importation de tableaux de codes Sprite, etc)

Si j'ai choisi JavaScript c'est pour pouvoir développer rapidement lesdits sprite (ça affichera les datas dans un div JavaScript) et surtout parce que pour des sprites faisant 32 pxl de haut ça me parait difficile de travailler dessus sur les TO en 200 colonnes (comme il faut 8 x8 pxl pour travailler sur chaque pixel). Dès que j'ai terminé le logiciel, je le met en ligne sur mon site Albatros Concept et ça aidera grandement à adapter pas mal de jeux (avec sprite). Il y aura aussi une application de création de monde à Tiles (comme pour Super Mario, ou d'autres du même genre).

En fait, le but est de pouvoir facilement développer sur émulateur (comme DCMoto) en faisant des copier coller de datas.


Haut
 Profil  
Répondre en citant le message  
MessagePosté: 05 Aoû 2020, 23:03 
Hors ligne

Inscription: 21 Fév 2020, 11:38
Messages: 366
Quelques news sur l'affichage de Sprite pour Bubble Bobble, en terme de vitesse.. En tenant compte des recommandation de Samuel, avec sa technique des opérateurs booléen et du masque, voici quelques résultats:
Affichage de 50 sprites sans transparence, code optimisé:
- Basic : 37"
- Pascal Base compilé : 3 seconde et des brouettes

Affichage de 50 Sprites avec transparence et code optimisé:
- Basic : 84 seconde (on gagne 33% du temps grâce à la méthode de Samuel!)
- Pascal compilé : 7 seconde et des brouettes. Ca devient intéressant... A voir maintenant en assembleur ce que ça donne.

En tout cas je valide cet algorithme qui servira de référence pour les futurs jeux Thomson (et même sur autres plateforme s'il y a des transparences à gérer).

Voici le code en Pascal pur l'affichage d'un sprite (en entrée, largeur et hauteur du sprite, adresse écran notamment)

Code:
PROGRAM SpritBB;
TYPE
    TAB = ARRAY[0..63] of integer;
    TABO = ARRAY[0..255] of integer;
VAR
    ADR, RAM, I, J, L,H,largeur, ADR : INTEGER;
    T : TAB;
    M : TABO;

PROCEDURE POKE (ADR, VAL : INTEGER);
BEGIN ENCODE ('10AE58EC56E7A4'); END;

FUNCTION PEEK (ADR: INTEGER): INTEGER;
BEGIN ENCODE ('10AE58E6A44FEDC4'); END;

procedure spritet(ram,h,l: INTEGER; T : TAB);
var A, ADR,I,J, Index, Compteur : INTEGER;
BEGIN
Compteur:=0;
Index:= 0;
poke(#E7C3,peek(#E7C3) OR 1);
WHILE Compteur<2 DO
    BEGIN
    ADR:=RAM;
    for J:=1 TO H DO
        BEGIN
        FOR I:=1 TO L DO
            BEGIN
            A:=T[Index];
            if A <> 255 THEN
                POKE (ADR, A AND NOT M[A] OR PEEK(ADR) AND M[A]);
            Index:=Index+1;
            ADR:=ADR+1;
            END;
        ADR:=ADR+40-L;
        END;
    poke(#E7C3,peek(#E7C3) AND 254);
    Compteur:=Compteur+1;
    END;
END;


M[] est le tableau des masques.


Haut
 Profil  
Répondre en citant le message  
MessagePosté: 06 Aoû 2020, 00:13 
Hors ligne

Inscription: 21 Aoû 2006, 09:06
Messages: 1804
Localisation: Brest
Questions:
1) je vois un while Compteur<2.. est-ce qu'une boucle FOR ne serait pas plus rapide ? Et même pourquoi ne pas avoir le corps de boucle dans une fonction locale (je crois que le pascal permet cela), et appeler cette fonction 2 fois dans le code plutôt qu'avoir une boucle.
2) Je suppose que le "if A <> 255 THEN" est une optimisation pour éviter des peek/poke inutiles. Sauf que ce test coûte lui même un peu alors qu'il est essentiellement vrai tout le temps. As tu essayé de le supprimer pour voir l'impact sur la vitesse ? Je ne serais pas surpris que sa suppression rende le code plus rapide au final (on perds plus à tester pour rien que de faire un masque inutile de temps en temps.)
3) Est-ce qu'à ta connaissance les CASE sont efficaces en pascal ? On pourrait retirer le tableau M[] en faisant ainsi:
Code:
A:=T[Index];
case A of
  #FF: ; (* optim de la mort: on fait rien quand tout est transparent *)
  #FE: poke(ADR, (PEEK(ADR) AND #F0) OR #0E);
  #FD: poke(ADR, (PEEK(ADR) AND #F0) OR #0D);
  #FC: poke(ADR, (PEEK(ADR) AND #F0) OR #0C);
  ...
  #F3: poke(ADR, (PEEK(ADR) AND #F0) OR #03);
  #F2: poke(ADR, (PEEK(ADR) AND #F0) OR #02);
  #F1: poke(ADR, (PEEK(ADR) AND #F0) OR #01);
  #F0: poke(ADR, (PEEK(ADR) AND #F0));

  #EF: poke(ADR, (PEEK(ADR) AND #0F) OR #E0);
  #DF: poke(ADR, (PEEK(ADR) AND #0F) OR #D0);
  #CF: poke(ADR, (PEEK(ADR) AND #0F) OR #C0);
  ...
  #2F: poke(ADR, (PEEK(ADR) AND #0F) OR #20);
  #1F: poke(ADR, (PEEK(ADR) AND #0F) OR #10);
  #0F: poke(ADR, (PEEK(ADR) AND #0F));
 
  otherwise poke(ADR, A); (* a moins que ce soit un ELSE, je ne connais pas la syntaxe *)
END

Bien que nettement plus long à écrire (une trentaine de cas), j'ai le presentiment que le code ci-dessus sera plus rapide que la formule avec le tableau car il y a beaucoup de constantes qui économisent des accès mémoire toujours un peu coûteux. Je pense en outre qu'on voit qu'on fait pleins de fois "le même genre de poke" : poke(ADDR, PEEK(ADDR) AND X OR Y), ce qui pourrait entrer dans le cadre d'une primitive ASM. Bref, c'est à tester si tu veux mon avis.

_________________
Good morning, that's a nice Tnetennba


Haut
 Profil  
Répondre en citant le message  
MessagePosté: 06 Aoû 2020, 11:45 
Hors ligne

Inscription: 21 Fév 2020, 11:38
Messages: 366
Salut Samuel
Samuel Devulder a écrit:
Questions:
1) je vois un while Compteur<2.. est-ce qu'une boucle FOR ne serait pas plus rapide ? Et même pourquoi ne pas avoir le corps de boucle dans une fonction locale (je crois que le pascal permet cela), et appeler cette fonction 2 fois dans le code plutôt qu'avoir une boucle.


Non, test effectué, même temps d'exécution. Même si ici j'avoue ne pas savoir pourquoi j'ai mis while (je pensais qu'il s'agissait d'une boucle avec un pas différent de 1 ?)

Citation:
2) Je suppose que le "if A <> 255 THEN" est une optimisation pour éviter des peek/poke inutiles. Sauf que ce test coûte lui même un peu alors qu'il est essentiellement vrai tout le temps. As tu essayé de le supprimer pour voir l'impact sur la vitesse ? Je ne serais pas surpris que sa suppression rende le code plus rapide au final (on perds plus à tester pour rien que de faire un masque inutile de temps en temps.)


Je viens de le faire et sans le test, c'est même légèrement plus lent (7.7" contre 7.3). Un test ne prend pas beaucoup de cycles alors que les opérations de masquage en prennent pas mal en fin de compte. Et il se trouve qu'il y a au moins 5% (voire 10) de 255 dans les sprites...

Citation:
3) Est-ce qu'à ta connaissance les CASE sont efficaces en pascal ? On pourrait retirer le tableau M[] en faisant ainsi:...
Code:
otherwise poke(ADR, A); (* a moins que ce soit un ELSE, je ne connais pas la syntaxe *)
END

Bien que nettement plus long à écrire (une trentaine de cas), j'ai le pressentiment que le code ci-dessus sera plus rapide que la formule avec le tableau car il y a beaucoup de constantes qui économisent des accès mémoire toujours un peu coûteux. Je pense en outre qu'on voit qu'on fait pleins de fois "le même genre de poke" : poke(ADDR, PEEK(ADDR) AND X OR Y), ce qui pourrait entrer dans le cadre d'une primitive ASM. Bref, c'est à tester si tu veux mon avis.


En fait je compte mettre ce masque (qui est générique) dans un fichier data téléchargeable avec LOADM, donc la question ne se pose même pas. Le "case of" est inutile. Ici, je mets la boucle for pour éviter de tout copier à la main (déjà qu'avec la saisie du tableau du sprite c'est laborieux).

Sinon oui c'est bien OtherWise (je ne le savais même pas).
Pour ce qui est des POKE et PËEk, je ne vois pas où est le problème, c'est une routine nécessaire. Le test et l'allocation des Pokes, se fait avec une matrice de points de couleurs différentes indépendant les uns des autres (on pourrait dire que c'est une base de N en mathématiques, c'est à dire que les données sont indépendantes les unes des autres). Donc ya aucun moyen d'optimiser ceci (puisque le tableau des pixels des sprites est une base de N).

Il y a aussi un autre paramètre à voir, c'est l'adresse en mémoire écran.. Quand c'est pair, ça affiche tel qu'elle mais quand c'est impair, il faut changer quelques instructions dans la procédure (j'ai déjà fait le test en BASIC). En mode bm16, ça gère les pixels par paquets de 4 par adresse mémoire écran (2 pour la RAMA et 2 pour la RAMB), et dans les Bank, en fait, on a tous les pixel de RAMA dans la partie haute de la banque et ceux de la RAMB dans la partie basse. Il faut tenir compte de cela...
Il vient qu'en fait, pour le moment, je sais comment décaler un sprite de 2PXl à droite ou à gauche en utilisant les PEEK/POKE (et donc avec inversion des attributions RAMA et RAMB en E7C3) mais pour le pixel/pixel, ça me parait plus compliqué... Je suppose que l'animation de 2 pxl en 2 pxl des Sprite ne se verra pas trop enfin j'espère...).
Enfin il y a aussi le cas d'un affichage partiel à gauche ou à droite de l'écran, d'un sprite. Ceci nécessitera une autre procédure appelée en cas de X trop à gauche ou trop à droite. Mais pour Bubble Bobble ce n'est pas le cas. Par contre j'ai vu que c'était le cas pour mission liftOff. Et c'est là quo'n se dit que la fonction PSET est quand même bien utile, sauf qu'elle est inutilisable si on se sert de la techniquye du double buffer (nécessite un travail dans une bank ram non utilisé par la RAM écran à un instant T, alors que PSET utilise la RAM écran)


Haut
 Profil  
Répondre en citant le message  
MessagePosté: 06 Aoû 2020, 13:10 
Hors ligne

Inscription: 21 Aoû 2006, 09:06
Messages: 1804
Localisation: Brest
Perso pour le placement des sprites aux pixels (au demi-octet en fait) près je ne ferais pas de routines spéciale. Je garderais la routine optimisée pour un placement sur un octet (RAMA ou RAMB c'est selon), mais j'utiliserais 2 jeux de data. Un pour les colonnes paires, l'autres pour l'impair.

Pour les poke et peek, disons que tu as 2 appels de fonction. Or tu pourrais te faire une routine ASM qui fasse les 2 d'un coup en interne, en asm.

_________________
Good morning, that's a nice Tnetennba


Haut
 Profil  
Répondre en citant le message  
MessagePosté: 06 Aoû 2020, 18:24 
Hors ligne

Inscription: 21 Fév 2020, 11:38
Messages: 366
Samuel Devulder a écrit:
Perso pour le placement des sprites aux pixels (au demi-octet en fait) près je ne ferais pas de routines spéciale. Je garderais la routine optimisée pour un placement sur un octet (RAMA ou RAMB c'est selon), mais j'utiliserais 2 jeux de data. Un pour les colonnes paires, l'autres pour l'impair.


Ok.

Pour ce qui est des 2 jeux de datas c'est déjà ce que je fais dans ma routine (et d'ailleurs, si tu va voir sur mon appli JavaScript http://albatros.concept.free.fr/_tests/Sprite_Builder.html l'avant dernière ligne de data permet de faire ça (et je m'en sers d'ailleurs pour ma routine d'affichage de Sprites, ça me permet de ne faire qu'une bascule de RAM pour un Sprite complet). Tu peux tester avec l'importation de ces datas par exemple
Code:
15, 7, 7, 7, 7, 7, 15, 15, 7, 7, 7, 7, 7, 7, 7, 15, 7, 7, 7, 7, 7, 7, 7, 15, 7, 7, 7, 7, 7, 7, 7, 15, 7, 7, 7, 7, 7, 7, 7, 15, 0, 7, 0, 7, 7, 7, 7, 4, 0, 7, 0, 7, 7, 7, 4, 4, 0, 7, 0, 7, 7, 7, 4, 4, 7, 7, 7, 7, 7, 4, 4, 15, 7, 7, 7, 7, 7, 4, 7, 15, 0, 0, 0, 0, 7, 7, 7, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 15, 8, 8, 8, 8, 8, 15, 15, 15, 1, 15, 15, 1, 1, 15, 15, 1, 1, 1, 15, 15, 15, 15, 15

ou celui là
Code:
15, 7, 7, 7, 7, 15, 15, 15, 7, 7, 7, 7, 7, 7, 15, 15, 7, 7, 7, 7, 7, 7, 7, 15, 7, 7, 7, 7, 7, 7, 7, 15, 0, 7, 0, 7, 7, 7, 7, 15, 0, 7, 0, 7, 7, 7, 7, 15, 0, 7, 0, 7, 7, 7, 7, 15, 7, 7, 7, 7, 7, 7, 4, 15, 7, 7, 7, 7, 7, 4, 4, 4, 15, 15, 7, 7, 7, 4, 4, 4, 15, 15, 15, 15, 7, 7, 7, 15, 8, 8, 8, 8, 8, 8, 8, 15, 8, 8, 8, 8, 8, 8, 8, 15, 15, 8, 8, 8, 8, 8, 15, 15, 15, 1, 15, 15, 1, 15, 15, 15, 15, 15, 15, 1, 1, 1, 15, 15


Et voir qu'il y a ici un des monstres de Bubble Bobble (mais je pense qu'il est un peu raté)


Haut
 Profil  
Répondre en citant le message  
MessagePosté: 07 Aoû 2020, 00:10 
Hors ligne

Inscription: 21 Aoû 2006, 09:06
Messages: 1804
Localisation: Brest
J'ai enfin pris le courage de regarder Pascal Base de plus près. J'ai testé le code suivant
Code:
PROGRAM A;
VAR F,G : INTEGER;
BEGIN
   F := 256;
   G := 32767;
   G := F+G;
   WRITE(G);
END.
et observé le code ASM généré.
Code:
B36C CC0100     LDD    #$0100              3 ; 256
B36F 3406       PSHS   B,A                 7 ; \ opération inutile
B371 3506       PULS   A,B                 7 ; /
B373 EDC9FFF8   STD    $FFF8,U             9 ; F := 256 (les variables sont stockées à partir de -8,U
B377 CC7FFF     LDD    #$7FFF              3 ; 32767
B37A 3406       PSHS   B,A                 7 ; \ opération inutile
B37C 3506       PULS   A,B                 7 ; /
B37E EDC9FFF6   STD    $FFF6,U             9 ; G := 32767
B382 ECC9FFF6   LDD    $FFF6,U             9 ; Charge G (inutile)
B386 3406       PSHS   B,A                 7 ; Empile G
B388 ECC9FFF8   LDD    $FFF8,U             9 ; Charge F
B38C 3406       PSHS   B,A                 7 ; Empile F \ inutile !
B38E 3506       PULS   A,B                 7 ; Récup F /
B390 E3E4       ADDD   ,S                  6 ; calcul F+G
B392 AD8806     JSR    $06,X               8 ; test d'overflow <== coûte pas mal avec les sauts etc
B395 EDE4       STD    ,S                  5 ; sauve F+G sur la pile   \ LEAS 2,S serait plus rapide
B397 3506       PULS   A,B                 7 ; Récupère F+G            /
B399 EDC9FFF6   STD    $FFF6,U             9 ; Sauve F+G dans F
B39D ECC9FFF6   LDD    $FFF6,U             9 ; Charge F (inutile: on vient de le sauver)
B3A1 3406       PSHS   B,A                 7 ; Empile F
B3A3 CC0006     LDD    #$0006              3 ; \ Empile 6 (implicite dans un WRITE, c'est la taille à afficher à l'écran)
B3A6 3406       PSHS   B,A                 7 ; /
B3A8 AD8833     JSR    $33,X               8 ; WRITE
B3AB AD883F     JSR    $3F,X               8 ; FIN PROGRAMME

On voit que le compilateur utilise pas mal la pile. Il pourrait optimiser un peu et s’apercevoir que PSHS A,B suivi par un PULS A,B ne sert à rien. En fait c'est lié au principe de traduction qui fait que toute valeur est pushée sur la pile avant d'être stockée. Quelque part le compilo gagnerait à considérer que le registre D (=A,B) est toujours le sommet de la pile au lieu d'être un registre scratch. Bon c'est pas grave, car je pense qu'il doit être possible de faire un programme qui scann le binaire généré et remplace les $3406$3506 par une série de NOP.

Autre truc coûteux, le test du débordement, et effectivement je me souviens que le pascal détecte les débordements. C'est utile pour mettre au point mais en production c'est inutile. Cela se produit toujours avec l'appel JSR $06,X, aussi l'optimisateur pourrait remplacer $AD8806 par une série de NOP.

Avec
Code:
 F := F OR G
le code ASM est presque pareil:
Code:
B382 ECC9FFF6   LDD    $FFF6,U             9 ; empile F
B386 3406       PSHS   B,A                 7
B388 ECC9FFF8   LDD    $FFF8,U             9
B38C 3406       PSHS   B,A                 7 ; empile G
B38E 3506       PULS   A,B                 7 ; recup valeur G
B390 AAE4       ORA    ,S                  4 ; OR  "valeur F"
B392 EA61       ORB    $01,S               5
B394 EDE4       STD    ,S                  5 ; empile résultat \ LEAS 2,S ferait pareil
B396 3506       PULS   A,B                 7 ; dépile résultat /
B398 EDC9FFF6   STD    $FFF6,U             9 ; F := résultat
Il y a les mêmes défauts de manips sur la pile mais ce coup-ci on évite l'appel de la routine qui détecte le débordement. C'est plus rapide. Donc quand on fait des opération "logique" il vaut mieux écrire F:= (F and #F0) or (G and #0F) que d'utiliser un "+" à la place du "or".

J'ai aussi essayé les IF:
Code:
IF F<G THEN F := -1 END
est traduit en:
Code:
B382 ECC9FFF8   LDD    $FFF8,U             9 ; charge F
B386 3406       PSHS   B,A                 7 ; empile valeur de F (256)
B388 ECC9FFF6   LDD    $FFF6,U             9 ; charge G
B38C 3406       PSHS   B,A                 7 ; empile valeur de G (32767)
B38E AD8812     JSR    $12,X               8 ; dépile 32767 et 256, fait la différence et si>0, ecrit $FFFF si vrai $0000 sinon sur la pile
B391 ECE1       LDD    ,S++                8 ; récup $FFFF ou $0000
B393 10270011   LBEQ   $B3A8               6/5 ; SI FAUX saute vers le WRITE
B397 CC0001     LDD    #$0001              3 ;
B39A 3406       PSHS   B,A                 7 ; empile 1
B39C 4F         CLRA                       2
B39D 5F         CLRB                       2
B39E A3E4       SUBD   ,S                  6; calcule 0-1
B3A0 EDE4       STD    ,S                  5 ; empile 0-1
B3A2 3506       PULS   A,B                 7 ; dépile 0-1
B3A4 EDC9FFF8   STD    $FFF8,U             9 ; sauve en F
B3A8 ECC9FFF6   LDD    $FFF6,U             9 ; debut du WRITE

Bon on voit que -1 est compilé en 0 - 1, ce qui est coûteux, il vaut mieux écrire #FFFF dans le code. A part ca, c'est pas mal, mais on peut regretter le LBEQ quand un BEQ est à la fois plus court et plus rapide. Peut-être qu'un optimisateur pourrait remplacer $10270011 par 2 NOPS suivis par $2711 (BEQ même adresse), mais mais c'est plus lent.

Enfin petit test sur les tableaux:
Code:
PROGRAM A;
VAR F : INTEGER; G : ARRAY[2..3] OF INTEGER;
BEGIN
   F := 3;
   G[F] := 1;
END.
donne
Code:
B36C CC0003     LDD    #$0003              3
B36F 3406       PSHS   B,A                 7 ; empile 3
B371 3506       PULS   A,B                 7 ; depile 3
B373 EDC9FFF8   STD    $FFF8,U             9 ; et le stock dans F (bref F:=3)
B377 31C9FFF6   LEAY   $FFF6,U             8 ; adresse G
B37B 3420       PSHS   Y                   7 ; empile adresse G  // pile = G ...
B37D ECC9FFF8   LDD    $FFF8,U             9
B381 3406       PSHS   B,A                 7 ; empile F // pile = F G ...
B383 CC0000     LDD    #$0000              3 ; ???
B386 AD880F     JSR    $0F,X               8 ; ??? peut être modifile pile = F (G+taille-max)
B389 CC0002     LDD    #$0002              3 ; charge 2
B38C 3406       PSHS   B,A                 7 ; empile 2 // pile = 2 F G ...
B38E EC62       LDD    $02,S               6 ; récup F
B390 A3E1       SUBD   ,S++                9 ; cacule F-2 (borde inf)
B392 AD8806     JSR    $06,X               8 ; test overflow min/max
B395 EDE4       STD    ,S                  5 ; empile F-2 (index-borne_inf) // pile = F-2 G
B397 CC0002     LDD    #$0002              3 ;
B39A 3406       PSHS   B,A                 7 ; empile la taille des elements du tableau (2) // pile = 2 (F-2) G
B39C AD8809     JSR    $09,X               8 ; calcule taille-element*(index-borne_inf) (signé) // pile = 2*(F-2) G
B39F EC62       LDD    $02,S               6 ; récup G (index-borne_inf)
;oops j'ai loupé un SUBD ,S++ + JSR <test overflow> // pile = G+taillemax-(F-2)
B3A6 EDE4       STD    ,S                  5 ; sauve adresse
B3A8 CC0001     LDD    #$0001              3
B3AB 3406       PSHS   B,A                 7 ; empile 1 ; pile = 1 adresse
B3AD 3526       PULS   A,B,Y               9 ; récup 1 (dans A/B) + adresse  (dans Y)
B3AF EDA4       STD    ,Y                  5 ; sauve 1 en mémoire.. bref G[F] := 1
B3B1 AD883F     JSR    $3F,X               8 ; END. fin du prog

Où la la on voit qu'ici il y a vraiment beaucoup d'opérations. Il y a des tests d'overflow déjà coûteux, mais en plus une belle multiplication signée super coûteuse alors que lorsque la taille de l'element est une puissance de 2, une suite de décalage est infiniment plus rapide. Bon c'est le cas général, mais ca suggère que les appels aux tableaux sont coûteux. Je pense qu'il serait rentable de se faire 2 procédures asm qui réalisent une lecture/écriture plus efficacement plus efficacement.

Reste à tester le case.. mais là je suis trop fatigué. Affaire à suivre.

[EDIT] bah si j'ai quand même regardé les cases.. bon c'est +/- une série de "IF variable=valeur THEN. Il n'y a pas d'optimisation du style recherche dichotomique (on trie les valeurs et par dichotomie on voit si la valeur d'entrée correspond à l'un des CASE sans les tester tous). De ce que j'en déduis c'est que les CASE de fin sont évalués super tard et donc sont plus lents. On a intérêt à mettre au début les cas les plus probables pour avoir une correspondance rapide. Mais bon c'est pas exceptionnel tout ca quand même.

_________________
Good morning, that's a nice Tnetennba


Haut
 Profil  
Répondre en citant le message  
MessagePosté: 07 Aoû 2020, 14:40 
Hors ligne

Inscription: 13 Juin 2005, 21:50
Messages: 290
Localisation: Planete Zorg (31)
Tout ça pour une simple addition, opération logique ou comparaison :eek:

Il est vrai que les compilateurs sont spécialistes des opérations inutiles et de l'utilisation intensive des routines moniteur. La combinaison de tout ceci est très chronophage

Par exemple pour une addition
Code:
     LDD   $#0100   ; F=256
     PSHS  B,A
     LDD   $#7FFF   ; G=32767
     ADDD  ,S++     ; G=G+F et on restaure au passage l'état de la pile
/*  On peut tester le débordement pour imposer une valeur stricte non signée comme par exemple
/*   BCS   WRITE    ; pas d'overflow alors on écrit
/*   LDD   $#0001   ; sinon G=1 (par exemple)
/*  Mais ça ne sert à rien dans notre cas car le résultat sera toujours inférieur à 65535 et supérieur ou égal  à 0
WRITE.


Haut
 Profil  
Répondre en citant le message  
MessagePosté: 07 Aoû 2020, 17:13 
Hors ligne

Inscription: 21 Aoû 2006, 09:06
Messages: 1804
Localisation: Brest
En pascal tous les entiers sont signés et les opérations arithmétique font des vérifications de débordement (de mémoire on peut définir dans le standard des sous-type d'entier genre 0..3 et les opérations arithmétiques vérifiene qu'on ne produit pas de valeurs <0 ou >3 à la suite d'un calcul sur ce type là). Les tableaux commencent à un indice arbitraire, et leur accès contiennent des tests de débordement d'indice. Ca permet de faire des programmes propres et sécurisés (on est pas sans filet comme en C). La conséquence est que le pascal contrairement au C perds pas mal de temps à faire plein de vérif de sécurité.

Autre truc important à savoir: le pascal ne fait pas de raccourcis dans les calculs de booléens.
Code:
exp1 or exp2
appelle toujours exp2 même si exp1 est déjà évalué à vrai contrairement au C. Donc les expression logiques qui sont trivialement vraies ou faux dès le début sont évaluées jusqu'au bout même si ca prends plus de temps. Par contre ca réduit le nombre de sauts dans le code (les raccourcis sont des gotos implicites à la partie then/else.) Du coup la structure ASM est très régulière et peut s'analyser plus facilement.

J'ai ma petite idée d'un truc qui permettrait de rendre PASCAL-BASE un peu plus efficace.. il faut juste que je check s'il faut garder les adresses fixes où si cela est géré par le machin binaire final. Il faut aussi que je teste quelques autres instructions fréquentes qu'on espère rapides (boucles for).

_________________
Good morning, that's a nice Tnetennba


Haut
 Profil  
Répondre en citant le message  
Afficher les messages postés depuis:  Trier par  
Poster un nouveau sujet Répondre au sujet  [ 168 messages ]  Aller à la page Précédente  1, 2, 3, 4, 5 ... 12  Suivante

Heures au format UTC + 1 heure


Qui est en ligne

Utilisateurs parcourant ce forum: Aucun utilisateur enregistré et 44 invités


Vous ne pouvez pas poster de nouveaux sujets
Vous ne pouvez pas répondre aux sujets
Vous ne pouvez pas éditer vos messages
Vous ne pouvez pas supprimer vos messages
Vous ne pouvez pas joindre des fichiers

Rechercher:
Aller à:  
cron
Développé par phpBB® Forum Software © phpBB Group
Traduction par phpBB-fr.com