Macros are another method of implementing modularity and abstraction in Assembler (and C, spreadsheets, and many languages). Though functions and macros are both abstraction techniques, macros are very different from a function in use.
| Extrn
PutStrng:Far
Data Segment
|
Since we are already familiar with implementing functions in C++, it
is to our advantage to build upon that knowledge when implementing assembler
functions. The following C++ and Assembly program call a function f
that has zero parameters but each produces the same results. It is instructive
to follow the execution of the C++ first and compare that with the Assembler.
int x; // Global x 1. void main(void) {
2. f();
3. x = 4; // Side effect
4. }
5. void f(void) {
6. x = 5; // Side effect
7. }
|
.386
.model flat, stdcall
include v:\common\user\c335\masm32\include\kernel32.inc
include v:\common\user\c335\masm32\include\masm32.inc
includelib v:\common\user\c335\masm32\lib\kernel32.lib
includelib v:\common\user\c335\masm32\lib\masm32.lib
.data X Dw ? ;; Global X .code 1. main Proc near 2. Call f 3. Mov X, 4 ;; Side effect 4. Push 0 ; Stop 5. Call ExitProcess 6. main Endp 7. f proc near 8. Mov X, 5 ;; Side effect 9. Ret 10. f Endp End main |
| C++ | 1, 2, 5, 6, 7, 3, 4 |
| Assembler | 1, 2, 7, 8, 9, 3, 4, 5 |
| Assembly Listing | Stack, Sp, and IP |
Address
0005 C7 06 0000r 0004 Mov X, 4
000B E8 0012 Call f
000E B4 0000 Push 0
0010 CD 21 Call ExitProcess
main Endp
f Proc Near
0012 C7 06 0000r 0005 Mov X, 5
0018 C3 Ret
0019 f Endp
|
After Call f ____ |____| Sp -> |000E| | | Ip = 0012 After Ret ____ Sp -> |____| |000E| | | Ip = 000E |
Function Return - Returning from a function is the reverse of the Call requiring the following step:
Global Variables - It should be remembered that all variables
declared in the data segment and all CPU registers are accessible globally,
from within any function in the algorithm. While this may seem wonderfully
useful, reflect upon writing a large C++ program with no local variables
or function parameters, a globally defined variable can be accessed in
any function allowing accidental change to occur undetected. When globals
are used to exchange data, the same variable is accessed in one or more
functions. The effects of function execution are not localized to that
function, since global variables can be changed in any function as a hidden
side-effect of the function's execution. When global variables are used
to exchange data between functions, the likelihood of creating unwanted
side-effects increases directly with the number of assignment statements
and the number of global variables. The potential for side-effects increases
exponentially as the number of global variables increase, creating a daunting
challenge for a programmer to produce reliable programs.
While global variables and the globally accessible CPU registers share many of the same problems when used to exchange data between assembly procedures, there are two strong reasons for using registers over global variables: 1) As part of the CPU, the same registers are known by the same name everywhere and are shared by all functions/programs/etc. making it possible to pass data between two separately written functions/programs. Global variable names are normally limited to the source program and data segment where defined. 2) The Push/Pop mechanism can be used to preserve register values from unwanted side-effects.
For a simple example of global variables as parameters see the program
above, variable x in the C++ and X in the Assembler are globals,
accessible from all functions. To illustrate the difference between the
use of global variables in the data segment and register parameters, programs
in C++ and Assembler using global variables and another using register parameters
are listed below. Each of the included examples compute N!. All of the
factorial functions return a value in the Ax register, Ax is used by convention
for returning a word result from a function.
// Determine sum of 4! and 5! // using global variables 1. #include <iostream.h>
2. int Sum, N; // Global variable N
// Factorial parameter
3. int Factorial(void) {
4. int F = 1;
5. while (N > 1) {
6. F = F * N; //
7. N = N - 1; // Side-effect N
8. }; //
9. return F;
10. } // Factorial
11. void main(void) {
12. N = 4;
13. Sum = Factorial(); //
14. N = 5; // Side-effect N
15. Sum =+ Factorial(); //
16. cout << N;
17. }
|
.386
.model flat, stdcall
include v:\common\user\c335\masm32\include\kernel32.inc
include v:\common\user\c335\masm32\include\masm32.inc
includelib v:\common\user\c335\masm32\lib\kernel32.lib
includelib v:\common\user\c335\masm32\lib\masm32.lib
PutDec proto
.data
N dd ? ;; Global N parameter to
Sum dd ? ;; Factorial function
.code
;; int Factorial()
;; - Assumptions
;; Parameters - N positive integer (global)
;; Returns - eAx factorial value
;; Registers Altered - eAx, flags
1. Factorial Proc Near ;; int Factorial()
2. Push eDx ;; Save eDx
3. Mov eAx, 1 ;; F = 1;
4. While1: Cmp N, 1 ;; while (N > 1) {
5. Jg Do1 ;; F = F * N;
6. Jmp EndWhile1 ;; N = N - 1;
7. Do1:
8. Mul N ;; ______________
9. Dec N ;; |Side-effect N |
10. Jmp While1 ;; |______________|
11. EndWhile1:
12. Pop eDx ;; Restore Dx
13. Ret ;; eAx has result
14. Factorial Endp
15. Main Proc Near
16. Mov N, 4
17. Call Factorial
18. Mov Sum, eAx ;; Sum = Factorial
19. Mov N, 5
20. Call Factorial
21. Add Sum, eAx ;; Sum =+ Factorial;
;; ________________
22. Mov eAx, N ;; | N side-effected|
23. Call PutDec ;; |________________|
24. Push 0 ;; Stop
25. Call ExitProcess
26. Main Endp
27. End Main
|
Register Parameters -When registers are used to pass parameters
to a procedure or function, it again creates the opportunity for unwanted
side effects. However, using Push and Pop to save and restore
registers used within the procedure one can eliminate at least side-effect
to registers. The C++ algorithms reflects the use of parameters although,
as we will see in later chapters, C++ passes parameters on the stack rather
than in registers (with only a few registers the number of possible parameters
would be severely limited). The Assembler example instead uses registers
to pass data to the function. The key weaknesses of register parameters
are:
18. The value 4 is moved to parameter eBx.
20. The returned result eAx = 4! is used.
21. The value 5 is moved to the parameter eBx.
23. The returned result eAx = 5! is used.
3. The value of parameter in eBx is saved on stack.
10. The parameter eBx is side-effected (changed) in function.
13. The original value of parameter eBx is restored.
// Determine sum of 4! and 5!
// using parameters
1. #include <iostream.h>
2. int Sum, N;
3. void main(void) {
4. N = 4;
5. Sum = Factorial(N);
6. N = 5;
7. Sum =+ Factorial(N);
8. cout << N;
9. }
10. int Factorial( int N ) {
11. int F=1;
12. while (N > 1) {
13. F = F * N; // ______________
14. N = N - 1; //|No Side-effect|
15. }; //|______________|
16. return F;
17.} //Factorial
|
.386
.model flat, stdcall
include v:\common\user\c335\masm32\include\kernel32.inc
include v:\common\user\c335\masm32\include\masm32.inc
includelib v:\common\user\c335\masm32\lib\kernel32.lib
includelib v:\common\user\c335\masm32\lib\masm32.lib
PutDec proto
.data
Sum dd ?
.code
;; int Factorial()
;; - Assumptions
;; Parameters - ebx positive integer (register)
;; Returns - eAx factorial value
;; Registers Altered - eAx, flags
1. Factorial Proc Near ;; int Factorial()
2. Push eDx ;; Save eDx, eBx
3. Push eBx
4. Mov eAx, 1 ;; F = 1;
5. While1: Cmp eBx, 1 ;; while (eBx > 1) {
6. Jg Do1 ;; F = F * eBx;
7. Jmp EndWhile1 ;; eBx = eBx - 1;
8. Do1:
9. Mul eBx ;; ________________
10. Dec eBx ;; |Side-effect eBx |
11. Jmp While1 ;; |________________|
12. EndWhile1:
13. Pop eBx
14. Pop eDx ;; Restore eDx, eBx
15. Ret ;; eAx has result
16. Factorial Endp
17. Main Proc Near
18. Mov eBx, 4
19. Call Factorial
20. Mov Sum, eAx ;; Sum = Factorial
21. Mov eBx, 5
22. Call Factorial
23. Add Sum, eAx ;; Sum =+ Factorial;
24. Mov eAx, Sum
25. Call PutDec ;; cout << Sum
26. Push 0 ;; Stop
27. Call ExitProcess
28. Main Endp
29. End Main
|
Mult Proc Near Push eBx ; Save registers Push eDx PushFD Mov eAx, 5 Mov eBx, 10 ; eBx altered Mul eBx ; eDx, eAx and flags altered PopFD ; Restore registers in reverse order Pop eDx Pop eBx Ret Mult Endp |
Common errors - One common error is to Push/Pop eAx on entry and exit to the function, the undesirable effect is that eAx may change during the function execution but is restored to its original value.
Local variables - Local variables are accessible only in the file where defined. In the example below, both file have separate .data definitions, each independent and each data accessible only within the file defined.
External functions - External functions are defined in a separate file, assembled separately, and linked to a calling program. In the following, main.asm calls the factorial function in the factorial.asm file.
v:\common\user\c335\assembler32
ml /c /coff /Zi /Fl factorial.asm
ml /coff /Zi /Fl main.asm /link io.obj factorial.obj /subsystem:console
main
; main.asm
.386
.model flat, stdcall
include v:\common\user\c335\masm32\include\kernel32.inc
include v:\common\user\c335\masm32\include\masm32.inc
includelib v:\common\user\c335\masm32\lib\kernel32.lib
includelib v:\common\user\c335\masm32\lib\masm32.lib
PutDec proto
Factorial proto
.data
Sum dd ?
.code
Main Proc Near
Mov eBx, 4
Call Factorial
Mov Sum, eAx ;; Sum = Factorial
Mov eBx, 5
Call Factorial
Add Sum, eAx ;; Sum =+ Factorial;
Mov eAx, Sum
Call PutDec ;; cout << Sum
Push 0 ;; Stop
Call ExitProcess
Main Endp
End Main
|
; factorial.asm
.386
.model flat, stdcall
include v:\common\user\c335\masm32\include\kernel32.inc
include v:\common\user\c335\masm32\include\masm32.inc
includelib v:\common\user\c335\masm32\lib\kernel32.lib
includelib v:\common\user\c335\masm32\lib\masm32.lib
.data
OverFlow db ?
.code
Factorial Proc Near ;; int Factorial()
Push eDx ;; Save eDx, eBx
Mov OverFlow, 0
Mov eAx, 1 ;; F = 1;
While1: Cmp eBx, 1 ;; while (eBx > 1 && OverFlow == 0) {
Jg And1
Jmp EndWhile1
And1: Cmp OverFlow, 0
Je Do1 ;; F = F * eBx;
Jmp EndWhile1 ;; eBx = eBx - 1;
Do1:
Mul eBx
IfA: Jo ThenA
Jmp EndifA
ThenA:
Mov OverFlow, 1
EndifA:
;; ________________
Dec eBx ;; |Side-effect eBx |
Jmp While1 ;; |________________|
EndWhile1:
Pop eDx ;; Restore Dx
Ret ;; eAx has result
Factorial Endp
End
|