Assembly Language Procedures and the Stack Stack A
Assembly Language Procedures and the Stack
Stack • A stack is a last-in–first-out (LIFO) data structure. • Insert and delete operations are referred to as push and pop operations, respectively.
Pentium’s Stack • The stack grows toward lower memory addresses. • Top-of-stack (TOS) always points to the last inserted item (points to the lower byte of the last word inserted into the stack). • • The registers SS and ESP are used to implement the Pentium stack. SS: ESP points to the top-of-stack (the last item inserted). – SS register pointing to the beginning of the stack segment. – ESP register giving the offset value of the last item
Pentium’s Stack
Basic Instructions The push and pop instruction operate on word or doubleword data items. Syntax: push source pop destination • The operand can be a 16 - or 32 -bit – general-purpose register, segment register – A word or doubleword in memory. – For push instruction the course can be an immediate of size 8, 16, or 32 bits. operations. push 21 ABH push 7 FBD 329 AH pop EBX
Stack Instruction Summary
Other Stack Instructions pushfd (push 32 -bit flags) popfd (pop 32 -bit flags) pusha and popa instructions to save and restore the eight general-purpose registers. pushad saves the 32 -bit general-purpose registers EAX, ECX, EDX, EBX, ESP, EBP, ESI, and EDI. These registers are pushed in the order specified. popad restores these registers except that it will not copy the ESP value The corresponding instructions for the 16 -bit registers are pushaw and popaw.
Temporary Storage of Data mov EAX, value 1 mov EBX, value 2 mov value 1, EBX mov value 2, EAX . . . ; Save EAX and EBX registers on the stack push EAX push EBX ; EAX and EBX registers can now be used mov EAX, value 1 mov EBX, value 2 mov value 1, EBX mov value 2, EAX ; restore EAX and EBX registers from the stack pop EBX pop EAX. . .
Example Illustration
Procedures Sub-program (routine) is a logically self-contained unit of code designed to perform a particular task. – Functions Receives a list of arguments and performs a computation based on the arguments passed onto it and returns a single value. Procedures Receives a list of arguments and works similar to functions, but does not return a value. call and ret (return) instructions are used to handle sub-programs. call routine_name ; routine_name is the name of the routine to be called Ret return to the instruction following the last call.
Transfer of Control • The offset value provide by the call instruction is relative displacement in bytes from the instruction following the call instruction. • The processor adds the 32 -bit relative displacement found in the call instruction to the contents of the EIP register. • The Pentium scheme ESP = ESP − 4 ; push return address onto the stack SS: ESP = EIP + relative displacement ; update EIP to point to the procedure
Transfer of Control-Example
Transfer of Control-Example • E 816000000 H is the machine language encoding of the call instruction 002 call instruction – E 8 H is the opcode – 00000016 H (32 bit signed) is a relative displacement 0000001 DH − 00000007 H = 00000016 H (in little-endian order) 002 call instruction – The displacement is 0000001 DH − 0000002 DH = FFFFFFF 0 H. – A negative numbers corresponds to − 10 H (i. e. , − 16 D
The ret Instruction • ret [integer] ; the integer is optional • The ret (return) instruction is used to transfer control from the called procedure to the calling procedure. • The Pentium Scheme EIP = SS: ESP ; pop return address at TOS into IP ESP = ESP + 4 ; update TOS by adding 4 to ESP
Parameter Passing • Parameters could be passed from the caller routine to the call routine in two methods – The register method uses general-purpose registers to pass parameters. – The stack method passes the parameters in the stack. • There are two types of parameter passing mechanisms – call-by-value - The called function is provided only the current value of the arguments for its use. • The values of these actual parameters are not changed in the called function call-by-reference. – call-by-value - The called function actually receives the addresses of the parameters from the calling function. • The function can change the contents of these
Register Method • The calling routine places the necessary parameters in the general purpose registers before call the routine. • The called routine has access to these register and their values. • To pass parameters by value, the current value of these parameters are placed in the registers. • To pass parameters by reference, the address of these parameters are placed in the registers.
call-by-value using registers. Example %include "io. mac". DATA prompt_msg 1 db "Please input the first number: ", 0 prompt_msg 2 db "Please input the second number: ", 0 sum_msg db "The sum is ", 0. CODE. STARTUP Put. Str prompt_msg 1 Get. Int CX Put. Str prompt_msg 2 Get. Int DX call sum Put. Str sum_msg Put. Int AX newline done: . EXIT ; request first number ; CX = first number ; request second number ; DX = second number ; returns sum in AX ; display sum
call-by-value using registers. Example ; -----------------------------; Procedure sum receives two integers in CX and DX. ; The sum of the two integers is returned in AX. ; -----------------------------sum: mov AX, CX ; sum = first number add AX, DX ; sum = sum + second number ret
call-by-reference using registers. Example %include "io. mac" BUF_LEN EQU 41 ; string buffer length. DATA prompt_msg db "Please input a string: ", 0 length_msg db "The string length is ", 0. UDATA string resb BUF_LEN ; input string < BUF_LEN chars. . CODE. STARTUP Put. Str prompt_msg ; request string input Get. Str string, BUF_LEN ; read string from keyboard mov EBX, string ; EBX = string address call str_len ; returns string length in AX Put. Str length_msg ; display string length Put. Int AX nwwline done: . EXIT
call-by-reference using registers. Example ; -----------------------------; Procedure str_len receives a pointer to a string in EBX. ; String length is returned in AX. ; -----------------------------str_len: push EBX sub AX, AX ; string length = 0 repeat: cmp byte [EBX], 0 ; compare with NULL char. je str_len_done ; if NULL we are done inc AX ; else, increment string length inc EBX ; point EBX to the next char. jmp repeat ; and repeat the process str_len_done: pop EBX ret
Passing Parameters Via Registers • Advantages – The register method is convenient and easier for passing a small number of parameters. – This method is also faster because all the parameters are available in registers. • Disadvantages – Only a few parameters can be passed by using registers, as there is a limited number of general-purpose registers available. – The general-purpose registers are often used by the calling procedure for some other purpose. Thus, it is necessary to temporarily save their contents before calling a procedure, and restore them after returning.
Passing Parameters using the Stack • The parameters are pushed onto the stack before the procedure is called. Example push param 1 push param 2 call sum • How to access these parameters • pop EAX pop BX pop CX And then push EAX • This approach is problematic, as it require using more registers and involves unnecessary push and pop operations.
Accessing Parameters • To simplify accessing the parameters, it is better to leave them on the stack and read them off the stack as needed. • The stack is a sequence of memory locations – ESP + 4 points to param 2 – ESP + 6 points to param 1 – Ex. mov BX, [ESP+4] • However, the stack pointer is updated by the push and pop instructions, which changes the relative • We can use the EBP register instead of ESP to specify an offset into the stack segment. mov EBP, ESP mov AX, [EBP+4]
Accessing Parameters Param 1 Param 2 Save the EBP register and set to ESP push EBP mov EBP, ESP … Before returning restore the EBP register pop EBP Param 1 Param 2
Organizing the Stack • • After completing the routine (procedure) the memory of the stack occupied by the parameters are no longer useful. To free this bytes, one could increment ESP accordingly after the call push param 1 push param 2 call sum cdd ESP, 4 • Or before returning sum: . . . add ESP, 4 ret • But the best solution is using the argument of the ret instruction. ret optional-value Which mean (optional-value is a 16 -bit immediate ). EIP = SS: ESP = ESP + 4 + optional-value
The State of The Calling Routine • Routines share the system registers • The system registers are an essential part of each routine – Save the registers that are used by the calling routine but changed by the called procedure. – Which routine , the calling or the called, should save the registers? – The programs will be longer as they need to save and restore register for each time a routine is called. • The calling routine saves the registers – It needs to know the registers used by the called procedure. • The called routine saves the registers – It know what registers it is going to use and save them. – It also restore them before it returns.
Why not to use Pusha and Popa • Some of the registers saved by pusha are used for returning results? – The EAX register is often used to return integer results. • Pusha is usuall more expensive than a singe push. – Pusha takes around five time the time of a single push – It is more efficient to save one or two registers using push than using pusha • pusha improves the readability of code and reduces memory required for the instructions.
ENTER and LEAVE Instructions • The enter instruction can be used to allocate a stack frame on entering a routine. – enter bytes, level – bytes specifies the number of bytes of local variable storage we want on the stack. level the nesting level of the procedure; a nonzero level copies level stack frame pointers into the new frame. enter XX, 0 push EBP mov EBP, ESP sub ESP, XX • The leave instruction mov ESP, EBP pop EBP proc-name: enter XX, 0. . . routine body. . . leave ret YY
Code Example %include "io. mac". DATA prompt_msg 1 db "Please input the first number: ", 0 prompt_msg 2 db "Please input the second number: ", 0 sum_msg db "The sum is ", 0. CODE. STARTUP Put. Str prompt_msg 1 ; request first number Get. Int CX ; CX = first number Put. Str prompt_msg 2 ; request second number Get. Int DX ; DX = second number push CX ; place first number on stack push DX ; place second number on stack call sum ; returns sum in AX Put. Str sum_msg ; display sum Put. Int AX newline done: . EXIT
Code Example ; -----------------------------; Procedure sum receives two integers via the stack. ; The sum of the two integers is returned in AX. ; -----------------------------sum: enter 0, 0 ; save EBP mov AX, [EBP+10] ; sum = first number add AX, [EBP+8] ; sum = sum + second number leave ; restore EBP ret 4 ; return and clear parameters
Code Example %include "io. mac". DATA prompt_msg db "Please input a string: ", 0 output_msg db "The swapped string is: ", 0. UDATA string resb BUF_LEN ; input string < BUF_LEN chars. . CODE. STARTUP Put. Str prompt_msg ; request string input Get. Str string, BUF_LEN ; read string from the user mov EAX, string ; EAX = string[0] pointer push EAX inc EAX ; EAX = string[1] pointer push EAX call swap ; swaps the first two characters Put. Str output_msg ; display the swapped string Put. Str string newline done: . EXIT
Code Example ; -----------------------------; Procedure swap receives two pointers (via the stack) to ; characters of a string. It exchanges these two characters. ; -----------------------------. CODE swap: enter 0, 0 push EBX ; save EBX - procedure uses EBX ; swap begins here. Because of xchg, AL is preserved. mov EBX, [EBP+12] ; EBX = first character pointer xchg AL, [EBX] mov EBX, [EBP+8] ; EBX = second character pointer xchg AL, [EBX] mov EBX, [EBP+12] ; EBX = first character pointer xchg AL, [EBX] ; swap ends here pop EBX ; restore registers leave ret 8 ; return and clear parameters
Code Example %define CRLF 0 DH, 0 AH MAX_SIZE EQU 20 %include "io. mac". DATA prompt_msg db "Enter nonzero integers to be sorted. ", CRLF db "Enter zero to terminate the input. ", 0 output_msg db "Input numbers in ascending order: ", 0. UDATA array resd MAX_SIZE ; input array for integers. CODE. STARTUP Put. Str prompt_msg ; request input numbers newline mov EBX, array ; EBX = array pointer mov ECX, MAX_SIZE ; ECX = array size sub EDX, EDX ; number count = 0 read_loop: Get. LInt EAX ; read input number cmp EAX, 0 ; if the number is zero je stop_reading ; no more numbers to read
Code Example mov [EBX], EAX add EBX, 4 inc EDX loop read_loop stop_reading: push EDX push array call bubble_sort Put. Str output_msg newline mov EBX, array mov ECX, EDX print_loop: Put. LInt [EBX] newline add EBX, 4 loop print_loop done: . EXIT ; copy the number into array ; EBX points to the next element ; increment number count ; reads a max. of MAX_SIZE numbers ; push array size onto stack ; place array pointer on stack ; display sorted input numbers ; ECX = number count
Code Example ; This procedure receives a pointer to an array of integers ; and the size of the array via the stack. It sorts the ; array in ascending order using the bubble sort algorithm. SORTED EQU 0 UNSORTED EQU 1 bubble_sort: pushad mov EBP, ESP ; ECX serves the same purpose as the end_index variable ; in the C procedure. ECX keeps the number of comparisons ; to be done in each pass. Note that ECX is decremented ; by 1 after each pass. mov ECX, [EBP+40] ; load array size into ECX next_pass: dec ECX ; if # of comparisons is zero jz sort_done ; then we are done mov EDI, ECX ; else start another pass ; DL is used to keep SORTED/UNSORTED status mov DL, SORTED ; set status to SORTED mov ESI, [EBP+36] ; load array address into ESI ; ESI points to element i and ESI+4 to the next element
Code Example pass: ; This loop represents one pass of the algorithm. ; Each iteration compares elements at [ESI] and [ESI+4] ; and swaps them if ([ESI]) < ([ESI+4]). mov EAX, [ESI] mov EBX, [ESI+4] cmp EAX, EBX jg swap increment: ; Increment ESI by 4 to point to the next element add ESI, 4 dec EDI jnz pass cmp EDX, SORTED ; if status remains SORTED je sort_done ; then sorting is done jmp next_pass ; else initiate another pass
Code Example swap: ; swap elements at [ESI] and [ESI+4] mov [ESI+4], EAX ; copy [ESI] in EAX to [ESI+4] mov [ESI], EBX ; copy [ESI+4] in EBX to [ESI] mov EDX, UNSORTED ; set status to UNSORTED jmp increment sort_done: popad ret 8
Variable Number of Parameters • Functions and procedure may take variable number of parameters – printf and scanf in C • The called routine is not aware of the number of parameters – The first parameter in the parameter list specifies the number of parameters • The stack size imposes a limit on the number of parameters that can be passed.
Code Example Variable number of parameters passed via stack %define CRLF 0 DH, 0 AH ; carriage return and line feed %include "io. mac". DATA prompt_msg db "Please input a set of nonzero integers. ", CRLF db "You must enter at least one integer. ", CRLF db "Enter zero to terminate the input. ", 0 sum_msg db "The sum of the input numbers is: ", 0. CODE. STARTUP Put. Str prompt_msg ; request input numbers newline sub ECX, ECX ; ECX keeps number count read_number: Get. LInt EAX ; read input number cmp EAX, 0 ; if the number is zero je stop_reading ; no more numbers to read push EAX ; place the number on stack inc ECX ; increment number count jmp read_number
Code Example Variable number of parameters passed via stack stop_reading: push ECX ; place number count on stack call variable_sum ; returns sum in EAX ; clear parameter space on the stack inc ECX ; increment ECX to include count add ECX, ECX ; ECX = ECX * 4 (space in bytes) add ECX, ECX add ESP, ECX ; update ESP to clear parameter ; space on the stack Put. Str sum_msg ; display the sum Put. LInt EAX newline done: . EXIT
Code Example Variable number of parameters passed via stack ; This procedure receives variable number of integers via the ; stack. The last parameter pushed on the stack should be ; the number of integers to be added. Sum is returned in EAX. variable_sum: enter 0, 0 push EBX ; save EBX and ECX push ECX mov ECX, [EBP+8] ; ECX = # of integers to be added mov EBX, EBP add EBX, 12 ; EBX = pointer to first number sub EAX, EAX ; sum = 0 add_loop: add EAX, [SS: EBX] ; sum = sum + next number add EBX, 4 ; EBX points to the next integer loop add_loop ; repeat count in ECX pop ECX ; restore registers pop EBX leave ret ; parameter space cleared by main
Local Variables • Consider a code in C int average(int a, int b){ int temp, N; . . . } • Allocating local variable in data segment – It is static and remains active even when the procedure is not. – It does not work for recursive routines • Space for local variables is reserved on the stack.
Code Example Variable number of parameters passed via stack stop_reading: push ECX ; place number count on stack call variable_sum ; returns sum in EAX ; clear parameter space on the stack inc ECX ; increment ECX to include count add ECX, ECX ; ECX = ECX * 4 (space in bytes) add ECX, ECX add ESP, ECX ; update ESP to clear parameter ; space on the stack Put. Str sum_msg ; display the sum Put. LInt EAX newline done: . EXIT
Code Example Local variables - Fibonacci numbers %include "io. mac". DATA prompt_msg db "Please input a positive number (>1): ", 0 output_msg 1 db "The largest Fibonacci number less than " db "or equal to ", 0 output_msg 2 db " is ", 0. CODE. STARTUP Put. Str prompt_msg ; request input number Get. LInt EDX ; EDX = input number call fibonacci Put. Str output_msg 1 ; display Fibonacci number Put. LInt EDX Put. Str output_msg 2 Put. LInt EAX newline done: . EXIT
Code Example Local variables - Fibonacci numbers ; Procedure fibonacci receives an integer in EDX and computes ; the largest Fibonacci number that is less than or equal to ; the input number. The Fibonacci number is returned in EAX. fibonacci: push EBX ; EAX maintains the smaller of the last two Fibonacci ; numbers computed ; EBX maintains the larger one. mov EAX, 1 ; initialize EAX and EBX to mov EBX, EAX ; first two Fibonacci numbers fib_loop: add EAX, EBX ; compute next Fibonacci number xchg EAX, EBX ; maintain the required order cmp EBX, EDX ; compare with input number in EDX jle fib_loop ; if not greater, find next number ; EAX contains the required Fibonacci number pop EBX ret
- Slides: 45