Mass find & replace for comments with no spacing
This commit is contained in:
parent
4185ae9208
commit
da482945ad
154 changed files with 781 additions and 781 deletions
|
@ -24,8 +24,8 @@ namespace ChocolArm64.Decoders
|
||||||
{
|
{
|
||||||
public static Condition Invert(this Condition cond)
|
public static Condition Invert(this Condition cond)
|
||||||
{
|
{
|
||||||
//Bit 0 of all conditions is basically a negation bit, so
|
// Bit 0 of all conditions is basically a negation bit, so
|
||||||
//inverting this bit has the effect of inverting the condition.
|
// inverting this bit has the effect of inverting the condition.
|
||||||
return (Condition)((int)cond ^ 1);
|
return (Condition)((int)cond ^ 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,12 +29,12 @@ namespace ChocolArm64.Decoders
|
||||||
|
|
||||||
if (IsBranch(lastOp) && !IsCall(lastOp) && lastOp is IOpCodeBImm op)
|
if (IsBranch(lastOp) && !IsCall(lastOp) && lastOp is IOpCodeBImm op)
|
||||||
{
|
{
|
||||||
//It's possible that the branch on this block lands on the middle of the block.
|
// It's possible that the branch on this block lands on the middle of the block.
|
||||||
//This is more common on tight loops. In this case, we can improve the codegen
|
// This is more common on tight loops. In this case, we can improve the codegen
|
||||||
//a bit by changing the CFG and either making the branch point to the same block
|
// a bit by changing the CFG and either making the branch point to the same block
|
||||||
//(which indicates that the block is a loop that jumps back to the start), and the
|
// (which indicates that the block is a loop that jumps back to the start), and the
|
||||||
//other possible case is a jump somewhere on the middle of the block, which is
|
// other possible case is a jump somewhere on the middle of the block, which is
|
||||||
//also a loop, but in this case we need to split the block in half.
|
// also a loop, but in this case we need to split the block in half.
|
||||||
if ((ulong)op.Imm == address)
|
if ((ulong)op.Imm == address)
|
||||||
{
|
{
|
||||||
block.Branch = block;
|
block.Branch = block;
|
||||||
|
@ -79,7 +79,7 @@ namespace ChocolArm64.Decoders
|
||||||
|
|
||||||
while (workQueue.TryDequeue(out Block currBlock))
|
while (workQueue.TryDequeue(out Block currBlock))
|
||||||
{
|
{
|
||||||
//Check if the current block is inside another block.
|
// Check if the current block is inside another block.
|
||||||
if (BinarySearch(blocks, currBlock.Address, out int nBlkIndex))
|
if (BinarySearch(blocks, currBlock.Address, out int nBlkIndex))
|
||||||
{
|
{
|
||||||
Block nBlock = blocks[nBlkIndex];
|
Block nBlock = blocks[nBlkIndex];
|
||||||
|
@ -96,7 +96,7 @@ namespace ChocolArm64.Decoders
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
//If we have a block after the current one, set the limit address.
|
// If we have a block after the current one, set the limit address.
|
||||||
ulong limitAddress = ulong.MaxValue;
|
ulong limitAddress = ulong.MaxValue;
|
||||||
|
|
||||||
if (nBlkIndex != blocks.Count)
|
if (nBlkIndex != blocks.Count)
|
||||||
|
@ -119,10 +119,10 @@ namespace ChocolArm64.Decoders
|
||||||
|
|
||||||
if (currBlock.OpCodes.Count != 0)
|
if (currBlock.OpCodes.Count != 0)
|
||||||
{
|
{
|
||||||
//Set child blocks. "Branch" is the block the branch instruction
|
// Set child blocks. "Branch" is the block the branch instruction
|
||||||
//points to (when taken), "Next" is the block at the next address,
|
// points to (when taken), "Next" is the block at the next address,
|
||||||
//executed when the branch is not taken. For Unconditional Branches
|
// executed when the branch is not taken. For Unconditional Branches
|
||||||
//(except BL/BLR that are sub calls) or end of executable, Next is null.
|
// (except BL/BLR that are sub calls) or end of executable, Next is null.
|
||||||
OpCode64 lastOp = currBlock.GetLastOp();
|
OpCode64 lastOp = currBlock.GetLastOp();
|
||||||
|
|
||||||
bool isCall = IsCall(lastOp);
|
bool isCall = IsCall(lastOp);
|
||||||
|
@ -138,7 +138,7 @@ namespace ChocolArm64.Decoders
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Insert the new block on the list (sorted by address).
|
// Insert the new block on the list (sorted by address).
|
||||||
if (blocks.Count != 0)
|
if (blocks.Count != 0)
|
||||||
{
|
{
|
||||||
Block nBlock = blocks[nBlkIndex];
|
Block nBlock = blocks[nBlkIndex];
|
||||||
|
@ -236,25 +236,25 @@ namespace ChocolArm64.Decoders
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Note: On ARM32, most instructions have conditional execution,
|
// Note: On ARM32, most instructions have conditional execution,
|
||||||
//so there's no "Always" (unconditional) branch like on ARM64.
|
// so there's no "Always" (unconditional) branch like on ARM64.
|
||||||
//We need to check if the condition is "Always" instead.
|
// We need to check if the condition is "Always" instead.
|
||||||
return IsAarch32Branch(op) && op.Cond >= Condition.Al;
|
return IsAarch32Branch(op) && op.Cond >= Condition.Al;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsAarch32Branch(OpCode64 opCode)
|
private static bool IsAarch32Branch(OpCode64 opCode)
|
||||||
{
|
{
|
||||||
//Note: On ARM32, most ALU operations can write to R15 (PC),
|
// Note: On ARM32, most ALU operations can write to R15 (PC),
|
||||||
//so we must consider such operations as a branch in potential as well.
|
// so we must consider such operations as a branch in potential as well.
|
||||||
if (opCode is IOpCode32Alu opAlu && opAlu.Rd == RegisterAlias.Aarch32Pc)
|
if (opCode is IOpCode32Alu opAlu && opAlu.Rd == RegisterAlias.Aarch32Pc)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Same thing for memory operations. We have the cases where PC is a target
|
// Same thing for memory operations. We have the cases where PC is a target
|
||||||
//register (Rt == 15 or (mask & (1 << 15)) != 0), and cases where there is
|
// register (Rt == 15 or (mask & (1 << 15)) != 0), and cases where there is
|
||||||
//a write back to PC (wback == true && Rn == 15), however the later may
|
// a write back to PC (wback == true && Rn == 15), however the later may
|
||||||
//be "undefined" depending on the CPU, so compilers should not produce that.
|
// be "undefined" depending on the CPU, so compilers should not produce that.
|
||||||
if (opCode is IOpCode32Mem || opCode is IOpCode32MemMult)
|
if (opCode is IOpCode32Mem || opCode is IOpCode32MemMult)
|
||||||
{
|
{
|
||||||
int rt, rn;
|
int rt, rn;
|
||||||
|
@ -268,8 +268,8 @@ namespace ChocolArm64.Decoders
|
||||||
wBack = opMem.WBack;
|
wBack = opMem.WBack;
|
||||||
isLoad = opMem.IsLoad;
|
isLoad = opMem.IsLoad;
|
||||||
|
|
||||||
//For the dual load, we also need to take into account the
|
// For the dual load, we also need to take into account the
|
||||||
//case were Rt2 == 15 (PC).
|
// case were Rt2 == 15 (PC).
|
||||||
if (rt == 14 && opMem.Emitter == InstEmit32.Ldrd)
|
if (rt == 14 && opMem.Emitter == InstEmit32.Ldrd)
|
||||||
{
|
{
|
||||||
rt = RegisterAlias.Aarch32Pc;
|
rt = RegisterAlias.Aarch32Pc;
|
||||||
|
@ -296,14 +296,14 @@ namespace ChocolArm64.Decoders
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Explicit branch instructions.
|
// Explicit branch instructions.
|
||||||
return opCode is IOpCode32BImm ||
|
return opCode is IOpCode32BImm ||
|
||||||
opCode is IOpCode32BReg;
|
opCode is IOpCode32BReg;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsCall(OpCode64 opCode)
|
private static bool IsCall(OpCode64 opCode)
|
||||||
{
|
{
|
||||||
//TODO (CQ): ARM32 support.
|
// TODO (CQ): ARM32 support.
|
||||||
return opCode.Emitter == InstEmit.Bl ||
|
return opCode.Emitter == InstEmit.Bl ||
|
||||||
opCode.Emitter == InstEmit.Blr;
|
opCode.Emitter == InstEmit.Blr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,8 @@ namespace ChocolArm64.Decoders
|
||||||
|
|
||||||
public uint GetPc()
|
public uint GetPc()
|
||||||
{
|
{
|
||||||
//Due to backwards compatibility and legacy behavior of ARMv4 CPUs pipeline,
|
// Due to backwards compatibility and legacy behavior of ARMv4 CPUs pipeline,
|
||||||
//the PC actually points 2 instructions ahead.
|
// the PC actually points 2 instructions ahead.
|
||||||
return (uint)Position + (uint)OpCodeSizeInBytes * 2;
|
return (uint)Position + (uint)OpCodeSizeInBytes * 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ namespace ChocolArm64.Decoders
|
||||||
{
|
{
|
||||||
uint pc = GetPc();
|
uint pc = GetPc();
|
||||||
|
|
||||||
//When the condition is never, the instruction is BLX to Thumb mode.
|
// When the condition is never, the instruction is BLX to Thumb mode.
|
||||||
if (Cond != Condition.Nv)
|
if (Cond != Condition.Nv)
|
||||||
{
|
{
|
||||||
pc &= ~3u;
|
pc &= ~3u;
|
||||||
|
|
|
@ -23,16 +23,16 @@ namespace ChocolArm64.Decoders
|
||||||
Extend64 = ((opCode >> 22) & 3) == 2;
|
Extend64 = ((opCode >> 22) & 3) == 2;
|
||||||
WBack = ((opCode >> 24) & 1) == 0;
|
WBack = ((opCode >> 24) & 1) == 0;
|
||||||
|
|
||||||
//The type is not valid for the Unsigned Immediate 12-bits encoding,
|
// The type is not valid for the Unsigned Immediate 12-bits encoding,
|
||||||
//because the bits 11:10 are used for the larger Immediate offset.
|
// because the bits 11:10 are used for the larger Immediate offset.
|
||||||
MemOp type = WBack ? (MemOp)((opCode >> 10) & 3) : MemOp.Unsigned;
|
MemOp type = WBack ? (MemOp)((opCode >> 10) & 3) : MemOp.Unsigned;
|
||||||
|
|
||||||
PostIdx = type == MemOp.PostIndexed;
|
PostIdx = type == MemOp.PostIndexed;
|
||||||
Unscaled = type == MemOp.Unscaled ||
|
Unscaled = type == MemOp.Unscaled ||
|
||||||
type == MemOp.Unprivileged;
|
type == MemOp.Unprivileged;
|
||||||
|
|
||||||
//Unscaled and Unprivileged doesn't write back,
|
// Unscaled and Unprivileged doesn't write back,
|
||||||
//but they do use the 9-bits Signed Immediate.
|
// but they do use the 9-bits Signed Immediate.
|
||||||
if (Unscaled)
|
if (Unscaled)
|
||||||
{
|
{
|
||||||
WBack = false;
|
WBack = false;
|
||||||
|
@ -40,12 +40,12 @@ namespace ChocolArm64.Decoders
|
||||||
|
|
||||||
if (WBack || Unscaled)
|
if (WBack || Unscaled)
|
||||||
{
|
{
|
||||||
//9-bits Signed Immediate.
|
// 9-bits Signed Immediate.
|
||||||
Imm = (opCode << 11) >> 23;
|
Imm = (opCode << 11) >> 23;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//12-bits Unsigned Immediate.
|
// 12-bits Unsigned Immediate.
|
||||||
Imm = ((opCode >> 10) & 0xfff) << Size;
|
Imm = ((opCode >> 10) & 0xfff) << Size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,14 +30,14 @@ namespace ChocolArm64.Decoders
|
||||||
switch (op | (modeLow << 1))
|
switch (op | (modeLow << 1))
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
//64-bits Immediate.
|
// 64-bits Immediate.
|
||||||
//Transform abcd efgh into abcd efgh abcd efgh ...
|
// Transform abcd efgh into abcd efgh abcd efgh ...
|
||||||
imm = (long)((ulong)imm * 0x0101010101010101);
|
imm = (long)((ulong)imm * 0x0101010101010101);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
//64-bits Immediate.
|
// 64-bits Immediate.
|
||||||
//Transform abcd efgh into aaaa aaaa bbbb bbbb ...
|
// Transform abcd efgh into aaaa aaaa bbbb bbbb ...
|
||||||
imm = (imm & 0xf0) >> 4 | (imm & 0x0f) << 4;
|
imm = (imm & 0xf0) >> 4 | (imm & 0x0f) << 4;
|
||||||
imm = (imm & 0xcc) >> 2 | (imm & 0x33) << 2;
|
imm = (imm & 0xcc) >> 2 | (imm & 0x33) << 2;
|
||||||
imm = (imm & 0xaa) >> 1 | (imm & 0x55) << 1;
|
imm = (imm & 0xaa) >> 1 | (imm & 0x55) << 1;
|
||||||
|
@ -52,29 +52,29 @@ namespace ChocolArm64.Decoders
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
case 3:
|
case 3:
|
||||||
//Floating point Immediate.
|
// Floating point Immediate.
|
||||||
imm = DecoderHelper.DecodeImm8Float(imm, Size);
|
imm = DecoderHelper.DecodeImm8Float(imm, Size);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ((modeHigh & 0b110) == 0b100)
|
else if ((modeHigh & 0b110) == 0b100)
|
||||||
{
|
{
|
||||||
//16-bits shifted Immediate.
|
// 16-bits shifted Immediate.
|
||||||
Size = 1; imm <<= (modeHigh & 1) << 3;
|
Size = 1; imm <<= (modeHigh & 1) << 3;
|
||||||
}
|
}
|
||||||
else if ((modeHigh & 0b100) == 0b000)
|
else if ((modeHigh & 0b100) == 0b000)
|
||||||
{
|
{
|
||||||
//32-bits shifted Immediate.
|
// 32-bits shifted Immediate.
|
||||||
Size = 2; imm <<= modeHigh << 3;
|
Size = 2; imm <<= modeHigh << 3;
|
||||||
}
|
}
|
||||||
else if ((modeHigh & 0b111) == 0b110)
|
else if ((modeHigh & 0b111) == 0b110)
|
||||||
{
|
{
|
||||||
//32-bits shifted Immediate (fill with ones).
|
// 32-bits shifted Immediate (fill with ones).
|
||||||
Size = 2; imm = ShlOnes(imm, 8 << modeLow);
|
Size = 2; imm = ShlOnes(imm, 8 << modeLow);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//8 bits without shift.
|
// 8 bits without shift.
|
||||||
Size = 0;
|
Size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -151,7 +151,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
public static void Extr(ILEmitterCtx context)
|
public static void Extr(ILEmitterCtx context)
|
||||||
{
|
{
|
||||||
//TODO: Ensure that the Shift is valid for the Is64Bits.
|
// TODO: Ensure that the Shift is valid for the Is64Bits.
|
||||||
OpCodeAluRs64 op = (OpCodeAluRs64)context.CurrOp;
|
OpCodeAluRs64 op = (OpCodeAluRs64)context.CurrOp;
|
||||||
|
|
||||||
context.EmitLdintzr(op.Rm);
|
context.EmitLdintzr(op.Rm);
|
||||||
|
@ -309,7 +309,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
private static void EmitDiv(ILEmitterCtx context, OpCode ilOp)
|
private static void EmitDiv(ILEmitterCtx context, OpCode ilOp)
|
||||||
{
|
{
|
||||||
//If Rm == 0, Rd = 0 (division by zero).
|
// If Rm == 0, Rd = 0 (division by zero).
|
||||||
context.EmitLdc_I(0);
|
context.EmitLdc_I(0);
|
||||||
|
|
||||||
EmitAluLoadRm(context);
|
EmitAluLoadRm(context);
|
||||||
|
@ -323,7 +323,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
if (ilOp == OpCodes.Div)
|
if (ilOp == OpCodes.Div)
|
||||||
{
|
{
|
||||||
//If Rn == INT_MIN && Rm == -1, Rd = INT_MIN (overflow).
|
// If Rn == INT_MIN && Rm == -1, Rd = INT_MIN (overflow).
|
||||||
long intMin = 1L << (context.CurrOp.GetBitsCount() - 1);
|
long intMin = 1L << (context.CurrOp.GetBitsCount() - 1);
|
||||||
|
|
||||||
context.EmitLdc_I(intMin);
|
context.EmitLdc_I(intMin);
|
||||||
|
@ -381,10 +381,10 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
context.Emit(OpCodes.And);
|
context.Emit(OpCodes.And);
|
||||||
|
|
||||||
//Note: Only 32-bits shift values are valid, so when the value is 64-bits
|
// Note: Only 32-bits shift values are valid, so when the value is 64-bits
|
||||||
//we need to cast it to a 32-bits integer. This is fine because we
|
// we need to cast it to a 32-bits integer. This is fine because we
|
||||||
//AND the value and only keep the lower 5 or 6 bits anyway -- it
|
// AND the value and only keep the lower 5 or 6 bits anyway -- it
|
||||||
//could very well fit on a byte.
|
// could very well fit on a byte.
|
||||||
if (context.CurrOp.RegisterSize != RegisterSize.Int32)
|
if (context.CurrOp.RegisterSize != RegisterSize.Int32)
|
||||||
{
|
{
|
||||||
context.Emit(OpCodes.Conv_I4);
|
context.Emit(OpCodes.Conv_I4);
|
||||||
|
|
|
@ -87,7 +87,7 @@ namespace ChocolArm64.Instructions
|
||||||
{
|
{
|
||||||
if (op.SetFlags)
|
if (op.SetFlags)
|
||||||
{
|
{
|
||||||
//TODO: Load SPSR etc.
|
// TODO: Load SPSR etc.
|
||||||
|
|
||||||
context.EmitLdflg((int)PState.TBit);
|
context.EmitLdflg((int)PState.TBit);
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ namespace ChocolArm64.Instructions
|
||||||
{
|
{
|
||||||
public static void EmitAdcsCCheck(ILEmitterCtx context)
|
public static void EmitAdcsCCheck(ILEmitterCtx context)
|
||||||
{
|
{
|
||||||
//C = (Rd == Rn && CIn) || Rd < Rn
|
// C = (Rd == Rn && CIn) || Rd < Rn
|
||||||
context.EmitSttmp();
|
context.EmitSttmp();
|
||||||
context.EmitLdtmp();
|
context.EmitLdtmp();
|
||||||
context.EmitLdtmp();
|
context.EmitLdtmp();
|
||||||
|
@ -35,7 +35,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
public static void EmitAddsCCheck(ILEmitterCtx context)
|
public static void EmitAddsCCheck(ILEmitterCtx context)
|
||||||
{
|
{
|
||||||
//C = Rd < Rn
|
// C = Rd < Rn
|
||||||
context.Emit(OpCodes.Dup);
|
context.Emit(OpCodes.Dup);
|
||||||
|
|
||||||
EmitAluLoadRn(context);
|
EmitAluLoadRn(context);
|
||||||
|
@ -47,7 +47,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
public static void EmitAddsVCheck(ILEmitterCtx context)
|
public static void EmitAddsVCheck(ILEmitterCtx context)
|
||||||
{
|
{
|
||||||
//V = (Rd ^ Rn) & ~(Rn ^ Rm) < 0
|
// V = (Rd ^ Rn) & ~(Rn ^ Rm) < 0
|
||||||
context.Emit(OpCodes.Dup);
|
context.Emit(OpCodes.Dup);
|
||||||
|
|
||||||
EmitAluLoadRn(context);
|
EmitAluLoadRn(context);
|
||||||
|
@ -69,7 +69,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
public static void EmitSbcsCCheck(ILEmitterCtx context)
|
public static void EmitSbcsCCheck(ILEmitterCtx context)
|
||||||
{
|
{
|
||||||
//C = (Rn == Rm && CIn) || Rn > Rm
|
// C = (Rn == Rm && CIn) || Rn > Rm
|
||||||
EmitAluLoadOpers(context);
|
EmitAluLoadOpers(context);
|
||||||
|
|
||||||
context.Emit(OpCodes.Ceq);
|
context.Emit(OpCodes.Ceq);
|
||||||
|
@ -88,7 +88,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
public static void EmitSubsCCheck(ILEmitterCtx context)
|
public static void EmitSubsCCheck(ILEmitterCtx context)
|
||||||
{
|
{
|
||||||
//C = Rn == Rm || Rn > Rm = !(Rn < Rm)
|
// C = Rn == Rm || Rn > Rm = !(Rn < Rm)
|
||||||
EmitAluLoadOpers(context);
|
EmitAluLoadOpers(context);
|
||||||
|
|
||||||
context.Emit(OpCodes.Clt_Un);
|
context.Emit(OpCodes.Clt_Un);
|
||||||
|
@ -102,7 +102,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
public static void EmitSubsVCheck(ILEmitterCtx context)
|
public static void EmitSubsVCheck(ILEmitterCtx context)
|
||||||
{
|
{
|
||||||
//V = (Rd ^ Rn) & (Rn ^ Rm) < 0
|
// V = (Rd ^ Rn) & (Rn ^ Rm) < 0
|
||||||
context.Emit(OpCodes.Dup);
|
context.Emit(OpCodes.Dup);
|
||||||
|
|
||||||
EmitAluLoadRn(context);
|
EmitAluLoadRn(context);
|
||||||
|
@ -170,7 +170,7 @@ namespace ChocolArm64.Instructions
|
||||||
{
|
{
|
||||||
switch (context.CurrOp)
|
switch (context.CurrOp)
|
||||||
{
|
{
|
||||||
//ARM32.
|
// ARM32.
|
||||||
case OpCode32AluImm op:
|
case OpCode32AluImm op:
|
||||||
context.EmitLdc_I4(op.Imm);
|
context.EmitLdc_I4(op.Imm);
|
||||||
|
|
||||||
|
@ -190,7 +190,7 @@ namespace ChocolArm64.Instructions
|
||||||
context.EmitLdc_I4(op.Imm);
|
context.EmitLdc_I4(op.Imm);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
//ARM64.
|
// ARM64.
|
||||||
case IOpCodeAluImm64 op:
|
case IOpCodeAluImm64 op:
|
||||||
context.EmitLdc_I(op.Imm);
|
context.EmitLdc_I(op.Imm);
|
||||||
break;
|
break;
|
||||||
|
@ -245,7 +245,7 @@ namespace ChocolArm64.Instructions
|
||||||
context.EmitStflg((int)PState.NBit);
|
context.EmitStflg((int)PState.NBit);
|
||||||
}
|
}
|
||||||
|
|
||||||
//ARM32 helpers.
|
// ARM32 helpers.
|
||||||
private static void EmitLoadRmShiftedByImmediate(ILEmitterCtx context, OpCode32AluRsImm op, bool setCarry)
|
private static void EmitLoadRmShiftedByImmediate(ILEmitterCtx context, OpCode32AluRsImm op, bool setCarry)
|
||||||
{
|
{
|
||||||
int shift = op.Imm;
|
int shift = op.Imm;
|
||||||
|
@ -432,7 +432,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
private static void EmitRrxC(ILEmitterCtx context, bool setCarry)
|
private static void EmitRrxC(ILEmitterCtx context, bool setCarry)
|
||||||
{
|
{
|
||||||
//Rotate right by 1 with carry.
|
// Rotate right by 1 with carry.
|
||||||
if (setCarry)
|
if (setCarry)
|
||||||
{
|
{
|
||||||
context.Emit(OpCodes.Dup);
|
context.Emit(OpCodes.Dup);
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
if (op.Pos < op.Shift)
|
if (op.Pos < op.Shift)
|
||||||
{
|
{
|
||||||
//BFI.
|
// BFI.
|
||||||
context.EmitLdintzr(op.Rn);
|
context.EmitLdintzr(op.Rn);
|
||||||
|
|
||||||
int shift = op.GetBitsCount() - op.Shift;
|
int shift = op.GetBitsCount() - op.Shift;
|
||||||
|
@ -39,7 +39,7 @@ namespace ChocolArm64.Instructions
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//BFXIL.
|
// BFXIL.
|
||||||
context.EmitLdintzr(op.Rn);
|
context.EmitLdintzr(op.Rn);
|
||||||
|
|
||||||
context.EmitLsr(op.Shift);
|
context.EmitLsr(op.Shift);
|
||||||
|
|
|
@ -31,8 +31,8 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
context.EmitPrivateCall(typeof(CpuThreadState), mthdName);
|
context.EmitPrivateCall(typeof(CpuThreadState), mthdName);
|
||||||
|
|
||||||
//Check if the thread should still be running, if it isn't then we return 0
|
// Check if the thread should still be running, if it isn't then we return 0
|
||||||
//to force a return to the dispatcher and then exit the thread.
|
// to force a return to the dispatcher and then exit the thread.
|
||||||
context.EmitLdarg(TranslatedSub.StateArgIdx);
|
context.EmitLdarg(TranslatedSub.StateArgIdx);
|
||||||
|
|
||||||
context.EmitCallPropGet(typeof(CpuThreadState), nameof(CpuThreadState.Running));
|
context.EmitCallPropGet(typeof(CpuThreadState), nameof(CpuThreadState.Running));
|
||||||
|
|
|
@ -66,8 +66,8 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
context.EmitStint(GetBankedRegisterAlias(context.Mode, RegisterAlias.Aarch32Lr));
|
context.EmitStint(GetBankedRegisterAlias(context.Mode, RegisterAlias.Aarch32Lr));
|
||||||
|
|
||||||
//If x is true, then this is a branch with link and exchange.
|
// If x is true, then this is a branch with link and exchange.
|
||||||
//In this case we need to swap the mode between Arm <-> Thumb.
|
// In this case we need to swap the mode between Arm <-> Thumb.
|
||||||
if (x)
|
if (x)
|
||||||
{
|
{
|
||||||
context.EmitLdc_I4(isThumb ? 0 : 1);
|
context.EmitLdc_I4(isThumb ? 0 : 1);
|
||||||
|
|
|
@ -90,10 +90,10 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
if (isJump)
|
if (isJump)
|
||||||
{
|
{
|
||||||
//The tail prefix allows the JIT to jump to the next function,
|
// The tail prefix allows the JIT to jump to the next function,
|
||||||
//while releasing the stack space used by the current one.
|
// while releasing the stack space used by the current one.
|
||||||
//This is ideal for BR ARM instructions, which are
|
// This is ideal for BR ARM instructions, which are
|
||||||
//basically indirect tail calls.
|
// basically indirect tail calls.
|
||||||
context.Emit(OpCodes.Tailcall);
|
context.Emit(OpCodes.Tailcall);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,10 +114,10 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
private static void EmitContinueOrReturnCheck(ILEmitterCtx context)
|
private static void EmitContinueOrReturnCheck(ILEmitterCtx context)
|
||||||
{
|
{
|
||||||
//Note: The return value of the called method will be placed
|
// Note: The return value of the called method will be placed
|
||||||
//at the Stack, the return value is always a Int64 with the
|
// at the Stack, the return value is always a Int64 with the
|
||||||
//return address of the function. We check if the address is
|
// return address of the function. We check if the address is
|
||||||
//correct, if it isn't we keep returning until we reach the dispatcher.
|
// correct, if it isn't we keep returning until we reach the dispatcher.
|
||||||
if (context.CurrBlock.Next != null)
|
if (context.CurrBlock.Next != null)
|
||||||
{
|
{
|
||||||
context.Emit(OpCodes.Dup);
|
context.Emit(OpCodes.Dup);
|
||||||
|
|
|
@ -192,7 +192,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
if (!op.PostIdx)
|
if (!op.PostIdx)
|
||||||
{
|
{
|
||||||
//Pre-indexing.
|
// Pre-indexing.
|
||||||
context.EmitLdc_I(op.Imm);
|
context.EmitLdc_I(op.Imm);
|
||||||
|
|
||||||
context.Emit(OpCodes.Add);
|
context.Emit(OpCodes.Add);
|
||||||
|
@ -213,7 +213,7 @@ namespace ChocolArm64.Instructions
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Save address to Scratch var since the register value may change.
|
// Save address to Scratch var since the register value may change.
|
||||||
context.Emit(OpCodes.Dup);
|
context.Emit(OpCodes.Dup);
|
||||||
|
|
||||||
context.EmitSttmp();
|
context.EmitSttmp();
|
||||||
|
@ -221,8 +221,8 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
private static void EmitWBackIfNeeded(ILEmitterCtx context)
|
private static void EmitWBackIfNeeded(ILEmitterCtx context)
|
||||||
{
|
{
|
||||||
//Check whenever the current OpCode has post-indexed write back, if so write it.
|
// Check whenever the current OpCode has post-indexed write back, if so write it.
|
||||||
//Note: AOpCodeMemPair inherits from AOpCodeMemImm, so this works for both.
|
// Note: AOpCodeMemPair inherits from AOpCodeMemImm, so this works for both.
|
||||||
if (context.CurrOp is OpCodeMemImm64 op && op.WBack)
|
if (context.CurrOp is OpCodeMemImm64 op && op.WBack)
|
||||||
{
|
{
|
||||||
context.EmitLdtmp();
|
context.EmitLdtmp();
|
||||||
|
|
|
@ -137,15 +137,15 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
EmitWriteCall(context, WordSizeLog2);
|
EmitWriteCall(context, WordSizeLog2);
|
||||||
|
|
||||||
//Note: If Rn is also specified on the register list,
|
// Note: If Rn is also specified on the register list,
|
||||||
//and Rn is the first register on this list, then the
|
// and Rn is the first register on this list, then the
|
||||||
//value that is written to memory is the unmodified value,
|
// value that is written to memory is the unmodified value,
|
||||||
//before the write back. If it is on the list, but it's
|
// before the write back. If it is on the list, but it's
|
||||||
//not the first one, then the value written to memory
|
// not the first one, then the value written to memory
|
||||||
//varies between CPUs.
|
// varies between CPUs.
|
||||||
if (offset == 0 && op.PostOffset != 0)
|
if (offset == 0 && op.PostOffset != 0)
|
||||||
{
|
{
|
||||||
//Emit write back after the first write.
|
// Emit write back after the first write.
|
||||||
EmitLoadFromRegister(context, op.Rn);
|
EmitLoadFromRegister(context, op.Rn);
|
||||||
|
|
||||||
context.EmitLdc_I4(op.PostOffset);
|
context.EmitLdc_I4(op.PostOffset);
|
||||||
|
@ -233,7 +233,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
context.Emit(OpCodes.Brtrue_S, lblBigEndian);
|
context.Emit(OpCodes.Brtrue_S, lblBigEndian);
|
||||||
|
|
||||||
//Little endian mode.
|
// Little endian mode.
|
||||||
context.Emit(OpCodes.Conv_U4);
|
context.Emit(OpCodes.Conv_U4);
|
||||||
|
|
||||||
EmitStoreToRegister(context, op.Rt);
|
EmitStoreToRegister(context, op.Rt);
|
||||||
|
@ -246,7 +246,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
context.Emit(OpCodes.Br_S, lblEnd);
|
context.Emit(OpCodes.Br_S, lblEnd);
|
||||||
|
|
||||||
//Big endian mode.
|
// Big endian mode.
|
||||||
context.MarkLabel(lblBigEndian);
|
context.MarkLabel(lblBigEndian);
|
||||||
|
|
||||||
context.EmitLsr(32);
|
context.EmitLsr(32);
|
||||||
|
@ -288,7 +288,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
context.Emit(OpCodes.Brtrue_S, lblBigEndian);
|
context.Emit(OpCodes.Brtrue_S, lblBigEndian);
|
||||||
|
|
||||||
//Little endian mode.
|
// Little endian mode.
|
||||||
EmitLoadFromRegister(context, op.Rt | 1);
|
EmitLoadFromRegister(context, op.Rt | 1);
|
||||||
|
|
||||||
context.Emit(OpCodes.Conv_U8);
|
context.Emit(OpCodes.Conv_U8);
|
||||||
|
@ -299,7 +299,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
context.Emit(OpCodes.Br_S, lblEnd);
|
context.Emit(OpCodes.Br_S, lblEnd);
|
||||||
|
|
||||||
//Big endian mode.
|
// Big endian mode.
|
||||||
context.MarkLabel(lblBigEndian);
|
context.MarkLabel(lblBigEndian);
|
||||||
|
|
||||||
context.EmitLsl(32);
|
context.EmitLsl(32);
|
||||||
|
|
|
@ -89,10 +89,10 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
if (pair)
|
if (pair)
|
||||||
{
|
{
|
||||||
//Exclusive loads should be atomic. For pairwise loads, we need to
|
// Exclusive loads should be atomic. For pairwise loads, we need to
|
||||||
//read all the data at once. For a 32-bits pairwise load, we do a
|
// read all the data at once. For a 32-bits pairwise load, we do a
|
||||||
//simple 64-bits load, for a 128-bits load, we need to call a special
|
// simple 64-bits load, for a 128-bits load, we need to call a special
|
||||||
//method to read 128-bits atomically.
|
// method to read 128-bits atomically.
|
||||||
if (op.Size == 2)
|
if (op.Size == 2)
|
||||||
{
|
{
|
||||||
context.EmitLdtmp();
|
context.EmitLdtmp();
|
||||||
|
@ -101,7 +101,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
context.Emit(OpCodes.Dup);
|
context.Emit(OpCodes.Dup);
|
||||||
|
|
||||||
//Mask low half.
|
// Mask low half.
|
||||||
context.Emit(OpCodes.Conv_U4);
|
context.Emit(OpCodes.Conv_U4);
|
||||||
|
|
||||||
if (exclusive)
|
if (exclusive)
|
||||||
|
@ -111,7 +111,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
context.EmitStintzr(op.Rt);
|
context.EmitStintzr(op.Rt);
|
||||||
|
|
||||||
//Shift high half.
|
// Shift high half.
|
||||||
context.EmitLsr(32);
|
context.EmitLsr(32);
|
||||||
context.Emit(OpCodes.Conv_U4);
|
context.Emit(OpCodes.Conv_U4);
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
context.Emit(OpCodes.Dup);
|
context.Emit(OpCodes.Dup);
|
||||||
|
|
||||||
//Load low part of the vector.
|
// Load low part of the vector.
|
||||||
context.EmitLdc_I4(0);
|
context.EmitLdc_I4(0);
|
||||||
context.EmitLdc_I4(3);
|
context.EmitLdc_I4(3);
|
||||||
|
|
||||||
|
@ -144,7 +144,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
context.EmitStintzr(op.Rt);
|
context.EmitStintzr(op.Rt);
|
||||||
|
|
||||||
//Load high part of the vector.
|
// Load high part of the vector.
|
||||||
context.EmitLdc_I4(1);
|
context.EmitLdc_I4(1);
|
||||||
context.EmitLdc_I4(3);
|
context.EmitLdc_I4(3);
|
||||||
|
|
||||||
|
@ -164,7 +164,7 @@ namespace ChocolArm64.Instructions
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//8, 16, 32 or 64-bits (non-pairwise) load.
|
// 8, 16, 32 or 64-bits (non-pairwise) load.
|
||||||
context.EmitLdtmp();
|
context.EmitLdtmp();
|
||||||
|
|
||||||
EmitReadZxCall(context, op.Size);
|
EmitReadZxCall(context, op.Size);
|
||||||
|
@ -180,7 +180,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
public static void Pfrm(ILEmitterCtx context)
|
public static void Pfrm(ILEmitterCtx context)
|
||||||
{
|
{
|
||||||
//Memory Prefetch, execute as no-op.
|
// Memory Prefetch, execute as no-op.
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Stlr(ILEmitterCtx context) => EmitStr(context, AccessType.Ordered);
|
public static void Stlr(ILEmitterCtx context) => EmitStr(context, AccessType.Ordered);
|
||||||
|
@ -223,13 +223,13 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
context.Emit(OpCodes.Brtrue_S, lblEx);
|
context.Emit(OpCodes.Brtrue_S, lblEx);
|
||||||
|
|
||||||
//Address check failed, set error right away and do not store anything.
|
// Address check failed, set error right away and do not store anything.
|
||||||
context.EmitLdc_I4(1);
|
context.EmitLdc_I4(1);
|
||||||
context.EmitStintzr(op.Rs);
|
context.EmitStintzr(op.Rs);
|
||||||
|
|
||||||
context.Emit(OpCodes.Br, lblEnd);
|
context.Emit(OpCodes.Br, lblEnd);
|
||||||
|
|
||||||
//Address check passed.
|
// Address check passed.
|
||||||
context.MarkLabel(lblEx);
|
context.MarkLabel(lblEx);
|
||||||
|
|
||||||
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
|
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
|
||||||
|
@ -241,7 +241,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
void EmitCast()
|
void EmitCast()
|
||||||
{
|
{
|
||||||
//The input should be always int64.
|
// The input should be always int64.
|
||||||
switch (op.Size)
|
switch (op.Size)
|
||||||
{
|
{
|
||||||
case 0: context.Emit(OpCodes.Conv_U1); break;
|
case 0: context.Emit(OpCodes.Conv_U1); break;
|
||||||
|
@ -293,10 +293,10 @@ namespace ChocolArm64.Instructions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//The value returned is a bool, true if the values compared
|
// The value returned is a bool, true if the values compared
|
||||||
//were equal and the new value was written, false otherwise.
|
// were equal and the new value was written, false otherwise.
|
||||||
//We need to invert this result, as on ARM 1 indicates failure,
|
// We need to invert this result, as on ARM 1 indicates failure,
|
||||||
//and 0 success on those instructions.
|
// and 0 success on those instructions.
|
||||||
context.EmitLdc_I4(1);
|
context.EmitLdc_I4(1);
|
||||||
|
|
||||||
context.Emit(OpCodes.Xor);
|
context.Emit(OpCodes.Xor);
|
||||||
|
@ -305,7 +305,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
context.EmitStintzr(op.Rs);
|
context.EmitStintzr(op.Rs);
|
||||||
|
|
||||||
//Only clear the exclusive monitor if the store was successful (Rs = false).
|
// Only clear the exclusive monitor if the store was successful (Rs = false).
|
||||||
context.Emit(OpCodes.Brtrue_S, lblEnd);
|
context.Emit(OpCodes.Brtrue_S, lblEnd);
|
||||||
|
|
||||||
Clrex(context);
|
Clrex(context);
|
||||||
|
@ -341,9 +341,9 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
private static void EmitBarrier(ILEmitterCtx context)
|
private static void EmitBarrier(ILEmitterCtx context)
|
||||||
{
|
{
|
||||||
//Note: This barrier is most likely not necessary, and probably
|
// Note: This barrier is most likely not necessary, and probably
|
||||||
//doesn't make any difference since we need to do a ton of stuff
|
// doesn't make any difference since we need to do a ton of stuff
|
||||||
//(software MMU emulation) to read or write anything anyway.
|
// (software MMU emulation) to read or write anything anyway.
|
||||||
context.EmitCall(typeof(Thread), nameof(Thread.MemoryBarrier));
|
context.EmitCall(typeof(Thread), nameof(Thread.MemoryBarrier));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
private static void EmitReadCall(ILEmitterCtx context, Extension ext, int size)
|
private static void EmitReadCall(ILEmitterCtx context, Extension ext, int size)
|
||||||
{
|
{
|
||||||
//Save the address into a temp.
|
// Save the address into a temp.
|
||||||
context.EmitStint(_tempIntAddress);
|
context.EmitStint(_tempIntAddress);
|
||||||
|
|
||||||
bool isSimd = IsSimd(context);
|
bool isSimd = IsSimd(context);
|
||||||
|
@ -99,7 +99,7 @@ namespace ChocolArm64.Instructions
|
||||||
{
|
{
|
||||||
bool isSimd = IsSimd(context);
|
bool isSimd = IsSimd(context);
|
||||||
|
|
||||||
//Save the value into a temp.
|
// Save the value into a temp.
|
||||||
if (isSimd)
|
if (isSimd)
|
||||||
{
|
{
|
||||||
context.EmitStvec(_tempVecValue);
|
context.EmitStvec(_tempVecValue);
|
||||||
|
@ -109,7 +109,7 @@ namespace ChocolArm64.Instructions
|
||||||
context.EmitStint(_tempIntValue);
|
context.EmitStint(_tempIntValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Save the address into a temp.
|
// Save the address into a temp.
|
||||||
context.EmitStint(_tempIntAddress);
|
context.EmitStint(_tempIntAddress);
|
||||||
|
|
||||||
if (size < 0 || size > (isSimd ? 4 : 3))
|
if (size < 0 || size > (isSimd ? 4 : 3))
|
||||||
|
|
|
@ -1298,8 +1298,8 @@ namespace ChocolArm64.Instructions
|
||||||
{
|
{
|
||||||
if (Optimizations.UseSse41 && size == 0)
|
if (Optimizations.UseSse41 && size == 0)
|
||||||
{
|
{
|
||||||
//If the type is float, we can perform insertion and
|
// If the type is float, we can perform insertion and
|
||||||
//zero the upper bits with a single instruction (INSERTPS);
|
// zero the upper bits with a single instruction (INSERTPS);
|
||||||
context.EmitLdvec(reg);
|
context.EmitLdvec(reg);
|
||||||
|
|
||||||
VectorHelper.EmitCall(context, nameof(VectorHelper.Sse41VectorInsertScalarSingle));
|
VectorHelper.EmitCall(context, nameof(VectorHelper.Sse41VectorInsertScalarSingle));
|
||||||
|
|
|
@ -96,7 +96,7 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
if (op.Replicate)
|
if (op.Replicate)
|
||||||
{
|
{
|
||||||
//Only loads uses the replicate mode.
|
// Only loads uses the replicate mode.
|
||||||
if (!isLoad)
|
if (!isLoad)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
|
|
|
@ -11,12 +11,12 @@ namespace ChocolArm64.Instructions
|
||||||
{
|
{
|
||||||
public static void Hint(ILEmitterCtx context)
|
public static void Hint(ILEmitterCtx context)
|
||||||
{
|
{
|
||||||
//Execute as no-op.
|
// Execute as no-op.
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Isb(ILEmitterCtx context)
|
public static void Isb(ILEmitterCtx context)
|
||||||
{
|
{
|
||||||
//Execute as no-op.
|
// Execute as no-op.
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Mrs(ILEmitterCtx context)
|
public static void Mrs(ILEmitterCtx context)
|
||||||
|
@ -85,21 +85,21 @@ namespace ChocolArm64.Instructions
|
||||||
|
|
||||||
public static void Nop(ILEmitterCtx context)
|
public static void Nop(ILEmitterCtx context)
|
||||||
{
|
{
|
||||||
//Do nothing.
|
// Do nothing.
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Sys(ILEmitterCtx context)
|
public static void Sys(ILEmitterCtx context)
|
||||||
{
|
{
|
||||||
//This instruction is used to do some operations on the CPU like cache invalidation,
|
// This instruction is used to do some operations on the CPU like cache invalidation,
|
||||||
//address translation and the like.
|
// address translation and the like.
|
||||||
//We treat it as no-op here since we don't have any cache being emulated anyway.
|
// We treat it as no-op here since we don't have any cache being emulated anyway.
|
||||||
OpCodeSystem64 op = (OpCodeSystem64)context.CurrOp;
|
OpCodeSystem64 op = (OpCodeSystem64)context.CurrOp;
|
||||||
|
|
||||||
switch (GetPackedId(op))
|
switch (GetPackedId(op))
|
||||||
{
|
{
|
||||||
case 0b11_011_0111_0100_001:
|
case 0b11_011_0111_0100_001:
|
||||||
{
|
{
|
||||||
//DC ZVA
|
// DC ZVA
|
||||||
for (int offs = 0; offs < (4 << CpuThreadState.DczSizeLog2); offs += 8)
|
for (int offs = 0; offs < (4 << CpuThreadState.DczSizeLog2); offs += 8)
|
||||||
{
|
{
|
||||||
context.EmitLdintzr(op.Rt);
|
context.EmitLdintzr(op.Rt);
|
||||||
|
@ -115,7 +115,7 @@ namespace ChocolArm64.Instructions
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//No-op
|
// No-op
|
||||||
case 0b11_011_0111_1110_001: //DC CIVAC
|
case 0b11_011_0111_1110_001: //DC CIVAC
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -583,9 +583,9 @@ namespace ChocolArm64.Instructions
|
||||||
{
|
{
|
||||||
if (Sse41.IsSupported)
|
if (Sse41.IsSupported)
|
||||||
{
|
{
|
||||||
//Note: The if/else if is necessary to enable the JIT to
|
// Note: The if/else if is necessary to enable the JIT to
|
||||||
//produce a single INSERTPS instruction instead of the
|
// produce a single INSERTPS instruction instead of the
|
||||||
//jump table fallback.
|
// jump table fallback.
|
||||||
if (index == 0)
|
if (index == 0)
|
||||||
{
|
{
|
||||||
return Sse41.Insert(vector, value, 0x00);
|
return Sse41.Insert(vector, value, 0x00);
|
||||||
|
@ -628,7 +628,7 @@ namespace ChocolArm64.Instructions
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static Vector128<float> Sse41VectorInsertScalarSingle(float value, Vector128<float> vector)
|
public static Vector128<float> Sse41VectorInsertScalarSingle(float value, Vector128<float> vector)
|
||||||
{
|
{
|
||||||
//Note: 0b1110 is the mask to zero the upper bits.
|
// Note: 0b1110 is the mask to zero the upper bits.
|
||||||
return Sse41.Insert(vector, value, 0b1110);
|
return Sse41.Insert(vector, value, 0b1110);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,9 +96,9 @@ namespace ChocolArm64.Memory
|
||||||
IntPtr[] addresses,
|
IntPtr[] addresses,
|
||||||
out ulong count)
|
out ulong count)
|
||||||
{
|
{
|
||||||
//This is only supported on windows, but returning
|
// This is only supported on windows, but returning
|
||||||
//false (failed) is also valid for platforms without
|
// false (failed) is also valid for platforms without
|
||||||
//write tracking support on the OS.
|
// write tracking support on the OS.
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
{
|
{
|
||||||
return MemoryManagementWindows.GetModifiedPages(address, size, addresses, out count);
|
return MemoryManagementWindows.GetModifiedPages(address, size, addresses, out count);
|
||||||
|
|
|
@ -50,12 +50,12 @@ namespace ChocolArm64.Memory
|
||||||
AddressSpaceBits = addressSpaceBits;
|
AddressSpaceBits = addressSpaceBits;
|
||||||
AddressSpaceSize = 1L << addressSpaceBits;
|
AddressSpaceSize = 1L << addressSpaceBits;
|
||||||
|
|
||||||
//When flat page table is requested, we use a single
|
// When flat page table is requested, we use a single
|
||||||
//array for the mappings of the entire address space.
|
// array for the mappings of the entire address space.
|
||||||
//This has better performance, but also high memory usage.
|
// This has better performance, but also high memory usage.
|
||||||
//The multi level page table uses 9 bits per level, so
|
// The multi level page table uses 9 bits per level, so
|
||||||
//the memory usage is lower, but the performance is also
|
// the memory usage is lower, but the performance is also
|
||||||
//lower, since each address translation requires multiple reads.
|
// lower, since each address translation requires multiple reads.
|
||||||
if (useFlatPageTable)
|
if (useFlatPageTable)
|
||||||
{
|
{
|
||||||
PtLevelBits = addressSpaceBits - PageBits;
|
PtLevelBits = addressSpaceBits - PageBits;
|
||||||
|
@ -237,13 +237,13 @@ namespace ChocolArm64.Memory
|
||||||
|
|
||||||
if (nextPtr == IntPtr.Zero)
|
if (nextPtr == IntPtr.Zero)
|
||||||
{
|
{
|
||||||
//Entry does not yet exist, allocate a new one.
|
// Entry does not yet exist, allocate a new one.
|
||||||
IntPtr newPtr = Allocate((ulong)(PtLevelSize * IntPtr.Size));
|
IntPtr newPtr = Allocate((ulong)(PtLevelSize * IntPtr.Size));
|
||||||
|
|
||||||
//Try to swap the current pointer (should be zero), with the allocated one.
|
// Try to swap the current pointer (should be zero), with the allocated one.
|
||||||
nextPtr = Interlocked.Exchange(ref *ptePtr, newPtr);
|
nextPtr = Interlocked.Exchange(ref *ptePtr, newPtr);
|
||||||
|
|
||||||
//If the old pointer is not null, then another thread already has set it.
|
// If the old pointer is not null, then another thread already has set it.
|
||||||
if (nextPtr != IntPtr.Zero)
|
if (nextPtr != IntPtr.Zero)
|
||||||
{
|
{
|
||||||
Free(newPtr);
|
Free(newPtr);
|
||||||
|
@ -533,7 +533,7 @@ namespace ChocolArm64.Memory
|
||||||
|
|
||||||
private void AbortWithAlignmentFault(long position)
|
private void AbortWithAlignmentFault(long position)
|
||||||
{
|
{
|
||||||
//TODO: Abort mode and exception support on the CPU.
|
// TODO: Abort mode and exception support on the CPU.
|
||||||
throw new InvalidOperationException($"Tried to compare exchange a misaligned address 0x{position:X16}.");
|
throw new InvalidOperationException($"Tried to compare exchange a misaligned address 0x{position:X16}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -726,7 +726,7 @@ namespace ChocolArm64.Memory
|
||||||
|
|
||||||
public void ReadBytes(long position, byte[] data, int startIndex, int size)
|
public void ReadBytes(long position, byte[] data, int startIndex, int size)
|
||||||
{
|
{
|
||||||
//Note: This will be moved later.
|
// Note: This will be moved later.
|
||||||
long endAddr = position + size;
|
long endAddr = position + size;
|
||||||
|
|
||||||
if ((ulong)size > int.MaxValue)
|
if ((ulong)size > int.MaxValue)
|
||||||
|
@ -924,7 +924,7 @@ namespace ChocolArm64.Memory
|
||||||
|
|
||||||
public void WriteBytes(long position, byte[] data, int startIndex, int size)
|
public void WriteBytes(long position, byte[] data, int startIndex, int size)
|
||||||
{
|
{
|
||||||
//Note: This will be moved later.
|
// Note: This will be moved later.
|
||||||
long endAddr = position + size;
|
long endAddr = position + size;
|
||||||
|
|
||||||
if ((ulong)endAddr < (ulong)position)
|
if ((ulong)endAddr < (ulong)position)
|
||||||
|
@ -954,7 +954,7 @@ namespace ChocolArm64.Memory
|
||||||
|
|
||||||
public void CopyBytes(long src, long dst, long size)
|
public void CopyBytes(long src, long dst, long size)
|
||||||
{
|
{
|
||||||
//Note: This will be moved later.
|
// Note: This will be moved later.
|
||||||
if (IsContiguous(src, size) &&
|
if (IsContiguous(src, size) &&
|
||||||
IsContiguous(dst, size))
|
IsContiguous(dst, size))
|
||||||
{
|
{
|
||||||
|
|
|
@ -36,7 +36,7 @@ namespace ChocolArm64
|
||||||
static OpCodeTable()
|
static OpCodeTable()
|
||||||
{
|
{
|
||||||
#region "OpCode Table (AArch32)"
|
#region "OpCode Table (AArch32)"
|
||||||
//Integer
|
// Integer
|
||||||
SetA32("<<<<0010100xxxxxxxxxxxxxxxxxxxxx", InstEmit32.Add, typeof(OpCode32AluImm));
|
SetA32("<<<<0010100xxxxxxxxxxxxxxxxxxxxx", InstEmit32.Add, typeof(OpCode32AluImm));
|
||||||
SetA32("<<<<0000100xxxxxxxxxxxxxxxx0xxxx", InstEmit32.Add, typeof(OpCode32AluRsImm));
|
SetA32("<<<<0000100xxxxxxxxxxxxxxxx0xxxx", InstEmit32.Add, typeof(OpCode32AluRsImm));
|
||||||
SetA32("<<<<1010xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.B, typeof(OpCode32BImm));
|
SetA32("<<<<1010xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.B, typeof(OpCode32BImm));
|
||||||
|
@ -66,7 +66,7 @@ namespace ChocolArm64
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region "OpCode Table (AArch64)"
|
#region "OpCode Table (AArch64)"
|
||||||
//Integer
|
// Integer
|
||||||
SetA64("x0011010000xxxxx000000xxxxxxxxxx", InstEmit.Adc, typeof(OpCodeAluRs64));
|
SetA64("x0011010000xxxxx000000xxxxxxxxxx", InstEmit.Adc, typeof(OpCodeAluRs64));
|
||||||
SetA64("x0111010000xxxxx000000xxxxxxxxxx", InstEmit.Adcs, typeof(OpCodeAluRs64));
|
SetA64("x0111010000xxxxx000000xxxxxxxxxx", InstEmit.Adcs, typeof(OpCodeAluRs64));
|
||||||
SetA64("x00100010xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Add, typeof(OpCodeAluImm64));
|
SetA64("x00100010xxxxxxxxxxxxxxxxxxxxxxx", InstEmit.Add, typeof(OpCodeAluImm64));
|
||||||
|
@ -217,7 +217,7 @@ namespace ChocolArm64
|
||||||
SetA64("10011011101xxxxx1xxxxxxxxxxxxxxx", InstEmit.Umsubl, typeof(OpCodeMul64));
|
SetA64("10011011101xxxxx1xxxxxxxxxxxxxxx", InstEmit.Umsubl, typeof(OpCodeMul64));
|
||||||
SetA64("10011011110xxxxx0xxxxxxxxxxxxxxx", InstEmit.Umulh, typeof(OpCodeMul64));
|
SetA64("10011011110xxxxx0xxxxxxxxxxxxxxx", InstEmit.Umulh, typeof(OpCodeMul64));
|
||||||
|
|
||||||
//Vector
|
// Vector
|
||||||
SetA64("0101111011100000101110xxxxxxxxxx", InstEmit.Abs_S, typeof(OpCodeSimd64));
|
SetA64("0101111011100000101110xxxxxxxxxx", InstEmit.Abs_S, typeof(OpCodeSimd64));
|
||||||
SetA64("0>001110<<100000101110xxxxxxxxxx", InstEmit.Abs_V, typeof(OpCodeSimd64));
|
SetA64("0>001110<<100000101110xxxxxxxxxx", InstEmit.Abs_V, typeof(OpCodeSimd64));
|
||||||
SetA64("01011110111xxxxx100001xxxxxxxxxx", InstEmit.Add_S, typeof(OpCodeSimdReg64));
|
SetA64("01011110111xxxxx100001xxxxxxxxxx", InstEmit.Add_S, typeof(OpCodeSimdReg64));
|
||||||
|
@ -656,12 +656,12 @@ namespace ChocolArm64
|
||||||
|
|
||||||
for (int index = 0; index < encoding.Length; index++, bit--)
|
for (int index = 0; index < encoding.Length; index++, bit--)
|
||||||
{
|
{
|
||||||
//Note: < and > are used on special encodings.
|
// Note: < and > are used on special encodings.
|
||||||
//The < means that we should never have ALL bits with the '<' set.
|
// The < means that we should never have ALL bits with the '<' set.
|
||||||
//So, when the encoding has <<, it means that 00, 01, and 10 are valid,
|
// So, when the encoding has <<, it means that 00, 01, and 10 are valid,
|
||||||
//but not 11. <<< is 000, 001, ..., 110 but NOT 111, and so on...
|
// but not 11. <<< is 000, 001, ..., 110 but NOT 111, and so on...
|
||||||
//For >, the invalid value is zero. So, for >> 01, 10 and 11 are valid,
|
// For >, the invalid value is zero. So, for >> 01, 10 and 11 are valid,
|
||||||
//but 00 isn't.
|
// but 00 isn't.
|
||||||
char chr = encoding[index];
|
char chr = encoding[index];
|
||||||
|
|
||||||
if (chr == '1')
|
if (chr == '1')
|
||||||
|
|
|
@ -126,8 +126,8 @@ namespace ChocolArm64.State
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
internal bool Synchronize()
|
internal bool Synchronize()
|
||||||
{
|
{
|
||||||
//Firing a interrupt frequently is expensive, so we only
|
// Firing a interrupt frequently is expensive, so we only
|
||||||
//do it after a given number of instructions has executed.
|
// do it after a given number of instructions has executed.
|
||||||
_syncCount++;
|
_syncCount++;
|
||||||
|
|
||||||
if (_syncCount >= MinCountForCheck)
|
if (_syncCount >= MinCountForCheck)
|
||||||
|
|
|
@ -56,10 +56,10 @@ namespace ChocolArm64.Translation
|
||||||
private OpCode64 _optOpLastCompare;
|
private OpCode64 _optOpLastCompare;
|
||||||
private OpCode64 _optOpLastFlagSet;
|
private OpCode64 _optOpLastFlagSet;
|
||||||
|
|
||||||
//This is the index of the temporary register, used to store temporary
|
// This is the index of the temporary register, used to store temporary
|
||||||
//values needed by some functions, since IL doesn't have a swap instruction.
|
// values needed by some functions, since IL doesn't have a swap instruction.
|
||||||
//You can use any value here as long it doesn't conflict with the indices
|
// You can use any value here as long it doesn't conflict with the indices
|
||||||
//for the other registers. Any value >= 64 or < 0 will do.
|
// for the other registers. Any value >= 64 or < 0 will do.
|
||||||
private const int ReservedLocalsCount = 64;
|
private const int ReservedLocalsCount = 64;
|
||||||
|
|
||||||
private const int RorTmpIndex = ReservedLocalsCount + 0;
|
private const int RorTmpIndex = ReservedLocalsCount + 0;
|
||||||
|
@ -69,7 +69,7 @@ namespace ChocolArm64.Translation
|
||||||
private const int IntGpTmp2Index = ReservedLocalsCount + 4;
|
private const int IntGpTmp2Index = ReservedLocalsCount + 4;
|
||||||
private const int UserIntTempStart = ReservedLocalsCount + 5;
|
private const int UserIntTempStart = ReservedLocalsCount + 5;
|
||||||
|
|
||||||
//Vectors are part of another "set" of locals.
|
// Vectors are part of another "set" of locals.
|
||||||
private const int VecGpTmp1Index = ReservedLocalsCount + 0;
|
private const int VecGpTmp1Index = ReservedLocalsCount + 0;
|
||||||
private const int VecGpTmp2Index = ReservedLocalsCount + 1;
|
private const int VecGpTmp2Index = ReservedLocalsCount + 1;
|
||||||
private const int VecGpTmp3Index = ReservedLocalsCount + 2;
|
private const int VecGpTmp3Index = ReservedLocalsCount + 2;
|
||||||
|
@ -139,10 +139,10 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
public void ResetBlockStateForPredicatedOp()
|
public void ResetBlockStateForPredicatedOp()
|
||||||
{
|
{
|
||||||
//Check if this is a predicated instruction that modifies flags,
|
// Check if this is a predicated instruction that modifies flags,
|
||||||
//in this case the value of the flags is unknown as we don't know
|
// in this case the value of the flags is unknown as we don't know
|
||||||
//in advance if the instruction is going to be executed or not.
|
// in advance if the instruction is going to be executed or not.
|
||||||
//So, we reset the block state to prevent an invalid optimization.
|
// So, we reset the block state to prevent an invalid optimization.
|
||||||
if (CurrOp == _optOpLastFlagSet)
|
if (CurrOp == _optOpLastFlagSet)
|
||||||
{
|
{
|
||||||
ResetBlockState();
|
ResetBlockState();
|
||||||
|
@ -167,8 +167,8 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
public bool TryOptEmitSubroutineCall()
|
public bool TryOptEmitSubroutineCall()
|
||||||
{
|
{
|
||||||
//Calls should always have a next block, unless
|
// Calls should always have a next block, unless
|
||||||
//we're translating a single basic block.
|
// we're translating a single basic block.
|
||||||
if (_currBlock.Next == null)
|
if (_currBlock.Next == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -239,15 +239,15 @@ namespace ChocolArm64.Translation
|
||||||
&& cond != Condition.GtUn
|
&& cond != Condition.GtUn
|
||||||
&& cond != Condition.LeUn)
|
&& cond != Condition.LeUn)
|
||||||
{
|
{
|
||||||
//There are several limitations that needs to be taken into account for CMN comparisons:
|
// There are several limitations that needs to be taken into account for CMN comparisons:
|
||||||
//- The unsigned comparisons are not valid, as they depend on the
|
// - The unsigned comparisons are not valid, as they depend on the
|
||||||
//carry flag value, and they will have different values for addition and
|
// carry flag value, and they will have different values for addition and
|
||||||
//subtraction. For addition, it's carry, and for subtraction, it's borrow.
|
// subtraction. For addition, it's carry, and for subtraction, it's borrow.
|
||||||
//So, we need to make sure we're not doing a unsigned compare for the CMN case.
|
// So, we need to make sure we're not doing a unsigned compare for the CMN case.
|
||||||
//- We can only do the optimization for the immediate variants,
|
// - We can only do the optimization for the immediate variants,
|
||||||
//because when the second operand value is exactly INT_MIN, we can't
|
// because when the second operand value is exactly INT_MIN, we can't
|
||||||
//negate the value as theres no positive counterpart.
|
// negate the value as theres no positive counterpart.
|
||||||
//Such invalid values can't be encoded on the immediate encodings.
|
// Such invalid values can't be encoded on the immediate encodings.
|
||||||
if (_optOpLastCompare is IOpCodeAluImm64 op)
|
if (_optOpLastCompare is IOpCodeAluImm64 op)
|
||||||
{
|
{
|
||||||
Ldloc(CmpOptTmp1Index, RegisterType.Int, _optOpLastCompare.RegisterSize);
|
Ldloc(CmpOptTmp1Index, RegisterType.Int, _optOpLastCompare.RegisterSize);
|
||||||
|
@ -513,11 +513,11 @@ namespace ChocolArm64.Translation
|
||||||
public void EmitLdflg(int index) => Ldloc(index, RegisterType.Flag);
|
public void EmitLdflg(int index) => Ldloc(index, RegisterType.Flag);
|
||||||
public void EmitStflg(int index)
|
public void EmitStflg(int index)
|
||||||
{
|
{
|
||||||
//Set this only if any of the NZCV flag bits were modified.
|
// Set this only if any of the NZCV flag bits were modified.
|
||||||
//This is used to ensure that when emiting a direct IL branch
|
// This is used to ensure that when emiting a direct IL branch
|
||||||
//instruction for compare + branch sequences, we're not expecting
|
// instruction for compare + branch sequences, we're not expecting
|
||||||
//to use comparison values from an old instruction, when in fact
|
// to use comparison values from an old instruction, when in fact
|
||||||
//the flags were already overwritten by another instruction further along.
|
// the flags were already overwritten by another instruction further along.
|
||||||
if (index >= (int)PState.VBit)
|
if (index >= (int)PState.VBit)
|
||||||
{
|
{
|
||||||
_optOpLastFlagSet = CurrOp;
|
_optOpLastFlagSet = CurrOp;
|
||||||
|
|
|
@ -153,7 +153,7 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
public static long ClearCallerSavedIntRegs(long mask, ExecutionMode mode)
|
public static long ClearCallerSavedIntRegs(long mask, ExecutionMode mode)
|
||||||
{
|
{
|
||||||
//TODO: ARM32 support.
|
// TODO: ARM32 support.
|
||||||
if (mode == ExecutionMode.Aarch64)
|
if (mode == ExecutionMode.Aarch64)
|
||||||
{
|
{
|
||||||
mask &= ~(CallerSavedIntRegistersMask | PStateNzcvFlagsMask);
|
mask &= ~(CallerSavedIntRegistersMask | PStateNzcvFlagsMask);
|
||||||
|
@ -164,7 +164,7 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
public static long ClearCallerSavedVecRegs(long mask, ExecutionMode mode)
|
public static long ClearCallerSavedVecRegs(long mask, ExecutionMode mode)
|
||||||
{
|
{
|
||||||
//TODO: ARM32 support.
|
// TODO: ARM32 support.
|
||||||
if (mode == ExecutionMode.Aarch64)
|
if (mode == ExecutionMode.Aarch64)
|
||||||
{
|
{
|
||||||
mask &= ~CallerSavedVecRegistersMask;
|
mask &= ~CallerSavedVecRegistersMask;
|
||||||
|
|
|
@ -10,9 +10,9 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
class TranslatedSub
|
class TranslatedSub
|
||||||
{
|
{
|
||||||
//This is the minimum amount of calls needed for the method
|
// This is the minimum amount of calls needed for the method
|
||||||
//to be retranslated with higher quality code. It's only worth
|
// to be retranslated with higher quality code. It's only worth
|
||||||
//doing that for hot code.
|
// doing that for hot code.
|
||||||
private const int MinCallCountForOpt = 30;
|
private const int MinCallCountForOpt = 30;
|
||||||
|
|
||||||
public ArmSubroutine Delegate { get; private set; }
|
public ArmSubroutine Delegate { get; private set; }
|
||||||
|
@ -84,7 +84,7 @@ namespace ChocolArm64.Translation
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Only return true once, so that it is added to the queue only once.
|
// Only return true once, so that it is added to the queue only once.
|
||||||
_rejit = false;
|
_rejit = false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -209,11 +209,11 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
context.ResetBlockStateForPredicatedOp();
|
context.ResetBlockStateForPredicatedOp();
|
||||||
|
|
||||||
//If this is the last op on the block, and there's no "next" block
|
// If this is the last op on the block, and there's no "next" block
|
||||||
//after this one, then we have to return right now, with the address
|
// after this one, then we have to return right now, with the address
|
||||||
//of the next instruction to be executed (in the case that the condition
|
// of the next instruction to be executed (in the case that the condition
|
||||||
//is false, and the branch was not taken, as all basic blocks should end
|
// is false, and the branch was not taken, as all basic blocks should end
|
||||||
//with some kind of branch).
|
// with some kind of branch).
|
||||||
if (isLastOp && block.Next == null)
|
if (isLastOp && block.Next == null)
|
||||||
{
|
{
|
||||||
context.EmitStoreContext();
|
context.EmitStoreContext();
|
||||||
|
|
|
@ -8,13 +8,13 @@ namespace ChocolArm64.Translation
|
||||||
{
|
{
|
||||||
class TranslatorCache
|
class TranslatorCache
|
||||||
{
|
{
|
||||||
//Maximum size of the cache, the unit used is completely arbitrary.
|
// Maximum size of the cache, the unit used is completely arbitrary.
|
||||||
private const int MaxTotalSize = 0x800000;
|
private const int MaxTotalSize = 0x800000;
|
||||||
|
|
||||||
//Minimum time required in milliseconds for a method to be eligible for deletion.
|
// Minimum time required in milliseconds for a method to be eligible for deletion.
|
||||||
private const int MinTimeDelta = 2 * 60000;
|
private const int MinTimeDelta = 2 * 60000;
|
||||||
|
|
||||||
//Minimum number of calls required to update the timestamp.
|
// Minimum number of calls required to update the timestamp.
|
||||||
private const int MinCallCountForUpdate = 250;
|
private const int MinCallCountForUpdate = 250;
|
||||||
|
|
||||||
private class CacheBucket
|
private class CacheBucket
|
||||||
|
@ -122,10 +122,10 @@ namespace ChocolArm64.Translation
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//The bucket value on the dictionary may have changed between the
|
// The bucket value on the dictionary may have changed between the
|
||||||
//time we get the value from the dictionary, and we acquire the
|
// time we get the value from the dictionary, and we acquire the
|
||||||
//lock. So we need to ensure we are working with the latest value,
|
// lock. So we need to ensure we are working with the latest value,
|
||||||
//we can do that by getting the value again, inside the lock.
|
// we can do that by getting the value again, inside the lock.
|
||||||
if (_cache.TryGetValue(position, out CacheBucket latestBucket))
|
if (_cache.TryGetValue(position, out CacheBucket latestBucket))
|
||||||
{
|
{
|
||||||
latestBucket.CallCount = 0;
|
latestBucket.CallCount = 0;
|
||||||
|
|
|
@ -124,10 +124,10 @@ namespace Ryujinx.Audio
|
||||||
|
|
||||||
if (ReleasedCount > 0)
|
if (ReleasedCount > 0)
|
||||||
{
|
{
|
||||||
//If we signal, then we also need to have released buffers available
|
// If we signal, then we also need to have released buffers available
|
||||||
//to return when GetReleasedBuffers is called.
|
// to return when GetReleasedBuffers is called.
|
||||||
//If playback needs to be re-started due to all buffers being processed,
|
// If playback needs to be re-started due to all buffers being processed,
|
||||||
//then OpenAL zeros the counts (ReleasedCount), so we keep it on the queue.
|
// then OpenAL zeros the counts (ReleasedCount), so we keep it on the queue.
|
||||||
while (ReleasedCount-- > 0 && QueuedTagsQueue.TryDequeue(out long Tag))
|
while (ReleasedCount-- > 0 && QueuedTagsQueue.TryDequeue(out long Tag))
|
||||||
{
|
{
|
||||||
AL.SourceUnqueueBuffers(SourceId, 1);
|
AL.SourceUnqueueBuffers(SourceId, 1);
|
||||||
|
@ -209,7 +209,7 @@ namespace Ryujinx.Audio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//If it's not slept it will waste cycles.
|
// If it's not slept it will waste cycles.
|
||||||
Thread.Sleep(10);
|
Thread.Sleep(10);
|
||||||
}
|
}
|
||||||
while (KeepPolling);
|
while (KeepPolling);
|
||||||
|
|
|
@ -109,7 +109,7 @@ namespace Ryujinx.Graphics
|
||||||
switch (submissionMode)
|
switch (submissionMode)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
//Incrementing.
|
// Incrementing.
|
||||||
SetNonImmediateState(word);
|
SetNonImmediateState(word);
|
||||||
|
|
||||||
_state.NonIncrementing = false;
|
_state.NonIncrementing = false;
|
||||||
|
@ -118,7 +118,7 @@ namespace Ryujinx.Graphics
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
//Non-incrementing.
|
// Non-incrementing.
|
||||||
SetNonImmediateState(word);
|
SetNonImmediateState(word);
|
||||||
|
|
||||||
_state.NonIncrementing = true;
|
_state.NonIncrementing = true;
|
||||||
|
@ -127,7 +127,7 @@ namespace Ryujinx.Graphics
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 4:
|
case 4:
|
||||||
//Immediate.
|
// Immediate.
|
||||||
_state.Method = (word >> 0) & 0x1fff;
|
_state.Method = (word >> 0) & 0x1fff;
|
||||||
_state.SubChannel = (word >> 13) & 7;
|
_state.SubChannel = (word >> 13) & 7;
|
||||||
_state.NonIncrementing = true;
|
_state.NonIncrementing = true;
|
||||||
|
@ -138,7 +138,7 @@ namespace Ryujinx.Graphics
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 5:
|
case 5:
|
||||||
//Increment-once.
|
// Increment-once.
|
||||||
SetNonImmediateState(word);
|
SetNonImmediateState(word);
|
||||||
|
|
||||||
_state.NonIncrementing = false;
|
_state.NonIncrementing = false;
|
||||||
|
|
|
@ -2,7 +2,7 @@ namespace Ryujinx.Graphics.Gal
|
||||||
{
|
{
|
||||||
public struct GalVertexBinding
|
public struct GalVertexBinding
|
||||||
{
|
{
|
||||||
//VboKey shouldn't be here, but ARB_vertex_attrib_binding is core since 4.3
|
// VboKey shouldn't be here, but ARB_vertex_attrib_binding is core since 4.3
|
||||||
|
|
||||||
public bool Enabled;
|
public bool Enabled;
|
||||||
public int Stride;
|
public int Stride;
|
||||||
|
|
|
@ -275,7 +275,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//Fallback to non-mirrored clamps
|
// Fallback to non-mirrored clamps
|
||||||
switch (wrap)
|
switch (wrap)
|
||||||
{
|
{
|
||||||
case GalTextureWrap.MirrorClampToEdge: return TextureWrapMode.ClampToEdge;
|
case GalTextureWrap.MirrorClampToEdge: return TextureWrapMode.ClampToEdge;
|
||||||
|
@ -291,7 +291,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
GalTextureFilter minFilter,
|
GalTextureFilter minFilter,
|
||||||
GalTextureMipFilter mipFilter)
|
GalTextureMipFilter mipFilter)
|
||||||
{
|
{
|
||||||
//TODO: Mip (needs mipmap support first).
|
// TODO: Mip (needs mipmap support first).
|
||||||
switch (minFilter)
|
switch (minFilter)
|
||||||
{
|
{
|
||||||
case GalTextureFilter.Nearest: return TextureMinFilter.Nearest;
|
case GalTextureFilter.Nearest: return TextureMinFilter.Nearest;
|
||||||
|
|
|
@ -96,7 +96,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
_rasterizer = rasterizer;
|
_rasterizer = rasterizer;
|
||||||
_shader = shader;
|
_shader = shader;
|
||||||
|
|
||||||
//These values match OpenGL's defaults
|
// These values match OpenGL's defaults
|
||||||
_old = new GalPipelineState
|
_old = new GalPipelineState
|
||||||
{
|
{
|
||||||
FrontFace = GalFrontFace.Ccw,
|
FrontFace = GalFrontFace.Ccw,
|
||||||
|
@ -564,14 +564,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
{
|
{
|
||||||
_vaoHandle = GL.GenVertexArray();
|
_vaoHandle = GL.GenVertexArray();
|
||||||
|
|
||||||
//Vertex arrays shouldn't be used anywhere else in OpenGL's backend
|
// Vertex arrays shouldn't be used anywhere else in OpenGL's backend
|
||||||
//if you want to use it, move this line out of the if
|
// if you want to use it, move this line out of the if
|
||||||
GL.BindVertexArray(_vaoHandle);
|
GL.BindVertexArray(_vaoHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (GalVertexAttrib attrib in binding.Attribs)
|
foreach (GalVertexAttrib attrib in binding.Attribs)
|
||||||
{
|
{
|
||||||
//Skip uninitialized attributes.
|
// Skip uninitialized attributes.
|
||||||
if (attrib.Size == 0)
|
if (attrib.Size == 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -77,11 +77,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
private int _cropRight;
|
private int _cropRight;
|
||||||
private int _cropBottom;
|
private int _cropBottom;
|
||||||
|
|
||||||
//This framebuffer is used to attach guest rendertargets,
|
// This framebuffer is used to attach guest rendertargets,
|
||||||
//think of it as a dummy OpenGL VAO
|
// think of it as a dummy OpenGL VAO
|
||||||
private int _dummyFrameBuffer;
|
private int _dummyFrameBuffer;
|
||||||
|
|
||||||
//These framebuffers are used to blit images
|
// These framebuffers are used to blit images
|
||||||
private int _srcFb;
|
private int _srcFb;
|
||||||
private int _dstFb;
|
private int _dstFb;
|
||||||
|
|
||||||
|
@ -109,8 +109,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
|
||||||
private void TextureDeletionHandler(object sender, int handle)
|
private void TextureDeletionHandler(object sender, int handle)
|
||||||
{
|
{
|
||||||
//Texture was deleted, the handle is no longer valid, so
|
// Texture was deleted, the handle is no longer valid, so
|
||||||
//reset all uses of this handle on a render target.
|
// reset all uses of this handle on a render target.
|
||||||
for (int attachment = 0; attachment < RenderTargetsCount; attachment++)
|
for (int attachment = 0; attachment < RenderTargetsCount; attachment++)
|
||||||
{
|
{
|
||||||
if (_colorHandles[attachment] == handle)
|
if (_colorHandles[attachment] == handle)
|
||||||
|
@ -484,7 +484,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
|
||||||
GL.BindBuffer(BufferTarget.PixelPackBuffer, _copyPbo);
|
GL.BindBuffer(BufferTarget.PixelPackBuffer, _copyPbo);
|
||||||
|
|
||||||
//The buffer should be large enough to hold the largest texture.
|
// The buffer should be large enough to hold the largest texture.
|
||||||
int bufferSize = Math.Max(ImageUtils.GetSize(oldImage),
|
int bufferSize = Math.Max(ImageUtils.GetSize(oldImage),
|
||||||
ImageUtils.GetSize(newImage));
|
ImageUtils.GetSize(newImage));
|
||||||
|
|
||||||
|
|
|
@ -115,7 +115,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
data[1] = flipY;
|
data[1] = flipY;
|
||||||
data[2] = BitConverter.Int32BitsToSingle(instance);
|
data[2] = BitConverter.Int32BitsToSingle(instance);
|
||||||
|
|
||||||
//Invalidate buffer
|
// Invalidate buffer
|
||||||
GL.BufferData(BufferTarget.UniformBuffer, ExtraDataSize * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw);
|
GL.BufferData(BufferTarget.UniformBuffer, ExtraDataSize * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw);
|
||||||
|
|
||||||
GL.BufferSubData(BufferTarget.UniformBuffer, IntPtr.Zero, ExtraDataSize * sizeof(float), (IntPtr)data);
|
GL.BufferSubData(BufferTarget.UniformBuffer, IntPtr.Zero, ExtraDataSize * sizeof(float), (IntPtr)data);
|
||||||
|
@ -228,9 +228,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
|
||||||
if (blockIndex < 0)
|
if (blockIndex < 0)
|
||||||
{
|
{
|
||||||
//This may be fine, the compiler may optimize away unused uniform buffers,
|
// This may be fine, the compiler may optimize away unused uniform buffers,
|
||||||
//and in this case the above call would return -1 as the buffer has been
|
// and in this case the above call would return -1 as the buffer has been
|
||||||
//optimized away.
|
// optimized away.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -207,7 +207,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//TODO: Use KHR_texture_compression_astc_hdr when available
|
// TODO: Use KHR_texture_compression_astc_hdr when available
|
||||||
if (IsAstc(image.Format))
|
if (IsAstc(image.Format))
|
||||||
{
|
{
|
||||||
int textureBlockWidth = ImageUtils.GetBlockWidth(image.Format);
|
int textureBlockWidth = ImageUtils.GetBlockWidth(image.Format);
|
||||||
|
|
|
@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Gal
|
||||||
|
|
||||||
ulong instruction = 0;
|
ulong instruction = 0;
|
||||||
|
|
||||||
//Dump until a NOP instruction is found
|
// Dump until a NOP instruction is found
|
||||||
while ((instruction >> 48 & 0xfff8) != 0x50b0)
|
while ((instruction >> 48 & 0xfff8) != 0x50b0)
|
||||||
{
|
{
|
||||||
uint word0 = (uint)memory.ReadInt32(position + 0x50 + offset + 0);
|
uint word0 = (uint)memory.ReadInt32(position + 0x50 + offset + 0);
|
||||||
|
@ -46,8 +46,8 @@ namespace Ryujinx.Graphics.Gal
|
||||||
|
|
||||||
instruction = word0 | (ulong)word1 << 32;
|
instruction = word0 | (ulong)word1 << 32;
|
||||||
|
|
||||||
//Zero instructions (other kind of NOP) stop immediately,
|
// Zero instructions (other kind of NOP) stop immediately,
|
||||||
//this is to avoid two rows of zeroes
|
// this is to avoid two rows of zeroes
|
||||||
if (instruction == 0)
|
if (instruction == 0)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
|
@ -59,7 +59,7 @@ namespace Ryujinx.Graphics.Gal
|
||||||
offset += 8;
|
offset += 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Align to meet nvdisasm requirements
|
// Align to meet nvdisasm requirements
|
||||||
while (offset % 0x20 != 0)
|
while (offset % 0x20 != 0)
|
||||||
{
|
{
|
||||||
fullWriter.Write(0);
|
fullWriter.Write(0);
|
||||||
|
|
|
@ -109,7 +109,7 @@ namespace Ryujinx.Graphics
|
||||||
{
|
{
|
||||||
if (oldType == ImageType.ColorBuffer || oldType == ImageType.ZetaBuffer)
|
if (oldType == ImageType.ColorBuffer || oldType == ImageType.ZetaBuffer)
|
||||||
{
|
{
|
||||||
//Avoid data destruction
|
// Avoid data destruction
|
||||||
MemoryRegionModified(vmm, position, size, NvGpuBufferType.Texture);
|
MemoryRegionModified(vmm, position, size, NvGpuBufferType.Texture);
|
||||||
|
|
||||||
skipCheck = true;
|
skipCheck = true;
|
||||||
|
|
|
@ -82,8 +82,8 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
|
|
||||||
while (Step(vmm, mme));
|
while (Step(vmm, mme));
|
||||||
|
|
||||||
//Due to the delay slot, we still need to execute
|
// Due to the delay slot, we still need to execute
|
||||||
//one more instruction before we actually exit.
|
// one more instruction before we actually exit.
|
||||||
Step(vmm, mme);
|
Step(vmm, mme);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,14 +108,14 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
|
|
||||||
if ((_opCode & 7) < 7)
|
if ((_opCode & 7) < 7)
|
||||||
{
|
{
|
||||||
//Operation produces a value.
|
// Operation produces a value.
|
||||||
AssignmentOperation asgOp = (AssignmentOperation)((_opCode >> 4) & 7);
|
AssignmentOperation asgOp = (AssignmentOperation)((_opCode >> 4) & 7);
|
||||||
|
|
||||||
int result = GetAluResult();
|
int result = GetAluResult();
|
||||||
|
|
||||||
switch (asgOp)
|
switch (asgOp)
|
||||||
{
|
{
|
||||||
//Fetch parameter and ignore result.
|
// Fetch parameter and ignore result.
|
||||||
case AssignmentOperation.IgnoreAndFetch:
|
case AssignmentOperation.IgnoreAndFetch:
|
||||||
{
|
{
|
||||||
SetDstGpr(FetchParam());
|
SetDstGpr(FetchParam());
|
||||||
|
@ -123,7 +123,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Move result.
|
// Move result.
|
||||||
case AssignmentOperation.Move:
|
case AssignmentOperation.Move:
|
||||||
{
|
{
|
||||||
SetDstGpr(result);
|
SetDstGpr(result);
|
||||||
|
@ -131,7 +131,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Move result and use as Method Address.
|
// Move result and use as Method Address.
|
||||||
case AssignmentOperation.MoveAndSetMaddr:
|
case AssignmentOperation.MoveAndSetMaddr:
|
||||||
{
|
{
|
||||||
SetDstGpr(result);
|
SetDstGpr(result);
|
||||||
|
@ -141,7 +141,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Fetch parameter and send result.
|
// Fetch parameter and send result.
|
||||||
case AssignmentOperation.FetchAndSend:
|
case AssignmentOperation.FetchAndSend:
|
||||||
{
|
{
|
||||||
SetDstGpr(FetchParam());
|
SetDstGpr(FetchParam());
|
||||||
|
@ -151,7 +151,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Move and send result.
|
// Move and send result.
|
||||||
case AssignmentOperation.MoveAndSend:
|
case AssignmentOperation.MoveAndSend:
|
||||||
{
|
{
|
||||||
SetDstGpr(result);
|
SetDstGpr(result);
|
||||||
|
@ -161,7 +161,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Fetch parameter and use result as Method Address.
|
// Fetch parameter and use result as Method Address.
|
||||||
case AssignmentOperation.FetchAndSetMaddr:
|
case AssignmentOperation.FetchAndSetMaddr:
|
||||||
{
|
{
|
||||||
SetDstGpr(FetchParam());
|
SetDstGpr(FetchParam());
|
||||||
|
@ -171,7 +171,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Move result and use as Method Address, then fetch and send parameter.
|
// Move result and use as Method Address, then fetch and send parameter.
|
||||||
case AssignmentOperation.MoveAndSetMaddrThenFetchAndSend:
|
case AssignmentOperation.MoveAndSetMaddrThenFetchAndSend:
|
||||||
{
|
{
|
||||||
SetDstGpr(result);
|
SetDstGpr(result);
|
||||||
|
@ -183,7 +183,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Move result and use as Method Address, then send bits 17:12 of result.
|
// Move result and use as Method Address, then send bits 17:12 of result.
|
||||||
case AssignmentOperation.MoveAndSetMaddrThenSendHigh:
|
case AssignmentOperation.MoveAndSetMaddrThenSendHigh:
|
||||||
{
|
{
|
||||||
SetDstGpr(result);
|
SetDstGpr(result);
|
||||||
|
@ -198,7 +198,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//Branch.
|
// Branch.
|
||||||
bool onNotZero = ((_opCode >> 4) & 1) != 0;
|
bool onNotZero = ((_opCode >> 4) & 1) != 0;
|
||||||
|
|
||||||
bool taken = onNotZero
|
bool taken = onNotZero
|
||||||
|
@ -355,7 +355,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
|
|
||||||
private int GetImm()
|
private int GetImm()
|
||||||
{
|
{
|
||||||
//Note: The immediate is signed, the sign-extension is intended here.
|
// Note: The immediate is signed, the sign-extension is intended here.
|
||||||
return _opCode >> 14;
|
return _opCode >> 14;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -204,10 +204,10 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
dstBlitX + dstBlitW,
|
dstBlitX + dstBlitW,
|
||||||
dstBlitY + dstBlitH);
|
dstBlitY + dstBlitH);
|
||||||
|
|
||||||
//Do a guest side copy as well. This is necessary when
|
// Do a guest side copy as well. This is necessary when
|
||||||
//the texture is modified by the guest, however it doesn't
|
// the texture is modified by the guest, however it doesn't
|
||||||
//work when resources that the gpu can write to are copied,
|
// work when resources that the gpu can write to are copied,
|
||||||
//like framebuffers.
|
// like framebuffers.
|
||||||
|
|
||||||
// FIXME: SUPPORT MULTILAYER CORRECTLY HERE (this will cause weird stuffs on the first layer)
|
// FIXME: SUPPORT MULTILAYER CORRECTLY HERE (this will cause weird stuffs on the first layer)
|
||||||
ImageUtils.CopyTexture(
|
ImageUtils.CopyTexture(
|
||||||
|
|
|
@ -67,8 +67,8 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
_constBuffers[index] = new ConstBuffer[18];
|
_constBuffers[index] = new ConstBuffer[18];
|
||||||
}
|
}
|
||||||
|
|
||||||
//Ensure that all components are enabled by default.
|
// Ensure that all components are enabled by default.
|
||||||
//FIXME: Is this correct?
|
// FIXME: Is this correct?
|
||||||
WriteRegister(NvGpuEngine3dReg.ColorMaskN, 0x1111);
|
WriteRegister(NvGpuEngine3dReg.ColorMaskN, 0x1111);
|
||||||
|
|
||||||
WriteRegister(NvGpuEngine3dReg.FrameBufferSrgb, 1);
|
WriteRegister(NvGpuEngine3dReg.FrameBufferSrgb, 1);
|
||||||
|
@ -333,12 +333,12 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
|
|
||||||
if (vpAEnable)
|
if (vpAEnable)
|
||||||
{
|
{
|
||||||
//Note: The maxwell supports 2 vertex programs, usually
|
// Note: The maxwell supports 2 vertex programs, usually
|
||||||
//only VP B is used, but in some cases VP A is also used.
|
// only VP B is used, but in some cases VP A is also used.
|
||||||
//In this case, it seems to function as an extra vertex
|
// In this case, it seems to function as an extra vertex
|
||||||
//shader stage.
|
// shader stage.
|
||||||
//The graphics abstraction layer has a special overload for this
|
// The graphics abstraction layer has a special overload for this
|
||||||
//case, which should merge the two shaders into one vertex shader.
|
// case, which should merge the two shaders into one vertex shader.
|
||||||
int vpAOffset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset);
|
int vpAOffset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset);
|
||||||
int vpBOffset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + 0x10);
|
int vpBOffset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + 0x10);
|
||||||
|
|
||||||
|
@ -360,7 +360,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
int control = ReadRegister(NvGpuEngine3dReg.ShaderNControl + index * 0x10);
|
int control = ReadRegister(NvGpuEngine3dReg.ShaderNControl + index * 0x10);
|
||||||
int offset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + index * 0x10);
|
int offset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + index * 0x10);
|
||||||
|
|
||||||
//Note: Vertex Program (B) is always enabled.
|
// Note: Vertex Program (B) is always enabled.
|
||||||
bool enable = (control & 1) != 0 || index == 1;
|
bool enable = (control & 1) != 0 || index == 1;
|
||||||
|
|
||||||
if (!enable)
|
if (!enable)
|
||||||
|
@ -405,7 +405,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
|
|
||||||
GalFrontFace frontFace = (GalFrontFace)ReadRegister(NvGpuEngine3dReg.FrontFace);
|
GalFrontFace frontFace = (GalFrontFace)ReadRegister(NvGpuEngine3dReg.FrontFace);
|
||||||
|
|
||||||
//Flipping breaks facing. Flipping front facing too fixes it
|
// Flipping breaks facing. Flipping front facing too fixes it
|
||||||
if (signX != signY)
|
if (signX != signY)
|
||||||
{
|
{
|
||||||
switch (frontFace)
|
switch (frontFace)
|
||||||
|
@ -574,8 +574,8 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//It seems that even when independent blend is disabled, the first IBlend enable
|
// It seems that even when independent blend is disabled, the first IBlend enable
|
||||||
//register is still set to indicate whenever blend is enabled or not (?).
|
// register is still set to indicate whenever blend is enabled or not (?).
|
||||||
state.Blends[index].Enabled = ReadRegisterBool(NvGpuEngine3dReg.IBlendNEnable);
|
state.Blends[index].Enabled = ReadRegisterBool(NvGpuEngine3dReg.IBlendNEnable);
|
||||||
|
|
||||||
if (state.Blends[index].Enabled)
|
if (state.Blends[index].Enabled)
|
||||||
|
@ -632,8 +632,8 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
|
|
||||||
private void SetRenderTargets()
|
private void SetRenderTargets()
|
||||||
{
|
{
|
||||||
//Commercial games do not seem to
|
// Commercial games do not seem to
|
||||||
//bool SeparateFragData = ReadRegisterBool(NvGpuEngine3dReg.RTSeparateFragData);
|
// bool SeparateFragData = ReadRegisterBool(NvGpuEngine3dReg.RTSeparateFragData);
|
||||||
|
|
||||||
uint control = (uint)(ReadRegister(NvGpuEngine3dReg.RtControl));
|
uint control = (uint)(ReadRegister(NvGpuEngine3dReg.RtControl));
|
||||||
|
|
||||||
|
@ -711,8 +711,8 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
{
|
{
|
||||||
if (textureHandle == 0)
|
if (textureHandle == 0)
|
||||||
{
|
{
|
||||||
//FIXME: Some games like puyo puyo will use handles with the value 0.
|
// FIXME: Some games like puyo puyo will use handles with the value 0.
|
||||||
//This is a bug, most likely caused by sync issues.
|
// This is a bug, most likely caused by sync issues.
|
||||||
return (0, default(GalImage), default(GalTextureSampler));
|
return (0, default(GalImage), default(GalTextureSampler));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -751,7 +751,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
{
|
{
|
||||||
Profile.End(Profiles.GPU.Engine3d.UploadTexture);
|
Profile.End(Profiles.GPU.Engine3d.UploadTexture);
|
||||||
|
|
||||||
//FIXME: Shouldn't ignore invalid addresses.
|
// FIXME: Shouldn't ignore invalid addresses.
|
||||||
return (0, default(GalImage), default(GalTextureSampler));
|
return (0, default(GalImage), default(GalTextureSampler));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -910,8 +910,8 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
// Check vertex array is enabled to avoid out of bounds exception when reading bytes
|
// Check vertex array is enabled to avoid out of bounds exception when reading bytes
|
||||||
bool enable = (ReadRegister(NvGpuEngine3dReg.VertexArrayNControl + arrayIndex * 4) & 0x1000) != 0;
|
bool enable = (ReadRegister(NvGpuEngine3dReg.VertexArrayNControl + arrayIndex * 4) & 0x1000) != 0;
|
||||||
|
|
||||||
//Note: 16 is the maximum size of an attribute,
|
// Note: 16 is the maximum size of an attribute,
|
||||||
//having a component size of 32-bits with 4 elements (a vec4).
|
// having a component size of 32-bits with 4 elements (a vec4).
|
||||||
if (enable)
|
if (enable)
|
||||||
{
|
{
|
||||||
byte[] data = vmm.ReadBytes(vbPosition + offset, 16);
|
byte[] data = vmm.ReadBytes(vbPosition + offset, 16);
|
||||||
|
@ -954,7 +954,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
|
|
||||||
if (vbPosition > vbEndPos)
|
if (vbPosition > vbEndPos)
|
||||||
{
|
{
|
||||||
//Instance is invalid, ignore the draw call
|
// Instance is invalid, ignore the draw call
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1057,14 +1057,14 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
|
|
||||||
long iboKey = vmm.GetPhysicalAddress(indexPosition);
|
long iboKey = vmm.GetPhysicalAddress(indexPosition);
|
||||||
|
|
||||||
//Quad primitive types were deprecated on OpenGL 3.x,
|
// Quad primitive types were deprecated on OpenGL 3.x,
|
||||||
//they are converted to a triangles index buffer on IB creation,
|
// they are converted to a triangles index buffer on IB creation,
|
||||||
//so we should use the triangles type here too.
|
// so we should use the triangles type here too.
|
||||||
if (primType == GalPrimitiveType.Quads || primType == GalPrimitiveType.QuadStrip)
|
if (primType == GalPrimitiveType.Quads || primType == GalPrimitiveType.QuadStrip)
|
||||||
{
|
{
|
||||||
//Note: We assume that index first points to the first
|
// Note: We assume that index first points to the first
|
||||||
//vertex of a quad, if it points to the middle of a
|
// vertex of a quad, if it points to the middle of a
|
||||||
//quad (First % 4 != 0 for Quads) then it will not work properly.
|
// quad (First % 4 != 0 for Quads) then it will not work properly.
|
||||||
if (primType == GalPrimitiveType.Quads)
|
if (primType == GalPrimitiveType.Quads)
|
||||||
{
|
{
|
||||||
indexFirst = QuadHelper.ConvertSizeQuadsToTris(indexFirst);
|
indexFirst = QuadHelper.ConvertSizeQuadsToTris(indexFirst);
|
||||||
|
@ -1084,14 +1084,14 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
int vertexFirst = ReadRegister(NvGpuEngine3dReg.VertexArrayFirst);
|
int vertexFirst = ReadRegister(NvGpuEngine3dReg.VertexArrayFirst);
|
||||||
int vertexCount = ReadRegister(NvGpuEngine3dReg.VertexArrayCount);
|
int vertexCount = ReadRegister(NvGpuEngine3dReg.VertexArrayCount);
|
||||||
|
|
||||||
//Quad primitive types were deprecated on OpenGL 3.x,
|
// Quad primitive types were deprecated on OpenGL 3.x,
|
||||||
//they are converted to a triangles index buffer on IB creation,
|
// they are converted to a triangles index buffer on IB creation,
|
||||||
//so we should use the triangles type here too.
|
// so we should use the triangles type here too.
|
||||||
if (primType == GalPrimitiveType.Quads || primType == GalPrimitiveType.QuadStrip)
|
if (primType == GalPrimitiveType.Quads || primType == GalPrimitiveType.QuadStrip)
|
||||||
{
|
{
|
||||||
//Note: We assume that index first points to the first
|
// Note: We assume that index first points to the first
|
||||||
//vertex of a quad, if it points to the middle of a
|
// vertex of a quad, if it points to the middle of a
|
||||||
//quad (First % 4 != 0 for Quads) then it will not work properly.
|
// quad (First % 4 != 0 for Quads) then it will not work properly.
|
||||||
if (primType == GalPrimitiveType.Quads)
|
if (primType == GalPrimitiveType.Quads)
|
||||||
{
|
{
|
||||||
vertexFirst = QuadHelper.ConvertSizeQuadsToTris(vertexFirst);
|
vertexFirst = QuadHelper.ConvertSizeQuadsToTris(vertexFirst);
|
||||||
|
@ -1111,7 +1111,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
// Reset pipeline for host OpenGL calls
|
// Reset pipeline for host OpenGL calls
|
||||||
_gpu.Renderer.Pipeline.Unbind(state);
|
_gpu.Renderer.Pipeline.Unbind(state);
|
||||||
|
|
||||||
//Is the GPU really clearing those registers after draw?
|
// Is the GPU really clearing those registers after draw?
|
||||||
WriteRegister(NvGpuEngine3dReg.IndexBatchFirst, 0);
|
WriteRegister(NvGpuEngine3dReg.IndexBatchFirst, 0);
|
||||||
WriteRegister(NvGpuEngine3dReg.IndexBatchCount, 0);
|
WriteRegister(NvGpuEngine3dReg.IndexBatchCount, 0);
|
||||||
}
|
}
|
||||||
|
@ -1140,7 +1140,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
|
|
||||||
case QueryMode.WriteCounterAndTimestamp:
|
case QueryMode.WriteCounterAndTimestamp:
|
||||||
{
|
{
|
||||||
//TODO: Implement counters.
|
// TODO: Implement counters.
|
||||||
long counter = 1;
|
long counter = 1;
|
||||||
|
|
||||||
long timestamp = PerformanceCounter.ElapsedMilliseconds;
|
long timestamp = PerformanceCounter.ElapsedMilliseconds;
|
||||||
|
|
|
@ -56,7 +56,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
{
|
{
|
||||||
Profile.Begin(Profiles.GPU.EngineM2mf.Execute);
|
Profile.Begin(Profiles.GPU.EngineM2mf.Execute);
|
||||||
|
|
||||||
//TODO: Some registers and copy modes are still not implemented.
|
// TODO: Some registers and copy modes are still not implemented.
|
||||||
int control = methCall.Argument;
|
int control = methCall.Argument;
|
||||||
|
|
||||||
bool srcLinear = ((control >> 7) & 1) != 0;
|
bool srcLinear = ((control >> 7) & 1) != 0;
|
||||||
|
|
|
@ -73,7 +73,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
{
|
{
|
||||||
Profile.Begin(Profiles.GPU.EngineP2mf.Execute);
|
Profile.Begin(Profiles.GPU.EngineP2mf.Execute);
|
||||||
|
|
||||||
//TODO: Some registers and copy modes are still not implemented.
|
// TODO: Some registers and copy modes are still not implemented.
|
||||||
int control = methCall.Argument;
|
int control = methCall.Argument;
|
||||||
|
|
||||||
long dstAddress = MakeInt64From2xInt32(NvGpuEngineP2mfReg.DstAddress);
|
long dstAddress = MakeInt64From2xInt32(NvGpuEngineP2mfReg.DstAddress);
|
||||||
|
|
|
@ -7,8 +7,8 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
private const int MacrosCount = 0x80;
|
private const int MacrosCount = 0x80;
|
||||||
private const int MacroIndexMask = MacrosCount - 1;
|
private const int MacroIndexMask = MacrosCount - 1;
|
||||||
|
|
||||||
//Note: The size of the macro memory is unknown, we just make
|
// Note: The size of the macro memory is unknown, we just make
|
||||||
//a guess here and use 256kb as the size. Increase if needed.
|
// a guess here and use 256kb as the size. Increase if needed.
|
||||||
private const int MmeWords = 256 * 256;
|
private const int MmeWords = 256 * 256;
|
||||||
|
|
||||||
private NvGpu _gpu;
|
private NvGpu _gpu;
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Texture
|
||||||
public AstcDecoderException(string exMsg) : base(exMsg) { }
|
public AstcDecoderException(string exMsg) : base(exMsg) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
//https://github.com/GammaUNC/FasTC/blob/master/ASTCEncoder/src/Decompressor.cpp
|
// https://github.com/GammaUNC/FasTC/blob/master/ASTCEncoder/src/Decompressor.cpp
|
||||||
public static class AstcDecoder
|
public static class AstcDecoder
|
||||||
{
|
{
|
||||||
struct TexelWeightParams
|
struct TexelWeightParams
|
||||||
|
@ -1356,7 +1356,7 @@ namespace Ryujinx.Graphics.Texture
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
//Don't know this layout...
|
// Don't know this layout...
|
||||||
texelParams.Error = true;
|
texelParams.Error = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,7 @@ namespace Ryujinx.Graphics.Texture
|
||||||
{ GalTextureFormat.D32Fx24S8, GalImageFormat.D32S8 | Float },
|
{ GalTextureFormat.D32Fx24S8, GalImageFormat.D32S8 | Float },
|
||||||
{ GalTextureFormat.D16, GalImageFormat.D16 | Unorm },
|
{ GalTextureFormat.D16, GalImageFormat.D16 | Unorm },
|
||||||
|
|
||||||
//Compressed formats
|
// Compressed formats
|
||||||
{ GalTextureFormat.BptcSfloat, GalImageFormat.BptcSfloat | Float },
|
{ GalTextureFormat.BptcSfloat, GalImageFormat.BptcSfloat | Float },
|
||||||
{ GalTextureFormat.BptcUfloat, GalImageFormat.BptcUfloat | Float },
|
{ GalTextureFormat.BptcUfloat, GalImageFormat.BptcUfloat | Float },
|
||||||
{ GalTextureFormat.BptcUnorm, GalImageFormat.BptcUnorm | Unorm | Srgb },
|
{ GalTextureFormat.BptcUnorm, GalImageFormat.BptcUnorm | Unorm | Srgb },
|
||||||
|
@ -248,7 +248,7 @@ namespace Ryujinx.Graphics.Texture
|
||||||
|
|
||||||
int bytesPerPixel = desc.BytesPerPixel;
|
int bytesPerPixel = desc.BytesPerPixel;
|
||||||
|
|
||||||
//Note: Each row of the texture needs to be aligned to 4 bytes.
|
// Note: Each row of the texture needs to be aligned to 4 bytes.
|
||||||
int pitch = (width * bytesPerPixel + 3) & ~3;
|
int pitch = (width * bytesPerPixel + 3) & ~3;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -146,8 +146,8 @@ namespace Ryujinx.Graphics.Memory
|
||||||
|
|
||||||
private long GetFreePosition(long size, long align = 1, long start = 1L << 32)
|
private long GetFreePosition(long size, long align = 1, long start = 1L << 32)
|
||||||
{
|
{
|
||||||
//Note: Address 0 is not considered valid by the driver,
|
// Note: Address 0 is not considered valid by the driver,
|
||||||
//when 0 is returned it's considered a mapping error.
|
// when 0 is returned it's considered a mapping error.
|
||||||
long position = start;
|
long position = start;
|
||||||
long freeSize = 0;
|
long freeSize = 0;
|
||||||
|
|
||||||
|
|
|
@ -33,13 +33,13 @@ namespace Ryujinx.Graphics
|
||||||
Buffer.BlockCopy(data, src, output, dst, copyCount * entrySize);
|
Buffer.BlockCopy(data, src, output, dst, copyCount * entrySize);
|
||||||
}
|
}
|
||||||
|
|
||||||
//0 1 2 -> 0 1 2.
|
// 0 1 2 -> 0 1 2.
|
||||||
AssignIndex(0, 0, 3);
|
AssignIndex(0, 0, 3);
|
||||||
|
|
||||||
//2 3 -> 3 4.
|
// 2 3 -> 3 4.
|
||||||
AssignIndex(2, 3, 2);
|
AssignIndex(2, 3, 2);
|
||||||
|
|
||||||
//0 -> 5.
|
// 0 -> 5.
|
||||||
AssignIndex(0, 5);
|
AssignIndex(0, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,13 +65,13 @@ namespace Ryujinx.Graphics
|
||||||
Buffer.BlockCopy(data, src, output, dst, copyCount * entrySize);
|
Buffer.BlockCopy(data, src, output, dst, copyCount * entrySize);
|
||||||
}
|
}
|
||||||
|
|
||||||
//-2 -1 0 -> 0 1 2.
|
// -2 -1 0 -> 0 1 2.
|
||||||
AssignIndex(-2, 0, 3);
|
AssignIndex(-2, 0, 3);
|
||||||
|
|
||||||
//0 1 -> 3 4.
|
// 0 1 -> 3 4.
|
||||||
AssignIndex(0, 3, 2);
|
AssignIndex(0, 3, 2);
|
||||||
|
|
||||||
//-2 -> 5.
|
// -2 -> 5.
|
||||||
AssignIndex(-2, 5);
|
AssignIndex(-2, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -112,13 +112,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
|
|
||||||
public static bool NeedsParenthesis(IAstNode node, Instruction pInst, InstInfo pInfo, bool isLhs)
|
public static bool NeedsParenthesis(IAstNode node, Instruction pInst, InstInfo pInfo, bool isLhs)
|
||||||
{
|
{
|
||||||
//If the node isn't a operation, then it can only be a operand,
|
// If the node isn't a operation, then it can only be a operand,
|
||||||
//and those never needs to be surrounded in parenthesis.
|
// and those never needs to be surrounded in parenthesis.
|
||||||
if (!(node is AstOperation operation))
|
if (!(node is AstOperation operation))
|
||||||
{
|
{
|
||||||
//This is sort of a special case, if this is a negative constant,
|
// This is sort of a special case, if this is a negative constant,
|
||||||
//and it is consumed by a unary operation, we need to put on the parenthesis,
|
// and it is consumed by a unary operation, we need to put on the parenthesis,
|
||||||
//as in GLSL a sequence like --2 or ~-1 is not valid.
|
// as in GLSL a sequence like --2 or ~-1 is not valid.
|
||||||
if (IsNegativeConst(node) && pInfo.Type == InstType.OpUnary)
|
if (IsNegativeConst(node) && pInfo.Type == InstType.OpUnary)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -69,8 +69,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
arrayIndexElem = pCount++;
|
arrayIndexElem = pCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
//The sampler 1D shadow overload expects a
|
// The sampler 1D shadow overload expects a
|
||||||
//dummy value on the middle of the vector, who knows why...
|
// dummy value on the middle of the vector, who knows why...
|
||||||
bool hasDummy1DShadowElem = texOp.Type == (TextureType.Texture1D | TextureType.Shadow);
|
bool hasDummy1DShadowElem = texOp.Type == (TextureType.Texture1D | TextureType.Shadow);
|
||||||
|
|
||||||
if (hasDummy1DShadowElem)
|
if (hasDummy1DShadowElem)
|
||||||
|
@ -83,8 +83,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
pCount++;
|
pCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
//On textureGather*, the comparison value is
|
// On textureGather*, the comparison value is
|
||||||
//always specified as an extra argument.
|
// always specified as an extra argument.
|
||||||
bool hasExtraCompareArg = isShadow && isGather;
|
bool hasExtraCompareArg = isShadow && isGather;
|
||||||
|
|
||||||
if (pCount == 5)
|
if (pCount == 5)
|
||||||
|
@ -199,8 +199,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
Append(Src(VariableType.F32));
|
Append(Src(VariableType.F32));
|
||||||
}
|
}
|
||||||
|
|
||||||
//textureGather* optional extra component index,
|
// textureGather* optional extra component index,
|
||||||
//not needed for shadow samplers.
|
// not needed for shadow samplers.
|
||||||
if (isGather && !isShadow)
|
if (isGather && !isShadow)
|
||||||
{
|
{
|
||||||
Append(Src(VariableType.S32));
|
Append(Src(VariableType.S32));
|
||||||
|
|
|
@ -102,9 +102,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
|
|
||||||
public static string GetConstantBufferName(IAstNode slot, string offsetExpr, GalShaderType shaderType)
|
public static string GetConstantBufferName(IAstNode slot, string offsetExpr, GalShaderType shaderType)
|
||||||
{
|
{
|
||||||
//Non-constant slots are not supported.
|
// Non-constant slots are not supported.
|
||||||
//It is expected that upstream stages are never going to generate non-constant
|
// It is expected that upstream stages are never going to generate non-constant
|
||||||
//slot access.
|
// slot access.
|
||||||
AstOperand operand = (AstOperand)slot;
|
AstOperand operand = (AstOperand)slot;
|
||||||
|
|
||||||
string ubName = GetUbName(shaderType, operand.Value);
|
string ubName = GetUbName(shaderType, operand.Value);
|
||||||
|
@ -157,7 +157,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
}
|
}
|
||||||
else if (_builtInAttributes.TryGetValue(value & ~3, out BuiltInAttribute builtInAttr))
|
else if (_builtInAttributes.TryGetValue(value & ~3, out BuiltInAttribute builtInAttr))
|
||||||
{
|
{
|
||||||
//TODO: There must be a better way to handle this...
|
// TODO: There must be a better way to handle this...
|
||||||
if (shaderType == GalShaderType.Fragment)
|
if (shaderType == GalShaderType.Fragment)
|
||||||
{
|
{
|
||||||
switch (value & ~3)
|
switch (value & ~3)
|
||||||
|
@ -180,7 +180,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Warn about unknown built-in attribute.
|
// TODO: Warn about unknown built-in attribute.
|
||||||
|
|
||||||
return isOutAttr ? "// bad_attr0x" + value.ToString("X") : "0.0";
|
return isOutAttr ? "// bad_attr0x" + value.ToString("X") : "0.0";
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||||
|
|
||||||
while (workQueue.TryDequeue(out Block currBlock))
|
while (workQueue.TryDequeue(out Block currBlock))
|
||||||
{
|
{
|
||||||
//Check if the current block is inside another block.
|
// Check if the current block is inside another block.
|
||||||
if (BinarySearch(blocks, currBlock.Address, out int nBlkIndex))
|
if (BinarySearch(blocks, currBlock.Address, out int nBlkIndex))
|
||||||
{
|
{
|
||||||
Block nBlock = blocks[nBlkIndex];
|
Block nBlock = blocks[nBlkIndex];
|
||||||
|
@ -68,7 +68,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
//If we have a block after the current one, set the limit address.
|
// If we have a block after the current one, set the limit address.
|
||||||
ulong limitAddress = ulong.MaxValue;
|
ulong limitAddress = ulong.MaxValue;
|
||||||
|
|
||||||
if (nBlkIndex != blocks.Count)
|
if (nBlkIndex != blocks.Count)
|
||||||
|
@ -96,10 +96,10 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||||
GetBlock(ssyOp.GetAbsoluteAddress());
|
GetBlock(ssyOp.GetAbsoluteAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
//Set child blocks. "Branch" is the block the branch instruction
|
// Set child blocks. "Branch" is the block the branch instruction
|
||||||
//points to (when taken), "Next" is the block at the next address,
|
// points to (when taken), "Next" is the block at the next address,
|
||||||
//executed when the branch is not taken. For Unconditional Branches
|
// executed when the branch is not taken. For Unconditional Branches
|
||||||
//or end of program, Next is null.
|
// or end of program, Next is null.
|
||||||
OpCode lastOp = currBlock.GetLastOp();
|
OpCode lastOp = currBlock.GetLastOp();
|
||||||
|
|
||||||
if (lastOp is OpCodeBranch op)
|
if (lastOp is OpCodeBranch op)
|
||||||
|
@ -113,7 +113,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Insert the new block on the list (sorted by address).
|
// Insert the new block on the list (sorted by address).
|
||||||
if (blocks.Count != 0)
|
if (blocks.Count != 0)
|
||||||
{
|
{
|
||||||
Block nBlock = blocks[nBlkIndex];
|
Block nBlock = blocks[nBlkIndex];
|
||||||
|
@ -187,7 +187,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Ignore scheduling instructions, which are written every 32 bytes.
|
// Ignore scheduling instructions, which are written every 32 bytes.
|
||||||
if (((address - startAddress) & 0x1f) == 0)
|
if (((address - startAddress) & 0x1f) == 0)
|
||||||
{
|
{
|
||||||
address += 8;
|
address += 8;
|
||||||
|
@ -208,7 +208,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||||
|
|
||||||
if (emitter == null)
|
if (emitter == null)
|
||||||
{
|
{
|
||||||
//TODO: Warning, illegal encoding.
|
// TODO: Warning, illegal encoding.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||||
|
|
||||||
public bool InvertPredicate { get; protected set; }
|
public bool InvertPredicate { get; protected set; }
|
||||||
|
|
||||||
//When inverted, the always true predicate == always false.
|
// When inverted, the always true predicate == always false.
|
||||||
public bool NeverExecute => Predicate.Index == RegisterConsts.PredicateTrueIndex && InvertPredicate;
|
public bool NeverExecute => Predicate.Index == RegisterConsts.PredicateTrueIndex && InvertPredicate;
|
||||||
|
|
||||||
public OpCode(InstEmitter emitter, ulong address, long opCode)
|
public OpCode(InstEmitter emitter, ulong address, long opCode)
|
||||||
|
|
|
@ -36,7 +36,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
context.Copy(GetDest(context), res);
|
context.Copy(GetDest(context), res);
|
||||||
|
|
||||||
//TODO: CC, X, corner cases
|
// TODO: CC, X, corner cases
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Iadd(EmitterContext context)
|
public static void Iadd(EmitterContext context)
|
||||||
|
@ -60,7 +60,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
if (op.Extended)
|
if (op.Extended)
|
||||||
{
|
{
|
||||||
//Add carry, or subtract borrow.
|
// Add carry, or subtract borrow.
|
||||||
res = context.IAdd(res, isSubtraction
|
res = context.IAdd(res, isSubtraction
|
||||||
? context.BitwiseNot(GetCF(context))
|
? context.BitwiseNot(GetCF(context))
|
||||||
: context.BitwiseAnd(GetCF(context), Const(1)));
|
: context.BitwiseAnd(GetCF(context), Const(1)));
|
||||||
|
@ -102,7 +102,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//TODO: Warning.
|
// TODO: Warning.
|
||||||
}
|
}
|
||||||
|
|
||||||
return src;
|
return src;
|
||||||
|
@ -126,7 +126,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//TODO: Warning.
|
// TODO: Warning.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
context.Copy(GetDest(context), res);
|
context.Copy(GetDest(context), res);
|
||||||
|
|
||||||
//TODO: CC, X, corner cases
|
// TODO: CC, X, corner cases
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Imnmx(EmitterContext context)
|
public static void Imnmx(EmitterContext context)
|
||||||
|
@ -162,7 +162,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
SetZnFlags(context, dest, op.SetCondCode);
|
SetZnFlags(context, dest, op.SetCondCode);
|
||||||
|
|
||||||
//TODO: X flags.
|
// TODO: X flags.
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Iscadd(EmitterContext context)
|
public static void Iscadd(EmitterContext context)
|
||||||
|
@ -193,7 +193,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
context.Copy(GetDest(context), res);
|
context.Copy(GetDest(context), res);
|
||||||
|
|
||||||
//TODO: CC, X
|
// TODO: CC, X
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Iset(EmitterContext context)
|
public static void Iset(EmitterContext context)
|
||||||
|
@ -225,7 +225,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
context.Copy(dest, res);
|
context.Copy(dest, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: CC, X
|
// TODO: CC, X
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Isetp(EmitterContext context)
|
public static void Isetp(EmitterContext context)
|
||||||
|
@ -330,10 +330,10 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
public static void Rro(EmitterContext context)
|
public static void Rro(EmitterContext context)
|
||||||
{
|
{
|
||||||
//This is the range reduction operator,
|
// This is the range reduction operator,
|
||||||
//we translate it as a simple move, as it
|
// we translate it as a simple move, as it
|
||||||
//should be always followed by a matching
|
// should be always followed by a matching
|
||||||
//MUFU instruction.
|
// MUFU instruction.
|
||||||
OpCodeAlu op = (OpCodeAlu)context.CurrOp;
|
OpCodeAlu op = (OpCodeAlu)context.CurrOp;
|
||||||
|
|
||||||
bool negateB = op.RawOpCode.Extract(45);
|
bool negateB = op.RawOpCode.Extract(45);
|
||||||
|
@ -363,13 +363,13 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
if (!isMasked)
|
if (!isMasked)
|
||||||
{
|
{
|
||||||
//Clamped shift value.
|
// Clamped shift value.
|
||||||
Operand isLessThan32 = context.ICompareLessUnsigned(srcB, Const(32));
|
Operand isLessThan32 = context.ICompareLessUnsigned(srcB, Const(32));
|
||||||
|
|
||||||
res = context.ConditionalSelect(isLessThan32, res, Const(0));
|
res = context.ConditionalSelect(isLessThan32, res, Const(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: X, CC
|
// TODO: X, CC
|
||||||
|
|
||||||
context.Copy(GetDest(context), res);
|
context.Copy(GetDest(context), res);
|
||||||
}
|
}
|
||||||
|
@ -401,7 +401,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
if (!isMasked)
|
if (!isMasked)
|
||||||
{
|
{
|
||||||
//Clamped shift value.
|
// Clamped shift value.
|
||||||
Operand resShiftBy32;
|
Operand resShiftBy32;
|
||||||
|
|
||||||
if (isSigned)
|
if (isSigned)
|
||||||
|
@ -418,7 +418,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
res = context.ConditionalSelect(isLessThan32, res, resShiftBy32);
|
res = context.ConditionalSelect(isLessThan32, res, resShiftBy32);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: X, CC
|
// TODO: X, CC
|
||||||
|
|
||||||
context.Copy(GetDest(context), res);
|
context.Copy(GetDest(context), res);
|
||||||
}
|
}
|
||||||
|
@ -454,7 +454,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
Operand srcB = GetSrcB(context);
|
Operand srcB = GetSrcB(context);
|
||||||
Operand srcC = GetSrcC(context);
|
Operand srcC = GetSrcC(context);
|
||||||
|
|
||||||
//XMAD immediates are 16-bits unsigned integers.
|
// XMAD immediates are 16-bits unsigned integers.
|
||||||
if (srcB.Type == OperandType.Constant)
|
if (srcB.Type == OperandType.Constant)
|
||||||
{
|
{
|
||||||
srcB = Const(srcB.Value & 0xffff);
|
srcB = Const(srcB.Value & 0xffff);
|
||||||
|
@ -541,12 +541,12 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
if (extended)
|
if (extended)
|
||||||
{
|
{
|
||||||
//Add with carry.
|
// Add with carry.
|
||||||
res = context.IAdd(res, context.BitwiseAnd(GetCF(context), Const(1)));
|
res = context.IAdd(res, context.BitwiseAnd(GetCF(context), Const(1)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//Add (no carry in).
|
// Add (no carry in).
|
||||||
res = context.IAdd(res, srcC);
|
res = context.IAdd(res, srcC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -654,12 +654,12 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
if (!extended || isSubtraction)
|
if (!extended || isSubtraction)
|
||||||
{
|
{
|
||||||
//C = d < a
|
// C = d < a
|
||||||
context.Copy(GetCF(context), context.ICompareLessUnsigned(res, srcA));
|
context.Copy(GetCF(context), context.ICompareLessUnsigned(res, srcA));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//C = (d == a && CIn) || d < a
|
// C = (d == a && CIn) || d < a
|
||||||
Operand tempC0 = context.ICompareEqual (res, srcA);
|
Operand tempC0 = context.ICompareEqual (res, srcA);
|
||||||
Operand tempC1 = context.ICompareLessUnsigned(res, srcA);
|
Operand tempC1 = context.ICompareLessUnsigned(res, srcA);
|
||||||
|
|
||||||
|
@ -668,7 +668,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
context.Copy(GetCF(context), context.BitwiseOr(tempC0, tempC1));
|
context.Copy(GetCF(context), context.BitwiseOr(tempC0, tempC1));
|
||||||
}
|
}
|
||||||
|
|
||||||
//V = (d ^ a) & ~(a ^ b) < 0
|
// V = (d ^ a) & ~(a ^ b) < 0
|
||||||
Operand tempV0 = context.BitwiseExclusiveOr(res, srcA);
|
Operand tempV0 = context.BitwiseExclusiveOr(res, srcA);
|
||||||
Operand tempV1 = context.BitwiseExclusiveOr(srcA, srcB);
|
Operand tempV1 = context.BitwiseExclusiveOr(srcA, srcB);
|
||||||
|
|
||||||
|
|
|
@ -65,12 +65,12 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
if (extended)
|
if (extended)
|
||||||
{
|
{
|
||||||
//When the operation is extended, it means we are doing
|
// When the operation is extended, it means we are doing
|
||||||
//the operation on a long word with any number of bits,
|
// the operation on a long word with any number of bits,
|
||||||
//so we need to AND the zero flag from result with the
|
// so we need to AND the zero flag from result with the
|
||||||
//previous result when extended is specified, to ensure
|
// previous result when extended is specified, to ensure
|
||||||
//we have ZF set only if all words are zero, and not just
|
// we have ZF set only if all words are zero, and not just
|
||||||
//the last one.
|
// the last one.
|
||||||
Operand oldZF = GetZF(context);
|
Operand oldZF = GetZF(context);
|
||||||
|
|
||||||
Operand res = context.BitwiseAnd(context.ICompareEqual(dest, Const(0)), oldZF);
|
Operand res = context.BitwiseAnd(context.ICompareEqual(dest, Const(0)), oldZF);
|
||||||
|
|
|
@ -47,7 +47,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
WriteFP(context, dstType, srcB);
|
WriteFP(context, dstType, srcB);
|
||||||
|
|
||||||
//TODO: CC.
|
// TODO: CC.
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void F2I(EmitterContext context)
|
public static void F2I(EmitterContext context)
|
||||||
|
@ -88,7 +88,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
srcB = context.FPConvertToS32(srcB);
|
srcB = context.FPConvertToS32(srcB);
|
||||||
|
|
||||||
//TODO: S/U64, conversion overflow handling.
|
// TODO: S/U64, conversion overflow handling.
|
||||||
if (intType != IntegerType.S32)
|
if (intType != IntegerType.S32)
|
||||||
{
|
{
|
||||||
int min = GetIntMin(intType);
|
int min = GetIntMin(intType);
|
||||||
|
@ -103,7 +103,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
context.Copy(dest, srcB);
|
context.Copy(dest, srcB);
|
||||||
|
|
||||||
//TODO: CC.
|
// TODO: CC.
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void I2F(EmitterContext context)
|
public static void I2F(EmitterContext context)
|
||||||
|
@ -137,7 +137,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
WriteFP(context, dstType, srcB);
|
WriteFP(context, dstType, srcB);
|
||||||
|
|
||||||
//TODO: CC.
|
// TODO: CC.
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void I2I(EmitterContext context)
|
public static void I2I(EmitterContext context)
|
||||||
|
@ -149,7 +149,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
if (srcType == IntegerType.U64 || dstType == IntegerType.U64)
|
if (srcType == IntegerType.U64 || dstType == IntegerType.U64)
|
||||||
{
|
{
|
||||||
//TODO: Warning. This instruction doesn't support 64-bits integers
|
// TODO: Warning. This instruction doesn't support 64-bits integers
|
||||||
}
|
}
|
||||||
|
|
||||||
bool srcIsSmallInt = srcType <= IntegerType.U16;
|
bool srcIsSmallInt = srcType <= IntegerType.U16;
|
||||||
|
@ -189,7 +189,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
context.Copy(GetDest(context), srcB);
|
context.Copy(GetDest(context), srcB);
|
||||||
|
|
||||||
//TODO: CC.
|
// TODO: CC.
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void WriteFP(EmitterContext context, FPType type, Operand srcB)
|
private static void WriteFP(EmitterContext context, FPType type, Operand srcB)
|
||||||
|
@ -206,7 +206,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//TODO.
|
// TODO.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,7 +146,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
context.Copy(dest, res);
|
context.Copy(dest, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: CC, X
|
// TODO: CC, X
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Fsetp(EmitterContext context)
|
public static void Fsetp(EmitterContext context)
|
||||||
|
|
|
@ -19,8 +19,8 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
{
|
{
|
||||||
OpCodeExit op = (OpCodeExit)context.CurrOp;
|
OpCodeExit op = (OpCodeExit)context.CurrOp;
|
||||||
|
|
||||||
//TODO: Figure out how this is supposed to work in the
|
// TODO: Figure out how this is supposed to work in the
|
||||||
//presence of other condition codes.
|
// presence of other condition codes.
|
||||||
if (op.Condition == Condition.Always)
|
if (op.Condition == Condition.Always)
|
||||||
{
|
{
|
||||||
context.Return();
|
context.Return();
|
||||||
|
@ -54,8 +54,8 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
if (op.Targets.Count == 1)
|
if (op.Targets.Count == 1)
|
||||||
{
|
{
|
||||||
//If we have only one target, then the SSY is basically
|
// If we have only one target, then the SSY is basically
|
||||||
//a branch, we can produce better codegen for this case.
|
// a branch, we can produce better codegen for this case.
|
||||||
OpCodeSsy opSsy = op.Targets.Keys.First();
|
OpCodeSsy opSsy = op.Targets.Keys.First();
|
||||||
|
|
||||||
EmitBranch(context, opSsy.GetAbsoluteAddress());
|
EmitBranch(context, opSsy.GetAbsoluteAddress());
|
||||||
|
@ -79,8 +79,8 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
private static void EmitBranch(EmitterContext context, ulong address)
|
private static void EmitBranch(EmitterContext context, ulong address)
|
||||||
{
|
{
|
||||||
//If we're branching to the next instruction, then the branch
|
// If we're branching to the next instruction, then the branch
|
||||||
//is useless and we can ignore it.
|
// is useless and we can ignore it.
|
||||||
if (address == context.CurrOp.Address + 8)
|
if (address == context.CurrOp.Address + 8)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -53,7 +53,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
}
|
}
|
||||||
else if (floatType == FPType.FP64)
|
else if (floatType == FPType.FP64)
|
||||||
{
|
{
|
||||||
//TODO.
|
// TODO.
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ArgumentException($"Invalid floating point type \"{floatType}\".");
|
throw new ArgumentException($"Invalid floating point type \"{floatType}\".");
|
||||||
|
|
|
@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
if (op.Size > IntegerSize.B64)
|
if (op.Size > IntegerSize.B64)
|
||||||
{
|
{
|
||||||
//TODO: Warning.
|
// TODO: Warning.
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isSmallInt = op.Size < IntegerSize.B32;
|
bool isSmallInt = op.Size < IntegerSize.B32;
|
||||||
|
@ -121,7 +121,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
if (!(emit || cut))
|
if (!(emit || cut))
|
||||||
{
|
{
|
||||||
//TODO: Warning.
|
// TODO: Warning.
|
||||||
}
|
}
|
||||||
|
|
||||||
if (emit)
|
if (emit)
|
||||||
|
|
|
@ -443,7 +443,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
TextureProperty property = (TextureProperty)op.RawOpCode.Extract(22, 6);
|
TextureProperty property = (TextureProperty)op.RawOpCode.Extract(22, 6);
|
||||||
|
|
||||||
//TODO: Validate and use property.
|
// TODO: Validate and use property.
|
||||||
Instruction inst = Instruction.TextureSize;
|
Instruction inst = Instruction.TextureSize;
|
||||||
|
|
||||||
TextureType type = TextureType.Texture2D;
|
TextureType type = TextureType.Texture2D;
|
||||||
|
|
|
@ -16,11 +16,11 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
{
|
{
|
||||||
Operand expr = null;
|
Operand expr = null;
|
||||||
|
|
||||||
//Handle some simple cases, or cases where
|
// Handle some simple cases, or cases where
|
||||||
//the KMap would yield poor results (like XORs).
|
// the KMap would yield poor results (like XORs).
|
||||||
if (imm == 0x96 || imm == 0x69)
|
if (imm == 0x96 || imm == 0x69)
|
||||||
{
|
{
|
||||||
//XOR (0x96) and XNOR (0x69).
|
// XOR (0x96) and XNOR (0x69).
|
||||||
if (imm == 0x69)
|
if (imm == 0x69)
|
||||||
{
|
{
|
||||||
srcA = context.BitwiseNot(srcA);
|
srcA = context.BitwiseNot(srcA);
|
||||||
|
@ -33,18 +33,18 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
}
|
}
|
||||||
else if (imm == 0)
|
else if (imm == 0)
|
||||||
{
|
{
|
||||||
//Always false.
|
// Always false.
|
||||||
return Const(IrConsts.False);
|
return Const(IrConsts.False);
|
||||||
}
|
}
|
||||||
else if (imm == 0xff)
|
else if (imm == 0xff)
|
||||||
{
|
{
|
||||||
//Always true.
|
// Always true.
|
||||||
return Const(IrConsts.True);
|
return Const(IrConsts.True);
|
||||||
}
|
}
|
||||||
|
|
||||||
int map;
|
int map;
|
||||||
|
|
||||||
//Encode into gray code.
|
// Encode into gray code.
|
||||||
map = ((imm >> 0) & 1) << 0;
|
map = ((imm >> 0) & 1) << 0;
|
||||||
map |= ((imm >> 1) & 1) << 4;
|
map |= ((imm >> 1) & 1) << 4;
|
||||||
map |= ((imm >> 2) & 1) << 1;
|
map |= ((imm >> 2) & 1) << 1;
|
||||||
|
@ -54,7 +54,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
map |= ((imm >> 6) & 1) << 2;
|
map |= ((imm >> 6) & 1) << 2;
|
||||||
map |= ((imm >> 7) & 1) << 6;
|
map |= ((imm >> 7) & 1) << 6;
|
||||||
|
|
||||||
//Solve KMap, get sum of products.
|
// Solve KMap, get sum of products.
|
||||||
int visited = 0;
|
int visited = 0;
|
||||||
|
|
||||||
for (int index = 0; index < 8 && visited != 0xff; index++)
|
for (int index = 0; index < 8 && visited != 0xff; index++)
|
||||||
|
@ -76,7 +76,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//The mask should wrap, if we are on the high row, shift to low etc.
|
// The mask should wrap, if we are on the high row, shift to low etc.
|
||||||
int mask2 = (index & 4) != 0 ? mask >> 4 : mask << 4;
|
int mask2 = (index & 4) != 0 ? mask >> 4 : mask << 4;
|
||||||
|
|
||||||
if ((map & mask2) == mask2)
|
if ((map & mask2) == mask2)
|
||||||
|
|
|
@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
Inst = inst;
|
Inst = inst;
|
||||||
Dest = dest;
|
Dest = dest;
|
||||||
|
|
||||||
//The array may be modified externally, so we store a copy.
|
// The array may be modified externally, so we store a copy.
|
||||||
_sources = (Operand[])sources.Clone();
|
_sources = (Operand[])sources.Clone();
|
||||||
|
|
||||||
for (int index = 0; index < _sources.Length; index++)
|
for (int index = 0; index < _sources.Length; index++)
|
||||||
|
|
|
@ -158,7 +158,7 @@ namespace Ryujinx.Graphics.Shader
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Depth register is always two registers after the last color output.
|
// Depth register is always two registers after the last color output.
|
||||||
return count + 1;
|
return count + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
|
|
||||||
while (node != null)
|
while (node != null)
|
||||||
{
|
{
|
||||||
//We reached a child block, visit the nodes inside.
|
// We reached a child block, visit the nodes inside.
|
||||||
while (node is AstBlock childBlock)
|
while (node is AstBlock childBlock)
|
||||||
{
|
{
|
||||||
Block = childBlock;
|
Block = childBlock;
|
||||||
|
@ -43,7 +43,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
BlockEntered?.Invoke(this, new BlockVisitationEventArgs(Block));
|
BlockEntered?.Invoke(this, new BlockVisitationEventArgs(Block));
|
||||||
}
|
}
|
||||||
|
|
||||||
//Node may be null, if the block is empty.
|
// Node may be null, if the block is empty.
|
||||||
if (node != null)
|
if (node != null)
|
||||||
{
|
{
|
||||||
IAstNode next = Next(node);
|
IAstNode next = Next(node);
|
||||||
|
@ -53,7 +53,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
node = next;
|
node = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
//We reached the end of the list, go up on tree to the parent blocks.
|
// We reached the end of the list, go up on tree to the parent blocks.
|
||||||
while (node == null && Block.Type != AstBlockType.Main)
|
while (node == null && Block.Type != AstBlockType.Main)
|
||||||
{
|
{
|
||||||
BlockLeft?.Invoke(this, new BlockVisitationEventArgs(Block));
|
BlockLeft?.Invoke(this, new BlockVisitationEventArgs(Block));
|
||||||
|
|
|
@ -8,8 +8,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
{
|
{
|
||||||
static class GotoElimination
|
static class GotoElimination
|
||||||
{
|
{
|
||||||
//This is a modified version of the algorithm presented on the paper
|
// This is a modified version of the algorithm presented on the paper
|
||||||
//"Taming Control Flow: A Structured Approach to Eliminating Goto Statements".
|
// "Taming Control Flow: A Structured Approach to Eliminating Goto Statements".
|
||||||
public static void Eliminate(GotoStatement[] gotos)
|
public static void Eliminate(GotoStatement[] gotos)
|
||||||
{
|
{
|
||||||
for (int index = gotos.Length - 1; index >= 0; index--)
|
for (int index = gotos.Length - 1; index >= 0; index--)
|
||||||
|
@ -43,10 +43,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
|
|
||||||
if (Previous(stmt.Goto) is AstBlock elseBlock && elseBlock.Type == AstBlockType.Else)
|
if (Previous(stmt.Goto) is AstBlock elseBlock && elseBlock.Type == AstBlockType.Else)
|
||||||
{
|
{
|
||||||
//It's possible that the label was enclosed inside an else block,
|
// It's possible that the label was enclosed inside an else block,
|
||||||
//in this case we need to update the block and level.
|
// in this case we need to update the block and level.
|
||||||
//We also need to set the IsLoop for the case when the label is
|
// We also need to set the IsLoop for the case when the label is
|
||||||
//now before the goto, due to the newly introduced else block.
|
// now before the goto, due to the newly introduced else block.
|
||||||
lBlock = ParentBlock(stmt.Label);
|
lBlock = ParentBlock(stmt.Label);
|
||||||
|
|
||||||
lLevel = Level(lBlock);
|
lLevel = Level(lBlock);
|
||||||
|
@ -97,7 +97,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
|
|
||||||
private static bool DirectlyRelated(AstBlock lBlock, AstBlock rBlock, int lLevel, int rLevel)
|
private static bool DirectlyRelated(AstBlock lBlock, AstBlock rBlock, int lLevel, int rLevel)
|
||||||
{
|
{
|
||||||
//If the levels are equal, they can be either siblings or indirectly related.
|
// If the levels are equal, they can be either siblings or indirectly related.
|
||||||
if (lLevel == rLevel)
|
if (lLevel == rLevel)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -171,9 +171,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
|
|
||||||
AstBlock block = origin;
|
AstBlock block = origin;
|
||||||
|
|
||||||
//Check if a loop is enclosing the goto, and the block that is
|
// Check if a loop is enclosing the goto, and the block that is
|
||||||
//directly related to the label is above the loop block.
|
// directly related to the label is above the loop block.
|
||||||
//In that case, we need to introduce a break to get out of the loop.
|
// In that case, we need to introduce a break to get out of the loop.
|
||||||
AstBlock loopBlock = origin;
|
AstBlock loopBlock = origin;
|
||||||
|
|
||||||
int loopLevel = gLevel;
|
int loopLevel = gLevel;
|
||||||
|
@ -199,7 +199,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Insert ifs to skip the parts that shouldn't be executed due to the goto.
|
// Insert ifs to skip the parts that shouldn't be executed due to the goto.
|
||||||
bool tryInsertElse = stmt.IsUnconditional && origin.Type == AstBlockType.If;
|
bool tryInsertElse = stmt.IsUnconditional && origin.Type == AstBlockType.If;
|
||||||
|
|
||||||
while (gLevel > lLevel)
|
while (gLevel > lLevel)
|
||||||
|
@ -210,10 +210,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
|
|
||||||
AstBlock child = block;
|
AstBlock child = block;
|
||||||
|
|
||||||
//We can't move the goto in the middle of a if and a else block, in
|
// We can't move the goto in the middle of a if and a else block, in
|
||||||
//this case we need to move it after the else.
|
// this case we need to move it after the else.
|
||||||
//IsLoop may need to be updated if the label is inside the else, as
|
// IsLoop may need to be updated if the label is inside the else, as
|
||||||
//introducing a loop is the only way to ensure the else will be executed.
|
// introducing a loop is the only way to ensure the else will be executed.
|
||||||
if (Next(child) is AstBlock elseBlock && elseBlock.Type == AstBlockType.Else)
|
if (Next(child) is AstBlock elseBlock && elseBlock.Type == AstBlockType.Else)
|
||||||
{
|
{
|
||||||
child = elseBlock;
|
child = elseBlock;
|
||||||
|
@ -256,7 +256,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
|
|
||||||
if (child.Type == AstBlockType.If)
|
if (child.Type == AstBlockType.If)
|
||||||
{
|
{
|
||||||
//Modify the if condition to allow it to be entered by the goto.
|
// Modify the if condition to allow it to be entered by the goto.
|
||||||
if (!ContainsCondComb(child.Condition, Instruction.LogicalOr, stmt.Condition))
|
if (!ContainsCondComb(child.Condition, Instruction.LogicalOr, stmt.Condition))
|
||||||
{
|
{
|
||||||
child.OrCondition(stmt.Condition);
|
child.OrCondition(stmt.Condition);
|
||||||
|
@ -264,7 +264,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
}
|
}
|
||||||
else if (child.Type == AstBlockType.Else)
|
else if (child.Type == AstBlockType.Else)
|
||||||
{
|
{
|
||||||
//Modify the matching if condition to force the else to be entered by the goto.
|
// Modify the matching if condition to force the else to be entered by the goto.
|
||||||
if (!(Previous(child) is AstBlock ifBlock) || ifBlock.Type != AstBlockType.If)
|
if (!(Previous(child) is AstBlock ifBlock) || ifBlock.Type != AstBlockType.If)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Found an else without a matching if.");
|
throw new InvalidOperationException("Found an else without a matching if.");
|
||||||
|
@ -309,14 +309,14 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
{
|
{
|
||||||
if (block.Type == AstBlockType.DoWhile && first == block.First)
|
if (block.Type == AstBlockType.DoWhile && first == block.First)
|
||||||
{
|
{
|
||||||
//We only need to insert the continue if we're not at the end of the loop,
|
// We only need to insert the continue if we're not at the end of the loop,
|
||||||
//or if our condition is different from the loop condition.
|
// or if our condition is different from the loop condition.
|
||||||
if (Next(stmt.Goto) != null || block.Condition != stmt.Condition)
|
if (Next(stmt.Goto) != null || block.Condition != stmt.Condition)
|
||||||
{
|
{
|
||||||
EncloseSingleInst(stmt, Instruction.LoopContinue);
|
EncloseSingleInst(stmt, Instruction.LoopContinue);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Modify the do-while condition to allow it to continue.
|
// Modify the do-while condition to allow it to continue.
|
||||||
if (!ContainsCondComb(block.Condition, Instruction.LogicalOr, stmt.Condition))
|
if (!ContainsCondComb(block.Condition, Instruction.LogicalOr, stmt.Condition))
|
||||||
{
|
{
|
||||||
block.OrCondition(stmt.Condition);
|
block.OrCondition(stmt.Condition);
|
||||||
|
@ -356,10 +356,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
cond = InverseCond(cond);
|
cond = InverseCond(cond);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Do a quick check, if we are enclosing a single block,
|
// Do a quick check, if we are enclosing a single block,
|
||||||
//and the block type/condition matches the one we're going
|
// and the block type/condition matches the one we're going
|
||||||
//to create, then we don't need a new block, we can just
|
// to create, then we don't need a new block, we can just
|
||||||
//return the old one.
|
// return the old one.
|
||||||
bool hasSingleNode = Next(first) == last;
|
bool hasSingleNode = Next(first) == last;
|
||||||
|
|
||||||
if (hasSingleNode && BlockMatches(first, type, cond))
|
if (hasSingleNode && BlockMatches(first, type, cond))
|
||||||
|
|
|
@ -69,10 +69,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
|
|
||||||
AstAssignment assignment;
|
AstAssignment assignment;
|
||||||
|
|
||||||
//If all the sources are bool, it's better to use short-circuiting
|
// If all the sources are bool, it's better to use short-circuiting
|
||||||
//logical operations, rather than forcing a cast to int and doing
|
// logical operations, rather than forcing a cast to int and doing
|
||||||
//a bitwise operation with the value, as it is likely to be used as
|
// a bitwise operation with the value, as it is likely to be used as
|
||||||
//a bool in the end.
|
// a bool in the end.
|
||||||
if (IsBitwiseInst(inst) && AreAllSourceTypesEqual(sources, VariableType.Bool))
|
if (IsBitwiseInst(inst) && AreAllSourceTypesEqual(sources, VariableType.Bool))
|
||||||
{
|
{
|
||||||
inst = GetLogicalFromBitwiseInst(inst);
|
inst = GetLogicalFromBitwiseInst(inst);
|
||||||
|
|
|
@ -65,8 +65,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
|
|
||||||
private void LookForDoWhileStatements(BasicBlock block)
|
private void LookForDoWhileStatements(BasicBlock block)
|
||||||
{
|
{
|
||||||
//Check if we have any predecessor whose index is greater than the
|
// Check if we have any predecessor whose index is greater than the
|
||||||
//current block, this indicates a loop.
|
// current block, this indicates a loop.
|
||||||
bool done = false;
|
bool done = false;
|
||||||
|
|
||||||
foreach (BasicBlock predecessor in block.Predecessors.OrderByDescending(x => x.Index))
|
foreach (BasicBlock predecessor in block.Predecessors.OrderByDescending(x => x.Index))
|
||||||
|
@ -146,9 +146,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
{
|
{
|
||||||
AddNode(gotoTempAsg);
|
AddNode(gotoTempAsg);
|
||||||
|
|
||||||
//For block 0, we don't need to add the extra "reset" at the beginning,
|
// For block 0, we don't need to add the extra "reset" at the beginning,
|
||||||
//because it is already the first node to be executed on the shader,
|
// because it is already the first node to be executed on the shader,
|
||||||
//so it is reset to false by the "local" assignment anyway.
|
// so it is reset to false by the "local" assignment anyway.
|
||||||
if (block.Index != 0)
|
if (block.Index != 0)
|
||||||
{
|
{
|
||||||
Info.MainBlock.AddFirst(Assign(gotoTempAsg.Destination, Const(IrConsts.False)));
|
Info.MainBlock.AddFirst(Assign(gotoTempAsg.Destination, Const(IrConsts.False)));
|
||||||
|
|
|
@ -29,8 +29,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
public const int UserAttributeEnd = UserAttributeBase + UserAttributesCount * 16;
|
public const int UserAttributeEnd = UserAttributeBase + UserAttributesCount * 16;
|
||||||
|
|
||||||
|
|
||||||
//Note: Those attributes are used internally by the translator
|
// Note: Those attributes are used internally by the translator
|
||||||
//only, they don't exist on Maxwell.
|
// only, they don't exist on Maxwell.
|
||||||
public const int FragmentOutputDepth = 0x1000000;
|
public const int FragmentOutputDepth = 0x1000000;
|
||||||
public const int FragmentOutputColorBase = 0x1000010;
|
public const int FragmentOutputColorBase = 0x1000010;
|
||||||
public const int FragmentOutputColorEnd = FragmentOutputColorBase + 8 * 16;
|
public const int FragmentOutputColorEnd = FragmentOutputColorBase + 8 * 16;
|
||||||
|
|
|
@ -5,8 +5,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
{
|
{
|
||||||
static class Dominance
|
static class Dominance
|
||||||
{
|
{
|
||||||
//Those methods are an implementation of the algorithms on "A Simple, Fast Dominance Algorithm".
|
// Those methods are an implementation of the algorithms on "A Simple, Fast Dominance Algorithm".
|
||||||
//https://www.cs.rice.edu/~keith/EMBED/dom.pdf
|
// https://www.cs.rice.edu/~keith/EMBED/dom.pdf
|
||||||
public static void FindDominators(BasicBlock entry, int blocksCount)
|
public static void FindDominators(BasicBlock entry, int blocksCount)
|
||||||
{
|
{
|
||||||
HashSet<BasicBlock> visited = new HashSet<BasicBlock>();
|
HashSet<BasicBlock> visited = new HashSet<BasicBlock>();
|
||||||
|
|
|
@ -19,13 +19,13 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
|
|
||||||
private static bool IsRedundantBranch(Operation current, BasicBlock nextBlock)
|
private static bool IsRedundantBranch(Operation current, BasicBlock nextBlock)
|
||||||
{
|
{
|
||||||
//Here we check that:
|
// Here we check that:
|
||||||
//- The current block ends with a branch.
|
// - The current block ends with a branch.
|
||||||
//- The next block only contains a branch.
|
// - The next block only contains a branch.
|
||||||
//- The branch on the next block is unconditional.
|
// - The branch on the next block is unconditional.
|
||||||
//- Both branches are jumping to the same location.
|
// - Both branches are jumping to the same location.
|
||||||
//In this case, the branch on the current block can be removed,
|
// In this case, the branch on the current block can be removed,
|
||||||
//as the next block is going to jump to the same place anyway.
|
// as the next block is going to jump to the same place anyway.
|
||||||
if (nextBlock == null)
|
if (nextBlock == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
|
|
||||||
if (exponent == 0x1f)
|
if (exponent == 0x1f)
|
||||||
{
|
{
|
||||||
//NaN or Infinity.
|
// NaN or Infinity.
|
||||||
mantissa <<= 13;
|
mantissa <<= 13;
|
||||||
exponent = 0xff;
|
exponent = 0xff;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
{
|
{
|
||||||
if (exponent == 0)
|
if (exponent == 0)
|
||||||
{
|
{
|
||||||
//Denormal.
|
// Denormal.
|
||||||
int e = -1;
|
int e = -1;
|
||||||
int m = mantissa;
|
int m = mantissa;
|
||||||
|
|
||||||
|
|
|
@ -81,8 +81,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
|
|
||||||
private static void PropagateCopy(Operation copyOp)
|
private static void PropagateCopy(Operation copyOp)
|
||||||
{
|
{
|
||||||
//Propagate copy source operand to all uses of
|
// Propagate copy source operand to all uses of
|
||||||
//the destination operand.
|
// the destination operand.
|
||||||
Operand dest = copyOp.Dest;
|
Operand dest = copyOp.Dest;
|
||||||
Operand src = copyOp.GetSource(0);
|
Operand src = copyOp.GetSource(0);
|
||||||
|
|
||||||
|
@ -102,8 +102,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
|
|
||||||
private static bool PropagatePack(Operation packOp)
|
private static bool PropagatePack(Operation packOp)
|
||||||
{
|
{
|
||||||
//Propagate pack source operands to uses by unpack
|
// Propagate pack source operands to uses by unpack
|
||||||
//instruction. The source depends on the unpack instruction.
|
// instruction. The source depends on the unpack instruction.
|
||||||
bool modified = false;
|
bool modified = false;
|
||||||
|
|
||||||
Operand dest = packOp.Dest;
|
Operand dest = packOp.Dest;
|
||||||
|
@ -132,8 +132,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
|
|
||||||
private static void RemoveNode(BasicBlock block, LinkedListNode<INode> llNode)
|
private static void RemoveNode(BasicBlock block, LinkedListNode<INode> llNode)
|
||||||
{
|
{
|
||||||
//Remove a node from the nodes list, and also remove itself
|
// Remove a node from the nodes list, and also remove itself
|
||||||
//from all the use lists on the operands that this node uses.
|
// from all the use lists on the operands that this node uses.
|
||||||
block.Operations.Remove(llNode);
|
block.Operations.Remove(llNode);
|
||||||
|
|
||||||
Queue<INode> nodes = new Queue<INode>();
|
Queue<INode> nodes = new Queue<INode>();
|
||||||
|
|
|
@ -48,9 +48,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
|
|
||||||
private static void TryEliminateBitwiseAnd(Operation operation)
|
private static void TryEliminateBitwiseAnd(Operation operation)
|
||||||
{
|
{
|
||||||
//Try to recognize and optimize those 3 patterns (in order):
|
// Try to recognize and optimize those 3 patterns (in order):
|
||||||
//x & 0xFFFFFFFF == x, 0xFFFFFFFF & y == y,
|
// x & 0xFFFFFFFF == x, 0xFFFFFFFF & y == y,
|
||||||
//x & 0x00000000 == 0x00000000, 0x00000000 & y == 0x00000000
|
// x & 0x00000000 == 0x00000000, 0x00000000 & y == 0x00000000
|
||||||
Operand x = operation.GetSource(0);
|
Operand x = operation.GetSource(0);
|
||||||
Operand y = operation.GetSource(1);
|
Operand y = operation.GetSource(1);
|
||||||
|
|
||||||
|
@ -70,9 +70,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
|
|
||||||
private static void TryEliminateBitwiseOr(Operation operation)
|
private static void TryEliminateBitwiseOr(Operation operation)
|
||||||
{
|
{
|
||||||
//Try to recognize and optimize those 3 patterns (in order):
|
// Try to recognize and optimize those 3 patterns (in order):
|
||||||
//x | 0x00000000 == x, 0x00000000 | y == y,
|
// x | 0x00000000 == x, 0x00000000 | y == y,
|
||||||
//x | 0xFFFFFFFF == 0xFFFFFFFF, 0xFFFFFFFF | y == 0xFFFFFFFF
|
// x | 0xFFFFFFFF == 0xFFFFFFFF, 0xFFFFFFFF | y == 0xFFFFFFFF
|
||||||
Operand x = operation.GetSource(0);
|
Operand x = operation.GetSource(0);
|
||||||
Operand y = operation.GetSource(1);
|
Operand y = operation.GetSource(1);
|
||||||
|
|
||||||
|
@ -125,8 +125,8 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//The condition is constant, we can turn it into a copy, and select
|
// The condition is constant, we can turn it into a copy, and select
|
||||||
//the source based on the condition value.
|
// the source based on the condition value.
|
||||||
int srcIndex = cond.Value != 0 ? 1 : 2;
|
int srcIndex = cond.Value != 0 ? 1 : 2;
|
||||||
|
|
||||||
Operand source = operation.GetSource(srcIndex);
|
Operand source = operation.GetSource(srcIndex);
|
||||||
|
|
|
@ -86,7 +86,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
|
||||||
Queue<BasicBlock> dfPhiBlocks = new Queue<BasicBlock>();
|
Queue<BasicBlock> dfPhiBlocks = new Queue<BasicBlock>();
|
||||||
|
|
||||||
//First pass, get all defs and locals uses.
|
// First pass, get all defs and locals uses.
|
||||||
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
|
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
|
||||||
{
|
{
|
||||||
Operand[] localDefs = new Operand[RegisterConsts.TotalCount];
|
Operand[] localDefs = new Operand[RegisterConsts.TotalCount];
|
||||||
|
@ -157,7 +157,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Second pass, rename variables with definitions on different blocks.
|
// Second pass, rename variables with definitions on different blocks.
|
||||||
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
|
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
|
||||||
{
|
{
|
||||||
Operand[] localDefs = new Operand[RegisterConsts.TotalCount];
|
Operand[] localDefs = new Operand[RegisterConsts.TotalCount];
|
||||||
|
@ -251,10 +251,10 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
|
||||||
private static Operand InsertPhi(DefMap[] globalDefs, BasicBlock block, Register reg)
|
private static Operand InsertPhi(DefMap[] globalDefs, BasicBlock block, Register reg)
|
||||||
{
|
{
|
||||||
//This block has a Phi that has not been materialized yet, but that
|
// This block has a Phi that has not been materialized yet, but that
|
||||||
//would define a new version of the variable we're looking for. We need
|
// would define a new version of the variable we're looking for. We need
|
||||||
//to materialize the Phi, add all the block/operand pairs into the Phi, and
|
// to materialize the Phi, add all the block/operand pairs into the Phi, and
|
||||||
//then use the definition from that Phi.
|
// then use the definition from that Phi.
|
||||||
Operand local = Local();
|
Operand local = Local();
|
||||||
|
|
||||||
PhiNode phi = new PhiNode(local);
|
PhiNode phi = new PhiNode(local);
|
||||||
|
|
|
@ -28,7 +28,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
|
||||||
if (addressB != 0)
|
if (addressB != 0)
|
||||||
{
|
{
|
||||||
//Dual vertex shader.
|
// Dual vertex shader.
|
||||||
Operation[] shaderOpsB = DecodeShader(memory, addressB, config.Type);
|
Operation[] shaderOpsB = DecodeShader(memory, addressB, config.Type);
|
||||||
|
|
||||||
shaderOps = Combine(shaderOps, shaderOpsB);
|
shaderOps = Combine(shaderOps, shaderOpsB);
|
||||||
|
@ -86,10 +86,10 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
|
||||||
if (op is OpCodeSync opSync)
|
if (op is OpCodeSync opSync)
|
||||||
{
|
{
|
||||||
//If the instruction is a SYNC instruction with only one
|
// If the instruction is a SYNC instruction with only one
|
||||||
//possible target address, then the instruction is basically
|
// possible target address, then the instruction is basically
|
||||||
//just a simple branch, we can generate code similar to branch
|
// just a simple branch, we can generate code similar to branch
|
||||||
//instructions, with the condition check on the branch itself.
|
// instructions, with the condition check on the branch itself.
|
||||||
skipPredicateCheck |= opSync.Targets.Count < 2;
|
skipPredicateCheck |= opSync.Targets.Count < 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,15 +136,15 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
|
||||||
private static Operation[] Combine(Operation[] a, Operation[] b)
|
private static Operation[] Combine(Operation[] a, Operation[] b)
|
||||||
{
|
{
|
||||||
//Here we combine two shaders.
|
// Here we combine two shaders.
|
||||||
//For shader A:
|
// For shader A:
|
||||||
//- All user attribute stores on shader A are turned into copies to a
|
// - All user attribute stores on shader A are turned into copies to a
|
||||||
//temporary variable. It's assumed that shader B will consume them.
|
// temporary variable. It's assumed that shader B will consume them.
|
||||||
//- All return instructions are turned into branch instructions, the
|
// - All return instructions are turned into branch instructions, the
|
||||||
//branch target being the start of the shader B code.
|
// branch target being the start of the shader B code.
|
||||||
//For shader B:
|
// For shader B:
|
||||||
//- All user attribute loads on shader B are turned into copies from a
|
// - All user attribute loads on shader B are turned into copies from a
|
||||||
//temporary variable, as long that attribute is written by shader A.
|
// temporary variable, as long that attribute is written by shader A.
|
||||||
List<Operation> output = new List<Operation>(a.Length + b.Length);
|
List<Operation> output = new List<Operation>(a.Length + b.Length);
|
||||||
|
|
||||||
Operand[] temps = new Operand[AttributeConsts.UserAttributesCount * 4];
|
Operand[] temps = new Operand[AttributeConsts.UserAttributesCount * 4];
|
||||||
|
|
|
@ -89,7 +89,7 @@ namespace Ryujinx.Graphics.VDec
|
||||||
{
|
{
|
||||||
H264BitStreamWriter writer = new H264BitStreamWriter(data);
|
H264BitStreamWriter writer = new H264BitStreamWriter(data);
|
||||||
|
|
||||||
//Sequence Parameter Set.
|
// Sequence Parameter Set.
|
||||||
writer.WriteU(1, 24);
|
writer.WriteU(1, 24);
|
||||||
writer.WriteU(0, 1);
|
writer.WriteU(0, 1);
|
||||||
writer.WriteU(3, 2);
|
writer.WriteU(3, 2);
|
||||||
|
@ -145,7 +145,7 @@ namespace Ryujinx.Graphics.VDec
|
||||||
|
|
||||||
writer.End();
|
writer.End();
|
||||||
|
|
||||||
//Picture Parameter Set.
|
// Picture Parameter Set.
|
||||||
writer.WriteU(1, 24);
|
writer.WriteU(1, 24);
|
||||||
writer.WriteU(0, 1);
|
writer.WriteU(0, 1);
|
||||||
writer.WriteU(3, 2);
|
writer.WriteU(3, 2);
|
||||||
|
@ -196,7 +196,7 @@ namespace Ryujinx.Graphics.VDec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//ZigZag LUTs from libavcodec.
|
// ZigZag LUTs from libavcodec.
|
||||||
private static readonly byte[] ZigZagDirect = new byte[]
|
private static readonly byte[] ZigZagDirect = new byte[]
|
||||||
{
|
{
|
||||||
0, 1, 8, 16, 9, 2, 3, 10,
|
0, 1, 8, 16, 9, 2, 3, 10,
|
||||||
|
|
|
@ -254,7 +254,7 @@ namespace Ryujinx.Graphics.VDec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Copy chroma data from both channels with interleaving.
|
// Copy chroma data from both channels with interleaving.
|
||||||
for (int y = 0; y < halfHeight; y++)
|
for (int y = 0; y < halfHeight; y++)
|
||||||
{
|
{
|
||||||
int src = y * halfSrcWidth;
|
int src = y * halfSrcWidth;
|
||||||
|
|
|
@ -287,7 +287,7 @@ namespace Ryujinx.Graphics.VDec
|
||||||
|
|
||||||
bool showFrame = !isFrameIntra;
|
bool showFrame = !isFrameIntra;
|
||||||
|
|
||||||
//Write compressed header.
|
// Write compressed header.
|
||||||
byte[] compressedHeaderData;
|
byte[] compressedHeaderData;
|
||||||
|
|
||||||
using (MemoryStream compressedHeader = new MemoryStream())
|
using (MemoryStream compressedHeader = new MemoryStream())
|
||||||
|
@ -437,7 +437,7 @@ namespace Ryujinx.Graphics.VDec
|
||||||
compressedHeaderData = compressedHeader.ToArray();
|
compressedHeaderData = compressedHeader.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Write uncompressed header.
|
// Write uncompressed header.
|
||||||
using (MemoryStream encodedHeader = new MemoryStream())
|
using (MemoryStream encodedHeader = new MemoryStream())
|
||||||
{
|
{
|
||||||
VpxBitStreamWriter writer = new VpxBitStreamWriter(encodedHeader);
|
VpxBitStreamWriter writer = new VpxBitStreamWriter(encodedHeader);
|
||||||
|
@ -460,8 +460,8 @@ namespace Ryujinx.Graphics.VDec
|
||||||
|
|
||||||
_cachedRefFrames.Clear();
|
_cachedRefFrames.Clear();
|
||||||
|
|
||||||
//On key frames, all frame slots are set to the current frame,
|
// On key frames, all frame slots are set to the current frame,
|
||||||
//so the value of the selected slot doesn't really matter.
|
// so the value of the selected slot doesn't really matter.
|
||||||
GetNewFrameSlot(keys.CurrKey);
|
GetNewFrameSlot(keys.CurrKey);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -593,8 +593,8 @@ namespace Ryujinx.Graphics.VDec
|
||||||
|
|
||||||
int tileColsLog2IncMask = (1 << tileColsLog2Diff) - 1;
|
int tileColsLog2IncMask = (1 << tileColsLog2Diff) - 1;
|
||||||
|
|
||||||
//If it's less than the maximum, we need to add an extra 0 on the bitstream
|
// If it's less than the maximum, we need to add an extra 0 on the bitstream
|
||||||
//to indicate that it should stop reading.
|
// to indicate that it should stop reading.
|
||||||
if (header.TileColsLog2 < maxTileColsLog2)
|
if (header.TileColsLog2 < maxTileColsLog2)
|
||||||
{
|
{
|
||||||
writer.WriteU(tileColsLog2IncMask << 1, tileColsLog2Diff + 1);
|
writer.WriteU(tileColsLog2IncMask << 1, tileColsLog2Diff + 1);
|
||||||
|
@ -653,8 +653,8 @@ namespace Ryujinx.Graphics.VDec
|
||||||
return node.Value;
|
return node.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Reference frame was lost.
|
// Reference frame was lost.
|
||||||
//What we should do in this case?
|
// What we should do in this case?
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -668,8 +668,8 @@ namespace Ryujinx.Graphics.VDec
|
||||||
|
|
||||||
private void WriteCoefProbabilityUpdate(VpxRangeEncoder writer, int txMode, byte[] New, byte[] old)
|
private void WriteCoefProbabilityUpdate(VpxRangeEncoder writer, int txMode, byte[] New, byte[] old)
|
||||||
{
|
{
|
||||||
//Note: There's 1 byte added on each packet for alignment,
|
// Note: There's 1 byte added on each packet for alignment,
|
||||||
//this byte is ignored when doing updates.
|
// this byte is ignored when doing updates.
|
||||||
const int blockBytes = 2 * 2 * 6 * 6 * 4;
|
const int blockBytes = 2 * 2 * 6 * 6 * 4;
|
||||||
|
|
||||||
bool NeedsUpdate(int baseIndex)
|
bool NeedsUpdate(int baseIndex)
|
||||||
|
|
|
@ -546,7 +546,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler
|
||||||
_position += 2;
|
_position += 2;
|
||||||
// FIXME: GNU c++flit returns this but that is not what is supposed to be returned.
|
// FIXME: GNU c++flit returns this but that is not what is supposed to be returned.
|
||||||
return new NameType("half");
|
return new NameType("half");
|
||||||
//return new NameType("decimal16");
|
// return new NameType("decimal16");
|
||||||
case 'i':
|
case 'i':
|
||||||
_position += 2;
|
_position += 2;
|
||||||
return new NameType("char32_t");
|
return new NameType("char32_t");
|
||||||
|
@ -560,7 +560,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler
|
||||||
_position += 2;
|
_position += 2;
|
||||||
// FIXME: GNU c++flit returns this but that is not what is supposed to be returned.
|
// FIXME: GNU c++flit returns this but that is not what is supposed to be returned.
|
||||||
return new NameType("decltype(nullptr)");
|
return new NameType("decltype(nullptr)");
|
||||||
//return new NameType("std::nullptr_t");
|
// return new NameType("std::nullptr_t");
|
||||||
case 't':
|
case 't':
|
||||||
case 'T':
|
case 'T':
|
||||||
_position += 2;
|
_position += 2;
|
||||||
|
@ -1314,7 +1314,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler
|
||||||
if (result != null)
|
if (result != null)
|
||||||
{
|
{
|
||||||
// TODO: ABI Tags
|
// TODO: ABI Tags
|
||||||
//throw new Exception("ABI Tags not implemented");
|
// throw new Exception("ABI Tags not implemented");
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,26 +7,26 @@ namespace Ryujinx.HLE.HOS
|
||||||
{
|
{
|
||||||
public const string TemporaryNroSuffix = ".ryu_tmp.nro";
|
public const string TemporaryNroSuffix = ".ryu_tmp.nro";
|
||||||
|
|
||||||
//http://switchbrew.org/index.php?title=Homebrew_ABI
|
// http://switchbrew.org/index.php?title=Homebrew_ABI
|
||||||
public static void WriteHbAbiData(MemoryManager memory, long position, int mainThreadHandle, string switchPath)
|
public static void WriteHbAbiData(MemoryManager memory, long position, int mainThreadHandle, string switchPath)
|
||||||
{
|
{
|
||||||
//MainThreadHandle.
|
// MainThreadHandle.
|
||||||
WriteConfigEntry(memory, ref position, 1, 0, mainThreadHandle);
|
WriteConfigEntry(memory, ref position, 1, 0, mainThreadHandle);
|
||||||
|
|
||||||
//NextLoadPath.
|
// NextLoadPath.
|
||||||
WriteConfigEntry(memory, ref position, 2, 0, position + 0x200, position + 0x400);
|
WriteConfigEntry(memory, ref position, 2, 0, position + 0x200, position + 0x400);
|
||||||
|
|
||||||
//Argv.
|
// Argv.
|
||||||
long argvPosition = position + 0xC00;
|
long argvPosition = position + 0xC00;
|
||||||
|
|
||||||
memory.WriteBytes(argvPosition, Encoding.ASCII.GetBytes(switchPath + "\0"));
|
memory.WriteBytes(argvPosition, Encoding.ASCII.GetBytes(switchPath + "\0"));
|
||||||
|
|
||||||
WriteConfigEntry(memory, ref position, 5, 0, 0, argvPosition);
|
WriteConfigEntry(memory, ref position, 5, 0, 0, argvPosition);
|
||||||
|
|
||||||
//AppletType.
|
// AppletType.
|
||||||
WriteConfigEntry(memory, ref position, 7);
|
WriteConfigEntry(memory, ref position, 7);
|
||||||
|
|
||||||
//EndOfList.
|
// EndOfList.
|
||||||
WriteConfigEntry(memory, ref position, 0);
|
WriteConfigEntry(memory, ref position, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -156,8 +156,8 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
AutoObjectNames = new ConcurrentDictionary<string, KAutoObject>();
|
AutoObjectNames = new ConcurrentDictionary<string, KAutoObject>();
|
||||||
|
|
||||||
//Note: This is not really correct, but with HLE of services, the only memory
|
// Note: This is not really correct, but with HLE of services, the only memory
|
||||||
//region used that is used is Application, so we can use the other ones for anything.
|
// region used that is used is Application, so we can use the other ones for anything.
|
||||||
KMemoryRegionManager region = MemoryRegions[(int)MemoryRegion.NvServices];
|
KMemoryRegionManager region = MemoryRegions[(int)MemoryRegion.NvServices];
|
||||||
|
|
||||||
ulong hidPa = region.Address;
|
ulong hidPa = region.Address;
|
||||||
|
@ -660,7 +660,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
//Force all threads to exit.
|
// Force all threads to exit.
|
||||||
lock (Processes)
|
lock (Processes)
|
||||||
{
|
{
|
||||||
foreach (KProcess process in Processes.Values)
|
foreach (KProcess process in Processes.Values)
|
||||||
|
@ -669,8 +669,8 @@ namespace Ryujinx.HLE.HOS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//It's only safe to release resources once all threads
|
// It's only safe to release resources once all threads
|
||||||
//have exited.
|
// have exited.
|
||||||
ThreadCounter.Signal();
|
ThreadCounter.Signal();
|
||||||
ThreadCounter.Wait();
|
ThreadCounter.Wait();
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ namespace Ryujinx.HLE.HOS.Ipc
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Whats the difference between IpcDuplicateSession/Ex?
|
// TODO: Whats the difference between IpcDuplicateSession/Ex?
|
||||||
case 2:
|
case 2:
|
||||||
case 4:
|
case 4:
|
||||||
{
|
{
|
||||||
|
@ -95,7 +95,7 @@ namespace Ryujinx.HLE.HOS.Ipc
|
||||||
}
|
}
|
||||||
else if (request.Type == IpcMessageType.CloseSession)
|
else if (request.Type == IpcMessageType.CloseSession)
|
||||||
{
|
{
|
||||||
//TODO
|
// TODO
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -133,9 +133,9 @@ namespace Ryujinx.HLE.HOS.Ipc
|
||||||
|
|
||||||
int pad0 = (int)GetPadSize16(cmdPtr + 8 + handleData.Length);
|
int pad0 = (int)GetPadSize16(cmdPtr + 8 + handleData.Length);
|
||||||
|
|
||||||
//Apparently, padding after Raw Data is 16 bytes, however when there is
|
// Apparently, padding after Raw Data is 16 bytes, however when there is
|
||||||
//padding before Raw Data too, we need to subtract the size of this padding.
|
// padding before Raw Data too, we need to subtract the size of this padding.
|
||||||
//This is the weirdest padding I've seen so far...
|
// This is the weirdest padding I've seen so far...
|
||||||
int pad1 = 0x10 - pad0;
|
int pad1 = 0x10 - pad0;
|
||||||
|
|
||||||
dataLength = (dataLength + pad0 + pad1) / 4;
|
dataLength = (dataLength + pad0 + pad1) / 4;
|
||||||
|
|
|
@ -114,9 +114,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
nvServicesRg = new KMemoryArrangeRegion(nvServicesRgEnd - nvServicesRgSize, nvServicesRgSize);
|
nvServicesRg = new KMemoryArrangeRegion(nvServicesRgEnd - nvServicesRgSize, nvServicesRgSize);
|
||||||
appletRg = new KMemoryArrangeRegion(nvServicesRgEnd, appletRgSize);
|
appletRg = new KMemoryArrangeRegion(nvServicesRgEnd, appletRgSize);
|
||||||
|
|
||||||
//Note: There is an extra region used by the kernel, however
|
// Note: There is an extra region used by the kernel, however
|
||||||
//since we are doing HLE we are not going to use that memory, so give all
|
// since we are doing HLE we are not going to use that memory, so give all
|
||||||
//the remaining memory space to services.
|
// the remaining memory space to services.
|
||||||
ulong serviceRgSize = nvServicesRg.Address - DramMemoryMap.SlabHeapEnd;
|
ulong serviceRgSize = nvServicesRg.Address - DramMemoryMap.SlabHeapEnd;
|
||||||
|
|
||||||
serviceRg = new KMemoryArrangeRegion(DramMemoryMap.SlabHeapEnd, serviceRgSize);
|
serviceRg = new KMemoryArrangeRegion(DramMemoryMap.SlabHeapEnd, serviceRgSize);
|
||||||
|
|
|
@ -34,16 +34,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
|
|
||||||
if (range == -1)
|
if (range == -1)
|
||||||
{
|
{
|
||||||
//Increment would cause a overflow, special case.
|
// Increment would cause a overflow, special case.
|
||||||
return GenRandomNumber(2, 2, 32, 0xffffffffu, 0xffffffffu);
|
return GenRandomNumber(2, 2, 32, 0xffffffffu, 0xffffffffu);
|
||||||
}
|
}
|
||||||
|
|
||||||
range++;
|
range++;
|
||||||
|
|
||||||
//This is log2(Range) plus one.
|
// This is log2(Range) plus one.
|
||||||
int nextRangeLog2 = 64 - BitUtils.CountLeadingZeros64(range);
|
int nextRangeLog2 = 64 - BitUtils.CountLeadingZeros64(range);
|
||||||
|
|
||||||
//If Range is already power of 2, subtract one to use log2(Range) directly.
|
// If Range is already power of 2, subtract one to use log2(Range) directly.
|
||||||
int rangeLog2 = nextRangeLog2 - (BitUtils.IsPowerOfTwo64(range) ? 1 : 0);
|
int rangeLog2 = nextRangeLog2 - (BitUtils.IsPowerOfTwo64(range) ? 1 : 0);
|
||||||
|
|
||||||
int parts = rangeLog2 > 32 ? 2 : 1;
|
int parts = rangeLog2 > 32 ? 2 : 1;
|
||||||
|
|
|
@ -84,7 +84,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
ulong clientAddrTruncated = BitUtils.AlignDown(desc.ClientAddress, KMemoryManager.PageSize);
|
ulong clientAddrTruncated = BitUtils.AlignDown(desc.ClientAddress, KMemoryManager.PageSize);
|
||||||
ulong clientAddrRounded = BitUtils.AlignUp (desc.ClientAddress, KMemoryManager.PageSize);
|
ulong clientAddrRounded = BitUtils.AlignUp (desc.ClientAddress, KMemoryManager.PageSize);
|
||||||
|
|
||||||
//Check if address is not aligned, in this case we need to perform 2 copies.
|
// Check if address is not aligned, in this case we need to perform 2 copies.
|
||||||
if (clientAddrTruncated != clientAddrRounded)
|
if (clientAddrTruncated != clientAddrRounded)
|
||||||
{
|
{
|
||||||
ulong copySize = clientAddrRounded - desc.ClientAddress;
|
ulong copySize = clientAddrRounded - desc.ClientAddress;
|
||||||
|
|
|
@ -16,8 +16,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
|
|
||||||
private object _countIncLock;
|
private object _countIncLock;
|
||||||
|
|
||||||
//TODO: Remove that, we need it for now to allow HLE
|
// TODO: Remove that, we need it for now to allow HLE
|
||||||
//SM implementation to work with the new IPC system.
|
// SM implementation to work with the new IPC system.
|
||||||
public IpcService Service { get; set; }
|
public IpcService Service { get; set; }
|
||||||
|
|
||||||
public KClientPort(Horizon system, KPort parent, int maxSessions) : base(system)
|
public KClientPort(Horizon system, KPort parent, int maxSessions) : base(system)
|
||||||
|
|
|
@ -13,8 +13,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
|
|
||||||
public ChannelState State { get; set; }
|
public ChannelState State { get; set; }
|
||||||
|
|
||||||
//TODO: Remove that, we need it for now to allow HLE
|
// TODO: Remove that, we need it for now to allow HLE
|
||||||
//services implementation to work with the new IPC system.
|
// services implementation to work with the new IPC system.
|
||||||
public IpcService Service { get; set; }
|
public IpcService Service { get; set; }
|
||||||
|
|
||||||
public KClientSession(Horizon system, KSession parent) : base(system)
|
public KClientSession(Horizon system, KSession parent) : base(system)
|
||||||
|
|
|
@ -327,7 +327,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
|
|
||||||
uint offset;
|
uint offset;
|
||||||
|
|
||||||
//Copy handles.
|
// Copy handles.
|
||||||
if (clientHeader.HasHandles)
|
if (clientHeader.HasHandles)
|
||||||
{
|
{
|
||||||
if (clientHeader.MoveHandlesCount != 0)
|
if (clientHeader.MoveHandlesCount != 0)
|
||||||
|
@ -399,7 +399,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
offset = 2;
|
offset = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Copy pointer/receive list buffers.
|
// Copy pointer/receive list buffers.
|
||||||
uint recvListDstOffset = 0;
|
uint recvListDstOffset = 0;
|
||||||
|
|
||||||
for (int index = 0; index < clientHeader.PointerBuffersCount; index++)
|
for (int index = 0; index < clientHeader.PointerBuffersCount; index++)
|
||||||
|
@ -455,7 +455,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
offset += 2;
|
offset += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Copy send, receive and exchange buffers.
|
// Copy send, receive and exchange buffers.
|
||||||
uint totalBuffersCount =
|
uint totalBuffersCount =
|
||||||
clientHeader.SendBuffersCount +
|
clientHeader.SendBuffersCount +
|
||||||
clientHeader.ReceiveBuffersCount +
|
clientHeader.ReceiveBuffersCount +
|
||||||
|
@ -551,7 +551,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
offset += 3;
|
offset += 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Copy raw data.
|
// Copy raw data.
|
||||||
if (clientHeader.RawDataSizeInWords != 0)
|
if (clientHeader.RawDataSizeInWords != 0)
|
||||||
{
|
{
|
||||||
ulong copySrc = clientMsg.Address + offset * 4;
|
ulong copySrc = clientMsg.Address + offset * 4;
|
||||||
|
@ -683,13 +683,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
return KernelResult.InvalidCombination;
|
return KernelResult.InvalidCombination;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Read receive list.
|
// Read receive list.
|
||||||
ulong[] receiveList = GetReceiveList(
|
ulong[] receiveList = GetReceiveList(
|
||||||
clientMsg,
|
clientMsg,
|
||||||
clientHeader.ReceiveListType,
|
clientHeader.ReceiveListType,
|
||||||
clientHeader.ReceiveListOffset);
|
clientHeader.ReceiveListOffset);
|
||||||
|
|
||||||
//Copy receive and exchange buffers.
|
// Copy receive and exchange buffers.
|
||||||
clientResult = request.BufferDescriptorTable.CopyBuffersToClient(clientProcess.MemoryManager);
|
clientResult = request.BufferDescriptorTable.CopyBuffersToClient(clientProcess.MemoryManager);
|
||||||
|
|
||||||
if (clientResult != KernelResult.Success)
|
if (clientResult != KernelResult.Success)
|
||||||
|
@ -699,11 +699,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
return serverResult;
|
return serverResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Copy header.
|
// Copy header.
|
||||||
System.Device.Memory.WriteUInt32((long)clientMsg.DramAddress + 0, serverHeader.Word0);
|
System.Device.Memory.WriteUInt32((long)clientMsg.DramAddress + 0, serverHeader.Word0);
|
||||||
System.Device.Memory.WriteUInt32((long)clientMsg.DramAddress + 4, serverHeader.Word1);
|
System.Device.Memory.WriteUInt32((long)clientMsg.DramAddress + 4, serverHeader.Word1);
|
||||||
|
|
||||||
//Copy handles.
|
// Copy handles.
|
||||||
uint offset;
|
uint offset;
|
||||||
|
|
||||||
if (serverHeader.HasHandles)
|
if (serverHeader.HasHandles)
|
||||||
|
@ -763,7 +763,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
offset = 2;
|
offset = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Copy pointer/receive list buffers.
|
// Copy pointer/receive list buffers.
|
||||||
uint recvListDstOffset = 0;
|
uint recvListDstOffset = 0;
|
||||||
|
|
||||||
for (int index = 0; index < serverHeader.PointerBuffersCount; index++)
|
for (int index = 0; index < serverHeader.PointerBuffersCount; index++)
|
||||||
|
@ -811,7 +811,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
offset += 2;
|
offset += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Set send, receive and exchange buffer descriptors to zero.
|
// Set send, receive and exchange buffer descriptors to zero.
|
||||||
uint totalBuffersCount =
|
uint totalBuffersCount =
|
||||||
serverHeader.SendBuffersCount +
|
serverHeader.SendBuffersCount +
|
||||||
serverHeader.ReceiveBuffersCount +
|
serverHeader.ReceiveBuffersCount +
|
||||||
|
@ -828,7 +828,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
offset += 3;
|
offset += 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Copy raw data.
|
// Copy raw data.
|
||||||
if (serverHeader.RawDataSizeInWords != 0)
|
if (serverHeader.RawDataSizeInWords != 0)
|
||||||
{
|
{
|
||||||
ulong copyDst = clientMsg.Address + offset * 4;
|
ulong copyDst = clientMsg.Address + offset * 4;
|
||||||
|
@ -861,7 +861,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Unmap buffers from server.
|
// Unmap buffers from server.
|
||||||
clientResult = request.BufferDescriptorTable.UnmapServerBuffers(serverProcess.MemoryManager);
|
clientResult = request.BufferDescriptorTable.UnmapServerBuffers(serverProcess.MemoryManager);
|
||||||
|
|
||||||
if (clientResult != KernelResult.Success)
|
if (clientResult != KernelResult.Success)
|
||||||
|
@ -1125,9 +1125,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Client sessions can only be disconnected on async requests (because
|
// Client sessions can only be disconnected on async requests (because
|
||||||
//the client would be otherwise blocked waiting for the response), so
|
// the client would be otherwise blocked waiting for the response), so
|
||||||
//we only need to handle the async case here.
|
// we only need to handle the async case here.
|
||||||
if (request.AsyncEvent != null)
|
if (request.AsyncEvent != null)
|
||||||
{
|
{
|
||||||
SendResultToAsyncRequestClient(request, KernelResult.PortRemoteClosed);
|
SendResultToAsyncRequestClient(request, KernelResult.PortRemoteClosed);
|
||||||
|
@ -1204,7 +1204,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
|
|
||||||
private void WakeClientThread(KSessionRequest request, KernelResult result)
|
private void WakeClientThread(KSessionRequest request, KernelResult result)
|
||||||
{
|
{
|
||||||
//Wait client thread waiting for a response for the given request.
|
// Wait client thread waiting for a response for the given request.
|
||||||
if (request.AsyncEvent != null)
|
if (request.AsyncEvent != null)
|
||||||
{
|
{
|
||||||
SendResultToAsyncRequestClient(request, result);
|
SendResultToAsyncRequestClient(request, result);
|
||||||
|
@ -1237,7 +1237,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
|
||||||
|
|
||||||
private void WakeServerThreads(KernelResult result)
|
private void WakeServerThreads(KernelResult result)
|
||||||
{
|
{
|
||||||
//Wake all server threads waiting for requests.
|
// Wake all server threads waiting for requests.
|
||||||
System.CriticalSection.Enter();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
foreach (KThread thread in WaitingThreads)
|
foreach (KThread thread in WaitingThreads)
|
||||||
|
|
|
@ -23,8 +23,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
|
|
||||||
private const int KMemoryBlockSize = 0x40;
|
private const int KMemoryBlockSize = 0x40;
|
||||||
|
|
||||||
//We need 2 blocks for the case where a big block
|
// We need 2 blocks for the case where a big block
|
||||||
//needs to be split in 2, plus one block that will be the new one inserted.
|
// needs to be split in 2, plus one block that will be the new one inserted.
|
||||||
private const int MaxBlocksNeededForInsertion = 2;
|
private const int MaxBlocksNeededForInsertion = 2;
|
||||||
|
|
||||||
private LinkedList<KMemoryBlock> _blocks;
|
private LinkedList<KMemoryBlock> _blocks;
|
||||||
|
@ -215,13 +215,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
|
|
||||||
if (CodeRegionStart - baseAddress >= addrSpaceEnd - CodeRegionEnd)
|
if (CodeRegionStart - baseAddress >= addrSpaceEnd - CodeRegionEnd)
|
||||||
{
|
{
|
||||||
//Has more space before the start of the code region.
|
// Has more space before the start of the code region.
|
||||||
mapBaseAddress = baseAddress;
|
mapBaseAddress = baseAddress;
|
||||||
mapAvailableSize = CodeRegionStart - baseAddress;
|
mapAvailableSize = CodeRegionStart - baseAddress;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//Has more space after the end of the code region.
|
// Has more space after the end of the code region.
|
||||||
mapBaseAddress = CodeRegionEnd;
|
mapBaseAddress = CodeRegionEnd;
|
||||||
mapAvailableSize = addrSpaceEnd - CodeRegionEnd;
|
mapAvailableSize = addrSpaceEnd - CodeRegionEnd;
|
||||||
}
|
}
|
||||||
|
@ -250,8 +250,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
tlsIoRegion.AslrOffset = GetRandomValue(0, aslrMaxOffset >> 21) << 21;
|
tlsIoRegion.AslrOffset = GetRandomValue(0, aslrMaxOffset >> 21) << 21;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Regions are sorted based on ASLR offset.
|
// Regions are sorted based on ASLR offset.
|
||||||
//When ASLR is disabled, the order is Map, Heap, NewMap and TlsIo.
|
// When ASLR is disabled, the order is Map, Heap, NewMap and TlsIo.
|
||||||
aliasRegion.Start = mapBaseAddress + aliasRegion.AslrOffset;
|
aliasRegion.Start = mapBaseAddress + aliasRegion.AslrOffset;
|
||||||
aliasRegion.End = aliasRegion.Start + aliasRegion.Size;
|
aliasRegion.End = aliasRegion.Start + aliasRegion.Size;
|
||||||
heapRegion.Start = mapBaseAddress + heapRegion.AslrOffset;
|
heapRegion.Start = mapBaseAddress + heapRegion.AslrOffset;
|
||||||
|
@ -336,8 +336,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
|
|
||||||
private KernelResult InitializeBlocks(ulong addrSpaceStart, ulong addrSpaceEnd)
|
private KernelResult InitializeBlocks(ulong addrSpaceStart, ulong addrSpaceEnd)
|
||||||
{
|
{
|
||||||
//First insertion will always need only a single block,
|
// First insertion will always need only a single block,
|
||||||
//because there's nothing else to split.
|
// because there's nothing else to split.
|
||||||
if (!_blockAllocator.CanAllocate(1))
|
if (!_blockAllocator.CanAllocate(1))
|
||||||
{
|
{
|
||||||
return KernelResult.OutOfResource;
|
return KernelResult.OutOfResource;
|
||||||
|
@ -466,13 +466,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
|
|
||||||
public KernelResult MapNormalMemory(long address, long size, MemoryPermission permission)
|
public KernelResult MapNormalMemory(long address, long size, MemoryPermission permission)
|
||||||
{
|
{
|
||||||
//TODO.
|
// TODO.
|
||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult MapIoMemory(long address, long size, MemoryPermission permission)
|
public KernelResult MapIoMemory(long address, long size, MemoryPermission permission)
|
||||||
{
|
{
|
||||||
//TODO.
|
// TODO.
|
||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -696,7 +696,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Missing some checks here.
|
// TODO: Missing some checks here.
|
||||||
|
|
||||||
if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
|
if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
|
||||||
{
|
{
|
||||||
|
@ -730,7 +730,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
|
|
||||||
if (currentHeapSize <= size)
|
if (currentHeapSize <= size)
|
||||||
{
|
{
|
||||||
//Expand.
|
// Expand.
|
||||||
ulong diffSize = size - currentHeapSize;
|
ulong diffSize = size - currentHeapSize;
|
||||||
|
|
||||||
lock (_blocks)
|
lock (_blocks)
|
||||||
|
@ -800,7 +800,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//Shrink.
|
// Shrink.
|
||||||
ulong freeAddr = HeapRegionStart + size;
|
ulong freeAddr = HeapRegionStart + size;
|
||||||
ulong diffSize = currentHeapSize - size;
|
ulong diffSize = currentHeapSize - size;
|
||||||
|
|
||||||
|
@ -1147,7 +1147,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
out _,
|
out _,
|
||||||
out MemoryAttribute attribute))
|
out MemoryAttribute attribute))
|
||||||
{
|
{
|
||||||
//TODO: Missing checks.
|
// TODO: Missing checks.
|
||||||
|
|
||||||
if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
|
if (!_blockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
|
||||||
{
|
{
|
||||||
|
@ -1225,8 +1225,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
MemoryState newState = oldState;
|
MemoryState newState = oldState;
|
||||||
|
|
||||||
//If writing into the code region is allowed, then we need
|
// If writing into the code region is allowed, then we need
|
||||||
//to change it to mutable.
|
// to change it to mutable.
|
||||||
if ((permission & MemoryPermission.Write) != 0)
|
if ((permission & MemoryPermission.Write) != 0)
|
||||||
{
|
{
|
||||||
if (oldState == MemoryState.CodeStatic)
|
if (oldState == MemoryState.CodeStatic)
|
||||||
|
@ -1362,8 +1362,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
|
|
||||||
lock (_blocks)
|
lock (_blocks)
|
||||||
{
|
{
|
||||||
//Scan, ensure that the region can be unmapped (all blocks are heap or
|
// Scan, ensure that the region can be unmapped (all blocks are heap or
|
||||||
//already unmapped), fill pages list for freeing memory.
|
// already unmapped), fill pages list for freeing memory.
|
||||||
ulong heapMappedSize = 0;
|
ulong heapMappedSize = 0;
|
||||||
|
|
||||||
KPageList pageList = new KPageList();
|
KPageList pageList = new KPageList();
|
||||||
|
@ -1400,7 +1400,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
return KernelResult.OutOfResource;
|
return KernelResult.OutOfResource;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Try to unmap all the heap mapped memory inside range.
|
// Try to unmap all the heap mapped memory inside range.
|
||||||
KernelResult result = KernelResult.Success;
|
KernelResult result = KernelResult.Success;
|
||||||
|
|
||||||
foreach (KMemoryInfo info in IterateOverRange(address, endAddr))
|
foreach (KMemoryInfo info in IterateOverRange(address, endAddr))
|
||||||
|
@ -1416,7 +1416,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
if (result != KernelResult.Success)
|
||||||
{
|
{
|
||||||
//If we failed to unmap, we need to remap everything back again.
|
// If we failed to unmap, we need to remap everything back again.
|
||||||
MapPhysicalMemory(pageList, address, blockAddress + blockSize);
|
MapPhysicalMemory(pageList, address, blockAddress + blockSize);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -1508,7 +1508,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
MemoryAttribute attributeMask,
|
MemoryAttribute attributeMask,
|
||||||
MemoryAttribute attributeExpected)
|
MemoryAttribute attributeExpected)
|
||||||
{
|
{
|
||||||
//Client -> server.
|
// Client -> server.
|
||||||
return CopyDataFromOrToCurrentProcess(
|
return CopyDataFromOrToCurrentProcess(
|
||||||
size,
|
size,
|
||||||
src,
|
src,
|
||||||
|
@ -1531,7 +1531,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
MemoryAttribute attributeExpected,
|
MemoryAttribute attributeExpected,
|
||||||
ulong src)
|
ulong src)
|
||||||
{
|
{
|
||||||
//Server -> client.
|
// Server -> client.
|
||||||
return CopyDataFromOrToCurrentProcess(
|
return CopyDataFromOrToCurrentProcess(
|
||||||
size,
|
size,
|
||||||
dst,
|
dst,
|
||||||
|
@ -1731,7 +1731,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
|
|
||||||
foreach (KMemoryInfo info in IterateOverRange(address, endAddrRounded))
|
foreach (KMemoryInfo info in IterateOverRange(address, endAddrRounded))
|
||||||
{
|
{
|
||||||
//Check if the block state matches what we expect.
|
// Check if the block state matches what we expect.
|
||||||
if ((info.State & stateMask) != stateMask ||
|
if ((info.State & stateMask) != stateMask ||
|
||||||
(info.Permission & permission) != permission ||
|
(info.Permission & permission) != permission ||
|
||||||
(info.Attribute & attributeMask) != MemoryAttribute.None)
|
(info.Attribute & attributeMask) != MemoryAttribute.None)
|
||||||
|
@ -1830,12 +1830,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
|
|
||||||
ulong unusedSizeAfter;
|
ulong unusedSizeAfter;
|
||||||
|
|
||||||
//When the start address is unaligned, we can't safely map the
|
// When the start address is unaligned, we can't safely map the
|
||||||
//first page as it would expose other undesirable information on the
|
// first page as it would expose other undesirable information on the
|
||||||
//target process. So, instead we allocate new pages, copy the data
|
// target process. So, instead we allocate new pages, copy the data
|
||||||
//inside the range, and then clear the remaining space.
|
// inside the range, and then clear the remaining space.
|
||||||
//The same also holds for the last page, if the end address
|
// The same also holds for the last page, if the end address
|
||||||
//(address + size) is also not aligned.
|
// (address + size) is also not aligned.
|
||||||
if (copyData)
|
if (copyData)
|
||||||
{
|
{
|
||||||
ulong unusedSizeBefore = address - addressTruncated;
|
ulong unusedSizeBefore = address - addressTruncated;
|
||||||
|
@ -1883,7 +1883,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
|
|
||||||
if (endAddrTruncated != endAddrRounded)
|
if (endAddrTruncated != endAddrRounded)
|
||||||
{
|
{
|
||||||
//End is also not aligned...
|
// End is also not aligned...
|
||||||
dstLastPagePa = AllocateSinglePage(region, aslrDisabled);
|
dstLastPagePa = AllocateSinglePage(region, aslrDisabled);
|
||||||
|
|
||||||
if (dstLastPagePa == 0)
|
if (dstLastPagePa == 0)
|
||||||
|
@ -2109,7 +2109,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
foreach (KMemoryInfo info in IterateOverRange(address, endAddrTruncated))
|
foreach (KMemoryInfo info in IterateOverRange(address, endAddrTruncated))
|
||||||
{
|
{
|
||||||
//Check if the block state matches what we expect.
|
// Check if the block state matches what we expect.
|
||||||
if ((info.State & stateMask) != stateMask ||
|
if ((info.State & stateMask) != stateMask ||
|
||||||
(info.Attribute & attributeMask) != MemoryAttribute.IpcMapped)
|
(info.Attribute & attributeMask) != MemoryAttribute.IpcMapped)
|
||||||
{
|
{
|
||||||
|
@ -2331,7 +2331,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
info = node.Value.GetInfo();
|
info = node.Value.GetInfo();
|
||||||
|
|
||||||
//Check if the block state matches what we expect.
|
// Check if the block state matches what we expect.
|
||||||
if ( firstState != info.State ||
|
if ( firstState != info.State ||
|
||||||
firstPermission != info.Permission ||
|
firstPermission != info.Permission ||
|
||||||
(info.Attribute & attributeMask) != attributeExpected ||
|
(info.Attribute & attributeMask) != attributeExpected ||
|
||||||
|
@ -2367,7 +2367,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
{
|
{
|
||||||
foreach (KMemoryInfo info in IterateOverRange(address, address + size))
|
foreach (KMemoryInfo info in IterateOverRange(address, address + size))
|
||||||
{
|
{
|
||||||
//Check if the block state matches what we expect.
|
// Check if the block state matches what we expect.
|
||||||
if ((info.State & stateMask) != stateExpected ||
|
if ((info.State & stateMask) != stateExpected ||
|
||||||
(info.Permission & permissionMask) != permissionExpected ||
|
(info.Permission & permissionMask) != permissionExpected ||
|
||||||
(info.Attribute & attributeMask) != attributeExpected)
|
(info.Attribute & attributeMask) != attributeExpected)
|
||||||
|
@ -2404,9 +2404,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
MemoryPermission newPermission,
|
MemoryPermission newPermission,
|
||||||
MemoryAttribute newAttribute)
|
MemoryAttribute newAttribute)
|
||||||
{
|
{
|
||||||
//Insert new block on the list only on areas where the state
|
// Insert new block on the list only on areas where the state
|
||||||
//of the block matches the state specified on the old* state
|
// of the block matches the state specified on the old* state
|
||||||
//arguments, otherwise leave it as is.
|
// arguments, otherwise leave it as is.
|
||||||
int oldCount = _blocks.Count;
|
int oldCount = _blocks.Count;
|
||||||
|
|
||||||
oldAttribute |= MemoryAttribute.IpcAndDeviceMapped;
|
oldAttribute |= MemoryAttribute.IpcAndDeviceMapped;
|
||||||
|
@ -2472,8 +2472,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
MemoryPermission permission = MemoryPermission.None,
|
MemoryPermission permission = MemoryPermission.None,
|
||||||
MemoryAttribute attribute = MemoryAttribute.None)
|
MemoryAttribute attribute = MemoryAttribute.None)
|
||||||
{
|
{
|
||||||
//Inserts new block at the list, replacing and splitting
|
// Inserts new block at the list, replacing and splitting
|
||||||
//existing blocks as needed.
|
// existing blocks as needed.
|
||||||
int oldCount = _blocks.Count;
|
int oldCount = _blocks.Count;
|
||||||
|
|
||||||
ulong endAddr = baseAddress + pagesCount * PageSize;
|
ulong endAddr = baseAddress + pagesCount * PageSize;
|
||||||
|
@ -2537,9 +2537,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
BlockMutator blockMutate,
|
BlockMutator blockMutate,
|
||||||
MemoryPermission permission = MemoryPermission.None)
|
MemoryPermission permission = MemoryPermission.None)
|
||||||
{
|
{
|
||||||
//Inserts new block at the list, replacing and splitting
|
// Inserts new block at the list, replacing and splitting
|
||||||
//existing blocks as needed, then calling the callback
|
// existing blocks as needed, then calling the callback
|
||||||
//function on the new block.
|
// function on the new block.
|
||||||
int oldCount = _blocks.Count;
|
int oldCount = _blocks.Count;
|
||||||
|
|
||||||
ulong endAddr = baseAddress + pagesCount * PageSize;
|
ulong endAddr = baseAddress + pagesCount * PageSize;
|
||||||
|
|
|
@ -126,21 +126,21 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
|
|
||||||
ulong blockPagesCount = bestFitBlockSize / KMemoryManager.PageSize;
|
ulong blockPagesCount = bestFitBlockSize / KMemoryManager.PageSize;
|
||||||
|
|
||||||
//Check if this is the best fit for this page size.
|
// Check if this is the best fit for this page size.
|
||||||
//If so, try allocating as much requested pages as possible.
|
// If so, try allocating as much requested pages as possible.
|
||||||
while (blockPagesCount <= pagesCount)
|
while (blockPagesCount <= pagesCount)
|
||||||
{
|
{
|
||||||
ulong address = AllocatePagesForOrder(blockIndex, backwards, bestFitBlockSize);
|
ulong address = AllocatePagesForOrder(blockIndex, backwards, bestFitBlockSize);
|
||||||
|
|
||||||
//The address being zero means that no free space was found on that order,
|
// The address being zero means that no free space was found on that order,
|
||||||
//just give up and try with the next one.
|
// just give up and try with the next one.
|
||||||
if (address == 0)
|
if (address == 0)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Add new allocated page(s) to the pages list.
|
// Add new allocated page(s) to the pages list.
|
||||||
//If an error occurs, then free all allocated pages and fail.
|
// If an error occurs, then free all allocated pages and fail.
|
||||||
KernelResult result = pageList.AddRange(address, blockPagesCount);
|
KernelResult result = pageList.AddRange(address, blockPagesCount);
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
if (result != KernelResult.Success)
|
||||||
|
@ -159,13 +159,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Success case, all requested pages were allocated successfully.
|
// Success case, all requested pages were allocated successfully.
|
||||||
if (pagesCount == 0)
|
if (pagesCount == 0)
|
||||||
{
|
{
|
||||||
return KernelResult.Success;
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Error case, free allocated pages and return out of memory.
|
// Error case, free allocated pages and return out of memory.
|
||||||
foreach (KPageNode pageNode in pageList)
|
foreach (KPageNode pageNode in pageList)
|
||||||
{
|
{
|
||||||
FreePages(pageNode.Address, pageNode.PagesCount);
|
FreePages(pageNode.Address, pageNode.PagesCount);
|
||||||
|
@ -321,8 +321,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
|
|
||||||
if (address != 0)
|
if (address != 0)
|
||||||
{
|
{
|
||||||
//If we are using a larger order than best fit, then we should
|
// If we are using a larger order than best fit, then we should
|
||||||
//split it into smaller blocks.
|
// split it into smaller blocks.
|
||||||
ulong firstFreeBlockSize = 1UL << block.Order;
|
ulong firstFreeBlockSize = 1UL << block.Order;
|
||||||
|
|
||||||
if (firstFreeBlockSize > bestFitBlockSize)
|
if (firstFreeBlockSize > bestFitBlockSize)
|
||||||
|
@ -416,7 +416,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Free inside aligned region.
|
// Free inside aligned region.
|
||||||
ulong baseAddress = addressRounded;
|
ulong baseAddress = addressRounded;
|
||||||
|
|
||||||
while (baseAddress < endAddrTruncated)
|
while (baseAddress < endAddrTruncated)
|
||||||
|
@ -430,7 +430,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
|
|
||||||
int nextBlockIndex = blockIndex - 1;
|
int nextBlockIndex = blockIndex - 1;
|
||||||
|
|
||||||
//Free region between Address and aligned region start.
|
// Free region between Address and aligned region start.
|
||||||
baseAddress = addressRounded;
|
baseAddress = addressRounded;
|
||||||
|
|
||||||
for (blockIndex = nextBlockIndex; blockIndex >= 0; blockIndex--)
|
for (blockIndex = nextBlockIndex; blockIndex >= 0; blockIndex--)
|
||||||
|
@ -445,7 +445,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Free region between aligned region end and End Address.
|
// Free region between aligned region end and End Address.
|
||||||
baseAddress = endAddrTruncated;
|
baseAddress = endAddrTruncated;
|
||||||
|
|
||||||
for (blockIndex = nextBlockIndex; blockIndex >= 0; blockIndex--)
|
for (blockIndex = nextBlockIndex; blockIndex >= 0; blockIndex--)
|
||||||
|
|
|
@ -73,7 +73,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: ARM32.
|
// TODO: ARM32.
|
||||||
long framePointer = (long)threadState.X29;
|
long framePointer = (long)threadState.X29;
|
||||||
|
|
||||||
trace.AppendLine($"Process: {_owner.Name}, PID: {_owner.Pid}");
|
trace.AppendLine($"Process: {_owner.Name}, PID: {_owner.Pid}");
|
||||||
|
@ -87,8 +87,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Note: This is the return address, we need to subtract one instruction
|
// Note: This is the return address, we need to subtract one instruction
|
||||||
//worth of bytes to get the branch instruction address.
|
// worth of bytes to get the branch instruction address.
|
||||||
AppendTrace(_owner.CpuMemory.ReadInt64(framePointer + 8) - 4);
|
AppendTrace(_owner.CpuMemory.ReadInt64(framePointer + 8) - 4);
|
||||||
|
|
||||||
framePointer = _owner.CpuMemory.ReadInt64(framePointer);
|
framePointer = _owner.CpuMemory.ReadInt64(framePointer);
|
||||||
|
@ -243,7 +243,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
long ehHdrEndOffset = memory.ReadInt32(mod0Offset + 0x14) + mod0Offset;
|
long ehHdrEndOffset = memory.ReadInt32(mod0Offset + 0x14) + mod0Offset;
|
||||||
long modObjOffset = memory.ReadInt32(mod0Offset + 0x18) + mod0Offset;
|
long modObjOffset = memory.ReadInt32(mod0Offset + 0x18) + mod0Offset;
|
||||||
|
|
||||||
//TODO: Elf32.
|
// TODO: Elf32.
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
long tagVal = memory.ReadInt64(dynamicOffset + 0);
|
long tagVal = memory.ReadInt64(dynamicOffset + 0);
|
||||||
|
|
|
@ -350,7 +350,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
|
||||||
private KernelResult ParseProcessInfo(ProcessCreationInfo creationInfo)
|
private KernelResult ParseProcessInfo(ProcessCreationInfo creationInfo)
|
||||||
{
|
{
|
||||||
//Ensure that the current kernel version is equal or above to the minimum required.
|
// Ensure that the current kernel version is equal or above to the minimum required.
|
||||||
uint requiredKernelVersionMajor = (uint)Capabilities.KernelReleaseVersion >> 19;
|
uint requiredKernelVersionMajor = (uint)Capabilities.KernelReleaseVersion >> 19;
|
||||||
uint requiredKernelVersionMinor = ((uint)Capabilities.KernelReleaseVersion >> 15) & 0xf;
|
uint requiredKernelVersionMinor = ((uint)Capabilities.KernelReleaseVersion >> 15) & 0xf;
|
||||||
|
|
||||||
|
@ -429,7 +429,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
|
||||||
if (_freeTlsPages.Count > 0)
|
if (_freeTlsPages.Count > 0)
|
||||||
{
|
{
|
||||||
//If we have free TLS pages available, just use the first one.
|
// If we have free TLS pages available, just use the first one.
|
||||||
KTlsPageInfo pageInfo = _freeTlsPages.Values.First();
|
KTlsPageInfo pageInfo = _freeTlsPages.Values.First();
|
||||||
|
|
||||||
if (!pageInfo.TryGetFreePage(out address))
|
if (!pageInfo.TryGetFreePage(out address))
|
||||||
|
@ -448,7 +448,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//Otherwise, we need to create a new one.
|
// Otherwise, we need to create a new one.
|
||||||
result = AllocateTlsPage(out KTlsPageInfo pageInfo);
|
result = AllocateTlsPage(out KTlsPageInfo pageInfo);
|
||||||
|
|
||||||
if (result == KernelResult.Success)
|
if (result == KernelResult.Success)
|
||||||
|
@ -522,7 +522,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
|
||||||
if (_fullTlsPages.TryGetValue(tlsPageAddr, out pageInfo))
|
if (_fullTlsPages.TryGetValue(tlsPageAddr, out pageInfo))
|
||||||
{
|
{
|
||||||
//TLS page was full, free slot and move to free pages tree.
|
// TLS page was full, free slot and move to free pages tree.
|
||||||
_fullTlsPages.Remove(tlsPageAddr);
|
_fullTlsPages.Remove(tlsPageAddr);
|
||||||
|
|
||||||
_freeTlsPages.Add(tlsPageAddr, pageInfo);
|
_freeTlsPages.Add(tlsPageAddr, pageInfo);
|
||||||
|
@ -538,8 +538,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
|
||||||
if (pageInfo.IsEmpty())
|
if (pageInfo.IsEmpty())
|
||||||
{
|
{
|
||||||
//TLS page is now empty, we should ensure it is removed
|
// TLS page is now empty, we should ensure it is removed
|
||||||
//from all trees, and free the memory it was using.
|
// from all trees, and free the memory it was using.
|
||||||
_freeTlsPages.Remove(tlsPageAddr);
|
_freeTlsPages.Remove(tlsPageAddr);
|
||||||
|
|
||||||
System.CriticalSection.Leave();
|
System.CriticalSection.Leave();
|
||||||
|
@ -574,7 +574,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
|
||||||
private void GenerateRandomEntropy()
|
private void GenerateRandomEntropy()
|
||||||
{
|
{
|
||||||
//TODO.
|
// TODO.
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult Start(int mainThreadPriority, ulong stackSize)
|
public KernelResult Start(int mainThreadPriority, ulong stackSize)
|
||||||
|
@ -603,9 +603,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
|
||||||
ulong neededSize = stackSizeRounded + _imageSize;
|
ulong neededSize = stackSizeRounded + _imageSize;
|
||||||
|
|
||||||
//Check if the needed size for the code and the stack will fit on the
|
// Check if the needed size for the code and the stack will fit on the
|
||||||
//memory usage capacity of this Process. Also check for possible overflow
|
// memory usage capacity of this Process. Also check for possible overflow
|
||||||
//on the above addition.
|
// on the above addition.
|
||||||
if (neededSize > _memoryUsageCapacity ||
|
if (neededSize > _memoryUsageCapacity ||
|
||||||
neededSize < stackSizeRounded)
|
neededSize < stackSizeRounded)
|
||||||
{
|
{
|
||||||
|
@ -742,10 +742,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
|
||||||
SetState(newState);
|
SetState(newState);
|
||||||
|
|
||||||
//TODO: We can't call KThread.Start from a non-guest thread.
|
// TODO: We can't call KThread.Start from a non-guest thread.
|
||||||
//We will need to make some changes to allow the creation of
|
// We will need to make some changes to allow the creation of
|
||||||
//dummy threads that will be used to initialize the current
|
// dummy threads that will be used to initialize the current
|
||||||
//thread on KCoreContext so that GetCurrentThread doesn't fail.
|
// thread on KCoreContext so that GetCurrentThread doesn't fail.
|
||||||
/* Result = MainThread.Start();
|
/* Result = MainThread.Start();
|
||||||
|
|
||||||
if (Result != KernelResult.Success)
|
if (Result != KernelResult.Success)
|
||||||
|
@ -935,7 +935,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
|
||||||
if (shallTerminate)
|
if (shallTerminate)
|
||||||
{
|
{
|
||||||
//UnpauseAndTerminateAllThreadsExcept(System.Scheduler.GetCurrentThread());
|
// UnpauseAndTerminateAllThreadsExcept(System.Scheduler.GetCurrentThread());
|
||||||
|
|
||||||
HandleTable.Destroy();
|
HandleTable.Destroy();
|
||||||
|
|
||||||
|
@ -948,12 +948,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
|
||||||
private void UnpauseAndTerminateAllThreadsExcept(KThread thread)
|
private void UnpauseAndTerminateAllThreadsExcept(KThread thread)
|
||||||
{
|
{
|
||||||
//TODO.
|
// TODO.
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SignalExitForDebugEvent()
|
private void SignalExitForDebugEvent()
|
||||||
{
|
{
|
||||||
//TODO: Debug events.
|
// TODO: Debug events.
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SignalExit()
|
private void SignalExit()
|
||||||
|
|
|
@ -131,7 +131,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
|
||||||
int codeMask = 1 << (32 - BitUtils.CountLeadingZeros32(code + 1));
|
int codeMask = 1 << (32 - BitUtils.CountLeadingZeros32(code + 1));
|
||||||
|
|
||||||
//Check if the property was already set.
|
// Check if the property was already set.
|
||||||
if (((mask0 & codeMask) & 0x1e008) != 0)
|
if (((mask0 & codeMask) & 0x1e008) != 0)
|
||||||
{
|
{
|
||||||
return KernelResult.InvalidCombination;
|
return KernelResult.InvalidCombination;
|
||||||
|
@ -223,7 +223,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
|
||||||
case 0x800:
|
case 0x800:
|
||||||
{
|
{
|
||||||
//TODO: GIC distributor check.
|
// TODO: GIC distributor check.
|
||||||
int irq0 = (cap >> 12) & 0x3ff;
|
int irq0 = (cap >> 12) & 0x3ff;
|
||||||
int irq1 = (cap >> 22) & 0x3ff;
|
int irq1 = (cap >> 22) & 0x3ff;
|
||||||
|
|
||||||
|
@ -256,7 +256,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
|
||||||
case 0x4000:
|
case 0x4000:
|
||||||
{
|
{
|
||||||
//Note: This check is bugged on kernel too, we are just replicating the bug here.
|
// Note: This check is bugged on kernel too, we are just replicating the bug here.
|
||||||
if ((KernelReleaseVersion >> 17) != 0 || cap < 0x80000)
|
if ((KernelReleaseVersion >> 17) != 0 || cap < 0x80000)
|
||||||
{
|
{
|
||||||
return KernelResult.ReservedValue;
|
return KernelResult.ReservedValue;
|
||||||
|
|
|
@ -161,7 +161,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
process = thread.Owner;
|
process = thread.Owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: KDebugEvent.
|
// TODO: KDebugEvent.
|
||||||
}
|
}
|
||||||
|
|
||||||
pid = process?.Pid ?? 0;
|
pid = process?.Pid ?? 0;
|
||||||
|
@ -549,10 +549,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
|
|
||||||
switch (id)
|
switch (id)
|
||||||
{
|
{
|
||||||
//Memory region capacity.
|
// Memory region capacity.
|
||||||
case 0: value = (long)region.Size; break;
|
case 0: value = (long)region.Size; break;
|
||||||
|
|
||||||
//Memory region free space.
|
// Memory region free space.
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
ulong freePagesCount = region.GetFreePages();
|
ulong freePagesCount = region.GetFreePages();
|
||||||
|
|
|
@ -151,10 +151,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//For functions returning output values, the first registers
|
// For functions returning output values, the first registers
|
||||||
//are used to hold pointers where the value will be stored,
|
// are used to hold pointers where the value will be stored,
|
||||||
//so they can't be used to pass argument and we must
|
// so they can't be used to pass argument and we must
|
||||||
//skip them.
|
// skip them.
|
||||||
int byRefArgsCount = 0;
|
int byRefArgsCount = 0;
|
||||||
|
|
||||||
for (int index = 0; index < methodArgs.Length; index++)
|
for (int index = 0; index < methodArgs.Length; index++)
|
||||||
|
@ -165,7 +165,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Print all the arguments for debugging purposes.
|
// Print all the arguments for debugging purposes.
|
||||||
int inputArgsCount = methodArgs.Length - byRefArgsCount;
|
int inputArgsCount = methodArgs.Length - byRefArgsCount;
|
||||||
|
|
||||||
generator.Emit(OpCodes.Ldc_I4_S, inputArgsCount);
|
generator.Emit(OpCodes.Ldc_I4_S, inputArgsCount);
|
||||||
|
@ -200,7 +200,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
|
|
||||||
generator.Emit(OpCodes.Call, printArgsMethod);
|
generator.Emit(OpCodes.Call, printArgsMethod);
|
||||||
|
|
||||||
//Call the SVC function handler.
|
// Call the SVC function handler.
|
||||||
generator.Emit(OpCodes.Ldarg_0);
|
generator.Emit(OpCodes.Ldarg_0);
|
||||||
|
|
||||||
List<LocalBuilder> locals = new List<LocalBuilder>();
|
List<LocalBuilder> locals = new List<LocalBuilder>();
|
||||||
|
@ -239,7 +239,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
|
|
||||||
Type retType = methodInfo.ReturnType;
|
Type retType = methodInfo.ReturnType;
|
||||||
|
|
||||||
//Print result code.
|
// Print result code.
|
||||||
if (retType == typeof(KernelResult))
|
if (retType == typeof(KernelResult))
|
||||||
{
|
{
|
||||||
MethodInfo printResultMethod = typeof(SvcTable).GetMethod(nameof(PrintResult), staticNonPublic);
|
MethodInfo printResultMethod = typeof(SvcTable).GetMethod(nameof(PrintResult), staticNonPublic);
|
||||||
|
@ -249,7 +249,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
generator.Emit(OpCodes.Call, printResultMethod);
|
generator.Emit(OpCodes.Call, printResultMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Save return value into register X0 (when the method has a return value).
|
// Save return value into register X0 (when the method has a return value).
|
||||||
if (retType != typeof(void))
|
if (retType != typeof(void))
|
||||||
{
|
{
|
||||||
CheckIfTypeIsSupported(retType, svcName);
|
CheckIfTypeIsSupported(retType, svcName);
|
||||||
|
@ -275,7 +275,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
generator.Emit(OpCodes.Stfld, GetStateFieldX(outRegIndex++));
|
generator.Emit(OpCodes.Stfld, GetStateFieldX(outRegIndex++));
|
||||||
}
|
}
|
||||||
|
|
||||||
//Zero out the remaining unused registers.
|
// Zero out the remaining unused registers.
|
||||||
while (outRegIndex < SvcFuncMaxArguments)
|
while (outRegIndex < SvcFuncMaxArguments)
|
||||||
{
|
{
|
||||||
generator.Emit(OpCodes.Ldarg_1);
|
generator.Emit(OpCodes.Ldarg_1);
|
||||||
|
|
|
@ -175,7 +175,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
|
|
||||||
public KernelResult SetThreadPriority(int handle, int priority)
|
public KernelResult SetThreadPriority(int handle, int priority)
|
||||||
{
|
{
|
||||||
//TODO: NPDM check.
|
// TODO: NPDM check.
|
||||||
|
|
||||||
KThread thread = _process.HandleTable.GetKThread(handle);
|
KThread thread = _process.HandleTable.GetKThread(handle);
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue