-- XXDebugGlobals.mesa  
--   Edited by Bruce, October 13, 1980  6:26 PM
--   Edited by Sweet, September 4, 1980  2:37 PM

DIRECTORY
  BcdDefs: FROM "bcddefs",
  BcdOps: FROM "bcdops",
  ControlDefs: FROM "controldefs" USING [GlobalFrameHandle],
  DebugUsefulDefs: FROM "debugusefuldefs" USING [
    Enumerate, Name, ShortCopyREAD, ShortREAD, window],
  Event: FROM "event",
  FrameDefs: FROM "framedefs",
  FormSW: FROM "formsw",
  Inline USING [COPY],
  Process USING [Detach, Yield],
  RESOut: FROM "resout",
  SegmentDefs: FROM "segmentdefs",
  State: FROM "state",
  STDebugDefs: FROM "stdebugdefs",
  StreamDefs: FROM "streamdefs",
  StringDefs: FROM "stringdefs" USING [
    AppendString, CompareStrings, InvalidNumber, StringToNumber],
  SymbolSegment: FROM "symbolsegment" USING [bodyType, ctxType, extType, 
    htType, ltType, mdType, seType, ssType, STHeader, stType, treeType],
  SymbolTable: FROM "symboltable" USING [Base],
  Table: FROM "table" USING [Base, chunkType, Selector],
  UserInput USING [
    CancelPeriodicNotify, CreatePeriodicNotify, PeriodicNotifyHandle, 
    PeriodicProcType],
  XXDebugDefs: FROM "xxdebugdefs",
  XXDebugGlobalDefs;

XXDebugGlobals: PROGRAM 
  IMPORTS DebugUsefulDefs, Event, FormSW, FrameDefs, Inline, Process, 
  RESOut, SegmentDefs, STDebugDefs, StreamDefs, StringDefs, UserInput
  EXPORTS STDebugDefs, XXDebugDefs =
  BEGIN OPEN DebugUsefulDefs, RESOut, XXDebugGlobalDefs, XXDebugDefs;

  al: POINTER ← NIL;
  stateCold: POINTER ← NIL;
  pcPack: POINTER ← NIL;
  symbolCopier: POINTER ← NIL;

  stSource: PUBLIC STSource ← allocator;
  stFile: PUBLIC STRING ← NIL;
  stHandle: SegmentDefs.FileSegmentHandle ← NIL;
  stAddr: POINTER TO SymbolSegment.STHeader; -- to actual symbol table bits
  STMissing: SIGNAL = CODE;

  TopCtx: PUBLIC PROCEDURE RETURNS [State.Handle] =
    BEGIN
    IF stateCold = NIL THEN
      BEGIN
      RESOut.Complain["Base of StateCold unknown"L];
      ERROR cancelAction;
      END;
    RETURN [ShortREAD[stateCold+XXDebugGlobalDefs.StateColdTopOffset]];
    END;

  GS: PUBLIC PROCEDURE RETURNS [State.GSHandle] =
    BEGIN
    IF stateCold = NIL THEN
      BEGIN
      RESOut.Complain["Base of StateCold unknown"L];
      ERROR cancelAction;
      END;
    RETURN [ShortREAD[stateCold+XXDebugGlobalDefs.StateColdGsOffset]];
    END;

  PCCache: PUBLIC PROCEDURE RETURNS [POINTER] =
    BEGIN
    IF pcPack = NIL THEN
      BEGIN
      RESOut.Complain["Base of PCPack unknown"L];
      ERROR cancelAction;
      END;
    RETURN [ShortREAD[pcPack+XXDebugGlobalDefs.PCPackCacheOffset]];
    END;

  PCHead: PUBLIC PROCEDURE RETURNS [UNSPECIFIED] =
    BEGIN
    IF pcPack = NIL THEN
      BEGIN
      RESOut.Complain["Base of PCPack unknown"L];
      ERROR cancelAction;
      END;
    RETURN [ShortREAD[pcPack+XXDebugGlobalDefs.PCPackHeadOffset]];
    END;


  ClearFileSource: PUBLIC PROCEDURE =
    BEGIN
    IF stSource # file THEN RETURN;
    IF stHandle # NIL THEN {
      SegmentDefs.DeleteFileSegment[stHandle];
      stHandle ← NIL};
    stSource ← allocator;
    END;

  SourceChanged: PUBLIC FormSW.EnumeratedNotifyProcType =
    BEGIN
    lookupString: STRING ← [40];
    RESOut.Complain[""L]; StreamDefs.ResetControlDEL[];
    IF stHandle # NIL THEN 
      BEGIN
      SegmentDefs.DeleteFileSegment[stHandle];
      stHandle ← NIL;
      END;
    SELECT stSource FROM
      file =>
	BEGIN OPEN SegmentDefs;
	IF stFile = NIL OR stFile.length > 36 THEN GO TO nope;
	StringDefs.AppendString[lookupString, stFile];
	stHandle ← STTableForString[lookupString ! 
	  STMissing => GO TO nope];
	SwapIn[stHandle];
	stAddr ← FileSegmentAddress[stHandle];
	PCr[]; PCr[]; PString["From file: "L]; PString[lookupString];
        PChar[' ];
	STDebugDefs.PutVersion[@stAddr.version];
	Unlock[stHandle]; stHandle.inuse ← TRUE;
	RETURN;
	EXITS
	  nope => 
	    BEGIN
	    RESOut.Complain["Invalid file"L];
	    stSource ← none;
	    FormSW.DisplayItem[sw, index];
	    END;
	END;
      allocator => 
	BEGIN
	PCr[]; PCr[]; PString["From allocator symbol table"L];
	END;
      alFrame => 
	BEGIN
	PCr[]; PCr[]; PString["From Allocator per frame pointer"L];
	END;
      spFrame => 
	BEGIN
	PCr[]; PCr[]; PString["From SymbolPack per frame pointer"L];
	END;
      copier =>
	BEGIN
        IF symbolCopier = NIL THEN GO TO noCopier;
	PCr[]; PCr[]; PString["From SymbolCopier"L];
	EXITS
	  noCopier => 
	    BEGIN
	    RESOut.Complain["base of SymbolCopier unknown"L];
	    stSource ← none;
	    FormSW.DisplayItem[sw, index];
	    END;
	END;
      ENDCASE;
    END;

  LockSymbols: PUBLIC PROCEDURE =
    BEGIN
    IF stSource # file THEN RETURN;
    SegmentDefs.SwapIn[stHandle];
    stAddr ← SegmentDefs.FileSegmentAddress[stHandle];
    END;

  UnlockSymbols: PUBLIC PROCEDURE =
    BEGIN
    IF stHandle = NIL THEN RETURN;
    WHILE stHandle.lock # 0 DO SegmentDefs.Unlock[stHandle]; ENDLOOP;
    stHandle.inuse ← TRUE;
    END;

  TableBase: PUBLIC PROCEDURE [table: Table.Selector ← Table.chunkType]
      RETURNS [Table.Base] =
    BEGIN
    base: DESCRIPTOR FOR ARRAY Table.Selector OF Table.Base;

    SELECT stSource FROM
      none =>
	BEGIN
	RESOut.Complain["Select source"L];
	ERROR cancelAction;
	END;
      copier =>
	BEGIN
	copierSymbols: SymbolTable.Base =
	  ShortREAD[symbolCopier + XXDebugGlobalDefs.SymbolCopierIBaseOffset];
        IF copierSymbols = NIL THEN GO TO noSymbols;
	stAddr ← ShortREAD[@copierSymbols.stHandle];
        IF stAddr = NIL THEN GO TO noSymbols;
        RETURN[FileTableBase[table]];
	EXITS
	  noSymbols => 
	    BEGIN
	    RESOut.Complain["SymbolCopier has no current table"L];
	    ERROR cancelAction;
	    END;
	END;
      file => RETURN[FileTableBase[table]];
      spFrame =>
	BEGIN
	spBase: SymbolTable.Base =
	  LOOPHOLE[StringDefs.StringToNumber[stFile, 10 !
	    StringDefs.InvalidNumber => GO TO noSymbols]];
        IF spBase = NIL THEN GO TO noSymbols;
	stAddr ← ShortREAD[@spBase.stHandle];
        IF stAddr = NIL THEN GO TO noSymbols;
        RETURN[FileTableBase[table]];
	EXITS
	  noSymbols => 
	    BEGIN
	    RESOut.Complain["SymbolPack has no current symbols"L];
	    ERROR cancelAction;
	    END;
	END;
      alFrame =>
	BEGIN
	alf: POINTER =
	  LOOPHOLE[StringDefs.StringToNumber[stFile, 10 !
	    StringDefs.InvalidNumber => GO TO noAl]];
	ShortCopyREAD[to: @base, from: alf+AllocatorbaseOffset, nwords: 
	  SIZE[DESCRIPTOR FOR ARRAY Table.Selector OF Table.Base]];
        RETURN[ShortREAD[@base[table]]];
	EXITS
	  noAl => 
	    BEGIN
	    RESOut.Complain["addr of Allocator invalid"L];
	    ERROR cancelAction;
	    END;
	END;
      ENDCASE;
    IF al = NIL THEN
      BEGIN
      RESOut.Complain["Base of Allocator unknown"L];
      ERROR cancelAction;
      END;
    ShortCopyREAD[to: @base, from: al+AllocatorbaseOffset, nwords: 
      SIZE[DESCRIPTOR FOR ARRAY Table.Selector OF Table.Base]];
    RETURN [ShortREAD[@base[table]]];
    END;

  FileTableBase: PROCEDURE [table: Table.Selector] RETURNS [Table.Base] =
    BEGIN OPEN SymbolSegment;
    delta: CARDINAL;
    SELECT table FROM
      treeType => delta ← STRead[@stAddr.treeBlock.offset];
      seType => delta ← STRead[@stAddr.seBlock.offset];
      htType => delta ← STRead[@stAddr.htBlock.offset];
      ssType => delta ← STRead[@stAddr.ssBlock.offset];
      ctxType => delta ← STRead[@stAddr.ctxBlock.offset];
      mdType => delta ← STRead[@stAddr.mdBlock.offset];
      bodyType => delta ← STRead[@stAddr.bodyBlock.offset];
      ltType => delta ← STRead[@stAddr.litBlock.offset];
      stType => delta ← STRead[@stAddr.sLitBlock.offset];
      extType => delta ← STRead[@stAddr.extBlock.offset];
      ENDCASE => ERROR;
    RETURN [LOOPHOLE[stAddr+delta]];
    END;

  TableSize: PUBLIC PROCEDURE [table: Table.Selector ← Table.chunkType]
      RETURNS [CARDINAL] =
    BEGIN
    top: DESCRIPTOR FOR ARRAY Table.Selector OF CARDINAL;

    SELECT stSource FROM
      none =>
	BEGIN
	RESOut.Complain["Select source"L];
	ERROR cancelAction;
	END;
      copier =>
	BEGIN
	copierSymbols: SymbolTable.Base =
	  ShortREAD[symbolCopier + XXDebugGlobalDefs.SymbolCopierIBaseOffset];
        IF copierSymbols = NIL THEN GO TO noSymbols;
	stAddr ← ShortREAD[@copierSymbols.stHandle];
        IF stAddr = NIL THEN GO TO noSymbols;
        RETURN[FileTableSize[table]];
	EXITS
	  noSymbols => 
	    BEGIN
	    RESOut.Complain["SymbolCopier has no current table"L];
	    ERROR cancelAction;
	    END;
	END;
      file => RETURN [FileTableSize[table]];
      ENDCASE;
    IF al = NIL THEN
      BEGIN
      RESOut.Complain["Base of Allocator unknown"L];
      ERROR cancelAction;
      END;
    DebugUsefulDefs.ShortCopyREAD[to: @top, from: al+AllocatortopOffset, nwords: 
      SIZE[DESCRIPTOR FOR ARRAY Table.Selector OF CARDINAL]];
    RETURN [ShortREAD[@top[table]]];
    END;

  FileTableSize: PROCEDURE [table: Table.Selector] RETURNS [size: CARDINAL] =
    BEGIN OPEN SymbolSegment;
    SELECT table FROM
      treeType => size ← STRead[@stAddr.treeBlock.size];
      seType => size ← STRead[@stAddr.seBlock.size];
      htType => size ← STRead[@stAddr.htBlock.size];
      ssType => size ← STRead[@stAddr.ssBlock.size];
      ctxType => size ← STRead[@stAddr.ctxBlock.size];
      mdType => size ← STRead[@stAddr.mdBlock.size];
      bodyType => size ← STRead[@stAddr.bodyBlock.size];
      ltType => size ← STRead[@stAddr.litBlock.size];
      stType => size ← STRead[@stAddr.sLitBlock.size];
      extType => size ← STRead[@stAddr.extBlock.size];
      ENDCASE => ERROR;
    RETURN;
    END;

  STRead: PUBLIC PROCEDURE [p: UNSPECIFIED] RETURNS [UNSPECIFIED] =
    BEGIN
    IF stSource = file THEN RETURN [LOOPHOLE[p, POINTER]↑]
    ELSE RETURN[ShortREAD[p]];
    END;

  STCopyRead: PUBLIC PROCEDURE [from: POINTER, nwords: CARDINAL, to: POINTER] =
    BEGIN
    IF stSource = file THEN
      Inline.COPY[from: from, nwords: nwords, to: to]
    ELSE DebugUsefulDefs.ShortCopyREAD[from: from, nwords: nwords, to: to];
    END;

  STTableForString: PUBLIC PROCEDURE [name: STRING]
      RETURNS [SegmentDefs.FileSegmentHandle] =
    BEGIN OPEN SegmentDefs;
    file: FileHandle;
    base: PageNumber;
    pages: PageCount;
    symseg: FileSegmentHandle;
    CheckForExtension[name, ".bcd"L];
    [file, base, pages] ← STFindSymbolTable[name];
    symseg ← NewFileSegment[file, base, pages, Read];
    RETURN[symseg]
    END;

  STFindSymbolTable: PUBLIC PROCEDURE [name: STRING] 
    RETURNS [file: SegmentDefs.FileHandle, base: SegmentDefs.PageNumber, pages: SegmentDefs.PageCount] =
    BEGIN OPEN SegmentDefs, BcdDefs;
    headerseg: FileSegmentHandle;
    bcd: POINTER TO BCD;
    mth: BcdOps.MTHandle;
    sgh: BcdOps.SGHandle;
    file ← NewFile[name, Read, OldFileOnly !
      FileNameError => ERROR STMissing];
    headerseg ← NewFileSegment[file,1,1,Read];
    SwapIn[headerseg];
    bcd ← FileSegmentAddress[headerseg];
    BEGIN OPEN bcd;
      ENABLE UNWIND =>
        BEGIN
        Unlock[headerseg];
        DeleteFileSegment[headerseg];
        END;
      IF versionIdent # BcdDefs.VersionID OR nModules # 1
        THEN ERROR STMissing;
      IF (pages←nPages) # 1 THEN
	BEGIN
	Unlock[headerseg];
	MoveFileSegment[headerseg,1,pages];
	SwapIn[headerseg];
	bcd ← FileSegmentAddress[headerseg];
	END;
      mth ← @LOOPHOLE[bcd+bcd.mtOffset, Base][FIRST[MTIndex]];
      sgh ← @LOOPHOLE[bcd+bcd.sgOffset, Base][mth.sseg];
      base ← sgh.base;
      pages ← sgh.pages+sgh.extraPages;
      END;
    Unlock[headerseg];
    LockFile[file];
    DeleteFileSegment[headerseg];
    UnlockFile[file];
    RETURN
    END;

  CheckForExtension: PROCEDURE [name, ext: STRING] =
    BEGIN
    i: CARDINAL;
    FOR i IN [0..name.length) DO
      IF name[i] = '. THEN RETURN;
      ENDLOOP;
    StringDefs.AppendString[name, ext];
    RETURN
    END;


  nMods: CARDINAL = 4;
  LookForFrames: PROCEDURE =
    BEGIN
    moduleName: ARRAY [0..nMods) OF STRING ← [
      "Allocator"L, "PcHot"L, "StateCold"L, "SymbolCopier"L];
    basePtr: ARRAY [0..nMods) OF POINTER ← [
      @al, @pcPack, @stateCold, @symbolCopier];
    keyString: STRING = [40];
    firstOut: BOOLEAN ← TRUE;
    i, nFound: CARDINAL;

    CheckOneFrame: PROCEDURE [han: ControlDefs.GlobalFrameHandle]
	RETURNS [BOOLEAN] =
      BEGIN
      l, u, i: CARDINAL;
      name: POINTER TO ARRAY [0..nMods) OF STRING = @moduleName;
      base: POINTER TO ARRAY [0..nMods) OF POINTER = @basePtr;
      key: STRING = keyString;

      key.length ← 0;
      DebugUsefulDefs.Name[name: key, gf: han];
      l ← 0; u ← nMods-1;
      WHILE l <= u DO
	i ← (l+u)/2;
        SELECT StringDefs.CompareStrings[key, name[i], FALSE] FROM
	  < 0 => u ← i-1;
	  > 0 => l ← i+1;
	  ENDCASE =>
	    BEGIN
	    IF base[i]↑ = NIL THEN
	      BEGIN base[i]↑ ← han; nFound ← nFound + 1 END
	    ELSE
	      BEGIN
	      IF firstOut THEN
		BEGIN
		firstOut ← FALSE;
		RESOut.Complain["Duplicate: "L];
		END
	      ELSE RESOut.Complain[", "L, FALSE];
	      RESOut.Complain[key, FALSE];
	      END;
	    EXIT
	    END;
        ENDLOOP;
      Process.Yield[];
      RETURN[nFound = nMods];
      END;

    FOR i IN [0..nMods) DO basePtr[i]↑ ← NIL; ENDLOOP;
    nFound ← 0;
    IF FrameDefs.IsBound[DebugUsefulDefs.Enumerate] THEN
      [] ← DebugUsefulDefs.Enumerate[CheckOneFrame];
    IF nFound # nMods THEN
      BEGIN
      IF ~firstOut THEN RESOut.Complain[", "L, FALSE];
      RESOut.Complain["Missing: "L, ~firstOut];
      firstOut ← TRUE;
      FOR i IN [0..nMods) DO
	IF basePtr[i]↑ = NIL THEN
	  BEGIN
	  IF firstOut THEN firstOut ← FALSE
	  ELSE RESOut.Complain[", "L, FALSE];
	  RESOut.Complain[moduleName[i], FALSE];
	  END;
        ENDLOOP;
      END;
    END;

  LookupTheFrames: UserInput.PeriodicProcType =
    BEGIN
    periodic ← UserInput.CancelPeriodicNotify[periodic];
    Process.Detach[FORK LookForFrames[]];
    END;


  FindFrames: PUBLIC PROCEDURE =
    BEGIN
    END;


  Notify: Event.Notifier =
    BEGIN
    SELECT why FROM
      newSession => IF periodic = NIL THEN
	periodic ← UserInput.CreatePeriodicNotify[LookupTheFrames, DebugUsefulDefs.window, 1];
      abortSession => ClearFileSource[];
      ENDCASE;
    END;

  notifierItem: Event.Item ← [
    eventMask: Event.Masks[newSession] + Event.Masks[abortSession],
    eventProc: Notify];
  periodic: UserInput.PeriodicNotifyHandle ←
    UserInput.CreatePeriodicNotify[LookupTheFrames, DebugUsefulDefs.window, 1];

  Event.AddNotifier[@notifierItem];

  END.