CPU-x86_64-InstructionWriter
view release on metacpan or search on metacpan
lib/CPU/x86_64/InstructionWriter.pm view on Meta::CPAN
$reg1= ($regnum32{$reg1} // croak("$reg1 is not a 32-bit register"));
$reg2= ($regnum32{$reg2} // croak("$reg2 is not a 32-bit register"));
$self->{_buf} .= $self->_encode_op_reg_reg(0, $opcode, $reg1, $reg2);
$self;
}
sub _append_op32_regnum_reg {
my ($self, $opcode, $regnum, $reg2)= @_;
$reg2= ($regnum32{$reg2} // croak("$reg2 is not a 32-bit register"));
$self->{_buf} .= $self->_encode_op_reg_reg(0, $opcode, $regnum, $reg2);
$self;
}
sub _append_op16_reg_reg {
my ($self, $opcode, $reg1, $reg2)= @_;
$reg1= ($regnum16{$reg1} // croak("$reg1 is not a 16-bit register"));
$reg2= ($regnum16{$reg2} // croak("$reg2 is not a 16-bit register"));
$self->{_buf} .= "\x66" . $self->_encode_op_reg_reg(0, $opcode, $reg1, $reg2);
$self;
}
sub _append_op16_regnum_reg {
my ($self, $opcode, $regnum, $reg2)= @_;
$reg2= ($regnum16{$reg2} // croak("$reg2 is not a 16-bit register"));
$self->{_buf} .= "\x66" . $self->_encode_op_reg_reg(0, $opcode, $regnum, $reg2);
$self;
}
sub _append_op8_regnum_reg {
_append_op8_reg_reg(@_, $_[2]);
}
sub _append_op8_reg_reg {
my ($self, $opcode, $reg1, $reg2, $reg1num)= @_;
$reg1num //= $regnum8{$reg1} // croak "$reg1 is not a valid 8-bit register";
my $reg2num= $regnum8{$reg2} // croak "$reg2 is not a valid 8-bit register";
# special case for the "high byte" registers. They can't be used in an
# instruction that uses the REX prefix.
my $uses_high_byte= ($reg1num|$reg2num)&0x10;
my $uses_extended_reg= ($reg1num|$reg2num)&0x20;
my $mod_rm= 0xC0 | (($reg1num & 7) << 3) | ($reg2num & 7);
if ($uses_extended_reg) {
croak "Can't combine $reg1 with $reg2 in same instruction"
if $uses_high_byte;
$self->{_buf} .= pack('CCC', 0x40|(($reg1num & 8) >> 1) | (($reg2num & 8) >> 3), $opcode, $mod_rm)
} else {
$self->{_buf} .= pack('CC', $opcode, $mod_rm);
}
$self;
}
sub _append_op128_xreg_xreg {
my ($self, $prefix, $rex, $opcode, $xreg1, $xreg2)= @_;
$xreg1= $regnum128{$xreg1} // croak("$xreg1 is not a 128-bit register");
$xreg2= $regnum128{$xreg2} // croak("$xreg2 is not a 128-bit register");
$rex |= ($xreg1 & 8) >> 1 | ($xreg2 & 8) >> 3;
my $modrm= 0xC0 | ($xreg1 & 7) << 3 | ($xreg2 & 7);
$_[0]{_buf} .= !$rex? pack('a*CCC', $prefix, $opcode >> 8, $opcode & 0xFF, $modrm)
: pack('a*CCCC', $prefix, 0x40|$rex, $opcode >> 8, $opcode & 0xFF, $modrm);
$_[0]
}
sub _append_op128_xreg_reg {
my ($self, $prefix, $rex, $opcode, $xreg, $reg)= @_;
$xreg= $regnum128{$xreg} // croak("$xreg is not a 128-bit register");
if (defined(my $regid= $regnum64{$reg})) {
$rex |= 8;
$reg= $regid;
} elsif (defined($regid= $regnum32{$reg})) {
$reg= $regid;
} else {
croak("$reg is not a 32 or 64-bit register");
}
$rex |= ($xreg & 8) >> 1 | ($reg & 8) >> 3;
my $modrm= 0xC0 | ($xreg & 7) << 3 | ($reg & 7);
$_[0]{_buf} .= !$rex? pack('a*CCC', $prefix, $opcode >> 8, $opcode & 0xFF, $modrm)
: pack('a*CCCC', $prefix, 0x40|$rex, $opcode >> 8, $opcode & 0xFF, $modrm);
$_[0]
}
#=head2 _append_op##_reg_mem
#
#Encode standard ##-bit instruction with REX prefix which addresses memory for one of its operands.
#The encoded length might not be resolved until later if an unknown displacement value was given.
#
#=cut
sub _append_op128_xreg_mem {
my ($self, $prefix, $rex, $opcode, $reg, $mem)= @_;
$reg= $regnum128{$reg} // croak "$reg is not a valid 128-bit register";
$self->_append_op_reg_mem($prefix, $rex, $opcode, $reg, $mem);
}
sub _append_op64_reg_mem {
my ($self, $rex, $opcode, $reg, $mem)= @_;
$reg= $regnum64{$reg} // croak "$reg is not a valid 64-bit register"
if defined $reg;
$self->_append_op_reg_mem(undef, $rex, $opcode, $reg, $mem);
}
sub _append_op32_reg_mem {
my ($self, $rex, $opcode, $reg, $mem)= @_;
$reg= $regnum32{$reg} // croak "$reg is not a valid 32-bit register"
if defined $reg;
$self->_append_op_reg_mem(undef, $rex, $opcode, $reg, $mem);
}
sub _append_op16_reg_mem {
my ($self, $rex, $opcode, $reg, $mem)= @_;
$reg= $regnum16{$reg} // croak "$reg is not a valid 16-bit register"
if defined $reg;
$self->_append_op_reg_mem("\x66", $rex, $opcode, $reg, $mem);
}
sub _append_op8_reg_mem {
my ($self, $rex, $opcode, $reg, $mem)= @_;
my $regnum= $regnum8{$reg} // croak "$reg is not a valid 8-bit register";
# special case for needing REX byte for SPL, BPL, DIL, and SIL
$rex |= 0x40 if $regnum & 0x20;
# special case for the "high byte" registers
if ($regnum & 0x10) {
my ($base_reg, $disp, $index_reg, $scale)= @$mem;
croak "Cannot use $reg in instruction with REX prefix"
if $rex || (($regnum64{$base_reg//''}//0) & 8) || (($regnum64{$index_reg//''}//0) & 8);
}
$self->_append_op_reg_mem(undef, $rex, $opcode, $regnum, $mem);
}
sub _append_op_reg_mem {
my ($self, $prefix, $rex, $opcode, $regnum, $mem)= @_;
( run in 3.147 seconds using v1.01-cache-2.11-cpan-5735350b133 )