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.
— 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.
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.
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é.
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
— 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.
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 ! -
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.
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à !
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 !
— 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).
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).
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
) :
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
.
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
).
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