-- Conditionals.Mesa
-- Edited by:
--           Barbara on February 9, 1979  4:40 PM
--           Sandman on July 18, 1980  11:18 AM
--           Bruce on September 26, 1980  10:42 PM

DIRECTORY
  BP USING [
    AbortWithError, BBHandle, BytePC, Error, Fail, FindBBNum, WriteError],
  DebugFormat USING [BBHandle, Foo],
  DebugOps USING [
    Foo, InvalidNumber, Interpret, ShortCopyREAD, ShortCopyWRITE, ShortREAD, ShortWRITE,
    StringExpToDecimal],
  DI USING [dereferenced],
  Inline USING [LowHalf],
  MachineDefs USING [BBHandle, BYTE, ByteToRealPC, GFHandle, RealPC, RealToBytePC, WordLength, UBBPointer, UserBreakBlock],
  SDDefs USING [sBreakBlock, sBreakBlockSize, SD],
  State USING [ContextType, Get, GetGS, GSHandle, Handle],
  Storage USING [CopyString, FreeStringNil],
  Tree USING [NodeName];

Conditionals: PROGRAM
  IMPORTS BP, DebugOps, DI, Inline, MachineDefs, State, Storage
  EXPORTS BP =
  BEGIN OPEN BP;

  UBBPointer: TYPE = MachineDefs.UBBPointer;
  GFHandle: TYPE = MachineDefs.GFHandle;

  data: State.GSHandle ← State.GetGS[];
  ubb: UBBPointer;

  AttachCondition: PUBLIC PROCEDURE [bbNum, cond: STRING] =
    BEGIN Conditionalize[GetBreak[bbNum],cond] END;

  ClearCondition: PUBLIC PROCEDURE [bbNum: STRING] = {AttachCondition[bbNum,NIL]};

  Conditionalize: PUBLIC PROC [bb: DebugFormat.BBHandle, cond: STRING] =
    BEGIN
    userBB: MachineDefs.UserBreakBlock;
    IF bb = NIL THEN AbortWithWrittenError[notFound];
    IF cond = NIL THEN {
      IF bb.condition # NIL THEN bb.condition ← Storage.FreeStringNil[bb.condition];
      FreeUserBB[bb.gf,bb.pc];
      RETURN};
    IF data.worryBreaks THEN WriteError[worryOn];
    userBB ← [
      frame: bb.gf, pc: MachineDefs.ByteToRealPC[bb.pc], ptrL: NIL, ptrR: NIL,
      posnL: 0, posnR: 0, sizeL: 16, sizeR: 16, inst:bb.inst, relation:,
      immediateR: FALSE, counterL: FALSE, localL: FALSE, localR: FALSE,
      stackRelative: FALSE];
    ubb ← @userBB;
    ParseConditional[cond, @userBB, bb];
    bb.condition ← Storage.CopyString[cond];
    WriteUserBB[@userBB];
    END;

  GetBreak: PROC [num: STRING] RETURNS [bb: DebugFormat.BBHandle] =
    BEGIN
    i: INTEGER ← 0;
    i ← DebugOps.StringExpToDecimal[num ! DebugOps.InvalidNumber => CONTINUE];
    IF i = 0 THEN RETURN[NIL];
    RETURN [BP.FindBBNum[LOOPHOLE[i,CARDINAL]]];
    END;

  ParseConditional: PROCEDURE [c: STRING, p: UBBPointer, bb: DebugFormat.BBHandle] =
    BEGIN
    h: State.Handle = State.Get[];
    howSet: State.ContextType = h.howSet;
    context: POINTER = h.interpretContext;
    IF Counter[c,p] THEN RETURN;
    h.interpretContext ← bb;
    h.howSet ← break;
    DebugOps.Interpret[c ! UNWIND => {h.interpretContext ← context; h.howSet ← howSet}];
    h.interpretContext ← context;
    h.howSet ← howSet;
    RETURN
    END;
  
  Counter: PROC [c: STRING, p: UBBPointer] RETURNS [BOOLEAN] =
    BEGIN
    FOR i: CARDINAL IN [0..c.length) DO
      IF c[i] ~IN['0..'9] THEN RETURN[FALSE];
      ENDLOOP;
    p.counterL ← TRUE;
    p.relation ← eq;
    p.ptrL ← LOOPHOLE[0];
    p.ptrR ← LOOPHOLE[DebugOps.StringExpToDecimal[c]];
    RETURN[TRUE];
    END;

  Condition: PUBLIC PROC [
      left: DebugFormat.Foo, rel: Tree.NodeName, right: DebugFormat.Foo] =
    BEGIN
    bitsL: CARDINAL = left.words*MachineDefs.WordLength + left.bits;
    bitsR: CARDINAL = right.words*MachineDefs.WordLength + right.bits;
    IF DI.dereferenced THEN AbortWithWrittenError[spare1];
    SELECT TRUE FROM
      bitsL > MachineDefs.WordLength => AbortWithWrittenError[size];
      bitsR > MachineDefs.WordLength => AbortWithWrittenError[size];
      ENDCASE;
    SELECT rel FROM
      relE => ubb.relation ← eq;
      relN => ubb.relation ← ne;
      relL => ubb.relation ← lt;
      relLE => ubb.relation ← le;
      relG => ubb.relation ← gt;
      relGE => ubb.relation ← ge;
      ENDCASE => AbortWithWrittenError[invalidRelation];
    ubb.ptrL ← Inline.LowHalf[left.addr.base];
    ubb.posnL ← left.addr.offset;
    ubb.localL ← left.addr.local;
    ubb.stackRelative ← left.addr.useStack;
    ubb.sizeL ← bitsL;
    IF right.there THEN {
      IF right.addr.useStack THEN AbortWithWrittenError[stack];
      ubb.ptrR ← Inline.LowHalf[right.addr.base];
      ubb.posnR ← right.addr.offset;
      ubb.localR ← right.addr.local;
      ubb.sizeR ← bitsR}
    ELSE {
      ubb.immediateR ← TRUE;
      ubb.ptrR ← right.addr.base↑;
      ubb.posnR ← 0;
      ubb.sizeR ← 16};
    END;

  WriteUserBB: PUBLIC PROC [ubb: MachineDefs.UBBPointer] =
    BEGIN
    i: CARDINAL;
    bbHandle: MachineDefs.BBHandle ← DebugOps.ShortREAD[@SDDefs.SD[SDDefs.sBreakBlock]];
    i ← GetUserBB[ubb.frame, MachineDefs.RealToBytePC[ubb.pc] !
      BP.Fail => {AbortWithWrittenError[type]; CONTINUE}];
    DebugOps.ShortCopyWRITE[from: ubb, to: @bbHandle.blocks[i],
	nwords: SIZE[MachineDefs.UserBreakBlock]];
    RETURN
    END;

  GetUserBB: PUBLIC PROC [gf: GFHandle, pc: BytePC] RETURNS [i: CARDINAL] =
    BEGIN OPEN SDDefs;
    bbHandle: MachineDefs.BBHandle;
    n: CARDINAL;
    i ← FindUserBB[gf,pc];
    IF i # LAST[CARDINAL] THEN RETURN;
    bbHandle ← DebugOps.ShortREAD[@SD[sBreakBlock]];
    n ← DebugOps.ShortREAD[@bbHandle.length];
    IF n >= LOOPHOLE[DebugOps.ShortREAD[@SD[sBreakBlockSize]],CARDINAL] /
      SIZE[MachineDefs.UserBreakBlock] THEN SIGNAL BP.Fail[tooManyConditions];
    DebugOps.ShortWRITE[@bbHandle.length,n+1];
    RETURN[n]
    END;

  FindUserBB: PUBLIC PROC [gf: GFHandle, pc: BytePC] RETURNS [i: CARDINAL] =
    BEGIN OPEN SDDefs;
    bbHandle: MachineDefs.BBHandle = DebugOps.ShortREAD[@SD[sBreakBlock]];
    n: CARDINAL = DebugOps.ShortREAD[@bbHandle.length];
    wpc: MachineDefs.RealPC = MachineDefs.ByteToRealPC[pc];
    FOR i IN [0..n) DO
      IF DebugOps.ShortREAD[@bbHandle.blocks[i].frame] = gf
      AND DebugOps.ShortREAD[@bbHandle.blocks[i].pc] = wpc
	THEN RETURN[i];
      ENDLOOP;
    RETURN[LAST[CARDINAL]]
    END;

  FreeUserBB: PUBLIC PROCEDURE [gf: GFHandle, pc: BytePC] =
    BEGIN OPEN SDDefs;
    i: CARDINAL = FindUserBB[gf,pc];
    bbHandle: MachineDefs.BBHandle;
    n: CARDINAL;
    local: MachineDefs.UserBreakBlock;
    IF i = LAST[CARDINAL] THEN AbortWithWrittenError[conditionNotFound];
    bbHandle ← DebugOps.ShortREAD[@SD[sBreakBlock]];
    n ← DebugOps.ShortREAD[@bbHandle.length];
    FOR j: CARDINAL IN [i+1..n) DO
      DebugOps.ShortCopyREAD[from: @bbHandle.blocks[j], to: @local,
        nwords: SIZE[MachineDefs.UserBreakBlock]];
      DebugOps.ShortCopyWRITE[from: @local, to: @bbHandle.blocks[j-1],
        nwords: SIZE[MachineDefs.UserBreakBlock]];
      ENDLOOP;
    DebugOps.ShortWRITE[@bbHandle.length,n-1];
    RETURN
    END;

  AbortWithWrittenError: PUBLIC PROC [err: Error] = {WriteError[err]; AbortWithError[err]};

  END.