mirror of
https://github.com/libretro/parallel-ps2.git
synced 2026-11-10 17:26:34 -10:00
Three changes, one intent: bare `make` and bare cmake now build the C89 emitter with nothing to specify, and common/emitter contains only the six headers the recompilers actually use (c89emit.h, x86emitter.h, x86types.h, instructions.h, legacy_instructions.h, x86emitter_shim.h). Default flip. Makefile gains `C89_EMITTER ?= 1` -- set BEFORE the Makefile.common include, because the source exclusion there reads it. The first attempt set it next to the CXXFLAGS append at line 839, after the include: the define applied but the exclusion never fired, and the build went on linking reference objects it no longer needed. Caught by `make -n` grep: a bare plan now names zero reference objects, a C89_EMITTER=0 plan names all eight. CMake's option defaults to ON with the same semantics (-DC89_EMITTER=OFF for the reference). Reference headers. internal.h, legacy_internal.h and the thirteen-header implement/ tree move to tests/emitter/reference beside the sources that need them. internal.h now includes the implement set itself -- it is the reference implementation's own root and must be self-sufficient; x86types.h includes them only under the reference configuration, for the extern declarations in instructions.h's #else branches. The three G*Type operator enums move into x86types.h: recompilers name their values, which makes them API, not implementation. iR5900-32.cpp's MOVQ-workaround include of internal.h was already reference-gated and is repointed. cmake's installed- header list is repointed too (the stale entries failed configure). Legacy jump API. The fourteen rel8/rel32 wrappers become macros over two static helpers (J8Rel, J32Rel), matching the C89 core's idiom. The helpers are static -- internal linkage per TU -- so there is no header-linkage subtlety left to get wrong, where the previous inline definitions carried one. The speculative conditional mode initially drafted into J32Rel was removed before commit: only the unconditional form has callers. The legacy SSE C-API wrappers (SSE_MINSS_XMM_to_XMM and friends) now emit through E_SSE_RR directly instead of hopping through the C++ shim objects. Their constants are proven end-to-end in newbind: reference function vs C89 macro across all register pairs, 2,048 cases within the 110,070 total. Verified: six suites green, switchover identical, both compile gates 31/31 in both configurations, bare make links at 11,351,104 bytes with zero reference encoder symbols and boots, cmake configure passes in both configurations. The cmake three-target build has not completed in this container (cmake 4 needs -DCMAKE_POLICY_VERSION_MINIMUM=3.5 for the vendored cpuinfo; GL dev packages were also missing) -- it needs one run on a normally-provisioned machine. C89_EMITTER=0 static: the gates and suites compile and byte-verify that configuration; its last full link ran two commits ago unchanged.
246 lines
8.2 KiB
C++
246 lines
8.2 KiB
C++
/* PCSX2 - PS2 Emulator for PCs
|
|
* Copyright (C) 2002-2010 PCSX2 Dev Team
|
|
*
|
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with PCSX2.
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*
|
|
* ix86 core v0.9.1
|
|
*
|
|
* Original Authors (v0.6.2 and prior):
|
|
* linuzappz <[email protected]>
|
|
* alexey silinov
|
|
* goldfinger
|
|
* zerofrog(@gmail.com)
|
|
*
|
|
* Authors of v0.9.1:
|
|
* Jake.Stine(@gmail.com)
|
|
* cottonvibes(@gmail.com)
|
|
* sudonim([email protected])
|
|
*/
|
|
|
|
#include "tests/emitter/reference/internal.h"
|
|
|
|
namespace x86Emitter
|
|
{
|
|
|
|
// =====================================================================================================
|
|
// Group 1 Instructions - ADD, SUB, ADC, etc.
|
|
// =====================================================================================================
|
|
|
|
// Note on "[Indirect],Imm" forms : use int as the source operand since it's "reasonably inert" from a
|
|
// compiler perspective. (using uint tends to make the compiler try and fail to match signed immediates
|
|
// with one of the other overloads).
|
|
void xImpl_Group1::operator()(const xIndirect64orLess& sibdest, int imm) const
|
|
{
|
|
if (sibdest.Is8BitOp())
|
|
{
|
|
xOpWrite(sibdest.GetPrefix16(), 0x80, InstType, sibdest, 1);
|
|
|
|
*(s8*)x86Ptr = imm;
|
|
x86Ptr += sizeof(s8);
|
|
}
|
|
else
|
|
{
|
|
u8 opcode = is_s8(imm) ? 0x83 : 0x81;
|
|
xOpWrite(sibdest.GetPrefix16(), opcode, InstType, sibdest, is_s8(imm) ? 1 : sibdest.GetImmSize());
|
|
|
|
if (is_s8(imm))
|
|
{
|
|
*(s8*)x86Ptr = imm;
|
|
x86Ptr += sizeof(s8);
|
|
}
|
|
else
|
|
sibdest.xWriteImm(imm);
|
|
}
|
|
}
|
|
|
|
void xImpl_Group1::operator()(const xRegisterInt& to, const xRegisterInt& from) const
|
|
{
|
|
u8 opcode = (to.Is8BitOp() ? 0 : 1) | (InstType << 3);
|
|
xOpWrite(to.GetPrefix16(), opcode, from, to);
|
|
}
|
|
|
|
void xImpl_Group1::operator()(const xIndirectVoid& sibdest, const xRegisterInt& from) const
|
|
{
|
|
u8 opcode = (from.Is8BitOp() ? 0 : 1) | (InstType << 3);
|
|
xOpWrite(from.GetPrefix16(), opcode, from, sibdest);
|
|
}
|
|
|
|
void xImpl_Group1::operator()(const xRegisterInt& to, const xIndirectVoid& sibsrc) const
|
|
{
|
|
u8 opcode = (to.Is8BitOp() ? 2 : 3) | (InstType << 3);
|
|
xOpWrite(to.GetPrefix16(), opcode, to, sibsrc);
|
|
}
|
|
|
|
void xImpl_Group1::operator()(const xRegisterInt& to, int imm) const
|
|
{
|
|
if (!to.Is8BitOp() && is_s8(imm))
|
|
{
|
|
xOpWrite(to.GetPrefix16(), 0x83, InstType, to);
|
|
*(s8*)x86Ptr = imm;
|
|
x86Ptr += sizeof(s8);
|
|
}
|
|
else
|
|
{
|
|
if (to.Id == 0)
|
|
{
|
|
u8 opcode = (to.Is8BitOp() ? 4 : 5) | (InstType << 3);
|
|
xOpAccWrite(to.GetPrefix16(), opcode, InstType, to);
|
|
}
|
|
else
|
|
{
|
|
u8 opcode = to.Is8BitOp() ? 0x80 : 0x81;
|
|
xOpWrite(to.GetPrefix16(), opcode, InstType, to);
|
|
}
|
|
to.xWriteImm(imm);
|
|
}
|
|
}
|
|
|
|
#ifndef PCSX2_C89_EMITTER
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImpl_G1Logic xAND = {{G1Type_AND}, {0x00, 0x54}, {0x66, 0x54}};
|
|
const xImpl_G1Logic xOR = {{G1Type_OR}, {0x00, 0x56}, {0x66, 0x56}};
|
|
const xImpl_G1Logic xXOR = {{G1Type_XOR}, {0x00, 0x57}, {0x66, 0x57}};
|
|
|
|
const xImpl_G1Arith xADD = {{G1Type_ADD}, {0x00, 0x58}, {0x66, 0x58}, {0xf3, 0x58}, {0xf2, 0x58}};
|
|
const xImpl_G1Arith xSUB = {{G1Type_SUB}, {0x00, 0x5c}, {0x66, 0x5c}, {0xf3, 0x5c}, {0xf2, 0x5c}};
|
|
|
|
const xImpl_Group1 xADC = {G1Type_ADC};
|
|
const xImpl_Group1 xCMP = {G1Type_CMP};
|
|
#endif
|
|
#endif
|
|
|
|
// =====================================================================================================
|
|
// Group 2 Instructions - SHR, SHL, etc.
|
|
// =====================================================================================================
|
|
|
|
void xImpl_Group2::operator()(const xRegisterInt& to, const xRegisterCL& /* from */) const
|
|
{
|
|
xOpWrite(to.GetPrefix16(), to.Is8BitOp() ? 0xd2 : 0xd3, InstType, to);
|
|
}
|
|
|
|
void xImpl_Group2::operator()(const xRegisterInt& to, u8 imm) const
|
|
{
|
|
if (imm == 0)
|
|
return;
|
|
|
|
if (imm == 1)
|
|
{
|
|
// special encoding of 1's
|
|
xOpWrite(to.GetPrefix16(), to.Is8BitOp() ? 0xd0 : 0xd1, InstType, to);
|
|
}
|
|
else
|
|
{
|
|
xOpWrite(to.GetPrefix16(), to.Is8BitOp() ? 0xc0 : 0xc1, InstType, to);
|
|
xWrite8(imm);
|
|
}
|
|
}
|
|
|
|
void xImpl_Group2::operator()(const xIndirect64orLess& sibdest, const xRegisterCL& /* from */) const
|
|
{
|
|
xOpWrite(sibdest.GetPrefix16(), sibdest.Is8BitOp() ? 0xd2 : 0xd3, InstType, sibdest);
|
|
}
|
|
|
|
void xImpl_Group2::operator()(const xIndirect64orLess& sibdest, u8 imm) const
|
|
{
|
|
if (imm == 0)
|
|
return;
|
|
|
|
if (imm == 1)
|
|
{
|
|
// special encoding of 1's
|
|
xOpWrite(sibdest.GetPrefix16(), sibdest.Is8BitOp() ? 0xd0 : 0xd1, InstType, sibdest);
|
|
}
|
|
else
|
|
{
|
|
xOpWrite(sibdest.GetPrefix16(), sibdest.Is8BitOp() ? 0xc0 : 0xc1, InstType, sibdest, 1);
|
|
xWrite8(imm);
|
|
}
|
|
}
|
|
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImpl_Group2 xROL = {G2Type_ROL};
|
|
const xImpl_Group2 xROR = {G2Type_ROR};
|
|
const xImpl_Group2 xRCL = {G2Type_RCL};
|
|
const xImpl_Group2 xRCR = {G2Type_RCR};
|
|
const xImpl_Group2 xSHL = {G2Type_SHL};
|
|
const xImpl_Group2 xSHR = {G2Type_SHR};
|
|
const xImpl_Group2 xSAR = {G2Type_SAR};
|
|
#endif
|
|
|
|
|
|
// =====================================================================================================
|
|
// Group 3 Instructions - NOT, NEG, MUL, DIV
|
|
// =====================================================================================================
|
|
|
|
static void _g3_EmitOp(G3Type InstType, const xRegisterInt& from)
|
|
{
|
|
xOpWrite(from.GetPrefix16(), from.Is8BitOp() ? 0xf6 : 0xf7, InstType, from);
|
|
}
|
|
|
|
static void _g3_EmitOp(G3Type InstType, const xIndirect64orLess& from)
|
|
{
|
|
xOpWrite(from.GetPrefix16(), from.Is8BitOp() ? 0xf6 : 0xf7, InstType, from);
|
|
}
|
|
|
|
void xImpl_Group3::operator()(const xRegisterInt& from) const
|
|
{
|
|
xOpWrite(from.GetPrefix16(), from.Is8BitOp() ? 0xf6 : 0xf7, InstType, from);
|
|
}
|
|
|
|
void xImpl_Group3::operator()(const xIndirect64orLess& from) const
|
|
{
|
|
xOpWrite(from.GetPrefix16(), from.Is8BitOp() ? 0xf6 : 0xf7, InstType, from);
|
|
}
|
|
|
|
template <typename SrcType>
|
|
static void _imul_ImmStyle(const xRegisterInt& param1, const SrcType& param2, int imm)
|
|
{
|
|
// xOpWrite, not xOpWrite0F: 6B and 69 are one-byte opcodes. The trailing
|
|
// argument here is an extraRIPOffset, which is what xOpWrite takes. The
|
|
// five-argument xOpWrite0F overload takes an imm8 instead, so it both
|
|
// prepends 0x0F and appends the byte.
|
|
xOpWrite(param1.GetPrefix16(), is_s8(imm) ? 0x6b : 0x69, param1, param2, is_s8(imm) ? 1 : param1.GetImmSize());
|
|
|
|
if (is_s8(imm))
|
|
{
|
|
xWrite8((u8)imm);
|
|
}
|
|
else
|
|
param1.xWriteImm(imm);
|
|
}
|
|
|
|
void xImpl_iMul::operator()(const xRegister32& to, const xRegister32& from) const { xOpWrite0F(0, 0xaf, to, from); }
|
|
void xImpl_iMul::operator()(const xRegister32& to, const xIndirectVoid& src) const { xOpWrite0F(0, 0xaf, to, src); }
|
|
void xImpl_iMul::operator()(const xRegister16& to, const xRegister16& from) const { xOpWrite0F(0x66, 0xaf, to, from); }
|
|
void xImpl_iMul::operator()(const xRegister16& to, const xIndirectVoid& src) const { xOpWrite0F(0x66, 0xaf, to, src); }
|
|
|
|
void xImpl_iMul::operator()(const xRegister32& to, const xRegister32& from, s32 imm) const { _imul_ImmStyle(to, from, imm); }
|
|
void xImpl_iMul::operator()(const xRegister32& to, const xIndirectVoid& from, s32 imm) const { _imul_ImmStyle(to, from, imm); }
|
|
void xImpl_iMul::operator()(const xRegister16& to, const xRegister16& from, s16 imm) const { _imul_ImmStyle(to, from, imm); }
|
|
void xImpl_iMul::operator()(const xRegister16& to, const xIndirectVoid& from, s16 imm) const { _imul_ImmStyle(to, from, imm); }
|
|
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImpl_Group3 xNOT = {G3Type_NOT};
|
|
const xImpl_Group3 xNEG = {G3Type_NEG};
|
|
const xImpl_Group3 xUMUL = {G3Type_MUL};
|
|
const xImpl_Group3 xUDIV = {G3Type_DIV};
|
|
#endif
|
|
|
|
#ifndef PCSX2_C89_EMITTER
|
|
const xImpl_iDiv xDIV = {{G3Type_iDIV}, {0x00, 0x5e}, {0x66, 0x5e}, {0xf3, 0x5e}, {0xf2, 0x5e}};
|
|
const xImpl_iMul xMUL = {{G3Type_iMUL}, {0x00, 0x59}, {0x66, 0x59}, {0xf3, 0x59}, {0xf2, 0x59}};
|
|
#endif
|
|
|
|
} // End namespace x86Emitter
|