-- DumpHot.mesa; modified by Johnsson, July 16, 1980  8:33 AM
--		 modified by Bruce, October 25, 1980  8:16 PM

DIRECTORY
  Ascii USING [CR, DEL, LF, NUL, SP, TAB],
  Commands USING [WriteError],
  DebugFormat USING [BitAddress, Fob, Foo, LongSubString],
  DebugOps USING [
    Foo, FooProc, Lengthen, LongCopyREAD, LongREAD, NotImplemented, ShortREAD,
    InvalidAddress, UserAborted],
  DI USING [
    CheckClass, CSEIndex, CTXIndex, EnumeratedSEIndex, FindField, Foo,
    Format, GetValue, ISEIndex, LongSEIndex, Normalize, Pad, SearchCtxForVal,
    SEIndex, SubrangeSEIndex, TagIsei, TypeForSe, UnionSEIndex, ValFormat, VariantType],
  DOutput USING [
    Blanks, Char, Decimal, EOL, LongDecimal, LongOctal, Number, Octal, SubString, Text],
  Strings USING [SubString, SubStringDescriptor],
  Dump USING [
    Array, ArrayDesc, LongArrayDesc, Opaque, Printer, printers, PrintRec, Real,
    Type, Xfer, CompareSes, Sequence],
  DHeap USING [AllocFob],
  Init USING [],
  Lookup USING [CopyMore],
  State USING [GetGS, GSHandle],
  String USING [SubString],
  SymbolOps USING [BitsForType, RecordRoot, SubStringForHash],
  Symbols USING [
    CSEIndex, CTXIndex, HTIndex, HTNull, ISEIndex, ISENull, RecordSEIndex,
    RecordSENull, SEIndex, SENull, SERecord, TransferMode, TypeClass, typeTYPE],
  SymbolSegment USING [bodyType, ctxType, seType],
  Table USING [AddNotify, Base, DropNotify, Notifier],
  UserInput USING [ResetUserAbort, userAbort];

DumpHot: PROGRAM
  IMPORTS
    Commands, DebugOps, DI, DOutput, Dump, DHeap, Lookup,
    State, SymbolOps, Table, UserInput
  EXPORTS DebugOps, Dump, Init =
BEGIN OPEN DI, Dump, SymbolOps, Symbols;

NoTypeInfo: PUBLIC ERROR [sei: SEIndex] = CODE;
StrangeRecord: ERROR = CODE;

seb: Table.Base;
ctxb: Table.Base;
bb: Table.Base;
data: State.GSHandle ← State.GetGS[];
entryDepth: CARDINAL ← 0;

StringLimit: PUBLIC CARDINAL ← LAST[CARDINAL];

Enter: PROCEDURE = {
  IF entryDepth = 0 THEN Table.AddNotify[Notify];
  entryDepth ← entryDepth + 1};

Exit: PROC = {IF (entryDepth ← entryDepth-1) = 0 THEN Table.DropNotify[Notify]};

Notify: Table.Notifier =
  BEGIN OPEN SymbolSegment;
  seb ← base[seType];
  ctxb ← base[ctxType];
  bb ← base[bodyType];
  END;

UserPrint: PROCEDURE [f: Foo] RETURNS [BOOLEAN] =
  BEGIN
  p: POINTER TO PrintRec;
  IF f.tsei = SENull THEN RETURN[FALSE];
  FOR p ← printers, p.link UNTIL p = NIL DO
    IF ~CompareSes[p.tsei, f.tsei] THEN LOOP;
    p.proc[f !ANY => BEGIN p.tsei ← SENull; CONTINUE END];
    RETURN [TRUE];
    ENDLOOP;
  RETURN[FALSE];
  END;

DisplayFoo: PUBLIC DebugOps.FooProc = {Display[f]};

Display: PUBLIC PROCEDURE [f: Foo, rec: BOOLEAN ← FALSE] =
  BEGIN
  csei: CSEIndex;
  IF f = NIL THEN RETURN;
  Enter[];
  IF f.typeOnly THEN
    WITH seb[f.tsei] SELECT FROM
      id => Type[LOOPHOLE[f.tsei]];
      ENDCASE => {Exit[]; ERROR NoTypeInfo[f.tsei]}
  ELSE
    BEGIN ENABLE {
	DebugOps.InvalidAddress => {
	  Commands.WriteError[badAddress,FALSE];
	  DOutput.LongOctal[address]; DOutput.Char[']];
	  CONTINUE};
	UNWIND => Exit[]};
    IF f.hti # HTNull THEN {
      IF f.indent # 0 THEN DOutput.Blanks[f.indent];
      HtiVal[f.hti];
      DOutput.Text[IF rec THEN ":"L ELSE " = "L]};
    IF ~UserPrint[f] THEN {
      csei ← DI.TypeForSe[f.tsei];
      WITH seb[csei] SELECT FROM
	basic, enumerated, ref, arraydesc,
	relative, subrange, long, real, zone => GetValue[f];
	transfer, record, array, union, opaque => NULL;
	ENDCASE;
      WITH seb[csei] SELECT FROM
	basic, ref, enumerated => TypedNum[f.addr.base↑, f.tsei];
	zone => Zone[f, mds];
	relative => Relative[f];
	record => Record[f];
	array => Array[f];
	arraydesc => ArrayDesc[f];
	transfer => Xfer[f];
	union => UnionErr[];
	subrange => Subrange[f];
	long => Long[f];
	real => Real[f];
	opaque => Opaque[f];
	ENDCASE};
    END;
  Exit[];
  IF ~rec THEN DOutput.EOL[];
  END;

UnionErr: PROC = {SIGNAL DebugOps.NotImplemented[" Printing entire variant parts"L]};

Zone: PUBLIC PROCEDURE [f: Foo, short: BOOLEAN] =
  BEGIN
  lp: LONG POINTER TO LONG UNSPECIFIED = f.addr.base;
  IF short THEN TypedNum[f.addr.base↑, f.tsei] ELSE TypedLongNum[lp↑, f.tsei];
  END;

Basic: PUBLIC PROCEDURE [f: Foo] =
  BEGIN
  [] ← CheckClass[basic, f];
  TypedNum[f.addr.base↑, f.tsei];
  END;

Pointer, StringPrinter: PUBLIC PROCEDURE [f: Foo] =
  BEGIN
  [] ← CheckClass[ref, f];
  TypedNum[f.addr.base↑, f.tsei];
  END;

Relative: PUBLIC PROCEDURE [f: Foo] =
  BEGIN
  type: {p, long, desc};
  csei: Symbols.CSEIndex ← DI.TypeForSe[f.tsei];
  [] ← CheckClass[relative, f];
  Enter[];
  WITH seb[csei] SELECT FROM
    arraydesc => {f.tsei ← csei; DOutput.Text[" RELATIVE "L]; type ← desc};
    long => {
      csei: Symbols.CSEIndex ← DI.TypeForSe[rangeType];
      IF seb[csei].typeTag = arraydesc THEN {f.tsei ← csei; DOutput.Text[" RELATIVE "L]};  
      type ← long};
    ENDCASE => type ← p;
  Exit[];
  SELECT type FROM
    long => Long[f];
    desc => ArrayDesc[f];
    ENDCASE => TypedNum[f.addr.base↑, f.tsei];
  END;

Enumerated: PUBLIC PROCEDURE [f: Foo] =
  BEGIN
  [] ← CheckClass[enumerated, f];
  TypedNum[f.addr.base↑, f.tsei];
  END;

Record: PUBLIC PROCEDURE [f: Foo] = {
  variant: RecordSEIndex ← CheckClass[record, f];
  root: RecordSEIndex ← SymbolOps.RecordRoot[variant];
  VariantType: PROC RETURNS [RecordSEIndex] = {RETURN[variant]};
  IF root # RecordSENull THEN f.tsei ← root;
  IF root = variant THEN variant ← RecordSENull;
  RecordCommon[f,VariantType]};

NullVariant: PUBLIC PROC RETURNS [Symbols.RecordSEIndex] = {RETURN[Symbols.RecordSENull]};

RecordCommon: PROC [f: Foo, variant: PROC RETURNS [Symbols.RecordSEIndex]] =
  BEGIN
  rsei: RecordSEIndex ← CheckClass[record, f];
  Lookup.CopyMore[rsei];
  Enter[];
  WITH seb[f.tsei] SELECT FROM
    id => HtiVal[hash];
    ENDCASE;
  FieldCtx[f, seb[rsei].fieldCtx, Pad[f,rsei], variant, rsei !UNWIND => Exit[]];
  Exit[];
  END;

FieldCtx: PUBLIC PROCEDURE [
    f: Foo, ctx: Symbols.CTXIndex, pad: CARDINAL,
    variant: PROC RETURNS [Symbols.RecordSEIndex] ← NullVariant,
    rsei: Symbols.RecordSEIndex ← Symbols.RecordSENull] =
  BEGIN
  notXfer: BOOLEAN ← ~f.xfer;
  root,isei: ISEIndex; 
  first: BOOLEAN ← TRUE;
  csei: CSEIndex;
  GetNextSe: PROC =
    BEGIN
    WITH id: seb[isei] SELECT FROM
      sequential =>  isei ← isei + SIZE[sequential id Symbols.SERecord];
      linked =>  IF (isei ← id.link) = root THEN isei ← Symbols.ISENull;
      ENDCASE => isei ← Symbols.ISENull;
    END;
  Enter[];
  isei ← root ← ctxb[ctx].seList;
  IF notXfer THEN DOutput.Char['[];
  DO
    IF isei = Symbols.ISENull THEN EXIT;
    IF seb[isei].constant OR seb[isei].idType = Symbols.typeTYPE OR seb[isei].idCtx # ctx THEN
      {GetNextSe[]; LOOP};
    IF notXfer AND ~first THEN DOutput.Text[", "L];
    csei ← DI.TypeForSe[isei];
    SELECT seb[csei].typeTag FROM
      union => Variant[f, pad, LOOPHOLE[csei], variant];
      sequence => {
	HashVal[isei]; DOutput.Char[':]; Sequence[f, pad, LOOPHOLE[csei], variant]};
      ENDCASE =>
	BEGIN ENABLE UNWIND => Exit[];
	temp: Foo ← FindField[f,pad,isei];
	IF temp.typeOnly THEN {GetNextSe[]; LOOP};
	temp.addr.useStack ← temp.xfer ← FALSE;
	IF notXfer THEN temp.indent ← 0;
	Display[temp, notXfer];
	END;
    IF first THEN first ← FALSE;
    GetNextSe[];
    ENDLOOP;
  WITH ctxb[ctx] SELECT FROM
    included => IF ~complete THEN {
      IF notXfer AND ~first THEN DOutput.Text[", "L] ELSE DOutput.Blanks[f.indent];
      DOutput.Text["..."L]};
    ENDCASE;
  IF notXfer THEN DOutput.Char[']];
  Exit[];
  END;

Variant: PUBLIC PROCEDURE [f: Foo, pad: CARDINAL, usei: UnionSEIndex,
  variant: PROC RETURNS [Symbols.RecordSEIndex] ← NullVariant] =
  BEGIN
  isei: ISEIndex;
  bound: RecordSEIndex ← variant[];
  SELECT VariantType[usei] FROM
    controlled =>
      BEGIN
      IF (isei ← TagIsei[f,pad,usei]) = ISENull THEN
	BEGIN DOutput.Text["UnknownVariant[...]"L]; RETURN END;
      HashVal[isei];
      RecordCommon[MakeVarFoo[f, pad, DI.TypeForSe[isei], bound], NullVariant];
      END;
    overlaid => {
      DOutput.Text["OVERLAID"L];
      IF bound = Symbols.RecordSENull THEN DOutput.Text["[...]"L]
      ELSE RecordCommon[MakeVarFoo[f, pad, bound, Symbols.RecordSENull], NullVariant]};
    computed => {
      DOutput.Text["COMPUTED"L];
      IF bound = Symbols.RecordSENull THEN DOutput.Text["[...]"L]
      ELSE RecordCommon[MakeVarFoo[f, pad, bound, Symbols.RecordSENull], NullVariant]};
    ENDCASE => ERROR StrangeRecord;
  END;

MakeVarFoo: PROC [r: Foo, pad: CARDINAL, tag, bound: CSEIndex]
    RETURNS [f: Foo] =
  BEGIN
--  IF bound # Symbols.RecordSENull AND bound # tag THEN
--    DOutput.Text[" ! Incorrect tag for bound variant. "L];
  f ← DHeap.AllocFob[];
  f.tsei ← tag;
  f.there ← r.there;
  f.addr.base ← r.addr.base;
  f.addr.offset ← pad;
  [f.words, f.bits] ← Normalize[BitsForType[f.tsei]];
  END;

Subrange: PUBLIC PROCEDURE [f: Foo] =
  BEGIN
  sei: SubrangeSEIndex ← CheckClass[subrange, f];
  vf: ValFormat ← Format[sei].vf;
  org,end,val: INTEGER;
  Enter[];
  org ← seb[sei].origin;
  end ← org + seb[sei].range;
  Exit[];
  val ← org+f.addr.base↑;
  WITH vf SELECT FROM
    card,none => NULL;
    ENDCASE => SELECT org FROM
      < end => IF val ~IN [org..end] THEN vf ← [none[]];
      > end => IF LOOPHOLE[val,CARDINAL]
        ~IN [LOOPHOLE[org,CARDINAL]..LOOPHOLE[end,CARDINAL]] THEN vf ← [none[]];
      ENDCASE;
  Num[val, vf];
  END;

Long: PUBLIC PROCEDURE [f: Foo] =
  BEGIN
  sei: LongSEIndex ← CheckClass[long, f];
  p: LONG POINTER TO LONG UNSPECIFIED ← LOOPHOLE[f.addr.base];
  rsei: SEIndex;
  Enter[];
  rsei ← seb[sei].rangeType;
  WITH seb[DI.TypeForSe[rsei]] SELECT FROM
    arraydesc => LongArrayDesc[f !UNWIND => Exit[]];
    ENDCASE => TypedLongNum[p↑, rsei];
  Exit[];
  END;

Char: PUBLIC PROCEDURE [c: UNSPECIFIED] =
  BEGIN OPEN Ascii;	
  SELECT c FROM
    NUL => DOutput.Text["NUL"L];
    TAB => DOutput.Text["TAB"L];
    LF => DOutput.Text["LF"L];
    14C => DOutput.Text["FF"L];
    CR => DOutput.Text["CR"L];
    33C => DOutput.Text["ESC"L];
    IN CHARACTER[NUL..SP) =>
      BEGIN DOutput.Char['↑]; DOutput.Char[LOOPHOLE[c+100B, CHARACTER]] END;
    SP => DOutput.Text["SP"L];
    DEL => DOutput.Text["DEL"L];
    ENDCASE =>
      IF c ~IN CHARACTER[NUL..DEL] THEN DOutput.Octal[c]
      ELSE BEGIN DOutput.Char['']; DOutput.Char[c] END;
  RETURN
  END;

HashVal: PUBLIC PROCEDURE [sei: ISEIndex] =
  BEGIN
  Enter[];
  HtiVal[IF sei = SENull THEN HTNull ELSE seb[sei].hash];
  Exit[];
  END;

HtiVal: PUBLIC PROCEDURE [hti: HTIndex] =
  BEGIN
  IF hti = HTNull THEN DOutput.Text["(anon)"L]
  ELSE
    BEGIN OPEN Strings;
    desc: SubStringDescriptor;
    ss: SubString ← @desc;
    SubStringForHash[ss,hti];
    DOutput.SubString[ss];
    END;
  END;

EnumVal: PUBLIC PROCEDURE [val: UNSPECIFIED, esei: EnumeratedSEIndex] =
  BEGIN
  ictx: CTXIndex = seb[esei].valueCtx;
  sei: ISEIndex = DI.SearchCtxForVal[val,ictx,none];
  IF sei # ISENull THEN HashVal[sei] ELSE BadNum[val];
  END;

TypedNum: PUBLIC PROCEDURE [val: UNSPECIFIED, tsei: SEIndex] =
  BEGIN Num[val, Format[tsei].vf] END;

Num: PUBLIC PROCEDURE [val: UNSPECIFIED, vf: ValFormat] =
  BEGIN
  WITH vf SELECT FROM
    card => DOutput.Octal[val];
    int => MyDecimal[val];
    char => Char[val];
    pointer => IF val = NIL THEN DOutput.Text["NIL"L]
      ELSE BEGIN DOutput.Octal[val]; DOutput.Char['↑]; END;
    relative => {
	DOutput.Number[val, [base: 10, zerofill: FALSE, unsigned: TRUE, columns: 0]];
	DOutput.Text["↑R"L]};
    string => StringCommon[DebugOps.Lengthen[val]];
    enum => EnumVal[val, esei];
    ENDCASE => BadNum[val];
  END;

BadNum: PUBLIC PROCEDURE [val: UNSPECIFIED] =
  BEGIN DOutput.Text["?["L]; DOutput.Octal[val]; DOutput.Char[']] END;

TypedLongNum: PUBLIC PROCEDURE [val: LONG UNSPECIFIED, tsei: SEIndex] =
  BEGIN LongNum[val, Format[tsei].vf] END;

LongNum: PUBLIC PROCEDURE [val: LONG UNSPECIFIED, vf: ValFormat] =
  BEGIN
  WITH vf SELECT FROM
    card => DOutput.LongOctal[val];
    int => DOutput.LongDecimal[LOOPHOLE[val, LONG INTEGER]];
    pointer => IF val = NIL THEN DOutput.Text["NIL"L]
      ELSE BEGIN DOutput.LongOctal[val]; DOutput.Char['↑]; END;
    relative =>
      {DOutput.LongDecimal[LOOPHOLE[val, LONG INTEGER]]; DOutput.Text["R↑"L]};
    string => StringCommon[val];
    ENDCASE => BadLongNum[val];
  END;

BadLongNum: PROCEDURE [val: LONG UNSPECIFIED] =
  BEGIN DOutput.Text["?["L]; DOutput.LongOctal[val]; DOutput.Char[']] END;

MyDecimal: PROCEDURE [u: UNSPECIFIED] = INLINE 
  BEGIN DOutput.Decimal[LOOPHOLE[u,INTEGER]] END;

StringCommon: PROCEDURE [ls: LONG STRING] =
  BEGIN OPEN DOutput;
  sb: StringBody;
  IF ls = NIL THEN BEGIN DOutput.Text["NIL"L]; RETURN END;
  DebugOps.LongCopyREAD[from: ls, nwords: SIZE[StringBody], to: @sb];
  Char['(]; MyDecimal[sb.length]; Char[',];
  MyDecimal[sb.maxlength]; Text[")"""L];
  UserLongText[ls ! DebugOps.UserAborted => {ControlDel[]; CONTINUE}];
  Char['"];
  END;

-- write strings from user world

UserText: PUBLIC PROCEDURE [s: STRING] =
  BEGIN
  length: CARDINAL ← DebugOps.ShortREAD[@s.length];
  UserDump[DebugOps.Lengthen[s], 0, length];
  RETURN
  END;

UserSubString: PUBLIC PROCEDURE[ss: String.SubString] =
  BEGIN UserDump[DebugOps.Lengthen[ss.base], ss.offset, ss.length] END;

UserLongText: PUBLIC PROCEDURE [s: LONG STRING] =
  BEGIN
  length: CARDINAL = DebugOps.LongREAD[@s.length];
  UserDump[s, 0, length];
  RETURN
  END;

UserLongSubString: PUBLIC PROCEDURE [ls: DebugFormat.LongSubString] =
  BEGIN UserDump[ls.base, ls.offset, ls.length] END;

UserDump: PROCEDURE [base: LONG STRING, offset, length: CARDINAL] =
  BEGIN
  i: CARDINAL;
  s: PACKED ARRAY [0..1] OF CHARACTER;
  p: POINTER = @s;
  bad: BOOLEAN ← offset > 5000 OR length > 5000 OR
    LONG[offset]+LONG[length] > LAST[CARDINAL];
  IF offset MOD 2 # 0 THEN p↑ ← DebugOps.LongREAD[@base.text+offset/2];
  FOR i IN [offset..offset+length) DO
    IF i MOD 2 = 0 THEN p↑ ← DebugOps.LongREAD[@base.text+i/2];
    DOutput.Char[s[i MOD 2]];
    IF i - offset > StringLimit THEN RETURN;
    IF UserInput.userAbort THEN {ControlDel[]; RETURN};
    ENDLOOP;
  RETURN
  END;

ControlDel: PROC ={UserInput.ResetUserAbort[]; DOutput.Text[" ... aborted"L]};
  
END.