Exemples de code
Cette page présente des exemples complets de programmes en assembleur 6502. Chaque exemple est commenté et illustre une technique ou un algorithme fondamental. Les exemples utilisent les conventions de l'assembleur ASM6502.PAS (syntaxe compatible Motorola/MOS Technology).
Note : Les exemples utilisent les routines KERNAL du Commodore 64 pour les entrées/sorties, ce qui est la convention la plus répandue pour le 6502. Sur d'autres systèmes (Apple II, Atari 800, NES), les appels d'entrées/sorties varient mais le code algorithmique reste identique.
Conventions communes aux exemples :
- CHROUT ($FFD2) : afficher un caractère (dans A) sur l'écran
- GETIN ($FFE4) : lire un caractère au clavier (dans A)
- CHRIN ($FFCF) : lire un caractère avec echo
- $0400-$07E7 : mémoire écran (Commodore 64)
- $D020/$D021 : registres de couleur bordure/fond
Hello World
Programme minimal affichant un message à l'écran :
- ; ------------------------------------------------------------------
- ; Exemple 9.1 : Hello World
- ; Affiche "Hello, World!" puis retourne au BASIC.
- ; Cible : Commodore 64 (KERNAL)
- ; ------------------------------------------------------------------
-
- ORG $C000
-
- CHROUT EQU $FFD2 ; Routine KERNAL : afficher caractere
-
- start:
- LDX #0 ; Index dans la chaine
- @loop:
- LDA message,X ; Charger le caractere suivant
- BEQ @done ; Si zero, fin de chaine
- JSR CHROUT ; Afficher le caractere
- INX ; Caractere suivant
- BNE @loop ; Boucler (max 256 caracteres)
- @done:
- RTS ; Retour au BASIC
-
- message:
- DB 'Hello, World!', $0D, 0
-
- END start
Variante Apple II (moniteur) :
- ; ------------------------------------------------------------------
- ; Exemple 9.1b : Hello World - Apple II
- ; Utilise la routine COUT du moniteur Apple II.
- ; ------------------------------------------------------------------
-
- ORG $0300
-
- COUT EQU $FDED ; Routine moniteur : afficher caractere
-
- start:
- LDY #0 ; Index dans la chaine
- @loop:
- LDA message,Y ; Charger le caractere
- BEQ @done ; Zero = fin
- ORA #$80 ; Mettre le bit 7 (Apple II normal)
- JSR COUT ; Afficher
- INY ; Suivant
- BNE @loop ; Boucler
- @done:
- RTS
-
- message:
- DB 'Hello, World!', $0D, 0
-
- END start
Variante NES (PPU) :
- ; ------------------------------------------------------------------
- ; Exemple 9.1c : Hello World - NES
- ; Ecrit directement dans la memoire video PPU.
- ; ------------------------------------------------------------------
-
- ORG $8000
-
- PPUCTRL EQU $2000
- PPUMASK EQU $2001
- PPUSTAT EQU $2002
- PPUADDR EQU $2006
- PPUDATA EQU $2007
-
- start:
- ; Attendre 2 VBlank pour que le PPU soit pret
- LDA PPUSTAT ; Effacer le flag VBlank
- @vb1: LDA PPUSTAT
- BPL @vb1 ; Attendre bit 7 = 1
- @vb2: LDA PPUSTAT
- BPL @vb2 ; Deuxieme VBlank
-
- ; Positionner l'adresse PPU (nametable 0, ligne 14, col 10)
- LDA #$21 ; Adresse haute = $21
- STA PPUADDR
- LDA #$CA ; Adresse basse = $CA
- STA PPUADDR
-
- ; Ecrire les tiles "Hello, World!"
- LDX #0
- @loop: LDA message,X
- BEQ @done
- STA PPUDATA ; Ecrire le tile dans la VRAM
- INX
- BNE @loop
- @done:
-
- ; Activer l'affichage
- LDA #%00001010 ; Afficher le fond, pas de clip gauche
- STA PPUMASK
-
- @infini: JMP @infini ; Boucle infinie (le NES ne retourne pas)
-
- message:
- DB 'Hello, World!', 0
-
- ; Vecteurs d'interruption
- ORG $FFFA
- DW start ; NMI
- DW start ; RESET
- DW start ; IRQ/BRK
-
- END
Calcul de factorielle
Calcul itératif de N! pour des valeurs petites (résultat 8 bits ou 16 bits). La récursion est peu pratique sur le 6502 en raison de la pile limitée a 256 octets :
- ; ------------------------------------------------------------------
- ; Exemple 9.2 : Factorielle iterative (8 bits)
- ; Entree : X = N (0 a 5 pour rester en 8 bits)
- ; Sortie : A = N!
- ; Note : 5! = 120 (max pour un resultat 8 bits)
- ; Pour N > 5, utiliser la version 16 bits ci-dessous.
- ; ------------------------------------------------------------------
-
- ORG $C000
-
- CHROUT EQU $FFD2
-
- start:
- LDX #5 ; Calculer 5!
- JSR factorielle ; A = 120
- JSR print_dec ; Afficher en decimal
- RTS
-
- ; ------------------------------------------------------------------
- ; Sous-routine : factorielle
- ; Entree : X = N (0-5)
- ; Sortie : A = N!
- ; Modifie : A, X, Y
- ; ------------------------------------------------------------------
- factorielle:
- CPX #0 ; N == 0 ?
- BEQ @un ; 0! = 1
- CPX #1 ; N == 1 ?
- BEQ @un ; 1! = 1
-
- LDA #1 ; Accumulateur = 1
- STX @count ; Sauvegarder N
- LDY #1 ; Compteur de boucle = 1
- @loop:
- ; Multiplier A par Y (addition repetee)
- TAX ; Sauvegarder A dans X
- LDA #0 ; Resultat partiel = 0
- STY @mult ; Nombre de fois a ajouter
- @madd: CLC
- ADC @mult+1 ; Ajouter... non, on va simplifier
- ; Methode simple : table de multiplication
- ; Pour factorielle, on multiplie A * (Y+1)
- ; On va utiliser une approche directe par table
-
- ; Version simplifiee avec table precalculee :
- LDX #0 ; Trouver X dans la table
- RTS ; (voir version 16 bits ci-dessous)
-
- @un: LDA #1
- RTS
-
- @count: DB 0
- @mult: DW 0
-
- ; ------------------------------------------------------------------
- ; Version 16 bits : factorielle avec resultat sur 2 octets
- ; Entree : A = N (0-8, car 8! = 40320 < 65535)
- ; Sortie : result (2 octets, little-endian)
- ; ------------------------------------------------------------------
-
- result: DW 0 ; Resultat 16 bits
- temp: DW 0 ; Temporaire pour multiplication
-
- fact16:
- CMP #0
- BEQ @un16
- CMP #1
- BEQ @un16
-
- TAX ; X = N (compteur)
- LDA #1 ; result = 1
- STA result
- LDA #0
- STA result+1
-
- LDY #2 ; Multiplicateur = 2
- @loop16:
- ; Multiplier result par Y
- ; result = result * Y (multiplication 16x8 -> 16)
- LDA result
- STA temp
- LDA result+1
- STA temp+1
-
- LDA #0
- STA result
- STA result+1
-
- ; Additionner temp Y fois
- STY @cnt
- @addlp: CLC
- LDA result
- ADC temp
- STA result
- LDA result+1
- ADC temp+1
- STA result+1
- DEC @cnt
- BNE @addlp
-
- INY ; Multiplicateur suivant
- DEX ; Decrementer N
- CPX #1
- BNE @loop16 ; Continuer si N > 1
-
- RTS
-
- @un16: LDA #1
- STA result
- LDA #0
- STA result+1
- RTS
-
- @cnt: DB 0
-
- ; ------------------------------------------------------------------
- ; Sous-routine : print_dec
- ; Affiche la valeur de A en decimal (0-255).
- ; ------------------------------------------------------------------
- print_dec:
- LDX #0 ; Flag centaines
- CMP #100
- BCC @dizaines
- @cent: SBC #100 ; Soustraire 100
- INX ; Compter les centaines
- CMP #100
- BCS @cent
- PHA ; Sauvegarder le reste
- TXA
- CLC
- ADC #'0' ; Convertir en ASCII
- JSR CHROUT ; Afficher centaines
- PLA
- LDX #1 ; Marquer qu'on a affiche
- @dizaines:
- LDY #0 ; Compteur dizaines
- CMP #10
- BCC @unites_chk
- @diz: SBC #10
- INY
- CMP #10
- BCS @diz
- PHA
- TYA
- CLC
- ADC #'0'
- JSR CHROUT ; Afficher dizaines
- PLA
- JMP @unites
- @unites_chk:
- CPX #0 ; A-t-on deja affiche un chiffre ?
- BEQ @unites ; Non -> afficher directement
- PHA
- LDA #'0' ; Afficher '0' pour les dizaines
- JSR CHROUT
- PLA
- @unites:
- CLC
- ADC #'0' ; Convertir en ASCII
- JSR CHROUT ; Afficher unites
- RTS
-
- END start
Tri a bulles
Tri d'un tableau d'octets par l'algorithme du tri a bulles (bubble sort). Algorithme simple mais O(n^2) :
- ; ------------------------------------------------------------------
- ; Exemple 9.3 : Tri a bulles (Bubble Sort)
- ; Trie un tableau d'octets en ordre croissant.
- ; Entree : tableau = adresse du tableau
- ; taille = nombre d'elements
- ; ------------------------------------------------------------------
-
- ORG $C000
-
- CHROUT EQU $FFD2
-
- taille EQU 8 ; Nombre d'elements
-
- start:
- JSR bubble_sort ; Trier le tableau
- JSR afficher ; Afficher le resultat
- RTS
-
- ; ------------------------------------------------------------------
- ; Sous-routine : bubble_sort
- ; Trie 'tableau' (taille octets) en ordre croissant.
- ; Algorithme :
- ; Repeter
- ; echange = faux
- ; Pour i = 0 a taille-2
- ; Si tableau[i] > tableau[i+1] Alors
- ; Echanger tableau[i] et tableau[i+1]
- ; echange = vrai
- ; Jusqu'a echange = faux
- ; ------------------------------------------------------------------
- bubble_sort:
- @passe:
- LDA #0
- STA @echange ; echange = faux (0)
- LDX #0 ; i = 0
- @comp:
- LDA tableau,X ; A = tableau[i]
- CMP tableau+1,X ; Comparer avec tableau[i+1]
- BCC @ok ; Si tableau[i] < tableau[i+1], ok
- BEQ @ok ; Si egaux, ok
-
- ; Echanger
- LDY tableau+1,X ; Y = tableau[i+1]
- STA tableau+1,X ; tableau[i+1] = A (ancien tableau[i])
- TYA
- STA tableau,X ; tableau[i] = Y (ancien tableau[i+1])
- LDA #1
- STA @echange ; echange = vrai
- @ok:
- INX ; i++
- CPX #taille-1 ; i < taille-1 ?
- BNE @comp ; Oui -> continuer
-
- LDA @echange ; Y a-t-il eu un echange ?
- BNE @passe ; Oui -> nouvelle passe
-
- RTS
-
- @echange: DB 0
-
- ; ------------------------------------------------------------------
- ; Sous-routine : afficher
- ; Affiche les elements du tableau separes par des espaces.
- ; ------------------------------------------------------------------
- afficher:
- LDX #0
- @loop:
- LDA tableau,X
- JSR print_dec ; Afficher en decimal (voir 9.2)
- LDA #' '
- JSR CHROUT ; Espace separateur
- INX
- CPX #taille
- BNE @loop
- LDA #$0D
- JSR CHROUT ; Retour chariot
- RTS
-
- ; Donnees de test (non triees)
- tableau:
- DB 64, 25, 12, 99, 1, 47, 83, 36
-
- print_dec:
- LDX #0 ; Flag centaines
- CMP #100
- BCC @dizaines
- @cent: SBC #100 ; Soustraire 100
- INX ; Compter les centaines
- CMP #100
- BCS @cent
- PHA ; Sauvegarder le reste
- TXA
- CLC
- ADC #'0' ; Convertir en ASCII
- JSR CHROUT ; Afficher centaines
- PLA
- LDX #1 ; Marquer qu'on a affiche
- @dizaines:
- LDY #0 ; Compteur dizaines
- CMP #10
- BCC @unites_chk
- @diz: SBC #10
- INY
- CMP #10
- BCS @diz
- PHA
- TYA
- CLC
- ADC #'0'
- JSR CHROUT ; Afficher dizaines
- PLA
- JMP @unites
- @unites_chk:
- CPX #0 ; A-t-on deja affiche un chiffre ?
- BEQ @unites ; Non -> afficher directement
- PHA
- LDA #'0' ; Afficher '0' pour les dizaines
- JSR CHROUT
- PLA
- @unites:
- CLC
- ADC #'0' ; Convertir en ASCII
- JSR CHROUT ; Afficher unites
- RTS
-
- END start
Analyse de complexité :
- Pire cas (tableau inverse) : n*(n-1)/2 comparaisons
- Meilleur cas (déjà trié) : n-1 comparaisons (1 passe)
- Pour 8 éléments : 7 a 28 comparaisons
- Chaque comparaison/échange : ~20 cycles
- Temps approximatif : 0.14 ms a 0.56 ms a 1 MHz
Multiplication 16 bits
Multiplication de deux nombres 16 bits non signes avec résultat sur 32 bits, par l'algorithme de décalage et addition.
- ; ------------------------------------------------------------------
- ; Exemple 9.4 : Multiplication 16 bits non signee
- ; Entree : mplier (2 octets) = multiplicande
- ; mpcand (2 octets) = multiplicateur
- ; Sortie : result (4 octets) = produit 32 bits
- ; Methode : decalage et addition (shift-and-add)
- ; ------------------------------------------------------------------
-
- ORG $C000
-
- mplier: DW 0 ; Multiplicande (16 bits)
- mpcand: DW 0 ; Multiplicateur (16 bits)
- result: DS 4 ; Resultat (32 bits)
-
- start:
- ; Exemple : 1000 * 500 = 500000
- LDA #<1000 ; $E8
- STA mplier
- LDA #>1000 ; $03
- STA mplier+1
-
- LDA #<500 ; $F4
- STA mpcand
- LDA #>500 ; $01
- STA mpcand+1
-
- JSR mult16
- RTS
-
- ; ------------------------------------------------------------------
- ; Sous-routine : mult16
- ; Multiplication 16x16 -> 32 bits non signee
- ; Algorithme :
- ; result = 0
- ; Pour chaque bit du multiplicateur (16 bits) :
- ; Decaler result a droite
- ; Si bit courant du multiplicateur = 1 :
- ; Ajouter multiplicande aux 16 bits hauts de result
- ; Decaler multiplicateur a droite
- ; ------------------------------------------------------------------
- mult16:
- ; Initialiser result a 0
- LDA #0
- STA result
- STA result+1
- STA result+2
- STA result+3
-
- LDX #16 ; 16 bits a traiter
- @loop:
- ; Decaler result (32 bits) a droite
- LSR result+3
- ROR result+2
- ROR result+1
- ROR result
-
- ; Tester le bit 0 du multiplicateur
- LSR mpcand+1 ; Decaler mpcand a droite
- ROR mpcand
- BCC @noadd ; Bit = 0 -> pas d'addition
-
- ; Ajouter mplier aux octets hauts de result
- CLC
- LDA result+2
- ADC mplier
- STA result+2
- LDA result+3
- ADC mplier+1
- STA result+3
-
- @noadd:
- DEX
- BNE @loop
-
- RTS
-
- END start
Vérification : 1000 * 500 = 500000 = $0007A120
- result+3 = $00, result+2 = $07, result+1 = $A1, result = $20
Variante optimisée (multiplicande 8 bits) :
- ; Multiplication rapide 8x8 -> 16 bits
- ; ------------------------------------------------------------------
- ; Entree : A = multiplicande, Y = multiplicateur
- ; Sortie : prod (2 octets)
- ; Plus rapide pour les petites valeurs.
- ; ------------------------------------------------------------------
-
- prod: DW 0
-
- mult8x8:
- STA @mcand ; Sauvegarder multiplicande
- LDA #0
- STA prod
- STA prod+1
-
- TYA ; A = multiplicateur
- LDX #8 ; 8 bits
- @loop:
- LSR ; Bit de poids faible dans C
- BCC @noadd
- PHA ; Sauvegarder A
- CLC
- LDA prod
- ADC @mcand
- STA prod
- LDA prod+1
- ADC #0
- STA prod+1
- PLA ; Restaurer A
- @noadd:
- ASL @mcand ; Decaler multiplicande a gauche
- DEX
- BNE @loop
- RTS
-
- @mcand: DB 0
Performances :
- Multiplication 16x16 : ~400 cycles (~0.4 ms a 1 MHz)
- Multiplication 8x8 : ~180 cycles (~0.18 ms a 1 MHz)
- Par comparaison, un MUL 16 bits sur 8086 : ~130 cycles
- Le 6502 n'a pas d'instruction de multiplication native, d'où la nécessité de ces routines logicielles.
Routine de délai (temporisation)
Routines de temporisation utilisant des boucles calibrées. Le temps est calcule en cycles d'horloge du processeur :
- ; ------------------------------------------------------------------
- ; Exemple 9.5 : Routines de delai
- ; Base sur un processeur a 1 MHz (1 cycle = 1 microseconde).
- ;
- ; Delai court : delai_256us (~256 microsecondes)
- ; Delai moyen : delai_ms (A millisecondes, 1-255)
- ; Delai long : delai_sec (A secondes, 1-255)
- ; ------------------------------------------------------------------
-
- ORG $C000
-
- start:
- ; Exemple : clignoter la bordure pendant 5 secondes
- LDX #10 ; 10 clignotements
- @blink:
- LDA #1 ; Couleur blanche
- STA $D020 ; Bordure blanche
-
- LDA #250 ; 250 ms
- JSR delai_ms ; Attendre
-
- LDA #0 ; Couleur noire
- STA $D020 ; Bordure noire
-
- LDA #250 ; 250 ms
- JSR delai_ms ; Attendre
-
- DEX
- BNE @blink ; Repeter
- RTS
-
- ; ------------------------------------------------------------------
- ; Sous-routine : delai_256us
- ; Delai d'environ 256 microsecondes (a 1 MHz).
- ; Modifie : Y
- ;
- ; Calcul exact :
- ; LDY #$33 = 2 cycles
- ; DEY = 2 cycles * 51
- ; BNE = 3 cycles * 50 + 2 cycles * 1
- ; RTS = 6 cycles
- ; Total = 2 + (2+3)*50 + (2+2) + 6 = 260 cycles ~ 260 us
- ; ------------------------------------------------------------------
- delai_256us:
- LDY #$33 ; 51 iterations
- @loop: DEY ; 2 cycles
- BNE @loop ; 3 cycles (2 si pas de branchement)
- RTS ; 6 cycles
-
- ; ------------------------------------------------------------------
- ; Sous-routine : delai_ms
- ; Delai de A millisecondes (1-255).
- ; Entree : A = nombre de millisecondes
- ; Modifie : A, X, Y
- ;
- ; 1 ms = 1000 cycles a 1 MHz
- ; Boucle interne : ~5 cycles * 200 = 1000 cycles = 1 ms
- ; ------------------------------------------------------------------
- delai_ms:
- TAX ; X = nombre de ms
- @outer:
- LDY #200 ; 200 iterations internes
- @inner: NOP ; 2 cycles (ajustement)
- DEY ; 2 cycles
- BNE @inner ; 3 cycles -> 5 * 200 = 1000 cycles
- DEX ; ms restantes
- BNE @outer ; Boucler
- RTS
-
- ; ------------------------------------------------------------------
- ; Sous-routine : delai_sec
- ; Delai de A secondes (1-255).
- ; Entree : A = nombre de secondes
- ; Modifie : A, X, Y, @sec_cnt
- ; ------------------------------------------------------------------
- delai_sec:
- STA @sec_cnt ; Sauvegarder le compteur
- @sec_lp:
- ; 1 seconde = 4 * 250 ms
- LDA #250
- JSR delai_ms
- LDA #250
- JSR delai_ms
- LDA #250
- JSR delai_ms
- LDA #250
- JSR delai_ms
-
- DEC @sec_cnt
- BNE @sec_lp
- RTS
-
- @sec_cnt: DB 0
-
- END start
Notes sur la précision :
- Les délais sont approximatifs (±5%) en raison des cycles supplémentaires des instructions de contrôle de boucle
- Pour une temporisation précise, utiliser les minuteries CIA du Commodore 64 ($DC04-$DC05) ou un minuterie équivalent
- Les interruptions (IRQ) perturbent la minuterie ; pour un délai précis, désactiver les interruptions (SEI) avant et les réactiver (CLI) après
- Fréquences courantes :
| Plateforme | Fréquences |
|---|---|
| Commodore 64 (PAL) | 985248 Hz |
| Commodore 64 (NTSC) | 1022727 Hz |
| Apple II | 1020484 Hz |
| Atari 800 (PAL) | 1773447 Hz |
| NES (NTSC) | 1789773 Hz |
Copie de bloc mémoire
Routines pour copier, remplir et déplacer des blocs de mémoire. Le 6502 n'a pas d'instructions de bloc (comme REP MOVSB sur x86), donc ces opérations doivent être codées manuellement :
- ; ------------------------------------------------------------------
- ; Exemple 9.6a : Copie de bloc (jusqu'a 256 octets)
- ; Entree : src = adresse source (page zero, 2 octets)
- ; dst = adresse destination (page zero, 2 octets)
- ; Y = nombre d'octets a copier (1-256, 0 = 256)
- ; ------------------------------------------------------------------
-
- ORG $C000
-
- src EQU $FB ; Pointeur source (page zero)
- dst EQU $FD ; Pointeur destination (page zero)
-
- start:
- ; Copier 128 octets de $2000 vers $4000
- LDA #<$2000
- STA src
- LDA #>$2000
- STA src+1
-
- LDA #<$4000
- STA dst
- LDA #>$4000
- STA dst+1
-
- LDY #128 ; 128 octets
- JSR copy_blk
- RTS
-
- ; ------------------------------------------------------------------
- ; Sous-routine : copy_blk
- ; Copie Y octets de (src) vers (dst).
- ; Copie de la fin vers le debut (Y-1 a 0).
- ; ------------------------------------------------------------------
- copy_blk:
- DEY ; Commencer a l'offset Y-1
- @loop:
- LDA (src),Y ; Charger octet source
- STA (dst),Y ; Stocker a destination
- DEY ; Octet precedent
- BPL @loop ; Continuer tant que Y >= 0
- RTS
-
- ; ------------------------------------------------------------------
- ; Exemple 9.6b : Copie de grand bloc (plus de 256 octets)
- ; Entree : src = adresse source (page zero)
- ; dst = adresse destination (page zero)
- ; blklen = longueur en octets (2 octets)
- ; ------------------------------------------------------------------
-
- blklen: DW 0 ; Longueur du bloc
-
- copy_big:
- LDY #0 ; Index dans la page courante
- LDX blklen+1 ; Nombre de pages completes
- BEQ @reste ; Si 0, aller au reste
-
- ; Copier les pages completes (256 octets chacune)
- @page:
- LDA (src),Y ; Charger
- STA (dst),Y ; Stocker
- INY ; Octet suivant
- BNE @page ; 256 octets par page
-
- INC src+1 ; Page source suivante
- INC dst+1 ; Page destination suivante
- DEX ; Page suivante
- BNE @page ; Continuer
-
- ; Copier le reste (0 a 255 octets)
- @reste:
- LDX blklen ; Nombre d'octets restants
- BEQ @done ; Si 0, termine
- @tail:
- LDA (src),Y
- STA (dst),Y
- INY
- DEX
- BNE @tail
- @done:
- RTS
-
- ; ------------------------------------------------------------------
- ; Exemple 9.6c : Remplissage de bloc (memset)
- ; Entree : dst = adresse destination (page zero)
- ; A = valeur de remplissage
- ; blklen = longueur en octets (2 octets)
- ; ------------------------------------------------------------------
-
- fill_blk:
- LDY #0
- LDX blklen+1 ; Pages completes
- BEQ @reste
- @page:
- STA (dst),Y ; Remplir
- INY
- BNE @page ; 256 octets
- INC dst+1 ; Page suivante
- DEX
- BNE @page
- @reste:
- LDX blklen ; Octets restants
- BEQ @done
- @tail:
- STA (dst),Y
- INY
- DEX
- BNE @tail
- @done:
- RTS
-
- ; ------------------------------------------------------------------
- ; Exemple 9.6d : Copie avec recouvrement (memmove)
- ; Si src < dst : copier de la fin vers le debut
- ; Si src >= dst : copier du debut vers la fin
- ; Entree : src, dst, blklen
- ; ------------------------------------------------------------------
-
- move_blk:
- ; Comparer src et dst
- LDA src+1
- CMP dst+1
- BCC @backward ; src < dst (page haute)
- BNE @forward ; src > dst
- LDA src
- CMP dst
- BCC @backward ; src < dst (page basse)
-
- @forward:
- ; Copie avant (src >= dst) - identique a copy_big
- JMP copy_big
-
- @backward:
- ; Copie arriere (src < dst)
- ; Positionner a la fin du bloc
- CLC
- LDA src
- ADC blklen
- STA src
- LDA src+1
- ADC blklen+1
- STA src+1
-
- CLC
- LDA dst
- ADC blklen
- STA dst
- LDA dst+1
- ADC blklen+1
- STA dst+1
-
- ; Copier du dernier octet vers le premier
- LDX blklen+1 ; Pages
- LDY blklen ; Reste
- BEQ @bkpage ; Si reste = 0, commencer par pages
- @bktail:
- DEY
- LDA (src),Y
- STA (dst),Y
- CPY #0
- BNE @bktail
-
- @bkpage:
- CPX #0
- BEQ @bkdone
- DEC src+1
- DEC dst+1
- LDY #$FF
- @bklp:
- LDA (src),Y
- STA (dst),Y
- DEY
- CPY #$FF ; Wraparound de 0 a $FF
- BNE @bklp
- DEX
- BNE @bkpage
-
- @bkdone:
- RTS
-
- END start
Performances comparées (copie de 1024 octets a 1 MHz) :
| Méthode | Cycles | Temps |
|---|---|---|
| copy_blk (256 octets) | ~1540 | ~1.5 ms |
| copy_big (1024 octets) | ~9240 | ~9.2 ms |
| fill_blk (1024 octets) | ~6160 | ~6.2 ms |
| DMA (C64, si disponible) | ~1024 | ~1.0 ms |
Comparaison avec d'autres microprocesseurs :
| Microprocesseur | Instruction | Cycles/1024 octets |
|---|---|---|
| MOS 6502 | Boucle LDA/STA | ~9240 |
| Zilog Z80 | LDIR | ~5120 |
| Intel 8086 | REP MOVSB | ~9216 |
| Motorola 68000 | MOVE.L (loop) | ~12288 |
| Intel 80386 | REP MOVSD | ~2048 |
Récapitulatif des exemples
| Exemple | Titre | Instructions clefs |
|---|---|---|
| 9.1 | Hello World | LDA, JSR, INX, BNE, BEQ |
| 9.2 | Calcul de factorielle | ADC, SBC, STA, CMP, BCS |
| 9.3 | Tri a bulles | CMP, BCC, LDY, STA, INX |
| 9.4 | Multiplication 16 bits | LSR, ROR, ADC, BCC, DEX |
| 9.5 | Routine de délai | DEY, BNE, NOP, DEC |
| 9.6 | Copie de bloc mémoire | LDA (zp),Y / STA (zp),Y |