C64 Interrupts FR Part06
C64 Interrupts FR Part06
C64 Interrupts FR Part06
avec la collaboration de
Olivier Bernhard - Collaborateur externe "RetroProgramming
Italia - RP Italia".
C’est ce que nous allons faire en montrant comment avec peu de modifications
nous pouvons introduire ce comportement différent :
Dans le programme modifié, nous avons tout d'abord défini comme premier
gestionnaire d’interruption irq00 suivi de irq01 (contrairement à ce qui se
passait dans la routine originale).
Notez également que nous avons éliminé la constante IRQSPLIT car, comme nous
l'avons dit, l'irq01 ne se déclenche plus à une position fixe.
Ceci détermine comment chacune des 128 valeurs est calculée. Chacune des 128
valeurs est indicée par i, i variant de 0 à 127 (128 valeurs).
...
...
En fin de compte, cette directive nous permet calculer simplement 128 valeurs
de lignes de trame qui oscillent entre 144+64 et 144-64 selon la fonction
sinusoïdale indiquée ci-dessus.
à la ligne 48, nous trouvons l'une des 2 instructions clés du « SELF MODIFYING
CODE » : dec idx+1.
Tant que le contenu de l'adresse idx+1 est >=0, nous continuons l'exécution
du programme à partir de la ligne 52 (instruction BPL de la ligne 49).
Lorsque l'indice - à la suite des décrémentations successives – atteint la
valeur $00, un « DEC » supplémentaire provoque un changement de la valeur
contenue à l'emplacement idx+1 qui devient alors égal à $ff ($00-$01=$ff).
dès lors, la condition du saut conditionnel bpl (ligne 49) ne sera plus
vérifiée puisque le MSB de l'emplacement sera fixé et par conséquent changera,
l'indicateur N du registre d'état en le fixant à 1.
Pour ramener l'index de la table à 127, à la ligne 50, nous chargeons 127
dans l'Accumulateur, dont nous stockons ensuite le contenu à la ligne 51 dans
l'adresse idx+1.
Bien que les économies, dans cet exemple précis, puissent sembler minimes et
peut-être négligeables - nous rappelons aux lecteurs que notre objectif ici
est essentiellement didactique - il existe de nombreux autres cas dans
lesquels la technique ci-dessus s'avère fondamentale pour obtenir des
économies en termes de mémoire et de cycles cpu (code compact et plus rapide).
Voici quelques captures d'écran de l’exécution de la routine modifié :
Le code complet du programme modifié se trouve ci-dessous :
irq01:
lda #0
ldx $d012
!: cpx $d012
beq !-
sta $d020
sta $d021
lda #<irq00
sta $0314
lda #>irq00
sta $0315
lda #$08
sta $d012
lsr $d019
jmp $ea31
irq00:
lda #$01
sta $d020
sta $d021
lda #<irq01
sta $0314
lda #>irq01
sta $0315
lsr $d019
jmp $ea81
irq_table:
.fill 128,144.0 + 64.0 * sin(i * PI * 2 / 128.0)
Avant de montrer d'autres exemples d'application, nous pensons qu'il est
nécessaire de clarifier certains aspects théoriques rencontrés précédemment.
Lorsque nous avons traité les accès en mémoire effectués par le VIC notamment
pour l’affichage du texte ou des graphismes, nous avons évoqué les accès à
la mémoire vidéo pour la lecture des Codes Ecran (au moins en Mode Texte).
A cette occasion, nous avons utilisé comme synonymes de « mémoire vidéo » les
termes de « mémoire d'écran » et de « matrice vidéo ».
Figure 1 : Différence entre la mémoire d’'écran et la matrice vidéo (extrait de “Mappa di Memoria del C64”)
Quoi qu'il en soit, en pratique, les deux termes - mémoire d'écran et matrice
vidéo - peuvent en principe être considérés comme équivalents et donc
interchangeables.
Toujours à propos des accès mémoire supplémentaires effectués par le VIC lors
d’une « BAD LINE », nous avons dit qu'en plus des accès à la matrice vidéo,
le VIC lit également les informations de la couleur - code couleur à 4 bits,
Fig. 2 - de la « color RAM » entre les adresses $d800 et $dbff.
La « color RAM » est une zone de mémoire fixe qui n'est pas déplaçable par
l'utilisateur contrairement à la mémoire d'écran.
Le code couleur sera stocké dans les quatre bits les plus significatifs du
bus de données du VIC (DB8 à DB11) à la fois en Mode text – Standard
(également appelé Hires ou Normal ou Monocolor), Multicolor et Extended
Background (ECM) - et en mode Bitmap Multicolor.
Les 8 bits restants du bus de données de la puce graphique (DB0 à DB7) peuvent
contenir soit la valeur du code écran - en mode texte - soit, en mode bitmap,
des informations supplémentaires sur les couleurs (Fig. 4, 5 et 6).
Figure 5 : Origine des couleurs en mode texte Extended Background (ou ECM : Extended Color
Mode). Les bits D6 et D7 dans la figure se réfèrent aux 2 bits les plus significatifs du code caractère.
// Scroll Text
// Défilement horizontal du texte (HSCroll) Registre du $d016 du VIC (bit 0-2)
// Par Attilio Capuozzo pour "RetroProgramming Italia - RP Italia"
// KickAssembler Version
* = $c000
.label NMIRoutine = $fec1 // Adresse de la dernière instruction du NMI Handler par défaut ($fe47) correspondant à l'instruction RTI
scrolling: // Label de Gestion des Interruptions de trame à la ligne 0 (moitié supérieure de l'écran avec effet de défilement horizontal "smooth")
lda scrollValue,x // Lecture de la valeur de défilement horizontal du tableau
sta $d016 // Stocker la valeur du défilement horizontal en bits 0-2 (valeurs de 0 à 7) de $d016
stx $fb // Sauvez le compteur en mémoire
lda #$9a // Ligne de trame de la prochaine interruption de trame : ligne 154 (9a)
sta $d012
jmp testTimerAInterrupt
nextRaster: // Gestion de la moitié inférieure de l'écran à la ligne de trame 154 (9a)
lda #$08
sta $d016 // Nous chargeons la valeur par défaut (08) en $d016 qui correspond à l'absence de Défilement Horizontal (bit 0-2=0)
lda #$00 // Ligne de trame de la prochaine interruption de trame : ligne $00
sta $d012
testTimerAInterrupt:
lda $dc0d // Vérifions si une demande d'interruption système a été générée dans l'intervalle (IRQ du Timer A du CIA 1)
and #$01 // Vérifions donc si le bit 0 (LSB) de $dc0d est défini(=1)
beq exitPullStack // Si une interruption IRQ du Timer A n'est PAS signalée (c'est-à-dire le bit 0=0 de $dc0d), nous sortons normalement
jmp $ea31 // sinon (bit 0=1 de $dc0d) nous faisons un JMP vers le gestionnaire d'interruption du système ($ea31) pour le Keyboard Scan, etc.
exitPullStack:
pla // Les instructions suivantes occupent 6 octets et sont l'équivalent de Jmp $ea81 ou Jmp $febc
tay // les points d'entrée (pointant sur les 6 derniers octets) du gestionnaire d'interruption du système (IRQ-->$ea31 et NMI-->$fe47)
pla // Extraction de la pile des registres Y, X et A + retour depuis l'interruption
tax // La poussée vers la pile des 3 registres de données du 6510 est effectuée par le gestionnaire d'interruption IRQ principal du système
pla // mappé à l'adresse ROM $ff48 et vectorisée par les emplacements $fffe-$ffff de la ROM Kernal (derniers 8K de la carte mémoire C64).
rti // Le gestionnaire d'interruption IRQ principal du système ($ff48) est exécuté AVANT le gestionnaire d'IRQ système ($ea31) vectorisé en RAM
// par $0314-$0315 (CINV).
scrollValue: // Tableau des 14 valeurs de défilement horizontal du registre $d016 de (0 à 7) + 8 (valeur par défaut du registre $d016)
.byte 8,9,10,11,12,13,14,15
.byte 14,13,12,11,10,9
Nous notons tout d'abord l'astuce utilisée dans le Setup pour désactiver la
sortie forcée du programme en appuyant simultanément sur les touches RUN/STOP
et RESTORE :
ldx #<NMIRoutine
ldy #>NMIRoutine
stx $0318
sty $0319
N'oubliez pas qu'en appuyant sur la touche RESTORE, vous déclenchez une
interruption NMI.
bit $d012
bmi nextRaster
testTimerAInterrupt:
lda $dc0d
and #$01
Nous tenons à souligner que, bien que toutes les sources d’interruption de
la CIA 1 aient été précédemment désactivées dans la configuration :
lda #$7f
sta $dc0d
le flag d'interruption du Timer A - bit 0 de $dc0d - sera également activé
au cas où la condition d'interruption se produirait (Underflow du Timer A de
CIA 1 comme déjà vu) mais la demande ne sera PAS servie et l'interruption
IRQ ne sera PAS déclenchée.
exitPullStack:
pla
tay
pla
tax
pla
rti