Développement d'une version de la SDL pour Casio PRIZM (fx-CG10/20/50)
Posté le 23/04/2022 00:25
Hello,
juste un tout petit message pour vous dire que je suis sur la SDL 1.2(.15) en ce moment pour essayer de faire une version opérationnelle sur la prizm (fx-CG10/20 et fx-CG50/Graph 90+E).
C'est un gros morceau et il y a pas mal de trucs à regarder, donc ça va certainement prendre du temps, mais je m'inspire (sans jeu de mot) de l'excellent portage de Hoffa (a.k.a Christopher Rehn) de la SDL sur TI nSpire. Toutes les fonctions qui commencent par nSDL_ sont typiquement issues du port de la SDL sur nSpire.
Il y a trois gros morceaux à prendre l'un après l'autre :
- créer le driver vidéo afin d'implémenter les routines graphiques de la SDL
- créer le driver clavier afin d'interagir avec les touches de la calculatrice
- créer le driver des timers pour tout ce qui concerne le temps/les pauses/etc.
Je me concentre dans un premier temps sur la vidéo, afin de voir ce qui est faisable.
A ce stade, j'ai réussi à compiler la
libSDL_prizm.a avec un driver video basé sur gint/display-cg via gint_vram. Et après avoir compilé/linké ma première appli dont voici le code :
#include <gint/keyboard.h>
#include <SDL/SDL.h>
nSDL_Font *fontwhite, *fontred, *fontgreen, *fontblue; // This part is coming from nSpire port of SDL
int main(void)
{
if (SDL_Init(SDL_INIT_VIDEO) == -1) exit( EXIT_FAILURE );
SDL_Surface *screen = SDL_SetVideoMode( 396, 224, 16, SDL_SWSURFACE );
fontwhite = nSDL_LoadFont(NSDL_FONT_VGA, 255, 255, 255);
fontred = nSDL_LoadFont(NSDL_FONT_VGA, 255, 0, 0);
fontgreen = nSDL_LoadFont(NSDL_FONT_VGA, 0, 255, 0);
fontblue = nSDL_LoadFont(NSDL_FONT_VGA, 0, 0, 255);
SDL_FillRect( screen, NULL, 0x0000);
nSDL_DrawString(screen, fontwhite, 10, 20, "This is SDL in action !!" );
nSDL_DrawString(screen, fontred, 30, 40, "This is SDL in action !!" );
nSDL_DrawString(screen, fontgreen, 50, 60, "This is SDL in action !!" );
nSDL_DrawString(screen, fontblue, 70, 80, "This is SDL in action !!" );
SDL_Flip( screen );
// Need to use Gint/Keyboard now as there is no keyboard driver for Prizm yet
getkey();
SDL_FillRect( screen, NULL, 0xFFFF );
nSDL_DrawString(screen, fontred, 30, 40, "This is SDL in action !!" );
nSDL_DrawString(screen, fontgreen, 50, 60, "This is SDL in action !!" );
nSDL_DrawString(screen, fontblue, 70, 80, "This is SDL in action !!" );
nSDL_DrawString(screen, fontred, 90, 100, "This is SDL in action !!" );
nSDL_SetPixel( screen, 150, 150, 0x0000 );
nSDL_SetPixel( screen, 150, 151, 0x0000 );
nSDL_SetPixel( screen, 151, 150, 0x0000 );
nSDL_SetPixel( screen, 151, 151, 0x0000 );
SDL_Flip( screen );
// Need to use Gint/Keyboard now as there is no keyboard driver for Prizm yet
getkey();
nSDL_FreeFont(fontwhite);
nSDL_FreeFont(fontred);
nSDL_FreeFont(fontgreen);
nSDL_FreeFont(fontblue);
SDL_Quit();
return 1;
}
Nous pouvons dire que ça fonctionne ...
Et voici les 2 screenshots correspondants après chaque blit:
Bon, il y a pas grand chose pour le moment, et je pense que niveau RAM on est quasi plein, il faudra je pense allouer une arène spécifique, car par exemple si j'alloue une police supplémentaire, elle ne s'affiche pas. J'ai encore pas mal de choses à vérifier, mais c'est un bon début.
La prochaine grosse étape sera le driver clavier avec la gestion des événements. Je vais essayer d'interfacer avec Gint/Keyboard.
Ensuite viendront les timers...
Aller Ciao et @RDP
Sly
Citer : Posté le 23/04/2022 09:16 | #
Bon je viens de lever une pierre et de trouver un énorme serpent dessous, il y a un point que j'avais complètement occulté et qui va me faire transpirer je pense : le chargement des ressources externes, par exemple via un SDL_LoadBMP( filename ).
Je comprenais pas pourquoi ça ne fonctionnait pas, mais en fait il faut encapsuler ça dans un gint_world_switch( ) . Du coup pour le moment, tous les calls de la SDL à des fopen/fread... restent à l'état de lettre morte et les SDL_Surface correspondant à NULL.
Je le sais pourtant, mais j'avoue que je l'avais pas vu venir celui-là
Donc en fait il y a un quatrième gros volet :
- créer l'interface entrée/sortie avec les ressources externes.
Suite au prochain épisode...
Sly
PS : du coup je pense que ce fil risque de devenir un sorte de devlog et un listing de mes misères
PPS : mon petit doigt me dit que j'ai pas fini de découvrir des trucs auxquels j'ai pas pensé ...
Citer : Posté le 23/04/2022 12:37 | #
Excellent boulot ! Merci beaucoup pour cette initiative, sans toi ça aurait encore attendu sans doute longtemps
Note que les fonctions sur les fichiers ne cherchent pas à "détecter" si un world switch a été fait. Si tu les utilises hors d'un world switch elles vont se lancer normalement, et parfois même fonctionner ; un problème lié à l'absence de world switch se manifeste quasiment toujours comme un crash (écran noir + reboot). Si tu obtiens NULL de façon consistante et sans crash alors il y a sans doute quelque chose d'autre qui coince.
Je suis à peu près sûr que la vidéo c'est le pus gros. Bon courage
Citer : Posté le 23/04/2022 17:21 | #
Excellent boulot ! Merci beaucoup pour cette initiative, sans toi ça aurait encore attendu sans doute longtemps
Merci, mais on s’emballe surtout pas, c'est un gros truc je pense et il y aura plein de galères à gérer (du genre là les gint_world_switch combiné au little/big endian qui rend les choses pénibles). Mais bon, en effet, au moins c'est commencé et c'est sur le feu
Note que les fonctions sur les fichiers ne cherchent pas à "détecter" si un world switch a été fait. Si tu les utilises hors d'un world switch elles vont se lancer normalement, et parfois même fonctionner ; un problème lié à l'absence de world switch se manifeste quasiment toujours comme un crash (écran noir + reboot). Si tu obtiens NULL de façon consistante et sans crash alors il y a sans doute quelque chose d'autre qui coince.
Oui je suis d'accord, à mon avis je passais à côté de qq chose. je suis dessus pour analyser plus en détail.
Je suis à peu près sûr que la vidéo c'est le pus gros. Bon courage
Merci, il en faudra
Ajouté le 23/04/2022 à 20:46 :
Up,
après m'être battu avec SDL_LoadBMP(), j'en suis arrivé à réécrire la fonction.
Donc voici un splendide BMP d'illustration (pas taper Massena ) qui se charge dans une SDL_Surface.
Cette SDL_Surface est ensuite "blittée" en entier en bas à droite et par portion en haut à droite.
Ceci pour montrer que l'on peut interagir avec les SDL_Surface, les SDL_Rect et surtout les SDL_CreateRGBSurface. Faudra que je teste plus mais à priori cette partie est OK.
D'ailleurs il y aurait peut être moyen de bricoler une passerelle entre le format bopti_image_t et les SDL_Surface via la commande SDL_CreateRGBSurfaceFrom. A regarder de plus près (en gros ça permet de récupérer les data pixels et de s'en servir dans une SDL_Surface, faudrait voir si ça peut passer).
Pour le moment j'ai fait mes essais en 16bits RGB565, faut que je teste ce que ça donne dans les autres modes.
Et voici le screen de l'horreur ...
Je sais c'est très moche
Je vais me mettre un peu sur la partie clavier ce soir, pour voir ce que je peux en tirer.
Tchooooooooooooooooooooooooo !!!!
Citer : Posté le 24/04/2022 22:50 | # | Fichier joint
Yo, BIG BIG BIG UPDATE !!!!
C'est bon on a une version utilisable en production de la SDL 1.2.15 avec comme modules fonctionnels :
- le driver vidéo
- le driver clavier
- les timers
et en bonus l'ensemble de la libSDL_gfx (primitives, rotozoom, ...)
Voici pour le démonter le premier jeu réel fonctionnant sous la SDL sur PRIZM (je l'avais fait pour la nSpire, c'était mon premier programme, et je l'ai converti en 4min30 chrono en main ) :
Je vous mets en PJ l'addin pour tester.
Tous les modules sont utilisés par ce petit Tetris (video/timers/keys et les primitives graphiques de SDL_gfx ).
Les touches sont SHIFT pour rotation des tetrominos, BAS pour descendre et GAUGHE / DROITE pour déplacer + EXIT pour quitter).
A plus
Sly
PS : la SDL est sur le dépôt à l'état de dev, je vais poursuivre les tests pour la passer en master et sous forme d'un dépôt Giteapc.
Ajouté le 24/04/2022 à 22:55 :
Et pour montrer que ca tourne bien sur toutes les Prizm :
Citer : Posté le 28/04/2022 15:04 | # | Fichier joint
Hello à Toutes et Tous,
cette fois c'est tout bon, on a une version de la SDL fonctionnelle sur la plateforme Prizm au sens large, à savoir que les machines suivantes sont couvertes par ce port :
- fx-CG50 et Graph 90+E
- fx-CG10/20 a.k.a Prizm
La librairie utilisable se nomme libSDL_prizm.a et correspond à une fusion de la librairie SDL 1.2.15 et de la librairie SDL_gfx 2.0.25 (je précise que le numéro 2.0.25 de cette dernière ne la rend pas dépendante de la lib SDL 2.0, mais bien de la 1.2.x).
Il est désormais possible d'installer la libSDL_prizm via GiteaPC par la commande suivante
Pour les plus aventuriers, il y a aussi un Makefile à lancer dans le répertoire racine d'extraction via un
A ce stade, vous pourrez commencer à créer un programme avec la SDL très simplement.
Créer un nouveau projet via
Je précise que la cible build-fx n'est à ce stade pas interdite, mais la libSDL_prizm n'est pas adaptée aux monochromes, vous obtiendrez d'ailleurs systématiquement une erreur de taille de l'addin. Je tâcherai d'empêcher cela dans les prochains jours. Juste pour le moment, si vous avez une monochrome, passez votre chemin, c'est pas adapté à votre machine.
Maintenant, quelques limites et pour de mise en garde concernant cette version de la SDL :
- video : le seul mode supporté est le 396px x 224px x 16bpp en SDL_SWSURFACE (double buffering) qui est appelé par un
- Souris / son / CDROM / threads / joystick : tout est désactivé
- clavier : le clavier est mappé avec des nom de touches spécifiques (pour que la SDL s'y retrouve). Les touches ont le même nom que dans Gint, mais précédé de SDL_PRZ_ ainsi la touche EXE (donc KEY_EXE dans gint), s'appelle SDL_PRZ_KEY_EXE dans la SDL. A priori toutes les fonctions relatives à la gestion du clavier, y compris SDL_GetKeyState(NULL) fonctionnent. Il y a plein de tutos sur internet sur les pollevent/pushevent/etc.. de la SDL1.2, je vous laisse donc regarder.
- l'event "SDL_QUIT" ne correpond à rien (donc pas la peine de le considérer), tout comme les event liés à la souris/au joystick.
- attention avec les entrées/sorties dans les fichiers, pensez bien au gint_world_switch. Par exemple, pour importer une image BMP, il faut utiliser
Concernant l'importation d'images, à ce stade seul les BMP sont couverts, on pourra peut être regarder pour faire avoir une librairie SDL_image pour la Prizm plus tard, mais déjà on peut importer des ressources graphiques externes. Pour ceux qui râleront sur l'absence de transparence dans le BMP, il faut faire un fond d'une couleur unie (souvent on prend le rose fushia (255,0,255) ) et on indique à la SDL que cette couleur sera la couleur transparente par
uint32_t colortransparent = (uint32_t) cSDL_GetPixel( playerTR, 1, 1 ); // le pixel (1,1) de l'image player.bmp est de la couleur de la transparence
SDL_SetColorKey(playerTR, SDL_SRCCOLORKEY, colortransparent );
cf exemple d'image en PJ.
J'espère que cela vous sera utile. J'oublie certainement plein de choses.
N'hésitez pas à faire remonter vos soucis (ou vos succès d'ailleurs, pourquoi toujours seulement parler des trains qui arrivent en retard).
Si vous demandez gentiment, je vous préparerai un squelette d'addin qui va bien pour vous permettre d'utiliser le bouzin
A plus
Sly
Citer : Posté le 29/04/2022 10:10 | # | Fichier joint
Yo, c'est encore moi
Dans le long voyage qui nous amènera peut-être (enfin j'espère bien) à avoir une librairie SDL complète (avec SDL_image), il y a un certain nombre de prérogatives à passer, notamment pour le support des ressources externes en format PNG. En effet la SDL_image s'appuie sur la libPNG, qui elle même repose sur la zlib. Donc il faut prendre les choses les unes après les autres et aujourd'hui je vous crée un dépôt avec la zlib fonctionnelle pour les Casio (fx/cg)
Celle-ci se nomme cZlib et repose sur la zlib 1.2.5 : dépôt Gitea ici : cZlib1.2.5
Comme d'habitude il est possible d'installer la libczlib via GiteaPC par la commande suivante
Pour les plus aventuriers, il y a aussi un Makefile à lancer dans le répertoire racine d'extraction via un
Là encore comme d'hab', vous pourrez commencer à créer un programme avec la zlib très simplement.
Créer un nouveau projet via
voici un code exemple pour utiliser :
#include <gint/gint.h>
#include <gint/display.h>
#include <stdio.h>
#include <string.h> // for strlen
#include <assert.h>
#include "zlib.h"
int main()
{
// original string to be compressed
char a[50] = "Hello Hello Hello Hello Hello Hello !!!!!!";
// placeholder for the compressed (deflated) version of "a"
char b[50];
// placeholder for the UNcompressed (inflated) version of "b"
char c[50];
dclear( 0xFFFF );
dprint(1,10,0x0000, "Uncompressed size is: %lu", strlen(a));
dprint(1,20,0x0000, "Uncompressed string is:");
dprint(1,30,C_BLUE, "%s", a);
// STEP 1 : deflate a into b. (that is, compress a into b)
// zlib struct
z_stream defstream;
defstream.zalloc = Z_NULL;
defstream.zfree = Z_NULL;
defstream.opaque = Z_NULL;
// setup "a" as the input and "b" as the compressed output
defstream.avail_in = (uInt)strlen(a)+1; // size of input, string + terminator
defstream.next_in = (Bytef *)a; // input char array
defstream.avail_out = (uInt)sizeof(b); // size of output
defstream.next_out = (Bytef *)b; // output char array
// the actual compression work.
deflateInit(&defstream, Z_BEST_COMPRESSION);
deflate(&defstream, Z_FINISH);
deflateEnd(&defstream);
dprint(1,50,0x0000, "Compressed size is: %lu", strlen(b));
dprint(1,60,0x0000, "Compressed string is:" );
dprint(1,70, C_BLUE, "%s", b);
dprint(1,80,C_RED, "Don't worry if not all char are visible :");
dprint(1,90,C_RED, "may contain non printable ones" );
// STEP 2 : inflate b into c (should return to string a)
// zlib struct
z_stream infstream;
infstream.zalloc = Z_NULL;
infstream.zfree = Z_NULL;
infstream.opaque = Z_NULL;
// setup "b" as the input and "c" as the compressed output
infstream.avail_in = (uInt)((char*)defstream.next_out - b); // size of input
infstream.next_in = (Bytef *)b; // input char array
infstream.avail_out = (uInt)sizeof(c); // size of output
infstream.next_out = (Bytef *)c; // output char array
// the actual DE-compression work.
inflateInit(&infstream);
inflate(&infstream, Z_NO_FLUSH);
inflateEnd(&infstream);
dprint(1,110,0x0000, "Uncompressed size is: %lu", strlen(c));
dprint(1,120,0x0000, "Uncompressed string is:" );
dprint(1,130,C_BLUE, "%s", c);
// make sure uncompressed is exactly equal to original.
if(strcmp(a,c)==0) dprint(1,150, C_GREEN, "Everything is OK" );
else dprint(1,150, C_RED, "There is a problem somewhere" );
dupdate();
getkey();
return 0;
}
Et voici deux screenshots pris sur ma G90+E pour montrer que ça marche :
Fun fact : vous noterez l'efficacité de la compression dans le premier cas
Cela pourra toujours vous servir pour des utilitaires à un moment ou à un autre.
Attention, comme d’habitude avec les flux dans les fichiers : bien penser au gint_world_switch qui va bien !!!
Ciao
Sly
Citer : Posté le 05/05/2022 10:17 | # | Fichier joint
Yo, je poursuis mes update sur les lib.
Concernant la libSDL, j'ai dû faire face à un problème très très idiot d'alignement des modes de "seek" dans les fichiers entre la SDL et <stdio.h> qui me causait de gros maux de tête (heureusement un Lephé Salvateur est passé par là ). En effet SDL redéfinit un certain nombre de constantes de manière à s'abstraire des implémentations "locales" sur telle ou telle architecture. En particulier pour les opérations dans les fichiers, elle définit les constantes RW_SEEK_SET, RW_SEEK_CUR et RW_SEEK_END qu'elle réinjecte dans les fonction ad-hoc par la suite (en l'occurence chez nous fseek de <stdio.h>).
La "bonne blague" vient de l'implémentation sur Casio de ces constantes qui ne sont pas celles "officielles" du standard, (cf le ticket ici : Ticket SDL_image) ainsi RW_SEEK_SET correspondait à SEEK_CUR dans notre implémentation de fseek, RW_SEEK_CUR correspondait à SEEK_END et RW_SEEK_END à SEEK_SET.
Et bien entendu pour tester, je faisais la transposition des constantes à la main dans mon code donc passais à côté du truc (Ou comment galérer pour une ânerie ... ). Quand on a le nez dans le guidon, on a parfois du mal à prendre le recul nécessaire pour voir ce qui saute directement aux yeux des autres.
Bref, long story short, c'est fixé, et désormais les entrées/sorties dans les fichiers via l'abstraction SDL_RWops est fonctionnelle, testée et validée.
Donc en résumé :
- Video : OK en 16bpp 396px * 224px (les autres modes ne sont pas supportés donc attention)
- Timers : OK
- Keyboard : OK
- Fichiers : OK
J'ai aussi ajouté une fonction de debug dans la SDL car dans la version 1.2, le log dans un fichier n'est pas prévu (c'est dans la v2.0.0 que c'est arrivé). Je l'ai intégrée dans <SDL/SDL.h> afin qu'elle soit toujours accessible.
Elle se nomme cSDL_LogToFile( char *fmt, ... ) est s'utilise comme un printf.
Attention, comme elle opère sur les fichiers, il faut l'utiliser via un gint_world_switch si on est pas déjà sorti.
par exemple :
// pour loger juste un simple texte :
gint_world_switch( GINT_CALL(cSDL_LogToFile, "Simple text to be written." ) );
// et pour utiliser une sortie log formatée
gint_world_switch( GINT_CALL(cSDL_LogToFile, "Sortie formatée : x= %d.", 25 ) );
Attention donc au nombre de paramètres utilisables dans GINT_CALL() au maximum.
Si besoin, il faut passer par une "couche intermédiaire" de fonction écran (si on appelle directement cSDL_LogToFile depuis une fonction qui est appelée dans un gint_world_switch, c'est Ok.
Autre (et dernière modification) : du coup j'avais auparavant un bug dans la fonction SDL_LoadBMP qui est offerte en standard pour charger les fichier ressources en BMP (comme son nom l'indique), le problème venant du point sur les constantes SEEK. J'avais donc réécrit cette fonction à la main pour charger un BMP, mais tous les formats n'étaient pas reconnus. Du coup la version native de la fonction est désormais opérationnelle. Là encore testée et validée (j'en reparlerai dans le topic SDL_image).
Voilà voilà.
Honnêtement, je pense que l'on a désormais une SDL assez stable et fonctionnelle. Je ne pense pas avoir pu tout tester à ce stade, mais je n'ai pas croisé de problème majeur. A priori les fonctions principales sont dispos et testées. Je regarderai les bugs au fur et à mesure de leur apparition et des remontées pour faire les MàJ et fix nécessaire, mais en terme de développement spécifique pour les Casio, je considère qu'on est OK avec cette version.
N'hésitez donc pas à tester et à me faire vos retours (si ça marche ou si vous voyez des soucis).
Sly
et @RDP