home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Fred Fish Collection 1.5
/
ffcollection-1-5-1992-11.iso
/
ff_disks
/
300-399
/
ff339.lzh
/
PCQ
/
Source.lzh
/
Source
/
Calls.p
next >
Wrap
Text File
|
1989-11-26
|
16KB
|
627 lines
External;
{
Calls.p (of PCQ Pascal)
Copyright (c) 1989 Patrick Quaid
Calls.p is the first attempt to organize the various
addressing and code generating routines in one section. If you
read the other sections you'll find that not much effort went into
this project. Nonetheless, a couple of common addressing things
can be found here.
If the compiler were designed so that all the addressing
things were here, it would be much easier to port to a different
processor.
}
{$O-}
{$I "Pascal.i"}
Function Match(s : Symbols) : Boolean;
external;
Procedure Error(s : string);
external;
Function FindField(s : string; TP : TypePtr): IDPtr;
external;
Function FindWithField(S : String) : IDPtr;
External;
Procedure NextSymbol;
external;
Function Expression() : TypePtr;
external;
Function TypeCheck(t1, t2 : TypePtr): Boolean;
external;
Function TypeCmp(t1, t2 : TypePtr) : Boolean;
external;
Function FindID(s : string) : IDPtr;
external;
Function IsVariable(i : IDPtr) : Boolean;
external;
Function GetLabel() : Integer;
external;
Procedure PrintLabel(l : Integer);
external;
Procedure ns;
external;
Function Suffix(s : Integer): Char;
external;
Procedure Mismatch;
external;
Function SimpleType(t : TypePtr): Boolean;
external;
Function NumberType(t : TypePtr): Boolean;
external;
Procedure PromoteType(var f : TypePtr; o : TypePtr; r : Short);
external;
Procedure PushLongD0;
begin
writeln(OutFile, "\tmove.l\td0,-(sp)");
StackLoad := StackLoad + 4;
end;
Procedure PopLongD0;
begin
Writeln(OutFile, '\tmove.l\t(sp)+,d0');
StackLoad := StackLoad - 4;
end;
Procedure PopStackSpace(amount : Integer);
begin
Writeln(OutFile, '\tadd.l\t#', amount, ',sp');
StackLoad := StackLoad - amount;
end;
Procedure PushWordD0;
begin
writeln(OutFile, "\tmove.w\td0,-(sp)");
StackLoad := StackLoad + 2;
end;
Procedure PushLongD1;
begin
Writeln(OutFile, '\tmove.l\td1,-(sp)');
StackLoad := StackLoad + 4;
end;
Procedure PopLongD1;
begin
writeln(OutFile, "\tmove.l\t(sp)+,d1");
StackLoad := StackLoad - 4;
end;
Procedure PushLongA0;
begin
writeln(OutFile, '\tmove.l\ta0,-(sp)');
StackLoad := StackLoad + 4;
end;
Procedure PopLongA0;
begin
writeln(OutFile, '\tmove.l\t(sp)+,a0');
StackLoad := StackLoad - 4;
end;
Procedure PopLongA1;
begin
writeln(OutFile, '\tmove.l\t(sp)+,a1');
StackSpace := StackSpace - 4;
end;
Procedure DoRangeCheck(VarType : TypePtr);
{
This routine is called from selector() when range checking
is turned on. Notice that the code is now in a library, rather
than inline as it was in 1.0. Also note that the library code fixes
the stack after the call.
}
begin
Writeln(OutFile, '\tpea\t', VarType^.Lower);
Writeln(OutFile, '\tpea\t', VarType^.Upper);
Writeln(OutFile, '\tjsr\t_p%CheckRange');
end;
Function GetFramePointer(Reference : Integer) : Short;
var
Current : Integer;
begin
Current := CurrentBlock^.Level;
if Current = Reference then
GetFramePointer := 5
else begin
writeln(OutFile, "\tmove.l\t8(a5),a4");
Current := Pred(Current);
while Current > Reference do begin
Writeln(OutFile, "\tmove.l\t8(a4),a4");
Current := Pred(Current);
end;
GetFramePointer := 4;
end;
end;
Procedure GetPointerVal(ID : IDPtr);
{
This routine puts the value of a pointer variable (or a
reference parameter) into a0.
}
var
Reg : Short;
begin
case ID^.Object of
global : writeln(OutFile, "\tmove.l\t_", ID^.Name, ',a0');
typed_const :
if ID^.Level <= 1 then
writeln(OutFile, '\tmove.l\t_', ID^.Name, ',a0')
else
writeln(OutFile, '\tmove.l\t_',ID^.Name,'%',ID^.Unique);
refarg : begin
Reg := GetFramePointer(ID^.Level);
writeln(OutFile, "\tmove.l\t", ID^.Offset, '(a', Reg, '),a0');
writeln(OutFile, "\tmove.l\t(a0),a0");
end;
else begin
Reg := GetFramePointer(ID^.Level);
writeln(OutFile, "\tmove.l\t", ID^.Offset, '(a', Reg, '),a0');
end;
end;
end;
Procedure SimpleAddress(ID : IDPtr);
{
simpleaddress() is passed a idrecord of some sort of
variable, and just loads its address into a0.
}
var
Reg : Short;
begin
case ID^.Object of
global : writeln(OutFile, "\tlea\t_", ID^.Name, ',a0');
typed_const :
if ID^.Level <= 1 then
writeln(OutFile, '\tlea\t_', ID^.Name, ',a0')
else
writeln(OutFile, '\tlea\t_', ID^.Name, '%',
ID^.Unique, ',a0');
refarg : begin
Reg := GetFramePointer(ID^.Level);
writeln(OutFile, "\tmove.l\t", ID^.Offset, '(a', Reg, '),a0');
end;
else begin
Reg := GetFramePointer(ID^.Level);
writeln(OutFile, "\tlea\t", ID^.Offset, '(a', Reg, '),a0');
end;
end;
end;
Procedure DoMultiply(size : Integer);
Procedure ShiftLeft(num : Short);
begin
Writeln(OutFile, "\tlsl.l\t#", num, ',d0');
end;
begin
case size of
1 : ;
2 : ShiftLeft(1);
4 : ShiftLeft(2);
8 : ShiftLeft(3);
16 : ShiftLeft(4);
32 : ShiftLeft(5);
64 : ShiftLeft(6);
128: ShiftLeft(7);
256: ShiftLeft(8);
else
writeln(OutFile, "\tmuls\t#", size, ',d0');
end;
end;
Function Selector(ID : IDPtr) : TypePtr;
{
This is an overlarge function that handles all the
selectors- in other words ^, ., and []. It can handle a series of
them, of course. selector() returns Nil if no selection was
required, and the type if there was some selection.
}
var
VarType : TypePtr;
FieldID : IDPtr;
IndexType : TypePtr;
Stacked : Boolean;
Leave : Boolean;
bufsize,
WithOffset : Integer;
Substitute : TypePtr;
begin
if ID^.Object = field then begin
WithOffset := StackLoad - LastWith^.Offset;
if WithOffset = 0 then
Writeln(OutFile, '\tmove.l\t(sp),a0')
else
Writeln(OutFile, '\tmove.l\t', WithOffset, '(sp),a0');
if ID^.Offset <> 0 then
Writeln(OutFile, '\tadda.l\t#', ID^.Offset, ',a0');
Stacked := True;
end else
Stacked := False;
VarType := ID^.VType;
while (CurrSym = period1) or (CurrSym = leftbrack1) or
(CurrSym = carat1) do begin
if (not Stacked) and (VarType^.Object <> ob_pointer) then begin
SimpleAddress(ID);
Stacked := True;
end;
if Match(Period1) then begin
if VarType^.Object <> ob_record then
error("not a record type");
FieldID := FindField(symtext, VarType);
if FieldID = Nil then
Error("unknown field")
else if FieldID^.Offset <> 0 then
writeln(OutFile, "\tadda.l\t#", FieldID^.Offset, ',a0');
NextSymbol;
VarType := FieldID^.VType;
end else if Match(Carat1) then begin
if VarType^.Object = ob_file then begin
BufSize := VarType^.SubType^.Size;
writeln(OutFile, '\tjsr\t_p%FilePtr');
if IOCheck then
Writeln(OutFile, '\tjsr\t_p%CheckIO');
VarType := VarType^.SubType;
end else if VarType^.Object = ob_pointer then begin
if not Stacked then begin
GetPointerVal(ID);
Stacked := True;
end else
writeln(OutFile, "\tmove.l\t(a0),a0");
VarType := VarType^.SubType;
end else
error("Need a file or pointer for ^");
end else if Match(LeftBrack1) then begin
if VarType^.Object = ob_array then begin
Leave := False;
repeat
PushLongA0;
IndexType := Expression();
Substitute := Indextype;
PromoteType(Substitute, Inttype, 0);
if RangeCheck then
DoRangeCheck(VarType);
if not TypeCheck(IndexType, VarType^.Ref) then
Mismatch;
if VarType^.Lower <> 0 then
writeln(OutFile, "\tsub.l\t#", VarType^.Lower, ',d0');
VarType := VarType^.SubType;
DoMultiply(VarType^.Size);
PopLongA0;
writeln(OutFile, "\tadd.l\td0,a0");
if Match(Comma1) then begin
if VarType^.Object <> ob_array then begin
Error("Not a multidimensional array");
Leave := True;
end;
end else
Leave := True;
until Leave;
if not Match(RightBrack1) then
Error("Expecting ]");
end else if TypeCheck(Vartype, StringType) then begin
if not Stacked then begin
GetPointerVal(ID);
Stacked := True;
end else
writeln(OutFile, "\tmove.l\t(a0),a0");
PushLongA0;
IndexType := Expression();
if not TypeCheck(IndexType, IntType) then
Mismatch
else
PromoteType(IndexType, IntType, 0);
if not Match(RightBrack1) then
error("expecting ]");
PopLongA0;
writeln(OutFile, "\tadd.l\td0,a0");
VarType := CharType;
end else
error("Expecting an Array or String");
end;
end;
if Stacked then
Selector := VarType
else
Selector := Nil;
end;
Function LoadAddress() : TypePtr;
{
This is the routine used wherever I need the address of a
variable, for example reference parameters or the adr() function.
The address is loaded into a0.
}
var
ArgIndex : IDPtr;
ArgType : TypePtr;
begin
if CurrSym = Ident1 then begin
ArgIndex := FindWithField(SymText);
if ArgIndex = Nil then
ArgIndex := FindID(SymText);
NextSymbol;
if ArgIndex = Nil then begin
error("Unknown ID");
LoadAddress := BadType;
end else begin
if IsVariable(ArgIndex) then begin
ArgType := Selector(ArgIndex);
if ArgType = Nil then begin
SimpleAddress(ArgIndex);
LoadAddress := ArgIndex^.VType
end else
LoadAddress := ArgType;
end else if (ArgIndex^.Object = proc) or
(ArgIndex^.Object = func) then begin
with ArgIndex^ do begin
if Level <= 1 then
Writeln(OutFile, "\tlea\t_", Name, ',a0')
else
Writeln(OutFile, "\tlea\t_", Name, '%', Unique, ',a0');
end;
LoadAddress := AddressType;
end else
error("Expecting a variable (reference parameter)");
end
end else
error("Expecting a variable identifier");
LoadAddress := BadType;
end;
Procedure PushArgs(ProcID : IDPtr);
{
This routine handles the parameters of a call (not the
declaration, which is handled in doblock()). It sorts out the
various reference and value parameters and gets the stack properly
set up.
}
var
CurrentParam : IDPtr;
stay : Boolean;
argtype : TypePtr;
argindex : integer;
totalsize : integer;
lab : integer;
begin
Stay := True;
if Match(LeftParent1) then begin
CurrentParam := ProcID^.Param;
while (not Match(RightParent1)) and Stay do begin
if CurrentParam = Nil then begin
error("argument not expected");
nextsymbol;
Stay := False;
end else begin
if CurrentParam^.Object = valarg then begin
ArgType := Expression();
if not TypeCheck(ArgType, CurrentParam^.VType) then begin
Mismatch;
ArgType := BadType;
end else begin
if NumberType(ArgType) then
PromoteType(ArgType, CurrentParam^.VType, 0);
ArgType := CurrentParam^.VType;
if SimpleType(ArgType) then begin
if ArgType^.Size <= 2 then
PushWordD0
else if ArgType^.Size = 4 then
PushLongD0;
end else begin
writeln(OutFile, "\tmove.l\td0,a0");
writeln(OutFile, "\tmove.l\tsp,a1");
writeln(OutFile, "\tsub.l\t#",
ArgType^.Size, ',a1');
writeln(OutFile, "\tmove.l\t#",
ArgType^.Size - 1, ',d1');
lab := GetLabel();
PrintLabel(lab);
writeln(OutFile, "\tmove.b\t(a0)+,d0");
writeln(OutFile, "\tmove.b\td0,(a1)+");
write(OutFile, "\tdbra\td1,");
PrintLabel(lab);
writeln(OutFile);
write(OutFile, "\tsub.l\t#");
if odd(ArgType^.Size) then begin
write(OutFile, ArgType^.Size + 1);
StackLoad := StackLoad + ArgType^.Size + 1;
end else begin
write(OutFile, ArgType^.Size);
StackLoad := StackLoad + ArgType^.Size;
end;
writeln(OutFile, ',sp');
end;
end;
end else if CurrentParam^.Object = refarg then begin
if CurrSym = ident1 then begin
ArgType := LoadAddress();
PushLongA0;
if not TypeCmp(ArgType, CurrentParam^.VType) then
Mismatch;
end else
error("Expecting a variable name (reference param)");
end;
CurrentParam := CurrentParam^.Next;
if CurrentParam <> Nil then
if not Match(Comma1) then
error("Expected ,");
end;
end;
if CurrentParam <> Nil then
error("more parameters needed");
end else begin
if ProcID^.Param <> Nil then
error("Expecting some parameters");
end
end;
Procedure PushFrame(Callee : Integer);
var
Caller : Integer;
begin
if Callee <= 1 then { global-level routines, which include externs }
return
else begin
Caller := CurrentBlock^.Level - 1;
if Callee = Caller + 1 then { calling child procedure }
writeln(OutFile, "\tmove.l\ta5,-(sp)")
else if Callee = Caller then { same level }
writeln(OutFile, "\tmove.l\t8(a5),-(sp)")
else begin
writeln(OutFile, "\tmove.l\t8(a5),a4");
Caller := Pred(Caller);
while Caller > Callee do begin
writeln(OutFile, "\tmove.l\t8(a4),a4");
Caller := Pred(Caller);
end;
writeln(OutFile, "\tmove.l\t8(a4),-(sp)");
end;
StackLoad := StackLoad + 4;
end;
end;
Procedure CallFunc(FuncID : IDPtr);
{
This calls a function. It's mostly the same as callproc,
but it's called from deep within expression() rather than
statement(). This will also have to push a back pointer.
}
var
ArgSize : Integer;
BaseOffset : Integer;
begin
PushArgs(FuncID);
PushFrame(FuncID^.Level);
if FuncID^.Level <= 1 then
writeln(OutFile, "\tjsr\t_", FuncID^.Name)
else
Writeln(OutFile, "\tjsr\t_", FuncID^.Name, '%', FuncID^.Unique);
if FuncID^.Param <> Nil then begin
if FuncID^.Param^.Object = refarg then
ArgSize := FuncID^.Param^.Offset - 4
else
ArgSize := FuncID^.Param^.Offset - 8 +
FuncID^.Param^.VType^.Size;
end else begin
if FuncID^.Level <= 1 then
ArgSize := 0
else
ArgSize := 4;
end;
if ArgSize <> 0 then begin
if odd(ArgSize) then
ArgSize := Succ(ArgSize);
PopStackSpace(ArgSize);
end;
end;
Procedure CallProc(ProcID : IDPtr);
var
ArgSize : Integer;
begin
NextSymbol;
CallFunc(ProcID);
end;
Procedure SaveThrougha0(TotalSize : Integer);
{
This saves a complex data object pointed to by d0 to the
memory at a0.
}
var
lab : integer;
begin
writeln(OutFile, "\tmove.l\td0,a1");
writeln(OutFile, "\tmove.l\t#", TotalSize - 1, ',d1');
lab := GetLabel();
PrintLabel(lab);
writeln(OutFile, "\tmove.b\t(a1)+,d0");
writeln(OutFile, "\tmove.b\td0,(a0)+");
write(OutFile, "\tdbra\td1,");
PrintLabel(lab);
writeln(OutFile);
end;
Procedure SaveStack(TP : TypePtr);
{
This saves a variable into the memory pointed to by the
longword on the top of the stack. Odd as it may sound, this occurs
fairly often.
}
begin
PopLongA0;
if SimpleType(TP) then
writeln(OutFile, "\tmove.", suffix(TP^.Size), "\td0,(a0)")
else
SaveThrougha0(TP^.Size);
end;
Procedure SaveVal(ID : IDPtr);
{
This saves whatever's in d0 into the variable pointed to by
ID.
}
var
TotalSize : Integer;
Reg : Short;
begin
TotalSize := ID^.VType^.Size;
if (ID^.Object = global) or (ID^.Object = typed_const) then begin
if SimpleType(ID^.VType) then begin
if ID^.Level > 1 then { only for typed_const, of course }
writeln(OutFile, '\tmove.', Suffix(TotalSize), '\td0,_',
ID^.Name, '%', ID^.Unique)
else
writeln(OutFile, "\tmove.", Suffix(TotalSize),
"\td0,_", ID^.Name)
end else begin
if ID^.Level > 1 then { only for typed_const, of course }
writeln(OutFile, '\tlea\t_',ID^.Name,'%',ID^.Unique,',a0')
else
writeln(OutFile, "\tlea\t_", ID^.Name, ',a0');
SaveThrougha0(TotalSize);
end;
end else if (ID^.Object = local) or (ID^.Object = valarg) then begin
Reg := GetFramePointer(ID^.Level);
if SimpleType(ID^.VType) then
writeln(OutFile, "\tmove.", Suffix(TotalSize), "\td0,",
ID^.Offset, '(a', Reg, ')')
else begin
writeln(OutFile, "\tlea\t", ID^.Offset, '(a', Reg, '),a0');
savethrougha0(totalsize);
end;
end else begin
Reg := GetFramePointer(ID^.Level);
writeln(OutFile, "\tmove.l\t", ID^.Offset, '(a', Reg, '),a0');
if SimpleType(ID^.VType) then
writeln(OutFile, "\tmove.", Suffix(TotalSize), "\td0,(a0)")
else
SaveThrougha0(TotalSize);
end;
end;