{$D-,L-,S+,R-,E-,N-}
PROGRAM TWU1B;
Uses TWU1EQU, TWU1UAM, TWU1RPT, TWU1UNA, Dos, Crt;

TYPE
   MethodName	= String[127];
   HeadProc	= PROCEDURE;
   LGClass   = (
   		LG_ABSQ,		{Absolute Equivalence}
                LG_ARBC,		{Array Bounds}
                LG_ASGN,		{Biggest Assgn Compat Type}
                LG_BASE,		{Base Type}
   	        LG_CONS,		{Const Type}
                LG_FUNR,		{Function Result}
                LG_OBJP,		{Parent Object}
                LG_PARM,		{Formal Parameter}
                LG_TYPE			{Named Type, Xtrn Var}
                );
   LGString	= String[21];

VAR
   NoteTime,	JobTime:	LongInt;	{ Elapsed Time Buckets	}
   NextLL,	LastLL:		LongInt;	{ Location Counters	}
   OffsetLL:			LongInt;	{ Section Relative Base }
   TabStop,	NoteX,	NoteY:	Integer;	{ Miscellaneous		}
   CPUType:	CPUGate;			{ Optional Parameter	}
   DisAssembly: Boolean;			{ Optional Parameter	}
   SurveyWork: 	SurveyRec;			{ Common Work Variable	}
   Map,MapC: 	MapRefRec;			{ Common Work Variables }
   Win: 	Boolean;			{ WINDOWS Option	}

CONST
   TypTxt : Array[0..15] of String[11] = (
   	{ $0} 'untyped', { $1} 'ARRAY', { $2} 'RECORD',	{ $3} 'OBJECT',
        { $4} 'FILE',	 { $5} 'TEXT',  { $6} 'proc',	{ $7} 'SET',
        { $8} 'POINTER', { $9} 'STRING',{ $A} '8087 float',
	{ $B} '6-byte real',		{ $C} 'fixed-point',
	{ $D} 'boolean', { $E} 'char',	{ $F} 'enumeration');

PROCEDURE NoteBegin(S:String);					{.CP08}
VAR HH,MM,SS,CS : Word;
BEGIN
	NoteX := WhereX; NoteY := WhereY; ClrEol;
	GetTime(HH,MM,SS,CS);
	NoteTime := (LongInt(HH*60+MM)*60+SS)*100+CS;
	If S <> '' Then Write(S);
END;

PROCEDURE PageOverFlow(Lines : Word; CallProc : HeadProc);	{.CP09}
BEGIN
	IF LinesRemaining < Lines THEN
	BEGIN
		NewTxtPage;
		CallProc;
	END
	ELSE	NewTxtLine;
END;

PROCEDURE NoteEnd;						{.CP11}
VAR HH,MM,SS,CS : Word; SF : String[3];  I : Integer;
BEGIN
	GetTime(HH,MM,SS,CS);
	NoteTime := ((LongInt(HH*60+MM)*60+SS)*100+CS) - JobTime;
        Str(NoteTime MOD 100 + 100:3,SF);
        I := NoteTime DIV 100;
        GoToXY(NoteX,NoteY+1);
        ClrEol;
	Write('Elapsed Time: ',I,'.',Copy(SF,2,2),' seconds');
	GoToXY(NoteX,NoteY);
END;

FUNCTION NameOfMethod(U:UnitPtr;UsrDE:LL):MethodName;		{.CP20}
VAR DS, DC : DNamePtr; S : DStubPtr; T : TypePtr; N, M : String[64];
BEGIN
	N := ''; M := '???';
	IF UsrDE <> $FFFF THEN
	BEGIN
	   DS := DNamePtr(PtrAdjust(U,UsrDE));
	   M  := DS^.DSymb;
	   S  := AddrStub(DS);
	   IF Public(DS^.DForm) = 'S' THEN {ensure subprogram entry}
	   IF (S^.sSTp AND $10) <> 0  THEN {get OBJECT Name Qualifier}
	   IF  S^.sSPS <> 0 THEN
	   BEGIN
		T  := TypePtr(PtrAdjust(U,S^.sSPS));	  {to Object TD}
		DC := DNamePtr(PtrAdjust(U,T^.ObjtName)); {to Object DE}
		N  := DC^.Dsymb+'.';
	   END
	END;
	NameOfMethod := N + M
END;   {NameOfMethod}

PROCEDURE PrintTitleBlk(S : String; LinesNeeded : Integer);	{.CP08}
BEGIN {PrintTitleBlk}
	IF LinesRemaining < LinesNeeded+3
		THEN NewTxtPage	ELSE SetCol(1);
	PutTxt('-----');   NewTxtLine;
	PutTxt('- ' + S); NewTxtLine;
	PutTxt('-----');   SetCol(1);
END; {PrintTitleBlk}

PROCEDURE PrintAddress(Arg : LongInt);				{.CP06}
BEGIN
	IF ColumnsUsed <> 0 THEN NewTxtLine;
	PutTxt(HexA(Arg));
	SetCol(7);
END; {PrintAddress}

PROCEDURE PrintByteList(U : Pointer; Count, Space : Word);	{.CP10}
BEGIN
	WHILE Count > 0 DO
	BEGIN
		PutTxt(HexB(Mem[Seg(U^):Ofs(U^)+NextLL-OffsetLL]));
		SetCol(ColumnsUsed+Space+1);
		Inc(NextLL);
		Dec(Count);
	END
END; {PrintByteList}

PROCEDURE PrintWd(U : UnitPtr; S : String);			{.CP07}
BEGIN
	PrintAddress(NextLL);
	PrintByteList(U,2,1);
	SetCol(TabStop);
	PutTxt(S);
END; {PrintWd}

PROCEDURE PrintDWd(U : UnitPtr; S : String);			{.CP07}
BEGIN
	PrintAddress(NextLL);
	PrintByteList(U,4,1);
	SetCol(TabStop);
	PutTxt(S);
END; {PrintDWd}

PROCEDURE PrintLL(U : UnitPtr; S : String);			{.CP07}
BEGIN
	PrintAddress(NextLL);
	PrintByteList(U,2,1);
	SetCol(TabStop);
	PutTxt('LL('+S+')');
END; {PrintLL}

PROCEDURE PrintSoloByte(U : UnitPtr; S : String);		{.CP08}
VAR B : Byte;
BEGIN
	PrintAddress(NextLL);
	PrintByteList(U,1,0);
	SetCol(TabStop);
	PutTxt(S);
END; {PrintSoloByte}

PROCEDURE PrintBytes(U : UnitPtr; Count, Limit : Word);		{.CP12}
VAR I : Integer;
BEGIN
	I := 0;
	WHILE Count > 0 DO BEGIN
		I := I MOD Limit;
		IF I = 0 THEN PrintAddress(NextLL);
		PrintByteList(U,1,1);
		Inc(I);
		Dec(Count);
	END;
END; {PrintBytes}

PROCEDURE PrintBytesOff(U: UnitPtr; Cnt, Lim, Indent : Word);	{.CP16}
VAR I : Integer;
BEGIN
	I := 0;
	WHILE Cnt > 0 DO BEGIN
		I := I MOD Lim;
		IF I = 0 THEN
		Begin
		   PrintAddress(NextLL);
		   SetCol(Indent);
		End;
		PrintByteList(U,1,1);
		Inc(I);
		Dec(Cnt);
	END;
END; {PrintBytesOff}

FUNCTION NilLG(L: LG) : Boolean;				{.CP02}
BEGIN NilLG := (L.UntLL = 0) AND (L.UntId = 0) END;

Function GetArrayBounds(U: UnitPtr; Arg: LG):String;		{.CP14}
Var Tp: TypePtr; V: DNamePtr; Tu: UnitPtr; R: RespLG; Bl,Bu: String[12];
Begin
   GetArrayBounds := '';
   V := AddrLGUnit(U,Arg);		{Point to Host Unit Name}
   ResolveLG(V^.DSymb,Arg,R);		{Find Unit in Heap}
   Tu := R.Uptr;                        {Get Ptr to Host Unit}
   If Tu <> Nil Then
   Begin
      Tp := TypePtr(PtrAdjust(Tu,Arg.UntLL)); {to bounds descriptor}
      Str(Tp^.LoBnd, Bl); Str(Tp^.HiBnd, Bu);
      GetArrayBounds := Bl + '..' + Bu;
   End;
End; {GetArrayBounds}

PROCEDURE PrintLG(U : UnitPtr; LGS: LGClass; S : String);	{.CP38}
CONST
   LG_Txt : Array[LGClass] Of LGString =
   	   ({LG_ABSQ} 'ABSOLUTE Target-Stub',
	    {LG_ARBC} 'Array[',		{LG_ASGN} 'Assgn Cmpat Type',
	    {LG_BASE} 'Base Type',	{LG_CONS} 'CONST Cmpat Type',
	    {LG_FUNR} 'Return Result',	{LG_OBJP} 'Ancestor Object',
	    {LG_PARM} 'Parm ',		{LG_TYPE} 'Named Type');

VAR L: LG; V : DNamePtr; R: RespLG; X: _UnitName; W : String;
BEGIN
        L := LG(Ptr(Seg(U^),Ofs(U^)+NextLL)^);
	IF NOT NilLG(L) THEN
	BEGIN
             V := AddrLGUnit(U,L);		{point to Unit Entry}
	     X := '';				{its name}
             R.Ownr := $FFFF;
             If V <> Nil Then
	     Begin
	        X := V^.DSymb;
		ResolveLG(X,L,R)
             End;
             If (R.Ownr <> $FFFF) AND (R.Ownr <> 0) Then
	     Begin
	     	W := X + '.' + NameOfMethod(R.Uptr,R.Ownr);
                If LGS <> LG_PARM Then S := '' End
             Else  W := 'in [' + X + '] ';
	     W := 'LG(' + W + ') ' + LG_Txt[LGS];
             If LGS = LG_ARBC
	     Then W := W + GetArrayBounds(U,L) +']'
	     Else W := W + S;
             S := W;
	END Else S := 'LG(nil type) ' + S;
	PrintAddress(NextLL);
	PrintByteList(U,4,1);
	SetCol(TabStop);
	PutTxt(S);
END; {PrintLG}

PROCEDURE BoundaryAlign(UH : UnitPtr);				{.CP12}
VAR I : Integer;
BEGIN {BoundaryAlign}
	I := ((NextLL + $F) AND Masker) - NextLL;
	IF I > 0 THEN
	BEGIN
		PrintBytes(UH,I,8);
		SetCol(36);
		PutTxt('Align to Paragraph Boundary');
		NewTxtLine
	END;
END;  {BoundaryAlign}

PROCEDURE PrintOffset(Base: Word);				{.CP06}
BEGIN
     IF ColumnsUsed <> 0 THEN NewTxtLine;
     PutTxt(HexA(NextLL));SetCol(7);
     PutTxt('[+'+HexW(NextLL-Base)+'] ');
END;

PROCEDURE PrintCodeBytes(U : UnitPtr;				{.CP38}
			Count,		{ Byte Count }
			Limit,		{ Max Bytes/Line }
			Base: Word;	{ Offset Origin  }
			X : Boolean);   { ASCII Panel	 }
CONST Xlat : SET OF Char = [' '..Chr($7F)];
VAR I : Integer; j, k : Word; S : String;  C : ^Char;
BEGIN
	j := 0; S := ''; k := Limit*3 + 17;	{ ASCII Panel Tab Stop }
	WHILE Count > 0 DO BEGIN
		I := j MOD Limit;	{ I = 0 if Line Full }
		IF I = 0 THEN
		BEGIN
			IF X AND (J > 0) THEN	{ ASCII & Data on Line }
			BEGIN
				SetCol(K);
				PutTxt(S); S := '';
			END;
			PrintOffset(Base);
		END;
		IF X THEN			{ Compile ASCII Panel }
		BEGIN
			C :=Ptr(Seg(U^),Ofs(U^)+NextLL-OffsetLL);
			IF C^ IN Xlat THEN S := S + C^
				      ELSE S := S + '.'
		END;
		PrintByteList(U,1,1);		{ Print a Hex Byte }
		Inc(j);
		Dec(Count);
	END;
	IF X THEN				{ Emit ASCII Panel }
	BEGIN
		SetCol(K);
		PutTxt(S);
		S := '';
	END;
END; {PrintCodeBytes}

PROCEDURE PrintListBytes(U: UnitPtr;				{.CP35}
			 Pfx,		{ Bytes to Omit from ASCII Panel }
			 Count,		{ Bytes to Print }
			 Limit,		{ Max Bytes/Line }
			 Base: Word);	{ Offset Origin	 }
CONST Xlat : SET OF Char = [' '..Chr($7F)];
VAR I : Integer; j, k : Word; S : String;  C : ^Char;
BEGIN
	j := 0; S := '='''; k := Limit*3 + 18;	{ ASCII Panel Tab Stop }
	WHILE Count > 0 DO BEGIN
		I := j MOD Limit;
		IF I = 0 THEN
		BEGIN
			IF J > 0 THEN
			BEGIN
				SetCol(K);
				PutTxt(S);
				S := '';
			END;
			PrintOffset(Base);
		END;
		IF J > Pfx THEN		{ ASCII Bytes to Compile }
		BEGIN
			C :=Ptr(Seg(U^),Ofs(U^)+NextLL-OffsetLL);
			IF C^ IN Xlat THEN S := S + C^
				      ELSE S := S + '.'
		END;
		PrintByteList(U,1,1);
		Inc(j);
		Dec(Count);
	END;
	SetCol(K);
	PutTxt(S+'''');
	S := '';
END; {PrintListBytes}

PROCEDURE PrintUnknowns(U: UnitPtr; Till: LL);			{.CP06}
BEGIN {PrintUnknowns}
	PrintTitleBlk('The Purpose of the data below is Unknown',1);
	PrintBytes(U,Till-NextLL,8);
	NewTxtLine;
END;  {PrintUnknowns}

PROCEDURE FormatHeader(U : UnitPtr);				{.CP42}
VAR I: Integer; J: Word; W: String;
BEGIN
	NoteBegin('Formatting Unit Header');
	PrintAddress(NextLL);
	FOR I := 0 TO 3 DO PutTxt(HexB(Byte(U^.UHEYE[I]))+' ');
	SetCol(TabStop);
	PutTxt('=''');
	FOR I := 0 TO 3 DO PutTxt(U^.UHEYE[I]);
	PutTxt('''');
	NewTxtLine;
	Inc(NextLL,4);
	PrintAddress(NextLL);
	FOR I := 0 TO 3 DO PutTxt(HexB(Byte(U^.UHxxx[I]))+' ');
	NewTxtLine;
	Inc(NextLL,4);
	PrintLL(U,'Dict Hdr-This Unit');
	PrintLL(U,'INTERFACE Hash Table');
	PrintLL(U,'PROC Map');
	PrintLL(U,'CSEG Map');
	PrintLL(U,'DSEG Map-Typed CONST''s');
	PrintLL(U,'DSEG Map-Global VARs');
	PrintLL(U,'DLL Module List');
	PrintLL(U,'Donor Unit List');
	PrintLL(U,'Source File List');
        With U^ Do If UHDBT = UHZDA
        Then PrintWd(U,'No Trace Table')
	Else PrintLL(U,'Debug TRACE Table');
	PrintWd(U,'Size of DICTIONARY Area');
	PrintWd(U,'CSEG Size (Aggregate)');
	PrintWd(U,'DSEG Size (Typed CONST''s)');
	PrintWd(U,'Fix-Up List Size (CSegs)');
	PrintWd(U,'Fix-Up List Size (Typed CONST''s)');
	PrintWd(U,'DSEG Size (Global VARs)');
	PrintLL(U,'DEBUG Hash Table');
        J := U^.UHSOV;
        W := '';
        If Odd(J SHR 2) Then	{ WINDOWS }
        BEGIN
           W := 'TPW';
           If Odd(J)
	   Then W := W + ',{$E+}' Else W := W + ',{$E-}';
           If Odd(J SHR 4)
           Then W := W + ',Moveable' Else W := W + ',Fixed';
           If Odd(J SHR 6)
           Then W := W + ',Preload' Else W := W + ',Demandload';
           If Odd(J SHR 12)
           Then W := W + ',Discardable' Else W := W + ',Permanent';
           If (J AND $EFAA) <> 0 Then W := W + ',Unknown Flags';
        END ELSE
        BEGIN			{ MS-DOS }
           W := 'TP6';
	   If Odd(J SHR 1)
	   Then W := W + ',{$O+}';
           If Odd(J) Then W := W + ',{$E+}' Else W := W + ',{$E-}';
           If (J AND $FFFC) <> 0 Then W := W + ',Unknown Flags';
        END;
        PrintWd(U,W);
	NewTxtLine;
	IF NextLL < U^.UHIHT THEN PrintUnknowns(U,U^.UHIHT);
	NoteEnd;
END; {FormatHeader}

PROCEDURE FormatDictionary(U : UnitPtr);			{.CP19}

   PROCEDURE PrintDictEntry;
   VAR D, DB: DNamePtr; S: DStubPtr; I: Integer; It: Byte;
       RP: VarStubPtr; DF: Char; DFM: String[8];
       T : String[44]; W : String;
   BEGIN {PrintDictEntry}
      D := AddrDict(U,SurveyWork.LocLL); S := AddrStub(D);
      RP := @S^.sRVF;
      WITH SurveyWork, D^, S^ DO
      BEGIN
         DF := Public(DForm);
         IF DF <> DForm Then DFM := 'Private ' Else DFM := '';
         I := 4+(Length(DSymb) SHR 4);
	 CASE DF OF 'R','Y': Inc(I,4);
                        'S': Inc(I,6);
	                'P': Inc(I,2);
           'Q','O','T'..'X': Inc(I);
	 END; {CASE}
	 W := '';						{.CP12}
	 IF DF = 'R' THEN
              Case sRAM Of
		  $08: IF SurveyWork.LocOwn <> 0
		       THEN W := NameOfMethod(U,SurveyWork.LocOwn);
                  $10,$01,$00: ;
		  ELSE IF RP^.ROB <> 0 THEN W := NameOfMethod(U,RP^.ROB);
              End; {Case}
	 IF W = '???' THEN W := '' ELSE
	 IF W <> ''   THEN W := W + '.';
	 PrintTitleBlk('Dictionary Entry For: "'+ W +
	 NameOfMethod(U,SurveyWork.LocLL)+'"',I);
	 IF HLink <> 0						{.CP06}
	    THEN PrintLL(U,AddrDict(U,HLink)^.DSymb)
	    ELSE PrintWd(U,'(no backward link)');
	 PrintBytes(U,1,1);
	 SetCol(TabStop);
	 PutTxt(DFM+'Type "'+DF+'" -> ');
	 CASE DF OF						{.CP18}
	   'O': W := 'GOTO Label';  'P': W := 'Un-Typed CONST';
	   'Y': W := 'Unit';        'T': W := 'Built-In Procedure';
	   'W': W := 'Port Array';  'U': W := 'Built-In Function';
	   'Q': W := 'Named Type';  'V': W := 'Built-In "NEW"';
	   'X': W := 'MEM_ Array';
	   'R': CASE sRAM OF
		  $00: W := 'Global VAR';
		  $01: W := 'Typed CONST';
		  $02: W := 'Local VAR (on Stack)';
		  $03: W := 'Absolute VAR [Seg:Ofs]';
		  $06: W := 'Self VAR (ADDR on Stack)';
		  $08: W := 'Record/Object Field';
		  $10: W := 'Absolute VAR (Equated)';
		  $22: W := 'VALUE Arg on Stack';
		  $26: W := 'VAR Arg on Stack';
		  Else W := 'New Data Type';
		END; {CASE sRAM}
	   'S': IF sSVM = 0 Then				{.CP13}
		   Case (sSTp AND $70) Of
		     $10: W := 'Method';
		     $30: W := 'Constructor';
		     $50: W := 'Destructor';
		     Else W := 'Subprogram'
		   End
		Else If (sSxx AND $10) <> 0
			Then W := 'Dynamic Method'
			Else W := 'Virtual Method';
	 END; {CASE DF OF}
	 PutTxt(W);
	 PrintBytes(U,Length(DSymb)+1,16);
	 SetCol(TabStop); PutTxt('="'+DSymb+'"');
	 NewTxtLine;
	 CASE DF OF { Format the Stub Part }			{.CP13}
	   'O': PrintWd(U,'Unknown purpose)');
	   'P': BEGIN
		   PrintLG(U,LG_CONS,'');
		   PrintBytes(U,LastLL-NextLL,8); {Temporary Fix}
		   {since value can be a string, we really need to check
		    the type descriptor out but that usually lies in the
		    system unit.  We circumvent for now by relying on the
		    distance to the next structure to determine the size
		    of the constant data for print purposes }
		   SetCol(TabStop); PutTxt('Constant Value');
		   NewTxtLine;
	        END; {CASE 'P'}
	   'Y': BEGIN						{.CP07}
		   PrintWd(U,'TURBO Work?');
		   PrintWd(U,'Unit Version Number???');
		   PrintLL(U,'next unit in list');
		   PrintLL(U,'prior unit in list');
		   NewTxtLine;
	        END; {CASE 'Y'}
   'T','U','V': BEGIN						{.CP04}
		   PrintWd(U,'Meaning Unknown');
		   NewTxtLine;
		END;
	   'W': BEGIN						{.CP04}
		   PrintSoloByte(U,'0=byte array, 1=word array');
		   NewTxtLine;
	        END;
       'Q','X': BEGIN						{.CP04}
		   PrintLG(U,LG_TYPE,'');
		   NewTxtLine;
		END;
	   'R': BEGIN						{.CP49}
		   It := sRAM AND $1F;
		   CASE sRAM OF
                      $00: T := 'Global VAR in DS';
                      $01: T := 'Typed CONST in DS';
                      $02: IF RP^.ROfs > $7FFF
			     THEN T := 'Local VAR on Stack'
                             ELSE T := 'VALUE(Stack)';
                      $03: T := 'Absolute [Seg:Ofs]';
                      $06: T := 'ADDR(Self) on Stack';
                      $08: T := 'Record/Object Field';
                      $10: T := 'Absolute Equivalence';
                      $22: T := 'Arg On Stack (VALUE)';
                      $26: T := 'Arg On Stack (VAR)';
                      ELSE T := '**** NEW CODE TO CHECK ****'
		   END; {CASE sRAM}
		   PrintSoloByte(U,T);
		   T := '';
                   Case It Of
                     $03: Begin
                             PrintWd(U,'Absolute Offset');
                             PrintWd(U,'Absolute Segment');
                          End;
                     $10: PrintLG(U,LG_ABSQ,'');
                     Else
                     Begin
			IF (It = $2) OR (It = $6) THEN With RP^ DO
			IF RP^.ROfs > $7FFF
			   THEN T := 'BP-'+HexW($10000-ROfs)
			   ELSE T := 'BP+'+HexW(ROfs)
			ELSE T := 'bytes';
			PrintWd(U,'allocation offset ('+T+')');
			CASE It OF
                          $0: T := 'Entry offset in VAR DSeg Map';
                          $1: T := 'Entry offset in CON DSeg Map';
                          $2,$6:
                                IF RP^.ROB = 0
				THEN T := 'no containing scope'
				ELSE T := 'LL(containing Scope)';
			  $8: IF RP^.ROB = 0
                              THEN T := 'no successor field/method'
                              ELSE T := 'LL(successor field/method)';
			  ELSE T := 'Usage Unknown'
			END; {CASE It}
			PrintWd(U,T);
                     End {Case It}
                   End; {Case sRAM}
		   PrintLG(U,LG_BASE,'');
	        END; {CASE 'R'}
	   'S': BEGIN						{.CP37}
		   T := '';
		   IF ((sSTp AND $01) = 0) AND ((sSTp AND $16) = 0)
                   THEN T := '+NEAR'
                   ELSE IF  (sSTp AND $10) <> 0 THEN
		            CASE (sSTp AND $60) OF
			      $00: T := '+Method';
                              $20: T := '+Constructor';
			      $40: T := '+Destructor';
			      ELSE T := '+Method?'
		            END;
		   IF (sSTp AND $08) <> 0 THEN T := T + '+EXTERNAL';
		   IF (sSTp AND $01) <> 0 THEN T := T + '+FAR';
		   IF (sSTp AND $02) <> 0 THEN T := T + '+INLINE';
                   IF (sSTp AND $04) <> 0 THEN T := T + '+INTERRUPT';
                   IF (sSTp AND $80) <> 0 THEN T := T + '+ASSEMBLER';
		   IF Length(T) > 0 THEN Delete(T,1,1);
		   PrintSoloByte(U,T);
                   T := 'PMap Flags';
                   If (sSxx AND $04) <> 0 Then
                   	If (sSxx AND $08) <> 0
			Then T := 'DLL ref by NAME'
			Else T := 'DLL ref by INDEX'
		   Else If (sSxx AND $10) <> 0 Then T := 'Dynamic Method';
                   PrintSoloByte(U,T);
		   IF (sSTp AND $02) <> 0  THEN T := 'INLINE Code Bytes'
			                   ELSE T := 'offset in PROC Map';
		   PrintWd(U,T);
		   IF sSPS = 0	THEN T := 'no containing scope'
				ELSE T := 'LL(containing scope)';
		   PrintWd(U,T);
		   IF sSHT = 0	THEN T := 'no local Hash Table'
				ELSE T := 'LL(local scope Hash Table)';
		   PrintWd(U,T);
		   IF sSVM = 0	THEN T := 'Not Used' ELSE
		   If (sSxx AND $10) <> 0
		   THEN T := 'Dynamic Method Index'
                   ELSE T := 'Method Ptr Offset in VMT';
		   PrintWd(U,T);
		   SetCol(1);
	        END; {CASE 'S'}
	 END; {CASE DF OF}
      END; {WITH}

   END;  {PrintDictEntry}

   PROCEDURE PrintTypeEntry;					{.CP53}
   VAR T : TypePtr; D : DNamePtr; I : Integer; W : String[64];

   BEGIN {PrintTypeEntry}
      T := TypePtr(PtrAdjust(U,SurveyWork.LocLL)); I := 0;
      CASE T^.tpTC OF
        $01, $02, $09: I := 2; $04, $05, $07, $08: I := 1;
             $0C..$0F: I := 3; $03: I := 10;  $06: I := 7 + 2*T^.PNPrm;
      END; {CASE}
      W := '';
      IF SurveyWork.LocOwn <> 0
      THEN W := NameOfMethod(U,SurveyWork.LocOwn) ELSE
      IF T^.tpTC = $03 THEN W := NameOfMethod(U,T^.ObjtName);
      IF (W <> '') AND (W <> '???') THEN W := ' For: "' + W + '"';
      PrintTitleBlk('Type Descriptor' + W,I+2);
      WITH T^ DO BEGIN
         PrintBytes(U,2,8);SetCol(TabStop);
         CASE tpTC OF
           $00: W := 'un-typed';  $01: W := 'Array';
           $02: W := 'Record';    $03: W := 'Object';
           $04: W := 'File';      $05: W := 'Text';
           $06: If NilLG(PFRes)
		Then W := 'Procedure'
		Else W := 'Function';
           $07: W := 'Set';
           $08: W := 'Pointer';   $09: W := 'String';
           $0A: CASE tpTQ OF
                  $00: W := 'Single'; $02: W := 'Extended';
		  $04: W := 'Double'; $06: W := 'Comp';
		  ELSE W := '8087-Floating?'
	        END; {CASE tpTQ}
           $0B: W := 'Real';
           $0C: CASE tpTQ OF
		  $00: W := 'un-named byte integer';  $01: W := 'ShortInt';
                  $02: W := 'Byte';      $04: W := 'un-named word integer';
                  $05: W := 'Integer';   $06: W := 'Word';
                  $0C: W := 'un-named DWORD integer';
                  $0D: W := 'LongInt';
                  ELSE W := 'unknown integer type';
                END; {CASE tpTQ}
           $0D: W := 'Boolean';     $0E: W := 'Char';
           $0F: W := 'enumeration';
           ELSE W := 'unknown type code';
         END; {CASE tpTC OF}
         PutTxt('Type='+W);
         PrintWd(U,'Storage Width (bytes)');
         If tpML = 0
           Then If tpTC = $06
                Then PrintWd(U,'NO Next Method')
                Else PrintWd(U,'Usage Unknown')
           Else If tpTC = $06
	   	Then PrintLL(U,'Dict Hdr, Next Method')
		Else PrintWd(U,'Meaning Unknown');
         CASE tpTC OF						{.CP05}
           $01: BEGIN
		   PrintLG(U,LG_BASE,'');
		   PrintLG(U,LG_ARBC,'');
		END;
	   $02: BEGIN						{.CP04}
		   PrintLL(U,'Field List Hash Table');
		   PrintLL(U,'Dict Entry of 1st Field');
		END;
	   $03: BEGIN						{.CP22}
		   PrintLL(U,'Field/Method Hash Table');
		   PrintLL(U,'Field/Method Dictionary');
		   IF NilLG(ObjtOwnr)
			THEN PrintDWd(U,'nothing inherited')
			ELSE PrintLG(U,LG_OBJP,'');
		   PrintWd(U,'Size of VMT (bytes)');
		   IF ObjtDMap = $FFFF
			THEN PrintWd(U,'there is no VMT')
			ELSE PrintWd(U,'DSeg Map Offset of VMT Template');
		   IF ObjtVMTO = $FFFF
			THEN PrintWd(U,'Object has no VIRTUAL Methods')
			ELSE PrintWd(U,'Offset in Object to VMT Pointer');
		   D := AddrDict(U,ObjtName);
		   PrintLL(U,'Dict Entry ('+D^.DSymb+')');
		   IF ObjtDMTp = $FFFF
			Then PrintWd(U,'Object has no DYNAMIC Methods')
			Else PrintWd(U,'DSeg Map Offset of DMT Template');
		   PrintBytes(U,6,8);
                   SetCol(TabStop);
                   PutTxt('Usage Unknown');
		END;
	   $06: BEGIN						{.CP21}
	   	   IF NilLG(PFRes)
		   THEN PrintDWd(U,'Procedures have no Result')
		   ELSE PrintLG(U,LG_FUNR,'');
		   IF PNPrm = 0 THEN PrintWd(U,'no parameter list') ELSE
		   BEGIN
		      Str(PNPrm,W); W := W + ' Formal Parameter';
		      IF PNPrm > 1 THEN W := W + 's';
		      PrintWd(U,W);
		      FOR I := 1 TO PNPrm DO WITH PFPar[I] DO BEGIN
			Str(I,W);
			PrintLG(U,LG_PARM,W);
			IF fPAM = $02
			THEN W := 'Pass VALUE on Stack'
			ELSE IF fPAM = $06
				THEN W := 'Pass ADDRESS on Stack'
				ELSE W := '**** NEW CODE VALUE ***';
			PrintSoloByte(U,W)
		      END; {FOR}
		   END;
		END;  { CASE $06 }
	   $04: PrintLG(U,LG_BASE,' FILE');			{.CP08}
	   $05: PrintLG(U,LG_BASE,' TEXT');
	   $07: PrintLG(U,LG_BASE,' SET');
	   $08: PrintLG(U,LG_BASE,' POINTER');
	   $09: BEGIN
		   PrintLG(U,LG_BASE,'STRING');
		   PrintLG(U,LG_ARBC,'');
		END;
	   $0C..						{.CP12}
	   $0F: BEGIN
	   	   PrintBytes(U,SizeOf(T^.LoBnd),8);
		   SetCol(TabStop);PutTxt('Subrange Lower Bound');
		   PrintBytes(U,SizeOf(T^.HiBnd),8);
		   SetCol(TabStop);PutTxt('Subrange Upper Bound');
		   PrintLG(U,LG_ASGN,'');
	   	END; { $0C,$0D,$0E,$0F}
	 END; {CASE tpTC OF}
      END; {WITH}

   END;  {PrintTypeEntry}

   PROCEDURE PrintHashEntry;					{.CP22}
   VAR H : HashPtr;

	FUNCTION PrintEmptyHash(Bot,Top:Word):Word;
	VAR  I, J, K : Word;
	BEGIN
	   I := Bot;
	   WITH H^ DO REPEAT
	   	IF Slt[I] = 0
		THEN Inc(I)
		ELSE Top := I-1;
	   UNTIL Top < I;
	   K := 0;
	   WITH H^ DO FOR J := Bot TO Top DO BEGIN
	      IF (K AND $3)=0 THEN PrintAddress(NextLL);
	      PutTxt(HexB(LO(Slt[J]))+' ');
	      PutTxt(HexB(HI(Slt[J]))+' ');
	      Inc(NextLL,2);
	      Inc(K);
	   END;
	   PrintEmptyHash := I
	END; {PrintEmptyHash}

   VAR  D : DNamePtr; I, J, K, N : Word; W : String[44];	{.CP26}

   BEGIN {PrintHashEntry}
   	H := AddrHash(U,SurveyWork.LocLL);
	N := H^.Bas DIV 2;
	W := '';
	IF SurveyWork.LocLL = U^.UHIHT
	THEN W := '- INTERFACE Dictionary'	ELSE
	IF SurveyWork.LocLL = U^.UHDHT
	THEN W := '- Turbo DEBUG Dictionary'	ELSE
	IF SurveyWork.LocOwn <> 0
	THEN W := 'Owned By: "'+NameOfMethod(U,SurveyWork.LocOwn)+'"';
	PrintTitleBlk('Hash Table '+W,3);
	PrintWd(U,'Bytes in Hash Table - 2');
	SetCol(1);PutTxt('-----');
	I := 0;

	WITH H^ DO REPEAT
	   IF Slt[I] <> 0 THEN
	   BEGIN
	      PrintLL(U,AddrDict(U,Slt[I])^.DSymb);
	      Inc(I)
	   END ELSE I := PrintEmptyHash(I,N);
	UNTIL I > N;
	NewTxtLine;
   END;  {PrintHashEntry}

   PROCEDURE PrintInLineEntry;					{.CP15}
   VAR D : DNamePtr; S : DStubPtr; I : Integer;  T : TypePtr;

   BEGIN {PrintInLineEntry}
      D := AddrDict(U,SurveyWork.LocOwn);   { Procedure  Header }
      S := AddrStub(D);                     { Procedure  Stub   }
      T := AddrProcType(S);                 { Type Descriptor   }
      WITH SurveyWork, T^ DO BEGIN
	 I := (S^.sSPM+15) SHR 4;
	 PrintTitleBlk('INLINE Code Bytes FOR: "'+
	 		NameOfMethod(U,SurveyWork.LocOwn)+'"',I);
	 PrintBytes(U,S^.sSPM,16);
	 SetCol(1);
      END;
   END;  {PrintInLineEntry}

VAR I: Word; BU: SurveyRec; DoneDict, DoneHash: Boolean; BUL: LL; {.CP30}
BEGIN {FormatDictionary}
	NoteBegin('Formatting Dictionary');
	DoneHash := False; DoneDict := False;
        FetchSurveyRec(SurveyWork);
	WITH SurveyWork DO
	While LocTyp <> cvNULL DO BEGIN
                LastLL := LocNxt;
		BU := SurveyWork;
		IF NextLL < LocLL THEN
		IF NOT DoneHash THEN PrintUnknowns(U,LocLL) ELSE
                IF DoneDict     THEN PrintUnknowns(U,LocLL) ELSE
		BEGIN
			BUL := LastLL;
			LocLL := NextLL; LastLL := BU.LocLL;
			LocOwn := 0; LocTyp := cvType;
			PrintTypeEntry;
			SurveyWork := BU; LastLL := BUL;
		END;
		CASE LocTyp OF
		     cvName: BEGIN PrintDictEntry; DoneDict := True END;
		     cvType: PrintTypeEntry;
		     cvHash: BEGIN PrintHashEntry; DoneHash := True END;
		     cvINLN: PrintInLineEntry;
		END; {CASE}
                FetchSurveyRec(SurveyWork);
	END;   {While}
	IF NextLL < U^.UHPMT THEN PrintUnknowns(U,U^.UHPMT);
	NoteEnd;
END;  {FormatDictionary}

FUNCTION NameOfObject(U: UnitPtr; UsrDE: LL): _LexName;		{.CP15}
VAR D: DNamePtr; T: TypePtr;
BEGIN
   NameOfObject := '???';
   IF UsrDE <> $0000 THEN
   BEGIN
	T  := TypePtr(PtrAdjust(U,UsrDE));	{to Object TD}
	D  := Nil;
	IF T^.tpTC = $03 THEN
	BEGIN
	   D  := DNamePtr(PtrAdjust(U,T^.ObjtName)); {to Object DE}
	   NameOfObject := D^.Dsymb
	END
   END
END;  {NameOfObject}

PROCEDURE CSegHeadings; Far;					{.CP45}
BEGIN
   SetCol(8);
   PutTxt('Entry  Turbo Segmt FixUp Trace :  Load  [Fix-Ups]  Source File');
   SetCol(8);
   PutTxt('Offset Work? Bytes Bytes Entry :  ADDR  1''st  Last For CODE Seg');
   SetCol(8);
   PutTxt('------ ----- ----- ----- ----- : ----- ----- ----- ------------');
END; {CSegHeadings}

PROCEDURE FormatCSegMap(UPt: UnitPtr);

VAR	C: CMapTabPtr; SF: SrcFilePtr;
	OldTabSet, Base, Cx, NMapC : Word;
BEGIN
	NMapC := Upt^.UHTMT - Upt^.UHCMT; Cx := 0;

	IF NMapC > 0 THEN	{ make sure CSeg Map non-empty }
	BEGIN
		NoteBegin('Formatting CSeg Map');
		OldTabSet := TabStop;
		TabStop := 41;
		PrintTitleBlk('CSeg Map Table',7);
		NextLL := Upt^.UHCMT;
		CSegHeadings;  Base := NextLL;
		REPEAT
		   PageOverFlow(6,CSegHeadings);
                   FetchMapRef(Map,rCSEG,Cx);
		   SF := AddrSrcTabOff(UPt,Map.MapSrc);
		   PrintCodeBytes(UPt,8,8,Base,False);
		   SetCol(TabStop);
		   PutTxt(HexA(Map.MapLod+Base_Code)+' ');
		   IF Map.MapFxJ <> 0 THEN With Map Do
		   BEGIN
		      PutTxt(HexA(MapFxI+Base_FixC)+' ');
		      PutTxt(HexA(MapFxJ+MapFxI-SizeOf(FixUpRec)+Base_FixC));
		   END;
		   SetCol(TabStop+18);
		   PutTxt(SF^.SrcName);
		   Inc(Cx,SizeOf(CMapRec));
		UNTIL Cx > NMapC-1;
		TabStop := OldTabSet;
		NoteEnd;
	END;
END;  { FormatCSegMap }

PROCEDURE ProcHeadings; Far;					{.CP55}
BEGIN
  SetCol(8); PutTxt('                         Entry   DLL-Name/');
  SetCol(8); PutTxt('Entry  Turbo PROC  CSeg  Ofset : Jump  Byte Name Of');
  SetCol(8); PutTxt('Offset Work? Flags Map^  /Indx : Addr  Cnt  Procedure');
  SetCol(8); PutTxt('------ ----- ----- ----- ----- : ----- ---- ----------');
END; {ProcHeadings}

PROCEDURE FormatProcMap(UPt: UnitPtr);
VAR Base, I, OldTabStop: Word; W, WB: String[11]; S: DLLPtr; J, K: LongInt;
BEGIN {FormatProcMap}
	IF CountPMapSlots(UPt) > 0 THEN  { Make Sure PROC Map not empty }
	BEGIN
		NoteBegin('Formatting PROC Map');
		FillChar(WB,SizeOf(WB),' ');
                WB[0] := Chr(SizeOf(WB)-1);
		OldTabStop := TabStop;
		TabStop := 41;
		SetCol(1);
		PrintTitleBlk('PROC Map Table',8);
		NextLL := Upt^.UHPMT;
		I := 0; Base := NextLL;
		ProcHeadings;
		REPEAT
		   PageOverFlow(4,PROCHeadings);
                   FetchMapRef(Map,rPROC,I);
		   PrintCodeBytes(UPt,8,8,Base,False);
		   SetCol(TabStop);
                   With Map Do If MapTyp = mfPDLL Then
                   Begin
                      W := WB;
                      S := AddrDLLTabOff(UPt,MapSrc);
                      If S <> Nil Then
                         Move(S^.DLLMod[1],W[1],Ord(S^.DLLMod[0]));
                      PutTxt(W+NameOfMethod(UPt,MapOwn));
                   End Else
                   Begin
                      If MapCSM <> $FFFF Then
		      Begin
                         K := Base_Code + MapEPT;
		         PutTxt(HexA(K)+' ');
		         PutTxt(HexW(MapSiz)+' ');
                      End Else SetCol(TabStop+11);
		      IF MapTyp = mfPRUI THEN
			IF MapCSM = $FFFF
			THEN PutTxt('Not Used (No Unit Init Code)')
			ELSE PutTxt('Unit Init Code')
		      ELSE PutTxt(NameOfMethod(UPt,MapOwn));
                   End;
		   Inc(I,SizeOf(PMapRec));
		UNTIL NextLL >= Upt^.UHCMT;
		TabStop := OldTabStop;
		NoteEnd;
	END;
END; {FormatProcMap}

PROCEDURE CONSTHeadings; Far;					{.CP53}
BEGIN
  SetCol(8); PutTxt('Entry  Turbo Segmt FixUp  VMT  :  Load  [Fix-Ups]');
  SetCol(8); PutTxt('Offset Work? Bytes Bytes Owner :  ADDR  1''st last');
  SetCol(8); PutTxt('------ ----- ----- ----- ----- : ----- ----- -----');
END; {CONSTHeadings}

PROCEDURE FormatTypedConMap(UPt:UnitPtr);
VAR I, J, K : Integer; Sofs, Base : Word;
BEGIN { FormatTypedConMap }
	J := CountDMapSlots(UPt);
	IF J > 0 THEN
	BEGIN
		NoteBegin('Formatting CONST DSeg Map');
		PrintTitleBlk('CONST DSeg Map Table',7);
		K := TabStop;
		TabStop := 59;
		NextLL := Upt^.UHTMT;
		Base := NextLL; Sofs := 0;
		CONSTHeadings;
		FOR I := 0 TO J-1 DO With Map Do
		BEGIN
		   PageOverFlow(7,ConstHeadings);
		   FetchMapRef(Map,rCONS,Sofs);
		   PrintCodeBytes(UPt,8,8,Base,False);
		   PutTxt('  '+HexA(MapLod+Base_Data)+' ');
		   If MapFxJ > 0 Then
		   Begin
		      PutTxt(HexA(MapFxI+Base_FixD)+' ');
		      PutTxt(HexA(MapFxJ+MapFxI+Base_FixD-SizeOf(FixUpRec)));
		   End;
		   SetCol(TabStop);
		   IF (MapTyp = mfTVMT)
		   THEN PutTxt('VMT For: '+NameOfObject(UPt,MapOwn)) ELSE
		   IF (MapTyp = mfTDMT)
		   THEN PutTxt('DMT For: '+NameOfObject(UPt,MapOwn)) ELSE
		   Begin
		      PutTxt('From: ');
		      Case MapTyp Of
			mfXTRN: PutTxt('Linked File');
			mfINTF: PutTxt('_INTERFACE');
			mfIMPL: PutTxt('_IMPLEMENTATION');
			mfNEST: PutTxt('PROC('
				     +NameOfMethod(Upt,MapOwn)+')');
			Else    PutTxt('???');
		      End;
		   End;
		   Inc(Sofs,SizeOf(DMapRec));
		END; { FOR }
		TabStop := K;
		NoteEnd;
	END; { IF }
END;  { FormatTypedConMap }

PROCEDURE VARHeadings; Far;					{.CP42}
BEGIN
	SetCol(8); PutTxt('Entry  Turbo Segmt Usage Usage');
	SetCol(8); PutTxt('Offset Work? Bytes  ???   ??? ');
	SetCol(8); PutTxt('------ ----- ----- ----- -----');
END; {VARHeadings}

PROCEDURE FormatGlobalVarMap(U : UnitPtr);

VAR Base, Sofs, I : Word; SaveTab : Integer;
BEGIN
	IF U^.UHDMT <> U^.UHDLL THEN
	BEGIN
		NoteBegin('Formatting Global VAR Map');
		SaveTab := TabStop;
		TabStop := 41;
		I := 0;
		PrintTitleBlk('Global VAR DSeg Map Table',5);
		VARHeadings;
		NextLL := U^.UHDMT;
		Base := NextLL;
                Sofs := 0;
		WHILE U^.UHDLL > NextLL DO
		BEGIN
			PageOverFlow(5,VARHeadings);
			PrintCodeBytes(U,8,8,Base,False);
			SetCol(TabStop);
                        FetchMapRef(Map,rVARS,Sofs);
                        PutTxt('From: ');
                        Case Map.MapTyp Of
                          mfXTRN: PutTxt('Linked File');
                          mfINTF: PutTxt('_INTERFACE');
                          mfIMPL: PutTxt('_IMPLEMENTATION');
                          Else    PutTxt('???');
                        End;
                        Inc(Sofs,SizeOf(DMapRec));
			Inc(I);
		END;
		NoteEnd;
		TabStop := SaveTab;
	END;
END; {FormatGlobalVarMap}

PROCEDURE FormatDLLList(U: UnitPtr);				{.CP18}
Var P : DLLPtr; Base, I : LL;
Begin
   P := AddrDLLTabOff(U,0);
   If P <> Nil Then
   Begin
	NoteBegin('Formatting DLL List');
        SetCol(1);
        PrintTitleBlk('DLL List',2);
        Base := NextLL;
        While P <> Nil Do With P^ Do Begin
           I := Ord(DLLMod[0]) + SizeOf(DLLWrk) + 1;
           PrintListBytes(U,4,I,13,Base);
           P := AddrNxtDLL(U,P);
        End;
	NoteEnd;
   End;
End; {FormatDLLList}

PROCEDURE FormatUnitDonorList(U : UnitPtr);			{.CP20}
VAR UCP : UDonorPtr; I, J: LL;
BEGIN
	IF U^.UHLSF <> NextLL THEN
	BEGIN
	   NoteBegin('Formatting Donor Unit List');
	   SetCol(1);
	   PrintTitleBlk('Code/Data Donor Unit List',2);
	   UCP := UDonorPtr(PtrAdjust(U,U^.UHLDU));
           With UCP^ Do J := PtrDelta(@UDEnam,UCP);
	   WHILE NextLL < U^.UHLSF DO WITH UCP^ DO BEGIN
	      IF LinesRemaining < 2 THEN NewTxtPage;
              I := J + Ord(UDEnam[0]) + 1;
              PrintListBytes(U,J,I,13,U^.UHLDU);
	      SetCol(1);
	      UCP := UDonorPtr(PtrAdjust(UCP,I));
	   END;
	   NoteEnd;
	END;
END; {FormatUnitDonorList}

PROCEDURE FormatSourceFileList(U : UnitPtr);			{.CP34}
VAR S : SrcFilePtr; SLL: LL; Lines, OldTabStop : Integer;
	FlagCode: String[10]; Stamps : String[22];

BEGIN {FormatSourceFileList}
	NoteBegin('Formatting Source File List');
	OldTabStop := TabStop;
	TabStop := 47;
	PrintTitleBlk('Source File List',5);
	SLL := U^.UHDBT;
	S := SrcFilePtr(PtrAdjust(U,NextLL));
	WHILE SLL <> NextLL DO WITH S^ DO BEGIN
	   Lines := Ord(SrcName[0]) DIV 11 + 2;
	   IF LinesRemaining < Lines THEN NewTxtPage;
	   PrintCodeBytes(U,7,16,U^.UHLSF,False);
           If SrcDate <> 0
	   Then Stamps := ', '	+ FormatDate(SrcDate) + ', '
				+ FormatTime(SrcTime)
           Else Stamps := '';
	   CASE SrcFlag OF
		$03: FlagCode := 'Include ';
		$04: FlagCode := 'Primary ';
                $06: FlagCode := 'Resource ';
		Else FlagCode := 'Linked ';
	   END;   { CASE }
	   SetCol(TabStop);PutTxt(FlagCode+'File'+Stamps);
	   PrintBytesOff(U,1+Ord(SrcName[0]),11,15);
	   SetCol(TabStop);PutTxt('='''+SrcName+'''');
	   SetCol(1);
	   S := AddrNxtSrc(U,S);
	END;
	TabStop := OldTabStop;
	NoteEnd;
END; {FormatSourceFileList}

PROCEDURE FormatTraceTable(U : UnitPtr);			{.CP38}
VAR	T : TraceRecPtr; S,X : String[6]; I,J, Limit : Word;
BEGIN
   T := AddrTraceTab(U);
   IF T <> Nil THEN
   BEGIN
	NoteBegin('Formatting Trace Table');
	SetCol(1);
	Limit := GetTrExecSize(T);
	PrintTitleBlk('Trace Table for Turbo Debugger is Next (LL at 001A)',
			7+(Limit SHR 3));
	WHILE T <> Nil DO WITH T^ DO BEGIN
		Limit := GetTrExecSize(T);
		IF LinesRemaining < (7+Limit SHR 3) THEN NewTxtPage;
		IF TrName <> 0
		THEN PrintLL(U,NameOfMethod(U,TrName))
		ELSE PrintWd(U,'Unit Init Code Block');
		PrintWd(U,'Src File: "' + AddrSrcTabOff(U,TrFill)^.SrcName + '"');
		Str(T^.TrPfx,S);  PrintWd(U,S+' Data bytes precede Code');
		Str(T^.TrBeg,S);  PrintWd(U,'BEGIN Stmt at Line # '+S);
		Str(T^.TrLNos,S); PrintWd(U,S+' Lines of Code to Execute');
		I := 1;
		WHILE I <= Limit DO BEGIN
			J := I + 7;
			IF J > Limit THEN J := Limit;
			Str(I-1+TrBeg,S); Str(J-1+TrBeg,X);
			PrintBytes(U,J+1-I,8);
			SetCol(TabStop);
			PutTxt('Code Bytes in Lines '+S+' Thru '+X);
			NewTxtLine;
			I := J + 1;
		END;
		T := AddrNxtTrace(U,T);
		NewTxtLine;
	END;
	NoteEnd;
   END;
END; {FormatTraceTable}

PROCEDURE FormatEndNonCode(U : UnitPtr);			{.CP05}
BEGIN
	PrintTitleBlk('End Unit Dictionary Area',0);
	BoundaryAlign(U);
END; {FormatEndNonCode}

PROCEDURE FormatObjectCode(UH : UnitPtr);			{.CP09}
VAR
   MyFil, MyTrc: LL; SaveTab: Word;
   CMaps, CXs, I, J: Integer; SF: Byte; SP: SrcFilePtr; R: FixUpPtr;
   UC: Pointer; HexOff, MyOrg, MyEnd: LongInt; PM: MapRefRec;

   PROCEDURE DisplayCode(  U : UnitPtr;	{ Dictionary Pointer }
			Count: Word;	{ Byte Count to Emit }
		       TrcNdx: LL);	{ Trace Entry Index  }

	PROCEDURE DisplayCodeLine(VAR P : ObjArg);		{.CP19}
        Var I: Word; T: String;
	BEGIN
	   WITH P DO WHILE Lim > 0 DO BEGIN
	      UnAssemble(UC,P);
	      NextLL := Locn+OffsetLL;
	      PrintOffset(HexOff);
              FillChar(T[1],SizeOf(T)-1,' ');
              T[0] := Chr(15-ColumnsUsed-1);
              T := T + Code;
              T[0] := Chr(38-ColumnsUsed-1);
              T := T + Mnem;
              T[0] := Chr(50-ColumnsUsed-1);
              IF Length(Opr1) > 0 THEN T := T + Opr1;
	      IF Length(Opr2) > 0 THEN
	      If Length(Opr1) > 0 Then T := T +','+Opr2 Else T := T + Opr2;
	      IF Length(Opr3) > 0 THEN
	      BEGIN
	         IF Opr3[1] <> ';' THEN T := T + ';'
		 		   ELSE T := T + ' ';
		 T := T + Opr3;
	      END;
              TrimString(T); 		{ Removes trailing blanks }
              PutTxt(T);
	      NewTxtLine;
	   END;
	END;	{DisplayCodeLine}

   VAR P: ObjArg; I, J, K, L: Word; Limit, IP: LongInt;		{.CP42}
       T: TraceRecPtr; S: String[6];
   BEGIN   {DisplayCode}
      IF Count > 0 THEN
      BEGIN
         Limit := Count;
	 IP  := NextLL;
	 P.TCpu := CPUType;
	 T := AddrTraceTab(U);
         J := IP - OffsetLL;
         P.CBase := Base_Code;
	 P.Obj := J;
	 IF (T = Nil) OR (TrcNdx = $FFFF) THEN
	 BEGIN
	    P.Lim := Limit;
	    DisplayCodeLine(P);
	 END ELSE
	 BEGIN
	    T := Ptr(Seg(T^),Ofs(T^)+TrcNdx);
	    L := T^.TrBeg;
	    K := GetTrExecSize(T);
	    I := 1;
	    WHILE I <= K DO BEGIN
		IF T^.TrExec[I] = $80 THEN Inc(I);
		P.Lim := T^.TrExec[I];
		IF P.Lim > 0 THEN
		BEGIN
		   PutTxt('; ---------> Code From Line: ');
		   Str(L,S);
		   PutTxt(S);
		   IF I = 1 THEN PutTxt('  ("BEGIN/ASM" Statement)') ELSE
		   IF I = K THEN PutTxt('  ("END" Statement)');
		   NewTxtLine;
		   DisplayCodeLine(P);
		END;
		Inc(L); Inc(I);
	    END;
	 END;
	 Inc(IP,P.Obj - J);
	 NextLL := IP;
      END;
   END; {DisplayCode}

   PROCEDURE UnAssembleCode(Hash: LL;	   { Owner }		{.CP38}
			      SF: Byte;    { Source Flag }
			     Org,          { Entry Point }
			   Limit: LongInt; { Next Entry  }
			  TrcNdx: LL;      { to Trace Entry }
			 Comment: Boolean; { Explanations   }
			      MT:MapFlags);{ Type of MapRef }
   VAR Stopper : LongInt;
   BEGIN
      IF LinesRemaining < 4 THEN NewTxtPage;
      Stopper := Limit - Org;				{ Byte Count }
      IF (NextLL - OffsetLL) > Org
      THEN Stopper := Limit + OffsetLL - NextLL;	{ Safety Valve }
      IF Stopper > 0 THEN
      BEGIN
	IF Comment THEN {Allow Remarks}
	BEGIN
	   SetCol(7); PutTxt('Code For ');
	   IF SF < $05
	   THEN
	     IF (Hash <> $FFFF) AND (Hash <> 0)
	     THEN PutTxt('PROC "'+NameOfMethod(UH,Hash)+'"')
	     ELSE If MT = mfPRUI
	          Then PutTxt('Unit Initialization')
                  Else PutTxt('Implementation PROC')
	   ELSE
	     IF (Hash <> $FFFF) AND (Hash <> 0)
	     THEN PutTxt('PUBLIC "'+NameOfMethod(UH,Hash)+'"')
	     ELSE PutTxt('PRIVATE or Un-named PUBLIC');
	   PutTxt(' starts at '+HexA(NextLL));
	   NewTxtLine;NewTxtLine;
	END;
	IF DisAssembly
	THEN DisplayCode(UH,Stopper,TrcNdx)
	ELSE Begin
	  PrintCodeBytes(UC,Stopper,16,HexOff,True);
          NewTxtLine
        End;
	NewTxtLine;
      END;
   END;  {UnAssembleCode}

   PROCEDURE UnAssembleData(S: MapRefRec; SF: Byte);		{.CP13}
   Var SectOff: WORD;
   BEGIN
     SetCol(7);
     IF SF < $05
     THEN PutTxt('(Preamble Data Begins at ')
     ELSE PutTxt('(PRIVATE Code or Data Begins at ');
     PutTxt(HexW(NextLL)+')');
     NewTxtLine;NewTxtLine;
     SectOff := NextLL - OffsetLL;
     IF SF < $05
     THEN Begin
       PrintCodeBytes(UC,S.MapEPT-SectOff,16,HexOff,True);
       NewTxtLine End
     ELSE UnAssembleCode(S.MapOwn,SF,NextLL,OffsetLL+S.MapEPT,$FFFF,False,S.MapTyp);
     NewTxtLine;
   END;  {UnAssembleData}

BEGIN  {FormatObjectCode}					{.CP55}
   IF UH^.UHCMT < UH^.UHTMT THEN  { We have Code Segments }
   BEGIN
      NoteBegin('Formatting CODE Segments');
      SaveTab := TabStop;
      TabStop := 57;
      R := AddrCodeFixUps(UH);
      UC := AddrCodeArea(UH);
      OffsetLL := Base_Code;
      PrintTitleBlk('Unit Code Group',0);
      CMaps := CountCMapSlots(UH)  *SizeOf(CMapRec);   { Code Segments }
      CXs := (CountPMapSlots(UH)-1)*SizeOf(PMapRec);
      SortProcRefs(CSegOrder);
      FetchMapRef(Map,rPROC,CXs);
      IF (Map.MapEPT = $FFFF)        { remove unused init proc	}
      THEN Dec(CXs,SizeOf(PMapRec));
      I := 0;                        { Track PMRefs Table	}
      J := 0;                        { Track CSeg Map Table	}

      REPEAT
         FetchMapRef(Map,rCSEG,J);		{ Fetch CSeg Map Ref }
         FetchMapRef(PM,rPROC,I);		{ Fetch PROC Map Ref }
	 WHILE PM.MapCSM < J DO Begin		{ Synchronize Maps   }
            Inc(I,SizeOf(PMapRec));
            FetchMapRef(PM,rPROC,I);
         End;
	 MyOrg := Map.MapLod + Base_Code;	{ Segment Load Point }
	 MyEnd := MyOrg + Map.MapSiz;		{ Next Segment Start }
	 MyFil := Map.MapSrc;			{ Segment Source Fil }
	 MyTrc := AddrCMapTab(UH)^[PM.MapCSM DIV SizeOf(CMapRec)].CsegTrc;
	 SP := AddrSrcTabOff(UH,MyFil);
	 IF LinesRemaining < 6 THEN NewTxtPage;
	 PutTxt('----  Code Map[+'+HexW(PM.MapCSM)
	 	 +'] Segment at '+HexA(NextLL)+' Found In "');
	 PutTxt(SP^.SrcName+'"');
	 NewTxtLine; NewTxtLine;
	 HexOff := NextLL;
	 SF := SP^.SrcFlag;
	 IF (PM.MapEPT + OffsetLL) > NextLL
	 THEN UnAssembleData(PM,SF);
	 WHILE (I <= CXs) AND (PM.MapCSM = J) DO BEGIN
         WITH PM DO
	    UnAssembleCode(MapOwn,SF,MapEPT,MapEPT+MapSiz,MyTrc,True,MapTyp);
	    Inc(I,SizeOf(PMapRec));
            FetchMapRef(PM,rPROC,I);
	 END;
	 Inc(J,SizeOf(CMapRec));
      UNTIL (J >= CMaps);

      TabStop := SaveTab;
      PrintTitleBlk('End Code Group',0);
      BoundaryAlign(UC);
      NoteEnd;
   END;
END; {FormatObjectCode}

PROCEDURE FormatDataAreas(UH : UnitPtr);			{.CP46}
VAR	PD: DMapTabPtr; SaveTab: Word; T: TypePtr;
	I, MapEnd,Base: Word; EndLL: LL; UD: Pointer; S: MapRefRec;
BEGIN
   EndLL := NextLL + UH^.UHZDT;
   IF EndLL <> NextLL THEN
   BEGIN
      NoteBegin('Formatting CONST Data Segments');
      SaveTab := TabStop;
      PrintTitleBlk('Typed CONST Data Group',5);
      WITH UH^ DO MapEnd := (UHDMT-UHTMT) DIV SizeOf(DMapRec);
      PD := AddrDMapTab(UH); UD := AddrDataArea(UH);
      OffsetLL := Base_Data;
      FOR I := 0 TO MapEnd-1 DO WITH PD^[I] DO BEGIN
	 NewTxtLine;
	 SetCol(7);
         FetchMapRef(S,rCONS,SizeOf(DMapRec)*I);
         PutTxt('Typed CONST''s Map[+'+HexW(I*SizeOf(DMapRec))+'] ');
	 IF DSegOwn <> 0 THEN
	 BEGIN
	    T := TypePtr(PtrAdjust(UH,DSegOwn));
	    If S.MapTyp = mfTVMT Then PutTxt('VMT Template for "')
            			 Else PutTxt('DMT Template for "');
	    PutTxt(AddrDict(UH,T^.ObjtName)^.DSymb+'"');
	 END ELSE
         Begin
            PutTxt('From: ');
            Case S.MapTyp Of
               mfXTRN: PutTxt('Linked File');
               mfINTF: PutTxt('_INTERFACE');
               mfIMPL: PutTxt('_IMPLEMENTATION');
               mfNEST: PutTxt('PROC('+NameOfMethod(UH,S.MapOwn)+')');
               Else    PutTxt('???');
            End;
         End;
	 Base := NextLL;
	 SetCol(1);NewTxtLine;
	 PrintCodeBytes(UD,DSegCnt,16,Base,True);
	 SetCol(1);
      END; {FOR}
      PrintTitleBlk('End Typed CONST Data Group',0);
      TabStop := SaveTab;
      NoteEnd;
   END; {IF}
   BoundaryAlign(UD);
END; {FormatDataAreas}

PROCEDURE FixUpHeadings; Far;					{.CP06}
BEGIN
   SetCol(7); PutTxt('Un Fl  Map  E-Adr Patch : Ptch Type Refers');
   SetCol(7); PutTxt('it ag Ofset Ofset Ofset : Size  Map To Unit');
   SetCol(7); PutTxt('-- -- ----- ----- ----- : ---- ---- --------');
END; {FixUpHeadings}

PROCEDURE FormatFixUpList(UH : UnitPtr);			{.CP03}
TYPE Remark = String[8]; T4 = String[4]; T8 = String[8];
Var  RB: Pointer;

	PROCEDURE FixUpIdentify(	R : FixUpRec;		{.CP31}
				VAR S2, S1 : T4; VAR S3 : T8);
	VAR PU : UDonorPtr;
	BEGIN  {FixUpIdentify}
           If (R.FixFlg <> $FF) AND (R.FixDnr <> $FF) Then
           Begin
	      CASE (R.FixFlg SHR 6) AND $3 OF
	   	0: S1 := 'PROC';	1: S1 := 'CSeg';
		2: S1 := 'DATA';	3: S1 := 'CONS';
	      END;
	      CASE (R.FixFlg SHR 4) AND $3 OF
	   	0: S2 := 'WORD';	1: S2 := 'WD+E';
		2: S2 := 'SEG ';	3: S2 := 'FPTR';
	      END;
	      IF (R.FixFlg AND $F) <> 0 THEN
	      BEGIN S1 := '??? ';	S2 := '????';  END;
	      PU := UDonorPtr(PtrAdjust(UH,UH^.UHLDU+R.FixDnr));
	      S3 := PU^.UDENam;
           End Else
           Begin
	      S1 := 'CSeg'; S2 := 'EM87';
              Case R.FixWd1 Of
                 2:   S3 := '(SEGSS)';
                 3:   S3 := '(SEGCS)';
                 4:   S3 := '(SEGES)';
                 5:   S3 := '';
                 6:   S3 := '(FWAIT)';
                 Else S3 := '?';
              End;
           End;
	END;   {FixUpIdentify}

        PROCEDURE PrintFixEntry(RR: FixUpRec);			{.CP10}
        Var S1, S2: T4; S3: T8;
        Begin
	   PageOverFlow(2,FixUpHeadings);
	   PrintBytes(RB,8,8);
	   FixUpIdentify(RR,S1,S2,S3);
	   SetCol(TabStop);   PutTxt(S1);
	   SetCol(TabStop+5); PutTxt(S2);
	   SetCol(TabStop+10);PutTxt(S3);
        End; {PrintFixEntry}

VAR  R: FixUpPtr; T: TypePtr; PU: UDonorPtr; S: MapRefRec;	{.CP43}
     RR: FixUpRecPtr; EndS, EndLL: LongInt; S1, S2: T4; S3: T8;
     I, J, K, MapEnd: Word; SaveTab: Word; OV: HeadProc;
BEGIN
   NoteBegin('Formatting Fix-Up Lists');
   SaveTab := TabStop;
   TabStop := 33;
   EndLL := NextLL + UH^.UHZFA;
   IF EndLL <> NextLL THEN WITH UH^ DO
   BEGIN
      PrintTitleBlk('Code Group Fix-Ups',7);
      SetCol(1);
      J := 0;
      RB := AddrCodeFixUps(UH);
      OffsetLL := Base_FixC;
      IF UHCMT < UHTMT THEN
      BEGIN
         MapEnd := UHTMT-UHCMT; I := 0;
	 While I < MapEnd DO Begin
            FetchMapRef(Map,rCSEG,I);
	    With Map Do IF MapFxJ <> 0 THEN
	    BEGIN
	       SetCol(1);
	       IF LinesRemaining < 9 THEN NewTxtPage
	       			     ELSE NewTxtLine;
	       SetCol(7);
	       EndS := MapLod+Base_Code;
	       PutTxt('Segment Load Addr = ' + HexA(EndS));
               SetCol(7);
	       EndS := EndS + MapSiz;
	       PutTxt('Fix-Up''s For CSeg Map Entry at ' + HexA(I + UHCMT));
	       SetCol(1);NewTxtLine;
	       FixUpHeadings;
               K := MapFxI;
	       While K < (MapFxJ+MapFxI) DO BEGIN
                  RR := PtrAdjust(RB,K);
                  PrintFixEntry(RR^);
		  Inc(K,SizeOf(FixUpRec));
	       END; {While}
            End; {IF}
            Inc(I,SizeOf(CMapRec));
      	 END;  {While}
         PrintTitleBlk('End Code Group Fix-Ups',0);
	 BoundaryAlign(RB);
      END;   { IF CSeg Map non-Empty }
                                                                    
      If UH^.UHZFT > 0 Then Begin
        PrintTitleBlk('CONST Data Group Fix-Ups',7);
      IF UHTMT < UHDMT THEN	{DSeg Map non-Empty}		{.CP56}
      BEGIN
	K := NextLL;
	MapEnd := UHDMT-UHTMT;
	EndS := 0;
        I := 0; RB := AddrDataFixUps(UH);
	OffsetLL := Base_FixD;
	With Map Do While I < MapEnd DO Begin
           FetchMapRef(Map,rCONS,I);
	   IF MapFxJ <> 0 THEN
	   BEGIN
	      SetCol(1);
	      IF LinesRemaining < 9 THEN NewTxtPage
	      			    ELSE NewTxtLine;
	      SetCol(7);
              If MapTyp = mfTVMT
	      THEN PutTxt('VMT Fix-Up''s For: '+NameOfObject(UH,MapOwn)) Else
              If MapTyp = mfTDMT
	      THEN PutTxt('DMT Fix-Up''s For: '+NameOfObject(UH,MapOwn))
              Else Begin
                PutTxt('Typed CONST Fix-Up''s for: ');
                Case MapTyp Of
                   mfXTRN: PutTxt('Linked File');
                   mfINTF: PutTxt('_INTERFACE');
                   mfIMPL: PutTxt('_IMPLEMENTATION');
                   mfNEST: PutTxt('PROC('+NameOfMethod(UH,MapOwn)+')');
                   Else    PutTxt('???');
                End {case}
              End;
              NewTxtLine;NewTxtLine;
              EndS := MapLod+Base_Data;
	      PutTxt('Seg Load Addr = ' + HexA(EndS) + ' --');
              Inc(EndS,MapSiz);
	      PutTxt(' CONST DSeg Map Entry at '+ HexW(I+UHTMT));
	      SetCol(1);NewTxtLine;
	      FixUpHeadings;
	      K := MapFxI;
	      WHILE K < (MapFxJ+MapFxI) DO BEGIN
                 RR := PtrAdjust(RB,K);
                 PrintFixEntry(RR^);
		 Inc(K,SizeOf(FixUpRec));
	      END; {WHILE}
	   END; {If Fixups to print}
           Inc(I,SizeOf(DMapRec));
        End; {While}
      END;   { IF DSeg Map non-Empty }
      PrintTitleBlk('End CONST Data Group Fix-Ups',0);
      BoundaryAlign(UnitPtr(RB));
   END;   {IF FixUp List non-Empty}
   End;
   TabStop := SaveTab;
   NoteEnd;
END; {FormatFixUpList}

PROCEDURE DocumentUnit(P : UnitPtr);				{.CP17}
BEGIN
	FormatHeader(P);
	FormatDictionary(P);		{ PRINT the Dictionary     }
	FormatProcMap(P);               { PRINT the PROC Map       }
	FormatCSegMap(P);               { PRINT the CSeg Map       }
	FormatTypedConMap(P);		{ PRINT the CONST Map      }
	FormatGlobalVarMap(P);		{ PRINT the VAR Map        }
        FormatDLLList(P);		{ PRINT the DLL List	   }
	FormatUnitDonorList(P);		{ PRINT the Donor Unit Tab }
	FormatSourceFileList(P);	{ PRINT the Source Files   }
	FormatTraceTable(P);		{ PRINT the Trace Table    }
	FormatEndNonCode(P);		{ PRINT separator          }
	FormatObjectCode(P);		{ PRINT CODE Segments      }
	FormatDataAreas(P);		{ PRINT CONST Segment Data }
	FormatFixUpList(P);		{ PRINT LINKER FixUp Data  }
        PrintTitleBlk('End Unit',0);
END; {DocumentUnit}

VAR i,j : integer; P: UnitPtr; Module: String[8]; c: char;	{.CP63}
    K: LongInt;   NS: String[5]; 

BEGIN       { Main Program }
	ClrScr;
	Write('Enter Name of Unit to Document: ');ReadLn(Module);
	Write('Is Unit for WINDOWS or DOS? [W|D] ');
	i := WhereX; j := WhereY;
	REPEAT
		GoToXY(i,j);ClrEol;
		ReadLn(c);
	UNTIL UpCase(c) IN ['W','D'];
	If UpCase(c) = 'W' Then _Lib_Nam := _Win_Lib
			   Else _Lib_Nam := _Dos_Lib;
	Write('Do You Want Dis-Assembly of Code? [Y|N] ');
	i := WhereX; j := WhereY;
	REPEAT
		GoToXY(i,j);ClrEol;
		ReadLn(c);
	UNTIL UpCase(c) IN ['Y','N'];
	DisAssembly := UpCase(c) = 'Y';
	i := WhereX; j := WhereY;
        IF DisAssembly Then Begin
	  Write('What CPU? (0=8086,1=80186,2=80286,3=80386) ');
	  i := WhereX; j := WhereY;
	   REPEAT
		GoToXY(i,j);ClrEol;
		ReadLn(c);
	   UNTIL c IN ['0'..'3'];
	   Case C Of '0': CPUType := C086; '1': CPUType := C186;
           	     '2': CPUType := C286; '3': CPUType := C386;
           End; {Case}
        End;
	FOR I := 1 TO Length(Module) DO Module[I] := UpCase(Module[I]);
	TabStop := 37;
        OffsetLL := 0;
	OpenTxt(Module+'.LST',59,80);
        NoteBegin(''); JobTime := NoteTime;
        NoteBegin('Starting Analysis of "'+Module+'"');
        Win := _Lib_Nam = _Win_Lib;
        PutTxt('Analysis of '+ Module +'.TPU');
        NewTxtLine;
        PutTxt('Assumed Compiled by TURBO PASCAL for ');
        If Win 	Then PutTxt('WINDOWS (Ver. 1.0)')
		Else PutTxt('DOS (Ver. 6.0)');
        NewTxtLine;
	PutTxt('========================================='); NewTxtLine;
        P := AnalyzeUnit(Module,'');
        NoteEnd;
	IF P <> Nil THEN
	BEGIN
	   PutTxt('========================================='); NewTxtLine;
	   PutTxt('* Dictionary Area begins with Unit Header'); NewTxtLine;
	   PutTxt('========================================='); NewTxtLine;
	   NextLL := 0;
	   DocumentUnit(P); NewTxtPage;
	END ELSE
        BEGIN
           WriteLn; WriteLn('Unit "',module,'" Not Found!'); WriteLn;
        End;
        PutTxt('Heap Utilization Summary');NewTxtLine;
        K := PtrDelta(HeapEnd,HeapOrg);
	Str(K/1024.0:5:1, NS);
        NewTxtLine; PutTxt(NS+' Kb Available at Start');
        K := PtrDelta(_HeapHighWaterMark,_HeapOriginalMark);
	Str(K/1024.0:5:1, NS);
        NewTxtLine; PutTxt(NS+' Kb used during Analyses');
        K := PtrDelta(HeapPtr,HeapOrg);
	Str(K/1024.0:5:1, NS);
        NewTxtLine; PutTxt(NS+' Kb in use during print');
        PurgeAllUnits;
        NewTxtLine; PutTxt('---- End Report');
        NewTxtPage;
	CloseTxt;
        NoteBegin('');
        Write('End of Job');
        NoteTime := JobTime;
        NoteEnd;
        GotoXY(NoteX,NoteY+2);
END.