INTRODUCTION TO PROCEDURES
· A procedure is a unit of code designed to perform a particular sub-task of some main task. It is written out only once in some module, but can be used many times.
· The advantages of using procedures are:
· Improves code readability because the code does not need to be laid out as one long sequence.
· Allows code to be reused because it can be written once and called from more than one place in the code.
· Allows tasks to be broken down into simpler components because procedures can be written for certain tasks (procedures can also call other procedures).
· Modular code facilitates modification.
· A procedure is always enclosed within some code segment.
· A procedure is defined as:
PROCEDURE_NAME PROC
.
.
.
PROCEDURE_NAME ENDP
where PROCEDURE_NAME is any valid identifier.
· The PROC directive usually includes one of the operands NEAR or FAR. Example:
PROCEDURE_NAME PROC FAR
.
.
.
PROCEDURE_NAME ENDP
A NEAR procedure is defined in the same code segment from which it is called, and a FAR procedure is ordinarily defined in a separate code segment. Note if none of the operands NEAR or FAR follows the PROC directive, then the procedure is by default a NEAR procedure.
CALLING A NEAR PROCEDURE
· A procedure is invoked by a CALL instruction that can be direct or indirect. A direct procedure call has the format:
CALL PROCEDURE_NAME
· In an indirect near procedure call, the operand for the CALL instruction is either a 16-bit general-purpose register or a memory word containing the offset address of the procedure.
Example: An indirect procedure call using a register operand.
. . .
MOV SI , OFFSET COMP
CALL SI
. . .
COMP PROC NEAR
. . .
RET
COMP ENDP
· A procedure may be invoked by a JMP instruction if it does not return control to the caller:
JMP PROCEDURE_NAME
Executing a near CALL
· The return address to the calling program (the current value of the IP) is saved on the stack
· IP get the offset address of the first instruction of the procedure (this transfers control to the procedure)
RETURNING FROM A PROCEDURE
The RET instruction returns control to the caller of a procedure. There are two formats for the RET instruction:
RET
and
RET UnsignedInteger
The second form is used to discard parameters passed to the procedure through the stack. (This is discussed in another lecture )
Note: A procedure may have zero, one, or more RET instructions. A procedure will have no RET instruction in those programming situations where we don’t want to return control to the caller.
Executing a near RET
RET causes word at the top of the stack to be popped into IP (Since this value is the offset address of the statement after the CALL statement, control is transferred to that statement.)
THE GENERAL STRUCTURE OF AN EXE-FORMAT PROGRAM CONTAINING PROCEDURES
.MODEL SMALL
.STACK 0400H
.DATA
. . .
.CODE
MAIN PROC
MOV AX , @DATA ; Initialize DS
MOV DS , AX ;
. . .
CALL SUB1
. . .
CALL SUB2
. . .
MOV AX , 4C00H ; Return to DOS
INT 21H
MAIN ENDP
SUB1 PROC
. . .
RET
SUB1 ENDP
SUB2 PROC
. . .
CALL SUB3
. . .
RET
SUB2 ENDP
SUB3 PROC
. . .
RET
SUB3 ENDP
END MAIN
THE GENERAL STRUCTURES OF A COM-FORMAT PROGRAM CONTAINING PROCEDURES
.MODEL TINY .CODE ORG 100H MAIN PROC . . . CALL SUB1 . . . CALL SUB2 . . . MOV AX , 4C00H ; Return to DOS INT 21H MAIN ENDP SUB1 PROC . . . RET SUB1 ENDP SUB2 PROC . . . CALL SUB3 . . . RET SUB2 ENDP SUB3 PROC . . . RET SUB3 ENDP . . . ; Data definitions, if any . . . END MAIN | .MODEL TINY .CODE ORG 100H START: JMP MAIN . . . ; Data definitions, if any . . . MAIN PROC . . . CALL SUB1 . . . CALL SUB2 . . . MOV AX , 4C00H ; Return to DOS INT 21H MAIN ENDP SUB1 PROC . . . RET SUB1 ENDP SUB2 PROC . . . CALL SUB3 . . . RET SUB2 ENDP SUB3 PROC . . . RET SUB3 ENDP END START |
Note: If the entry point is specified in the END directive, the procedures may appear in any order.
PRESERVING THE VALUES OF REGISTERS IN PROCEDURES
It is good programming practice to preserve the values of all the registers modified by a procedure, except if a register is used by the procedure to return a value to the calling program. This is done by PUSHing (i.e., saving) the values of those registers in the stack at the beginning of the procedure, and then POPing (i.e., retrieving) them from the stack, in the reverse order, before the RET instruction.
Example: MYPROC PROC
PUSH AX
PUSH DX
. . . ; statements that modify AX and DX
. . . ;
POP DX
POP AX
RET
MYPROC ENDP
· Syntax of PUSH:
PUSH source
Where source is either imm8, imm16, imm32, segment register, 16- or 32-bit general purpose register. If source is imm8, then the value is zero or sign extended to 16-bits.
· Syntax of POP:
POP destination
Where destination is either mem16, mem32, segment register, 16- or 32-bit general purpose register.
Thus only words or double-words are pushed or popped from the stack. An 8-bit immediate value pushed in the stack is zero- or sign- extended to 16-bits.
Note: (a) Since the stack is used by the system to store addresses during procedure calls, and to store addresses and the values of the flags during interrupts, it is necessary that to every PUSH instruction in your program there is a corresponding POP instruction.
(b) To preserve the FLAGS register the instructions PUSHF and POPF are used.
To preserve the EFLAGS register the instructions PUSHFD and POPFD are used.
(c) PUSHA pushes the 16-bit general purpose registers in the order AX, CX, DX, BX, SP, BP, SI, and DI. POPA pops the 16-bit general purpose registers in the reverse order of PUSHA.
PUSHAD pushes the 32-bit general purpose registers in the order EAX, ECX, EDX, EBX, ESP, EBP, ESI,
EDI.
POPAD pops the 32-bit general-purpose registers in the reverse order of PUSHAD.
(d) USES directive: The PROC directive may have the form:
procedure_name PROC USES RegisterList
where:
RegisterList is a list of registers that are used by the procedure and which must be preserved. The registers in the list are separated by spaces. Including a register in this list will cause the assembler to automatically generate the necessary PUSH instruction to save the register value, and then to generate the necessary POP instruction to restore the value of that register before control is returned to the caller.
Example:
DISPLAY PROC USES AX BX CX
. . .
RET
DISPLAY ENDP
EXAMPLES
· Procedures should normally be general and not specific, i.e., a procedure must be written such that it can be used by passing to it different parameters.
· One way of passing parameters to procedures is to use registers.
· Example1: DISPLAY_STRING PROC
PUSH AX
MOV AH , 09H
INT 21H
POP AX
RET
DISPLAY_STRING ENDP
This procedure will be invoked by a call of the form:
. . .
MOV DX , OFFSET STRING_NAME
CALL DISPLAY_STRING
. . .
Note: If the previous procedure were coded as:
DISPLAY_STRING PROC
PUSH AX
PUSH DX
MOV AH , 09H
MOV DX , OFFSET STRING1
INT 21H
POP DX
POP AX
RET
DISPLAY_STRING ENDP
then a separate procedure will be required for every string to be displayed !
· Example2: STRING_DISPLAY PROC
PUSH AX
PUSH BX
MOV AH , 40H
MOV BX , 01H
INT 21H
POP BX
POP AX
RET
STRING_DISPLAY ENDP
This procedure will be invoked by a call of the form:
. . .
MOV CH , 00H
MOV CL , string_length
LEA DX , StringName
CALL STRING_DISPLAY
. . .
· Example3:
DISPLAY_CHAR PROC
PUSH AX
MOV AH , 02H
INT 21H
POP AX
RET
DISPLAY_CHAR ENDP
This procedure will be invoked by a call of the form:
. . .
MOV DL , character
CALL DISPLAY_CHAR
. . .
· Example4:
READ_CHAR PROC
; Returns the character read in the AL register
PUSH CX ; Preserve the CX register
MOV CH, AH ; Preserve the AH register
MOV AH , 01H
INT 21H
MOV AH, CH ; Restore the AH register
POP CX ; Restore the CX register
RET
READ_CHAR ENDP
Note: In this procedure the AX register is not pushed and then popped (i.e., it is not preserved) because the procedure returns a value to the calling program in the AL register. This procedure will be invoked by a call of the form:
. . .
CALL READ_CHAR
. . .
THE CALL AND RETURN MECHANISM FOR A NEAR PROCEDURE
Assume that a program defines a stack segment as:
.STACK 200
then the original state of this stack is:
| | | high address |
| | | |
byte 200 | | ¬ SP | |
byte 199 | | | |
byte 198 | | | |
| | | |
| | | |
| . | stack segment | |
| . | | |
| . | | |
| | | |
byte 1 | | | |
byte 0 | | ¬ SS | |
| | | low address |
Notice that the SP register is initialized to point one byte beyond the stack. The reason is that when a PUSH instruction:
PUSH Operand16
is executed the value in the SP register is automatically decremented by 2 and then the value of the word operand is pushed into the stack at the new word pointed to by SP:
(SP) ¬ (SP) - 2
(Word at Top of Stack) ¬ (Operand16)
In the above example, the first word to be pushed into the stack will be stored at bytes 198 and 199. The state of the stack after pushing one word is then:
| | | high address |
| | | |
byte 200 | | | |
byte 199 | XX | | |
byte 198 | XX | ¬ SP | |
| | | |
| | | |
| . | stack segment | |
| . | | |
| . | | |
| | | |
byte 1 | | | |
byte 0 | | ¬ SS | |
| | | low address |
When a POP instruction:
POP Operand16
is executed, the value of the word at the current top of the stack (i.e., the word pointed to by SP) is copied to the operand of the POP instruction and then the value of the SP register is automatically incremented by 2:
Operand16 ¬ (Word at top of stack)
(SP) ¬ (SP) + 2
Since the value of the popped word is no longer accessible in the stack it will be overwritten by a subsequent PUSH operation.
When a NEAR procedure is called the following sequence of events occurs:
· (SP) ¬ (SP) - 2
· (Word at top of Stack) ¬ (IP)
i.e., the offset address of the statement after the CALL statement is stored in the stack.
· (IP) ¬ offset address of first executable statement in the called procedure.
Since the logical address CS:IP now refers to the first executable statement in the called NEAR procedure, program execution continues with the execution of the procedure statements.
When a RET instruction is executed in the procedure the following sequence of events occurs:
· (IP) ¬ (Word at top of Stack)
i.e., restore the offset address of the statement, in the calling program, after the CALL statement.
· (SP) ¬ (SP) + 2
Since now the logical address CS:IP refers to the statement, in the calling program, after the CALL statement, program execution continues from that statement.
Note: For a NEAR procedure call the value of CS is not pushed in the stack, because both the calling program and the called procedure are in the same code segment; the value of CS does not change.
No comments:
Post a Comment