``Toto, le menu''

Il m'est plutôt difficile de vous annoncer que vous allez devoir, pour commencer, taper un listing aussi long que la barbe de Mahomet, ceci afin que, sous prétexte de pondre un programme affichant un menu Wimp, nous progressions ensemble de concert dans notre apprentissage de l'assembleur ARM...

Mais voilà qui est fait. Je vous l'ai annoncé, et vous allez le taper. Que les paresseux se rassurent: une copie de cette toute petite application pédagogique se trouve sur l'ARMadisque.


Prolégomènes

Mais avant de vous lancer dans ce travail, qui vous prendra moins d'une nuit (moins d'une nuit: voilà de l'approximation, n'est-ce pas?), vous devez créer un nouveau dossier sur votre disque!

Car en effet nous allons, pour la première fois, en profiter pour nous taper le squelette de base d'une application Wimp!

Créez donc un nouveau dossier, que vous appelerez: !Toto1 . Ensuite, ouvrez-le (pour cela, vous appuyez sur la touche <shift> en même temps que vous double-cliquez sur l'icône de ce nouveau dossier).

A l'intérieur, vous allez créer pour l'instant trois fichiers: deux fichiers de type "Obey" que vous nommerez respectivement !Boot et !Run , et un fichier de type "Sprite" que vous appelerez !Sprites .

Dans le fichier !Boot , vous écrirez simplement la ligne suivante:

IconSprites <Obey$Dir>.!Sprites
Dans le fichier !Run , vous taperez ces trois lignes:
WimpSlot -min 8k -max 8k
Run <Obey$Dir>.!Boot
Run <Obey$Dir>.Object
Dans le fichier !Sprites , vous mettrez (avec l'application !Paint) un seul icone, n'importe lequel (celui qui se trouve dans le !Toto1 de l'ARMadisque n'est qu'une suggestion), mais vous devrez IMPERATIVEMENT le nommer !Toto1 , sans quoi vous ne pourrez voir que la triste figure par défaut des Apps dans la fenêtre de votre disque dur. Et puis, comme nous réutilisons cet icone dans le programme, il vaut mieux que le Wimp puisse le localiser, sans quoi rien n'apparaîtra sur la barre de menu.

Si vous n'avez pas envie de créer tous ces fichiers, vous pouvez toujours les récupérer à partir de l'ARMadisque.


Je tape le listing principal...

A présent, puisque vous ètes très courageux aujourd'hui, vous allez me taper ça pendant que je vous regarde faire (moi je ne le tape pas: c'est déjà fait!) dans un fichier de type "Text" que vous appelerez, mettons Toto1 pour prendre un nom au hasard...


;"Mon Adorable Premier Petit Menu Wimp en Assembleur"
;(C)1996 A. VIDOVIC pour l'ARMada

#smile
#set lenpile=1024		;ceci devrait suffire pour une pile (en mots)
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#entry
	ADR R0,pile
	STR R13,[R0]
	MOV R13,R0		;on crée une pile DB (ED)
	
;-------Initialisation du Wimp
	MOV R0,#200		;dernière version connue de Wimp*100
	MOV R1,#&4B534154	;"TASK"
	ADR R2,nom_prog		;short description for Task Mgr
	SWI Wimp_Initialise
	ADR R0,task_handle
	STR R1,[R0]		;sauvegarder le task_handle	

;-------Création de l'icone de la barre des icones
	ADR R1,menuicon
	SWI Wimp_CreateIcon	;créer l'icone de barre des icones

;-------Polling loop
.je_polle
	MOV R0,#%0110111111	;masque
	ADR R1,user_buf
	SWI Wimp_Poll		;=>R0: reason code...

	CMP R0,#6	
	BEQ clickation		;6:  mouse click

	CMP R0,#9
	BEQ choix		;9:  menu selection
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
.clickation			;Traitement du click souris
	ADR R10,user_buf
	LDR R0,[R10,#12]	;Window handle (ou -2 si iconbar)
	CMP R0,#-2		;iconbar?
	BNE je_polle		;non: rien pour cette fois
	
	LDR R0,[R10,#8]		;mouse buttons
	CMP R0,#2		;menu button?
	BLEQ create_menu	;oui: demander au wimp de créer le menu
	
	B je_polle
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
.create_menu	;Sous-routine de création par le Wimp du menu principal

	ADR R1,menu_block
	LDR R2,[R1,#16]		;largeur des items
	LDR R0,[R10]		;mouse_x
	SUB R2,R0,R2,ASR #1
	SUB R2,R2,#24		;x du menu (cf style guide du PRM)
	MOV R3,#96+(1*44)+(0*24)	;y du menu (cf idem)
	SWI Wimp_CreateMenu

	MOV R15,R14		;retour à l'appelant
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
.choix				;Traitement du menu
	ADR R10,user_buf
	LDR R0,[R10]		;numéro de l'item sélectionné dans le main

	CMP R0,#0		;si 0=quit: suicide du prog
	BNE je_polle		;sinon (pas d'autre option): retour au poll

;-------Fermeture su slot Wimp et sortie du programme
	ADR R0,task_handle	;récupérer le task handle
	LDR R0,[R0]
	MOV R1,#&4B534154	;"TASK"
	SWI Wimp_CloseDown	

	ADR R0,pile
	LDR R13,[R0]		;on restaure l'ancienne pile

	MOV R0,#0
	SWI OS_Exit
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	DBD lenpile,0		;espace pour la pile programme
.pile	DBD 1,0			;sauvegarde ancien pointeur de pile

.task_handle	DBD 1,0		;espace pour contenir le handle
.user_buf	DBB 256,0	;un bloc de 256 octets (nuls) de long
.nom_prog	DCB "Toto The Menu",0	;nom à donner au Wimp, au début
	ALIGN 4
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
.menuicon			;lors de la creation de l'icone
	DCD -1			;win handle (-1=iconbar right)
	DCD 0			;xmin de l'icon bouding box
	DCD 0			;ymin
	DCD 90			;xmax
	DCD 90			;ymax
	DCD %11000100000010	;icon flags
	DCD spr_name		;ptr sur nom du sprite
	DCD 1			;ptr to sprite ctrl block (1=Wimp)
	DCD 10			;longueur du nom du sprite pointé

spr_name		;sprite à utiliser pour la barre d'icone
	DCB "!toto1",0	;en fait, le même que pour le prog (cf <!sprites>)
	ALIGN 4
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
.menu_block		;le menu de ce programme
	DCB "Toto"	;12 caractères de titre de menu
	DBD 2,0
	DCB 7		;title foreground and frame colour
	DCB 2		;title background colour
	DCB 7		;work-area foreground colour
	DCB 0		;work-area background colour
	DCD 6*16	;width of menu items (ici 4 car. max + 2 espaces=6)
	DCD 44		;height of menu items
	DCD 0		;vertical gap between menu items
;-------quit
	DCD %10000000	;item flags
	DCD -1
	DCD &07000021	;icon flags
	DCB "Quit"	;12 caractères de long
	DBD 2,0
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -	
#end

Une fois ce source tapé, sauvé, compilez-le avec !ExtASM: vous devez obtenir, dans le dossier que nous avons créé en début de leçon et à côté des autres fichiers déjà créés, un fichier appelé Object de type "Absolute". Si jamais le nom est différent, changez-le et modifiez l'option de !ExtASM qui spécifie le nom du fichier exécutable créé après une compilation: en effet le fichier !Run de l'application cherche à déclencher un exécutable de ce nom et pas un autre...

Au fait: n'oubliez pas de jouer avec votre application ainsi créée, en guise de récréation. Faites durer cette récréation aussi longtemps que vous prendra la flemme de passer à la suite de l'article.


J'ai tapé tout ça, moi?

Eh oui! Vous avez tapé tout ça, avec vos mains en plus! Mais arrive le moment où tout être doué de raison se pose des questions: il faut absolument comprendre ce que vous avez tapé! Qui sait... Au cas où toutes ces instructions, une fois compilées, vous aient inoculé le virus du siècle (tiens, ce serait une bonne blague!)...

Vous l'avez noté: vous reconnaissez des instructions, voire des pans entiers de code, que nous avons déjà abordés dans l'acte précédent. Nous n'y reviendrons pas, ce qui implique qu'en cas de mauvaise compréhension de ces parties du listing, vous devez en premier lieu vous reporter au numéro précédent de ARMada news, et ensuite seulement, si vous ne comprenez toujours pas, vous pouvez joindre le bureau de l'ARMada, qui me transmettra vos doléances.

L'icone de la barre de menu

Rien de très nouveau pour nos lecteurs qui programment déjà le Wimp en d'autres langages: vers le début du programme, pendant la phase d'initialisation, vous tombez sur:
	ADR R1,menuicon
	SWI Wimp_CreateIcon	;créer l'icone de barre des icones
C'est un appel à la fonction du Wimp "Wimp_CreateIcon" avec comme paramètre une adresse, passée dans R1, celle du bloc de données ".menuicon", défini plus bas dans le listing. Pour les détails sur la structure de cet "icon-bloc", je vous renvoie au PRM ou à StrongHelp... Quoi de particulier ici? Simplement la ligne du bloc suivante:
	DCD spr_name		;ptr sur nom du sprite
Ceci, vous l'avez probablement intuité, stocke à cet endroit un mot, qui est une adresse, celle définie par le label ".spr_name", défini lui juste après le bloc-icone, et qui est le début d'une chaîne de caractères:
	DCB "!toto1",0
Et c'est le nom de l'icone contenu dans le fichier !Sprites .

La polling loop - encore!

Oui, encore, mais pas pour longtemps: ici vous avez remarqué:
	MOV R0,#%11111111110110111111	;masque
Contrairement à la dernière fois, nous ne mettons pas zéro comme masque, mais un nombre en binaire (préfixe %) avec pour seuls bits à zéro ceux dont le numéro correspond aux seuls évênements que nous avons l'intention de traiter dans ce programme: le Wimp ne rendra la main au programme apres l'appel à la fonction "Wimp_Poll" que lorsqu'il aura quelque chose de pertinent à transmettre: ceci évite de manger le temps du processeur inutilement, en permettant au Wimp de venir nous voir sans arrêt.

Après cela, je vous renvoie à une bonne documentation sur le Wimp pour les différents "reason codes".

Accès à un élément d'un tableau - Pré-incrémentation

Plus loin, vous rencontrez:
	ADR R10,user_buf
	LDR R0,[R10,#12]	;Window handle (ou -2 si iconbar)
Ces deux instructions mettent l'adresse du user_buf dans le registre R10, puis nous chargeons dans R0 le mot situé à l'adresse qui est la somme de l'adresse spécifiée par R10 et de 12. Autrement dit: ajouter 12 au nombre contenu par R10, puis à l'adresse ainsi obtenue chercher un mot, que l'on met dans le registre R0!

REMARQUE: le contenu du registre R10 n'est pas altéré par cette opération, que l'on appelle "pré-incrémentation". Souvenez-vous: pré-incrémentation: l'incrément AVANT le crochet fermé!

Nous verrons ultérieurement comment faire pour que R10 soit mis à jour par une telle opération.

Appel à une sous-routine

Vous vous souvenez des GOSUB en basic? Et bien examinez ces deux lignes:
	CMP R0,#2		;menu button?
	BLEQ create_menu	;oui: demander au wimp de créer le menu
La première, c'est une comparaison: on compare le contenu du registre R0 à la valeur 2.

La seconde est une instruction dans sa forme conditionnelle. L'instruction, à la base, s'écrit comme ceci:

	BL label_de_sous_routine
On peut la lire "Branch with Link", ce qui ne veut pas dire grand chose, si ce n'est que son action sera celle-ci: d'abord, le contenu du registre R15 sera recopié dans le registre R14. Puis un branchement sera fait au label spécifié.

Pourquoi copier R15 dans R14? Et bien, parceque R15 est un registre un peu spécial: c'est le COMPTEUR DE PROGRAMME. C'est R15 en effet qui contient l'adresse de l'instruction en cours (en fait c'est un peu différent, mais restons imprécis pour cette fois). Si à un moment donné nous désirons faire appel à une sous-routine, il faudra que, une fois celle-ci terminée, le processeur sache où reprendre l'exécution. Nous avons besoin de connaître l'ADRESSE DE RETOUR, pour la remettre dans R15, de sorte que le processeur saura où aller chercher l'instruction suivant la fin de la sous-routine.

Retour d'une sous-routine

Le "Return" de l'assembleur, vous le trouverez à la fin de la sous-routine que nous avons écrite pour la création du menu:
	MOV R15,R14		;retour à l'appelant
C'est tout simple, non? Nous nous contentons de copier le contenu de R14 dans R15, ce qui implicitement la réciproque de l'opération précédente!

Nous verrons ultérieurement quelles solutions nous pouvons adopter (elles sont légion!) pour avoir droit à des sous-routines imbriquées sans perdre, à chaque étage, le contenu de R14...

Le décalage des registres en cours d'opération

Pendant la routine de création de menu, vous n'avez pas manqué d'être intrigué par l'intruction:
	SUB R2,R0,R2,ASR #1
Bon, vous connaissez déjà les instructions arithmétiques, addition, etc. Je vous rappelle par exemple comment soustraire R2 à R0 et mettre le résultat dans R2:
	SUB R2,R0,R2
"Traduit" en basic, cela donnerait: R2=R0-R2. Mais que vient faire le petit bout qui dépasse plus haut? Et bien, on pourrait le lire "Arithmetic Shift Right by 1 bit" (Décalage Arithmétique vers la Droite de 1 bit).

En fait, juste avant d'être soustrait de R0, le contenu de R2 est décalé de 1 bit vers la droite, ce qui revient à le DIVISER PAR DEUX!
"Traduit" en basic, cela donnerait: R2=R0-(R2/2)
"Traduit" en C: R2 = R0 - (R2 >> 1);

Eh oui! Nous venons, en une seule opération, de diviser par 2 le contenu d'un registre et de le soustraire à un autre registre!

Vous pouvez comme cela, la plupart du temps, effectuer des décalages de bits, des rotations, sur les données en même temps que d'autres opérations. Les principaux opérateurs que vous pouvez rajouter sont:

	LSL	Logical Shift Left (Décalage Logique vers la gauche)
	ASL	Arithmetic Shift Left (Décalage Arithmétique vers la gauche)
	LSR	Logical Shift Right (Décalage Logique vers la droite)
	ASR	Arithmetic Shift Right (Décalage Arithmétique vers la droite)
	ROR	Rotate Right (Rotation vers la droite)
En paramètre, vous pouvez spécifier soit un nombre (par exemple: ASR #1), soit un registre (par exemple: ASR R1), registre qui, à ce moment-là, contient le nombre qui sera pris comme paramètre, ce qui permet des possibilités intéressantes.

Il y a un autre opérateur, RRX "Rotate right with extent", sans paramètre, qui permet de faire tourner vers la droite, d'un bit seulement, et avec extension... C'est un peu compliqué à expliquer, ce n'est pas tellement utile pour le moment, alors nous en parlerons plus tard.


Bon sang! J'ai tout compris!

J'espère que c'est ce que vous venez de hurler (n'oubliez pas de réveiller aussi les voisins: ce doit être l'heure de partir pour le boulot, à présent!), car sinon, c'est que vous n'avez pas suffisamment accroché à un endroit.

Alors si tel est le cas, reprenez calmement cette leçon, au besoin revoyez les précédentes... Et si ce n'est pas suffisant, écrivez-moi, au bureau de l'association ou par e-mail.

Dans tous les cas, vous avez probablement noté que nous commençons à être en mesure de faire des programmes qui commencent à avoir de la gueule... Hé hé...

Retour au sommaire