Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

229 lines
5.3 KiB

  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. package B::Assembler;
  8. use Exporter;
  9. use B qw(ppname);
  10. use B::Asmdata qw(%insn_data @insn_name);
  11. @ISA = qw(Exporter);
  12. @EXPORT_OK = qw(assemble_fh assemble_insn strip_comments
  13. parse_statement uncstring);
  14. use strict;
  15. my %opnumber;
  16. my ($i, $opname);
  17. for ($i = 0; defined($opname = ppname($i)); $i++) {
  18. $opnumber{$opname} = $i;
  19. }
  20. my ($linenum, $errors);
  21. sub error {
  22. my $str = shift;
  23. warn "$linenum: $str\n";
  24. $errors++;
  25. }
  26. my $debug = 0;
  27. sub debug { $debug = shift }
  28. #
  29. # First define all the data conversion subs to which Asmdata will refer
  30. #
  31. sub B::Asmdata::PUT_U8 {
  32. my $arg = shift;
  33. my $c = uncstring($arg);
  34. if (defined($c)) {
  35. if (length($c) != 1) {
  36. error "argument for U8 is too long: $c";
  37. $c = substr($c, 0, 1);
  38. }
  39. } else {
  40. $c = chr($arg);
  41. }
  42. return $c;
  43. }
  44. sub B::Asmdata::PUT_U16 { pack("n", $_[0]) }
  45. sub B::Asmdata::PUT_U32 { pack("N", $_[0]) }
  46. sub B::Asmdata::PUT_I32 { pack("N", $_[0]) }
  47. sub B::Asmdata::PUT_objindex { pack("N", $_[0]) } # could allow names here
  48. sub B::Asmdata::PUT_svindex { &B::Asmdata::PUT_objindex }
  49. sub B::Asmdata::PUT_opindex { &B::Asmdata::PUT_objindex }
  50. sub B::Asmdata::PUT_strconst {
  51. my $arg = shift;
  52. $arg = uncstring($arg);
  53. if (!defined($arg)) {
  54. error "bad string constant: $arg";
  55. return "";
  56. }
  57. if ($arg =~ s/\0//g) {
  58. error "string constant argument contains NUL: $arg";
  59. }
  60. return $arg . "\0";
  61. }
  62. sub B::Asmdata::PUT_pvcontents {
  63. my $arg = shift;
  64. error "extraneous argument: $arg" if defined $arg;
  65. return "";
  66. }
  67. sub B::Asmdata::PUT_PV {
  68. my $arg = shift;
  69. $arg = uncstring($arg);
  70. error "bad string argument: $arg" unless defined($arg);
  71. return pack("N", length($arg)) . $arg;
  72. }
  73. sub B::Asmdata::PUT_comment_t {
  74. my $arg = shift;
  75. $arg = uncstring($arg);
  76. error "bad string argument: $arg" unless defined($arg);
  77. if ($arg =~ s/\n//g) {
  78. error "comment argument contains linefeed: $arg";
  79. }
  80. return $arg . "\n";
  81. }
  82. sub B::Asmdata::PUT_double { sprintf("%s\0", $_[0]) }
  83. sub B::Asmdata::PUT_none {
  84. my $arg = shift;
  85. error "extraneous argument: $arg" if defined $arg;
  86. return "";
  87. }
  88. sub B::Asmdata::PUT_op_tr_array {
  89. my $arg = shift;
  90. my @ary = split(/\s*,\s*/, $arg);
  91. if (@ary != 256) {
  92. error "wrong number of arguments to op_tr_array";
  93. @ary = (0) x 256;
  94. }
  95. return pack("n256", @ary);
  96. }
  97. # XXX Check this works
  98. sub B::Asmdata::PUT_IV64 {
  99. my $arg = shift;
  100. return pack("NN", $arg >> 32, $arg & 0xffffffff);
  101. }
  102. my %unesc = (n => "\n", r => "\r", t => "\t", a => "\a",
  103. b => "\b", f => "\f", v => "\013");
  104. sub uncstring {
  105. my $s = shift;
  106. $s =~ s/^"// and $s =~ s/"$// or return undef;
  107. $s =~ s/\\(\d\d\d|.)/length($1) == 3 ? chr(oct($1)) : ($unesc{$1}||$1)/eg;
  108. return $s;
  109. }
  110. sub strip_comments {
  111. my $stmt = shift;
  112. # Comments only allowed in instructions which don't take string arguments
  113. $stmt =~ s{
  114. (?sx) # Snazzy extended regexp coming up. Also, treat
  115. # string as a single line so .* eats \n characters.
  116. ^\s* # Ignore leading whitespace
  117. (
  118. [^"]* # A double quote '"' indicates a string argument. If we
  119. # find a double quote, the match fails and we strip nothing.
  120. )
  121. \s*\# # Any amount of whitespace plus the comment marker...
  122. .*$ # ...which carries on to end-of-string.
  123. }{$1}; # Keep only the instruction and optional argument.
  124. return $stmt;
  125. }
  126. sub parse_statement {
  127. my $stmt = shift;
  128. my ($insn, $arg) = $stmt =~ m{
  129. (?sx)
  130. ^\s* # allow (but ignore) leading whitespace
  131. (.*?) # Instruction continues up until...
  132. (?: # ...an optional whitespace+argument group
  133. \s+ # first whitespace.
  134. (.*) # The argument is all the rest (newlines included).
  135. )?$ # anchor at end-of-line
  136. };
  137. if (defined($arg)) {
  138. if ($arg =~ s/^0x(?=[0-9a-fA-F]+$)//) {
  139. $arg = hex($arg);
  140. } elsif ($arg =~ s/^0(?=[0-7]+$)//) {
  141. $arg = oct($arg);
  142. } elsif ($arg =~ /^pp_/) {
  143. $arg =~ s/\s*$//; # strip trailing whitespace
  144. my $opnum = $opnumber{$arg};
  145. if (defined($opnum)) {
  146. $arg = $opnum;
  147. } else {
  148. error qq(No such op type "$arg");
  149. $arg = 0;
  150. }
  151. }
  152. }
  153. return ($insn, $arg);
  154. }
  155. sub assemble_insn {
  156. my ($insn, $arg) = @_;
  157. my $data = $insn_data{$insn};
  158. if (defined($data)) {
  159. my ($bytecode, $putsub) = @{$data}[0, 1];
  160. my $argcode = &$putsub($arg);
  161. return chr($bytecode).$argcode;
  162. } else {
  163. error qq(no such instruction "$insn");
  164. return "";
  165. }
  166. }
  167. sub assemble_fh {
  168. my ($fh, $out) = @_;
  169. my ($line, $insn, $arg);
  170. $linenum = 0;
  171. $errors = 0;
  172. while ($line = <$fh>) {
  173. $linenum++;
  174. chomp $line;
  175. if ($debug) {
  176. my $quotedline = $line;
  177. $quotedline =~ s/\\/\\\\/g;
  178. $quotedline =~ s/"/\\"/g;
  179. &$out(assemble_insn("comment", qq("$quotedline")));
  180. }
  181. $line = strip_comments($line) or next;
  182. ($insn, $arg) = parse_statement($line);
  183. &$out(assemble_insn($insn, $arg));
  184. if ($debug) {
  185. &$out(assemble_insn("nop", undef));
  186. }
  187. }
  188. if ($errors) {
  189. die "Assembly failed with $errors error(s)\n";
  190. }
  191. }
  192. 1;
  193. __END__
  194. =head1 NAME
  195. B::Assembler - Assemble Perl bytecode
  196. =head1 SYNOPSIS
  197. use Assembler;
  198. =head1 DESCRIPTION
  199. See F<ext/B/B/Assembler.pm>.
  200. =head1 AUTHOR
  201. Malcolm Beattie, C<[email protected]>
  202. =cut