-- File: WiskOperations.mesa 
-- last edit by Smokey on Sep 16, 1980 3:45 PM
-- last edit by Sandman on July 28, 1980  9:08 AM
-- last edit by Bruce on October 25, 1980  7:47 PM

DIRECTORY
  BP USING [BytePC, Fail, GFHandle, Insert, Remove],
  Context USING [Create, Data, Destroy, Find, Type, UniqueType],
  Copier USING [Outer],
  DContext USING [GetGlobal, GetModule],
  DebugFormat USING [BT, EXOI],
  DebugOps USING [window],
  DSyms USING [GFHandle, GFrameMdi, NoFGT],
  FileSW USING [GetFile, IsIt],
  FileWindow USING [
    Create, CreateMCR, DestroyMCR, Event, Enumerate, EnumerateProcType, LoadWindow,
    NotifyProcType, SetExtension, SetMinimumWindows, SetSize, SetSourceMenu,
    SetNotifyProc, WindowForFile],
  Frames USING [Invalid],
  Gf USING [Copied, CopiedFrame, Frame, Handle, Validate],
  HeapString USING [AppendChar],
  Init USING [],
  Inline USING [LongNumber],
  Lookup USING [Fail],
  MachineDefs USING [BytePC, DestroyStream, GFHandle, NullGF, SHandle],
  Menu USING [Create, Handle, ItemObject, MCRType],
  Pc USING [BytePC, CacheCBti, EntryPC, EVRange, ExitPC, GetPc],
  Profile USING [bitmap],
  Segments USING [FHandle, FileNameProblem, NewFile, Read],
  Selection USING [Clear, Convert],
  Source USING [
    BreakRec, FileMissing, GetGF, GFHandle, Handle, LittleParser,
    LoadWindow, Open, PcToSource, SC, SetGF, Validate],
  Storage USING [Free, FreeString, Node],
  Streams USING [CreateStream, Handle],
  String USING [EqualString],
  Strings USING [AppendString],
  Symbols USING [BTIndex, CBTIndex, MDIndex, RootBti],
  SymbolTable USING [Base, Missing],
  Table USING [Base],
  TextSW USING [GetPosition, GetSelection, SetSelection],
--, RemoveSecondarySelection, SecondarySelectionFromPosition, SetSecondarySelection],
  ToolWindow USING [GetName, Handle, SetName, WindowForSubwindow],
  UserTerminal USING [BlinkDisplay],
  Window USING [Dims, Handle];
  
WiskOperations: PROGRAM
  IMPORTS
    BP, Context, Copier, DContext, DebugOps, DSyms, FileSW, FileWindow, Frames, Gf,
    HeapString, Lookup, MachineDefs, Menu, Pc, Profile, Segments, Selection, Source,
    Storage, Streams, String, Strings, SymbolTable, TextSW, ToolWindow, UserTerminal
  EXPORTS Init, Source =
  BEGIN OPEN MachineDefs, Symbols;
  
  RelPC: TYPE = [0..LAST[CARDINAL]/2];
  
-- Define Menu and related data

  sourceItemsCount: CARDINAL = 6;
  sourceItems: ARRAY [0..sourceItemsCount) OF Menu.ItemObject ← [
    ["Create", FileWindow.CreateMCR],
    ["Destroy", FileWindow.DestroyMCR],
    ["Attach", AttachMCR],
    ["Set Break", BreakMCR],
    ["Set Trace", TraceMCR],
    ["Clr Break", ClearMCR]];
  sourceMenu: Menu.Handle =
    Menu.Create[DESCRIPTOR[sourceItems], "SourceOps", TRUE];
    
-- Debugger Menu Command Routines

  AttachMCR: Menu.MCRType = {
    item: ItemHandle ← FindItem[window];
    IF item = NIL THEN UserTerminal.BlinkDisplay[]
    ELSE item.trustTime ← TRUE};
    
  BreakMCR: Menu.MCRType = BEGIN SetUp[window,set,break] END;
  ClearMCR: Menu.MCRType = BEGIN SetUp[window,clear,break] END;
  TraceMCR: Menu.MCRType = BEGIN SetUp[window,set,trace] END;
  
-- Public stuff

  Destroy: PUBLIC PROC [d: MachineDefs.SHandle] = {MachineDefs.DestroyStream[d]};
  
  LoadWindow: PUBLIC PROCEDURE [
    name: STRING, gf: MachineDefs.GFHandle ← MachineDefs.NullGF,
    file: MachineDefs.SHandle ← NIL, position: LONG CARDINAL] =
    BEGIN
    w: Window.Handle = FileWindow.WindowForFile[name];
    FileWindow.LoadWindow[fileName: name, position: position, s: file, sw: w];
    SetGF[w, gf];
    END;
    
  Open: PUBLIC PROC [s: STRING] RETURNS [stream: Streams.Handle] =
    BEGIN
    file: Segments.FHandle ← NIL;
    file ← Segments.NewFile[s, Segments.Read
      ! Segments.FileNameProblem[] => CONTINUE ];
    IF file = NIL THEN SIGNAL Source.FileMissing[s];
    stream ← Streams.CreateStream[file];
    END;
    
  SetSize: PUBLIC PROCEDURE =
    BEGIN OPEN DebugOps.window.box;
    rootDims: Window.Dims = Profile.bitmap.dims;
    FileWindow.SetSize[NIL, [
      place: [x: 0, y: place.y+dims.h],
      dims: [w: rootDims.w, h: rootDims.h - (place.y+dims.h)]] ];
    END;
    
  SetupSourceWindow: PUBLIC PROCEDURE =
    BEGIN
    FileWindow.SetNotifyProc[SomethingChanged];
    FileWindow.SetExtension[".mesa"L];
    FileWindow.SetSourceMenu[sourceMenu];
    FileWindow.SetMinimumWindows[1];
    [] ← FileWindow.Create[[[x:32, y: 0], [w: 480, h: 352]]];
    END;
    
  Shorten: PROC [lc: LONG CARDINAL] RETURNS [CARDINAL] =
    BEGIN OPEN ln: LOOPHOLE[lc,Inline.LongNumber];
    RETURN [ln.lowbits]
    END;
    
-- Utilities

  AddDollar: PROC [w: Window.Handle] = {
    s: STRING ← ToolWindow.GetName[w];
    IF s[s.length-1] = '. THEN s[s.length-1] ← '$ ELSE HeapString.AppendChar[@s,'$];
    ToolWindow.SetName[w,s]};
    
  Break: PROC [sw: Window.Handle, br: Source.BreakRec] RETURNS [ok: BOOLEAN] =
    BEGIN
    gf: GFHandle;
    pc: BytePC;
    cbti: CBTIndex;
    ep: Pc.EVRange;
    index: CARDINAL;
    getfgt: BOOLEAN ← TRUE;
    mdi: Symbols.MDIndex;
    
    SetUp: PROC [base: SymbolTable.Base] =
      BEGIN
      startPc: BytePC;
      relPc: RelPC;
      [relPc,cbti,index] ←
	ExploreFGT[base, Shorten[TextSW.GetSelection[sw].left]];
      IF base.bb[cbti].inline THEN ERROR BP.Fail[inInline];
      ep ← base.bb[cbti].entryIndex;
      startPc ← Pc.GetPc[gf,ep];
      pc ← [startPc+relPc];
      index ← Source.PcToSource[base, gf, pc, cbti, startPc];
      END;
      
    IF sw = NIL THEN RETURN[FALSE];
    gf ← GetContext[sw !
      Gf.CopiedFrame, Lookup.Fail, Source.FileMissing => GOTO nogood;
      DSyms.NoFGT =>
	BEGIN
	IF ~getfgt OR br.bt.ex # entry THEN GOTO nogood;
	getfgt ← FALSE;
	CONTINUE;
	END;
      SymbolTable.Missing => GOTO nogood];
    IF gf = NIL OR ~Gf.Validate[gf] OR Gf.Copied[gf] THEN RETURN[FALSE];
    mdi ← DSyms.GFrameMdi[gf,getfgt !
      DSyms.NoFGT =>
	BEGIN
	IF ~getfgt OR br.bt.ex # entry THEN GOTO nogood;
	getfgt ← FALSE;
	RETRY;
	END];
    Copier.Outer[mdi, SetUp ! BP.Fail => GOTO nogood];
    cbti ← Pc.CacheCBti[mdi,gf,cbti];
    SELECT br.bt.ex FROM
      entry => pc ← Pc.EntryPC[ep,gf];
      exit => pc ← Pc.ExitPC[cbti];
      in => NULL;
      ENDCASE;
    IF br.sc=set THEN [] ← BP.Insert[gf,pc,br.bt,TRUE ! BP.Fail => GOTO nogood]
    ELSE BP.Remove[gf,pc];
    FixSelection[sw: sw, left: index,
      right: IF br.bt.ex = in THEN index + 1 
	ELSE Shorten[TextSW.GetSelection[sw].right],
      set: br.sc=set];
    RETURN[TRUE];
    EXITS
      nogood => RETURN[FALSE];
    END;
    
  ClosestSource: PROC [
    bti: BTIndex, index: CARDINAL, base: SymbolTable.Base]
      RETURNS [closest: CARDINAL, firstPc: RelPC] =
    BEGIN OPEN body: base.bb[bti];
    i, start, length: CARDINAL;
    currentPc: RelPC;
    closest ← body.sourceIndex;
    currentPc ← firstPc ← 0;
    IF BASE[base.fgTable] = NIL THEN RETURN;
    WITH body.info SELECT FROM
      External => {start ← startIndex; length ← indexLength};
      ENDCASE => RETURN;
    FOR i IN [start..start+length) DO
      WITH base.fgTable[i] SELECT FROM
	normal =>
	  BEGIN
	  IF deltaSource + closest > index THEN RETURN;
	  firstPc ← currentPc ← currentPc + deltaObject;
	  closest ← closest + deltaSource;
	  END;
	step =>
	  IF which = source THEN
	    IF delta + closest > index THEN RETURN
	    ELSE {firstPc ← currentPc; closest ← closest + delta}
	  ELSE currentPc ← currentPc + delta;
	ENDCASE;
      ENDLOOP;
    END;
    
  ExploreFGT: PROC [base: SymbolTable.Base, index: CARDINAL]
    RETURNS [bestPc: RelPC, bestCbti: CBTIndex, bestSource: CARDINAL] =
    BEGIN
    CheckBti: PROC [bti: BTIndex] RETURNS [stop: BOOLEAN] =
      BEGIN OPEN body: base.bb[bti];
      source: CARDINAL;
      pc: RelPC;
      stop ← FALSE;
      IF body.kind # Callable OR body.sourceIndex > index THEN RETURN;
      [closest: source, firstPc: pc] ← ClosestSource[bti,index, base];
      IF source <= bestSource THEN RETURN;
      bestSource ← source; bestCbti ← LOOPHOLE[bti]; bestPc ← pc;
      END;
    bestPc ← 0; bestCbti ← Symbols.RootBti; bestSource ← 0;
    [] ← base.EnumerateBodies[Symbols.RootBti,CheckBti];
    END;
    
  FixSelection: PROCEDURE [sw: Window.Handle, left, right: CARDINAL, set: BOOLEAN] = { 
    OPEN TextSW;
    Selection.Clear[];
    --IF set THEN [] ← SetSecondarySelection[sw,left,right,grayBox]
    --ELSE RemoveSecondarySelection[sw,SecondarySelectionFromPosition[sw,left]]};
    TextSW.SetSelection[sw,left,right]};
    
  GetContext: PROCEDURE [w: Window.Handle] RETURNS [frame: GFHandle] =
    BEGIN
    mod: STRING ← [40];
    h: Source.Handle;
    retry: BOOLEAN ← TRUE;
    frame ← Source.GetGF[w];
    IF frame # NIL THEN RETURN;
    h ← LOOPHOLE[FileSW.GetFile[w].s];
    Source.LittleParser[h, mod];
    IF mod.length = 0 THEN RETURN[NIL];
    frame ← IF String.EqualString[mod, DContext.GetModule[]] THEN
	DContext.GetGlobal[]
      ELSE Gf.Frame[mod ! Frames.Invalid => CONTINUE];
    Source.Validate[h,frame];
    Source.SetGF[w,frame];
    RETURN
    END;
    
  MakeEXOI: PROCEDURE [s: STRING] RETURNS [DebugFormat.EXOI] =
    BEGIN OPEN String;
    RETURN[SELECT TRUE FROM
      EqualString[s,"PROC"L] => entry,
      EqualString[s,"PROCEDURE"L] => entry,
      EqualString[s,"RETURN"L] => exit,
      ENDCASE => in];
    END;
    
  Reload: PROC [gf: GFHandle, w: Window.Handle] = {
    fileId: STRING ← [40];
    file: Source.Handle;
    Inner: PROC [base: SymbolTable.Base] = {
      Strings.AppendString[fileId,base.sourceFile]};
    AddDollar[ToolWindow.WindowForSubwindow[w]];
    Copier.Outer[DSyms.GFrameMdi[gf],Inner];
    file ← Source.Open[fileId];
    IF file = NIL THEN RETURN;
    Source.LoadWindow[fileId, gf, file, TextSW.GetPosition[w,0]]};
    
  SetUp: PROCEDURE [w: Window.Handle, sc: Source.SC, bt: DebugFormat.BT] =
    BEGIN
    br: Source.BreakRec;
    text: STRING;
    IF ~ValidateSource[w] THEN GOTO fail;
    text ← Selection.Convert[string];
    IF text = NIL THEN GOTO fail;
    br ← [sc: sc, bt: [ex: MakeEXOI[text], bt: bt]];
    Storage.FreeString[text];
    IF ~Break[w, br] THEN GOTO fail;
    EXITS
      fail => UserTerminal.BlinkDisplay[];
    END;
    
  ValidateSource: PROC [w: Window.Handle] RETURNS [BOOLEAN] =
    BEGIN OPEN Selection;
    IF ~FileSW.IsIt[w] THEN RETURN[FALSE];
    RETURN[Convert[subwindow] = w]
    END;
    
-- GlobalFrame/SourceWindow correspondence stuff

  GetGF: PUBLIC PROCEDURE [w: Window.Handle] RETURNS [MachineDefs.GFHandle] =
    BEGIN
    item: ItemHandle ← FindItem[w];
    RETURN[IF item = NIL THEN NIL ELSE item.gf];
    END;
    
  GetWindow: PUBLIC PROCEDURE [gf: MachineDefs.GFHandle]
    RETURNS [w: Window.Handle] = 
    BEGIN
    proc: FileWindow.EnumerateProcType = 
      BEGIN
      item: ItemHandle ← FindItem[sw];
      IF item # NIL AND item.gf = gf THEN w ← sw;
      RETURN[IF w = NIL THEN continue ELSE stop];
      END;
    w ← NIL;
    FileWindow.Enumerate[proc];
    RETURN[w]
    END;
    
  IgnoreTimeStamp: PUBLIC PROCEDURE [w: Window.Handle] RETURNS [BOOLEAN] =
    BEGIN
    item: ItemHandle ← FindItem[w];
    RETURN[IF item = NIL THEN FALSE ELSE item.trustTime];
    END;
    
  ResetFrames: PUBLIC PROCEDURE =
    BEGIN
    proc: FileWindow.EnumerateProcType = 
      BEGIN ResetGF[sw]; RETURN[continue] END;
    FileWindow.Enumerate[proc];
    END;
    
  SetGF: PUBLIC PROCEDURE [w: Window.Handle, gf: MachineDefs.GFHandle] =
    BEGIN
    item: ItemHandle;
    AddItem[w, gf];
    item ← FindItem[w];
    IF item # NIL THEN item.trustTime ← TRUE;  -- not clear how it could = NIL but...
    END;
    
  myContext: Context.Type = Context.UniqueType[];
  ItemHandle: TYPE = POINTER TO Item;
  Item: TYPE = RECORD [
    trustTime: BOOLEAN,
    gf: MachineDefs.GFHandle];
    
  AddItem: PROCEDURE [sw: Window.Handle, gf: MachineDefs.GFHandle] = 
    BEGIN
    item: ItemHandle ← FindItem[sw];
    IF item # NIL THEN {item.gf ← gf; item.trustTime ← FALSE; RETURN};
    item ← Storage.Node[SIZE[Item]];
    item↑ ← [gf: gf, trustTime: FALSE];
    Context.Create[myContext, item, DestroyItem, sw];
    RETURN
    END;
    
  DeleteItem: PROCEDURE [sw: Window.Handle] = INLINE
    BEGIN Context.Destroy[myContext, sw]; END;
    
  DestroyItem: PROCEDURE [item: Context.Data, sw: Window.Handle] =
    BEGIN Storage.Free[item]; END;
    
  FindItem: PROCEDURE [sw: Window.Handle] RETURNS [item: ItemHandle] = INLINE {
    RETURN[IF sw # NIL THEN Context.Find[myContext, sw] ELSE NIL]};
    
  ResetGF: PROC [w: Window.Handle] = 
    BEGIN
    item: ItemHandle = FindItem[w];
    IF item # NIL THEN
      BEGIN
      item.gf ←  MachineDefs.NullGF;  -- forget old one (else funny breakpoints)
      item.trustTime ← FALSE;
      END;
    END;
    
  SomethingChanged: FileWindow.NotifyProcType =
    BEGIN
    SELECT event FROM
      create => AddItem[sw, MachineDefs.NullGF];
      destroy => DeleteItem[sw];
      load => ResetGF[sw];
      edit => ResetGF[sw];
      reset => ResetGF[sw];
      store => ResetGF[sw];
      ENDCASE => ERROR;
    END;
    
END.