Idée de projet - add-in pour debug
Posté le 28/05/2018 20:01
salut;
Je sais pas si vous programmez sur Casio mais si c’est le cas vous devez savoir à quel point programmer dessus c’est pratique: on programme un jeu, on le compile, on vérifie sur l’Émulateur si tout fonctionne, si tout fonctionne on le transfère sur la calto puis on peut transporter son jeu de partout.
Problématique
Si vous programmez un gros jeu il est possible que des petits bugs apparaissent APRÈS avoir compilé qui font planter la Casio.
Et pourtant le jeu fonctionnait sur l’Émulateur sauf que l’Émulateur ne sera jamais comme la console.
Et c’est là que ça devient méga chiant parce que, modifier un octet, compiler, connecter la calto et voir que ça ne fonctionne pas...on vient de perdre 5 minutes de notre vie à brasser de l’aire.
De plus si vos faites des jeux en multijoueur on ne peut pas vérifier via l’Émulateur, il faut donc tout se taper et c’est...long, trop long.
Idée
C’est pourquoi une idée m’est venue alors que j’écrivais la librairie “mySerail.h”.
Pourquoi ne pas créer un add-in qui récupère les données via le port USB puis exécute le jeu à la fin du transfère ?
Pour résumer éviter d’aller dans “LINK”, et attendre que la calto se connecte à l’ordi, attendre que l’add-in soit transféré.
En gros avoir un add-in de debug qui nous permet de ne pas avoir à toucher à la console pour tester ses programmes.
Théorie
Pour ça il nous faut une connexion directe entre la Casio et le PC...bon ya un port USB donc c’est rapide.
L’add-in se met directement en ‘Receive’ (elle attend un fichier).
Ensuite on vérifie s'il y a assez de place dans la RAM.
Si ya assez de place, on met tout le programme dans la RAM, puis on l’exécute.
(c’est tellement simple à dire^^)
Pourquoi la RAM ?
C’est un add-in de debug je vous rappelle donc on s’en fout que le programme soit stocké dans la ROM.
De plus la RAM est rapide à écrire, bien plus rapide que le ROM.
Possible ?
Oui MAIS seulement des petits programmes car la ‘vrai’ RAM disponible sur SH4 est de 12 ko (d’après
la doc de
Lephenixoir) OU 64 ko (d’après le
wiki).
Donc bon...
Oublions 2 secondes la taille de la RAM et le fait qu’il y en est pas autant sur les SH3 que sur le SH4.
1.On sait communiquer via le port USB (exemple :
P7 de
Cakeisalie5), du moins on sait communiquer PC->Casio par contre il me semble pas avoir entendu parler de syscall permettant de jouer avec le port USB (j’ai chercher mais je n'ai rien comprit
).
2.On sait exécuter du code Assembleur depuis la RAM (#sysCall) (
gint fait ça merveilleusement bien aussi <3).
3.On sait faire des interfaces par dégueux.
Donc oui c’est possible !
Es-ce que j’ai commencer a faire un truc ?
Non, déjà parce que j'ai pas beaucoup de temps en ce moment et j’ai déjà plusieurs projets à finir.
C’est juste pour signaler que c’est théoriquement possible donc s'il y a des personnes intéressées par ce projet...bah allez-y ça pourrait être sympa et il y a sûrement beaucoup de choses à apprendre
.
Citer : Posté le 06/06/2018 11:52 | #
Tu as lu la documentation du processeur mais l'UBC est sur le microcontrôleur, à côté du processeur.
C'est pas faux
C'est pas dit qu'il existe sur SH4, alors teste bien si tu veux l'utiliser !
D’après la doc sur SH-4A section 10: l'UBC existe sur SH4
Tu peux faire un debugger, c'est un super projet ça !
C'est vrai mais il me manque trop de connaissances, déjà faut que je trouve l'address de UBC, comprendre les signaux qu'il m'envoie et du coup faudra que je fasse une librairie je sais pas en quel langage ni comment la faire ça va me prendre tellement de temps... surtout que j'ai aucune connaissance en debugger Mais tout ça s'apprend donc je ne perds pas espoir
Oui, le MMU est responsable des mappings mémoire
Mais quel intérêt a gint d'y toucher ?
Quant à gint, je ne vois vraiment pas à quoi tu fais référence. Je n'ai jamais eu de problème de vitesse et je n'ai manipulé les timers qu'assez tard. Si tu pouvais retrouver le passage qui t'a fait penser ça, il faudrait le clarifier !
Je me suis trompé, tu avais un problème d'handler et vu que quelqu'un avais évoqué GetKey() qui ennuie les timers j'ai tout confondu
J'en ai fait un tutoriel, de ce désassemblage de syscall, si jamais :
Je l'ai déjà lu plusieurs fois, mais j'ai envie d'essayer tout seul pour voir si je tombe sur ce que tu as dit dans ton tuto
L'adresse des timers, comme tout le reste, est dans la doc de SimLo.
Ha bon ?! Faut vraiment que je me mette à lire correctement les docs (mais bon avec le bac je n'ai pas des masses de temps)
Le but d'un timer c'est d'exécuter du code de façon périodique. Donc oui, on peut mettre du code ; à vrai dire si on n'en mettait pas ça ne servirait à rien. Un timer ce n'est pas une simple horloge !
Mais ya un truc que je comprends pas, si on met du code dans un timer faut aller vérifier si le timer a fini pour exécuter le code ? du coup autant utiliser la RTC... Sauf si le timer et indépendant du code et dans ce cas-là c'est génial (même si je croyais qu'il était impossible de faire 2 choses à la fois, "faire 2 actions en parallèle"...)
Citer : Posté le 06/06/2018 17:51 | #
Aha donc cette fois il est bien dans le processeur ! xD
Tu n'es pas obligé d'utiliser l'UBC pour faire de debuggage. Il y a plein de choses à essayer (même si les breakpoints sont incontournables) et toutes ne sont pas inaccessibles.
Je te propose de faire quelques crackme avec gdb pour t'initier au debugger. Tu peux en trouver facilement sur Internet ; sinon je dois en avoir quelques-uns issus de mes TPs de système. Ça te donnera un avant-goût de ce qu'un debugger peut faire.
Ben pour rajouter des mappings pardi. Par exemple pour mapper un deuxième add-in à 0x00400000 et exécuter les deux en « parallèle »... ou bien pour mapper plus de RAM statique... ou bien pour mapper des fichiers ROM...
Et surtout, parce que l'add-in qui réside à 0x00300000 ne peut pas être mappé en entier d'un bloc s'il est gros. Pour faire simple, la quantité maximum de mémoire qui peut être mappée à un instant donné sur SH3 est de 512k. Mais, à cause de la RAM statique qui occupe 8k et de considérations un peu plus compliquées (4-way associative table), on ne peut mapper que 384k de code d'add-in. Si l'add-in est plus gros, certaines pages ne seront pas mappées.
Bien sûr, si tu accèdes à une page non mappée, le système va s'en rendre compte et il va la mapper - au détriment d'une autre page qui sera retirée pour libérer de la place. C'est un va-et-vient tout à fait conventionnel.
gint aimerait faire ce boulot tout seul, mais je ne veux pas toucher au TLB, donc je suis obligé de rendre la main au système à chaque fois qu'une page manque si je veux utiliser un add-in de plus de 384k. (Actuellement je ne le fais pas et ça se contente de planter.)
Sur monochrome ça va, mais la suite des mauvaises nouvelles c'est que pour encore d'autres raisons sur G90 la limite est de 220k et qu'un add-in Prizm ça fait vite 2M. Donc il va falloir être intelligent.
Le timer ne contient pas de code à proprement parler. Supposons que tu aies lancé un timer pour dans 1 seconde avec une fonction callback f(). Ton programme configure le timer puis revient à ce qu'il était en train de faire. Une seconde plus tard, le timer balance une interruption, et ton add-in se fait interrompre entre deux instructions, sans préavis. Le processeur saute dans le gestionnaire d'interruptions, qui va chercher le callback (ici c'est donc f()) et l'appelle. Pendant que f() s'exécute, le timer recommence à compter 1 seconde. Une fois que f() a fini, le gestionnaire d'interruptions dit "suivant !" et si aucune interruption n'est en attente ton add-in reprend son travail. La fonction interrompue, elle, n'a rien vu.
Si jamais f() met tout pile 1 seconde à s'exécuter, le timer va renvoyer une interruption dès qu'elle aura fini et elle va se faire re-exécuter. Ton add-in ne reprendra jamais son travail : il freeze. Et si f() met plus d'1 seconde à s'exécuter, elle va prendre du retard et certaines interruptions seront perdues, c'est la débandade générale.
Pour conclure :
- Le code n'est pas vraiment dans le timer. Quand le timer arrive à expiration, le processeur interrompt ce qu'il fait pour exécute le gestionnaire d'interruptions, et en général ça se termine par un appel à une fonction personnalisée.
- On ne vérifie pas si le timer a fini : il nous prévient via une interruption.
- Le callback n'est pas exécuté en parallèle du reste parce que l'add-in est interrompu quand le gestionnaire d'interruptions est appelé. Soit dit en passant le parallélisme c'est si galère que tu n'as pas envie d'en manipuler.
Citer : Posté le 07/06/2018 13:17 | #
Merci beaucoup pour ton explication <3
Je vais essayer de faire quelques crackme en espérant réussir.
gint aimerait faire ce boulot tout seul, mais je ne veux pas toucher au TLB, donc je suis obligé de rendre la main au système à chaque fois qu'une page manque si je veux utiliser un add-in de plus de 384k. (Actuellement je ne le fais pas et ça se contente de planter.)
Ha yes x)
Sur monochrome ça va, mais la suite des mauvaises nouvelles c'est que pour encore d'autres raisons sur G90 la limite est de 220k et qu'un add-in Prizm ça fait vite 2M. Donc il va falloir être intelligent.
Toujours sans toucher la TLB ? ha bah bonne chance x)
Ton programme configure le timer puis revient à ce qu'il était en train de faire. Une seconde plus tard, le timer balance une interruption, et ton add-in se fait interrompre entre deux instructions, sans préavis. Le processeur saute dans le gestionnaire d'interruptions, qui va chercher le callback (ici c'est donc f()) et l'appelle
Ok donc il faut absolument une fonction de callback . Et si j'ai bien compris les fonctions de callback permette de récupérer l'adresse de la fonction ? c'est bien un truc qui ressemble à ça ?
char test(int a, int b, int c, int(callback*)(int d, int e))
{
return a < callback(b, c);
}
int soustraction(int d, int e)
{
return d - e;
}
void main()
{
char o;
o = test(2, 7, 4, &soustraction);
if(o)PrintXY(0, 0, (const unsigned char*)"Oui", 0);
else PrintXY(0, 0, (const unsigned char*)"Non", 0);
}
Donc le gestionnaire d’interruption c'est juste une "liste" avec plein d'adresses .
Mais du coup:
comment on sait si le timer n'est pas déjà utilisé ? (Je pense qu'on cherche simplement pas Charlie et qu'on le configure quand on veut)
Comment on sait quel timer est associé à quelle interruption ? (La doc (?) : E)
On peut modifier des timers au milieu d'un programme sans faire tout planter ? (je suppose que oui... *réfléchie 2 secondes* ... ... Mais c'est ça les valeurs qu'on peut changer dans gint pour le gris ?! mais je suppose qu'on ne peut pas le changer quand on veut (?))
Citer : Posté le 07/06/2018 13:36 | #
J'ai commencé à concevoir un système qui permette de quitter gint, faire charger par le système la page appropriée, et revenir à gint, mais- il est probable que le système ne tente pas de faire des choses extravagantes au moment de charger la page. Je peux donc décharger gint en partie uniquement et ne pas restaurer les timers, l'écran, le clavier, etc. C'est soumis à test bien sûr, mais il faut que ce soit extrêmement rapide.
Autre solution, comme il y a beaucoup de données mais que le code tiendra probablement dans moins de 220k, c'est l'utilisateur qui dit à gint « là, va voir le système, dis-lui de mapper la page pour telle ressource ». Le code resterait toujours mappé en intégralité. Ou alors, on s'arrange pour découper le code en parties indépendantes et on évite de changer de partie toutes les deux minutes. Plusieurs possibilités et heuristiques se présentent naturellement, je ne détaille pas.
Tu peux considérer que le callback du timer c'est le gestionnaire d'interruptions. Mais le code du gestionnaire d'interruptions ne change jamais. Donc à moins que tu donnes un autre callback au gestionnaire d'interruptions, il ne va pas te permettre d'exécuter une action de ton choix à chaque fois que le timer expire.
Pas tout à fait : ce que tu as écrit là s'appelle une fonction d'ordre supérieur (fonction qui prend en paramètre ou renvoie une fonction). C'est plus général qu'un callback. Des fonctions comme SetTimer() de fxlib ou timer_setup() de gint sont en effet d'ordre supérieur, mais elles font quelque chose du plus spécifique avec le paramètre. L'essence du callback c'est d'appeler la fonction passée en paramètre au bon moment. C'est une question de timing au fond !
Non, le gestionnaire d'interruptions c'est une fonction (un peu spéciale). Car elle ne fait pas qu'exécuter le callback, elle doit aussi signaler au hardware que l'interruption a été traitée, des petits trucs comme ça qui sont cachés à l'utilisateur mais que si on ne fait pas tout plante.
Par contre effectivement il y a un certain nombre d'interruptions possibles dans le MPU (timer 0, timer 1, timer 2, RTC, clavier, port série, USB, UBC, TPU, etc) et le gestionnaire d'interruptions est une sorte de liste de points d'entrée (que j'appelle gates) où chaque point d'entrée est associé à une interruption. Ce n'est pas une obligation de faire comme ça ; mais si tu regardes les détails techniques, alors il se trouve que c'est optimisé.
Tu le gardes dans une variable. Moi j'utilise le bit UNIE (UNderflow Interrupt Enable) du registre TCR (Timer Control Register) qui dit si l'interruption est activée ou non. Comme je n'utilise jamais le timer sans activer l'interruption, ça suffit. Et si, il faut vérifier ; sinon tu commences à faire des bibliothèques bancales.
La doc, oui. Quand le gestionnaire d'interruptions est appelé, il y a un registre (INTEVT pour Interrupt Event sur SH4) qui contient un numéro spécifique à l'interruption. Par exemple TMU0 c'est 0x400, TMU1 c'est 0x420 et TMU2 c'est 0x440.
Que peut-il se passer de mal au fond ? Pas grand-chose. Tu risques principalement 1- de configurer un timer déjà utilisé, ce qui casse le travail qui était en cours et peut conduire à des fuites de mémoire, etc ; et 2- des problèmes d'asynchronisme liés au fait que tu peux te faire interrompre par le timer pendant que tu configures le timer s'il tournait déjà.
Si tu es précautionneux, tu peux justifier que ni l'un ni l'autre ne se produira. Et dans gint tu peux changer les valeurs du moteurs de gris à la volée quand tu veux. L'add-in de démo de gint contient d'ailleurs un menu où tu peux tester et ajuster en temps réel les valeurs pour chercher des beaux gris. Ce qui se passe précisément c'est que je recharge les compteurs du timer pendant qu'il tourne ; du coup ça l'oblige à compter à partir d'une nouvelle valeur qui est délai légèrement différent.
Edit : D'ailleurs ce rechargement à la volée se produit à tous les frames du moteur de gris soit plusieurs dizaines de fois par seconde. Comme quoi on fait bien ce qu'on veut.
Citer : Posté le 17/10/2018 16:10 | #
Ayé j'ai enfin une idée précise de ce que je cherche, 'fin du moins j'ai un objectif, (tu la dis quelque message avant) c'est de faire un débuggeur. J'ai déjà une vague idée des étapes à franchir pour y arriver:
-Comprendre comment les interruptions sont gérées
-Prendre le contrôle des interruptions
-Comprendre comment fonctionne l'UBC & l'UDI.
-Prendre le control de l'UBC & l'UDI
-Commencer à programmer le fameux débuggeur.
Pour l'instant je ne fais que lire la doc, histoire se comprend un peu mieux comment fonctionne la calto, la mémoire, etc...
Le mieux que j'ai réussi à faire pour l'instant c'est réécrire un "driver" pour l'écran, un autre pour clean la vram (....il est presque stable), comprendre comment fonctionne le TMU et le setup .
Mais je trouve qu'il me manque trop de connaissances encore pour commencer un tel projet (ex: TLB, MMU, VAS, VBR, etc.) mais je pense y arriver au bout d'un moment (je commence comprendre certaines subtiliter.)
(D'ailleurs petite question à propos de git : il s'installe dans la RAM au niveau de l'aera P3 (donc git passe le MMU en "privilège mode" (?)) ?)
Ajouté le 17/10/2018 à 19:30 :
Bon, je viens de lire la doc (une seule fois pour l'instant) mais je ne suis pas sure d'avoir bien compris certaines choses:
La VBR contient les adresses pour les interruptions (en gros c'est une liste d'adresses de fonction).
INTC permet de définir la prioritée des interruptions (ou de dire de les "ignorer") et ce constante, lorsqu'elle recois une/des interruptions, de mettre "en pause" ce qu'est en train de faire la cato, pour appeler la VBR puis clean les flag qui ont indiqué une interruption, pour finir par remettre la calto là où elle en était (en gros) ?
Citer : Posté le 17/10/2018 19:35 | #
Tout cet échange est très instructif, à nouveau merci Lephé pour prendre le temps de détailler Et Yatis de persévérer !
En attendant si tu veux gagner du temps Yatis, quand tu lances l'émulateur, appuie sur la touche 'A' de ton ordi, ça lance l'add-in directement
Citer : Posté le 17/10/2018 22:01 | #
-Prendre le contrôle des interruptions
-Comprendre comment fonctionne l'UBC & l'UDI.
-Prendre le control de l'UBC & l'UDI
-Commencer à programmer le fameux débuggeur.
Gros programme ! Ne te trompe pas par contre, si tu contrôles les interruptions tu es responsable pour tout, notamment le clavier et les timers ; tu ne pourras plus utiliser fxlib (elle ne marchera plus parce qu'elle a besoin des interruptions pour marcher). Tu devras les contrôler toi-même. En fait, c'est comme ça que gint a commencé : je voulais juste un timer et je savais que je devrais tout gérer. Je me suis lancé, et voilà
Bien joué ! Juste une remarque de vocabulaire : "driver" désigne un programme ou un ensemble de fonctions pour contrôler un périphérique. Donc tu as un seul driver pour l'écran en tout, par contre tu peux lui rajouter des fonctions
* MMU : Permet de faire le lien entre la mémoire virtuelle est la mémoire physique.
* TLB : C'est le tableau qui permet de faire ça, il contient des correspondances virtuel <-> physique.
* VAS : Virtual Address Space? Si oui, alors c'est juste l'espace virtuel.
* VBR : Une interruption se produit ? Le proco saute à la fonction située à VBR + 0x600. Un problème de MMU ? On saute à VBR + 0x400. Une exception (erreur de processeur) : VBR + 0x100.
Le processeur est toujours en mode privilégié, parce que le système de Casio est bancal. Et c'est tant mieux pour nous. Si le processeur n'avait pas été en mode privilégié, pas une ligne sur deux de gint ne serait possible - et pas un seul message de ce topic serait réalisable
Pas tout à fait, la VBR c'est juste une adresse de laquelle on déduit les positions des 3 fonctions :
- VBR + 0x100
- VBR + 0x400
- VBR + 0x600
Tu dois donc t'arranger pour que tes fonctions soient espacées du bon nombre d'octets.
Oui, c'est l'idée. Attention, c'est à toi de clear le flag en question, si tu ne le fais pas l'interruption va revenir tout de suite et la calculatrice resautera à VBR + 0x600 avant de reprendre le programme. Tu vas donc retourner indéfiniment dans le gestionnaire d'interruptions, et ton programme va donc freezer (en apparence) parce qu'il ne pourra plus s'exécuter.
Ça veut dire que tu ne dois jamais activer des interruptions que tu n'as pas étudiées à l'avance !
Si ça vous intéresse, rien ne pourrait me faire plus plaisir que de vous en parler !
Citer : Posté le 19/10/2018 20:04 | #
Gros programme ! Ne te trompe pas par contre, si tu contrôles les interruptions tu es responsable pour tout, notamment le clavier et les timers ; tu ne pourras plus utiliser fxlib (elle ne marchera plus parce qu'elle a besoin des interruptions pour marcher). Tu devras les contrôler toi-même. En fait, c'est comme ça que gint a commencé : je voulais juste un timer et je savais que je devrais tout gérer. Je me suis lancé, et voilà
Ça je le sais bien c'est pour cette raison que je m'intéresse de plus en plus aux interruptions, j'aimerais être dépendant d'aucune lib (ou du moins pour l'instant de comprendre ce que font certaines fonctions comme setTimer() ou encore comment gint fonctionne par exemple)
* VBR : Une interruption se produit ? Le proco saute à la fonction située à VBR + 0x600. Un problème de MMU ? On saute à VBR + 0x400. Une exception (erreur de processeur) : VBR + 0x100.
Et c'est là que je comprends pas, comment il peut savoir que, par exemple, pour un timer j'ai une fonction (un compteur par exemple) et pour un autre j'ai une autre fonction qui est le moteur 2D de mon jeu ? Les fonctions ne peuvent pas être mis au même endroit ? (C'est ça qui me bloque depuis quelques jours pour comprendre la gestion des interruptions (ou alors je regarde pas au bonne endroit dans la doc...)).
Pour l'instant de ce que j'ai compris:
* 1: Une interruption se produit.
* 2: Elle passe par Interupt Controller (INTC) histoire de voir si elle est prioritaire sur une autre interruption survenue au même moment.
* 3: le Program Counter (PC) (si j'ai bien compris c'est lui qui contient l'adresse du code qui va être exécuté) est save dans SPC.
* 4: Le Satus Register (SR) (je n'ai pas tout comprit de sont utilisés (il faut que je me documente un peu plus)) est save aussi (dans SSR).
* 5: Le CPU saute a VBR: [+0x100 si c'est un problème de TLB] || [+0x400 si c'est un problème du MMU] || [+0x600 si c'est une interruption "classique"].
* 6: Le code de l'intéruption est exécuté.
* 7: Le CPU vérifie s'il n'y a pas d'autre interruption/ erreur à gérer. (si oui il répète l'étape 5, 6, 7)
* 8: sinon il restaure tout et le CPU reprend là où il était.
(J'espère ne pas avoir trop dit de bêtise).
J'ai aussi passé pas mal de temps à lire des info sur "comment le mémoire est structuré".
Comme tu la dis plus haut: on est en mode privilégié alors voila ce que j'ai compris:
* P0: 2go d'espace où on fait ce qu'on veut
* P1: Il est utilisé par l'OS (si je dis pas de bêtise) (ou utilisé par le cache mais je me suis pas encore informé sur ce que c'est....)
* P2: C'est là où est le TLB (mais j'ai lu aussi que c'est là où l'OS s'initialise).
* P3: 0.5go d'espace où on fait ce qu'on veut (c'est la ou ce met gint n'es-ce point ?)
P4: Utiliser par le CPU (Control Register Area).
(Là aussi je ne suis pas sure de ce que j'avance).
Tout cet échange est très instructif, à nouveau merci Lephé pour prendre le temps de détailler Et Yatis de persévérer !
En attendant si tu veux gagner du temps Yatis, quand tu lances l'émulateur, appuie sur la touche 'A' de ton ordi, ça lance l'add-in directement
Désoler de poser des questions (surement obvious) mais je veux juste apprendre et comprendre (et imiter si possible) Puis cette discussion pourra sans doute aider des personnes
Merci pour ta patience et tes réponses très détaillés Lephe <3
Citer : Posté le 19/10/2018 20:48 | #
En fait il saute à un endroit fixe (VBR + 0x600) et il met dans un registre (INTEVT2 sur SH3, INTEVT sur SH4) un nombre qui indique la source (timer, rtc, clavier, etc). Ensuite la fonction qui est à VBR + 0x600 se débrouille pour faire ce qu'il faut selon la valeur.
* 4: Le Satus Register (SR) (je n'ai pas tout comprit de sont utilisés (il faut que je me documente un peu plus)) est save aussi (dans SSR).
En fait PC c'est l'adresse de l'instruction en cours d'exécution, ie. où tu es dans le programme. Il est sauvegardé dans SPC pour que quand l'interruption se termine, le programme puisse reprendre là où il en était. Pour SR, c'est des infos de statut, moins important.
0x100 c'est pour les exceptions génériques : division par zéro, lecture d'une mauvaise adresse, accès non aligné, trap... pour ton information le TLB est le tableau qui permet de faire la traduction entre adresses physiques et virtuelles, c'est le composant principal du MMU. (Donc MMU et TLB c'est quasiment pareil.)
Pour être exact VBR + 0x600 est exécuté, tu y mets le code que tu veux (c'est pas tellement « le code de l'interruption » donc).
Comme tu la dis plus haut: on est en mode privilégié alors voila ce que j'ai compris:
* P0: 2go d'espace où on fait ce qu'on veut
* P1: Il est utilisé par l'OS (si je dis pas de bêtise) (ou utilisé par le cache mais je me suis pas encore informé sur ce que c'est....)
* P2: C'est là où est le TLB (mais j'ai lu aussi que c'est là où l'OS s'initialise).
* P3: 0.5go d'espace où on fait ce qu'on veut (c'est la ou ce met gint n'es-ce point ?)
P4: Utiliser par le CPU (Control Register Area).
(Là aussi je ne suis pas sure de ce que j'avance).
Pas exactement, en gros il y a P0 et le reste. P0 c'est la zone accessible aux programmes non privilégiés. En théorie l'add-in devrait être ici, d'ailleurs il y est, sauf qu'il y est en privilégié. Je sais pas trop pourquoi.
Le reste c'est P1/P2/P3/P4, ce qui les distingue c'est surtout s'ils ont du caching et du MMU. Tu peux te souvenir que dans P1 tu peux accéder à toute la ROM et toute la RAM. Dans P2 tu peux aussi accéder à la ROM, mais c'est pas les mêmes paramètres de caching et de MMU. P3 tu ne t'en serviras jamais, et dans P4 il n'y a que des registres de périphériques en vrac.
Merci pour tes questions très détaillées surtout !
Citer : Posté le 06/11/2018 22:30 | #
Pour être exact VBR + 0x600 est exécuté, tu y mets le code que tu veux (c'est pas tellement « le code de l'interruption » donc).
Donc si j'ai bien compris on peut créer notre propre VBR, structuré sur le même parterne que l'ancienne, et mettre les fonctions qu'on veut ? Par conséquent il faut copier es address à VBR + 100 et VBR + 400...Ou alors on se met directement à VBR + 600...mais du coup on risque de perdre l'addresse originelle >_<
Puis je dois avouer ne toujours pas avoir compris comment le proco peut savoir quel "fonction" placer à VBR + 600. Je veux dire par là: comment la calto peut savoir faire la différence entre deux (ou plus) interruptions "normal" ? Je sais qu'il y a une gestion de la priorité avec l'INTC. Mais dans la doc, c'est écrit (et tu me la confirmé) que le proco saute toujours à VBR + 600...
(...Je dois lire au mauvais endroit je pense... XD).
Pas exactement, en gros il y a P0 et le reste. P0 c'est la zone accessible aux programmes non privilégiés. En théorie l'add-in devrait être ici, d'ailleurs il y est, sauf qu'il y est en privilégié. Je sais pas trop pourquoi.
Le reste c'est P1/P2/P3/P4, ce qui les distingue c'est surtout s'ils ont du caching et du MMU. Tu peux te souvenir que dans P1 tu peux accéder à toute la ROM et toute la RAM. Dans P2 tu peux aussi accéder à la ROM, mais c'est pas les mêmes paramètres de caching et de MMU. P3 tu ne t'en serviras jamais, et dans P4 il n'y a que des registres de périphériques en vrac.
Ha d'accord, je pensais qu'on pouvait utiliser la zone P3...tant pis
Citer : Posté le 07/11/2018 07:39 | #
Donc si j'ai bien compris on peut créer notre propre VBR, structuré sur le même parterne que l'ancienne, et mettre les fonctions qu'on veut ? Par conséquent il faut copier es address à VBR + 100 et VBR + 400...
C'est tout à fait ça, et aussi copier le gestionnaire d'interruptions à VBR + 0x600.
De toute façon la VBR orignielle pointe quelque part dans la ROM je pense, donc tu ne peux pas y écrire. Ce n'est pas une bonne idée de toute façon.
Le proco, lui, ne se pose pas de questions. Il saute à VBR + 0x600 quand il y a une interruption, peu importe l'interruption. Le proco ne place aucune fonction à VBR + 0x600, et il ne sait pas ce qu'est une fonction. Par contre, toi, l'add-in, tu places une fonction à 0x600. C'est une fonction qui sera appelée quand il y a une interruption, peu importe l'interruption.
Bon. Maintenant, comment tu distingues deux interruptions différentes ? Tu regardes le registre INTEVT2 (SH3) ou INTEVT (SH4) qui contient un code d'interruption unique, différent pour chaque source.
Fais attention, je te parle de mémoire virtuelle là. P3, il n'y a rien dedans : c'est du vide. Il y a des adresses, mais pas de mémoire en face. N'oublie pas que ce qui t'intéresse c'est :
* Qu'y a-t-il comme mémoire sur la calculatrice ?
* Quelles adresses utiliser pour accéder à cette mémoire ?
Le reste des adresses, tu t'en moques royalement. D'ailleurs il y en a 4 Go en tout et on n'a pas ça de mémoire.
Citer : Posté le 25/11/2018 17:17 | #
C'est tout à fait ça, et aussi copier le gestionnaire d'interruptions à VBR + 0x600.
[...] De toute façon la VBR orignielle pointe quelque part dans la ROM je pense, donc tu ne peux pas y écrire. Ce n'est pas une bonne idée de toute façon.
Hooooo je viens de comprendre Du coup admettons que j'arrive à créer ma VBR et à placé mon gestionnaire d'interruption à VBR + 0x600; mais, que je ne gère (par exemple) que les interruptions des timers. Quand mon gestionnaire est appelé je vérifie (via ENTEVT) si je gère l'interruption sinon il faut que je saute avec l'ancienne VBR ? (donc VBR + 0x600 si c'est une interruption "classique", VBR + 0x400 si c'est une erreur MMU, ou VBR + 0x100 si c'est une erreur CPU (je redis ce que tu ma expliquer pour vraiment être sûr d'avoir bien compris )).
Fais attention, je te parle de mémoire virtuelle là. P3, il n'y a rien dedans : c'est du vide. Il y a des adresses, mais pas de mémoire en face. N'oublie pas que ce qui t'intéresse c'est :
* Qu'y a-t-il comme mémoire sur la calculatrice ?
* Quelles adresses utiliser pour accéder à cette mémoire ?
Le reste des adresses, tu t'en moques royalement. D'ailleurs il y en a 4 Go en tout et on n'a pas ça de mémoire.
Mmmm...en fait j'ai l'impression d'avoir pas bien saisi l'histoire de la mémoire virtuelle et la mémoire physique.
De ce que j'ai compris la taille de la mémoire virtuelle est défini soit par le bus d'address, soit par la taille des registres du CPU. Par exemple si le CPU est 32 bits alors il peut avoir 4 Go de mémoire virtuelle mais si son bus de donné fait 20 bits alors il n'y aura que 1 Mo de mémoire virtuel. (et vise versa).
Là où j'ai du mal à comprendre c'est ce que représente cette mémoire virtuel. Chaque programme a ça propre mémoire virtuel ou chaque programme "pioche" dans ces 4 Go ?
Puis une autre question me viens à l'esprit: Si un programme fait...mmmm...10 Go par exemple comment il peut tourner comme il le faut ? il est chargé petit bout par petit bout ? Dans ce cas-là comment le MMU sait quoi charger en RAM ?
Je crois que je commence à comprendre pourquoi tu veux toucher le moins possible au MMU, c'est tellement crucial comme truc x)
Citer : Posté le 25/11/2018 22:11 | #
Du coup admettons [...] que je ne gère (par exemple) que les interruptions des timers. Quand mon gestionnaire est appelé je vérifie (via ENTEVT) si je gère l'interruption sinon il faut que je saute avec l'ancienne VBR ?
Ça c'est la technique de Kristaba, rediriger les interruptions. C'est compliqué à mettre en place, parce que le gestionnaire d'interruptions du système fait plein de trucs bordéliques, y compris re-changer la VBR, ce qui casse ton système. Il y a un contournement pour ça, pour exécuter du code à toi une fois l'interruption terminée, pour re-re-changer la VBR. Ça marchait sur le proto SH3 de Kristaba, mais sur SH4 je n'ai jamais réussi. (À vrai dire j'ai essayé quand j'ai commencé à bidouiller tout ça, donc il y avait peut-être juste des bugs que j'ai pas vus.)
Soit dit en passant le premier moteur de gris, celui de Kucalc, changeait la VBR et laissait tomber toutes les interruptions qu'il ne connait pas. Ne fais pas ça.
(donc VBR + 0x600 si c'est une interruption "classique", VBR + 0x400 si c'est une erreur MMU, ou VBR + 0x100 si c'est une erreur CPU (je redis ce que tu ma expliquer pour vraiment être sûr d'avoir bien compris )).
C'est ça.
De ce que j'ai compris la taille de la mémoire virtuelle est défini soit par le bus d'address, soit par la taille des registres du CPU. Par exemple si le CPU est 32 bits alors il peut avoir 4 Go de mémoire virtuelle mais si son bus de donné fait 20 bits alors il n'y aura que 1 Mo de mémoire virtuel. (et vise versa).
Ça, c'est un peu théorique, mais en gros oui. Ici, les adresses (virtuelles) ont une taille limitée par 32 bits, les adresses physiques sont même de 28 bits pour une autre raison.
Bon. L'idée est la suivante. Sur un ordinateur où plusieurs programmes tournent, chacun a son espace d'adressage qui va de 0 à 4 Go, et chacun a donc les mêmes adresses (0, 1, 2, 12, 0xa44b0000...). Cela permet d'isoler les programmes, car aucune adresse accessible depuis un programme ne représente une donnée gérée par un autre (puisque leurs espaces sont individuels).
Ensuite l'OS, qui supervise la RAM, découpe l'espace de chaque processus en pages et charge les pages dans la RAM. La correspondance entre les pages des espaces virtuels et l'endroit où elles sont chargées dans la RAM est assurée par le MMU.
Tout cela est détaillé dans le chapitre 3 de la doc du proco (SH7705), en anglais mais mieux que je ne pourais le faire à plus de 22 heures. x)
Sur un ordinateur, le programme peut être chargé partiellement, et s'il utilise une adresse qui n'a pas été chargée, l'OS charge la page associée en retirant une autre page. Ce mécanisme de va-et-vient utilise la swap.
Sur la calculatrice la taille maximale autorisée pour les add-ins est 512k et si ton add-in fait plus la calculatrice refuse de le charger. (La limite est de 2M sur la Prizm et la Graph 90).[/quote]
C'est surtout que si tu le plantes, comme certains paramètres ne sont pas réinitialisés par les resets, tu peux casser complètement le système.
Citer : Posté le 26/11/2018 15:14 | #
Soit dit en passant le premier moteur de gris, celui de Kucalc, changeait la VBR et laissait tomber toutes les interruptions qu'il ne connait pas. Ne fais pas ça.
C'est pour ça qu'il forçait le plantage de la calto xD.
J'aimerais bien faire un truc propre seulement avant de commencer à coder il me manque quelque chose d'essentiel (je pense): faire mon propre linker script. Car comme la VBR doit être bien aligné en memoire je pense que le linker sera indispensable pour ça, seulement je pense ne pas avoir encore les connaissances requises pour en faire un, ne serait-ce que potable.
Ça c'est la technique de Kristaba, rediriger les interruptions. C'est compliqué à mettre en place, parce que le gestionnaire d'interruptions du système fait plein de trucs bordéliques, y compris re-changer la VBR, ce qui casse ton système. Il y a un contournement pour ça, pour exécuter du code à toi une fois l'interruption terminée, pour re-re-changer la VBR. Ça marchait sur le proto SH3 de Kristaba, mais sur SH4 je n'ai jamais réussi. (À vrai dire j'ai essayé quand j'ai commencé à bidouiller tout ça, donc il y avait peut-être juste des bugs que j'ai pas vus.)
Pour l'instant je vais essayer de faire un linker script qui fonctionne sans casser ma calto, et je dois avouer être dans l'inconnue totale pour ce truc...mais je pense y arriver (je te demanderai surement encore de l'aide, d'ailleur je te remercie d'avoir pris le temps de bien m'expliquer certaines notions qui sont, pour toi, surement bateau ).
Tout cela est détaillé dans le chapitre 3 de la doc du proco (SH7705), en anglais mais mieux que je ne pourais le faire à plus de 22 heures. x)
J'ai commencé à me replonger dans la doc, doucement mais surement
D'ailleurs petite question, tu penses qu'il vaut mieux que mon gestionnaire d'interruption ce mettre au début de P0 ou à la fin de P0 ? (ou alors ce n'est pas important du moment que le linker est stable ?).
Citer : Posté le 26/11/2018 17:57 | #
C'est pour ça qu'il forçait le plantage de la calto xD.
Exactement !
Tu peux regarder ceux de gint, ils fonctionnent essentiellement comme ça jusqu'à une certaine version : https://git.planet-casio.com/lephe/gint/blob/master/demo/gintdemo.ld
Le linker script c'est une affaire de compilation, après tu peux utiliser la méthode Kucalc pour un temps ! Autre solution : en cas d'interruption, tu affiches un truc et tu boucles à l'infini dans le handler. Je m'en sers tout le temps pour debugger !
Et puis non, c'est pas bateau, j'ai juste l'habitude parce que ce projet dure depuis... wow, bientôt 4 ans
Tu dois le mettre dans P1 ou P2, tu n'as pas le choix ! C'est pour ça que le linker script de gint vole une certaine zone de mémoire. Cherche bien dans la doc, c'est marqué. P1 ou P2.
Citer : Posté le 12/12/2018 20:23 | #
Tu dois le mettre dans P1 ou P2, tu n'as pas le choix ! C'est pour ça que le linker script de gint vole une certaine zone de mémoire. Cherche bien dans la doc, c'est marqué. P1 ou P2.
J'ai commencé à faire mon linker script.
Alors je comprends pas tout mais voila ce que j'ai codé en me basant sur le linker de toi, Kristaba (fixos) et addin.ld:
OUTPUT_ARCH(sh3) /* specify machine architecture. */
ENTRY(initialize) /* set first function use (?) */
MEMORY
{
rom : o = 0x00300200, l = 512k
ram : o = 0x08100000, l = 64k
my_ram : o = ????, l = 16k
}
SECTIONS
{
.text : {
*(.text)
*(.text.*)
} > rom
.bss : {
*(.bss)
*(.bss.*)
} > ram
.data : {
*(.data)
*(.data.*)
} > ram
/*
* RAM section witch set my interupt handler.
* vbr + 0x100 -> exeption interupt.
* vbr + 0x400 -> tlb miss.
* vbr + 0x600 -> interupt handler.
*/
.glados : ALIGN(4) {
. = ALIGN(0x100)
_glados_vbr = . ;
. = _glados_vbr + 0x100 ;
*(.glados.exept)
. = _glados_vbr + 0x400 ;
*(.glados.tlb)
. = _glados_vbr + 0x600 ;
*(.glados.interupt)
} > my_ram
}
J'ai beaucoup de problèmes, le premier c'est qu'il n'est pas fini, je sais qu'il manque plein de choses notamment _bbss, _ebss et tout le .rodata seulement je n'ai aucune idée de ce que le .rodata représente, tu pourrais m'éclairer ?
Mais ce qui me bloque depuis plusieurs jours c'est les addresses. J'ai réussi a comprendre pourquoi rom = 0x00300200 (les g1a sont chargé a 0x00300000 mais le crt0.s prend de la place donc on décale (?)).
Je ne suis pas sûr de comprendre pourquoi la ram = 0x08100000 (section 7 de la Bible, page 153, area 1 est réservée pour les I/O et area2 commence à 0x08000000, donc +0x0010000 pour être sûr de rien casser (?)).
Mais du coup je comprends pas pourquoi tu utilise l'address 0x88000d00, je n'arrive pas trouver cette addresse, enfin si, c'est la fin de l'area P1 mais du coup ça veut dire que je regarde pas au bon endroit dans la doc pour les adresses citées plus haut.
Autre question, pourquoi tu charges pas tout ton programme la RAM comme le faisait Kristaba ?
C'est crade et en plus pas forcément stable je suppose.
Sinon mon linker est bien partie ou j'ai réellement rien comprit ? x)
glados c'est temporaire le temps que je trouve un nom et un vrai but au projet xD
Citer : Posté le 12/12/2018 21:43 | #
Quelques notes sur ton script...
Oui, ENTRY c'est le nom du symbole qui sera appelé en tout premier dans le programme... en principe. Dans le fichier ELF ça apparaît à un endroit, mais la calculatrice s'en fout et saute à 00300200. Tu dois donc être sûr que la première fonction appparaît ici, et c'est pour ça qu'on a inventé .pretext. Ici ton *(.text) peut mettre les fonctions dans n'importe quel ordre, donc ça ne va pas marcher.
Retiens : si tu veux placer les choses finement en mémoire, crée de nouvelles sections.
La zone de ram que tu as choisie ne fait que 8k, je crois que c'est Kristaba qui avait mis 64k - c'est une erreur sur SH3. Je crois que sur SH4 c'est bien 64k, mais c'est un truc que j'ai découvert récemment, il faudra que je revérifie. Pour la zone my_ram, prends la mémoire utilisée par gint, c'est safe par expérience, et on n'a pas grand-chose d'autre.
C'est pas mal du tout !
J'ai beaucoup de problèmes, le premier c'est qu'il n'est pas fini, je sais qu'il manque plein de choses notamment _bbss, _ebss et tout le .rodata seulement je n'ai aucune idée de ce que le .rodata représente, tu pourrais m'éclairer ?
On utilise un système un peu spécial ici. Tu vois les > rom et > ram, ça indique au linker d'assigner des adresses qui vont dans ces sections. Toutefois, quand notre programme va être lancé, tout sera dans la ROM. C'est le travail de crt0.s de copier ce qu'il faut dans la RAM. Et ce sont les valeurs b* (begin) et e* (end) qui indiquent à crt0.s où c'est (dans la RAM) qu'il doit copier ces données.
De plus, si tu regardes dans les autres scripts, il y a des AT() : ils servent à dire au linker à quel endroit de la ROM vont résider les données avant que tu ne les mette dans la RAM. Il faut que tu les mettes, et que tu le fasses de sorte que le AT() d'une section soit égal au AT() de la section d'avant plus sa taille (pour que tout soit à la suite dans la ROM).
Non c'est le header g1a avec (entre autres) l'icône. crt0.s est constitué d'une section .text qui est quelque part dans ton *(.text)
D'abord si tu regardes bien la RAM statique (c'est ainsi qu'on appelle la zone ram) est à 08100000 et Area 2 commence à 80000000. Le 8 n'est pas à la même position ; c'est donc que la RAM statique est dans P0. En effet, toute la mémoire du programme devrait être dans P0 si le système était bien fait. Le système utilise le MMU pour mapper cette zone vers une adresse qui lui convient, ce qui permet notamment à deux applications d'être ouvertes en même temps (si les deux écrivaient au même endroit, elles se gêneraient, heureusement le système peut envoyer leurs deux RAM statiques à des endroits différents grâce au MMU).
À 80000000:4M se trouve la ROM. À 88000000:512k (256k sur SH3) se trouve la RAM. J'utilise donc une adresse de la RAM, prise un peu au hasard mais par chance inutilisée par le système.
C'est crade et en plus pas forcément stable je suppose.
Il n'y a surtout pas la place, et ça ne sert à rien puisque la vitesse d'accès à la Flash est quasiment la même (fais les tests de perfs' de Ftune2, tu verras tout de suite ). Kristaba ne faisait pas ça pour autant que je sache ?
C'est un très bon début ! Bravo !
Citer : Posté le 13/12/2018 12:53 | #
On utilise un système un peu spécial ici. Tu vois les > rom et > ram, ça indique au linker d'assigner des adresses qui vont dans ces sections. Toutefois, quand notre programme va être lancé, tout sera dans la ROM. C'est le travail de crt0.s de copier ce qu'il faut dans la RAM. Et ce sont les valeurs b* (begin) et e* (end) qui indiquent à crt0.s où c'est (dans la RAM) qu'il doit copier ces données.
Du coup j'ai essayé de comprendre le crt0.s et voila ce que j'ai compris: Dans un premier temps la fonction initialize (appelé par ENTRY) initialise le TLB, bouge toute la section.bss dans la RAM puis toute la section.data. Ensuite seulement le main() est appelé. Une fois le main() finit, crt0.s s'occupe de quitter un handler (?) puis de fermer le g1a (?).
Du coup si j'ai bien compris il va falloir que fasse mon propre crt0.s (il me semble que tu la fais en C) ou il doit charger mon handler dans la RAM en plus des sections .bss et .data ?
En lisant le crt0.s je me suis rendu compte qu'il a besoin de _ebss, _bdss, _bssdatasize, _romdata, _bdata et _edata donc je les ai ajouté à mon linker, de plus j'ai fait ce que tu m'as dit avec le AT() mais je ne suis pas sûr de moi.
OUTPUT_ARCH(sh3) /* specify machine architecture. */
ENTRY(initialize) /* set first function use*/
MEMORY
{
rom : o = 0x00300200, l = 512k
ram : o = 0x08100000, l = 8k
my_ram : o = 0x8800d000, l = 12k
}
SECTIONS
{
.text : {
*(.pretext)
*(.pretext.*)
*(.text)
*(.text.*)
_etext = . ;
} > rom
.rodata : AT(_etext) {
*(.rodata)
*(.rodata.*)
_romdata = . ; /* end of .rodata section (use by crt0)*/
} > rom
.bss : AT(_romdata) {
_bbss = . ; /* begin of .bss section (use by crt0 to copy all .bss section on RAM)*/
*(.bss)
*(.bss.*)
_ebss = . ; /* end of .bss section (use by crt0 to bound the size of .bss)*/
_bssdatasize = SIZEOF(.bss); /* use by crt0.s */
} > ram
.data : AT(_ebss) {
_bdata = . ; /* begin of .data section (used by crt0.s)*/
*(.data)
*(.data.*)
_edata = . ; /* end of .data section (used by crt0.s) */
} > ram
/*
* RAM section witch set my interupt handler.
* vbr + 0x100 -> exeption interupt.
* vbr + 0x400 -> tlb miss.
* vbr + 0x600 -> other interupt.
*/
.glados : AT(_edata) ALIGN(4) {
. = ALIGN(0x100)
_bglados = . ;
_glados_vbr = . ;
. = _glados_vbr + 0x100 ;
*(.glados.exept)
. = _glados_vbr + 0x400 ;
*(.glados.tlb)
. = _glados_vbr + 0x600 ;
*(.glados.interupt)
_eglados = . ;
} > my_ram
}
Chaque section se suit, je sais pas si c'est ce qu'il faut faire mais ça me semble logique. J'ai ajouté aussi le .pretext que j'ai pu lire dans crt0.s.
Il n'y a surtout pas la place, et ça ne sert à rien puisque la vitesse d'accès à la Flash est quasiment la même (fais les tests de perfs' de Ftune2, tu verras tout de suite ). Kristaba ne faisait pas ça pour autant que je sache ?
Si, il me semble qu'il charge tous à partir de 0x88000000 ou alors j'ai mal lu son linker.
MEMORY
{
osram : o = 0x88000000, l = 128k /* for fx-9860G */
}
En ce qui concerne les adresses, il faut que je me replonge dans la doc pour bien tout comprendre, je dois confondre les addresses physiques et les adresses logiques....
Citer : Posté le 13/12/2018 13:32 | #
Du coup j'ai essayé de comprendre le crt0.s et voila ce que j'ai compris: Dans un premier temps la fonction initialize (appelé par ENTRY) initialise le TLB, bouge toute la section.bss dans la RAM puis toute la section.data. Ensuite seulement le main() est appelé. Une fois le main() finit, crt0.s s'occupe de quitter un handler (?) puis de fermer le g1a (?).
C'est ça. Ce ne sont pas des déplacements mais des copies, même si dans l'idée on ne servira pas des parties en ROM. (D'ailleurs .bss n'est pas copié mais juste mis à zéro, cela te permet de l'éliminer du fichier g1a, j'y reviendrai peut-être plus trad.)
Ah oui, j'ai oublié de te dire, .rodata c'est read-only data donc c'est des données qui restent dans la ROM. C'est le même genre de données que .data mais on ne les charge pas en RAM. Morale : pour contrôler plus finement .data, on coupe en deux morceaux : .data et .rodata.
Si tu veux tout faire à la main, oui. Sinon tu peux exporter les bons symboles e* et b* et réutiliser celui-ci.
Le chargement du handler tu le fais quand tu veux, mais dans gint ce n'est pas tout de suite car je dois faire la détection du matériel avant (SH3 vs. SH4 change pas mal de paramètres du l'application).
Ton intuition est bonne. Toutefois, quand tu fais _ebss dans un bloc qui se termine par > ram, cela assigne une adresse qui est dans la zone ram, or tu veux connaître l'adresse absolue... je t'invite à profiter de cette occasion pour chercher un peu dans la doc du linker. Je crois que c'est ABSOLUTE(.). Une façon de t'en sortir sans erreur est .data : AT(_romdata + sizeof(.bss)), c'est ce que je faisais à une certaine époque. (Plus moderne : > ram AT> rom.)
Dans tous les cas, infos essentielles pour débugger tes linkers scripts :
* https://sourceware.org/binutils/docs/ld/ ou info ld
* sh3eb-elf-objdump -t <elf> affiche la table des symboles
* -Wl,-M affiche plein d'infos utiles sur stdout lors de la compilation, c'est le must-have
Sinon ça m'a l'air vraiment pas mal, encore bravo.
Je peux avoir le lien où tu as trouvé ça ? Il n'a probablement pas tout mis là, ça aurait cassé des trucs quasiment certainement...
Commence par ici : https://sourceware.org/binutils/docs/ld/Basic-Script-Concepts.html#Basic-Script-Concepts
Citer : Posté le 14/12/2018 22:44 | #
J'ai update mon linker en essayant de prendre en compte ce que tu m'as dit (merci beaucoup au passage de prendre du temp pour m'aider <3 )
OUTPUT_ARCH(sh3) /* specify machine architecture. */
ENTRY(initialize) /* set first function use */
MEMORY
{
rom : o = 0x00300200, l = 512k
ram : o = 0x08100000, l = 8k
my_ram : o = 0x8800d000, l = 12k
}
SECTIONS
{
.text : {
*(.pretext)
*(.pretext.*)
*(.text)
*(.text.*)
_etext = ABSOLUTE (.) ;
} > rom
.rodata : AT(_etext) {
*(.rodata)
*(.rodata.*)
_romdata = ABSOLUTE (.) ; /* end of .rodata section (use by crt0)*/
} > rom
.bss : {
_bbss = ABSOLUTE (.) ; /* begin of .bss section (use by crt0 to copy all .bss section on RAM)*/
*(.bss)
*(.bss.*)
_ebss = ABSOLUTE (.) ; /* end of .bss section (use by crt0 to bound the size of .bss)*/
_bssdatasize = SIZEOF(.bss); /* use by crt0.s */
} > ram
.data : AT(_rodata + SIZEOF(.bss)) {
_bdata = ABSOLUTE (.) ; /* begin of .data section (used by crt0.s)*/
*(.data)
*(.data.*)
_edata = ABSOLUTE (.) ; /* end of .data section (used by crt0.s) */
} > ram
/*
* RAM section witch set my interupt handler.
* vbr + 0x100 -> exeption interupt.
* vbr + 0x400 -> tlb miss.
* vbr + 0x600 -> other interupt.
*/
.glados : AT(_rodata + SIZEOF(.bss) + SIZEOF(.data)) ALIGN(4) {
. = ALIGN(0x100)
_bglados = ABSOLUTE (.) ;
_glados_vbr = . ;
. = _glados_vbr + 0x100 ;
*(.glados.exept)
. = _glados_vbr + 0x400 ;
*(.glados.tlb)
. = _glados_vbr + 0x600 ;
*(.glados.interupt)
. = ALIGN(4)
*(.gdata)
_eglados = ABSOLUTE (.) ;
} > my_ram
}
Je me suis ajouté une parite .gdata pour y mettre ma structure (et plus si affinité) mais je sais pas s'il faudrait mieux créer une autre section spécifique, histoire de pas tout mélanger (je verrai au fur et à mesure du projet ). J'ai aussi ajouté tous les ABSOLUTE() et essayé de faire des AT() pas trop moche.
Si tu veux tout faire à la main, oui. Sinon tu peux exporter les bons symboles e* et b* et réutiliser celui-ci.
Du coup j'ai réécrit un crt0.c en me basant sur mon crt0.s et ton crt0.c.
Je copy toute la section.data ainsi que mon handler et je mets à 0 toutes la section.bss, Je save tous les registres (r8 ~ r15), vbr, ect... avant d'initialiser mon handler puis appeler le main(). À la fin du main je restore tous les registres, vbr, ect... et je free() ce dont'il y a besoin dans mon handler
#include "../../include/stream.h"
extern int main(void);
extern uint32_t bbss;
extern uint32_t ebss;
extern uint32_t bdata;
extern uint32_t edata;
extern uint32_t rodata;
extern uint32_t bglados;
extern uint32_t eglados;
extern uint32_t glados_vbr;
__attribute__((section(".pretext"))) int ini_glados(void)
{
volatile uint32_t *bss = &bbss;
volatile uint32_t *data = &bdata;
volatile uint32_t *src_data = &rodata;
volatile uint32_t *glados = &bglados;
volatile uint32_t *glados_end = (uint32_t)&bglados - (uint32_t)&eglados;
volatile uint32_t *data_end = (uint32_t)&edata - (uint32_t)&bdata;
volatile uint32_t *bss_end = (uint32_t)&ebss - (uint32_t)&bbss;
int exit;
while (glados < glados_end)
*glados++ = *src_glados++;
while (data < data_end)
*data++ = *src_data++;
while (bss < bss_end)
*bss++ = 0;
init_stream();
save_stuff();
exit = main();
restore_stuff();
quit_stream();
return (exit);
}
C'est ça. Ce ne sont pas des déplacements mais des copies, même si dans l'idée on ne servira pas des parties en ROM. (D'ailleurs .bss n'est pas copié mais juste mis à zéro, cela te permet de l'éliminer du fichier g1a, j'y reviendrai peut-être plus trad.)
Si ça te dérange par j aimerait bien avoir une explication de cette section qui est géré bizarrement (au vu de la compilation ou on supprime la section (si j'ai bien suivie) et en plus on le force a etre à 0) ça a quel intérêt de la mettre a 0si on la deja supprimée cette section .bss ?
Je peux avoir le lien où tu as trouvé ça ? Il n'a probablement pas tout mis là, ça aurait cassé des trucs quasiment certainement...
Voilà le script complet (https://lab.knightsofnii.com/fixos-core-team/fixos/blob/master/fixos.ld)
et il me semble bien qui load tout à 0x88000000.
ENTRY(_initialize)
/*
Now, with an external bootloader, and targetting ELF output instead of
raw binary, the linker script is realy simpler.
For the kernel, most important things to know is were the RAM begins,
because it will be fully copied in RAM by the bootloader.
Sections can follow freely (.text, .rodata, .data and .bss)
So, now, the main goal of this file is to reduce the number of sections
and to order them if needed (VBR purpose for example).
*/
MEMORY
{
osram : o = 0x88000000, l = 128k /* for fx-9860G */
}
SECTIONS
{
/* place for command-line arguments */
.cmdargs : {
_cmdargs_begin = . ;
. = . + 1024 ;
_cmdargs_end = . ;
} > osram
.text : {
_kernel_text_begin = . ;
*(.pretext); /* init stuff */
*(.text);
*(.text.*);
/* Exceptions and Interrupt handlers : due tu the SH3 VBR system, we have some restrictions */
/* The handlers should reside at VBR relative position, in P1 or P2 protected space (0x8* or 0xA*) */
/* There are 3 vectors offset called by the processor : VBR + 0x100, 0x400 and 0x600 */
/* The bootstrap routine must copy them at these places, and bytes between handlers are undefined */
/* Aligned in 0x100 because of ld issue*/
. = ALIGN(0x100);
_fixos_vbr = . ; /* VBR value. WARNING : VBR must point to P1 or P2 space! */
. = _fixos_vbr + 0x100 ; /* cleaner than "vbr = . - 0x100" */
_bexhandler = ABSOLUTE(.) ;
*(.handler.exception.pretext);
*(.handler.exception); /* general exception handler, VBR + 0x100 */
_exhandler_size = ABSOLUTE(.) - _bexhandler ;
. = _fixos_vbr + 0x400 ;
_btlbhandler = ABSOLUTE(.) ;
*(.handler.tlb.pretext);
*(.handler.tlb); /* TLB Miss handler, VBR + 0x400 */
_tlbhandler_size = ABSOLUTE(.) - _btlbhandler ;
. = _fixos_vbr + 0x600 ;
_binthandler = ABSOLUTE(.) ;
*(.handler.interrupt.pretext);
*(.handler.interrupt); /* Interrupt Handler, VBR + 0x600 */
_inthandler_size = ABSOLUTE(.) - _binthandler ;
*(.handler.stuff);
_kernel_text_end = . ;
} > osram
.rodata : {
. = ALIGN(4);
_argvector_begin = . ;
*(.vector.kernelarg);
_argvector_end = . ;
. = ALIGN(4);
_sysctl_objs_begin = . ;
*(.sysctl.objs);
_sysctl_objs_end = . ;
*(.rodata);
*(.rodata.*);
. = ALIGN(4);
_symbols_begin = . ;
*(.symbols.entries);
_symbols_end = . ;
*(.symbols.names)
} > osram
.data : {
*(.data);
*(.data.*);
} > osram
.bss : {
. = ALIGN(4);
_bbss = . ;
*(.bss) *(COMMON);
_ebss = . ;
/* symbol for the end of static data */
_end_static_ram = . ;
/* initial kernel stack is set here, inside the .bss section, but can be
* free'd once control is transfered to user-space init */
. = ALIGN(1024);
_begin_stack = . ;
. = . + 4096 ;
_end_stack = . ;
} > osram
}
Citer : Posté le 14/12/2018 22:49 | #
Attention à ne pas mettre des ABSOLUTE() partout, tous les symboles b* et e* doivent en être exempts sinon tu ne sauras pas où copier dans la RAM !
Pas besoin de volatile durant ta copie, les données ne vont pas changer dans ton dos.
Pour caricaturer, on peut dire que c'est la section où on met tout ce qui est initialisé à 0. Il est absolument indispensable de la mentionner dans le linker script car c'est ce qui permet aux symboles dedans d'avoir des adresses assignées. Cependant, les valeurs à proprement parler ne sont que des 0, donc on ne garde pas cette section dans l'ELF. On la vire et on génère les zéros à la main au début de l'exécution.
Ah oui mais c'est le kernel de FiXos là, contrairement à ton add-in il ne rend pas la main au système à la fin de l'exécution. Tu ne peux pas te permettre autant de largesses si tu veux revoir un jour le menu principal...