Revenir au plan du site

Utilisation des structures pour définir un menu

Avant d'attaquer la lecture de cet article, assurez-vous d'avoir téléchargé la dernière version de [ RASM ], à minima une 2.3.1 !

Les structures, c'est pratique! Je pensais vous faire un petit exemple pour vous montrer comment réaliser un menu flexible (au hasard pour un de vos futurs programmes!).

Pour notre menu, nous aurons des libellés à placer à l'écran, un texte à afficher et on va relier notre entrées du menu avec des champs précédent/suivants faisant le lien entre les structures, ainsi, nul besoin de savoir combien de structures nous avons à gérer, on saute d'une à l'autre ce qui va nous aider aussi pour la gestion du curseur.
Enfin, on associera une action (l'adresse d'une routine) à notre entrée de menu, si l'utilisateur valide l'entrée, alors on saute à la routine "action".
struct entite_menu
  adresse_ecran defw
  libelle defs 20
  suivant defw
  precedent defw
  action defw
  actif defb
endstruct

On va s'installer 3 entrées de menu qui vont appeler des routines qui changent les couleurs de l'écran. On rempli nos structures lors de la déclaration :
- premier paramètre, le type de structure (entite_menu)
- deuxième paramètre, le nom de l'instance de la-dite structure (entree1, entree2, ...)
- troisième paramètre, le nombre de structures, nous on va les déclarer une par une mais on peut faire des tableaux
- les autres paramètres correspondent chacun aux champs de la structure, on peut s'arrêter avant le dernier, ça complète alors par défaut
struct entite_menu entree1,1,#C000+80*0,'Jaune sur Bleu\xFF',entree2,entree3,JauneBleu,33 ; activer la première
struct entite_menu entree2,1,#C000+80*2,'Blanc sur Rouge\xFF',entree3,entree1,BlancRouge
struct entite_menu entree3,1,#C000+80*4,'Noir sur Gris\xFF',entree1,entree2,NoirGris

Il va nous falloir une routine qui initialise toutes les entrées. L'indexation des champs va se faire facilement avec l'usage des registres d'index IX ou IY.
AfficheEntrees
ld iy,entree1
.boucle
ld hl,(iy+entite_menu.adresse_ecran) : ld (position_char),hl
ld a,(iy+entite_menu.actif) : call affiche_caractere
ld de,iy : ld hl,entite_menu.libelle : add hl,de : call affiche_chaine
ld a,(iy+entite_menu.actif) : call affiche_caractere
ld de,(iy+entite_menu.suivant) : ld iy,de
ld hl,entree1 : or a : sbc hl,de ; test rebouclage
jr nz,.boucle
ret

J'ai bouclé jusqu'à revenir à la première entrée, ça permet de modifier la liste et ne pas se soucier du nombre d'entrées. On pouvait aussi parcourir linéairement notre tableau et sauter de structure en structure.

Il va nous falloir aussi 3 routines pour changer les couleurs (voir 4 pour factoriser un peu le code)
JauneBleu ld hl,#444A : jr setColor
BlancRouge ld hl,#4B4C : jr setColor
NoirGris ld hl,#5440
setColor ld bc,#7F10 : out (c),c : out (c),h : out (c),0 : out (c),h : ld c,1 : out (c),c : out (c),l : ret

Pour la gestion, 3 touches, haut/bas pour naviguer, espace pour valider. L'entrée active devra être d'une autre couleur.

gestionMenu
call JauneBleu ; couleurs par défaut
call AfficheEntrees ; init de l'affichage
.laBoucle
call lectureMatriceClavier
ld a,(OCTET_CURSEUR_HAUT) : and BIT_CURSEUR_HAUT : jr z,.versLeHaut
ld a,(OCTET_CURSEUR_BAS)  : and BIT_CURSEUR_BAS  : jr z,.versLeBas
ld a,(OCTET_ESPACE) : and BIT_ESPACE : jr z,.action
jr .laBoucle

.action
ld hl,laBoucle : push hl ; l'action reviendra ici
ld hl,(iy+entite_menu.action) : jp (hl) ; exécuter l'action

.versLeHaut
ld de,(iy+entite_menu.precedent) : ld iy,de : jr .laBoucle

.versLeBas
ld de,(iy+entite_menu.suivant) : ld iy,de : jr .laBoucle

J'ai gardé le source de l'article précédent, on laisse tout en ROM, le menu, la fonte, les routines! On ajoute le tableau des touches en mémoire vive, il faut bien pouvoir écrire dedans :)

Vous aurez besoin du [ binaire de la fonte ] pour assembler ce source
struct entite_menu
  adresse_ecran defw
  libelle defs 20
  suivant defw
  precedent defw
  action defw
  actif defb
endstruct
OCTET_CURSEUR_HAUT   equ matriceClavier+0 : BIT_CURSEUR_HAUT   equ 1
OCTET_CURSEUR_DROITE equ matriceClavier+0 : BIT_CURSEUR_DROITE equ 2
OCTET_CURSEUR_BAS    equ matriceClavier+0 : BIT_CURSEUR_BAS    equ 4
OCTET_CURSEUR_GAUCHE equ matriceClavier+1 : BIT_CURSEUR_GAUCHE equ 1
OCTET_ESPACE equ matriceClavier+5 : BIT_ESPACE equ 128
MODE_0 equ 0 : MODE_1 equ 1 : MODE_2 equ 2 : MODE_3 equ 3 : CLEAR_INT equ %10000
ROM_OFF equ %1100 : ROM_BOTH equ 0 : ROM_UP equ %100 : ROM_LOW equ %1000 : INTRESET equ %10000
macro RMR tags : ld a,{tags}+%10000000 : ld b,#7F : out (c),a : mend

BUILDSNA : BANKSET 0
ORG #100 : RUN #100 : LD SP,#100
RMR ROM_UP | MODE_1 ; activer la ROM haute + Mode 1
ld bc,#DF00+{bank}affiche_caractere : out (c),c ; sélectionner la ROM qui contient la routine, la fonte, le message
jp gestionMenu

position_char defw #C000 ; besoin d'avoir cette VARIABLE en mémoire vive pour la modifier
entree_active defw entree1 ; entree par défaut
matriceClavier defs 10,#FF
charset ' ABCDEFGHIJKLMNOPQRSTUVWXYZ!()*+,-./0123456789:;<=>?@_abcdefghijklmnopqrstuvwxyz#',0
struct entite_menu entree1,1,#C000+80*0,'Jaune sur Bleu\xFF',entree2,entree3,JauneBleu,33
struct entite_menu entree2,1,#C000+80*2,'Blanc sur Rouge\xFF',entree3,entree1,BlancRouge
struct entite_menu entree3,1,#C000+80*4,'Noir sur Gris\xFF',entree1,entree2,NoirGris
charset

ROMBANK 15 ; peu importe le numéro!
org #C000 ; par contre une ROM haute commence TOUJOURS en #C000!
affiche_chaine
  ld a,(hl) : inc hl ; récupérer la valeur dans le tableau et incrémenter le pointeur
  cp #FF : ret z ; terminateur?
  push hl : call affiche_caractere : pop hl ; on a besoin de conserver notre pointeur HL
  jr affiche_chaine

affiche_caractere
; nos caractères font 48 octets
add a : ld h,0 : ld l,a : add hl,hl : add hl,hl : add hl,hl ; x 16
ld bc,hl : add hl,hl : add hl,bc ; x 48
ld bc,fonte : add hl,bc ; HL=adresse des données du caractère dans le tableau fonte
ld de,(position_char) ; on récupère la position du curseur
ld xl,16 ; nombre de lignes
ld bc,#10FF ; le LDI décrémente BC donc on charge C au maximum pour que B puisse servir de compteur
affiche_ligne_caractere
push de : ldi 3 : pop de
ld a,d : add 8 : ld d,a : and #38 : jr nz,.noOVF
ld a,80 : add e : ld e,a : ld a,#C0 : adc d : ld d,a : res 3,d
.noOVF
djnz affiche_ligne_caractere
; avancer le curseur
ld hl,(position_char) : inc hl : inc hl : inc hl : ld (position_char),hl
ret
lectureMatriceClavier
ld hl,matriceClavier
ld bc,#f782
out (c),c
ld bc,#f40e
ld e,b
out (c),c
ld bc,#f6c0
ld d,b
out (c),c
out (c),0
ld bc,#f792
out (c),c
ld a,#40
ld c,d
.loop ld b,d
out (c),a ; sélectionner la ligne
ld b,e
ini ; lire et stocker dans notre tableau
inc a
inc c
jr nz,.loop
ld bc,#f782
out (c),c
ret

afficheEntrees
push iy : ld iy,entree1
.boucle
ld hl,(iy+entite_menu.adresse_ecran) : ld (position_char),hl
ld a,(iy+entite_menu.actif) : call affiche_caractere
ld de,iy : ld hl,entite_menu.libelle : add hl,de : call affiche_chaine
ld a,(iy+entite_menu.actif) : call affiche_caractere
ld de,(iy+entite_menu.suivant) : ld iy,de
ld hl,entree1 : or a : sbc hl,de ; test rebouclage
jr nz,.boucle
pop iy : ret

gestionMenu
call JauneBleu ; couleurs par défaut
ld iy,entree1
.laBoucle
call afficheEntrees ; init de l'affichage
call lectureMatriceClavier
ld a,(OCTET_CURSEUR_HAUT) : and BIT_CURSEUR_HAUT : jr z,.versLeHaut
ld a,(OCTET_CURSEUR_BAS)  : and BIT_CURSEUR_BAS  : jr z,.versLeBas
ld a,(OCTET_ESPACE) : and BIT_ESPACE : jr z,.action
jr .laBoucle

.action
ld hl,.laBoucle : push hl ; l'action reviendra ici
ld hl,(iy+entite_menu.action) : jp (hl) ; exécuter l'action

.versLeHaut
ld (iy+entite_menu.actif),0
ld de,(iy+entite_menu.precedent) : ld iy,de : ld (iy+entite_menu.actif),33 : jr .laBoucle

.versLeBas
ld (iy+entite_menu.actif),0
ld de,(iy+entite_menu.suivant) : ld iy,de : ld (iy+entite_menu.actif),33 : jr .laBoucle

JauneBleu ld hl,#444A : jr setColor
BlancRouge ld hl,#4B4C : jr setColor
NoirGris ld hl,#5440
setColor ld bc,#7F10 : out (c),c : out (c),h : out (c),0 : out (c),h : ld c,1 : out (c),c : out (c),l : ret

fonte incbin 'simpleFonte.bin'
; définir le charset pour les messages



Bon, c'est minimaliste. Déjà, je m'aperçois que la fonte est bien décalée vers le haut pour les minuscules et puis surtout le curseur va beaucoup trop vite.

Pour le curseur, on peut se faire une fonction qui attend qu'on appuie sur aucune touche avant de continuer
ClavierDisponible
call lectureMatriceClavier
ld hl,matriceClavier : ld b,10
.boucle ld a,(hl) : cpl : or a : jr nz,ClavierDisponible : djnz .boucle
ret

Et on insère l'appel dans la gestion haut/bas
ld de,(iy+entite_menu.precedent) : ld iy,de : ld (iy+entite_menu.actif),33 : call ClavierDisponible : jr .laBoucle
[ ... ]
ld de,(iy+entite_menu.suivant) : ld iy,de : ld (iy+entite_menu.actif),33 : call ClavierDisponible : jr .laBoucle

Pour nos glitchs de fonte, il faudrait reprendre le fichier PNG que j'ai un peu trop fait à l'arrache (vous savez faire?)