jit bcctrx and misc code cleanup. NES games launched from animal crossing work, but have major video problems...
git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@4504 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
parent
5579a9169d
commit
42cd2838a3
|
@ -251,9 +251,7 @@ union bba_descr {
|
||||||
u32 word;
|
u32 word;
|
||||||
};
|
};
|
||||||
bool CEXIETHERNET::cbwriteDescriptor(u32 size) {
|
bool CEXIETHERNET::cbwriteDescriptor(u32 size) {
|
||||||
//if(size < 0x3C) {//60
|
if(size < SIZEOF_ETH_HEADER)
|
||||||
#define ETHERNET_HEADER_SIZE 0xE
|
|
||||||
if(size < ETHERNET_HEADER_SIZE)
|
|
||||||
{
|
{
|
||||||
DEBUGPRINT("Packet too small: %i bytes\n", size);
|
DEBUGPRINT("Packet too small: %i bytes\n", size);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
// Official SVN repository and contact information can be found at
|
// Official SVN repository and contact information can be found at
|
||||||
// http://code.google.com/p/dolphin-emu/
|
// http://code.google.com/p/dolphin-emu/
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include "StringUtil.h"
|
#include "StringUtil.h"
|
||||||
#include "../Memmap.h"
|
#include "../Memmap.h"
|
||||||
// GROSS CODE ALERT: headers need to be included in the following order
|
// GROSS CODE ALERT: headers need to be included in the following order
|
||||||
|
@ -226,8 +225,7 @@ bool CEXIETHERNET::activate()
|
||||||
if ( !(info[0] > TAP_WIN32_MIN_MAJOR
|
if ( !(info[0] > TAP_WIN32_MIN_MAJOR
|
||||||
|| (info[0] == TAP_WIN32_MIN_MAJOR && info[1] >= TAP_WIN32_MIN_MINOR)) )
|
|| (info[0] == TAP_WIN32_MIN_MAJOR && info[1] >= TAP_WIN32_MIN_MINOR)) )
|
||||||
{
|
{
|
||||||
#define PACKAGE_NAME "Dolphin"
|
DEBUGPRINT("ERROR: This version of Dolphin requires a TAP-Win32 driver that is at least version %d.%d -- If you recently upgraded your Dolphin distribution, a reboot is probably required at this point to get Windows to see the new driver.",
|
||||||
DEBUGPRINT("ERROR: This version of " PACKAGE_NAME " requires a TAP-Win32 driver that is at least version %d.%d -- If you recently upgraded your " PACKAGE_NAME " distribution, a reboot is probably required at this point to get Windows to see the new driver.",
|
|
||||||
TAP_WIN32_MIN_MAJOR,
|
TAP_WIN32_MIN_MAJOR,
|
||||||
TAP_WIN32_MIN_MINOR);
|
TAP_WIN32_MIN_MINOR);
|
||||||
return false;
|
return false;
|
||||||
|
@ -322,7 +320,7 @@ bool CEXIETHERNET::handleRecvdPacket()
|
||||||
//DUMPWORD(mRBRPP);
|
//DUMPWORD(mRBRPP);
|
||||||
//DUMPWORD(available_bytes_in_cb);
|
//DUMPWORD(available_bytes_in_cb);
|
||||||
|
|
||||||
assert(available_bytes_in_cb <= CB_SIZE);
|
_dbg_assert_(SP1, available_bytes_in_cb <= CB_SIZE);
|
||||||
if (available_bytes_in_cb != CB_SIZE)//< mRecvBufferLength + SIZEOF_RECV_DESCRIPTOR)
|
if (available_bytes_in_cb != CB_SIZE)//< mRecvBufferLength + SIZEOF_RECV_DESCRIPTOR)
|
||||||
return true;
|
return true;
|
||||||
cbwriteDescriptor(mRecvBufferLength);
|
cbwriteDescriptor(mRecvBufferLength);
|
||||||
|
@ -440,9 +438,7 @@ union bba_descr
|
||||||
|
|
||||||
bool CEXIETHERNET::cbwriteDescriptor(u32 size)
|
bool CEXIETHERNET::cbwriteDescriptor(u32 size)
|
||||||
{
|
{
|
||||||
//if(size < 0x3C) {//60
|
if (size < SIZEOF_ETH_HEADER)
|
||||||
#define ETHERNET_HEADER_SIZE 0xE
|
|
||||||
if (size < ETHERNET_HEADER_SIZE)
|
|
||||||
{
|
{
|
||||||
DEBUGPRINT("Packet too small: %i bytes", size);
|
DEBUGPRINT("Packet too small: %i bytes", size);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -116,12 +116,13 @@ private:
|
||||||
// TODO: convert into unions
|
// TODO: convert into unions
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
BBA_RECV_SIZE = 0x800,
|
BBA_RECV_SIZE = 0x800,
|
||||||
BBA_MEM_SIZE = 0x1000,
|
BBA_MEM_SIZE = 0x1000,
|
||||||
|
|
||||||
CB_OFFSET = 0x100,
|
CB_OFFSET = 0x100,
|
||||||
CB_SIZE = (BBA_MEM_SIZE - CB_OFFSET),
|
CB_SIZE = (BBA_MEM_SIZE - CB_OFFSET),
|
||||||
SIZEOF_RECV_DESCRIPTOR = 4,
|
SIZEOF_ETH_HEADER = 0xe,
|
||||||
|
SIZEOF_RECV_DESCRIPTOR = 4,
|
||||||
|
|
||||||
EXI_DEVTYPE_ETHER = 0x04020200,
|
EXI_DEVTYPE_ETHER = 0x04020200,
|
||||||
|
|
||||||
|
|
|
@ -589,7 +589,10 @@ enum EQuantizeType
|
||||||
// branches
|
// branches
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
BO_DONT_DECREMENT_FLAG = 0x4,
|
BO_BRANCH_IF_CTR_0 = 2, // 3
|
||||||
|
BO_DONT_DECREMENT_FLAG = 4, // 2
|
||||||
|
BO_BRANCH_IF_TRUE = 8, // 1
|
||||||
|
BO_DONT_CHECK_CONDITION = 16, // 0
|
||||||
};
|
};
|
||||||
|
|
||||||
// Special purpose register indices
|
// Special purpose register indices
|
||||||
|
|
|
@ -69,33 +69,32 @@ void bcx(UGeckoInstruction _inst)
|
||||||
|
|
||||||
void bcctrx(UGeckoInstruction _inst)
|
void bcctrx(UGeckoInstruction _inst)
|
||||||
{
|
{
|
||||||
if ((_inst.BO & BO_DONT_DECREMENT_FLAG) == 0)
|
_dbg_assert_msg_(POWERPC, _inst.BO_2 & BO_DONT_DECREMENT_FLAG, "bcctrx with decrement and test CTR option is invalid!");
|
||||||
CTR--;
|
|
||||||
|
|
||||||
int condition = ((_inst.BO>>4) | (GetCRBit(_inst.BI) == ((_inst.BO>>3) & 1))) & 1;
|
int condition = ((_inst.BO_2>>4) | (GetCRBit(_inst.BI_2) == ((_inst.BO_2>>3) & 1))) & 1;
|
||||||
|
|
||||||
if (condition)
|
if (condition)
|
||||||
{
|
{
|
||||||
if (_inst.LK)
|
|
||||||
LR = PC + 4;
|
|
||||||
NPC = CTR & (~3);
|
NPC = CTR & (~3);
|
||||||
|
if (_inst.LK_3)
|
||||||
|
LR = PC + 4;
|
||||||
}
|
}
|
||||||
m_EndBlock = true;
|
m_EndBlock = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bclrx(UGeckoInstruction _inst)
|
void bclrx(UGeckoInstruction _inst)
|
||||||
{
|
{
|
||||||
if ((_inst.BO & BO_DONT_DECREMENT_FLAG) == 0)
|
if ((_inst.BO_2 & BO_DONT_DECREMENT_FLAG) == 0)
|
||||||
CTR--;
|
CTR--;
|
||||||
|
|
||||||
int counter = ((_inst.BO >> 2) | ((CTR != 0) ^ (_inst.BO >> 1)))&1;
|
int counter = ((_inst.BO_2 >> 2) | ((CTR != 0) ^ (_inst.BO_2 >> 1))) & 1;
|
||||||
int condition = ((_inst.BO >> 4) | (GetCRBit(_inst.BI) == ((_inst.BO >> 3) & 1))) & 1;
|
int condition = ((_inst.BO_2 >> 4) | (GetCRBit(_inst.BI_2) == ((_inst.BO_2 >> 3) & 1))) & 1;
|
||||||
|
|
||||||
if (counter & condition)
|
if (counter & condition)
|
||||||
{
|
{
|
||||||
NPC = LR & (~3);
|
NPC = LR & (~3);
|
||||||
if (_inst.LK)
|
if (_inst.LK_3)
|
||||||
LR = PC+4;
|
LR = PC + 4;
|
||||||
}
|
}
|
||||||
m_EndBlock = true;
|
m_EndBlock = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,10 +42,8 @@ using namespace Gen;
|
||||||
|
|
||||||
void Jit64::sc(UGeckoInstruction inst)
|
void Jit64::sc(UGeckoInstruction inst)
|
||||||
{
|
{
|
||||||
if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITBranchOff)
|
INSTRUCTION_START
|
||||||
{Default(inst); return;} // turn off from debugger
|
JITDISABLE(Branch)
|
||||||
|
|
||||||
INSTRUCTION_START;
|
|
||||||
|
|
||||||
gpr.Flush(FLUSH_ALL);
|
gpr.Flush(FLUSH_ALL);
|
||||||
fpr.Flush(FLUSH_ALL);
|
fpr.Flush(FLUSH_ALL);
|
||||||
|
@ -54,10 +52,8 @@ void Jit64::sc(UGeckoInstruction inst)
|
||||||
|
|
||||||
void Jit64::rfi(UGeckoInstruction inst)
|
void Jit64::rfi(UGeckoInstruction inst)
|
||||||
{
|
{
|
||||||
if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITBranchOff)
|
INSTRUCTION_START
|
||||||
{Default(inst); return;} // turn off from debugger
|
JITDISABLE(Branch)
|
||||||
|
|
||||||
INSTRUCTION_START;
|
|
||||||
|
|
||||||
gpr.Flush(FLUSH_ALL);
|
gpr.Flush(FLUSH_ALL);
|
||||||
fpr.Flush(FLUSH_ALL);
|
fpr.Flush(FLUSH_ALL);
|
||||||
|
@ -79,10 +75,8 @@ void Jit64::rfi(UGeckoInstruction inst)
|
||||||
|
|
||||||
void Jit64::bx(UGeckoInstruction inst)
|
void Jit64::bx(UGeckoInstruction inst)
|
||||||
{
|
{
|
||||||
if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITBranchOff)
|
INSTRUCTION_START
|
||||||
{Default(inst); return;} // turn off from debugger
|
JITDISABLE(Branch)
|
||||||
|
|
||||||
INSTRUCTION_START;
|
|
||||||
|
|
||||||
if (inst.LK)
|
if (inst.LK)
|
||||||
MOV(32, M(&LR), Imm32(js.compilerPC + 4));
|
MOV(32, M(&LR), Imm32(js.compilerPC + 4));
|
||||||
|
@ -121,10 +115,8 @@ void Jit64::bx(UGeckoInstruction inst)
|
||||||
// variants of this instruction.
|
// variants of this instruction.
|
||||||
void Jit64::bcx(UGeckoInstruction inst)
|
void Jit64::bcx(UGeckoInstruction inst)
|
||||||
{
|
{
|
||||||
if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITBranchOff)
|
INSTRUCTION_START
|
||||||
{Default(inst); return;} // turn off from debugger
|
JITDISABLE(Branch)
|
||||||
|
|
||||||
INSTRUCTION_START;
|
|
||||||
|
|
||||||
// USES_CR
|
// USES_CR
|
||||||
_assert_msg_(DYNA_REC, js.isLastInstruction, "bcx not last instruction of block");
|
_assert_msg_(DYNA_REC, js.isLastInstruction, "bcx not last instruction of block");
|
||||||
|
@ -138,13 +130,13 @@ void Jit64::bcx(UGeckoInstruction inst)
|
||||||
//const bool only_condition_check = (inst.BO & 4) ? true : false;
|
//const bool only_condition_check = (inst.BO & 4) ? true : false;
|
||||||
//if (only_condition_check && only_counter_check)
|
//if (only_condition_check && only_counter_check)
|
||||||
// PanicAlert("Bizarre bcx encountered. Likely bad or corrupt code.");
|
// PanicAlert("Bizarre bcx encountered. Likely bad or corrupt code.");
|
||||||
bool doFullTest = (inst.BO & 16) == 0 && (inst.BO & 4) == 0;
|
bool doFullTest = ((inst.BO & BO_DONT_CHECK_CONDITION) == 0) && ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0);
|
||||||
bool ctrDecremented = false;
|
bool ctrDecremented = false;
|
||||||
|
|
||||||
if ((inst.BO & 16) == 0) // Test a CR bit
|
if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0) // Test a CR bit
|
||||||
{
|
{
|
||||||
TEST(8, M(&PowerPC::ppcState.cr_fast[inst.BI >> 2]), Imm8(8 >> (inst.BI & 3)));
|
TEST(8, M(&PowerPC::ppcState.cr_fast[inst.BI >> 2]), Imm8(8 >> (inst.BI & 3)));
|
||||||
if (inst.BO & 8) // Conditional branch
|
if (inst.BO & BO_BRANCH_IF_TRUE) // Conditional branch
|
||||||
branch = CC_NZ;
|
branch = CC_NZ;
|
||||||
else
|
else
|
||||||
branch = CC_Z;
|
branch = CC_Z;
|
||||||
|
@ -158,13 +150,13 @@ void Jit64::bcx(UGeckoInstruction inst)
|
||||||
MOV(32, R(EAX), Imm32(1));
|
MOV(32, R(EAX), Imm32(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((inst.BO & 4) == 0) // Decrement and test CTR
|
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) // Decrement and test CTR
|
||||||
{
|
{
|
||||||
// Decrement CTR
|
// Decrement CTR
|
||||||
SUB(32, M(&CTR), Imm8(1));
|
SUB(32, M(&CTR), Imm8(1));
|
||||||
ctrDecremented = true;
|
ctrDecremented = true;
|
||||||
// Test whether to branch if CTR is zero or not
|
// Test whether to branch if CTR is zero or not
|
||||||
if (inst.BO & 2)
|
if (inst.BO & BO_BRANCH_IF_CTR_0)
|
||||||
branch = CC_Z;
|
branch = CC_Z;
|
||||||
else
|
else
|
||||||
branch = CC_NZ;
|
branch = CC_NZ;
|
||||||
|
@ -217,56 +209,58 @@ void Jit64::bcx(UGeckoInstruction inst)
|
||||||
|
|
||||||
void Jit64::bcctrx(UGeckoInstruction inst)
|
void Jit64::bcctrx(UGeckoInstruction inst)
|
||||||
{
|
{
|
||||||
if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITBranchOff)
|
INSTRUCTION_START
|
||||||
{Default(inst); return;} // turn off from debugger
|
JITDISABLE(Branch)
|
||||||
|
|
||||||
INSTRUCTION_START;
|
|
||||||
|
|
||||||
gpr.Flush(FLUSH_ALL);
|
gpr.Flush(FLUSH_ALL);
|
||||||
fpr.Flush(FLUSH_ALL);
|
fpr.Flush(FLUSH_ALL);
|
||||||
|
|
||||||
// bool fastway = true;
|
// bcctrx doesn't decrement and/or test CTR
|
||||||
|
_dbg_assert_msg_(POWERPC, inst.BO_2 & BO_DONT_DECREMENT_FLAG, "bcctrx with decrement and test CTR option is invalid!");
|
||||||
|
|
||||||
if ((inst.BO & 16) == 0)
|
if (inst.BO_2 & BO_DONT_CHECK_CONDITION)
|
||||||
{
|
{
|
||||||
// Rare condition usually used by NES Emulators
|
// BO_2 == 1z1zz -> b always
|
||||||
// TODO: JIT does not support this
|
|
||||||
ERROR_LOG(DYNA_REC, "Bizarro bcctrx %08x, not supported.", inst.hex);
|
|
||||||
//_assert_msg_(DYNA_REC, 0, "Bizarro bcctrx");
|
|
||||||
/*
|
|
||||||
fastway = false;
|
|
||||||
MOV(32, M(&PC), Imm32(js.compilerPC+4));
|
|
||||||
MOV(32, R(EAX), M(&CR));
|
|
||||||
XOR(32, R(ECX), R(ECX));
|
|
||||||
AND(32, R(EAX), Imm32(0x80000000 >> inst.BI));
|
|
||||||
|
|
||||||
CCFlags branch;
|
//NPC = CTR & 0xfffffffc;
|
||||||
if(inst.BO & 8)
|
MOV(32, R(EAX), M(&CTR));
|
||||||
branch = CC_NZ;
|
if (inst.LK_3)
|
||||||
else
|
MOV(32, M(&LR), Imm32(js.compilerPC + 4)); // LR = PC + 4;
|
||||||
branch = CC_Z;
|
AND(32, R(EAX), Imm32(0xFFFFFFFC));
|
||||||
*/
|
WriteExitDestInEAX(0);
|
||||||
// TODO(ector): Why is this commented out?
|
}
|
||||||
//SETcc(branch, R(ECX));
|
else
|
||||||
// check for EBX
|
{
|
||||||
//TEST(32, R(ECX), R(ECX));
|
// Rare condition seen in (just some versions of?) Nintendo's NES Emulator
|
||||||
//linkEnd = J_CC(branch);
|
|
||||||
|
// BO_2 == 001zy -> b if false
|
||||||
|
// BO_2 == 011zy -> b if true
|
||||||
|
|
||||||
|
// Ripped from bclrx
|
||||||
|
TEST(8, M(&PowerPC::ppcState.cr_fast[inst.BI >> 2]), Imm8(8 >> (inst.BI & 3)));
|
||||||
|
Gen::CCFlags branch;
|
||||||
|
if (inst.BO_2 & BO_BRANCH_IF_TRUE)
|
||||||
|
branch = CC_Z;
|
||||||
|
else
|
||||||
|
branch = CC_NZ;
|
||||||
|
MOV(32, R(EAX), Imm32(js.compilerPC + 4));
|
||||||
|
FixupBranch b = J_CC(branch, false);
|
||||||
|
MOV(32, R(EAX), M(&CTR));
|
||||||
|
MOV(32, M(&PC), R(EAX));
|
||||||
|
if (inst.LK_3)
|
||||||
|
MOV(32, M(&LR), Imm32(js.compilerPC + 4)); // LR = PC + 4;
|
||||||
|
// Would really like to continue the block here, but it ends. TODO.
|
||||||
|
SetJumpTarget(b);
|
||||||
|
WriteExitDestInEAX(0);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
// NPC = CTR & 0xfffffffc;
|
|
||||||
MOV(32, R(EAX), M(&CTR));
|
|
||||||
if (inst.LK)
|
|
||||||
MOV(32, M(&LR), Imm32(js.compilerPC + 4)); // LR = PC + 4;
|
|
||||||
AND(32, R(EAX), Imm32(0xFFFFFFFC));
|
|
||||||
WriteExitDestInEAX(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Jit64::bclrx(UGeckoInstruction inst)
|
void Jit64::bclrx(UGeckoInstruction inst)
|
||||||
{
|
{
|
||||||
if (Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITBranchOff)
|
INSTRUCTION_START
|
||||||
{Default(inst); return;} // turn off from debugger
|
JITDISABLE(Branch)
|
||||||
|
|
||||||
INSTRUCTION_START;
|
|
||||||
|
|
||||||
gpr.Flush(FLUSH_ALL);
|
gpr.Flush(FLUSH_ALL);
|
||||||
fpr.Flush(FLUSH_ALL);
|
fpr.Flush(FLUSH_ALL);
|
||||||
|
@ -281,13 +275,14 @@ void Jit64::bclrx(UGeckoInstruction inst)
|
||||||
#endif
|
#endif
|
||||||
MOV(32, R(EAX), M(&LR));
|
MOV(32, R(EAX), M(&LR));
|
||||||
MOV(32, M(&PC), R(EAX));
|
MOV(32, M(&PC), R(EAX));
|
||||||
if (inst.LK)
|
if (inst.LK_3)
|
||||||
MOV(32, M(&LR), Imm32(js.compilerPC + 4)); // LR = PC + 4;
|
MOV(32, M(&LR), Imm32(js.compilerPC + 4)); // LR = PC + 4;
|
||||||
WriteExitDestInEAX(0);
|
WriteExitDestInEAX(0);
|
||||||
return;
|
return;
|
||||||
} else if ((inst.BO & 4) == 0) {
|
} else if ((inst.BO_2 & BO_DONT_DECREMENT_FLAG) == 0) {
|
||||||
// Decrement CTR?? in bclrx?? this goes to fallback.
|
// Decrement CTR. Not mutually exclusive...
|
||||||
} else if ((inst.BO & 16) == 0) {
|
// Will fall back to int, but we should be able to do it here, sometime
|
||||||
|
} else if ((inst.BO_2 & BO_DONT_CHECK_CONDITION) == 0) {
|
||||||
// Test a CR bit. Not too hard.
|
// Test a CR bit. Not too hard.
|
||||||
// beqlr- 4d820020
|
// beqlr- 4d820020
|
||||||
// blelr- 4c810020
|
// blelr- 4c810020
|
||||||
|
@ -296,7 +291,7 @@ void Jit64::bclrx(UGeckoInstruction inst)
|
||||||
// etc...
|
// etc...
|
||||||
TEST(8, M(&PowerPC::ppcState.cr_fast[inst.BI >> 2]), Imm8(8 >> (inst.BI & 3)));
|
TEST(8, M(&PowerPC::ppcState.cr_fast[inst.BI >> 2]), Imm8(8 >> (inst.BI & 3)));
|
||||||
Gen::CCFlags branch;
|
Gen::CCFlags branch;
|
||||||
if (inst.BO & 8)
|
if (inst.BO_2 & BO_BRANCH_IF_TRUE)
|
||||||
branch = CC_Z;
|
branch = CC_Z;
|
||||||
else
|
else
|
||||||
branch = CC_NZ;
|
branch = CC_NZ;
|
||||||
|
@ -304,7 +299,7 @@ void Jit64::bclrx(UGeckoInstruction inst)
|
||||||
FixupBranch b = J_CC(branch, false);
|
FixupBranch b = J_CC(branch, false);
|
||||||
MOV(32, R(EAX), M(&LR));
|
MOV(32, R(EAX), M(&LR));
|
||||||
MOV(32, M(&PC), R(EAX));
|
MOV(32, M(&PC), R(EAX));
|
||||||
if (inst.LK)
|
if (inst.LK_3)
|
||||||
MOV(32, M(&LR), Imm32(js.compilerPC + 4)); // LR = PC + 4;
|
MOV(32, M(&LR), Imm32(js.compilerPC + 4)); // LR = PC + 4;
|
||||||
// Would really like to continue the block here, but it ends. TODO.
|
// Would really like to continue the block here, but it ends. TODO.
|
||||||
SetJumpTarget(b);
|
SetJumpTarget(b);
|
||||||
|
|
Loading…
Reference in New Issue