Canalblog
Editer l'article Suivre ce blog Administration + Créer mon blog
Publicité
Rétro Poke
30 avril 2019

Assembleur: Les Maxi-Caractères (Dernière partie)

Hello,
Un projet se termine, un autre prend forme. Depuis que je me suis remis à la programmation (Basic ou Assembleur), une grande partie de mes projets que j'avais dans les années peek et poke, me reviennent en mémoire.
Il faut avouer que l' Amstrad à joué un grand rôle dans ma jeunesse. Son language 'Basic' et 'Binaire' m'a ouvert des portes qui ne se refermeront plus. Je ne sais pas le nombre d'heures que j'ai passé à recopier des listing ou à essayer des routines, mais tous les jours, en rentrant du taf, ma priorité était consacré à l'Amstrad.

Revenons-en a nos Maxi Caractères et MaxiCHR. C'est à dire en MAI 2019 😃

Vous trouverez tous les détails de chaque routines (RSX) et ma méthode de programmation. Si vous avez des questions, des suggestions ou des critiques, n'hésitez pas, j'en serais ravi !
En bas de la page vous aurez le lien de téléchargement du fichier 'ZIP' qui comprend la disquette et les listings 'ASM'

ROUTINE RSXENCRE et RSXPAPIER:
; ########## - ########## - ########## - ########## - ########## - ########## - ########## - ##########
; # RSX ENCRE et RSX PAPIER C'est deux RSX font le meme topo, seul le registre 'HL' du depart change
RSXENCRE                   ; ENCRE, Coul1 [, Coul2] [, Coul3] [, Coul4]
                           ; PAPIER, Coul1 [, Coul2] [, Coul3] [, Coul4]
; CE> Les parametres [optionnels] gardent leurs anciennes valeurs

       LD HL, Couleur - 1 ; 'HL' est positionne sur l' emplacement des encres - 1
       JR PourLePapier ; On saute pour la suite du programme

RSXPAPIER
       LD HL, Couleur + 3 ; 'HL' pointe sur l'emplacement du papiers - 1

PourLePapier              ; A ce stade, 'HL' pointe soit sur les encres soit sur les papiers
       CP 5               ; On regarde si on ne depasse pas les 4 parametres
       RET NC             ; Sinon on sort sans bouder vers le basic
       OR A               ; Et si il n'y a pas de parametre
       RET Z              ; On quitte le navire aussi
       LD B, 0            ; On va ajouter a 'HL' le nombre de parametres
       LD C, A            ; 'C' = nombre de parametre
       ADD HL, BC         ; 'HL' pointe sur la derniere couleur a traiter
       LD B, A            ; 'B' contient le nombres de parametre

Couleur_Encre
       LD A, (IX + 0)     ; 'A' = valeur du parametre
       CP 16              ; Si >= 16, il n'y a que 16 couleurs en MODE 0
       RET NC             ; On ne reste pas, Faut pas pousser non plus
; Les couleurs sont codees et il faut chercher la correspondance avec les couleurs du MODE 0
       LD DE, CouleurCodee ; 'DE' pointe sur la table des couleurs codees
       ADD A, E           ; 'A' Contient le numero de la couleur demandee
       LD E, A            ; 'DE' Pointe sur la correspondance de la couleur demandee
       LD A, (DE)         ; 'A' est charge avec le code de la couleur codee de MODE 0
       LD (HL), A         ; On l'incrit simplement dans 'HL'
       DEC HL             ; Et on passe a la couleur suivant
       INC IX
       INC IX             ; Et aussi au parametre suivant
       DJNZ Couleur_Encre ; On recommence tant qu'il y a des parametres
       RET

Vous remarquerez que j'ai programmé deux routines en une. Les deux routines sont identiques, seul le positionnement sur la variable 'Couleur' change.
Au début je déclare la variable qui servira d'adresse de départ pour les RSX, mais ici deux variables seront déclarées (RSXENCRE et RSXPAPIER). Lors de la création des RSX, chacun sera ou commencer le code à executer.
- RSXENCRE, Coul1 [,Coul2] [,Coul3] [,Coul4]
Cet RSX sera chargé de décoder et d'inscrire les couleurs correspondants de vos encres à partir de la variables (Couleur)
- RSXPAPIER, Coul1 [,Coul2] [,Coul3] [,Coul4]
Et ce RSX fera la même chose mais à partir de la variable (Couleur + 4)
Au départ des deux RSX, on charge le registre 'HL' avec la valeur appropriée -1.
Pourquoi ce  -1 ?, Tout simplement parce que lorsque l'on appelle un RSX, le dernier paramètre transmis se trouve en début du registre (IX). Il nous faut ajouter le nombre de paramètre transmis avec le registre 'HL' pour que celui-ci point sur  sur la bonne couleurs. Si je n'avais pas opté pour des paramètres optionnels, on aurait pu simplifier la programmation, mais de cette façon, je vous montre comment mettre en option certains des paramètres.

Voici mon principe simplifié pour créer un RSX avec des paramètres optionnels: 

parametre

En 1:On appelle le RSX 'ENCRE' avec 3 paramètres
'HL' pointe sur la variable 'Couleur' -  1

En 2: On addition le nombre de paramètres avec 'HL'
Celui-ci pointe sur la variable 'Couleur' + 2 (Couleur-1) + (3paramètres)
On y inscrit le paramètre pointé par 'IX' (Couleur + 2) = 4

En 3: On décrémente 'HL' (Couleur+1) et on passe au paramètre-1 en incrémentent 'IX' deux fois
La valeur du paramètre est inscrite dans 'HL' (Couleur + 1) = 8

En 4 On procède de même en décrémentant 'HL' (Couleur) et et remontant au premier paramètre
On inscrit la valeur du premier paramètre dans 'HL' (Couleur) = 2

Conclusion: On a bien inscrit depuis la variable (Couleur) tous les paramètres transmis tout en gardant les valeurs optionnels dans leurs états. Mais ce n'était qu'un exemple car il faut aussi décoder les fameuses couleurs codées avant de les inscrire. Cela est expliqué ici: Les Maxi-Caractères (Deuxième partie)

Pour le reste, le programme est auto-documenté mais si vous avez des questions, laissez moi un commentaire, merci.

On passe à la routine suivante:
Routine RSXCURSEUR; # RSX CURSEUR
RSXCURSEUR               ; Positionne le curseur pour le prochain affichage des maxis caracteres
; CE> CURSEUR, Col, Lig (Comme pour la commande Basic 'LOCATE' du MODE 0)
       CP A, 2           ; Si le nombre de parametres envoye est different de 2
       RET NZ            ; On renvoie au basic
       LD A, (IX + 2)    ; 'A' = Col
       CP A , 21         ; Col (1 a 20) MODE 0
       RET NC            ; Retour au basic, parametres non valides
       CP A, 1
       RET C
       DEC A             ; On reajuste pour les calculs (0 a 19)
       ADD A, A          ; (x2)
       ADD A, A          ; (x4)
       ADD A, A          ; (x8) Par 8 car chaque caractere fait 8 points de largeur
       LD H, A           ; 'H' Contient la valeur pour les Colonnes
       LD A, E           ; 'A' = Lig (A l'appel du RSX, 'E' contient le dernier parametre)
       CP A, 26          ; Lig (1 a 25) On garde le principe du 'LOCATE'
       RET NC            ; Retour basic, les parametres ne sont pas valides
       CP A, 1
       RET C
       DEC A             ; Mais il faut soustraire 1 car les vrais coordonnes commence a 0
       ADD A, A          ; (x2) Il faut multiplier sa valeur par 8
       ADD A, A          ; (x4)
       ADD A, A          ; (x8) multiplie par 8, 1 caractere=8 lignes
       LD L, A           ; 'L' contient les lignes en partant du bas
       LD A, 199         ; Pour partir du haut a gauche, il faut inverser les lignes
       SUB A, L          ; Car la position de depart commence en bas a gauche
       LD L, A
       LD (Curseur), HL  ; On inscrit les coordonnees a l'abris des poussiere
       RET

En prenant le principe de la commande 'LOCATE' du basic Amstrad, cette routine est plus que simple à mettre en oeuvre. On aurait pu garder la position du curseur de la commande 'LOCATE' mais pour le RSX 'GCURSEUR', il nous fallait une base pour gérer les coordonnées.
Au début, on regarde si les coordonnées de l'axe X (Col) sont compris de (1 à 20), sinon on retourne au basic. Je n'ai pas su comment provoquer une erreur et retourner au basic avec pour effet une sorte de (PRINT "ERREUR:n°": END) donc rien n'indique qu'il y a des erreurs dans les paramètres, mais rien n'est pris en compte si c'est le cas.
Il faut ensuite décrémenter de 1 car l'origine réelle de l'axe X est zéro. Les caractères font 8 pixels de large, donc le résultat doit être multiplié par 8.
Détail: Si vous voulez positionner le curseur sur la deuxième colonne, la valeur calculée sera (2-1)*8=8. De 8 à 15 le caractère sera affiché, et en regardant bien pour la position 1 de l'axe X (1-1)*8=0 de 0 à 7 cela correspond bien à 1 caractère de largeur sur la première colonne.
On procède de la même façon pour l'Axe Y. Après les fameux tests pour être situer de (1 à 25), puis décrémenter de 1 et multiplier aussi par 8 (Les caractères ont une hauteur de 8 pixels).
Mais cette fois, l'origine de l'axe Y démarre du bas, or pour les calculs de l'adresse d'écran, puisque j'ai voulu garder le principe de la commande 'LOCATE', l'origine doit se situer en haut. Il faut donc inverser l'origine des lignes, et comme l' Amstrad affiche 200 lignes (0 à 199), il faut simplement soustraire 199 avec le résultat obtenu de l'axe Y.
En exemple on va partir sur un CURSEUR,x, 14: (14-1)*8=104
104 représente le début de l'affichage d'un caractère sur la 14ème ligne. Le vecteur &BC1D qui calcule l'adresse d'écran, s'appuie sur ces coordonnées.  Donc, pour placer le point de départ sur l'écran, c'est plus simple de faire les calculs avant de d'appeler ce vecteur.
Une fois les calculs fait, on l'inscrit dans la variable prévue pour cet effet (Curseur).

Routine RSXGCURSEUR:
; # RSX GCURSEUR
RSXGCURSEUR             ; Place le curseur en position graphique (Du bas vers la gauche)
; CE> GCURSEUR, Col, Lig (Col 0-152) et (Lig 7-199)
       CP A, 2          ; Si le nombre de parametres envoye est different de 2
       RET NZ           ; Renvoie au basic
       LD A, (IX + 2)   ; 'A' = Col
       CP A , 153       ; de 152 a 159 On OK pour un autre caractere
       RET NC           ; Sinon retour au basic, parametres non valides
       LD H, A
       LD A, E          ; 'A' = Lig >>> 'E' = Dernier parametre donc 'A' = Ligne
       CP A, 200        ; Lig (7 a 199) 7 pour rester dans l'ecran
       RET NC
       CP A, 7          ; De 0 a 7 on peut encore afficher un caractere
       RET C            ; Retour basic, les parametres ne sont pas valides
       LD L, A          ; Pause cafe
       LD (Curseur), HL ; On inscrit les coordonnees dans la variable prevue
       RET

Cette routine est plus simple, puisque les coordonnées sont déjà calculées par vous. 
On regarde simplement si on pourra afficher un caractère à l'endroit que vous indiquez. Pour cela, les coordonnées de l'axe X ne doivent pas être supérieur à 152 sinon on sortirait de l'écran et on risquerait de faire planter des choux blancs. 🤔
Pour l'axe Y, c'est pareil, on ne doit pas dépasser le 200 et en dessous on est OK, mais on doit aussi contrôler la position du bas de l'écran et regarder si le caractère pourra s'affiche. En dessous de 7 on retourne vers le basic Tchou Tchou car il faut au minimum 8 pixels de hauteur (0 à 7)

ROUTINE RSX AFFICHE
; # RSX AFFICHE          ; Routine d'affiche d'une chaine de caractere
RSXAFFICHE
; AFFICHE, A$             Affiche la chaine A$ sans effet de ZOOM
; AFFICHE, Zoom, A$       Pareil que ce dessus et ZOOM en hauteur
; CE> A$ doit contenir au moins un caractere et les 256 caracteres Amstrad sont affichables
; CE> Zoom (1 a 25). Valeur par defaut ou si sortie ecran ZOOM = 1

       CP A, 3           ; Si le nombre de parametre envoye est >2... Retour Basic
       RET NC
       OR A              ; Ou si il n'y en a pas >Direction BASIC
       RET Z
       CP A, 1           ; Si 1 seul parametre, alors ZOOM = 1, Pas de ZOOM
       JR Z, AFF_PasDeZoom
       LD A, (IX + 2)    ; Le ZOOM doit etre verifie pour ne pas sortir de l'ecran
       CP A, 26 RET NC   ; Le ZOOM ne peut pas depasser 25x en hauteur
       LD B, A
       LD C, A
       SLA B             ; Il faut multiplier la hauteur par 8 pour calculer la position du curseur
       SLA B
       SLA B             ; En deplacant les BIT vers la gauche (x2 x4 x8)
       LD A, (Curseur)   ; On charge dans 'A' les coordonnees des lignes du (Curseur)
       DEC B             ; NB> pour les coordonnees des colonnes -> LD A, (Curseur + 1)
       CP A, B
       JR NC, AFF_ZoomOK
       LD C, 1           ; ca deborde, on remet la valeur par defaut du ZOOM pour eviter la casse

AFF_ZoomOK
       LD A, C

AFF_PasDeZoom
       LD (Zoom + 1), A  ; On inscrit la hauteur du ZOOM VERTICAL avant d'appeler la routine
       LD A, 8           ; On inscrit aussi le nombre de ligne (8=caractere et 16=MaxiCHR)
       LD (CarCHR + 1), A
       LD A, 170
       LD (RotCou + 1), A ; Et la rotation des couleurs, 170="10101010"
       LD H, (IX + 1)     ; On va recuperer l'adresse de la chaine A$
       LD L, (IX + 0)
       LD A, (HL)
       OR A               ; Si la chaine est vide... Retour vers le Basic
       RET Z
       INC HL             ; 'HL' contient l'adresse ou est stocke la chaine A$
       LD E, (HL)         ; Il faut faire pointer 'DE' pour recuperer les caracteres
       INC HL
       LD D, (HL)         ; 'DE' Pointe sur le 1er Caractere de la chaine
       LD B, A            ; Et 'B' = Longueur de la chaine

AFF_Total
   PUSH BC                ; On sauvegarde la longeur de la chaine
   PUSH DE                ; Ainsi que l'endroit ou elle est stockee
       LD A,(DE)          ; 'A' contient le code ascii du caractere

AFF_AMSTRAD
       LD DE, Caractere   ; DE pointe vers notre table reservee pour le travail
       CALL #BBA5         ; HL contient l'adresse en ROM du caractere
       CALL #B906         ; On Active la ROM Inferieur pour recuperer la matrice du caractere
       LD BC, 8           ; 'BC' contient le nombre d'octet a recuperer
       LDIR               ; Le transfert vers notre table est fini
       CALL #B90C         ; On restore la ROM utilisee

AFF_CHOIXOK
       CALL AfficheCarCHR ; On peut affiche le caractere en appelant la sous routine
       LD HL, (Curseur)   ; Et modifier la position du curseur pour le prochain caractere
       LD A, H            ; Cette fois, il faut compter le caractere qui vient d'etre affiche
       CP A, 146          ; Et voir si le prochain pourra etre affiche
       JR C, AFF_ColPlus  ; Alors on ajoutera 8 aux coordonnees des colonnes
       LD A, L            ; Sinon il faudra verifier si on peux descendre d'une ligne
       CP A, 15
       JR NC, AFF_LigPlus
       LD H,0             ; Si ni l'un ni l'autre est possible
       LD L, 199          ; On remet les origine par defaut (Haut Gauche)
       JP AFF_ColLigOK

AFF_LigPlus
       SUB A, 8           ; On descent d'une ligne donc Col=0
       LD L, A
       LD H, 0
       JP AFF_ColLigOK

AFF_ColPlus
       ADD A, 8 ; On se deplace d'une colonne et on garde la ligne
       LD H, A AFF_ColLigOK
       LD (Curseur), HL ; La variable est remise a jour avec les nouvelles coordonnees
   POP DE
   POP BC
       INC DE           ; Puis on passe au caractere suivant
       DEC B            ; Et on remonte jusqu'a afficher le dernier caractere
       JP NZ, AFF_Total
       RET

Cette routine affichera une chaîne de caractères avec la possibilité d'y ajouter un effet de zoom.
Les 256 caractères de l'Amstrad CPC peuvent être affichés. Pour avoir accès aux caractères non représentés sur le clavier, il vous faudra les ajouter à votre chaîne de caractère avec la commande 'CHR$(x)' du basic Amstrad.

Après la série de comparaison des paramètres, on regarde si la chaîne de caractères pourra être affiché avec le zoom demandé, sinon on affichera la chaîne sans zoom. Pour le savoir, on multiplie le zoom par 8 car on travaille avec des caractères de 8 pixels de haut. Ensuite on charge la position des lignes du curseur dans le registre 'A'. Une simple comparaison nous dira si le zoom est possible, sinon on met un zoom de 1 ce qui équivaut à pas de zoom.
Il faut initialiser la sous-routine 'AfficheCarLut' avec des valeurs pour afficher les caractères de 8 lignes.
D'abord on inscrit l'effet d'un zoom, puis vient le nombre de ligne à traiter. Avec des caractères, on a 8 lignes à traiter, mais avec les MaxiCHR, c'est 16 lignes à faire. Ici c'est pour des caractères Amstrad de 8 lignes.
Pour le roulement des couleurs en mode caractère de 8 lignes, il faut qu'elles tournent toutes les deux lignes, c'est pourquoi la valeur est 170='10101010' et pour les MaxiCHR la valeur est '10001000' car elles doivent tourner toutes les 4 lignes.
Les détails sont expliqués plus haut, je m'y attarde pas.
Dans un deuxième temps, on va récupérer l'adresse ou est stocké la chaîne à afficher. C'est un peu compliquer, mais une fois que l'on a compris, c'est acquis.
Je dirige le registre 'HL' pour récupérer l'adresse transmise en paramètres au travers de "IX + 0' et 'IX + 1'
A cet adresse, on a déjà le nombre de caractères contenu dans la chaîne.
Un octet plus loin, on obtient le poids faible de l'adresse de la chaîne que l'on place dans le registre 'E'
Et un octet plus loin, on obtient le poids fort que l'on place dans le registre 'D'
Au final: 'A' = Longueur de la chaîne et 'DE' pointe sur le premier caractère
Si c'est acquis, alors on continu, sinon faites donc !

On est fait prêt pour la troisième épreuves. Télécharger la matrice du caractère à afficher depuis la ROM Inférieur de l'Amstrad.
Il faut savoir que toutes les matrices des 256 caractères de l'Amstrad sont stockées dans une mémoire ROM qui n'est qu'en état de lecture uniquement. Pour afficher un caractère, il faut savoir ou il est stocké, un 'A' n'a pas la même adresse que un '3' par exemple mais notre ami 'SUGAR' y a pensé, et grâce aux vecteurs système on peut naviguer dans une partie de cet ROM.
Voici les vecteurs et leurs conditions:
- Vecteur &BBA5 : Ce vecteur recherche l'adresse du caractère dont le code ascii est dans le registre 'A'. Si le caractère n'est pas un caractère redéfini par l'utilisateur, alors 'HL' pointera sur son adresse située dans la ROM inférieur sinon le registre 'HL' pointera sur l'adresse en RAM ou le caractère est redéfini.
On ne s'occupera pas de savoir ou il est, on va simplement recopier les 8 octets qui forment le caractères vers notre variable (Caractere) afin de le travailler.
- Vecteur &B906 : Active la rom inférieur afin de pouvoir la lire
- Vecteur &B90C : Restaure la ROM , parfois j'ai des doutes, mais comme ça marche bien comme ça, je laisse en l'état.

Une fois que l'on a récupéré le code ASCII du caractère à afficher, on recherche l'adresse de sa matrice à l'aide du vecteur &BBA5, et on active la ROM inférieur pour récupérer 8 octets qui composent la matrice du caractère. On remet la ROM dans l'état avant de continuer le programme.
Il ne reste plus qu'à appeler la sous routine pour afficher le caractère situé à l'adresse de la variable (Caractere).

dernière étape. Repositionner le curseur pour le prochain affichage d'un caractère.
On a pas encore modifier la position du Curseur, on s'en est servi, mais sans remplacer les valeurs. Si je le précise, c'est pour bien comprendre la suite.
Tout d'abord, on place dans le registre 'H' les colonnes et dans le registre 'L' les lignes d'un coup avec l'instruction: LD (HL), Curseur.
Il faut se rappeler que l'on travail avec les coordonnées graphiques, de (0 à 159) pour les colonnes et de (0 à 199) pour les lignes.
On teste donc si le caractère suivant pourra s'afficher sur l'axe X, mais comme on a pas encore déplacer le curseur et que l'on vient d'afficher un caractère, il faut contrôler si  la position de 2 caractères est possible. Si l'axe X est en dessous de 146 alors on ajoutera 8 à l'axe X pour le déplacer d'un caractère et dire que le prochain emplacement est autorisé sinon l' Axe X sera remis à zéro et on testera si on peut l'afficher en dessous mais la c'est pareil, on doit aussi compter  le caractère que l'on vient d'afficher car l'origine se trouve en haut à gauche des caractères. Et si l'Axe Y est supérieur à 14, (de 0 à 15) alors c'est possible et on déduira 8 lignes mais autrement on remet les origines par défaut en haut à gauche de l'écran.

Il ne reste qu' à passer toute la chaîne de caractères, caractère par caractère pour finir cette routine.

 ROUTINE RSX DEFMAXICHR
; # RSX DEFMAXICHR RSXDEFMAXICHR
; DEFMAXICHR, Numero, Lig1 [,lig2] [,lig3]... ... [,lig16]
; Definit un MaxiCHR de 8x16 affichable avec le RSX MAXICHR (8=largeur et 16=hauteur)
; Les parametres optionnels sont remplaces par des ZERO
; CE > Numero, (de 0 a 23), indique le numero du MaxiCHR a redefinir

       CP 18             ; Si on a trop de parametre > Retour Basic
       RET NC
       CP 2              ; Si il y a pas assez de parametres > BASIC
       RET C
       LD B, 0           ; Il faut trouver le numero du MaxiCHR qui est dans le premier parametre
       DEC A             ; A l'appelle d'un RSX, 'A' Contient le nombre de parametre transmis
       LD C, A           ; 'C' = nombre de parametre moins 1
       SLA C             ; 'C' est multiplie par 2 car les parametres font 2 octets
       ADD IX, BC        ; IX pointe sur le premier parametre (L'Adresse)
       LD C, A           ; On remet dans 'C' le nombre de parametre moins 1 (Celui du numero)
       LD A, (IX + 0)    ; Et dans 'A', on inscrit le numero du MaxiCHR du premier parametre
       LD HL, TableMaxiChr ; 'HL' pointe sur le MaxiCHR numero 0
       OR A              ; Si on veut redefinir le MaxiCHR 0, alors on a pas besoin de chercher l'adresse
       JR Z, DEFMAXICHR_NUMERO
       CP A, 23          ; Ne pas depasser le numero 22 pour ne pas ecraser les donnees
       RET NC
       LD DE, 16         ; Il faut recherche l'adresse qui correspond au numero du MaxiCHR

DEFMAXICHR_TABLE
       ADD HL, DE        ; Un MaxiCHR fait 16 octets, donc on ajoute 16 a 'HL' pour chaque MaxiCHR
       DEC A             ; Jusqu'a obtenir le MaxiCHR que l'on a indique en premier parametre
       JR NZ, DEFMAXICHR_TABLE

DEFMAXICHR_NUMERO
       LD A, C           ; 'A' contient le nombre de parametre moins 1
       LD B, 16          ; On a 16 chiffre a inscrire.

DEF_MAXICHR_Lig
       LD D, 0           ; Valeur par defaut si le nombre de parametre est plus petit que 16
       OR A              ; 'A' sert de compteur, si 'A'=0 alors il n'y en a plus de parametre
       JR Z, DEF_MAXICHR_Param
       DEC IX            ; On fait pointer 'IX' sur le parametre
       DEC IX
       DEC A             ; Et on decremente le compteur de parametre 'A'
       LD D, (IX + 0)    ; 'D' est charge avec la valeur du parametre

DEF_MAXICHR_Param
       LD (HL), D       ; On inscrit soit le parametre, soit ZERO
       INC HL           ; et on passe au suivant
       DJNZ, DEF_MAXICHR_Lig ; Jusqu' inscrire 16 valeurs au total
       RET

Je ne sais pas comment ils ont programmé la commande 'SYMBOL' du basic Amstrad, j'aimerais bien le savoir afin de comparer ma routine qui se base exactement sur le même principe.
Cette routine permet de redéfinir un caractère de 8*16 dans les mêmes conditions que la routine 'SYMBOL'
Les paramètres omis sont remplacés par des zéro.
Il est peut être intéressant de comprendre comment compenser des paramètres optionnels pour leurs attribuer une valeur par défaut.
Au début on regarde si in n'y a pas trop ou pas assez de paramètres. Il faut 2 paramètres au minimum: Le numéro du MaxiCHR suivit d'au moins de la première ligne de définition. Tout de suite après, on va remonter au premier paramètre (Celui du Numéro) afin de savoir quel MaxiCHR redéfinir et aussi pour le comparer au maximum autorisé.
Pour cette opération, il faut additionner le nombre de paramètre - 1 avec le registre pointeur (IX). On sait que à l'appelle d'une fonction par un CALL ou un RSX, le registre (IX) pointe sur le dernier paramètre, mais que les paramètres qui précédent sont enregistrés en aval du registre (IX)
Par exemple: |DEFMAXICHR, 4, 255, 127, 63, 31, 15, 8, 19
Chaque paramètre est enregistré sur l' adresse que pointe le registre 'IX' - (Nombre de paramètre - 1) * 2
Il ne faut pas oublier que chacun des paramètres sont enregistrés sur deux octets (le poids fort et le poids faible), c'est le pourquoi qu'il faut multiplier par 2 pour retrouver les bonnes valeurs.
Avec l'image ci-dessous, les choses prennent sens:
appelle
Ici on a 8 paramètres, et pour retrouver le paramètre qui contient le numéro des MaxiCHR, il faut faire le calcul suivant:
'IX' = 'IX' + (Nombre de paramètre - 1) * 2, soit: 'BC' = (8 - 1) * 2 = 14: 'IX' = 'IX' + 'BC'
Maintenant que 'IX' pointe sur le premier paramètre, on n'a plus qu'à comparer que son contenu ne dépasse pas le nombre de MaxiCHR autorisé.
Si le numéro du MaxiCHR est correcte, on poursuit pour rechercher l'emplacement ou stocker ce caractère.
On connais l'adresse de départ du MaxiCHR numéro 0 qui correspond à la variable 'Caractere', alors pour savoir quelle est l'adresse des autres MaxiCHR, il faut faire un simple calcul en multipliant par 16 le numéro du caractère. Le MaxiCHR 1 sera donc stocké à l'adresse 'Caractère' + 16 et le MaxiCHR 2 sera stocké à l'adresse 'Caractere' + 32... etc... etc
Le programme se poursuit pour simplement écrire les valeurs des paramètres dans les emplacements et de continuer en remplaçant les paramètres manquants par la valeur zéro.

Routine RSX MAXICHR

; # RSX MAXICHR
RSXMAXICHR
; MAXICHR, [EFFET], Numero
; Affiche le MaxiCHR de 8x16 prealablement definit avec le RSX DEFMAXICHR
; Numero (0 a 22) designe le MaxiCHR a afficher
; EFFET de (1 a 4) 1=(Pas d'effet) 2=(Retournement horizontal) 3=(Retournement vertical) ; 4=(Pivotement de 90 degree)
       CP A, 3               ; Maximum 2 parametres
       RET NC
       OR A                  ; Minimum 1 parametre
       RET Z
       LD C, A               ; 'C' Contient le nombre de parametre (1 ou 2)
       LD HL, TableMaxiChr   ; 'HL' pointe sur le MaxiCHR numero 0
       LD A, E               ; EQUIVALENT LD A, (IX + 0) car 'D' contient le dernier parametre
       OR A
       JR Z, MAXICHR_NUMERO ; Si c'est le Numero zero, on reajuste pas la table
       CP A, 23              ; Sortir car le numero est trop fort (0 - 22)
       RET NC
       LD DE, 16             ; 'DE' contient le nombre d'octets qui forme un MaxiCHR

MAXICHR_TABLE ADD HL, DE     ; Additionner la table et le nombre de (MaxiCHR * ligne)
       DEC A                 ; Jusqu'a obtenir l'adresse du MaxiCHR
       JR NZ, MAXICHR_TABLE

MAXICHR_NUMERO
       LD A, C               ; Recuperer le Nombre de parametre
       LD DE, Caractere      ; 'DE' pointe vers la zone de travail
       CP A, 1               ; Pas d' OPTION, donc affichage rapide Effet=1
       JR Z, EFFET_01        ; SE DIRIGER Vers l'Option desiree
       LD A, (IX + 2)        ; Lire le numero de l'OPTION
       CP 1                  ; Si OPTION=1 alors affichage normal
       JR Z, EFFET_01
       CP 2                  ; Si OPTION=2 alors Rotation GD
       JR Z, EFFET_02
       CP 3                  ; Si OPTION=3 alors rotation HB
       JR Z, EFFET_03
       CP 4                  ; Si OPTION=4 alors rotation HB GD 
       JR Z, EFFET_04        ;
       JP EFFET_01           ; Sinon ERREUR donc affichage normal

EFFET_01                     ; EFFET NUMERO 1 AFFICHAGE SIMPLE
       LD BC, 16             ; Option 1, On va simplement transferer les 16 Octets du MaxiCHR
       LDIR                  ; vers la zone de travail... Voila c'est fait
       JP MAXICHR_OK         ; Reste plus qu'a afficher le MaxiCHR

EFFET_02                     ; ROTATION GAUCHE DROITE GD
       EX HL, DE             ; On echange les deux registres car 'HL' est plus adapte pour cette tache
       LD C, 16              ; 16 Lignes de hauteur = 16 Octets du MaxiCHR

EFFET_GD_LIGNES   
       LD B, 8               ; Il faut faire tourner les 8 Bits de chaques octets
       LD A, (DE)            ; 'A' contient l'octet a modifier

EFFET_GD_8BITS
       RRA                   ; 'A' est pousse vers la droite, et le BIT 0 va dans le 'CARRY'
       RL (HL)               ; '(HL)' est pousse vers la gauche et le BIT 0 recupere le 'CARRY'
       DJNZ, EFFET_GD_8BITS
       INC DE
       INC HL
       DEC C
       JR NZ, EFFET_GD_LIGNES
       JP MAXICHR_OK         ; Une fois les 16 octets operes, on peut afficher le MaxiCHR

EFFET_03                     ; ROTATION HAUT BAS HB
       LD DE, Caractere + 15 ; 'DE' pointe ver la zone de travail + 16 (0 a 15) situee en bas
       LD B, 16              ; 16 lignes a transferer

EFFET_HB_LIGNES
       LD A,(HL)             ; 'A' contient l'octet a transferer
       LD (DE), A            ; Qui est inscrit en partant du bas vers le haut
       INC HL
       DEC DE
       DJNZ, EFFET_HB_LIGNES
       JP MAXICHR_OK         ; Les 16 octets sont transferes, on affiche le MaxiCHR

EFFET_04                     ; ROTATION 90 DEGRES HBGD
       LD DE, Caractere + 15 ; On fait les deux options en meme temps
       EX HL, DE             ; 'HL' pointe sur la zone de travail + 16
       LD C, 16              ; 'DE' est la source. 16 octets a traiter

EFFET_HBGD_LIGNES
       LD A, (DE)            ; 'A' Contient l'octet source
       LD B, 8               ; Et on va tourner les 8 BITS pour les transferer dans 'HL'

EFFET_HBGD_8BITS
       RRA RL (HL)
       DJNZ, EFFET_HBGD_8BITS
       DEC HL
       INC DE
       DEC C
       JR NZ, EFFET_HBGD_LIGNES ; Les 16 octets sont tournes puis tranferer du bas vers le haut
       JP MAXICHR_OK MAXICHR_OK
       LD A, (Curseur)          ; Regarder si on peut l'afficher en bas de l'ecran
       CP A, 15
       RET C                    ; Si A < 15 alors on n'affiche pas et on sort
       LD A, 1
       LD (Zoom + 1), A         ; On inscrit la hauteur du ZOOM VERTICAL avant d'appeler la routine
       LD A, 16                 ; On inscrit aussi le nombre de ligne (8=caractere et 16=MaxiCHR)
       LD (CarCHR + 1), A
       LD A, 136
       LD (RotCou + 1), A       ; Rotation (10001000) A chaque 1, les couleurs changes
       CALL AfficheCarCHR       ; Et on affiche le caractere en appelant la sous routine
       RET
       NOP

La dernière routine avant la création des RSX.
Cette routine est un peu longue  mais au début on recherche l'adresse du MaxiCHR à afficher puis si il il y a une option qui doit être effectuée avant l'affichage et on oriente le programme en conséquence.
On va donc raccourcir directement sur les options possibles.
Option 1: 
La c'est très simple, on inscrit la matrice que l'on veut afficher dans la zone de travail et on dirige le programme vers l'affichage.
L' instruction 'LDIR' transfert le nombre d'octets voulu du registre 'HL' vers le registre 'DE'
Comme 'HL' pointe sur le MaxiCHR et 'DE' pointe sur la variable 'Caractere', on a plus qu'à inscrire dans 'BC' le nombre d'octets à transférer et à lancer l'instruction 'LDIR' puis de sauter vers la suite du programme.

Option 2: 
Cette fois on va faire subir une rotation horizontal au MaxiCHR.
Pour cette option, on a besoin de deux instructions pour faire tourner les BITs de chaque lignes du MaxiCHR
On place d'abord dans le registre 'A' la ligne à traiter, puis on lui fait faire une rotation vers la droite afin de récupérer dans le 'CARRY' la valeur du BIT 0 du registre 'A'
On injecte dans le contenu de 'HL' ce BIT en lui faisant subir une rotation vers la gauche. 
Les images parlent mieux:
rotation hl
L' instruction 'RRA' récupère le BIT 0 du registre 'A' dans le 'CARRY' et fait tourner son contenu vers la droite. Et l'instruction  'RL (HL)' fait tourner le contenu du registre 'HL' vers la gauche et injecte le 'CARRY' dans le BIT 0. En effectuant cette opération 8 fois de suite, on fait faire une rotation horizontale du contenu du registre 'HL'. Et on répète aussi l'opération pour parcourir les 16 lignes du MaxiCHR avant de sauter vers la suite du programme.

Option 3:
La rotation verticale est assez simple à faire. Au lieu de faire pointer la destination sur la variable 'Caractere' on va la faire pointer 15 octets plus loin. Et on inscrira la valeur de chaque ligne dans les sens inverse en décrémentant le registre 'DE' de sorte que la dernière ligne du MaxiCHR se retrouve sur la première ligne de la zone de travail. On sautera à la suite du programme pour finir.

Option 4:
Ici il s'agit de faire l'option 2 et l'option 3 en même temps. Ce qui ne devrait pplus être un problème maintenant.

Il y a plein de possibilités si vous voulez aller plus loin, comme par exemple afficher un MaxiCHR à l'orizontal, mais  ce n'est pas le but de mon programme que je voulais de moins de 1 Koctet une fois assemblé.

Le reste du programme parle de lui même. Une fois les variables définies, on affiche le MaxiCHR grâce à la sous routine 'AfficheCarCHR'.
Cette routine ne déplace pas le curseur, c'est voulu pour la rendre plus rapide et dans ma logique, ce n'est pas une routine pour afficher plusieurs MaxiCHR les uns derrières les autres.

Il nous reste à voir comment implanter les RSX.; ########## ########## ########## ##########
; MISE EN PLACE DES RSX : CALL &A22C
FIXERLESRSX
       LD A, #C9             ; Un Retour au basic sera effectue pour ne pas rappeler la routine 
       LD (FIXERLESRSX), A   ; des RSX en inscrivant 'C9' a l'adresse de l'appel
       LD BC, AdrNomsRSX     ; 'BC' pointe sur l'adresse des noms des RSX
       LD HL, BuffetRSX      ; 'HL' pointe sur un buffet de 4 octets reserve par la routine
       JP #BCD1              ; Fabuleuse routine qui fixe les RSX

AdrNomsRSX DW NOMSdesRSX     ; 2 octets qui stocks l'adresse des noms
       JP RSXENCRE           ; Suivis d'autant de sauts vers les routines
       JP RSXPAPIER          ; Qu'il y a de noms de RSX dans le meme ordre
       JP RSXCURSEUR
       JP RSXGCURSEUR
       JP RSXAFFICHE
       JP RSXDEFMAXICHR
       JP RSXMAXICHR

BuffetRSX DS 4               ; Zone de 4 Octets reserves par le vecteur RSX #BCD1

NOMSdesRSX                   ; Adresse des noms des RSX
       STR "ENCRE"           ; Chaque nom devraient finir par un + #80
       STR "PAPIER"          ; Mais WinApe le fait pour nous
       STR "CURSEUR"         ; Exemple pour la commande 'CURSEUR'
       STR "GCURSEUR"        ; DEFB "CURSEU", "R" + #80
       STR "AFFICHE"
       STR "DEFMAXICHR"
       STR "MAXICHR"
       NOP


Au lancement, on empêchera de rappeler la routine en plaçant l'instruction de 'RET' qui effectue un retour vers le basic. Je ne sais pas si c'est vraiment utile, mais dans le sens ou la construction des RSX sera effectuée, je ne vois pas trop l'utilité de rappeler cette routine. Peut être pour pouvoir changer le nom des RSX grâce à une routine prévue pour ça... Bref 

Le Vecteur &BCD1 a besoin de 2 variables (ou adresses) dans les registres pour pouvoir s'orienter vers les RSX à définir.
Le registre 'BC' doit pointer sur 2 octets qui doivent contenir l'adresse ou sont stockés les noms des RSX.
Le registre 'HL' doit pointer sur 4 octets de libres que le vecteur &BCD1 utilisera pour son fonctionnement.  
Ensuite on exécute le vecteur &BCD1 en faisant directement un saut.

En dessous, j'y ai mis les 2 octets qui contiennent l'adresse vers les noms des RSX suivis des sauts à effectuer pour chacun des RSX.
Entre les sauts et les noms, j'ai programmé le buffet de 4 octets utilisé par le vecteurs &BCD1. Puis viennent naturellement les noms de chaque RSX.

C'est terminé avec les Maxi Caractères et MaxiCHR. D'autres projet suivront !

✔ Vous avez la possibilité de redéfinir vos 23 MaxiCHR et d'en faire une sauvegarde avec les RSX. Pour ce fait, une fois redéfinis, tapez: SAVE"MAXICARA.BIN", b, &A000, &400. Elle n'est pas belle la vie 😁

Contenu du fichier 'MaxiCaracteres.ZIP'
MaxiCaracteres.ZIP (Contenu)
        Maxi caracteres - RSX.ASM    ; Fichier assembleur des RSX
Maxi caractere - matrice x 23.ASM    ; Fichier de définition des 23 MaxiCHR prêt à l'emploi
                     MaxiCara.DSK    ; Disquette AMSTRAD contenant les programmes
       ALIRE.BAS                     ; Fichier expliquant l'utilisation des RSX
        DEMO.BAS                     ; Petite démo rapide
    MAXICARA.BIN                     ; Fichier binaire des RSX
     IMPLANT.BAS                     ; Programme d'implantation du code binaire


Téléchargement: MaxiCaracteres.ZIP 

Publicité
Publicité
Commentaires
Favoris

 

ùcpm le fanzine
ùCPM le fanzine

 

 

cheshirecats logo

amsnews logo

amsnews logo

amstradeu logo

cpcpower logo

crazi

acme logo

amstradfunlogo

amstradfunlogo

amstradMuseum

amstrad

 

 

 

me contacter

votre banniere ici

 


Publicité
Rétro Poke
Derniers commentaires
Archives
Visiteurs
Depuis la création 16 242
Publicité