FxLibC: La bibliothèque standard C pour les calculatrices
Posté le 22/10/2020 17:39
Salut à toi jeune développeur !
FxLibC est une bibliothèque standard C basé sur l'interface que propose GLIBC, la "vrai" bibliothèque standard.
Alors, je sais, ce n'est pas la première fois qu'une telle initiative est prise mais j'ai de bonnes raisons de le faire :
L'OS de Casio n'est pas adapté pour oser faire un portage de la Glibc original car trop de mécanismes sont manquants et la plupart des abstractions fournies par Casio sont, au mieux, bancales. C'est pourquoi j'ai décidé de refaire une librairie de 0 qui permettra de tirer toute la puissance des OS et permettra d'avoir une interface commune pour les OS Casio monochrome/couleur et des noyaux custom comme Vhex.
Pour l'instant, c'est juste un "proof of concept" où la lib peut être compilée, installée et désinstallée en fonction de l'OS et du format* mais en matière de fonctionnalité, il n'y pas grand-chose d'incroyable, seules quelques fonctions basiques y sont implémenté. Je continuerai de l'améliorer au fur et à mesure de mes expérimentations / projets qui, je l'espère, arriverons bientôt.
Pour pas oublier, voici le lien vers le dépôt Git :
> Lien vers le dépôt Gitea, Vhex-Kernel-Core/fxlibc <
Mes prévisions pour la bibliothèque :
Changement de la gestion des versions
C'est une des choses que je n'ai jamais vraiment regardées mais je vois de plus en plus d'intérêt à avoir une gestion des versions correctes si on veut avoir quelque chose de maintenable dans le temps. Actuellement, la version est obtenable uniquement avec
make version et elle est mise a la main dans le makefile. J'avais pensé a une gestion automatique des versions basées sur le git: je cherche le dernier commit avec un tag (tag qui contiendra le major.minor (ex: 2.6)) et le nombre de commit après le tag indique la version des patchs. Mais je ne sais pas s’il y a moyen plus simple et plus "automatisant"(?)
Abstraction de Bfile (l'interface mise en place par Casio pour gérer les fichiers)
Comme je vais devoir implémenter les "appels système"
open(), lseek(), close(), etc ; Je me suis dit que j'aurais tout intérêt à faire une vraie abstraction de Bfile car elle apporte trop de contrainte (par exemple, on doit spécifier la taille du fichier qu'on veut créer, il y a quelque subtilité avec l'écriture, c'est lent, ...) Donc j'hésite entre :
1) faire juste des wrappers autour des appels système Bfile: le plus simple mais on aura la plupart des contraintes imposées par le Bfile.
2) faire une plus grosse abstraction sur le Bfile: mais ça implique de "monter" le système de fichier au début du programme, potentiellement plus rapide mais on n'échappera pas aux limitations de création et d'écriture dans un fichier).
3) embarquer un système de fichiers custom en RAM et enregistré les informations uniquement quand l'addins retourne au menu : mais ça pose beaucoup de problèmes organisationnel et architectural car je trouve qu'un FS n'a pas sa place dans une bibliothèque standard. Est-ce qu'il faudrait faire un projet à part ? Si oui alors : comment on relit les deux projets ensemble sans que ce sois infernal pour les développeurs ? Sur quel type de format on part ? Comment l'installer proprement en RAM et où ? Comment et quand installer le FS ? Comment on y accède si le système est isolé de la librairie ?
Personnellement je suis plus pour la 3ᵉ options car elle permettra d'avoir un vrai abstraction sur le FS de Casio et on pourrait implémenter beaucoup plus de fonctions pour la manipulation et la sécurité des fichiers (en plus de gagner en performance(?)) mais ça pose beaucoup de problèmes. À méditer.
Abstraction de la gestion de la mémoire
Est-ce qu'on continue d'utiliser des wrappers autour des appels système pour la gestion de la mémoire (malloc, calloc, free, ...) ? Ou est-ce qu'il faudrait mieux refaire cette partie dans la lib ? Actuellement, j'utilise l'ABI de Casio et Vhex pour la gestion de la mémoire car du côté de Casio c'est flou et du côté de Vhex je n'ai pas le choix car l'entièreté de l'environnement (processus, kernel, FS, ...) est en RAM et je n'ai pas le MMU à disposition. Donc est-ce que ça vaut vraiment le coup d'implementer un tas custom, sachant qu'on aura surement rien de commun entre les différents OS ?
Écriture de la documentation
C'est une des parties cruciales si on veut avoir quelque chose d'utilisable et d'améliorable, seulement je ne sais encore sur quel médium faire la documentation : sur le wiki, faire un topic, générer une documentation HTML / PDF ? D'ailleurs, ça me fait penser, es-ce que la v5 de planet-casio améliore cette partie ? J'entends par là, est-ce qu'il y aura un moyen de synchroniser la bible, le wiki du Gitea et le topic en même temps ? Ça pourrait être extrêmement intéressant de centraliser les projets ainsi que leurs documentations / topics.
Support des librairies dynamique pour Vhex
C'est une des fonctionnalités cruciale pour Vhex car la version 2.0 repose entièrement sur le mécanisme de pouvoir relocaliser / charger des fonctions à la volée. Mais malheureusement, GCC semble ignorer les flags pour la génération des bibliothèques (pourtant on peut créer des exécutables en PIE !). Bref, j'avais envoyé
un mail à GCC pour avoir des informations mais je n'ai toujours pas eu de réponse.
Optimisation des fonctions
Hé oui, l'intérêt d'avoir une lib complètement indépendante des OS et communautaire, c'est qu'on va pouvoir optimiser la plupart des fonctions basique ! Attention toute fois, s’il vous prend l'envie de participer, évitez d'écrire du code utilisant des modules hardware pouvant générer des interruptions (comme le DMA et le SPU) a moins d'être absolument certain que le système d'exploitation est capable de les gérer. Mais n'oubliez pas que le but de la lib est d'être la plus indépendante possible vis-a-vis des OS pour faciliter son portage.
Actuellement, rien n'est optimisé et rien n'est garantie de fonctionner comme il est décrit dans la norme POSIX ou dans la documentation de la glibc mais à l'avenir, j'espère pouvoir les respecter le plus fidèlement possible.
D'ailleurs, si vous voulez participer au projet n'hésitez pas ! J'ai besoin de pas mal de retour pour que la bibliothèque puisse être un jour utilisable par d'autre personnes que moi
Citer : Posté le 23/11/2021 19:52 | #
Je suis déjà en train de coder l'API Unix (open(), read(), write(), etc) et ça se passe pas trop mal (sur la Graph 90+E pour l'instant).
Perso, je préfère un fs lent mais fonctionnel. J'achète ce changement et une fois dans la libc je pense que je me servirai beaucoup plus du système de fichiers !
J'achète !!! Dis nous quand tu as un truc qui te parait suffisamment fonctionnel pour faire un "push" de version.
Je suis preneur aussi d'un fprintf/fscanf perso ;-)
Je sais j'abuse :-)
Citer : Posté le 23/11/2021 19:56 | #
Je suis preneur aussi d'un fprintf/fscanf perso ;-)
J'aurais pas besoin d'écrire un truc a moitié buggé du coup
Citer : Posté le 23/11/2021 19:58 | #
Merci ! fprintf ok, fscanf j'ai pas encore codé le scanf donc ça attendra xD
Ajouté le 23/11/2021 à 23:07 :
J'ai commencé à tester sur la Graph 35+E II... c'est instable. Va falloir creuser un peu.
Citer : Posté le 24/11/2021 18:33 | #
Merci ! fprintf ok, fscanf j'ai pas encore codé le scanf donc ça attendra xD
Ajouté le 23/11/2021 à 23:07 :
J'ai commencé à tester sur la Graph 35+E II... c'est instable. Va falloir creuser un peu.
Est-ce vraiment abuser si je demande un "dirent.h" et les fonctions/structures qui vont bien (opendir/readdir ...) à long terme ?
Citer : Posté le 24/11/2021 18:42 | #
Hmm ça va dépendre si l'API de la calculatrice est assez précise. Pour l'instant je pense que je sais faire glob() mais pas plus (et c'est instable de mémoire). Il faudra voir au fur et à mesure
Citer : Posté le 25/11/2021 17:32 | #
salut! je suis sans calculatrice mais je fais un jeu sur SDL2 avec la résolution d'une calculatrice (un port gint du jeu pourra être fait en 2h)
j'utilise math.h pour utiliser cos() et sin()
int xPoint, yPoint;
float x, y;
angle = angle * PI / 180;
xPoint = point.x - center.x;
yPoint = point.y - center.y;
x = xPoint * cos(angle) + yPoint * sin(angle) + center.x;
}
mais j'obtiens
/usr/bin/ld : /lib/x86_64-linux-gnu/libm.so.6 : erreur lors de l'ajout de symboles : DSO manquant dans la ligne de commande
collect2: error: ld returned 1 exit status
Citer : Posté le 25/11/2021 18:42 | #
Il faut ajouter -lm à ta ligne de commande pour obtenir la lib mathématique (sur gint c'est -lopenlibm mais ça revient au même)
Citer : Posté le 04/12/2021 22:07 | #
Plusieurs personnes m'ont demandé récemment de supporter <time.h> dans la lib standard. J'ai donc codé ça la semaine dernière, ce qui a été assez vite puisque j'avais déjà un prototype.
Je supporte essentiellement tout le C99 à part des trucs relativement marginaux genre l'heure d'été (DST), les timezones, et la numérotation alternative des années par les semaines (dans strftime()).
Voilà les principales fonctions disponibles, pour situer (voir aussi <time.h>) :
Notez que si la RTC est pas bien configurée vous risquez pas d'avoir des dates utiles, ça on n'y peut rien. La RTC se réinitialise en plus à chaque reset, ce qui est très chiant.
Edit pour @Yatis : Pour Vhex il te suffit de supporter clock() et time(), tout le reste tu peux le récupérer.
Citer : Posté le 05/12/2021 21:21 | #
Il y a un peu moins de deux semaines, j'ai commencé à jouer avec les fichiers, mais le code sur Graph 35+E II avait tendance à crasher :
J'ai commencé à tester sur la Graph 35+E II... c'est instable. Va falloir creuser un peu.
C'est maintenant résolu. Le problème était causé par un dépassement de pile ; sur SH4 je limitais la taille de la pile à 8 ko, ce qui est tout à fait suffisant pour les programmes écrits raisonnablement, ie. qui utilisent malloc() pour tous les trucs un peu gros (ce qui est de façon générale la bonne chose à faire). Malheureusement BFile est plus consommateur que ça et pouvait dépasser, écrasant ce que se situe avant la pile (les gestionnaires d'interruption).
Ce commit implémente la solution, ce qui me permet de revenir à la gestion des fichiers.
Excuses spéciales à KikooDX, j'ai vraiment essayé de faire marcher le truc vite, des fois c'est juste pas possible. ^^"
Citer : Posté le 06/12/2021 22:04 | #
On progresse ! Pour information j'ai réussi à faire marcher ça sur ma Graph 90+E et ma Graph 35+E II.
write(fd, "_ft_unistd_simple_read", 22);
close(fd);
fd = open("ft_read.txt", O_RDONLY);
char buffer[29] = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx";
read(fd, buffer, 22);
close(fd);
// buffer = "_ft_unistd_simple_readxxxxxxx"
Je supporte aussi quelques flags de open() qui sont pas visibles ici, creat() et unlink(). Sur Graph 35+E II c'est assez lent, mais au moins c'est fonctionnel (la lenteur c'est pas de ma faute c'est le fs lui-même).
Notez que sur les autres modèles mono qui ont l'ancien système de fichiers toutes ces fonctions renvoient un code d'erreur avec errno = ENOSUP ("Operation not supported"), parce que c'est juste impossible de faire une API standard avec. (Pour rappel, les fichiers doivent être créés avec une taille fixe, on ne peut écrire qu'un nombre pair d'octets, on ne peut que changer les 1 en 0, pas l'inverse, etc.) Dans certains cas si on utilisait mon API en cours on pourrait bloquer la mémoire de stockage donc les protections sont là pour l'empêcher. Sur ces modèles on peut toujours utiliser BFile directement.
Bonnes évolutions en perspective !
PS. J'ai un squelette de scanf() Slyvtt mais c'est un peu loin encore, je te dirai.
Citer : Posté le 06/12/2021 22:14 | #
On progresse ! Pour information j'ai réussi à faire marcher ça sur ma Graph 90+E et ma Graph 35+E II.
write(fd, "_ft_unistd_simple_read", 22);
close(fd);
fd = open("ft_read.txt", O_RDONLY);
char buffer[29] = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx";
read(fd, buffer, 22);
close(fd);
// buffer = "_ft_unistd_simple_readxxxxxxx"
Je supporte aussi quelques flags de open() qui sont pas visibles ici, creat() et unlink(). Sur Graph 35+E II c'est assez lent, mais au moins c'est fonctionnel (la lenteur c'est pas de ma faute c'est le fs lui-même).
Bonnes évolutions en perspective !
PS. J'ai un squelette de scanf() Slyvtt mais c'est un peu loin encore, je te dirai.
Génial, ca avance bien. Ca va servir dans plein de prochain pour sûr ...
Ne serait-ce que pour debugger, c'est hyper important.
+1
Citer : Posté le 06/12/2021 22:16 | #
Note que tu peux déjà debugger par USB si jamais ça urge
Citer : Posté le 06/12/2021 22:19 | #
Note que tu peux déjà debugger par USB si jamais ça urge
Oui j'ai repéré ça mais pas encore testé. Faut que je regarde mieux.
J'ai encore plein de choses à tester, je suis un newbie
Citer : Posté le 08/12/2021 23:09 | #
Une subtilité avec laquelle j'espérais me débrouiller c'est que l'API Graph 35+E II est vaguement héritée de celle des Graph mono précédentes, qui ont le système de fichiers tout nul. Pas mal de fonctions ont changé de sémantique (par exemple BFile_Read() renvoie bien le nombre d'octets lu, pas la quantité de données restant à la lire dans le fichier), ce qui est bien. Par contre il n'y a pas de fonction permettant de connaître la position actuelle dans le fichier.
J'ai cherché sans trouver de solution satisfaisante, donc pour ce qui est de l'API Unix la Graph 35+E II ne supporte pas lseek() avec SEEK_CUR ; et pour l'API standard C elle ne supportera pas ftell() ni fseek() avec SEEK_CUR, au moins dans un premier temps.
On pourrait imaginer traquer la position nous-mêmes, mais on ne sait pas trop ce qui peut se passer en cas d'erreur, et ça casserait complètement si un appel est fait hors de l'API, ce que je n'aime pas trop.
Citer : Posté le 09/12/2021 08:38 | #
Une subtilité avec laquelle j'espérais me débrouiller c'est que l'API Graph 35+E II est vaguement héritée de celle des Graph mono précédentes, qui ont le système de fichiers tout nul. Pas mal de fonctions ont changé de sémantique (par exemple BFile_Read() renvoie bien le nombre d'octets lu, pas la quantité de données restant à la lire dans le fichier), ce qui est bien. Par contre il n'y a pas de fonction permettant de connaître la position actuelle dans le fichier.
J'ai cherché sans trouver de solution satisfaisante, donc pour ce qui est de l'API Unix la Graph 35+E II ne supporte pas lseek() avec SEEK_CUR ; et pour l'API standard C elle ne supportera pas ftell() ni fseek() avec SEEK_CUR, au moins dans un premier temps.
On pourrait imaginer traquer la position nous-mêmes, mais on ne sait pas trop ce qui peut se passer en cas d'erreur, et ça casserait complètement si un appel est fait hors de l'API, ce que je n'aime pas trop.
Question certainement idiote, mais ne peux tu pas opérer par différence ?
Si tu sais la taille complète du fichier avec BFile_Size() et que tu sais ce que tu as lu avec BFile_Read(), ne peux tu pas contourner le problème ?
si fseek( SEEK_SET ) : tu fermes le fichier et le réouvre pour te mettre au début
si fseek( SEEK_END ) : tu regardes où tu es actuellement et tu lis le reste (d'ailleurs à mon avis le soucis sera alors avec rewind(), pas avec fseek, mais ca revient à fermer et réouvrir le fichier)
si fseek( SEEK_CUR ) : tu sais ou tu es avec BFile_Size() donc c'est bon, non ?
Note : c'est peut être une grosse bêtise ce que je raconte ;-), si tel est le cas, mea culpa.
Citer : Posté le 09/12/2021 09:01 | #
Globalement oui mais en pratique c'est pas assez solide. D'une part, le curseur se déplace quand tu lis et écris mais il peut potentiellement aussi se déplacer durant d'autres opérations internes. Ensuite l'ensemble des positions valides n'est pas trivial ; le fichier a toujours une taille réservée qui est multiple de 4096 octets, et le système de fichiers peut t'autoriser à avancer jusque-là, ou même à avancer à n'importe quelle longueur en étendant le fichier. Si tu fais le moindre appel à l'API Unix ou BFile sans me consulter je perdrai l'information. Et surtout en cas d'erreur on ne sait pas forcément de combien le curseur bouge.
Le risque c'est qu'il y ait une perte de synchronisation entre la valeur que je cache et la valeur réelle, et là tout peut casser de façon plus ou moins prévisible (plutôt moins que plus). Pour l'instant je préfère ne pas rajouter de bugs et intervenir si ce manque devient un problème.
Note qu'on a déjà BFile_Seek() qui permet de se déplacer à une position absolue. Ça implémente trivialement SEEK_SET. BFile_Size() donne la taille du fichier ce qui donne SEEK_END. On ne peut juste pas connaître la position actuelle.
Note que
Ce serait vachement lent, il vaut mieux utiliser BFile_Seek(fd, <offset>)
Lire le reste serait aussi très lent surtout si le fichier est gros ; heureusement on peut faire BFile_Seek(fd, BFile_Size(fd) - offset). Fermer et rouvrir le fichier est un peu casse-pieds parce que le descripteur de fichiers changerait donc il faudrait ajouter un niveau de traduction (faisable, juste pas idéal).
BFile_Size() ne renvoie que la taille totale donc ça n'aide pas particulièrement (il faut quand même traquer la position absolue indépendamment).
Citer : Posté le 09/12/2021 09:25 | #
Effectivement ca relève de la débrouille et il faut jouer avec la lenteur du fs.
Bon après c'est pas forcément un gros souci dans la majeure partie des utilisations si on a pas tout.
Perso le fseek/ftell ne me serviront pas très souvent.
C'est plutôt lors de la construction de bibliothèques plus bas niveau que cela risque de servir le plus.
En tout cas, chapeau pour tout le taf que tu fais.
Citer : Posté le 09/12/2021 09:27 | #
Merci. Oui j'ai un poil peur que des libs s'appuient dessus. Mon espoir secret c'est de mettre la main sur un syscall BFile_GetPos() similaire à celui de la Graph 90+E, mais bon c'est pas gagné d'avance.
Citer : Posté le 09/12/2021 09:28 | #
D'ailleurs, en relisant ton post initial, je vois "API Graph 35+E II" (j'avais zappé cette partie de la ligne), ca veut dire que sur Graph 90+E / CG50 c'est plus ouvert ?
Citer : Posté le 09/12/2021 13:37 | #
Oui ; enfin c'est la seule différence. C'est le même système de fichiers entre les deux si tu veux. Le truc c'est qu'on appelle le système de fichiers à travers des syscalls, et la Prizm / Graph 90+E a toujours eu le nouveau système de fichiers donc sa liste de syscalls a été conçue avec BFile_GetPos() tout prêt dedans. Alors que la Graph 35+E II hérite de l'ancienne liste/numérotation de syscalls de Graph mono, qui était faite pour l'ancien système de fichiers dont l'API maison n'a pas de BFile_GetPos() (et en fait on pouvait tricher pour obtenir l'information autrement).
Citer : Posté le 31/12/2021 10:45 | #
Nouvelle version : fxlibc 1.3.0
Release associée de gint : gint 2.7.0
Release associée du fxSDK : fxSDK 2.7.0
Cette release ajoute les fonctions du <time.h> C99, ainsi que les en-têtes <unistd.h>, <fcntl.h>, <dirent.h> et <sys/stat.h>. Pour ces derniers la fxlibc ne donne pas de code, c'est gint/vhex qui le fournit (gint 2.7.0 notamment).