450 likes | 484 Views
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.
E N D
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
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 21ABH push 7FBD329AH pop EBX
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 . . . ;Save EAX and EBX registers on the stack push EAX push EBX ;EAX and EBX registers can now be used mov EAX,value1 mov EBX,value2 mov value1,EBX mov value2,EAX ;restore EAX and EBX registers from the stack pop EBX pop EAX . . . mov EAX,value1 mov EBX,value2 mov value1,EBX mov value2,EAX
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 EIP = EIP + relative displacement ; update EIP to point to the procedure
Transfer of Control-Example • E816000000H is the machine language encoding of the call instruction 002 call instruction • E8H is the opcode • 00000016H (32bit signed) is a relative displacement 0000001DH − 00000007H = 00000016H (in little-endian order) 002 call instruction • The displacement is 0000001DH − 0000002DH = FFFFFFF0H. • A negative numbers corresponds to −10H (i.e., −16D
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_msg1 db "Please input the first number: ",0 prompt_msg2 db "Please input the second number: ",0 sum_msg db "The sum is ",0 .CODE .STARTUP PutStr prompt_msg1 ; request first number GetInt CX ; CX = first number PutStr prompt_msg2 ; request second number GetInt DX ; DX = second number call sum ; returns sum in AX PutStr sum_msg ; display sum PutInt AX newline done: .EXIT
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 PutStr prompt_msg ; request string input GetStr string,BUF_LEN ; read string from keyboard mov EBX,string ; EBX = string address call str_len ; returns string length in AX PutStr length_msg ; display string length PutInt 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 param1 push param2 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 param2 • ESP + 6 points to param1 • 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]
Param1 Param1 Param1 Param2 Param2 Param2 Accessing Parameters Save the EBP register and set to ESP push EBP mov EBP,ESP … Before returning restore the EBP register pop EBP
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 param1 push param2 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 = 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. • enterbytes, 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 • The leave instruction • proc-name: • enter XX,0 • . . . • routine body • . . . • leave • ret YY • push EBP • mov EBP,ESP • sub ESP,XX mov ESP,EBP pop EBP
Code Example %include "io.mac" .DATA prompt_msg1 db "Please input the first number: ",0 prompt_msg2 db "Please input the second number: ",0 sum_msg db "The sum is ",0 .CODE .STARTUP PutStr prompt_msg1 ; request first number GetInt CX ; CX = first number PutStr prompt_msg2 ; request second number GetInt DX ; DX = second number push CX ; place first number on stack push DX ; place second number on stack call sum ; returns sum in AX PutStr sum_msg ; display sum PutInt 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 PutStr prompt_msg ; request string input GetStr 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 PutStr output_msg ; display the swapped string PutStr 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 0DH, 0AH 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 PutStr 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: GetLInt 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 ; copy the number into array add EBX,4 ; EBX points to the next element inc EDX ; increment number count loop read_loop ; reads a max. of MAX_SIZE numbers stop_reading: push EDX ; push array size onto stack push array ; place array pointer on stack call bubble_sort PutStr output_msg ; display sorted input numbers newline mov EBX,array mov ECX,EDX ; ECX = number count print_loop: PutLInt [EBX] newline add EBX,4 loop print_loop done: .EXIT
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 ExampleVariable number of parameters passed via stack %define CRLF 0DH,0AH ; 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 PutStr prompt_msg ; request input numbers newline sub ECX,ECX ; ECX keeps number count read_number: GetLInt 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 ExampleVariable 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 PutStr sum_msg ; display the sum PutLInt EAX newline done: .EXIT
Code ExampleVariable 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 ExampleVariable 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 PutStr sum_msg ; display the sum PutLInt EAX newline done: .EXIT
Code ExampleLocal variables - Fibonacci numbers %include "io.mac" .DATA prompt_msg db "Please input a positive number (>1): ",0 output_msg1 db "The largest Fibonacci number less than " db "or equal to ",0 output_msg2 db " is ",0 .CODE .STARTUP PutStr prompt_msg ; request input number GetLInt EDX ; EDX = input number call fibonacci PutStr output_msg1 ; display Fibonacci number PutLInt EDX PutStr output_msg2 PutLInt EAX newline done: .EXIT
Code ExampleLocal 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