#!/usr/local/bin/perl
#
# Trace  - searches program path stored in the debug printout
# which x64/x128 generates.
#
#	V1.2 - 18 Jun 1996
#	  Reduce repeated loops
#	  Remove CR characters
#	  Use TAB's whenever possible
#	  Added output redirection to file
#	  Initial level for indent as option
#	  Notify mode changes
#
#	V1.1 - ??
#	  Command line options
#
#	V1.0 - 12 Dec 1994
#	  Indent debug log file
#
#
# To clear out duplicate lines from the input stream, use e.g. 'grep "\."'
# or 'grep -v "\."'. If necessary, the output from 'trace' can be filtered
# by 'unic -c'.
#
# Written by
#   Jouko Valta  (jopi@stekt.oulu.fi)
#


# Default Values

$USAGE = "\n  trace  [-long | -short] [-tab <n>]  file [outfile]\n\n";
$INDENTLEVEL = 0;
$INDENTDEPTH = 4;
$SUBTRACE = 0;
$p_long = 1;			# list everything by default


 opt: while ($_ = $ARGV[0], /^-/) {
	shift;
	if (/^-l/)	{ $p_long++;   next opt; }	# list everything
	if (/^-s/)	{ $p_long = 0; next opt; }	# list branches only
	if (/^-S/)	{ $SUBTRACE++; next opt; }	# subroutines in loops
	if (/^-i/|/^-t/){ $INDENTDEPTH = $ARGV[0]; shift; next opt; }
	if (/^-I/)	{ $INDENTLEVEL = $ARGV[0]; shift; next opt; }
	if (/^-/)	{ printf STDERR $USAGE; die "Stopped"; }
    }


$, = ' ';			# set output field separator
$\ = "\n";			# set output record separator

if ($INDENTDEPTH >= 8) {
    $INDENTDEPTH = 8;
}

$tab = $INDENTLEVEL * $INDENTDEPTH;
$lefthits = 0;

# Process file

$BackStart = 0;
$BackLoop = 0;


if ($ARGV[0] && $ARGV[0] ne "-") {
    open (I, "tr -d '\015' < $ARGV[0] |") || die "Cannot open input file '$ARGV[0]'";
}
else {
    open (I, "tr -d '\015' - |");
}

if ($ARGV[1]) {
    open (O, ">$ARGV[1]") || die "Cannot open output file '$ARGV[1]'";
    select (O);
}


while (<I>) {
    chop;

#    if (/^$/) {
#	next;						# empty line
#    }


    # Re-excute a loop
    # Lines from long output are ignored (printed). Loop tracing works, though.

    if ($BackStart && /\.(\S+)/) {
	if (/JSR/) {		# JSR breaks the loop if not filtered out...
	    ++$INDENTLEVEL;
	    $tab += $INDENTDEPTH;

	    if ($SUBTRACE) {
		print "\t" x ($tab/8), ' ' x ($tab%8), $_;	# print indent
	    }
	    next;
	}

	if ($BackStart le $1 && $BackLoop ge $1) {	# still in loop
	    ++$BackCnt;

	    if ($BackLoop eq $1) {			# loop condition
		++$LoopCnt;
	    }
	    next;
	}

	if (!$SUBTRACE && ($INDENTLEVEL > $BackLev)) {	# kludge subroutines
	    ++$BackCnt;
	if (!/RTS/) {
	    next;
	}
	}

	if ($LoopCnt) {
	    print "\n  $BackLoop: Loop repeated $LoopCnt times. ($BackCnt opcodes)\n\n";
	}
	$BackStart = 0;
	$BackLoop = 0;
	# Continue to process the command.
    }


    # Special Commands

    if (/BPL|BMI|BV|BCC|BCS|BNE|BEQ/) {
	if (/\.(\S+)\s+\S+\s+\$(\S+)/ && hex($1) > hex($2)) {	# repeat ?
	    $BackLoop = $1;				# yes, mark the loop
	    $BackStart = $2;
	    $BackStart =~ tr/A-F/a-f/;
	    $BackCnt = 0;
	    $LoopCnt = 0;
	    $BackLev = $INDENTLEVEL;			# kludge subroutines
	}
	print "\t" x ($tab/8), ' ' x ($tab%8), $_;	# print indent
	next;
    }

    elsif (/BRK|RTI|JMP/) {
	print "\t" x ($tab/8), ' ' x ($tab%8), "$_\n";	# print indent
	next;
    }

    elsif (/JSR/) {
	print "\t" x ($tab/8), ' ' x ($tab%8), $_;	# print indent
	++$INDENTLEVEL;
	$tab += $INDENTDEPTH;
	next;
    }

    elsif (/RTS/) {
	if ($INDENTLEVEL <= 0) {
	    $INDENTLEVEL = 0;
	    print  $_ . "***\n\n";			# out of TABs
	    ++$lefthits;
	}
	else {
	    print "\t" x ($tab/8), ' ' x ($tab%8), "$_\n";	# print indent
	    $tab -= $INDENTDEPTH;
	    --$INDENTLEVEL;
	}
        next;
    }


    # Mode Control Commands

    elsif (/SED/) { print "\n  MODE: Set DECIMAL mode.\n\n"; }
    elsif (/CLD/) { print "\n  MODE: Set Binary mode.\n\n"; }
    elsif (/SEI/) { print "\n  MODE: Disable Interrupts.\n\n"; }
    elsif (/CLI/) { print "\n  MODE: Enable Interrupts.\n\n"; }


    # Printing lines that do not branch may be suppressed by option

    elsif (/ANC|ANE|ARR|ASR|DCP|ISB|JAM|LAS|LAX|LXA|NOOP|RLA|RRA|SAX|USBC|SBX|SHA|SHS|SHX|SHY|SLO|SRE/) {
	print "\t" x ($tab/8), ' ' x ($tab%8), $_, "\t; Undocumented";;	# print indent
	next;
    }

    $p_long && print "\t" x ($tab/8), ' ' x ($tab%8), $_;	# print indent
}

$lefthits && print STDERR "Suggest  trace -I $lefthits\n";

