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