home *** CD-ROM | disk | FTP | other *** search
/ Netrunner 2004 October / NETRUNNER0410.ISO / regular / ActivePerl-5.8.4.810-MSWin32-x86.msi / _65115b6443a0d12d512c275995273d5c < prev    next >
Text File  |  2004-06-01  |  8KB  |  329 lines

  1. #      Assembler.pm
  2. #
  3. #      Copyright (c) 1996 Malcolm Beattie
  4. #
  5. #      You may distribute under the terms of either the GNU General Public
  6. #      License or the Artistic License, as specified in the README file.
  7.  
  8. package B::Assembler;
  9. use Exporter;
  10. use B qw(ppname);
  11. use B::Asmdata qw(%insn_data @insn_name);
  12. use Config qw(%Config);
  13. require ByteLoader;        # we just need its $VERSIOM
  14.  
  15. no warnings;            # XXX
  16.  
  17. @ISA = qw(Exporter);
  18. @EXPORT_OK = qw(assemble_fh newasm endasm assemble asm);
  19. $VERSION = 0.07;
  20.  
  21. use strict;
  22. my %opnumber;
  23. my ($i, $opname);
  24. for ($i = 0; defined($opname = ppname($i)); $i++) {
  25.     $opnumber{$opname} = $i;
  26. }
  27.  
  28. my($linenum, $errors, $out); #    global state, set up by newasm
  29.  
  30. sub error {
  31.     my $str = shift;
  32.     warn "$linenum: $str\n";
  33.     $errors++;
  34. }
  35.  
  36. my $debug = 0;
  37. sub debug { $debug = shift }
  38.  
  39. sub limcheck($$$$){
  40.     my( $val, $lo, $hi, $loc ) = @_;
  41.     if( $val < $lo || $hi < $val ){
  42.         error "argument for $loc outside [$lo, $hi]: $val";
  43.         $val = $hi;
  44.     }
  45.     return $val;
  46. }
  47.  
  48. #
  49. # First define all the data conversion subs to which Asmdata will refer
  50. #
  51.  
  52. sub B::Asmdata::PUT_U8 {
  53.     my $arg = shift;
  54.     my $c = uncstring($arg);
  55.     if (defined($c)) {
  56.     if (length($c) != 1) {
  57.         error "argument for U8 is too long: $c";
  58.         $c = substr($c, 0, 1);
  59.     }
  60.     } else {
  61.         $arg = limcheck( $arg, 0, 0xff, 'U8' );
  62.     $c = chr($arg);
  63.     }
  64.     return $c;
  65. }
  66.  
  67. sub B::Asmdata::PUT_U16 {
  68.     my $arg = limcheck( $_[0], 0, 0xffff, 'U16' );
  69.     pack("S", $arg);
  70. }
  71. sub B::Asmdata::PUT_U32 {
  72.     my $arg = limcheck( $_[0], 0, 0xffffffff, 'U32' );
  73.     pack("L", $arg);
  74. }
  75. sub B::Asmdata::PUT_I32 {
  76.     my $arg = limcheck( $_[0], -0x80000000, 0x7fffffff, 'I32' );
  77.     pack("l", $arg);
  78. }
  79. sub B::Asmdata::PUT_NV  { sprintf("%s\0", $_[0]) } # "%lf" looses precision and pack('d',...)
  80.                            # may not even be portable between compilers
  81. sub B::Asmdata::PUT_objindex { # could allow names here
  82.     my $arg = limcheck( $_[0], 0, 0xffffffff, '*index' );
  83.     pack("L", $arg);
  84. sub B::Asmdata::PUT_svindex { &B::Asmdata::PUT_objindex }
  85. sub B::Asmdata::PUT_opindex { &B::Asmdata::PUT_objindex }
  86. sub B::Asmdata::PUT_pvindex { &B::Asmdata::PUT_objindex }
  87.  
  88. sub B::Asmdata::PUT_strconst {
  89.     my $arg = shift;
  90.     my $str = uncstring($arg);
  91.     if (!defined($str)) {
  92.     error "bad string constant: $arg";
  93.     $str = '';
  94.     }
  95.     if ($str =~ s/\0//g) {
  96.     error "string constant argument contains NUL: $arg";
  97.         $str = '';
  98.     }
  99.     return $str . "\0";
  100. }
  101.  
  102. sub B::Asmdata::PUT_pvcontents {
  103.     my $arg = shift;
  104.     error "extraneous argument: $arg" if defined $arg;
  105.     return "";
  106. }
  107. sub B::Asmdata::PUT_PV {
  108.     my $arg = shift;
  109.     my $str = uncstring($arg);
  110.     if( ! defined($str) ){
  111.         error "bad string argument: $arg";
  112.         $str = '';
  113.     }
  114.     return pack("L", length($str)) . $str;
  115. }
  116. sub B::Asmdata::PUT_comment_t {
  117.     my $arg = shift;
  118.     $arg = uncstring($arg);
  119.     error "bad string argument: $arg" unless defined($arg);
  120.     if ($arg =~ s/\n//g) {
  121.     error "comment argument contains linefeed: $arg";
  122.     }
  123.     return $arg . "\n";
  124. }
  125. sub B::Asmdata::PUT_double { sprintf("%s\0", $_[0]) } # see PUT_NV above
  126. sub B::Asmdata::PUT_none {
  127.     my $arg = shift;
  128.     error "extraneous argument: $arg" if defined $arg;
  129.     return "";
  130. }
  131. sub B::Asmdata::PUT_op_tr_array {
  132.     my @ary = split /\s*,\s*/, shift;
  133.     return pack "S*", @ary;
  134. }
  135.  
  136. sub B::Asmdata::PUT_IV64 {
  137.     return pack "Q", shift;
  138. }
  139.  
  140. sub B::Asmdata::PUT_IV {
  141.     $Config{ivsize} == 4 ? &B::Asmdata::PUT_I32 : &B::Asmdata::PUT_IV64;
  142. }
  143.  
  144. sub B::Asmdata::PUT_PADOFFSET {
  145.     $Config{ptrsize} == 8 ? &B::Asmdata::PUT_IV64 : &B::Asmdata::PUT_U32;
  146. }
  147.  
  148. sub B::Asmdata::PUT_long {
  149.     $Config{longsize} == 8 ? &B::Asmdata::PUT_IV64 : &B::Asmdata::PUT_U32;
  150. }
  151.  
  152. my %unesc = (n => "\n", r => "\r", t => "\t", a => "\a",
  153.          b => "\b", f => "\f", v => "\013");
  154.  
  155. sub uncstring {
  156.     my $s = shift;
  157.     $s =~ s/^"// and $s =~ s/"$// or return undef;
  158.     $s =~ s/\\(\d\d\d|.)/length($1) == 3 ? chr(oct($1)) : ($unesc{$1}||$1)/eg;
  159.     return $s;
  160. }
  161.  
  162. sub strip_comments {
  163.     my $stmt = shift;
  164.     # Comments only allowed in instructions which don't take string arguments
  165.     # Treat string as a single line so .* eats \n characters.
  166.     $stmt =~ s{
  167.     ^\s*    # Ignore leading whitespace
  168.     (
  169.       [^"]*    # A double quote '"' indicates a string argument. If we
  170.         # find a double quote, the match fails and we strip nothing.
  171.     )
  172.     \s*\#    # Any amount of whitespace plus the comment marker...
  173.     .*$    # ...which carries on to end-of-string.
  174.     }{$1}sx;    # Keep only the instruction and optional argument.
  175.     return $stmt;
  176. }
  177.  
  178. # create the ByteCode header: magic, archname, ByteLoader $VERSION, ivsize,
  179. #     ptrsize, byteorder
  180. # nvtype is irrelevant (floats are stored as strings)
  181. # byteorder is strconst not U32 because of varying size issues
  182.  
  183. sub gen_header {
  184.     my $header = "";
  185.  
  186.     $header .= B::Asmdata::PUT_U32(0x43424c50);    # 'PLBC'
  187.     $header .= B::Asmdata::PUT_strconst('"' . $Config{archname}. '"');
  188.     $header .= B::Asmdata::PUT_strconst(qq["$ByteLoader::VERSION"]);
  189.     $header .= B::Asmdata::PUT_U32($Config{ivsize});
  190.     $header .= B::Asmdata::PUT_U32($Config{ptrsize});
  191.     $header;
  192. }
  193.  
  194. sub parse_statement {
  195.     my $stmt = shift;
  196.     my ($insn, $arg) = $stmt =~ m{
  197.     ^\s*    # allow (but ignore) leading whitespace
  198.     (.*?)    # Instruction continues up until...
  199.     (?:    # ...an optional whitespace+argument group
  200.         \s+        # first whitespace.
  201.         (.*)    # The argument is all the rest (newlines included).
  202.     )?$    # anchor at end-of-line
  203.     }sx;
  204.     if (defined($arg)) {
  205.     if ($arg =~ s/^0x(?=[0-9a-fA-F]+$)//) {
  206.         $arg = hex($arg);
  207.     } elsif ($arg =~ s/^0(?=[0-7]+$)//) {
  208.         $arg = oct($arg);
  209.     } elsif ($arg =~ /^pp_/) {
  210.         $arg =~ s/\s*$//; # strip trailing whitespace
  211.         my $opnum = $opnumber{$arg};
  212.         if (defined($opnum)) {
  213.         $arg = $opnum;
  214.         } else {
  215.         error qq(No such op type "$arg");
  216.         $arg = 0;
  217.         }
  218.     }
  219.     }
  220.     return ($insn, $arg);
  221. }
  222.  
  223. sub assemble_insn {
  224.     my ($insn, $arg) = @_;
  225.     my $data = $insn_data{$insn};
  226.     if (defined($data)) {
  227.     my ($bytecode, $putsub) = @{$data}[0, 1];
  228.     my $argcode = &$putsub($arg);
  229.     return chr($bytecode).$argcode;
  230.     } else {
  231.     error qq(no such instruction "$insn");
  232.     return "";
  233.     }
  234. }
  235.  
  236. sub assemble_fh {
  237.     my ($fh, $out) = @_;
  238.     my $line;
  239.     my $asm = newasm($out);
  240.     while ($line = <$fh>) {
  241.     assemble($line);
  242.     }
  243.     endasm();
  244. }
  245.  
  246. sub newasm {
  247.     my($outsub) = @_;
  248.  
  249.     die "Invalid printing routine for B::Assembler\n" unless ref $outsub eq 'CODE';
  250.     die <<EOD if ref $out;
  251. Can't have multiple byteassembly sessions at once!
  252.     (perhaps you forgot an endasm()?)
  253. EOD
  254.  
  255.     $linenum = $errors = 0;
  256.     $out = $outsub;
  257.  
  258.     $out->(gen_header());
  259. }
  260.  
  261. sub endasm {
  262.     if ($errors) {
  263.     die "There were $errors assembly errors\n";
  264.     }
  265.     $linenum = $errors = $out = 0;
  266. }
  267.  
  268. sub assemble {
  269.     my($line) = @_;
  270.     my ($insn, $arg);
  271.     $linenum++;
  272.     chomp $line;
  273.     if ($debug) {
  274.     my $quotedline = $line;
  275.     $quotedline =~ s/\\/\\\\/g;
  276.     $quotedline =~ s/"/\\"/g;
  277.     $out->(assemble_insn("comment", qq("$quotedline")));
  278.     }
  279.     if( $line = strip_comments($line) ){
  280.         ($insn, $arg) = parse_statement($line);
  281.         $out->(assemble_insn($insn, $arg));
  282.         if ($debug) {
  283.         $out->(assemble_insn("nop", undef));
  284.         }
  285.     }
  286. }
  287.  
  288. ### temporary workaround
  289.  
  290. sub asm {
  291.     return if $_[0] =~ /\s*\W/;
  292.     if (defined $_[1]) {
  293.     return if $_[1] eq "0" and
  294.         $_[0] !~ /^(?:newsvx?|av_pushx?|av_extend|xav_flags)$/;
  295.     return if $_[1] eq "1" and $_[0] =~ /^(?:sv_refcnt)$/;
  296.     }
  297.     assemble "@_";
  298. }
  299.  
  300. 1;
  301.  
  302. __END__
  303.  
  304. =head1 NAME
  305.  
  306. B::Assembler - Assemble Perl bytecode
  307.  
  308. =head1 SYNOPSIS
  309.  
  310.     use B::Assembler qw(newasm endasm assemble);
  311.     newasm(\&printsub);    # sets up for assembly
  312.     assemble($buf);     # assembles one line
  313.     endasm();        # closes down
  314.  
  315.     use B::Assembler qw(assemble_fh);
  316.     assemble_fh($fh, \&printsub);    # assemble everything in $fh
  317.  
  318. =head1 DESCRIPTION
  319.  
  320. See F<ext/B/B/Assembler.pm>.
  321.  
  322. =head1 AUTHORS
  323.  
  324. Malcolm Beattie, C<mbeattie@sable.ox.ac.uk>
  325. Per-statement interface by Benjamin Stuhl, C<sho_pi@hotmail.com>
  326.  
  327. =cut
  328.