#!/bin/awk -f
#
#	stabn -- add line number information to assembly source
#
#	cannot handle lines with both comment styles (/**/ and !).
#	assumes that the file will be run through cpp later.
#

# initialize line number and give cpp the filename
#	field separator '!' strips off !-style comments
#	lnum keeps track of the line number
#	inone is true when inside ENTRY...EXIT statements
#	intext is true when text segment is the current segment
#	left is the left side of the output line
#	right is the right side of the output line
#
#	start by spewing out the symbol-table entry containing the
#	current working directory and the filename.  awk cannot get
#	the current working directory (until the SysVr3 version) so
#	we send a command to /bin/csh to get it for us.
#	XXX but sometimes the csh command takes too long, messing up the output
BEGIN {
#	printf "echo '\t'.stabs '\"'$cwd/'\"',0144,0,0,LL0;\n" | "/bin/csh -f"
	printf "\t.stabs \"%s\",0x64,0,0,LL0;\n", substr(FILENAME, 1, 256);
	printf "\t.stabs \"int:t(0,1)=r(0,1);-2147483648;2147483647;\""
	printf ",0x80,0,0,0;\n"
	printf "LL0:\n"
	FS="!";
	lnum=0;
	isascii=0;
	inone=0;
	force=0;
	inpp=0;
	intext=1;
	left=sprintf "#line 1 \"%s\"", substr(FILENAME, 1, 256);
	right=""
}

# check for blank lines
/^[ 	]*$/		{ inpp=0; if (!force) right="" }
/^[ 	]*!.*$/		{ inpp=0; if (!force) right="" }

# check for assembler directives
/^[ 	]*\./		{ right="" }

# check for .ascii strings with embeded exclamation points
/^[ 	].*.ascii[ 	][ 	]*".*!.*"/ {isascii=NF; }
# check for preprocessor directives with embedded exclamation points
/^[ \t]*#[ \t]*if.*!.*/ {isascii=NF; }
/^[ \t]*#[ \t]*endif.*!.*/ {isascii=NF; }
/^[ \t]*#[ \t]*else.*!.*/ {isascii=NF; }

# if end of a function then stop generating stab entries
/^[ 	]*EXIT/		{ right=""; inone=0 }

# check for change out of text segment
/^[ 	]*\.seg[ 	]*"[Dd][Aa][Tt][Aa]"/	{ intext=0 }
/^[ 	]*\.seg[ 	]*"data1"/		{ intext=0 }
/^[ 	]*\.seg[ 	]*"bss"/		{ intext=0 }

# emit the last line
{
	printf "%s%s\n", left, right;
	lnum++;
	left=$1;
# if we saw a .ascii string with '!' characters embedded, concatenate
# all the fields to rebuild current output line
	for (i = 2; i <= isascii; i++) left = left "!" $i
	isascii=0;
	force=0;
}

# check for delay slot
						{ skipthis=0 }
/^[ 	]*call/					{ skipthis=1 }
/^[ 	]*[a-zA-Z0-9_][a-zA-Z0-9_]*:[ 	]*call/	{ skipthis=1 }
/^[ 	]*jmp/					{ skipthis=1 }
/^[ 	]*[a-zA-Z0-9_][a-zA-Z0-9_]*:[ 	]*jmp/	{ skipthis=1 }
/^[ 	]*fb/					{ skipthis=1 }
/^[ 	]*[a-zA-Z0-9_][a-zA-Z0-9_]*:[ 	]*fb/	{ skipthis=1 }
/^[ 	]*cb/					{ skipthis=1 }
/^[ 	]*[a-zA-Z0-9_][a-zA-Z0-9_]*:[ 	]*cb/	{ skipthis=1 }
/^[ 	]*ret/					{ skipthis=1 }
/^[ 	]*[a-zA-Z0-9_][a-zA-Z0-9_]*:[ 	]*ret/	{ skipthis=1 }

# check for preprocessor lines
/^#/						{ inpp=1 }
inpp==1						{ skipthis=1 }
/[^\\]$/					{ inpp=0 }

# all other branch instructions start with 'b':
/^[ 	]*b/					{ skipthis=1 }
/^[ 	]*[a-zA-Z0-9_][a-zA-Z0-9_]*:[ 	]*b/	{ skipthis=1 }

# but there are four pseudo-instructions that start with 'b':
/^[ 	]*btst[ 	]/			{ skipthis=0 }
/^[ 	]*bset[ 	]/			{ skipthis=0 }
/^[ 	]*bclr[ 	]/			{ skipthis=0 }
/^[ 	]*btog[ 	]/			{ skipthis=0 }

# this is to avoid the problem when a '!' is imbedded in a C comment
/!.*\*\// {
	if (inone) {
		printf "*/\n#HELP -- line mixes bang with C comment\n";
		printf "stabn: Comment Error line %d\n", lnum >> "/dev/tty"
	}
}

# if inside a function, emit the stab entry for the next line
{
	if (NF && inone && intext && (!skipthis)) {
		if (length($1))
			right=sprintf\
				"; .stabn 0x44,0,__LINE__+1,Line%d; Line%d:;",\
					lnum, lnum;
	} else
		right=""
}

# if beginning of a function then start generating stab entries
/^[ 	]*ENTRY2\(/	{
	inone=1;
	force=1;
	namestart=substr(left, index(left, "ENTRY2") + 6, 256);
	name=substr(namestart, 2, index(namestart, ",") - 2);
	right=sprintf "; .stabs \"%s:F(0,1)\",0x24,0,4,_%s; .stabn 0x44,0,__LINE__,_%s; .stabn 0x44,0,__LINE__+1,Line%d; Line%d:", name, name, name, lnum, lnum;
}
/^[ 	]*ENTRY\(/	{
	inone=1;
	force=1;
	namestart=substr(left, index(left, "ENTRY") + 5, 256);
	name=substr(namestart, 2, index(namestart, ")") - 2);
	right=sprintf "; .stabs \"%s:F(0,1)\",0x24,0,4,_%s; .stabn 0x44,0,__LINE__,_%s; .stabn 0x44,0,__LINE__+1,Line%d; Line%d:", name, name, name, lnum, lnum;
}
/^[ 	]*RTENTRY\(/	{
	inone=1;
	force=1;
	namestart=substr(left, index(left, "RTENTRY") + 7, 256);
	name=substr(namestart, 2, index(namestart, ")") - 2);
	right=sprintf "; .stabs \"%s:F(0,1)\",0x24,0,4,%s; .stabn 0x44,0,__LINE__,%s; .stabn 0x44,0,__LINE__+1,Line%d; Line%d:", name, name, name, lnum, lnum;
}
/^[ 	]*ZENTRY2\(/	{
	inone=1;
	force=1;
	namestart=substr(left, index(left, "ZENTRY2") + 7, 256);
	name=substr(namestart, 2, index(namestart, ",") - 2);
	right=sprintf "; .stabs \"%s:F(0,1)\",0x24,0,4,_%s; .stabn 0x44,0,__LINE__,_%s; .stabn 0x44,0,__LINE__+1,Line%d; Line%d:", name, name, name, lnum, lnum;
}
/^[ 	]*ZENTRY\(/	{
	inone=1;
	force=1;
	namestart=substr(left, index(left, "ZENTRY") + 6, 256);
	name=substr(namestart, 2, index(namestart, ")") - 2);
	right=sprintf "; .stabs \"%s:F(0,1)\",0x24,0,4,_%s; .stabn 0x44,0,__LINE__,_%s; .stabn 0x44,0,__LINE__+1,Line%d; Line%d:", name, name, name, lnum, lnum;
}
/^[ 	]*ZRTENTRY\(/	{
	inone=1;
	force=1;
	namestart=substr(left, index(left, "ZRTENTRY") + 8, 256);
	name=substr(namestart, 2, index(namestart, ")") - 2);
	right=sprintf "; .stabs \"%s:F(0,1)\",0x24,0,4,%s; .stabn 0x44,0,__LINE__,%s; .stabn 0x44,0,__LINE__+1,Line%d; Line%d:", name, name, name, lnum, lnum;
}

# check for the beginning of a text segment
/^[ 	]*\.seg[ 	]*"[tT][eE][xX][tT]"/	{ intext=1 }

# flush the last line
END {
	printf "%s%s\n", left, right
}
