#! perl

# Copyright (C) 2004-2007, The Perl Foundation.

# $Id: /mirror/trunk/tools/dev/as2c.pl 21451 2007-09-21T09:33:44.090048Z paultcochrane  $

=head1 NAME

as2c.pl - convert gas assember listing to i386 code array

=head1 DESCRIPTION

The plan behind of as2c.pl is to create compiler independent
machine code for an architecture. Code in e.g. masm, gas, nasm syntax
doesn't fit all compilers. Therefore as2c.pl translates gas syntax to a
bytestring, which is then used as the asm code.

as2c.pl is used very rarely. Once the code is generated and
checked in, there's usually no need to change it later.

=cut

use strict;
use warnings;

my $src = $ARGV[0];
my $cmd = "cc -c $src.c -Wall -O3 -fomit-frame-pointer -DNDEBUG -Wa,-a > $src.s";
my ($func);

print_header($src);
create_s($cmd);
parse_s("$src.s");
add_glue("$src.c");
print_coda();

sub print_header {
    my $s = shift;
    print <<EOT;
/*
 * DO NOT EDIT THIS FILE
 *
 * Generated from $s.c via $s.s
 * by '$0 $s'
 */

EOT
}

sub print_coda {
    print <<EOT;
/*
 * Local variables:
 *   c-file-style: "parrot"
 * End:
 * vim: expandtab shiftwidth=4:
*/
EOT
}

sub create_s {
    my $cmd = shift;
    my $r   = system($cmd);
    if ($r) {
        die "$cmd failed: $r";
    }
}

sub parse_s {
    my $s = shift;
    open my $IN, "<", "$s" or die "Can't read '$s': $1";
    my ($in_comment);
    $in_comment = 1;
    print "/*\n";
    while (<$IN>) {
        next if (/^\f/);             # FF
        next if (/#(?:NO_)?APP/);    # APP, NO_APP
        chomp;
        if (/^\s*\d+\s[\da-fA-F]{4}\s([\dA-F]{2,8})\s+(.*)/) {
            if ($in_comment) {
                print " */\n";
            }
            my ( $bytes, $src ) = ( $1, $2 );
            $src =~ s/\t/ /g;
            my $len = length($bytes);
            my @pairs = ( $bytes =~ m/../g );
            print "    " . join '', map { "0x$_, " } @pairs;
            print " " x ( 3 * ( 8 - $len ) );
            print "    /* $src */\n";
        }
        elsif (/\.type\s+(\w+)\s*,\s*\@function/) {
            $in_comment = 0;
            $func       = $1;
            print " *\n */\n";
            print "static const char ${func}_code[] = {\n";
        }
        elsif (/^\s*\d+\s+(\w+):/) {
            print " " x 26, " /* $1: */\n";
        }
        elsif ($in_comment) {
            s/\s+//g;
            print " * $_\n";
        }
    }
    print "    0x00\n";
    print "};\n";
    close $IN;
}

sub add_glue {
    my $s = shift;
    open $IN, "<", "$s" or die "Can't read '$s': $1";
    while (<$IN>) {
        if (/\/\*INTERFACE/) {
            my $text = "";
            while (<$IN>) {
                last if (/INTERFACE\*\//);
                $text .= $_;
            }
            $text =~ s/\@FUNC\@/$func/g;
            $text =~ s!\@\*!/*!g;
            $text =~ s!\*\@!*/!g;
            print $text;
        }
    }
    close $IN;
}

# Local Variables:
#   mode: cperl
#   cperl-indent-level: 4
#   fill-column: 100
# End:
# vim: expandtab shiftwidth=4:
