Новые операторы
Сегодня рассмотрим всего один оператор, но зато какой!
Команда | Перевод (с англ.) | Назначение | Процессор |
CALL метка | call - вызов | Вызов подпрограммы | 8086 |
Итак, вы уже немного знакомы с подпрограммами.
Допустим, нам необходимо написать программу, которая выводит на экран сообщение
Нажмите любую клавишу...
ждет нажатия клавиши, а затем выводит еще одно сообщение:
Вы успешно нажали клавишу!
ждет от пользователя клавишу и завершается.
Что нужно для этого? Вызвать два раза функцию 09h прерывания и столько же функцию 10h прерывания (вы это уже прекрасно знаете).
Вот так: ... (1) mov ah,9 (2) mov dx,offset Mess1 (3) int 21h (4) mov ah,10h (5) int 16h (6) mov ah,9 (7) mov dx,offset Mess2 (8) int 21h (9) mov ah,10h (10) int 16h (11) int 20h ... (12) Mess1 db 'Нажмите любую клавишу...$' (13) Mess2 db 'Вы успешно нажали клавишу!$' ...
Там, где стоит многоточие, опущен код программы. Я буду часто так делать для того, чтобы постоянно не печатать операторы, тем самым занимая место. Я думаю, вы без труда разберете, какие команды необходимо вставить...
Смотрим дальше. Строки (1) - (3) и (6) - (8) выводят строку. Они очень похожи, за исключением загрузки в DX разных строк.
Строки же (4) - (5) и (9) - (10) полностью идентичны. Получается, что мы теряем байты...
Чтобы упростить программу и, тем самым, уменьшить ее размер, воспользуемся оператором CALL (создадим подпрограммы). Вот что у нас получится: ... (1) mov dx,offset Mess1 (2) call Out_string (3) call Wait_key (4) mov dx,offset Mess2 (5) call Out_string (6) call Wait_key (7) int 20h (8) Out_string proc (9) mov ah,9 (10) int 21h (11) ret (12) Out_string endp (13) Wait_key proc (14) mov ah,10h (15) int 16h (16) ret (17) Wait_key endp (18) Mess1 db 'Нажмите любую клавишу...$' (19) Mess2 db 'Вы успешно нажали клавишу!$' ...
Длинее получилось... Не обращайте на это пока внимания. Подпрограммы используются в более сложных программах. Наш же вариант представлен для изучения.
Итак, что здесь происходит? Думаю, что многие уже догадались. Однако, нужны некоторые пояснения.
Минутку внимания! В строке (1) загружаем в DX адрес строки Mess1. В строке (2) вызываем подпрограмму, которую мы назвали Out_string. Что делает компьютер? Он запоминает адрес (смещение) следующей команды (строка (3)) и переходит на метку Out_string (строка (8)). DX при этом не меняется (т.е. в нем сохраняется адрес строки Mess1)! В строках (9) - (10) используем функцию 09h прерывания для вывода строки на экран. В строке (11) компьютер берет запомненный адрес и переходит на него (в данном случае на строку (3)) (ret - return - возврат). ВСЕ! Процедура отработала!
У вас, вероятно, появился вопрос: А где это, интересно, компьютер запоминает куда нужно возвращаться???
Отвечу вкратце, т.к. эта тема достойна отдельного выпуска. Есть такая область памяти. Назвается стек (stack). Вот именно туда и "ложится" адрес для возврата из подпрограммы. В следующем выпуске мы подробно рассмотрим стек, сегмент стека, а также регистры SS и SP, которые отвечают за стек.
Надеюсь, что разобраться в дальнейшей работе программы (начиная со строки (3)) не составит труда. Все ведь очень просто!
Обратите внимание на оформление процедуры: ______________ Out_string proc Out_string endp ______________
Out_string - название процедуры
Proc - procedure - процедура
endp - end procedure - конец процедуры
Стоит еще отметить, что из одной подпрограммы можно вызывать другие. Из других - третьи. Главное правило - не запутаться в возвратах, иначе компьютер просто виснет! Внимательно следить за работой подпрограмм позволяет отладчик.