addin en 2 parties
Posté le 19/12/2021 18:34
J'ai bien avance sur la creation d'un addin en 2 parties.
Voici un fichier testv.cc qui cree un vecteur (avec la uSTL) puis appelle une fonction declaree dans le fichier r8c2.h ci-dessous
#define std ustl
#include <vector>
void r8c2_print_vector(const std::vector<int> & v);
La fonction r8c2_print_vector affiche le vecteur, elle est implementee dans r82c.cc ci-dessous et sera ulterieurement stockee en RAM.
#include <fxcg/keyboard.h>
#include <fxcg/display.h>
#include <fxcg/file.h>
#include <fxcg/keyboard.h>
#include <fxcg/system.h>
#include <fxcg/misc.h>
#include <fxcg/app.h>
#include <fxcg/serial.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define std ustl
#include <vector>
#include <string>
using namespace ustl;
void r8c2_print_vector(const vector<int> & v){
for (int j=0;j<v.size();++j){
printf("%d\n",v[j]);
}
}
Et le fichier testv.cc avec le code de chargement de la fonction en ram et avec les commandes de compilation (a modifier selon installation)
// -*- compile-command: "sh3eb-elf-g++ -g -mb -m4a-nofpu -mhitachi -std=c++11 -fpermissive -fno-use-cxa-atexit -fno-strict-aliasing -fno-rtti -fno-exceptions -I. -I/home/parisse/casiolocal/include/ustl -c r8c2.cc -o r8c2.o && sh3eb-elf-g++ -g -mb -m4a-nofpu -mhitachi -std=c++11 -fpermissive -fno-use-cxa-atexit -fno-strict-aliasing -fno-rtti -fno-exceptions -I. -I/home/parisse/casiolocal/include/ustl -c testv.cc -o testv.o && sh3eb-elf-g++ testv.o r8c2.o -g -static -nostdlib -Taddinram.ld -Wl,--gc-sections,--print-memory-usage -L. -L/home/parisse/casiolocal/lib -Wl,--start-group -ltommath -lustl -lm -lc -lgcc -Wl,--end-group -o testv.elf && sh3eb-elf-objdump -C -t testv.elf | sort > dump && sh3eb-elf-objcopy -R .comment -R .bss -R .rominram -O binary testv.elf testv.bin && sh3eb-elf-objcopy -j .rominram -O binary testv.elf testv.8c2 && mkg3a -n basic:Testv -n internal:TESTV -V 0.0.0 testv.bin testv.g3a && /bin/cp testv.g3a testv.8c2 ~/.wine/drive_c" -*-
// debug with sh3eb-elf-gdb -i=mi -ex "target remote localhost:31188" testv.elf
#include <fxcg/keyboard.h>
#include <fxcg/display.h>
#include <fxcg/file.h>
#include <fxcg/keyboard.h>
#include <fxcg/system.h>
#include <fxcg/misc.h>
#include <fxcg/app.h>
#include <fxcg/serial.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define std ustl
#include <vector>
#include <string>
#include "r8c2.h"
using namespace ustl;
unsigned ram2Msize=0;
char ram_filename[]="\\\\fls0\\testv.8c2";
bool chk_ram2M(const char * filename,bool reload){
unsigned ram2Maddr=0x8c200000, ram2Mendaddr=ram2Maddr+ram2Msize;
unsigned * ram2M=(unsigned *) ram2Maddr,*ram2Mend=(unsigned *) ram2Mendaddr;
const unsigned chkinit=0x12345678;
unsigned chk=chkinit,*ptr;
const unsigned inc=1;
if (ram2Msize){
// if it's too slow, modify incr
for (ptr=ram2M;ptr<ram2Mend;ptr+=inc){
chk ^= (*ptr);
}
if (chk==*ptr)
return true; // proba to return true with corruption is tiny, about 1/4e9
if (!reload)
return false;
}
// load khicas overlay
int nchar=strlen(filename);
unsigned short pFile[nchar+1];
Bfile_StrToName_ncpy(pFile, (const unsigned char *)filename, strlen(filename)+1);
int hf = Bfile_OpenFile_OS(pFile, READWRITE); // Get handle
if (hf < 0)
return false; // nothing to load
int size=Bfile_GetFileSize_OS(hf);
if (size>2*1024*1024-4 || Bfile_ReadFile_OS(hf,ram2M,size,0)!=size){
Bfile_CloseFile_OS(hf);
return false;
}
ram2Msize=size;
ram2Mendaddr=ram2Maddr+ram2Msize;
ram2Mend=(unsigned *) ram2Mendaddr;
Bfile_CloseFile_OS(hf);
// compute chksum
chk=chkinit;
for (ptr=ram2M;ptr<ram2Mend;ptr+=inc){
chk ^= (*ptr);
}
*ptr=chk; // save chksum
return true;
}
int main(){
if (!chk_ram2M(ram_filename,true)){
printf("%s%s\n","Unable to load ",ram_filename);
int key;
for(;;)
GetKey(&key);
return 0;
}
if (!chk_ram2M(ram_filename,false)){
printf("%s\n"," ",ram_filename);
int key;
for(;;)
GetKey(&key);
return 0;
}
int i=4321;
string s("Coucou");
//int * jptr=new int;
char * buf=(char *)malloc(256);
char * buf2=new char[256];
printf("Hello\n");
// printf("malloc %d\n new %d\nstack %d",unsigned(buf)-0x88100000,unsigned(buf2)-0x8100000,unsigned(&buf)-0x88100000);
strcpy(buf,"!!Hello abcdefghijklmnopqrstuvwxyz 123456789");
strcpy(buf2,"!!Hello2 abcdefghijklmnopqrstuvwxyz 123456789");
printf("%s\n",buf);
printf("%s\n",buf2);
// char buf[]="!!Hello abcdefghijklmnopqrstuvwxyz 123456789";
if (1){
std::vector<int> v(3);
for (int j=0;j<v.size();++j){
v[j]=j*j;
}
r8c2_print_vector(v);
i=unsigned (&v[2])-0x08100000;
// v.clear();
}
int key;
for(;;)
GetKey(&key);
return 0;
}
Voici le fichier loader addinram.ld pour stocker en RAM le contenu du fichier objet r8c2.o, en creant un fichier elf qu'on divisera ensuite en 2 fichiers, le fichier d'addin g3a et un fichier d'extension 8c2 qui sera charge par l'addin.
OUTPUT_FORMAT(elf32-sh)
OUTPUT_ARCH(sh3)
ENTRY(initialize)
MEMORY
{
rom : o = 0x00300000, l = 2M
ram : o = 0x08100000, l = 64k /* pretty safe guess */
r8c2 (rx) : o = 0x8c200000, l = 3M-4
}
SECTIONS
{
/* 2nd part of code at 0x8c20000 */
.rominram : {
r8c2.o(.text)
r8c2.o (.text.*)
r8c2.o (.rodata)
r8c2.o (.rodata.*)
} > r8c2
/* Code, in ROM */
.text : {
*(.pretext) /* init stuff */
*(.text)
*(.text.*)
} > rom
/* Read-only data, in ROM */
.rodata : {
*(.rodata)
*(.rodata.*)
} > rom
/* RW initialized data, VMA in RAM but LMA in ROM */
.data : ALIGN(4) {
_datald = LOADADDR(.data) ;
_sdata = . ;
*(.data)
*(.data.*);
_edata = . ;
} >ram AT>rom
/* Uninitialized data (fill with 0), in RAM */
.bss ALIGN(4) : ALIGN(4) {
_bbss = . ;
*(.bss)
*(.bss*)
*(COMMON)
_ebss = . ;
/* Extra unused static space */
_sextra = ALIGN(32);
_eextra = ORIGIN(ram) + LENGTH(ram);
} >ram
}
J'ai fait ce petit test en preparation d'une mise a jour majeure de KhiCAS qui pourrait utiliser beaucoup plus d'espace, pour recueillir l'avis des experts sur le meilleur modele a choisir (ceux qui ont lu jusqu'ici sont probablement des experts...).
Mais d'abord j'ai un gros probleme avec l'emulateur, impossible d'ecrire dans la zone memoire 0x8c20000, est-ce qu'il y a une autre adresse a tester? Ou peut-etre que la zone de RAM n'est pas supportee par l'emulateur, ce qui me compliquerait le travail de mise au point, mais pas que, ca rendrait impossible l'execution a l'emulateur pour une demo dans un but pedagogique,
Ensuite, mon idee serait d'avoir le moteur de calcul de giac en RAM jusqu'a une taille de 3M (ca gagne 1M par rapport aux 2M actuels) et de garder toute l'UI (ainsi bien sur que le chargeur en RAM) dans la partie addin en ROM. On peut meme envisager a moyen terme de rajouter mon implementation de MicroPython avec ma collection de modules pour avoir la meme chose que sur Numworks/Nspire.
J'en profite pour signaler que j'ai mis a jour la section developpement de la doc de KhiCAS avec une description assez detaillee je pense de
comment utiliser le debugger mis au point par redoste, (cf.
ce fil)
Citer : Posté le 19/12/2021 18:54 | #
Sur l'émulateur, mon loader ELF perso m'indique que la RAM physique se trouve à 0x08000000, que la RAM utilisateur commence à 0x08160000 et que cette zone a seulement 5 Mo d'espace disponible. Ce qui est sensiblement différent que sur la vraie machine.
Voici le code que j'ai pondu si jamais ça peut vous aider:
#include "vxBoot/hardware.h"
#include <gint/mmu.h>
/* __ram_get_size() : try to determine the RAM size */
static ptrdiff_t __ram_get_size(uintptr_t start_ram)
{
volatile uint32_t *ptr = (void*)start_ram;
volatile uint32_t *tst = (void*)start_ram;
uint32_t backup;
//TODO: use UTLB page size information to walk through each pages
while (1) {
ptr = &ptr[4096];
backup = ptr[0];
ptr[0] = 0xdeadbeef;
if (ptr[0] != 0xdeadbeef || ptr[0] == tst[0]) break;
ptr[0] = backup;
}
ptr[0] = backup;
return ((ptrdiff_t)((uintptr_t)ptr - (uintptr_t)tst));
}
/* hardware_get_info() : get hardware information */
int hardware_get_info(struct hwinfo * const hwinfo)
{
if (hwinfo == NULL)
return (-1);
uintptr_t uram = mmu_translate(0x08100000, NULL);
hwinfo->ram.physical.origin_addr = uram & 0xff000000;
hwinfo->ram.physical.user_addr = uram;
hwinfo->ram.size = __ram_get_size(uram | 0xa0000000);
hwinfo->ram.available = hwinfo->ram.size - (uram & 0x00ffffff);
return (0);
}
Citer : Posté le 19/12/2021 18:55 | #
J'ai pas encore lu le thread mais je confirme que sur l'émulateur la RAM commence à 0x88000000. Sur la Prizm aussi c'est ça, il n'y a que la Graph 90+E et fx-CG 50 physiques où la RAM commence à 0x8c000000. Parisse, j'avais mentionné ça par mail notamment, en réponse à la question sur la virtualisation.
Citer : Posté le 19/12/2021 19:46 | #
Je suis perdu, je dois utiliser quoi comme adresse pour que ca marche sur l'emulateur ET sur les calculatrices physiques? J'ai essaye 88200000 et ac200000 mais ca n'affiche rien a l'emulateur, et le 1er crashe sur la 90.
Citer : Posté le 19/12/2021 19:52 | #
La RAM ne se trouve absolument pas à la même place entre émulateur (0x88000000) et la calculatrice (0x8c000000)
Citer : Posté le 19/12/2021 20:25 | #
La RAM ne se trouve absolument pas à la même place entre émulateur (0x88000000) et la calculatrice (0x8c000000)
Yatis, Lephé,
y a t il un moyen de savoir depuis un addins si on tourne sur l'émulateur ou la machine physique ? Un code à lire qq part ou un test à effectuer pour discriminer les deux architectures ?
Si oui, Bernard pourrait peut être effectuer ce test afin de choisir la bonne adresse "au vol".
Citer : Posté le 19/12/2021 20:34 | #
Oui et c'est relativement simple, il suffit de lire le registre "Product Version" (0xFF000044), s'il retourne 0, vous êtes dans un émulateur. Voilà un fix que j'ai récemment fait pour un projet qui utilise cette technique :
/* workaround to disable cursor blink only in FXCG-Manager (emulator) */
if (*(volatile uint32_t *)0xff000044 != 0x00000000) {
terminal.private.timer.id = timer_configure(
TIMER_ANY,
250000,
GINT_CALL(terminal_cursor_handler)
);
if (terminal.private.timer.id < 0) {
terminal_close();
return (-1);
}
}
Citer : Posté le 19/12/2021 20:34 | #
Non, car les adresses d'appel sont codes en dur, il faudrait 2 versions de l'addin. Mais la je n'arrive pas a le faire marcher l'addin test sur l'emulateur meme avec l'adresse 0x88... Je suis en train d'upgrader l'emulateur au cas ou...
Citer : Posté le 19/12/2021 20:38 | #
Je suis perdu, je dois utiliser quoi comme adresse pour que ca marche sur l'emulateur ET sur les calculatrices physiques? J'ai essaye 88200000 et ac200000 mais ca n'affiche rien a l'emulateur, et le 1er crashe sur la 90.
Comme Yatis l'a dit, aucune ! Il faut faire la disjonction de cas émulateur vs. physique à la main. Bienvenue dans le monde des considérations bas-niveau. :3
Yatis, Lephé,
y a t il un moyen de savoir depuis un addins si on tourne sur l'émulateur ou la machine physique ? Un code à lire qq part ou un test à effectuer pour discriminer les deux architectures ?
Si oui, Bernard pourrait peut être effectuer ce test afin de choisir la bonne adresse "au vol".
La version de Yatis est très bien, on peut aussi regarder le numéro de série :
bool is_emulator = *(volatile uint32_t *)0xff000044 == 0x00000000;
// Version numéro de série
bool is_emulator = !memcmp((void *)0xa001ffd0, "\xff\xff\xff\xff\xff\xff\xff\xff", 8);
Pour information, dans gint :
gint[HWCALC] == HWCALC_PRIZM;
gint[HWCALC] == HWCALC_FXCG50;
gint[HWCALC] == HWCALC_FXCG_MANAGER;
Ajouté le 19/12/2021 à 20:42 :
r8c2.o(.text)
r8c2.o (.text.*)
r8c2.o (.rodata)
r8c2.o (.rodata.*)
} > r8c2
Bien joué, c'est pas mal du tout. Ce bloc devrait sans doute être après .text et se terminer par AT>rom >r8c2, juste par souci de faire les choses proprement (même si ça peut marcher sans c'est un peu brutal tel quel).
Citer : Posté le 19/12/2021 20:59 | #
Ok, ca marche sur l'emulateur avec 0x88.... Mais il va falloir compiler 2 versions de la partie ram, en utilisant le test emulateur, on devrait pouvoir charger automatiquement le bon.
Citer : Posté le 19/12/2021 21:31 | #
Aha oui j'avais pas pensé à ça c'est malin. Là t'as vraiment besoin de l'adresse physique au link donc tu peux pas échapper à la double version x)
Citer : Posté le 20/12/2021 09:18 | #
Bon en fait il faut aussi 2 versions de la partie en ROM, sinon les appels vers la RAM ne se font pas a la bonne adresse. Donc 2 addins et 2 parties RAM. Je vais rajouter emu dans le nom de l'addin pour l'emulateur, et pour les parties ram je vais mettre les extensions .882 et .ac2. Du coup, je cree 2 scripts ld aussi. J'ai fait une archive de l'addin test avec partie en RAM pour ceux qui voudraient faire comme moi (mais ca doit pas etre frequent d'avoir besoin de plus de 2M).
https://www-fourier.univ-grenoble-alpes.fr/~parisse/casio/addinram.tgz
Du coup on a jusque 5M de disponible sur la 90, est-ce vrai aussi sur tous les modeles Prizm?
Il me reste encore a gerer la RAM et le OFF/ON. Actuellement j'ai des problemes de corruption, qui n'ont pas l'air de se produire si on tape sur la touche MENU. Est-ce qu'on sait simuler l'appui sur une touche?
Ajouté le 20/12/2021 à 09:53 :
Ca a l'air possible de reinjecter un code vers GetKey avec int Keyboard_PutKeycode(int x, int y, int keycode), du coup je vais essayer de gerer OFF/ON en remplacant tous les appels actuels a GetKey par une fonction de l'addin, qui gererait shift ON par l'affichage d'un message demandant de confirmer par shift-ON a nouveau, injecter MENU dans le buffer de GetKey et appeler GetKey. A moins qu'il soit possible d'injecter plusieurs code clavier dans le buffer GetKey.
Citer : Posté le 20/12/2021 09:58 | #
Ton système avec les doubles versions a l'air correct.
Non, juste la Graph 90+E (fx-CG 50) ! J'espère que tu ne le découvres pas maintenant, je m'en voudrais de pas avoir été clair et de t'avoir fait miroiter du support universel alors que c'est clairement plus difficile. :x
Oui Keyboard_PutKeyCode() est la bonne option. Je crois que tu peux l'appeler plusieurs fois de suite.
Citer : Posté le 20/12/2021 10:57 | #
Donc ca veut dire qu'il faut maintenir une version de KhiCAS en anglais proche de l'actuelle, sans partie ram, pour les fxcg10 et 20. Je vais regarder le clavier avant de me lancer dans la version avec ram...
Ajouté le 20/12/2021 à 11:01 :
En fait, il y a peut-etre quand meme de la memoire sur les Prizm? Moins de 3M mais un petit quelque chose?
Citer : Posté le 20/12/2021 11:13 | #
Ça m'étonnerait la ref hardware de la puce dit 2M.
Citer : Posté le 20/12/2021 22:07 | #
Pas de chance
Keyboard_PutKeyCode(-1,-1,KEY_CTRL_MENU); GetKey(keyptr);
n'est pas equivalent a une pression sur la touche MENU.
Le MENU n'est pas affiche, et GetKey renvoie tout de suite le code clavier de la touche MENU.
Citer : Posté le 20/12/2021 22:12 | #
Oui c'est documenté dans le SimLo, regarde la description de 0x0248 (c'est pour les Graph mono mais ça doit s'appliquer). Pour obtenir les comportements spéciaux type retour au menu il faut injecter directement les codes matriciels, ie. utiliser les deux premiers arguments et pas le troisième.
Il y a aussi toute une histoire avec injecter le code après avoir lancé GetKey(), avec un timer. Tu peux commencer par tester sans le timer, de mémoire ça marche la plupart du temps. Et ensuite tu peux rajouter le timer en prenant son code, qui marche très bien, ou bien t'inspirer de celui de gint pour la méthode.
Citer : Posté le 20/12/2021 23:06 | #
Je me mefie de l'utilisation de timer, d'abord c'est un peu plus complexe a mettre en oeuvre et ensuite ca risque de creer des interactions nefastes. Je vais utiliser GetKeyWait OS, en autorisant MENU. Et tester si PowerOff() pose des problemes de corruption memoire, sinon shift-ON affichera un message de taper MENU pour pouvoir eteindre en toute securite.
Citer : Posté le 20/12/2021 23:15 | #
N'oublie pas que si tu autorises le retour au menu dans GetKeyWait() l'utilisateur peut toujours éteindre la calculatrice dans le menu et revenir ensuite dans l'add-in
Citer : Posté le 21/12/2021 07:32 | #
Oui, c'est ce que je souhaite faire. Il me semble qu'il n'y a pas de corruption memoire dans ce cas la.
Ajouté le 21/12/2021 à 10:22 :
Bon, je vais utiliser le timer pour simuler la touche menu quand il y a un timeout. Ce qui devrait permettre de gerer correctement l'alimentation...
Citer : Posté le 21/12/2021 10:24 | #
On est d'accord que pour la corruption mémoire on n'a pas encore d'explication solide right? J'ai testé encore de mon côté, la mémoire on-chip (XRAM/YRAM/ILRAM) n'est pas toujours préservée durant une extinction (même dans le menu principal), la RAM utilisateur si. J'aimerais bien creuser encore...