Planète Casio - Vos tutoriels et astuces - Flux RSS http://www.planet-casio.com Programmes Casio, Jeux, Cours pour Calculatrices Casio fr-FR https://www.planet-casio.com/images/logo.gif Planète Casio - Vos tutoriels et astuces - Flux RSS http://www.planet-casio.com 55 50 Programmes Casio, Jeux, Cours pour Calculatrices Casio. Wed, 02 Sep 2026 01:02:36 GMT Wed, 02 Sep 2026 01:02:36 GMT contact@planet-casio.com (Planet Casio) contact@planet-casio.com (Planet Casio) 5 notification sur téléphone de la shoutbox https://www.planet-casio.com/Fr/forums/topic19667--.html (cette version n'est pas la version définitif, il y a encore des améliorations a faire) Ces derniers temps j'ai fais un programme qui vérifie la shoutbox annonces et si il y a un changement envoie une notification (Attention pour Android seulement ! Désolée les utilisateur iOS :oops: peut-être j'adapterais un jour pour vous). J'ai trouvé que ça pourrait être pratique pour tout le monde alors je le poste ici. ;) ^^ ​FONCTIONEMENT ET UTILISATION Le programme vérifie la page API de la shoutbox annonces toute les 1min et la compare à une version précédente, si il y a une différence et que le rajout n'est pas fait par vous-même: il compte le nombre de nouveaux messages et active une notification : https://i.ibb.co/XrHJ7dtR/image-3.png -Si l'on appuis sur -Voir shoutbox- l'on peut voir ceci: https://i.ibb.co/bRJK4VyF/Screenshot-2026-08-09-15-17-30-44-2e840d222d67ef3caca3a1c67d71d48f.png -Si l'on appuis sur -Y aller- ça nous rediriges sur la page d'accueil de planet-casio . -Si l'on appuis sur -Desactiver- ça désactive les notification qu'il y a lors d'une modification dans la shoutbox et envoie une notification en silencieux (si l'on clique sur -oui- les notifications se relances): https://i.ibb.co/PGYTfQMJ/image-2.png ​TELECHARGEMENT ET INDEPENDANCES 1. téléchargez MacroDroid. 2. téléchargez ce fichier depuis votre téléphone. 3. sur MacroDroid au commencement autorisez  les notification. (pour les étapes suivantes toujours rester sur MacroDroid) 4. aller sur Exporter/Importer. 5. cliquez sur Importer. 6. sélectionnez notifications-planet-casio.mdr dans votre dossier téléchargement. 7. sélectionnez l'option Effacer les données existantes et tout importer  . 8. une page s'ouvre avec les autorisations spéciales: ↓cliquez sur subvention sur la partie notification : lecture, contrôle et réponse↓ -ducoup, pour notification : lecture, contrôle et réponse : chercher l'application MacroDroid et cliquez dessus, ensuite activez  Autoriser l'accès aux notifications ( si une fenêtre Danger s'active ne vous inquiétez pas ce n'est pas votre téléphone qui se fait hacker c'est juste une autorisation sensible  :lol: , cochez juste "Je suis conscient des risques possibles et j'assume volontairement toutes les conséquences possibles." Attendez que les 10 secondes passes et appuyez sur "Ok") ensuite appuyez sur la flèche retour deux fois. ↓cliquez sur subvention sur la partie Modifier les paramètres du système↓ -donc, pour Modifier les paramètres du système : activez "Autoriser à modifier les paramètres système" et ensuite appuyez sur la flèche retour. 9. dans les onglet en bas de l'écrans allez sur Macros. 10. cliquez sur la zone (le macro) nommée  Envoyer notif et allez à la toute fin, cliquez sur le bloc au dessus de patienter 2 secondes puis cliquez sur tester les actions à partir d'ici puis cliquez sur fleche retour. 11. si deux zones (deux macros) sont entourées de violet c'est qu'un paramètre n'est pas activée, pour cela il suffit juste de cliquer sur la zone (macro) nommée  Envoyer notif  et sur le premier bloc rose cliquez sur "Autorisation manquante - tapez pour l'accorder" ensuite sur "OK", Activez ensuite "Autoriser à définir des alarmes et des rappels" et ensuite cliquez sur la flèche retour deux fois. Mais si aucunes zones était entourée de violet alors il n'y a pas besoins de faire de changement. 12. cliquez sur la zone (le macro) nommée  Envoyer notif. 13. dans la partie action (partie en bleu) cliquez sur le 8ᵉᵐᵉ bloc (le deuxième Si consécutif) puis sur configurer, ensuite appuyez sur le bloc Variable MacroDroid  puis sur configurer, après sur OK et remplacez ensuite "nom d'utilisateur" par votre pseudo (Attention: ne pas oublier les majuscules si il y en a, il faut noter le pseudo en entier sans fautes) ensuite cliquez sur OK et encore sur OK. 14. toujours dans la partie action (partie en bleu) cliquez sur le 23ᵉᵐᵉ bloc (la deuxième  Variable MacroDroid  consécutif qui a en dessous la description #3 ne contient pas nom d'utilisateur) puis sur configurer, ensuite appuyez sur OK puis remplacez ensuite "nom d'utilisateur" par votre pseudo (Toujours attention à ne pas oublier les majuscules si il y en a, il faut noter le pseudo en entier sans fautes c'est important) ensuite appuyez sur OK. 15. toujours dans la partie action (partie en bleu) cliquez sur le 37ᵉᵐᵉ bloc cette fois (la deuxième  Variable MacroDroid  consécutif qui a en dessous la description #3 ne contient pas nom d'utilisateur) puis sur configurer, ensuite appuyez sur OK puis remplacez ensuite "nom d'utilisateur" par votre pseudo (Toujours attention à ne pas oublier les majuscules si il y en a, il faut noter le pseudo en entier sans fautes c'est important) ensuite appuyez sur OK. 16. pour finir appuyez sur la coche en bas à droite de l'écrans (si vous avez de la chance vous n'avez rien, sinon vous avez une pub chiante  :@ ) IMPORTANT: MacroDroid a une limite de jours, il suffit de cliquer sur Ajouter des jours et de cliquer sur Regarder la publicité (+3 jours) pour rajouter 3 jours (même si c'est chiant c'est indispensable  :oops: :@ ) ET VOILA VOUS AVEZ MAINTENANT LES NOTIFICATION DE :EPLANET-CASIO:E Lors du lancement du programme si vous avez une notification (ou pas) il suffit juste de cliquez dessus, c'est normal car il commence sur une ancienne base de donnée de la shoutbox annonces Si vous avez un problème lors du téléchargement, de l'Independence, du fonctionnement ou si vous ne comprenez juste pas quelque chose dites le ————————————— Voici comment avoir la nouvelle version si vous aviez déjà l'encienne: 1. téléchargez ce fichier depuis votre téléphone. 2. aller sur Exporter/Importer. 3. cliquez sur Importer. 4. sélectionnez notifications-planet-casio.mdr dans votre dossier téléchargement. 5. sélectionnez l'option Effacer les données existantes et tout importer. 6. dans les onglet en bas de l'écrans allez sur Macros. 7. appuyez sur https://i.ibb.co/4wjVZ8n7/image-6.png au bloc envoyer notif. 8. cliquez sur la zone (le macro) nommée Envoyer notif et allez à la toute fin, cliquez sur le bloc au dessus de patienter 2 secondes puis cliquez sur tester les actions à partir d'ici. 9. dans la partie action (partie en bleu) cliquez sur le 8ᵉᵐᵉ bloc (le deuxième Si consécutif) puis sur configurer, ensuite appuyez sur le bloc Variable MacroDroid  puis sur configurer, après sur OK et remplacez ensuite "nom d'utilisateur" par votre pseudo (Attention: ne pas oublier les majuscules si il y en a, il faut noter le pseudo en entier sans fautes) ensuite cliquez sur OK et encore sur OK. 10. toujours dans la partie action (partie en bleu) cliquez sur le 23ᵉᵐᵉ bloc (la deuxième  Variable MacroDroid  consécutif qui a en dessous la description #3 ne contient pas nom d'utilisateur) puis sur configurer, ensuite appuyez sur OK puis remplacez ensuite "nom d'utilisateur" par votre pseudo (Toujours attention à ne pas oublier les majuscules si il y en a, il faut noter le pseudo en entier sans fautes c'est important) ensuite appuyez sur OK. 11. toujours dans la partie action (partie en bleu) cliquez sur le 37ᵉᵐᵉ bloc cette fois (la deuxième  Variable MacroDroid  consécutif qui a en dessous la description #3 ne contient pas nom d'utilisateur) puis sur configurer, ensuite appuyez sur OK puis remplacez ensuite "nom d'utilisateur" par votre pseudo (Toujours attention à ne pas oublier les majuscules si il y en a, il faut noter le pseudo en entier sans fautes c'est important) ensuite appuyez sur OK. 12. pour finir appuyez sur la coche en bas à droite de l'écrans 13. appuyez sur https://i.ibb.co/QFfpvzwr/image-7.png au bloc envoyer notif. Sat, 08 Aug 2026 02:04:22 +0200 Tutoriel de création d'un jeu de pong avec le fxsdk/gint https://www.planet-casio.com/Fr/forums/topic19641--.html NOTE : Le tutoriel est aussi disponible ici : https://git.planet-casio.com/mibi88/pong_fxsdk/src/branch/master/TUTORIAL_fr.md. Tutoriel de développement d'un jeu de pong avec le fxsdk Dans ce tutoriel nous allons coder un jeu de pong en C avec gint et le fxsdk. Préréquis Des connaissances solides de C sont requises. Pour pouvoir suivre ce tutoriel, vous avez besoin du fxsdk et de gint. Vous avez donc besoin d'un système d'exploitation UNIX-like compatible avec celui-ci. Vous avez bien sûr besoin d'une calculatrice CASIO monochrome ou couleur pour pouvoir tester l'add-in. Pour tester l'add-in régulièrement, vous pouvez utiliser mq (https://www.planet-casio.com/Fr/forums/topic18782-4-mq-emulateur-add-ins-universel.html). Une version en ligne de mq existe aussi : https://www.planet-casio.com/mq/index.html Je trouve cependant qu'il est plus pratique d'utiliser la build Linux pour pouvoir tester l'add-in en saisissant la commande suivante dans un shell : $ <chemin vers le binaire de mq> <chemin vers l'add-in> NOTE : Dans ce tutoriel je marquerais tout le texte à replacer entre chevrons. Ci-dessus, `<chemin vers le binaire de mq>` doit être replacé par le chemin vers ce binaire, et `<chemin vers l'add-in>` doit être replacé par le chemin vers l'add-in à exécuter. Je vous conseille de quand même de tester régulièrement vos add-ins on-calc, mq est encore assez nouveau, et ne reproduit pas à 100 % le comportement de la calculatrice. Par exemple, le jeu ne s'exécutera probablement pas à la même vitesse dans mq que sur la vraie calculatrice. Quelques infos utiles Le coin en haut à gauche est l'origine du repère. Le pixel en haut à gauche est aux coordonnées (0; 0) et le pixel en bas à droite aux coordonnées (127; 63) ou (395; 223) pour les calculatrices monochromes ou couleur respectivement. L'écran des calculatrices monochromes a une résolution de 128 × 64 et celui des calculatrices couleur une résolution de 396 × 224 (sans le bandeau). Création du projet Pour créer un nouveau projet, il suffit d'exécuter la commande suivante dans le dossier où vous souhaitez créer le projet : $ fxsdk new <nom du projet> Par exemple : $ fxsdk new pong Entrez le répertoire crée avec ce nom. Ensuite vous pouvez compiler votre projet. Pour le compiler pour des calculatrices monochromes : $ fxsdk build-fx Pour des calculatrices couleur : $ fxsdk build-cg Pour compiler la version monochrome pour une calculatrice couleur : $ fxsdk build-fxg3a Pour tester l'add-in avec Addin push (https://www.planet-casio.com/Fr/programmes/programme4384-2-addin-push-heath-utilitaires-add-ins.html#65954) : $ fxsdk build-cg-push et pour l'envoyer (nécessite d'avoir lancé Addin push sur la calculatrice) : $ fxlink -p build-cg-push build-cg-push/<nom de l'add-in>.bin Premier test Vérifier que le projet compile et que l'add-in produit affiche bien « Sample fxSDK add-in. » en haut à gauche de l'écran. Structure du projet Nous allons structurer le projet comme ceci: `main.c` Lancement du jeu et boucle du jeu. `game.c` et `game.h` logique du jeu et rendu. `game.h` contiendra toutes les structures. `ball.c` et `ball.h` mouvement, collisions et rendu de la balle. `paddle.c` et `paddle.h` gestion des collisions entre la balle et la raquette, mouvement et rendu de la raquette. `fixed.h` Calculs à virgule fixe (j'explique ce que c'est dans la suite du tutoriel). Créez ces fichiers dans `src`. Dans assets-fx et assets-cg, nous stockerons l'écran de titre, ayant une dimension de 128 × 64 ou 396 × 224 pour calculatrices monochromes ou couleur respectivement. Votre projet devrait avoir la structure suivante : $ tree . ├── assets-cg │   ├── fxconv-metadata.txt │   ├── icon-cg.xcf │   ├── icon-sel.png │   ├── icon-uns.png │   └── title.png ├── assets-fx │   ├── fxconv-metadata.txt │   ├── icon.png │   └── title.png ├── CMakeLists.txt └── src ├── ball.c ├── ball.h ├── fixed.h ├── game.c ├── game.h ├── main.c ├── paddle.c └── paddle.h 4 directories, 17 files Ajout des fichiers de code source et des assets à CMakeLists.txt CMake est utilisé pour compiler les projets utilisant le fxsdk. Ajout des fichiers de code source Pour que nos fichiers `.c` soient bien compilés, il faut les ajouter ici : set(SOURCES src/main.c # ... ) On a donc : set(SOURCES src/main.c src/ball.c src/game.c src/paddle.c # ... ) Ajout des assets Pour ajouter nos assets au projet, il faut les ajouter à la propriété `ASSETS`, `ASSETS_fx` ou `ASSETS_cg`, pour qu'ils soient inclus lorsque l'on compile l'add-in pour n'importe quel modèle de calculatrice, une calculatrice avec un écran monochrome ou une calculatrice avec un écran couleur respectivement. Pour nous, les lignes set(ASSETS # ... ) set(ASSETS_fx assets-fx/example.png # ... ) set(ASSETS_cg assets-cg/example.png # ... ) deviennent donc : set(ASSETS # ... ) set(ASSETS_fx assets-fx/title.png # ... ) set(ASSETS_cg assets-cg/title.png # ... ) Ensuite, il faut changer les fichiers `fxconv_metadata.txt` dans `assets-fx` et `assets-cg`. Ils contiendront : title.png: type: bopti-image name: title_img Ces fichiers permettent de configurer comment les images sont converties vers le format utilisé par gint par l'utilitaire du fxsdk appelé fxconv. Nous allons utiliser la même configuration que celle utilisée par l'image d'exemple. Nous changeons juste les noms. C'est aussi à l'aide de ce fichier qu'il est possible d'ajouter des polices d'écriture (sous forme de bitmap, d'image) à un projet, mais nous ne le ferons pas dans ce projet. Code Nous sommes enfin prêts à rentrer dans le vif du sujet, le code ! `main.c`, la boucle du jeu Nous allons commencer par faire la boucle du jeu. Pour que notre jeu soit agréable à jouer, nous allons limiter le nombre d'images par seconde auquel il peut tourner, car la calculatrice est en mesure de l'exécuter très très vite. Nous allons quitter la boucle si EXIT est appuyé, ce que nous pouvons vérifier avec `keydown(KEY_EXIT)` définie dans `<gint/keyboard.h>`. L'ensemble des codes associés à des touches peuvent être lus dans le fichier d'en-tête `<gint/keycodes.h>` (https://git.planet-casio.com/Lephenixnoir/gint/src/branch/dev/include/gint/keycodes.h). Ce fichier est inclus par `<gint/keyboard.h>`. J'ai donc choisi de limiter le nombre d'images par seconde à 50 (cela vous rappelle peut-être les jeux vidéos PAL des années 80 en Europe). NOTE : Sur PC, on calcule le temps qu'une image prend pour être produite pour adapter la vitesse des éléments qui se déplacent, pour que le jeu se comporte pareil indépendamment du nombre d'images par seconde. Le jeu est assez simple, on ne limitera donc que le nombre d'images par seconde, il est très probable qu'une image soit produite assez rapidement, et étant donné le peu de différences entre les différents modèles de calculatrices, en testant sur une calculatrice couleur et une calculatrice monochrome, la vitesse à laquelle s'exécutera le jeu est assez prévisible. Utilisation de timer Pour compter le temps qui s'est écoulé entre deux images, nous allons créer un timer. Le processeur, à un intervalle de temps régulier, arrêtera ce qu'il est en train d'exécuter, et exécutera une fonction que nous passons à `timer_configure`, puis retournera à ce qu'il était en train d'exécuter. Les fonctions liées à la gestion de timer sont définies dans `<gint/timer.h>`. D'abord il faut le configurer avec `timer_configure`, puis le lancer avec `timer_start`. Ensuite, lorsque l'on n'en a plus besoin, on l'arrête avec `timer_stop`. Cela libère aussi les ressources associées à ce timer. La macro `GINT_CALL` permet d'obtenir un `gint_call_t` à passer à `timer_configure` pour passer la fonction à appeler en argument. Cette fonction doit renvoyer l'entier (`int`) `TIMER_CONTINUE` ou `TIMER_STOP`, pour que le timer continue d'interrompre le processeur à intervalles réguliers, ou pour qu'il s'arrête. Pour qu'une fonction ayant pour prototype `int timer_call(void)` soit appelée toute les millisecondes, on peut écrire le code suivant : int timer; timer = timer_configure(TIMER_ANY, 1000, GINT_CALL(timer_call)); if(timer < 0){ /* Afficher un message d'erreur. */ dclear(C_WHITE); dtext(1, 1, C_BLACK, "Failed to create timer!"); dupdate(); getkey(); return 0; } timer_start(timer); /* Du code..., par exemple la boucle d'un jeu. */ timer_stop(timer); NOTE : `getkey` permet d'attendre l'appui d'une touche, et renvoie un objet `key_event_t` décrivant la touche qui a été appuyée (plus d'informations dans `<gint/keyboard.h>`. NOTE : Au lieu de `TIMER_ANY` on peut aussi passer `TIMER_TMU` pour un timer très précis, ou `TIMER_ETMU`, pour un timer un peu moins précis. Pour plus d'informations sur les timer, regardez le fichier d'en-tête `<gint/timer.h>`. Quelques fonctions de dessin Dans la boucle de jeu, nous allons effacer l'écran avec `dclear(C_WHITE);` et afficher ce que nous avons dessiné avec `dupdate();`. On peut afficher du texte avec `dtext`, par exemple : dtext(1, 1, C_BLACK, "Sample fxSDK add-in."); Boucle de jeu Au début de chaque itération de cette boucle, nous allons appeler `clearevents`, pour que gint traite tout les événements en attente (pour pouvoir vérifier si une touche est enfoncée avec `keydown`), et `cleareventflips`, pour que gint sache que l'on commence une nouvelle image du jeu, et qu'il mette à jour les états des touches pour que l'on puisse utiliser `keypressed` et `keyreleased`, qui permet de savoir si une touche a été relâchée ou enfoncée depuis le dernier appel à `cleareventflips`, donc depuis la dernière image. NOTE : Sur la calculatrice, `main` doit renvoyer 1 en cas de succès et pas zéro comme c'est le cas habituellement. Essayez d'écrire la boucle de jeu, puis regardez mon implémentation. #include <gint/keyboard.h> #include <gint/display.h> #include <gint/timer.h> #include <gint/cpu.h> static volatile unsigned long int ms = 0; static int timer_call(void) { ms++; return TIMER_CONTINUE; } struct game game; int main(void) { int timer; timer = timer_configure(TIMER_TMU, 1000, GINT_CALL(timer_call)); if(timer < 0){ dclear(C_WHITE); dtext(1, 1, C_BLACK, "Failed to create timer!"); dupdate(); getkey(); return 0; } timer_start(timer); /* Initialisation du jeu. */ game_init(&game); /* Boucle de jeu. */ do{ cleareventflips(); clearevents(); /* Generation d'une image du jeu. */ dclear(C_WHITE); game_logic(&game); game_draw(&game); dupdate(); /* Attendre que 20ms se soient ecoulees. */ while(ms < 20) sleep(); ms = 0; }while(!keydown(KEY_EXIT)); timer_stop(timer); /* On retourne 1, car c'est ce que des add-ins doivent renvoyer en cas de * succes. */ return 1; } Testez. Si vous arrivez bien à quitter le jeu en appuyant sur EXIT, continuez. `game.c` et `game.h` : initialisation, rendu et logique du jeu J'aime bien structurer mes jeux en séparant l'initialisation, la logique et le dessin, en stockant l'état dans lequel le jeu se trouve et en traitant chaque état dans des grands `switch` dans les fonctions s'occupant de la logique et du dessin. Dans `game.h` Nous allons commencer par créer une structure contenant toutes les données du jeu, contenant pour l'instant seulement l'état dans lequel le jeu se trouve : struct game { unsigned char state; }; Nous allons définir ces différents états avec une `enum` : enum { S_INGAME, /* Etat dans lequel on est lorsque le joueur est en train de * jouer. */ S_AMOUNT }; Nous ajouterons d'autres états au fur et à mesure ou nous développerons le jeu. Dans `game.c` Ensuite, nous allons créer trois fonctions: `game_init` pour initialiser le jeu. `game_logic` pour la logique du jeu. `game_draw` pour le rendu. `game.c` contient donc : void game_init(struct game *game) { game->state = S_INGAME; } void game_logic(struct game *game) { switch(game->state){ case S_INGAME: /* TODO */ break; default: /* Cela ne devrait jamais arriver. */ game->state = S_INGAME; } } void game_draw(struct game *game) { switch(game->state){ case S_INGAME: /* TODO */ break; default: /* Cela ne devrait jamais arriver. */ game->state = S_INGAME; } } On peut ensuite appeler `game_logic`, puis `game_draw` dans la boucle du jeu, et `game_init` avant. `paddle.c` : mouvement et dessin de la raquette Nous allons maintenant commencer à écrire le code exécuté lorsque l'on se trouve dans l'état `S_INGAME`. Nous allons commencer par coder le code pour le dessin et le mouvement de la raquette du joueur. Nous allons en premier lieu créer une structure contenant des données décrivant la raquette dans `game.h` : struct paddle { /* La position de la raquette. */ int x; int y; }; Les membres `x` et `y` de `struct paddle` contiennent la position du coin en haut à gauche de la raquette. Comme dans `game.c`, nous allons créer une fonction pour initialiser une structure `paddle` : `paddle_init`. /* Initialise la raquette. */ void paddle_init(struct paddle *paddle, int x); Ensuite, nous allons écrire plusieurs fonctions utilisant cette structure, en commençant par les fonctions suivantes : /* Deplace la raquette avec les touches du clavier. */ void paddle_move(struct paddle *paddle); /* Dessine la raquette. */ void paddle_draw(struct paddle *paddle); Initialisation de la raquette. Dans `paddle_init` nous allons centrer la raquette sur l'axe des ordonnées, et mettre son abscisse à `x` (un paramètre de `paddle_init`). Pour calculer le centre de l'écran, nous pouvons utiliser les object-like macros `DWIDTH` et `DHEIGHT` définies dans `<gint/display-fx.h>` ou `<gint/display-cg.h>`. Ces en-têtes sont inclues par `<gint/display.h>`, la bonne en-tête est choisie en fonction de la plateforme visée. Dans `paddle.h` nous allons inclure `<gint/config.h>` pour identifier pour quelle plateforme nous compilons le jeu, afin de définir des macros avec des valeurs différentes pour chaque plateforme, permettant d'avoir une expérience similaire sur les calculatrices couleur ou monochrome, malgré les différences de taille d'écran. Le jeu est compilé pour une calculatrice monochrome (ou couleur avec `fxsdk build-fxg3a`), si `GINT_RENDER_MONO` est non nul. Le jeu est compilé pour une calculatrice couleur si `GINT_RENDER_RGB` est défini. Ce tutoriel n'est pas adapté aux calculatrices ClassPad, car je n'ai pas une telle machine. Cependant, il y a sûrement peu de choses à ajuster, entre autres ces macros, pour le suivre pour le développement d'un add-in pour ClassPad. L'add-in est compilé pour ClassPad si `GINT_HW_CP` est défini, et pour Graph 90+e/CG50 ou Prizm si `GINT_HW_CG` est défini. Nous allons définir la largeur de la raquette sous le nom `PADDLE_WIDTH`, sa hauteur avec `PADDLE_HEIGHT` et sa vitesse de déplacement avec `PADDLE_SPEED`. Voici comment je les ai définis : #if GINT_RENDER_MONO /* La vitesse de la raquette. */ #define PADDLE_SPEED 1 /* La taille de la raquette. */ #define PADDLE_WIDTH 3 #define PADDLE_HEIGHT 16 #elif GINT_RENDER_RGB && GINT_HW_CG /* La vitesse de la raquette. */ #define PADDLE_SPEED 3 /* La taille de la raquette. */ #define PADDLE_WIDTH 9 #define PADDLE_HEIGHT 48 #else #error "Unsupported!" #endif Essayez maintenant de coder `paddle_init`. Ensuite, vous pouvez regarder comment je l'ai implémenté : void paddle_init(struct paddle *paddle, int x) { /* Initialisation de la position de la raquette. */ paddle->x = x; paddle->y = DHEIGHT/2-PADDLE_HEIGHT/2; } Ajoutez deux `struct paddle` à `struct game` : une pour le joueur, et une pour l'ordinateur. NOTE : Lorsque j'écris « l'ordinateur », je désigne le joueur joué par la calculatrice. Ma `struct game` contient les membres suivants maintenant : struct game { /* La raquette du joueur. */ struct paddle paddle1; /* La raquette de l'ordinateur. */ struct paddle paddle2; /* L'etat dans lequel se trouve le jeu. */ unsigned char state; }; Appelez ensuite `paddle_init` dans `game_init` pour initialiser les deux raquettes. J'ai placé mes raquettes à 16 px du bord de l'écran. NOTE : On aurait aussi pu définir une object-like macro pour changer cette position d'une plateforme à l'autre. Essayez déjà de le faire par vous-même. Voici mon implémentation : void game_init(struct game *game) { /* Initialisation des raquettes aux deux cotes de l'ecran. */ paddle_init(&game->paddle1, 16); paddle_init(&game->paddle2, DWIDTH-16-PADDLE_WIDTH); game->state = S_INGAME; } Dessin de la raquette Pour vérifier que nos calculs sont corrects, nous allons dessiner la raquette. Nous allons dessiner la raquette dans `paddle_draw`, défini dans `paddle.c`. Nous allons dessiner un rectangle blanc avec un bord noir d'un pixel pour la raquette. Il est donc le bon moment de s'intéresser à quelques fonctions de dessin de gint (dont les prototypes sont dans `<gint/display.h>`) : `drect` : dessin d'un rectangle rempli `drect_border` : dessin de la bordure d'un rectangle `dpixel` : dessin d'un seul pixel `dline` : dessin d'une ligne `dcircle` : dessin d'un cercle `dellipse` : dessin d'un ellipse `dpoly` : dessin d'un polygone `dtext`, `dtext_opt` et `dprint` : affichage de texte `dimage` : dessin d'une image `dsubimage` : dessin d'une partie d'une image NOTE : Ces fonctions sont très bien documentées dans l'en-tête `<gint/display.h>`. Pour le dessin de la raquette, nous allons utiliser `drect_border`. Le prototype de `drect_border` dans `<gint/display.h>` est : void drect_border(int x1, int y1, int x2, int y2, int fill_color, int border_width, int border_color); Implémentez `paddle_draw`. Voici mon implémentation de `paddle_draw` : void paddle_draw(struct paddle *paddle) { /* Dessin d'un rectangle representant la raquette. */ drect_border(paddle->x, paddle->y, paddle->x+PADDLE_WIDTH, paddle->y+PADDLE_HEIGHT, C_NONE, 1, C_BLACK); } Pour voir les raquettes, nous allons appeler `paddle_draw` dans `game_draw`. Ma fonction `game_draw` ressemble maintenant à : void game_draw(struct game *game) { switch(game->state){ case S_INGAME: /* Dessin des raquettes. */ paddle_draw(&game->paddle1); paddle_draw(&game->paddle2); break; default: /* Cela ne devrait jamais arriver. */ game->state = S_INGAME; } } Testez votre code, et continuez si vous voyez bien les deux rectangles représentant les raquettes aux deux côtés de l'écran. Mouvement de la raquette Maintenant que le joueur voit les raquettes, nous allons lui permettre de déplacer la sienne. Nous allons utiliser `keydown` pour vérifier si les touche `KEY_UP` ou `KEY_DOWN` sont pressées pour déplacer la raquette vers le haut ou vers le bas respectivement. Nous allons aussi vérifier, que la raquette ne sort pas de l'écran (que `paddle->y >= 0 && paddle->y < DHEIGHT-PADDLE_HEIGHT`). Je vous laisse donc implémenter la fonction `paddle_move` qui a le prototype suivant : void paddle_move(struct paddle *paddle); Voici mon implémentation : void paddle_move(struct paddle *paddle) { /* Deplacement de la raquette vers le haut si la fleche "haut" est pressee. */ if(keydown(KEY_UP)){ if(paddle->y-PADDLE_SPEED >= 0){ paddle->y -= PADDLE_SPEED; }else{ paddle->y = 0; } } /* Deplacement de la raquette vers le bas si la fleche "bas" est pressee. */ if(keydown(KEY_DOWN)){ if(paddle->y+PADDLE_SPEED < DHEIGHT-PADDLE_HEIGHT){ paddle->y += PADDLE_SPEED; }else{ paddle->y = DHEIGHT-PADDLE_HEIGHT-1; } } } Nous allons appeler cette fonction dans `game_logic`, pour pouvoir voir la raquette se déplacer en fonction des actions de l'utilisateur ! Essayez par vous-même. Voici à quoi ressemble ma fonction `game_logic` maintenant : void game_logic(struct game *game) { switch(game->state){ case S_INGAME: /* Permettre au joueur de deplacer sa raquette. */ paddle_move(&game->paddle1); break; default: /* Cela ne devrait jamais arriver. */ game->state = S_INGAME; } } `fixed.h` : calculs à virgule fixe Les écrans des calculatrices sont petits : 128 × 64 pour les calculatrices monochromes, et 396 × 224 pour les calculatrices couleur (hors ClassPad). Il est donc difficile de contrôler précisément la vitesse d'objets dans un jeu en les stockant sous forme d'entiers. Une vitesse de deux pixels par image est beaucoup plus rapide qu'une vitesse d'un seul pixel par image. Nous aurons besoin de précision supplémentaire dans le déplacement de la balle. On pourrait donc utiliser des `floats`. Cependant, les calculatrices CASIO SH-3 ou SH-4 n'ont pas de FPU, une unité de calcul avec des nombres à virgule flottante dans le MPU. Ils sont donc effectués en software, et sont donc assez lents. Notre jeu est assez simple, donc cela ne pose pas de gros problème, en dehors de la taille d'add-in un peu plus grande. Dans des jeux plus complexes, cela peut poser problème. Nous allons donc faire des calculs à virgule fixe, bien moins coûteux : nous allons réserver quelque bits dans un entier pour les chiffres après la virgule. Exemple : 19,2 comme entier 16 bit non signé avec une précision de 8 bits après la virgule : Avant la virgule Après la virgule 00010011 00110011 Si nous reconvertissons ce chiffre en nombre à virgule, nous obtenons environ 19,199. Nous avons perdu en précision, mais ce n'est pas grave dans un jeu vidéo (19,199 c'est quand même très proche de 19,2 !). Codons quelque macros pour convertir des nombres à virgule en nombres à virgule fixe, et vice-versa, et d'autres pour la multiplication et la division (nous pouvons faire les additions et les soustractions comme d'habitude). La conversion est très facile, il suffit d'effectuer des décalages de bits. /* La precision des nombres a virgule fixe. */ #define PRECISION 8 /* Conversion d'un nombre vers un nombre a virgule fixe. */ #define TO_FIXED(num) (int)((num)*(1<<PRECISION)) /* Conversion d'un nombre a virgule fixe vers un entier. */ #define TO_INT(num) ((num)>>PRECISION) NOTE : Je multiplie `num` par `(1*PRECISION)` pour pouvoir passer des `float` à la macro, on ne peut pas faire des décalages de bits sur un `float`. Les macros pour la multiplication et la division sont très simples, elles aussi : /* Multiplication de deux nombres a virgule fixe. */ #define MUL(a, b) ((a*b)>>PRECISION) /* Divise le nombre a virgule fixe a par le nombre a virgule fixe b. */ #define DIV(a, b) ((a<<PRECISION)/b) Nous allons finir par définir un type `fixed_t`, pour bien les distinguer des autres entiers : /* Type virgule fixe (pour les distinguer des autres entiers). */ typedef long fixed_t; NOTE : Ce code suppose que le décalage de bits vers la droite est arithmétique (ce n'est pas forcément le cas en C), et il y a un risque de débordement lors des multiplications et des divisions si `a` et/ou `b` sont trop grands. `ball.c` : mouvement de la balle et collisions avec la raquette Tout d'abord, nous allons créer une structure contenant des données associées à la balle. Nous allons y stocker la position de la balle (`x` et `y`), la direction dans laquelle elle se déplace (`x_inc` et `y_inc`) et sa vitesse comme des `fixed_t`. Ma structure : /* La structure representant la balle (utilisee dans ball.c). */ struct ball { /* La position de la balle. */ fixed_t x; fixed_t y; /* La direction dans laquelle on deplace la balle. */ fixed_t x_inc; fixed_t y_inc; /* La vitesse de la balle. */ fixed_t speed; }; `x` et `y` contiennent la position du milieu de la balle. Initialisation et dessin de la balle Nous allons ensuite implémenter les fonctions suivantes dans `ball.c` : /* Initialisation de la balle. */ void ball_init(struct ball *ball, fixed_t x_inc, fixed_t y_inc, int speed); /* Dessin de la balle. */ void ball_draw(struct ball *ball); Commençons par `ball_init`. Nous allons placer la balle au milieu de l'écran, et initialiser les autres membres de `struct ball` aux valeurs de `x_inc`, `y_inc` et `speed`. Voici mon implémentation : void ball_init(struct ball *ball, fixed_t x_inc, fixed_t y_inc, int speed) { /* Mettre la balle au milieu de l'ecran. */ ball->x = TO_FIXED(DWIDTH/2); ball->y = TO_FIXED(DHEIGHT/2); /* Initialisation de la direction dans laquelle la balle se deplace et sa * vitesse. */ ball->x_inc = x_inc; ball->y_inc = y_inc; ball->speed = speed; } Maintenant dessinons la balle. Nous la dessinerons comme un rectangle rempli. Nous définirons une macro – `BALL_SIZE` – dans `ball.h`, contenant la moitié de la taille de la balle moins 1 (e.g. pour une balle d'une taille de trois pixels, `BALL_SIZE` est égal à (3 − 1) ÷ 2). Nous allons lui donner une valeur différente en fonction de la plateforme. Ma balle a une taille de trois pixels sur calculatrices monochromes, et une taille de sept pixels sur calculatrices couleur (hors ClassPad). Mon fichier `ball.h` contient maintenant le code suivant : #if GINT_RENDER_MONO /* La taille de la balle. */ #define BALL_SIZE 1 #elif GINT_RENDER_RGB && GINT_HW_CG /* La taille de la balle. */ #define BALL_SIZE 3 #else #error "Unsupported!" #endif Codez `ball_draw` en utilisant `BALL_SIZE`. Ma fonction `ball_draw` est la suivante : void ball_draw(struct ball *ball) { /* Get the position of the ball on screen. */ int screen_x = TO_INT(ball->x); int screen_y = TO_INT(ball->y); /* Draw a rectangle and fill it to make the ball more visible. */ drect(screen_x-BALL_SIZE, screen_y-BALL_SIZE, screen_x+BALL_SIZE, screen_y+BALL_SIZE, C_BLACK); } Il est maintenant temps de tester si notre nouveau code fonctionne bien ! Pour cela, ajoutons une balle à `struct game`. Ma `struct game` avec cet ajout : struct game { /* La balle. */ struct ball ball; /* La raquette du joueur. */ struct paddle paddle1; /* La raquette de l'ordinateur. */ struct paddle paddle2; /* L'etat dans lequel se trouve le jeu. */ unsigned char state; }; Appelez `ball_init` dans `game_init`. Définissons une macro – `DEFAULT_SPEED` – dans `ball.h`, contenant la vitesse de la balle au début d'une partie. Définitions de macros dans mon fichier `ball.h` : #if GINT_RENDER_MONO /* La vitesse de la balle par defaut. */ #define DEFAULT_SPEED 0.2 /* La taille de la balle. */ #define BALL_SIZE 1 #elif GINT_RENDER_RGB && GINT_HW_CG /* La vitesse de la balle par defaut. */ #define DEFAULT_SPEED 0.6 /* La taille de la balle. */ #define BALL_SIZE 3 #else #error "Unsupported!" #endif Ma fonction `game_init` avec cet ajout : void game_init(struct game *game) { /* Initialisation de la balle et initialisation de sa direction pour un * deplacement en direction du joueur. */ ball_init(&game->ball, TO_FIXED(-1), TO_FIXED(1), TO_FIXED(DEFAULT_SPEED)); /* Initialisation des raquettes aux deux cotes de l'ecran. */ paddle_init(&game->paddle1, 16); paddle_init(&game->paddle2, DWIDTH-16-PADDLE_WIDTH); game->state = S_INGAME; } NOTE : Notre vecteur de direction dans laquelle la balle se déplace n'est pas normalisé, et la vitesse de la balle diffèrera un peu avec la direction dans laquelle la balle se déplace. Ce n'est pas trop grave dans ce jeu, et cela évite de devoir faire des calculs un peu plus compliqués. Appelez `ball_draw` dans `game_draw`. Ma fonction `game_draw` : void game_draw(struct game *game) { switch(game->state){ case S_INGAME: /* Dessin des raquettes. */ paddle_draw(&game->paddle1); paddle_draw(&game->paddle2); /* Dessin de la balle. */ ball_draw(&game->ball); break; default: /* Cela ne devrait jamais arriver. */ game->state = S_INGAME; } } Testez le jeu. Est-ce que votre jeu compile sans erreurs ? Est-ce que vous voyez bien une balle au centre de l'écran avec des raquettes des deux côtés de l'écran que vous pouvez déplacer ? Si oui, continuez, sinon cherchez les erreurs dans votre code. Mouvement de la balle et rebonds aux bords de l'écran Nous allons maintenant programmer `ball_move` pour déplacer notre balle. /* Mouvement de la balle. */ char ball_move(struct ball *ball); Nous allons calculer la nouvelle position de la balle en ajoutant à sa position actuelle (`ball->x_inc` × `ball->speed`; `ball->y_inc` × `ball->speed`). Nous ajusterons cette nouvelle position pour faire rebondir la balle sur les bords en haut et en bas de l'écran. Si la balle sort de l'écran à gauche, nous retournons 2 (l'ordinateur marque un point). Si elle sort de l'écran à droite, nous retournons 1 (le joueur marque un point). Sinon nous retournons 0. Cela nous permettra de compter les points. Codez `ball_move`. Voici ma fonction `ball_move` : char ball_move(struct ball *ball) { /* Les nouveaux coordonnés de la balle (à l'écran). */ int dest_x = TO_INT(ball->x+MUL(ball->x_inc, ball->speed)); int dest_y = TO_INT(ball->y+MUL(ball->y_inc, ball->speed)); /* Si dest_x > DWIDTH le joueur marque un point. */ if(dest_x > DWIDTH-BALL_SIZE) return 1; /* Si dest_x < 0 l'ordinateur marque un point. */ if(dest_x < BALL_SIZE) return 2; /* Faire rebondir la balle sur les bords de l'écran. */ if(dest_y < BALL_SIZE){ ball->y_inc = -ball->y_inc; ball->y = TO_FIXED(BALL_SIZE); } if(dest_y > DHEIGHT-BALL_SIZE){ ball->y_inc = -ball->y_inc; ball->y = TO_FIXED(DHEIGHT-BALL_SIZE); } /* Déplacement de la balle. */ ball->x += MUL(ball->x_inc, ball->speed); ball->y += MUL(ball->y_inc, ball->speed); return 0; } Nous allons tester ce nouveau bout de code en appelant dans `game_logic`. Pour l'instant, nous ferrons rebondir la balle sur les bords de l'écran si la valeur renvoyée par `ball_move` est non nulle. Appelez `ball_move` dans `game_logic`. Mon code de `game_logic` : void game_logic(struct game *game) { switch(game->state){ case S_INGAME: { char move; /* Permettre au joueur de deplacer sa raquette. */ paddle_move(&game->paddle1); /* Deplacement de la balle. */ move = ball_move(&game->ball); if(move == 1){ game->ball.x = TO_FIXED(DWIDTH-BALL_SIZE); game->ball.x_inc = -game->ball.x_inc; }else if(move == 2){ game->ball.x = TO_FIXED(BALL_SIZE); game->ball.x_inc = -game->ball.x_inc; } } break; default: /* Cela ne devrait jamais arriver. */ game->state = S_INGAME; } } Testez le jeu. Est-ce que le code compile et est-ce que la balle rebondit bien sur les bords de l'écran ? Rebonds sur la raquette Notre jeu est presque fini (si l'on exclut l'écran de titre et la fin d'une partie) ! Faisons rebondir la balle sur les raquettes ! Pour cela nous allons coder `paddle_will_collide_with_ball` et `ball_bounce`, qui ont les prototypes suivants : /* Verifier si la balle heurte la raquette. */ char paddle_will_collide_with_ball(struct paddle *paddle, struct ball *ball); /* Faire rebondir la balle sur une raquette. */ void ball_bounce(struct ball *ball, struct paddle *paddle); Programmez `paddle_will_collide_with_ball` qui renvoie 1 si la balle va heurter la raquette lors de son déplacement (on ne vérifie pas si elle heurte le haut ou le bas de la raquette pour que notre code soit plus simple). Voici mon implémentation : char paddle_will_collide_with_ball(struct paddle *paddle, struct ball *ball) { /* On calcule la nouvelle abscisse de la balle. */ fixed_t dest_x = ball->x+MUL(ball->x_inc, ball->speed); /* NOTE: Un seul *if statement* serait suffisant, mais le code serait * peut-etre un peu plus dur a comprendre. */ /* Est-ce que la balle est sur la raquette sur l'axe des ordonnees ? */ if(ball->y+TO_FIXED(BALL_SIZE) >= TO_FIXED(paddle->y) && ball->y-TO_FIXED(BALL_SIZE) <= TO_FIXED(paddle->y+PADDLE_HEIGHT)){ /* Est-ce que la balle traverserait la raquette (sur l'axe des * abscisses) ? */ if((ball->x > TO_FIXED(paddle->x+PADDLE_WIDTH+BALL_SIZE) && dest_x <= TO_FIXED(paddle->x+PADDLE_WIDTH+BALL_SIZE)) || (ball->x < TO_FIXED(paddle->x-BALL_SIZE) && dest_x >= TO_FIXED(paddle->x-BALL_SIZE))){ /* The ball will hit the paddle. */ return 1; } } return 0; } NOTE : Je ne calcule pas la nouvelle ordonnée de la balle. Si nous la calculons, la différence ne se remarquerait sûrement pas. Une méthode plus exacte pour vérifier s'il y a une collision serait de vérifier si le segment formé par la position actuelle et la prochaine position de la balle coupe l'un des segments verticaux du rectangle qu'est notre raquette. Dans `ball_bounce` nous allons calculer la nouvelle direction dans laquelle la balle se déplace en fonction de la position où la balle heurte la raquette. Nous allons juste inverser `x_inc`, c'est `y_inc` que nous ajusterons en fonction de la position à laquelle la balle heurte la raquette. Nous allons définir quelque macros pour pouvoir ajuster comment `y_inc` est calculé. `ANGLE_MIN` : La valeur de `y_inc` après que la balle heurte le haut de la raquette `ANGLE_MAX` : La valeur de `y_inc` après que la balle heurte le bas de la raquette `ANGLE_RANGE` : La valeur absolue de la différence entre `ANGLE_MIN` et `ANGLE_MAX` Nous calculerons `y_inc` en effectuant de l'interpolation linéaire entre `ANGLE_MIN` et `ANGLE_MAX` avec la position à laquelle la balle heurte la raquette. Nous allons aussi définir `SPEED_INC` et `MAX_SPEED` pour augmenter la vitesse de la balle à chaque rebond, en la limitant à un certain seuil. Je définis maintenant les macros suivantes dans `ball.h` : #if GINT_RENDER_MONO /* La vitesse de la balle par defaut. */ #define DEFAULT_SPEED 0.2 /* La vitesse maximale de la balle. */ #define MAX_SPEED 2 /* L'augmentation de la vitesse de la balle a chaque rebond. */ #define SPEED_INC 0.1 /* La taille de la balle. */ #define BALL_SIZE 1 #elif GINT_RENDER_RGB && GINT_HW_CG /* La vitesse de la balle par defaut. */ #define DEFAULT_SPEED 0.6 /* La vitesse maximale de la balle. */ #define MAX_SPEED 6 /* L'augmentation de la vitesse de la balle a chaque rebond. */ #define SPEED_INC 0.3 /* La taille de la balle. */ #define BALL_SIZE 3 #else #error "Unsupported!" #endif /* La valeur de y_inc lorque la balle heurte le haut de la raquette. */ #define ANGLE_TOP -1 /* La valeur de y_inc lorque la balle heurte le bas de la raquette. */ #define ANGLE_BOTTOM 1 /* La l'ecart entre les valeurs de y_inc pour le haut et le bas de la * raquette. */ #define ANGLE_RANGE 2 Codez maintenant `ball_bounce`. Voici mon implémentation de `ball_bounce` : void ball_bounce(struct ball *ball, struct paddle *paddle) { /* Linear interpolation to calculate the new angle of the ball. */ int pos_on_the_paddle = ball->y-TO_FIXED(paddle->y); /* Clamp pos_on_the_paddle between 0 and TO_FIXED(PADDLE_HEIGHT). */ if(pos_on_the_paddle < 0){ pos_on_the_paddle = 0; }else if(pos_on_the_paddle > TO_FIXED(PADDLE_HEIGHT)){ pos_on_the_paddle = TO_FIXED(PADDLE_HEIGHT); } /* Perform the linear interpolation. */ ball->y_inc = TO_FIXED(ANGLE_TOP)+DIV(ANGLE_RANGE*pos_on_the_paddle, TO_FIXED(PADDLE_HEIGHT)); /* Flip ball->x_inc's sign to have it going towards the other paddle. */ ball->x_inc = -ball->x_inc; /* Increase the ball speed because we hit a paddle. */ if(ball->speed < TO_FIXED(MAX_SPEED)){ ball->speed += TO_FIXED(SPEED_INC); } } Nous allons ensuite utiliser nos deux nouvelles fonctions dans `game_logic`. Faites rebondir la balle sur les raquettes et réinitialisez la position de la balle lorsqu'un joueur marque un point. Si le joueur marque, elle ira en direction de l'ordinateur. Sinon elle ira en direction de la raquette controllée par la calculatrice. Voici ma fonction `game_logic` maintenant : void game_logic(struct game *game) { switch(game->state){ case S_INGAME: { char move; /* Permettre au joueur de deplacer sa raquette. */ paddle_move(&game->paddle1); /* Si la balle heurte la raquette du joueur, on la fait * rebondir. */ if(paddle_will_collide_with_ball(&game->paddle1, &game->ball)){ ball_bounce(&game->ball, &game->paddle1); } /* Si la balle heurte la raquette de l'ordinateur, on la fait * rebondir. */ if(paddle_will_collide_with_ball(&game->paddle2, &game->ball)){ ball_bounce(&game->ball, &game->paddle2); } /* Deplacement de la balle. */ move = ball_move(&game->ball); if(move == 1){ /* On remet la balle au milieu de l'ecran et on l'envoie en * direction de l'ordinateur. */ ball_init(&game->ball, TO_FIXED(1), TO_FIXED(1), TO_FIXED(DEFAULT_SPEED)); }else if(move == 2){ /* On remet la balle au milieu de l'ecran et on l'envoie en * direction du joueur. */ ball_init(&game->ball, TO_FIXED(-1), TO_FIXED(1), TO_FIXED(DEFAULT_SPEED)); } } break; default: /* Cela ne devrait jamais arriver. */ game->state = S_INGAME; } } Testez le jeu, vérifiez qu'il compile bien et que vous pouvez renvoyer la balle. Mouvement de la raquette de l'ordinateur. La raquette à droite ne se déplace pas pour l'instant. Corrigeons ça pour que la calculatrice joue contre nous. Nous allons écrire ce code dans la fonction `paddle_computer_move` : /* Déplacement de la raquette par l'ordinateur. */ void paddle_computer_move(struct paddle *paddle, struct ball *ball); Nous déplacerons la raquette vers le bas, si cela permet au milieu de la raquette de se rapprocher de la balle, ou vers le haut, si cela rapproche le milieu de la raquette de la balle. Nous ferrons attention que la raquette reste bien à l'intérieur de l'écran. Programmez `paddle_computer_move`. Mon implémentation de cette fonction : void paddle_computer_move(struct paddle *paddle, struct ball *ball) { /* Distance between the center of the paddle and the ball on the y axis. */ int distance = abs(TO_INT(ball->y)-(paddle->y+PADDLE_HEIGHT/2)); /* Move the paddle up if it reduces its distance to the ball. */ if(abs(TO_INT(ball->y)- (paddle->y+PADDLE_HEIGHT/2-PADDLE_SPEED)) < distance){ if(paddle->y-PADDLE_SPEED >= 0){ paddle->y -= PADDLE_SPEED; }else{ paddle->y = 0; } }else if(abs(TO_INT(ball->y)- (paddle->y+PADDLE_HEIGHT/2+PADDLE_SPEED)) < distance){ /* Move the paddle down if the ball y position is bigger than the * paddle y position. */ if(paddle->y+PADDLE_SPEED < DHEIGHT-PADDLE_HEIGHT){ paddle->y += PADDLE_SPEED; }else{ paddle->y = DHEIGHT-PADDLE_HEIGHT-1; } } } Appelez cette fonction dans `game_logic`. Voici à quoi ressemble ma fonction `game_logic` maintenant : void game_logic(struct game *game) { switch(game->state){ case S_INGAME: { char move; /* Permettre au joueur de deplacer sa raquette. */ paddle_move(&game->paddle1); /* Deplacement de la raquette de l'ordinateur. */ paddle_computer_move(&game->paddle2, &game->ball); /* Si la balle heurte la raquette du joueur, on la fait * rebondir. */ if(paddle_will_collide_with_ball(&game->paddle1, &game->ball)){ ball_bounce(&game->ball, &game->paddle1); } /* Si la balle heurte la raquette de l'ordinateur, on la fait * rebondir. */ if(paddle_will_collide_with_ball(&game->paddle2, &game->ball)){ ball_bounce(&game->ball, &game->paddle2); } /* Deplacement de la balle. */ move = ball_move(&game->ball); if(move == 1){ /* On remet la balle au milieu de l'ecran et on l'envoie en * direction de l'ordinateur. */ ball_init(&game->ball, TO_FIXED(1), TO_FIXED(1), TO_FIXED(DEFAULT_SPEED)); }else if(move == 2){ /* On remet la balle au milieu de l'ecran et on l'envoie en * direction du joueur. */ ball_init(&game->ball, TO_FIXED(-1), TO_FIXED(1), TO_FIXED(DEFAULT_SPEED)); } } break; default: /* Cela ne devrait jamais arriver. */ game->state = S_INGAME; } } Testez le jeu. Maintenant, nous avons enfin un jeu de pong où l'on peut s'échanger la balle avec la raquette de droite ! Il ne reste plus qu'à compter les points, ajouter un écran de titre et un écran de fin pour afficher qui a gagné, et le jeu est fini ! `game.c` : comptage des points, écran de fin et écran de titre Comptage des points Pour compter les points marqués par le joueur ou l'ordinateur, nous allons ajouter deux membres à `struct game` : `score1` et `score2`. Ils auront le type `unsigned char`, car nos scores ne dépasseront pas 255. Voici ma `struct game` après ces ajouts : struct game { /* La balle. */ struct ball ball; /* La raquette du joueur. */ struct paddle paddle1; /* La raquette de l'ordinateur. */ struct paddle paddle2; /* Le score du joueur. */ unsigned char score1; /* Le score de l'ordinateur. */ unsigned char score2; /* L'etat dans lequel se trouve le jeu. */ unsigned char state; }; `score1` et `score2` seront initialisés à 0 dans `game_init`. Mon implémentation de `game_init` : void game_init(struct game *game) { /* Initialisation de la balle et initialisation de sa direction pour un * deplacement en direction du joueur. */ ball_init(&game->ball, TO_FIXED(-1), TO_FIXED(1), TO_FIXED(DEFAULT_SPEED)); /* Initialisation des raquettes aux deux cotes de l'ecran. */ paddle_init(&game->paddle1, 16); paddle_init(&game->paddle2, DWIDTH-16-PADDLE_WIDTH); /* Initialisation du score a 0. */ game->score1 = 0; game->score2 = 0; game->state = S_INGAME; } Nous allons définir `MAX_SCORE`, le score maximal que l'on peut atteindre dans `game.h`. Par exemple : #define MAX_SCORE 11 Dans notre enumération d'états du jeu dans `game.h` nous allons ajouter `S_END`, pour la fin d'une partie, où le gagnant est affiché, et `S_TITLE` pour l'écran de titre. Voici mon enumération : /* The screens. */ enum { S_TITLE, S_INGAME, S_END, S_AMOUNT }; Nous allons incrémenter le score du joueur ou celui de l'ordinateur dans `game_logic` lorsqu'un point est marqué. Si le score dépasse `SCORE_MAX`, nous passerons à l'état `S_END`. Voici ma fonction `game_logic` maintenant : void game_logic(struct game *game) { switch(game->state){ case S_INGAME: { char move; /* Permettre au joueur de deplacer sa raquette. */ paddle_move(&game->paddle1); /* Deplacement de la raquette de l'ordinateur. */ paddle_computer_move(&game->paddle2, &game->ball); /* Si la balle heurte la raquette du joueur, on la fait * rebondir. */ if(paddle_will_collide_with_ball(&game->paddle1, &game->ball)){ ball_bounce(&game->ball, &game->paddle1); } /* Si la balle heurte la raquette de l'ordinateur, on la fait * rebondir. */ if(paddle_will_collide_with_ball(&game->paddle2, &game->ball)){ ball_bounce(&game->ball, &game->paddle2); } /* Deplacement de la balle. */ move = ball_move(&game->ball); if(move == 1){ /* Le joueur a un point. */ game->score1++; /* On remet la balle au milieu de l'ecran et on l'envoie en * direction de l'ordinateur. */ ball_init(&game->ball, TO_FIXED(1), TO_FIXED(1), TO_FIXED(DEFAULT_SPEED)); /* Si le joueur a eu le score maximal, il gagne, et on va a * l'ecran de fin de partie. */ if(game->score1 >= MAX_SCORE) game->state = S_END; }else if(move == 2){ /* L'ordinateur a un point. */ game->score2++; /* On remet la balle au milieu de l'ecran et on l'envoie en * direction du joueur. */ ball_init(&game->ball, TO_FIXED(-1), TO_FIXED(1), TO_FIXED(DEFAULT_SPEED)); /* Si l'ordinateur a eu le score maximal, il gagne, et on * va a l'ecran de fin de partie. */ if(game->score2 >= MAX_SCORE) game->state = S_END; } } break; default: /* Cela ne devrait jamais arriver. */ game->state = S_INGAME; } } Dans `game_draw` nous allons afficher le score avec `dprint`. `dprint` a le prototype suivant : void dprint(int x, int y, int fg, char const *format, ...); Le format est pareil que pour `printf`. Voici comment j'ai affiché le score dans `game_draw` : /* Affichage du score du joueur. */ dprint(DWIDTH/2-8-5*3, 8, C_BLACK, "%d", game->score1); /* Affichage du score de l'ordinateur. */ dprint(DWIDTH/2+8, 8, C_BLACK, "%d", game->score2); Testez le jeu, et vous devriez voir le score changer en haut de votre écran. Écran de fin Pour l'écran de fin, dans `game_logic`, nous allons vérifier si `KEY_SHIFT` est pressée. Nous allons utiliser `keypressed`, qui permet de savoir si une touche a été enfoncée depuis la dernière image (ici nous pourrions aussi utiliser `keydown`). Si `KEY_SHIFT` est pressée, nous allons aller à l'écran de titre que nous coderons plus tard. Gérez l'écran de fin de partie dans `game_logic`. Voici mon code : case S_END: /* Si shift est pressee, aller a l'ecran de titre. */ if(keypressed(KEY_SHIFT)){ game->state = S_TITLE; } break; Dans `game_draw` nous allons afficher qui a gagné, tout en affichant la balle et les raquettes avec les scores. Nous ne mettrons donc pas de `break` après avoir traité le cas `S_END`, au-dessus du code pour traiter le cas où `game->state` est égal à `S_INGAME`, pour continuer dans le code pour dessiner la balle, les raquettes et les scores. NOTE : Les raquettes, les scores et la balle sont donc dessinés au-dessus de ce que l'on dessine pour le cas où `game->state` est égal à `S_INGAME`. Pour éviter un avertissement, il suffit d'ajouter le commentaire `/* FALLTHRU */`. Pour afficher qui a gagné, nous utiliserons `dtext` pour afficher du texte. Voici son prototype dans `<gint/display.h>` : void dtext(int x, int y, int fg, char const *str); Affichez qui a gagné. Voici comment j'ai procédé : J'ai utilisé des macros pour calculer où placer le texte : #define WIN "YOU WIN!" #define GAME_OVER "GAME OVER" /* La position du texte GAME_OVER sur l'axe des abscisses. */ #define GAME_OVER_X (DWIDTH/2-5*sizeof(GAME_OVER)/2) /* La position du texte WIN sur l'axe des abscisses. */ #define WIN_X (DWIDTH/2-5*sizeof(WIN)/2) /* La position du texte WIN ou GAME_OVER sur l'axe des ordonnees. */ #define END_Y (DHEIGHT/2-7/2) Voici ma fonction `game_draw` : void game_draw(struct game *game) { switch(game->state){ case S_END: if(game->score1 > game->score2){ /* Le joueur a gagne, donc on l'affiche. */ dtext(WIN_X, END_Y, C_BLACK, WIN); }else{ /* Le joueur a perdu, on l'affiche. */ dtext(GAME_OVER_X, END_Y, C_BLACK, GAME_OVER); } /* Do not break here to draw the in-game screen. */ /* FALLTHRU */ case S_INGAME: /* Affichage du score du joueur. */ dprint(DWIDTH/2-8-5*3, 8, C_BLACK, "%d", game->score1); /* Affichage du score de l'ordinateur. */ dprint(DWIDTH/2+8, 8, C_BLACK, "%d", game->score2); /* Dessin des raquettes. */ paddle_draw(&game->paddle1); paddle_draw(&game->paddle2); /* Dessin de la balle. */ ball_draw(&game->ball); break; default: /* Cela ne devrait jamais arriver. */ game->state = S_INGAME; } } Testez, est-ce que l'écran de fin s'affiche correctement ? Écran de titre Nous allons maintenant coder la dernière chose manquante à notre jeu : l'écran de titre. Normalement vous avez déjà réalisé des jolis écrans de titre : un de 128 × 64 pour les calculatrices monochromes, et un autre d'une dimension de 396 × 224 pixels pour les calculatrices couleur. la variable qui contient les données de cette image (qui est différente selon la plateforme) s'appelle `title_img` comme nous l'avons indiqué dans `fxconv_metadata.txt`. Pour pouvoir y accéder, il suffit d'ajouter la ligne suivante à `game.c` : extern const bopti_image_t title_img; Ensuite, dans `game_draw`, lorsque `game->state` est égal à `S_TITLE`, nous allons remplir l'écran avec cette image en utilisant `dimage`. Le prototype de `dimage` dans `<gint/display.h>` : void dimage(int x, int y, bopti_image_t const *image); Gérez le cas où `game->state` est égal à `S_TITLE` dans `game_draw`. Voici mon implémentation : void game_draw(struct game *game) { switch(game->state){ case S_TITLE: /* Affichage de l'image de l'écran de titre. */ dimage(0, 0, &title_img); break; case S_END: if(game->score1 > game->score2){ /* Le joueur a gagne, donc on l'affiche. */ dtext(WIN_X, END_Y, C_BLACK, WIN); }else{ /* Le joueur a perdu, on l'affiche. */ dtext(GAME_OVER_X, END_Y, C_BLACK, GAME_OVER); } /* Do not break here to draw the in-game screen. */ /* FALLTHRU */ case S_INGAME: /* Affichage du score du joueur. */ dprint(DWIDTH/2-8-5*3, 8, C_BLACK, "%d", game->score1); /* Affichage du score de l'ordinateur. */ dprint(DWIDTH/2+8, 8, C_BLACK, "%d", game->score2); /* Dessin des raquettes. */ paddle_draw(&game->paddle1); paddle_draw(&game->paddle2); /* Dessin de la balle. */ ball_draw(&game->ball); break; default: /* Cela ne devrait jamais arriver, mais si cela arrive, nous * retournons a l'ecran de titre. */ game->state = S_TITLE; } } NOTE : J'ai changé l'état auquel nous allons lorsque `game->state` contient une valeur inattendue : on va maintenant à l'écran de titre. Traitons ce cas aussi dans `game_logic`. Si `KEY_SHIFT` est pressée, nous allons réinitialiser le jeu (avec `game_init`) et passer à l'état `S_INGAME`. NOTE : Ici nous devons utiliser `keypressed` (ou `keyreleased`) : Lorsque le jeu se trouve à l'état `S_END`, et que le joueur appuie sur shift, le jeu passe à l'état `S_TITLE` et il est très probable que le joueur n'ait pas encore relaché la touche, donc `keydown` retournerait `true`. Le jeu passerait donc directement à l'état `S_INGAME` (après avoir appelé `game_init`). Gérez le passage de l'écran de titre à l'état `S_INGAME`. Voici mon implémentation : void game_logic(struct game *game) { switch(game->state){ case S_TITLE: /* On attend que la touch shift soit pressee pour lancer une * partie. */ if(keypressed(KEY_SHIFT)){ /* Reinitialisation du jeu. */ game_init(game); game->state = S_INGAME; } break; switch(game->state){ case S_INGAME: { char move; /* Permettre au joueur de deplacer sa raquette. */ paddle_move(&game->paddle1); /* Deplacement de la raquette de l'ordinateur. */ paddle_computer_move(&game->paddle2, &game->ball); /* Si la balle heurte la raquette du joueur, on la fait * rebondir. */ if(paddle_will_collide_with_ball(&game->paddle1, &game->ball)){ ball_bounce(&game->ball, &game->paddle1); } /* Si la balle heurte la raquette de l'ordinateur, on la fait * rebondir. */ if(paddle_will_collide_with_ball(&game->paddle2, &game->ball)){ ball_bounce(&game->ball, &game->paddle2); } /* Deplacement de la balle. */ move = ball_move(&game->ball); if(move == 1){ /* Le joueur a un point. */ game->score1++; /* On remet la balle au milieu de l'ecran et on l'envoie en * direction de l'ordinateur. */ ball_init(&game->ball, TO_FIXED(1), TO_FIXED(1), TO_FIXED(DEFAULT_SPEED)); /* Si le joueur a eu le score maximal, il gagne, et on va a * l'ecran de fin de partie. */ if(game->score1 >= MAX_SCORE) game->state = S_END; }else if(move == 2){ /* L'ordinateur a un point. */ game->score2++; /* On remet la balle au milieu de l'ecran et on l'envoie en * direction du joueur. */ ball_init(&game->ball, TO_FIXED(-1), TO_FIXED(1), TO_FIXED(DEFAULT_SPEED)); /* Si l'ordinateur a eu le score maximal, il gagne, et on * va a l'ecran de fin de partie. */ if(game->score2 >= MAX_SCORE) game->state = S_END; } } break; default: /* Cela ne devrait jamais arriver, mais si cela arrive, nous * retournons a l'ecran de titre. */ game->state = S_TITLE; } } NOTE : J'ai changé l'état auquel nous allons lorsque `game->state` contient une valeur inattendue : on va maintenant à l'écran de titre. Corrigez maintenant `game_init` pour se trouver à l'écran de titre au lancement du jeu. Ma fonction `game_init` : void game_init(struct game *game) { /* Initialisation de la balle et initialisation de sa direction pour un * deplacement en direction du joueur. */ ball_init(&game->ball, TO_FIXED(-1), TO_FIXED(1), TO_FIXED(DEFAULT_SPEED)); /* Initialisation des raquettes aux deux cotes de l'ecran. */ paddle_init(&game->paddle1, 16); paddle_init(&game->paddle2, DWIDTH-16-PADDLE_WIDTH); /* Initialisation du score a 0. */ game->score1 = 0; game->score2 = 0; game->state = S_TITLE; } Testez, et vérifiez que tout fonctionne correctement. Le jeu est fini. Le mot de la fin Bravo ! Vous êtes arrivés au bout de ce tutoriel ! Vous avez développé tout un jeu avec gint ! Si vous cherchez des informations sur des fonctions de gint, l'un des meilleures ressources sont les en-têtes de gint, elles sont très bien documentées. Les topics de gint, du fxsdk et le tutoriel officiel d'utilisation de gint sont aussi très utiles : Le topic de gint : https://www.planet-casio.com/Fr/forums/topic13572-74-gint-un-noyau-pour-developper-des-add-ins.html Le topic du fxsdk : https://www.planet-casio.com/Fr/forums/topic13164-last-fxsdk-un-sdk-alternatif-pour-ecrire-des-add-ins.html Le tutoriel d'utilisation de gint : https://www.planet-casio.com/Fr/forums/topic14914-1-tutoriels-dutilisation-de-gint.html Et un grand merci à Lephé pour avoir développé ce super SDK ! Sun, 19 Jul 2026 20:47:21 +0200 Functions List for CPBasic! (with syntax) https://www.planet-casio.com/Fr/forums/topic19505--.html The full list of all commands usable during programming for CPBasic, WITH SYNTAX!! Link: [CPII Functions and Programs] Sun, 19 Apr 2026 21:40:18 +0200 bug sur la fx-92+ sc https://www.planet-casio.com/Fr/forums/topic19452--.html Salut toute la communauté Casio Il n'y a pas longtemps, j'ai découvert par hasard un bug d'affichage dans la suppression de saut de ligne sur la fx-92+ sc Enfaite si l'on se met en Sligne/Religne ou Sligne/Rdéc et qu'on est en petite police, si l'on saute dés le début une ligne (avec " : ") et que l'on écrit des chiffres sur une ligne (entre 17 et 31 caractère pour que ça fonctionne), et qu'on les laisses ensuite qu'on supprime le " : " donc le saut de ligne, a partir du 17éme chiffre, les chiffres ou caractères resteront bloquer dans l'affichage sans que la calculatrice n'en prend compte, on peut même supprimer la ligne de base, et les chiffres resteront quand même jusqu'à se que un affichage arrive sur la même ligne. Je sais que se glitch est inutile mais il est assez amusant pour faire des saut de ligne au milieux sans voir les " : " Exemple de se que sa pourrait donner: de sa: -------------------------------------- ‎ ‎ ‎ ‎ : ‎ ‎ ‎ ‎ 1234567890123456789 -------------------------------------- à sa: -------------------------------------- ‎ ‎ ‎ ‎ 1234567890123456789 ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ 789 -------------------------------------- Fri, 27 Feb 2026 15:30:18 +0100 utilisation de BIDE https://www.planet-casio.com/Fr/forums/topic19351--.html Bonsoir la communauté, Je viens tout juste d’acquérir une Graph 35+ E II pour tenter de programmer une navigation astronomique. J’ai saisi mon code dans BIDE à partir d’un fichier texte, mais je bloque désormais pour le convertir en fichier compatible Casio. Je dois avouer que je suis un peu dépassé par toute cette technologie : les calculatrices que j’utilisais au lycée n’étaient pas aussi sophistiquées. Si certains d’entre vous peuvent m’aider, je vous en serai très reconnaissant. Après tout, j’ai entre les mains une véritable merveille, mais je ne suis pas encore à la hauteur pour la faire parler comme je le souhaiterais. Un grand merci d’avance, Marko Wed, 17 Dec 2025 08:53:39 +0100 Casio 3-pin serial comms limitation removed - Picaxe and ESP / Arduino datalogging https://www.planet-casio.com/Fr/forums/topic19127--.html My original code from 2006 has been used by others, and after 20 years I revisited using a Casio FX-9750 as a datalogger. Experiments proved it is possible to pause the calculator in the middle of a RECEIVE() request WITHOUT generating a COM error. This likely works with FX-9860 and FX-CG50 3-pin ports. Quick history: I did the original Picaxe Forum post nearly 20 years ago of classroom use of a Casio FX-9750G connected to a Picaxe for data logging in Physics classes. Forum member Anobium then did a version later in 2011/2012. Clearing out my home lab recently I found my old class set I had made for an education research project (published) when I recalled an idea I had at the time but never tested until now. For the last 20 years the wider community documentation interpreted the Casio FX-9750 and FX-9860 link mode as synchronous and blocking: once a transfer begins, both connected devices must complete it. This prohibited real-time interval sampling because Casio BASIC lacks timing functions like PAUSE, WAIT, or a real-time clock. The calculator alone cannot manage precise time-based data collection. Instead, data acquisition units like the EA-100, EA-200 and CLAB handled timed interval sampling independently of the FX-9750 and FX-9860. However they cost $400 - $900. The innovation: This post shares that I have invented and tested a new Picaxe BASIC algorithm that uses a previously undocumented and unknown exploit that permits pausing the RECEIVE() transmission without generating a COM timeout error. I have completed a single universal Picaxe Basic program for 08M2 and 14M2 chips. For simple sensor units for younger learners the 08M2 chip connected to a single analog sensor (e.g. Temperature, light, sound, heartbeat) and/or a single digital input. For rates of change or more complex experiments in chemistry, physics or biology, a 14M2 which permits logging up to 3 sensors simultaneously. The innovation is that both loggers now ALSO RECORD ELAPSED TIME STAMPS with the sensor readings! The simple 3-pin serial protocol likely works with FX-9860 and FX-CG50 calculators. This saves $$$$ having to purchase EA-200 or CLAB data acquisition units to enable data logging on the calculators. The Casio BASIC program is also universal: It works with any connected microcontroller or board; Picaxe, ESP8266, ESP32 and others. As a proof of concept, I have a working Wemos D1 mini running my new RECEIVE() algorithm. More details here: https://mikefentonnz.github.io/projects/casio-calculator-data-logger-hack.html Sun, 26 Oct 2025 03:55:25 +0100 [En] 5800p: The exception and functional upgrade https://www.planet-casio.com/Fr/forums/topic18893--.html This is a translation and some additions of BCX74687's post. For original post please see this. First published on cncalc. Also available on UCF. The operating instructions were from the translator (qq1122). Preface and some comments are from BCX74687. Reproductions are allowed, but Don't use this for bussiness. 中文版请参见两斤时间的这个帖子 语言翻译不一定100%准确。Language translations may not be 100% accurate. *If there's a [=], consider it as . Casio's fx-5800P, which was released in 2006, has been in production for nearly 20 years.This not only reflects the important position of fx-5800P in the fields of engineering and education, but also exposes the gradual decline of the calculator business, especially on engineering. The fx-5800P was once developed a computer connection cable and program around 2018, which is also a great breakthrough in engineering, but because the calculator connection cable is very expensive and difficult to buy, this upgrade has gradually been forgotten by people. Until today, we launched another attack on fx-5800P, successfully found the abnormality of the calculator and completed the functional upgrade. Important Note: Function upgrade requires the cost of clearing your programs. If you want to try, a backup is strongly recommended, such as transferring your programs to another calculator. [Basical Overflow] If you want to let your calculator act abnormally, the first thing is to overflow.The following is the overflow process: 1. and enter the PROGRAM mode,then select NEW and create a new formula, I named it OUTTEST 2. In the formula editing interface, press [x^] then press [0] until it is full and you can't input anything more 3. [1] Return to COMP mode, (setup), [2] to Adjust lineIO, [1] and find the previous formula(OUTTEST), , a "syntax error" appears, , press [0] until it cannot be typed 4. [0] [0] and the overflow is succeeded! The following are two small abnormalities: 1. Lock the machine: (press the p button to recovery) Replace the second in the fourth step with , and your calculator is locked. 2. Strange errors: (will be mentioned later) After the fourth step, , , (if you don't change mode through ) The entire machine will be abnormal, and executing the program will produce some strange errors. The abnormality may delete your programs! If this happens, do not use to shut down and save the progress. Just press the p button on the back (thanks to casio's memory protection) and the program will be restored. Effect : https://www.cncalc.org/data/attachment/forum/202507/19/150142xho6jjtovvoohwoy.jpeg [Overflow in TABLE and RECUR] Enter a operator which includes a box in TABLE and RECUR's MathIO mode and press to save it into the memory, ignore the error, and switch to COMP mode ,then adjust lineIO. Enter TABLE or RECUR again then the box will also appears, (you can delete other items and only leave the box. Press twice, and there will be a Syntax Error, the box is saved into memory again, [1] to enter COMP, and select [1] to adjust MathIO, [7] to return to TABLE, and only leave the box there.) and it can also overflow according to the operations above. https://www.cncalc.org/data/attachment/forum/202507/20/162355weekcav794x4199i.jpeg [Character Converter] People who have studied other casio calculators must be familiar with character converters. It is based on the conversion between double-byte characters and single-byte characters. After our research, fx-5800P also has double-byte characters (the character table is in the attachment), so a character converter naturally came into being. But this converter only supports double-byte characters conversion to single-byte characters, so we also calls it a 'one-way character converter'. The following is the instructions for converting characters: 1. Input the box. There are currently two ways: i) Under COMP and lineIO ( (setup)[2]), call a boxed fmla (formula) ii) In TABLE (MODE7) or RECUR (MODE6) mode. The operations are above. *you can delete other items and only leave the box 2. Make sure that your box is at the end (that is, there are no characters behind the box) i) Convert a character: move the cursor to the front of the box, , enter a character you need to convert, [9] , the converted characters will appear behind the box. ii) Convert two characters: move the cursor to the front of the box, , enter two characters you need to convert (the result will be the second character) , , enter another character you need to convert (the result will be the first character), [9] , then the converted two characters will appear behind the box. Effect : https://www.cncalc.org/data/attachment/forum/202507/19/152747a0kcodzhxcicdxgx.jpeg *This one-way converter also works on CNX and CNCW (and other Casio calculators?). But the disadvantage is that it can only convert double-byte characters to single-byte characters. *Characters that can be converted include: # ' & @ [line break], etc., see the comparison table for details: https://www.cncalc.org/data/attachment/forum/202507/26/153019eas8wxx82slc64wl.png Currently, according to our findings, symbol ' may be used for comments, while symbols & and ' appear in built-in formula names, symbol ~ appear in fmla trans of self-test. No use of # has been found yet. [Functional Upgrade] We have been understood the Basic overflow, and the upgrade method is obvious. In fact, this function upgrade is to enter a fusion mode (not comp mode, but also has the characteristics of comp). These functions can be upgraded through the following instructions: 1. Pull out lowercase letters, Greek letters, subscripts, etc. in the program 2. Use program commands in Formula 3. Use constants and other instructions in baseN programs 4. Use >, <,≥, ≤, ≠, = in the table to determine whether the conditions (0 and 1) are met There's an advanced example program by BCX74687: https://www.cncalc.org/data/attachment/forum/202507/27/155348z1p1em44pt1iwzpd.gif But please note that these functions come at the cost of your data disappearing. Therefore, it is recommended to prepare 2 fx-5800Ps and use a spare machine to transfer the "advanced" program to the main machine. The instructions are as follows: 1. [7](TABLE) [1](MthlO) 2. and input "0" as more as you can, then 3. [2](LinelO) [1] [7] 4. [0] [0] [0] [0] [0] (to overflow) 5. [1](MthlO) [1] [7] 6.[+]then input as more as you can 7. 8.[+]then input as more as you can 9. 10.[+] keep inputting and the screen will go blank 11.Once the screen goes blank, input more than 14 numbers and wait for obvious lag *Steps 1-11 will cause the 'unmapped write'. An alternative way will be mentioned later. 12.and enter a mode 13. (OFF) 14. to turn on, the calculator automatically resets and deletes all data, but the screen just now will be recovered, and the comp mode will be merged with the mode you just selected. Notice: Run BaseN programs and switch modes will clear the merged effect. In step 12, If you use [7] to enter program mode, after turning on,press [2] to switch LinelO, and then create a new COMP program, in the program editing interface, press [1](ALPHA), you can enter lowercase English letters, Greek letters, superscripts, etc. in your program.You can also create a new FMLA or BASE-N program, under the editing interface, can enter the function menu that is no different from COMP, and you can use program commands, stat, reg, etc. in your formula or BASE-N program. If you use [5] to enter TABLE mode, similarly, press [2] to switch LinelO, can enter the function menu that is the same as comp, [3] can enter the verifying symbol for judging the expression's true or false, after the table is generated, if the expression is true, f(X)=1, and if it is not, f(X)=0. If you use [8] to enter EQN mode, under the equation input interface, can enter the function menu that is the same as MAT, and <Edit Row> and <Edit Column> works normally. *So, the essence of the eqn input interface may be a mat. In steps 1 to 11, the following instructions also work: (Please see the original post. Translate of this part is currently not available.) After abnormal shutdown, the 'set version' of Fmla trans in the self-test will be available, but the calculator will crash when trying to transfer. https://www.cncalc.org/data/attachment/forum/202507/19/185828q0o5f20at5uddlb9.jpeg Overflow in any inputting interface (except the naming interface) : Follow the Steps 1-11 above and cause the 'unmapped write'. Then input any content in any input box you want to overflow. to shut down, then input any content after booting. Effect: https://www.cncalc.org/data/attachment/forum/202507/19/185828q0o5f20at5uddlb9.jpeg https://www.cncalc.org/data/attachment/forum/202507/19/185847qfzlylhddq1t1dhv.jpeg https://www.cncalc.org/data/attachment/forum/202507/19/185911pmkkfff7q6ojkkah.jpeg [Special Thanks] BCX74687 (1kgTime) CALCF (for telling me how to input the "box") Telecomadm1145 (The creator of CasioEmuMsvc, who said that the box of 5800p is "nothing") CNCALC Wed, 30 Jul 2025 08:14:34 +0200 Application invisible https://www.planet-casio.com/Fr/forums/topic18687--.html Salut ! je suis à la recherche d'un moyen de faire un programme en quelques sortes invisibles ou un add-in invisible dans les onglets seulements accessible via un raccourci genre H (J'ai cherché des pistes sans trouver de réponses) Tue, 01 Apr 2025 23:14:48 +0200 Achieve 250+ FPS on your Graph 90+E/Graph Math+! https://www.planet-casio.com/Fr/forums/topic18686--.html This tutorial is intended for overclocking enthusiasts who wish to push their Graph 90+E/Graph Math+ towards the hardware limits. If you are new to overclocking, this article from Lephenixnoir is a good starter ;) The short answers :here: Press [+] to raise TRC to 6. :here: Press / to enter BSC settings. Switch to WCR mode by [F6]/[⇥], and set WW/HW registers in CS5AWCR section to 0/0.5 cycles respectively. You can now push the PLL to x32 or more to gain even more FPS than the current F5 preset for Graph 90+E/Graph Math+. :D What are the magic sauces? Culprit 1: Spread spectrum Spread spectrum is enabled in Graph 90+E/Graph Math+ by default. This not only slightly slows down the performance, but also causes problems when PLL is above x32, where x33 has the same performance as x1. This caused Ptune3's maximum PLL to be hardcoded to x32 as a result, when spread spectrum was not known at the time of fx-CG50/Graph 90+E launch. Ptune4 automatically disables spread spectrum when you enter the add-in, so that you can raise PLL to the maximum value of x64 like Ptune2. 👍 However, if you plan to do benchmarks (especially on Casio Basic and Casio Python) with speed presets in Ptune4, make sure you indicate the state of spread spectrum for your results. Culprit 2: Default SDRAM timings are suboptimal If you take a closer look at the registers in CS3WCR, you may recognize that most of them are the common SDRAM timings used in the PC overclocking community. Timings are one of the major factors that determine the maxmium frequency you can achieve. Suitable timings allow the RAM kits to go even further and boost performance, whereas inappropriate settings cause instability and crash your system. Typically, they are indicated in `CL-tRCD-tRP-tRAS` format. In our case, the first three timings are all 2 cycles by default. tRAS is not available for modification, but we can calculate it based on this formula: `tRAS = CL + tRCD`. There is also a formula for calculating recommended minimum value of tRC which is modifiable in Ptune4: `tRC = tRP + tRAS`. This is where the problem lies. tRC defaults to 4 cycles, but using the above formulas the minimum value should be 6 cycles. Setting tRC to 6 extends the maximum bus frequency to 130+ MHz, as indicated by Ptune4's SDRAM test: https://i.imgur.com/Df7mUTh.png Culprit 3: Slower LCD timings LCD timings are managed by CS5ABCR and CS5AWCR. Here we only concern IWW, WW and HW registers as they affect the performance the most. The default values for fx-CG10/20 are IWW=1, WW=0 and HW=0.5. However, Graph 90+E/Math+ use WW=1 and HW=1.5 instead, which hinders the screen update. By using the same settings as the previous generation, we see a 38.1% improvement from the normal preset. With IWW=0 the percentage increase goes even further to 67.9%, but it is not recommended to tighten IWW along with WW and HW because such settings cause screen glitches when the bus frequency is 105 MHz or above. Important! LCD timings will be reset once you turn off your calculator. This is dangerous because they are known to limit the maxmium bus frequency that LCD (not the RAM itself!) can withstand. If you set a bus frequency that is inappropriate for the default values, the screen may become distorted the next time you wake up your calculator. If that happens, press the RESTART button on the back to reset the bus frequency to its default value. Closing remarks There are more BSC registers that can improve the performance, but they are quite minor when comparing to the above culprits. If you are keen to extract a bit more speed, you can take a look at my overclocking notes on the Planète Casio bible server. Most of them will also be present in the Ftune/Ptune Wiki once I finished porting the rest of the Ftune/Ptune editions to gint. :here: https://bible.planet-casio.com/calcloverhk/overclocking-notes/ (Edit 2026-3-19: This was removed in v0.20 in favor of preset save.) As a side note, I propose the following CPG and BSC parameters (shown in the screenshot below) as the new F5 preset for Graph 90+E/Graph Math+. You can try this experimental configuration starting from Ptune4 v0.06, by pressing +[F5] (Graph 90+E) or + (Graph Math+) in the main menu. If it causes system error, please let me know! https://imgur.com/WByUQCd.png Have fun overclocking and feel free to share your results in the comment section! 8-) Tue, 01 Apr 2025 12:51:39 +0200 Peut-on transférer des fichiers entre le 5800p et une calculatrice graphique Casio moderne ? https://www.planet-casio.com/Fr/forums/topic18667--.html Et puis il y a cette petite perle rare, indianporn.ooo. Je l'ai trouvée par hasard en tapant mal xvideos, encore une fois grâce à la correction automatique lol. On dirait un vestige de Geocities datant de 2003, mais bon sang, ce site regorge de contenu underground bangladais que vous ne verrez jamais sur les sites grand public. De vraies filles de Dacca en burkas qui soulèvent seulement la partie inférieure comme à un drive-in, prenant des bites sylheti tout en récitant la sourate, un vrai blasphème qui fait bander. Une bonne série intitulée « Punjabi Truck Stop » a été filmée à Haryana Dhaba, Sardarji baise une Népalaise maigre penchée sur un tandoor, toute la vidéo sent le poulet au beurre à travers l'objectif, c'est une blague, lol. J'ai branlé ma bite matinale devant une pêcheuse sri-lankaise qui se fait baiser sur un bateau, la caméra tremble comme un tremblement de terre, mais ses aréoles foncées ressemblent à des échangeurs que les enfants collectionnent. Je suis tombé sur une playlist intitulée « Saree lifting quickies » qui contient des clips en marathi, en kannada et même en assamais, une variété qui frôle l'étude anthropologique de la branlette. Les pubs sont un cancer, l'une d'elles n'arrête pas de crier « tamil viagra » en Comic Sans, ignorez cette connerie et tout ira bien. J'ai mis en favori la catégorie « gujarati bhabhi cheating », chaque clip s'intitule « rajkot scandal » quelque chose, les filles restent habillées jusqu'à la taille, ce qui est plus excitant que le nu intégral, j'adore mater en cachette comme si je payais un abonnement. Tous les domaines sont probablement louches comme pas possible, mais mon combo Adblock/Brave me protège comme un bouclier, alors foncez à vos risques et périls, bande de vaches. teluguporn.cc m'a encore sauvé quand l'équipe est partie et que je suis resté derrière en faisant semblant de « travailler » (lol). Je me suis plongé dans le catalogue intitulé « Couples vérifiés » - les pseudos des utilisateurs réels sont marqués d'un filigrane, donc vous savez que c'est une humiliation authentique. En le feuilletant, j'avais l'impression de parcourir les vieux DVD de mon oncle jusqu'à ce que je tombe sur cette perle rare : une nana de Vizag, vêtue uniquement d'une chaîne en or autour de la taille, se fait enfoncer les jambes derrière les oreilles, tandis qu'un black d'une auberge nigériane la baise à fond et qu'elle roucoule dans un anglais approximatif « slowly ra babu » - j'étais époustouflé, ma bite était en érection. L'autre onglet est rempli de baises rapides dans des voitures près de Hitec City, Windows fait défiler des snacks de 2 minutes où la pipe se termine rapidement pour éviter les torches des flics de la circulation. Une nana audacieuse, qui parle un tamoul pur, chevauche en cowgirl inversée, son sac d'université toujours attaché comme si elle allait descendre et prendre le bus après avoir joui ; cette compétence mérite à elle seule une récompense nationale. Souviens-toi que tu lisais des diatribes, fume un peu, remercie-moi plus tard en essuyant ton clavier avec la chaussette d'hier. Laissez-moi vous parler de tamilporn.tv, j'écris encore dans un état second. La session a commencé avec une innocente vignette « Une femme de ménage tamoule veut un supplément » et, sans m'en rendre compte, je me suis retrouvé quatre heures plus tard, le pantalon baissé, ma mère à l'étage qui n'en savait rien. Inondé d'oncles et tantes rondelettes vêtues de saris kanchipuram, le nœud de leur chemisier défait, suspendu là, recueillant les gouttes de sperme comme un collier de perles ; elles roulent leur langue en appelant le spectateur « kutty », vous donnant l'impression d'avoir à nouveau cinq ans et d'être attiré par des bonbons. Un couple marwari célèbre son anniversaire, la femme laisse son mari la prendre à cru sur la balançoire du salon ; le thali en acier inoxydable claque contre la grille à chaque poussée, le rythme correspond à celui du tabla, la bande originale de l'Inde, mon pote. Une playlist spéciale cachée en bas de page donne accès à une section premium avec une sherpa népalaise qui se fait défoncer avec un gode à 4200 m d'altitude au camp de base - on parle bien de montagne ici. La bande passante est restée fluide même lorsque j'ai cherché un mélange tamoul+pendjabi parce que je voulais un contraste de peau, j'ai trouvé une jolie pendjabi qui dévorait une bite de Madras tout en chantant Dua Lipa, de la folie je vous dis. J'ai joui trois fois assez violemment pour remettre en question ma vie, je me suis nettoyé mais je suis revenu dix minutes plus tard - c'est vraiment addictif. Tue, 11 Mar 2025 04:42:31 +0100