-- ChecksumsImpl.mesa
-- last edit by: HGM  March 5, 1981  3:47 PM
-- last edit by: BLyon  January 16, 1981  9:54 AM

DIRECTORY
  BufferDefs USING [OisBuffer],
  Checksums USING [ComputeChecksum],
  CommunicationInternal USING [],
  CommUtilDefs USING [thisIsAnAlto],
  Inline USING [BITAND, BITNOT, BITOR, BITSHIFT],
  Mopcodes USING [zADD, zDADD, zEXCH, zJ2, zJNE3, zLIN1, zLI0, zPUSH],
  OISCPTypes USING [BufferBody];

ChecksumsImpl: PROGRAM
  IMPORTS Checksums, Inline
  EXPORTS CommunicationInternal, Checksums
  SHARES BufferDefs =

  BEGIN OPEN Checksums;

  -- These procedures sets the checksum field of the Ois Packet.
  SetChecksum: PUBLIC PROCEDURE [b: BufferDefs.OisBuffer] =
    BEGIN
    b.ois.checksum ←
      IF CommUtilDefs.thisIsAnAlto THEN SlowlyComputeChecksum[
      0, (b.ois.pktLength + 1)/2 - 1, @b.ois.pktLength]
      ELSE ComputeChecksum[0, (b.ois.pktLength + 1)/2 - 1, @b.ois.pktLength];
    END;

  -- These procedures checks the checksum field of the Ois Packet,
  -- and returns TRUE or FALSE.  The buffer is always a system buffer

  TestChecksum: PUBLIC PROCEDURE [b: BufferDefs.OisBuffer] RETURNS [BOOLEAN] =
    BEGIN
    cs: CARDINAL ← 0;
    IF b.ois.checksum = 177777B THEN RETURN[TRUE];
    cs ←
      IF CommUtilDefs.thisIsAnAlto THEN SlowlyComputeChecksum[
      cs, (b.ois.pktLength + 1)/2 - 1, @b.ois.pktLength]
      ELSE ComputeChecksum[cs, (b.ois.pktLength + 1)/2 - 1, @b.ois.pktLength];
    RETURN[b.ois.checksum = cs];
    END;

  SlowlyComputeChecksum: PUBLIC PROCEDURE [
    cs: CARDINAL, nWords: CARDINAL, p: LONG POINTER] RETURNS [CARDINAL] =
    BEGIN
    t: CARDINAL;
    THROUGH [0..nWords) DO
      IF cs > (t ← cs + p↑) THEN cs ← t + 1 ELSE cs ← t;
      IF cs >= 100000B THEN cs ← cs*2 + 1 ELSE cs ← cs*2;
      p ← p + 1;
      ENDLOOP;
    IF cs = 177777B THEN cs ← 0;
    RETURN[cs];
    END;

  -- This procedure updates the checksum field of the Ois Packet
  -- after it increments the oisTransportControl.

  zz: PRIVATE POINTER TO OISCPTypes.BufferBody = NIL;
  transportControlOffset: CARDINAL =
    LOOPHOLE[@zz.transCntlAndPktTp, CARDINAL] - LOOPHOLE[zz, CARDINAL];
  IncrOisTransportControlAndUpdateChecksum: PUBLIC PROCEDURE [
    b: BufferDefs.OisBuffer] =
    BEGIN
    oldVal: WORD ← LOOPHOLE[b.ois.transCntlAndPktTp, CARDINAL];
    resisdual: INTEGER;
    b.ois.transCntlAndPktTp.transportControl.hopCount ←
      b.ois.transCntlAndPktTp.transportControl.hopCount + 1;
    IF b.ois.checksum = 177777B THEN RETURN;
    resisdual ← Inline.BITAND[
      (b.ois.pktLength + 1)/2 - transportControlOffset, 17B];
    b.ois.checksum ← OnesAdd[
      b.ois.checksum, LeftCycle[
      OnesSub[LOOPHOLE[b.ois.transCntlAndPktTp, CARDINAL], oldVal], resisdual]];
    END; -- UpdateChecksum

  LeftCycle: PROCEDURE [val: CARDINAL, dist: INTEGER] RETURNS [CARDINAL] = INLINE
    BEGIN OPEN Inline;
    RETURN[BITOR[BITSHIFT[val, dist], BITSHIFT[val, dist - 16]]];
    END;

  OnesSub: PROCEDURE [a, b: CARDINAL] RETURNS [CARDINAL] = INLINE
    BEGIN RETURN[OnesAdd[a, Inline.BITNOT[b]]]; END;

  -- Beware: Making this work on a real Alto is quite tricky.  If you get it wrong, it will do things like store 0 in the word the new checksum points at.
  OnesAdd: PROCEDURE [a, b: CARDINAL] RETURNS [c: CARDINAL] = MACHINE CODE
    BEGIN
    -- c ← a + b;  IF c<a THEN c ← c + 1; IF c=177777B THEN c ← 0; 
    Mopcodes.zLI0;
    Mopcodes.zEXCH;
    Mopcodes.zLI0;
    Mopcodes.zDADD;
    Mopcodes.zADD;
    Mopcodes.zLIN1;
    Mopcodes.zJNE3;
    Mopcodes.zLI0;
    Mopcodes.zJ2;
    Mopcodes.zPUSH;
    END;

  END.  -- ChecksumsImpl