Title: Procedures and Macros
1Procedures and Macros
- CS227
- Western Washington University
2Procedures
- Why?
- To avoid writing the same sequence of
instructions every time you need them - Code readability and clarity
- Top-down design
- How?
- CALL instruction
- RET instruction
3The CALL Instruction
- CALL print_char
- takes the address of the procedure in memory as
an operand - stores the current value contained in IP on the
stack - this is the return address, because it is the
address of the instruction to be executed after
completing the procedure - this is accomplished by subtracting 2 from the SP
- loads the instruction pointer with the starting
address of the procedure
4Calling Mechanisms
- intra-segment NEAR call
- the procedure being called is in the same segment
- inter-segment FAR call
- the procedure being called is in another segment
- both NEAR and FAR calls can be direct or indirect
- direct - adds the 16-bit signed displacement to
the IP - indirect - the instruction pointer is replaced
with the 16-bit value from a specified register
or memory location
5The RET Instruction
- copies the word at the top of the stack,
hopefully the value of the instruction pointer,
from the stack back to the IP - this returns execution to the caller of the
procedure - for a return from a far procedure, it also needs
to pop the value of the CS register - it does this by incrementing the SP by 2 and copy
the word starting there into CS - the assembler automatically codes a near RET for
a near PROC and a far RET for a far PROC
6The Stack
- the stack is a segment of memory used for
- storing return addresses
- saving the contents of registers for the calling
program - hold data or addresses that will be used by the
procedure - recall the SS and the SP registers
- SS holds the value of the upper 16 bits of the
starting address of the stack segment - SP holds the value of the offset of the last word
written to the stack
7Using the Stack
- To write a word to the stack - PUSH
- the SP register is decremented by 2
automatically, before writing the word to the
stack - soyou need to initialize the SP register to the
top of the memory you have set aside as the stack
segment, rather than the bottom - Retrieve a word from the stack - POP
- the word starting at SP is copied to the
specified destination - the SP register is incremented by 2 automatically
8An Example
- Assume SS 7000H and SP 0050H
- 001D CALL foo
- 0020
- SP gets decremented by 2
- the word 0020H is written to the memory locations
starting at (SS SP) 7004EH - RET is executed
- word starting at SP is popped into the IP
register automatically - SP incremented, so SP 0050H, which is the top
of the stack
9Instructions to set up a stack
- STACK_SEG SEGMENT STACK
- DW 40 DUP(0)
- STACK_TOP LABEL WORD
- STACK_SEG ENDS
- CODE SEGMENT
- Assume CSCODE, SSSTACK_SE
- MOV AX, STACK_SEG Initialize stack
- MOV SS, AX segment register
- LEA SP, STACK_TOP Initialize
- stack pointer
- CODE ENDS
- END
10Checklist
- Declare the stack segment
- STACK_SEG SEGMENT STACK
- STACK_SEG ENDS
- Declare the size of the stack
- DW 40 DUP(0) this stack is 40 words big
- Attach a name to the highest location in the
stack - STACK_TOP LABEL WORD
- this declares STACK_TOP as a label to the next
even address after the words set aside for the
stack - Inform the assembler that you are using a stack
- ASSUME SSSTACK_SEG
- Initialize the SS register
- MOV AX, STACK_SEG
- MOV SS, AX
- Initialize the SP register
- LEA SP, STACK_TOP
11Writing a Procedure
- WAIT_1MS PROC NEAR
- MOV CX, 23F2H
- HERE
- LOOP HERE
- RET
- WAIT_1MS ENDP
- WAIT_1MS is the name of the procedure specified
by PROC - its a near procedure specified by NEAR
- ENDP specifies the end of the procedure
12PUSH POP
- PUSH register/memory
- decrements the SP by 2
- copies the contents of the 16-bit register or
memory location to memory at the new SP location - you can PUSH
- any 16-bit general purpose register
- any of the base or pointer registers
- any of the segment registers
- any word from memory
- POP register/memory
- copies a word from the TOP to the specified
16-bit register or memory location - increments SP by 2
- you can POP
- to any register, except CS
- to any memory location
13A Stack Map
- Shows diagrammatically the effects of a set of
instructions on the stack and SP - MULTO PROC NEAR
- PUSHF
- PUSH AX
- PUSH BX
- PUSH CX
- POP CX
- POP BX
- POP AX
- POPF
- RET
- MULTO ENDP
14Preserving Registers
- Whose responsibility is it anyway?
- The callee (procedure being called)
- preferred
- it knows exactly which registers it needs to use
- less clutter in main program
- independent of which registers are used by
caller - The caller (calling program or procedure)
15Passing Parameters
- Passing in Registers
- move the parameter to a register, and do not push
that register onto the stack - Passing in General Memory
- directly access the parameters by name
- MOV AL, SOME_VAR
- Passing using Pointers
- copy the address of the parameter to a register,
usually SI or DI before calling the procedure - LEA SI, SOME_VAR
- Passing using the Stack
- push the parameters on the stack before calling
the procedure - use the BP register as a second pointer into the
stack
16Problems with Each Method
- Passing in registers
- the number of registers is limited
- Passing in general memory
- always using the same memory location
- Passing using pointers
- most versatile, because you can pass pointers to
anywhere in memory - Passing using the stack
- can be difficult to maintain the stackstack
overflow - can use RET ltsome constantgt
17Choosing a Method
- For simple procedures with few parameters...
- use registers
- When dealing with arrays
- use a register to store starting address
- Globals are bad even in assembly
- this is the case where you are directly accessing
a memory location - For procedures that will be called from a high
level language program, or recursive procs - use the stack
18Reentrant Procedures
- A reentrant procedure can be interrupted, used,
and then re-entered without losing or overwriting
anything useful - To be reentrant
- a procedure must push the flags and registers
onto the stack before doing anything else - a procedure should use only the registers or the
stack to pass parameters
19Why is reentrancy important?
- Normal program execution can be interrupted with
instructions to call a specified procedure - this is an interrupt service procedure
- The interrupt service procedure could call the
procedure that was executing when the interrupt
occurred - if were using the stack correctly, there are no
problems - if were directly using memory locations, were
in trouble!
20Recursive Procedures
- A recursive procedure is a procedure that calls
itself. It is useful for specific applications,
such as trees - We can compute the sum of a the first n integers
using a recursive procedure. - recursive sum example
- cx will hold the counter,
- ax will hold the sum
- main proc
- mov cx, 5
- xor ax, ax
- call sum
- mov ax, 4c00h
- int 21h
- main endp
21sum proc or cx, cx check counter value jz
QUIT quit if zero add ax, cx ow, add
to sum dec cx decrement counter call
sum recursive call QUIT ret sum
endp Factorial is a much more challenging
recursive routine to write. if n 1 then
factorial 1 else factorial n (factorial of
(n - 1))
22factorial proc push bp mov bp, sp mov
ax, bp4 get n cmp ax, 1 n lt 1? ja
RECUR no continue mov ax, 1 yes return
1 jmp EXIT RECUR dec ax push ax
factorial(n-1) call factorial mov bx,
bp4 get n mul bx ax ax bx EXIT
pop bp ret 2 ax result factorial
endp How would you use this factorial procedure?
23Writing Calling Far Procedures
- Writing
- PROCEDURES SEGMENT
- MULTIPLY_32 PROC FAR
- ASSUME CSPROCEDURES
- NOP
- RET
- MULTIPLY_32 ENDP
- PROCEDURES ENDS
- Calling
- the assembler takes care of translating a far
procedure call
24Using Multiple Assembly Units
- You can create a program that is made up of
multiple assembly units - can write each module and assemble, test, and
debug as a unit - then you link all the obj files from the modules
into an executable file - You need to use some new keywords
- PUBLIC
- EXTRN
25PUBLIC EXTRN
- SMART_DIVIDE.ASM
- PUBLIC SMART_DIVIDE
- PROCEDURES SEGMENT PUBLIC
- SMART_DIVIDE PROC FAR
- ASSUME CSPROCEDURES, DSDATA
- SMART_DIVIDE ENDP
- PROCEDURES ENDS
- END
- MAIN_PROG.ASM
- PROCEDURES SEGMENT PUBLIC
- lets the assembler know that
- EXTRN SMART_DIVIDE FAR
- smart_divide is of type far proc
- PROCEDURES ENDS
- and is located in the code
- segment named procedures
26More on PUBLIC
- Make a segment PUBLIC whenever you want it to be
linked with other segments of the same name in
other modules - this has the effect of concatenating the segments
in successive memory locations - Make a procedure PUBLIC when you want it to be
called by other assembly modules - PUBLIC sampleProc
- sampleProc proc
- sampleProc endp
- Make a variable PUBLIC whenever you want it to be
accessible from other assembly modules - PUBLIC DIVISOR
27More on EXTRN
- Use EXTRN to tell the assembler that the data
item is not in the present module - EXTRN SMART_DIVIDEFAR
- EXTRN DIVISORWORD
- Other possibilities include
- PROC, ABS, NEAR, BYTE
- Enclose the EXTRN statement within
- SEGMENT_NAME SEGMENT PUBLIC
- SEGMENT_NAME ENDS
- this tells the assembler and linker where the
data item is located - This is not necessary when using the .code
directive
28Macros
A macro is a symbolic name given to a sequence of
one or more assembly instructions. This is
similar to a define in C or an inlined function
in C. Once a macro is defined, it can be
invoked anywhere throughout your code. Why
use macros when you can write procedures? Macros
can be executed more quickly. No CALL
instruction is generated, so no status
information needs to be saved. Why not use
macros all the time then? Since the instructions
associated with a macro are directly inserted
into the code, the program size will expand
accordingly.
29Macro Syntax
A macro is defined using the MACRO directive and
the ENDM directive. mPutchar macro begin
macro mov ah, 2 int 21h endm end
macro Notice the use of for comments.
This simply prevents the comment being inserted
as part of the expanded instructions. To
invoke a macro, simply write the name of the
macro .code mov dl, a mPutchar
30Parameters
Macros have an advantage over procedures in that
parameters can be specified for macros. mPutchar
macro char push ax push dx mov ah, 2
mov dl, char use parameter int 21h pop
dx pop ax endm .code mPutchar a