Suivez-nous sur X

|
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
ALL
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
A propos d'Obligement
|
|
David Brunet
|
|
|
|
Programmation : Assembleur - Routine de lecture de la position des manettes
(Article écrit par Stéphane Schreiber et extrait d'Amiga News Tech - septembre 1991)
|
|
Quand on s'attaque à l'écriture d'un jeu, on a toujours besoin d'une routine de lecture de la
position des manettes, par exemple pour décider du mouvement à donner à un sprite et/ou à un
défilement. Bien que cela ne soit pas réellement compliqué sur l'Amiga, les routines habituelles
ne sont pas forcément les meilleures.
Bien que le matériel de l'Amiga l'autorise à utiliser des manettes analogiques - ou "proportionnels" -
comme l'IBM PC ou l'Apple II, le type le plus courant est celui mis au point par Atari à l'époque de
sa console de jeu, la VCS 2600, qui offre simplement quatre commutateurs, un par dilection possible
(haut, bas, gauche, droite) et qui a été repris par de multiples constructeurs, dont Commodore (on
parle d'ailleurs de "standard Atari" pour ce type de manettes, autant leur laisser au moins ça...).
Sur des systèmes plus anciens, comme le C64 ou l'Amstrad CPC, il suffisait de lire un octet en
mémoire ou sur un port pour savoir immédiatement dans quelle(s) direction(s) la manette
était poussée.
Sur l'Amiga, pour des raisons incompréhensibles, c'est un peu plus compliqué ; les bits "haut" et
"gauche" occupent deux bits d'un octet, et les "bas" et "droite", deux bits d'un autre octet d'état
du ou des boutons de feu étant quant à lui encore ailleurs. Bien qu'il soit toujours possible de
lire ces quatre bits avec une seule instruction (MOVEW), seul l'état des bits "gauche" et "droite"
est directement exploitable : il faudra d'abord masquer par un EOR l'état des bits "haut" et 'bas"
avant que ce pouvoir prendre leur valeur en compte.
De fait, lorsque que l'on commence à s'intéresser à la chose, la première routine que l'on était
ressemble souvent à celle publiée dans "La Bible De L'Amiga" :
Cette routine retourne deux valeurs, dans les registres D0 et D1, indiquant respectivement l'état
horizontal et l'état vertical de la manette : -1 signifie que la manette est à gauche (resp. en haut),
0 qu'il est au centre, et 1 qu'il est à droite (resp. en bas). A charge ensuite du programme
appelant, de faire ce qu'il convient de ces valeurs.
Ainsi écrite, cette routine fait ce qu'on attend d'elle, mais n'est pas réellement très efficace.
En tout cas, vue sa conception, elle serait beaucoup mieux si elle allait piocher
dans une table les valeurs correctes : on réduirait ainsi le nombre de tests
et de branchements, ce qui ferait gagner autant d'octets et de cycles. On diminurait également le
décalage à droite d'un bit et le masquage par EOR, devenus superflus.
Le problème maintenant est de savoir de quelle manière organiser nos quatre bits pour en faire
un index correct pour piocher dans la table ; pour cela, rappelons qu'une lecture de JOY0DAT
ou JOY1DAT donne les valeurs suivantes ('G' = gauche, 'H' = haut, 'D'= droite, 'B' = bas,
et 'x' indique un bit sans intérêt) :
Après plusieurs tests, il apparaît que la manière la plus probante est la suivante :
...qui s'obtient très facilement par une rotation de deux bits vers la droite de l'octet inférieur.
Il ne reste plus qu'à décaler le mot entier de six bits vers la droite, pour obtenir un index
valable, de la forme :
En étudiant tous les cas avec un débogueur, on trouve les valeurs possibles suivantes :
Notre table aura donc l'allure que voici (un 'X' indique une valeur non utilisée, par exemple
l'impossible combinaison HBG) :
Cette table peut être un rien optimisée, par exemple en omettant les deux 'X' finaux de change
ligne (8 octets de gagnés) et en utilisant des valeurs sur des octets plutôt que des mots
(50% de gagné !). Toutefois, je préfère utiliser des mots, étant donné que dans un jeu,
les coordonnées des sprites sont souvent codées sur des mots. Ainsi, dans le programme principal,
je n'ai plus qu'à écrire :
...pour que les coordonnées de mon sprite soient actualisées en fonction de la position de
la manette.
Il ne nous reste donc maintenant plus qu'à écrire la version finale de notre routine de
lecture des manettes. Vous remarquerez que je décale le quartet GHDB de cinq bits et non
de six, comme annoncé plus haut ; c'est simplement parce que j'accède à une table de mots.
Les deux instructions :
...équivalent en fait aux trois suivantes :
...mais sont bien entendues plus courtes et plus rapides. Dans le même ordre d'idées, je lis d'abord
la valeur de d1, afin de ne pas modifier d0, qui sert toujours d'index dans l'accès à la table
des valeurs en X.
Notez enfin que cette routine n'est prévue que pour le port manette 1 de l'Amiga (la configuration
standard de l'utilisateur voulant que la souris soit branchée dans le port 0 et la manette
dans le port 1). Pour gérer une manette branchée sur le port 0 (par exemple, dans un jeu à deux
joueurs), il suffit de remplacer $dff00c (JOY1DAT) par $dff00a (JOY0DAT) le reste est strictement
identique.
Conclusion
Mine de rien, on gagne beaucoup à essayer d'améliorer des routines à priori secondaires.
Dans ce cas précis, on est passé d'une routine de près de 30 lignes, à une d'à peine 10.
La vitesse d'exécution s'en est trouvée considérable multipliée et on s'en sort avec la
satisfaction personnelle d'avoir quasiment atteint la perfection. Ce qui est loin d'être
désagréable, au niveau de l'ego.
; ************************************
; * Illustration de la routine de *
; * lecture des manettes. Lit la *
; * manette connectée au GAMEPORT 1 *
; * et affiche son état. *
; ************************************
; * © S. Schreiber pour ANT - 1991 *
; ************************************
incdir "include:"
include "libraries/dos.i"
include "hardware/custom.i"
include "exec/exec_lib.i"
include "libraries/dos_lib.i"
include "misc/easystart.i" ; pour le Workbench startup
; ************************************
DEFSTR MACRO
dc.b .2-.1 ; longueur de la chaine
.1 dc.b \1 ; la chaine elle-même
IFNC '\2','' ; si \2 n'est pas vide...
dc.b \2 ; alors mettre \2 aussi
ENDC
.2 even ; alignement
ENDM
custom EQU $dff000
; ************************************
Start lea dosname(pc),a1
moveq #33,d0
CALLEXEC OpenLibrary
move.l d0,_DOSBase
beq NoDos
move.l #conname,d1
move.l #MODE_OLDFILE,d2
CALLDOS Open
move.l d0,_stdout
beq NoCon
; ************************************
TheLoop moveq #joy1dat,d0 ; test de la manette 1
bsr Joy
move.w d0,d4 ; sauve d0
moveq #0,d7 ; flag : imprimer LF si <> 0
; ***** Affiche l'état de l'axe vertical
addq.w #1,d1 ; 0 = haut, 1 = milieu, 2 = bas
lsl.w #2,d1
lea tabmsg1(pc),a0
move.l (a0,d1.w),d0
beq.s .1
movea.l d0,a0
bsr Print
addq.w #1,d7
; ***** Affiche l'état de l'axe horizontal
.1 addq.w #1,d4 ; 0 = gauche, 1 = milieu, 2 = droite
lsl.w #2,d4
lea tabmsg2(pc),a0
move.l (a0,d4.w),d0
beq.s .2
movea.l d0,a0
bsr Print
addq.w #1,d7
; ***** Au besoin, passe à la ligne
.2 tst.w d7
beq.s .3
lea lfmsg(pc),a0
bsr Print
; ***** Petit delai pour être 'amiga friendly'
.3 moveq #5,d1
CALLDOS Delay
; ***** Fin avec le bouton de feu de la manette
btst #7,$bfe001
bne.s TheLoop
; ************************************
Ciao move.l _stdout(pc),d1
CALLDOS Close
NoCon movea.l _DOSBase(pc),a1
CALLEXEC CloseLibrary
NoDos moveq #0,d0
rts
; ************************************
Joy lea custom,a0
move.w (a0,d0.w),d0
ror.b #2,d0
lsr.w #5,d0
andi.w #%11110,d0
move.w JoyTabY(pc,d0.w),d1
move.w JoyTabX(pc,d0.w),d0
rts
JoyTabY dc.w 0,1,1,0,-1,0,0,-1,-1,0,0,0,0,1,0,0
JoyTabX dc.w 0,0,1,1,0,0,0,1,-1,0,0,0,-1,-1,0,0
; ************************************
Print moveq #0,d3
move.b (a0)+,d3
move.l a0,d2
move.l _stdout(pc),d1
CALLDOS Write
rts
; ************************************
_DOSBase dc.l 0
_stdout dc.l 0
dosname dc.b "dos.library",0
even
conname dc.b "CON:0/11/640/200/JoyTest - par S. Schreiber",0
even
tabmsg1 dc.l hmsg,0,bmsg
tabmsg2 dc.l gmsg,0,dmsg
; ************************************
gmsg DEFSTR <"gauche ">
dmsg DEFSTR <"droite ">
hmsg DEFSTR <"haut ">
bmsg DEFSTR <"bas ">
lfmsg DEFSTR 1,$0a ; Line Feed
; ************************************
END
|
|