1#!/usr/bin/env perl
2
3package x86masm;
4
5*out=\@::out;
6
7$::lbdecor="\$L";	# local label decoration
8$nmdecor="_";		# external name decoration
9
10$initseg="";
11$segment="";
12
13sub ::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#
31sub ::call	{ &::emit("call",(&::islabel($_[0]) or "$nmdecor$_[0]")); }
32sub ::call_ptr	{ &::emit("call",@_);	}
33sub ::jmp_ptr	{ &::emit("jmp",@_);	}
34
35sub 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}
67sub ::BP	{ &get_mem("BYTE",@_);  }
68sub ::DWP	{ &get_mem("DWORD",@_); }
69sub ::QWP	{ &get_mem("QWORD",@_); }
70sub ::BC	{ "@_";  }
71sub ::DWC	{ "@_"; }
72
73sub ::file
74{ my $tmp=<<___;
75TITLE	$_[0].asm
76IF \@Version LT 800
77ECHO MASM version 8.00 or later is strongly recommended.
78ENDIF
79.486
80.MODEL	FLAT
81OPTION	DOTNAME
82IF \@Version LT 800
83.text\$	SEGMENT PAGE 'CODE'
84ELSE
85.text\$	SEGMENT ALIGN(64) 'CODE'
86ENDIF
87___
88    push(@out,$tmp);
89    $segment = ".text\$";
90}
91
92sub ::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}
105sub ::function_end_B
106{ my $func=shift;
107
108    push(@out,"$nmdecor$func ENDP\n");
109    $::stack=0;
110    &::wipe_labels();
111}
112
113sub ::file_end
114{ my $xmmheader=<<___;
115.686
116.XMM
117IF \@Version LT 800
118XMMWORD STRUCT 16
119DQ	2 dup (?)
120XMMWORD	ENDS
121ENDIF
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'
132COMM	${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
143sub ::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
148sub ::external_label
149{   foreach(@_)
150    {	push(@out, "EXTERN\t".&::LABEL($_,$nmdecor.$_).":NEAR\n");   }
151}
152
153sub ::public_label
154{   push(@out,"PUBLIC\t".&::LABEL($_[0],$nmdecor.$_[0])."\n");   }
155
156sub ::data_byte
157{   push(@out,("DB\t").join(',',@_)."\n");	}
158
159sub ::data_word
160{   push(@out,("DD\t").join(',',@_)."\n");	}
161
162sub ::align
163{   push(@out,"ALIGN\t$_[0]\n");	}
164
165sub ::picmeup
166{ my($dst,$sym)=@_;
167    &::lea($dst,&::DWP($sym));
168}
169
170sub ::initseg
171{ my $f=$nmdecor.shift;
172
173    $initseg.=<<___;
174.CRT\$XCU	SEGMENT DWORD PUBLIC 'DATA'
175EXTERN	$f:NEAR
176DD	$f
177.CRT\$XCU	ENDS
178___
179}
180
181sub ::dataseg
182{   push(@out,"$segment\tENDS\n_DATA\tSEGMENT\n"); $segment="_DATA";   }
183
1841;
185