Bonsoir,
L'instruction LEA est très simple, mais souvent mal comprise, peut-être à cause du fait qu'elle ne fait rien d'exceptionnel alors qu'on attend quelque chose de magique.
Oublions LEA pour l'instant. Tout part du mode d'adressage. De nombreuses instructions vont chercher une donnée en mémoire, c'est le mode mem8 ... mem64 de la doc. MOV, ADD, XOR, et bien d'autres. Prenons MOV, les autres ajoutant simplement une opération au passage. On aura donc à faire:'data' est une adresse en mémoire, et c'est le contenu de cette adresse qui sera transféré dans 'cible'. 'data' sera une expression assez complexe, mais très bien pensée pour pouvoir parcourir des zones, tableaux, etc. Cette expression est de la forme:
REGBASE + REGINDEX x [1, 2, 4 ou 8] + OFFSET (valeur immédiate sur 8, 16 ou 32 bits)
REGBASE et REGINDEX peuvent pratiquement être n'importe quel registre général. Les expressions incomplètes sont autorisées, et même fréquentes. Les noms ne doivent pas tromper, par exemple OFFSET pourra être une adresse de base codée en dur (donnée immédiate) et REGBASE un déplacement par rapport à cette adresse.
Le programmeur saisit son expression dans la syntaxe de son assembleur / compilateur. Il lui indique ainsi quels registres sont REGBASE et REGINDEX, la valeur du multiplicateur, la taille et la valeur de OFFSET. L'assembleur / compilateur va ensuite mouliner tout ça et en intégrer une grande partie dans l'opcode.
A l'exécution, le calcul de l'adresse de la donnée est pratiquement câblé dans le processeur, en tous cas fait très rapidement.
Sans ce système, que ferait le programmeur ? Ben, quelques lignes de code de plus:
1 2 3 4 5
| MOV UN_REG_LIBRE, REGINDEX
SHL UN_REG_LIBRE, (0, 1, 2 ou 3 bits)
ADD UN_REG_LIBRE, REGBASE
ADD UN_REG_LIBRE, OFFSET
MOV 'cible', [UN_REG_LIBRE] |
Revenons à LEA. LEA fait comme MOV, mais simplement ne va pas chercher la donnée, et transfère son adresse dans la cible. Ainsi:pourrait être écrit:
1 2 3 4 5
| MOV UN_REG_LIBRE, REGINDEX
SHL UN_REG_LIBRE, (0, 1, 2 ou 3 bits)
ADD UN_REG_LIBRE, REGBASE
ADD UN_REG_LIBRE, OFFSET
MOV 'cible', UN_REG_LIBRE |
ou même (version2):
1 2 3 4
| MOV 'cible', REGINDEX
SHL 'cible', (0, 1, 2 ou 3 bits)
ADD 'cible', REGBASE
ADD 'cible', OFFSET |
Pourquoi LEA pose-t-elle des problèmes de compréhension ? Peut-être simplement parce que quand on débute on n'a pas à sa disposition d'exemple "pour de vrai" d'expression à peu près complète de 'data'. A partir de là, on ne voit pas immédiatement l'intérêt de LEA, surtout remplacée par la version2. Par exemplec'estet rien d'autre.
Il y a peut-être un autre point qui trouble le débutant. Il faut pour parler de mode d'adressage au moins parcourir une zone. Une variable varréservée par une directive db ou autre est une adresse en valeur immédiate accessible par ADDRESS var par exemple. C'est parfois troublant, parce que cette adresse sera peut-être résolue par l'assembleur, le linker et le chargeur du programme, mais elle sera manipulée comme une valeur immédiate même si sa valeur numérique n'est pas "connue" du programmeur. Ça vaut le cout d'y réfléchir cinq minutes. Et LEA ne peut rien sur ce coup-là, trop de débutants pensent que ça va leur donner par magie l'adresse d'une variable. Si vous êtes en assembleur inline sur du C, l'adresse de var est &var, une constante (de valeur inconnue !!).
Utilisation normale de LEA: vous parcourez des structures de données à partir de deux registres et de l'expression classique de 'data'. A un moment, vous devez utiliser des instructions de type chaîne et vous utiliserez LEA pour initialiser les registres d'index ?SI et ?DI.
Utilisation marrante de LEA: vous utilisez son aspect "calcul rapide de polynome". Ainsi:
LEA eax, [ecx + ecx*4]
vous donnera une multplication par 5. Un petit hack, utilisé par tout le monde y compris les compilateurs C/C++ corrects.
Partager