EN GUISE D’INTRODUCTION
Fasciné par certaines utilisations détournées de la pile de matériel du 6502, telles que les programmes de protections, j’ai voulu en apprendre davantage sur son fonctionnement.
Je vous propose donc ici la traduction partielle d’un sujet dont la version originale se trouve à l’adresse suivante : http://wilsonminesco.com/stacks/
QUELQUES DEFINITIONS
Le type de pile dont il est question ici est une partie de la mémoire vive utilisée pour le stockage temporaire de données, qu’il s’agisse de variables ou d’adresses, avec comme mode d’accès, dernier entré, premier sorti (Last In First Out).
On compare généralement ce type de pile à une pile d’assiettes dans un meuble à ressorts que l’on peut trouver à côté d’un buffet restaurant.

La quantité de compression ou d’étirement des ressorts est proportionnelle au poids des assiettes que le meuble contient, de sorte que tant que le meuble n’est pas rempli complètement, un employé peut ajouter n’importe quel nombre d’assiettes récemment lavées et les assiettes seront toujours dans la même position au sommet, prêtes pour le prochain client.
D’habitude, ni l’employé ni le client n’ont besoin de savoir combien d’assiettes chaudes sont stockées en dessous.
On peut aussi prendre l’exemple d’une tige métallique avec un tas de papiers embrochés. Vous pouvez voir les documents qui sont sous le sommet, et vous pourriez même lire ou écrire sur eux dans une certaine mesure sans retirer ceux qui sont au-dessus, mais le retrait ou l’ajout réel de documents a toujours lieu par le sommet.

Le contraire d’une pile (ou mémoire LIFO) serait une mémoire de type premier entré, premier sorti (FIFO) ou file d’attente, qui peut dans ce cas être comparée au convoyeur d’une machine à rayons X dans un aéroport. Sur cette machine, les affaires passent d’un côté à un autre des rayons X, dans le même ordre qu’elles sont entrées.
Contrairement à la pile d’assiettes, une pile de nombres dans la mémoire de l’ordinateur ne se déplace pas de haut en bas quand quelque chose est ajouté ou enlevé de la pile. Il serait en effet contre-productif de déplacer toutes ces données vers le haut ou vers le bas comme pour les assiettes, alors qu’il est plus simple d’utiliser un pointeur réglable pour savoir où se trouve le haut ou, en fait, dans le cas de la pile matériel du 6502, quelle est l’adresse RAM qui est la prochaine disponible pour mettre quelque chose sur la pile.
Ce pointeur est le registre S (Stack pointer) du 6502.
Dès lors, si l’analogie du
distributeur d’assiettes ne tient plus, pourquoi l’évoquer ?
En fait, ce qu’il convient surtout de retenir ici, c’est qu’habituellement, on ne s’occupe que de ce qui se trouve au-dessus de la pile, sans se soucier de la profondeur de celle-ci, comme un navire en pleine mer qui flotte de la même manière que la profondeur de l’eau soit de 100 mètres ou de 1000 mètres. Tant qu’une action ou une routine dispose des éléments de la pile dont elle a besoin, peu importe combien d’autres éléments sont plus bas dans la pile, en attendant d’être utilisés par d’autres routines ou parties d’une routine.
La pile du processeur 6502 permet de stocker des adresses pour des sous-routines et des interruptions (moins fréquentes que pour des sous-routines) ainsi que des variables.
C’est dans la première page du 6502 ($0100-$01FF) que réside la pile de matériel. Les tests montrent d’ailleurs que contrairement à une légende urbaine, cette zone mémoire est beaucoup plus grande que ce dont le programmeur a réellement besoin.
Le pointeur de la pile de matériel est le registre S qui n’a que 8 bits de largeur; le 6502 ajustant automatiquement le numéro de page à $01 pour tous les accès à la pile. La pile de matériel en page 1 se développe vers le bas, et non vers le haut. Toute routine de réinitialisation doit donc initialiser le pointeur de pile au sommet de l’espace disponible. Il s’agit habituellement de l’adresse $01FF. Cette routine s’écrira simplement :
LDX #$FF ; Initialise X au sommet de la pileTXS ; Transfère dans le pointeur de pile
Puisque la pile se développe vers le bas, le pointeur de pile se décrémente après chaque octet poussé sur la pile, et s’incrémente avant que chaque octet ne soit tiré hors de la pile.
ADRESSES DE SOUS-ROUTINE ET NIDIFICATION
Une sous-routine est une partie de votre programme dont vous avez besoin à de multiples reprises et à différents endroits. Au lieu d’avoir une copie de cette routine à chaque endroit où vous en avez besoin, vous l’écrivez une fois pour toutes et vous l’appelez depuis la portion de programme requise. Le compteur de programme se rend à cette sous-routine puis doit pointer de nouveau sur la routine d’appel pour reprendre l’exécution. Pour savoir où revenir, le compteur de programme (donc l’adresse de retour) a au préalable été sauvée sur la pile.
C’est ce que réalise l’instruction JSR : « Jump, Saving Return address » (JSR est habituellement traduit par « Jump to SubRoutine », ce qui fonctionne tout aussi bien mais n’est pas tout à fait exact.) Supposons maintenant que cette routine doive en appeler d’autres. Comme chaque JSR met l’adresse de retour sur la pile, les adresses de retour nécessaires en premier lieu seront les dernières stockées sur la pile.
La nidification est ce qui convient de mieux pour ce genre de situation. On appelle nidification (ou imbrication), le fait qu’une sous-routine en appelle une autre, qui à son tour en appelle une autre, et ainsi de suite. Si vous n’aviez pas besoin de la nidification, vous pourriez inscrire l’adresse de retour dans une variable ou un registre; mais vous vous apercevez que dès la première nidification, la variable serait écrasée et la première adresse de retour serait perdue.
C’est là que la pile entre en jeu.
Considérons à présent la nidification des sous-routines ci-dessous.

Dans ce scénario, la pile contiendra les nombres hexadécimaux suivants aux points (a), (b) et (c) ; (sans compter ce qui aurait déjà pu être sur la pile avant (a).
| (a) | (b) | (c) | |
| Sommet pile (*) | $1915 | $2632 | $2F43 |
| $1915 | $2632 | ||
| $1915 |
(*) Top Of Stack (TOS)
Ce tableau semble comporter des erreurs. En effet, au point (a) l’adresse de la prochaine instruction est censée être $1916, et non $1915.
En fait, l’instruction RTS (Return From subroutine) incrémentera l’adresse de retour et l’exécution du programme reprendra bien à $1916 (ce qui n’est pas le cas pour RTI, ReTurn-from-Interrupt, et fera l’objet d’un autre article).
Puisque le bus de données et la mémoire du 6502 sont adressés sur 8 bits, chaque adresse de retour évoquée ci-dessus nécessite deux emplacements dans la pile. Lorsque JSR pousse l’adresse de retour (moins 1) sur la pile, il pousse d’abord l’octet haut, puis l’octet bas. N’oubliez pas que la pile grandit, et que le pointeur de la pile se décrémente lorsque les octets sont poussés sur la pile, de sorte que l’ordre d’octet résultant est l’octet de poids faible dans l’adresse mémoire la plus basse, comme le sont les autres adresses dans le 6502.
Supposons par exemple que la valeur du pointeur de pile (registre S) était $EF avant que le code n’entre dans le diagramme de pile ci-dessus. Cela donnerait une occupation de l’espace mémoire de la pile selon le tableau suivant (ne sachant pas ce qui figurait de $EF à $FF, ces octets ne sont pas cartographiés).

Lorsque vous quittez les sous routines et que vous revenez à la routine principale, voici à quoi ressemble la mémoire de la pile.

Les nombres entre parenthèses sont toujours là, mais cette partie de la mémoire de la pile est libérée. Retirer quelque chose de la pile signifie simplement que le processeur en lit le contenu et incrémente ensuite le pointeur de la pile. Pouvez-vous utiliser ces chiffres, puisqu’ils sont toujours là ? Si vous avez des interruptions, la réponse est non, car une interruption pourrait survenir à tout moment et les écraser. Cela violerait le protocole LIFO et le processus des interruptions nécessite que ce protocole soit respecté.
À suivre.
