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 » dxn -- machine virtuelle de mort
Kikoodx Hors ligne Ancien labélisateur Points: 3039 Défis: 11 Message

dxn -- machine virtuelle de mort

Posté le 02/01/2022 14:53

Salut ! J'ai commencé il y a quelques jours à travailler sur une machine virtuelle et son langage ASM associé destinés au gamedev expérimental. J'apprécierais énormèment des critiques sur le format du binaire, l'addressage et la syntaxe assembleur. Je suis relativement nouveau dans ce monde, et tout peut m'aider.

Je l'ai nommé dxn. dxn combine des caractéristiques de langages que j'aime dans l'idée, mais ne fonctionnent tous pas exactement comme je le souhaiterais. Les inspirations principales de dxn sont : la programmation d'un Gameboy DMG, uxn, CHIP-8 et TIS-100.

Le fichier concept.s de mon dépôt de développement contient un exemple de programme idéal. sample.s et sample.dxn représentent l'avancement actuel du projet.
dxna est l'assembleur, dxnd le désassembleur.
http://kiko.ovh/cgit/dxn/

Je veux terminer une toolchain complète pour systèmes POSIX, avec l'objectif final de porter un interpréteur dxn sur calculatrice.

Maintenant que dxna et dxnd fonctionnent, je vais pouvoir attaquer le premier interpréteur de prototypage.

Merci d'avoir lu jusqu'ici, tiens un cookie pour ton temps


1, 2 Suivante
Inikiwi Hors ligne Membre Points: 594 Défis: 8 Message

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



il a volé mon idée...
Lephenixnoir En ligne Administrateur Points: 24572 Défis: 170 Message

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


Je connais pas tout dans cette histoire, mais quelques intuitions du point de vue bas-niveau...

Sémantique de l'assembleur (ie. les concepts du langage et ce que ça calcule)

Assez clean à mon humble avis. Ça se rapproche pas mal d'un véritable assembleur. Il y a quelque points rugueux, peut-être juste parce que je suis pédant. Le plus gênant peut-être c'est que les labels montrent un certain mélange lvalue/rvalue en plus de pointeurs/valeurs. Cette ligne dit que les labels sont des pointeurs vers les données qui les suivent :

    mov i, player_sprite       ;labels are const pointers

Mais cette ligne dit qu'ils sont la valeur qui suit, et qu'il y a un opérateur d'adressage :

    mov g, &player_x           ;pointer

Et ces lignes indiquent même que c'est plus que la valeur qui suit, c'est même une lvalue :

    mov player_y, 0
; ...
    inc player_x

Généralement en assembleur le label est bien une adresse, et il y a un opérateur (par exemple noté @) pour déréférencer. Conceptuellement, ce système te donne :

mov i, player_sprite
mov g, player_x
mov @player_y, 0
inc @player_x

Tu peux aussi faire pareil avec un opérateur d'adressage & bien sûr. Mais c'est important que tes deux étages soient distingués explicitement (par la syntaxe, pas juste implicitement par l'opcode de l'instruction).

Après il y a aussi des détails. Si c'est noir et blanc pourquoi y a-t-il une instruction pal décrite comme "colors" ?

Le mélange RPN/opérandes est un peu bizarre, on ne sait pas trop si tu veux utiliser une pile ou si finalement tu ne veux pas. Tu as un peu le pire des deux mondes actuellement, ce que popz reflète à mon humble avis (tu es coincé entre stocker sur la pile et stocker dans tes variables).

Ta comparaison a l'air de se faire avec un bit de statut modifié par sub. Mais la comparaison signée et la non-signée sont différentes, et clairement tu n'en calcules qu'une ici. Les nombres signés étant relativement indispensables, ça veut dire que tout est du 16 bits signé... right? (tes pointeurs vont souffrir alors je préfère demander)

Tu n'as pas d'accès indexés à la pile, ce qui veut dire que tu n'as pas vraiment de variables locales. Je prédis que tu vas douiller sévère si un jour tu veux écrire une fonction récursive. :P

Syntaxe de l'assembleur

Tu ne différencies pas syntaxiquement les variables des noms de labels, ça pourrait être chiant si tu renommes et qu'il y a un conflit.

Le fait que 2F3F soit une valeur et $2F3F soit la valeur pointée, et que tu définis a comme $8000 avec bind me suggère que la seule façon de définir l'histoire des labels ci-dessus qui ne fera pas de noeuds au cerveau au programmeur c'est d'avoir les labels comme des lvalues et un opérateur d'adressage. C'est parce que sinon tu ne sais pas si trollolol est une adresse (dans le cas où c'est un label) ou si c'est une valeur dans la mémoire (dans le cas où c'est une variable définie avec bind). En particulier si tu changes une définition de label à macro et/ou inversement faut modifier tous les usages. Définir les labels comme des lvalues fait que c'est toujours une valeur.

Pourquoi est-ce qu'on ferait #bind $8000 a en se tapant ensuite la maintenance des adresses (notamment quand on supprime des variables) alors qu'on peut faire a: db 0 qui est plus court et laisse l'assembleur choisir l'adresse ? C'est plus une question de sémantique mais je vois pas pourquoi tu mets le code en lecture seule en forçant cette définition assez casse-pieds (en termes de maintenance) pour les adresses de variables.

Format du binaire

Les flags de l'instruction sont supposés indiquer les registres, mais tu n'as pas de registres ?

Si les instructions sont bien définies il n'y a en général pas besoin de flags pour identifier les lvalues/rvalues, surtout qu'il y a très peu de variations. Par exemple :

inc x
x: adresse de la case à incrémenter

movi x, y
x: adresse de la cible
y: valeur à stocker

movm x, y
x: adresse de la cible
y: adresse de la source

Bien sûr ces instructions hypothétiques movi et movm seraient toutes les deux notées mov puisque la syntaxe permet de les distinguer.

Vu ton format 16-bits où chaque unité d'adressage fait 16 bits tu n'as rien à y gagner en espace, mais c'est un peu louche de penser que tu as des flags dont plein de combinaisons sont invalides (genre mov constante constante ?).
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Kikoodx Hors ligne Ancien labélisateur Points: 3039 Défis: 11 Message

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


Merci beaucoup pour le retour Lephé, ça m'aide énormément.

T'as parfaitement raison pour les pointeurs, j'étais un peu confus et je ne savais pas trop que faire pour résoudre le problème. La syntaxe avec @ me plaît bien.

Après il y a aussi des détails. Si c'est noir et blanc pourquoi y a-t-il une instruction pal décrite comme "colors" ?

J'ai pas été très clair là dessus, le système est 1-bit mais pas noir et blanc. pal change la couleur RGB des états on et off de l'écran. Par exemple pal 0, 1, 0, 0 indique à la machine d'afficher tous les bits éteints en rouge.

Ta comparaison a l'air de se faire avec un bit de statut modifié par sub. Mais la comparaison signée et la non-signée sont différentes, et clairement tu n'en calcules qu'une ici. Les nombres signés étant relativement indispensables, ça veut dire que tout est du 16 bits signé... right? (tes pointeurs vont souffrir alors je préfère demander)

Dans l'idée, tout est non-signé. Je ne pense pas que les nombres signés soient indispensables, mais je fais peut-être une erreur ici.

Le mélange RPN/opérandes est un peu bizarre, on ne sait pas trop si tu veux utiliser une pile ou si finalement tu ne veux pas. Tu as un peu le pire des deux mondes actuellement, ce que popz reflète à mon humble avis (tu es coincé entre stocker sur la pile et stocker dans tes variables).

Le problème est que j'adore la RPN dans l'idée, mais ça rend le code imbitable. Je pense virer les interactions avec la pile, passer full opérandes, en laissant uniquement push et pop.

Tu n'as pas d'accès indexés à la pile, ce qui veut dire que tu n'as pas vraiment de variables locales. Je prédis que tu vas douiller sévère si un jour tu veux écrire une fonction récursive. :P

Comme dit plus haut je vais mettre la pile en retrait, les variables locales et la récursion sont intéressantes mais je ne pense pas que ce soit cohérent avec mon objectif de simplicité relative.

Pourquoi est-ce qu'on ferait #bind $8000 a en se tapant ensuite la maintenant des adresses (notamment quand on supprime des variables) alors qu'on peut faire a: db 0 qui est plus court et laisse l''assembleur choisir l'adresse ? C'est plus une question de sémantique mais je vois pas pourquoi tu mets le code en lecture seule en forçant cette définition assez casse-pieds (en termes de maintenance) pour les adresses de variables.

C'est très juste, je n'y avais pas pensé.

Si les instructions sont bien définies il n'y a en général pas besoin de flags pour identifier les lvalues/rvalues, surtout qu'il y a très peu de variations.

Je ne savais pas comment gérer ça avec mon ancienne syntaxe, mais avec ce que t'as montré plus haut ça me semble plus simple comme cela effectivement.

Je vais réécrire concept.s en prenant en compte tes remarques, merci encore !
ouais ouais
Kikoodx Hors ligne Ancien labélisateur Points: 3039 Défis: 11 Message

Citer : Posté le 02/01/2022 16:54 | # | Fichier joint


J'ai fini de réécrire concept.s, je trouve en effet que les changements que tu as suggéré rendent le système plus cohérent. Surtout le truc des labels pour les données, c'est propre.

http://kiko.ovh/cgit/dxn/tree/concept.s?id=8f4f

Je pense que je vais rester sur le système de flags pour les opcodes, mais considérer les pointeurs comme des données. Les bits indiqueront donc juste quels champs déférencer. Une implémentation pourra très bien utiliser ta méthode des différents instructions, ça ne change rien en pratique. Je veux juste éviter la duplication de code.
ouais ouais
Potter360 Hors ligne Rédacteur Points: 1254 Défis: 2 Message

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


Est ce que ca t'interesse qu'on parle de ton projet à la RDP ?
Globalement, coder. Mal, mais coder.
Kikoodx Hors ligne Ancien labélisateur Points: 3039 Défis: 11 Message

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


Oh oui je n'y avais pas pensé, merci d'avoir demandé ! Une petite mention serait appréciée
ouais ouais
Potter360 Hors ligne Rédacteur Points: 1254 Défis: 2 Message

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


Ok !
------ @RDP ------
Globalement, coder. Mal, mais coder.
Kikoodx Hors ligne Ancien labélisateur Points: 3039 Défis: 11 Message

Citer : Posté le 02/01/2022 23:45 | #


Après m'être arraché les cheveux sur un bug inexistant (shit happens), j'ai terminé la première version de la machine virtuelle. Pas de surprise, c'était le plus simple à programmer avec le désassembleur. La partie un peu tricky va se trouver sur le rendu, mais ça reste dans mon domaine de compétence. Il va falloir que je me concentre sur l'assembleur, c'est le morceau du projet le plus complexe pour moi. Je reste Confiant™

Edit à trois heures du matin
La base SDL pour dxnr est là, avec di (display init) dpx (draw pixel) et show (show...). Après ma nuit de sommeil j'ajouterai le support de drw ainsi que de jmp et variantes, avant de me remettre à travailler sur l'assembleur.
ouais ouais
Kikoodx Hors ligne Ancien labélisateur Points: 3039 Défis: 11 Message

Citer : Posté le 03/01/2022 17:24 | # | Fichier joint


Tout fonctionne, je suis content/20.

dxna compile maintenant en deux passes, la première s'occupant de localiser les labels. L'assembleur devient proche de complet, il reste les commentaires en très important puis des détails.

dxnd a été amélioré pour que la sortie soit plus utile, il est notamment indiqué l'addresse de chaque instruction au début de la ligne. Ça m'a été très utile pour le debugging.

dxnr fonctionne très bien. Il n'y a pas encore d'upscaling du rendu donc c'est un peu anxiogène à regarder, mais ça s'implèmente très facilement.

dxn* dans sa globalité supporte maintenant cette partie de la spec :
   0   0   0   0  |  0   0   0   0
   ^---^---^---^     ^---^---^---^
   arguments         opcode

$FFF0: display width  (default 128)
$FFF1: display height (default 64)
$FFFD: timer, decrease by one every ms if > 0
$FFFE: instruction pointer
$FFFF: unused ; jmp #FFFF ends the program

__: db
00: noop
01: mov &, *
20: add &, *
21: sub &, *
22: mul &, *
23: div &, *
24: inc &
25: dec &
30: jmp &
31: jz *, &
32: jnz *, &
d0: di
d1: show
d4: px *, *, *
d5: xpx *, *
d6: opx *, *, *
d7: apx *, *, *
da: sp &, *, *
db: xsp &, *, *
dc: osp &, *, *
dd: asp &, *, *
ff: err


Manque plus que l'input et je pourrai porter Dumb Clicker ! On ne peut pas faire mieux.


NB : un délai artificiel est créé par la boucle loop_in_wait, sans elle dxn n'a pas le temps d'afficher une frame que l'exécution est terminée.

Voici le code source. Non commenté, donc il peut sembler bien plus compliqué qu'il ne l'est réellement.
dinit:
    mov #FFF0, #80
    mov #FFF1, #40
    di
loop_in:
    xsp @mi, @mi_x, #1D
    xsp @tu, @tu_x, #1D
    show
    mov #FFFD, #30
    loop_in_wait:
        jnz $FFFD, loop_in_wait
    inc mi_x
    dec tu_x
    dec i
    jnz @i, loop_in
end:
    mov #FFFD, #400
end_loop_wait:
    jnz $FFFD, end_loop_wait
    jmp #FFFF

i:
    db #41
mi_x:
    db #0
tu_x:
    db #7B
mi:
    db #EAEA
tu:
    db #AEAE

ouais ouais
Lephenixnoir En ligne Administrateur Points: 24572 Défis: 170 Message

Citer : Posté le 03/01/2022 18:32 | #


Pas mal, pas mal. Quelle est la différence entre #FFFD (mov) et $FFFD (jnz) ?

J'ai essayé mais je comprends pas ce que cette instruction xsp fait pour avoir un pattern irrégulier comme ça, ni ce que le #EAEA/#AEAE vient jouer.
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Kikoodx Hors ligne Ancien labélisateur Points: 3039 Défis: 11 Message

Citer : Posté le 03/01/2022 22:47 | # | Fichier joint


Merci !

Quelle est la différence entre #FFFD (mov) et $FFFD (jnz) ?

# représente une valeur absolue, et $ est une référence. À l'exécution, $FFFD sera déférencé et la valeur pointée sera utilisée. $ est l'équivalent du @ pour les constantes.

J'ai essayé mais je comprends pas ce que cette instruction xsp fait pour avoir un pattern irrégulier comme ça, ni ce que le #EAEA/#AEAE vient jouer.


xsp dessine un sprite 4x4 en exécutant un xor sur le canvas. Le code suivant, qui dessine le même sprite avec un offset de deux pixels, le montre bien.



dinit:
    mov #FFF0, #f
    mov #FFF1, #f
    di
main:
    xsp #FFFF, #5, #5
    xsp #FFFF, #6, #6
    show
    mov #FFFD, #1000
end_wait:
    jnz $FFFD, end_wait
exit:
    jmp #FFFF


Pour #EAEA et #AEAE, chaque digit hexa représentant une ligne (4 bits), AEAE est le même sprite avec les lignes paires et impaires inversées. E est 0b1111 en binaire et A est 0b1010. Si tu traces la progression des deux symboles sur une ligne, le schèma devient plus clair. À la rencontre au centre, les E et les A s'annulent grâce à l'alignement.
ouais ouais
Lephenixnoir En ligne Administrateur Points: 24572 Défis: 170 Message

Citer : Posté le 03/01/2022 22:54 | #


Aaah mais oui c'est un sprite. J'étais en train de chercher une explication à base de pixels tous seuls et ça faisait trop complexe pour ça. Merci x)
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Kikoodx Hors ligne Ancien labélisateur Points: 3039 Défis: 11 Message

Citer : Posté le 03/01/2022 23:27 | #


Dumb Clicker™ dxn™ Edition® est terminé ! Ce Dumb Clicker est le plus classe de tous par pur hasard. L'affichage binaire est juste hyper satisfaisant pour le spam compulsif.
di #10, #10                    ;init display
show

loop:                          ;infinite loop
    wait_press:
        btn                    ;poll input events
        jz $FFE4, wait_press   ;wait O press
    inc score
    sp @score, #6, #6          ;draw score square
    show
    wait_rel:
        btn
        jnz $FFE4, wait_rel    ;wait O release
    jmp loop

score:
    db #0

Poussé dans le dépôt du projet avec les trois autres démos. Elles sont toutes commentées

http://kiko.ovh/cgit/dxn/tree/demos

J'ai simplifié l'instruction di, son design était stupide.
La touche O est espace avec mon implémentation.

ouais ouais
Lephenixnoir En ligne Administrateur Points: 24572 Défis: 170 Message

Citer : Posté le 04/01/2022 09:01 | #


Maintenant un compilateur CHIP-8 vers dxn ?
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Hackcell En ligne Maître du Puzzle Points: 1531 Défis: 11 Message

Citer : Posté le 04/01/2022 09:18 | #


de mes souvenirs pas frais de chip-8 certain trucs risque d'être non trivial…

notament le fait que la chip-8 utilise des registre de 8 bits (bonjours les if registre > 255 : registre =%256 partout)
et puis les graphisme, ça risque d'être marrant.

Après les données qui demarre avant le code et pas aux mêmes adresses ça doit se faire, tout comme le reste.

Ajouté le 04/01/2022 à 09:31 :
en fait ce serait sans doute plus simple d'implementer une vm chip-8 dans dxn
Lephenixnoir En ligne Administrateur Points: 24572 Défis: 170 Message

Citer : Posté le 04/01/2022 10:00 | #


Hackcell a écrit :
notament le fait que la chip-8 utilise des registre de 8 bits (bonjours les if registre > 255 : registre =%256 partout)
et puis les graphisme, ça risque d'être marrant.

Bah le truc avec un compilo c'est que tu peux toujours définir une opération "virtuelle" ext8 ou que sais-je qui fait reg &= 255 et ensuite la générer partout. Le fait que le programme compilé soit immonde/immense c'est pas grave

Bon ça reste une blague après

Edit : Ou alors vas-y on fait un compilateur dxn -> add-in
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Kikoodx Hors ligne Ancien labélisateur Points: 3039 Défis: 11 Message

Citer : Posté le 04/01/2022 12:03 | # | Fichier joint


Ce serait rigolo de voir un programme CHIP-8 tourner sur dxn, mais ce serait encore mieux de voir un programme dxn tourner sur CHIP-8. Vous avez deux heures

Ah update. Le sommeil c'est surcoté.

dxnr supporte de nouvelles instructions :
movp &, & : Comme mov mais déférence la deuxième valeur également. Une variable étant un pointeur, la manipulation de pointeurs se fait avec des pointeurs de pointeurs. Sans movp il n'y avait aucun moyen d'obtenir la valeur pointée par un pointeur de pointeur.
mod &, * : Opération modulo.
not & : NOT bitwise (équivalent de x=~x en C).
call *, ret : Les "fonctions" du peuple. La récursion est "limitée" à 0xFFFF appels.
push *, pop & : Permettent d'utiliser le stack utilisateur (pas le même stack que call/ret). 0xFFFF éléments avant le stack overflow.

dxnimg est un nouvel utilitaire qui permet de convertir une image grayscale en sprites dxn. Le programme parcourt l'image par blocs de 4x4, de haut à gauche jusqu'en bas à droite, et sort l'instruction db équivalent sur stdout. Programmé en C, devrait être rapide. La démo font.s a été créée en l'utilisant.



di #44, #8                     ;simple tileset demo

mov i, font
draw_next:
    movp a, @i
    sp @a, @x, #2
    add x, #4
    inc i
    movp a, @i
    jnz @a, draw_next

show
inf:                           ;stay open
    btn
    jmp inf

a:
    db #0
i:
    db #0
x:
    db #3
font:
    db #7557
    db #2232
    db #7143
    db #3463
    db #4475
    db #3417
    db #7517
    db #2247
    db #7577
    db #3477
    db #5757
    db #7353
    db #6116
    db #3553
    db #7137
    db #1137
    db #0


Je vais essayer de faire un petit jeu avec tout ça
ouais ouais
Lephenixnoir En ligne Administrateur Points: 24572 Défis: 170 Message

Citer : Posté le 04/01/2022 12:09 | #


Si tu freezes la spec' à un moment je m'amuserais bien à compiler ça vers de l'assembleur.
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Kikoodx Hors ligne Ancien labélisateur Points: 3039 Défis: 11 Message

Citer : Posté le 05/01/2022 00:17 | #


Ce serait stylé ! La spec devient de plus en plus stable, je te dirai quand ce sera posé.

Le change-mono-log yayaya !

dxna
Un bug stupide lié aux labels m'a pris des heures à dénicher, la structure contenant les labels stoquait des uint16_t dans des uint8_t. Je me déteste, c'est officiel.

dxnr
Upscale de l'affichage ! C'est enfin utilisable, et crisp as fuck.

Les instructions notb, orb, andb, xorb et dbg ont été ajoutées.
Les *b sont les opérations bitwise, c'est pratique surtout sachant que les sprites sont des entiers.
dbg prend un argument, et son comportement peut être défini par l'implémentation. dxnr écrit la valeur de l'argument et ce qu'il pointe sur stdout.

Deux nouveaux flags ont été implémentés :
FFF0 vaut 1 si le dernier inc, dec, add ou sub a underflow/overflow sa cible. Les j*f ont jarté, j*z $FFF0 ayant le même effet.
FFFC vaut 1 si le dernier xpx ou xsp a effacé au moins un pixel. Super pratique pour les collisions, même fonctionnement que CHIP-8.
J'ai sûrement oublié quelque chose.

dxnd
Solide comme un rock, pas de changement visible.


J'ai commencé à développer un petit jeu de scoring pour voir si ça s'utilise bien, je partage dès que c'est jouable

Edit 2022-1-5 2:15
Ce ne sera pas dans les specs, mais dxnr exécute sleep(0) quand noop est rencontré. Ça permet de préserver le CPU en plaçant un noop dans les boucles de délai.

Edit 2022-1-5 2:56
Macro #bind ajoutée
#bind #FFFD TIMER
#bind #FFFF END
mov TIMER, #100
wait:
    noop
    jnz @TIMER, wait
jmp END

ouais ouais
Lephenixnoir En ligne Administrateur Points: 24572 Défis: 170 Message

Citer : Posté le 05/01/2022 10:45 | #


Ce ne sera pas dans les specs, mais dxnr exécute sleep(0) quand noop est rencontré. Ça permet de préserver le CPU en plaçant un noop dans les boucles de délai.

Comment ? Je ne savais pas que sleep(0) avait un effet. Tu as une référence de de doc ou une info du même style ?
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
1, 2 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 114 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