Forums Casio - Projets de programmation

Index du Forum | Projets de programmation | Moteur propre de niveaux de gris - gint
Lephenixnoir
Hors ligne
Administrateur
Niveau: Confirmé
Points: 9837
Défis: 130
Message
Posté le 20/02/2015 17:30

Moteur propre de niveaux de gris - gint :

Le gestionnaire d'interruptions est un programme crucial qui gère de nombreuses choses dans le système. Le projet gint (prononcez 'guinne') vise à l'implémenter pour débloquer des fonctionnalités nouvelles telles que le moteur de gris et le multi-getkey.


En bref

Le gestionnaire d'interruptions permet de gérer clavier, timers, horloge interne, indépendamment de fxlib. Il servira de base aux bibliothèques de développement du fxSDK.
Le moteur de gris permet, comme son nom l'indique, de donner l'illusion de gris à l'écran en alternant rapidement deux images monochromes. L'ancien moteur de Kucalc n'était pas propre, mais ce problème peut être résolu si développe le moteur de gris par-dessus le gestionnaire d'interruptions.


Comment ça marche et pourquoi c'est intéressant

Les interruptions

Les interruptions sont des signaux émis par un composant interne (e.g. clavier, horloge interne) ou externe (e.g. connexion au port USB) lorsque quelque chose d'intéressant se produit. Ils signalent l'évènement et demandent au processeur de traiter l'interruption, c'est-à-dire de gérer l'évènement.

Par exemple dans le cas de la RTC (horloge temps réel), l'interruption va être traitée en ajoutant une seconde au compteur d'horloge de la machine, puis éventuellement une minute, une heure, et ainsi de suite. Pour un timer, le traitement de l'interruption va consister à appeler une fonction fournie par l'utilisateur au lancement du timer (par exemple actualisation automatique d'une donnée).

Dans la calculatrice, il n'y a pas moins de 23 sources d'interruptions différentes possibles : cependant, il s'avère lorsqu'on lance un add-in que seules 5 ou 6 sont effectivement activées, donc gérées par le système.

Le gestionnaire d'interruptions

Lorsqu'une interruption est générée, le programme utilisateur est mis en attente et le flot d'exécution se tourne vers appelée interrupt handler, c'est-à-dire le gestionnaire d'interruptions. Le système dispose de son propre gestionnaire.

Le gestionnaire d'interruptions doit exécuter ce qu'il pense être nécessaire pour gérer l'interruption, mais pas seulement : il doit également signaler au composant que l'interruption a été traitée. C'est pourquoi il ne doit recevoir que des interruptions qu'il est capable de traiter : sinon (pour des raisons techniques) il ne saura pas alerter le composant, qui insistera à l'infini pour qu'on traite son événement, empêchant le programme utilisateur de reprendre.

Écrire un gestionnaire d'interruptions

De nombreuses fonctionnalités, telles que les timers, l'horloge ou le clavier dépendent des interruptions et donc de leur gestionnaire. Le gestionnaire propriétaire de Casio est fait pour fonctionner avec GetKey(), RTC_getTicks() ou encore SetTimer() ; mais le projet d'écrire une bibliothèque standard pour le fxSDK ne peut pas se passer d'avoir ses propres fonctions en remplacement de celles-ci (sinon les programmes sont tous sous Copyright à cause de fxlib, ce qui est problématique).

À l'inverse, l'utilisation d'un gestionnaire d'interruptions personnalisé permet non seulement de gérer plus facilement le moteur de gris (voir la section « Moteur de gris »), mais également d'utiliser des fonctions personnalisées pour la gestion des clavier, timers, horloge et autres ; ce qui permet à la fois d'éviter l'utilisation de fxlib mais aussi ajouter des fonctionnalités (voir la section « Étendre les possibilités »).

Moteur de gris

Toutes ces considérations s'accordent très bien avec l'idée d'écrire un moteur de gris : car, dès lors qu'on a un gestionnaire d'interruptions personnalisé, le moteur de gris devient un jeu d'enfant !

Le défaut du moteur de gris de Kucalc, partie intégrante de revolution-fx, c'est que le gestionnaire d'interruptions qu'il utilise ne gère que le timer qui permet d'alterner les écrans. En d'autres termes, toutes les fonctionnalités qui dépendaient des interruptions ne fonctionnent plus !

Donc, plus de GetKey() mais IsKeyDown() / IsKeyUp() (qui lisent directement le clavier à n'importe quel moment), plus de Sleep(), plus de timers, plus d'horloge... et puis évidemment pour restaurer tout ça à l'état correct il faut un reset complet. Un prix assez lourd...

Étendre les possibilités

Ce gestionnaire d'interruptions est donc modulable à souhait, au point de pouvoir faire plus de choses que fxlib : accès direct aux timers du microprocesseur, quantité potentiellement infinie de timers système (oui, ceci est un peu inutile, mais c'est pour l'explication), exécution de tâches automatisées toutes les secondes (jeux de gestion temps réel) en étendant la RTC, répétition plus rapide des touches, répétitions de toutes les touches, moteur de gris, multi-getkey et beaucoup d'autres.


Là ou le projet en est

Je travaille actuellement sur les machines SH3 et SH4 en même temps, avec un seul fichier exécutable compatible. Voici ce qui fonctionne.

Sur SH3 et SH4
→ Utilisation du gestionnaire d'interruptions personnalisé et sortie propre
→ Utilisation des gestionnaires d'exceptions générales et d'erreurs du MMU
→ Gestion de la RTC (horloge temps réel)
→ Gestion de l'écran
→ Gestion des timers
→ Gestion du clavier (imparfaite sur le multi-getkey)
→ Moteur de gris


Ce qui reste à faire

La SH4 et la SH3 fonctionnent toutes les deux en totale compatibilité ! Il reste assez peu de travail en soi :
→ Finir le dessin de bitmaps (supporte déjà le monochrome, le gris et la transparence, reste la semi-transparence et quelques options de dessin)
→ Du texte plus évolué (choix de la couleur essentiellement)
→ Gérer plus d'interruptions et autoriser l'utilisateur à y mapper des callbacks
→ Écrire des programmes !


Là où vous intervenez

Le projet gint existe pour promouvoir le développement sous Linux et des possibilités plus larges. J'aurai grand besoin de vous pour tester la bibliothèque une fois que j'aurai un add-in de test correct

Sinon vous pouvez laissez un commentaire, me dire si le projet vous intéresse, ou ce que vous aimeriez y voir figurer... ça fait plaisir !


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


Lephenixnoir
Hors ligne
Administrateur
Niveau: Confirmé
Points: 9837
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

Kirafi
Hors ligne
Membre
Niveau: Confirmé
Points: 1993
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
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
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: 9837
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: 1725
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: 9837
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: 1725
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: 9837
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: 1725
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: 1292
Défis: 6
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: 9837
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: 1725
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: 1292
Défis: 6
Message
Citer : Posté le 18/11/2016 11:49 | # | Fichier joint

----------------------------------
Lephenixnoir
Hors ligne
Administrateur
Niveau: Confirmé
Points: 9837
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", _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: 9452
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



----------------------------------
Soutenez-les !





#YAPHPH
Dark storm
Hors ligne
Administrateur
Niveau: Aucun
Points: 9452
Défis: 170
Message
Citer : Posté le 03/03/2017 01:24 | # | Fichier joint
Post uniquement pour le fichier
----------------------------------
Soutenez-les !





#YAPHPH
Ninestars
Hors ligne
Membre
Niveau: Confirmé
Points: 1725
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: 9452
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).
----------------------------------
Soutenez-les !





#YAPHPH

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

Index du Forum | Projets de programmation | Moteur propre de niveaux de gris - gint
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 53 connectés | Nous contacter | Qui sommes-nous ? | Licences et remerciements
Rugby Manager | Jeu de basket | Jeu de handball | Jeu de tennis | Nova Raider | Réparation téléphone | Soccer Rush | Tasty Tale

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