Ceci est une ancienne révision du document !


Retourner au sommaire

Les auto-modifications

Basé sur l'article publié dans Quasar CPC numéro 6, Initiation à l'Assembleur, par Zik.

Snif... Comme le laisse présager le titre ci-dessus, nous sommes dans cette rubrique d'initiation à l'assembleur pour parler plus en détail des auto-modifications (j'espère que SNN sera content !).

C'est quoi une auto-modification ?

Eh bien c'est très simple : cela consiste à faire évoluer le programme au cours de son exécution. Par “évoluer”, je veux dire que le programme s'écrit dessus afin de s'adapter à une nouvelle situation ; on peut écrire des valeurs qui serviront de données ou qui correspondront carrément à des instructions , puisque nous avons vu dans la rubrique d'initiation au Z80 que chaque commande de l'assembleur correspond à un nombre ou à une séquence de nombres.

Par exemple, &7F est le codage en mémoire de LD A,(HL) et &ED puis &B0 correspond à la seule instruction LDIR. Le fait que chaque commande soit en fait un ou plusieurs nombres explique par ailleurs que l'on ait besoin d'assembler son programme avant de le lancer.

Nous verrons contrètemement un des intérêts des auto-modifs (j'abrège !) dans le traditionnel programme d'exemple, mais je vais d'abord vous en montrer de différents types pour se faire la main car ce n'est pas toujours évident.

Soyons concrets

Moi je m'y connais en modifications ! Imaginons qu'à un moment donné d'un programme on ait à changer l'adresse d'un JP. Au lieu de faire un test fastidieux et gourmand en temps machine (vous allez dire qu'on n'est pas à ça près mais bon, il est toujours bon de gagner quelques micro-secondes), on a la possibilité de faire une sympathique auto-modification (étonnant n'est-ce pas ?).

Admirez donc cette routine :

MODIF JP &4000         ; Voici un JumP bête mais pas méchant
      ...
      LD HL,&5000      ; HL = &5000
      LD (MODIF+1),HL  ; On met donc &5000 en MODIF+1

Ces deux lignes d'auto-modif changent le JP &4000 initial en tout aussi bête mais pas plus méchant JP &5000 (si vous ne me croyez pas, vous n'avez qu'à essayer vous même1) ). Mais pourquoi ai-je mis MODIF+1 et pas MODIF (tout court) ?! Eh bien c'est très simple, si on regarde le fabuleux tableau qui vous a été gracieusement dévoilé dans la rubrique d'initiation au Z80 et qu'on y cherche l'instruction JP addr (puisque c'est notre cas), on voit écrit à droite &C3,ll,hh. Cela signifie que l'adresse (llhh) est stockée 1 octet après l'adresse mémoire où est placée la commande (&C3 signifie JP, l'endroit du saut mémoire est codé à l'envers juste après : JP &5010 donnera en mémoire &C3,&10,&50 et non pas &C3,&50,&10 qui serait l'équivalent de JP &1050). On comprend donc le MODIF+1 (si vous n'avez pas compris, essayez donc avec une aspirine).

L'histoire du MODIF+1 est également valable avec un CP data : on a en mémoire &FE puis la valeur de comparaison un octet après. Ne soyez donc pas étonnés de recontrer le fameux +1 dans le programme d'exemple. À noter que pour certaines instructions, notamment celles utilisant IX ou IY, c'est un +2 qu'il vous faudra puisque le code d'instruction fait alors 2 octets au lieu d'un seul.

Le programme

Laissons tomber les auto-modiifcations un instant et penchons-nous sur les vecteurs du système que j'ai utilisé dans mes routines :

Attention, je renvoie la balle !

  • &BC0E : vous devez déjà le connaître, c'est l'équivalent de l'instruction MODE du BASIC. Il suffit de mettre le numéro du mode désiré dans l'accumulateur puis de faire le CALL. À la sortie du vecteur, les registres AF, BC, DE et HL ont été modifiés.
  • &BD19 : cela sert à attendre que le canon à électron de votre écran soit prêt à afficher une nouvelle image. Grâce à ce vecteur, on évite les clignotements dans les animations (c'est le FRAME du BASIC, disponible uniquement à partir du BASIC 1.1 des CPC664 et CPC6128).
  • &BB75 : correspond au LOCATE du BASIC. H contient l'abscisse et L contient l'ordonnée. AF et HL sont modifiés.
  • &BB5A : c'est presque le PRINT du BASIC. Cette routine affiche le caractère dont le code ASCII est dans l'accumulateur. Les registres ne sont pas modifés.
  • &BB1B : teste si une touche du clavier est enfoncée ; si c'est le cas, A contient le code ASCII du caractère correspondant à la touche et la carry vaut 1. Si aucune touche n'est enfoncée, A n'est pas modifié et la carry vaut 0. Comme A n'est pas modifé, je mets un XOR A (mise à 0 de A) avant le CALL &BB1B comme ça je suis sûr que si A=32 (espace) après le CALL c'est bien parce que la touche espace est enfoncée.

Je pense que le programme comporte assez de commentaires pour que vous compreniez. Il fait rebondir un “O” sur les bords de l'écran ; ainsi il faut un test de maximum et minimum sur les coordonnées en X et en Y. Quand ces limites sont dépassées, on change le sens d'évolution du compteurs (INC ou 'DEC'') et ce qui va avec. Puis on efface et réaffiche la balle.

Listing : exemple d'auto-modifs

Télécharger le listing au format Maxam 1.5

	Org &4000
;
	ld a,2
	call &bc0e	; Équivalent du MODE 2 du Basic (A contient le mode)
;
Debut	call &bd19
	ld hl,(oldpos)	; HL prend les valeurs du 2nd compteur
	call &bb75	; = LOCATE H,L (en Basic)
	ld a," "
	call &bb5a	; Affichage d'un espace (et donc effacement du O ancien)
;
	ld hl,(posit)	; HL prend les valeurs du 1er compteur
	ld (oldpos),hl
	call &bb75	; LOCATE H,L
	ld a,"O"
	call &bb5a	; Affichage d'un banal "O"
 
; Mise à jour du compteur en Y
 
	ld a,(posit)	; A contient la position du curseur en Y
Mod1	inc a
Mod2	cp 26
Mod3	call z,decy
	ld (posit),a
 
; Mise à jour du compteur en X
 
	ld a,(posit+1)	; A contient la position du curseur en X
Mod4	inc a
Mod5	cp 81		; (il y a 80 colonnes en mode 2)
Mod6	call z,decx
	ld (posit+1),a
 
; Test clavier
 
	xor a
	call &bb1b
	cp 32
	jp nz,debut
	ret
;
; Compteurs
;
Posit	dw &0101
OldPos	dw &0101
 
;
; Sous-programmes d'automodification
;
 
; En ordonnée
 
DecY	ld a,&3d	; &3d=DEC A (Voir Quasar CPC 5)
	ld (mod1),a	; On "poke" DEC A à la place du INC A
	xor a		; A contient 0
	ld (mod2+1),a	; On place 0 à la place du 26 pour avoir CP 0
	ld hl,incy	; HL contient l'adresse de la routine appelée IncY
	ld (mod3+1),hl	; On met cette adresse après le CALL Z
	ld a,24
	ret
 
IncY	ld a,&3c	; &3C=INC A (Voir Quasar CPC 5)
	ld (mod1),a	; On met un INC A à la place du DEC A
	ld a,26		; A contient 26
	ld (mod2+1),a	; On place 26 à la place du 0 pour avoir CP 26
	ld hl,decy	; HL contient l'adresse de la routine appelée DecY
	ld (mod3+1),hl	; On met cette adresse après le CALL Z
	ld a,2
	ret
 
; En abscisse
 
DecX	ld a,&3d	; &3d=DEC A (Voir Quasar CPC 5)
	ld (mod4),a	; On "poke" DEC A à la place du INC A
	xor a		; A contient 0
	ld (mod5+1),a	; On place 0 à la place du 01 pour avoir CP 0
	ld hl,incx	; HL contient l'adresse de la routine appelée IncX
	ld (mod6+1),hl	; On met cette adresse après le CALL Z
	ld a,79
	ret
 
IncX	ld a,&3c	; &3c=INC A (Voir Quasar CPC 5)
	ld (mod4),a	; On met un INC A à la place du DEC A
	ld a,81		; A contient 81
	ld (mod5+1),a	; On place 81 à la place du 0 pour avoir CP 81
	ld hl,decx	; HL contient l'adresse de la routine appelée DecX
	ld (mod6+1),hl	; On met cette adresse après le CALL Z
	ld a,2
	ret
1) et toc !
 
iassem/automodif.1420882102.txt.gz · Dernière modification: 2017/10/09 10:24 (édition externe)