Seuls les membres ayant 30 points peuvent parler sur le chat.

Forum Casio - Projets de programmation


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

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

Posté le 20/02/2015 17:30

Les SDKs classiques pour écrire des add-ins sont le fx-9860G SDK de Casio avec fxlib (pour Graph monochrome) et le PrizmSDK avec libfxcg (pour Prizm et Graph 90+E). Voici mon alternative : le fxSDK avec gint, pour toutes les plateformes.

Contrairement à fxlib et libfxcg, qui appellent les fonctions de l'OS pour faire leur travail, gint est un noyau indépendant de l'OS qui exploite seul le matériel et le met à disposition de votre add-in. Il vous offre plus de finesse sur le contrôle du matériel, notamment le clavier, l'écran et les horloges, de meilleurs performances sur le dessin, les drivers et la gestion de interruptions, et des choses entièrement nouvelles comme le moteur de gris.

Toutes les sources de gint sont publiques et accessibles sur la forge de Planète Casio :

» Dépôt Gitea Lephenixnoir/gint «

Voici plus précisément ce que gint vous offre de nouveau :

• Un contrôle détaillé du clavier pour les jeux, parfait pour les combos !
• Des timers avec une précision de 60 ns, d'autres à 30 µs
• Toutes vos images converties automatiquement sans code à copier (plus de Sprite Coder)
• Des polices personnalisées
• Des fonctions de dessin, d'images et de texte fulgurantes et optimisées la main
• Mesurer les performance de votre code à la microseconde près (avec libprof)
• Le contrôle du matériel et des interruptions
• Plein de petites choses pratiques comme dprint(1, 1, "x=%d", x)

• (Graph monochrome) Un moteur de gris pour faire des jeux en 4 couleurs !
• (Graph monochrome) La compatibilité SH3 et SH4, avec le même fichier g1a.

• (Graph 90+E) Une nouvelle police de texte, plus lisible et économe en espace
• (Graph 90+E) Le dessin en plein écran, sans les bordures blanches et la barre de statut !
• (Graph 90+E) Un driver écran capable de triple-buffering

Le coût de tout ceci, c'est que vous avez une copie du code de gint dans votre add-in. Cela prend environ 20 ko de place (selon la quantité de fonctions que vous utilisez), soit à peu près comme le sprintf() de fxlib qui fait 18 ko !

Et voici quelques photos et captures d'écran !





Tester gint sur votre machine

La fin du portage vers la Graph 90+E signera la sortie de gint v2. L'add-in de test de l'application est désormais gintctl :

» Dépôt Gitea Lephenixnoir/gintctl «

En plus de tester les fonctionnalités de gint, cet add-in contient quelques outils permettant d'inspecter la machine, la mémoire, et les registres. Je le développe au fur et à mesure, et je posterai un protocole de test complet avec la sortie de la v2 !

Utiliser gint pour développer des add-ins

Normalement, vous avez besoin du fxSDK pour développer avec gint. Le fxSDK est compatible avec Linux et Mac OS, et on peut réfléchir à un portage sous Windows s'il y a vraiment des intéressés. Il faut l'installer en premier (et avoir un cross-compilateur GCC).

La procédure de compilation et d'installation de gint est décrite sur le README du dépôt, c'est du configure - make tout à fait banal.

Une fois que gint est installé sur votre système, voyez les tutoriels de développement pour avoir un aperçu de son fonctionnement. La plupart des choses sont expliquées dans les en-têtes (fichiers .h) de la bibliothèque que vous pouvez consulter en ligne, sur votre copie locale du dépôt, ou dans les dossiers d'installation du compilateur.

Obtenir la dernière version de gint après une mise à jour

Je pousse régulièrement des mises à jour de gint sur le dépôt du projet. Pour les télécharger, tapez git pull, puis recompilez et réinstallez gint avec make et make install.


Fichier joint


Précédente 1, 2, 3 ··· 10 ··· 20 ··· 30 ··· 35, 36, 37, 38, 39, 40 Suivante
Lephenixnoir En ligne Administrateur Points: 17842 Défis: 142 Message

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.
Teusner Hors ligne Membre Points: 107 Défis: 0 Message

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)
Lephenixnoir En ligne Administrateur Points: 17842 Défis: 142 Message

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 :

charset:print grid.size:3x5 grid.border:1

Teusner Hors ligne Membre Points: 107 Défis: 0 Message

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 !
Lephenixnoir En ligne Administrateur Points: 17842 Défis: 142 Message

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.
Teusner Hors ligne Membre Points: 107 Défis: 0 Message

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 :
int main() {
    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*
Lephenixnoir En ligne Administrateur Points: 17842 Défis: 142 Message

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 ».
Teusner Hors ligne Membre Points: 107 Défis: 0 Message

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 ...

int main() {
    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 :

int main() {
    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
Lephenixnoir En ligne Administrateur Points: 17842 Défis: 142 Message

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 ^^")

KikooDX a écrit :
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.
Teusner Hors ligne Membre Points: 107 Défis: 0 Message

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)

int angle_callback() {
    gclear(C_WHITE);
    gimage(13, h-3, &img_cross);
    gupdate();
    return 0;
}


Et la
Spoiler
Ca ne marche toujours pas
Lephenixnoir En ligne Administrateur Points: 17842 Défis: 142 Message

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.
Teusner Hors ligne Membre Points: 107 Défis: 0 Message

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 :
static const unsigned short filename[] = {
    '\\', '\\', '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 ?
Yatis En ligne Membre Points: 498 Défis: 0 Message

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.
Lephenixnoir En ligne Administrateur Points: 17842 Défis: 142 Message

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 :

static const unsigned short filename[] = u"\\\\fls0\\space.sav";
/* 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.
Teusner Hors ligne Membre Points: 107 Défis: 0 Message

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 ...

static uint16_t const *filename = u"\\\\fls0\\space.sav";

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 ...)
Lephenixnoir En ligne Administrateur Points: 17842 Défis: 142 Message

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à :

// File system standard error code
#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
Lephenixnoir En ligne Administrateur Points: 17842 Défis: 142 Message

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 :

/* Quick and bad random number generator */
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 :

      .data_stride  = ((img_columns - columns) << 2) * layers,

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.

  303c30:       0c 17           mul.l   r1,r12
  303c32:       47 2c           shad    r2,r7
  303c34:       02 1a           sts     macl,r2

  303c38:       02 37           mul.l   r3,r2
  303c3a:       04 1a           sts     macl,r4

  303c76:       01 37           mul.l   r3,r1
  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.
Dark storm En ligne Membre d'honneur Points: 11051 Défis: 176 Message

Citer : Posté le 08/05/2020 17:47 | #


Mmmh, petit problème par ici…

git clone https://gitea.planet-casio.com/Lephenixnoir/gint.git
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 ?
Finir est souvent bien plus difficile que commencer. — Jack Beauregard
Lephenixnoir En ligne Administrateur Points: 17842 Défis: 142 Message

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.
Dark storm En ligne Membre d'honneur Points: 11051 Défis: 176 Message

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

~/P/P/gint-git $ makepkg -i
==> 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/
Finir est souvent bien plus difficile que commencer. — Jack Beauregard
Lephenixnoir En ligne Administrateur Points: 17842 Défis: 142 Message

Citer : Posté le 08/05/2020 21:13 | #


Merci ! Tu peux juste demander la branche compat et ce sera bon pour l'instant
Précédente 1, 2, 3 ··· 10 ··· 20 ··· 30 ··· 35, 36, 37, 38, 39, 40 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
Pour coloriser votre code, cliquez ici.
Sinon cliquez sur le bouton ci-dessous.
: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 v42 © créé par Neuronix et Muelsaco 2004 - 2020 | Il y a 70 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