Page 3 sur 6

Re: [FR] Langage assembleur sur ez80 - Tutoriel

Message non luPosté: 19 Mai 2017, 21:48
de Epharius
Yo les pitchouns, j'avais la flemme de programmer, alors du coup devinez ce que j'ai fait... j'ai écrit un tutoriel sur la programmation :troll:
Bref, j'ai fait la partie "Ports" du tuto. Si vous pensez que j'ai oublié quelque chose n'hésitez pas à me le signaler. Je n'ai pas parlé en détail de l'utilisation de l'écran, je pense que ce serait mieux de consacrer un chapitre au double-buffering et au mode 8bits avec tout ce que ça implique (palette, etc...).
(il peut y avoir des petite fautes même si normalement j'ai vérifié, j'étais un peu fatigué quand je l'ai rédigé).
Voilou, deux heures toute de même.


Les ports

Les ports sont très importants en assembleur : ils permettent entre autre, de gérer la pression des touches sur le clavier, configurer l'écran, communiquer par USB avec une autre calculatrice... c'est une notion incontournable de l'assembleur ! Ces derniers servent donc à faire le lien entre les composants de la calculatrice et le programmeur. Concrètement, on va, par exemple, pouvoir dire à l'horloge de la calculatrice de changer l'heure, à l'écran de s'éteindre, au port USB d'envoyer quelque chose... tout cela grâce aux ports !
Cependant, un élément va compliquer leur utilisation sur les calculatrice eZ80. En effet, les ports des calculatrices eZ80 sont dits "memory-mapped". Pour faire court, sur les calculatrices Z80 (Ti-83+/Ti-84+...), les ports étaient accessibles directement et normalement grâce aux instructions IN a,(port) pour récupérer la valeur d'un port, et OUT (port),a pour la modifier. Néanmoins, sur Ti-83 Premium CE l'utilisation des instructions IN et OUT est prohibée et tenter des les utiliser ne causera soit rien, soit un RAM CLEARED. Ces restrictions sont supposées protéger et donner un sens au mode examen qui aurait pu être facilement reproduit et contourné dans le cas où un programme avait pu utiliser le port de la LED du mode examen afin d'en imiter le fonctionnement.
Heureusement, il existe quand même une solution pour accéder aux ports que nous permet d'utiliser Texas Instrument. En effet, certains ports sont "mappés en mémoire" c'est-à-dire qu'ils vont pouvoir être modifiés seulement en modifiant une adresse mémoire. C'est le cas notamment pour le clavier, l'écran, les timers, l'USB... mais bien entendu pas la LED du mode examen !

Les ports sont mappés à partir de l'adresse mémoire $E00000. Par exemple, le port du clavier est mappé en mémoire à partir de l'adresse $F50000. Afin de connaître tous les ports qui peuvent être utilisés, je vous conseille de suivre ce lien vers le wikiti. Vous y trouverez tous les ports qui ont été découverts jusqu'à maintenant. Pour savoir si un port est "memory-mapped", il suffit de voir si le champ "Memory-mapped address:" existe en haut de la page décrivant le port, dans la section "Synopsis".

Afin de mieux comprendre comment cela fonctionne, nous allons aborder un exemple simple, récupérer les minutes de l'heure actuelle :
Code: Tout sélectionner
ld hl,($F30004) ; On récupère les minutes.
call _dispHL ; On affiche HL qui contient le nombre de secondes

Expliquons un petit peu. Nous voyons à cette adresse que le port "Real-Time Clock" est le port numéro $8000 et est mappé à l'adresse $F30000 en mémoire. De plus, les minutes sont stockées du port $8004 à $8007 (toutefois les minutes n'allant que de 0 à 59, seulement un octet sera utilisé sur les quatre dédiés aux minutes). L'adresse à récupérer est donc $F30004. Ensuite, on affiche les minutes avec la fonction système _dispHL qui, comme son nom le laisse présager, affiche la valeur du registre HL à l'écran.

Nous allons, en dernier lieu et partiellement, voir l'utilisation des ports les plus importants, c'est-à-dire le port du clavier et de l'écran.

Utilisation du clavier
Le clavier a une utilisation spéciale et un peu plus complexe malgré sa fréquente utilisation dans les programmes. Il nécessite quelques connaissances.
Le port du clavier a pour numéro $A000 et est mappé à l'adresse $F50000. Avant de détecter la pression des touches, nous avons besoin d'initialiser le clavier. Pour cela nous allons devoir d'abord choisir un mode pour la clavier. Il en existe quatre mais seulement trois peuvent éventuellement nous intéresser :
  • Le mode "repos" : le clavier ne fait rien et ne scanne rien
  • Le mode "unique scan" : le clavier est scanné une fois, puis il revient au mode "repos"
  • Le mode "scan continu" : le clavier est scanné encore et encore indéfiniment...
Le mode qui va être utilisé le plus souvent est le deuxième mode, l'unique scan. Le code minimal pour lire le clavier est par conséquent :
Code: Tout sélectionner
   di ; On désactive les interruptions (obligatoire)
   ld hl,$F50000
   ld (hl),2 ; On met le clavier en mode "un seul scan"
   xor a,a
scan_wait:
   cp a,(hl) ; On attend que le clavier soit retourné en mode repos ce qui voudra dire que la détection des touches est terminée
   jr nz,scan_wait

   ; Quand la détection est terminée, on peut commencer à lire les ports
   ld a,($F50012)
   cp a,32 ; si la touche 2NDE a été pressée...
   call z,routine
   ei ; On peut réactiver les interruptions

Ici, nous avons pris l'exemple de la touche 2NDE dont l'état (enfoncée ou non) se situe à l'adresse $F50012 sur le bit 5. Pour connaître la correspondance des touches, je vous conseille de jeter un œil au tableau sur cette page. Comme on peut le voir on fait une pause (scan_wait) avant de lire le clavier, sans elle la lecture serait erronée donc ne l'oubliez pas !

Utilisation de l'écran
Le port de l'écran nécessite un chapitre entier mais nous allons aborder brièvement les bases de son utilisation. L'écran est par défaut en 16bits, c'est à dire qu'il y a 2^16=65536 couleurs disponibles par pixel. Chaque pixel possède une nuance de rouge, de vert et de bleu, les trois couleurs primaires en informatique. Par défaut (et je dis par défaut car il est possible de mettre l'écran en mode 8bits) chaque pixels possède donc 16 bits. Les cinq premiers bits codent pour le rouge, les six suivants pour le vert, et enfin les cinq derniers pour le bleu. On remarque que nous avons :
%0011001110010110

Maintenant, l'objectif est de savoir comment modifier la valeur d'un pixel à l'écran. Notre Ti-83 Premium CE possède une vRam autrement dit une Vidéo RAM qui est un espace mémoire destiné à faire l'intermédiaire entre la mémoire et l'écran. Grosso modo, il suffit tout simplement de modifier la vRam pour que les pixels changent instantanément de couleur sur l'écran.
Nous savons que l'écran fait 320*240 pixels, avec 16 bits par pixel (=2 octets). La vRam fait donc logiquement 320*240*2, soit 153,6Ko, ce qui est énorme quand on sait que la RAM disponible à l'utilisateur fait autant !
La vRam est située à partir de l'adresse $D40000 sur 153600 octets. Prenons un exemple tout simple, on veut changer le 187ème pixel pour qu'il affiche du rose :
Code: Tout sélectionner
ld hl,%1111100000010000
ld (vRam+(186*2)),hl

À noter que comme le registre HL fait 24 bits, les 16 bits du pixel 187 seront bien modifiés, toutefois la moitié du pixel suivant (8 bits), le 188, sera mise à 0.
Il est alors possible de modifier une grand partie de l'écran grâce à un LDIR, et avec un peu de réflexion, d'afficher des sprites et des images à l'écran.

En rappel, n'oubliez pas d'aller voir le wikiti pour connaître tous les ports et leur adresse !
Voir la liste des ports connus à ce jour.

Re: [FR] Langage assembleur sur ez80 - Tutoriel

Message non luPosté: 25 Mai 2017, 07:51
de MMBC
Hello.

Très bon tutoriel ! :D
Cependant il sera aimable de rajouter comment faire les conditions et les boucles....

Aussi est il possible de faire par exemple
Code: Tout sélectionner
    call foo
    ret

foo:
    call _rgebf
    ld hl, $bfdf

Re: [FR] Langage assembleur sur ez80 - Tutoriel

Message non luPosté: 25 Mai 2017, 21:14
de Epharius
Non à la fin de ton call tu dois avoir un ret pour dire que c'était la fin de ta routine (ta fonction un peu comme le Return en C++)

Re: [FR] Langage assembleur sur ez80 - Tutoriel

Message non luPosté: 27 Mai 2017, 01:20
de Dark coco
Epharius super que tu as eu le temps de faire ça et en plus j'ai appris des infos sinon je suis pas si c'est moi mais la mis en page est mieux qu'avant enfin bref si vous avez une partie que vous voulez que je fasse je suis preneur ;)
Autre truc que je viens de voir il y a une petite erreur lors que l'explication des registres "IEF2 ADL est un bit indiquant que les interruptions de niveau 2 sont actives" ça serait plutôt IEF2 à la place ADL voilà :) .

Re: [FR] Langage assembleur sur ez80 - Tutoriel

Message non luPosté: 07 Juil 2017, 09:20
de MMBC
Bon, sinon quelqu'un sait comment faire du double buffering ? En 8bpp on peut utiliser le reste de la vRam comme buffer mais après, il faut echanger les pixels un par un avec une routine (très) lente où y'a une technique spéciale pour inverser l'écran et le buffer (genre changer une valeur à une adresse spécifique) ?

Re: [FR] Langage assembleur sur ez80 - Tutoriel

Message non luPosté: 07 Juil 2017, 09:26
de Adriweb

Re: [FR] Langage assembleur sur ez80 - Tutoriel

Message non luPosté: 07 Juil 2017, 12:44
de Epharius
Tu sais surement le principe de la chose mais j'explique pour ceux qui voudraient savoir ce que c'est :

Le double-buffering est donc une technique qui consiste à avoir deux espaces mémoire (buffer) de 320*240o chacun (en partant du principe que nous sommes en 8bpp) : l'un va être affiché à l'écran, pendant que l'autre va être modifié par le programme, le but étant d'éviter de pouvoir voir l'écran redessiné chaque frame. Quand on a fini de dessiner, on affiche le buffer où l'on a dessiné la frame suivante, et les deux espaces échangent leur rôle : le premier va devenir l'espace où on va dessiner la nouvelle frame et le deuxième va être affiché à l'écran.

Pour faire du double-buffering, la seule chose que tu as besoin de retenir c'est :
Code: Tout sélectionner
   ld hl,vRam
   ld (mpLcdBase),hl

En gros, mpLcdBase est l'adresse du port mappé en mémoire qui gère la position dans la mémoire de l'endroit que le LCD va afficher. Par défaut, sa valeur est à $D40000 ce qui correspond au début de la vRam.
Dans notre cas, les deux buffers sont à $D40000 et à $D40000+320*240.
Il suffit donc d'alterner entre ces deux buffers à chaque tour de boucle par exemple avec le code suivant :
Code: Tout sélectionner
; Swaping Vram Buffers (double-Buffering)
   ld hl,vRam
   ld bc,(mpLcdBase)
   or a
   sbc hl,bc 
   add hl,bc              ; Is the current buffer the screen buffer?
   jr nz,notCurrBuf
   ld hl,vram+(lcdWidth*lcdHeight)
notCurrBuf:
   ld (currentDrawLoc),bc ; Set the new buffer location to the old screen pointer
   ld bc,mpLcdIcr
   ld a,(bc)
   or a,4 ; bitLcdLnBuim
   ld (bc),a
   ld (mpLcdBase),hl
waitForSync:            ; Wait fot the LCD to be ready to update
   ld a,(mpLcdRis)
   and a,4 ; bitLcdLnBuim
   jr z,waitForSync

currentDrawLoc:
   .dl vram+(lcdWidth*lcdHeight)

Bon, c'est un code que j'ai trouvé sur le site qui a fait quelques tuto sur l'asm eZ80 (mais honte à moi je n'arrive plus à retrouver le lien), il est assez simple à comprendre : premièrement on change currentDrawLoc dont la valeur est l'adresse du buffer sur lequel il faut dessiner (en fait c'est juste l'opposé de mpLcdBase), puis on change le buffer qui va être affiché dans mpLcdBase, enfin on attend que la modif soit terminée avec une petite pause.

Ce qui change au niveau du programme, c'est qu'au lieu de mettre ld hl,vRam+45 pour écrire sur le pixel n°46, il faudra faire ld hl,(currentDrawLoc) / ld de,45 / add hl,de ce qui prend plus de temps malheureusement.
De plus, les changements que tu fais sur un buffer, tu devras les refaire par la suite sur le deuxième buffer ce qui nécessite de penser à la méthode qu'on va utiliser avant de faire son programme : soit on redessine tout à chaque fois et il n'y a pas de problème, soit on fait des changements "relatifs" (scrolling avec un LDIR, etc) et dans ce cas il faut s'organiser.

Si tu as des questions n'hésite pas, j'ai un peu abrégé parfois :p

Re: [FR] Langage assembleur sur ez80 - Tutoriel

Message non luPosté: 05 Aoû 2018, 15:40
de clifward
Moi j'ai une question... À quand un prochain épisode ? :troll:

Re: [FR] Langage assembleur sur ez80 - Tutoriel

Message non luPosté: 10 Aoû 2018, 12:14
de Epharius
Jsais pas tu veux savoir quoi ? :P

Re: [FR] Langage assembleur sur ez80 - Tutoriel

Message non luPosté: 13 Aoû 2018, 12:40
de clifward
Un tuto sur comment programmer son geometry dash :troll: