Nasm-X86
view release on metacpan or search on metacpan
lib/Nasm/X86.pm view on Meta::CPAN
eval $s;
confess $@ if $@;
}
if (1) # Instructions that take two operands
{my $s = '';
for my $i(@i2)
{my $I = ucfirst $i;
$s .= <<END;
sub $I(\$\$)
{my (\$target, \$source) = \@_;
\@_ == 2 or confess "Two arguments required";
push \@text, qq( $i \$target, \$source\\n);
}
END
}
eval $s;
confess $@ if $@;
}
if (1) # Instructions that take three operands
{my $s = '';
for my $i(@i3)
{my $I = ucfirst $i;
$s .= <<END;
sub $I(\$\$\$)
{my (\$target, \$source, \$bits) = \@_;
\@_ == 3 or confess "Three arguments required";
push \@text, qq( $i \$target, \$source, \$bits\\n);
}
END
}
eval $s;
confess $@ if $@;
}
}
sub ClearRegisters(@); # Clear registers by setting them to zero
sub Comment(@); # Insert a comment into the assembly code
sub PeekR($); # Peek at the register on top of the stack
sub PopR(@); # Pop a list of registers off the stack
sub PrintOutMemory; # Print the memory addressed by rax for a length of rdi
sub PrintOutRegisterInHex($); # Print any register as a hex string
sub PushR(@);
sub Syscall(); # System call in linux 64 format per: https://filippo.io/linux-syscall-table/
#D1 Data # Layout data
my $Labels = 0;
sub Label #P Create a unique label
{"l".++$Labels; # Generate a label
}
sub SetLabel($) # Set a label in the code section
{my ($l) = @_; # Label
push @text, <<END; # Define bytes
$l:
END
}
sub Ds(@) # Layout bytes in memory and return their label
{my (@d) = @_; # Data to be laid out
my $d = join '', @_;
$d =~ s(') (\')gs;
my $l = Label;
push @data, <<END; # Define bytes
$l: db '$d';
END
$l # Return label
}
sub Rs(@) # Layout bytes in read only memory and return their label
{my (@d) = @_; # Data to be laid out
my $d = join '', @_;
$d =~ s(') (\')gs;
return $_ if $_ = $rodatas{$d}; # Data already exists so return it
my $l = Label;
$rodatas{$d} = $l; # Record label
push @rodata, <<END; # Define bytes
$l: db '$d',0;
END
$l # Return label
}
sub Dbwdq($@) #P Layout data
{my ($s, @d) = @_; # Element size, data to be laid out
my $d = join ', ', @d;
my $l = Label;
push @data, <<END;
$l: d$s $d
END
$l # Return label
}
sub Db(@) # Layout bytes in the data segment and return their label
{my (@bytes) = @_; # Bytes to layout
Dbwdq 'b', @_;
}
sub Dw(@) # Layout words in the data segment and return their label
{my (@words) = @_; # Words to layout
Dbwdq 'w', @_;
}
sub Dd(@) # Layout double words in the data segment and return their label
{my (@dwords) = @_; # Double words to layout
Dbwdq 'd', @_;
}
sub Dq(@) # Layout quad words in the data segment and return their label
{my (@qwords) = @_; # Quad words to layout
Dbwdq 'q', @_;
}
sub Rbwdq($@) #P Layout data
{my ($s, @d) = @_; # Element size, data to be laid out
my $d = join ', ', @d; # Data to be laid out
return $_ if $_ = $rodata{$d}; # Data already exists so return it
my $l = Label; # New data - create a label
push @rodata, <<END; # Save in read only data
$l: d$s $d
END
$rodata{$d} = $l; # Record label
$l # Return label
}
sub Rb(@) # Layout bytes in the data segment and return their label
{my (@bytes) = @_; # Bytes to layout
Rbwdq 'b', @_;
}
sub Rw(@) # Layout words in the data segment and return their label
{my (@words) = @_; # Words to layout
Rbwdq 'w', @_;
}
sub Rd(@) # Layout double words in the data segment and return their label
{my (@dwords) = @_; # Double words to layout
Rbwdq 'd', @_;
}
sub Rq(@) # Layout quad words in the data segment and return their label
{my (@qwords) = @_; # Quad words to layout
Rbwdq 'q', @_;
}
#D1 Registers # Operations on registers
sub SaveFirstFour() # Save the first 4 parameter registers
{Push rax;
Push rdi;
Push rsi;
Push rdx;
4 * &RegisterSize(rax); # Space occupied by push
}
sub RestoreFirstFour() # Restore the first 4 parameter registers
{Pop rdx;
Pop rsi;
Pop rdi;
Pop rax;
}
sub RestoreFirstFourExceptRax() # Restore the first 4 parameter registers except rax so it can return its value
{Pop rdx;
Pop rsi;
Pop rdi;
Add rsp, 8;
}
sub SaveFirstSeven() # Save the first 7 parameter registers
{Push rax;
Push rdi;
Push rsi;
Push rdx;
Push r10;
Push r8;
Push r9;
7 * RegisterSize(rax); # Space occupied by push
}
sub RestoreFirstSeven() # Restore the first 7 parameter registers
{Pop r9;
Pop r8;
Pop r10;
Pop rdx;
Pop rsi;
Pop rdi;
Pop rax;
}
sub RestoreFirstSevenExceptRax() # Restore the first 7 parameter registers except rax which is being used to return the result
{Pop r9;
Pop r8;
Pop r10;
Pop rdx;
Pop rsi;
Pop rdi;
Add rsp, RegisterSize(rax); # Skip rax
}
sub RestoreFirstSevenExceptRaxAndRdi() # Restore the first 7 parameter registers except rax and rdi which are being used to return the results
{Pop r9;
Pop r8;
Pop r10;
Pop rdx;
Pop rsi;
Add rsp, 2*RegisterSize(rax); # Skip rdi and rax
}
sub RegisterSize($) # Return the size of a register
{my ($r) = @_; # Register
return 16 if $r =~ m(\Ax);
return 32 if $r =~ m(\Ay);
return 64 if $r =~ m(\Az);
8
}
sub ClearRegisters(@) # Clear registers by setting them to zero
{my (@registers) = @_; # Registers
@_ == 1 or confess;
for my $r(@registers)
{my $size = RegisterSize $r;
Xor $r, $r if $size == 8;
Vpxorq $r, $r if $size > 8;
}
}
#D1 Structured Programming # Structured programming constructs
sub If(&;&) # If
{my ($then, $else) = @_; # Then - required , else - optional
@_ >= 1 or confess;
if (@_ == 1) # No else
{Comment "if then";
my $end = Label;
Jz $end;
&$then;
SetLabel $end;
}
else # With else
{Comment "if then else";
my $endIf = Label;
my $startElse = Label;
Jz $startElse;
&$then;
Jmp $endIf;
SetLabel $startElse;
&$else;
SetLabel $endIf;
}
}
sub For(&$$$) # For
{my ($body, $register, $limit, $increment) = @_; # Body, register, limit on loop, increment
@_ == 4 or confess;
Comment "For $register $limit";
my $start = Label;
my $end = Label;
SetLabel $start;
Cmp $register, $limit;
Jge $end;
&$body;
if ($increment == 1)
{Inc $register;
}
else
{Add $register, $increment;
}
Jmp $start;
SetLabel $end;
}
sub S(&%) # Create a sub with optional parameters name=> the name of the subroutine so it can be reused rather than regenerated, comment=> a comment describing the sub
{my ($body, %options) = @_; # Body, options.
@_ >= 1 or confess;
my $name = $options{name}; # Optional name for subroutine reuse
my $comment = $options{comment}; # Optional comment
Comment "Subroutine " .($comment//'');
if ($name and my $n = $subroutines{$name}) {return $n} # Return the label of a pre-existing copy of the code
my $start = Label;
my $end = Label;
Jmp $end;
SetLabel $start;
&$body;
Ret;
SetLabel $end;
( run in 1.667 second using v1.01-cache-2.11-cpan-a49fcb8fa48 )