Вход в подпрограмму и выход из неё

{toc_noshowall}Посмотрим, как непосредственно производятся переход на подпрограмму и возврат из нее.

ТРЕБОВАНИЯ К ВЫЗЫВАЮЩЕЙ ПРОГРАММЕ И ПОДПРОГРАММАМ

Первая выполняемая команда подпрограммы называется точкой входа, ее адрес — соответственно адресом точки входа. Точкой входа, как правило, является первый байт подпрограммы. Адрес команды основной программы, выполняемой непосредственно вслед за последней командой подпрограммы, называется адресом возврата. Адрес возврата обычно является адресом команды, следующей за командой передачи управления подпрограмме.

Оставшаяся часть этой главы посвящена рассмотрению требований, предъявляемых к программам в связи с необходимостью организации связей. Мы будем различать требования, предъявляемые к вызывающим программам, и требования, предъявляемые к подпрограммам. Во-первых, основная программа должна определить адрес точки входа соответствующей подпрограммы и произвести передачу управления поэтому адресу, когда на самом деле потребуется выполнение данной подпрограммы. Подпрограмме же должен быть известен адрес возврата; очевидно, что он должен задаваться основной программой. Наконец, подпрограмма должна передавать управление обратно вызывающей программе, выполняя команду перехода по адресу возврата.

Выполнение перечисленных выше требований достигается посредством использования некоторых регистров специально для организации связей. (Именно поэтому была запрещена работа с регистрами 13, 14, 15, 0 и 1 в обычных программах.) При входе в подпрограмму регистр 15 содержит адрес первого ее байта, а регистр 14 — адрес возврата. Сформулируем теперь в более конкретных терминах требования, предъявляемые к программам и подпрограммам.

1. Требования к вызывающим программам:

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

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

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

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

S4: Осуществить переход по адресу, находящемуся в регистре 14 (обеспечить возврат).

Мы начали нумерацию требований не с С1 и S1 соответственно, поскольку в процессе дальнейшей работы нам придется дополнить составленные списки.

 

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

LA 15,SUB

или

L 15,=A(SUB)

если SUB — имя требуемой подпрограммы. Но откуда вызывающей программе может быть известен адрес начала подпрограммы? Использование команды LA или адресной константы A-типа предполагает, что по отношению к программе, в которой подобные предложения встречаются, адрес SUB является внутренним, т. е. соответствующее имя было определено в этой же программе. Однако по определению первый байт подпрограммы никак не может быть частью вызывающей программы. Имя, соответствующее первому байту подпрограммы, является примером, так называемого внешнего имени; внешним именем называется имя, определенное вне использующей его программы.

Помещая некоторое имя в поле имени предложения START подпрограммы, мы автоматически определяем его как внешнее по отношению к основной программе. Например, для задания SORT в качестве имени подпрограммы следует написать

SORT START 0

Если SORT было определено, как указано выше, ссылку на него можно производить с помощью адресной константы V-типа. Например, по команде

SORTADDR DC V(SORT)

будет зарезервировано полное слово с именем SORTADDR, содержащее адрес первого байта подпрограммы SORT. Теперь произвести загрузку 15-го регистра можно следующим образом:

L 15,SORTADDR

Можно также не резервировать память отдельной командой, а воспользоваться литералом:

L 15,=V(SORT)

Ассемблеру при обработке основной программы неизвестно значение адреса V-типа, поскольку оно зависит от размещения подпрограммы в памяти во время выполнения. Ассемблер резервирует полное слово и помечает его как внешнюю ссылку. При загрузке программ в память для выполнения адрес внешнего имени определяется загрузчиком и помещается на зарезервированное место.

 

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

LA 14,RET

BR 15

RET

Мы достигаем, таким образом, выполнения шагов С6 и С7. Однако команды BAL и BALR (ПЕРЕХОД С ВОЗВРАТОМ) позволяют устранить необходимость выполнения команды загрузки адреса.

BAL R1 ,D2(X2,B2)

Branch And Link

R1?(PC); PC?D2 + (X2) + (B2)

BALR R1, R2

Branch And Link Register

R1?(PC); PC?(R2)

Выполнение этих команд проходит в два этапа. Сначала содержимое счетчика команд попадает в регистр первого операнда. В соответствии с логикой работы счетчика команд это означает, что на первом этапе в R1 загружается адрес следующей команды. Затем в качестве нового значения счетчика команд для команды формата RX выбирается адрес второго операнда, а для команды формата RR—содержимое R2. Теперь становится понятным, чем объясняется название «Переход с возвратом». Дело в том, что при выполнении команд BAL и BALR производится не только переход, но и установление связи с тем местом, откуда переход был произведен. Достигается это заданием адреса возврата.

В дальнейшем мы будем часто пользоваться одной особенностью команды BALR. Эта особенность заключается в том, что если на месте R2 указан 0, то переход не производится, нотем не менее (PC) ? R1.

По команде

BALR 3,0

содержимое счетчика команд засылается в регистр 3, а переход не производится.

Поскольку мы договорились, что для хранения адреса возврата используется 14-й регистр, то его и следует задавать в качестве регистра первого операнда. Возвращаясь к нашему примеру, если мы хотим осуществить переход на начало подпрограммы SORT, то следует написать

BALR 14,15

Если же необходимо начать выполнение SORT с сорокового байта, то вид команды передачи управления будет несколько иным:

BAL 14,40(15)

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

BR 14

Рассмотрим теперь последовательно использование описанного принципа. Пусть мы хотим произвести вызов подпрограммы PCALC, причем выполнение ее начать с сотого байта. Команды

L 15, =V (PCALC)

BAL 14,100(15)

обеспечивают выполнение перехода и установление связи. Последней командой PCALC должна быть

BR 14