lundi 18 décembre 2017

Chapitre 13 : dessiner en assembleur avec le FrameBuffer -1-



Dans la première partie, nous avons découvert les principes pour réaliser des petits programmes en assembleur avec des données en entrée et des données en sortie dans la console ou dans des fichiers. Maintenant nous allons essayer de découvrir des utilisations plus poussées sur le Raspberry : dessin, gestion de fenêtres, gpio.
Déjà, nous avons pu remarquer que dans les instructions assembleur ARM, il n’y a pas d’instructions d’entrée-sorties pour accéder à des interfaces divers : écran, clavier, gpio, usb , réseau. Et donc tout me reste à découvrir.
Voyons déjà comment dessiner sur l’écran du Raspberry. Les premières recherches pour lire et écrire directement dans les adresses mémoires réservées à l’affichage sont des échecs. En effet, un mécanisme de protection géré par Linux interdit à un utilisateur non habilité à accéder aux adresses mémoire importantes pour le fonctionnement de l’ordinateur. Mais une autre solution est proposée, c’est d’utiliser une notion appelée frameBuffer  qui permet d’accéder à ces adresses en effectuant un mapping (mappage ?) entre les adresses physiques et des adresses en mémoire dont l’accès est autorisé. Voyons le source du programme.
Nous allons commencer par afficher quelques caractéristiques de l’affichage et pour cela nous décrivons 2 structures de données différentes, une pour les informations fixes de l’écran, l’autre pour les informations variables.
Dans la partie .data, nous décrivons les messages pour afficher les informations principales et dans la section .bss, nous réservons la place pour le stockage des 2 structures définies plus haut. En effet les structures ne servent qu’à attribuer des noms et des déplacements à des zones en fonction de leur emplacement et il faut donc réserver la place en mémoire pour attribuer un pointeur qui nous servira lors des appels.
Dans la partie code, nous ouvrons le périphérique concernant notre écran d’affichage. En fait c’est comme ouvrir un fichier mais avec un nom standard : /dev/fb0. Et pour nous permettre de travailler dessus, nous lui attribuons les autorisations de lecture écriture. Ensuite nous allons utiliser l’appel system IOCTL qui permet d’obtenir les informations sur un périphérique à l’aide de codes fonction spécifiques, d’une part les données fixes qui vont alimenter la structure fix_info puis les données variables dans la structure var_info. Comme exemple, nous affichons quelques données : identification, largeur et hauteur de l’affichage et nombre de bits par pixel.
Puis nous allons effectuer un mapping du périphérique dans une zone mémoire en utilisant l’appel systeme MMAP. Il faut lui fournir plusieurs paramètres dont la taille en octet nécessaire. Nous trouvons cette information dans les données fixes récupérées auparavant (zone FBFIXSCinfo_smem_len). Vous remarquerez que pour toutes ces opérations, nous testons le code retour des appels pour vérifier si tout se passe bien, cela évite de chercher trop longtemps en cas de dysfonctionnement. Le mapping permet de lire et modifier le contenu de l’affichage dans la mémoire et les modifications sont répercutées dans le périphérique. (il faudrait approfondir ce mécanisme et voir aussi le rôle de l’appel système SYNC).
Enfin pour vérifier si tout cela permet d’intervenir sur l’affichage, nous nous contentions de modifier les pixels sur un huitième de l’écran avec une couleur bleue (code rgb 0xFF). Évidement quand vous lancez ce programme depuis la console de la connexion ssh, rien ne se passe. Donc dans un premier temps, j’ai installé le serveur tightvncserve sur le raspberry et sur mon ordinateur portable pour afficher les images écran en mode graphique.
Cela n’a rien donné, j’ai donc vérifié plusieurs fois le programme, et j’étais désespéré de ne rien obtenir. Puis j’ai fait un dernier test en branchant directement un écran sur le Raspberry et là miracle !! les pixels bleu sont apparus. Je pensais que cette méthode d’affichage ne fonctionnait que sur un écran connecté au Raspberry par la prise HDMI. Mais en rédigeant ce post, je me suis repenché sur le problème. En fait le serveur tightvncserver utilise un port différent (display :1) de celui utilisé pour l’affichage direct (display :0) ce qui explique le dysfonctionnement. Mais j’ai trouvé sur le blog de framboise14 un post sur l’utilisation du serveur X11vnc qui permet d’afficher le display :0 à distance et cela fonctionne correctement.
Et maintenant quelle suite à donner : et bien vous savez modifier des pixels !! donc il ne reste plus qu’à écrire toutes les fonctions d’affichage classiques ( pixels, droites, cercles, polygones etc etc.) car aucune fonction du système n’est disponible pour cela !!!!   ou alors il faut utiliser les fonctions d’une librairie graphique.
Vous trouverez des exemples d’affichage par cette méthode en langage C sur le site : http://raspberrycompote.blogspot.fr/2013/ et il faudra adapter les programmes à l 'assembleur.
Exercices : Modifier le programme pour afficher des trames de couleurs différentes.
                 Pour ceux qui aiment les algorithmes, écrire les fonctions pour tracer une droite, un cercle une ellipse, un rectangle etc etc.

mardi 5 décembre 2017

Réflexions sur cette première partie



Ces premiers chapitres nous ont permis de découvrir les instructions de l’assembleur et son intégration dans le système Linux. Cela aussi nous permet de dresser quelques consignes pour faciliter la programmation :
-          Commenter chaque instruction car il est difficile quelques temps après de comprendre le fonctionnement.
-          Programmer des routines d’une cinquantaine de lignes et sinon la décomposer en plusieurs sous routines
-          Utiliser les registres r0 à r4 pour les calculs courants, les registres r5 à r7 pour les indices de boucles et les registres plus élevés pour des sauvegardes de registre ou pour des données utilisées tout le long du programme.
-          Préparer des squelettes de programmes qui serviront de modèles.
 Et nous avons découvert quelques erreurs dont les plus fréquentes sont :
Confondre la récupération d’une adresse avec sa valeur : ldr r0,=donnée  et ldr r0,donnée.
Oublier le b derrière le ldr ou le str pour travailler sur un seul octet
Oublier le s pour mettre à jour les flags après une opération.
Utiliser un registre déjà utilisé par ailleurs.
Ne pas gérer correctement la pile lors du retour de fonctions.
Ne pas sauvegarder (ou restaurer) le registre lr dans une sous routine.
Oublier la partie lsl #2 dans une instruction de stockage ou de lecture d’un poste dans une table de pointeurs.
Confondre les codes conditions après un test (blt, bgt, ble, bge)
Confondre le branchement à une sous fonction (bl) avec le branchement après un test (ble)
Essayer de modifier une zone située dans la partie code (Il n’est possible que d’y mettre des constantes).
Oublier que les octets d’un registre sont stockés dans l’ordre inverse en mémoire.
Confondre l'affectation d'une constante avec une reservation de place :
toto: .int  200 * 4    @ affecte la valeur 800 à l'adresse toto sur 4 octets.
truc: .skip 200 * 4  @ réserve 800 octets de mémoire commençant à l'adresse truc 


Dans la première partie, nous avons découvert les principes pour réaliser des petits programmes en assembleur avec des données en entrée et des données en sortie dans la console ou dans des fichiers. Maintenant nous allons essayer de découvrir des utilisations plus poussées sur le Raspberry : dessin, gestion de fenêtres, gpio.
Déjà, nous avons pu remarquer que dans les instructions assembleur ARM, il n’y a pas d’instructions d’entrée-sorties pour accéder à des interfaces divers : écran, clavier, gpio, usb , réseau. Et donc tout est à découvrir.