Download Lab_6_Outline

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
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