libcarrot - une alternative bonne pour la santé à fxlib :
Disclaimer : ceci n'est absolument pas destiné à être un gros projet ou quoi, simplement un support de hacking rapide. Si vous voulez suivre un projet qui vaut beaucoup plus le coup que celui-ci, jetez un coup d'oeil à gint.
Jusqu'ici, pour développer pour fx-9860G et dérivées, il y avait deux pillules libs : la fxlib, apparue en 2006 avec le SDK officiel de CASIO et intégrée à ce dernier, et gint, une lib qui prend le contrôle sur la machine pour avoir un contrôle beaucoup plus poussé sur ce dernier. Idéalement, il faudrait utiliser gint pour tout projet, mais voilà : gint est un projet qui date d'il y a deux ans (il me semble), et même si son développeur, Lephenixnoir, continue de l'écrire, certaines fonctionnalités pourtant utiles manquent encore à l'appel, tels que la communication USB. Jusqu'ici, pour du hacking rapide, j'essayais de me servir de la fxlib (donc du système, qui propose déjà un support pour son propre matériel), seulement, elle aussi a des failles : son portage pour GNU/Linux a quelques ratés (notamment la fonction sprintf), et elle ne respecte que le standard C89 alors qu'avec GCC, il est possible de développer en C11, donc il manque des fonctions, headers et macros.
La libcarrot est une réponse à ces problèmes. Elle utilise le système pour interagir avec le matériel, mais réimplémente certaines fonctions histoire de ne pas être trop lent non plus. Son but est d'être C11-compliant, mais ça ne sera clairement pas immédiat, puisque j'implémenterai ces fonctionnalités-là en fonction de mes besoins. Elle incluera aussi une compatibilité avec des libs populaires, telles que la fxlib ou la MonochromeLib.
Mon but avec la libcarrot est d'avoir une lib qui permette de faire une app rapidement, principalement pour pouvoir hacker facilement (e.g. faire une app qui utilise la libp7 pour afficher les infos d'une autre calculatrice branchée via série).
C'est un projet très jeune, donc pour le moment, rien n'est prêt. Je fais ce topic pour que les efforts ne se dilapident pas en projets similaires, et je ne pense pas avoir vu de projet similaire avant pour la fx-9860G (contrairement à, par exemple, la libfxcg pour la Prizm).
Oh, ben voilà qui pallie le défaut majeur de gint (enfin après celui de ne pas encore être sorti ). Je pourrai m'en inspirer pour implémenter la comm' ?
Je te conseille pas. Non pas que je copie/colle les noms en uppercase de Simon Lothar inspirés des noms de fonctions de CASIO, mais un peu beaucoup quand même.
A mon avis pour être utilisé, il faut pouvoir tester rapidement son prog. C'est l'avantage du SDK : il y a un émulateur. Si le transfert à lieu automatiquement après la compilation c'est parfait ! 8)
C'est assez facile à mettre en place Ninestars, avec P7.
Autrement, j'ai mis en place un SDK basique directement dans le projet de la lib, dont il n'y a pas besoin de s'y connaître particulièrement en Makefile pour faire son propre projet utilisant la libcarrot ; pour un projet basique (n'utilisant aucune autre lib), un Makefile d'exemple se trouve sur la page du projet (section Example Makefile using the SDK).
Et du coup, j'ai pu faire un Hello World exploitant ce SDK -- rien de transcendant, juste "I am Groot" puis on attend une key pour quitter. Et à part quelques pixels qui partent en cacahuète (j'étudierai le pourquoi de ça plus tard), c'est fonctionnel, je devrais pouvoir organiser les contributions dans pas très longtemps.
Ajouté le 16/04/2017 à 23:38 :
Je bosse pas mal sur la reproduction des *machine.h en ce moment, et de transformer les built-ins du compilateur d'Hitachi en macros pour GCC, histoire d'asseoir la domination de la communauté. Les *machine.h, en gros, c'est de l'assembleur SuperH en C (Hitachi/Renesas donne un nom stylé à ça, je l'ai plus en tête). Le problème, c'est que la majorité des macros des *machine.h de la libc d'Hitachi mènent en réalité aux built-ins correspondants dans le compilateur C/C++ d'Hitachi, et que ces built-ins ne sont pas dans GCC -- pour les rendre accessibles avec celui-ci, il faut donc jouer de macros et de fonctions inlines utilisant asm().
Ce soir, je viens vous montrer le résultat d'une expérience autour du portage de la macro macw(p1, p2, count), une macro plutôt problématique. En effet, cette macro, en plus de commencer clrmat et de sauver MACL dans la stack, exécute count fois l'instruction MAC.W @<p1>+, @<p2>+ : copier | log complet
Mon premier réflexe a été d'utiliser une boucle dans une fonction inline (puisqu'on ne peut pas utiliser une macro pour répeter une string par rapport à une valeur définie dans une autre macro, hélas), pensant que GCC optimiserait ça, mais non, puisque le count est passé en argument. J'ai donc cherché à générer une lambda générée avec un count bien précis pour l'appeler, et heureusement, il y a une extension GCC pour ça. Seulement, GCC n'optimisait pas encore la boucle, mais il y a un attribut pour ça, optimize("unroll-loops") ! De plus, macl n'était pas sauvegardé dans la stack, j'ai donc ajouté ça manuellement (ouais, c'est pas génial).
Soit un résultat pas trop éloigné de ce que j'obtiens avec le compilo d'Hitachi. Je ne sais pas encore si cela était bien utile (la boucle aurait peut-être suffi ?), mais je reste bien content de ce que je suis parvenu à faire. Qu'en pensez-vous ?
Wow, t'as complètement cracké. Je suis en train de rédiger un article sur tous les trucs funs qu'on peut faire avec de l'assembleur sur calto (pour optimiser des fonctions classiques ou des trucs du genre), et j'ai pas mal touché à l'instruction mac en fait. J'ai cherché pas mal de moyens de forcer gcc à la générer, mais en vain.
Je dois admettre que ta macro a de la gueule, c'est plutôt bien joué parce que le défi technique était pas tout petit. Par contre ton formatage est illisible, tu pouvais pas indenter un peu tout ça ?
J'ai quelques remarques à faire sur cette fonction, et je vais tenter d'en faire une analyse pertinente :
→ Tu as certes réinitialisé mach avec l'instruction clrmac, mais je voudrais citer un problème portentiel : la documentation indique : « mac.w @rm+, @rn+ : (rn) × (rm) + mac → mac (16×16 + 64 → 64 bits) ». En l'occurrence, le registre mach est affecté, en particulier en cas d'overflow de macl. Contrairement à toi, la macro d'origine n'était pas négligente sur ce point : la sauvegarde de macl n'est pas suffisante car les multiplications et optimisations réalisées par le compilateur peuvent déplacer ton assembleur inline en plein milieu d'une multiplication (en particulier car les accès au multiplicateur détruisent la concurrence possible avec le processeur, le fameux « 2 to 5 cycles »), ce qui peut introduire des bugs si tu le modifies par overflow.
→ De manière beaucoup moins subtile, tu as réinitialisé mach et tu ne l'as mis nulle part dans les clobbers. C'est le bug assuré quand tu te serviras de ta macro en conditions réelles. Ne pas le réinitialiser n'étant pas une solution à cause du problème ci-dessus, et le mettre dans les clobbers me semblant stupide vu le coût de la sauvegarde/restauration, je suggérerais d'opter pour le sauvegarder.
→ Le multiplicateur tourne relativement indépendamment du processeur. La multiplication prend 5 cycles à réaliser, mais le processeur n'est utilisé que pendant les deux premiers et est donc disponible pour n'importe quelle opération pendant les trois autres. Enchaîner les opérations sur le multiplicateur et les accès aux registres force une attente qui aurait pu être productive. La boucle ne peut pas être améliorée, mais tu pourrais profiter de l'espace entre chaque mac.w pour incrémenter un peu tes pointeurs... je te laisse deviner une application intéressante de cette technique (un peu de suspense ) mais ça a un rapport avec le stride d'array.
→ Tu peux faire un peu mieux dans l'épilogue de ta fonction. mac est un registre 64 bits donc toute troncature à 32 ou à 16 bits est une valeur valide sur la taille réduite. Le stockage de macl est une telle troncature qui te fournit un résultat signé valide sur 32 bits. Ton extension non-signée est non seulement fausse quand le résultat est négatif (car elle ajoute des 0 et génère une résultat positif) mais en plus elle réduit fortement la plage de valeurs sur laquelle ta fonction va marcher sans overflow. Une extension non-signée limite le produit scalaire (-- oups, j'ai cité une application intéressant pas très loin de l'autre...) à 65535 et c'est vite atteint. Alors qu'en gardant la valeur initiale de macl, tu offres deux extensions possibles de ta fonction : la gestion du signe, et le résultat sur 32 bits. Finalement ton nop ne sert à rien ici, même pas à gérer l'alignement puisque le nombre de mac.w est variable. Ce qui me donne :
Je rencontre encore quelques soucis. Le premier concerne __asm_inline, défini dans sys/cdefs.h (anciennement compiler.h). C'est une macro que j'ai faite spécialement pour les fonctions inlines présentes dans machine.h (et sous-headers). Avec un compilateur GCC-like (__GNUC__ défini), elle est définie ainsi :
Seulement, cela fait une fonction non-inline. En ajoutant always_inline avant optimize dans le même __attribute__, aucun changement, seulement, en ajoutant always_inline après, on obtient bel et bien du code intégré à la fonction... mais avec une boucle (optimize ineffectif).
Second souci, même sans always_inline, pour certains grands nombres genre vingt, GCC met quand même en place une boucle (deux tours de boucles avec dix MAC.W dedans). Troisième souci, je n'arrive pas à obtenir cette optimisation au niveau du ret, GCC refuse de mettre le second lds après.
Je compte envoyer un mail concernant ça sur la mailing-list gcc-help de GCC aussitôt que possible (il faut auparavant que Tutanota, mon provider mail, règle un souci avec son interface).
Pour ce qui concerne le stride d'array, je ne comprends pas bien où tu veux en venir. J'ai au début cru que tu voulais dire que je pourrais profiter de ce temps pour adapter les valeurs 16-bits dans des valeurs 32-bits pour les passer à un MAC.L qui serait plus rapide, mais en vérifiant dans le manuel, ça ne serait pas worth it... donc je n'ai pas compris, désolé. x)
Tu devrais utiliser une contrainte ="rm" pour le stockage de macl et mach. Le compilateur saura bien décider de ce qui est le plus optimisé. Pour l'optimisation du rts, c'est pas bien grave - mieux vaut laisser gcc faire.
Non, ce que je propose, c'est d'incrémenter un pointeur pendant la multiplication, par exemple...
Honnêtement, je suis plutôt satisfait du "r". x)
J'avoue ne pas avoir compris le coup du "add #6, r1". Après, pour le moment, je reste fidèle à ce que fait le compilateur d'Hitachi. On verra après pour les améliorations.
Du coup, aujourd'hui, nouvelle histoire les enfants : trapa_svc. En gros, cette macro fait un trapa avec arguments. Elle prend 2 à 6 arguments : la valeur immédiate du trapa, la valeur du registre r0, puis les registres r4 à r7 (heureusement, tout ce qui est après n'est pas géré). Voici toutes les utilisations : copier
Allons bons, du code différent selon le nombre d'arguments ? Mais cela est impossible à gérer avec GCC, voyons !
... eh bien c'est ce que s'est dit votre serviteur au début. Et puis, je me suis dit qu'on pourrait peut-être tout simplement faire différentes fonctions, type __trapa_svc_2, __trapa_svc_3, ..., __trapa_svc_6, et qu'on pourrait les appeler en fonction de la taille de __VA_ARGS__ (macro à utiliser lorsque vous avez une variable argument list dans une macro, type ACHETEZ_MON_MACRO(_BASE, ...)). Donc j'ai cherché, et effectivement, une technique existe pour GCC ! Du coup, je l'ai intégrée dans sys/cdefs.h (anciennement compiler.h) sous le joli nom de __count_va_args.
Après avoir dû jouer de fonctions intermédiaires de concaténation utilisant, au dernier appel, l'opérateur ## (c'est totalement du trial and error, je dois bien l'avouer), et après avoir mis en place mes fonctions inline comme il faut, je suis confronté à une erreur d'assembleur. De ma grace habituelle, je galère à l'afficher (en fait il faut juste rajouter -S aux options du compilateur, le fichier d'output sera en plain text, ici obj/ptdr.c.o puisque j'ai eu la flemme de changer le nom), mais je réussis finalement, et je chope un trapa #r1. Eh oui, l'instruction trapa prend une valeur immédiate, ce qui veut dire que je vais encore une fois devoir générer des lambda. Youpi !
Moi qui danse parce que c'est l'éclate la plus totale
Alors on génère les macros, on utilise "i" au lieu de "r" pour le code, et on est bon ! Je règle juste le problème du "mov r0, r0" en déclarant une variable locale à la fonction avec le mot clé register représentant r0, et ça marche. (on dirait que c'était simple comme ça, mais en fait non j'ai galéré)
Du coup, mais elle va bien entendu de soi, voici la macro et tout ce qui va avec :
En gros, sur un OS bien foutu (donc pas CASIOWIN), le trapa sert à faire un syscall (provoque une exception pour que le kernel la catche et fasse une action), et cette macro sert à faire un syscall avec arguments. Tu trouveras plus de détails dans le manuel.
En fait Cake a déjà tout dit, c'est dans le trapa_svc. Dans les systèmes d'exploitation, il est commun de distinguer plusieurs types d'interruptions :
→ Les interruptions causées par les périphériques externes ;
→ Les déroutements causés par des erreurs dans le programme ;
→ Les appels au superviseur, aka supervisor calls (svc) ;
Donc, on sent tout de suite que le trapa avec arguments c'est fait pour implémenter des syscalls ! ^^'
Cakeisalie5 a écrit : Mon but avec la libcarrot est d'avoir une lib qui permette de faire une app rapidement, principalement pour pouvoir hacker facilement [...]
J'ai jamais vu personne d'autre que Kuhee exécuter trapa en vrai. Mais bon, bien joué o/
En vrai, ce serait tellement plus propre dans un builtin ce truc. T'es totalement fou de le faire en assembleur inline :')
Un topic dédié a été publié sur Code Walrus. Comme je souhaite me concentrer sur le projet P7, je commence à chercher activement un nouveau développeur/mainteneur pour ce projet. Si vous pensez avoir les compétences et le temps, n'hésitez pas à postuler.
Ajouté le 27/04/2017 à 02:01 :
Bon, puisque je n'ai pas (encore ?) de réponse pour la reprise, je continue de m'amuser dessus, notamment pour tout ce qui est implémentation des fixed-point (que je découvre par la même occasion), avec toute la compatibilité chiante qu'il faut ajouter derrière. J'améliore aussi tout ce qui est détection de la machine avec GCC (en reproduisant les macros du compilateur d'Hitachi), et gestion de la compatibilité des macros de machine.h, e.g. ceci. Je suis pas loin d'obtenir un machine.h avec les mêmes fonctionnalités que celui d'Hitachi/Renesas, fonctionnel sous GCC, et ça c'est quand même plutôt cool.
Ajouté le 05/05/2017 à 03:15 :
Bon, du coup, je continue à développer ce projet, mais dans un sens légèrement différent : j'en fais une libc générale pour SuperH/J-Core, avec des extensions genre la fxlib. Il faudra alors faire genre ./configure --extensions=fxlib,ensigdsp par exemple. Tout ça sera organisé dans des sous-dossiers, et je vais en profiter pour faire une doc dédiée à chaque truc.
Pour le moment, je galère sur trouver une bonne organisation des utilitaires de construction. En effet, il faudra probablement que je fasse un fxmake (ou un autre nom décentré des calculatrices CASIO pour être plus généraliste sur le SuperH/J-Core, type jmake ou quelque chose qui n'existe pas, de préférence) qui crée un Makefile sous Linux, et un rules.mk compatible avec Hmake sous Windows. Mais je ne sais pas encore comment je vais m'organiser pour la construction de libs (dont la libcarrot, du coup), et l'utilisation de libs.
Donc c'est un peu le flou le temps que je détermine comment faire un bousin propre. Désolé de ça.
Ajouté le 08/05/2017 à 23:44 :
Bon, je reprends le projet, parce que je compte en faire quelque chose d'un peu plus évolué, à savoir une libc modulable pour toutes les plateformes que je veux implémenter (c'est toujours mieux de faire ça que de refaire une libc pour chaque plateforme). Je fais un gros commit, puis je devrais reprendre la lib sur mon compte Github, puisque son intérêt devrait dépasser la calculatrice CASIO (concernant à la libcasio qui arrivera un jour, par exemple).
Ajouté le 12/05/2017 à 01:19 :
Je commence à pouvoir build la libcarrot avec le compilateur d'Hitachi ! Je continue le portage. Voici un log pour vous donner une idée de comment ça avance
G:\libcarrot>python tool --tools=hitachi "--tooldir=C:\Program Files\CASIO\fx-9860G SDK\OS\SH\BIN"
[all/core] CC ctype\funcs.c
[all/core] CC ctype\tab.c
[all/core] CC stdio\stdout.c
[all/core] CC stdio\fprintf.c
arch\all\core\src\stdio\fprintf.c(49) : C2214 (E) Integer required for "%"
arch\all\core\src\stdio\fprintf.c(49) : C2222 (E) Type not compatible for "="
arch\all\core\src\stdio\fprintf.c(68) : C2214 (E) Integer required for "%"
arch\all\core\src\stdio\fprintf.c(68) : C2222 (E) Type not compatible for "="
(vous pouvez voir comment ça évolue sur le dépôt git)
Ajouté le 19/05/2017 à 02:40 :
Graou, les libs produites par ce SDK sont dans un format propriétaire, magic string "HLIB" (0x484C4942). Je sais pas comment le gars qui a produit fxlib.a a procédé, j'ai dû rater l'utilitaire de Renesas qui faisait ça. En tous les cas, voilà qui va bien me faire chier pour la production de la libcarrot avec les utilitaires de Renesas.
Équilibrer des coefficients stœchiométriques en moins de 2500 octets sur ta Prizm : Ekisto
Ma version du moteur de calcul symbolique libre Eigenmath
C'est hyper important
Oui
«Ceux qui exercent la profession d'herboriste-botaniste, lesquels sont de tous temps en possession de vendre des herbes ou plantes, seront soumis à la visite et inspection des gardes des apothicaires»
-- Arrêté du Conseil d'État, 30 oct. 1767
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