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.
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
- 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.
- Applications USB. Ajouter le support de descripteurs de fichiers USB. Potentiellement pousser jusqu'à avoir GDB pour debugger.
- 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.
Citer : Posté le 15/03/2020 13:13 | #
Le format n'est pas difficile à obtenir. Je te conseille de prendre ce que tu as trouvé et on peut l'adapter ensemble.
Citer : Posté le 15/03/2020 14:47 | # | Fichier joint
Voila c'est le fichier en pièces jointes que j'aimerai adapter. Bon je me doutes qu'il faudrait crop certains trucs comme le nom de la police sur le sprite, mais je ne pouvais pas par exemple définir le nombre de caractères sur chaque ligne dans gint (ici il n'y a que 3 lignes alors que gint prends par défaut 6 lignes)
Citer : Posté le 15/03/2020 15:11 | # | Fichier joint
Oh mais ça existe encore cette vieille police
Ci-joint la version appropriée. Les paramètres fxconv sont les suivants :
Citer : Posté le 17/03/2020 15:20 | #
Merci pour la police au fait,
J'ai une interrogation là : J'ai des choses bizarre qui se passent entre les "gimage" et les "dimage". Globalement je mixe de l'affichage gris et classique sans trop faire gaffe (sur une même fenetre je peux avoir du classique et du gris)...
Est ce que ca peut poser des problèmes ? Est ce que ca ne serait pas trop degeu/non-opti de tout mettre en gray même si j'ai certaines images en noir et blanc ?
PS : je précise tout de même que je met bien des gray_stop !
Citer : Posté le 17/03/2020 15:25 | #
L'idée derrière les d*() et les g*() est que la méthode de rendu est assez différente. En noir et blanc il n'y a qu'une VRAM, en gris il y en a deux. Les d*() modifient une seule VRAM, les g*() en modifient deux.
Si tu le moteur de gris est actif, tu dois toujours dessiner avec g*(), même si tes images sont noir et blanc, car le noir et blanc sur deux VRAMs ne se produit pas de la même façon que le noir et blanc sur une seule VRAM. Si le moteur de gris est éteint, tu dois toujours dessiner avec d*().
-
J'ajoute que j'ai pensé l'autre jour à un système qui élimine cette distinction en déterminant automatiquement quelle fonction appeler. Dans le futur, il est probable que seuls les d*() existent. Je laisserai les g*() comme des alias des d*() jusqu'à la version majeure suivante.
Citer : Posté le 17/03/2020 15:27 | #
Okay, merci pour la précision !
Ajouté le 17/03/2020 à 15:55 :
Du coup toujours le même soucis ...
Comme je veux tout passer en g* (vu qu'il y a du gris sur mes vues), j'ai une fonction principale, et un timer qui est lancé dans cette fonction pour modifier un peu l'affichage à l'écran qui est maintenant en niveau de gris.
Le problème c'est que ce qu'il y a dans le callback du timer ne semble pas s'executer (ou n'affiche rien en tout cas ...)
Pour l'instant grosso merdo j'ai ça :
gray_start();
timer_setup(0, timer_delay(0, 50*1000), 0, &callback, NULL);
timer_start(0);
getkey();
gray_stop();
}
void angle_callback(volatile void *arg) {
gimage(1, 1, &img_mypic);
gupdate();
}
J'ai l'impression (sans doute mauvaise) que le fait que le gray engine ne soit pas connu/lancé dans le callback fait qu'il ne sait pas où tracer ... Alors que tout le reste a survécu au passage au g*
Citer : Posté le 17/03/2020 16:28 | #
Le paramètre que tu envoies à timer_setup() dans cet exemple n'est pas le callback que tu décris.
Accessoirement, "modifier un peu" ça ne marche pas à cause du triple buffering actif quand le moteur de gris existe. Tu dois redessiner tous les frames de zéro parce que gupdate() « modifie la VRAM ».
Citer : Posté le 17/03/2020 16:34 | #
Oui effectivement c'est angle_callback qu'il aurait fallu mettre dans timer_setup ...
Du coup si j'ai bien compris l'idée serait de tout redessiner dans le callback ?
Ajouté le 17/03/2020 à 17:59 :
Bon je reviens vers toi Lephe car ca ne marche toujours pas ...
gray_start();
timer_setup(0, timer_delay(0, 50*1000), 0, &angle_callback, NULL);
timer_start(0);
getkey();
timer_stop(0);
gray_stop();
}
void angle_callback(volatile void *arg) {
gclear(C_WHITE);
gimage(1, 1, &img_mypic);
gupdate();
}
Cela ne m'affiche absolument rien à l'écran. Donc j'ai essayé de faire le tracé dans la fonction principale. Donc la avec ca :
gray_start();
while (1) {
gclear(C_WHITE);
gimage(1, 1, &img_mypic);
gupdate();
sleep_us(0, 500*1000);
}
gray_stop();
}
Le problème c'est que je perd les interruptions pour l'affichage et donc je ne peux plus gérer les touches.
Est ce qu'il y a une autre solution, ou alors est ce que je m'y prends mal ? J'aimerai comprendre pourquoi mon affichage ne marche plus avec mon timer ...
Merci d'avance,
Teusner
Citer : Posté le 17/03/2020 20:35 | #
Oh, je viens de voir autre chose, peut-être un problème. Ton callback n'est pas du bon type. Il devrait renvoyer un int. Si la valeur renvoyée n'est pas 0 le timer est arrêté. Tu devrais activer les warnings et ne pas le laisser passer !
Dis-moi si ça aide, je vais tester juste après (c'est pas que je veux pas mettre les mains dedans, juste que j'essaie de pas me faire engloutir ^^")
Le code compile et fonctionne avec les changements donnés, mais le return 0; de main ne retourne pas au menu.
Donc ça je l'ai traqué, c'est une concurrence entre l'arrêt de l'application et la fin du transfert DMA provoqué par dupdate() quand le triple buffering est actif. Je sais comment le résoudre, faut juste que je le fasse proprement et le pousse dans gint.
Citer : Posté le 17/03/2020 21:23 | #
Bon j'ai remplacé le type de retour de mon callback et les paramètres, puisque je n'en ai pas (donc j'avais aussi un warn)
gclear(C_WHITE);
gimage(13, h-3, &img_cross);
gupdate();
return 0;
}
Et la
Citer : Posté le 18/03/2020 13:27 | #
Ha ha.
Le moteur de gris utilise le timer 0 pour fonctionner, donc ton timer est refusé et ton callback n'est jamais appelé. Tu l'aurais vu si tu avais vérifié la valeur de retour de timer_setup(). Utilise un autre timer.
Je note toutefois que c'est pas écrit dans la description de gray_start(). J'ai ajouté ça en local, je le pousserai quand j'aurai nettoyé mon dépôt local qui est dans un état expérimental.
Citer : Posté le 18/03/2020 13:35 | #
Merci bcp ... J'avoue que j'ai bien lu la doc maintenant, mais je commencais à perdre espoir. Ca explique pourquoi ca marchait parfaitement en d* et que le passage en g* était foireux. Je teste ca et je te redis ! Merci encore
Ajouté le 18/03/2020 à 13:48 :
Bon bah ca marche niquel : Du coup penses à l'ajouter dans le header timer.h car ca pourrait en coincer plus d'un !
Ajouté le 19/03/2020 à 11:15 :
J'ai besoin de quelques explications pour la lecture/enregistrement de fichiers ...
j'ai ce code plus ou moins récupéré/adapté (car depuis les noms de fonctions ont changées) du jeu Arena que tu as fait :
'\\', '\\', 'f', 'l', 's', '0', '\\',
's', 'p', 'a', 'c', 'e', '.', 's', 'a', 'v', 0x00
};
void save_write(int n) {
BFile_Remove(filename);
BFile_Create(filename, BFile_File, sizeof(n));
int fd = BFile_Open(filename, BFile_WriteOnly);
BFile_Write(fd, &n, sizeof(n));
BFile_Close(fd);
}
Le problème c'est que quand j'appelle par exemple save_write(5) je m'attends à trouver dans la memoire secondaire un fichier space.sav avec l'entier 5 dedans .... chose qui ne se fait pas en réalité. Est ce que tu pourrais m'aider ?
Citer : Posté le 19/03/2020 11:21 | #
Alors, il faut savoir que la gestion de l'écriture sur l'EEPROM par Casio est catastrophique.
Pour pouvoir écrire dans l'EEPROM en utilisant Bfile_WriteFile() il faut que :
* la taille du buffer que tu veux écrire sois un multiple de 2.
* l'adresse du buffer sois multiple de 2.
Ces restrictions sont dues à la configuration en écriture de l'EEPROM par Casio.
Citer : Posté le 19/03/2020 11:22 | #
Comme remarque préliminaire (chose que je ne savais pas quand j'ai codé Arena), pour obtenir un tableau de u16 tu peux écrire ta chaîne normalement avec juste un "u" devant :
/* Mieux encore */
static uint16_t const *filename = u"\\\\fls0\\space.sav";
Vu le code, je pense que ça devrait marcher. Pour commencer à debugger, je te conseille de vérifier la valeur de retour de tous ces appels à BFile car il y en a probablement au moins qui plante. Les codes d'erreur de BFile sont bien faits et permettent généralement de trouver rapidement les problèmes.
Citer : Posté le 19/03/2020 13:34 | #
Yo, alors quand tu dis "Les codes d'erreur de BFile sont bien faits et permettent généralement de trouver rapidement les problèmes", j'ai l'impression que toutes les lignes renvoient des erreurs ...
void save_write(int n) {
dclear(C_WHITE);
int a = BFile_Remove(filename);
int b = BFile_Create(filename, BFile_File, sizeof(n));
int fd = BFile_Open(filename, BFile_WriteOnly);
int c = BFile_Write(fd, &n, sizeof(n));
int d = BFile_Close(fd);
dprint(1, 1, C_BLACK, C_NONE, "a=%d", a);
dprint(1, 9, C_BLACK, C_NONE, "b=%d", b);
dprint(1, 18, C_BLACK, C_NONE, "c=%d", fd);
dprint(1, 27, C_BLACK, C_NONE, "d=%d", c);
dprint(1, 36, C_BLACK, C_NONE, "e=%d", d);
dupdate();
}
m'écrit à l'écran :
a = -1
b = -4
c = -1
d = -5
e = -5
Que dois-je en conclure ? (La première ligne doit "planter" puisque le fichier n'existe pas sur ma calto ...)
Citer : Posté le 19/03/2020 13:38 | #
Oui donc la première erreur on s'en fout, le fichier n'existe pas mais c'est pas grave.
La deuxième erreur à la création du fichier est IML_FILEERR_DEVICEFULL. Ta mémoire de stockage est peut-être pleine, ou pas optimisée, ou 4 octets pour un fichier ça ne passe peut-être pas.
Donc le fichier n'est pas créé donc évidemment tout le reste échoue.
La liste complète des codes est dans filebios.h (header de fxlib), je te la remets là :
#define IML_FILEERR_NOERROR 0
#define IML_FILEERR_ENTRYNOTFOUND -1
#define IML_FILEERR_ILLEGALPARAM -2
#define IML_FILEERR_ILLEGALPATH -3
#define IML_FILEERR_DEVICEFULL -4
#define IML_FILEERR_ILLEGALDEVICE -5
#define IML_FILEERR_ILLEGALFILESYS -6
#define IML_FILEERR_ILLEGALSYSTEM -7
#define IML_FILEERR_ACCESSDENYED -8
#define IML_FILEERR_ALREADYLOCKED -9
#define IML_FILEERR_ILLEGALTASKID -10
#define IML_FILEERR_PERMISSIONERROR -11
#define IML_FILEERR_ENTRYFULL -12
#define IML_FILEERR_ALREADYEXISTENTRY -13
#define IML_FILEERR_READONLYFILE -14
#define IML_FILEERR_ILLEGALFILTER -15
#define IML_FILEERR_ENUMRATEEND -16
#define IML_FILEERR_DEVICECHANGED -17
//#define IML_FILEERR_NOTRECORDFILE -18 // Not used
#define IML_FILEERR_ILLEGALSEEKPOS -19
#define IML_FILEERR_ILLEGALBLOCKFILE -20
//#define IML_FILEERR_DEVICENOTEXIST -21 // Not used
//#define IML_FILEERR_ENDOFFILE -22 // Not used
#define IML_FILEERR_NOTMOUNTDEVICE -23
#define IML_FILEERR_NOTUNMOUNTDEVICE -24
#define IML_FILEERR_CANNOTLOCKSYSTEM -25
#define IML_FILEERR_RECORDNOTFOUND -26
//#define IML_FILEERR_NOTDUALRECORDFILE -27 // Not used
#define IML_FILEERR_NOTALARMSUPPORT -28
#define IML_FILEERR_CANNOTADDALARM -29
#define IML_FILEERR_FILEFINDUSED -30
#define IML_FILEERR_DEVICEERROR -31
#define IML_FILEERR_SYSTEMNOTLOCKED -32
#define IML_FILEERR_DEVICENOTFOUND -33
#define IML_FILEERR_FILETYPEMISMATCH -34
#define IML_FILEERR_NOTEMPTY -35
#define IML_FILEERR_BROKENSYSTEMDATA -36
#define IML_FILEERR_MEDIANOTREADY -37
#define IML_FILEERR_TOOMANYALARMITEM -38
#define IML_FILEERR_SAMEALARMEXIST -39
#define IML_FILEERR_ACCESSSWAPAREA -40
#define IML_FILEERR_MULTIMEDIACARD -41
#define IML_FILEERR_COPYPROTECTION -42
#define IML_FILEERR_ILLEGALFILEDATA -43
Citer : Posté le 01/05/2020 12:02 | # | Fichier joint
Je viens de résoudre un bug parmi les plus bizarres (en termes d'apparence) que j'ai rencontrés, donc je voulais vous partager un peu l'histoire...
Ce bug est décevant parce qu'il est compliqué de l'intérieur mais possède une explication très simple. De loin, j'avais un programme qui dessinait une animation avec des images placées de façon aléatoires. Le moteur physique tournait à 50 Hz dans un timer et le moteur graphique tournait dans la boucle principale, à 50 Hz aussi parce que le rendu de chaque frame était bien plus rapide que 20 ms. J'utilisais ce vieux LCG pour avoir des nombres pseudo-aléatoires :
static uint32_t seed = 0xb7b7b7b7;
uint32_t rand(void)
{
seed = (214013 * seed + 2531011);
return (seed >> 16) & 32767;
}
Et l'animation finissait par planter sur une System ERROR au bout d'un certain temps. Variable, bien sûr.
Le premier indicateur d'un bug chiant c'est quand il faut 5 minutes montre en main pour le reproduire à chaque fois. Ça prend un temps fou. Alors j'ai commencé à réduire le code, et fini par trouver que le bug se produisait dans... une routine de bopti appelée par gimage(). "Génial" je me dis, "les bugs de bopti sont généralement des formules ratées, donc je vais relire le truc posément et trouver tout de suite. Il y a sans doute une position où mon flocon animé de 3x3 (un truc un peu extrême) n'est pas bien dessiné, et le bug prend du temps à se produire parce que les flocons sont placés aléatoirement et il faut un moment avant qu'un arrive pile à la bonne place."
Donc j'ai modifié le programme pour afficher des flocons à toutes les positions possibles en attendant le bug. Qui a disparu sur le champ. À ce stade, je suis déjà obligé de lancer l'add-in 15 ou 20 fois d'affilée parce que je ne sais pas si le bug a été corrigé ou s'il ne s'est simplement pas manifesté parce que j'ai pas eu de chance. En tous cas, à l'écran, rien. Je commence à restaurer le programme complet, boum le bug revient. ^^"
Donc là ça commence à m'énerver un peu, et je finis par mettre le doigt sur le fait que le timer qui lance le moteur physique doit être actif pour que le bug se manifeste. C'est un "bon signe" dans le sens où les timers, du fait qu'ils invoquent le callback un peu n'importe quand, sont souvent un coupable tout désigné pour les bugs non-déterministes. Sauf que ce callback fait que des calculs tous cons dans un tableau de flocons qui a une taille fixe, et appeler la fonction rand().
Je vous saute une bonne partie (~2 jours) que j'ai passée à tenter des myriades de choses pour comprendre le bug. J'ai cherché des dépassements de tableaux, j'ai cherché des recouvrements d'adresses dans le linker script, j'ai debuggé avec des rapports de crashs toujours plus tordus, lu le code assembleur produit par GCC des dizaines de fois, et rien. Entre-temps j'ai découvert que le moteur physique doit appeler rand() pour que le plantage se produise, et réussi à reproduire le bug à tous les coups en boostant sa fréquence de 50 Hz à 2000 Hz. À ce stade-là c'est certain que rand() fait planter le programme, mais comment ?
Du côté de bopti où ça plante, j'ai réussi à remarquer qu'une variable change de valeur subitement entre deux appels de fonction. Cette variable est une métrique de l'image à afficher, et comme elle devient complètement aléatoire, on dépasse complètement de l'image, ce qui provoque une erreur d'accès mémoire. Bien sûr une variable ne change pas de valeur toute seule, donc certainement l'interruption du timer a quelque chose à voir avec l'histoire. À ce moment-là je n'ai pas vu le lien avec le fait que la variable est calculée comme ça :
Vous le voyez peut-être déjà. J'ai continué à chercher comme un idiot en commentant les bouts de code que je pouvais jusqu'à ce que je trouve que la multiplication dans rand() était nécessaire pour faire planter le programme.
C'est le bon moment pour une parenthèse bas niveau. Dans le processeur, les calculs sont fait entre un certain nombre fixe de "registres" qu'on peut voir comme des variables. Généralement on met dedans des entiers et des pointeurs, et le processeur nous permet soit de faire des opérations arithmétiques soit d'aller lire ou écrire les valeurs pointées. On peut donc, par exemple, utiliser une instruction pour additionner les contenus de deux registres. Pour la multiplication, c'est un peu différent. D'une part la multiplication est une opération plus lente que les additions/soustractions de base, et d'autre part quand on multiple deux nombres de 32 bits le résultat fait jusqu'à 64 bits. Pour cette raison, le résultat d'une multiplication est stocké dans un registre mac de 64 bits qui sert exclusivement à ça.
Une autre chose à savoir est les conventions d'appel. Chaque fonction fait ses calculs avec les registres du processeur. Mais si une sous-fonction est appelée, elle va aussi utiliser les registres et donc remplacer les précieux résultats de calculs de son appelant. Pour cette raison, une sous-fonction va normalement sauvegarder et restaurer les registres qu'elle utilise pour que la fonction appelante puisse reprendre ses calculs après coup. Pour différentes raisons d'optimisation, il y a deux genre de registres : ceux que la sous-fonction sauvegarde avant d'utiliser, et ceux qu'elle peut utiliser et modifier sans avertissement, et que la fonction appelante doit sauvegarder elle-même si elle en a besoin.
Là je suis essentiellement à 15 minutes de trouver la solution. mac est un registre que l'appelant doit sauvegarder lui-même, autrement dit une sous-fonction comme rand() est libre de modifier sa valeur sans le restaurer après coup. Et ça c'est un gros problème... vous voyez, si le calcul de data_stride se fait interrompre pendant la multiplication, la valeur de mac peut être modifiée par rand() pendant l'interruption, ce qui provoque une corruption du résultat, et ensuite c'est la porte ouverte à toutes les dérives. Pour être exact, il faut que l'interruption se produise entre le moment où bopti exécute l'instruction de multiplication et le moment où il récupère le résultat.
Si bopti avait appelé la fonction rand() lui-même, bien sûr il aurait sauvegardé mac, car c'est la procédure. Mais une interruption ne s'annonce pas, elle apparaît de nulle part sans prévenir. Donc mac n'étant pas sauvegardé parce que bopti ne s'attendait pas à l'interruption, il était tout à fait possible de le corrompre par rand(). Il y a bien sûr d'autres registres que l'appelant doit sauvegarder et qui auraient pu provoquer ce bug, mais sans rentrer dans les détails ceux-là sont déjà sauvegardés automatiquement par un mécanisme matériel pour gagner du temps. mac est l'un des seuls candidats restants.
Pour vous montrer le côté pété de ce bug, voici les trois endroits où bopti fait une multiplication autour du calcul de data_stride. Les instructions mul exécutent la multiplication, et les sts macl lisent le résultat dans mac. Tout ce qu'il y a entre les deux n'a rien à voir, et a été mis là par le compilateur pour optimiser le programme. En effet, comme la multiplication prend 5 cycles à calculer, si on met le sts juste derrière le mul, il faut attendre 5 cycles pour avoir le résultat. On peut, à la place, faire quelques calculs en attendant le résultat pour gagner du temps.
303c32: 47 2c shad r2,r7
303c34: 02 1a sts macl,r2
303c3a: 04 1a sts macl,r4
303c78: 16 75 mov.l r7,@(20,r6)
303c7a: e1 ff mov #-1,r1
303c7c: 07 29 movt r7
303c7e: 2a a8 tst r10,r10
303c80: 61 1a negc r1,r1
303c82: 16 19 mov.l r1,@(36,r6)
303c84: 05 1a sts macl,r5
Dans les deux premiers extraits, il y a 5 cycles où l'interruption peut se produire entre le mul et le sts, et dans le troisième il y a 7 cycles. Le processeur tourne à 30 MHz, ce qui laisse à l'interruption 0.5 µs pour s'insérer dans un des slots et provoquer le bug en corrompant mac par un appel à rand() avant que le résultat ne soit lu.
La solution de ce bug, je pense que vous l'avez devinée maintenant, ce que j'aurais dû moi-même programmer l'interruption du timer pour sauvegarder mac avant d'appeler le callback. Un oubli qui pose des questions sur les appels par pointeur de fonction, d'ailleurs... x)
Voilà qui est corrigé en tous cas, et vous voyez qu'on ne déconne pas avec les bugs qui ont une poignée de cycles pour se manifester. Parce qu'ils finissent toujours par le faire.
En fichier joint, quelques lignes que j'ai écrites sur #dev au moment où j'ai trouvé le problème.
Citer : Posté le 08/05/2020 17:47 | #
Mmmh, petit problème par ici…
Clonage dans 'gint'...
remote: Énumération des objets: 2485, fait.
remote: Décompte des objets: 100% (2485/2485), fait.
remote: Compression des objets: 100% (1325/1325), fait.
remote: Total 2485 (delta 1611), réutilisés 1681 (delta 1078), réutilisés du pack 0
Réception d'objets: 100% (2485/2485), 1023.54 Kio | 10.55 Mio/s, fait.
Résolution des deltas: 100% (1611/1611), fait.
warning: la HEAD distante réfère à une référence non existante, impossible de l'extraire.
Faut effectivement checkout la branche compat, mais du coup si c'est la branche par défaut, y'a pas moyen de la merge sur master histoire d'avoir un HEAD propre ?
Citer : Posté le 08/05/2020 18:00 | #
Je voulais le faire à la prochaine release... je vais me débrouiller pour publier une bêta *vite* et la fusionner.
Citer : Posté le 08/05/2020 18:20 | #
Ok, nice
Ajouté le 08/05/2020 à 19:03 :
Bon, du coup le paquet AUR de gint en pâtit
==> Création du paquet gint-git r155.61da7de-1 (ven. 08 mai 2020 18:59:29)
==> Vérification des dépendances pour l’exécution…
==> Vérification des dépendances pour la compilation…
==> Récupération des sources…
-> Mise à jour du dépôt gint-git git…
Récupération de origin
==> Validation des fichiers source avec sha256sums…
gint-git ... Ignoré
==> Extraction des sources…
-> Création d’une copie de travail du dépot gint-git git…
fatal: 'origin/HEAD' n'est pas un commit et une branche 'makepkg' ne peut pas en être créée depuis
==> ERREUR : Échec lors de la création d’une copie de travail du dépôt gint-git git
Abandon…
Je vais essayer de trouver un workaround, mais je suis pas hyper confiant.
Ajouté le 08/05/2020 à 19:11 :
Trouvé. On peut sélectionner un commit particulier : https://bbs.archlinux.org/viewtopic.php?id=247781
Par contre ça veut dire que le paquet ne sera pas compilé automatiquement avec la dernière version disponible tant que la branche master sera pas ok.
Ajouté le 08/05/2020 à 19:17 :
Bon, du coup le paquet gint-git est à jour o/
Citer : Posté le 08/05/2020 21:13 | #
Merci ! Tu peux juste demander la branche compat et ce sera bon pour l'instant