#!/usr/local/bin/perl

package alpha;
use Carp qw(croak cluck);

$label="100";

$n_debug=0;
$smear_regs=1;
$reg_alloc=1;

$align="3";
$com_start="#";

sub main'asm_init_output { @out=(); }
sub main'asm_get_output { return(@out); }
sub main'get_labels { return(@labels); }
sub main'external_label { push(@labels,@_); }

# General registers

%regs=(	'r0',	'$0',
	'r1',	'$1',
	'r2',	'$2',
	'r3',	'$3',
	'r4',	'$4',
	'r5',	'$5',
	'r6',	'$6',
	'r7',	'$7',
	'r8',	'$8',
	'r9',	'$22',
	'r10',	'$23',
	'r11',	'$24',
	'r12',	'$25',
	'r13',	'$27',
	'r14',	'$28',
	'r15',	'$21', # argc == 5
	'r16',	'$20', # argc == 4
	'r17',	'$19', # argc == 3
	'r18',	'$18', # argc == 2
	'r19',	'$17', # argc == 1
	'r20',	'$16', # argc == 0
	'r21',	'$9',  # save 0
	'r22',	'$10', # save 1
	'r23',	'$11', # save 2
	'r24',	'$12', # save 3
	'r25',	'$13', # save 4
	'r26',	'$14', # save 5

	'a0',	'$16',
	'a1',	'$17',
	'a2',	'$18',
	'a3',	'$19',
	'a4',	'$20',
	'a5',	'$21',

	's0',	'$9',
	's1',	'$10',
	's2',	'$11',
	's3',	'$12',
	's4',	'$13',
	's5',	'$14',
	'zero',	'$31',
	'sp',	'$30',
	);

$main'reg_s0="r21";
$main'reg_s1="r22";
$main'reg_s2="r23";
$main'reg_s3="r24";
$main'reg_s4="r25";
$main'reg_s5="r26";

@reg=(  '$0', '$1' ,'$2' ,'$3' ,'$4' ,'$5' ,'$6' ,'$7' ,'$8',
	'$22','$23','$24','$25','$20','$21','$27','$28');


sub main'sub	{ &out3("subq",@_); }
sub main'add	{ &out3("addq",@_); }
sub main'mov	{ &out3("bis",$_[0],$_[0],$_[1]); }
sub main'or	{ &out3("bis",@_); }
sub main'bis	{ &out3("bis",@_); }
sub main'br	{ &out1("br",@_); }
sub main'ld	{ &out2("ldq",@_); }
sub main'st	{ &out2("stq",@_); }
sub main'cmpult	{ &out3("cmpult",@_); }
sub main'cmplt	{ &out3("cmplt",@_); }
sub main'bgt	{ &out2("bgt",@_); }
sub main'ble	{ &out2("ble",@_); }
sub main'blt	{ &out2("blt",@_); }
sub main'mul	{ &out3("mulq",@_); }
sub main'muh	{ &out3("umulh",@_); }

$main'QWS=8;

sub main'asm_add
	{
	push(@out,@_);
	}

sub main'asm_finish
	{
	&main'file_end();
	print &main'asm_get_output();
	}

sub main'asm_init
	{
	($type,$fn)=@_;
	$filename=$fn;

	&main'asm_init_output();
	&main'comment("Don't even think of reading this code");
	&main'comment("It was automatically generated by $filename");
	&main'comment("Which is a perl program used to generate the alpha assember.");
	&main'comment("eric <eay\@cryptsoft.com>");
	&main'comment("");

	$filename =~ s/\.pl$//;
	&main'file($filename);
	}

sub conv
	{
	local($r)=@_;
	local($v);

	return($regs{$r}) if defined($regs{$r});
	return($r);
	}

sub main'QWPw
	{
	local($off,$reg)=@_;

	return(&main'QWP($off*8,$reg));
	}

sub main'QWP
	{
	local($off,$reg)=@_;

	$ret="$off(".&conv($reg).")";
	return($ret);
	}

sub out3
	{
	local($name,$p1,$p2,$p3)=@_;

	$p1=&conv($p1);
	$p2=&conv($p2);
	$p3=&conv($p3);
	push(@out,"\t$name\t");
	$l=length($p1)+1;
	push(@out,$p1.",");
	$ll=3-($l+9)/8;
	$tmp1=sprintf("\t" x $ll);
	push(@out,$tmp1);

	$l=length($p2)+1;
	push(@out,$p2.",");
	$ll=3-($l+9)/8;
	$tmp1=sprintf("\t" x $ll);
	push(@out,$tmp1);

	push(@out,&conv($p3)."\n");
	}

sub out2
	{
	local($name,$p1,$p2,$p3)=@_;

	$p1=&conv($p1);
	$p2=&conv($p2);
	push(@out,"\t$name\t");
	$l=length($p1)+1;
	push(@out,$p1.",");
	$ll=3-($l+9)/8;
	$tmp1=sprintf("\t" x $ll);
	push(@out,$tmp1);

	push(@out,&conv($p2)."\n");
	}

sub out1
	{
	local($name,$p1)=@_;

	$p1=&conv($p1);
	push(@out,"\t$name\t".$p1."\n");
	}

sub out0
	{
	push(@out,"\t$_[0]\n");
	}

sub main'file
	{
	local($file)=@_;

	local($tmp)=<<"EOF";
 # DEC Alpha assember
 # Generated from perl scripts contains in SSLeay
	.file	1 "$file.s"
	.set noat
EOF
	push(@out,$tmp);
	}

sub main'function_begin
	{
	local($func)=@_;

print STDERR "$func\n";
	local($tmp)=<<"EOF";
	.text
	.align $align
	.globl $func
	.ent $func
${func}:
${func}..ng:
	.frame \$30,0,\$26,0
	.prologue 0
EOF
	push(@out,$tmp);
	$stack=0;
	}

sub main'function_end
	{
	local($func)=@_;

	local($tmp)=<<"EOF";
	ret	\$31,(\$26),1
	.end $func
EOF
	push(@out,$tmp);
	$stack=0;
	%label=();
	}

sub main'function_end_A
	{
	local($func)=@_;

	local($tmp)=<<"EOF";
	ret	\$31,(\$26),1
EOF
	push(@out,$tmp);
	}

sub main'function_end_B
	{
	local($func)=@_;

	$func=$under.$func;

	push(@out,"\t.end $func\n");
	$stack=0;
	%label=();
	}

sub main'wparam
	{
	local($num)=@_;

	if ($num < 6)
		{
		$num=20-$num;
		return("r$num");
		}
	else
		{ return(&main'QWP($stack+$num*8,"sp")); }
	}

sub main'stack_push
	{
	local($num)=@_;
	$stack+=$num*8;
	&main'sub("sp",$num*8,"sp");
	}

sub main'stack_pop
	{
	local($num)=@_;
	$stack-=$num*8;
	&main'add("sp",$num*8,"sp");
	}

sub main'swtmp
	{
	return(&main'QWP(($_[0])*8,"sp"));
	}

# Should use swtmp, which is above sp.  Linix can trash the stack above esp
#sub main'wtmp
#	{
#	local($num)=@_;
#
#	return(&main'QWP(-($num+1)*4,"esp","",0));
#	}

sub main'comment
	{
	foreach (@_)
		{
		if (/^\s*$/)
			{ push(@out,"\n"); }
		else
			{ push(@out,"\t$com_start $_ $com_end\n"); }
		}
	}

sub main'label
	{
	if (!defined($label{$_[0]}))
		{
		$label{$_[0]}=$label;
		$label++;
		}
	return('$'.$label{$_[0]});
	}

sub main'set_label
	{
	if (!defined($label{$_[0]}))
		{
		$label{$_[0]}=$label;
		$label++;
		}
#	push(@out,".align $align\n") if ($_[1] != 0);
	push(@out,'$'."$label{$_[0]}:\n");
	}

sub main'file_end
	{
	}

sub main'data_word
	{
	push(@out,"\t.long $_[0]\n");
	}

@pool_free=();
@pool_taken=();
$curr_num=0;
$max=0;

sub main'init_pool
	{
	local($args)=@_;
	local($i);

	@pool_free=();
	for ($i=(14+(6-$args)); $i >= 0; $i--)
		{
		push(@pool_free,"r$i");
		}
	print STDERR "START :register pool:@pool_free\n";
	$curr_num=$max=0;
	}

sub main'fin_pool
	{
	printf STDERR "END %2d:register pool:@pool_free\n",$max;
	}

sub main'GR
	{
	local($r)=@_;
	local($i,@n,$_);

	foreach (@pool_free)
		{
		if ($r ne $_)
			{ push(@n,$_); }
		else
			{
			$curr_num++;
			$max=$curr_num if ($curr_num > $max);
			}
		}
	@pool_free=@n;
print STDERR "GR:@pool_free\n" if $reg_alloc;
	return(@_);
	}

sub main'NR
	{
	local($num)=@_;
	local(@ret);

	$num=1 if $num == 0;
	($#pool_free >= ($num-1)) || croak "out of registers: want $num, have @pool_free";
	while ($num > 0)
		{
		push(@ret,pop @pool_free);
		$curr_num++;
		$max=$curr_num if ($curr_num > $max);
		$num--
		}
	print STDERR "nr @ret\n" if $n_debug;
print STDERR "NR:@pool_free\n" if $reg_alloc;
	return(@ret);

	}

sub main'FR
	{
	local(@r)=@_;
	local(@a,$v,$w);

	print STDERR "fr @r\n" if $n_debug;
#	cluck "fr @r";
	for $w (@pool_free)
		{
		foreach $v (@r)
			{
			croak "double register free of $v (@pool_free)" if $w eq $v;
			}
		}
	foreach $v (@r)
		{
		croak "bad argument to FR" if ($v !~ /^r\d+$/);
		if ($smear_regs)
			{ unshift(@pool_free,$v); }
		else	{ push(@pool_free,$v); }
		$curr_num--;
		}
print STDERR "FR:@pool_free\n" if $reg_alloc;
	}
1;
