-- FramesCold.Mesa  Edited by: Sandman  July 18, 1980  10:30 AM
-- 		    Edited by: Bruce  October 21, 1980  12:28 PM

DIRECTORY
  BcdDefs USING [GFTIndex, MTIndex],
  BcdOps USING [BcdBase, MTHandle, NameString, ProcessModules],
  DContext USING [BcdBase, GetGlobal, SameConfig],
  DebugOps USING [Display, Foo, Lengthen, ShortCopyREAD, ShortREAD, UserAborted],
  DebugUsefulDefs USING [Name],
  DI USING [Foo, SEIndex, TypeForSe],
  DOutput USING [Char, EOL, Octal, Text],
  Strings USING [EqualSubStrings, SubString, SubStringDescriptor],
  DSyms USING [FindMod, GFHandle, GFrameMdi, Item, Shared, Stopping],
  Dump USING [FieldCtx, HashVal],
  Frames USING [Cache, Invalid, ValidateF, ValidateGF],
  Gf USING [
    Copied, Deleted, FrameGfi, GFI, Handle, Name, SameModule, Started, Validate],
  DHeap USING [AllocFob],
  Lf USING [GF, Handle, NoPrevious, PC, Previous],
  DLoadState USING [Acquire, AcquireBcd, Invalid, MapConfigToReal, ReleaseBcd, 
  Release],
  Lookup USING [
    Complete, Fail, HTIndex, HtiToString, InOut, StateCtx, StringToHti, XferCtx],
  MachineDefs USING [
    ConfigIndex, FHandle, Frame, GFHandle, GfiToFrame, GfiToOffset, GFTIndex,
    localbase, NullConfig, NullGF, RealToBytePC],
  Pc USING [CBti, ContextList, CtxLink, Entry, Ep, EpToCBti, Exit, FreeContextList, Son],
  SDDefs USING [SD, sGFTLength],
  State USING [Get, GetGS, GSHandle, Handle, LF],
  Storage USING [Free, Node],
  SymbolOps USING [FirstCtxSe],
  Symbols USING [
    CBTIndex, CBTNull, ContextLevel, CTXIndex, CTXNull, ctxType, HTIndex, HTNull,
    IncludedCTXIndex, lG, lL, MDIndex, RecordSENull, SEIndex, seType, bodyType],
  SymbolSegment USING [bodyType, ctxType],
  SymbolTable USING [Missing],
  Table USING [AddNotify, Base, Bounds, DropNotify, Notifier],
  UserInput USING [userAbort];
  
FramesCold: PROGRAM
  IMPORTS
    BcdOps, DContext, DebugOps, DebugUsefulDefs, DI, DLoadState, DOutput,
    Strings, DSyms, Dump, Frames,
    Gf, DHeap, Lf, Lookup, MachineDefs, Pc, State, Storage, SymbolOps, 
    SymbolTable, Table, UserInput
  EXPORTS DebugUsefulDefs, Frames, Gf, Lf =
  BEGIN OPEN Gf, MachineDefs;
  
  CopiedFrame: PUBLIC SIGNAL [name: STRING] = CODE;
  
  data: State.GSHandle = State.GetGS[];
  
  Enumerate: PUBLIC PROC [proc: PROC [Handle] RETURNS [BOOLEAN]] RETURNS [gf: Handle] =
    BEGIN OPEN MachineDefs;
    i: GFTIndex;
    gftLength: CARDINAL = DebugOps.ShortREAD[@SDDefs.SD[SDDefs.sGFTLength]];
    FOR i DECREASING IN [1..gftLength) DO
      gf ← GfiToFrame[i];
      IF gf # NullGF AND GfiToOffset[i] = 0
	AND Validate[gf] AND proc[gf] THEN RETURN;
      ENDLOOP;
    RETURN[NullGF];
    END;
    
  CatchFrame: PUBLIC PROCEDURE [f: FHandle] RETURNS [BOOLEAN] =
    BEGIN OPEN MachineDefs;
    next, L0: FHandle;
    SignalHandle: TYPE = POINTER TO signal Frame;
    CatchHandle: TYPE = POINTER TO catch Frame;
    copy: signal Frame;
    next ← Lf.Previous[f ! ANY => GOTO notcatch];
    IF Lf.GF[next] # data.sigGF THEN GOTO notcatch;
    L0 ← DebugOps.ShortREAD[@LOOPHOLE[f, CatchHandle].staticlink];
    IF ~Frames.ValidateF[(L0 ← LOOPHOLE[L0-localbase])] THEN GOTO notcatch;
    IF ~Frames.ValidateF[next] OR Lf.GF[f] # Lf.GF[L0] THEN GOTO notcatch;
    DebugOps.ShortCopyREAD[from: next, to: @copy, nwords: SIZE[signal Frame]];
    IF ~copy.mark THEN GOTO notcatch;
    RETURN[TRUE];
    EXITS
      notcatch => RETURN[FALSE];
    END;
    
  InMainBody: PUBLIC PROCEDURE [f: FHandle] RETURNS [in: BOOLEAN] =
    BEGIN
    gf: GFHandle ← Lf.GF[f];
    cbti: Symbols.CBTIndex ← Pc.CBti[Lf.PC[f],gf];
    bb: Table.Base ← Table.Bounds[SymbolSegment.bodyType].base;
    ctxb: Table.Base ← Table.Bounds[SymbolSegment.ctxType].base;
    IF cbti = Symbols.CBTNull THEN RETURN [FALSE];
    in ← ctxb[bb[cbti].localCtx].level = Symbols.lG;
    END;
    
  DisplayF: PUBLIC PROC [f: FHandle] =
    BEGIN OPEN DOutput;
    gf: GFHandle ← Lf.GF[f];
    cbti: Symbols.CBTIndex ← Pc.CBti[Lf.PC[f],gf];
    lf: FHandle ← Frames.Cache[f,local];
    IF cbti # Symbols.CBTNull THEN
      BEGIN
      bb: Table.Base ← Table.Bounds[SymbolSegment.bodyType].base;
      Dump.HashVal[bb[cbti].id];
      Text[", "L];
      END;
    Text["L: "L]; Octal[f];
    Text[", PC: "L]; Octal[MachineDefs.RealToBytePC[LOOPHOLE[lf.pc]]];
    DisplayInMsg[gf,NIL];
    END;
    
  FCbti: PROC [f: FHandle] RETURNS [Symbols.CBTIndex] =
    BEGIN RETURN[Pc.CBti[Lf.PC[f],Lf.GF[f]]] END;
    
  DisplayParametersF: PUBLIC PROC [f: FHandle] = 
    BEGIN FrameFoo[f,in,Pc.Entry[f],FCbti[f]] END;
    
  DisplayResultsF: PUBLIC PROC [f: FHandle] =
    BEGIN FrameFoo[f,out,Pc.Exit[f],FCbti[f]] END;
    
  FrameFoo: PROC [
    p: POINTER, io: Lookup.InOut, inState: BOOLEAN, cbti: Symbols.CBTIndex] =
    BEGIN OPEN Symbols;
    f: DebugOps.Foo;
    seb: Table.Base;
    sei: SEIndex;
    IF cbti = CBTNull THEN RETURN;
    sei ← Table.Bounds[SymbolSegment.bodyType].base[cbti].ioType;
    f ← IF inState THEN Lookup.StateCtx[sei, data.StatePtr, io]
      ELSE Lookup.XferCtx[sei, DebugOps.Lengthen[p],io];
    IF f = NIL OR f.tsei = Symbols.RecordSENull THEN RETURN;
    f.indent ← 2;
    f.xfer ← TRUE;
    DOutput.EOL[];
    seb ← Table.Bounds[seType].base;
    WITH seb[DI.TypeForSe[f.tsei]] SELECT FROM
      record => IF seb[SymbolOps.FirstCtxSe[fieldCtx]].hash = HTNull THEN
	DOutput.Text["  (anon) = "L];
      ENDCASE;
    DebugOps.Display[f]
    END;
    
  SetupFoo: PROC [p: POINTER] RETURNS [f: DebugOps.Foo] =
    BEGIN
    f ← DHeap.AllocFob[];
    f.addr.base ← DebugOps.Lengthen[p];
    f.indent ← 2; f.xfer ← f.there ← TRUE;
    END;
    
  DisplayLocalsF: PUBLIC PROC[lf: FHandle] =
    BEGIN OPEN Symbols;
    gf: MachineDefs.GFHandle ← Lf.GF[lf];
    cbti: CBTIndex ← Pc.CBti[Lf.PC[lf],gf];
    f: DebugOps.Foo;
    mainBody: BOOLEAN = InMainBody[lf];
    IF cbti = CBTNull THEN RETURN;
    IF ~CatchFrame[lf] THEN {DisplayParametersF[lf]; DisplayResultsF[lf]};
    f ← SetupFoo[lf];
    IF Pc.Son[cbti] THEN
      BEGIN
      list: Pc.CtxLink ← Pc.ContextList[Lf.PC[lf],gf,print];
      IF list = NIL THEN RETURN;
      DumpContextList[f, DSyms.GFrameMdi[gf], list !
	UNWIND => Pc.FreeContextList[list]];
      Pc.FreeContextList[list];
      END
    ELSE {
      ictx: Symbols.IncludedCTXIndex;
      level: ContextLevel ← Symbols.lG;
      Table.AddNotify[Notify];
      ictx ← LOOPHOLE[bb[cbti].localCtx];
      IF ictx # Symbols.CTXNull THEN level ← ctxb[ictx].level;
      Table.DropNotify[Notify];
      IF level > Symbols.lG THEN DumpOne[f,ictx]};
    END;
    
  DumpOne: PROC [f: DebugOps.Foo, ictx: Symbols.CTXIndex] =
    BEGIN
    IF ictx = Symbols.CTXNull THEN RETURN;
    Lookup.Complete[ictx];
    DOutput.EOL[];
    Dump.FieldCtx[f,ictx,0];
    END;
    
  ctxb: Table.Base;
  bb: Table.Base;
  
  Notify: Table.Notifier = {ctxb ← base[Symbols.ctxType]; bb ← base[Symbols.bodyType]};
  
  DumpContextList: PROC [
    f: DebugOps.Foo, mdi: Symbols.MDIndex, list: Pc.CtxLink] =
    BEGIN OPEN Symbols;
    level: ContextLevel;
    Table.AddNotify[Notify];
    level ← MAX[lL, GetLevel[list]];
    FOR i: Pc.CtxLink ← list, i.link UNTIL i = NIL DO
      level ← MAX[level, GetLevel[i]];
      ENDLOOP;
    FOR i: Pc.CtxLink ← list, i.link UNTIL i = NIL DO
      f.indent ← i.indent;
      IF GetLevel[i] < level OR i.null THEN LOOP;
      DumpOne[f, i.ictx ! UNWIND => Table.DropNotify[Notify]];
      ENDLOOP;
    Table.DropNotify[Notify];
    END;
    
  GetLevel: PROC [i: Pc.CtxLink] RETURNS [Symbols.ContextLevel] = {
    RETURN[IF i.null THEN i.level ELSE ctxb[i.ictx].level]};

  -- global frames
  
  GFCbti: PROC [gf: GFHandle] RETURNS [Symbols.CBTIndex] =
    BEGIN RETURN[Pc.EpToCBti[0,gf]] END;
    
  DisplayParametersGF: PUBLIC PROC [gf: GFHandle] =
    BEGIN
    lf: FHandle ← GetMainBody[gf];
    IF lf # NIL THEN DisplayParametersF[lf]
    ELSE FrameFoo[gf,in,FALSE,GFCbti[gf]];
    END;
    
  DisplayResultsGF: PUBLIC PROC [gf: GFHandle] =
    BEGIN
    lf: FHandle ← GetMainBody[gf];
    IF lf # NIL THEN DisplayResultsF[lf]
    ELSE FrameFoo[gf,out,FALSE,GFCbti[gf]];
    END;
    
  DisplayLocalsGF: PUBLIC PROC [gf: GFHandle] =
    BEGIN
    lf: FHandle ← GetMainBody[gf];
    cbti: Symbols.CBTIndex;
    ictx: Symbols.IncludedCTXIndex;
    cbti ← GFCbti[gf];
    FrameFoo[gf,in,FALSE,cbti];
    FrameFoo[gf,out,FALSE,cbti];
    Table.AddNotify[Notify];
    ictx ← LOOPHOLE[bb[cbti].localCtx];
    Table.DropNotify[Notify];
    DumpOne[SetupFoo[gf],ictx];
    IF lf # NIL THEN DisplayLocalsF[lf];
    END;
    
  DisplayInMsg: PUBLIC PROC [gf: GFHandle, delim: STRING] = {
    OPEN DOutput;
    Text[" (in "L];
    DisplayGF[gf,delim ! Frames.Invalid =>
      {Char[' ]; Octal[f]; Text[" is an invalid global frame!"L]; CONTINUE}];
    Char[')]};
    
  DisplayGF: PUBLIC PROC [gf: GFHandle, delim: STRING] =
    BEGIN OPEN DOutput;
    mod: STRING ← [40];
    Gf.Name[mod, gf ! DLoadState.Invalid, Frames.Invalid => CONTINUE];
    IF mod.length # 0 THEN
      BEGIN
      IF delim # NIL THEN {Text[delim]; Char[' ]};
      Text[mod]; Text[", "L]
      END;
    DOutput.Text["G: "L]; PrintFrame[gf];
    END;
    
  PrintFrame: PROC [gf: GFHandle] = {
    DOutput.Octal[gf];
    IF Copied[gf] THEN DOutput.Char['*];
    IF ~Started[gf] THEN DOutput.Char['~]};
    
  DisplayGFTEntry: PUBLIC PROC [gfi: MachineDefs.GFTIndex] =
    BEGIN
    IF Deleted[gfi] THEN DOutput.Text[" deleted"L]
    ELSE PrintFrame[FrameGfi[gfi]];
    END;
    
  GetMainBody: PUBLIC PROC [gf: GFHandle] RETURNS [f: MachineDefs.FHandle] =
    BEGIN
    looping: CARDINAL ← 0;
    IF ~Frames.ValidateGF[gf] OR ~Gf.Started[gf] THEN RETURN[NIL];
    IF DSyms.Stopping[gf ! SymbolTable.Missing => GOTO null] THEN
      RETURN[DebugOps.ShortREAD[@gf.global[0]]];
    FOR f ← State.LF[], Lf.Previous[f ! Lf.NoPrevious => GOTO null] UNTIL f = NIL DO
      IF (looping ← looping + 1) > 1000 THEN RETURN[NIL];
      IF Lf.GF[f] # gf THEN LOOP;
      IF Pc.Ep[Lf.PC[f],gf].ep = 0 THEN RETURN;
      ENDLOOP;
    EXITS null => RETURN[NIL];
    END;
    
  DisplayGFT: PUBLIC PROCEDURE =
    BEGIN OPEN DOutput;
    GFWrite: PROCEDURE[frame: Handle] RETURNS [BOOLEAN] = 
      BEGIN
      module: STRING ← [40];
      IF frame = MachineDefs.NullGF THEN RETURN[FALSE];
      DebugUsefulDefs.Name[module, frame !
	Frames.Invalid =>  module ← "!Invalid global frame"L];
      Text[module];
      Text[", G:"L]; PrintFrame[frame];
      Text[", gfi:"L]; Octal[GFI[frame]];
      EOL[];
      RETURN[FALSE]
      END;
    EOL[];
    [] ← DLoadState.Acquire[];
    [] ← Enumerate[GFWrite ! UNWIND => DLoadState.Release[]];
    DLoadState.Release[];
    RETURN
    END;
    
  Original: PUBLIC PROCEDURE [new: Handle] RETURNS [old: Handle] =
    BEGIN
    Find: PROC [f: Handle] RETURNS [BOOLEAN] =
      {RETURN[f # new AND ~Copied[f] AND Gf.SameModule[new, f]]};
    IF ~Copied[new] THEN RETURN[new];
    RETURN [Enumerate[Find]]
    END;
    
  FrameItem: TYPE = RECORD [
    next: POINTER TO FrameItem,
    frame: Handle];
    
  FreeFrameItems: PROC [head: POINTER TO FrameItem] RETURNS [nil: POINTER] =
    BEGIN
    nfl, fl: POINTER TO FrameItem;
    FOR fl ← head, nfl UNTIL fl = NIL DO
      nfl ← fl.next;
      Storage.Free[fl];
      ENDLOOP;
    RETURN[NIL]
    END;
    
  WriteFrameItems: PROCEDURE [head: POINTER TO FrameItem, mod: STRING] =
    INLINE BEGIN OPEN DOutput;
    fl: POINTER TO FrameItem;
    EOL[];
    Char['!]; Text[mod]; Text[" has frames at"L];
    FOR fl ← head, fl.next UNTIL fl = NIL DO
      Char[' ]; Octal[fl.frame];
      ENDLOOP;
    EOL[];
    Text["Use SEt Octal context or Display Frame command."L];
    RETURN
    END;
    
  FrameHti: PUBLIC PROCEDURE [hti: Symbols.HTIndex ← Symbols.HTNull]
    RETURNS [f: Handle] =
    BEGIN
    name: STRING ← [40];
    Lookup.HtiToString[hti,name];
    RETURN[Frame[name]];
    END;
    
  Frame: PUBLIC PROCEDURE [name: STRING ← NIL] RETURNS [f: Handle] =
    BEGIN OPEN BcdDefs, BcdOps, MachineDefs;
    data: State.Handle ← State.Get[];
    moddesc: Strings.SubStringDescriptor;
    modss: Strings.SubString ← @moddesc;
    ssb: NameString;
    bcd: BcdOps.BcdBase;
    dsh: DSyms.Item ← NIL;
    FList: POINTER TO FrameItem ← NIL;
    FindModuleString: PROC [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] = 
      BEGIN OPEN Strings;
      gfi: GFTIndex;
      ssd: SubStringDescriptor ←
	[base: @ssb.string, offset: mth.name, length: ssb.size[mth.name]];
      ss: SubString ← @ssd;
      fl: POINTER TO FrameItem ← FList;
      IF UserInput.userAbort THEN SIGNAL DebugOps.UserAborted;
      IF ~DContext.SameConfig[bcd, mth.config, data.cti] THEN RETURN[FALSE];
      IF EqualSubStrings[ss, modss] THEN
	BEGIN
	IF Deleted[gfi ← DLoadState.MapConfigToReal[mth.gfi, data.config]]
	  THEN RETURN [FALSE];
	FList ← Storage.Node[SIZE[FrameItem]];
	FList↑ ← FrameItem[next: fl, frame: GfiToFrame[gfi]];
	END;
      RETURN[FALSE]
      END;
    IF name = NIL THEN RETURN[DContext.GetGlobal[]];
    IF (dsh ← DSyms.FindMod[Lookup.StringToHti[name] ! DSyms.Shared => CONTINUE]) # NIL
      AND (f ← dsh.gf) # MachineDefs.NullGF THEN RETURN;
    moddesc ← Strings.SubStringDescriptor[
      base: name, offset: 0, length: name.length];
    IF data.config = MachineDefs.NullConfig THEN RETURN[NIL];
    [] ← DLoadState.Acquire[];
    bcd ← DLoadState.AcquireBcd[data.config];
    ssb ← LOOPHOLE[bcd+bcd.ssOffset];
    [] ← BcdOps.ProcessModules[bcd, FindModuleString !UNWIND => Cleanup[bcd]];
    Cleanup[bcd];
    IF FList = NIL THEN SIGNAL Lookup.Fail[name];
    IF FList.next = NIL THEN f ← FList.frame
    ELSE
      BEGIN
      SIGNAL CopiedFrame[name ! UNWIND => FList ← FreeFrameItems[FList]];
      f ← MachineDefs.NullGF;
      WriteFrameItems[FList, name];
      END;
    [] ← FreeFrameItems[FList];
    IF dsh # NIL THEN dsh.gf ← f;
    RETURN[f]
    END;
    
  Current: PUBLIC PROC RETURNS [Handle] = {RETURN[DContext.GetGlobal[]]};
  
  Cleanup: PROC [bcd: BcdOps.BcdBase] =
    {DLoadState.ReleaseBcd[bcd]; DLoadState.Release[]};
    
  END.