External Program Linkage on M680x0 Systems
You have seen the external program linkage code used on EMAS which was initially
implemented on an IBM360 style architecture.
The following section shows an example of the function calling sequences
typically used on the M680x0. After these we show how they might be modified to
deal with external linkage in the style of EMAS.
Function Calling Sequences on the M680x0
An M68000 C++ compiler locates the activation records of functions on the
system stack.
<---- current activation record ---->
local variables parameters --->
--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+--
| | | | | | | |LNK| R | | | | | |
--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+--
^ ^
not yet used | | used
sp a6
The diagram shows the activation record of the function enigma (described
below) after its parameters have been pushed onto the stack and the function
has been called with a JSR enigma instruction, and after the initial
LINK A6,#$FFFC instruction has been obeyed.
Here is the function enigma:
char enigma(char c, int stage, bool move)
{
bool nextMove;
Cipher *wheel;
wheel = wheels[stage];
if (stage == 0)
{ // It's the reflector (not a real wheel).
c = wheel->forward(c);
}
else
{ // Deal with the real wheels.
nextMove = move && wheel->isLast();
c = wheel->forward(c);
c = enigma(c, stage-1, nextMove);
c = wheel->backward(c);
if (move) wheel->step();
}
return c;
}
Here is the function enigma with an assembly language translation of
its statements:
char enigma(char c, int stage, bool move)
{
enigma:
00000000: 4E56 FFFC LINK A6,#$FFFC [1]
00000004: 48E7 1020 MOVEM.L D3/A2,-(A7) [1]
wheel = wheels[stage];
00000008: 262E 000A MOVE.L $000A(A6),D3 [2]
0000000C: E583 ASL.L #$2,D3 [2]
0000000E: 41F9 0000 0000 LEA wheels,A0 [2]
00000014: 2030 3000 MOVE.L $00(A0,D3.W),D0 [2]
00000018: 2440 MOVEA.L D0,A2 [2]
if (stage == 0)
0000001A: 202E 000A MOVE.L $000A(A6),D0
0000001E: 6612 BNE.S *+$0014 ; 00000032
{ // It's the reflector (not a real wheel).
c = wheel->forward(c);
00000020: 1F2E 000E MOVE.B $000E(A6),-(A7) [3]
00000024: 2F0A MOVE.L A2,-(A7) [3]
00000026: 4EB9 0000 0000 JSR Cipher::forward(char) [3]
0000002C: 1D40 000E MOVE.B D0,$000E(A6) [3]
}
00000030: 6060 BRA.S *+$0062 ; 00000092
else
{ // Deal with the real wheels.
nextMove = move && wheel->isLast();
00000032: 102E 0008 MOVE.B $0008(A6),D0
00000036: 670C BEQ.S *+$000E ; 00000044
00000038: 2F0A MOVE.L A2,-(A7)
0000003A: 4EB9 0000 0000 JSR Cipher::isLast(void)
00000040: 4A00 TST.B D0
00000042: 6604 BNE.S *+$0006 ; 00000048
00000044: 7000 MOVEQ #$00,D0
00000046: 6002 BRA.S *+$0004 ; 0000004A
00000048: 7001 MOVEQ #$01,D0
0000004A: 1D40 FFFC MOVE.B D0,$FFFC(A6)
c = wheel->forward(c);
0000004E: 1F2E 000E MOVE.B $000E(A6),-(A7)
00000052: 2F0A MOVE.L A2,-(A7)
00000054: 4EB9 0000 0000 JSR Cipher::forward(char)
0000005A: 1D40 000E MOVE.B D0,$000E(A6)
c = enigma(c, stage-1, nextMove);
0000005E: 1F00 MOVE.B D0,-(A7) [4]
00000060: 202E 000A MOVE.L $000A(A6),D0 [4]
00000064: 5380 SUBQ.L #$1,D0 [4]
00000066: 2F00 MOVE.L D0,-(A7) [4]
00000068: 1F2E FFFC MOVE.B $FFFC(A6),-(A7) [4]
0000006C: 4EB9 0000 0000 JSR enigma [4]
00000072: 1D40 000E MOVE.B D0,$000E(A6) [4]
c = wheel->backward(c);
00000076: 1F00 MOVE.B D0,-(A7)
00000078: 2F0A MOVE.L A2,-(A7)
0000007A: 4EB9 0000 0000 JSR Cipher::backward(char)
00000080: 1D40 000E MOVE.B D0,$000E(A6)
if (move) wheel->step();
00000084: 102E 0008 MOVE.B $0008(A6),D0
00000088: 6708 BEQ.S *+$000A ; 00000092
0000008A: 2F0A MOVE.L A2,-(A7)
0000008C: 4EB9 0000 0000 JSR Cipher::step(void)
}
return c;
00000092: 102E 000E MOVE.B $000E(A6),D0 [5]
}
00000096: 4CDF 0408 MOVEM.L (A7)+,D3/A2 [6]
0000009A: 4E5E UNLK A6 [6]
0000009C: 205F MOVEA.L (A7)+,A0 [6]
0000009E: 504F ADDQ.W #$8,A7 [6]
000000A0: 4ED0 JMP (A0) [6]
000000A2
R is the return address to the calling program. It was set when
the function was called with a JSR enigma instruction.
LNK is the old value of a6, ie the link to the activation record of the
calling program. It was set by the LINK A6,#$FFFC instruction.
"c" is addressed as a byte at $000E(A6).
"stage" is addressed as a long at $000A(A6).
"move" is addressed as a byte at $0008(A6).
"nextMove" is addressed as a byte at $FFFC(A6).
enigma:
00000000: 4E56 FFFC LINK A6,#$FFFC [1]
00000004: 48E7 1020 MOVEM.L D3/A2,-(A7) [1]
This causes the old value of a6 to be pushed onto the stack (LNK) and a6 to be
set to the new value of the stack pointer. Because the displacement is -4
(#$FFFC) the stack pointer is moved 4 bytes to the left to make space for
the local variables. a6 is used to address the current activation record.
The MOVEM.L D3/A2,-(A7) instruction is saving registers which the function
wishes to use.
wheel = wheels[stage];
00000008: 262E 000A MOVE.L $000A(A6),D3 [2]
0000000C: E583 ASL.L #$2,D3 [2]
0000000E: 41F9 0000 0000 LEA wheels,A0 [2]
00000014: 2030 3000 MOVE.L $00(A0,D3.W),D0 [2]
00000018: 2440 MOVEA.L D0,A2 [2]
stage is copied to D3, where it is multiplied by 4 (ASL.L #$2,D3), so that
it can index the array of longs (wheels). The address of the start of wheels
is loaded into A0 for use in the next instruction which picks up a long
from wheels[stage] and copies it to D0. D0 is then copied to A2 which will
be used to index the private variables of wheel.
{ // Its the reflector (not a real wheel).
c = wheel->forward(c);
00000020: 1F2E 000E MOVE.B $000E(A6),-(A7) [3]
00000024: 2F0A MOVE.L A2,-(A7) [3]
00000026: 4EB9 0000 0000 JSR Cipher::forward(char) [3]
0000002C: 1D40 000E MOVE.B D0,$000E(A6) [3]
parameter c is pushed onto the stack. A2 which holds a pointer to the variables
for this instance of the class is also pushed onto the stack. The function
forward(char) is then called. When it returns, we copy the result value (in D0)
into c.
c = enigma(c, stage-1, nextMove);
0000005E: 1F00 MOVE.B D0,-(A7) [4]
00000060: 202E 000A MOVE.L $000A(A6),D0 [4]
00000064: 5380 SUBQ.L #$1,D0 [4]
00000066: 2F00 MOVE.L D0,-(A7) [4]
00000068: 1F2E FFFC MOVE.B $FFFC(A6),-(A7) [4]
0000006C: 4EB9 0000 0000 JSR enigma [4]
00000072: 1D40 000E MOVE.B D0,$000E(A6) [4]
We first stack parameter c (still in D0), pick up stage, subtract 1
and stack that value (stage-1), we then stack parameter nextMove.
The function enigma is then called (recursively). On return we copy the result
value (returned in D0) into c.
}
return c;
00000092: 102E 000E MOVE.B $000E(A6),D0 [5]
This moves c into D0 which is used to return the result of the function.
}
00000096: 4CDF 0408 MOVEM.L (A7)+,D3/A2 [6]
0000009A: 4E5E UNLK A6 [6]
0000009C: 205F MOVEA.L (A7)+,A0 [6]
0000009E: 504F ADDQ.W #$8,A7 [6]
000000A0: 4ED0 JMP (A0) [6]
000000A2
These instructions implement the return from the function enigma to the
calling program.
00000096: 4CDF 0408 MOVEM.L (A7)+,D3/A2 [6]
restores the values of D3 and A2 which were saved on entry to enigma.
0000009A: 4E5E UNLK A6 [6]
makes sp equal to a6 to deallocate the local variables. A6 is then filled with
the long word (LNK) pulled from the stack, so that it points to the activation
record of the calling program.
0000009C: 205F MOVEA.L (A7)+,A0 [6]
The next long word on the stack is the return address R. This is pulled from
the stack by the MOVEA.L (A7)+,A0 instruction and stored in register A0.
0000009E: 504F ADDQ.W #$8,A7 [6]
The ADDQ.W #$8,SP instruction moves the stack pointer past the parameters which
occupy 8 bytes on the stack.
000000A0: 4ED0 JMP (A0) [6]
000000A2
Finally, the JMP (A0) instruction returns us to the calling program.
External Function Calling Sequences on the M680x0
From the above we see that the (internal) calling sequence is something like:
c = wheel->forward(c);
00000020: 1F2E 000E MOVE.B $000E(A6),-(A7) [3]
00000024: 2F0A MOVE.L A2,-(A7) [3]
00000026: 4EB9 0000 0000 JSR Cipher::forward(char) [3]
0000002C: 1D40 000E MOVE.B D0,$000E(A6) [3]
i.e. the parameters are first pushed onto the stack, the function is called and
the result value (if any) is recovered.
A called function has entry and exit sequences like:
enigma:
00000000: 4E56 FFFC LINK A6,#$FFFC [1]
00000004: 48E7 1020 MOVEM.L D3/A2,-(A7) [1]
}
00000096: 4CDF 0408 MOVEM.L (A7)+,D3/A2 [6]
0000009A: 4E5E UNLK A6 [6]
0000009C: 205F MOVEA.L (A7)+,A0 [6]
0000009E: 504F ADDQ.W #$8,A7 [6]
000000A0: 4ED0 JMP (A0) [6]
000000A2
of which the MOVEM.L instructions are peculiar to the particular function.
If we wished to have EMAS style external function calling sequences, we might do
something like the following:
We will retain the three word (code base, gla base, entry point) blocks used in
EMAS, but we will not normally need to use the code base as the M680x0 code
shown is already self-relocating.
We will leave the called routine entry and exit sequences unchanged so that
internal calls can also be left the same. We will, however, have to change calls
to external functions.
A call to an external function (ie a function loaded in a different segment and
having a different gla block), would look something like this:
A5 Gla base
A4 Code base (only if required)
b Displacement of linkage block from start of gla block of calling program
[MOVE.L A4,-(A7)] Save code base
MOVE.L A5,-(A7) Save gla base
MOVE p1,-(A7) Push parameters
... onto stack
MOVE pn,-(A7) as usual
MOVE.L b+$C(A5),A0 Load entry point
MOVE.L b+$8(A5),A5 Load new gla base
JSR (A0) Call function
MOVE D0,result Recover result
MOVE.L (A7)+,A5 Restore gla base
[MOVE.L (A7)+,A4] Restore code base
The instructions to save and restore the code base may not be needed.
The addresses b+$C and b+$8 are the displacements of the entry point and gla
base addresses in the linkage block. These are, of course, known to the compiler
that compiled the code.