fxSDK, un SDK alternatif pour écrire des add-ins
Posté le 29/08/2014 22:00
Cette page sert d'index pour la série de topics du fxSDK.
Le fxSDK est une collection d'outils permettant de développer des add-ins pour les calculatrices Casio des séries Graph. C'est une alternative au
fx-9860G SDK et
PrizmSDK qui ne sont plus activement maintenus, et le compagnon classique de mon noyau
gint.
Index des topics
Ce projet existe depuis 2015, alors il y a pas mal de topics liés. En voici une liste complète !
Topics principaux
Installation du fxSDK
Tutoriels
Compatibilité sur calculatrice et PC
Côté PC, le fxSDK est compatible avec
Linux, Mac OS, et WSL pour Windows ; normalement tout le monde peut l'utiliser. Je teste constamment sous Linux, et WSL est un Linux donc c'est testé aussi. Je n'ai pas de Mac OS donc il peut y avoir quelques surprises, mais en général c'est l'affaire de corriger un bug ou deux.
En termes de calculatrices, le fxSDK supporte :
Calculatrices monochromes
- Graph 35+E II
- Graph 35+ USB / Graph 35+E (SH3/SH4)
- Graph 75/75+/75+E
- Graph 85/85 SD/95 (SD) (pas activement testé)
Calculatrices couleurs
- Graph 90+E / fx-CG 50
- Prizm fx-CG 10/20
Comment installer le fxSDK et coder des add-ins
Le fxSDK s'installe à partir de dépôts Git sur la
forge de Planète Casio (par exemple
Lephenixnoir/fxsdk). Il y en a un peu beaucoup, donc manuellement c'est assez long. Pour que ça aille plus vite et pour simplifier le travail des débutants, il y a un outil appelé
GiteaPC qui peut faire ça pour vous.
Si vous utilisez Windows, vous aurez besoin de WSL pour accéder à un système Linux dans Windows. Heureusement, Microsoft a fait ça bien et c'est facile à faire. Voyez le
tutoriel d'installation de WSL 2 (et l'explication rapide de
ce que WSL 2 est).
Si vous utilisez Mac OS, ouvrez l’œil en lisant les topics pour ne pas manquer les informations supplémentaires et éventuelles déviations par rapport à la procédure normale sous Linux.
Méthode automatique avec GiteaPC (plus rapide / recommandée pour les débutants)
- Suivez le tutoriel d'utilisation de GiteaPC, qui explique comment obtenir le fxSDK.
Méthode automatique avec plugin VS Code
- Yannis300307 a créé un plugin VS Code Casio Dev Tools qui fonctionne sous Windows (avec WSL) et Debian (probablement les dérivés aussi).
Méthode AUR pour les utilisateurs Arch/Manjaro/dérivés (ils se reconnaîtront)
- Dark Storm maintient MiddleArch, un dépôt de paquets précompilés qui a entre autres le fxSDK.
Méthode manuelle (plus fine / classique pour les habitués)
- Compilez et installez le cross-compilateur GCC pour SuperH.
- installez (dans cet ordre) les dépôts fxSDK, OpenLibm, fxlibc, gint ; en option, Slyvtt/µSTL_2.3.
Description sommaire du fxSDK
Pour une introduction à l'utilisation du fxSDK qui montre comment utiliser les outils pour développer un add-in, lisez plutôt les
tutoriels d'utilisation de gint. Cette section est juste une description sommaire.
Le cœur du fxSDK est un cross-compilateur GCC pour SuperH, habituellement nommé
sh-elf-gcc. Bien sûr on a avec toute la suite d'outils, dont
as,
ld,
objdump,
objcopy (entre autres). Contrairement au vieux compilateur du SDK, GCC est un compilateur moderne avec beaucoup d'options et capable de très solides optimisations.
Sur la calculatrice, c'est
le noyau gint qui fait la majorité du travail. Il remplace fxlib/libfxcg et une partie de l'OS pour vous offrir des fonctionnalités plus cool et plus rapides. Les add-ins développés avec le fxSDK utilisent gint toutes les trois lignes !
Il y a enfin plusieurs outils utiles sur le PC qui sont utilisés durant le développement ou l'utilisation des add-ins :
- fxsdk est un script shell qui permet de créer et compiler les projets sans se prendre trop la tête. Le système de compilation officiel pour les add-ins est CMake, mais un système plus ancien de Makefile est encore supporté.
- fxconv est un outil très polyvalent qui convertit à la compilation les assets (images, polices, maps....). Il permet de travailler avec des logiciels et formats de fichiers normaux sur le PC et d'avoir automatiquement un format optimisé sur la calculatrice. fxconv est extrêmement extensible et chaque projet peut ajouter des conversions personnalisées.
- fxgxa crée les fichiers g1a (format des add-ins pour Graph monochromes) et g3a (format des add-ins pour Graph couleurs) à partir des programmes compilés.
- fxlink est un outil de communication qui peut transférer des fichiers vers les Graph 35+E II et Graph 90+E en ligne de commande, mais aussi échanger interactivement avec les add-ins gint par le câble USB, et est couramment utilisé pour réaliser des captures d'écran ou captures vidéo des add-ins.
Changelog et informations techniques
Ci-dessous se trouve la liste des posts annonçant les nouvelles versions du fxSDK, ainsi que des liens vers les instructions/tutoriels supplémentaires publiés avec.
Citer : Posté le 01/07/2019 17:49 | #
Prochaine étape : s'en servir, ça va pas être de la tarte non plus, mais là le problème c'est moi !
Ajouté le 09/07/2019 à 18:21 :
cf mon dernier post : Comment ça marche ?
Citer : Posté le 09/07/2019 18:28 | #
... tu pourrais situer le contexte au moins.
Shadow15510 : pour pas trop polluer le main
Lephenixnoir : Raah c'est moche.
Lephenixnoir : Tu devrais les coder dans des fichiers à part et les convertir avec fxconv, c'est fait pour ça :x
Qu'est-ce qu'une map ici ? C'est juste un gros paquet de données organisées dans un tableau. C'est des valeurs les unes à la suite des autres dans la mémoire.
Il y a plein de façon de s'arranger pour avoir à sa disposition un gros paquet de données dans la mémoire.
L'une d'elles consiste à écrire dans le code un gros tableau avec toutes les valeurs. C'est moche parce que la donnée d'origine n'a sans doute rien à voir. Pour une image gint par exemple c'est une structure avec plein d'infos. Et parce qu'il y a de la redondance entre le code et les assets.
La façon utilisée par fxconv consiste à générer un fichier objet avec les données et une variable dessus. Pendant la compilation, tu t'en sers pour obtenir un .o que tu linkes avec tous les autres .o issus de la compilation des fichiers sources et voilà, c'est comme si tu avais écrit ton tableau sauf que tu n'en a pas eu besoin.
La façon de produire un tel fichier est avec la commande :
Par exemple :
% fxconv -b str.bin -o str.o name:str
Cette commande crée un fichier str.o qui contient une variable str dont le contenu est "Hello, World!".
Citer : Posté le 09/07/2019 18:33 | #
Donc on créer un tableau en *.o sans le coder ? Mais comment je fais pour coder avec ? C'est pas pratique ça veut dire que je vais avoir 256 fichiers *.o qui vont se balader ? et qu'il faudra que je les linke à la main un par un ?
Citer : Posté le 09/07/2019 18:35 | #
Ces fichiers .o ne sont que le produit de la compilation. Ils sont dans build-fx et ne sont pas sur le dépôt. Ce qui compte c'est str.bin dans mon exemple.
Mais non, le linker c'est une commande à la fin de la compilation, c'est le Makefile qui la balance et tous les fichiers partent dedans en même temps. Il suffira de modifier une fois le Makefile xD
Citer : Posté le 09/07/2019 18:39 | #
Je comprend rien…
Comment on fait pour mettre les 256 map dans un fichier (disons Matrices_map) ?
Ça marche pas comme ça ?
Ajouté le 09/07/2019 à 20:04 :
Alors reprenons depuis le début ^^'
J'aimerai un système de cartes pour pouvoir afficher le maps du monde dans le cadre du projet Odyssée.
J'ai donc créer une fonction map_init() qui prends les coordonnées de la map à afficher au sein du monde. Une map représente un écran du jeu et le monde est l'ensemble des maps du jeu.
J'était partit sur un truc bête et méchant : map_init regarde les coordonnées et par deux switch() imbriqués la fonction retourne la map sous la forme d'une matrice… Le problème est que l'on ne peut pas passer de matrice en argument de fonctions comme ça… Je passe aux pointeurs pour modifier directement la matrice, mais celle-ci est déjà déclarée et impossible de modifié une matrice déjà déclarée à coup de {}… Donc Lephe m'a proposé la solution suivante qui m'a paru pas bête : avoir toutes les maps du monde déjà stockées quelques part. Et jouer avec un pointeur qui va pointer sur telle ou telle map.
La question est que j'ai voulu faire un fichier *.c qui contient toutes les map du monde. Avec son *.h j'aurais eu une sorte de bases de données avec mes maps. Lephe m'a ouvertement dit que c'était atroce comme méthode et j'ai toujours pas compris comment faire sans mon fichier *.c et son header…
Citer : Posté le 10/07/2019 01:29 | #
Tout ce que tu dis est juste.
Pourquoi est-ce une solution peu satisfaisante ?
1. Le format est terrible, tu veux faire tes map avec Tiled et pas en modifiant un fichier C.
2. Si tu as les deux (la map avec Tiled et le fichier C avec le tableau), tu as de l'information redondante. Un jour tu vas modifier le fichier C et oublier de modifier le fichier Tiled, ou l'inverse.
3. Demain tu veux rajouter la taille de la map à côté de la matrice. Tu vas créer des variables en plus ? Rajouter deux éléments (la hauteur et la largeur) dans la matrice alors que ce ne sont pas des cases ? Aucune solution n'est satisfaisante.
La vérité, c'est que ta map c'est des données qui ne proviennent pas du code mais du reste des fichiers du projet. Il n'y a pas de bonne raison d'en faire un tableau à part "je ne sais pas comment y accéder autrement".
Voici une autre méthode pour accéder à des données dans le programme. Cela repose sur un principe simple : le g1a ce n'est pas des fichiers C, c'est du code issu de plusieurs fichiers objets. Les fichiers C sont compilés en fichiers objets, mais il peut y avoir des fichiers objets venant d'ailleurs. En particulier il peut y en avoir qui contiennent juste une variable avec des données.
Avec fxconv, tu peux créer ce genre de fichiers. La forme la plus simple (-b) consiste à fournir un fichier quelconque <source>. fxconv crée alors un fichier objet <objet> qui contient juste les octets de <source> et une variable que indique l'adresse mémoire, <var>.
Prenons un exemple. Commeçons par créer un fichier contenant des données :
Pour voir les données contenues dans le fichier sous forme brute, j'utilise l'outil xxd, qui affiche les octets individuellement.
00000000: 4865 6c6c 6f2c 2057 6f72 6c64 00 Hello, World.
La ligne est découpée en trois parties : d'abord la position dans le fichier. 00000000 signifie que je suis au début du fichier, ça augmente de 16 octets par ligne soit 10 en hexa. Ensuite les octets eux-mêmes, 48, 65, 6c, etc. C'est noté en hexa là-aussi. À droite il y a les caractères ASCII correspondant, H, e, l, etc. Le dernier octet 00 n'est pas un caractère ASCII affichable, donc xxd affiche un point à la place. 00 sert à indiquer la fin d'une chaîne de caractères en C.
J'ai donc un fichier de 13 octets. J'aimerais que dans mon add-in, il y ait quelque part ces 13 octets dans la mémoire (pour que je puisse afficher "Hello, World" à l'écran) et il me faut une variable pour pointer dessus, disons hello_world.
Je demande à fxconv de créer un fichier objet qui contient ça :
On peut regarder à l'intérieur du fichier msg.o ce qui s'est passé avec sh3eb-elf-objdump. Voyons voir les variables définies là :
msg.o: file format elf32-sh
SYMBOL TABLE:
00000000 l d .rodata 00000000 .rodata
00000000 g .rodata 00000000 _hello_world
0000000d g .rodata 00000000 _hello_world_end
0000000d g *ABS* 00000000 _hello_world_size
Je ne veux pas rentrer dans les détails, on remarque juste qu'on a trois symboles _hello_world, _hello_world_end, et _hello_world_size. Le premier contiendra l'adresse de début du texte, le second l'adresse de fin, le troisième contient la taille. Les deux premiers n'ont pas encore leur valeur définitive car elle ne sera choisie que lorsqu'on liera ensemble tous les .o à la fin de la compilation.
Dans le code C, je peux alors dire que ce tableau existe, et l'utiliser :
dtext(1, 1, hello_world, C_BLACK, C_NONE);
Bon, jusque-là ça va ? Je n'ai pas fini mais je veux être sûr que t'as bien compris jusqu'ici.
Non, tu n'auras pas à taper toutes les commandes à la main, bien sûr que non.
Citer : Posté le 11/07/2019 08:29 | #
Buh… J'ai pas de map avec Tiled… je m'en sert juste pur voir si ça rend bien. Donc il n'y aura pas redondance puisque les map Tiled n'existent pas… non ?
Sinon je pense que j'ai compris à peu près tout… ^^'
Citer : Posté le 11/07/2019 13:03 | #
En soi tu peux très bien réaliser ta map avec Tiled et l'exporter dans ton propre format avec un script Python. Tu as une partie de la doc qui y est consacrée :
https://doc.mapeditor.org/en/stable/manual/python/
https://github.com/bjorn/tiled/tree/master/src/plugins/python/scripts
Ensuite, une fois ta map exportée dans un bon format binaire, tu la mets dans un truc que tu peux linker avec tes fichiers C grâce à la méthode qu'a décrite Lephé, et c'est bon. En plus une fois le script réalisé ça sera beaucoup plus facile pour toi et tes collaborateurs⋅trices de modifier les sources de ton jeu.
Mon blog ⋅ Mes autres projets
Citer : Posté le 11/07/2019 13:15 | #
Okay, donc les maps peuvent être enregistrées comme ça… ? Mais, et les obstacles ? Les dessins, on les affiches comment ? J'aime pas trop cette solution : je connais pas du tout je préfère autant les bonnes vieilles matrices avec un numéro par case qui correspond au dessin à afficher… Et surtout je ne vois pas l'interêt…
Citer : Posté le 11/07/2019 13:16 | #
Et comment tu gére les collisions avec ta bonne vieille matrice ?
Citer : Posté le 11/07/2019 13:17 | #
Ben on regarde le numéro de la case sur laquelle le joueur veut aller si c'est un numéro qui dessine un mur par exemple le joueur ne bouge pas si il n'y a rien d'affiché sur cette case, le joueur se déplace…
Citer : Posté le 11/07/2019 13:18 | #
spoiler, Tiled fait pareil
Citer : Posté le 11/07/2019 13:20 | #
Oui, mais comment il comprend que ce dessin est un mur ou un PnJ qu'il faut lancer la fonction interact() si c'est un PnJ ou maisons() si c'est une porte ?
Citer : Posté le 11/07/2019 13:23 | #
Bah comme tu ferais avec l'ancienne méthode. Tiled te permet juste d'automatiser le passage de ta map graphique vers ta "matrice", rendant le tout plus simple et diminuant les chances d'erreurs inattention.
Citer : Posté le 11/07/2019 13:26 | #
Mais vu que je n'ai pas besoin de rien afficher… >_<' je n'ai pas de correspondance entre dessins et numéro…
Citer : Posté le 11/07/2019 13:28 | #
En français ?
Citer : Posté le 11/07/2019 13:33 | #
Lorsque j'affiche ma map avec une matrice, j'ai une correspondance entre dessins et numéro :
0 -> case vide
1 -> mur
2 -> PnJ
3 -> porte
et quand je veux savoir ce qu'il y a sur la case dans laquelle le joueur veut aller, je me sert de ces correspondances pour savoir ce qu'il y a dans la case. Mais avec Tiled, toutes la map est affiché comme une image (de ce que j'ai compris… ) donc plus besoin de numéros. Autrement dit avec Tiled je suis incapable de savoir quel est le dessin affiché à l'écran pour des coordonnées données.
Citer : Posté le 11/07/2019 13:37 | #
T'as mal compris, la map est une image sur ton écran d'ordinateur et tu peux effectivement exporter cette image.
Mais ici on te parle des fonctions d'export qui te permettrait de générer une matrice qui fait correspondre les dessins avec des numéros. (test l'export csv par exemple et regarde ce que ça te donne
Citer : Posté le 11/07/2019 13:43 | #
Et les numéros correspondent donc… je vois…
Mais pour afficher la map dans gint ?
Citer : Posté le 11/07/2019 13:44 | #
ça faut voir avec Senpai
Citer : Posté le 11/07/2019 14:19 | #
Buh… J'ai pas de map avec Tiled… je m'en sert juste pur voir si ça rend bien. Donc il n'y aura pas redondance puisque les map Tiled n'existent pas… non ?
Toi non mais les autres personnes de l'équipe ne codent pas les maps en hexadécimal ! Et tu ne vas pas les convertir à la main... d'abord parce que c'est chiant et le risque d'erreur est élevé, et ensuite parce que ça te fera des duplications puisque les originaux seront sur le dépôt.
La méthode de Cake est exactement là où je voulais en venir.
Okay, donc les maps peuvent être enregistrées comme ça… ?
Essaie de te familiariser avec l'idée que les données c'est juste une suite d'octets. Ta matrice est une suite d'octets. Si tu apporte ces octets dans la mémoire, tu as une matrice.
Tu connais normalement deux façons de faire ça :
1. Faire un tableau
2. Créer un fichier qui contient ces octets et utiliser fxconv
-
Je fais une parenthèse pour considérer un autre cas : les images. Comme tu l'as vu dans le tutoriel d'utilisation de gint, les images sont converties automatiquement par fxconv. C'est pratique parce qu'il n'y a plus de Sprite Converter et plus de tableau à écrire à la main. Mais surtout c'est nécessaire parce que tu ne pourrais pas convertir à la main et créer un tableau : le format est beaucoup trop compliqué.
-
Ta map est toujours une matrice, les octets sont juste apportés par fxconv au lieu de te donner un tableau à écrire. Quant aux dessins, tu auras une grande image de tout le tileset qui sera convertie automatiquement et tu appeleras dsubimage() pour afficher un tile à chaque fois.
Mais c'est exactement ce que tu auras parce que le fichier créé par fxconv va contenir exactement la matrice que tu aurais écrite. Sauf qu'elle aura été écrite à ta place, avec plusieurs avantages :
1. L'action de calculer les valeurs pour la matrice à partir de la map sera automatisée (avec un script, comme Cake a dit)
2. Si un tile bouge sur le tileset, il suffira de reconvertir au lieu de devoir changer tous les numéros de ce tile à la main
3. Sur le dépôt il n'y aura qu'une version de la map : l'originale avec Tiled. Avoir deux versions (Tiled et le tableau) c'est risquer qu'il y a des différences entre les deux
4. Le concepteur de maps peut utiliser un outil agréable
Et ok, toi tu utilises peut-être pas Tiled. Pour tester ton rendu de map tu peux totalement faire un tableau, à la limite on s'en fout. Mais la personne de l'équipe d'Odyssée qui fait la map n'aura clairement pas envie de se taper une syntaxe aussi barbare que des numéros les uns à la suite des autres sans rendu visuel, et pour les vraies maps en production il faut prendre le temps de le faire correctement.
Bien sûr tu ne regardes pas le dessin, tu as toujours ta matrice avec les numéros que tu as annoncés, donc si c'est 1 (mur) ou 2 (PNJ) on lance interact() et si c'est 3 (porte) on lance maisons(). Tu as forcément à la fois la map d'un côté (la matrice) et le tileset de l'autre (les images).
Mais avec Tiled, toutes la map est affiché comme une image (de ce que j'ai compris… )
Aaaah, mais non. Tiled gère la map comme une matrice. Mais il te permet de voir la map pendant que tu la crées ! C'est pour ça que généralement t'as envie de l'utiliser : tu peux voir au fur et à mesure à quoi ça va ressembler.
Faire une map entièrement sur une image ce serait pas efficace du tout (et puis si deux tiles ont la même tête...).
J'en profite pour rajouter que quand tu exportes la map avec Tiled il va te mettre des numéros qui correspondent en gros à la position du tile dans ton tileset : c'est pour ça qu'il est important que le tileset bouge pas trop (ou en tout cas que les tiles utilisés restent à leur place). Après tu utilises un script pour créer la matrice dans un fichier à partir du fichier Tiled, tu appelles fxconv sur le fichier ce qui créer la matrice comme une variable, et à la prochaine compilation la variable sera disponible.
Tout ça est tellement automatisable qu'il suffira de réenregistrer le fichier dans Tiled et de taper fxsdk build-fx et hop, l'add-in est recompilé avec la nouvelle map.
Mais pour afficher la map dans gint ?
Tu auras deux variables : la map et le tileset. Pour afficher la map, tu fais une boucle pour parcourir toutes les cases visibles et tu récupères leur numéro. À partir du numéro tu calcules la position du tile associé dans l'image du tileset. Puis tu appelles dsubimage() pour afficher la partie correspondante.