-- file DJumps.mesa
-- last modified by Sweet, 30-Oct-81 14:36:52
-- last modified by JGS, 13-Nov-81 12:33:56
-- last modified by Satterthwaite, December 16, 1982 9:12 am

DIRECTORY
  Alloc: TYPE USING [Notifier],
  Code: TYPE USING [codeptr, CodePassInconsistency],
  CodeDefs: TYPE USING [
    Base, CCIndex, CCNull, codeType, JumpCCIndex, JumpType,
    RelativePC, TableCodeBytes],
  Mopcodes: TYPE USING [
    zJ6, zJ8, zJB, zJEB, zJEBB, zJEP, zJGB, zJGEB, zJLB, zJLEB, zJNEB, zJNEBB,
    zJNEP, zJNZ4, zJNZB, zJUGB, zJUGEB, zJULB, zJULEB, zJW, zJZ4, zJZB],
  OpCodeParams: TYPE USING [
    BYTE, HB, MaxBBJumps, MaxFBJumps, zJn, zJNZn, zJZn],
  P5: TYPE USING [C0, C1, C1W, C2],
  P5F: TYPE USING [StartIndex],
  PeepholeDefs: TYPE USING [CJump, PackPair];

DJumps: PROGRAM
    IMPORTS CPtr: Code, P5, P5F, PeepholeDefs
    EXPORTS CodeDefs, P5F =
  BEGIN
  OPEN PeepholeDefs, OpCodeParams, CodeDefs;

  -- imported definitions

  BYTE: TYPE = OpCodeParams.BYTE;


  cb: CodeDefs.Base;        -- code base (local copy)

  DJumpsNotify: PUBLIC Alloc.Notifier =
    BEGIN  -- called by allocator whenever table area is repacked
    cb ← base[codeType];
    END;

  BindJump: PUBLIC PROC [min, max: INTEGER, c: JumpCCIndex]
      RETURNS [bindable: BOOL ← TRUE] =
    BEGIN -- compute size of jump instruction(s)
    -- max and min are counts of the number of bytes between the
    -- jump and the label.
    t: JumpType = cb[c].jtype;
    js: [0..7];
    IF t = JumpC THEN
      BEGIN -- "bound" by OutBinary
      cb[c].fixedup ← TRUE;
      cb[c].completed ← TRUE;
      RETURN
      END;
    IF ~cb[c].forward THEN BEGIN max ← -max; min ← -min END;
    SELECT t FROM
      Jump, JumpA =>
        IF min = max THEN 
	  SELECT min FROM
	    1, 2, 3, 5, 7 => js ← 1;
	    4, 6 => js ← 2;
	    IN [-MaxBBJumps..MaxFBJumps-2] => js ← 2;
	    ENDCASE => js ← 3
	ELSE SELECT TRUE FROM
	  (min >= 1 AND max <= 3) => js ← 1;
	  (min > 7 AND max <= MaxFBJumps-2) => js ← 2;
	  max IN [-MaxBBJumps..0] => js ← 2;
	  min NOT IN [-MaxBBJumps..MaxFBJumps-2] => js ← 3;
	  ENDCASE => bindable ← FALSE;
      JumpLIO => js ← 3;
      JumpCA => SELECT TRUE FROM
	max IN [-MaxBBJumps..MaxFBJumps-2] => js ← 2;
	min NOT IN [-MaxBBJumps..MaxFBJumps-2] => js ← 3;
	ENDCASE =>  bindable ← FALSE;
      BYTEJumpN, BYTEJumpE => SELECT TRUE FROM
	(cb[c].jparam IN HB AND min >= 2 AND max <= 17) => js ← 2;
	max IN [-MaxBBJumps..MaxFBJumps-3] => js ← 3;
	min NOT IN [-MaxBBJumps..MaxFBJumps-3] => 
	  js ← IF cb[c].jparam IN HB THEN 5 ELSE 6;
	ENDCASE => bindable ← FALSE;
      ZJumpE => SELECT TRUE FROM
	(min >= 2 AND max <= 3) => js ← 1;
	(min > 3 AND max <= MaxFBJumps-2) => js ← 2;
	max IN [-MaxBBJumps..2) => js ← 2;
	min NOT IN [-MaxBBJumps..MaxFBJumps-2] => js ← 4;
	ENDCASE => bindable ← FALSE;
      ZJumpN => SELECT TRUE FROM
	(min >= 2 AND max <= 3) => js ← 1;
	(min > 3 AND max <= MaxFBJumps-2) => js ← 2;
	max IN [-MaxBBJumps..2) => js ← 2;
	min NOT IN [-MaxBBJumps..MaxFBJumps-2] => js ← 4;
	ENDCASE => bindable ← FALSE;
      ENDCASE => SELECT TRUE FROM
	max IN [-MaxBBJumps..MaxFBJumps-2] => js ← 2;
	min NOT IN [-MaxBBJumps..MaxFBJumps-2] => js ← 5;
	ENDCASE =>  bindable ← FALSE;
    IF bindable THEN {cb[c].fixedup ← TRUE; cb[c].jsize ← js};
    RETURN
    END;


  CodeJump: PUBLIC PROC [nbytes: INTEGER, c: JumpCCIndex] =
    BEGIN -- code all jump instruction(s)
    OPEN Mopcodes, OpCodeParams;
    l: [0..7] = cb[c].jsize;
    forward: BOOL = cb[c].forward;
    t: JumpType;
    RelJumpOps: ARRAY JumpType[JumpE..ZJumpN] OF BYTE = [
      zJEB, zJNEB, zJLB, zJGEB, zJGB, zJLEB, zJULB, zJUGEB, zJUGB, zJULEB,
      zJZB, zJNZB];
    jDist: INTEGER ← IF ~forward THEN -nbytes ELSE nbytes+l;
    IF l > 3 THEN jDist ← jDist + 3 - l;
    -- Computing dist copes with the fact that the parameter to a jump
    -- instruction is added to the byte pc of the first byte of the instruction
    -- nbytes is the number of bytes between the jump and its label
    CPtr.codeptr ← c;
    t ← cb[c].jtype;
    SELECT t FROM
     Jump, JumpA, JumpCA =>
      SELECT l FROM
       1 =>
        BEGIN
        SELECT jDist FROM
	  2, 3, 4 => P5.C0[zJn+jDist-2];
	  6 => P5.C0[zJ6];
	  8 => P5.C0[zJ8];
	  ENDCASE => ERROR;
        END;
       2 =>
        BEGIN
        P5.C1[zJB, jDist];
        END;
       ENDCASE =>
        BEGIN
        P5.C1W[zJW, jDist];
        END;
     ZJumpE, ZJumpN =>
      SELECT l FROM
       1 =>
        BEGIN
        P5.C0[(IF t=ZJumpE THEN zJZn ELSE zJNZn)+jDist-3];
        END;
       2 =>
        BEGIN
        P5.C1[(IF t = ZJumpE THEN zJZB ELSE zJNZB), jDist];
        END;
       ENDCASE =>
        BEGIN
        P5.C0[(IF t = ZJumpE THEN zJNZ4 ELSE zJZ4)];
        P5.C1W[zJW, jDist];
        END;
     BYTEJumpE =>
      SELECT l FROM
       2 => P5.C1[zJEP, PackPair[cb[c].jparam, jDist-4]];
       3 => P5.C2[zJEBB, cb[c].jparam, jDist];
       5 => {P5.C1[zJNEP, PackPair[cb[c].jparam, 5-4]]; P5.C1W[zJW, jDist]};
       6 => {P5.C2[zJNEBB, cb[c].jparam, 6]; P5.C1W[zJW, jDist]};
       ENDCASE => SIGNAL CPtr.CodePassInconsistency;
     BYTEJumpN =>
      SELECT l FROM
       2 => P5.C1[zJNEP, PackPair[cb[c].jparam, jDist-4]];
       3 => P5.C2[zJNEBB, cb[c].jparam, jDist];
       5 => {P5.C1[zJEP, PackPair[cb[c].jparam, 5-4]]; P5.C1W[zJW, jDist]};
       6 => {P5.C2[zJEBB, cb[c].jparam, 6]; P5.C1W[zJW, jDist]};
       ENDCASE => SIGNAL CPtr.CodePassInconsistency;
     JumpC => NULL;
     JumpLIO => NULL; -- later fixed up by OutBinary
     ENDCASE =>
      SELECT l FROM
       2 =>
        BEGIN
        P5.C1[RelJumpOps[t], jDist];
        END;
       ENDCASE =>
        BEGIN
        P5.C1[RelJumpOps[CJump[t]], 5];
        P5.C1W[zJW, jDist];
        END;
    cb[c].completed ← TRUE;
    cb[c].jsize ← 0; -- so it doesn't have to be ignored in ComputeJumpDistance
    RETURN
    END;

  dMinMax: ARRAY {unconditional, equal, byte, relational, enable} OF
    PACKED ARRAY BOOL OF RECORD [min,max: [0..15]] ← [
      unconditional:	[[2,3], [1,3]], -- (backward, forward)
      equal: 		[[2,4], [1,4]], -- (backward, forward)
      byte:		[[3,4], [2,4]], -- (backward, forward)
      relational:	[[2,5], [2,5]], -- (backward, forward)
      enable:		[[3,3], [3,3]]];

  FillInPCEstimates: PUBLIC PROC =
    BEGIN
    min, max: RelativePC ← 0;
    FOR k: CCIndex ← P5F.StartIndex, cb[k].flink UNTIL k = CCNull DO
      WITH cc:cb[k] SELECT FROM
	code =>
	  BEGIN
	  t: CARDINAL = cc.isize;
	  min ← min + t;
	  max ← max + t;
	  END;
	jump => IF cc.jtype # JumpC THEN
	  BEGIN
	  IF ~cc.fixedup THEN
	    BEGIN
	    dMin, dMax: [0..15];
	    [dMin,dMax] ← dMinMax[(SELECT cc.jtype FROM
		Jump, JumpA, JumpCA => unconditional,
		JumpE, JumpN, ZJumpE, ZJumpN => equal,
		JumpEnable => enable,
		BYTEJumpN => byte,
		ENDCASE => relational)][cc.forward];
	    IF cc.jtype = BYTEJumpN OR cc.jtype = BYTEJumpE THEN 
	      dMax ← dMax + (IF cc.jparam IN HB THEN 1 ELSE 2);
	    IF ~cc.forward THEN {cc.minPC ← min; cc.maxPC ← max};
	    min ← min+dMin; max ← max+dMax;
	    IF cc.forward THEN {cc.minPC ← min; cc.maxPC ← max};
	    END
	  ELSE IF ~cc.completed THEN
	    BEGIN
	    t: CARDINAL = cc.jsize;
	    IF ~cc.forward THEN {cc.minPC ← min; cc.maxPC ← max};
	    min ← min + t;
	    max ← max + t;
	    IF cc.forward THEN {cc.minPC ← min; cc.maxPC ← max};
	    END;
	  END;
	label => {cc.minPC ← min; cc.maxPC ← max};
	other =>  WITH cc SELECT FROM
	  table =>
	    BEGIN
	    min ← min + TableCodeBytes;
	    max ← max + TableCodeBytes;
	    END;
	  ENDCASE;
	ENDCASE;
      ENDLOOP;
    END;

  END.