Les membres ayant 30 points peuvent parler sur les canaux annonces, projets et hs du chat.
La shoutbox n'est pas chargée par défaut pour des raisons de performances. Cliquez pour charger.

Forum Casio - Projets de programmation


Index du Forum » Projets de programmation » gint : un noyau pour développer des add-ins
Lephenixnoir Hors ligne Administrateur Points: 24146 Défis: 170 Message

gint : un noyau pour développer des add-ins

Posté le 20/02/2015 17:30

Ce topic fait partie de la série de topics du fxSDK.

En plus des options de programmation intégrée comme le Basic Casio ou Python, la plupart des calculatrices Casio supportent des add-ins, des programmes natifs très polyvalents avec d'excellentes performances. Les add-ins sont généralement programmés en C/C++ avec l'aide d'un ensemble d'outils appelé SDK.

Plusieurs SDK ont été utilisés par la communauté avec le temps. D'abord le fx-9860G SDK de Casio avec fxlib pour Graph monochromes (plus maintenu depuis longtemps). Puis le PrizmSDK avec libfxcg pour Prizm et Graph 90+E (encore un peu actif sur Cemetech). Et plus récemment celui que je maintiens, le fxSDK, dont gint est le composant principal.

gint est un unikernel, ce qui veut dire qu'il embarque essentiellement un OS indépendant dans les add-ins au lieu d'utiliser les fonctions de l'OS de Casio. Ça lui permet beaucoup de finesse sur le contrôle du matériel, notamment la mémoire, le clavier, l'écran et les horloges ; mais aussi de meilleures performances sur le dessin, les drivers et la gestion des interruptions, plus des choses entièrement nouvelles comme le moteur de gris sur Graph monochromes.

Les sources de gint sont sur la forge de Planète Casio : dépôt Gitea Lephenixnoir/gint

Aperçu des fonctionnalités

Les fonctionnalités phares de gint (avec le fxSDK) incluent :

  • Toutes vos images et polices converties automatiquement depuis le PNG, sans code à copier (via fxconv)
  • Un contrôle détaillé du clavier, avec un GetKey() personnalisable et un système d'événements à la SDL
  • Une bibliothèque standard C plus fournie que celle de Casio (voir fxlibc), et la majorité de la bibliothèque C++
  • Plein de raccourcis pratiques, comme pour afficher la valeur d'une variable : dprint(1,1,"x=%d",x)
  • Des fonctions de dessin, d'images et de texte optimisées à la main et super rapides, surtout sur Graph 90+E
  • Des timers très précis (60 ns / 30 µs selon les cas, au lieu des 25 ms de l'OS), indispensables pour les jeux
  • Captures d'écran et capture vidéo des add-ins par USB, en temps réel (via fxlink)

Avec quelques mentions spéciales sur les Graph monochromes :
Un moteur de gris pour faire des jeux en 4 couleurs !
La compatibilité SH3, SH4 et Graph 35+E II, avec un seul fichier g1a
Une API Unix/POSIX et standard C pour accéder au système de fichiers (Graph 35+E II seulement)

Et quelques mentions spéciales sur les Graph 90+E :
Une nouvelle police de texte, plus lisible et économe en espace
Le dessin en plein écran, sans les bordures blanches et la barre de statut !
Un driver écran capable de triple-buffering
Une API Unix/POSIX et standard C pour accéder au système de fichiers

Galerie d'add-ins et de photos

Voici quelques photos et add-ins réalisés avec gint au cours des années !



Arena (2016)Plague (2021)



Rogue Life (2021)



Momento (2021)



Communication avec le PC (cliquez pour agrandir)


Utiliser gint pour développer des add-ins

Les instructions pour installer et utiliser gint sont données dans les divers tutoriels recensés dans le topic du fxSDK. Il y a différentes méthodes de la plus automatique (GiteaPC) à la plus manuelle (compilation/installation de chaque dépôt). Le fxSDK est compatible avec Linux, Mac OS, et marche aussi sous Windows avec l'aide de WSL, donc normalement tout le monde est couvert

Notez en particulier qu'il y a des tutoriels de développement qui couvrent les bases ; tout le reste est expliqué dans les en-têtes (fichiers .h) de la bibliothèque que vous pouvez consulter en ligne, ou dans les ajouts aux changelogs ci-dessous.

Changelog et informations techniques

Pour tester les fonctionnalités et la compatibilité de gint, j'utilise un add-in de test appelé gintctl (dépôt Gitea Lephenixnoir/gintctl). Il contient aussi une poignée d'utilitaires d'ordre général.

Ci-dessous se trouve la liste des posts indiquant les nouvelles versions de gint, et des liens vers des instructions/tutoriels supplémentaires qui accompagnent ces versions.

VersionDateInfos supplémentaires
gint 2.10.02 Avril 2023
gint 2.9.021 Août 2022
gint 2.8.017 Mai 2022Effets dynamiques sur les imagesAPI de manipulations d'images
Overclock intégré
gint 2.7.119 Mars 2022Tutoriel capture des flux standards
gint 2.7.031 Décembre 2021
gint 2.6.029 Août 2021Tutoriel de capture vidéo par USB
gint 2.5.28 Juin 2021
gint 2.5.12 Juin 2021
gint 2.5.026 Mai 2021Intégration de fxlibc (dépôt) — Tutoriel de communication par USB
gint 2.4.027 Avril 2021Api GINT_CALL() pour les callbacks
gint 2.3.12 Février 2021
gint 2.3.029 Janvier 2021
gint 2.2.112 Janvier 2021
gint 2.2.011 Janvier 2021
gint 2.1.116 Septembre 2020
gint 2.1.021 Août 2020Polices UnicodeNouvelle API du moteur de gris
gint 2.0.3-beta10 Juillet 2020Modifications de l'API timer
gint 2.0.2-beta17 Juin 2020
gint 2.0.1-beta1er Juin 2020

Anecdotes et bugs pétés

Ô amateurs de bas niveau, j'espère que vous ne tomberez pas dans les mêmes pièges que moi.


TODO list pour les prochaines versions (2023-04-03)

gint 2.11
  1. Changements de contextes CPU. À reprendre du prototype de threading de Yatis pour permettre l'implémentation d'un véritable ordonnanceur. Demandé par si pour faire du threading Java.
  2. Applications USB. Ajouter le support de descripteurs de fichiers USB. Potentiellement pousser jusqu'à avoir GDB pour debugger.
  3. Support de scanf() dans la fxlibc. Codé par SlyVTT, plus qu'à nettoyer et fusionner.

Non classé

  • Regarder du côté serial (plus facile que l'USB) pour la communication inter-calculatrices (multijoueur) et ultimement l'audio (libsnd de TSWilliamson).
  • Un système pour recompiler des add-ins mono sur la Graph 90+E avec une adaptation automatique.
  • Support des fichiers en RAM pour pouvoir utiliser l'API haut-niveau sur tous les modèles et éviter la lenteur de BFile à l'écriture quand on a assez de RAM.



Précédente 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ··· 20 ··· 30 ··· 40 ··· 50 ··· 60 ··· 70, 71, 72, 73 Suivante
Lephenixnoir Hors ligne Administrateur Points: 24146 Défis: 170 Message

Citer : 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.
Mon graphe (24 Mars): (gint#27 ; (Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; ...) || (shoutbox v5 ; v5)
Kirafi Hors ligne Membre Points: 2180 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)
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)
Lephenixnoir Hors ligne Administrateur Points: 24146 Défis: 170 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.
Mon graphe (24 Mars): (gint#27 ; (Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; ...) || (shoutbox v5 ; v5)
Ninestars Hors ligne Membre Points: 2461 Défis: 24 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 Points: 24146 Défis: 170 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.
Mon graphe (24 Mars): (gint#27 ; (Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; ...) || (shoutbox v5 ; v5)
Ninestars Hors ligne Membre Points: 2461 Défis: 24 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 Points: 24146 Défis: 170 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.
Mon graphe (24 Mars): (gint#27 ; (Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; ...) || (shoutbox v5 ; v5)
Ninestars Hors ligne Membre Points: 2461 Défis: 24 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 En ligne Ancien administrateur Points: 1896 Défis: 11 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)

Promotion ordinaire sur les inscriptions sur Planète Casio : en ce moment, c'est gratuit !

Mon blogBesoin d'utilitaires de transfert vers et depuis la calculatrice sous GNU/Linux ?
Lephenixnoir Hors ligne Administrateur Points: 24146 Défis: 170 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 ?
Mon graphe (24 Mars): (gint#27 ; (Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; ...) || (shoutbox v5 ; v5)
Ninestars Hors ligne Membre Points: 2461 Défis: 24 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 En ligne Ancien administrateur Points: 1896 Défis: 11 Message

Citer : Posté le 18/11/2016 11:49 | # | Fichier joint





Promotion ordinaire sur les inscriptions sur Planète Casio : en ce moment, c'est gratuit !

Mon blogBesoin d'utilitaires de transfert vers et depuis la calculatrice sous GNU/Linux ?
Lephenixnoir Hors ligne Administrateur Points: 24146 Défis: 170 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

Mon graphe (24 Mars): (gint#27 ; (Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; ...) || (shoutbox v5 ; v5)
Dark storm En ligne Labélisateur Points: 11631 Défis: 176 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 En ligne Labélisateur Points: 11631 Défis: 176 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 Points: 2461 Défis: 24 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 En ligne Labélisateur Points: 11631 Défis: 176 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 Points: 24146 Défis: 170 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
Mon graphe (24 Mars): (gint#27 ; (Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; ...) || (shoutbox v5 ; v5)
Ninestars Hors ligne Membre Points: 2461 Défis: 24 Message

Citer : Posté le 09/07/2017 15:16 | #


Tu pourrais en dire davantage sur la suppression du multigetkey ?
Lephenixnoir Hors ligne Administrateur Points: 24146 Défis: 170 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.
Mon graphe (24 Mars): (gint#27 ; (Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; ...) || (shoutbox v5 ; v5)
Ninestars Hors ligne Membre Points: 2461 Défis: 24 Message

Citer : Posté le 10/07/2017 12:48 | #


Mais là tu as quand même ta boucle While qui tourne à fond nan ?
Précédente 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ··· 20 ··· 30 ··· 40 ··· 50 ··· 60 ··· 70, 71, 72, 73 Suivante

LienAjouter une imageAjouter une vidéoAjouter un lien vers un profilAjouter du codeCiterAjouter un spoiler(texte affichable/masquable par un clic)Ajouter une barre de progressionItaliqueGrasSoulignéAfficher du texte barréCentréJustifiéPlus petitPlus grandPlus de smileys !
Cliquez pour épingler Cliquez pour détacher Cliquez pour fermer
Alignement de l'image: Redimensionnement de l'image (en pixel):
Afficher la liste des membres
:bow: :cool: :good: :love: ^^
:omg: :fusil: :aie: :argh: :mdr:
:boulet2: :thx: :champ: :whistle: :bounce:
valider
 :)  ;)  :D  :p
 :lol:  8)  :(  :@
 0_0  :oops:  :grr:  :E
 :O  :sry:  :mmm:  :waza:
 :'(  :here:  ^^  >:)

Σ π θ ± α β γ δ Δ σ λ
Veuillez donner la réponse en chiffre
Vous devez activer le Javascript dans votre navigateur pour pouvoir valider ce formulaire.

Si vous n'avez pas volontairement désactivé cette fonctionnalité de votre navigateur, il s'agit probablement d'un bug : contactez l'équipe de Planète Casio.

Planète Casio v4.3 © créé par Neuronix et Muelsaco 2004 - 2024 | Il y a 98 connectés | Nous contacter | Qui sommes-nous ? | Licences et remerciements

Planète Casio est un site communautaire non affilié à Casio. Toute reproduction de Planète Casio, même partielle, est interdite.
Les programmes et autres publications présentes sur Planète Casio restent la propriété de leurs auteurs et peuvent être soumis à des licences ou copyrights.
CASIO est une marque déposée par CASIO Computer Co., Ltd