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.