Использование подпрограмм
Общая идея использования подпрограмм очевидна: если в программе требуется многократно выполнять один и тот же фрагмент, его можно оформить в виде подпрограммы и вызвать по мере необходимости. Если подпрограмма не требует для своего выполнения никаких параметров и не должна возвращать в основную программу результат своей работы, то дело ограничивается оформлением текста подпрограммы в виде процедуры, завершающейся командой ret, и вызовом этой процедуры с помощью команды call. Как уже отмечалось ранее, подпрограмма может и не образовывать процедуру, а быть просто частью основной программы. Важно только, чтобы у нее была входная метка, и чтобы она завершалась командой ret.
В следующем примере подпрограмма delay используется для включения в основной текст программы программных задержек фиксированной величины.
Пример 3.8. Вызов подпрограммы без параметров.
code segment assume cs:code,ds:data delay proc; Процедура-подпрограмма push CX; Сохраним СХ основной программы mov CX,2000; Счетчик внешнего цикла del1: push CX; Сохраним его mov CX,0; Счетчик внутреннего цикла del2: loop del2; Внутренний цикл (64К шагов) pop CX; Восстановим внешний счетчик loop del1; Внешний цикл (2000 шагов) pop CX; Восстановим СХ программы ret; Возврат в подпрограмму delay endp main proc mov AX,data; Настроим DS mov DX,AX; на сегмент данных mov AH,09h; Функция вывода на экран mov DX,offset npl1; Адрес первой строки mov CX,3; Будем выводить строки в цикле cntrl1: int 21h; Вызов DOS cal1 delay; Вызов подпрограммы задержки add DX,msg_len; Прибавим к смещению длину строки loop cntrl; Цикл вызовов DOS mov AX,4C00h; Завершение программы int 21h main endp code ends data segment msg1 db "Процесс стартовал",13.10,'$' msg_len=$-msg1 msg2 db "Процесс идет",13.10,'$' msg3 db "Процесс завершается",13.10,'$' data ends stk segment stack dw 128 dup(') stk ends end main
В тексте программы сначала описана процедура-подпрограмма, затем основная программа. Как уже отмечалось, порядок их описания роли не играет; важно только, чтобы в завершающей директиве окончания трансляции end был указан в качестве точки входа адрес основной программы (main в нашем примере).
Подпрограмма реализует задержку с помощью вложенных циклов с командой loop, использующей в качестве счетчика шагов регистр СХ. В основной программе этот регистр используется для организации цикла вывода трех строк. Поэтому первое, что должна сделать подпрограмма – это сохранить содержимое регистра СХ, для чего естественно использовать стек. Перед завершающей командой ret регистр СХ должен быть восстановлен. Фрагмент, реализующий задержку, был описан ранее, в разделе 3.2.
Основная программа выводит на экран с помощью функции 09h три строки текста. Для упрощения программы, а также чтобы продемонстрировать некоторые приемы программирования, вывод строк реализован в цикле. Строки сделаны одной длины, и модификация смещения к очередной строке выполняется прибавлением к содержимому регистра DX длины строки. Полезно обратить внимание на организацию цикла в основной программе. В цикл, помимо команды вызова подпрограммы задержки и предложения, модифицирующего регистр DX, включена лишь команда int 21h. Регистр АН с номером функции заново не настраивается. Это и не нужно, так как DOS, выполняя затребованную операцию, первым делом сохраняет все регистры программы, а перед возвратом в программу их восстанавливает.
Поэтому, вызывая функции DOS (или BIOS) можно не заботиться о сохранении регистров – их содержимое система на разрушает. Надо только иметь в виду, что многие функции DOS и BIOS после своего завершения возвращают в программу некоторую информацию (число реально введенных символов, доступный объем памяти, номер видеорежима и т.п.) Обычно эта информация возвращается в регистре АХ, однако могут использоваться и другие регистры или их сочетания. Поэтому, обращаясь в программе к системным функциям, необходимо ознакомиться с их описанием и, в частности, посмотреть, какие регистры они могут использовать для возвращаемых значений.
Запустив программу, можно убедиться в том, что строки текста появляются на экране через заметные промежутки времени.