x86masm.pl 4.1 KB

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