LOCODUINO

L’assembleur

L’assembleur (2)

Sa forte relation au matériel

.
Par : Christian

DIFFICULTÉ :

Dans l’article précédent, nous avons vu ce qu’était l’assembleur et à quel point il était plus efficace que le code généré par l’IDE. Dans cet article, nous allons commencer à entrer dans le détail de tout ce qu’il faut avoir assimilé avant de se lancer dans cette aventure qu’est la programmation en assembleur. Attention : la lecture de cet article peut avoir un effet secondaire sur les neurones : surchauffe, blocage, insomnies !

Relation entre matériel et logiciel

Programmer en assembleur demande d’avoir une très bonne connaissance des possibilités matérielles du microcontrôleur cible (encore appelées ressources). Il est en effet primordial de bien connaître les ressources de votre microcontrôleur pour savoir ce que vous pourrez lui demander. On a d’ailleurs déjà un peu évoqué les ressources dans la série d’articles Les Timers (I). Par exemple, chaque timer possède son lot de registres de contrôle et utiliser les timers revient à lire ou écrire dans ces registres. Il faut donc connaître tout cela avant de pouvoir utiliser les timers.

Il faut bien comprendre qu’utiliser les ressources d’un microcontrôleur dans un programme écrit en assembleur demande beaucoup de travail. En effet, il devient difficile d’appeler les fonctions des bibliothèques Arduino de l’assembleur et utiliser ces bibliothèques ferait perdre l’intérêt de la compacité du code que nous avons évoqué dans le précédent article. D’abord, il faut étudier et comprendre comment utiliser ces ressources (et seule une lecture attentive de la datasheet du composant permet d’arriver à ce résultat), ensuite il faut mettre en pratique ces connaissances dans un programme qui soit fonctionnel. Un seul bit oublié dans un seul registre peut compromettre le résultat. Heureusement, les datasheets présentent souvent des exemples écrits en C et en assembleur, et on peut donc s’inspirer de ces derniers pour construire son programme.

Mais que faire si une ressource vous manque pour aller au bout de votre projet ? Et bien comme dans la vraie vie : soit vous trouvez la ressource qui vous manque (ce qui implique soit de changer de microcontrôleur cible, soit d’ajouter une carte externe), soit vous apprenez à vous en passer (modification du projet) !

Parmi les ressources à prendre en compte, on a en premier la quantité de mémoire disponible. Cette ressource n’est pas des moindres puisqu’elle limite la longueur de vos programmes donc aussi la complexité de votre projet ou bien elle limite le nombre de vos variables. Nous y reviendrons car il est très important de savoir comment cette mémoire est organisée.

Une deuxième ressource à prendre en compte est la faculté qu’aura le microcontrôleur à communiquer avec l’extérieur, donc le nombre de lignes d’entrée-sortie, comment elles sont organisées et ce qu’on peut en faire. Cela aussi nous en avons parlé dans la série d’articles sur les ATtiny : par exemple, pour faire un feu tricolore de carrefour ou une enseigne de commerçant, il faut au moins 6 sorties numériques et dans ce cas, un ATtiny24/44/84 est préférable à l’ATtiny25/45/85 [1].

Puisqu’on parle de communication avec l’extérieur, on peut aussi évoquer les différents canaux de communication (SPI, UART, I2C, CAN, etc.). Comme on vous l’a montré de nombreuses fois dans nos articles, il est toujours possible de rajouter une ressource avec une carte externe (cas du bus CAN non natif sur les AVR mais qui existe sur les ARM) ; toutefois, il est souvent moins cher de changer de microcontrôleur que de rajouter une carte externe. C’est donc à vous de peser le pour et le contre de chaque solution (trouver la ressource ou s’en passer).

Pour avoir une bonne vue d’ensemble des ressources d’un microcontrôleur, il suffit de consulter la première page de sa datasheet qui présente ces ressources de façon presque publicitaire. C’est en tout cas de cette façon que sont présentées les datasheet Microchip ou Atmel. Bien évidemment, la datasheet elle-même donne plus de précisions et il suffit de chercher à l’intérieur.

Microcontrôleurs AVR

Dans cette série d’articles sur l’assembleur, nous nous limiterons à décrire les microcontrôleurs de la série AVR (qui signifierait Advanced Virtual RISC) [2] bien que nous n’ayons rien trouvé sur la signification de ces trois lettres sur le site d’Atmel-Microchip [3]. En effet, les microcontrôleurs AVR sont les plus employés pour les cartes Arduino et nous prendrons souvent l’ATmega328P en exemple (celui de la carte Uno). Certaines cartes, de marque Arduino ou autre, utilisent des microcontrôleurs de la série ARM (initialement Acorn RISC Machine) [4] qui diffèrent par leur architecture et leurs ressources. Bien évidemment, si vous persévérez dans la programmation en assembleur, vous comprendrez aisément comment utiliser une ou l’autre série et comment tenir compte de leurs différences dans votre programmation.

Les microcontrôleurs AVR sont des microcontrôleurs RISC (voir article L’assembleur (1)) à architecture Harvard. Cela signifie que l’espace mémoire réservé aux données (mémoire statique) est physiquement différent de l’espace mémoire réservé au code du programme (mémoire flash). En conséquence, on trouve deux bus reliant l’unité centrale (appelée MCU pour Microcontroller Unit) aux zones de mémoire : un bus de données et un bus d’instructions. L’intérêt d’une telle architecture est une augmentation de la vitesse d’exécution des programmes : on peut simultanément rechercher le code d’une instruction et les données qu’elle manipule. Un inconvénient de cette architecture est que les constantes sont copiées en RAM (qui n’est jamais très étendue) lors du boot ou bien qu’il faut faire des manipulations non standard pour y accéder. [5]

Les microcontrôleurs AVR sont réalisés en technologie CMOS haute vitesse (faible consommation, vitesse de travail importante et haute intégration) et ils disposent de 32 registres internes de 8 bits appelés registres généraux, 64 registres internes d’entrée-sortie (I/O) et 160 registres internes d’entrée-sortie étendue (ces 256 emplacements mémoires sont différents de la mémoire utilisée pour stocker des données).

Cette série AVR propose de petits microcontrôleurs aux possibilités limitées comme les ATtiny dont le nombre de broches est restreint ou bien de plus importants comme les ATmega qui peuvent avoir 100 broches comme l’ATmega2560 des cartes Mega. Cependant, pour ne pas restreindre les possibilités du microcontrôleur, les broches sont très souvent multiplexées, ce qui signifie qu’elles sont capables de remplir des rôles différents. Bien évidemment, une broche utilisée pour une fonction ne peut pas être utilisée pour autre chose (par exemple, si vous utilisez la capacité de communiquer en I2C, les deux broches utilisées pour véhiculer les signaux SCL et SDA de l’I2C ne peuvent pas servir à relever la valeur d’un capteur simple (non I2C)).

Une dernière chose est à considérer : la fréquence de travail du microcontrôleur qui peut aller jusqu’à 20 MIPS (Mega Instructions Per Second) pour les circuits les plus rapides. L’horloge qui équipe la carte Arduino Uno a une fréquence de 16 MHz et règle la vitesse à laquelle le microcontrôleur va exécuter les différentes instructions ; à 16 MHz, un cycle d’horloge dure 62,5 ns (nanoseconde) et les instructions n’ont besoin que d’un seul cycle d’horloge pour être exécutées (de très rares instructions en nécessitent plus). On peut donc calculer avec précision combien de temps durera l’exécution d’un programme ou d’un sous-programme, ce qui permet d’avoir des timings extrêmement bien calibrés comme nous le disions dans l’article L’assembleur (1). À ce sujet, il est important de comprendre que la fréquence du quartz relié à un microcontrôleur (réglant la fréquence de travail) n’est pas le seul élément pour déterminer la rapidité d’un microcontrôleur ; en effet, un AVR exécute les instructions en un cycle d’horloge alors qu’un PIC fait de même avec un cycle machine qui vaut quatre cycles d’horloge et dans ces conditions, un AVR avec un quartz de 20 MHz est deux fois plus rapide qu’un PIC avec un quartz de 40 MHz !

Après ce rapide survol des microcontrôleurs AVR, nous allons rentrer dans plus de détails et nous allons commencer par la mémoire puisque programmer revient à manipuler des données stockées en mémoire.

Organisation de la mémoire

Tous les AVR sont munis de trois types de mémoire et seule la quantité diffère d’un modèle à l’autre. On trouve de la mémoire flash (analogue à la mémoire d’une clé USB) qui permet de stocker les instructions du programme et de les conserver même hors tension, de la mémoire vive réservée aux données (mémoire SRAM qui perd ses données lorsqu’elle n’est plus sous tension) et de la mémoire EEPROM qui conserve les données même hors tension. Voyons cela en détail car c’est important pour notre programmation.

Mémoire de programme
Rappelez-vous que cette mémoire est indépendante de toute autre mémoire, donc ses adresses lui sont propres. Mais les instructions sont codées sur 16 (parfois 32) bits, ce qui permet un codage plus efficace. L’espace mémoire de programme est donc organisée en 16K X 16 et commence donc à l’adresse 0x0000 jusqu’à une adresse qui dépend de la quantité disponible (par exemple 0x3FFF pour un ATmega328P qui dispose de 32 Kbytes).

Notre ATmega328P peut donc stocker 16384 instructions maximum (certaines instructions assez rares nécessitent plus que deux octets) et il faut se rappeler que le bootloader occupe également une petite partie de notre mémoire programme. C’est pourquoi la mémoire flash est organisée en deux sections, la section du programme de boot et la section du programme d’application, comme le montre la figure 1.

Pour exécuter un programme, le microcontrôleur va donc aller chercher les instructions les unes à la suite des autres, en tout cas dans un certain ordre déterminé par le programme (il peut y avoir des boucles ou des sous-programmes). Donc pour connaître l’instruction qu’il doit exécuter, le microcontrôleur a besoin d’avoir un compteur ordinal appelé PC (Program Counter) qui n’est rien d’autre qu’un registre dans lequel on met l’adresse de l’instruction qu’on traite et qu’on incrémente pour aller chercher l’instruction suivante.

Pour exécuter un sous-programme, le PC doit être chargé avec l’adresse de la première instruction du sous-programme mais auparavant, il faut sauvegarder l’adresse qui se trouvait dedans car à la fin du sous-programme, il faudra revenir au programme principal, exactement là où on l’a quitté. Retenez bien cela car on va en reparler dans le paragraphe suivant.

Figure 1
Figure 1
Organisation de la mémoire flash.

Mémoire de données
Comme son nom l’indique, la mémoire de données SRAM sert à stocker des données, c’est-à-dire des variables utilisées par votre programme (nombres, caractères, etc.). Par exemple, sur un ATmega328P, cette mémoire commence à l’adresse 0x0100, juste après les 256 registres internes (voir plus loin) dont les adresses vont de 00 à FF (toutes les cellules mémoires ne sont pas utilisées) [6]. Cette mémoire SRAM dont l’utilisateur peut disposer est donc en plus des registres comme on peut le voir sur la figure 2.

Figure 2
Figure 2
Organisation de la mémoire de données.

L’ATmega328P dispose de 2Kbytes de mémoire SRAM et vous vous dites sans doute que vous pouvez donc stocker 2048 octets de données. Et bien non, car le microcontrôleur a aussi besoin d’un peu de mémoire pour stocker certaines données comme le PC par exemple en cas d’appel de sous-programme. Et s’il y a des appels de sous-programme par d’autres sous-programmes, il faut stocker plusieurs fois la valeur du PC pour retrouver tout cela lorsqu’on sort des sous-programmes. Toutes ces données sont organisées en pile (on y reviendra) et il faut connaître en permanence la dernière adresse en mémoire vive utilisée pour stocker dans la pile, adresse qui est conservée dans un registre appelé SP (Stack Pointer).

Quelques mots au sujet de la pile qui permet de stocker des informations intéressantes. Comme on vient de le dire, la pile est une portion de la mémoire vive différemment gérée en fonction du microcontrôleur. Les petits ATtiny disposent d’une pile matérielle, contenant sa propre zone de mémoire vive, et son pointeur est géré automatiquement lors des appels ou retours de sous-programme. Cela entraîne un certain confort de programmation mais en contrepartie, cette pile est de taille définie limitant le nombre de valeurs à y stocker. Cette pile matérielle a une profondeur de trois mots ce qui limite à trois le nombre de sous-programmes imbriqués les uns dans les autres. Il faut bien évidemment en tenir compte.

Mémoire EEPROM
Cette mémoire sert à stocker des données de façon permanente puisque les informations ne sont pas perdues lors de la mise hors tension. Avec l’IDE d’Arduino, nous avions une bibliothèque qui se chargeait des cycles de lecture ou d’écriture de cette mémoire. Là, il faut tout gérer par nous-mêmes. Il faut cependant se rappeler une chose : ce type de mémoire est prévu pour 100 000 cycles d’écriture ou effacement. On peut penser que c’est beaucoup mais si on programme une petite boucle pour aller écrire une donnée dans la même cellule mémoire, on arrive rapidement à ce nombre vu que les microcontrôleurs travaillent rapidement. Il faut donc être extrêmement précautionneux quand on utilise la mémoire EEPROM mais c’était déjà le cas avec l’IDE.

Registres internes
Les AVR disposent de 32 registres à usage général (ou si vous préférez des registres de travail) et ce qui est plutôt cool, c’est que ces registres s’utilisent de la même façon (enfin presque) et que tous ont accès à l’ALU (Arithmetic and Logic Unit) ou encore unité arithmétique et logique, là où se font la plupart des calculs (d’arithmétique, de logique ou de manipulation de bits). Ceci offre une grande souplesse de programmation. Le choix de projeter les registres à usage général en mémoire reliée à la fois au bus de données et ayant accès à l’ALU est une caractéristique rare pour des microcontrôleurs et peut-être même exclusive aux microcontrôleurs Atmel-Microchip.

La figure 3 représente l’architecture interne d’un AVR et on peut déjà y voir ce dont nous avons parlé (les différentes mémoires, le Program Counter, les 32 registres à usage général, la SRAM et l’EEPROM. 

Figure 3
Figure 3
Architecture interne du microcontrôleur.

Les registres de travail sont appelés de R0 à R31 et bien-sûr sont des registres de 8 bits à accès rapide. Les adresses de ces registres commencent à 0x00 (pour R0) à 0x0F (pour R15) puis 0x10 (pour R16) à 0x1F (pour R31). Les six derniers registres (R26 à R31) peuvent être utilisés comme trois registres de 16 bits pour adresser l’espace mémoire de données, ce qui permet des opérations d’adressage plus rapides ; ils sont alors appelés X-register, Y-register ou Z-register. Cependant, sur les ATtiny, seul le registre Z est présent et est composé des registres R30 et R31. La figure 4 montre les 32 registres de travail.

Figure 4
Figure 4
Les 32 registres de travail.

Ces 32 registres de travail s’utilisent de façon identique et sont interchangeables ; néanmoins, quelques instructions lorsqu’elles sont utilisées entre un registre et une constante ne peuvent être utilisées qu’avec la deuxième moitié des registres de travail, donc ceux compris entre R16 et R31.

Registres d’état
L’ALU peut réaliser des calculs arithmétiques entre deux registres ou bien un registre et une constante. Après chaque opération arithmétique, un registre spécial appelé Status Register (SREG) est mis à jour en fonction du résultat de l’opération qui peut être zéro, un débordement (overflow), une retenue (carry), etc. Lorsqu’on programme en assembleur, il est fréquent d’avoir à surveiller les différents bits de SREG (voir figure 5).

Figure 5
Figure 5
Le registre d’état.

Un deuxième registre d’état, appelé MCUSR (pour MCU Status Register) permet d’identifier la source d’un RESET.

Registres de contrôle
Tous les circuits de la famille AVR possèdent un registre de contrôle appelé MCUCR (pour MCU Control Register) permettant de définir certains comportements de l’unité centrale. L’organisation de ce registre dépendant du modèle de microcontrôleur, un recours à la datasheet est nécessaire pour comprendre l’organisation des bits du registre.

Il existe aussi bien d’autres registres de contrôle, par exemple pour gérer les interruptions ou bien pour utiliser les timers mais le mieux sera de les découvrir au fur et à mesure des besoins.

Conseils aux programmeurs en assembleur

Nous venons de faire un rapide survol des possibilités matérielles des microcontrôleurs et nous n’avons pas tout évoqué puisque nous avons surtout décrit les différentes zones de mémoire. Chaque ressource du MCU possède ses propres registres de contrôle et pour utiliser ces ressources, il faut bien comprendre l’utilisation de leurs registres de contrôle. Nous vous l’avions déjà un peu montré dans la série d’articles sur les timers où nous allions directement modifier ces registres pour obtenir le bon mode et la bonne vitesse de comptage. Seule une étude approfondie de la datasheet vous permettra de comprendre comment écrire vos programmes ; cela demande du temps et une certaine énergie. Mais c’est indispensable pour programmer en assembleur !

Pour voir si vous avez bien assimilé les notions décrites dans cette deuxième partie, voici une petite question : quelle est la taille du PC sur le MCU ATmega328P ? Bien évidemment, la réponse doit venir de vous, pas de la datasheet du composant…

Dans le prochain article, nous commencerons à voir le jeu d’instructions des microcontrôleurs AVR et nous ferons un premier programme. Cela vous permettra de mettre le pied à l’étrier car il faut bien commencer le chemin pour parcourir la route qui promet d’être longue. Mais plus vous parcourrez cette route, plus vous la trouverez passionnante.

[1L’ATtiny25/45/85 dispose de six entrées-sorties, mais l’une d’elle est utilisée pour le RESET du microcontrôleur ; en pratique, seules cinq E/S sont aisément accessibles.

[2d’après Christian Tavernier dans son livre Microcontrôleurs AVR : des ATtiny aux ATmega chez Dunod

[3Une autre interprétation de ces trois lettres serait qu’elles proviennent des prénoms des concepteurs de l’architecture, deux étudiants du Norvegian Institute of Technologie, Alf-Egil Bogen et Vegard Wollan, d’où le terme AVR pour Alf and Vegard’s RISC processor.

[4Processeurs ARM - Architecture et langage d’assemblage de Jacques Jorda chez Dunod

[5Pour plus de détails sur l’architecture Harvard, vous pouvez consulter https://fr.wikipedia.org/wiki/Archi...

[6Le nombre de registres internes dépend du modèle de microcontrôleur, il est donc nécessaire de consulter les datasheets pour savoir où commence la SRAM.

17 Messages

  • L’assembleur (2) 1er février 2021 16:38, par trimarco232

    Bonjour,
    la réponse, c’est 14 ; mais je peux me tromper, il faudrait d’autres avis

    Répondre

    • L’assembleur (2) 1er février 2021 17:38, par Christian

      Super, cet article a été lu au moins une fois ! ;-)
      Et bien, la réponse dans l’article suivant... comme cela, ça permet à d’autres de se faire une idée.
      En admettant qu’il y en ait d’autres pour le lire ! ;-))

      Répondre

  • L’assembleur (2) 5 février 2021 13:02, par bricoleau

    Bravo pour le courage d’écrire un article technique aussi pointu. Ca mérite bien un troisième lecteur :-)
    Perso j’aurais voté pour 16 bits, qui est la taille d’adressage standard d’un avr, pour contenir l’adresse en flash de l’instruction en cours d’exécution.
    Mais sans certitude, d’autant qu’il m’est déjà arrivé de devoir jongler avec des "far pointer" sur une mega car le compilé dépassait les 64k.

    Répondre

    • L’assembleur (2) 5 février 2021 14:18, par Christian

      On n’est pas loin du fan-club ! ;-)
      En écrivant cette série sur l’assembleur, je voulais amener Locoduino en dehors de ce que nous avons déjà décrit et qui s’applique au modélisme ferroviaire. Et il est vrai que nous avons abordé tous les sujets possibles sur un réseau de trains miniatures : commande de trains, animation de réseau, automatisme...
      En tout cas, merci à ceux qui témoignent de leur intérêt : nul doute que cela me motivera à trouver des suites à cette série. Car le sujet est vaste.

      Répondre

      • L’assembleur (2) 5 février 2021 15:00, par bernard

        Il n’est pas impossible en effet qu’il s’agisse d’un registre 16 bits dont on n’utilise que les 14 bits de poids faible.
        C’est loin d’être clair cette histoire !!!

        Bernard

        Répondre

        • L’assembleur (2) 6 février 2021 13:08, par bricoleau

          Cela expliquerait aussi comment un atmega2560 peut adresser jusqu’à 256k=2^18 octets de mémoire flash, en utilisant les deux bits de poids fort d’un registre PC à 16 bits

          Répondre

          • L’assembleur (2) 6 février 2021 16:01, par Christian

            Désolé de vous dire qu’il faudra attendre une semaine de plus pour avoir la réponse, dans l’article suivant qui sera publié le 14 février. En effet, demain, je passe mon tour pour laisser la place à Dominique et Michel qui publient un article commun. Suspense...

            Répondre

        • L’assembleur (2) 10 février 2021 20:04, par trimarco232

          16 dont 2 qui ne servent pas, ça ne fait jamais que 14 ... il faut que le pc puisse repartir automatiquement à 0 quand il est arrivé au bout, si non il va se cogner 48k de nop !
          par contre je ne sais pas comment est organisé la flash : il faut peut-être quand-même un bit de poids faible pour aller chercher chaque demi mot (octet) ... mais là c’est purement silicium, le programme n’a pas en tenir compte
          ce qu’il faut c’est trouver les 32/2 k emplacements, et pour ça il faut 14 bits, donc je persiste

          Répondre

  • L’assembleur (2) 7 février 2021 18:24, par Bernard

    L’article est génial, hyper précis, pointu dans la prog, un grand merci.
    Je mesure le temps passé à écrire ce genre d’article, un grand bravo.
    Après, la technique du teasing on s’en fout royalement c’est vraiment pas le prob

    Bernard

    Répondre

  • L’assembleur (2) 7 février 2021 19:21, par msport

    Pour le teasing, seuls les initiés peuvent comprendre ...
    En tout cas, je me joins aux éloges ! Comme tous ceux qui ont trempé dans les Z80, 6502, 6809 et autres 68705.

    Répondre

  • L’assembleur (2) 12 février 2021 06:48, par Rémi

    Bonjour Christian, ne t’inquiètes pas. On lit tes articles. Pour ma part, j’attends la suite, avec impatience. Je vois qu’il y a des vétérans comme moi qui ont des anecdotes à raconter. En tout cas, bravo pour la précision. De toute façon, quand on utilise l’assembleur, il faut être très précis, à la moindre erreur on part dans les choux. Et puis, il n’y a pas de garde fou avec l’assembleur.
    Rémi

    Répondre

    • L’assembleur (2) 12 février 2021 10:17, par Christian

      Je savais que ces articles rencontreraient du succès. Mais effectivement, l’assembleur demande beaucoup de connaissances et la difficulté d’écriture est d’en dire assez sans pour autant noyer dans les détails. Je pense que l’essentiel permet de débuter, mais ensuite il faut approfondir. C’est pourquoi j’ai classé la série pour des experts parce qu’un expert est capable d’aller chercher de lui-même ce qu’il lui manque.
      J’ai décidé d’un article supplémentaire à cette série et ce n’est pas simple : je dois en dire suffisamment pour au moins susciter l’intérêt du lecteur, mais je ne peux rentrer dans tous les détails qui sont nombreux. Après, il est nécessaire de ne pas se contenter d’une simple lecture des articles : il faut pratiquer. C’est vrai, il n’y a pas de garde fou en assembleur, mais d’un autre côté, si cela ne fonctionne pas, rien ne se détériore : ce n’est qu’un programme.
      Peut-être qu’une section "assembleur" dans le forum permettrait de partager nos expériences. A méditer...

      Répondre

  • L’assembleur (2) 6 mars 2021 06:34, par BENET daniel

    Maintenant retraité et ferrovipathe (personne n’est parfait), j’ai (dans un passé lointain) beaucoup développé (automatismes industriels) en assembleur Z80.
    J’avais abandonné tout cela en raison du coût des outils de développement que j’utilisais professionnellement (Mostek).
    La découverte des Arduino et de leur très faible coût de mise en œuvre (en particulier de provenance directe chinoise) a réveillé ma curiosité (pas tout à fait morte !).
    Votre énorme travail de francisation et explications m’a décidé à "repiquer au truc". Giga-mercis !
    Après quelques mois passés à digéré l’UNO et le C++ (réalisation perso d’un PN), je me suis demandé si l’assembleur était toujours une voie aussi passionnante (et efficace).
    Me voici donc, de nouveau dévorant votre cours et .. c’est toujours avec le même plaisir !
    Encore Merci pour cet énorme boulot.
    Cordialement.
    Daniel

    Répondre

    • L’assembleur (2) 6 mars 2021 11:31, par Christian

      Heureux d’avoir contribué à ranimer la flamme !
      Cette série d’articles convient bien à ceux qui ont eu une expérience en assembleur avec d’autres µC ou µP et le Z80, je connais aussi... ;-)
      Et ce qui était difficile dans le passé à cause d’outils inexistants ou trop chers est maintenant à portée de tous, comme en témoignent les solutions que nous avons publiées dans ces articles.
      Alors bonne programmation et rendez-vous sur le forum si vous voulez discuter d’un sujet plus précis avec tous ceux qui s’y intéressent (et ils sont plus nombreux qu’on ne le croit).

      Répondre

Réagissez à « L’assembleur (2) »

Qui êtes-vous ?
Votre message

Pour créer des paragraphes, laissez simplement des lignes vides.

Lien hypertexte

(Si votre message se réfère à un article publié sur le Web, ou à une page fournissant plus d’informations, vous pouvez indiquer ci-après le titre de la page et son adresse.)

Rubrique « Programmation »

Les derniers articles

Les articles les plus lus