x86masm.pl 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  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 !~ /movq/)
  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. $ret .= "$size PTR " if ($size ne "");
  33. $addr =~ s/^\s+//;
  34. # prepend global references with optional underscore
  35. $addr =~ s/^([^\+\-0-9][^\+\-]*)/&::islabel($1) or "$nmdecor$1"/ige;
  36. # put address arithmetic expression in parenthesis
  37. $addr="($addr)" if ($addr =~ /^.+[\-\+].+$/);
  38. if (($addr ne "") && ($addr ne 0))
  39. { if ($addr !~ /^-/) { $ret .= "$addr"; }
  40. else { $post=$addr; }
  41. }
  42. $ret .= "[";
  43. if ($reg2 ne "")
  44. { $idx!=0 or $idx=1;
  45. $ret .= "$reg2*$idx";
  46. $ret .= "+$reg1" if ($reg1 ne "");
  47. }
  48. else
  49. { $ret .= "$reg1"; }
  50. $ret .= "$post]";
  51. $ret =~ s/\+\]/]/; # in case $addr was the only argument
  52. $ret =~ s/\[\s*\]//;
  53. $ret;
  54. }
  55. sub ::BP { &get_mem("BYTE",@_); }
  56. sub ::WP { &get_mem("WORD",@_); }
  57. sub ::DWP { &get_mem("DWORD",@_); }
  58. sub ::QWP { &get_mem("QWORD",@_); }
  59. sub ::BC { "@_"; }
  60. sub ::DWC { "@_"; }
  61. sub ::file
  62. { my $tmp=<<___;
  63. TITLE $_[0].asm
  64. IF \@Version LT 800
  65. ECHO MASM version 8.00 or later is strongly recommended.
  66. ENDIF
  67. .486
  68. .MODEL FLAT
  69. OPTION DOTNAME
  70. IF \@Version LT 800
  71. .text\$ SEGMENT PAGE 'CODE'
  72. ELSE
  73. .text\$ SEGMENT ALIGN(64) 'CODE'
  74. ENDIF
  75. ___
  76. push(@out,$tmp);
  77. $segment = ".text\$";
  78. }
  79. sub ::function_begin_B
  80. { my $func=shift;
  81. my $global=($func !~ /^_/);
  82. my $begin="${::lbdecor}_${func}_begin";
  83. &::LABEL($func,$global?"$begin":"$nmdecor$func");
  84. $func="ALIGN\t16\n".$nmdecor.$func."\tPROC";
  85. if ($global) { $func.=" PUBLIC\n${begin}::\n"; }
  86. else { $func.=" PRIVATE\n"; }
  87. push(@out,$func);
  88. $::stack=4;
  89. }
  90. sub ::function_end_B
  91. { my $func=shift;
  92. push(@out,"$nmdecor$func ENDP\n");
  93. $::stack=0;
  94. &::wipe_labels();
  95. }
  96. sub ::file_end
  97. { my $xmmheader=<<___;
  98. .686
  99. .XMM
  100. IF \@Version LT 800
  101. XMMWORD STRUCT 16
  102. DQ 2 dup (?)
  103. XMMWORD ENDS
  104. ENDIF
  105. ___
  106. if (grep {/\b[x]?mm[0-7]\b/i} @out) {
  107. grep {s/\.[3-7]86/$xmmheader/} @out;
  108. }
  109. push(@out,"$segment ENDS\n");
  110. if (grep {/\b${nmdecor}OPENSSL_ia32cap_P\b/i} @out)
  111. { my $comm=<<___;
  112. .bss SEGMENT 'BSS'
  113. COMM ${nmdecor}OPENSSL_ia32cap_P:QWORD
  114. .bss ENDS
  115. ___
  116. # comment out OPENSSL_ia32cap_P declarations
  117. grep {s/(^EXTERN\s+${nmdecor}OPENSSL_ia32cap_P)/\;$1/} @out;
  118. push (@out,$comm);
  119. }
  120. push (@out,$initseg) if ($initseg);
  121. push (@out,"END\n");
  122. }
  123. sub ::comment { foreach (@_) { push(@out,"\t; $_\n"); } }
  124. *::set_label_B = sub
  125. { my $l=shift; push(@out,$l.($l=~/^\Q${::lbdecor}\E[0-9]{3}/?":\n":"::\n")); };
  126. sub ::external_label
  127. { foreach(@_)
  128. { push(@out, "EXTERN\t".&::LABEL($_,$nmdecor.$_).":NEAR\n"); }
  129. }
  130. sub ::public_label
  131. { push(@out,"PUBLIC\t".&::LABEL($_[0],$nmdecor.$_[0])."\n"); }
  132. sub ::data_byte
  133. { push(@out,("DB\t").join(',',@_)."\n"); }
  134. sub ::data_short
  135. { push(@out,("DW\t").join(',',@_)."\n"); }
  136. sub ::data_word
  137. { push(@out,("DD\t").join(',',@_)."\n"); }
  138. sub ::align
  139. { push(@out,"ALIGN\t$_[0]\n"); }
  140. sub ::picmeup
  141. { my($dst,$sym)=@_;
  142. &::lea($dst,&::DWP($sym));
  143. }
  144. sub ::initseg
  145. { my $f=$nmdecor.shift;
  146. $initseg.=<<___;
  147. .CRT\$XCU SEGMENT DWORD PUBLIC 'DATA'
  148. EXTERN $f:NEAR
  149. DD $f
  150. .CRT\$XCU ENDS
  151. ___
  152. }
  153. sub ::dataseg
  154. { push(@out,"$segment\tENDS\n_DATA\tSEGMENT\n"); $segment="_DATA"; }
  155. sub ::safeseh
  156. { my $nm=shift;
  157. push(@out,"IF \@Version GE 710\n");
  158. push(@out,".SAFESEH ".&::LABEL($nm,$nmdecor.$nm)."\n");
  159. push(@out,"ENDIF\n");
  160. }
  161. 1;