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