x86masm.pl 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. #!/usr/bin/env perl
  2. package x86masm;
  3. *out=\@::out;
  4. $::lbdecor="\$L"; # local label decoration
  5. $nmdecor="_"; # external name decoration
  6. $initseg="";
  7. $segment="";
  8. sub ::generic
  9. { my ($opcode,@arg)=@_;
  10. # fix hexadecimal constants
  11. for (@arg) { s/(?<![\w\$\.])0x([0-9a-f]+)/0$1h/oi; }
  12. if ($opcode =~ /lea/ && @arg[1] =~ s/.*PTR\s+(\(.*\))$/OFFSET $1/) # no []
  13. { $opcode="mov"; }
  14. elsif ($opcode !~ /mov[dq]$/)
  15. { # fix xmm references
  16. $arg[0] =~ s/\b[A-Z]+WORD\s+PTR/XMMWORD PTR/i if ($arg[-1]=~/\bxmm[0-7]\b/i);
  17. $arg[-1] =~ s/\b[A-Z]+WORD\s+PTR/XMMWORD PTR/i if ($arg[0]=~/\bxmm[0-7]\b/i);
  18. }
  19. &::emit($opcode,@arg);
  20. 1;
  21. }
  22. #
  23. # opcodes not covered by ::generic above, mostly inconsistent namings...
  24. #
  25. sub ::call { &::emit("call",(&::islabel($_[0]) or "$nmdecor$_[0]")); }
  26. sub ::call_ptr { &::emit("call",@_); }
  27. sub ::jmp_ptr { &::emit("jmp",@_); }
  28. sub ::lock { &::data_byte(0xf0); }
  29. sub get_mem
  30. { my($size,$addr,$reg1,$reg2,$idx)=@_;
  31. my($post,$ret);
  32. if (!defined($idx) && 1*$reg2) { $idx=$reg2; $reg2=$reg1; undef $reg1; }
  33. $ret .= "$size PTR " if ($size ne "");
  34. $addr =~ s/^\s+//;
  35. # prepend global references with optional underscore
  36. $addr =~ s/^([^\+\-0-9][^\+\-]*)/&::islabel($1) or "$nmdecor$1"/ige;
  37. # put address arithmetic expression in parenthesis
  38. $addr="($addr)" if ($addr =~ /^.+[\-\+].+$/);
  39. if (($addr ne "") && ($addr ne 0))
  40. { if ($addr !~ /^-/) { $ret .= "$addr"; }
  41. else { $post=$addr; }
  42. }
  43. $ret .= "[";
  44. if ($reg2 ne "")
  45. { $idx!=0 or $idx=1;
  46. $ret .= "$reg2*$idx";
  47. $ret .= "+$reg1" if ($reg1 ne "");
  48. }
  49. else
  50. { $ret .= "$reg1"; }
  51. $ret .= "$post]";
  52. $ret =~ s/\+\]/]/; # in case $addr was the only argument
  53. $ret =~ s/\[\s*\]//;
  54. $ret;
  55. }
  56. sub ::BP { &get_mem("BYTE",@_); }
  57. sub ::WP { &get_mem("WORD",@_); }
  58. sub ::DWP { &get_mem("DWORD",@_); }
  59. sub ::QWP { &get_mem("QWORD",@_); }
  60. sub ::BC { "@_"; }
  61. sub ::DWC { "@_"; }
  62. sub ::file
  63. { my $tmp=<<___;
  64. TITLE $_[0].asm
  65. IF \@Version LT 800
  66. ECHO MASM version 8.00 or later is strongly recommended.
  67. ENDIF
  68. .486
  69. .MODEL FLAT
  70. OPTION DOTNAME
  71. IF \@Version LT 800
  72. .text\$ SEGMENT PAGE 'CODE'
  73. ELSE
  74. .text\$ SEGMENT ALIGN(64) 'CODE'
  75. ENDIF
  76. ___
  77. push(@out,$tmp);
  78. $segment = ".text\$";
  79. }
  80. sub ::function_begin_B
  81. { my $func=shift;
  82. my $global=($func !~ /^_/);
  83. my $begin="${::lbdecor}_${func}_begin";
  84. &::LABEL($func,$global?"$begin":"$nmdecor$func");
  85. $func="ALIGN\t16\n".$nmdecor.$func."\tPROC";
  86. if ($global) { $func.=" PUBLIC\n${begin}::\n"; }
  87. else { $func.=" PRIVATE\n"; }
  88. push(@out,$func);
  89. $::stack=4;
  90. }
  91. sub ::function_end_B
  92. { my $func=shift;
  93. push(@out,"$nmdecor$func ENDP\n");
  94. $::stack=0;
  95. &::wipe_labels();
  96. }
  97. sub ::file_end
  98. { my $xmmheader=<<___;
  99. .686
  100. .XMM
  101. IF \@Version LT 800
  102. XMMWORD STRUCT 16
  103. DQ 2 dup (?)
  104. XMMWORD ENDS
  105. ENDIF
  106. ___
  107. if (grep {/\b[x]?mm[0-7]\b/i} @out) {
  108. grep {s/\.[3-7]86/$xmmheader/} @out;
  109. }
  110. push(@out,"$segment ENDS\n");
  111. if (grep {/\b${nmdecor}OPENSSL_ia32cap_P\b/i} @out)
  112. { my $comm=<<___;
  113. .bss SEGMENT 'BSS'
  114. COMM ${nmdecor}OPENSSL_ia32cap_P:DWORD:4
  115. .bss ENDS
  116. ___
  117. # comment out OPENSSL_ia32cap_P declarations
  118. grep {s/(^EXTERN\s+${nmdecor}OPENSSL_ia32cap_P)/\;$1/} @out;
  119. push (@out,$comm);
  120. }
  121. push (@out,$initseg) if ($initseg);
  122. push (@out,"END\n");
  123. }
  124. sub ::comment { foreach (@_) { push(@out,"\t; $_\n"); } }
  125. *::set_label_B = sub
  126. { my $l=shift; push(@out,$l.($l=~/^\Q${::lbdecor}\E[0-9]{3}/?":\n":"::\n")); };
  127. sub ::external_label
  128. { foreach(@_)
  129. { push(@out, "EXTERN\t".&::LABEL($_,$nmdecor.$_).":NEAR\n"); }
  130. }
  131. sub ::public_label
  132. { push(@out,"PUBLIC\t".&::LABEL($_[0],$nmdecor.$_[0])."\n"); }
  133. sub ::data_byte
  134. { push(@out,("DB\t").join(',',splice(@_,0,16))."\n") while(@_); }
  135. sub ::data_short
  136. { push(@out,("DW\t").join(',',splice(@_,0,8))."\n") while(@_); }
  137. sub ::data_word
  138. { push(@out,("DD\t").join(',',splice(@_,0,4))."\n") while(@_); }
  139. sub ::align
  140. { push(@out,"ALIGN\t$_[0]\n"); }
  141. sub ::picmeup
  142. { my($dst,$sym)=@_;
  143. &::lea($dst,&::DWP($sym));
  144. }
  145. sub ::initseg
  146. { my $f=$nmdecor.shift;
  147. $initseg.=<<___;
  148. .CRT\$XCU SEGMENT DWORD PUBLIC 'DATA'
  149. EXTERN $f:NEAR
  150. DD $f
  151. .CRT\$XCU ENDS
  152. ___
  153. }
  154. sub ::dataseg
  155. { push(@out,"$segment\tENDS\n_DATA\tSEGMENT\n"); $segment="_DATA"; }
  156. sub ::safeseh
  157. { my $nm=shift;
  158. push(@out,"IF \@Version GE 710\n");
  159. push(@out,".SAFESEH ".&::LABEL($nm,$nmdecor.$nm)."\n");
  160. push(@out,"ENDIF\n");
  161. }
  162. 1;