Gestion du clavier confortable avec RASM

Dans cet article, je vous présente ma façon d’utiliser les macros RASM pour intégrer de façon rapide et confortable la gestion des touches du clavier du CPC. En effet, lorsque l’on écrit du code, on aimerait pouvoir ajouter rapidement la possibilité d’utiliser une ou plusieurs touches de clavier pour contrôler un paramètre de notre code. Comme cela nécessite toujours un peu de travail, on y réfléchit a deux fois, la paresse prenant parfois le dessus.

Un Exemple concret

Imaginons que nous ayons une fonction qui nous permette de dessiner un sprite à un endroit (x,y) de l’écran. Une fois que l’on a cette routine, on veut ajouter rapidement la gestion des flèches pour déplacer le sprite. Ce que je propose ici c’est un ensemble de macros qui permette d’ajouter simplement ces quelques lignes pour gérer le changement de coordonnées:

ACTION_KEYLEFT:
    LD a,(POSITION_X)
    INC A
    LD (POSITION_X),A
    RET

Et c’est tout! Tout l’astuce va résider dans le fait que le symbole ‘ACTION_KEYLEFT‘ a été déclaré dans le code, et par l’utilisation de la directive ‘IFDEF’, la gestion de la touche se fera de façon transparente.

Etat du clavier

La première chose à faire est de récupérer l’état des touches du clavier. Je ne décrirai pas la méthode en détail, cela a déjà été fait de nombreuse fois ailleurs, par exemple ici. On pourra aussi consulter l’article écrit par roudoudou. Le code que l’on retrouve dans différentes productions est bien souvent similaire, probablement parce que c’est suffisamment pénible pour ne pas avoir à le réinventer. Celui que je donne ici ne fait pas exception:

MACRO GETKBSTATE0 DEST,FIRST,NUM
    ; Selection registre 14 du PSG
    LD  BC,PPI_PORTA |#0E 	
    OUT (C),C 
    LD  BC,PPI_PORTC |#C0  
    OUT (C),C 		
    XOR A 
    OUT (C),A 	
    LD  BC,PPI_CTRL |#92 
    LD 	HL,{DEST}+{FIRST}	
    OUT (C),C 		
    
    LD  C,#40+{FIRST}
    LD 	D,{NUM}
@KBGETLINE
    ; Selectionne ligne de clavier
    LD  B,hi(PPI_PORTC)
    OUT (C),C 
    LD  B,hi(PPI_PORTA)
    IN  A,(C) 
    LD 	(HL),A
    INC HL		
    INC C    ; Ligne suivante
    DEC D    
    JR 	NZ, @KBGETLINE	
    ;On desactive 
    LD  BC,PPI_CTRL |#82 
    OUT (C),C 
    LD  BC,PPI_PORTC
    OUT (C),C 		
MEND

La routine principale de gestion des touches

MACRO CHECK_ACTION action,actionkey
   IFDEF{action}
       BIT {actionkey},A
       JP Z,{action}
   ENDIF
 MEND

Ensuite, on va définir notre macro principale, qui sera à utiliser dans notre code. Cette macro va vérifier pour chaque touche du clavier si un symbole ACTION_ a été défini, et si oui, va générer le code nécessaire à tester l’état de la touche et appeler l’action associée:

MACRO CHECK_ACTION_KEYS kbstate
    ; Pour gérer le retour de l'action
    ld HL,@end_actions
    push hl

    ; Test Ligne 0			
    LD 	a,({kbstate}+0)
    CP #ff
    JR Z,@kbline1		
    CHECK_ACTION ACTION_KEYUP,KEY_UP_L0BIT
    CHECK_ACTION ACTION_KEYDOWN,KEY_DOWN_L0BIT
    CHECK_ACTION ACTION_KEYRIGHT,KEY_RIGHT_L0BIT
    CHECK_ACTION ACTION_KP3,KEY_KP3_L0BIT
    CHECK_ACTION ACTION_KP6,KEY_KP6_L0BIT
    CHECK_ACTION ACTION_KPRET,KEY_KPRET_L0BIT
    CHECK_ACTION ACTION_KP9,KEY_KP9_L0BIT

@kbline1:
    ; Ligne 1			
    LD 	a,({kbstate}+1)
    CP #ff
    JR Z,@nxtline2
		
    CHECK_ACTION ACTION_KEYLEFT,KEY_LEFT_L1BIT
    CHECK_ACTION ACTION_KP0,KEY_KP0_L1BIT
    CHECK_ACTION ACTION_KP1,KEY_KP1_L1BIT
    ; ... le code pour gerer les autres touches...
@end:
    POP HL
@end_actions:
MEND

Bien entendu, il faut définir quelques constantes, pour indiquer le bit associé à chaque touche de chaque ligne d’état du clavier, les ports du PPI, etc:

KEY_UP_L0BIT    EQU 0
KEY_DOWN_L0BIT  EQU 2
KEY_LEFT_L1BIT  EQU 0
KEY_RIGHT_L0BIT EQU 1
...
PPI_PortA 	EQU #F400 ; RW
PPI_PortB 	EQU #F500 ; Read Only
PPI_PortC 	EQU #F600 ; Write Only
PPI_CTRL 	EQU #F700 ; Registre de controle. Write Only

Utilisation

Il reste maintenant à utiliser ces macros dans un exemple concret. Si on reprend l’exemple du début, on aurait cela:

ACTION_KEYLEFT:
    LD a,(POSITION_X)
    INC A
    LD (POSITION_X),A
    RET

LOOP:
    GET_KB_STATE kbstate,0,10
    CHECK_ACTION_KEYS kbstate
    JP LOOP

kbstate: ds 10,#ff

Conclusion

Le code présenté est très pratique pour développer rapidement, sans surcoût inutile (si on ne souhaite pas gérer une touche, le code associé n’est pas généré). Il existe quelques restrictions, facilement contournables:

  • L’appui simultané de plusieurs touches n’est pas géré. Pour cela, il faut remplacer le JP {action} par un CALL et retirer les quelques lignes relative à ‘end_actions’.
  • Il faut définir les actions avant l’appel à la macro CHECK_ACTION_KEYS . En effet la directive IFDEF ne concerne que les symboles qui ont été définis avant. On peut contourner le problème avec la directive ORG si on veut que le code des actions soit placé après, dans la mémoire

Liens