-- Copyright (C) 1982, 1984, 1985  by Xerox Corporation. All rights reserved. 
-- LupineRuntimeImpl.mesa, HGM, 18-Jan-85 17:45:03
-- Last edited by BZM on 12-May-82 19:17:55.

-- This module is used at runtime for marshaling and other common functions.


DIRECTORY
  Heap USING [systemZone, systemMDSZone],
  Inline USING [DIVMOD, LongCOPY],
  LupineRuntime USING [CheckPktLength, Words],
  MesaRPCLupine USING [
    DataLength, maxDataLength, ReceiveExtraPkt, RejectUnbound, RPCPkt,
    SendPrelimPkt],
  MesaRPC USING [CallFailed, Zones];


LupineRuntimeImpl: PROGRAM
  IMPORTS Heap, Inline, Lupine: LupineRuntime, RpcPrivate: MesaRPCLupine, RpcPublic: MesaRPC
  EXPORTS LupineRuntime =
  BEGIN

  Words: TYPE = LupineRuntime.Words;

  -- Lupine's errors.

  TranslationError: PUBLIC ERROR = CODE;
  BindingError: PUBLIC ERROR = CODE;
  RuntimeError: PUBLIC ERROR = CODE;

  -- These procedures map into ERRORs (for now).

  MarshalingError: PUBLIC PROCEDURE = {ERROR};

  MarshalingExprError: PUBLIC PROCEDURE RETURNS [never: UNSPECIFIED] = {
    RETURN[ERROR]};


  -- These procedures map into RpcRuntime ERRORs.

  UnmarshalingError: PUBLIC PROCEDURE = {
    ERROR RpcPublic.CallFailed[why: stubProtocol]};

  UnmarshalingExprError: PUBLIC PROCEDURE RETURNS [never: UNSPECIFIED] = {
    ERROR RpcPublic.CallFailed[why: stubProtocol]};

  DispatchingError: PUBLIC PROCEDURE RETURNS [never: UNSPECIFIED] = {
    ERROR RpcPrivate.RejectUnbound};


  defaultZones: PUBLIC MesaRPC.Zones ← [
    gc: NIL,
    heap: Heap.systemZone,
    mds: Heap.systemMDSZone];
  
  
  -- Parameter marshaling routines.


  Checking: BOOLEAN = TRUE;
  -- Turning this off is okay; critical runtime checks are always on.


  FinishThisPkt: PUBLIC PROCEDURE [
    pkt: RpcPrivate.RPCPkt, pktLength: RpcPrivate.DataLength]
    RETURNS [ --zeroPktLength:-- RpcPrivate.DataLength] =
    BEGIN
    Lupine.CheckPktLength[pkt: pkt, lastPkt: FALSE, pktLength: pktLength];
    [] ← RpcPrivate.ReceiveExtraPkt[prevPkt: pkt];
    RETURN[0];
    END;

  StartNextPkt: PUBLIC PROCEDURE [
    pkt: RpcPrivate.RPCPkt, pktLength: RpcPrivate.DataLength]
    RETURNS [ --zeroPktLength:-- RpcPrivate.DataLength] =
    BEGIN RpcPrivate.SendPrelimPkt[pkt: pkt, length: pktLength]; RETURN[0]; END;


  CopyToMultiplePkts: PUBLIC PROCEDURE [
    pkt: RpcPrivate.RPCPkt, pktLength: RpcPrivate.DataLength,
    dataAdr: LONG POINTER, dataLength: Words]
    RETURNS [ --newPktLength:-- RpcPrivate.DataLength] =
    BEGIN
    initialWords: Words = RpcPrivate.maxDataLength - pktLength;
    wholePkts: CARDINAL ← NULL;
    finalWords: Words ← NULL;
    IF Checking
      AND NOT
        (
          -- Preconditions:
          pktLength IN [0..RpcPrivate.maxDataLength] AND dataLength > 0
          AND pktLength + dataLength > RpcPrivate.maxDataLength) THEN ERROR;
    [quotient: wholePkts, remainder: finalWords] ← Inline.DIVMOD[
      num: dataLength - initialWords,
      ---- ---- ---- ---- ----  ----
      den: RpcPrivate.maxDataLength];
    -- Put initial data at the end of the current pkt.
    Inline.LongCOPY[
      from: dataAdr, to: @pkt.data[pktLength], nwords: initialWords];
    dataAdr ← dataAdr + initialWords;
    -- Put intermediate data into whole pkts.
    THROUGH [0..wholePkts) DO
      RpcPrivate.SendPrelimPkt[pkt: pkt, length: RpcPrivate.maxDataLength];
      Inline.LongCOPY[
        from: dataAdr, to: @pkt.data[0], nwords: RpcPrivate.maxDataLength];
      dataAdr ← dataAdr + RpcPrivate.maxDataLength;
      ENDLOOP;
    -- Put final data at the beginning of a fresh pkt.
    IF finalWords > 0 THEN
      BEGIN
      RpcPrivate.SendPrelimPkt[pkt: pkt, length: RpcPrivate.maxDataLength];
      Inline.LongCOPY[from: dataAdr, to: @pkt.data[0], nwords: finalWords];
      RETURN[finalWords];
      END
    ELSE RETURN[RpcPrivate.maxDataLength];
    END;


  CopyFromMultiplePkts: PUBLIC PROCEDURE [
    pkt: RpcPrivate.RPCPkt, pktLength: RpcPrivate.DataLength,
    dataAdr: LONG POINTER, dataLength: Words]
    RETURNS [ --newPktLength:-- RpcPrivate.DataLength] =
    BEGIN
    firstDataAdr: LONG POINTER = dataAdr;
    BEGIN
    ENABLE
      UNWIND =>
        -- If there's a fault, zero everything.  This ensures that any
        -- REF-containing fields are NIL and not chaos-causing garbage.
        IF dataLength > 0 THEN
          BEGIN
          firstDataAdr↑ ← 0;
          Inline.LongCOPY[
            from: firstDataAdr, to: firstDataAdr + 1, nwords: dataLength - 1];
          END;
    initialWords: Words = RpcPrivate.maxDataLength - pktLength;
    wholePkts: CARDINAL ← NULL;
    finalWords: Words ← NULL;
    IF Checking
      AND NOT
        (
          -- Preconditions:
          pktLength IN [0..RpcPrivate.maxDataLength] AND dataLength > 0
          AND pktLength + dataLength > RpcPrivate.maxDataLength) THEN ERROR;
    [quotient: wholePkts, remainder: finalWords] ← Inline.DIVMOD[
      num: dataLength - initialWords,
      ---- ---- ---- ---- ----  ----
      den: RpcPrivate.maxDataLength];
    -- Get initial data from the end of the current pkt.
    Inline.LongCOPY[
      to: dataAdr, from: @pkt.data[pktLength], nwords: initialWords];
    dataAdr ← dataAdr + initialWords;
    -- Get intermediate data from whole pkts.
    THROUGH [0..wholePkts) DO
      Lupine.CheckPktLength[
        pkt: pkt, lastPkt: FALSE, pktLength: RpcPrivate.maxDataLength];
      [] ← RpcPrivate.ReceiveExtraPkt[prevPkt: pkt];
      Inline.LongCOPY[
        to: dataAdr, from: @pkt.data[0], nwords: RpcPrivate.maxDataLength];
      dataAdr ← dataAdr + RpcPrivate.maxDataLength;
      ENDLOOP;
    -- Get final data from the beginning of a fresh pkt.
    IF finalWords > 0 THEN
      BEGIN
      Lupine.CheckPktLength[
        pkt: pkt, lastPkt: FALSE, pktLength: RpcPrivate.maxDataLength];
      [] ← RpcPrivate.ReceiveExtraPkt[prevPkt: pkt];
      Inline.LongCOPY[to: dataAdr, from: @pkt.data[0], nwords: finalWords];
      RETURN[finalWords];
      END
    ELSE RETURN[RpcPrivate.maxDataLength];
    END;  -- ENABLE UNWIND.
    END;


  -- Module initialization.

  -- CopyTo/FromMultiplePkts assume that zero is the representation of NIL.
  IF NIL # LOOPHOLE[0, POINTER] THEN ERROR;

  END.  -- LupineRuntimeImpl.