Incrémentation de 0 à 100 en langage assembleur


7

C'est un peu bizarre, mais je faisais des recherches avec l'assembleur GNU aujourd'hui (je veux au moins pouvoir lire la syntaxe), et j'essayais d'obtenir ce petit exemple inventé de le mien pour travailler. A savoir je veux juste aller de 0 à 100, en imprimant des nombres tout le temps. Donc, quelques minutes plus tard, je viens avec ceci:

# count.s: print the numbers from 0 to 100. 
    .text 
string: .asciz "%d\n" 
    .globl _main 

_main: 
    movl $0, %eax # The starting point/current value. 
    movl $100, %ebx # The ending point. 

_loop: 
    # Display the current value. 
    pushl %eax 
    pushl $string 
    call  _printf 
    addl  $8, %esp 

    # Check against the ending value. 
    cmpl %eax, %ebx 
    je _end 

    # Increment the current value. 
    incl %eax 
    jmp _loop 

_end: 

Tout ce que je reçois de c'est 3 imprimé maintes et maintes fois. Comme je l'ai dit, juste un petit exemple artificiel, alors ne vous inquiétez pas trop à ce sujet, ce n'est pas un problème de vie ou de mort.

(Le formatage est un peu foiré, mais rien de majeur).

+2

'xorl% eax,% eax' est totalement équivalent à' MOVL $ 0,% eax', et prend 3 octets moins. Je dis juste. :) 10 mars. 122012-03-10 00:38:24

12

Vous ne pouvez pas faire confiance à ce qu'une procédure appelée fait à l'un des registres. Poussez les registres sur la pile et retirez-les après avoir appelé printf ou gardez les valeurs d'incrément et de point final en mémoire et lisez-les dans les registres selon vos besoins.

J'espère que les travaux suivants. Je suppose que pushl a un popl équivalent et vous pouvez pousser un couple supplémentaire de nombres sur la pile.

# count.s: print the numbers from 0 to 100. 
    .text 
string: .asciz "%d\n" 
    .globl _main 

_main: 
    movl $0, %eax # The starting point/current value. 
    movl $100,  %ebx # The ending point. 

_loop: 
    # Remember your registers. 
    pushl %eax 
    pushl %ebx 

    # Display the current value. 
    pushl %eax 
    pushl $string 
    call  _printf 
    addl  $8, %esp 

    # reinstate registers. 
    popl %ebx 
    popl %eax 

    # Check against the ending value. 
    cmpl %eax, %ebx 
    je _end 

    # Increment the current value. 
    incl %eax 
    jmp _loop 

_end: 
+1

btw - pusha et popa pousseront tous les registres, et les pop tous .. J'ai trouvé que très pratique dans le passé 18 nov.. 082008-11-18 17:08:06

  0

De notes ci-dessous. Il est important de noter que ... "@seanyboy, votre solution est trop lourde, il suffit de remplacer eax par un autre registre comme ecx." 13 août. 092009-08-13 07:40:39

  0

@warren - 'pusha' et' popa' ne sont pas supportés en mode 64 bits. 10 mars. 122012-03-10 00:37:57

  0

@Daniel Kozar - n'a jamais travaillé en mode 64 bits, mais c'est bon à savoir :) 12 mars. 122012-03-12 13:52:36


6

Je ne connais pas trop _printf, mais est-ce que ça pourrait modifier eax? Printf devrait retourner le nombre de caractères imprimés, qui dans ce cas est deux: '0' et '\ n'. Je pense que ça retourne ça dans eax, et quand on l'incrémente, on obtient 3, ce qui est ce que vous allez imprimer. Il vaudrait peut-être mieux utiliser un registre différent pour le compteur.


1

Nathan est sur la bonne voie. Vous ne pouvez pas supposer que les valeurs de registre ne seront pas modifiées après l'appel d'un sous-programme. En fait, il est préférable de supposer qu'ils seront modifiés, sinon le sous-programme ne serait pas en mesure de faire son travail (au moins pour les architectures à faible nombre de registres comme x86). Si vous souhaitez conserver une valeur, vous devez la stocker en mémoire (par exemple, la placer sur la pile et suivre son emplacement).

Vous devrez faire la même chose pour toute autre variable que vous avez. L'utilisation de registres pour stocker les variables locales est plutôt réservée aux architectures avec suffisamment de registres pour les supporter (eg EPIC, amd64, etc.)


3

Les fonctions bien écrites vont généralement pousser tous les registres sur la pile et les faire sauter quand ils ' re fait pour qu'ils restent inchangés pendant la fonction. L'exception serait eax qui contient la valeur de retour. Les fonctions de bibliothèque comme printf sont très probablement écrit de cette façon, donc je ne ferais pas comme le suggère Wedge:

Vous aurez besoin de faire la même chose pour toute autre variable que vous avez. L'utilisation des registres pour stocker des variables locales est à peu près réservé aux architectures avec des registres assez pour le soutenir (par exemple EPIC, amd64, etc.)

En fait, d'après ce que je sais, compilateurs compilent généralement des fonctions de cette façon de traiter exactement avec ce problème.

@seanyboy, votre solution est trop lourde. Tout ce qui est nécessaire est de remplacer eax avec un autre registre comme ecx.


-1

Vous pouvez le réécrire de sorte que vous utilisiez des registres qui ne sont pas supposés changer, par exemple %ebp. Assurez-vous simplement de les repousser sur la pile au début et de les sortir à la fin de votre routine.

# count.s: print the numbers from 0 to 100. 
    .text 
string: .asciz "%d\n" 
    .globl _main 

_main: 
    push %ecx 
    push %ebp 
    movl $0, %ecx # The starting point/current value. 
    movl $100,  %ebp # The ending point. 

_loop: 
    # Display the current value. 
    pushl %ecx 
    pushl $string 
    call  _printf 
    addl  $8, %esp 

    # Check against the ending value. 
    cmpl %ecx, %ebp 
    je _end 

    # Increment the current value. 
    incl %ecx 
    jmp _loop 

_end: 
    pop  %ebp 
    pop  %ecx 

5

Vous pouvez utiliser en toute sécurité les registres qui sont « sauvés-callee » sans avoir à les sauver vous-même. Sur x86, ce sont edi, esi et ebx; d'autres architectures en ont plus.

sont consignées au dossier dans les références ABI: http://math-atlas.sourceforge.net/devel/assembly/