Forums Casio - Projets de programmation

Index du Forum | Projets de programmation | Gint, la matière grise au service de vos add-ins
Lephenixnoir
Hors ligne
Administrateur
Niveau: Confirmé
Points: 10036
Défis: 130
Message
Posté le 20/02/2015 17:30

Gint, la matière grise au service de vos add-ins :

Gint est une bibliothèque de développement d'add-ins pour GCC. Elle se substitue à fxlib et en augmente radicalement les fonctionnalités et l'optimisation, tout en maintenant une compatibilité complète et native entre SH3 et SH4.

Au programme :
— La vie en quatre couleurs grâce au moteur de gris ;
— Un contrôle poussé du clavier pour les jeux (événements), mais pas seulement ;
— 16 timers parallèles précis à la milliseconde pour contrôler des animations ;
— Des rendus de texte et d'images fulgurants ;
— La fin de la conversion des images avec Sprite Coder ;
— Tous vos add-ins deviennent compatibles SH3 et SH4 dans un seul fichier.

Et pour l'honnêteté, son inconvénient majeur : vous devez le trimballer dans votre add-in, au prix d'environ 30 ko. Pour rappel, utiliser sprintf() avec le SDK, ça coûte 18 ko, alors pensez-y.


Image 1 : Mon rêve qui devient réalité


Effectuer un test de gint sur votre machine

Gint est encore relativement jeune, et j'ai besoin de le tester sur un maximum de machines pour m'assurer qu'il marche bien. Si vous avez l'habitude d'utiliser des add-ins, n'hésitez pas à prendre 15 minutes pour effectuer un test sur la votre.

1. Transférez l'add-in de test de gint (téléchargeable ici), sur votre machine, et lancez-le en maintenant EXE appuyé. L'écran de contrôle (image 2) apparaît.
2. Recopiez ses contenus dans votre message/rapport de test/etc. S'il n'y a pas "Boot OK" en haut à droite, redémarrez votre calculatrice avec RESET et arrêtez-vous là, car gint n'y fonctionne pas. Sinon, appuyez sur n'importe quelle touche.
3. Dans le menu principal, rendez-vous dans la section TEST et effectuez les différents tests de la liste (voir plus loin). Notez quoi que ce soit de non conforme aux indications, ou qui vous semble bizarre. Si tout se passe bien, dites-le aussi !


Images 2 et 3 : L'écran de contrôle de gint, et le menu principal

Keyboard & events : vous devez pouvoir appuyer sur plusieurs touches simultanément et visualiser des nom et nombre de répétition corrects, même si vous suivez des séquences exotiques. Au-delà de 9 touches pressées (visibles sur le clavier de gauche), le test n'est plus fidèle, c'est normal. Profitez-en pour tester Left + Down + SHIFT et constater que ALPHA est déclenché, c'est également normal. Ça marche avec d'autres « carrés ».
Gray engine : vous pouvez contrôler les délais du timer et observer l'effet produit sur l'aspect du gris. S'il n'est pas bien équilibré, ajustez le contraste de votre machine dans le menu SYSTEM. Vérifiez que le réglage par défaut (DEFT) est satisfaisant quand le contraste est bien réglé.
Image rendering : affichez quelques images et déplacez-les à l'écran. Elles doivent s'afficher correctement même quand elles dépassent, et dans toutes les positions.
Text rendering : tant que vous voyez les 6 lignes de 32 caractères en entier pour chaque police, c'est gagné. Vous pouvez contrôler la couleur du texte et tester son aspect sur un fond noir.
Real-time clock : l'horloge doit avancer seconde après seconde. Mettez-la à l'heure ou modifiez légèrement l'heure indiquée : le réglage doit persister.
Clocks and timers : dans l'onglet TIME, le timer et l'horloge doivent avancer sensiblement à la même vitesse. L'onglet FREQ indique les différentes étapes qui mènent aux fréquences calculées ou mesurées pour le bus (BΦ), le processeur (IΦ) et les modules périphériques (PΦ).

4. Placez-vous ailleurs que dans le menu principal, appuyez sur MENU et vérifiez que vous retournez bien au menu de la calculatrice. Retournez dans l'application de test, vous devez réapparaître à l'endroit où vous l'aviez laissée.
5. Envoyez-moi tout ça et prenez ce badge super cool en remerciement.


Image 4 : Vous voilà bien décoré-e


Installer et utiliser gint sur votre système, pour développer

L'utilisation normale de gint est en l'associant avec le fxSDK, qui fournit les options de compilation/édition des liens appropriées, le linker script particulier de gint ainsi que les outils de conversion compatibles avec la bilbiothèque. Si vous voulez utiliser le fxSDK tout entier (recommandé), suivez ses instructions d'installation, gint est fourni avec.

Une fois que gint est installé sur votre système, voyez les tutoriels de développement pour avoir un aperçu de son fonctionnement. La plupart des choses sont expliquées dans les en-têtes (fichiers .h) de la bibliothèque. Ils sont tous dans le sous-dossier gint du dossier d'installation du fxSDK, que vous pouvez afficher en exécutant fxsdk --folder.

Si vous voulez utiliser directement gint sans le fxSDK (soyez sûrs que vous savez ce que vous faites), vous pouvez ou télécharger l'archive, ou le compiler à la main. Notez que la compilation requiert quand même le fxSDK... pas besoin de l'installer par contre, il vous suffit de placer fxsdk/bin dans le PATH après compilation.

$ git clone "http://git.planet-casio.com/lephe/gint" && cd gint
$ ./configure --startup-log # Ce que vous voulez
$ make all-lib

Ici aussi, vous ne voudrez probablement pas l'installer, juste linker la bibliothèque. Je rappelle que l'ordre de linkage compte, et que vous devez absolument compiler avec gcc <options> <objets...> -lgint -lc -lgcc, et dans cet ordre.


Comment ça marche

Gint est essentiellement organisé autour d'un gestionnaire d'interruptions, une routine qui répond aux signaux émis par les composants pour signaler des événements intéressants (décompte du temps, timer arrivé à expiration, touches du clavier pressées, données reçues sur le port série... entre 20 et 40 sources différentes sur la calculatrice). Normalement c'est le système qui fournit le sien, mais gint le change parce qu'il a besoin de le contrôler pour implémenter ses fonctionnalités et se rendre indépendant de fxlib.

Une fois qu'on contrôle les interruptions, installer un moteur de gris avec un timer à deux délais est un jeu d'enfant. Le moteur de Kucalc (Revolution-FX) ne les contrôlait pas, et devait toutes les supprimer sauf celle du timer, désactivant du même coup les autres timers, l'horloge, la fonction GetKey()... tout un dérangement qui se soldait par un redémarrage à la fin de l'exécution.


Fichier joint

La sagesse est la prunelle de tes yeux, et la gloire l’enveloppe de ton cœur.


Lephenixnoir
Hors ligne
Administrateur
Niveau: Confirmé
Points: 10036
Défis: 130
Message
Dernier message de la page précédente :Posté le 16/10/2016 12:04 |
Je pense que j'ai trouvé ce que je vais faire. Le dev' va demander des événements clavier, sous différentes formes et le clavier va lui renvoyer les événements qu'il a demandé. Chaque forme d'événements aura une fonction indépendante.

Certaines formes ne donneront que les touches pressées, d'autres donneront les touches unes par unes et d'autres simultanément. Certaines formes pourront utiliser les modificateurs SHIFT et ALPHA, d'autres ne pourront pas. Au final, ce seront les mêmes compte-rendus d'événements claviers, mais pas présentés pareil.

Ma priorité reste d'avoir du code simple pour chaque utilisateur. Darks, je compte bien passer WebCalc sous gint quand le projet sera assez stable. Il faut que le code du clavier soit aussi simple que possible, et la souplesse de ce système me convient plus que pour tout autre système auquel j'ai pensé jusqu'à maintenant. On pourra également intégrer la proposition de Ninestars.

Ça va être difficile à implémenter mais ce n'est pas mon problème, à la limite. Au final, on aboutirait sur différentes interfaces comme :
- getkey()
- multigetkey()
- getevent()
- multigetevent()
Ne criez pas à l'arnaque.
----------------------------------
La sagesse est la prunelle de tes yeux, et la gloire l’enveloppe de ton cœur.



Pages: Précédente | 1, 2, 3, 4, 5, 6, 7 | Suivante

Kirafi
Hors ligne
Membre
Niveau: Confirmé
Points: 1994
Défis: 10
Message
Citer : Posté le 20/10/2016 09:14 | #
Donc si j'ai bien compris :
- getkey(), servirait pour avoir l'état d'une touche.
- multigetkey(), servirait pour avoir l'état de 2 touches en même temps (avec un ET).
Mais est-ce que on peut mettre 2 getkey() qui gère des trucs différents en même temps ?
Genre le premier pour un déplacement de perso sur une map et le deuxième pour l'utilisation d'une attaque, et on pourrait faire soit l'un soit l'autre, ou les 2 en même temps. (Avec le SDK on mettrait 2 tout simplement KeyDown différent dans la boucle quoi).
----------------------------------
iPod
Pour des parties rapides
Jusqu'où pourras-tu aller dans ce jeu "partie rapide" qu'est Dextris (élu Jeu Du Mois)
Pourras-tu survivre plus de 20 secondes dans ce fameux tunnel appelé Graviton
Rebondis entre les murs en évitant les piques dans SpikeBird
Pourras-tu éviter de te faire écraser dans FallBlocs (élu Jeu Du Mois)
Réussiras-tu à rendre uniforme la grille Spin (élu Jeu Du Mois)
Autres
Franchement ils valent le coups
Deviens l'amiral de la marine dans SeaRush (jeu concours) (élu Jeu Du Mois)
La version 2048 tactile amélioré au plus haut point : 2048 Delux !
Pars à la recherche des morceaux d'étoile dans Lumyce (élu Jeu Du Mois)
L'adaptation du célèbre jeu de reflexion 2048 Tactile (élu Jeu Du Mois)
Remet de l'ordre dans cette mare à grenouille de délinquants dans Hopper
[/spoiler]
Lephenixnoir
Hors ligne
Administrateur
Niveau: Confirmé
Points: 10036
Défis: 130
Message
Citer : Posté le 20/10/2016 10:30 | #
Non. Définitivement, non. Sortez vous de la tête que
- getkey(), servirait pour avoir l'état d'une touche.

Ce n'est pas comme ça que ça fonctionne.

Toutes les fonctions de gestion de clavier de gint sont bloquantes.
- getkey() attend qu'on appuie sur une touche et renvoie le code de la touche pressée
- multigetkey() attend qu'on appuie sur une ou plusieurs touches et renvoie le ou les codes des touches pressées

Supprimez à tout jamais IsKeyDown() de votre cerveau. Jamais gint ne vous permettra de l'utiliser, parce que c'est une mauvaise solution qui peut toujours, et doit, être évitée.
----------------------------------
La sagesse est la prunelle de tes yeux, et la gloire l’enveloppe de ton cœur.
Ninestars
Hors ligne
Membre
Niveau: Confirmé
Points: 1735
Défis: 22
Message
Citer : Posté le 20/10/2016 11:47 | #
multigetkey() attend qu'on appuie sur une ou plusieurs touches et renvoie le ou les codes des touches pressées

Comment la fonction sait si elle doit attendre qu'on appuie sur une ou plusieurs touches.
Par exemple pour gérer les déplacements avec les diagonales, on va utiliser le multigetkey pour savoir si on appuie sur deux flèches en même temps. et si on appuie sur une seule flèche, la fonction va bloquer ? attendre un certain temps avant de se dire qu'on l'on veut presser une seule touche ?
----------------------------------
Lephenixnoir
Hors ligne
Administrateur
Niveau: Confirmé
Points: 10036
Défis: 130
Message
Citer : Posté le 20/10/2016 11:51 | #
En fait, multigetkey() possède une certaine notion de simultanéité. Elle va attendre qu'on presse quelque chose sur le clavier, et dès qu'elle va se rendre compte qu'on a pressé quelque chose, elle va s'arrêter. Mais en plus de getkey(), elle est capable (dans une certaine mesure) de détecter plusieurs touches à la fois, et donc elle saura si on appuyé en même temps sur plusieurs flèches, alors que getkey() ne renverra jamais qu'une seule touche.

En fait ces deux fonctions attendent une pression sur le clavier, mais multigetkey() sait en plus si la pression a été causée par plusieurs touches simultanément. Pour faire simple, multigetkey() s'arrêtera toujours dès qu'on presse quelque chose sur le clavier.
----------------------------------
La sagesse est la prunelle de tes yeux, et la gloire l’enveloppe de ton cœur.
Ninestars
Hors ligne
Membre
Niveau: Confirmé
Points: 1735
Défis: 22
Message
Citer : Posté le 20/10/2016 12:12 | #
Donc multigetkey à un certain delais avant de renvoyer le resultat, puisque j'imagine il faut une certaine souplesse pour ne pas devoir appuyer sur deux (ou plus) touches dans la même milliseconde
----------------------------------
Lephenixnoir
Hors ligne
Administrateur
Niveau: Confirmé
Points: 10036
Défis: 130
Message
Citer : Posté le 20/10/2016 12:15 | #
Oui. En fait le clavier est analysé automatiquement à une fréquence donnée (dans l'implémentation actuelle, c'est 16 Hz) et la simultanéité est en pratique le caractère de plusieurs événements à se produire entre deux analyses

Cette méthode fonctionne très bien par ailleurs.
----------------------------------
La sagesse est la prunelle de tes yeux, et la gloire l’enveloppe de ton cœur.
Ninestars
Hors ligne
Membre
Niveau: Confirmé
Points: 1735
Défis: 22
Message
Citer : Posté le 20/10/2016 12:20 | #
16 Hz c'est pas assez à mon avis.
Oui donc en fait tu vérifies le nombre d'évènements survenus pendant les 1/16 secondes écoulées et tu en déduis que pusieurs touches sont pressé si il y a plusieurs évènements. Et si on appuie sur une touche à la fin de la frame t0 et la seconde touche au début de la frame t1 (donc t1-t0 < 1/16), ça va gérer comme deux appuies différents et pas simultané
----------------------------------
Cakeisalie5
Hors ligne
Administrateur
Niveau: Confirmé
Points: 1373
Défis: 8
Message
Citer : Posté le 20/10/2016 12:34 | #
Je suis contre "imposer les méthodes pour améliorer les performances". Tu as peut-être en tête que les add-ins actuels sont dégeu parce que les développeurs ne cherchent pas les performances. Je pense surtout que c'est parce que jusque là, on cherchait à faire fonctionner le bousin. Prendre le développeur pour un gamin de neuf ans incapable de choisir ce qui lui sied le mieux, ça va deux minutes. è_é (je ne dis pas d'implémenter MONLIST non plus, on va pas déconner)

Supprimer toute existence de IsKeyDown parce qu'elle est souvent mal utilisée, c'est triste. Mieux vaut l'implémenter mais la déconseiller au profit d'autres méthodes - elle aura sans doute une utilisation justifiée dans un programme futur, à laquelle on n'a pas pensé, où ce sera facile et plus optimisé de l'utiliser.

Ensuite, je sais pas pourquoi tu te fais chier à te dire "je veux gérer la file du clavier et la file des events séparément". getkey peut parcourir les évènements récents (getevent) et renvoyer uniquement le code clavier de ceux qui sont de type KeyDown. (aussi, du coup, pour IsKeyDown etc, ce serait mieux d'utiliser le buffer clavier à ce moment-là)

Et puis bon, pour les poll, tu les enlèves si tu veux, mais tu les remplace par des *_timeout. (pour le coup, je sais que tu as une solution partielle avec max_cycles mais bon, on t'attend pour implémenter des timeouts plus préçis )

(et puis bon, je suis toujours pas chaud pour tout ce qui est multi*, mais bon, tu fais comme tu veux pour ça)
----------------------------------
Lephenixnoir
Hors ligne
Administrateur
Niveau: Confirmé
Points: 10036
Défis: 130
Message
Citer : Posté le 20/10/2016 13:11 | # | Fichier joint
Ninestars a écrit :
Oui donc en fait tu vérifies le nombre d'évènements survenus pendant les 1/16 secondes écoulées et tu en déduis que pusieurs touches sont pressé si il y a plusieurs évènements.

Je crois que tu n'a pas compris. Je ne compte pas le nombre d'événements survenus puisque je ne sais pas ce qu'il s'est passé. Tout ce que je connais, c'est l'état touches pressées / libres aux instants t et t + 1/16. Si tu presses une touche à t et que tu la relâches et t + 1/32, aucun événement ne sera détecté. La pression sera passée sous silence.

Ninestars a écrit :
16 Hz c'est pas assez à mon avis.

Pour l'instant c'est cadencé sur la RTC donc c'est ça ou 64 Hz. Je t'invite à tester le programme que je mets en fichier. Le test « Keyboard » (onglet F2) te démontrera les capacités du programme à 16 Hz.

Cakeisalie5 a écrit :
Je suis contre "imposer les méthodes pour améliorer les performances".

Et tu as parfaitement raison. Si je peux me permettre, je ne suis pas contre IsKeyDown() par performance, mais par design. Tu disais toi-même (et Darks a confirmé) que le système de getkey() actuel n'est pas hyper propre et qu'il faudrait des events pour que ça marche bien. Et pourtant, tu défends l'utilisation d'une fonction qui est pire que mon système actuel ? Ou bien je n'ai pas compris ce dont ton message retournait exactement, ou bien tu as besoin de lever des contradictions.

Cakeisalie5 a écrit :
Je pense surtout que c'est parce que jusque là, on cherchait à faire fonctionner le bousin. Prendre le développeur pour un gamin de neuf ans incapable de choisir ce qui lui sied le mieux, ça va deux minutes. è_é

Je pense que tu as raison sur le premier point, jusque-là on voulait que ça fonctionne. Maintenant qu'on est grands, il serait peut-être le temps de se poser des questions un peu plus subtiles, par exemple « comment écrire des programmes dont le flot sera structuré pour éviter qu'ils plantent toutes les deux secondes, tout en usant de méthodes stables pour gérer des entrées / sorties complexes ? ».

J'aurais honte rien qu'à donner l'impression que je prends les développeurs pour des gamins. Mais tu l'as vu toi-même, assez peu de gens se sont posés la question de la propreté de leurs systèmes. Et il ne faut pas me prendre pour un idiot, tu sais aussi bien que moi qu'user de méthodes propres pour gérer les entrées / sorties est aussi une protection importante contre les bugs stupides.

Je vais être honnête (quoique un peu dur), tout à l'heure tu m'as proposé :
if(getkey() == KEY_EXE && keyPressed(KEY_SHIFT)) { ... }

Ça, je suis désolé, c'est stupide. Ça n'a aucun sens. Tu veux combiner une méthode bloquante et une méthode qui refait une analyse partielle du clavier en plein milieu de ton test ? Tu veux utiliser getkey() pour surveiller deux touches alors que son comportement n'est pas garanti lorsque plusieurs touches sont pressées ?

Cakeisalie5 a écrit :
Supprimer toute existence de IsKeyDown parce qu'elle est souvent mal utilisée, c'est triste. Mieux vaut l'implémenter mais la déconseiller au profit d'autres méthodes [...]

J'aimerais bien, mais je ne suis pas assez naïf pour croire que ça va marcher... je veux démontrer qu'on peut faire des choses élégantes et je veux que gint soit le reflet de cette simplicité et de cette efficacité. Personne ici ou presque ne s'est demandé quels étaient les effets négatifs de IsKeyDown(). Tu penses vraiment qu'ils vont utiliser ce que je leur propose alors que j'ai déjà du mal à expliquer comment ça marche et pourquoi ça marche ?

Dans mon tuto sur le désassemblage de syscalls, je me suis penché sur le cas de la fonction Sleep() et attelé à montrer tout ce qu'elle faisait de travers. Il s'agissait de problèmes d'implémentation. Pour le coup d'IsKeyDown(), c'est carrément un problème de design. Comprends bien ça : pour moi, un appel à IsKeyDown() en plein milieu d'un programme ne vaut pas mieux qu'un goto bien placé.

Ça ne veut pas dire que je ne veux pas implémenter goto, ça veut dire que je veux expliquer pourquoi on ne doit pas s'en servir avant de l'implémenter.

Cakeisalie5 a écrit :
Ensuite, je sais pas pourquoi tu te fais chier à te dire "je veux gérer la file du clavier et la file des events séparément".

Imaginons qu'on n'a qu'une seule file, que dedans il y a des événements clavier et des messages provenant du port série. J'effectue des appels à getkey(). Il faut que getkey() supprime les événements clavier de la file sinon c'est le bordel total. Mais dans ce cas, je fais quoi des messages du port série qui sont coincés entre les pressions de touches ?
- Je les supprime. Très joli, il est intuitif que la fonction getkey() dérange le fonctionnement du port série et supprime des données.
- Je les garde. C'est bien beau, mais du coup je ne peux pas re-remplir la file, et y'a des trous en plein milieu.
Je te laisse méditer sur la question, tu as peut-être une meilleure solution que moi.

Cakeisalie5 a écrit :
Et puis bon, pour les poll, tu les enlèves si tu veux, mais tu les remplace par des *_timeout.

C'est complètement différent. Ceci existe déjà dans la fonction getkey_opt(), et c'est autrement plus propre que les analyses rapides. J'en profite pour placer que la présupposée méthode « multigetevent() » ne correspond à aucun fondement théorie et n'existera sans doute jamais. Pour le multigetkey(), c'est différent, il y a des considérations techniques.

Ajouté le 20/10/2016 à 13:53 :
Voilà le problème derrière mon système de files. Imaginons que j'ai une seule file. Je dois y enfiler les événements dans l'ordre pour que l'utilisateur les récupère dans l'ordre (ça c'est pas négociable, je pense). Selon la manière dont le programme s'exécute, je risque donc d'avoir ceci :

--- sommet
{serial} Connexion en cours...
{keybd}  KEY_SHIFT pressed
{keybd}  KEY_SHIFT released
{serial} Salut ! Comment ça va ?
{keybd}  KEY_B pressed
{keybd}  KEY_B released
{keybd}  KEY_I pressed
{keybd}  KEY_I released
{serial} Je vais brancher le port USB !
{usb}    INIT CONNECTION
--- fin

Maintenant l'utilisateur appelle plein de fois getkey(). Je vais renvoyer KEY_SHIFT, puis KEY_B, et KEY_I. Mais que se passe-t-il dans la file ? Soit je supprime les événements au passage (et donc le serial et l'usb partent à la poubelle), soit je les garde. Mais si je les garde, le critère qui m'oblige à garder les événements dans l'ordre m'empêche de re-remplir la file : je suis bloqué.

Mon idée est donc d'avoir trois files différentes : une pour le clavier, une pour le port série, une pour le port USB. Lorsqu'on appelle getevent(), la fonction va chercher l'événement le plus ancien dans l'ensemble des trois files (il suffit de comparer la date des premiers événements de chaque file) et retire cet événement de la file concernée. La fonction getkey() n'observe que la file du clavier et retire le premier événement disponible. De cette manière, le fait de « filtrer » les événements pour ne s'intéresser qu'aux événements clavier ne perturbe pas les événements des autres modules.

Suis-je clair ?
----------------------------------
La sagesse est la prunelle de tes yeux, et la gloire l’enveloppe de ton cœur.
Ninestars
Hors ligne
Membre
Niveau: Confirmé
Points: 1735
Défis: 22
Message
Citer : Posté le 18/11/2016 11:40 | #
Oui chef, très clair !

Et où en es-tu pour ton moteur de gris ? Je commence à avoir quelques idées pour mon moteur 3D
----------------------------------
Cakeisalie5
Hors ligne
Administrateur
Niveau: Confirmé
Points: 1373
Défis: 8
Message
Citer : Posté le 18/11/2016 11:49 | # | Fichier joint

----------------------------------
Lephenixnoir
Hors ligne
Administrateur
Niveau: Confirmé
Points: 10036
Défis: 130
Message
Citer : Posté le 18/11/2016 13:54 | #
Cake, si tu n'étais pas là pour discuter mes choix avec une pertinence tout à fait louable, je ne passerais pas mon temps à changer d'avis. Maintenant si t'en as marre, tu peux me laisser faire de la merde tout seul, mais faut pas te plaindre après quoi.

Ajouté le 10/02/2017 à 13:59 :
Histoire d'un bug particulièrement stupide, vulgarisée.
Les temps sont durs. Ces derniers temps je rencontre moins de bugs de code que d'erreurs de stupidité (aka programmer failure), et honnêtement c'est pas glorieux. x_x

Le dernier en date (qui m'a bloqué trois jours peut-être), est tellement grossier que je ne résiste pas à l'envie de vous le raconter. L'observation était la suivante : dans l'application de test de gint, la section « Keyboard » affiche une petite matrice où les touches pressées sont représentées en temps réel, et leurs noms affichés sur le côté. Il se trouvait que si j'appuyais sur plein de touches en même temps et bourrinais mon clavier, alors, au moment d'appuyer sur EXIT pour revenir au menu de l'application, le programme crashait. Mais pas si je ne manipulais que quelques touches normalement.

Le premier problème étant que, un crash ne devrait pas être possible. En effet, si une exception se produit (exécution d'une instruction illégale, accès à un pointeur invalide, des conneries de ce genre), le programme a le droit de la gérer en affichant par exemple un message d'erreur ou en se corrigeant. Pour ce faire, il installe une fonction appelée gestionnaire d'exceptions qui est exécutée en cas de problème. gint possède un gestionnaire d'exceptions (c'est un de ses composants principaux), qui rapporte l'erreur à l'écran et entre dans une boucle infinie. Donc, si une exception s'était produite, il aurait fallu qu'un message s'affiche : un crash n'aurait pas dû être possible.

Je ne vous embête pas avec mes longues recherches infructueuses ; voici plutôt l'absurde corrélation d'événements qui menait en réalité à ce bug.
→ En bourrinant dans la section « Keyboard », je provoquais un effet de bord du driver clavier qui lui faisait croire que des touches, qui ne sont en réalité pas pressées, le sont (c'est un effet général qui se produit dès qu'on a plusieurs touches pressées, d'où la nécessité de bourriner) ; en particulier la touche 7 ;
→ Ces touches étaient stockées dans un tableau d'événements qui n'était pas correctement vidé parce que c'est un travail en cours, et restaient donc en attente ;
→ Au retour au menu de l'application, le buffer était lu (il n'était pas lu par la section « Keyboard » pour des raisons que je ne détaillerai pas, mais il l'était par le menu) et les touches en attente, étaient exécutées.
→ Au milieu de ces touches se trouvaient le 7, que j'avais affecté (dans le menu de l'application seulement) à un test proposé par Cake.

Ce test consistait à revenir temporairement au menu principal comme GetKey() le permet. Pour faire cela, il faut rendre le contrôle de la machine au système, et donc il faut désinstaller temporairement gint, effectuer un ou deux appels système, et réinstaller gint si jamais l'utilisateur retourne dans l'application. C'est une manœuvre complexe, et je n'arrive pas encore à le faire, donc elle plantait. Et là, plot twist, comme j'avais désinstallé gint, mes messages d'erreur ne s'affichaient pas, me laissant dans la plus sombre perplexité alors que ce n'est même pas un bug au sens premier puisque ce test n'a jamais été intégré comme feature.

Je n'ai pas encore tenté de correctif mais je suis à peu près sûr que c'est ça. Vous voyez donc le niveau de fourberie et de stupidité qu'atteint parfois ce projet pourtant pas si compliqué dans le fond.


Ajouté le 01/03/2017 à 09:45 :
J'aurai appris un truc aujourd'hui. Si vous voulez voir un bug tordu, prenez donc quelques minutes.

J'avais cette structure-ci :
/*
    mod_tmu_timer_t
    Registers of a single timer.
*/
typedef struct
{
    uint32_t    TCOR;        /* Timer constant register */
    uint32_t    TCNT;        /* Timer counter */

    word_union(TCR,
        uint        :7;
        uint    UNF    :1;    /* Underflow flag */
        uint        :2;
        uint    UNIE    :1;    /* Underflow interrupt enable */
        uint    CKEG    :2;    /* Clock edge (SH7705) */
        uint    TPSC    :3;    /* Timer prescaler */
    );

    uint16_t    :16;

} __attribute__((packed)) mod_tmu_timer_t;

La word_union() est une macro qui crée une union du nom passé en premier paramètre contenant un uint16_t appelé word et une structure packée contenant les champs décrits ensuite. Il s'agit de permettre un accès au registre, soit par bit individuel (plus simple), soit avec le mot complet (plus rapide). La macro ressemble à ça :

/*
    word_union()
    Union between an uint16_t 'word' attribute and a provided bit-field
    structure that describe the contents of the word.
*/
#define word_union(name, fields)        \
    union                    \
    {                    \
        uint16_t word;            \
        struct { fields }        \
        __attribute__((packed));    \
    } __attribute__((packed)) name

Enfin, j'avais une structure nommée TMU dont les contenus sont déterminés dynamiquement, au début de l'exécution, selon le processeur détecté, qui indique les adresses du module (ici, les trois premiers pointeurs nous intéressent) :

/*
    mod_tmu_t (resides into gint memory)
    Basically three timer units and an additional register to control who's
    running.
*/
typedef struct
{
    /* Timer configurations */
    volatile mod_tmu_timer_t    *timers[3];
    /* Timer start register */
    volatile mod_tmu_tstr_t        *TSTR;
    /* Timer input capture 2 (SH7705) */
    volatile const uint32_t        *TCPR2;

} mod_tmu_t;

// The actual thing is there. It's initialized by gint at startup and contains
// addresses and information suitable for the current platform.
extern volatile mod_tmu_t TMU;

Les pointeurs sont libellés volatile pour indiquer au compilateur que les contenus sont susceptibles de changer à tout instant ; et qu'il doit effectivement aller lire la mémoire chaque fois qu'on les utilise (il n'a pas le droit de faire des optimisations). L'adresse du troisième timer sur SH7305 est a4490020. Le problème se situe alors dans le code suivant :
volatile mod_tmu_timer_t *_TMU1 = (void *)0xa4490020;
volatile mod_tmu_timer_t *_TMU2 = TMU.timers[2];
dprint(1, 41, "%04x %04x %d", _TMU1->TCR.word, _TMU2->TCR.word, _TMU1 == _TMU2);

Cette innocente fonction avait le défaut d'afficher quelque chose comme :
0003 0000 1

À savoir, même pointeur (puisque _TMU1 == _TMU2), mais pas les mêmes contenus !

Je ne vous fait pas attendre plus (comme d'habitu... eh, je passe ma vie à raconter des bugs moi ou quoi ?), le problème est en fait lié à la combinaison de deux facteurs :
- La structure mod_tmu_timer_t est packée ;
- L'adresse TMU.timers[2] est obtenue dynamiquement.

Le premier facteur faisait que l'union était placée le plus proche possible de ce qui précédait, et ces éléments-là mêmes étaient collés le plus possible. Cela a pour effet de détruire l'alignement : il est possible que l'union soit placée à une adresse qui n'est pas multiple de 2 ; dans ces conditions, hors de question de lire les deux octets en une opération (c'est interdit par le processeur) : il faut lire un octet à chaque fois.

Le problème c'est qu'un registre on n'y accède pas n'importe comment et en particulier on doit lire ou écrire l'intégralité des données quand on y accède -- c'est soit 8 bits, soit 16 bits, soit 32 bits, point final. L'accès au registre TCR2 ne pouvait donc pas fonctionner puisque le non-alignement potentiel forçait le compilateur à y accéder en deux fois.

Pourquoi alors, est-ce que ça marchait avec le pointeur explicite et pas l'autre ? Voyant que l'adresse explicite était effectivement multiple de 4, le compilateur déduisait que le champ word de l'union était, dans le premier cas, aligné, et lisait les deux octets d'un coup (ce qui marchait). À l'inverse, l'adresse TMU.timers[2] étant dynamique, le compilateur prenait des précautions pour pouvoir lire les deux octets dans tous les cas de figure d'alignement et les lisait donc séparément (ce qui échouait).

Une fois qu'on a compris ça, la résolution est simple :
typedef struct
{
    // ...

} __attribute__((packed, aligned(4))) mod_tmu_timer_t;

En voilà un qui n'était pas trivial, de bug
----------------------------------
La sagesse est la prunelle de tes yeux, et la gloire l’enveloppe de ton cœur.
Dark storm
Hors ligne
Administrateur
Niveau: Aucun
Points: 9597
Défis: 170
Message
Citer : Posté le 03/03/2017 01:23 | # | Fichier joint
Je savais pas où mettre ça, ici c'est l'endroit qui me paraît le plus adapté.

Pour résumer, on peut faire du son pas dégueux en faisant du PWM. Pour cela, deux méthodes : on utilise un montage qui optimise la démodulation, ou alors on joue sur les imperfections de la machine en bourrinant à quelques Mbauds (pour rappel, la vitesse max supportée via les syscalls est de 115200 bauds).

Ceux qui se disent que bourriner à ce point peut nuire à l'état de la machine (ce qui est, à l'heure actuelle, totalement incertain), et préfèrent donc la solution via du hardware, voici le schéma et quelques notes sur le démodulateur.

L'idée est de décomposer le signal d'entrée en plusieurs signaux qui vont être compensés par les condensateurs (1). Ces signaux sont ensuite pré-amplifiés pour régler le "poids" des différents canaux entre eux (2). On somme le tout (3), puis on amplifie (4). À la fin, il suffit de sortir sur le speaker (5).

La charge d'un condensateur en circuit RC est de 3RC pour une charge à 95%. Donc pour chaque période T=1/f à décomposer, on veut que T/4 = 3RC, pour que la charge de la capacité soit au plus proche du bout de sinusoïde qui nous intéresse.

Pour ce qui est des pré-amplificateurs et l'amplificateur, on a Vs = Ve (1 + R1/R2), avec Vs la tension de sortie, Ve la tension d'entrée, et R1/R2 le rapport du pont diviseur de tension du potentiomètre.

On peut remplacer les amplificateurs non-inverseurs par des amplificateurs inverseur. À ce moment là Vs = - Ve (R2/R1). Utile pour couper totalement la sortie du son par exemple. Cela fonctionne car la fréquence du signal n'est pas modifiée. Par contre on peut éventuellement avoir un changement de timbre très légèrement perceptible (enveloppe ADRS à priori inversée), j'ai pas trop réfléchi là-dessus.

Attention à ne pas dépasser la tension de saturation des AOP, sinon le rendu est quoi qu'il arrive dégueulasse !

Plus d'infos sur les montages à base d'AOP : Wikipédia



----------------------------------
Finir est souvent bien plus difficile que commencer. — Jack Beauregard
Dark storm
Hors ligne
Administrateur
Niveau: Aucun
Points: 9597
Défis: 170
Message
Citer : Posté le 03/03/2017 01:24 | # | Fichier joint
Post uniquement pour le fichier
----------------------------------
Finir est souvent bien plus difficile que commencer. — Jack Beauregard
Ninestars
Hors ligne
Membre
Niveau: Confirmé
Points: 1735
Défis: 22
Message
Citer : Posté le 05/03/2017 18:25 | #
Ton bug est assez fourbe, il joue sur la façon dont optimise le compilateur en fait, dont pas facile à trouver j'imagine

Dark ? Pourquoi tu parles de ça ici ?

Ajouté le 05/03/2017 à 18:27 :
Et tu devrais mettre un aop en suiveur à l'entrée du montage d'ailleurs
----------------------------------
Dark storm
Hors ligne
Administrateur
Niveau: Aucun
Points: 9597
Défis: 170
Message
Citer : Posté le 05/03/2017 18:33 | #
Pas faux le coup du suiveur. Je parle de ça ici parce que je sais pas si ça valait le coup de faire un nouveau topic étant donné que c'est des tests en marge de gint, qui n'ont pas vocation à être largement diffusés (à cause de la partie électronique).
----------------------------------
Finir est souvent bien plus difficile que commencer. — Jack Beauregard
Lephenixnoir
Hors ligne
Administrateur
Niveau: Confirmé
Points: 10036
Défis: 130
Message
Citer : Posté le 05/07/2017 21:04 | #
Malgré mon silence et les concours depuis deux mois et demi, je continue d'avancer tranquillement. Les derniers ajouts non signalés comportent :
→ Un retour au menu répliquant parfaitement celui du système tant que les syscalls sont activés (option de compilation de gint) ;
→ Une étude encore non implémentée d'un gestionnaire d'interruptions autrement plus puissant que celui qui existe actuellement ;
→ La suppression d'un système de debuggage (dont je ne crois pas avoir parlé) en faveur d'un nouveau type de logs au démarrage ;
→ Des corrections de bugs variées, notamment sur le clavier, et la suppression définitive de multigetkey() qui est avantageusement remplacée par le système d'événements, jugé plus souple ;
→ La description fastidieuse des modules de contrôle d'interruptions, une manière de m'émanciper de Revolution-FX en rendant gint plus souple.

Pas tant de changements visibles, mais l'application s'améliore significativement en interne, tant sur l'empreinte mémoire que sur celle de la vitesse, que je vais continuer de travailler jusqu'à perfection
----------------------------------
La sagesse est la prunelle de tes yeux, et la gloire l’enveloppe de ton cœur.
Ninestars
Hors ligne
Membre
Niveau: Confirmé
Points: 1735
Défis: 22
Message
Citer : Posté le 09/07/2017 15:16 | #
Tu pourrais en dire davantage sur la suppression du multigetkey ?
----------------------------------
Lephenixnoir
Hors ligne
Administrateur
Niveau: Confirmé
Points: 10036
Défis: 130
Message
Citer : Posté le 09/07/2017 15:38 | #
Ce système manquait de souplesse pour plein de raisons : nombre limité de touches détectées à chaque appel, ce qui augmentait la gêne créée par les faux positifs vraisemblablement liés aux matériel (effets de « carrés » et de « colonnes » qui existent tout le temps), aspect séquentiel qui se mêlait mal aux autres types d'événements notamment en multijoueur, risque de non-simultanéité si le timer passe pas au bon moment...

Le système d'événements actuel recoupe le multigetkey() mais le ne recouvre pas entièrement, car il ne permet pas de détecter la simultanéité. Cependant, pour la plupart des jeux, il permet de gérer correctement la pression de différentes touches en même temps, car le rôle de ces touches est indépendant. Voici par exemple le code d'un petit moteur de déplacement sur l'écran en 2D (qui devrait marcher si on fournissait le sprite à la compil') :

#include <events.h>
#include <timer.h>
#include <gint.h>
#include <keyboard.h>

static int x = 64, y = 32;
static int vx = 0, vy = 0

void update_speed(key_t key, int revert)
{
    /* If revert is set, reduce speed instead of increasing it */
    revert = (revert) ? (-1) : (1);

    switch(key)
    {
    case KEY_UP:    vy -= 1 * revert;
    case KEY_DOWN:    vy += 1 * revert;
    case KEY_LEFT:    vx -= 1 * revert;
    case KEY_RIGHT:    vx += 1 * revert;
}

int main(void)
{
    extern image_t my_sprite;
    event_t event;
    int game_running = 1;

    /* Create a timer that fires every 25 ms */
    timer_t *timer = timer_create(25, 0);
    timer_start(timer);

    /* Adjust the keyboard repeat rate to fire events every 25 ms */
    keyboard_setRepeatRate(25, 25);

    while(game_running) switch((event = waitevent()).type)
    {
    case event_key_press:
        /* gint_switch() performs a return to the main menu */
        if(event.key.code == KEY_MENU) gint_switch();
        else update_speed(event.key.code, 0);
        break;
    case event_key_release:
        update_speed(event.key.code, 1);
        break;

    /* The timer has fired! */
    case event_timer_underflow:
        x += vx;
        y += vy;
        if(x < 0) x = 0;
        if(x > 127) x = 127;
        if(y < 0) y = 0;
        if(y > 63) y = 63;

        /* Render the sprite at the new position */
        dclear();
        dimage(x, y, &my_sprite);
        dupdate();
    }

    timer_stop(timer);
    return 0;
}

Tu noteras que le système d'événements intègre le timer, ce qui permet avantageusement de rafraîchir l'écran à une fréquence prédéfinie sans s'embêter avec un setFPS() peu précis. Pour le reste, je crois que c'est assez explicite ou commenté, dis-moi si tu as des questions.

Note : j'ai pu intégrer un retour au menu au lieu de quitter l'application directement, car le timer est en mode « émission d'événements » et pas en mode « appel de callbacks ». Il reste deux-trois trucs subtils à gérer de ce côté-là en interne.

Ajouté le 09/07/2017 à 15:40 :
Note : Pour la question de la simultanéité (« pour lancer une boule de feu, appuyez simultanément sur [4] et [6] »), j'ai dit que le système actuel ne la gérait pas, mais il me faudrait 10 minutes pour le rendre possible, donc pas d'affolement.
----------------------------------
La sagesse est la prunelle de tes yeux, et la gloire l’enveloppe de ton cœur.
Ninestars
Hors ligne
Membre
Niveau: Confirmé
Points: 1735
Défis: 22
Message
Citer : Posté le 10/07/2017 12:48 | #
Mais là tu as quand même ta boucle While qui tourne à fond nan ?
----------------------------------

Pages: Précédente | 1, 2, 3, 4, 5, 6, 7 | Suivante

Index du Forum | Projets de programmation | Gint, la matière grise au service de vos add-ins
Publicité et partenaires
Casio Education
Casio éducation

TI-Planet
Casiopeia
Casiopeia
CasioFan, la communauté ClassPad
CasioFan
CodeWalrus
CodeWalrus

Planète Casio v42 © créé par Neuronix et Muelsaco 2004 - 2015 | Il y a 37 connectés | Nous contacter | Qui sommes-nous ? | Licences et remerciements
Rugby Manager | Jeu de basket | Jeu de handball | Construction maison Carcassonne

Planète Casio est un site communautaire indépendant, géré bénévolement et n'est donc pas affilié à Casio | Toute reproduction de Planète Casio, même partielle, est interdite
Les fichiers, programmes et autres publications présents sur Planète Casio restent la propriété de leurs auteurs respectifs et peuvent être soumis à des licences ou des copyrights.
CASIO est une marque déposée par CASIO Computer Co., Ltd