B2C : Convertisseur Basic en C
Posté le 02/07/2016 11:24
Mon nouveau projet est donc de faire un convertisseur basic -> C
Ce serait possible parce que toutes les fonctions du basic sont transposables en C (même les gotos), par exemple Locate(x, y, str) est remplacé par locate(x,y);Print(str), F-line remplacé par ML_line(), etc.
Par contre là où ça diffère de AldeBasicLib c'est que ce sera un convertisseur total. Par exemple si le programme voit "Mat M" il génèrera une matrice M adéquate. Le but étant de lancer le programme, de sélectionner un .g1r, et d'avoir un fichier .c prêt à compiler.
Cela pourrait être utile pour les gros jeux qui prennent toutes la mémoire principale et qui seraient mieux en C (je pense à CloneLab, Arkenstone, peut être Calcraft...)
Utilisation
Nécessite : Java, le SDK casio (ou un truc permettant de compiler des addins)
Clonez le repo
http://git.planet-casio.com/Zezombye/B2C/ et exécutez B2C.java en remplaçant les arguments si besoin.
À noter que B2C n'étant pas fini, il est très probable que le programme que vous essayez de convertir vous fasse plein d'erreurs, que ce soit de B2C ou du SDK.
Programmes basic déjà convertis
NOTE: Ces programmes sont compatibles SH3. Si vous voulez les tester, convertissez les en SH4 si besoin !
Je n'ai absolument rien changé dans ces programmes (à part l'icône et le nom) donc le code est celui qu'on obtient avec B2C.
Démineur de Cakeisalie5 :
http://www.mediafire.com/file/z6t5jmfh72wfnag/DEMNR.G1A (
original)
Puissance 4 de Zezombye :
http://www.mediafire.com/file/i1ucweo66ibjy67/PUISS4.G1A (
original)
Fonctions actuellement implémentées :
- Commentaires
- Gotos&Lbls (heureusement que ça marche exactement de la même façon en C qu'en basic)
- If, Then, Else, IfEnd, ⇒
- Do, LpWhile
- While, WhileEnd
- For, To, Step, Next
- Prog, Return, Break, Stop
- Locate
- GetKey
- -> (assignement de variable/matrice/liste/dim)
- Dim, Mat, List
- L'opérateur '~'
- Variables A-Z, r, theta, Ans
- Opérateurs de calcul : + (unaire et binaire), - (unaire et binaire), *, /, ^, sqrt, racine n-ième
- Multiplication implicite (normalement)
- Opérateurs de comparaison : <, >, ≤, ≥, =, ≠
- Opérateurs logiques : And, Not, Or, Xor
- Ran# (fonction de la libc), RanInt# (par l'interpréteur casio actuellement, à changer)
- Int
- Fill(), ClrMat, ClrText
- Str, StrCmp, StrInv, StrJoin, StrLeft, StrRight, StrLen, StrLwr, StrUpr, StrMid, StrRotate, StrSrc, StrShift
Fonctions à implémenter :
- Nombres complexes
- Fonctions graphiques
- Gestion des pictures et captures
- Sauvegarde des variables
Note : les 3 fonctions suivantes ne seront pas implémentées fidèlement, il n'y aura qu'une implémentation rudimentaire.
- Les strings sans assignement (écrire "TEST" puis une nouvelle ligne)
- La fonction disp (◢)
- L'opérateur ?-> pour l'input de l'utilisateur
Fonctions qui ne seront peut être pas implémentées :
- L'écriture d'un nombre via la console (par exemple écrire "2+3" comme seule instruction affiche "5" sur l'interpréteur basic, mais il est difficile de savoir s'il faut l'afficher ou seulement la mettre dans Ans)
Comment B2C optimise le programme (autre qu'en compilant au lieu d'interpréter) par rapport à Casio
- Les opérations sont implémentées (enfin pour l'instant il n'y a que l'addition et la soustraction pour les nombres non complexes) nativement au lieu de passer par l'interpréteur; ainsi, additionner 10000x des nombres prend 1.5s pour l'implémentation de l'addition, et 4.4s si on passe par l'interpréteur.
- Les listes de Casio séparent les parties imaginaires et réelles. Par exemple, pour la liste {3, 1+2i, -4i, 6}, Casio la stockera en {3, 1, 0, 6, ?, 2, -4, ?} avec '?' 12 octets de padding. B2C ne fait pas cette séparation et stocke la partie imaginaire à côté de la partie réelle, ce qui fait qu'une fonction peut appeler directement "list_1
" au lieu de passer par une fonction intermédiaire qui recolle les 2 parties.
L'inconvénient est que les listes ne possédant pas de complexes sont 2 fois plus grandes. (en fait, si le programme possède des nombres complexes, la taille du BCDvar est de 24 octets, et sinon 12)
- Pour les matrices : même chose qu'avec les listes.
- Les strings de Casio implémentent des caractères, qui peuvent être sur 1 ou 2 octets. Cette différence fait qu'on ne peut pas avoir un accès en O(1) car on ne peut pas savoir directement la position du i-ème caractère du string en calculant. Si on veut accéder à str_1[255], l'interpréteur casio doit itérer sur tout le string.
B2C permet un accès beaucoup plus rapide en stockant chaque caractère, multi-byte ou non, sur 2 octets. Ainsi, les fonctions des strings qui travaillent sur les caractères casio (toutes sauf StrCmp) sont plus rapides. StrCmp est possiblement plus lent que si B2C implémentait les strings comme le fait casio, mais la différence est négligeable.
Pour résumer : B2C bouffe un peu plus de RAM, mais il est beaucoup plus rapide.
Citer : Posté le 27/07/2016 22:27 | #
Détailles le typedef de List ?
Mon blog ⋅ Mes autres projets
Citer : Posté le 27/07/2016 22:29 | #
int nbElements;
BCDvar *data;
} List;
Mais je vois pas le rapport par contre ça s'applique aussi à n'importe quel type (retourner un int vs retourner un *int) non ?
Ecrivez vos programmes basic sur PC avec BIDE
Citer : Posté le 27/07/2016 22:32 | #
Retourner un int et retourner un *int, ce n'est clairement pas la même chose :')
Je voulais voir ton typedef parce qu'on peut typedef un pointeur -- en l'occurrence, ce n'est pas ce que tu as fait.
IMO, il vaut mieux que tu renvoies un pointeur vers la liste (puisque la taille de la structure liste est plus grande que la taille d'un pointeur), mais ce n'est pas une gigantesque perte si tu ne le fais pas.
Mon blog ⋅ Mes autres projets
Citer : Posté le 27/07/2016 22:49 | #
Mmm, si tu déclare ta liste comme ça
List list;
//...
return list;
}
ou
List *createNewList(int nbElements, ...) {
List list;
//...
return &list;
}
Il y aura un problème avec List list car list sera détruit et la mémoire déalloué à la fin de la fonction.
Il vaut mieux faire
List *createNewList(int nbElements, ...) {
List * list = new list(nbElements,....);
//...
return list;
Et penser à déallouer plus tard.
- Kirby's DreamLand : Gobe , Gobe , Gobe !!!
- L'invasion Seanchans : Détruit la flotte ennemis a bord du "Danseur des vagues".
Citer : Posté le 28/07/2016 20:08 | #
J'ai (encore) un problème avec la fonction de conversion BCD -> int :/
int result = 0;
int power = (nb.bytes[1]>>4) + 1; //prend l'exposant
for (i = 1; i <= power; i++) { //lit les chiffres de gauche à droite en appliquant la puissance de 10 adéquate
if (i%2) {
result += (nb.bytes[i/2+1]&0xF) * pow(10, power-i); //lit le chiffre à gauche de l'octet
} else {
result += (nb.bytes[i/2+1]>>4) * pow(10, power-i); //lit le chiffre à droite de l'octet
}
}
return result;
}
Ca me retourne des erreurs mémoire à tout va, or je ne comprends pas o_o ça pourrait peut être me retourner un nombre bizarre, mais je vois pas pourquoi ça provoque des erreurs mémoires. (sachant que BCDvar est une struct avec un char[24] et que power vaut bien 0)
Ecrivez vos programmes basic sur PC avec BIDE
Citer : Posté le 28/07/2016 20:18 | #
En C, appliquer du bit shifting sur des types signés n'est pas forcément une bonne idée : essaies de mettre "unsigned char" au lieu de "char" dans la déclaration de ta structure.
Remarque annexe : pourquoi une fonction "to uint" renvoie un int et non un unsigned int ?
Mon blog ⋅ Mes autres projets
Citer : Posté le 28/07/2016 21:12 | #
Bon, après une heure de recherche, il apparaîtrait que je n'ai pas inclus <math.h> et que le SDK s'en fout totalement et compile quand même. u_u
Ajouté le 29/07/2016 à 20:39 :
Apparemment, le SDK ne reconnaît pas exit(), ce qui est bizarre parce que d'après ce que j'ai vu c'est dans le C89. (j'ai bien inclus <stdlib.h>, et je ne vois pas non plus exit() dans la liste des fonctions)
Est ce qu'il y a une alternative pour quitter le programme et revenir au menu ?
Ecrivez vos programmes basic sur PC avec BIDE
Citer : Posté le 29/07/2016 20:43 | #
Tu injectes une key puis tu lis avec GetKey en activant le retour au menu ?
Mon blog ⋅ Mes autres projets
Citer : Posté le 29/07/2016 22:42 | #
Y'a un syscall pour injecter une key, je testerai (c'est quand même bizarre qu'il y ait pas de fonction exit o_o)
Ajouté le 30/07/2016 à 10:26 :
Bon, après beaucoup de débuggage, j'ai réussi à faire fonctionner mon puissance 4
(d'ailleurs, j'ai tellement mal optimisé que l'IA tourne à peu près à la même vitesse qu'en basic, c'est un bug ou une feature ?)
Encore une fois merci beaucoup à lephé et à SimLo, ça n'aurait pas été possible sans vous
Ecrivez vos programmes basic sur PC avec BIDE
Citer : Posté le 30/07/2016 12:13 | #
Je peux pas deviner out of nowhere si c'est un bug ou une feature, va falloir que tu nous montres le code généré là. x)
Qui plus est, il est possible de reproduire le exit, mais c'est pas évident. (j'ai un code de lephé où il l'a refait en fait, je sais pas si c'est public)
Mon blog ⋅ Mes autres projets
Citer : Posté le 30/07/2016 12:52 | #
Ouais pour l'exit j'ai pas réussi à le reproduire avec la méthode de simlo, du coup j'ai fait une popup similaire au basic. D'ailleurs est ce que c'est possible de pouvoir quitter à n'importe quel moment en appuyant sur ac/on ?
Pour le lag je me demandais juste si je le gardais pour être fidèle au basic ou si je l'optimisais (même si je vois pas trop comment on peut l'optimiser plus) d'ailleurs je pense pas que tu comprendrais grand chose au code, par exemple
Getkey
LpWhile Ans≠31 And Ans≠71
se transforme en
Ans = B2C_Getkey();
} while (B2C_and(B2C_notEqualTo(Ans, B2C_convToBCD("31")), B2C_notEqualTo(Ans, B2C_convToBCD("71"))).bytes[0]);
Ce qui est assez difficile à lire, surtout que j'ai pas encore implémenté l'indentation.
Ecrivez vos programmes basic sur PC avec BIDE
Citer : Posté le 30/07/2016 13:00 | #
Beh question simple du coup : pourquoi avoir fait un convertisseur Basic -> C si c'est pour garder la lenteur du Basic ?
Et si ça va, ton code est assez clair quand même x) (j'ai déjà fait du lisp hein )
Mon blog ⋅ Mes autres projets
Citer : Posté le 31/07/2016 08:39 | #
Faudrait tester si c'est pas un problème de compilateur d'ailleurs. Sur l'émulateur du SDK ça prend 0.69 secondes pour un tour d'IA, faudrait compiler ça avec GCC et regarder s'il y a une grande différence de vitesse. (j'arrive pas à compiler avec mingw perso, il fait des trucs bizarres)
Après quelques optimisations je suis passé à 0.48 secondes, je vois pas du tout comment optimiser mieux :/
D'ailleurs, est ce que c'est mieux d'utiliser le AND logique ou bitwise ? (même question pour le OR)
if (a.bytes[1] && b.bytes[1])
return ONE;
return ZERO;
}
Ecrivez vos programmes basic sur PC avec BIDE
Citer : Posté le 31/07/2016 09:42 | #
Le AND logique et le AND bitwise ne produisent pas du tout le même effet.
On pourrait se dire simplement que "ouais le AND logique ça produit 0 ou 1 et le AND bitwise ça produit pas que 1", sauf qu'il y a un cas particulier (parmi d'autres) à considérer: 0b10000000 et 0b01000000. Les deux sont non nuls, pourtant, le AND logique produira 1 où le AND bitwise produira 0.
Et il existe le même genre de cas pour OR. Le seul cas où on peut utiliser les deux AND et les deux OR de la même façon exactement, c'est quand tu es sûr de ne tomber que sur des zéros (0b00000000) et des uns (0b00000001).
Mon blog ⋅ Mes autres projets
Citer : Posté le 31/07/2016 12:44 | #
Hmm, j'y avais pas pensé à ça. Et le xor bitwise, il marche aussi ou je suis obligé d'utiliser celui de casio ?
(et c'est quoi les pièges pour le or, je vois pas :o)
Ecrivez vos programmes basic sur PC avec BIDE
Citer : Posté le 31/07/2016 12:47 | #
Joues avec des cas particuliers
0b10 XOR 0b01 = 0b11
Donc il fonctionne, mais pas de la façon dont tu veux, à mon avis.
Pourquoi ne pas faire tout simplement "(A and not B) or (B and not A)" ?
Mon blog ⋅ Mes autres projets
Citer : Posté le 31/07/2016 12:54 | #
Faudrait voir si c'est plus rapide que faire calculer ça au syscall, ou alors tester d'autres combinaisons genre "(A or B) and not (A and B)" sachant que le not est une fonction externe (je peux pas utiliser '!') et donc plus lente. Y'a pas une manière simple de faire des benchmarks autre que prendre en vidéo l'émulateur ? :/
Ajouté le 31/07/2016 à 13:20 :
J'ai des trucs très bizarres là. o_o
Les fonctions de comparaison sont de la forme :
BCDvar result;
char *function = "A""\x10""B";
setAlphaVar('A', &a);
setAlphaVar('B', &b);
calcExp(&function, dummyOpCode, &result, 1);
return result;
}
Du coup je me suis dit que ce serait plus rapide si je déclarais le string en globale et que j'y faisais référence, mais quand je le fais ça bug à fond :
//...
BCDvar B2C_lessOrEqualThan(BCDvar a, BCDvar b) {
BCDvar result;
setAlphaVar('A', &a);
setAlphaVar('B', &b);
calcExp(&function_lessOrEqualThan, dummyOpCode, &result, 1);
return result;
}
Une idée de pourquoi ? Sachant que la présence ou non du const ne change rien, ça bug quand c'est en globale et ça marche quand c'est déclaré dans la fonction.
Ecrivez vos programmes basic sur PC avec BIDE
Citer : Posté le 01/08/2016 06:08 | #
Vu que j'ai pas suivi toute l'histoire, je comprend pas si ce que t'as là est du code généré ou non, et si c'est le cas pourquoi passer par setAlphaVar.
Enfin, pour ce qui est de quitter à tout moment en appuyant sur Ac/on c'est tout à fait possible : au lancement de l'addin, tu lances un timer dans lequel avec un isKeyDown tu regarde si Ac/on est enfoncé. Suivant ce que tu veux faire, tu joues sur les injections getkey ou les temporisations pour afficher un message ou quitter le jeu. Attention dans ce cas à bien désactiver le timer le temps de faire tout ça, quitte à le réactiver si l'utilisateur décidé de revenir dans le jeu.
Citer : Posté le 01/08/2016 06:51 | #
Vu que j'ai pas suivi toute l'histoire, je comprend pas si ce que t'as là est du code généré ou non, et si c'est le cas pourquoi passer par setAlphaVar.
C'est du code généré, en fait pour les fonctions de comparaison je fais faire le boulot à la calto, je mets les valeurs des variables dans A et B et je calcule "A<=B" (dans cet exemple).
Enfin, pour ce qui est de quitter à tout moment en appuyant sur Ac/on c'est tout à fait possible : au lancement de l'addin, tu lances un timer dans lequel avec un isKeyDown tu regarde si Ac/on est enfoncé. Suivant ce que tu veux faire, tu joues sur les injections getkey ou les temporisations pour afficher un message ou quitter le jeu. Attention dans ce cas à bien désactiver le timer le temps de faire tout ça, quitte à le réactiver si l'utilisateur décidé de revenir dans le jeu.
Je pensais que le timer ne s'activait que quand elapse était fini par contre pour les injections getkey j'ai un problème : j'arrive à injecter la touche menu (donc ça revient au menu) mais elle reste injectée (donc ça oscille entre le menu et l'addin jusqu'à ce que j'appuie sur une autre touche). Mais je vais faire ça, merci
Ecrivez vos programmes basic sur PC avec BIDE
Citer : Posté le 01/08/2016 22:41 | #
Heu, pour le coup de l'injection pour quitter, c'est bidon. Si elle reste injectée, c'est que tu l'as trop injectée, ou que tu l'injecte en continu.
{
unsigned int key;
if (isKeyUp(key_acon)) return; // si on appuie pas sur Ac/on, on quitte le callback
KillTimer(1); // sinon, on commence par tuer le timer
PopUpWin(3); // on ouvre éventuellement une popup
locate(1, 1); Print("Quitter : <KEY>"); // on affiche un petit mot
GetKey(&key); // on récupère l'entrée utilisateur
if (key == <KEY>)
{
key_inject(key_menu); // utiliser le syscall correspondant
GetKey(&key); // on revient au menu
}
SetTimer(1, 50, callback_exit); // sinon, on retourne au jeu en remettant le timer d'aplomb
}
Et dans le main :
{
SetTimer(1, 50, callback_exit); // on lance le timer
… // faire ce que t'as à faire
}
J'en vois pas l'intérêt pour le coup, un double suffit amplement… Et puis c'est plus facile à manipuler, sûrement beaucoup, beaucoup, beaucoup plus rapide.
Citer : Posté le 01/08/2016 22:50 | #
Le timer n'a pas l'air de marcher :/
installTimer(INTERRUPTION_TIMER, (void*)&exitTimerHandler, INT_MAX);
startTimer(INTERRUPTION_TIMER);
//...
}
void exitTimerHandler() {
if (IsKeyDown(KEY_CTRL_EXIT)) {
uninstallTimer(INTERRUPTION_TIMER);
B2C_exit(NO_ERROR);
}
}
Ca ne fait tout simplement rien quand j'appuie sur exit.
Ajouté le 01/08/2016 à 22:56 :
J'en vois pas l'intérêt pour le coup, un double suffit amplement… Et puis c'est plus facile à manipuler, sûrement beaucoup, beaucoup, beaucoup plus rapide.
En fait non parce que je dois faire des calculs exacts, par exemple "A = 0.1; if (A+0.2 == 0.3)". Ce serait impossible avec des doubles, du coup je fais avec les variables BCD.
Pour l'injection c'est plus ou moins ce que je fais :
installTimer(6, (void*)&timerHandler, 1);
startTimer(6);
GetKey(&key);
uninstallTimer(6);
}
void timerHandler() {
short menuCode = 0x0308;
putMatrixCode(&menuCode);
}
En fait faudrait que je trouve un moyen d'injecter une autre touche, mais je pense que c'est impossible parce que le programme s'arrête dès que j'appuie sur menu.
Ecrivez vos programmes basic sur PC avec BIDE