4.6. Вызов подпрограмм

Программа организуется как набор подпрограмм, которые могут находиться в различных текстовых файлах и объектных модулях, объединяемых компилятором tasm.exe и сборщиком tlink.exe в загрузочный модуль. Среди этих подпрограмм выделяется главное – это подпрограмма, на которую передаёт управление загрузчик. Главная подпрограмма вызывает в процессе работы подпрограммы с помощью команды CALL, которые, в свою очередь, возвращают ей выполнение с помощью команды RET.

Команды CALL и RET. Команда CALL выполняет вызов процедуры, адрес которой может задаваться смещением, либо сегментом и смещением. В последнем случае вызов называется межсегментным. Алгоритм работы команды CALL:

1) если вызов межсегментный, то CS запоминается в стек и CS устанавливается равным адресу сегмента подпрограммы;

2) счётчик команд IP запоминается в стек и IP устанавливается равным смещению операнда.

(При записи в стек из SP предварительно вычитается 2.)

Команда возврата RET просто извлекает из стека значение регистра IP. Если возврат длинный, то применяется команда RETF. В этом случае после извлечения IP производится извлечение CS. Команда может иметь операнд:

ret    cnt   ;cnt – произвольная константа

после возврата из процедуры увеличивается SP на cnt.

Подпрограммы. Программа разбивается на небольшие модули, состоящие из групп команд, которые называются процедурами или подпрограммами и записываются следующим образом:

name1  proc

…           ;группа команд

ret

name1  endp

name2  proc  far

       …           ;группа команд

       retf

name2  endp

Здесь name1 и name2 – произвольные имена, proc – директива, обозначающая начало процедуры, endp – директива, обозначающая конец процедуры. Если к директиве proc добавляется слово far, то подпрограмма имеет дальний адрес. Если указано слово near, то процедура должна находиться в одном сегменте с вызывающей процедурой. Слово near может быть опущено, ибо оно подразумевается по умолчанию.

Пример 1. Напишем программу, состоящую из главного модуля и подпрограммы, выводящей символ из регистра DL на экран:

<1>        title display.exe

<2> mycode segment

<3>        assume     cs:mycode

<4> main   proc  far

<5>        push es          ;для

<6>        mov  ax,0        ;возврата

<7>        push ax          ;в операционную систему

<8>        mov  dl,’a’

       call display     ;вывод ‘a’

<1>

<2>        mov  dl,’b’

<3>        call display     ;вывод ‘b’

<4>        mov  dl,’c’

<5>        call display     ;вывод ‘c’

<6>        ret              ;выход в MS-DOS

<7> main   endp

<8> display     proc

<9>        mov  ah,2

<10>        int  21h

<11>        ret

<12> display     endp

<13> mycode ends

<14>        end  main        ;на главную подпрограмму

Здесь выход в MS-DOS осуществляется с помощью команды ret, которая передаёт управление на команду с адресом ES:0 (По этому адресу находится команда int 20h – вызов прерывания, производящего выход.)

Внешние подпрограммы. Подпрограммы называются внешними, если их объектные модули находятся в отдельных файлах или библиотеках. Для вызова внешней подпрограммы необходимо описать эту подпрограмму с помощью одной из директив

extrn  имя:far

extrn  имя:near

Доступ к подпрограмме или к другим данным вызываемого модуля обеспечивается директивой

public      имя

Пример 2. Главный модуль и вызываемую подпрограмму из примера 1 запишем в два файла. Добавим вызываемую подпрограмму Kbin ввода символа с клавиатуры. Получим следующий ниже текст. Первый, displ.asm:

<1>        title displ.exe

<2> mycode      segment

<3>        assume           cs:mycode

<4> extrn  display:near

<5> extrn  kbin:near

<6> ;— модуль mylib.obj —

<7> go:    mov   dl,’a’

<8>        call display

<9>        mov  dl,’b’

<10>        call display

<11>        mov  dl,’c’

<12>        call display

<13>        call kbin  ;ожидание 1C

<14>        ret

<15> mycode ends

<16>        end  go

Вызываемые подпрограммы запишем во втором файле mylib.asm:

<1> title  mylib.obj

<2> subr   segment

<3> public display,kbin

<4>        assume     cs:subr

<5> ;— подпрограммы —

<6> display proc

<7>        mov  ah,2

<8>        int  21h

<9>        ret

<10> display     endp

<11> kbin   proc near

<12>        mov  ah,1

<13>        int  21h

<14>        ret

<15> kbin   endp

subr  ends

<1>

<2>        end

Компиляция и сборка выполняются после ввода команд:

tasm        displ

tasm        mylib

tlink mylib+displ

В результате будет создан загрузочный модуль displ.exe, который после загрузки выведет символы abc и, после ввода ctrl/c, закончит работу.