-- SymsXD.Mesa  Edited by:
  -- Bruce September 2, 1980  2:20 PM
  -- Sandman July 17, 1980  11:10 AM
  -- Johnsson July 21, 1980  5:19 PM

DIRECTORY
  BcdDefs USING [Base, BCD, FTNull, FTSelf, GFTIndex, MTIndex, NullVersion, SGIndex, SGNull, VersionID, VersionStamp],
  BcdOps USING [BcdBase, FTHandle, MTHandle, NameString, ProcessModules, SGHandle],
  DebugOps USING [CacheNewFile, FileName, InvalidateFileCache, UserAborted],
  DLoadState USING [Acquire, AcquireBcd, Invalid, MapRealToConfig, Release, ReleaseBcd],
  DOutput USING [Blanks, Char, Text],
  DSymOps USING [CacheItem, DeleteItem, StripExtension, SymHandle, SymRec],
  DSyms USING [FindFrame, Item],
  Frames USING [Invalid],
  Gf USING [File, GFI, Original],
  ImageDefs USING [ImageVersion],
  Lookup USING [Fail],
  MachineDefs USING [ConfigIndex, FileHandle, FSHandle, GFHandle, GFTIndex, NullConfig, PageSize],
  SegmentDefs USING [DefaultAccess, DeleteFileSegment, FileError, FileHandle, FileNameError, FileSegmentAddress, FileSegmentHandle, LongVMtoFileSegment, MoveFileSegment, NewFile, NewFileSegment, OldFileOnly, PageNumber, Read, SwapIn, SwapOut, Unlock],
  Storage USING [Pages],
  String USING [AppendString],
  Strings USING [AppendSubString, SubStringDescriptor],
  SymbolOps USING [EnterString],
  Symbols USING [HTIndex, HTNull],
  SymbolTable USING [Missing],
  Table USING [Base, Region],
  UserInput USING [userAbort];

SymsXD: PROGRAM
  IMPORTS BcdOps, DLoadState, Strings, 
    DSymOps, DSyms, DebugOps, DOutput, Frames, Gf, ImageDefs, 
    Lookup, SegmentDefs, Storage, String, SymbolOps, SymbolTable, UserInput
  EXPORTS DSymOps =
  BEGIN OPEN DSymOps, DSyms, MachineDefs;

  myVersion: PUBLIC BcdDefs.VersionStamp ← ImageDefs.ImageVersion[];
  CantAttach: ERROR = CODE;
  SymbolTableProblem: ERROR = CODE;

  SymbolSegForFrame: PUBLIC PROCEDURE [h: SymHandle] =
    BEGIN
    cgfi: GFTIndex;
    config: ConfigIndex;
    ssb: BcdOps.NameString;
    bcd: BcdOps.BcdBase;
    cdesc, segdesc: BcdDefs.SGIndex;

    FindModule: PROCEDURE [mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex] RETURNS [BOOLEAN] =
      BEGIN
      ssd: Strings.SubStringDescriptor;
      IF cgfi IN [mth.gfi..mth.gfi+mth.ngfi) THEN
	BEGIN
	IF h.hti = Symbols.HTNull THEN {
	  ssd ← [base: @ssb.string, offset: mth.name, length: ssb.size[mth.name]];
	  h.hti ← SymbolOps.EnterString[@ssd]};
	cdesc ← mth.code.sgi;
	segdesc ← mth.sseg;
	h.jumped ← mth.crossJumped;
	SELECT mth.file FROM
	  BcdDefs.FTNull, BcdDefs.FTSelf => NULL;
	  ENDCASE => {
	    f: BcdOps.FTHandle = @LOOPHOLE[bcd+bcd.ftOffset, BcdDefs.Base][mth.file];
	    h.version ← f.version};
	RETURN[TRUE];
	END;
      IF UserInput.userAbort THEN SIGNAL DebugOps.UserAborted;
      RETURN[FALSE];
      END;

    [] ← DLoadState.Acquire[ ! DLoadState.Invalid => GOTO nil];
    [cgfi,config] ← DLoadState.MapRealToConfig[Gf.GFI[Gf.Original[h.gf]]];
    IF config = NullConfig THEN {
      DLoadState.Release[]; ERROR Frames.Invalid[h.gf]};
    bcd ← DLoadState.AcquireBcd[config];
    ssb ← LOOPHOLE[bcd+bcd.ssOffset];
    [] ← BcdOps.ProcessModules[bcd, FindModule ! UNWIND => Cleanup[bcd]];
    FindSeg[h, bcd, segdesc, cdesc !
	SegmentDefs.FileNameError, SegmentDefs.FileError => {Cleanup[bcd]; GOTO nil};
	UNWIND => Cleanup[bcd]];
    Cleanup[bcd];
    IF h.hti = Symbols.HTNull THEN ERROR Frames.Invalid[h.gf];
    EXITS nil => RETURN;
    END;
  
  FindSeg: PROCEDURE [h: SymHandle, bcd: BcdOps.BcdBase, syms, code: BcdDefs.SGIndex] =
    BEGIN OPEN SegmentDefs;
    sgb: BcdDefs.Base = LOOPHOLE[bcd+bcd.sgOffset];
    sgh: BcdOps.SGHandle = @sgb[syms];
    file: SegmentDefs.FileHandle ← NIL;
    f: BcdOps.FTHandle = @LOOPHOLE[bcd+bcd.ftOffset, BcdDefs.Base][sgh.file];
    symsbcd: BcdOps.BcdBase;
    SELECT sgh.file FROM
      BcdDefs.FTNull => RETURN;
      BcdDefs.FTSelf => 
	BEGIN
	bcdseg: FSHandle ← LongVMtoFileSegment[bcd];
	h.seg ← NewFileSegment[bcdseg.file, sgh.base, sgh.pages+sgh.extraPages, Read];
	h.fgt ← sgh.extraPages # 0;
	h.version ← f.version;
	RETURN;
	END;
      sgb[code].file => file ← Gf.File[h.gf];
      ENDCASE;
    IF file = NIL THEN file ← OpenFile[bcd,f];
    h.seg ← NewFileSegment[file, 1, 1, Read];
    SwapIn[h.seg];
    symsbcd ← FileSegmentAddress[h.seg];
    IF symsbcd.version # f.version THEN {
      Unlock[h.seg]; DeleteFileSegment[h.seg]; h.seg ← NIL;
      IncorrectVersion[file]; RETURN};
    Unlock[h.seg];
    MoveFileSegment[h.seg, sgh.base, sgh.pages+sgh.extraPages];
    h.fgt ← sgh.extraPages # 0;
    RETURN;
    END;

  OpenFile: PROC [bcd: BcdOps.BcdBase, f: BcdOps.FTHandle]
    RETURNS [fh: SegmentDefs.FileHandle] = {
    ssb: BcdOps.NameString = LOOPHOLE[bcd+bcd.ssOffset];
    ss: Strings.SubStringDescriptor ← [@ssb.string, f.name, ssb.size[f.name]];
    name: STRING ← [40];
    Strings.AppendSubString[name, @ss];
    CheckExtension[name];
    fh ← DebugOps.CacheNewFile[name, SegmentDefs.DefaultAccess] };

  Cleanup: PROC [bcd: BcdOps.BcdBase] =
    {DLoadState.ReleaseBcd[bcd]; DLoadState.Release[]};

  IncorrectVersion: PROC [file: SegmentDefs.FileHandle] =
    BEGIN OPEN DOutput;
    name: STRING ← [40];
    DebugOps.FileName[name, file];
    Char[' ]; Text[name]; Text[" referenced in different versions! "L];
    END;

  CheckExtension: PROCEDURE [s: STRING] =
    BEGIN
    i: CARDINAL;
    FOR i DECREASING IN [1..s.length) DO
      IF s[i] = '. THEN RETURN;
      ENDLOOP;
    String.AppendString[s,".bcd"L];
    END;
  
  AttachSyms: PUBLIC PROC [gf: GFHandle, file: STRING] =
    BEGIN
    syms: Item;
    mod: STRING ← [40];
    rec: SymRec ← [gf: gf, hti: Symbols.HTNull];
    fh: FileHandle;
    String.AppendString[mod,file]; DSymOps.StripExtension[mod];
    String.AppendString[mod,".bcd"L];
    fh ← SegmentDefs.NewFile[mod,SegmentDefs.Read,SegmentDefs.OldFileOnly !
      SegmentDefs.FileNameError => {SIGNAL Lookup.Fail[mod]; CONTINUE}];
    IF gf # NIL AND (syms ← FindFrame[gf]) # NIL THEN
      DSymOps.DeleteItem[syms];
    CompilerSeg[@rec,fh ! CantAttach =>
	BEGIN OPEN DOutput;
	Blanks[1]; Text[mod]; Text[" is a definitions file!"L];
	GOTO bad
	END];
    [] ← CacheItem[@rec ! SymbolTable.Missing =>
	  BEGIN OPEN DOutput;
	  Blanks[1]; Text[mod]; Text[" does not have a valid symbol table!"L];
	  CONTINUE
	  END];
    DebugOps.InvalidateFileCache[];
    EXITS bad => NULL
    END;
  
  CompilerSeg: PROC [h: SymHandle, file: FileHandle] =
    BEGIN OPEN SegmentDefs;
    sgh: BcdOps.SGHandle;
    mth: BcdOps.MTHandle;
    ssb: BcdOps.NameString;
    sSeg: BcdDefs.SGIndex;
    bcdPages: CARDINAL ← 1;
    bcd: BcdOps.BcdBase;
    headerSeg: FSHandle ← NewFileSegment[file, 1, bcdPages, Read];
    ssd: Strings.SubStringDescriptor;
    BEGIN
    DO
      SwapIn[headerSeg];  bcd ← FileSegmentAddress[headerSeg];
      IF bcd.versionIdent # BcdDefs.VersionID THEN GOTO nogood;
      IF bcdPages = bcd.nPages THEN EXIT;
      bcdPages ← bcd.nPages;
      Unlock[headerSeg];  SwapOut[headerSeg];
      MoveFileSegment[headerSeg, 1, bcdPages];
      ENDLOOP;
    IF bcd.nConfigs # 0 THEN GOTO nogood;
    IF bcd.definitions AND h.gf # NIL THEN {CleanupSeg[headerSeg]; ERROR CantAttach};
    mth ← @LOOPHOLE[bcd+bcd.mtOffset,Table.Base][FIRST[BcdDefs.MTIndex]];
    sSeg ← mth.sseg; h.jumped ← mth.crossJumped;
    sgh ← @LOOPHOLE[bcd+bcd.sgOffset,Table.Base][sSeg];
    IF sSeg=BcdDefs.SGNull OR sgh.pages = 0 OR sgh.file # BcdDefs.FTSelf THEN
      GOTO nogood;
    ssb ← LOOPHOLE[bcd+bcd.ssOffset];
    ssd ← [base: @ssb.string, offset: mth.name, length: ssb.size[mth.name]];
    h.hti ← SymbolOps.EnterString[@ssd];
    h.version ← bcd.version;
    h.seg ← NewFileSegment[file, sgh.base, sgh.pages+sgh.extraPages, Read];
    h.fgt ← sgh.extraPages # 0;
    CleanupSeg[headerSeg];
    EXITS nogood => CleanupSeg[headerSeg];
    END;
    END;

  CleanupSeg: PROCEDURE [seg: FSHandle] =
    BEGIN
    SegmentDefs.Unlock[seg];
    SegmentDefs.DeleteFileSegment[seg];
    END;
  
  SymbolTablePages: CARDINAL = 25;
  SymbolTableSize: CARDINAL = SymbolTablePages*MachineDefs.PageSize;

  GetRegion: PUBLIC PROCEDURE RETURNS [r: Table.Region] = {
    r.origin ← LOOPHOLE[Storage.Pages[SymbolTablePages], Table.Base];
    r.size ←  SymbolTableSize};

  END...