Les membres ayant 30 points peuvent parler sur les canaux annonces, projets et hs du chat.

Forum Casio - Projets de programmation


Index du Forum » Projets de programmation » gint : un noyau pour développer des add-ins
Lephenixnoir Hors ligne Administrateur Points: 22762 Défis: 149 Message

gint : un noyau pour développer des add-ins

Posté le 20/02/2015 17:30

Ce topic fait partie de la série de topics du fxSDK.

En plus des options de programmation intégrée comme le Basic Casio ou Python, la plupart des calculatrices Casio supportent des add-ins, des programmes natifs très polyvalents avec d'excellentes performances. Les add-ins sont généralement programmés en C/C++ avec l'aide d'un ensemble d'outils appelé SDK.

Plusieurs SDK ont été utilisés par la communauté avec le temps. D'abord le fx-9860G SDK de Casio avec fxlib pour Graph monochromes (plus maintenu depuis longtemps). Puis le PrizmSDK avec libfxcg pour Prizm et Graph 90+E (encore un peu actif sur Cemetech). Et plus récemment celui que je maintiens, le fxSDK, dont gint est le composant principal.

gint est un unikernel, ce qui veut dire qu'il embarque essentiellement un OS indépendant dans les add-ins au lieu d'utiliser les fonctions de l'OS de Casio. Ça lui permet beaucoup de finesse sur le contrôle du matériel, notamment la mémoire, le clavier, l'écran et les horloges ; mais aussi de meilleures performances sur le dessin, les drivers et la gestion des interruptions, plus des choses entièrement nouvelles comme le moteur de gris sur Graph monochromes.

Les sources de gint sont sur la forge de Planète Casio : dépôt Gitea Lephenixnoir/gint

Aperçu des fonctionnalités

Les fonctionnalités phares de gint (avec le fxSDK) incluent :

  • Toutes vos images et polices converties automatiquement depuis le PNG, sans code à copier (via fxconv)
  • Un contrôle détaillé du clavier, avec un GetKey() personnalisable et un système d'événements à la SDL
  • Une bibliothèque standard C plus fournie que celle de Casio (voir fxlibc), même un peu de C++ (voir µSTL)
  • Plein de raccourcis pratiques, comme pour afficher la valeur d'une variable : dprint(1,1,"x=%d",x)
  • Des fonctions de dessin, d'images et de texte optimisées à la main et super rapides, surtout sur Graph 90+E
  • Des timers très précis (60 ns / 30 µs selon les cas, au lieu des 25 ms de l'OS), indispensables pour les jeux
  • Captures d'écran et capture vidéo des add-ins par USB, en temps réel (via fxlink)

Avec quelques mentions spéciales sur les Graph monochromes :
Un moteur de gris pour faire des jeux en 4 couleurs !
La compatibilité SH3, SH4 et Graph 35+E II, avec un seul fichier g1a
Une API Unix/POSIX et standard C pour accéder au système de fichiers (Graph 35+E II seulement)

Et quelques mentions spéciales sur les Graph 90+E :
Une nouvelle police de texte, plus lisible et économe en espace
Le dessin en plein écran, sans les bordures blanches et la barre de statut !
Un driver écran capable de triple-buffering
Une API Unix/POSIX et standard C pour accéder au système de fichiers

Galerie d'add-ins et de photos

Voici quelques photos et add-ins réalisés avec gint au cours des années !



Arena (2016)Plague (2021)



Rogue Life (2021)



Momento (2021)



Communication avec le PC (cliquez pour agrandir)


Utiliser gint pour développer des add-ins

Les instructions pour installer et utiliser gint sont données dans les divers tutoriels recensés dans le topic du fxSDK. Il y a différentes méthodes de la plus automatique (GiteaPC) à la plus manuelle (compilation/installation de chaque dépôt). Le fxSDK est compatible avec Linux, Mac OS, et marche aussi sous Windows avec l'aide de WSL, donc normalement tout le monde est couvert

Notez en particulier qu'il y a des tutoriels de développement qui couvrent les bases ; tout le reste est expliqué dans les en-têtes (fichiers .h) de la bibliothèque que vous pouvez consulter en ligne, ou dans les ajouts aux changelogs ci-dessous.

Changelog et informations techniques

Pour tester les fonctionnalités et la compatibilité de gint, j'utilise un add-in de test appelé gintctl (dépôt Gitea Lephenixnoir/gintctl). Il contient aussi une poignée d'utilitaires d'ordre général.

Ci-dessous se trouve la liste des posts indiquant les nouvelles versions de gint, et des liens vers des instructions/tutoriels supplémentaires qui accompagnent ces versions.

VersionDateInfos supplémentaires
gint 2.9.021 Août 2022
gint 2.8.017 Mai 2022Effets dynamiques sur les imagesAPI de manipulations d'images
Overclock intégré
gint 2.7.119 Mars 2022Tutoriel capture des flux standards
gint 2.7.031 Décembre 2021
gint 2.6.029 Août 2021Tutoriel de capture vidéo par USB
gint 2.5.28 Juin 2021
gint 2.5.12 Juin 2021
gint 2.5.026 Mai 2021Intégration de fxlibc (dépôt) — Tutoriel de communication par USB
gint 2.4.027 Avril 2021Api GINT_CALL() pour les callbacks
gint 2.3.12 Février 2021
gint 2.3.029 Janvier 2021
gint 2.2.112 Janvier 2021
gint 2.2.011 Janvier 2021
gint 2.1.116 Septembre 2020
gint 2.1.021 Août 2020Polices UnicodeNouvelle API du moteur de gris
gint 2.0.3-beta10 Juillet 2020Modifications de l'API timer
gint 2.0.2-beta17 Juin 2020
gint 2.0.1-beta1er Juin 2020

Anecdotes et bugs pétés

Ô amateurs de bas niveau, j'espère que vous ne tomberez pas dans les mêmes pièges que moi.


TODO list pour les prochaines versions (2022-08-21)

gint 2.10
  1. Changements de contextes CPU. À reprendre du prototype de threading de Yatis pour permettre l'implémentation d'un véritable ordonnanceur. Demandé par si pour faire du threading Java. Je vais peut-être coder des effets algébriques pour m'amuser un peu.
  2. Fignoler le driver USB. Ajouter la communication PC→calto, des pipes interruption/isochrones. Ajouter le support de descripteurs de fichiers USB. Potentiellement pousser jusqu'à avoir GDB pour debugger.

Non classé

  • Support de scanf() dans la fxlibc.
  • Regarder du côté serial (plus facile que l'USB) pour la communication inter-calculatrices (multijoueur) et ultimement l'audio (libsnd de TSWilliamson).
  • Un système pour recompiler des add-ins mono sur la Graph 90+E avec une adaptation automatique.
  • Support des fichiers en RAM pour pouvoir utiliser l'API haut-niveau sur tous les modèles et éviter la lenteur de BFile à l'écriture quand on a assez de RAM.
  • Overclock sur Graph mono.



Précédente 1, 2, 3 ··· 10 ··· 20 ··· 30 ··· 40 ··· 50 ··· 60, 61, 62, 63, 64, 65, 66 ··· 68, 69, 70 Suivante
Kikoodx Hors ligne Labélisateur Points: 2979 Défis: 11 Message

Citer : Posté le 22/03/2022 00:26 | #


Joli programme ! Les effets dynamiques vont certainement ouvrir de nouveaux horizons, j'adore tout ce que tu prévois !

QOI

Je suis très hype pour QOI ! À la fois pour les options que ça donne pour la compression de l'add-in, et pour la flexibilité des projets.

Il serait potentiellement très utile d'avoir l'option de charger des ressources QOI externes en tant que bopti :
Le transfert d'un add-in peut devenir lent quand le projet grossit, transférer uniquement les ressources modifiées ferait gagner beaucoup (beaucoup !) de temps
SDL2_image supportera QOI avec sa prochaine release, il serait potentiellement beaucoup plus facile d'écrire des jeux cross platform complets avec le standard QOI et les fonctionnalités d'IO toutes fraîches de gint. Je dis ça, je dis rien

Les effets considérés sont les suivants (je prends les suggestions) :

Mon effet favori de libimg est img_dye, qui remplace tous les pixels opaques par une couleur spécifiée. C'était très pratique pour faire des effets

Tu as listé les deux flips séparèment, est-ce que ça veut dire qu'ils auront des fonctions dédiées ? D'intuition je me serais attendu à une fonction unique qui prend une combinaison de flags (IMG_FLIP_NONE ou IMG_FLIP_HORI | IMG_FLIP_VERT par exemple). Je suis curieux.
mi lape ala.

J'suis un méga chômeur
Lephenixnoir Hors ligne Administrateur Points: 22762 Défis: 149 Message

Citer : Posté le 22/03/2022 11:06 | #


Merci pour les retours !

Slyvtt a écrit :
Isolation de chacune des composantes d’une image

Ça c'est pas trop difficile en termes de calcul (ça dépend si on préserve que la composante ou si on passe en niveau de gris). Ça me fait penser au passage qu'un format 8-bits de niveau de gris serait peut-être utile ? À voir. En tous cas : probablement relégué à une transformation en RAM ou à l'effet dynamique "injection de code" (un système générique dans lequel on écrit du code assembleur personnalisé pour le traitement de chaque pixel, et le reste de la fonction est ajouté autour).

Conversion en niveau de gris

Un peu non trivial ça aussi, surtout vu les multiples formules pour la luminosité, donc peut-être moyennement viable comme effet dynamique. Mais ça ferait au moins une bonne transformation en mémoire. Notez aussi que ce genre de trucs pour les images à palette il suffit de changer la palette ; actuellement elle est "fixée" à l'image mais si je mets une indirection ça doit pouvoir se faire sans dupliquer l'image, juste la palette.

Blender une images avec un fond avec un niveau alpha

Ça c'est très non trivial et très calculatoire ; ça ne crée pas de nouvelles couleurs donc on peut l'imaginer sur les palettes. Sans vouloir trop spoiler j'ai des idées sur comment calculer ce genre de transformations efficacement à partir d'une LUT et un peu d'approximation... pour plus tard sans doute.

KikooDX a écrit :
Il serait potentiellement très utile d'avoir l'option de charger des ressources QOI externes en tant que bopti :
Le transfert d'un add-in peut devenir lent quand le projet grossit, transférer uniquement les ressources modifiées ferait gagner beaucoup (beaucoup !) de temps

Ça n'a pas énormément de sens de traiter QOI comme un format bopti ; si tu regardes le format tu verras vite que :

  • L'image ne peut être décodée que dans un sens (ce qui casserait les transformations géométriques dans le sens où je les implémente, ie. parcourir la VRAM à l'endroit et l'image à l'envers ; il faudrait faire l'inverse)
  • On ne peut pas extraire de sous-image à cause de l'absence de stride prévisible et des backrefs
  • Les transformations de couleur seraient au mieux dans le style de RGB565/RGB565A, ie. pas très intéressantes/performantes

Mon plan ç'aurait plutôt été d'avoir un header genre séparé genre <gint/qoi16.h> avec une fonction qui affiche la totalité de l'image en mode normal, éventuellement les miroirs, et une qui te la convertit vers un format bopti. Là encore, pas de sous-image/spritesheet possible.

En tous cas, je note bien ce que tu demandes, et je m'assurerai que tu puisses charger un fichier et en tirer l'image. Rien ne devrait t'empêcher de la transférer par USB une fois la communication double-sens supportée d'ailleurs. (Par contre par pitié charge pas les fichiers externes dans la version finale x3)

Mon effet favori de libimg est img_dye, qui remplace tous les pixels opaques par une couleur spécifiée. C'était très pratique pour faire des effets

Ça rentre dans "Opaque color remap" mais peut-être qu'on peut le faire plus optimisé que le cas général. J'essaierai de le garder en effet dynamique.

Tu as listé les deux flips séparèment, est-ce que ça veut dire qu'ils auront des fonctions dédiées ? D'intuition je me serais attendu à une fonction unique qui prend une combinaison de flags (IMG_FLIP_NONE ou IMG_FLIP_HORI | IMG_FLIP_VERT par exemple). Je suis curieux.

C'est là que tu touches au cœur du problème ! Hier j'ai juste fait une analyse de comment implémenter chaque transformation indépendamment. Mon but ultime ce serait d'avoir une fonction d'affichage générale qui permette de combiner ces effets, idéalement les options géométriques d'un côté et un effet de couleur (les effets de couleurs sont un peu exclusifs). (C'est ce que j'entends par «on sent une logique avec un très beau modèle de transformations», même si je réalise qu'il faut être un théoricien pour le comprendre, oups.)

Maintenant que j'y pense je suis sûr que quelqu'un va me demander les rotations de 90/270°... qui sont possibles (sauf sur P4) mais vraiment galères sur la méthode d'itération pour le coup. Oui il suffit d'échanger x et y, mais le code est tellement optimisé que c'est pas facile à faire proprement. x)

Mon plus gros souci pour l'instant c'est comment supporter ces effets dynamiques sans avoir 40 fonctions différentes qui implémentent chacune une combinaison d'effets. D'où le "stade actuel : réflexion" ha ha.
Kikoodx Hors ligne Labélisateur Points: 2979 Défis: 11 Message

Citer : Posté le 22/03/2022 12:14 | #


Ça n'a pas énormément de sens de traiter QOI comme un format bopti ; si tu regardes le format tu verras vite que :

Je me suis mal exprimé, mais j'en étais bien conscient. Je demandais ça dans le sens conversion QOI → bopti, ce que tu as confirmé juste après alors parfait

En tous cas, je note bien ce que tu demandes, et je m'assurerai que tu puisses charger un fichier et en tirer l'image.

Génial, merci

Mon but ultime ce serait d'avoir une fonction d'affichage générale qui permette de combiner ces effets, idéalement les options géométriques d'un côté et un effet de couleur

Aaaaaaaaaaaaan, bon courage !
mi lape ala.

J'suis un méga chômeur
Lephenixnoir Hors ligne Administrateur Points: 22762 Défis: 149 Message

Citer : Posté le 24/03/2022 13:56 | # | Fichier joint


Pour répondre à une demande de @Slyvtt sur la shoutbox :


Adresser la mémoire jusqu'à 3 Mo

La RAM de la Graph 90+E est une puce de 8 Mo, au lieu des 2 Mo présents sur la Prizm. Mais l'OS n'a pas changé, et pour autant qu'on le sache actuellement n'utilise pas les 6 Mo supplémentaires. Tout comme la puce de 256/512 ko de RAM sur les Graph mono, c'est un peu free real estate si vous voulez.

Cependant, il y a deux inconnues : (1) est-ce que la mémoire est vraiment libre, et (2) combien de temps ça va durer. La Graph 35+E II s'est finalement mise à utiliser les 256 ko "gratuits" pour étendre ses fonctionnalités et supporter Python, ce qui fait que les add-ins qui se servent sans réfléchir corrompent de la RAM, et ça c'est pas cool.

Cette mémoire est déjà utilisée dans CGDoom si on active l'option "Enable experimental RAM", avec deux protections pour répondre à ces deux problèmes :

  1. Il n'utilise que la première zone de 0 trouvée entre 2 Mo et la fin de la RAM (3 Mo environ sur les 6 Mo en tout)
  2. Il ne le fait que pour les OS testés (actuellement ≤ 3.60)

Bernard Parisse m'a fait remarquer que la partie 1 a des problèmes vu que si on écrit dans la RAM c'est plus des zéros ensuite. Je sais pas trop pourquoi ça marche en pratique ; au pire on peut sauter cette condition, c'est pas la plus solide.

Dans CGDoom, ce code calcule la taille disponible et ce code interdit son usage sur les versions d'OS non testées.

Dans gint, on voudra peut-être ajouter une arène pour utiliser cette mémoire dans malloc(), ce qui se fait avec ces fonctions ; voir l'exemple canonique dans ce fichier.

Son via le Jack 2.5 mm sur la Graph 90+E

TSWilliamson a trouvé le bon moyen de bourriner le port série pour moduler le signal 1-bit en quelque chose de bien plus fin, ce qui a donné lieu à sa libsnd et ce post explicatif sur Cemetech. Vous pouvez voir dans le post à quel point il approche bien le signal idéal (jaune) avec le port série de la calto (rouge).

Ça fait longtemps que je veux l'intégrer dans gint, mais pour l'instant je n'ai jamais programmé avec libsnd, donc je n'en sais pas beaucoup plus. La faisabilité est acquise a priori.
Slyvtt Hors ligne Community Manager Points: 891 Défis: 0 Message

Citer : Posté le 24/03/2022 14:36 | #


Donc si je résume ma compréhension de tout ça pour avoir 3Mo de mémoire dispo (dis moi si je me trompe).

1/ je vérifie qu'on a un OS < 3.60. Si oui on poursuit sinon on limite à l'arene "_uram" classique sans faire le kéké (pour le moment tout du moins)

2/ si Ok, alors je peux créer une nouvelle arène qui commence à 0xac200000 et qui fait par exemple 3Mo (la taille pouvant être ajustée en fonction de combien de 0 je trouve avec une fonction de vérification comme dans CGDoom). Disons que j'appelle cette arène "_extram".

avec un truc du genre :


    static kmalloc_arena_t extended_ram = { 0 };
    extended_ram.name = "_extram";
    extended_ram.is_default = true;   // Je suis pas sûr de ça, j'imagine que mettre true veut dire utilisation exclusive de "_extram"
    extended_ram.start =   (void *)0xac200000;
    extended_ram.end = (void *)0xac500000 ;   // 0xac200000 + 0x00300000 correspondant à 3Mo

    kmalloc_init_arena(&extended_ram , true);
    kmalloc_add_arena(&extended_ram );


3/ ensuite malloc() va venir allouer systématiquement (et seulement) dans cette arène ou il va piocher à la fois dans "_uram" et dans "_extram" selon ce qui est disponible (Si je mets extended_ram.is_default à true bien entendu) ? Sinon si on veut avoir les deux arènes dispo, on met quoi (false ?) ?

4/ pour faire propre à la libération, avec un free(), ca va se débrouiller tout seul si j'ai bien compris, mais est ce qu'il vaut mieux tout réécrire à 0 après, histoire de remettre les choses en l'état (même si a priori l'OS ne fait strictement rien dans cette zone) ?


Je préfère demander avant, je préfère ne pas avoir un presse-papier en forme de CG-50

------------------------------------------------------------------
Le monde est dangereux à vivre ! Non pas tant à cause de ceux qui font le mal, mais à cause de ceux qui regardent et laissent faire.
-------------------------------------------------------------------
Albert Einstein
Mathématicien, Physicien, Scientifique (1879 - 1955)
Lephenixnoir Hors ligne Administrateur Points: 22762 Défis: 149 Message

Citer : Posté le 24/03/2022 14:42 | #


1/ Et supérieur à 3.00 aussi, puisque la Prizm n'a que 2 Mo.

2/ Pour le nom, pas d'underscore - c'est réservé à gint au cas où pour le futur.

La fonction d'allocation interne c'est kmalloc(size_t size, char const *arena_name). Si tu mets is_default=true alors malloc() ira piocher dedans, sinon non ; mettre false peut être utile si tu veux une arène à part, ou si par exemple y'a des arnaques et tu peux pas allouer n'importe quoi dedans (eg. PRAM0) auquel cas ça protège contre des bugs potentiels. Pour toi true c'est bien.

Pour les adresses mets plutôt 0x8c200000 et 0x8c500000, c'est pareil mais le cache sera actif, ce qui est trèèès avantageux.

3/ malloc() alloue dans toutes les arènes dont le is_default est à true, donc il allouera là et dans _uram (et en pratique il n'ira dans ton arène que si _uram est pleine).

4/ Oui free() se débrouille, et oui si tu n'utilises qu'une zone de zéros je conseille de remettre à zéro avant de quitter l'add-in. Mais pour l'instant y'a pas de façon simple/fiable de s'assurer qu'un bout de code tourne quand on quitte par le menu principal (via getkey() par exemple) donc c'est pas vraiment "possible".
Slyvtt Hors ligne Community Manager Points: 891 Défis: 0 Message

Citer : Posté le 24/03/2022 14:49 | #


Ok je pense que j'essaierai, j'ai un cas potentiel d'utilisation : actuellement 1 biome dans Outrun utilise grosso modo tout ce qui est dispo dans "_uram", donc pas trop possible d'avoir deux ou 3 biomes dans le même circuit, sauf à limiter les sprites. Avec ça, ça devrait devenir possible.

Ajouté le 28/03/2022 à 13:04 :
Aujourd'hui sur la Shoutbox "projects" se déroulait un échange sympathique et très constructif sur ce que l'on aimerait voir poindre dans gint et compagnie. Tout étant partie d'une boutade sur mes nombreuses demandes d'ajouts faite à Lephé ces derniers temps (et oui, il découvre que je suis un gros pénible ).

La discussion portait notamment sur la priorisation de trucs qui sont dans le tuyau et qui demandent pas mal de boulot pour être implémentées et testées, sachant que notre Lephé National (enfin International là maintenant tout de suite ) n'a que 2 bras et 2 jambes pour tout faire.
D'aucun diront que si Lephé n'était pas aussi bon, il pourrait faire comme moi et rajouter à cette limite une paire de fesses pour "coder avec le c_l" ce qui du coup rajouterai de la ressource... ... où en enlèverait encore à d'autres, bon ... bref, je m'égare ... )

Tout ça pour dire que ce serait vraiment sympa de savoir ce qui en terme de features à ajouter vous plairait le plus et avec quelle priorité.

Les features listées à ce stade sont :
- routines dimage / dsubimage optimisée avec un code assembleur tout neuf basé sur Azur
- le support des scanf() et fonctions dérivées (fscanf / vscanf() / sscanf() ...) pour avoir un support complet de <stdio.h>
- le support de l'USB 2.0
- le support de l'overclocking "simple" directement intégré à gint (sans faire un coup de Ftune/Ptune avant)

On a discuté juste de ça, mais il y a certainement d'autres idées dans la nature (qui a dit le support du son via le jack 2.5mm ou le support des communications serial via le même jack ... et en avant le multijoueur ...).

Merci pour vos retours (si possible argumentés)

Sly

------------------------------------------------------------------
Le monde est dangereux à vivre ! Non pas tant à cause de ceux qui font le mal, mais à cause de ceux qui regardent et laissent faire.
-------------------------------------------------------------------
Albert Einstein
Mathématicien, Physicien, Scientifique (1879 - 1955)
Kikoodx Hors ligne Labélisateur Points: 2979 Défis: 11 Message

Citer : Posté le 28/03/2022 13:31 | #


Comme je l'ai dit sur la shoutbox, je pense que l'ordre d'utilité objectif pour la plupart des créateurs est :

1/ images, je pense que personne ne dira le contraire
2/ CPG (overclock), quelque chose que tout le monde peut utiliser pour obtenir des performances prédectibles
3/ USB 2.0, ça devient un peu niche mais les applications sont réelles
4/ *scanf, il y a de nombreuses alternatives disponibles (les ato* par exemple)
mi lape ala.

J'suis un méga chômeur
Slyvtt Hors ligne Community Manager Points: 891 Défis: 0 Message

Citer : Posté le 29/03/2022 08:04 | #


Bon alors pour mon histoire de RAM trop limite, j'utilise une partie supplémentaire

J'ai deux cas :
- si OS>3.00 et <3.6 (En gros un Graph90+E ou CG-50) : je prends très très large et pique 3Mb dans la zone commençant à 0x8c200000
- sinon (CG10/20 ou vieux OS ?) je prends la VRAM secondaire soit seulement 170ko commençant à partir de l'adresse retournée par dgetvram() en lui passant le paramètre secondary


en code ça me donne ça :


    // Autorise plus de RAM
    char const *osv = (char*) 0x80020020;

    static kmalloc_arena_t extended_ram = { 0 };
    kmalloc_gint_stats_t *extram_stats;
    bool canWeAllocate3Mb = false;


    if(!strncmp(osv, "03.", 3) && osv[3] <= '6')
    {
        extended_ram.name = "extram";
        extended_ram.is_default = true;  
        extended_ram.start =   (void *)0x8c200000;
        extended_ram.end = (void *)0x8c500000 ;   // 0x8c200000 + 0x00300000 correspondant à 3Mo

        kmalloc_init_arena(&extended_ram , true);
        kmalloc_add_arena(&extended_ram );
        canWeAllocate3Mb = true;
    }
    else
    {
        extended_ram.name = "extram";
        extended_ram.is_default = true;  
        dgetvram( nullptr, &extended_ram.start );
        extended_ram.end = extended_ram.start + 2*396*224 ;   // secondary + taille VRAM 2*396*224bytes

        kmalloc_init_arena(&extended_ram , true);
        kmalloc_add_arena(&extended_ram );
        canWeAllocate3Mb = false;
    }


// Tout le programme de OutRun
// Avec allocations / désallocation et tout le toutim

    
// on remet des zéros dans les zones allouées
    if (canWeAllocate3Mb) memset( extended_ram.start, 0, 0x00300000 );
    else memset( extended_ram.start, 0, 2*396*224 );  // pas forcément utile pour la VRAM, mais bon



Par contre j'ai pas moyen de tester sur autre chose que la G90/CG50, donc si quelqu'un pouvait confirmer que c'est OK.

Sly

Ajouté le 29/03/2022 à 09:11 :
pour éviter de me faire gueuler dessus,

j'ai changé la ligne (le C++ aime pas les opérations sur les void)

        dgetvram( nullptr, &extended_ram.start );


en

        dgetvram( nullptr, (uint16_t**) &extended_ram.start );


Mais la question reste la même

------------------------------------------------------------------
Le monde est dangereux à vivre ! Non pas tant à cause de ceux qui font le mal, mais à cause de ceux qui regardent et laissent faire.
-------------------------------------------------------------------
Albert Einstein
Mathématicien, Physicien, Scientifique (1879 - 1955)
Lephenixnoir Hors ligne Administrateur Points: 22762 Défis: 149 Message

Citer : Posté le 29/03/2022 10:43 | #


- si OS>3.00 et <3.6 (En gros un Graph90+E ou CG-50) : je prends très très large et pique 3Mb dans la zone commençant à 0x8c200000

Les Prizm supportent aussi les OS ≥ 3.00, il faut aussi que tu testes le modèle. Tu peux tester gint[HWCALC] de <gint/hardware.h> pour différencier les modèles.

Ton code devrait être correct mais attention aux arnaques, si tu prends des uint16_t * alors le + est par paire d'octets pas par octets. Le plus safe ce serait ça :

uint16_t *vram1, *vram2;
dgetvram(&vram1, &vram2);

extended_ram.start = vram2;
extended_ram.end = (char *)vram2 + 396*224*2;

// Dans les deux cas:
memset(extended_ram.start, 0, (char *)extended_ram.end - (char *)extended_ram.start);

Ce n'est pas tout cependant, il faut aussi indiquer à gint de ne plus utiliser la deuxième VRAM :

dsetvram(vram1, vram1);

Dans le header il est marqué que mettre NULL sur le second pointeur élimine le triple buffering, mais à lire le code ce n'est pas le cas (!). Il faut croire que j'ai toujours testé avec deux pointeurs identiques. Ça revient au même.
Slyvtt Hors ligne Community Manager Points: 891 Défis: 0 Message

Citer : Posté le 29/03/2022 11:18 | #


Voila, du coup maintenant j'ai transformé en ça
- si CG50 : +3Mb
- si PRIZM : +170Ko de VRAM2
- sinon on sort


    /* allow more RAM */
    char const *osv = (char*) 0x80020020;

    static kmalloc_arena_t extended_ram = { 0 };
    kmalloc_gint_stats_t *extram_stats;
    bool canWeAllocate3Mb = false;


    if((!strncmp(osv, "03.", 3) && osv[3] <= '6') && gint[HWCALC] == HWCALC_FXCG50)
    {
        extended_ram.name = "extram";
        extended_ram.is_default = true;
        extended_ram.start =   (void *)0x8c200000;
        extended_ram.end = (void *)0x8c500000 ;

        kmalloc_init_arena(&extended_ram , true);
        kmalloc_add_arena(&extended_ram );
        canWeAllocate3Mb = true;
    }
    else if (gint[HWCALC] == HWCALC_PRIZM)
    {

        extended_ram.name = "extram";
        extended_ram.is_default = true;

        uint16_t *vram1, *vram2;
        dgetvram(&vram1, &vram2);
        dsetvram(vram1, vram1);

        extended_ram.start = vram2;
        extended_ram.end = (char *)vram2 + 396*224*2;

        kmalloc_init_arena(&extended_ram , true);
        kmalloc_add_arena(&extended_ram );
        canWeAllocate3Mb = false;

    }
    else abort();


et je libère à la fin avec le memset commun

------------------------------------------------------------------
Le monde est dangereux à vivre ! Non pas tant à cause de ceux qui font le mal, mais à cause de ceux qui regardent et laissent faire.
-------------------------------------------------------------------
Albert Einstein
Mathématicien, Physicien, Scientifique (1879 - 1955)
Lephenixnoir Hors ligne Administrateur Points: 22762 Défis: 149 Message

Citer : Posté le 29/03/2022 11:34 | #


Ton else marche aussi pour la Graph 90+E en version > 3.60, tu peux donc mettre else tout seul sans exiger que ce soit une Prizm.

Et même tu peux faire le coup de prendre la vram2 peu importe la machine pour être honnête, je soupçonne que tu gagnes pas grand-chose en FPS avec le triple buffering sur un programme "de la vraie vie". N'hésite pas à essayer pour contribuer à la Science™.

Enfin, tu peux aussi récupérer le buffer de SaveDisp() qui est une autre VRAM de l'OS de taille 384x216x2 ; je m'en sers dans CGDoom. Je déconseille d'utiliser la vraie VRAM OS puisqu'elle se fait écraser dès que tu reviens au menu :P
Lephenixnoir Hors ligne Administrateur Points: 22762 Défis: 149 Message

Citer : Posté le 29/03/2022 12:05 | # | Fichier joint


Slyvtt a écrit :
Aujourd'hui sur la Shoutbox "projects" se déroulait un échange sympathique et très constructif sur ce que l'on aimerait voir poindre dans gint et compagnie. Tout étant partie d'une boutade sur mes nombreuses demandes d'ajouts faite à Lephé ces derniers temps (et oui, il découvre que je suis un gros pénible ).

La discussion portait notamment sur la priorisation de trucs qui sont dans le tuyau et qui demandent pas mal de boulot pour être implémentées et testées, sachant que notre Lephé National (enfin International là maintenant tout de suite ) n'a que 2 bras et 2 jambes pour tout faire.
D'aucun diront que si Lephé n'était pas aussi bon, il pourrait faire comme moi et rajouter à cette limite une paire de fesses pour "coder avec le c_l" ce qui du coup rajouterai de la ressource... ... où en enlèverait encore à d'autres, bon ... bref, je m'égare ... )

Tout ça pour dire que ce serait vraiment sympa de savoir ce qui en terme de features à ajouter vous plairait le plus et avec quelle priorité.

Les features listées à ce stade sont :
- routines dimage / dsubimage optimisée avec un code assembleur tout neuf basé sur Azur
- le support des scanf() et fonctions dérivées (fscanf / vscanf() / sscanf() ...) pour avoir un support complet de <stdio.h>
- le support de l'USB 2.0
- le support de l'overclocking "simple" directement intégré à gint (sans faire un coup de Ftune/Ptune avant)

On a discuté juste de ça, mais il y a certainement d'autres idées dans la nature (qui a dit le support du son via le jack 2.5mm ou le support des communications serial via le même jack ... et en avant le multijoueur ...).

Merci pour vos retours (si possible argumentés)

Sly

Kikoodx a écrit :
Comme je l'ai dit sur la shoutbox, je pense que l'ordre d'utilité objectif pour la plupart des créateurs est :

1/ images, je pense que personne ne dira le contraire
2/ CPG (overclock), quelque chose que tout le monde peut utiliser pour obtenir des performances prédectibles
3/ USB 2.0, ça devient un peu niche mais les applications sont réelles
4/ *scanf, il y a de nombreuses alternatives disponibles (les ato* par exemple)

Intéressant tout ça ! Effectivement je vois que tout le monde n'a pas les mêmes priorités. Pour moi, la TODO list ressemble actuellement à ça :

  1. Optimisation/extension de la gestion d'images. Vous avez vu le speech jusque-là, je ne sais pas ce qu'on peut gagner exactement mais ça me semble important parce qu'on a sous les yeux des projets qui sont soit à ras de poil des 30 FPS (Rogue Life) soit un peu inconsistants à cause des images (OutRun).
  2. Overclock. C'est pas forcément dur à faire et même si en développeurs bien élevés on préfère optimiser d'abord c'est casse-pieds de pas l'avoir disponible intégré.
  3. Fignoler le driver USB, qui est actuellement un peu laissé en plan depuis que fxlink permet de faire les screenshots et captures vidéo. Ça marche, mais c'est pas hyper véloce, on peut communiquer que dans un sens, et y'a que les pipes bulk pour l'instant (pas d'interruption/isochrone en particulier).

Le reste, sans ordre trop particulier :

  • La gestion de scanf(), qui est pas fondamentalement difficile mais qu'il faut faire proprement. S'il y a bien un truc que quelqu'un pourrait contribuer sans être un gourou complet, ce serait ça.
  • Regarder du côté serial (plus facile que l'USB) pour la communication inter-calculatrices (multijoueur) et ultimement l'audio (libsnd de TSWilliamson).
  • Ajouter des options pour recompiler des add-ins mono sur la Graph 90+E avec un upscaling automatique. En gros un niveau de compatibilité pour avoir les projets mono gratuitement sur Graph 90+E.
  • Support des fichiers en RAM pour pouvoir utiliser l'API haut-niveau sur tous les modèles et éviter la lenteur de BFile à l'écriture quand on a assez de RAM.
  • Des descripteurs de fichiers capables de communiquer par USB/serial pour que le fs soit complet.
Slyvtt Hors ligne Community Manager Points: 891 Défis: 0 Message

Citer : Posté le 29/03/2022 12:22 | #


Bon j'ai un truc que je ne comprends pas, en fait je sais pas où c'est le mieux pour poser cette question, je mets ici car c'est globalement du "gint"

Pour mes fichiers de save, j'ai une structure :


struct BestRanking
{
    char playerName[3] = { 'A', 'B', 'C' }; // name of the player
    uint32_t bestScore = 0x00112233;
    uint32_t bestTime = 0xAABBCCDD;
};


j'ai qq part une variable


BestRanking HallOfFame[10][5];          // Table of score : 10 levels * 5 best scores


et je veux stocker ça dans un fichier (je passe les détails de gint_world_switch(), ça c'est OK)

la sauvegarde se fait par la fonction saveprogress :


void saveprogress( void )
{
    sizeoffile = sizeof( BestRanking ) * 10 * 5;
    unsigned int sizecopy = sizeoffile;

    unsigned char* buffer;
    buffer = (unsigned char*) malloc( sizeoffile );


    for( int k=0; k<10; k++ )
    {
        for( int l=0; l<5; l++ )
        {
            int base = (k*5+l)*sizeof( BestRanking );

            buffer[ base      ] = HallOfFame[k][l].playerName[0];
            buffer[ base +  1 ] = HallOfFame[k][l].playerName[1];
            buffer[ base +  2 ] = HallOfFame[k][l].playerName[2];

            buffer[ base +  3 ] = (uint8_t) ((HallOfFame[k][l].bestScore & 0xFF000000) >> 24);
            buffer[ base +  4 ] = (uint8_t) ((HallOfFame[k][l].bestScore & 0x00FF0000) >> 16);
            buffer[ base +  5 ] = (uint8_t) ((HallOfFame[k][l].bestScore & 0x0000FF00) >> 8);
            buffer[ base +  6 ] = (uint8_t) ((HallOfFame[k][l].bestScore & 0x000000FF));

            buffer[ base +  7 ] = (uint8_t) ((HallOfFame[k][l].bestTime & 0xFF000000) >> 24);
            buffer[ base +  8 ] = (uint8_t) ((HallOfFame[k][l].bestTime & 0x00FF0000) >> 16);
            buffer[ base +  9 ] = (uint8_t) ((HallOfFame[k][l].bestTime & 0x0000FF00) >> 8);
            buffer[ base +  10 ] = (uint8_t) ((HallOfFame[k][l].bestTime & 0x000000FF));
        }
    }


    int file;

    if (saveexist) BFile_Remove( filepath );
    BFile_Create( filepath, BFile_File, &sizecopy );

    file = BFile_Open( filepath, BFile_WriteOnly );
    BFile_Write( file, buffer, sizeoffile );

    BFile_Close( file );

    free( buffer );
}



Problème : quand je regarde le fichier, je m'attends à avoir la séquence (en héxa) qui se répète 50 fois


41 42 43 00 11 22 33 AA BB CC DD 41 42 43 00 11 22 33 AA BB CC DD  etc ...


mais en fait j'ai qui se répète 50 fois (en fait la séquence fait un byte de plus avec ce byte toujours égal à 0x55)


41 42 43 00 11 22 33 AA BB CC DD 55 41 42 43 00 11 22 33 AA BB CC DD 55 etc ...


et je comprends pas d'où vient ce 0x55 ...

Je soupçonnais sizeof() de retourner 1 byte en trop, mais alors pourquoi toujours cette valeur à 0x55, ça devrait être aléatoire (en gros en fonction de ce qui trainait en mémoire à l'emplacement de buffer;

Bref je comprends pas le schmilblick. Une idée ?

------------------------------------------------------------------
Le monde est dangereux à vivre ! Non pas tant à cause de ceux qui font le mal, mais à cause de ceux qui regardent et laissent faire.
-------------------------------------------------------------------
Albert Einstein
Mathématicien, Physicien, Scientifique (1879 - 1955)
Lephenixnoir Hors ligne Administrateur Points: 22762 Défis: 149 Message

Citer : Posté le 29/03/2022 12:32 | #


Ouf, tu te casses pas mal la tête.

sizeof() ne retourne pas "un octet de trop" : ta structure a un octet de padding entre playerName et bestScore :

struct BestRanking
{
    char playerName[3] = { 'A', 'B', 'C' };
    // (padding: 1 octet)
    uint32_t bestScore = 0x00112233;
    uint32_t bestTime = 0xAABBCCDD;
};

C'est parce qu'un uint32_t ne peut être en mémoire qu'à une adresse multiple de 4, et donc pour garantir ça (1) le compilateur impose un alignement de 4 à la structure (ce qui veut dire qu'elle même ne sera créée qu'à des adresses multiples de 4) et (2) il ajoute un octet de padding pour que le uint32_t soit à une position multiple de 4 à l'intérieur de la structure.

L'octet de padding vaut 0x55 parce que c'est à cette valeur que la RAM est initialisée quand ton add-in démarre et que manifestement malloc() t'a attribué de la mémoire qui n'avait pas encore été utilisée depuis le début de l'exécution.

L'approche générale c'est que tu t'en fous de cet octet parce que ton code ne l'utilise jamais. T'as qu'à sauvegarder directement le tableau sous sa forme binaire :

void saveprogress( void )
{
    int file;

    if (saveexist) BFile_Remove( filepath );
    BFile_Create( filepath, BFile_File, &sizecopy );

    file = BFile_Open( filepath, BFile_WriteOnly );
    BFile_Write( file, (char *)HallOfFame, sizeof HallOfFame );

    BFile_Close( file );
}

Si tu veux absolument fixer les octets à 0 tu peux toujours déclarer l'octet de padding à la main (rajouter un char padding) et faire HallOfFame[i][j].padding = 0 avant de sauvegarder et/ou après le chargement.

Note : tu utilises sérieusement BFile dans OutRun ? :x
Slyvtt Hors ligne Community Manager Points: 891 Défis: 0 Message

Citer : Posté le 29/03/2022 12:54 | #


Ok merci, pour le coup j'étais pas près de trouver, maintenant que tu le dis, c'est hyper logique;

en fait j'utilise les fonctions de MagicLight qui étaient en BFile pour faire vite, mais je compte passer sur les "nouvelles fonctions" de gint <stdio.h> rapidos.

Oui ton code est bien radical comme j'aime, je prends le "déversage" bourrin du tableau

Ajouté le 29/03/2022 à 13:27 :
Bon comme tu m'as piqué au vif, je refais les fonctions à la mode <stdio.h>
Tu as raison, je fais le fainéant, c'est pas bon tout ça.

Par contre avec BFile, il faut utiliser un nom de fichier du style

static const uint16_t *filepath= u"\\\\fls0\\OutRun.sav";


dans le cas de fopen(), c'est quoi la convention ?

Est-ce :

static const char *filepath= "OutRun.sav";


ou faut spécifier le chemin ?

------------------------------------------------------------------
Le monde est dangereux à vivre ! Non pas tant à cause de ceux qui font le mal, mais à cause de ceux qui regardent et laissent faire.
-------------------------------------------------------------------
Albert Einstein
Mathématicien, Physicien, Scientifique (1879 - 1955)
Lephenixnoir Hors ligne Administrateur Points: 22762 Défis: 149 Message

Citer : Posté le 29/03/2022 13:37 | #


"OutRun.sav" ou "/OutRun.sav" pour la mémoire de stockage. Il y a des millions d'exemples dans fxlibctest si tu doutes
Slyvtt Hors ligne Community Manager Points: 891 Défis: 0 Message

Citer : Posté le 29/03/2022 13:40 | #


Merci, c'est tout bon.

Bon, suis-je le premier à utiliser le fopen() / fread() / fwrite() après toi ?

Pour info tout est réécrit, en fait je sais pas pourquoi je m'emm...dait avec BFile, 10 minutes et c'est tout beau avec stdio

Les sales habitudes ont parfois la vie dure ...

------------------------------------------------------------------
Le monde est dangereux à vivre ! Non pas tant à cause de ceux qui font le mal, mais à cause de ceux qui regardent et laissent faire.
-------------------------------------------------------------------
Albert Einstein
Mathématicien, Physicien, Scientifique (1879 - 1955)
Lephenixnoir Hors ligne Administrateur Points: 22762 Défis: 149 Message

Citer : Posté le 29/03/2022 13:41 | #


Bon, suis-je le premier à utiliser le fopen() / fread() / fwrite() après toi ?

Bonne question. C'est pas sur master depuis longtemps et Rogue Life n'a pas encore de sauvegarde, donc peut-être.

Oui c'est plus clean comme ça surtout quand t'as plusieurs plateformes à gérer. (Maintenant que j'y pense attention à l'endianness avec ma version.)
Slyvtt Hors ligne Community Manager Points: 891 Défis: 0 Message

Citer : Posté le 29/03/2022 13:43 | #


a priori je devrais pas avoir de soucis avec l'endianness, car j' "écris bourrin" et je "lis bourrin"


void saveprogress( void )
{
    sizeoffile = sizeof( HallOfFame );

    FILE *file = fopen( filepath, "w" );
    fwrite( HallOfFame, sizeoffile, 1, file );

    fclose( file );
}


void loadprogress( void )
{
    sizeoffile = sizeof( HallOfFame );

    FILE *file = fopen( filepath, "r" );
    fread( HallOfFame, sizeoffile, 1, file );

    fclose( file );
}


donc ca doit recoller à l'identique sans m'en préoccuper

------------------------------------------------------------------
Le monde est dangereux à vivre ! Non pas tant à cause de ceux qui font le mal, mais à cause de ceux qui regardent et laissent faire.
-------------------------------------------------------------------
Albert Einstein
Mathématicien, Physicien, Scientifique (1879 - 1955)
Lephenixnoir Hors ligne Administrateur Points: 22762 Défis: 149 Message

Citer : Posté le 29/03/2022 13:44 | #


Oui si tu lis sur la même machine ça marche. Je voulais dire que si tu fais ça une sauvegarde sur Casio et une sauvegarde sur Nspire (c'est little-endian une Nspire right?) ne seraient pas interchangeables.
Précédente 1, 2, 3 ··· 10 ··· 20 ··· 30 ··· 40 ··· 50 ··· 60, 61, 62, 63, 64, 65, 66 ··· 68, 69, 70 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 v42 © créé par Neuronix et Muelsaco 2004 - 2022 | Il y a 51 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