-- StmtMap.Mesa
-- Edited by:
--            Sandman on July 23, 1980  10:33 AM
--            Bruce on October 25, 1980  8:29 PM
--            Sweet on May 19, 1980  4:32 PM

DIRECTORY
  Copier USING [Outer],
  DOutput USING [Line],
  DSyms USING [CrossJumped, GFHandle, GFrameMdi, NoFGT],
  FileWindow USING [WindowForFile],
  Inline USING [LongNumber],
  MachineDefs USING [DestroyStream, GFHandle, NullGF],
  Pc USING [CBti, BytePC, Ep, NullPC],
  PcOps USING [FindCbti, cache, Item],
  PrincOps USING [BytePC],
  Source USING [
    GetCreateDate, GFHandle, Handle, IgnoreTimeStamp, LoadWindow, Open, PrintTextLine],
  State USING [GF],
  String USING [AppendString],
  Strings USING [AppendString],
  Symbols USING [BTIndex, BTNull, CBTIndex, CBTNull, MDIndex],
  SymbolSegment USING [FGTEntry],
  SymbolTable USING [Base],
  Time USING [Append, Unpack];

StmtMap: PROGRAM
  IMPORTS 
    Copier, DOutput, FileWindow, DSyms, MachineDefs, Pc, PcOps, Source, State,
    String, Strings, Time 
  EXPORTS Source =
BEGIN OPEN Pc, DSyms, MachineDefs, Symbols;

FileMissing: PUBLIC SIGNAL [name: STRING] = CODE;
MappingFailure: PUBLIC SIGNAL = CODE;
TooLong: ERROR = CODE;

PcToSource: PUBLIC PROCEDURE [
    base: SymbolTable.Base, gf: GFHandle, pc: PrincOps.BytePC,
    bti: BTIndex, start: BytePC]
    RETURNS [currentSource: CARDINAL] =
  BEGIN
  -- finds i such that pc is in [fgt[i].cIndex..fgt[i+1].cIndex)
  i, s, l: CARDINAL;
  deltaPc: CARDINAL ← pc - start;
  stride: CARDINAL ← 0;
  IF bti = BTNull THEN ERROR MappingFailure;
  currentSource ← base.bb[bti].sourceIndex;
  WITH base.bb[bti].info SELECT FROM
    External => BEGIN s ← startIndex; l ← indexLength; END;
    ENDCASE => ERROR;
  WITH base.bb[bti] SELECT FROM
    Other => deltaPc ← deltaPc - relOffset;
    ENDCASE;
  IF l = 1 THEN RETURN;
  FOR i IN (s..s+l) DO
    WITH base.fgTable[i] SELECT FROM
      normal =>
	BEGIN
	IF deltaObject > deltaPc THEN RETURN;
	deltaPc ← deltaPc - deltaObject;
	currentSource ← currentSource + deltaSource + stride;
	stride ← 0;
	END;
      step =>
	IF which = object THEN
	  IF delta > deltaPc THEN RETURN ELSE deltaPc ← deltaPc - delta
	ELSE stride ← stride + delta;
      ENDCASE;
    ENDLOOP;
  RETURN
  END;

PCToSourceIndex: PUBLIC PROC [pc: PrincOps.BytePC, frame: GFHandle ← NullGF]
  RETURNS [index: LONG CARDINAL] =
  {RETURN[PCToSourceCommon[pc,frame,FALSE].index]};

PCToSourceCommon: PROC [
    pc: PrincOps.BytePC, gf: GFHandle, load: BOOLEAN, s: STRING ← NIL]
  RETURNS [h: Source.Handle, index: LONG CARDINAL] =
  BEGIN
  mdi: MDIndex;
  cbti: CBTIndex;
  start: BytePC;
  OpenSymbols: PROC [base: SymbolTable.Base] =
    BEGIN
    fileId: STRING ← [40];
    dot: CARDINAL;
    version: LONG CARDINAL ← base.stHandle.sourceVersion.time;
    Strings.AppendString[fileId,base.sourceFile];
    IF fileId[dot ← fileId.length - 1] = '. THEN fileId.length ← dot;
    IF s # NIL THEN String.AppendString[s,fileId];
    h ← Source.Open[fileId];
    IF h = NIL THEN ERROR FileMissing[fileId];
    CheckTime[version,h,fileId];
    IF pc = Pc.NullPC THEN RETURN;
    index ← PcToSource[base, gf, pc, cbti, start];
    END;
  IF gf = NullGF THEN gf ← State.GF[];
  h ← NIL;
  cbti ← GetUserCbti[pc,gf];
  IF pc # Pc.NullPC THEN start ← Pc.Ep[pc,gf].start;
  mdi ← DSyms.GFrameMdi[gf,TRUE];
  index ← 0;
  Copier.Outer[mdi,OpenSymbols ! UNWIND => IF h # NIL THEN MachineDefs.DestroyStream[h]];
  IF ~load THEN MachineDefs.DestroyStream[h];
  END;

  GetUserCbti: PROC [pc: PrincOps.BytePC, gf: GFHandle] RETURNS [CBTIndex] =
    BEGIN
    cbti: CBTIndex = Pc.CBti[pc,gf];
    item: PcOps.Item;
    IF cbti = CBTNull THEN RETURN[cbti];
    item ← PcOps.FindCbti[cbti];
    RETURN[PcOps.cache[item].userCbti];
    END;

  Validate: PUBLIC PROC [h: Source.Handle, gf: GFHandle] =
    BEGIN
    fileId: STRING ← [40];
    version: LONG CARDINAL;
    CheckVersion: PROC [base: SymbolTable.Base] = {
      version ← base.stHandle.sourceVersion.time;
      Strings.AppendString[fileId,base.sourceFile]};
    Copier.Outer[DSyms.GFrameMdi[gf,FALSE],CheckVersion];
    IF h = NIL THEN ERROR FileMissing[fileId];
    CheckTime[version,h,fileId];
    END;

  CheckTime: PROC [bcd: LONG CARDINAL, sh: Source.Handle, name: STRING] =
    BEGIN OPEN String;
    msg: STRING ← [120];
    fileTime: LONG CARDINAL;
    fileTime ← Source.GetCreateDate[sh];
    IF bcd = fileTime OR Source.IgnoreTimeStamp[FileWindow.WindowForFile[name]] THEN
      RETURN;
    AppendString[msg, "Can't use "L];
    AppendString[msg,name]; AppendString[msg," of "L];
    AppendTime[msg,fileTime]; AppendString[msg," instead of version created "L];
    AppendTime[msg,bcd];
    ERROR FileMissing[msg];
    END;

  AppendTime: PROC [msg: STRING, time: LONG CARDINAL] = INLINE
    BEGIN Time.Append[msg,Time.Unpack[LOOPHOLE[time]]] END;

Display: PUBLIC PROC [gf: GFHandle, pc: PrincOps.BytePC, load: BOOLEAN] =
  BEGIN
  fileId: STRING ← [40];
  file: Source.Handle;
  index: LONG CARDINAL;
  [file,index] ← PCToSourceCommon[pc, gf, TRUE, fileId ! DSyms.NoFGT =>
    BEGIN ERROR FileMissing["-- Compressed Symbols --"L] END];
  IF DSyms.CrossJumped[gf] THEN DOutput.Line[" Cross jumped!"L];
  index ← Source.PrintTextLine[file,ShortenIndex[index]];
  IF ~load THEN {MachineDefs.DestroyStream[file]; RETURN};
  Source.LoadWindow[fileId, gf, file, index];
  RETURN
  END;

ShortenIndex: PROC [lc: LONG CARDINAL] RETURNS [CARDINAL] =
  BEGIN
  ln: Inline.LongNumber ← LOOPHOLE[lc];
  IF ln.highbits # 0 THEN ERROR TooLong;
  RETURN [ln.lowbits]
  END;

END.