Вызывающая программа

 

Итак, следующий список содержит все уже рассмотренные нами требования к подпрограммам:

S1: Сохранение регистров.

S2: Определение базовых регистров.

S3: Восстановление регистров.

S4: Переход по адресу, содержащемуся в регистре 14 (возврат к вызывающей программе).

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

S5: Требования к вызывающей программе, если подпрограмма сама производит обращение.

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

Однако в операционных системах DOS и DOS/VS все же существует различие между основными программами и подпрограммами, к которым они обращаются. Вопросы, связанные с организацией работы в системе DOS, будут рассмотрены в разд. 13.6.

Вернемся к списку требований, предъявляемых к вызывающим программам. К настоящему моменту он выглядит так:

С1: Требования к подпрограммам.

С5: Загрузка адреса начала подпрограммы в регистр 15.

С6: Загрузка адреса возврата в регистр 14.

С7: Передача управления по адресу точки входа.

Но список по-прежнему не полон. При обсуждении вопросов, связанных с сохранением регистров, мы сказали, что при обращении к подпрограмме регистр 13 должен содержать адрес начала 72-байтовой области сохранения. Задумывались ли вы над тем, где на самом деле расположена эта область и как ее адрес попадает в регистр 13? Ответ на поставленный вопрос таков: резервирование памяти под область сохранения выполняется вызывающей программой, она же производит загрузку адреса ее начала в регистр 13 перед вызовом подпрограммы.

Итак, подпрограмма переписывает информацию из регистров в поле, адрес которого находится в регистре 13. Эта информация, таким образом, оказывается в области сохранения основной программы. Если подпрограмма в свою очередь производит вызов еще одной подпрограммы, то эта последняя подпрограмма производит сохранение содержимого регистров в области, заданной вызвавшей ее подпрограммой. Описанный процесс может продолжаться неограниченно. При этом каждая вновь вызванная подпрограмма производит сохранение содержимого регистров в области, заданной вызвавшей ее программой.

На рис. 13.8 изображена цепочка областей сохранения, возникающих в описанном нами процессе. Программа MAIN производит запись в область, отведенную ей операционной системой. MAIN обращается к подпрограммеSUB1, которая в свою очередь использует область сохранения, заданную основной программой. SUB1 вызывает SUB2, копирующую содержимое регистров в поле, определенное в программе SUB1.

Область сохранения может быть зарезервирована так:

SAVEAREA DS 18F

Адрес начала этой области может быть загружен в регистр 13 командой

LA 13,SAVEAREA

если базовый регистр уже определен. Итак, общую структуру вызывающей программы можно попытаться представить в следующем виде (неверно!):

CALLER START 0

STM 14,12,12(13)

BALR 12,0

USING *,12

LA 13,SAVEAREA

вычисления и вызовы подпрограмм

Рис. 13.8 Программа использует область сохранения, принадлежащую вызывающей программе

LM 14,12,12(13)

BR 14

SAVE AREA DS 18F

резервирование памяти и константы

END

Посмотрим, как такая программа будет выполняться. По команде STM производится сохранение содержимого регистров с 14-го по 12-й в области памяти, заданной программой, вызвавшей программу CALLER. Следующие две команды служат для определения базового регистра и базового значения. Непосредственно за ними следует команда загрузки адреса начала области сохранения программы CALLER в регистр 13. После выполнения необходимых вычислений производится восстановление регистров и возврат к вызывающей программе. Но посмотрите! Из какой области регистры будут загружены перед осуществлением перехода к основной программе? После выполнения команды LA в регистре 13 находится адрес области сохранения данной программы. Выполнение команды LM (при условии, что (13) больше не менялось) приведет к помещению в регистры информации из поля SAVEAREA. Но это совсем не то место, куда исходное содержимое регистров было занесено с помощью команды STM. Теперь адрес настоящей области сохранения неизвестен.

Дело в том, что содержимое самого регистра 13 не было сохранено перед выполнением команды LA. После того как команда загрузки выполнена, прежнее содержимое регистра 13 потеряно и уже никак невозможно определить, где же находится область сохранения, необходимая нам для восстановления регистров. Карта острова сокровищ утеряна.

Почему нельзя сохранить регистр 13 вместе с остальными, используя команду

STM 13-, 12,8(13)

Несложные рассуждения показывают, что это никак не «поможет решению возникшей проблемы. Дело в том, что после выполнения команды LA всякая связь между данной программой и областью сохранения, предоставленной ей Вызывающей программой, исчезает.

Необходимо хранить (13) где-нибудь там, откуда его впоследствии несложно будет достать. В соответствии с принятым соглашением (13) записывается во второе слово области сохранения, определяемой данной подпрограммой. Такая запись содержимого регистра 13 должна быть произведена до того, как какая-нибудь команда работы с регистром изменит его содержимое. Затем, перед выполнением восстановления регистров, содержимое регистра 13 просто достается оттуда, куда оно было первоначально помещено.

Рис. 13.9. Структура вызывающей программы.

Результат следования описанному правилу таков: второе слово области сохранения каждой подпрограммы содержит ссылку на область сохранения программы, вызвавшей ее. После выполнения нескольких последовательных обращений все возникшие области сохранения имеют структуру списка со ссылками, в котором каждая область содержит указатель на область, принадлежащую более старшей программе. Каждый новый вызов ведет к добавлению новых элементов, каждый возврат укорачивает список.

Этот фант оказывается очень полезным при отладке программ, содержащих обращения к подпрограммам, некоторые из которых являются вложенными. Используя рассмотренный список со ссылками, можно определить местонахождение области сохранения вызывающей программы, найти содержимое регистров на момент обращения к подпрограмме. Особенно нас может интересовать содержимое регистра 14, показывающее, из какого именно места программы был произведен вызов. По информации второго слова области сохранения можно определить адрес начала области, относящейся к программе, вызвавшей данную. Этот процесс, если это необходимо, может быть продолжен до тех пор, пока мы не доберемся до основной программы.

На рис. 13.9 изображена общая структура программы CALLER, производящей вызов подпрограммы SUBR. CALLER содержит команды определения области сохранения содержимого регистра 13. Наш теперь почти завершенный список требований к вызывающим программам выглядит так:

С1: Требования к подпрограммам, если данная программа не является основной в системе DOS.

С2: Сохранение (13).

СЗ: Резервирование 72-байтовой области и загрузка адреса ее начала в регистр 13.

С4:

С5: Загрузка адреса первого байта подпрограммы в регистр 15. С6: Загрузка адреса возврата в регистр 14.

С7: Передача управления по адресу точки входа.

Мы рассмотрели весь процесс организации связей с подпрограммами за исключением вопроса, касающегося получения результатов. Этому посвящена первая часть следующего раздела.