Возможен следующий синтаксис описания процедуры:
ИМЯ proc [[МОДИФИКАТОР_ЯЗЫКА] ЯЗЫК] [РАССТОЯНИЕ]
[arg СПИСОК_АРГУМЕНТОВ]
[local СПИСОК_АРГУМЕНТОВ]
...
[ИМЯ] endp
Приведем синтаксис определения передаваемых процедуре аргументов:
arg АРГУМЕНТ [, АРГУМЕНТ ] ... [ =ИМЯ ]
При определении локальных переменных процедуры используется следующий синтаксис:
local АРГУМЕНТ [, АРГУМЕНТ ] ... [ =ИМЯ ]
Отдельные аргументы имеют следующий синтаксис:
ИМЯ_АРГУМЕНТА [[ СЧЕТЧИК_1 ]] [: ТИП [: СЧЕТЧИК_2 ]]
Здесь ТИП – это тип данных аргумента – byte, word, dword и т.п.
СЧЕТЧИК_2 задает, сколько элементов данного типа определяет аргумент.
Например, в определении аргумента:
arg TMP: dword: 4
определяется аргумент с именем TMP, состоящий из 4 двойных слов. По умолчанию СЧЕТЧИК_2 имеет значение 1 (кроме аргументов типа byte. Так как вы не можете занести в стек байтовое значение, для аргументов типа byte значение счетчика по умолчанию равно 2, что обеспечивает для них в стеке размер в слово.
Например:
arg REALBYTE: byte: 1
СЧЕТЧИК_1 представляет собой число элементов массива. Если поле СЧЕТЧИК_1 не задано, то по умолчанию оно равно 1.
Если список аргументов завершается символом равенства (=) и идентификатором, то ассемблер будет приравнивать этот идентификатор к общему размеру блока аргументов (в байтах). Если вы не используете автоматическое использование соглашений языков высокого уровня в ассемблере, то можете использовать данное значение в конце процедуры в качестве аргумента инструкции ret.
Аргументы и переменные определяются в процедуре как операнды в памяти относительно BP. Передаваемые аргументы, определенные с помощью директивы arg, имеют положительное смещение относительно BP. Локальные переменные, определенные с помощью директивы local, имеют отрицательное смещение от BP.
Например:
FUNC1 proc NEAR
arg A:word, B:word:4, C:byte =ArgSize
local X:dword, Y:word:2 =LocSize
...
Здесь A определяется, как [BP+4], B – [BP+6], C – [BP+14], а ArgSize – 20. X – [BP-2], Y – [BP-6], а LocSize – 8.
Область действия имен
Если для аргументов и локальных переменных, а также меток операторов в подпрограмме, не заданы имена с предшествующим префиксом локального идентификатора, все аргументы, заданные в заголовке процедуры, определены ли они с помощью директивы arg или local и метки имеют глобальную область действия.
Идентификаторы с локальной областью действия разрешает директива locals (не путать с директивой local).
Например:
locals __
TST1 proc
arg __A: word, __B: word, __C: byte
local __X: word, __Y: dword
...
В этом примере директива locals __ определяет двойной символ подчеркивания как префикс локальных имен. Это означает, что все имена, начинающиеся с данной пары символов, будут считаться локальными в пределах подпрограммы.
Итоги
Соединив все, что было сказано в предыдущих параграфах, можно предложить следующий шаблон подпрограммы:
locals __
...
Func proc near
arg __p1: word, __p2: word, ... = __ArgSize
local __v1: byte, __v2: word, ... = __LocSize
;Действие: ...
;Входные параметры: ...
;Выходные параметры: ...
;Возвращает: ...
;Обращение: ...
;Замечания:
; регистры не модифицирует
; стек чистит от параметров
push BP ;+ BP - указатель
mov BP, SP ;- кадра стека
sub SP, __LocSize ;выделение памяти для лок. переменных
push SI, DI ;сохранение регистров
...
mov SI, __p1 ;доступ к параметру
...
mov __v2, SI ;доступ к локальной переменной
...
pop DI, SI ;восстановление регистров
mov SP, BP ;чистка стека от лок. переменных
pop BP ;восстановление BP
ret __ArgSize ;возврат и чистка стека от параметров
Func endp
ПРИМЕР ВЫПОЛНЕНИЯ РАБОТЫ
Разработать подпрограмму, которая удаляет, начиная с заданной позиции строки, указанное число символов. Разработать программу, которая вводит с клавиатуры строку, вводит позицию и длину удаляемой части строки и удаляет эту часть.
Текст программы:
locals __
model small
stack 100h
dataseg
MESS1 db 0dh,0ah,0dh,0ah,"Введите строку:",0dh,0ah,"$"
MESS2 db 0dh,0ah,"Введите позицию: $"
MESS3 db 0dh,0ah,"Введите число удаляемых символов: $"
MESS4 db 0dh,0ah,0dh,0ah,"Строка после удаления:",0dh,0ah,"$"
S_BUFLEN db 80 ; Макс. длина строки
S_FACTLEN db ? ; Длина фактически введенной строки
S_INPBUF db 80 dup (?) ; Введенная строка
N_BUFLEN db 3 ; Макс. длина числа при вводе
N_FACTLEN db ? ; Фактическая длина
N_INPBUF db 3 dup (?) ; Строка представления числа
POSDEL dw ? ; Позиция, начиная с которой удаляем
LENDEL dw ? ; Сколько символов удалить
codeseg
startupcode
; Ввод строки
MLOOP: lea DX, MESS1
mov AH, 09h
int 21h ;Приглашение к вводу строки
lea DX, S_BUFLEN
mov AH, 0Ah
int 21h ;Ввод строки
mov BL, S_FACTLEN
cmp BL, 0 ;Строка пустая?
jne LLL0 ;Нет - продолжать
jmp QUIT ;Закончить работу
LLL0: mov BH, 0 ;Дополнить длину до слова
add BX, 2 ; и получить адрес позиции
add BX, DX ; сразу после конца строки
mov byte ptr[BX],0;Записать признак конца строки
; Ввод позиции удаления
LLL1: lea DX, MESS2 ;Приглашение
mov AH, 09h ; к вводу
int 21h ; позиции удаления
lea DX, N_BUFLEN
mov AH, 0Ah
int 21h ;Ввод строки числа
lea BX, N_INPBUF ;Адрес строки представления числа
mov CL, N_FACTLEN ;Длина этой строки
call VAL ;Перевод в целое число
jc LLL1 ;Ошибка? - повторить ввод
cmp AL, 0 ;Ноль?
je LLL1 ;Повторить ввод
cmp AL, S_FACTLEN ;Превышает длину строки?
jg LLL1 ;Повторить ввод
mov POSDEL, AX ;Запомнить позицию удаления
; Ввод длины удаляемой части
LLL2: lea DX, MESS3 ;Приглашение
mov AH, 09h ; к вводу
int 21h ; числа удаляемых
lea DX, N_BUFLEN
mov AH, 0Ah
int 21h ;Ввод строки числа удаляемых
lea BX, N_INPBUF ;Адрес строки представления числа
mov CL, N_FACTLEN ;Длина этой строки
call VAL ;Перевод в целое число
jc LLL2 ;Ошибка? - повторить ввод
mov LENDEL, AX ;Запомнить число удаляемых
add AX, POSDEL ;Подсчитать, не выходит ли
dec AX ; удаляемая часть
cmp AL, S_FACTLEN ; за конец строки?
jg LLL2 ;Если да - повторить ввод
; Занесение параметров в стек и вызов п/п удаления
lea AX, S_INPBUF
push AX ;1-й параметр - адрес строки
push POSDEL ;2-й параметр - позиция удаления
push LENDEL ;3-й параметр - число удаляемых
call DELSUBS ;Вызов подпрограммы
; Вывод результата
lea DX, MESS4
mov AH, 09h
int 21h ;Заголовок вывода
lea BX, S_INPBUF
mov CX, 80
LLL3: cmp byte ptr [BX],0;Цикл поиска конца строки и выход
je LLL4 ; - когда конец строки найден
inc BX ;Сдвиг по строке
loop LLL3
LLL4: mov byte ptr [BX],'$';Заменить признак конца строки
lea DX, S_INPBUF
mov AH, 09h
int 21h ; Вывод результата
jmp MLOOP ; На повторение работы
QUIT: exitcode 0
;Действие:
; функция вычисляет целое число по его строковому представлению.
; Результат не может быть больше 255.
; Для неверно введенных чисел устанавливает флаг переноса
;Параметры:
; BX - адрес строки - предстваления числа
; CX - длина этой строки
;Возвращает:
; CF - установлен, если в строке не цифры, AX - не определен
; сброшен, строка нормальная, AX - число
; AX - преобразованное число, если сброшен
VAL proc near
push DX ;Сохранить все изменяемые регистры,
; кроме AX, в котором результат
mov CH, 0 ;Расширяем длину до слова
mov AX, 0 ;Начальное значение результата
mov DL, 10 ;Основание системы счисления
__1: imul DL ;Умножить на основание
jc __2 ;Переполнение байта?
mov DH, [BX] ;Очередная цифра
sub DH, '0' ;Получить значение цифры
jl __2 ;Это была не цифра!
cmp DH, 9
jg __2 ;Это опять же была не цифра!
add AL, DH ;+ значение цифры к результату
jc __2 ;Переполнение байта?
inc BX ;Сдвиг по строке
loop __1 ;Цикл по строке
jmp __3 ;Нормальное число
__2: stc ;Было переполн. - устанавливаем CF
__3: pop DX ;Восстановить все, что сохраняли
ret
VAL endp
; Подпрограмма удаления подстроки
DELSUBS proc near
arg __Ldel: word, __Pdel: word, __StrAdr: word = __ArgSize
;Params struc ;Структура стека после сохранения BP
; SaveBP dw ? ; Сохраненное значение BP
; SaveIP dw ? ; Адрес возврата
; LDel dw ? ; 3-й параметр - число удаляемых
; PDel dw ? ; 2-й параметр - позиция удаления
; StrAdr dw ? ; 1-й параметр - адрес строки
;Params ends
push BP ;Сохранить BP
mov BP, SP ;Теперь BP адресует стек ПОСЛЕ сохр.BP,
; но ДО сохранения остальных регистров
push ES AX SI DI CX ;Сохранить все изменяемые регистры
mov AX,DS ; ES будет указывать на
mov ES,AX ; сегмент данных
mov DI,__StrAdr ;Вычислить в DI адрес,
add DI,__PDel ; куда надо
dec DI ; пересылать символы
mov SI,DI ;А в SI - адрес,
add SI,__LDel ; откуда их пересылать
cld ;Продвигаться от начала строки к концу
__REPEAT:
movsb
cmp byte ptr [SI-1], 0
jne __REPEAT
pop CX DI SI AX ES ;Восстановить все, что сохраняли
pop BP
ret __ArgSize ;Убрать из стека 3 параметра-слова
DELSUBS endp
end
ВАРИАНТЫ ЗАДАНИЙ
В приведенных ниже вариантах заданий используется стандартное представление строк ASCII с кодом 0 в качестве ограничителя конца строки. Способ передачи параметров выбирается программистом произвольно. Рекомендуется зациклить программу по вводу, а признаком окончания работы считать ввод пустой строки.
1. Разработать подпрограмму, которая определяет, содержится ли одна заданная строка в другой заданной строке, и если да, то, начиная с какой позиции. Разработать программу, которая вводит с клавиатуры две строки и сообщает, содержится ли одна из них в другой и сколько раз.
2. Разработать подпрограмму, которая подсчитывает, сколько раз заданный символ встречается в строке. Разработать программу, которая вводит с клавиатуры строку, вводит число N и выдает список символов, которые встречаются в строке не менее чем N раз.
3. Разработать две подпрограммы, одна из которых соединяет две строки в одну, а другая обрезает строку до заданной длины (или дополняет пробелами, если длина строки меньше заданной). Разработать программу, которая вводит с клавиатуры число N, затем вводит несколько строк (конец ввода – пустая строка) и формирует новую строку, состоящую из первых N символов каждой введенной строки.
4. Разработать две подпрограммы, одна из которых сравнивает две строки по лексикографическому порядку, а другая обменивает значения двух строк. Разработать программу, которая вводит с клавиатуры несколько строк (конец ввода – пустая строка) и сортирует их в лексикографическом порядке.
5. Разработать подпрограмму, которая разбивает заданную строку на две части: первое слово строки (до первого пробела) и остаток строки (пробелы после первого слова отбрасываются). Разработать программу, которая вводит с клавиатуры строку и выводит каждое слово с новой строки.
6. Разработать подпрограмму, которая переставляет символы заданной строки в обратном порядке. Разработать программу, которая вводит с клавиатуры строку и переставляет в обратном порядке символы в каждом слове (слова разделяются пробелами).
7. Разработать подпрограмму, которая вставляет подстроку в строку, начиная с заданной позиции. Разработать программу, которая вводит с клавиатуры исходную строку, вводит подстроку и позицию вставки, вставляет подстроку в строку.
8. Разработать две подпрограммы, одна из которых преобразует любую заданную букву в заглавную (в том числе для русских букв), а другая - преобразует букву в строчную. Разработать программу, которая вводит с клавиатуры строку и заменяет первые буквы всех слов на заглавные, а остальные буквы - на строчные.
КОНТРОЛЬНЫЕ ВОПРОСЫ
1. Что такое «ближние» и «дальние» подпрограммы?
2. Как определяется, «ближний» или «дальний» вариант команды call использован в программе?
3. Какие еще способы передачи параметров можно предложить, кроме двух, описанных в данной работе?
4. Может ли массив быть параметром процедуры?
5. Нельзя ли адресовать параметры в стеке через регистр SP, не используя BP?
6. Что и как нужно изменить в программе из примера, если используется версия ассемблера, не поддерживающая понятие структуры?
7. Изменить описание подпрограммы из примера с использованием упрощенных директив описания подпрограмм.
8. Что означает операнд команды ret?
9. Какой последовательностью команд можно было бы заменить команду ret 6?
Лабораторная работа |
ÈÑÏÎËÜÇÎÂÀÍÈÅ ÏÎÄÏÐÎÃÐÀÌÌ ÍÀ ßÇÛÊÅ ÀÑÑÅÌÁËÅÐÀ  ÏÐÎÃÐÀÌÌÀÕ ÍÀ ßÇÛÊÀÕ C È PASCAL |
ЦЕЛЬ РАБОТЫ
Цель настоящей работы – выработка навыков подготовки разноязыковых программ в операционной системе MS-DOS.
ОСНОВНЫЕ СВЕДЕНИЯ
Введение
Каждый язык программирования обладает своими достоинствами и недостатками. Поэтому при разработке больших проектов может возникнуть необходимость написания отдельных частей программы на различных языках программирования, например:
· вся программа на каком-либо языке высокого уровня (ЯВУ), а для доступа к нестандартной аппаратуре или нестандартного доступа к стандартной требуется написание подпрограмм на языке ассемблера;
· в программе на ЯВУ необходимо повысить эффективность выполнения какого-либо фрагмента и для этого переписать его на языке ассемблера;
· необходимо использовать библиотеку подпрограмм, написанную на языке, отличном от языка разрабатываемой программы.
Вся конкретная информация и примеры рассмотрены для системы программирования (СП) Borland C++ Version 3.1 и Borland Pascal Version 6.0 фирмы Borland International, Inc. и ориентированы на программирования в DOS.
ТерминЫ и сокращения
Подпрограмма – фрагмент программы, оформленный таким образом, что к нему можно обращаться (вызывать) из других фрагментов программы и возвращаться в точку вызова. При вызове подпрограммы ей можно передавать параметры и она может возвращать значение.
Процедура – подпрограмма, не возвращающая значения (см. процедуры Паскаля).
Функция – подпрограмма, возвращающая значение (см. функции Паскаля или Си).
Параметр – фрагмент данных, передаваемый подпрограмме, и возможно, изменяемый ею. Внешние (глобальные) данные параметрами не являются.
Формальный параметр – обозначение (идентификатор) параметра в подпрограмме.
Фактический параметр – выражение или идентификатор переменной, подставляемый при обращении (вызове) подпрограммы.
Прототип подпрограммы – описание заголовка подпрограммы с описанием формальных параметров.
Основной язык – язык, на котором написана вызывающая программа, язык подпрограммы – язык, на котором написана подпрограмма.
Сокращения:
СП – система программирования
ЯП – язык программирования
ЯВУ – язык программирования высокого уровня
ИМ – исходный модуль
ОМ – объектный модуль
Соглашения о связях
Вообще говоря, для каждого транслятора с языка высокого уровня существует свое собственное соглашение о связях между подпрограммами. То есть соглашение о том, в какие машинные команды и конструкции языка ассемблера транслируются операторы языка высокого уровня, служащие для описания и вызова подпрограмм.
Понятие «соглашение о связях» включает следующие аспекты: преобразование имен подпрограмм и переменных, передача и возврат управления, передача и возврат данных, преобразование данных при передаче и возврате, трансляция и компоновка подпрограмм в единую программу.
Преобразование имен
Под термином «преобразование имен» понимаются правила формирования имен транслятором при создании объектного модуля. Необходимо учитывать, по меньшей мере, следующие моменты:
· допустимое количество символов в имени в программе на ЯП и в ОМ;
· различаются ли прописные и строчные буквы в программе на ЯП и в ОМ;
· не добавляет ли что-нибудь транслятор от себя к именам в ОМ.
Длина внешнего имени (т.е. имени «видимого» компоновщиком) зависит от конкретной СП, как правило, это 32 символа.
В языке C в именах различаются прописные и строчные буквы, в языке Pascal, напротив, не различаются. Как правило, трансляторы с этих языков поступают соответствующим образом при формировании имен в ОМ. В частности, Pascal-трансляторы преобразуют строчные буквы в прописные.
С-трансляторы, как правило, включают символ подчеркивания в начало каждого имени в ОМ.