Les membres ayant 30 points peuvent parler sur les canaux annonces, projets et hs du chat.
La shoutbox n'est pas chargée par défaut pour des raisons de performances. Cliquez pour charger.

Forum Casio - Projets de programmation


Index du Forum » Projets de programmation » micropython dans KhiCAS
Parisse Hors ligne Membre Points: 522 Défis: 0 Message

micropython dans KhiCAS

Posté le 01/02/2022 14:22

J'ai avance sur le portage de MicroPython dans KhiCAS pour graph 90, ca compile, mais j'ai un crash lors de l'initialisation de MicroPython, lie a l'allocation memoire. Ca plante au 1er appel d'allocation dans la fonction mp_init (py/runtime.c) sur une demande d'allocation de 24 bits, lorsque le gc est actif, le pointeur renvoye est nul. Si je desactive le garbage collector, ca ne marche pas non plus, ca plante un peu plus tard a la 5eme allocation memoire (avec kmalloc de gint, mais c'est pas kmalloc la cause, car si on desactive les arena en ram en ne conservant que le malloc systeme ca plante tout autant).
C'est probablement lie a une mauvaise configuration. Mon mpconfigport.h est https://www-fourier.univ-grenoble-alpes.fr/~parisse/tmp/mpconfigport.h, j'ai beau le comparer avec celui de CasioPython je ne vois rien qui explique le crash (https://github.com/Zezombye/casiopy/blob/master/ports/minimal/mpconfigport.h). La compilation se fait avec des commandes du type
sh3eb-elf-gcc -DNUMWORKS -DMICROPY_LIB -I. -I.. -I../py  -Ibuild -Wall  -ansi -std=gnu99 -DFFCONF_H=\"lib/oofatfs/ffconf.h\" -Os -g   -DMODULE_ULAB_ENABLED=1 -mb -m4a-nofpu  -mhitachi  -fdata-sections -ffunction-sections -fno-strict-aliasing -fno-exceptions  -c -MD -o build/./main.o main.c
(le flag NUMWORKS sert a contourner un bug de sprintf dans un module additionnel, ca n'a rien a voir avec le code de micropython)
Le source complet (giac+mp) est la
https://www-fourier.univ-grenoble-alpes.fr/~parisse/tmp/giac2.tgz (se compile avec ./mklib dans micropython-1.12/fxcg et make dans giac2)
L'addin en 2 parties https://www-fourier.univ-grenoble-alpes.fr/~parisse/tmp/khicas.g3a et https://www-fourier.univ-grenoble-alpes.fr/~parisse/tmp/khicas.ac2
Pour lancer MicroPython depuis le shell de KhiCAS, faire shift-PRGM puis 8 pour mettre python( en ligne de commande et valider. Le crash apparait avec le print du malloc fautif que j'ai rajoute dans py/malloc.c, on devine aussi le msg d'erreur de micropython (FATAL uncaught NLR ...) genere suite au pointeur nul (non catche par un nlr_push).


1, 2, 3 Suivante
Parisse Hors ligne Membre Points: 522 Défis: 0 Message

Citer : Posté le 01/02/2022 15:01 | #


Il y a un truc vraiment bizarre, si j'ajoute dans py/gc.c a la fin de la fonciton gc_alloc un printf("gc6\n"); juste avant de renvoyer le pointeur (return ret_ptr;) alors ca ne crashe plus a l'initialisation, et ensuite l'interpreteur a l'air de fonctionner. Je suis completement ahuri. Je ne vais quand meme pas laisser ce genre de code, ca ralentirait trop!

Ajouté le 01/02/2022 à 17:43 :
Pour le moment je contourne en executant un affichage uniquement au 1er appel de gc_alloc. Mais ca ne fait que cacher la poussiere sous le tapis, il doit y avoir une raison plus profonde a comprendre...

Ajouté le 01/02/2022 à 19:42 :
Le mystere s'epaissit. Certaines commandes de Xcas crashent uniquement quand l'addin est compile avec la librairie micropython. Le code source est identique, sans mp ca marche, avec mp ca crashe. Et pour ajouter au mystere, ces fonctions de Xcas affichent des messages que j'ai ajoutes dans gc_alloc, donc dans le code du garbage collector de MicroPython, alors qu'elles n'y font jamais appel (et que MicroPython n'a pas ete active).
Du coup, j'ai bien peur de ne pas pouvoir integrer MicroPython...
Lephenixnoir Hors ligne Administrateur Points: 24700 Défis: 170 Message

Citer : Posté le 01/02/2022 20:16 | #


Je réponds rapidement, je n'ai pas encore pris le temps de tester... x_x

Le fait qu'ajouter/enlever le printf déplace le problème suggère que c'est typiquement un problème de bas niveau : buffer overflow, dépassement de pile, variables qui se chevauchent, data races et autres perturbations asynchrones sont les premiers trucs auxquels je pense dans ces situations (j'ai déjà eu droit à tous).

Le fait que compiler ou pas MicroPython dans l'add-in change le comportement d'autres fonctions, et surtout le fait que les messages se mélangent, ne font que le confirmer. Ma suggestion serait de délimiter l'effet que linker avec MicroPython a, du genre :

  • Est-ce qu'il y a des constructeurs (des fonctions d'initialisation de la lib) qui sont lancés dès que tu linkes avec même si tu ne les appelles pas explicitement ?
  • Est-ce que tout ce qui est linké tient bien dans la RAM et ne limite pas trop la pile (à la fin de la section RAM de ton linker script) ?
  • Quels fichiers de MicroPython exactement sont linkés dans ton add-in (tu peux le voir dans la map du linker), et est-ce qu'il y en a certains en particulier qui doivent être présents pour que les problèmes se produisent ?
  • À quelles adresses sont allouées les deux chaînes qui se font inverser ? (ie, est-ce que l'échange commence dès le link ou à l'exécution seulement ?)

Un seul truc louche dans la config :

#define MICROPY_EMIT_ARM            (1)

MicroPython n'a aucun compilateur natif compatible avec la calculatrice, cette macro ne devrait pas être définie.
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Parisse Hors ligne Membre Points: 522 Défis: 0 Message

Citer : Posté le 01/02/2022 20:42 | #


Je crois que j'ai une piste, qui serait que la commande de l'OS de lecture d'un fichier ne marche pas correctement si la taille depasse 2Mo, est-ce que ca te semble possible? Il semble que les commandes Xcas qui ne marchent plus quand MicroPython est inclus sont situees a la fin de la partie de l'addin charge en RAM, au-dela de l'adresse 0x8c400000.
Lephenixnoir Hors ligne Administrateur Points: 24700 Défis: 170 Message

Citer : Posté le 01/02/2022 20:48 | #


Ça paraît un peu fourbe mais ce serait pas le pire qu'on ait vu. Peut-être aussi que la limite joue ailleurs durant le processus de chargement.
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Parisse Hors ligne Membre Points: 522 Défis: 0 Message

Citer : Posté le 01/02/2022 21:22 | #


Fausse piste. J'ai remis la taille des 2 morceaux de l'addin sous les 2M, mais ca plante toujours.
Il n'y a pas de fonction d'initialisation de la lib micropython implicite, il faut les appeler explicitement. Le seul appel que je fais au demarrage de Xcas c'est pour initialiser une variable MicroPython qui marque la pile au demarrage.
Je vais regarder pour la RAM vs la stack mais j'ai reserve pas mal de place pour la stack ( ram (rwx) : o = 0x08101000, l = 432k /* skip 4k at start */)
Tous les fichiers de mp sont linkes dans l'addin. Je n'ai pas d'adresse du message affiche (c'est un printf("gc3\n")), mais en fait c'est l'affichage du message qui est aussi typique d'un appel a printf depuis gc_alloc (depuis Xcas les messages sont affiches en fonte plus grande, dans la console). Et je ne vois pas comment gc_alloc peut etre appele par Xcas, c'est comme si les appels a malloc dans Xcas etaient remplaces par gc_alloc mais je ne vois pas comment c'est possible (dans le source de MP il y a bien des undef malloc, mais ces sources ne sont pas lu depuis Xcas). En plus c'est un message de memoire full qui s'affiche meme si j'initialise le tas MP.

Ajouté le 02/02/2022 à 06:51 :
J'ai une nouvelle piste: c'est l'initialisation des data. Si ca se fait sur un fichier objet charge en ram ca va logiquement bugguer, car c'est fait avant que main soit appele et donc avant que main ait charge la partie ram de l'addin.
Donc il faudrait charger toutes les parties data en rom dans prizm.ld, ca te parait juste?
Lephenixnoir Hors ligne Administrateur Points: 24700 Défis: 170 Message

Citer : Posté le 02/02/2022 08:51 | #


Si le linker script est bien fait normalement tu n'as pas ce problème. La raison pour laquelle la section .data est initialisée à part dans l'add-in principal c'est que l'accès que l'OS te fournit au fichier d'add-in (par 00300000) est en lecture seule, donc tu peux pas l'utiliser pour les données. D'où le fait que dans le linker script on a >ram AT>rom, ce qui signifie qu'on stocke initialement la section dans la ROM (ie. dans le g3a) mais qu'ensuite on promet de la charger dans la RAM là où elle pourra être modifiée.

Comme toute la RAM dans la section r8c2 peut être modifiée, tu n'a pas ce problème ; tu peux charger les données où tu veux, y compris avec le code. Actuellement tu les mets avec le reste de la section .data, ce qui est correct aussi. Les variables globales des fichiers z*.o sont donc initialisées dès le démarrage de l'add-in, avant le chargement du second fichier.

Tu peux vérifier tout ça en affichant depuis le code de l'add-in principal une variable globale stockée dans un fichier z*.o initialisée à une valeur non triviale.

(J'ai essayé de le compiler mais j'y arrive pas encore, j'ai pas le bon système de compilation sous la main - ma libc se mélange avec celle de libfxcg.)
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Parisse Hors ligne Membre Points: 522 Défis: 0 Message

Citer : Posté le 02/02/2022 13:45 | #


En enlevant le printf de gc.c j'obtiens un message
FATAL uncaught NLR 0x08167d74
L'adresse se situe dans une zone allouee pour mp_state_ctx puisque sh3eb-elf-objdump -C -t khicas.elf | sort > dump_t me renvoie

...
08167d50 g     O .bss    00000004 mp_verbose_flag
08167d54 g     O .bss    00000001 cout
08167d58 g     O .bss    00000004 signgam
08167d5c g     O .bss    00000238 mp_state_ctx
08167f94 g     O .bss    00000004 mp_showbc_code_start
08167f98 g     O .bss    00000004 mp_showbc_const_table
08167f9c g     O .bss    00000001 ctrl_c
08167f9d g     O .bss    00000001 interrupted
08167fa0 g     O .bss    00000004 errno
08167fa4 g     O .bss    00000004 _strtoks_
...


Ajouté le 02/02/2022 à 14:18 :
Je suis sur une autre piste qui me semble plus prometteuse: un conflit entre libtommath et libmicropy, toutes les 2 ont une fonction mp_init.
Si je linke -lmicropy avant -ltommath il n'y a pas d'avertissement. Si je linke tommath avant micropy j'ai une erreur.

./libmicropy.a(runtime.o): In function `mp_init':
/home/parisse/casio/micropython-1.12/fxcg/../py/runtime.c:59: multiple definition of `_mp_init'
/home/parisse/casiolocal/lib/libtommath.a(bn_mp_init.o):bn_mp_init.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status


Ajouté le 02/02/2022 à 14:20 :
Voila qui expliquerait pourquoi micropython est appele mysterieusement.
Reste a trouver un moyen d'eviter le conflit, sachant que mp_ est le prefixe utilise a la fois par micropython et tommath (mp=multi precision)...

Ajouté le 02/02/2022 à 14:41 :
Je viens de renommer mp_init de micropython en mp_init_py et ca a l'air de fonctionner!
Lephenixnoir Hors ligne Administrateur Points: 24700 Défis: 170 Message

Citer : Posté le 02/02/2022 14:56 | #


Ok, bien vu cette histoire. C'était donc bien des mélanges au link, d'un genre que j'admets ne pas avoir vu du tout venir.

Maintenant la question c'est comment l'éviter dans le futur. Je ne pense pas que tu puisses renommer automatiquement les fonctions. Par contre ce que tu peux faire c'est extraire pour chaque archive la liste des symboles, et avoir un script qui les énumère et t'avertit en cas de doublon :

nm TON_ARCHIVE.a -f posix | sed -E '/:$/ d; /[^ ]+ [a-zU]/ d; s/^([^ ]+).*$/\1/'

Le sed commence par se débarasser des lignes indiquant les noms des fichiers de l'archive (/:$/ d), ensuite il élimine tous les symboles locaux (lettre minuscule) et les références qui ne sont pas des déclarations (lettre U) (/[^ ]+ [a-zU]/ d), et enfin il ne garde que le nom du symbole au début de la ligne (s/^([^ ]+).*$/\1/).

Tu peux faire ça pour tes deux archives et comparer l'intersection. Ça devrait aider à repérer ce problème.
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Parisse Hors ligne Membre Points: 522 Défis: 0 Message

Citer : Posté le 02/02/2022 15:53 | #


Je viens de le faire, je n'ai pas vu d'autre conflit.
Il reste encore des bugs, mais ce sont des bugs classiques, ouf!
Lephenixnoir Hors ligne Administrateur Points: 24700 Défis: 170 Message

Citer : Posté le 02/02/2022 16:04 | #


Excellent ! Bon courage pour la suite de l'intégration
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Parisse Hors ligne Membre Points: 522 Défis: 0 Message

Citer : Posté le 03/02/2022 07:50 | #


J'ai plutot une bonne nouvelle pour une fois, c'est que OFF/ON a l'air de preserver les 3Mo de RAM. Du coup je vais serieusement envisager d'en reserver une partie pour kmalloc ou/et le heap Python. Ca pourrait d'ailleurs aussi servir pour la version light d'addin en 1 seul morceau, mais il faudrait pouvoir verifier qu'on est sur une fxcg50 ou graph 90. Tu as du code pour ca?
Sinon, j'ai fait un essai de compilation avec -flto, mais ca a l'air incompatible avec le decoupage en 2 addins, tu as une idee de la raison ? Ou bien je m'y suis mal pris...
Lephenixnoir Hors ligne Administrateur Points: 24700 Défis: 170 Message

Citer : Posté le 03/02/2022 08:55 | #


...il faudrait pouvoir verifier qu'on est sur une fxcg50 ou graph 90. Tu as du code pour ca?

Mais bien sûr x3

uint32_t stack;
__asm__("mov r15, %0" : "=r"(stack));
if (stack < 0x8c000000) {
    /* Prizm ou émulateur Graph 90+E */
}
else {
    /* Graph 90+E */
}

En profitant de l'adresse de la RAM qui est différente.

Sinon, j'ai fait un essai de compilation avec -flto, mais ca a l'air incompatible avec le decoupage en 2 addins, tu as une idee de la raison ? Ou bien je m'y suis mal pris...

Les fois où j'ai essayé -flto je me suis retrouvé avec des add-ins qui ne marchent pas, il ne linkait pas du tout assez de choses. J'admets ne pas être très à l'aise avec la fonctionnalité pour l'instant.
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Parisse Hors ligne Membre Points: 522 Défis: 0 Message

Citer : Posté le 03/02/2022 10:22 | #


Du coup je fais

#ifdef GINT_MALLOC
  /* À appeler une seule fois au début de l'exécution */
  kmalloc_init();
  
  /* Ajouter une arène sur la RAM inutilisée */
  static_ram.name = "_uram";
  static_ram.is_default = 1; // 0 for system malloc first, 1 for ram
#ifdef VAR_HEAP
  static_ram.start = malloc_heap;
  static_ram.end = malloc_heap+sizeof(malloc_heap);
#else
  //void *malloc_start = &sextra;
  //void *malloc_end = &eextra;
  //int malloc_size = malloc_end - malloc_start;
  static_ram.start = &sextra;
  static_ram.end = &eextra;
#endif
  kmalloc_init_arena(&static_ram, true);
  kmalloc_add_arena(&static_ram);
  uint32_t stack;
  __asm__("mov r15, %0" : "=r"(stack));
  if (stack < 0x8c000000) {
    /* Prizm ou émulateur Graph 90+E */
  }
  else {
    /* Graph 90+E */
    ram3M,name="_3M";
    ram3M.is_default=1;
    ram3M.start=0x8c200000;
    ram3M.end=ram3M.start+3*1024*1024;
    kmalloc_init_arena(&ram3M, true);
    kmalloc_add_arena(&ram3M);
  }
#endif // GINT_MALLOC

pour l'addin seul, pour celui en 2 parties il faudra que je reserve moins de place et plus loin. Sauf si on peut aller au-dela des 3M!
Lephenixnoir Hors ligne Administrateur Points: 24700 Défis: 170 Message

Citer : Posté le 03/02/2022 10:41 | #


Yup, ça a l'air tout bon. Bien sûr static_ram et ram3M doivent être globales. Comme tu l'as vu du coup tu ne peux plus utiliser cette mémoire pour charger ta section r8c2 avec du code supplémentaire.

Il y a 6 Mo pour être exact mais il y a des données à la fin, et même si pour l'instant on n'a pas trouvé de cas où elles sont utilisées et où on ne peut pas les modifier, je range ça dans la catégorie "instable". Tant qu'à faire si tu peux garder la détection des zéros plutôt que la constante 3 Mo je suggère de le faire, on n'est pas à l'abri d'une mauvaise surprise avec une future version de l'OS

Si tu tiens à tenter le coup, je conseille de protéger ce cas d'usage par une option off par défaut et/ou une détection de la version d'OS.
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Parisse Hors ligne Membre Points: 522 Défis: 0 Message

Citer : Posté le 03/02/2022 11:26 | #


D'ailleurs, as-tu une idee de comment le reste de la RAM est utilisee? Les addins sont-ils aussi recopies dans une section de la RAM ou bien la flash est-elle executable?
Lephenixnoir Hors ligne Administrateur Points: 24700 Défis: 170 Message

Citer : Posté le 03/02/2022 12:03 | #


Le "reste" de la RAM c'est vague mais si tu veux dire l'autre moitié des 6 Mo alors on ne sait pas encore. Pour les 2 premiers Mo à 0x8c000000, y'a toutes les données du noyau et de l'OS, variables globales, la mémoire principale, et tout ce qui est utile à l'add-in : bloc utilisateur, pile, tas principalement.

Les add-ins ne sont pas copiés en RAM, il y a un mapping exécutable vers la ROM à travers le MMU. De façon générale la ROM est exécutable aussi (il faut bien que l'OS tourne !) mais la mémoire de stockage en pratique non puisque les fichiers qu'on met dedans sont fragmentés.
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Parisse Hors ligne Membre Points: 522 Défis: 0 Message

Citer : Posté le 03/02/2022 12:45 | #


Du coup l'OS doit detecter les fichiers d'extension g3a pour ne pas les fragmenter?
Lephenixnoir Hors ligne Administrateur Points: 24700 Défis: 170 Message

Citer : Posté le 03/02/2022 13:08 | #


Non, les fichiers g3a sont bel et bien fragmentés. Mais ensuite il y a un mapping de P0 via le MMU. Si tu n'es pas familier avec la mémoire virtuelle, en gros la zone 00300000 ne contient pas vraiment de mémoire ; elle est découpée en pages de 4 kio qui pointent chacune vers une adresse en ROM. Le MMU est le périphérique via lequel l'OS déclare quelles pages existent et où elles mènent dans la ROM. Chaque fois que tu fais un accès mémoire dans cette région, une translation d'adresse se produit dans le MMU pour déterminer la cible et ainsi accéder à la ROM.

La clé de ce système (pour nous ; il a bien d'autres propriétés désirables par ailleurs) c'est que les pages sont contrôlées individuellement, donc l'OS peut faire pointer la première page vers le premier fragment du fichier g3a, la seconde page vers le second, et ainsi de suite, même si le fichier est complètement en morceaux dans la ROM.

Ça veut aussi dire que seul le CPU peut lire le code et certaines données ; les périphériques comme le DMA qui n'ont pas accès au MMU en sont incapables.
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Parisse Hors ligne Membre Points: 522 Défis: 0 Message

Citer : Posté le 03/02/2022 18:38 | #


Du coup, est-ce que ce serait possible de programmer nous-memes le MMU (dans un addin classique supporte par Casio) pour lancer des addins de taille plus grande que 2M mais cette fois sans utiliser la RAM? Ou bien il y a une taille limite? C'est quand meme dommage d'utiliser de la RAM si on peut l'economiser.
L'autre mystere pour moi, c'est pourquoi Casio n'utilise pas lui-meme les capacites RAM de sa machine (et communique dessus). Est-ce juste parce que les 6Mo additionnels ne sont pas disponibles partout ?
D'aileurs les 6Mo sont-ils disponibles sur les fxcg50 ? J'ai beaucoup plus de telechargements de KhiCAS en anglais qu'en francais (308 contre 90 pour ce mois de janvier), ce serait dommage de les priver des ameliorations en cours.
Lephenixnoir Hors ligne Administrateur Points: 24700 Défis: 170 Message

Citer : Posté le 03/02/2022 18:53 | #


Ce n'est pas possible dans un add-in classique ; pour faire ce genre de manœuvres il faut contrôler les interruptions. Et de toute façon même quand on le peut on évite parce qu'en cas de plantage avec un MMU modifié on peut bricker une calto (AHelper a eu ce problème avec une Prizm à l'époque où il voulait implémenter des libs dynamiques).

La Graph 90+E est juste un successeur à la Prizm, qui n'avait que 2 Mo de RAM. Elle a beau avoir une puce RAM de 8 Mo, tout ce qu'on peut en déduire c'est que le R&D se sent à l'étroit dans 2 Mo et apprécie de pré-équiper le nouveau modèle avec une puce plus grosse pour qu'il soit compatible avec les futures mises à jour dont ils anticipent qu'elles utiliseront cet espace supplémentaire.

La même chose s'est produite avec la série monochrome ; elles ont eu des puces de 512 ko de RAM tout en n'en utilisant que 256 ko pendant 10 ans, et puis la Graph 35+E II est arrivée avec MicroPython et soudain la mémoire en plus est devenue nécessaire.

D'où le fait que j'insiste autant sur la stabilité de la manœuvre : cette RAM est probablement gratuite pour l'instante mais sera certainement utilisée plus tard, et on ne veut pas que quelqu'un dans 5 ans avec un OS plus lourd bricke quelque chose en utilisant un binaire qui traîne sur le forum.

Oui la fx-CG 50 a tout autant de mémoire, pour autant que je le sache matériellement elle est identique à la Graph 90+E.
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
1, 2, 3 Suivante

LienAjouter une imageAjouter une vidéoAjouter un lien vers un profilAjouter du codeCiterAjouter un spoiler(texte affichable/masquable par un clic)Ajouter une barre de progressionItaliqueGrasSoulignéAfficher du texte barréCentréJustifiéPlus petitPlus grandPlus de smileys !
Cliquez pour épingler Cliquez pour détacher Cliquez pour fermer
Alignement de l'image: Redimensionnement de l'image (en pixel):
Afficher la liste des membres
:bow: :cool: :good: :love: ^^
:omg: :fusil: :aie: :argh: :mdr:
:boulet2: :thx: :champ: :whistle: :bounce:
valider
 :)  ;)  :D  :p
 :lol:  8)  :(  :@
 0_0  :oops:  :grr:  :E
 :O  :sry:  :mmm:  :waza:
 :'(  :here:  ^^  >:)

Σ π θ ± α β γ δ Δ σ λ
Veuillez donner la réponse en chiffre
Vous devez activer le Javascript dans votre navigateur pour pouvoir valider ce formulaire.

Si vous n'avez pas volontairement désactivé cette fonctionnalité de votre navigateur, il s'agit probablement d'un bug : contactez l'équipe de Planète Casio.

Planète Casio v4.3 © créé par Neuronix et Muelsaco 2004 - 2024 | Il y a 43 connectés | Nous contacter | Qui sommes-nous ? | Licences et remerciements

Planète Casio est un site communautaire non affilié à Casio. Toute reproduction de Planète Casio, même partielle, est interdite.
Les programmes et autres publications présentes sur Planète Casio restent la propriété de leurs auteurs et peuvent être soumis à des licences ou copyrights.
CASIO est une marque déposée par CASIO Computer Co., Ltd