Sur ST le clavier (mais aussi les joystick/souris) possèdent leur propre CPU, un 6301 exactement, ce dernier dispose d'une ROM, d'une RAM et d'une interface série lui permettant de communiquer avec les ACIA du ST. Pour la petite anecdote, certain demomakers se sont amusés à reprogrammer ce 6301 pour lui faire exécuter des tâches qui n'avaient rien à voir avec la gestion des inputs. Nous n'iront pas jusque là je vous rassure, du coup nous nous contenterons d'utiliser les registres des ACIA pour communiquer avec le clavier.
Après ce bref rappel nous pouvons commencer, ouvrez le fichier "SOTB_03.S" et commencez à le parcourir, vous aller voir que nous y avons ajouté que peu de codes.
La première chose à savoir c'est que dès qu'un évènement clavier/joystick est détecté par le 6301 une interruption est déclenchée sur le 68000,
un peu à la manière de l'interruption VBL si vous voulez, son vecteur est à l'adresse $118
.
Nous allons y placer notre routine de gestion des évènements clavier puis nous autorisons les interruptions de ce niveau (ligne 221).
.SetInterruption:
move.l #HBL,VEC_HBL ; Nouvelle HBL
move.l #VBL,VEC_VBL ; Nouvelle VBL
move.l #KBD,VEC_KBD ; Nouveau clavier
or.b #$40,MFP_BASE+MFP_IERB ; Active IT clavier
A partir de là tout évènement lié au clavier sera traité par notre routine qui ira lire les données envoyées par le 6301 et réagira en conséquence. Le fonctionnement est le suivant, on commence par lire le statut de l'ACIA pour savoir si c'est bien une IT clavier qui a été levée, on contrôle ensuite que le buffer clavier n'est pas plein, si c'est le cas il faut traiter et vider le buffer, sinon on récupère la donnée émise. A ce niveau là il faut savoir une chose, la donnée récupérée correspond au code clavier de la touche, si le bit 7 est à 0 alors on vient d'enfoncer la touche, si il est à 7 alors on vient de la relacher. Pour les évènements souris ou joystick c'est plus compliqué. Le 6301 commence par envoyer un octet de code puis les octets suivants correspondent aux données en question, pour les joystick les codes de contrôle sont $FE (joystick 0) ou $FF (joystick 1) et de suite après on récupère l'octet de données qui contient la position du joystick dans les 4 premiers bits et l'état du bouton feu dans le bit 7. Il ne reste plus qu'à stocker cette info pour un usage ultérieur.
KBD:
movem.l d0-d1/a0,-(sp)
.CheckKbdIt:
lea ACIA_BASE,a0 ; ACIA registre de base
move.b ACIA_SR(a0),d1 ; Lit l'octet de statut
btst #7,d1 ; Interruption demandé
beq.s .NoKbdIt ; Non
btst #0,d1 ; Buffer de réception plein
beq.s .OverRun ; Oui
move.b ACIA_RDR(a0),d0 ; Récupère l'octet
tst.b FlagJoystick ; Etait-ce le joystick
bne.s .GetStatus ; Oui
cmpi.b #$ff,d0 ; Est-ce le joystick
bne.s .NotJoystick ; Non
st FlagJoystick ; Indique si c'etait le cas
.NotJoystick:
andi.b #$7f,d0 ; Clear le bit 7
move.b d0,KeyboardState ; Sauve la touche clavier
bra.s .OverRun
.GetStatus:
sf FlagJoystick ; Reset le flag
move.b d0,JoystickState ; Sauve le statut du joystick
.OverRun:
andi.b #$20,d1 ; Un overrun est arrivé
beq.s .NoKbdIt ; Non
move.b ACIA_RDR(a0),d0 ; Lit l'octet
.NoKbdIt:
btst #4,MFP_BASE+MFP_GPIP ; On a encore une interruption
beq.s .CheckKbdIt ; Oui
bclr #6,MFP_BASE+MFP_ISRB ; Clear le bit de l'IT
movem.l (sp)+,d0-d1/a0
rte
Vous devez aussi savoir qu'il est possible d'envoyer des commandes au 6301 qui changeront sa façon de gérer les évènements, vous pouvez ainsi stopper la détection continue des joystick et ne récupérer leur état qu'à la demande.
La question du clavier/joystick étant traité nous pouvons passer à l'animation de notre sprite, nous avons ajouté une table d'animation pour le déplacement vers la gauche "BeastLeftAnimTable". La première chose que nous faisons au début de la VBL c'est de vérifier l'état du joystick et de mettre à jour quelques variables en conséquence, c'est le rôle de la fonction "CheckJoystick".
CheckJoystick:
move.w BeastMovement,BeastMovement+2 ; Sauve l'ancienne direction
move.b JoystickState,d0
btst #JOY_BFIRE,d0 ; On a appuyé sur feu
beq.s .NotFire ; Non
move.w #-1,BeastFire ; Oui, on l'indique
.NotFire:
btst #JOY_BLEFT,d0 ; On va à gauche
beq.s .NotLeft ; Non
move.w #-1,BeastMovement ; Oui, on l'indique
rts
.NotLeft:
btst #JOY_BRIGHT,d0 ; On va à droite
beq.s .NotRight ; Non
move.w #1,BeastMovement ; On l'indique
rts
.NotRight:
move.w #0,BeastMovement ; On ne bouge pas
rts
L'étape suivante consiste à gérer le sens de défilement du décor en fonction de la position du joystick, c'est ce que fait notre fonction "PrepareBackgroundScrolling". Nous avons également légèrement modifié la fonction "PrepareBeastAnim" pour prendre en compte les actions du joystick.
PrepareBeastAnim:
move.w BeastMovement,d0 ; Sens du mouvement du sprite
cmp.w BeastMovement+2,d0 ; Vérifie si on a changé de sens
beq.s .PrepareSprite ; Non, on peut continuer l'animation
cmpi.w #1,d0
bne.s .ToTheLeft ; On va à gauche
move.l #BeastRightAnimTable,BeastCurrentAnim ; Animation vers la droite
bra.s .NewAnimation
.ToTheLeft:
cmpi.w #-1,d0
bne.s .Fixed ; On bouge pas
move.l #BeastLeftAnimTable,BeastCurrentAnim ; Animation vers la gauche
bra.s .NewAnimation
.Fixed:
cmpi.w #1,BeastMovement+2
beq.s .ToTheRight
move.l #BeastFixedLeftAnimTable,BeastCurrentAnim ; Animation fixe vers la gauche
bra.s .NewAnimation
.ToTheRight:
move.l #BeastFixedRightAnimTable,BeastCurrentAnim ; Animation fixe vers la droite
.NewAnimation:
move.w #-1,BeastNewAnim ; Indique qu'il faut jouer une nouvelle animation
Enfin il faut légèrement modifier la fonction "AnimateBeast" pour lui permettre de démarrer une nouvelle animation en cas de changement de sens du défilement. La partie affichage pure ne change pas, on a toujours un découpage en trois partie du sprite pour s'adapter au décalage du décor.
A gauche
A droite
Encore une bonne chose de faite, pour le prochain épisode nous verrons comment afficher un nouvel élément de décor, en l'occurence une barrière.