Posté le 10/08/2018 20:17
Planète Casio v4.3 © créé par Neuronix et Muelsaco 2004 - 2024 | Il y a 206 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
Citer : Posté le 10/08/2018 21:54 | #
T'essaie d'accéder à une zone de mémoire non-valide. Sûrement un pointeur qui foire. N'hésite pas à mettre des breakpoints pour voir où ça merdoie. C'est peut-être le seul avantage du SDK par rapport au fxsdk
Citer : Posté le 11/08/2018 11:43 | #
J'adore la contradiction complète entre le SDK qui dit « c'est ffff'ffe4 » et la SysERROR qui dit « c'est 0810'0689 ». Bon, tiens-toi bien Shadow, je vais tenter d'expliquer en contournant les contradictions partout...
L'erreur est ADDRESS (W), ce qui signifie que tu (ou une fonction de ML que tu as appelée) as tenté d'écrire à un endroit de la mémoire, mais que ça n'a pas marché parce que l'adresse n'est pas bonne. Alors où et pourquoi ?
Le SDK te dit que l'adresse que tu as tenté d'utiliser est ffff'ffe4 (tu as oublié un f). Pour des raisons que je vais pas expliquer, il est super peu probable qu'une écriture à cet endroit échoue.
La SysERROR dit que l'adresse en question est 0810'0689. Cette adresse se situe dans la zone de RAM qui est utilisée pour stocker les variables statiques et globales, c'est donc un endroit où tu as normalement le droit d'écrire.
Si on regarde ce qui se passe dans ML, il y a effectivement des écritures dans la mémoire à l'endroit indiqué : mais c'est dans la VRAM, et la VRAM ne devrait jamais recouvrir l'adresse 0810'0689.
Je sais de plus que si l'adresse n'existait pas, tu aurais eu une TLB ERROR et non une ADDRESS (W).
Cette erreur ADDRESS (W) est plutôt typique des accès mal alignés : quand tu écris dans la mémoire, tu écris soit 1, soit 2, soit 4 octets d'un coup. Mais quand tu écris 2 octets, tu dois le faire à une adresse multiple de 2, sinon la mémoire ne sait pas faire. Pour écrire 4 octets, il faut être sur une adresse multiple de 4. Or l'adresse 0810'0689 est impaire donc on ne peut y accéder qu'octet par octet.
Mon hypothèse est donc que tu as tenté de modifier une variable globale mais que sans faire exprès tu as causé un accès mal aligné.
-
Bon. Maintenant, il y a plusieurs choses incohérentes dans ce rapport de SysERROR : d'abord le SDK et l'OS ne sont pas d'accord sur l'adresse fautive, ensuite la valeur de PC annoncée est du bullshit.
Quitte à supposer qu'ils se sont également plantés sur l'erreur exacte, on peut penser que c'est la ligne line = bmp[i] << shift qui a planté. (Ce qui aurait dû être signalé sous la forme ADDRESS (R).)
Pourquoi ? Parce que bmp est un tableau de unsigned short autrement dit des éléments qui font tous 2 octets. On ne peut donc lire les éléments que si leur adresse est multiple de 2 : et justement, là on a un problème avec un accès à une adresse impaire.
Ma seconde hypothèse est donc : tu as déclaré ton bmp avec const unsigned char bmp[] = { ... }, ce qui signifie "un tableau dont les éléments font un octet chacun". Le compilateur a alors choisi de mettre le tableau sur une adresse impaire, ce qui est entièrement légitime mais empêche d'y accéder 2 octets par 2 octets comme le veut ML.
Citer : Posté le 11/08/2018 14:08 | #
... Nan, mais le BASIC Casio, c'est pas si mal en fait...
Dans le code j'utilise la fonction ML_bmp_or_cl(piece,x,y,128,64);
Où pièce est un tableau déclaré en : char pièce[ ]={0x...}; Je ne déclare par le "const unsigned" c'est de ce côté qu'il faut fouiller ?
x et y sont les coordonnées (des entiers)... Je n'y connais pas grand-choses merci de vos aides
Le coup des variables globales... Vous allez encore m'envoyer ch*** mais je met toutes mes variables en global je sais, c'est pas bien mais je le fait quand même...
Citer : Posté le 11/08/2018 14:15 | #
Justement, ML_bmp_or attends bien un char* (source)
Citer : Posté le 11/08/2018 14:20 | #
Plus ça va moins ça va...
Je ne comprends pas trop comment je peux faire pour contourner l'erreur...
Par contre avant que cela plante j'avais des ML_bmp_16_or_cl(bmp,x,y);
avec "bmp" déclaré en "char"
Citer : Posté le 11/08/2018 14:42 | #
Pas d'inquiétude. Il y a peut-être 5 personnes sur ce forum qui arriveraient à suivre ce que je raconte, alors pas de panique.
char signifie « un nombre sur un octet ». Avec un octet de stockage, on peut représenter 256 séquences de 0 et de 1 différentes. D'après la convention qui nous dit comment on représente des entiers avec des bits, ça fait des nombres qui peuvent aller de -128 à 127. Voilà ce qu'est un char.
unsigned signifie que les nombres négatifs ne nous intéressent pas. Dans ce cas on change la convention utilisée pour représenter les nombres ; les nombres non signés sur un char peuvent aller de 0 à 255. Ça c'est un unsigned char.
const signifie que l'on n'a pas l'intention ou le droit de modifier la variable. Cela incite le compilateur à stocker le tableau dans une zone de mémoire où on ne peut pas écrire, comme la ROM, au lieu de le mettre dans la RAM, qui est très petite et très convoitée !
Jusque-là, ça va ?
Pour une image pour MonochromeLib, ce qu'on veut, c'est juste avoir une séquence de bits (un bit par pixel avec 1=noir et 0=blanc) bien précise dans la mémoire. Mais bon, en C, on ne peut pas dire comme ça « je veux avoir la séquence 100010100101 dans la mémoire ».
Le Sprite Coder (ou outil équivalent) que tu as utilisé a généré à la place un tableau de char. Et il a mis dedans des nombres entiers bien choisis : des nombres qui, une fois représentés en binaire, donnent exactement la séquence de bits qu'il lui faut pour l'image.
Toujours bon ?
Quand MonochromeLib veut afficher l'image, elle va lire les bits de l'image dans la mémoire. Les programmes peuvent lire 1 octet, 2 octets ou 4 octets d'un coup dans la mémoire. (Soit 8 pixels, 16 pixels ou 32 pixels d'un coup.)
Pour des raisons que je n'explique pas, ML_bmp_or() lit toujours 1 seul octet (8 pixels) à la fois. Mais ML_bmp_16_or() est une fonction optimisée spécialement pour les images de 16×16. PierrotLL (l'auteur de ML) a bien compris que pouvoir lire toute une ligne en une seule opération, c'est avantageux ! Et donc ML_bmp_16_or() lit toujours 2 octets (16 pixels) à chaque étape, comme ça elle traite une ligne entière d'un coup !
Tu respires encore ? Mettons une checkpoint là, je n'ai pas tout à fait fini, mais mieux vaut que j'explique ça d'abord. Si tu as des questions, lâche-toi !
L'explication et la solution au prochain post. T'inquiète ça va être très simple à résoudre. :3
C'est pas si grave, mais n'oublie pas d'apprendre un jour pourquoi tu dois aussi utiliser des locales...
Justement, ML_bmp_or attends bien un char*
On parle de ML_bmp_16_or_cl() là ;D
Citer : Posté le 11/08/2018 14:46 | #
J'ai plutôt bien compris je pense... En tout cas ça me rassure d'entendre (de lire) que le problème est simple à résoudre.
Citer : Posté le 11/08/2018 15:43 | #
En fait, j'ai déjà expliqué tout ce qu'il faut. Voilà la fin.
Je rappelle qu'une adresse dans la mémoire correspond à un octet. En face de chaque adresse, un octet. Une variable qui prend deux octets prend deux places consécutives aux adresses n et n + 1.
Alright?
Quand tu fais un tableau de char, tu annonces au compilateur que tu vas accéder à un élément à la fois, soit 1 octet à la fois... et comme je l'ai dit précédemment, tu peux lire un octet à n'importe quelle adresse, donc le compilateur est libre de mettre ton tableau où il veut dans la mémoire, sur n'importe quelle adresse.
Quand tu fais un tableau de short, tu annonces au compilateur que tu vas accéder à un élément à la fois, soit 2 octets à la fois... et tu ne peux lire 2 octets d'un coup que sur des adresses paires. Donc le compilateur va faire attention à mettre ton tableau sur une adresse paire. Comme ça, chaque short du tableau occupe deux adresses consécutives n et n + 1, où n est pair. Car on ne peut lire 2 octets d'un coup que si on commence à une adresse paire.
Quand tu fais un tableau de int, tu annonces au compilateur que tu vas accéder à un élément à la fois, soit 4 octets à la fois... et tu ne peux lire 4 octets d'un coup que sur des adresses multiples de 4. Donc le compilateur va faire attention à mettre ton tableau sur une adresse multiple de 4, car on ne peut lire les 4 octets aux adresses n, n + 1, n + 2 et n + 3 d'un coup que si n est multiple de 4.
Toujours bon ?
ML_bmp_16_or() prend en paramètre un tableau de short, donc aligné sur une adresse paire. Or tu lui as donné un tableau de char... qui peut être n'importe où, et ici pas de bol, il est sur une adresse impaire ! Diantre !
Quand ML_bmp_16_or() veut lire le premier élément, il fait bmp[0], et ça veut dire « lis-moi deux octets consécutifs au début du tableau ». Ça veut dire les deux octets aux adresses bmp et bmp + 1. Et ça tu ne peux le faire que si bmp est une adresse paire, or elle est impaire... vraiment pas de bol !
Il est temps de conclure.
La "vraie" solution serait de faire en sorte que ton tableau commence à une adresse paire. Mais sans toucher au code, ben, c'est un peu difficile.
La solution rapide c'est d'utiliser ML_bmp_or() à la place, car cette fonction-là n'essaie jamais de lire 2 octets d'un coup dans ton tableau. Tant pis si c'est moins optimisé
Citer : Posté le 11/08/2018 15:53 | #
j'essaye ça tout de suite
C'est bon : ça marche
Citer : Posté le 12/08/2018 00:43 | #
Merci pour toutes ces précisions ! Ça va m'aider à interpréter des erreurs.
Du coup quand on cast un char en int, si le char n'est pas aligné sur un multiple de 4 ça plante aussi ? Je pense pas sinon ça arriverait bien plus souvent je suppose.
Tu es un vrai romancier quand il s'agit d'expliquer en plus toi, c'est très clair
Citer : Posté le 12/08/2018 12:03 | #
Non, je pense justement que le cast permet de réaligner la donnée. À l'inverse, ceci devrait planter.
char *ptrc = &c;
int *ptri = ptrc;
*ptri = 43;
(Avec confirmation de Lephe, je suis pas sûr de ce que j'avance)
Citer : Posté le 12/08/2018 14:39 | #
Merci pour toutes ces précisions ! Ça va m'aider à interpréter des erreurs.
Je savais que je ne perdais pas mon temps
Non, caster un char en int, ça ne se fait pas dans la mémoire. Ça revient à charger le char dans un registre et ensuite faire une extension de signe pour obtenir une représentation de la même valeur numérique, mais sur 32 bits. Ça ne plantera jamais.
Non, je pense justement que le cast permet de réaligner la donnée. À l'inverse, ceci devrait planter.
char *ptrc = &c;
int *ptri = ptrc;
*ptri = 43;
Voilà, tu as compris le principe. Après il est possible que c soit placé sur une adresse multiple de 4. Et en l'occurrence c est une variable locale, donc placée sur la pile, donc elle très probablement sur une adresse multiple de 4. Ce code risquerait donc bien de marcher.
Sous GCC on peut forcer l'alignement d'une variable à n (si n est une puissance de 2) avec l'attribut :
__attribute__((aligned(n)))