Table des matières

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 !

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 !