-- Copyright (C) 1984  by Xerox Corporation. All rights reserved. 
-- DESDLion.mesa, HGM, 12-Jan-84 22:48:38

DIRECTORY
  Environment USING [Byte],
  Inline USING [DBITXOR],
  Mopcodes USING [zESC],
  
  DESFace USING [Block, Blocks, Direction, IV, Key, Mode, parityTable];

DESDLion: MONITOR IMPORTS Inline EXPORTS DESFace SHARES DESFace =
  BEGIN
  
  BadParity: ERROR = CODE;
  NotImplemented: ERROR = CODE;
  
  Register: TYPE = MACHINE DEPENDENT {
    data(0), command(2), mode(6)};
  
  DESCtl: PROCEDURE [Register] = MACHINE CODE
    BEGIN
    Mopcodes.zESC, 0C0H;
    END;
    
  DESMP: PROCEDURE [Environment.Byte] = MACHINE CODE
    BEGIN
    Mopcodes.zESC, 0C1H;
    END;
    
  DESSP: PROCEDURE RETURNS [Environment.Byte] = MACHINE CODE
    BEGIN
    Mopcodes.zESC, 0C2H;
    END;

  DESWriteBlock: PROCEDURE [DESFace.Block] = MACHINE CODE
    BEGIN
    Mopcodes.zESC, 0C3H;
    END;
    
  DESReadBlock: PROCEDURE RETURNS [DESFace.Block] = MACHINE CODE
    BEGIN
    Mopcodes.zESC, 0C4H;
    END;

  DESWritePair: PROCEDURE [LONG CARDINAL] = MACHINE CODE
    BEGIN
    Mopcodes.zESC, 0C5H;
    END;
    
  DESReadPair: PROCEDURE RETURNS [LONG CARDINAL] = MACHINE CODE
    BEGIN
    Mopcodes.zESC, 0C6H;
    END;
    
  Delay6: PROCEDURE =
    BEGIN  -- Wait (at least) 6 clicks
    END;

  ResetTheChip: PROCEDURE =
    BEGIN
    DESCtl[command];
    DESMP[00H];
    Delay6[];
    [] ← DESSP[];  -- Discard leftover byte, if any (if not causes error, but nobody looks)
    END;
    
  LoadIV: PROCEDURE [ivP: LONG POINTER TO DESFace.IV, direction: DESFace.Direction] =
    BEGIN
    iv: LONG POINTER TO DESFace.Block = LOOPHOLE[ivP];
    DESCtl[command];
    DESMP[IF direction = encrypt THEN 85H ELSE 84H];
    Delay6[];
    DESCtl[data];
    DESWriteBlock[iv↑];
    END;
    
  LoadKey: PROCEDURE [keyP: LONG POINTER TO DESFace.Key, direction: DESFace.Direction] =
    BEGIN
    key: LONG POINTER TO DESFace.Block = LOOPHOLE[keyP];
    FOR i: CARDINAL IN [0..7] DO
      IF keyP[i].p # DESFace.parityTable[keyP[i].b] THEN ERROR BadParity;
      ENDLOOP;
    DESCtl[command];
    DESMP[IF direction = encrypt THEN 11H ELSE 12H];
    Delay6[];
    DESCtl[data];
    DESWriteBlock[key↑];
    END;
    
  SetMode: PROCEDURE [mode: DESFace.Mode, direction: DESFace.Direction] =
    BEGIN
    byte: Environment.Byte;
    SELECT direction FROM
      encrypt => byte ← 14H;
      decrypt => byte ← 00H;
      ENDCASE => ERROR;
    SELECT mode FROM
      ecb => NULL;
      cbc => byte ← byte + 2;
      cbcCheck => byte ← byte + 2;
      ENDCASE => ERROR;
    DESCtl[mode];
    DESMP[byte];
    Delay6[];
    END;
    
  CrunchBytes: PROCEDURE [from, to: DESFace.Blocks, nBlks: CARDINAL] =
    BEGIN
    Bytes: TYPE = LONG POINTER TO PACKED ARRAY [0..8) OF Environment.Byte;
    DESCtl[command];
    DESMP[0C0H];  -- Start
    DESCtl[data];
    FOR i: CARDINAL IN [0..nBlks) DO
      f: Bytes = LOOPHOLE[@from[i]];
      t: Bytes = LOOPHOLE[@to[i]];
      FOR i: CARDINAL IN [0..8) DO DESMP[f[i]]; ENDLOOP;
      Delay6[]; Delay6[]; Delay6[];
      FOR i: CARDINAL IN [0..8) DO t[i] ← DESSP[]; ENDLOOP;
      ENDLOOP;
    END;
    
  CrunchSingleBlocks: PROCEDURE [from, to: DESFace.Blocks, nBlks: CARDINAL] =
    BEGIN
    f: LONG POINTER TO DESFace.Block ← @from[0];
    t: LONG POINTER TO DESFace.Block ← @to[0];
    DESCtl[command];
    DESMP[0C0H];  -- Start
    Delay6[];
    DESCtl[data];
    FOR i: CARDINAL IN [0..nBlks) DO
      DESWriteBlock[f↑];
      Delay6[]; Delay6[]; Delay6[]; Delay6[];
      t↑ ← DESReadBlock[];
      f ← f + SIZE[DESFace.Block];
      t ← t + SIZE[DESFace.Block];
      ENDLOOP;
    END;
    
  CrunchBlocks: PROCEDURE [from, to: DESFace.Blocks, nBlks: CARDINAL] =
    BEGIN
    f: LONG POINTER TO DESFace.Block ← @from[0];
    t: LONG POINTER TO DESFace.Block ← @to[0];
    DESCtl[command];
    DESMP[0C0H];  -- Start
    Delay6[];
    DESCtl[data];
    IF nBlks = 0 THEN RETURN;
    DESWriteBlock[f↑];
    Delay6[];
    IF nBlks = 1 THEN BEGIN Delay6[]; Delay6[]; END;
    FOR i: CARDINAL IN [0..nBlks-1) DO
      f ← f + SIZE[DESFace.Block];
      DESWriteBlock[f↑];
      t↑ ← DESReadBlock[];
      t ← t + SIZE[DESFace.Block];
      ENDLOOP;
    t↑ ← DESReadBlock[];
    END;
    
  Crunch: PROCEDURE [from, to: DESFace.Blocks, nBlks: CARDINAL] =
    BEGIN
    TwoPairs: TYPE = RECORD [a, b: LONG CARDINAL];
    f: LONG POINTER TO ARRAY [0..9) OF TwoPairs ← LOOPHOLE[@from[0]];
    t: LONG POINTER TO ARRAY [0..8) OF TwoPairs ← LOOPHOLE[@to[0]];
    DESCtl[command];
    DESMP[0C0H];  -- Start
    Delay6[];
    DESCtl[data];
    IF nBlks = 0 THEN RETURN;
    DESWritePair[f[0].a];
    DESWritePair[f[0].b];
    f ← f + SIZE[DESFace.Block];
    nBlks ← nBlks - 1;
    IF nBlks = 0 THEN BEGIN Delay6[]; Delay6[]; END;
    -- This is the GO FASTER feature
    -- 4 blocks is as far as RLDILP+WLDILP can reach
    UNTIL nBlks <= 4 DO
      DESWritePair[f[0].a]; DESWritePair[f[0].b];
      t[0].a ← DESReadPair[]; t[0].b ← DESReadPair[];
      DESWritePair[f[1].a]; DESWritePair[f[1].b];
      t[1].a ← DESReadPair[]; t[1].b ← DESReadPair[];
      DESWritePair[f[2].a]; DESWritePair[f[2].b];
      t[2].a ← DESReadPair[]; t[2].b ← DESReadPair[];
      DESWritePair[f[3].a]; DESWritePair[f[3].b];
      t[3].a ← DESReadPair[]; t[3].b ← DESReadPair[];
      f ← f + SIZE[DESFace.Block, 4];
      t ← t + SIZE[DESFace.Block, 4];
      nBlks ← nBlks - 4;
      ENDLOOP;
    UNTIL nBlks = 0 DO
      DESWritePair[f[0].a];
      DESWritePair[f[0].b];
      t[0].a ← DESReadPair[];
      t[0].b ← DESReadPair[];
      f ← f + SIZE[DESFace.Block];
      t ← t + SIZE[DESFace.Block];
      nBlks ← nBlks - 1;
      ENDLOOP;
    t[0].a ← DESReadPair[];
    t[0].b ← DESReadPair[];
    END;
    
  CryptData: PUBLIC ENTRY PROCEDURE [
    keyP: POINTER TO DESFace.Key, nBlks: CARDINAL, from, to: DESFace.Blocks,
    direction: DESFace.Direction, mode: DESFace.Mode,
    seedP: LONG POINTER TO DESFace.IV ← NIL] =
    BEGIN ENABLE UNWIND => NULL;
    
    IF mode = checksum THEN ERROR NotImplemented;

    IF mode = cbcCheck AND direction = encrypt AND nBlks > 0 THEN
      BEGIN
      FOR blk: CARDINAL IN [0..nBlks - 1) DO
        XOR64[@from[nBlks - 1], @from[blk], @from[nBlks - 1]]; ENDLOOP;
      END;

    ResetTheChip[];
    
    SetMode[mode, direction];
    
    LoadKey[keyP, direction];
    
    IF mode = cbc OR mode = cbcCheck THEN LoadIV[seedP, direction];
    
    Crunch[from, to, nBlks];

    IF mode = cbcCheck AND direction = decrypt AND nBlks > 0 THEN
      BEGIN
      FOR blk: CARDINAL IN [0..nBlks - 1) DO
        XOR64[@to[nBlks - 1], @to[blk], @to[nBlks - 1]]; ENDLOOP;
      END

    END;
    
    
  Words4: TYPE = MACHINE DEPENDENT RECORD [a, b: LONG CARDINAL];

  XOR64: PROCEDURE [a, b, out: LONG POINTER] = INLINE
    BEGIN OPEN
      aLP: LOOPHOLE[a, LONG POINTER TO Words4],
      bLP: LOOPHOLE[b, LONG POINTER TO Words4],
      outP: LOOPHOLE[out, LONG POINTER TO Words4];

    outP.a ← Inline.DBITXOR[aLP.a, bLP.a];
    outP.b ← Inline.DBITXOR[aLP.b, bLP.b];

    END;




  END.