Programmation avec le 6502 : adressage et mathématiques

Magazine
Marque
Hackable
Numéro
32
Mois de parution
janvier 2020
Domaines


Résumé

L'article précédent était assez pessimiste et peut-être même un peu rébarbatif, avec cette liste des instructions du 6502. On était arrivé à la conclusion que les possibilités de ce processeur étaient finalement assez limitées : opérations seulement sur 8 bits, pas de multiplications ni de divisions, pas de nombre à virgule, très très peu de registres utilisables (A, X et Y).


Body

Aujourd'hui, nous allons commencer à contourner ces limitations, notamment pour ce qui concerne l'arithmétique entière.

1. Les modes d'adressage, la vraie puissance du 6502

Le 6502 ne dispose que de 56 opcodes (instructions), mais beaucoup de ces opcodes peuvent avoir un paramètre et ce paramètre peut à son tour avoir plusieurs formes. Cette forme est appelée mode d'adressage. En effet, le paramètre est généralement une donnée 8 bits stockée quelque part en mémoire et donc, à une certaine adresse, et le 6502 offre une multitude de façons d'indiquer l'adresse en question.

1.1 Les modes directs, simples

Le mode le plus évident à comprendre est probablement le mode immédiat. Dans ce cas-là, la donnée est fournie immédiatement (d'où le nom, c'est bien fichu, hein ?) après l'opcode. Une donnée immédiate est forcément sur 8 bits. Elle peut donc aller de 0 à 255 (ou de -128 à 127 si l'on veut indiquer une donnée signée). On indique qu'on veut utiliser ce mode avec le caractère #, de cette façon :

  LDA #22

Cela place immédiatement la valeur 22 dans le registre A.

Un mode encore plus simple, mais auquel on ne pense pas vraiment comme à un mode d'adressage est le mode implicite. Dans ce cas, on n'a simplement pas d'adresse à indiquer, car l'opcode n'en a pas besoin ! Par exemple :

  INX ; incrémente X
  NOP ; ne fait rien

Donc, si on ne met aucun nombre, il s'agit du mode implicite, et si on met un nombre précédé d'un dièse, il s'agit d'une donnée immédiate. Mais que se passe-t-il si on écrit un nombre sans ce # ? Et bien, il s'agit du mode absolu. Le nombre est considéré comme une adresse (sur 16 bits, donc), et la donnée à manipuler est alors celle présente à cette adresse. Si l'adresse est inférieure à 256, elle tient sur 8 bits, dans ce cas, l'instruction sera légèrement plus rapide, car il y a un octet de moins à traiter pour créer l'adresse. C'est pourquoi les 256 premiers octets des machines qui utilisent un 6502 sont souvent nommés « mémoire rapide ». Ce n'est pas vraiment la mémoire qui est rapide, c'est la manière d'y accéder. L'adressage absolu utilisant cette zone mémoire, celle dont les 8 bits de poids forts sont à 0 est appelée ZP (pour Zero Page).

Ces quatre modes d'adressage sont de loin les plus utilisés. On les retrouve d'ailleurs dans la plupart des microprocesseurs. Et ils vont suffire à détailler quelques algorithmes.

Par exemple, voici comment réaliser une simple addition :

  CLC     ; on remet la retenue (C) à zéro
  LDA #42 ; chargement de A avec 42
  ADC #53 ; A = A + 53 + C
  STA 28 ; on stocke le résultat à l'adresse 28

Deux petites remarques sur ce bout de code : Premièrement, l'opcode ADC (ADdition with Carry) réalise une addition avec retenue et il n'existe pas d'opcode permettant de faire une addition sans retenue. Il faut donc systématiquement penser à remettre la retenue à zéro avant une addition. D'autre part, l'exemple cité n'est pas très intéressant, on aurait pu faire l'addition de tête et directement utiliser ceci : LDA #95 / STA resultat.

Additionner deux nombres est nettement plus intéressant lorsqu'il ne s'agit pas de constantes, et c'est là que l'adressage absolu (ou l'adressage ZP) intervient :

  CLC     ; mise à zéro de la retenue (C)
  LDA 16 ; A prend la valeur située l'adresse 16
  ADC $11 ; plus celle située à l'adresse 17 ($11 en hexadécimal)
  STA 28 ; stockage du résultat à l'adresse 28

On notera que le choix du mode d'adressage entre absolu et absolu ZP est réalisé automatiquement par l'assembleur qu'on utilise.

Les valeurs en mémoire RAM se comportent comme des variables et il est même possible d'effectuer certaines opérations directement sur celles-ci, sans passer par un registre. On peut par exemple incrémenter, décrémenter, multiplier par deux ou diviser par deux une valeur en mémoire en utilisant l'adressage absolu ou ZP (et d'autres que nous allons voir bientôt). Même si toute la RAM peut être utilisée de cette façon, on préfère souvent utiliser les 256 premiers octets pour cela, puisque l’instruction est alors 50 % plus rapide en moyenne.

Les utilisateurs chevronnés du 6502 s'accordent à dire que chaque octet de la mémoire, et plus particulièrement les 256 premiers, sont autant de registres disponibles, on est donc loin de la limite des 3 registres en tout. Les opérations sur les « vrais » registres restent toutefois nettement plus rapides.

Cependant, afin que le code soit plus facile à lire, on aime bien donner des noms aux différentes adresses que l'on utilise. Suivant l'assembleur que vous utilisez, cela se fera de différentes manières. Souvent ce genre de code fonctionnera :

variable EQU 23
        ou
variable = 42

Cependant, si vous utilisez l'assembleur en ligne [1] comme je le conseille pour cette série, il faudra utiliser la syntaxe suivante :

define variable 42

C'est la syntaxe que j'utiliserai dans mes exemples pour que vous puissiez facilement les tester.

1.1.1 Addition et soustraction de nombres de 16 bits

Le fait de toujours devoir se soucier de la retenue à chaque addition peut paraître lourd. Mais cela devient un avantage lorsque l'on veut manipuler des données de 16 bits.

En effet, même si le 6502 est incapable de manipuler autre chose que des données de 8 bits, rien ne nous empêche d'en associer deux pour les considérer comme une seule de 16 bits. Et de la même façon que nous réalisons des additions à la main en base dix en commençant par les unités, puis les dizaines, les centaines, etc. tout en reportant la retenue, le 6502 va nous permettre de réaliser des additions en base 256, avec une retenue éventuelle.

On stocke généralement les données 16 bits comme deux octets consécutifs en mémoire, en commençant par la partie des « unités ». Par exemple, si on veut stocker 520, qui est 2*256+8, on stockera 8 à une certaine adresse, et 2 à l'adresse suivante. C'est juste une convention, mais c'est celle que le 6502 utilise dans certains cas. Et comme on commence par le « petit bout » de la donnée (8 est plus petit que 2*256), on dit que les données sont en « petit-boutiste », ou little-endian en anglais.

Ainsi, le bout de code suivant permettra d'ajouter la valeur 16 bits stockée aux adresses val1 et val1 + 1 à la valeur 16 bits stockée aux adresses val2 et val2 + 1 et stocke le résultat dans resultat et resultat + 1. Dans cet exemple, on considérera que val1 contient 480 (soit 224, 1) et que val2 contient 806 (soit 38, 3).

   ; val1 est aux adresses 24 et 25
   define val1 24
   ; val2 est aux adresses 26 et 27
   define val2 26
   ; resultat est aux adresses 28 et 29
   define resultat 28
   CLC               ; on remet la retenue à 0
   LDA val1          ; A contient maintenant 224
   ADC val2          ; A = A + 38 + 0 = 224 + 38 = 262, soit 256+6
                     ; donc A vaut 6 et C vaut 1
   STA resultat      ; on stocke 6 dans resultat
   LDA val1 + 1      ; A contient maintenant 1
   ADC val2 + 1      ; A = A + 3 + 1 = 5
   STA resultat + 1 ; on stocke 5

À l'issue de ce code, resultat contient (6, 5), soit 5*256+6=1286, ce qui est bien la somme de 480 et de 806.

Pour la soustraction, cela marche de la même manière, mais en positionnant la retenue avec SEC (SEt Carry) au lieu de CLC (CLear Carry), car le 6502 gère la retenue à l'envers pour les soustractions.

Il peut arriver d'avoir envie d'utiliser des nombres entiers de plus de 16 bits, 24 ou 32 par exemple. Il suffit alors de continuer de la même manière, avec la retenue qui se propage petit à petit.

1.2 L'adressage relatif

Pour aller plus loin et faire des choses plus intéressantes, nous allons avoir besoin de tests et de sauts conditionnels.

Certains opcodes comme CMP, CPX et CPY sont spécialisés dans le test de valeur contenue respectivement dans A, X ou Y. Mais la plupart des opcodes modifient les drapeaux du 6502 (registre P) suivant le résultat de leur opération. On peut ensuite tirer parti de l'état de ses drapeaux pour effectuer un saut dans le programme suivant, si un drapeau est positionné ou pas. La destination du saut est donnée par un décalage par rapport à l'endroit actuel. Ce décalage est compris entre -128 et +127 et cette donnée est appelée adressage relatif.

L'adressage relatif n'est utilisé que par les opcodes de branchement conditionnels, Bxx.

Voici un exemple d'utilisation de cela :

...           ; un peu de code
boucle:       ; label (destination d'un saut)
  ...         ; encore un peu de code
  LDA valeur ; on charge une valeur dans A
  CMP #25     ; que l'on compare à 25
  BCC apres   ; si elle est plus grande, on saute à apres:
  DEX         ; décrémente X
  BNE boucle ; saut au label boucle si X n'est pas nul
apres:        ; autre label

La mémoire adressable par un 6502 peut aller jusqu’à 64 Ko, mais on vient de voir que les opcodes Bxx ne permettaient pas de se déplacer de plus de 128 octets, ce qui est très peu. Aussi, si l'on veut effectuer un saut plus grand, on utilisera le saut inverse, associé à l'opcode JMP comme ceci :

; on veut sauter à label (qui est très loin d’ici) si valeur vaut 5
  LDA valeur
  CMP #5
  BNE suite   ; suite: n'est pas loin, tout va bien
  JMP label   ; label: peut être n'importe où en mémoire
suite:

L'opcode JMP utilise donc généralement le mode d'adressage absolu ; il peut aussi utiliser un autre mode d'adressage : l'adressage indirect, qui lui est d'ailleurs réservé.

L'adressage indirect s'utilise ainsi :

  LDA #$34
  STA place
  LDA #$12
  STA place+1
  JMP (place) ; saute à l'adresse $1234

place étant un endroit en mémoire où l'on trouve l'adresse à laquelle cette instruction va sauter.

1.2.1 Incrémentation et décrémentation sur 16 bits

Maintenant que l'on a dans notre arsenal les sauts conditionnels, on va pouvoir implémenter les incrémentations et les décrémentations sur 16 bits. En effet, les opcodes INC (incrémentation d'une valeur en mémoire) ou DEX (décrémentation du registre X) par exemple, n'utilisent pas du tout la retenue (C). On ne peut donc pas réutiliser l'astuce des additions et des soustractions. Mais ils positionnent le flag Z si la nouvelle valeur est 0.

On peut donc implémenter l'incrémentation sur 16 bits ainsi :

  INC nombre   ; partie basse du nombre sur 16 bits que l'on incrémente
  BNE suite    ; on saute à suite: si on n'est pas passé de 255 à 0
  INC nombre+1 ; on incrémente la partie haute sinon.
suite:         ;

La décrémentation sur 16 bits est un poil plus complexe, mais pas tant que ça :

  LDA nombre   ; on teste la partie basse du nombre
  BNE suite    ; s'il n'est pas nul, on passe à la suite
  DEC nombre+1 ; sinon, on décrémente (aussi) la partie haute
suite:         ;
  DEC nombre   ; et dans les deux cas, on décrémente la partie basse.

1.3 L'adressage absolu avec index

Les modes d'adressages que l'on a vus jusqu'à maintenant sont présents sur la grande majorité des microprocesseurs, même ceux de l'époque. Là où le 6502 se distingue, c'est avec les trois restants.

Chacun de ses modes utilise à la fois une adresse et l'un des registres X ou Y. Le premier de ces modes est l'adressage absolu avec index. Il peut s'utiliser par exemple comme ceci :

LDA $1234,Y ; charge A avec la valeur à l'adresse $1234+Y

La position de la valeur est donnée par l'addition d'une valeur (comme pour l'adressage absolu) et du contenu d'un registre. Et cela peut être utilisé par beaucoup d'opcodes, pratiquement partout où l'on peut utiliser le mode d'adressage absolu !

Et comme il est facile d'incrémenter ou de décrémenter X ou Y, il devient très facile de faire des boucles. Par exemple, le bout de code suivant permet d'additionner les dix éléments du tableau tab1 aux éléments correspondants de tab2 et de stocker le résultat dans les éléments de tab3.

  LDX #0      ; Initialisation du compteur de boucle X à zéro
boucle:       ; Début de la boucle
  CLC         ; On remet la retenue (C) à zéro
  LDA tab1,X ; A = tab1[X]
  ADC tab2,X ; A = tab1[X] + tab2[X]
  STA tab3,X ; tab3[X] = A
  INX         ; Incrémentation du compteur de boucle
  CPX #10     ; Et s'il est égal à 10...
  BNE boucle ; ... on boucle.

Cela correspondrait à ce code C : for (x = 0; x < 10; x++) { tab3[x] = tab[1] + tab[2]; }. Qui a dit que l'assembleur était beaucoup plus compliqué que le C ?

Comme il est très fréquent de devoir faire des boucles, le mode d'adressage absolu avec index est très très répandu en programmation 6502.

1.4 Adressage indexé indirect

Le mode d'adressage indexé indirect est un dérivé du précédent. Il est un peu plus complexe à comprendre, mais aussi beaucoup plus puissant. Il s'utilise par exemple ainsi :

  LDA #$34    ; A = $34 (en hexadécimal)
  STA adr     ; adr = $34
  LDA #$12    ; A = $12
  STA adr+1   ; À ce point adr (et adr+1) contient l'adresse $1234
  LDY #3      ; Y = 3
  LDA (adr),Y ; Charge A avec la valeur située en $1234+3 = $1237
  INC (adr),Y ; Incrémente la valeur située en $1237

La valeur concernée par l'opération est celle trouvée à l'endroit dont on trouve l'adresse en adr, auquel on ajoute la valeur de Y. Deux restrictions cependant : seul Y peut être utilisé comme registre d'index dans ce mode, et pas X, et adr doit être inférieur à 255.

Afin d'en comprendre l'utilité, voyons comment utiliser ce mode d'adressage dans un exemple concret. Il arrive souvent que l'on veuille copier un ensemble de données d'un endroit vers un autre. Évidemment, si la zone de départ et la zone d'arrivée sont tout le temps les mêmes, ce n'est pas très intéressant et on peut utiliser le mode d'adressage précédent pour cela. Dans notre exemple, on va paramétrer la copie en indiquant dans le registre A quelle zone doit être copiée :

Copie:
; Préparation des variables
  TAX                      ; copie de A dans X
  LDA sources_basse,X      ; utilisation du mode absolu indexé
  STA source_indirecte     ; pour copier l'adresse source
  LDA sources_haute,X      ; dans source_indirecte
  STA source_indirecte+1   ; source_indirecte contient donc l'adresse de la source
; La copie proprement dite
  LDY #0                   ; initialisation de l'index à 0
boucle:
  LDA (source_indirecte),Y ; charge A avec la donnée de la bonne zone à la position Y
  STA dest,Y               ; stocke cette donnée dans la zone dest
  INY                      ; incrémente le compteur de boucle
  CPY #40                  ; et s'il n'est pas à 40...
  BNE boucle               ; ... on boucle
  RTS                      ; retour à la fonction appelante
; partie basse des adresses sources
sources_basse:
  DC.B $00, $48, $90
; partie haute des adresses sources
sources_haute:
  DC.B $01, $02, $03

Ce bout de code implémente une fonction Copie, qui va copier une zone de 40 caractères vers la zone mémoire dest. La zone copiée pourra au choix commencer à $0100, $0248 ou $390, suivant la valeur du registre A (0, 1 ou 2) avant l'appel à cette fonction. Notez que les DC.B ne sont pas des opcodes, mais une syntaxe permettant d'insérer des données directement à côté du code, sans avoir à les placer là par une série interminable de LDA #donne / STA adresse. Malheureusement, l'émulateur en ligne [1] ne supporte pas cette syntaxe, qui est tout de même très fréquente sur pratiquement tous les assembleurs comme ASM6 ou AS65. On peut convertir ce bout de code pour qu'il fonctionne tout de même sur [1], mais je vous laisse faire cela à titre d'exercice.

1.5 Adressage indirect indexé

Le dernier mode d'adressage du 6502 est nommé « indirect indexé ». Il est un peu le pendant du précédent. Voyons comment il s'utilise :

  LDA (table_adresses,X) ; charge A avec la valeur dont l'adresse est à en table_adresses+X
  INC (table_adresses,X) ; incrémente la valeur dont l'adresse est à en table_adresses+X

On a cette fois-ci un mode d'adressage qui nécessite l'utilisation du registre X. La valeur à charger/modifier est trouvée en commençant par additionner l'adresse (ou plutôt la table d'adresses) fournie et X, puis en regardant à l'adresse ainsi obtenue. Cela permet de considérer une zone mémoire comme un tableau d'adresses de variables, ce qui peut être très puissant dans certains cas. Ce mode d'adressage est toutefois nettement moins utilisé que les deux précédents.

J'ai donné des exemples d'opcodes utilisant certains modes d'adressage. Nous n'avons évidemment pas vu toutes les combinaisons possibles, et toutes ne sont d'ailleurs pas disponibles. Il n'est pas toujours évident de savoir ce qu'on a le droit de faire ou pas et les documentations sont souvent longues et rébarbatives. Je conseille donc l'infographie [2] qui regroupe sur une même page tous les opcodes, la liste des modes d'adressage utilisables pour chacun de ces opcodes, ainsi que les drapeaux que ces opcodes peuvent éventuellement modifier.

2. Multiplication

Après ce tour assez complet des modes d'adressage du 6502, nous allons nous intéresser quelques instants à un algorithme fondamental en arithmétique : la multiplication.

Le 6502 ne propose pas d'opcode pour les multiplications. Nous allons donc devoir les réaliser « à la main ». La manière la plus simple pour faire ça maintenant que nous disposons des boucles est de transformer une multiplication du genre 4x6 en 6+6+6+6.

Par exemple, on peut multiplier la valeur 8 bits en $00 par la valeur 8 bits en $01 et mettre le résultat dans $02 avec le bout de code suivant :

  LDA #0     ; Initialise A à 0
  LDX $00    ; X servira de compteur de boucle (à rebours)
  BEQ fini   ; Si X est nul, on a fini
  CLC        ; Sinon, on remet la retenue à 0
boucle:      ; Commence une série d'addition
  ADC $01    ; Ajout d'une valeur
  DEX        ; Décrémentation du compteur
  BNE boucle ; Boucle s'il n'est pas nul
fini:        ;
  STA $02    ; On stocke le résultat à l'adresse 2

Les avantages de cette façon de faire sont qu'elle est très simple et facile à adapter pour des données 16 bits. Mais l'inconvénient majeur est que c'est hyper lent, on doit pouvoir faire mieux.

Dans certains cas particuliers, on peut aller beaucoup plus vite. Par exemple, pour multiplier par 2 ou une puissance de deux, on peut utiliser l'opcode ASL (Arithmetic Shift Left) une ou plusieurs fois. En effet, comme dans notre base 10, on multiplie par 10 en décalant un nombre d'un chiffre vers la gauche, en binaire, on multiplie par deux en décalant d'un bit vers la gauche. On peut donc aussi multiplier par 4 en décalant deux fois, par 8 en décalant 3 fois, etc.

On peut même étendre cette idée pour multiplier assez rapidement par n'importe quelle constante. Par exemple, pour multiplier une valeur par 9, on peut simplement (et rapidement) multiplier par 8, puis ajouter la valeur initiale, pour multiplier par 15 on multipliera par 16 et on enlèvera une fois la valeur, etc.

Mais si on ne connaît pas par avance la valeur par laquelle on veut multiplier, il va falloir être plus inventif. Plus exactement, il va falloir revenir au primaire, du temps où l'on apprenait à faire des multiplications à la main.

Cela ressemblait à ça :

  13
x 17
----
  91
130
----
221

C'est-à-dire que l'on multipliait la première valeur par chacun des chiffres de la seconde, en décalant chaque nouvelle ligne vers la gauche avant de tout additionner.

Faire ça en binaire est en fait encore plus simple :

      1101
x    10001
----------
      1101
     00000
    000000
   0000000
  11010000
----------
  11011101

On voit que chacune des lignes que l'on ajoute à la fin est soit 0 (si le chiffre du multiplicateur est un 0), soit la valeur de départ décalée vers la gauche.

Cela va être facile à implémenter (ADC pour additionner, ASL pour décaler vers la gauche), il reste juste à tester chacun des chiffres pour savoir s'il est à 0 ou à 1. Pour cela, on va utiliser LSR (Logical Shift Right) qui place le chiffre le plus à droite dans la retenue (C) et décale vers la droite.

Voici une implémentation possible, que j'ai adaptée d'un exemple récupéré sur [3] qui multiplie assez rapidement la valeur présente en $00 par la valeur présente en $01 et stocke le résultat en $02. Ce code est très court (18 octets une fois compilé) et fait les choses dans un ordre pas forcément naturel, mais permet de gérer tous les cas sans avoir de duplication de code.

  LDA #0           ; A stockera le résultat (et commence à 0)
  JMP debut_boucle ; Saute au cœur de l'algorithme
addition:          ; Additionne une ligne
  CLC              ; Mise à 0 de la retenue
  ADC $00          ; Additionne $00 qui a été décalé
boucle:            ; Début de la boucle principale
  ASL $00          ; Décalage à gauche de la nouvelle ligne
debut_boucle:      ; Point d'entrée de l'algorithme
  LSR $01          ; Décalage à droite en récupérant le chiffre dans C
  BCS addition     ; Si C vaut 1, on additionne
  BNE boucle       ; Sinon et si $01 n'est pas encore vide, on boucle
  STA $02          ; Stockage du résultat

Note : ce code modifie aussi $00 et $01.

Voyons comment cela fonctionne par exemple pour multiplier 7 (en $00) par 5 (en $01). 5 est représenté par %101 en binaire. On commence par mettre A à zéro, puis on saute à debut_boucle.

premier tour :
$01 (%101) est décalé vers la droite : $01 vaut maintenant %10 et C vaut 1
$00 (qui vaut 7) est additionné à A (qui vaut donc maintenant 7)
$00 est décalé à gauche, il vaut maintenant 14
deuxième tour :
$01 (%10) est décalé vers la droite : $01 vaut maintenant %1 et C vaut 0, on boucle
$00 est décalé à gauche, il vaut maintenant 28
troisième tour :
$01 (%1) est décalé vers la droite : $01 vaut maintenant %0 et C vaut 1
$00 (qui vaut 28) est additionné à A (qui vaut donc maintenant 35)
$00 est décalé à gauche, il vaut maintenant 56
quatrième tour :
$01 (%0) est décalé vers la droite : $01 vaut maintenant %0 et C vaut 0
On sort de la boucle et on stocke A (qui vaut 35) dans $02.

Je vous encourage à tester ce code avec différentes valeurs et à l'exécuter pas-à-pas grâce à l'émulateur en ligne [1]. Vous pouvez voir une exécution de la multiplication de 7 par 5 sur la figure 1 justement. Notez les valeurs de $00 ($38 = 56 en décimal), $01 (0) et $02 ($23 = 35 en décimal) qui sont bien celles que l'on attendait.

multiply

Figure 1 : L’émulateur en ligne en action, effectuant 7 x 5.

Notez qu'il existe plein d'autres méthodes pour faire des multiplications rapidement. On peut par exemple précalculer les résultats, mais cela prend une place folle, et on doit alors se limiter par exemple a un maximum de 16 fois 16. Une astuce que je trouve assez géniale consiste à ne précalculer que les carrés (1x1, 2x2, 3x3, etc.) et à utiliser la formule suivante : a x b = ((a+b)/2)^2-((a-b)/2)^2. Cela permet de faire une multiplication avec seulement une addition, deux soustractions, deux décalages et deux lectures dans une table, quels que soient les nombres à multiplier !

On peut vérifier facilement que 7 x 5 = ((7+5)/2)^2-((7-5)/2)^2 = (12/2)^2-(2/2)^2 = 6^2 - 1^2 = 36 - 1 = 35.

Ce n'est pas très difficile à implémenter et les plus courageux d'entre vous peuvent largement le faire avant le prochain article !

3. La prochaine fois

Dans le prochain article, on continuera d'utiliser astucieusement les quelques opcodes du 6502 pour faire des choses de plus en plus complexes. On verra par exemple comment réaliser des divisions par 2, 4, 3, 5 ou même n'importe quelle valeur. On abordera aussi la représentation des nombres à virgule afin de faire des opérations dessus et même, un peu de trigonométrie. Avouez que ce n'est pas mal pour un processeur qui est a priori tout juste capable de faire une addition entière sur 8 bits !

Références

[1] https://skilldrick.github.io/easy6502/ assembleur/émulateur en ligne.

[2] https://raw.githubusercontent.com/camsaul/nesasm/master/beagle_bros_6502_reference.png résumé en une seule page de tous les opcodes et modes d’adressage du 6502.

[3] https://codebase64.org/doku.php plein d’exemples de code.

[4] http://www.6502.org/ qui regorge d'informations, d'archives et d'astuces.



Articles qui pourraient vous intéresser...

Programmation avec le 6502 : découverte de la NES

Magazine
Marque
Hackable
Numéro
34
Mois de parution
juillet 2020
Domaines
Résumé

Dans les articles précédents, nous avons étudié de près le langage d'assemblage du microprocesseur 6502. Et même si j'ai essayé d'étayer le tout avec beaucoup d'exemples, tout cela est resté très théorique. Aujourd'hui, nous allons vraiment passer à la pratique en réalisant des programmes graphiques pouvant s'exécuter sur une véritable console NES ou sur un émulateur.

Programmation avec le 6502 : trigonométrons !

Magazine
Marque
Hackable
Numéro
33
Mois de parution
avril 2020
Domaines
Résumé

Lors du précédent article, nous avons parcouru les différents modes d'adressage du 6502, ce qui nous a permis d'élaborer quelques algorithmes simples, notamment pour réaliser des additions ou soustractions sur des nombres entiers de plus de 8 bits et même, des multiplications. Aujourd'hui, nous allons continuer dans cette voie en nous intéressant à la division et même aux nombres décimaux (à virgule), ce qui nous permettra de mettre un pied dans le monde effrayant de la trigonométrie !

Faites dialoguer une carte Arduino avec un vieux Commodore 64

Magazine
Marque
Contenu Premium
Domaines
Résumé

Il y a des jours où il nous arrive des idées saugrenues mais où, plutôt que de les chasser rapidement de notre esprit, on fonce tête baissée pour les mettre en œuvre. Le jour où je me suis mis en tête de connecter un Commodore 64 à une carte Arduino pour les faire communiquer était un de ceux-là. Pire encore, hors de question de faire cela en BASIC. Si c'est du C côté Arduino, ce sera aussi du C côté Commodore 64 !

Programmation avec le 6502

Magazine
Marque
Hackable
Numéro
31
Mois de parution
octobre 2019
Domaines
Résumé

Il y a quelques années, les musiques des années 70 et 80 sont revenues à la mode. Puis, cela a été le tour du rétrogaming. Et voici venu le temps du rétrocomputing. Créer de petits programmes ou même des jeux à l’ancienne, tout en assembleur sur de très vieilles machines 8 bits n’est pas si complexe que ça. C’est d’ailleurs le but de cette série, qui s’intéresse à la mise en œuvre d’un des tout premiers microprocesseurs.

De la poubelle au salon : le ZX Spectrum

Magazine
Marque
Hackable
Numéro
29
Mois de parution
avril 2019
Domaines
Résumé
Lorsqu'on s'intéresse à l'Histoire et plus précisément l'histoire de l'informatique, au point de vouloir posséder l'un de ses morceaux, il y a deux approches possibles : acquérir du matériel testé et fonctionnel ou récupérer ce qui pourrait passer pour un déchet électronique (pour le commun des mortels) et lui redonner vie. N'étant pas du genre à apprécier qu'on fasse les choses à ma place, je préfère largement la seconde option, me permettant par la même occasion d'apprendre et de partager mes découvertes et déboires avec vous...