A bâton rompu

Interlude technique

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.