Шрифт:
{–}
{ Write the Prolog for a Procedure }
procedure ProcProlog(N: char);
begin
PostLabel(N);
EmitLn('LINK A6,#0');
end;
{–}
{ Write the Epilog for a Procedure }
procedure ProcEpilog;
begin
EmitLn('UNLK A6');
EmitLn('RTS');
end;
{–}
Процедура DoProc теперь просто вызывает их:
{–}
{ Parse and Translate a Procedure Declaration }
procedure DoProc;
var N: char;
begin
Match('p');
N := GetName;
FormalList;
Fin;
if InTable(N) then Duplicate(N);
ST[N] := 'p';
ProcProlog(N);
BeginBlock;
ProcEpilog;
ClearParams;
end;
{–}
В заключение, мы должны изменить ссылки на SP в процедурах LoadParam и StoreParam:
{–}
{ Load a Parameter to the Primary Register }
procedure LoadParam(N: integer);
var Offset: integer;
begin
Offset := 8 + 2 * (NumParams – N);
Emit('MOVE ');
WriteLn(Offset, '(A6),D0');
end;
{–}
{ Store a Parameter from the Primary Register }
procedure StoreParam(N: integer);
var Offset: integer;
begin
Offset := 8 + 2 * (NumParams – N);
Emit('MOVE D0,');
WriteLn(Offset, '(A6)');
end;
{–}
(Заметьте, что вычисление Offset изменяется чтобы учесть дополнительное сохранение A6.)
Это все что требуется. Попробуйте и посмотрите как вам это нравится.
К этому моменту мы генерируем некоторый относительно хороший код для процедур и вызовов процедур. С ограничениями, что нет никаких локальных переменных (пока) и не разрешено вложение процедур этот код именно то что нам нужно.
Все еще остается только одна небольшая проблема:
У нас нет способа возвратить результат в вызывающую программу!
Но это, конечно, не ограничение генерируемого нами кода, а ограничение, свойственное протоколу передачи по значению. Обратите внимание, что мы можем использовать формальные параметры любым способом внутри процедуры. Мы можем вычислять для них новое значение, использовать их как счетчики циклов (если бы мы имели циклы!) и т.д. Так что код делает то, что предполагается. Чтобы решить эту последнюю проблему мы должны рассмотреть альтернативный протокол.
Передача по ссылке
Это просто теперь, когда мы уже имеем механизм. Мы только должны внести несколько изменений в генерацию кода. Вместо помещения значения в стек, мы должны помещать адрес. Оказывается, 68000 имеет инструкцию PEA которая как раз делает это.
Для этого мы сделаем новую версию тестовой программы. Перед тем, как сделать что-нибудь еще, сделайте копию программы в ее текущем состоянии, потому что позже она понадобится нам снова.
Давайте начнем с рассмотрения кода, который мы хотели бы видеть сгенерированным для нового случая. Используя тот же самый пример что и раньше, мы должны вызов
FOO(X, Y)
оттранслировать в:
PEA X(PC) ; Сохранить адрес X
PEA Y(PC) ; Сохранить адрес Y
BSR FOO ; Вызвать FOO
Это просто вопрос небольших изменений в Param:
{–}
{ Process an Actual Parameter }
procedure Param;
begin
EmitLn('PEA ' + GetName + '(PC)');
end;
{–}
(Обратите внимание, что при передачей по ссылке мы не можем использовать выражения в списке параметров, поэтому Param может просто непосредственно считывать имя).
На другой стороне, ссылки на формальные параметры должны получить один уровень косвенности:
FOO: LINK A6,#0
MOVE.L 12(A6),A0 ; Извлечь адрес A
MOVE (A0),D0 ; Извлечь A
MOVE D0,-(SP) ; Сохранить
MOVE.L 8(A6),A0 ; Извлечь адрес B
MOVE (A0),D0 ; Извлечь B
ADD (SP)+,D0 ; Добавить A
MOVE.L 12(A6),A0 ; Извлечь адрес A
MOVE D0,(A0) : Сохранить A
UNLK A6
RTS
Все это может быть обработано с изменениями в LoadParam and StoreParam:
{–}
{ Load a Parameter to the Primary Register }
procedure LoadParam(N: integer);
var Offset: integer;
begin
Offset := 8 + 4 * (NumParams – N);
Emit('MOVE.L ');
WriteLn(Offset, '(A6),A0');
EmitLn('MOVE (A0),D0');
end;
{–}
{ Store a Parameter from the Primary Register }
procedure StoreParam(N: integer);
var Offset: integer;
begin
Offset := 8 + 4 * (NumParams – N);
Emit('MOVE.L ');
WriteLn(Offset, '(A6),A0');
EmitLn('MOVE D0,(A0)');
end;
{–}