Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Assembly Language Lab 6 1. Runtime Stack Structure Runtime Stack is a temporary array (segment) in memory managed by CPU using SS and ESP registers. We rarely manipulate ESP directly; instead we use PUSH and POP instructions (in addition to, other instructions like CALL, RET…). ESP register points to the last (top) item in the stack, while SS register contains an index in the descriptor table like any segment register. Push and Pop operations in stack are reversed; when an item is pushed, the ESP is decremented, while in popping it is incremented. This is Intel design issue. For example, when push values x and y then pop a single element then push a value z, the stack will be as follows: After Push x Stack 1000 x FFC ? FF8 ? ESP After Push y Stack 1000 x FFC y ESP FF8 ? After Pop Stack 1000 x FFC y FF8 ? ESP After Push z Stack 1000 x FFC z FF8 ? ESP Primary usages of stack are: • Saving register values temporarily • Saving the return address of the called procedures • Passing parameters to procedures • Local variables defined in a procedure are created in its beginning and destroyed in its end. 2. Stack instructions PUSH r/m16 | r/m32 | imm32 PUSH instruction decrements ESP first, then copies its operand into stack (the number decremented from ESP depends on operand size; 4 if double‐word operand and 2 if word operand) POP r/m16 | r/m32 POP instruction copies the contents of the top element in the stack into its operand, then increments ESP (number incremented to ESP depends on operand size also) PUSHAD PUSHAD instruction pushes all 32‐bit general registers into stack in the following order: EAX, ECX, EDX, EBX, ESP, EBP, ESI, and then EDI. 2013-2014 1 POPAD POPAD instruction popes all 32‐bit general registers from stack in the reverse order that PUSHAD pushes them (i.e. EDI, ESI, EBP, ESP, EBX, EDX, ECX, and then EAX) PUSHFD/POPFD PUSHFD/POPFD instructions pushes/popes 32‐bit EFLAGS register into/from stack Notes: • We can use PUSHAD in the beginning of a procedure and POPAD in its end to guarantee that the procedure won’t affect register values as it may be used by the calling procedure. • Also, take care that number of pop instructions should match with the number of push instructions in a procedure; to safely return from the procedure as the return address is stored in the stack. 3. Procedures: Definition and Usage As we know, we should not write the whole program in a single chunk. Program code should be divided into pieces, each piece performs specific function and takes input and produces output. We often called these pieces as functions in high‐level languages, however they are called differently in assembly language, they are known as procedures but it still has the same meaning. To define a procedure, use PROC and ENDP directives to encapsulate procedure code, for example, Sample1 PROC … … … RET Sample1 ENDP Note that the labels preceding PROC and ENDP is the procedure name and they must match to express about the begin and end of a procedure respectively. Also, any procedure should be able to return to the main program, this done by RET instruction. To call a procedure, just use CALL instruction, for example, to call previous procedure, just write this instruction: … CALL Sample1 … 2013-2014 2 Example 1a: Sum Integer Array (Revisited) Revise Lab 4 for nonprocedural version INCLUDE Irvine32.inc .data Arr1 SDWORD 10, 20, 30, 40, 50 sum_val ? SDWORD .code main PROC call SumArr call writeint call CrLf ;main procedure begin ;call SumArr procedure ;output the sum (which already stored in EAX) mov sum_val, eax exit main ENDP SumArr PROC mov esi, offset Arr1 mov eax, 0 ;main procedure end ;SumArr procedure begin ;put array address in esi ;initialize eax by zero for temporary sum mov ecx, LENGTHOF Arr1 ;initialize ecx (loop counter) by array size sum_loop: add eax, DWORD PTR [esi] add esi, TYPE Arr1 ;increment esi pointer by the size of the array ;element loop sum_loop ;ECX decremented implicitly by LOOP instruction RET ;Return to the main procedure SumArr ENDP ;SumArr procedure end END main Notes: • As we write procedural code, we make the main program code itself in a procedure called main (however, it can take any other name). • The main procedure does not require a RET instruction at its end as it is ended by exit which terminates the whole program and returns to Windows OS. (exit is an alias for ExitProcess Windows API function). In contrast, the SumArr must ended by RET instruction to allow return to the main program, if it is missed, the program continues execution to next machine code which it does not correspond to a valid machine instruction and causing a program crash. • The SumArr procedure calculates the summation in EAX register and implicitly returns this value to the main program in EAX itself. Returning values in registers is possible as the registers values do not change when returning from a procedure. 2013-2014 3 4. CALL and RET instructions: In deep As discussed previously, CPU knows the next instruction by storing its address in EIP (instruction pointer) register. So, at any time during the program run, EIP contains the address of next instruction to be executed. Therefore, to change the program flow by a jump or CALL instructions, CPU updates the EIP value to be the address of the jumped instruction or the address of the called procedure. A trivial question popped in mind, how RET instruction knows the correct address to return to after a procedure end? This can be accomplished by saving the return address, where the return address is the address of the next instruction after CALL instruction. Well, good answer, but where to save it? The most suitable place to save the return address is the stack. Therefore, return address is pushed by CALL instruction and popped by RET instruction. Trace this example for more clarification: Address 00000020 00000025 Code main PROC call MySub mov eax, ebx … … 00000040 main ENDP MySub PROC mov eax, edx … RET MySub ENDP Before executing CALL After executing CALL Stack Stack ESP ESP ? 00000025 ? ? 00000020 00000040 ? ? EIP EIP After executing RET Stack 00000025 ? ? ESP 00000025 EIP Exercise: Apply the above trace on a program that contains nested calls (i.e. MySub procedure contains a CALL instruction for another procedure, say Sub2, and Sub2 contains a CALL instruction for another procedure and so on.). What will be the stack content after each CALL instruction? And what will it be after returning to the main program? 2013-2014 4 5. Passing parameters to procedures using registers In Example 1a, we saw how to sum integer arrays by a separate procedure from the main program, but it has a problem. It runs only on fixed array (i.e. Arr1) with fixed length, and so this procedure cannot be reused with any other array. So, we need to pass the array address and its length to the procedure to be able to run on different arrays with different lengths. We can use general‐purpose registers to pass parameters to procedures. Like we do with WriteInt, or WriteString procedures defined in Irvine library. They receive parameters in EAX and EDX registers respectively. See the following example. Example 1b: Sum Integer Array procedure with register parameters This example calculates the sum of an integer array using a procedure. The procedure takes the address of the array in ESI register and its length in ECX register INCLUDE Irvine32.inc .data arr1 DWORD 10, 20, 30, 40, 50 sum_val DWORD ? .code main PROC ;main procedure begin mov esi, offset arr1 mov ecx, LENGTHOF arr1 ;pass array address in esi as a parameter ;pass array length in ecx as a parameter call SumArr ;call SumArr procedure to sum array call writeint ;output the summation mov sum_val, eax exit main ENDP SumArr PROC ;main procedure end ;SumArr procedure begin push esi ;save the esi and ecx values from changes push ecx ;happened inside this procedure mov eax, 0 sum_loop: add eax, DWORD PTR [esi] add esi, 4 ;increment esi pointer by 4 ;(size of array element) loop sum_loop pop ecx pop esi Ret SumArr ENDP ;restore ecx and then esi values from the ;stack ;Return execution to the main procedure ;SumArr procedure end END main 2013-2014 5 Notes: • SumArr procedure now uses ESI and ECX registers as the array address and the array length respectively. And so, it can be used with any integer array of any length. • As SumArr modifies the ESI and ECX registers, it pushes them in its beginning in the stack to temporarily save them. At its end, it restores their values from stack and so calling this procedure is transparent for the main program as it doesn’t modify registers values (of course, except EAX which is used for returning summation value) 6. Passing parameters to procedures using stack Passing parameters using registers is limited according to the limited number of registers. Therefore, there is a more efficient way to pass parameters to procedures which pushes parameters into runtime stack while the called procedure pop them to work on. MASM provides an easy way to pass parameters in the stack, by allowing PROC directive to accept a list of parameters. In addition, INVOKE directive can be used to call procedures with a list of arguments instead of CALL instruction. You can do the job of passing parameters in the stack manually without using INVOKE directive, but using it will simplify the operation because it is much like calling functions in high‐level languages. Defining procedures with parameters: <Label> PROC paramName:type, ..., paramName:type ... RET <Label> ENDP paramName: is an arbitrary name you assign to the parameter. type: is the parameter type. It can be one of types we know (BYTE, WORD, …) or also a pointer for one of them to handle calling by reference. To specify pointer for a type, use PTR specifier before the type like PTR BYTE, PTR WORD, etc. Calling procedures with stack parameters: INVOKE procedureName [, argumentList] INVOKE directive allows calling procedures defined with stack parameters while CALL instruction doesn’t. argumentList is a comma‐separated list of arguments to be passed to the procedure named procedureName. Note that a procedure must be defined before calling it by INVOKE directive. Otherwise, assembler gives undefined symbol error. Therefore, we use PROTO 2013-2014 6 directive to declare this procedure, and put the definition anywhere in the code. Declaring a procedure: <Label> PROTO paramName:type, ..., paramName:type PROTO directive allows declaring the prototype of a specific procedure. Note that label declared in PROTO must match with the label specified in PROC directive. Example 1c: Sum Integer Array procedure with stack parameters INCLUDE Irvine32.inc .data arr1 DWORD 10, 20, 30, 40, 50 sum_val DWORD ? .code ;Specify procedure prototype. (It can placed in any location ;before invoke statement, even inside main procedure) SumArr PROTO x:PTR DWORD, sz:DWORD main PROC ;use invoke directive to call SumArr procedure and ;specify arguments INVOKE SumArr, offset arr1, lengthof arr1 call writeint mov sum_val, eax exit main ENDP ;Specify SumArr procedure definition SumArr PROC Addrs:PTR DWORD, sze:DWORD push esi push ecx mov eax, 0 mov esi, Addrs mov ecx, sze sum_loop: add eax, DWORD PTR [esi] add esi, 4 loop sum_loop pop ecx pop esi Ret SumArr ENDP End main 2013-2014 7 Example 2: Reverse a string This example shows a procedure that reverses an entered string. INCLUDE Irvine32.inc .data MAX_LEN equ 80 prompt1 byte "Enter a string: ", 0 prompt2 byte "The reversed string: ", 0 strinp byte MAX_LEN+1 Dup(?) strout byte MAX_LEN+1 Dup(?) .code ;the prototype of the ReverseString procedure ReverseString PROTO Src:PTR BYTE, Dest:PTR BYTE, sze:DWORD main PROC mov edx, offset prompt1 call writestring mov edx, offset strinp mov ecx, MAX_LEN call readstring ;ReadString:inputs a string from user ;[IN] EDX: address of the input buffer ;[IN] ECX: Max number of chars to read ;[OUT] EAX: size of the input string ;Call ReverseString procedure and pass its parameters INVOKE ReverseString, offset strinp, offset strout, eax mov edx, offset prompt2 call writestring mov edx, offset strout call writestring call crlf exit main ENDP ReverseString PROC Src:PTR BYTE, Dest:PTR BYTE, sze:DWORD ;save register values in stack push ecx push edi push eax mov ecx, sze mov edi, Src l1: movzx eax, BYTE PTR [edi] ;push the source string in stack push eax inc 2013-2014 8 edi loop l1 mov ecx, sze mov edi, Dest l2: pop eax mov [edi], al inc edi ;pop the string from stack to the dest string ;it is reversed loop l2 mov BYTE PTR [edi], 0 ;terminate the reversed string by a null char pop eax pop edi pop ecx Ret ReverseString ENDP END main Notes: • Readstring is an Irvine function that reads a string from a user. It takes the address of the input buffer in EDX and the maximum number of characters to read in ECX. It fills the buffer by the input string and set EAX by the size of the input string. • ReverseString procedure takes two pointers to the input string and the reversed string. The pointer is defined by PTR operator followed by the size that the pointer points to such as PTR BYTE and PTR DWORD. Exercises: Your Turn :) You MUST use procedures in the following exercises 1. Write an assembly program that reverses an input string. 2. A palindrome is a string that reads the same backwards as forwards. For example, abcba, 1221, 55555 …etc. Write an assembly program that reads a string and its size and check if it is palindrome or not. Assignment 3: You MUST use procedures in the following exercises 1. Write an assembly program that calculates the Fibonacci value of the input index from user. Write the Fibonacci procedure twice, once repetitive and another recursive. (The Fibonacci series is 0, 1, 1, 2, 3, 5, 8, 13…) 2013-2014 9 2. Write an assembly program that computes the characters frequencies of a given string. For example if a string given ‘adbdbbbaadd’, it should print: a=3 b=4 d=4 3. Write an assembly program that validates the number and order of parentheses in a mathematical expression input from user. It should check that the number of right parentheses and left parentheses are equal. Also, it checks every right (closing) parenthesis is preceded by a matching left (opening) parenthesis. To be postponed to next section: 1. Write an assembly program that calculates the factorial of the input number. 2. Write an assembly program that checks if the input number is prime or not. 3. Write an assembly program that calculates the greatest common divisor (GCD) of two integers. GCD is the largest integer that evenly divides each of the two numbers. 2013-2014 10