Table des matières

Retourner au sommaire

Florilège de programmes

Hum ? Vous trouverez ici divers petits programmes faisant un peu tout et n'importe quoi, mais dont le fonctionnement est décrit en détail. Cette rubrique s'adresse surtout aux débutants qui cherchent de petits exemples de programmation pratique pour mettre le pied à l'étrier.

Spirale en ASCII

Il faudrait remanier le listing car il est vraiment rock'n roll !

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

Qu'est-ce qui se cache donc derrière ce titre énigmatique ? Eh bien, Il s'agit de remplir un écran avec un caractère ASCII donné… mais pas de la bête façon que tout le monde connait : HAUTEUR*LARGEUR de CHR$(ASCII). Non, c'est plus subtil. On fait un carré qui se resserre ; les bords du carré sont constitués du caractère en question. Mais où est l'intérêt d'un tel programme ? J'explique : tout d'abord le choix du caractère, puis l'automodification.

Le choix du caractère

Rhôôôô ! Comme vous le savez tous, lors d'un CALL &4000,128, A vaudra 1 et DE vaudra 128 (D vaut 0 et E vaut 128). J'explique. L'accumulateur (de son petit nom accu) est chargé du nombre de paramètres du CALL et DE contient la valeur du dernier paramètre. Un petit exemple s'impose :

CALL &4000,1,2,3,9 → A vaudra 4 et DE vaudra 9.

Compris ?

Mais est-ce pour autant que les autres paramètres sont oubliés ? Que nenni, que nenni. Ils sont gardés bien au chaud dans une pile sur laquelle pointe le registres d'index IX. Mais, ne serait-ce point là le principe des RSX ? Si !

Dernier point, jetez un coup d'œil aux deuxième et troisième lignes du programme OR A & JP Z,RESTA (j'aurais pu faire RET Z). Faites un essai avec CALL &4000 tout seul. Rien ne se passe. Magie ? Non. Traduisons donc. CALL &4000. Pas d'attibuts au CALL. A=0, c'est fini.

L'auto-modification

Dirigez-vous vers la fin du programme : le label CHANGE. On peut se permettre une auto-modif', car tout ceci n'est pas bien compliqué. Dès que le pointeur arrive aux nouvelles coordonnées, on change de séquence (on passe de SUITE2 à SUITE3 par la fin d'une boucle).

La valeur de la boucle est codée un octet après le label SUITE désiré. LD B,16 soit les opcodes &06,&10. C'est &10 que nous modifierons dans CHANGE. Comme les “cercles” sont “concentriques”, on diminue de 2 la longueur (1 à droite et 1 à gauche).

Le principe des coordonnées est on ne peut plus simple : je décompose les abscisses (ABS) et les ordonnées (ORD), que je charge dans l'accu qui, tel un bon fiston à son papa, les dépose langoureusement dans les poubelles H et L (qui sont en fait deux containers). Ok, c'est pas la plus rapide des méthodes, mais moi, j'men fous : j'ai plein de temps dans la synchro.

Dernière chose, le label RESTA. C'est bien beau de modifier le code, mais si on veut que le programme marche deux fois de suite, il vaut mieux faire comme si rien ne s'était passé. C'est pourquoi, inconnito, nous pokons avec de simples LD (ADRESSE),A les valeurs z'adéquates. Ok ?

Le point important du programme est le label AFFICHE qui contient aussi une auto-modification. LD (CHAR+1),A envoie un &3E valeur soit le numéro du caractère ASCII demandé.

Ben quoi ?

Listing : spirale en ASCII

Télécharger le listing assembleur au format Maxam 1.5

; Affichage de caractàres en spirale
; Par SNN pour Quasar CPC 7
;
; Lancement par : CALL &4000,ASC("X")
 
	ORG &4000
	NOLIST
 
	OR A		; A-t-on bien entré une valeur ?
	JP Z,RESTA
	LD A,E
	LD (CHAR+1),A
	JP START
 
ABS	db 1
ORD	db 1
RANG	db 1
 
START	LD A,(RANG)
	CP 13
	JP NZ,SUITEE
FIN	LD HL,&0C0D
	CALL &BB75
	LD B,29
	LD A,(CHAR+1)
BOUC	CALL &BB5A
	CALL &BD19
	DJNZ BOUC
 
RESTA	LD A,1
	LD (RANG),A
	LD A,39
	LD (SUITE+1),A
	LD (SUITE+3),A
	LD A,24
	LD (SUITE2+1),A
	LD (SUITE4+1),A
	RET
 
SUITEE	LD A,(RANG)
	LD (ABS),A
	LD A,(RANG)
	LD (ORD),A
SUITE	LD B,39
BOUC1	PUSH BC
	CALL AFFICHE
	LD A,(ABS)
	INC A
	LD (ABS),A
	POP BC
	DJNZ BOUC1
	JP SUITE2
 
AFFICHE	LD A,(ABS)
	LD H,A
	LD A,(ORD)
	LD L,A
	CALL &BB75
CHAR	LD A,140
	CALL &BB5A
	CALL &BD19
	RET
 
SUITE2	LD B,24
BOUC2	PUSH BC
	CALL AFFICHE
	LD A,(ORD)
	INC A
	LD (ORD),A
	POP BC
	DJNZ BOUC2
SUITE3	LD B,39
BOUC3	PUSH BC
	CALL AFFICHE
	LD A,(ABS)
	DEC A
	LD (ABS),A
	POP BC
	DJNZ BOUC3
SUITE4	LD B,24
BOUC4	PUSH BC
	CALL AFFICHE
	LD A,(ORD)
	DEC A
	LD (ORD),A
	POP BC
	DJNZ BOUC4
CHANGE	LD A,(RANG)
	INC A
	LD (RANG),A
	LD A,(SUITE+1)
	SUB A,2
	LD (SUITE+1),A
	LD A,(SUITE2+1)
	SUB A,2
	LD (SUITE2+1),A
	LD A,(SUITE3+1)
	SUB A,2
	LD (SUITE3+1),A
	LD A,(SUITE4+1)
	SUB A,2
	LD (SUITE4+1),A
	JP START

Scroll text

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

Il faudrait remanier le listing car il est un peu space !

À que salut, toi, mon lecteur, cette fois-ci, je te sors le grand jeu : un scroll-text hyper sympa, fluide et tout et tout, sans auto-modifications (désolé Zik), mais avec une tonne d'astuces qui donneront un plus à vos réalisations.

Un peu de brutalité est parfois nécessaire ! Bon, on dissèque (comme dirait Nicky One).

Qui fait quoi...

IX pointe sur le texte. Celui-ci, sauvé sous forme ASCII ne demande rien à personne. Dès qu'on se sert d'un caractère, on incrémente IX. C'est simple.

Bon, le petit blond à lunettes (qui est-ce donc ?) ne comprend pas le &C781. Ah… Bien. Voilà pourquoi. - Je lui attrape la tête, un coup de genou dans le nez - &C781 c'est la 25ème ligne de l'écran. C'est, en réalité 25*80+1. Pourquoi plus un ? - Coup de pied dans les burnes, uppercut dans la gu…. - Tout se rapporte au sous-programme ”SCROLL” qui fait, ça tombe bien, scroller la dernière ligne d'un octet sur la gauche. La mémoire source de LDIR (HL) pointe sur le label ”ECRAN” et la destination (DE), un octet avant, pointe sur la gauche, en &C780.

Halte, me dit le petit blond à lunettes - une baffe, il saigne, j'aime ça ! - Ok. J'ai oublié de dire qu'on ajoute ce nombre à l'adresse d'origine de l'écran, soit &C000. Mea culpa. - J'te file un mouchoir, tu salis ton t-shirt ! -

Mais...

Le principe du scrol est très simple. Qu'est-ce que tu veux encore ! Oui, je sais, on met deux “l” à scroll. - un direct du droit ; le nez part avec mon poing. C'est moche, le cartilage élastique - Les 79 octets sont tous décalés. L'octet 2 devient 1, le 3 devient 2, etc. dans cet ordre ! Dès la ligne finie, pas le temps de dire “ouf”, on récupère l'adresse d'origine, on trouve l'adresse du dessous (par un CALL &BC26 sur HL, c'est lent, mais ô combien pratique).

Cela revient à ajouter &800 (et pas &80) à l'adresse pointée par HL, sauf si on a un dépassement, dans ce cas, tout est prévu par le système, ça marche aussi. - Coup de pied au c.. ! Je sais, il a rien dit, le p'tit blond à lunettes, mais il allait le dire, que sur seulement 8 lignes, c'est pas la peine d'utiliser ce vecteur - En tout cas, le principe marche sur plus de 8 lignes.

Et après...

Inutile de préciser le rôle de &BB75 - Mer.. ! Je lui ai pété le bassin, il souffre, je vais l'achever - car Zik l'a fait dans l'avant dernier numéro. Chouette. - Tiens, une batte de baseball ! - Dès qu'on a décalé, on affiche à des coordonnées précises le caractère, par ce ”LOCATE”, au bout de la 25ème ligne, donc. - 5… 4… 3… - Voici donc le listing source, que vous allez vous empresser de saisir. - 2… 1… - S'il foire, c'est d'votre faute. Une remarque : il n'y a pas de limite, côté capacité, pour votre texte. Alors, - Splatch ! Boum ! Niark Niark Niark ! - bon scroll-text ! - Offset, Zik, vous êtes vengés, il ne vous emm…era plus. C'était de rien, inutile de me remercier, je n'ai fait que mon devoir. - Là !

Listing : scroll-text

     ORG &4000
        LD A,2
        CALL &BC0E  ; Vecteur MODE A
        LD IX,TEXTE ; Lisez TOUS les fanzines
START   LD HL,&C781
        LD (ECRAN),HL
        CALL &BD19  ; Vecteur FRAME (attente VBL)
        LD B,8
BOUC    PUSH BC
        LD HL,(ECRAN)
        PUSH HL
        LD DE,(ECRAN) ; Oui, tous !
        DEC DE
        LD BC,79
        LDIR
        POP HL
        CALL &BC26  ; Ligne du dessous !
        LD (ECRAN),HL
        POP BC
        DJNZ BOUC
        LD H,79
        LD L,25
        CALL &BB75  ; Vecteur LOCALE H,L
        LD A,(IX+0)
        OR A
        JP Z,RESET
        CALL &BB5A  ; Vecteur PRINT A
        INC IX
        JP START
TEXTE   DB " LISEZ QUASAR CPC !  AH ? VOUS LE LISEZ"
        DB " DEJA ? BEN ... LISEZ INFO SYSTEME CPC,"
        DB " BOXON ...  ET PUIS VENEZ AU MEETING DE"
        DB " NICKY ONE ! VOILA ! "
; Pour finir le scrolling, on fait une ligne vide !
; (80 caractères espace "chr$(&20)", logique)
        DS 80,&20
; Fini ! On n'oublie pas le NOP (&00) détecté par
; le bienheureux OR A, que remercie tous les soirs
; OffseT en allant au lit !
        NOP
RESET   LD A,2
        CALL &BC0E
        RET
ECRAN   DS 2 ; De la place pour stocker l'adresse !

Affichage de sprites

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

Je vais ici essayer de vous apprendre (enfin j'espère…) comment afficher un sprite (ou lutin pour les anglophobes).

Le LDIR

Allez, tire ! Commençons d'abord par la commande LDIR qui va nous servir. Cette instruction fait partie des quelques mnémoniques intégrées, c'est-à-dire que LDIR est équivalent à une succession de commandes élémentaires (qui prendraient beaucoup plus de temps machine utilisées directement). Je signale que LDIR signifie block LoaD and IncRment. Mais à quoi sert-ce ? me direz-vous d'un air hum, je m'égare… Cela sert tout simplement (bof !) à copier un bloc d'une adresse mémoire vers une autre (C'est étonnant ! N'est-il pas ?) ; on précisera également la longueur de ce bloc. Pour utiliser cette commande (enfin !), il faut préciser l'adresse mémoire du bloc à copier dans HL, l'adresse de destination de notre copie dans DE, et la longueur du bloc (en octets) dans BC.

Comme je veille consciencieusement à ce que vous compreniez tout ce que je raconte, voici un exemple :

       LD HL,&4000
       LD DE,&C000
       LD BC,&1000
       LDIR
       RET

Ce fabuleux exemple copie à l'écran un bloc de &1000 octets qui se situe en &4000, en commençant par le bord haut-gauche car, comme chacun sait, l'écran commence en &C000 et finit à l'adresse &FFFF (ce qui peut nous aider quelque peu pour afficher notre sprite).

Le sprite

Pour afficher un sprite, il faut qu'il soit stocké en mémoire et que l'on connaisse ses dimensions (qui sont ici en octets). Je vous donne tout de suite le programme avec LDIR qui affiche un sprite stocké en &4000 en haut à gauche de l'écran (donc en &C000) :

Eh ! Je suis pas un lutin moi, je suis un troll !

       LD DE,&C000
       LD HL,&4000
       LD B,50
BOUCLE PUSH BC
       PUSH DE
       LD BC,10
       LDIR
       POP DE
       EX,DE,HL
       CALL &BC26
       EX DE,HL
       POP BC
       DJNZ BOUCLE
       RET

Vous avez sûrement remarqué le CALL &BC26. Vous connaissez le CALL mais pas le vecteur &BC26 et c'est là votre tort ! Ce vecteur sert à calculer l'adresse de la ligne en dessous sur l'écran de l'adresse qu'HL contient. Je m'explique : vous mettez dans HL l'adresse écran et après le &BC26, HL contient l'adresse de l'octet qui, sur l'écran, est en dessous du précédent. Pour information, le vecteur &BC29 calcule l'adresse au dessus. Si vous êtes curieux de la structure de la mémoire vidéo sur CPC, je ne saurais trop vous conseiller d'aller lire cet article.

Par ailleurs, comme &BC26 agit sur HL et qu'on utilise DE pour pointeur sur l'écran, on place un EX DE,HL qui échange les contenus de DE et de HL ; le contenu de DE va dans HL et vice versa.

Une autre chose qui peut vous paraître curieuse est que je sauve DE et pas HL (PUSH et POP). En effet, LDIR modifie des registres, ainsi après un LDIR :

Donc, toujours dans notre exemple, après le LDIR, le registre HL pointe juste à la bonne adresse ; mais par contre, pour afficher la ligne en dessous de la précédente, il faut que DE pointe sur la gauche de celle-ci au moment où l'on veut faire le calcul de la ligne suivante, c'est-à-dire avant le CALL &BC26 (précédé du EX). Et c'est pourquoi on sauve DE quand il contient la bonne adresse, c'est-à-dire avant le LDIR.

Ce programme affiche donc 50 fois une ligne de 10 octets de large en descendant à chaque fois d'une ligne grâce au fameux &BC26.

L'animation du sprite

Voyons maintenant le programme qui se trouve ci-après. Il affiche un sprite qui se déplace de gauche à droite, puis de droite à gauche, et ainsi de suite. J'espère que les commentaire que j'ai rajouté vous aideront efficacement pour comprendre ce programme qui est assez complexe puisqu'il s'auto-modifie.

L'auto-modification consiste à faire modifier les commandes d'un programme par lui-même lors de son exécution. Pour remplacer des commandes par d'autres, il faut savoir en combient d'octets sont codées les instructions à écrire, la manière la plus simple étant d'observer à l'assemblage. Pour plus d'informations, allez consulter la rubrique consacrée aux auto-modifications.

La structure du programme est la suivante :

Les sous-programmes d'auto-modif écrivent soit un DEC HL, soit un INC HL (ces commandes font un seul octet), ils changent également le test qu'il faut faire sur HL (CP exprimé avec une valeur prendre 2 octets : le second est la valeur de comparaison), et enfin, ils modifient l'adresse du sous-programme d'auto-modif' où il doit sauter (un CALL est codé sur 3 octets : les deux derniers codent l'adresse où aller, les adresses sont toujours stockées à l'envers, ainsi &C000 sera codé &00 puis &C0).

Bouge non d'un chien !

Listing : sprite animé

Télécharger le listing au format Maxam 1.5

;
; Routine d'affichage de sprite
;   Par Zik pour Quasar CPC 4
;
 
SPR	EQU	&B000		; Adresse de stockage du sprite
HAUT	EQU	16		; Hauteur du sprite en octets
LARGE	EQU	20		; Largeur du sprite en octets
 
	ORG 	&4000		; Programme à assembler en &4000
 
	LD 	A,2		; A contient le numéro du mode
	CALL 	&BC0E		; écran désiré et &BC0E le met.
 
	LD	HL,&C000
DEBUT	LD	B,&F5		; Cette petite boucle correspond
ATTEND	IN	A,(C)		; au vecteur &BD19 qui attend la
	RRA			; fin du balayage de l'écran pour
	JR	NC,ATTEND	; rendre la main...
 
	CALL	AFFICHE		; Copie du sprite SPR à l'adresse HL
 
MODIF1	INC 	HL
	LD	A,L		; On teste si l'on est à droite
MODIF2	CP	80-LARGE	; ou à gauche de l'écran.
MODIF3	CALL	Z,RECTIF1
	XOR 	A		; &BB1B teste tout le clavier et
	CALL	&BB1B		; renvoie le code ASCII d'une touche
	CP	32		; éventuellement enfoncée dans A
	JR	NZ,DEBUT	; 32 = Espace. Si A=32 on passe au RET
 
	RET
 
RECTIF1	PUSH	HL
	LD	A,(DATA1)	; On écrit un DEC HL à la place du
	LD	(MODIF1),A	; INC HL en MODIF1 (Voir texte)
	XOR	A		; On écrit un 0 après le CP de
	LD	(MODIF2+1),A	; MODIF2 (Voir texte)
	LD	HL,RECTIF2	; On "poke" l'adresse de RECTIF2
	LD	(MODIF3+1),HL	; après le CALL de MODIF3 (V.T.)
	POP	HL
	RET
 
RECTIF2	PUSH	HL
	LD	A,(DATA2)	; On écrit un INC HL à la place du
	LD	(MODIF1),A	; DEC HL en MODIF1 (Voir texte)
	LD	A,80-LARGE	; On écrit un 80-LARGE après le CP
	LD	(MODIF2+1),A	; de MODIF2 (Voir texte)
	LD	HL,RECTIF1	; On "poke" l'adresse de RECTIF1
	LD	(MODIF3+1),HL	; après le CALL de MODIF3 (V.T.)
	POP	HL
	RET
 
AFFICHE	PUSH	HL		; Revoici à peu près la routine
	LD	DE,SPR		; d'affichage que l'on a vu dans la
	LD	B,HAUT		; première partir de ce cours.
BOUCLE	PUSH	BC		; La seule différence est que l'on
	PUSH	HL		; considère que LDIR écrit en HL
	LD	BC,LARGE	; et lit en DE jusqu'à ce que l'on
	EX	DE,HL		; ait à utiliser cette commande que
	LDIR			; l'on encadre alors de deux jolis
	EX	DE,HL		; EX DE,HL (qui inversent les contenus
	POP	HL		; de DE et de HL).
	CALL	BC26		; On appelle notre BC26
	POP	BC		; Et on continue tant qu'il y a
	DJNZ	BOUCLE		; des lignes à afficher...
	POP	HL
	RET
 
BC26	LD	A,H		; Notre fameux (et adoré) BC26...
	ADD	A,8		; Encore et toujours
	LD	H,A		; Ça continue
	RET	NC		; On revient ou pas ?
	LD	BC,&C050	; Ici on est resté...
	ADD	HL,BC		; et puis...
	RET			; on repart.
 
DATA1	DEC	HL		; On connait pas la valeur hexa des
DATA2	INC	HL		; commandes -> on écrit pour lire après