2011-05-02 36 views
13

Estaba leyendo The Art of Assembly Language (Randall Hyde, link to Amazon) y probé una aplicación de consola en ese libro. Era un programa que creaba una nueva consola para sí mismo utilizando las funciones API de Win32. El programa contiene un procedimiento llamado LENSTR, que almacena la longitud de la cadena en el registro EBP. El código para esta función es la siguiente:¿Entrar y ABANDONAR en el montaje?

LENSTR PROC 
ENTER 0, 0 
PUSH EAX 
;---------------------- 
CLD 
MOV EDI, DWORD PTR [EBP+08H] 
MOV EBX, EDI 
MOV ECX, 100 ; Limit the string length 
XOR AL, AL 
REPNE SCASB ; Find the 0 character 
SUB EDI, EBX ; String length including 0 
MOV EBX, EDI 

DEC EBX 
;---------------------- 
POP EAX 
LEAVE 
RET 4 
LENSTR ENDP 

Podría explicar el uso de los comandos enter y leave aquí?

Respuesta

12

Esta es la configuración del marco de pila (registro de activación) para la función. Internamente lo que normalmente se ve algo como esto:

push(ebp);   // Save a copy of the old EBP value 

mov(esp, ebp);  // Get ptr to base of activation record into EBP 

sub(NumVars, esp); // Allocate storage for local variables. 

Luego, cuando el marco de la pila ha de ser destruida de nuevo, lo que tiene que hacer algo a lo largo de las siguientes líneas:

mov(ebp, esp); // Deallocate locals and clean up stack. 

    pop(ebp);   // Restore pointer to caller's activation record. 

    ret();    // Return to the caller. 

Here es una mejor explicación de la misma usando HLA. Aunque está bien explicado en el libro que estás leyendo, ya que también tengo ese libro, y he leído la sección que lo explica.

+3

no el hla ...; ( – BlackBear

+0

[Esto] (http://x86.renejeschke.de/html/file_module_x86_id_154.html) y [este] (http://x86.renejeschke.de/html/file_module_x86_id_78.html) puede ser más correcto. – lzutao

0

Ingrese y salga solo configure el marco de la pila. Por lo general, los compiladores generan código que manipula directamente los punteros del marco de pila ya que enter y leave no son exactamente rápidos en relación con mov/sub (solían serlo, en los 286 días siguientes :-)).

+0

No, nunca fueron más rápidos, simplemente más compactos y versátiles (admiten funciones anidadas al estilo Pascal). –

+2

Curiosamente, MASM32 (cuando se emplean variables locales) hace la parte de entrada con PUSH/MOV/ADD, pero los destruye con LEAVE ... –

+5

derecha: 'enter' tiene una sobrecarga para admitir funciones anidadas, incluso cuando no se usa ellos. 'leave' no agrega overhead, y es un poco más pequeño que el mov/pop equivalente. –

34

Enter crea un marco de pila, y leave destruye un marco de pila. Con los parámetros en la 0,0enter, son básicamente equivalente a:

; enter 
push ebp 
mov ebp, esp 

; leave 
mov esp, ebp 
pop ebp 

Aunque no se utiliza en el código que envió, enter qué apoyan haciendo un poco más que la simple combinación de empuje/mov se muestra arriba. El primer parámetro a enter especifica una cantidad de espacio para asignar para las variables locales. Por ejemplo, enter 5, 0 es más o menos equivalente a:

push ebp 
mov ebp, esp 
sub esp, 5 

Enter también es compatible con lenguajes como Pascal que pueden utilizar las funciones anidadas/procedimientos:

procedure X; 
    procedure Y; 
    begin 
     { ... } 
    end 
begin 
    { ... } 
end 

En un caso como este, Y tiene acceso no sólo a su propias variables locales, pero también a todas las variables locales a X. Estos pueden anidarse a profundidad arbitraria, por lo que podría tener un Z dentro de Y que tuviera acceso a sus propias variables locales, y las variables de Y y las variables de X. El segundo parámetro a enter especifica la profundidad de anidación, por lo X usaría enter Sx, 0, Y usaría enter Sy, 1 y Z usaría enter Sz, 2 (donde Sx, Sy y Sz significar el tamaño de las variables locales para X, Y y Z respectivamente).

Esto crearía una cadena de marcos de pila para proporcionar acceso Z a las variables locales a Y y X, y así sucesivamente. Esto se convierte en bastante no trivial si las funciones son recursivas, por lo que una invocación de Z no puede simplemente subir la pila a los dos marcos de pila más recientes; necesita saltarse los fotogramas de pila de invocaciones previas de sí mismo, e ir directamente volver a apilar marcos para la función/procedimiento principal léxica, que es diferente de su llamador en el caso de recursión.

Esta complejidad también explica por qué C y C++ prohíben las funciones anidadas. Dada la presencia de entrar/salir, son bastante fáciles de admitir en los procesadores Intel, pero pueden ser considerablemente más difíciles en muchos otros procesadores que carecen de dicho soporte directo.

Esto también al menos ayuda a explicar otro ...función de enter --para el caso trivial que se usa aquí (es decir, enter 0, 0) es bastante más lento que el equivalente usando push/mov.

+0

así que son ellos macros ..? – BlackBear

+3

@BlackBear: No, son instrucciones, pero son como una especie de "taquigrafía": puedes lograr lo mismo sin ellas. –

+0

ok lo tengo :) (límite de caracteres) – BlackBear