Вы находитесь на странице: 1из 12

ELEC 242 - Subroutines

Subroutines
Subroutines are procedures written separate from the main program. Whenever the main
program must perform a function that is defined by a subroutine, it calls the subroutine into
operation. In order to do this, control must be passed from the main program to the starting
point of the subroutine. Execution continues with the subroutine and upon completion control is
returned back to the main program at the instruction that follows the one that called the
subroutine. Notice that the difference between the operation of a subroutine call and a jump is
that a call to a subroutine not only produces a jump to an appropriate address in program
storage memory, but it also has a mechanism for saving information such as IP and CS, which
is needed to return back to the main program. We should begin by defining some standard
programming language terms.
These terms are generic and are language dependent.
 Subroutine – a section of code that is that is typically designed to be called more than
once from different points in a program.
 Function – a subroutine that returns a result
 Procedure – a subroutine that does not return a result
In assembly language, a Function is a Procedure that returns a result, and the terms Procedure
and Subroutine are interchangeable. It is common practice today to refer to all assembly
language subroutines as procedures since the term is more closely associated with current high
level programming languages.
So just what is a subroutine? It is a transfer of control statement that is invoked with the CALL
instruction. The other transfer of control statement is the JUMP. We learned about the different
types of jump when we studied the loop. A CALL is similar to a JUMP except:
 The address of the next instruction after the CALL instruction is saved.
 The IP or CS:IP is reloaded with the address of the subroutine
 The code for the subroutine is executed
 When the subroutine is completed, a RETurn is executed which reloads the IP or CS:IP
with the address of the instruction immediately after the CALL
 The program resumes from there.
So the difference between a JUMP and a CALL is that the JUMP does not preserve the address
(IP or CS:IP) of the next instruction after the call, and, therefore, or program cannot return to
the next instruction after the JUMP.
There are two types of procedures: Near and Far.

NEAR Procedures
When the subroutine is located in the same segment, usually the same code segment, as the
CALL statement, this is a near for the near procedure. In the following examples, Procname is
any label we wish to use for the name of the procedure. The assembly language statement for
the near procedure is:
call Procname
In addition, the subroutine code would be:
Procname proc
; procedure code here
ret

 1998 – 2003 Andrew H. Andersen Page 1


ELEC 242 - Subroutines

Procname endp
Before branching to the near subroutine, the call instruction causes the IP to be PUSHed on the
Stack. The offset address of the subroutine in the current code segment is loaded in the IP and
program execution continues from there.
When the subroutine is finished, the RETurn instruction causes the contents of the stack to be
POPed into the IP. Program execution continues from the memory location immediately after
the call instruction. In place of RET, we could have used the near return instruction RETN.
However, the assembler will use the correct return since it knows this is a near procedure.

FAR Procedures
When the subroutine and the CALL statement are located in different segments, this is a near
for the far procedure. The assembly language statement for the far procedure is:
CALL far ptr Procname
; procedure code here
ret
Procname endp
In addition, the subroutine code would be:
Procname proc far
; procedure code here
ret
Procname endp

CALL and RET Instructions


There are two basic instructions in the instruction set of the 8086 for subroutine handling. They
are the call (CALL) and return (RET) instructions. Together they provide the mechanism for
calling a subroutine into operation and returning control back to the main program at its
completion. We will first discuss these two instructions and later introduce other instructions,
which can be used in conjunction with subroutines.
Just like the JMP instruction, CALL allows implementation of two types of operations, the
intrasegment CALL and the intersegment CALL.

It is the operand that initiates either ran inter segment or an intrasegment call. The operands
Near-proc, Memptr16, and Regptr16 all specify intrasegment calls to a subroutine. In all three
cases, execution of the instruction causes the contents of IP to be saved on the stack. Then the
stack pointer (SP) is decremented by 2. The saved value of IP is the address of the instruction
that follows the CALL instruction. After saving the return address, a new 16-bit value, which
corresponds to the storage location of the first instruction in the subroutine, is loaded into IP.
The three types of intrasegment operands represent different ways of specifying this new value
of IP. In a Near-proc operand, the displacement of the first instruction of the subroutine from
the current value of IP is supplied directly by the instruction. An example is

CALL NEAR PROC Here the label NEAR determines the 16-bit displacement and is coded as
an immediate operand following the opcode for the call instruction. Call is actually a relative
addressing mode instruction; that is, the offset address is calculated relative to the address of
the call instruction itself. With 16 bits, the displacement is limited to 32K bytes.

 1998 – 2003 Andrew H. Andersen Page 2


ELEC 242 - Subroutines

The Memptr16 and Regptr16 operands provide indirect subroutine addressing by specifying a
memory location or an internal register, respectively, as the source of a new value for IP. The
value specified in this way is not a displacement. It is the actual offset that is to be loaded into
IP. An example of the Regptr 16 operand is

CALL BX
When this instruction is executed, the contents of BX are loaded into IP and execution
continues with the subroutine starting at a physical address derived from CS and IP.

By using one of the various addressing modes of the 8086, an internal register can be used as a
pointer to an operand that resides in memory. This represents a Memptr16 type of operand. In
this case, the value of the physical address of the off- set is obtained from the current contents
of the data segment register OS and the address of addresses held in the specified registers. For
instance, the instruction CALL [BX] has its subroutine offset address at the memory location
whose physical address is derived from the contents of OS and BX. The value stored at this
memory location is loaded into IP. Again the current contents of CS and the new value in IP
point to the first instruction of the subroutine.

Notice that in both intrasegment call examples the subroutine was located with- in the same
code segment as the call instruction. The other type of CALL instruction, the intersegment call,
permits the subroutine to reside in another code segment. It corresponds to the Far-proc and
Memptr32 operands. These operands specify both a new offset address for IP and a new
segment address for CS. In both cases, execution of the call instruction causes the contents of
the CS and IP registers to be saved on the stack and then new values are loaded into IP and CS.
The saved values of CS and IP permit return to the main program from a different code
segment.

Far-proc represents a 32-bit immediate operand that is stored in the four bytes that follow the
opcode of the call instruction in program memory. These two words are loaded directly from
code segment memory into IP and CS with execution of the CALL instruction. An example is
the instruction

CALL FAR PROC


On the other hand, when the operand is Memptr32, the pointer for the sub- routine is stored as
four bytes in data memory. The location of the first byte of the pointer can be specified
indirectly by one of the 8086's registers. An example is CALL FAR [DI] Here the physical
address of the first byte of the four-byte pointer in memory is derived from the contents of DS
and DI.

Every subroutine must end by executing an instruction that returns control to the main program.
This is the return (RET) instruction. Its execution causes the value of IP or both the values of IP
and CS that were saved on the stack to be returned back to their corresponding registers. In
general, an intrasegment return results from an intrasegment call and an intersegment return
results from an intersegment call.
There is an additional option with the return instruction. It is that a two-byte code following the
return instruction can be included. This code gets added to the stack pointer after restoring the
return address into IP or IP and CS for Far-proc calls. The purpose of this stack pointer

 1998 – 2003 Andrew H. Andersen Page 3


ELEC 242 - Subroutines

displacement is to provide a simple means by which the parameters that were saved on the
stack before the call to the subroutine was initiated can be discarded.

After the context switch to a subroutine, we find that it is usually necessary to save the contents
of certain registers or some other main program parameters. These values are saved by pushing
them onto the stack. Typically, these data correspond to registers and memory locations that are
used by the subroutine. In this way, their original contents are kept intact in the stack segment
of memory during the execution of the subroutine. Before a return to the main program takes
place, the saved registers and main program parameters are restored. This is done by popping
the saved values from the stack back into their original locations.
Before branching to the far subroutine, the call instruction causes both the CS and the IP to be
PUSHed on the Stack. The segment address and the offset address of the subroutine in the a
different segment are loaded in the CS and IP and program execution continues from there.
When the subroutine is finished, the RETurn instruction causes the CS and IP to be POPed
from of the stack. Program execution continues from the memory location immediately after
the call instruction. Since the subroutine was invoked as a far procedure, we actually execute a
RETF even though we may use the RET.

Nested Procedures
A nested procedure is a procedure called from within a procedure. There is nothing significant
here, and all earlier comments apply. The setup may be similar to the following:
call ProcName
In addition, the subroutine code would be:
ProcName proc
; procedure code here
call Time_Delay
; perhaps more code here
ret
ProcName endp

Time_Delay proc
; procedure code here
ret
Time_Delay endp

Placement of Subroutines in the Code Segment


We must take care that we do not place a subroutine where the main program or any procedure
may walk into the subroutine. This would cause our program to crash because the RET would
POP an address into the IP that is not the address of the next instruction. Therefore, we should
place all subroutines after the normal MS/DOS program exit statements, and immediately
before the .exit assembler directive. We should also make sure that we do not inadvertently
place the procedure statements inside another procedure.
We could place the subroutine earlier in the code segment as long as the instruction
immediately before the CALL is an unconditional JUMP. However, this is not advised.

 1998 – 2003 Andrew H. Andersen Page 4


ELEC 242 - Subroutines

Note: Some texts and programmers consider the main part of the program to be a procedure.
You may feel free to do so. Just make sure your subroutines are after main. If you do, then the
code segment of your program would look like this:
.code
.startup
main proc
;program goes here
mov ah,04ch
int 21h
main endp

sub1 proc
; subroutine code
sub1 endp

sub2 proc
; subroutine code
sub2 endp

.exit
end

Preserving Registers
The instruction that is used to save parameters on the stack is the push (PUSH) instruction and
that used to retrieve them back is the pop (POP) instruction. The standard PUSH and POP
instructions can be written with a general-purpose register, a segment register except CS, or a
storage location in memory as their operand.
Registers that will be used during the subroutine that contain data that is necessary on the return
should be preserved. While there are many ways to do this, the easiest method is to PUSH them
on the stack. A few things that we must remember are:
 We must make sure to create a Stack, and make it large enough, for any program that
has a CALL
 POP all registers that contain data or addresses that we need after the RETurn
 We must POP each register that we PUSH
 We must POP in the reverse order of the PUSH
 If we PUSH after the CALL, we must POP before the RETurn (inside the subroutine) or
if we PUSH before the CALL, we must after the RETurn (in the calling module or
subroutine) The former is preferred.
PUSH Examples
PUSH AX – the contents of a 16-bit register
PUSH EBX - the contents of a 32-bit register
PUSHA (286 and higher) – Preserves all usable registers of 80286
PUSHAD (386 and higher) – Preserves all usable registers of 80386

 1998 – 2003 Andrew H. Andersen Page 5


ELEC 242 - Subroutines

Note: POPA and POPAD restore the registers in the reverse order of the PUSH, so you do not
have to worry about scrambling register contents. While this is the easiest way, it takes longer
than specific PUSHes, and requires a large Stack if we PUSH many times before POPping.

Execution of a PUSH instruction causes the data corresponding to the operand to be pushed
onto the top of the stack. For instance, if the instruction is PUSH AX its execution results in the
following:
SP <- SP - 1 ;SP is decremented
SS:SP <= AH ;AH is PUSHed on the Stack
SP <- SP - 1 ;SP is decremented
SS:SP <= AL ;AL is PUSHed on the Stack

This shows that the two bytes of AX are saved in the stack part of memory and the stack
pointer is decremented by 2 such that it points to the new top of the stack. On the other hand, if
the instruction is POP AX, its execution results in the following:
AL <- SS:SP ;AL is POPped from the Stack
SP <- SP + 1 ;SP is incremented
AH <= SS:SP ;AH is POPped from the Stack
SP <- SP + 1 ;SP is incremented

Parameter Passing in Registers


Often a subroutine will require data or addresses that are known to the calling module, such as
an address of some message that we wish to display for the user. The most common method in
assembly language to pass parameters between the calling module and the subroutine is to pass
them in a register. There are many different ways to pass data to or from a subroutine:
 We can pass a single byte, word, or double word data to or from a subroutine in an
appropriate 8-bit, 16-bit or 32-bit register.
 We can pass multiple pieces of data in memory using a label to identify the address of
the data. This memory location can be understood by the calling module and the
subroutine
 We can pass data in an array, and pass the address of the array in a register. If necessary,
we could also pass the number of pieces of data in another register, or as the first entry
in the array.

Video BIOS Revisited


INT 10H Video BIOS Routines
Function 6 – Screen Scroll Up
In MS/DOS, the display consists of 80 columns across, and 25 rows. There are also 16 different
colors that can be assigned. Each character on the display is 8 pixels wide and 8 pixels high. We
make characters by illuminating or darkening each pixel of the 64 pixels that makes each
character. In the CGA mode, the screen resolution is 640 pixels (80 columns of characters by 8
pixels per character) by 200 (25 rows x 8 pixels per character).
Armed with the knowledge of the row and column position where we wish to print, we may
place the cursor anywhere you like on the display. Once positioned, the cursor moves one

 1998 – 2003 Andrew H. Andersen Page 6


ELEC 242 - Subroutines

character to the right for each additional character. When we clear the screen in MS/DOS, the
screen is cleared and cursor is homed to the top left corner of the screen. As characters are sent
to the CRT, the cursor moves from left to right and top to bottom. When the display is filled, the
screen scrolls up.
Clear Screen Using Screen Scroll Up
Function 6: Screen Scroll Up
With appropriate
Entry Parameters:
values, this function
clears the Register AH: 06H screen just
like the Register AL: number of rows to scroll, 0 for all DOS
command Register BH: see explanation below CLS.
Register CH: Top Row number
Register CL: Top Column Number
Register DH: End Row Number
Register DL: End Column Number
Exit Parameter:
Display is scrolled
Uses INT 10H
Requirements: If the 7 registers contain data that must be used after the interrupt (before
initialization) they should be pushed on the Stack, or saved in storage.
Register AH <= 06H the Video BIOS Function
Register AL <= Number of rows to scroll (0 for all)
Register BH <= Controls foreground and background
Register CH <= Row number at top of the region
Register CL <= Column number at top left of the region
Register DH <= Row number at bottom of the region
Register DL <= Column number at bottom right of the region

VIDEO DISPLAY NUMBER


D7 D6 D5 D4 D3 D2 D1 D0
I/B Red Green Blue Intensity Red Green Blue
Background Color Foreground Color
In a color monitor with three guns, Red-Green-Blue (RGB) colors are obtained by turning a
gun on or off at each pixel position. Each gun has a corresponding bit in the Video Display
Number for the Foreground Color and the Background Color. Foreground color is usually the
text, and the background is the rest of the display. For Black RGB = 000 (all guns off). For
White, RGB = 111 (all guns on). In Hex, White text on a Black background has a value of 07H.
A dark Blue background with bright Yellow text is 1EH.
This is not intuitive for many colors.
Depending on the mode of operation, the MSB of the background is either for intensity (if blink
is not enabled) or Blink (if blink is enabled) The default is intensity.

 1998 – 2003 Andrew H. Andersen Page 7


ELEC 242 - Subroutines

We can put his routine anywhere. However, it seems like something we may wish to do from
various locations in a program, so we will discuss how to do this as a procedure
Clear Screen Algorithm
AH <= 6 ; The Video BIOS function for scroll up
AH <= 7 ; The Video BIOS function for scroll down (use one or the other)
AL <= Number of lines to scroll, 0 = all
CX <= Start row column info (0 for top left)
We may enter the Row in CH and the column in CL separately
DX <= End row column data. The data in Hex is
For the bottom, the 25th row is 19H and placed in DH
For the right, the 80th column is 50H and placed in DL
So DX <= 1950h
BH <= foreground/background information
1EH for a dark blue screen with bright yellow text
As a procedure, we might wish to define the Video BIOS data as words and bytes, and just load
the registers as needed:
.data
vbios dw 600h
row0 dw 0
row26 dw 1950h
color db 1Eh
At the end of the main code area, we would write the clear screen procedure
CLR PROC
AX <= vbios
CX <= row0
DX <= row26
BH <= color
INT 10h
RET
CLR endp

Function 2 - Direct Cursor Positioning


Often, we wish to place the cursor in a specific location on the display and begin writing data to
the display from that point on. Once we place the cursor at a particular position on the display,
writing to the display functions normally.
We identify the row and column where we wish to place the cursor as an offset from the top left
position on the display. Positioning the cursor function does not erase the display from the top
left to the new cursor position so any current messages above and to the left of the new cursor
position is retained on the display. However, any screen contents from the current position will
be overwritten one character at a time as data are sent to the display at the current cursor
position.

 1998 – 2003 Andrew H. Andersen Page 8


ELEC 242 - Subroutines

Function 2: Set Cursor Position


Entry Parameters:
Register AH: 02H
Register DH: Row position (0 – 24)
Register DL: Column position (0 – 79)
Register BH: Video Page number

Exit Parameter:
Cursor positioned on display

Uses INT 10H

This interrupt is used to position the cursor at a desired row and column. When used with BIOS
INT 21H Function 9, the output string begins at the current cursor location and continues from
there normally.
Requirements: If the registers contain data that must be used after the interrupt (before
initialization) they should be pushed on the Stack, or saved in storage. The pseudo code to
initialize this function is:
Register AX <= 02H
Register DH <= Row data from 0 to 24
Register DL <= Column data from 0 to 79
Register BH <= Video Page (always 0 in this course)
Here is a typical setup modifying the previous example. Assume we wish to start on Row 7
Column 20. We could load DX (061F) or load DH (06) and DL (1F) Keep in mind, the
computer counts from 0 so you must subtract 1. Here is a simple example.
.data
Position1 dw 61Fh

There may be other declarations. In the code section, there may be other statements. This
routine might be coded directly where needed or as a procedure. More than likely, this will be
written and a procedure, so that is how it will be demonstrated.
We will first look at the basic procedure where we will always place the cursor in the same
position.

SetCur proc
DX <= Position1 ;the row and column data
AH <= 2 ;The BIOS function
BH <= 0 ;video page no. 0
INT 10h
RET
SetCur endp

 1998 – 2003 Andrew H. Andersen Page 9


ELEC 242 - Subroutines

Subroutine Examples
We wish to display multiple messages or prompts on the console device to the user so that
he/she will enter a reply via the console device. The message addresses are passed into the
subroutine. The subroutine displays the message and waits for a reply. When it receives a reply,
it returns the reply in a register.

Subroutine GetIt
On Entry
DX has the address of the message
We display the message using BIOS function 9
On Exit
AH has the Character
We receive input using BIOS Function 1
A subroutine is used since multiple prompts and replies will occur

TITLE Subrountine example written by A. H. Andersen

; Procedure Example
;
; written by ANDREW H. ANDERSEN, JR.
;
;

.model small
.stack 1000h
.data

conin equ 1
prnt equ 9

; For Cursor Positioning


line1 equ 0515h
line2 equ 0615h
line3 equ 0715h
line4 equ 0915h
line5 equ 0a15h
line25 equ 1914h

; For user prompts and messages


msg1 db 'To exit: enter <CR> instead '
db 'of entering the answer.$'

prmpt1 db 'Message 1 Y/N -----> $'


prmpt2 db 'Message 2 Y/N -----> $'
prmpt3 db 'Message 3 Y/N -----> $'
prmpt4 db 'Message 4 Y/N -----> $'
prmpt5 db 'Message 5 Y/N -----> $'

 1998 – 2003 Andrew H. Andersen Page 10


ELEC 242 - Subroutines

.code
.startup

mov ax,@data
mov ds,ax

init:
call Clr

mov dx,offset line25


call SetCur
lea dx,msg1
call ShowIt

mov dx,offset line1


call SetCur
lea dx,prmpt1
call ShowIt

mov dx,offset line2


call SetCur
lea dx,prmpt2
call ShowIt

mov dx,offset line3


call SetCur
lea dx,prmpt3
call ShowIt

mov dx,offset line4


call SetCur
lea dx,prmpt4
call ShowIt

mov dx,offset line5


call SetCur
lea dx,prmpt5
call ShowIt

fini:
mov ah,04ch
int 21h

; All Procedures go here

Clr proc
;Clear Screen with 25 line scroll.

mov ah, 6 ;The BIOS function


mov al, 0 ;Scroll # of lines 0 = all
mov cx, 0 ;row/col at top
mov dx, 1950h ;row/col at bottom
mov bh, 1eh ;foreground/background colors
int 10h

ret
Clr endp

 1998 – 2003 Andrew H. Andersen Page 11


ELEC 242 - Subroutines

SetCur proc
;On Entry DX must have row column position
;On Exit The Cursor is positioned

mov ah, 2 ;The BIOS function


mov bh, 0 ;video page no. 0
int 10h

ret
SetCur endp

ShowIt proc
; On Entry DX must have address of message
; On Exit, AL has a Y or N
mov ah,prnt
int 21h
mov ah,conin
int 21h
ret

ShowIt endp

.exit
end

 1998 – 2003 Andrew H. Andersen Page 12

Вам также может понравиться