#!/usr/bin/perl
#
#

sub usage {
	print STDERR $0." machine\n";
	return;
}

sub headerfile {
	my($arch) = @_;

	open(HFILE, "> $arch.h") || die "Can't open $arch.h: $!";
	print HFILE <<EOF;
#ifndef INST_$arch
#define INST_$arch

#include "bits.h"
#include "inst.h"
#include "$arch\_arch.h"

extern struct inst_32flat_s Inst_$arch\[];

int Cpu_$arch(void *memory, uint64_t pc, uint64_t sp);

#endif /* INST_$arch */
EOF
	close(HFILE);
}

sub bin2hex {
	my($bin) = @_;

	my $val,$i;
	my(@bits)=split(//,$bin);
	for ($i = 0; $i < @bits; $i++) {
		$val <<= 1;
		$val += $bits[$i];
	}

	return sprintf("%x",$val);
}

sub main {
	my($arch) = @_;

	if ( $arch eq '') {
		&usage;
		return 1;
	}

	my($i,@Mask,@Zero);
	$Mask[0]='';
	for ($i = 1; $i < 32; $i++) {
		$Mask[$i]=$Mask[$i-1].'1';
		$Zero[$i]=$Zero[$i-1].'0';
	}

	open(SPECFILE, "< $arch.spec") || die "Can't open $arch.spec: $!";

	&headerfile($arch);

	open(CFILE, "> $arch.c") || die "Can't open $arch.c: $!";
	print CFILE "#include \"$arch.h\"\n";

	my($line,$func,$d,$comment,$func_start);
	my @map;
	while ($line = <SPECFILE>) {
		if ($line =~ /^\/\*/) {
			($d,$func,$comment) = split(/ /,$line,3);
		}

		if ($line =~ /^[01].*:/) {
			my($match,$junk)=split(/:/,$line);
			my(@args)=split(/\s+/,$match);
			my($mask,$compare,@regs);
			foreach $match (@args) {
				if ($match =~ /^[01]/) {
					$mask .= $Mask[length($match)];
					$compare .= $match;
				} else {
					my($var,$len)=split(/[\(\)]/,$match);
					push @regs,{'name'=>$var,'end'=>31-length($mask),'start'=>31-length($mask)-$len+1};
					$mask .= $Zero[$len];
					$compare .= $Zero[$len];
				}
			}
			if (length($mask) != 32 || length($compare) != 32) {
				print STDERR "Match definition for $func is incorrect:\n".$line;
				exit(1);
			}
			$func =~ s/[\.\/]/_/g;
			$func_start=1;
			push @map,{'func'=>$arch."_".$func,'mask'=>$mask,'compare'=>$compare,'regs'=>\@regs};
			print CFILE "static void $arch"."_"."$func(CPU_t pcpu, uint32_t instruction)\n";
			print CFILE "{\n\t$arch\_CPU_t cpu=pcpu;\n";
			my(@vals,@fmt);
			foreach $i (@regs) {
				print CFILE "\tint ".$i->{'name'}."=bits(instruction,".$i->{'start'}.",".$i->{'end'}.");\n";
				push @vals,$i->{'name'};
				push @fmt,$i->{'name'}.'=%d';
			}
			print CFILE "\tDEBUG_MSG(D_INST,\"$func ".join(",",@fmt)."\\n\"";
			if ( @vals > 0) {
				print CFILE ",".join(",",@vals);
			}
			print CFILE ");\n";
			print CFILE "\t{\n";
			next;
		}

		if ($line =~ /^\s*$/ && $func_start) {
			print CFILE "\t}\n";
			print CFILE "}\n";
			$func_start=0;
		}

		print CFILE $line;
	}

	print CFILE "\n\nstruct inst_32flat_s Inst_".$arch."[".(@map+1)."]={\n";
	foreach $i (@map) {
		print CFILE "\t{ ".$i->{'func'}.", 0x".bin2hex($i->{'mask'}).", 0x".bin2hex($i->{'compare'})." },\n";
	}
	print CFILE "\t{ NULL, 0, 0 }\n};\n";
	close(SPECFILE);
	close(CFILE);

	return 0;
}


exit &main($ARGV[0]);
