[-HP-49G : 1_Introduction a l'ASM - ]
Un des principal avantage de la
HP est que la programmation
Assembleur est tres bien suivie
par rapport a TI ou Casio ou c'est
le BASIC qui prime.
BASIC : Facile + tres tres tres
lent
ASM : tres peu d'instructions mais
necessite des connaissances en
programmation + tres tres tres
rapide
Pourquoi l'assembleur est si
rapide ? Parce qu'il est tres
proche du processeur (le SATURN
sur Hp49) : il faut savoir que le
processeur lit vos programmes
avec des 0 et des 1 (notation
binaire) et que l'ASM est
justement tres proche des 0 et
des 1 alors que le BASIC rajoute
plein de 0 et de 1 inutiles qui
ralentissent votre programme car
il ne sait pas exactement ce que
vous voulez.
Par exemple : pour afficher une ligne
: en BASIC : Line(x,y,xx,yx)
en ASM : ecrire un 1 dans la
memoire de l'ecran pour chaque
point de ma ligne
-> Resultat : vous avez ecrit
vous-meme votre ligne a l'ecran au
lieu d'ordonner a quelqu'un d'autre
de le faire.
AVERTISSEMENT: J'utilise des notions que je n'expliquerais pas parce
qu'elles ont deja ete expliquees : la notation hexadecimale, les
boucles ..
vous pouvez vous referer a ces textes :
Asm.zip de HpMd
manuelASM.zip de Sebastien Munch (BBossByll)
beaucoup d'autres traīnent sur le net
La base de l'ASM sur HP, c'est en
gros:
les variables 20 bits : A, B, C
les pointeurs d'adresse : D0, D1
les pointeurs de memoire :
DAT0, DAT1
par exemple, si vous voulez ecrire
F (notation hexadecimale bien
sur) a l'adresse memoire 1A2EF :
D0=1A2EF ; stock l'adresse 1A2F
dans le pointeur
DAT0=F ; maintenant que le
pointeur pointe sur notre adresse
mem, on stock FF a cet
emplacement
En realite, ce programme ne
marcherait pas car vous ne
pouvez pas utiliser certaines
instructions, le vrai programme
serait :
D0=1A2EF
LC F ; je fait une transition en
stockant F dans C (LC:LoadC)
DAT0=C B ; je copie le contenu
de C (cad F) en 1A2EF
** Le B signifie 'Byte' et s'appelle
un champs (j'expliquerais les
champs au fur et a mesure)
Vous allez me dire : C'est bien joli
tout a mais a sert a quoi d'ecrire
des 0 et des 1 dans la memoire de
ma HP ?
Et bien en fait, tous les jeux de
votre HP peuvent etre resumes a des
affichages de pixels donc a des
ecritures dans la memoire de l'ecran
Bien sur, si vous voulez afficher une
ligne, vous n'allez pas ecrire 100 fois
'afficher pixel' mais faire un boucle.
XsFl00d - 30/10/99
Pour tout commentaire etc ...
n'hesitez pas a ecrire :
p4x@hotmail.com
[-HP-49G : 2_Petit programme de base - ]
"GOSBVL
SAVPTR
GOSBVL
GETPTRLOOP
@
"
Tout d'abord, ce programme doit etre compile avec MASD et EXTABLE
installe sur votre HP: MASD est fourni avec la rom 1.14-2 et EXTABLE
est telechargeable ici
pour compiler ce programme, vous devez mettre la source sur la pile et
taper : 257 ATTACH [enter] ASM [enter]
et si la compilation a marche vous obtenez 'Code' sur la pile, vous
devez alors taper : EVAL
Ce qui a lance votre programme. Et vous constatez que rien ne s'est
passe ! Maintenant, regardez les explications:
GOSBVL : instruction qui permet de lancer un sous-programme.
SAVPTR : grāce a extable, nous avons pu taper SAVPTR au lieu de
0679B. 0679B est l'adresse ou est situe le sous programme. Il est
situe en ROM (a nous evite d'avoir a le reecrire) et il fait les
sauvegardes qui permettrons de retourner a l'affichage normal de la HP
avec :
GETPTRLOOP : mme genre que SAVPTR sauf que celui-ci nous permet
de restaurer ce que nous avons sauvegarde tout a l'heure (adresse rom
: 05143). Essayez de compiler le prog sans GOSBVL GETPTRLOOP :
ca va faire un beau bordel !(tapez ON-C pour rebooter)
@ : tout programme compile avec MASD doit se terminer avec ce
symbole
" : le programme doit se trouver entre guillemets (car c'est un 'string')
Il est normal que ce programme ne fasse rien : il se lance puis s'arrete.
Vous avez donc compris que si je veux compliquer, il suffit de rajouter
des instructions entre GOSBVL SAVPTR et GOSBVL GETPTRLOOP.
Si quelque chose vous semble obscur, ecrivez-moi.
Recuperez les sources sans les commentaires : ex1
XsFl00d - 30/10/99
Pour tout commentaire etc ...
n'hesitez pas a ecrire :
p4x@hotmail.com
[-HP-49G : 3_Programme plus complet - ]
J'ai subdivise ce programme en plusieurs parties pour pouvoir en
reutiliser certaines dans un futur prog.
source principale:
"GOSBVL SAVPTR; fait une sauvegarde du systeme en cours
'SCREEN ; cf. source suivante : permet de mettre ce qui se trouve dans
'SCREEN' a la place de cette instruction
*Boucle; je cree un label
GOTO Boucle ; je fais une boucle sans fin pour que vous puissiez voir
le resultat (sinon, ce serait trop rapide) !
'UNSCREEN; cf. plus bas
GOSBVL GETPTRLOOP ; restaure les sauvegardes du systeme
@" ; fin de la source.
SCREEN:
"LC 881 ; met 880 (=2176 en decimal -> la taille de l'ecran en pixels)
dans C + 1 au cas ou l'adresse soit impaire (cf. plus bas)
GOSBVL MAKE$N ; cet appel en rom permet de reserver un espace
pour
l'ecran dans la memoire de la HP49 (adresse rom : 05B7D)
CD0EX ; D0 contient maintenant l'adresse de l'ecran que je viens de
reserver. et j'echange les contenus de C et de D0. Donc maintenant :
D0=880 et C contient l'adresse de l'ecran
?CBIT=0 0 ; si le 1er bit de C est a 0 (donc si l'adresse est paire) -->
cf. plus bas pour voir pkoi cette adresse doit etre paire
GOYES Paire ; alors je saute au label 'Paire' (un label commence par
*)
C=C+1 A ; sinon, si l'adresse est impaire, je lui ajoute 1 pour la rendre
paire
*Paire ; voila mon label. maintenant que je suis sur que l'adresse est
paire, je peux continuer ..
D0=00120 ; je charge l'adresse 00120 dans D0 (c'est la que je dois
ecrire l'adresse de mon ecran pour que la HP sache que c'est cette
portion de memoire et pas une autre qu'elle doit afficher)
DAT0=C A ; j'utilise DAT0 pour ecrire a l'adresse pointee par D0
l'adresse de mon ecran a moi. 'A' signifie que cette adresse comporte 5
chiffres (toutes les adresses sont de 5 chiffres : ex : 039BE; 00120;
0679B; l'adresse de mon ecran etc.)
D0=C ; maintenant D0 pointe sur mon ecran : je vais enfin pouvoir
ecrire des pixels!
B=C A ; je fais une sauvegarde de l'adresse de l'ecran en B pour
pouvoir la reutiliser dans mon prog principal. 'A' car l'adresse a 5
chiffres!
LC 87 ; la, je mets 87 (135 en decimal) dans C. J'ai dans l'idee
d'utiliser C comme compteur ..
A=0 W ; je mets 16 zeros dans A car 'W' correspond a 16 chiffres
*Efface ; je mets un label ici car je vais devoir repeter les expressions
qui suivent plusieurs fois
DAT0=A W ; D0 contient l'adresse de mon ecran donc DAT0=A W ecris
16 zeros dans l'ecran (cad eteint les pixels) (W : 16 chiffres!)
D0=D0+16 ; j'ajoute 16 a l'adresse de l'ecran pour pouvoir effacer les
16 pixels suivants
C=C-1 B ; je decremente mon compteur car l'ecran doit etre efface en
16*136 fois. 16*136=2176 : c'est exactement la taille de l'ecran en
quartets (portions de 4 bits)!
GONC Efface ; GONC=GoifNotCarry : en gros ca veut dire que je dois
continuer a eteindre des pixels tant que C est different de zero (C est
utilise comme compteur et je lui enleve 1 a chaque tour)
D0=00128 ; adresse ou on peut ecrire la taille de la barre de menu du
bas
LC 3F ; 3F correspond a un menu de taille 0 donc invisible ! La taille
normale est 37
DAT0=C B ; j'ecris la nouvelle taille en 00128. 'B' correspond a un
nombre de 2 chiffres (B=Byte=octet=8bits=2chiffres hexadecimaux)
@" ; la source de ce fichier est finie
Quelques precisions :
** Un ecran est une portion de memoire qui ne peut commencer a une
adresse impaire, d'ou le test avant de donner a la HP mon adresse
d'ecran.
** L'ecran est divise en 2 parties : le menu et l'ecran lui-mme. Mme
apres avoir efface tout l'ecran il faut encore que je baisse le menu pour
avoir un ecran totalement vierge. Essayez le mme programme en
changeant la taille du menu entre 37 (taille normale) et 3F (taille 0).
** vous avez peut-etre remarque que 87 = 135 en decimal et que
bizarrement, je vous dit que la boucle va s'executer 136 fois.. C'est d
au fait que mme lorsque C=0, la boucle va s'executer puisque le test
sur C n'a lieu qu'a la fin : j'ai donc : 135+1=136
UNSCREEN:
"D0=8068D ; A cette adresse, je peux trouver l'emplacement de l'ecran
normal de la HP49, cad avant que mon programme ait change l'adresse
en 00120. Mais alors, me direz-vous, on aurait pu lire l'adresse stockee
en 00120 avant de la changer au debut, puis la restaurer ! Non ! Car
00120 n'est accessible qu'en ecriture, vous n'y trouverez pas l'adresse
de votre ecran.
C=DAT0 A ; je recupere l'adresse de l'ecran normal.
D0=00120 ; je pointe en 00120
DAT0=C A ; et j'ecris l'adresse recuperee en 8068D pour dire a la HP
d'afficher l'ecran habituel
D0=00128 ; adresse de la hauteur du menu (accessible aussi qu'en
ecriture)
LC 37 ; je mets la taille habituelle du menu dans C
DAT0=C B ; et je mets la nouvelle taille en 00128
@"
** Vous n'avez plus qu'a compiler et lancer tout a comme indique
dans la section 'programme de base' ou regarder dans le fichier ABOUT
(rom 1.14-2 et extable necessaires).
** Ha oui !! Pour arreter le programme: faites ON-C pour redemarrer la
HP49. Sinon, voici un petit exercice pour voir si vous avez compris :
Au lieu de faire une boucle infinie, vous allez initialiser un compteur
avant le label et dans la boucle, vous decrementerez le compteur et
verifierez si le compteur est 0. Pour cela, vous utiliserez une technique
identique celle de l'effaable de l'ecran (conseil : initialisez le compteur
AAAAA et utilisez un champs A pour la decrementation).
** Vous avez peut etre note que dans le programme principal, les
instructions 'UNSCREEN et GOSBVL GETPTRLOOP ne servent rien
puisque je vous oblige le quitter en appuyant sur ON-C donc ces
instructions ne sont pas executees (on quitte pendant la boucle).. Si
vous enlevez la boucle infinie et la remplacez avec l'exercice ci-dessus,
vous pourrez voir leur utilite.
XsFl00d - 30/10/99
Pour tout commentaire etc. n'hesitez pas a ecrire
: p4x@hotmail.com
[-HP-49G : 4_Affichage d'un grob - ]
Pour compiler ce programme, vous aurez besoin de SCREEN et UNSCREEN vus dans
le programme precedent.
Il vous permet d'afficher un grob que vous aurez stocke dans le fichier GROB1
(precisions sous la source)
source
principale:
"GOSBVL SAVPTR ; fait une sauvegarde du systeme en cours
'SCREEN ; inclus le fichier SCREEN (creation d'un ecran et mise zero de celui-
ci)
C=B A ; recuperation de l'adresse de l'ecran (stockee en B par SCREEN)
D0=C ; D0 pointe sur l'ecran
C=PC ; PC (ou Program Counter) pointe sur la prochaine instruction a executer
GOINA grob1 ; GOINA renvoie la difference entre l'emplacement de
l'instruction et le label
A=A+C A ; resultat : avec mon addition, je recupere l'adresse de mon image
(GROB=GRaphicOBject) car
adresse_image = l'emplacement_actuel + distance_entre
l'image_et_emplacement_actuel
D1=A ; donc D1 contient l'adresse de l'image
LC 87F ; taille de l'ecran (et de l'image) en quartets 87Fh+1=880h=2176 et
2176*4=8704 (un quartet = 4
pixels)
*Recop ; label qui va me permettre de recopier mon dessin par quartets (cad 4
pixels par boucle)
A=DAT1 P ; A contient 4 pixels du dessin (P = Pointeur de registre : couvre 1
quartet)
DAT0=A P ; recopie les 4 pixels sur l'ecran
D0=D0+1 ; je passe au quartet suivant sur l'ecran
D1=D1+1 ; idem mais pour l'image
C=C-1 X ; decrementation du compteur (champs X : 3 quartets)
GONC Recop ; recommence jusqu'a ce que l'image soit recopiee totalement
(apres 880 fois)
LC AAAAA ; charge un compteur (pour que le programme s'arrete apres
quelques secondes)
*Boucle ; je cree un label
C=C-1 A ; decrementation du compteur
GONC Boucle ; si le compteur n'est pas zero, je reboucle, sinon, le programme
s'arrete
'UNSCREEN ; inclus UNSCREEN (restauration de l'ecran original de la HP)
GOSBVL GETPTRLOOP ; restaure les sauvegardes du systeme
*grob1 ; ce label permet de faire GOINA sur l'adresse de l'image
'GROB1 ; mets les donnees de l'image ici (cf GROB1)
@" ; fin de la source
** PC (ou program counter) contient l'adresse de la prochaine instruction a executer. Si
vous changez sa valeur, a
revient a faire un saut.
** Au cas ou vous n'ayez pas tres bien suivi l'histoire des quartets : 4 bits = 1 quartets ; 8
bits = 2 quartets = 1
octet (1 byte en anglais)
** Le champs P (ou pointeur de registre ) est utilise pour 1 quartet.
** Le champs A (Adresse) est utilise pour 5 quartets : sur HP, les adresses sont de 5
quartets (20 bits)
** Le champs B (Byte) est utilise pour 2 quartets (1 octet) : il peut tre utilise pour des
compteurs a 2 chiffres
hexadecimaux (FF par exemple)
** Le champs W (Wide) est utilise pour 16 quartets (64 bits) : c'est le champs le plus
grand et il generalement utilise
pour des copies de memoire (j'aurai pu l'utiliser pour recopier le dessin -> tiens ! j'y
pense ! Pour le prochain cours,
vous allez recopier le dessin en utilisant le champs W :)
** Le champs X (eXposant) est utilise pour 3 quartets.
** Si vous avez vraiment bien compris, je peux vous compliquer le truc : la position de
P est variable et le champs
WP depend de sa position. Par exemple : si P=A, alors P couvre le 10eme quartet et WP
couvre les quartets 0 a 10
(donc 11 quartets).
** Il existe d'autres champs que j'expliquerais quand on en aura besoin.
** Vous avez pu remarquer que j'utilise non plus une boucle infinie mais une boucle qui
dure quelques secondes :
c'est la reponse de l'exercice du cours precedent.
GROB1:
"$000000000000000000000000000000000000000000000000000000000000000000000
000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000
000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000
000000000000000
00000000000000000200000010000000000000000EFFF30000040000080000000000000
000020000000004
00000400000000000000000200000000080000040000000000000000020000000000100
0020000000000000
00002000000000020000100000000000000000200000000002000800000000000000000
0200000000004000
40000000000000000002000000000080004000000000000000000200000000000100200
0000000000000000
2000000000001001EF34008F00000000002000000000002080200400603000FFF700EF
FF70000000404020
04001040000404000000400000004040200408008083040C10000040000000802020040
800806C04001000
004000000001102004040001280400300000400000000A00E1040400011014002000004
000000006002004
0400011014002000004000000004002004040001101400200000400000000A002004040
001280400200000
40000000090020040800806C04002000004000000080102004080080830400200000400
000004020200400
104000040020000040000000404020040060300004003000004000000020402004008F0
000040810000040
0000001080000CF30000000408000000400000080001000000000000EFFF0000004000
000400020000000
00000000000000040000004000200000000000000000000004000000200040000000000
000000000000400
00001000800000000000000000000004000008000080000000000000000000000400000
400000100000000
00000000000004000004000002000000000000000000000400000200000400000000000
000000000040000
010000040000000000000000000004000000000008000000000008FFFFFFFFF7000000
000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000
000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000
000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000
000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000
000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000
000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000
000000000000000
000000000000000000000000000
@"
** Un GROB est en fait une suite de chiffres hexadecimaux (donc de quartets, donc de 4
pixels). Par exemple 'F' en
binaire , s'ecrit : 1111 donc F dans le grob va se traduire par une ligne de 4 pixels sur
mon dessin.
** Si vous voulez mettre vos propres images, vous devez la dessiner sur la HP, puis la
stocker dans un grob .
Seulement vous n'avez pas acces a la suite de chiffres donc vous devez mettre : "" dans
le niveaux 2 de la pile et
votre grob dans le niveaux 1 puis faire + . Ca vous donne votre grob mais avec un
prologue qui va ressembler a a :
GROB 131 64 vous n'avez qu'a le virer pour obtenir ce dont vous avez besoin.
XsFl00d - 30/10/99
Pour tout commentaire etc ... n'hesitez pas a ecrire : p4x@hotmail.com
[-HP-49G : 5_Gestion des touches - ]
attention : desormais je vais utiliser la syntaxe MASD bien pratique.. Il vous faut
donc une ROM recente
et EXTABLE pour compiler les programmes
source principale:
"
SAVE ; MASD le remplace par GOSBVL SAVPTR
LC 3F D0=00128 DAT0=C.B ; Baisse le menu
*Touches
LC 001 GOSBVL OUTCINRTN ; la valeur 001 correspond aux
tests des touches
de la premiere colonne du clavier a droite (pareil pour ENTER,
TAN etc ..) -> cf
la table des correspondances
?CBIT=1 6 GOYES Exit ; si le 6eme bit de C est a 1, alors c'est
que DROP a
ete appuyee, on va donc arreter le programme
GOTO Touches ; Si DROP n'a pas ete appuyee on continue a
tester les touches
*Exit
LC 37 D0=00128 DAT0=C.B ; Je remonte le menu
GOSBVL Flush ; Je vide le buffer du clavier
LOADRPL ; et je quitte le programmme (MASD le remplace par
GOSBVL
GETPTRLOOP)
@" ; fin de la source
** pour la detection des touches, le systeme agit ainsi : il va faire un OUT=001, c'est
a dire envoyer du
courant sur la colonne de droite du clavier. Quand une touche est appuyee, il se cree
une sorte de cours
circuit detecte par C=IN . Ainsi, suivant les valeurs de C, on pourra determiner
quelle touche a ete tapee.
** A la fin, on vide le buffer du clavier car quand vous appuyez sur plusieurs
touches les unes a la suite
des autres, le systeme cree une sorte de liste d'attente qui permettra aux touches
d'etre traitees dans le
bon ordre . Pour eviter qu'a la sortie du programme, le systeme veuille s'occuper des
touches appuyees
durant son execution, on vide cette liste d'attente.
Table de correspondance
des touches :
(en partie recuperee dans
smhp49 de SunHP)
TOUCHES
OUT (LC
???)
IN (CBIT=1 ?)
[ENTER]
001
0
[+]
001
1
[-]
001
2
[*]
001
3
[/]
001
4
[U]
001
5
[DROP]
001
6
[L]
001
7
[SPACE]
002
0
[3]
002
1
[6]
002
2
[9]
002
3
[Y]
002
4
[T]
002
5
[P]
002
6
[K]
002
7
[.]
004
0
[2]
004
1
[5]
004
2
[8]
004
3
[X]
004
4
[S]
004
5
[O]
004
6
[J]
004
7
[0]
008
0
[1]
008
1
[4]
008
2
[7]
008
3
[W]
008
4
[R]
008
5
[N]
008
6
[I]
008
7
[V]
010
4
[Q]
010
5
[M]
010
6
[H]
010
7
[G]
020
7
[ALPHA]
080
3
[Left Shift]
080
2
[Right Shift]
080
1
DROITE
040
0
BAS
040
1
GAUCHE
040
2
HAUT
040
3
XsFl00d - 21/12/99
Pour tout commentaire etc ... n'hesitez pas a ecrire :
p4x@hotmail.com
[-HP-49G : 6_Petit jeu - ]
Bon, il est temps de compliquer un peu les choses.
Tout d'abord, le programme ne va pas etre ecrit de maniere lineaire mais avec des
sous-programmes, cad
des petits bouts de codes que l'on peut appeler dans la boucle principale.
Ensuite, la syntaxe MASD nous permet de nous rapprocher du C avec des structures
IF ELSE etc ... ce qui
est bien pratique..
Et maintenant, que va faire ce petit jeu ?
Deux vaisseaux s'affichent a l'ecran. Chaque joueur peut le deplacer dans n'importe
quel sens avec ses
touches. On designe un joueur qui doit toucher le vaisseau de l'autre, si il y a
collision, alors le jeu
s'arrete
Comment va-t-on programmer a ?
1 j'initialise des constantes (par exemple chaque occurence a 00128 sera remplacee
par "setmenu")
2 je sauvegarde le systeme et je fais differentes tāches qui lui sont relatives
3 je recupere un ecran et sauvegarde son adresse
4 je sauvegarde l'adresse de l'image des vaisseau (du sprite)
5 je sauvegarde les coordonnees initiales des 2 vaisseaux
6 j'efface l'ecran, baisse le menu et affiche l'ecran
7 j'execute ma boucle principale jusqu'a ce que la touche DROP soit appuyee. Dans
la boucle :
1 j'affiche les deux sprites aux coordonnees sauvegardees
2 je detecte si des touches ont ete appuyees (et je mets a jour les coordonnees si les
touches de
direction ont ete detectees)
3 je detecte si il y a eu collision entre les 2 vaisseaux. si oui, je passe en 8, sinon, je
continue la
boucle.
4 je met une petite boucle d'attente car l'ASM est tres rapide.
5 j'efface mes deux vaisseaux pour les reafficher a un autre endroit. Puis je
retourne en 7-1 pour les
afficher aux nouvelles coordonnees
8 Je reaffiche l'ecran normal de la calto et je rends la main au systeme
1
"
CP=822B2 ; en 822B2, il y a de l'espace libre ou je peux
stocker
mes constantes
DCCP 5 ECRAN ; je reserve 5 quartets a cet endroit pour y
mettre
l'adresse de mon ecran. DCCP est genial car il fait
automatiquement 822B2+5
DCCP 5 GROB ; en 822B7 : je reserve 5 quartets pour
stocker
l'adresse de mon GROB
DCCP 2 X1 ; en 822BC : 2 quartets pour l'abscisse du 1er
vaisseau
DCCP 2 Y1 ; en 822BE : " " l'ordonnee "
"
DCCP 2 X2 ; en 822C0 : " " l'abscisse du 2eme
vaisseau
DCCP 2 Y2 ; en 822C2 : " " l'ordonnee "
"
DC setecran 00120 ; la je dis a MASD qu'a chaque fois qu'il
rencontre "setecran", il le remplace par l'adresse 00120
DC setmenu 00128 ; meme chose pour l'adresse de la taille
du
menu
ST=1 0 ; Program Status Bit. Il y en a 16. Seuls les 11
premiers
sont utilisables par le coder. Ils representent un etat (1 :
TRUE ;0 :
FALSE). Ici, j'utilise le ST 0 et je le met a 1. Je vais
l'utiliser pour
savoir si mes sprites ont change de position depuis la
derniere
Boucle. (si j'avais voulu utiliser le ST 5, j'aurai ecrit : ST=1
5 ). Je
le met initialement a 1 pour la premiere execution de la
boucle.
** un petit schema pour mieux comprendre l'instruction DCCP :
|----ECRAN----|----GROB----|----X1-----|----Y1----|---X2-----|----Y2---|
822B2<--5-->822B7<--5-->822BC<-2->822BE<-2->822C0<-2->822C2<-2-
>822C3
2
SAVE ; GOSBVL SAVPTR
INTOFF2 ; ha ! Les fameuses interruptions ! Je vais ici
simplement
vous dire que je les desactive et vous expliquer plus bas ce
que
c'est.
ST=0 15 ; un des Status bit que le programmeur ne peut pas
utiliser. Celui-ci est utilise par le systeme pour savoir s'il
doit gerer
les interruptions clavier.
** Les interruptions sont en fait les evenements que doit gerer la HP comme l'appui
d'une touche, la mise
a jour de l'horloge, le refresh de l'ecran.
Pour tester, essayez de compiler ce programme sans les 2 instructions qui desactivent
les interruptions et
essayez de taper ON-C, votre calto va rebooter. Maintenant, essayez la meme chose
mais en ayant
desactive les inter. , la calto ne bouge pas d'un poil.
Il ne faudra surtout pas oublier de reactiver les interruptions a la fin du prog, donc
faites-y moi penser.
note : vous entendrez souvent les programmeurs poser la question : "Comment je fais
pour detourner les
interruptions ?". Je fais parti de ceux-la donc des que je sais faire, je vous explique :).
Mais en gros, a
correspondrait a dire vous-meme quand l'ecran doit se raffraichir ou a augmenter les
secondes de
l'horloge vous-memes donc vous voyez comme c'est puissant !
3
LC 00881 ; je veux un ecran de 880 quartets (881 au cas ou
l'adresse de depart soit impaire)
RES.STR ; MASD le remplace par GOSBVL MAKE$N
LA(5)ECRAN ; je mets en A l'endroit ou je vais stocker
l'adr de
l'ecran (822B2)
AD0EX ; j'echange les contenus de A et de D0. Maintenant,
D0=ECRAN et A=adr de l'ecran
?ABIT=0.0 {A+1.A } ; voila un exemple de syntaxe propre
a MASD
(avec les accolades). Ici, si le premier bit (bit n°0) de A est
a 0,
c'est que l'adresse est paire (souvenez-vous, on est en
binaire).
SINON, alors executer ce qui est entre {}
DAT0=A.A ; j'ecris en 822B2 l'adresse (maintenant
forcement paire)
de notre ecran sur un champs A.
4
A=PC ; PC (program counter) contient l'adr de la prochaine
instruction a executer
GOINC Grob ; GOINC met en C le nombre de quartets qui
le separe
du label "Grob" (a nous permet de mettre les donnees du
grob a la
fin de la source a l'aide d'un label)
A=A+C.A ; en additionnant les 2 valeurs, j'obtiens l'adresse
du
GROB
D0=(5)GROB ; je la met en 822B7
DAT0=A.A ; j'ecris l'adresse avec un champs A
5
D0=(2)X1 LA 0A DAT0=A.B ; j'ecris l'abscisse du premier
sprite sur
2 quartets (champs B) en 822BC
D0=(2)Y1 DAT0=A.B ; j'ecris son ordonnee.
Sprite1(0A;0A)
D0=(2)X2 LA 1A DAT0=A.B ; les coordonnees du 2e
vaisseau
D0=(2)Y2 DAT0=A.B ; Sprite2(1A;1A)
6
%Efface l'ecran ; un commentaire en style MASD
A=0.W D1=(5)ECRAN C=DAT1.A D0=C LC 87 ; D0 : adr
ecran
{DAT0=A.W D0+16 C-1.B UPNC} ; Encore une structure
MASD.
UPNC correspond a GONC (cf exemples precedents), cad :
tant que
C est plus grand ou egal a zero, j'execute ce qui est entre
accolades.
%Baisse le menu
D0=00128 LC 3F DAT0=C.B ; vous etes cense savoir faire
(sinon cf
ex precedents)
%Affiche Ecran
D0=(2)setecran D1=(5)ECRAN C=DAT1.A DAT0=C.A ;
lit la
sauvegarde de l'adr de l'ecran pour l'ecrire en 00120
7
*Main
?ST=0 0 SKIPYES ; est-ce que mes sprites ont change de
place ? Si
oui, alors mon ST est a 1 et je dois executer ce qui est entres
accolades, sinon, je passe directement a la suite
{GOSUBL Sprite1 ; affichage du premier sprite aux
nouvelles
coordonnees
GOSUBL Sprite2 } ; id pour le second sprite
GOSUBL Touches ; test des touches et mise a jour des
coordonnees des vaisseaux
GOSUBL Collision ; detecte si il y a eu collision
GOSUBL Wait ; une petite boucle d'attente pour ne pas que
les
sprites se redessinent trop vite
?ST=0 0 SKIPYES ; si les coordonnees n'ont pas bouge, il
n'y a pas
de raison d'effacer l'ecran puisque je ne vais pas redessiner
mes
sprites
{GOSUBL Clrscr } ; efface l'ecran
GOTO Main ; ma boucle principale s'arrete la, je la relance.
7-1
*Sprite1
D0=(5)ECRAN A=DAT0.A ; A : adr ecran
D0=(5)X1 C=0.A C=DAT0.B A=A+C.A D0=A ; C :
abscisse (C est
mis a 0 sur A car on ne le charge que sur un champs B et on
fait un
addition sur un champs A, il faut donc etre sūr que les 3
quartets
de forte valeur soient a 0 car leur valeur est indeterminee,
on aura
donc : 000X1 au lieu de ???X1). Pour que D0 pointe sur
l'abscisse
du vaisseau, je dois additionner l'abscisse et l'adresse de
debut de
l'ecran (comme si on faisait un changement de repere) (cf
diagramme de l'ecran)
D1=(5)Y1 C=DAT1.B {D0+34 C-1.B UPNC} D0-34; C :
ordonnee.
j'incremente D0 d'une ligne autant de fois que son ordonnee
(cf
schema). D0-34 car cette boucle s'execute C+1 fois (j'aurai
pu
ecrire C-1.B C=DAT1.B et ensuite executer la boucle sans
faire la
soustraction a la fin).
D1=(5)GROB C=DAT1.A D1=C LC 07 {A=DAT1.B
D1+2 DAT0=A.B
D0+34 C-1.B UPNC} ; Recopie le GROB en commenant a
l'adresse
pointee par D0 qui correspond aux coordonnees de Sprite1
(grob de
8 lignes de 8 pixels (2 quartets) donc je copie 2 quartets
avant de
passer a la ligne suivante)
RTN ; Retourne dans la boucle principale
*Sprite2 ; Fonctionnement identique a Sprite1
D0=(5)ECRAN A=DAT0.A
D0=(5)X2 C=0.A C=DAT0.B A=A+C.A D0=A
D1=(5)Y2 C=DAT1.B {D0+34 C-1.B UPNC} D0-34
D1=(5)GROB C=DAT1.A D1=C LC 07 {A=DAT1.B
D1+2 DAT0=A.B
D0+34 C-1.B UPNC}
RTN ; retourne dans la boucle principale
7-2
*Touches
ST=0 0 ; je mets ST 0 a zero puisque son etat est
susceptible
d'etre modifie pendant cette routine.
%Player 1
LC 040 GOSBVL 0020F ?CBIT=0 3 SKIPYES ; touche
HAUT. SKIPYES
en syntaxe MASD permet d'ignorer ce qu'il y a entre
accolades si la
touche n'a pas ete pressee
{D1=(5)Y1 A=DAT1.B ?A=0.B EXIT A-1.B DAT1=A.B
ST=1 0} ; si la
touche a ete appuyee, je reduit l'ordonnee et je mets ST 0 a
1 pour
que le sprite soit affiche a sa nouvelle position par Sprite1.
En
plus, je detecte si le sprite est tout en haut de l'ecran
(ordonnee
de 0), si oui, je passe a la touche suivante sans toucher a ses
coordonnees (EXIT).
?CBIT=0 1 SKIPYES ;touche BAS
{D1=(5)Y1 A=DAT1.B B=A.B LA 37 ?A=B.B EXIT
A=B.B A+1.B
DAT1=A.B ST=1 0} ; augmente l'ordonnee en regardant si
il est
tout en bas (ordonnee de 37 et ?A=37.B n'existe pas donc je
transite par le registre B).
?CBIT=0 2 SKIPYES ; touche GAUCHE
{D1=(5)X1 A=DAT1.B ?A=0.B EXIT A-1.B DAT1=A.B
ST=1 0} ; si
pas a gauche alors je reduit son abscisse
?CBIT=0 0 SKIPYES ; touche DROITE
{D1=(5)X1 A=DAT1.B B=A.B LA 1F ?A=B.B EXIT
A=B.B A+1.B
DAT1=A.B ST=1 0} ;si pas a droite (abscisse 1F), alors
augmente
son abscisse
%Player 2 ; cf Player1 mais avec des touches differentes
LC 008 GOSBVL 0020F ?CBIT=0 0 SKIPYES ; touche 0
{D1=(5)Y2 A=DAT1.B B=A.B LA 37 ?A=B.B EXIT
A=B.B A+1.B
DAT1=A.B ST=1 0}
?CBIT=0 1 SKIPYES ; touche 1
{D1=(5)X2 A=DAT1.B B=A.B LA 1F ?A=B.B EXIT
A=B.B A+1.B
DAT1=A.B ST=1 0}
?CBIT=0 2 SKIPYES ; touche 4
{D1=(5)Y2 A=DAT1.B ?A=0.B EXIT A-1.B DAT1=A.B
ST=1 0}
LC 080 GOSBVL 0020F ?CBIT=0 1 SKIPYES ; touche
RightShift
{D1=(5)X2 A=DAT1.B ?A=0.B EXIT A-1.B DAT1=A.B
ST=1 0}
%Exit ; DROP a-t-il ete appuye ?
LC 001 GOSBVL 0020F ?CBIT=0 6 SKIPYES ; touche
DROP
{GOSUBL Restore } ; si oui, alors je quitte le programme
RTN ; sinon, je retourne dans la boucle principale avec mes
nouvelles coordonnees sauvegardees est pretes a etre
utilisees
dans Sprite1 et Sprite2
7-3
*Collision ; Un point assez complique mais en gros, a se
passe
comme a : si la difference des abscisses de mes vaisseaux
est
plus grande ou egale a deux, alors il n'y a pas de probleme.
Dans
le cas contraire, je verifie leurs ordonnees et si leur
difference est
plus grande ou egale a 8, alors c'est OK, sinon je quitte le
programme. J'ai pris la peine de vous faire un schema plus
bas ..
D0=(5)X1 A=DAT0.B D1=(5)X2 C=DAT1.B ?A>C.B
SKIPYES ; je ne
veux pas que la soustraction me sorte un chiffre negatif
donc je
regarde quel est la plus grande abscisse. Ce qui me permet
d'utiliser une structure IF (=SKIPYES) ELSE (=SKELSE)
specifique a
MASD
{C-A.B LA 02 ?C>=A.B -> {RTN} } ; je retourne dans la
boucle
principale si les abscisses sont OK (C-A.B est identique a
C=C-A.B
et A-C.B est identique a A=A-C.B)
SKELSE {C=A-C.B LA 02 ?C>=A.B -> {RTN} }
D0=(5)Y1 A=DAT0.B D1=(5)Y2 C=DAT1.B ?A>C.B
SKIPYES ; meme
principe
{C-A.B LA 08 ?C>=A.B -> {RTN} }
SKELSE {C=A-C.B LA 08 ?C>=A.B -> {RTN} }
GOSUBL Restore RTN ; si les abscisses ET les ordonnees
revelent
une collision, alors je quitte le programme. Note : le dernier
RTN
est inutile puisque Restore arrete l'execution du prog.
7-4
*Clrscr ; Ca fait du bien de retrouver des choses qu'on sait
faire !!
A=0.W D0=(5)ECRAN C=DAT0.A D0=C LC 87
{DAT0=A.W D0+16 C-1.B UPNC}
RTN
7-5
*Wait
LC FFF {C-1.X UPNC} ; cette boucle va s'executer FFF+1
fois
RTN
8
*Restore
D1=8068D C=DAT1.A D0=(5)setecran DAT0=C.A ;
affiche l'ecran
normal
D0=(5)setmenu LC 37 DAT0=C.B ; remonte le menu
GOSBVL Flush INTON2 ST=1 15 ; vide le buffer clavier,
remet les
interruptions (MASD remplace INTON2 par GOSBVL
AllowIntr
(GOSBVL 26767) )
LOADRPL ; rend la main au systeme
RTN ; inutile car la boucle principale ne sera plus jamais
executee,
Sniff :(
@"
**Quelques notes :
vous avez remarque que pour chaque GOSBVL, je vous donne l'adresse
correspondante. En fait, c'est
extable qui se charge de remplacer les noms par les adresses pour MASD. Les
correspondances sont dans
le fichier des entries 49 certainement dispo sur hpcalc.org.
Ce programme peut certainement etre optimise, j'ai evite quelques complications
mais si vous avez des
idees, envoyez-les et je ferai une petite rubrique sur l'optimisation.
Je pense que pour bien comprendre cet exemple, vous devriez programmer un jeu
du meme style et
quand vous avez du mal, pompez sur ces sources, c'est le seul moyen (a mon avis).
Pour les betes de l'asm, n'hesitez pas a m'insulter si je code des aberrations vu que
c'est mon premier
"vrai" jeu (si on peut appeler a un jeu)
Jetez un coup d'oeil sur les programmes qui sortent avec les sources (doom49,
terminaltor, puzzle
bobble)
A moins qu'on me dise le contraire, je vais eviter de donner des programmes
complets en exemples, a
me semble trop complique de suivre le raisonnement du coder sur plusieurs dizaines
de lignes.
il se peut que j'ai ecrit des betises, si vous n'etes pas sūr, alors lisez la source
fournie dans l'archive, je
suis sūr qu'elle est compilable et qu'elle marche.
Si le democoding vous interesse, il faut absolument m'ecrire, j'avais dans l'idee de
faire des effets genre
plasma et fire mais je ne m'y connais pas assez.
Vous avez de la chance, je vous donne les sources avec des italiques et du soulignage
pour bien
comprendre (je les dorlotte mes lecteurs, moi). L'executable est fourni avec :
Vessel.zip
XsFl00d - 21/12/99
Pour tout commentaire etc ... n'hesitez pas a ecrire :
p4x@hotmail.com
[-HP-49G : 7_La pile- ]
Cette fois nous allons etudier la gestion de la pile (stack en anglais)..
On va commencer par expliquer comment elle fonctionne puis programmer les
foncions usuelles comme DUP, DROP, etc ..
Qu'est-ce que la pile ?
Imaginez que vous allez dans une cafeteria. Vous arrivez, vous prenez un plateau sur
la pile, vous mangez des trucs degueux et vous remettez le
plateau sur le dessus de la pile. Donc la prochaine personne qui va arriver va prendre
le plateau que vous venez de poser : c'est donc une structure
LIFO (Last In First Out = dernier arrive premier parti).
Rien de nouveau pour le moment si vous n'utilisez pas le mode algebrique (je
l'espere pour vous !). Ce qu'il y a sur la pile (par exemple un GROB)
n'est pas le GROB lui-meme mais l'adresse de ce GROB en memoire. Ainsi, chaque
niveau utilise 5 quartets (cad la taille d'une adresse). Le
niveau le plus haut est la ligne de commande, ensuite c'est le dernier niveau, l'avant
dernier et enfin le niveau 1.
Pour monter d'un niveau, on incremente l'adresse du pointeur de 5 quartets.
Pour trouver l'adresse du niveau 1, il faut savoir que la HP utilise un registre qui
contient l'adresse de ce niveau : D1. C'est d'ailleurs pour a qu'on
fait un SAVE et un LOADRPL : si on a modifie D1 dans notre programme, il faut le
reinitialiser a la valeur qu'il avait avant le lancement de notre
soft. Bien sūr, si on veut que la HP ne retourne pas a l'etat initial pour par exemple
changer quelque chose a la pile, il ne faut pas faire de
LOADRPL.
Regardez plutļt le schema.
NIVEAU
EXEMPLE
ADRESSE
RELATIVE
Ligne de commande
FFF3C (a trouver en 806FD)
+15
Niveau 3
FFF37
+10
Niveau 2
FFF32
+5
Niveau 1
FFF2D (D1 pointe dessus au debut du soft)
0
DROP (efface le niveau 1 de la pile)
D1+5 ; D1 contient l'adresse du niveau 1 de la pile. DROP
consiste a ce que le niveau
2 devienne le niveau 1. Donc je fais pointer D1 5 quartets
plus loin.
D+1.A ; D contient le nombre de blocs de 5 quartets libres
dans la pile. Vu que j'elimine
un niveau, j'ai libere 5 quartets, d'ou D+1.A
RPL ; equivalent a A=DAT0.A D0+5 PC=(A)
@
** Voila ce que nous avons fait:
En debut de programme, D1 contient l'adresse du niveau 1 de la pile. Augmenter D1
de 5 signifie que le premier niveau va correspondre a l'ancien
niveau 2. Ainsi, le niveau 1 est parti aux oubliettes. D1+10 aurait correspondu a 2
DROP, D1+15 a 3 DROP etc ...
Ici, nous ne faisons pas de test pour savoir si la pile contient quelque chose dans le
niveau que l'on veut dropper mais la HP en fait un elle.
** A part D1, la HP utilise d'autres registres qui doivent etre sauvegardes et restaures
si vous les modifiez dans votre code :
D0 : pointe l'adresse du prochain objet a executer
D1 : le pointeur de pile
B : pointeur de la pile de retours (return stack)
D : nombre de blocs de 5 quartets libres.
Donc A et C sont libres. Si vous n'utilisez qu'eux, vous pouvez vous dispenser de
SAVE et LOAD mais RPL est toujours necessaire (cf
explication de RPL plus loin).
** RPL (ou LOOP)
A=DAT0.A ; D0 pointe sur l'adresse du prochain objet a executer. Donc je recupere
en A cette adresse.
D0+5 ; On fait pointer D0 sur ce qu'il faut lancer apres ce qui va etre execute par
PC=(A)
PC=(A) ; Program Counter : l'instruction a executer (ce qui etait en D0 avant le
lancement de notre programme). C'est cette instruction qui permet
a la HP de faire comme si rien n'avait change depuis qu'on l'a pertubee en lanant
notre code.
** Si vous avez compile le code, que vous obtenez Code sur la pile et que vous faites
EVAL pour l'executer, sachez qu'il n'entre pas en compte
dans le decompte des niveaux (dans ce cas, niveau2=niveau1 a l'ecran). Ceux qui se
sont poses la question me comprennent, sinon, tout est OK.
SWAP (echange les contenus des niveaux 1 et 2)
A=DAT1.A ; A : adr de l'objet du niveau 1
D1+5 ; je passe au niveau 2
C=DAT1.A ; C : adr de l'objet du niveau 2
DAT1=A.A ; je copie l'adr de l'objet du niveau 1 sur le
niveau 2
D1-5 ; retourne au niveau 1
DAT1=C.A ; copie l'ancienne adr de l'objet du niveau 2
sur le niveau 1
RPL ; rend la main au systeme
@
** Il serait tres facile de faire un echange des niveaux 1 et 3 par exemple en
remplaant D1+5 par D1+10 et D1-5 par D1-10.
Les autres operations de la pile comme ROT qui ne necessitent pas la creation d'un
nouveau niveau 1 seraient un bon entrainement (envoyez-moi
vos sources et je les inclurerais dans cette partie).
Maintenant, nous allons voir la modification du contenu d'un niveau, puis l'ajout d'un
nouvel objet dans la pile (sans destruction de l'objet du niveau
1).
MODIFICATION DU CONTENU D'UN NIVEAU
Avant tout, vous allez recuperer l'adresse d'un fichier de
votre HP (un fichier texte par
exemple) en le mettant dans la pile et en utilisant la
commande ->A du menu 256 (tapez
256 MENU pour y acceder). Il vous retourne une adresse
que vous allez noter (a va
etre du genre #FFF3C par exemple).
Stockez ce que vous voulez dans le niveau 1 de la pile.
LC FFF3C ; on suppose que le fichier est a cette adresse
en RAM
DAT1=C.A ; je veux que le niveau 1 affiche le contenu de
ce fichier
RPL ; rend la main au systeme
@
AJOUT D'UN NOUVEL OBJET DANS LA PILE
LC FFF3C ; le fichier a mettre au niveau 1 est a cette
adresse (cf exemple precedent)
D1-5 ; je fait pointer le niveau 1 5 quartets sous le niveau
1 actuel (qui devient le niveau
2).
D-1.A ; 5 quartets en moins sont disponibles
DAT1=C.A ; Maintenant que j'ai insere un nouveau
niveau et que j'ai decale les autres,
je peux lui dire ce qu'il doit contenir
RPL
@
** Voila, j'ai fini pour ce qui est de la gestion de la pile mais je voudrais expliquer
quelque chose qui pourrait vous etre utile : Creer un nouveau
fichier texte, lui ajouter des caracteres, et l'afficher au niveau 1 de la pile. Ca peut
vous etre utile pour donner le score a la fin d'un jeu.
Pour cela, il faut reserver en memoire un espace pour y mettre le fichier tout
simplement en utilisant la meme methode que pour reserver un ecran.
Puis on va lui ajouter des caracteres et enfin l'afficher au niveau 1 de la pile.
Un STRING (fichier texte) est constitue :
D'un prologue qui tient sur 5 quartets. Chaque type d'objets (repertoire, programme
etc..) a son propre prologue qui permet a la HP de savoir en
quoi consiste l'objet. Pour un string : 02A2C
De 5 quartets contenant sa taille (prologue non-inclu ; 5 quartets de la taille inclu).
De nombres hexadecimaux. Chaque caractere est code par 2 chiffres. La commande
S->H du menu 256 permet de voir la chaine hexadecimale
correspondant a la chaīne de caracteres.
AFFICHAGE D'UNE CHAINE DE
CARACTERES
SAVE ; sauvegarde des registres (ils vont etre alteres dans
la suite)
LC 00018 ; Taille de la chaine : 5 (prologue) + 5 (taille) +
14 (7 chars * 2) = 18h
RES.STR ; reserve 18 quartets en memoire
LC 02A2C ; prologue d'un string
DAT0=C.A ; je l'ecris sur 5 quartets
LC 00013 ; taille : 5 + 14 = 13h
D0+5 ; saute le prologue
DAT0=C.A ; ecrit la taille
LCASC(7) XsFl0oD ; LCASC permet d'entrer
directement sa chaine de caracteres au
lieu de taper les nombres hexadecimaux. Le chiffre entre
parentheses est le nombre de
caracteres.
D0+5 ; saute la taille
DAT0=C.14 ; ecrit mes caracteres (7 caracteres donc 14
chiffres hexadecimaux)
D0-10 ; retourne au debut de l'objet
D1-5 ; cree un nouveau niveau 1 dans la pile
D-1.A ; 5 quartets en moins de disponibles
A=D0 ; A : adresse du string
DAT1=A.A ; le niveau 1 affiche le string
A=D1 ; sauvegarde de D1 pour l'actualiser apres la
restauration des registres
LOAD ; restaure les sauvegardes (meme D1)
D1=A ; mais dit a la HP d'afficher mon nouveau niveau 1
RPL ; rend la main au systeme
@
** Un truc de derniere minute pour debugger vos programmes : MASD contient une
fonction qui s'appelle DISPKEY. Il suffit de l'integrer dans
votre source et elle vous affichera les valeurs de tous les registres, les variables etc...
Il suffit alors d'appuyer sur une touche pour continuer le
deroulement du programme.
Voili voilou. Bon, il faut qu'on parle d'hommes a hommes. J'ai un grand service a
vous demander. Je m'explique : une societe vous permet de
gagner 50 cents de l'heure en allant sur internet avec un bandeau publicitaire. Ce
n'est pas un "attrape-nigaud" (certaines revues en parlent en bien).
Si vous etes interesses, merci de vous inscrire a partir de cette adresse :
http://www.alladvantage.com/go.asp?refid=HOG-495 . Ca me rajoutera
des dollars :) et peut-etre meme que je pourrais devenir rentier et ecrire des tutoriaux
a plein temps. Merci et a bientļt pour de nouvelles
aventures.
[XsFl0od/P4X] - 3/02/00
Pour tout commentaire etc ... n'hesitez pas a ecrire : p4x@hotmail.com
Downloader la doc
Retour au sommaire Programmation
HOME
|