#!/usr/bin/env perl package x86nasm; *out=\@::out; $lbdecor="\@L"; # local label decoration $nmdecor=$::netware?"":"_"; # external name decoration $drdecor=$::mwerks?".":""; # directive decoration $label="000"; $initseg=""; sub ::generic { my $opcode=shift; my $tmp; if (!$::mwerks) { if ($opcode =~ m/^j/o && $#_==0) # optimize jumps { $_[0] = "NEAR $_[0]"; } elsif ($opcode eq "lea" && $#_==1)# wipe storage qualifier from lea { $_[1] =~ s/^[^\[]*\[/\[/o; } } &::emit($opcode,@_); 1; } # # opcodes not covered by ::generic above, mostly inconsistent namings... # sub ::movz { &::movzx(@_); } sub ::pushf { &::pushfd; } sub ::popf { &::popfd; } sub ::call { &::emit("call",(&islabel($_[0]) or "$nmdecor$_[0]")); } sub ::call_ptr { &::emit("call",@_); } sub ::jmp_ptr { &::emit("jmp",@_); } # chosen SSE instructions sub ::movq { my($p1,$p2,$optimize)=@_; if ($optimize && $p1=~/^mm[0-7]$/ && $p2=~/^mm[0-7]$/) # movq between mmx registers can sink Intel CPUs { &::pshufw($p1,$p2,0xe4); } else { &::emit("movq",@_); } } sub ::pshufw { &::emit("pshufw",@_); } sub get_mem { my($size,$addr,$reg1,$reg2,$idx)=@_; my($post,$ret); if ($size ne "") { $ret .= "$size"; $ret .= " PTR" if ($::mwerks); $ret .= " "; } $ret .= "["; $addr =~ s/^\s+//; # prepend global references with optional underscore $addr =~ s/^([^\+\-0-9][^\+\-]*)/islabel($1) or "$nmdecor$1"/ige; # put address arithmetic expression in parenthesis $addr="($addr)" if ($addr =~ /^.+[\-\+].+$/); if (($addr ne "") && ($addr ne 0)) { if ($addr !~ /^-/) { $ret .= "$addr+"; } else { $post=$addr; } } if ($reg2 ne "") { $idx!=0 or $idx=1; $ret .= "$reg2*$idx"; $ret .= "+$reg1" if ($reg1 ne ""); } else { $ret .= "$reg1"; } $ret .= "$post]"; $ret =~ s/\+\]/]/; # in case $addr was the only argument $ret; } sub ::BP { &get_mem("BYTE",@_); } sub ::DWP { &get_mem("DWORD",@_); } sub ::QWP { &get_mem("",@_); } sub ::BC { (($::mwerks)?"":"BYTE ")."@_"; } sub ::DWC { (($::mwerks)?"":"DWORD ")."@_"; } sub ::file { if ($::mwerks) { push(@out,".section\t.text\n"); } else { my $tmp=<<___; %ifdef __omf__ section code use32 class=code align=64 %else section .text code align=64 %endif ___ push(@out,$tmp); } } sub ::function_begin_B { my $func=shift; my $global=($func !~ /^_/); my $begin="${lbdecor}_${func}_begin"; $label{$func}=$global?"$begin":"$nmdecor$func"; $func=$nmdecor.$func; push(@out,"${drdecor}global $func\n") if ($global); push(@out,"${drdecor}align 16\n"); push(@out,"$func:\n"); push(@out,"$begin:\n") if ($global); $::stack=4; } sub ::function_end_B { my $i; foreach $i (keys %label) { delete $label{$i} if ($label{$i} =~ /^${lbdecor}[0-9]{3}/); } $::stack=0; } sub ::file_end { # try to detect if SSE2 or MMX extensions were used on Win32... if ($::win32 && grep {/\b[x]?mm[0-7]\b|OPENSSL_ia32cap_P\b/i} @out) { # $1<<10 sets a reserved bit to signal that variable # was initialized already... my $code=<<___; align 16 ${lbdecor}OPENSSL_ia32cap_init: lea edx,[${nmdecor}OPENSSL_ia32cap_P] cmp DWORD [edx],0 jne NEAR ${lbdecor}nocpuid mov DWORD [edx],1<<10 pushfd pop eax mov ecx,eax xor eax,1<<21 push eax popfd pushfd pop eax xor eax,ecx bt eax,21 jnc NEAR ${lbdecor}nocpuid push ebp push edi push ebx mov edi,edx xor eax,eax cpuid xor eax,eax cmp ebx,'Genu' setne al mov ebp,eax cmp edx,'ineI' setne al or ebp,eax cmp eax,'ntel' setne al or ebp,eax mov eax,1 cpuid cmp ebp,0 jne ${lbdecor}notP4 and ah,15 cmp ah,15 jne ${lbdecor}notP4 or edx,1<<20 ${lbdecor}notP4: bt edx,28 jnc ${lbdecor}done shr ebx,16 cmp bl,1 ja ${lbdecor}done and edx,0xefffffff ${lbdecor}done: or edx,1<<10 mov DWORD [edi],edx pop ebx pop edi pop ebp ${lbdecor}nocpuid: ret segment .CRT\$XCU data align=4 dd ${lbdecor}OPENSSL_ia32cap_init ___ my $data=<<___; segment .bss common ${nmdecor}OPENSSL_ia32cap_P 4 ___ ##push (@out,$code); # comment out OPENSSL_ia32cap_P declarations grep {s/(^extern\s+${nmdecor}OPENSSL_ia32cap_P)/\;$1/} @out; push (@out,$data) } push (@out,$initseg) if ($initseg); } sub ::comment { foreach (@_) { push(@out,"\t; $_\n"); } } sub islabel # see is argument is known label { my $i; foreach $i (values %label) { return $i if ($i eq $_[0]); } $label{$_[0]}; # can be undef } sub ::external_label { push(@labels,@_); foreach (@_) { push(@out, "${drdecor}extern\t${nmdecor}$_\n"); } } sub ::public_label { $label{$_[0]}="${nmdecor}${_[0]}" if (!defined($label{$_[0]})); push(@out,"${drdecor}global\t$label{$_[0]}\n"); } sub ::label { if (!defined($label{$_[0]})) { $label{$_[0]}="${lbdecor}${label}${_[0]}"; $label++; } $label{$_[0]}; } sub ::set_label { my $label=&::label($_[0]); &::align($_[1]) if ($_[1]>1); push(@out,"$label{$_[0]}:\n"); } sub ::data_byte { push(@out,(($::mwerks)?".byte\t":"db\t").join(',',@_)."\n"); } sub ::data_word { push(@out,(($::mwerks)?".long\t":"dd\t").join(',',@_)."\n"); } sub ::align { push(@out,"${drdecor}align\t$_[0]\n"); } sub ::picmeup { my($dst,$sym)=@_; &::lea($dst,&::DWP($sym)); } sub ::initseg { my($f)=$nmdecor.shift; if ($::win32) { $initseg=<<___; segment .CRT\$XCU data align=4 extern $f dd $f ___ } } 1;