Merge branch 'master' into CoOp

This commit is contained in:
Dudejoe870 2018-09-06 11:49:43 -05:00 committed by GitHub
commit 3c38a19fcb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
97 changed files with 7282 additions and 1202 deletions

View file

@ -180,6 +180,10 @@ namespace ChocolArm64
SetA64("0>001110<<1xxxxx101111xxxxxxxxxx", AInstEmit.Addp_V, typeof(AOpCodeSimdReg));
SetA64("000011100x110001101110xxxxxxxxxx", AInstEmit.Addv_V, typeof(AOpCodeSimd));
SetA64("01001110<<110001101110xxxxxxxxxx", AInstEmit.Addv_V, typeof(AOpCodeSimd));
SetA64("0100111000101000010110xxxxxxxxxx", AInstEmit.Aesd_V, typeof(AOpCodeSimd));
SetA64("0100111000101000010010xxxxxxxxxx", AInstEmit.Aese_V, typeof(AOpCodeSimd));
SetA64("0100111000101000011110xxxxxxxxxx", AInstEmit.Aesimc_V, typeof(AOpCodeSimd));
SetA64("0100111000101000011010xxxxxxxxxx", AInstEmit.Aesmc_V, typeof(AOpCodeSimd));
SetA64("0x001110001xxxxx000111xxxxxxxxxx", AInstEmit.And_V, typeof(AOpCodeSimdReg));
SetA64("0x001110011xxxxx000111xxxxxxxxxx", AInstEmit.Bic_V, typeof(AOpCodeSimdReg));
SetA64("0x10111100000xxx<<x101xxxxxxxxxx", AInstEmit.Bic_Vi, typeof(AOpCodeSimdImm));
@ -251,6 +255,10 @@ namespace ChocolArm64
SetA64("x00111100x110000000000xxxxxxxxxx", AInstEmit.Fcvtms_Gp, typeof(AOpCodeSimdCvt));
SetA64("x00111100x110001000000xxxxxxxxxx", AInstEmit.Fcvtmu_Gp, typeof(AOpCodeSimdCvt));
SetA64("0x0011100x100001011010xxxxxxxxxx", AInstEmit.Fcvtn_V, typeof(AOpCodeSimd));
SetA64("010111100x100001101010xxxxxxxxxx", AInstEmit.Fcvtns_S, typeof(AOpCodeSimd));
SetA64("0>0011100<100001101010xxxxxxxxxx", AInstEmit.Fcvtns_V, typeof(AOpCodeSimd));
SetA64("011111100x100001101010xxxxxxxxxx", AInstEmit.Fcvtnu_S, typeof(AOpCodeSimd));
SetA64("0>1011100<100001101010xxxxxxxxxx", AInstEmit.Fcvtnu_V, typeof(AOpCodeSimd));
SetA64("x00111100x101000000000xxxxxxxxxx", AInstEmit.Fcvtps_Gp, typeof(AOpCodeSimdCvt));
SetA64("x00111100x101001000000xxxxxxxxxx", AInstEmit.Fcvtpu_Gp, typeof(AOpCodeSimdCvt));
SetA64("x00111100x111000000000xxxxxxxxxx", AInstEmit.Fcvtzs_Gp, typeof(AOpCodeSimdCvt));
@ -361,6 +369,7 @@ namespace ChocolArm64
SetA64("0x001110<<1xxxxx011101xxxxxxxxxx", AInstEmit.Sabd_V, typeof(AOpCodeSimdReg));
SetA64("0x001110<<1xxxxx011100xxxxxxxxxx", AInstEmit.Sabdl_V, typeof(AOpCodeSimdReg));
SetA64("0x001110<<100000011010xxxxxxxxxx", AInstEmit.Sadalp_V, typeof(AOpCodeSimd));
SetA64("0x001110<<1xxxxx000000xxxxxxxxxx", AInstEmit.Saddl_V, typeof(AOpCodeSimdReg));
SetA64("0x001110<<100000001010xxxxxxxxxx", AInstEmit.Saddlp_V, typeof(AOpCodeSimd));
SetA64("0x001110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Saddw_V, typeof(AOpCodeSimdReg));
SetA64("x0011110xx100010000000xxxxxxxxxx", AInstEmit.Scvtf_Gp, typeof(AOpCodeSimdCvt));
@ -370,10 +379,12 @@ namespace ChocolArm64
SetA64("01011110000xxxxx010100xxxxxxxxxx", AInstEmit.Sha256h2_V, typeof(AOpCodeSimdReg));
SetA64("0101111000101000001010xxxxxxxxxx", AInstEmit.Sha256su0_V, typeof(AOpCodeSimd));
SetA64("01011110000xxxxx011000xxxxxxxxxx", AInstEmit.Sha256su1_V, typeof(AOpCodeSimdReg));
SetA64("0x001110<<1xxxxx000001xxxxxxxxxx", AInstEmit.Shadd_V, typeof(AOpCodeSimdReg));
SetA64("010111110>>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_S, typeof(AOpCodeSimdShImm));
SetA64("0x0011110>>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_V, typeof(AOpCodeSimdShImm));
SetA64("0x101110<<100001001110xxxxxxxxxx", AInstEmit.Shll_V, typeof(AOpCodeSimd));
SetA64("0x00111100>>>xxx100001xxxxxxxxxx", AInstEmit.Shrn_V, typeof(AOpCodeSimdShImm));
SetA64("0x001110<<1xxxxx001001xxxxxxxxxx", AInstEmit.Shsub_V, typeof(AOpCodeSimdReg));
SetA64("0x1011110>>>>xxx010101xxxxxxxxxx", AInstEmit.Sli_V, typeof(AOpCodeSimdShImm));
SetA64("0x001110<<1xxxxx011001xxxxxxxxxx", AInstEmit.Smax_V, typeof(AOpCodeSimdReg));
SetA64("0x001110<<1xxxxx101001xxxxxxxxxx", AInstEmit.Smaxp_V, typeof(AOpCodeSimdReg));
@ -403,6 +414,7 @@ namespace ChocolArm64
SetA64("0x001110<<100001010010xxxxxxxxxx", AInstEmit.Sqxtn_V, typeof(AOpCodeSimd));
SetA64("01111110<<100001001010xxxxxxxxxx", AInstEmit.Sqxtun_S, typeof(AOpCodeSimd));
SetA64("0x101110<<100001001010xxxxxxxxxx", AInstEmit.Sqxtun_V, typeof(AOpCodeSimd));
SetA64("0x001110<<1xxxxx000101xxxxxxxxxx", AInstEmit.Srhadd_V, typeof(AOpCodeSimdReg));
SetA64("0x00111100>>>xxx001001xxxxxxxxxx", AInstEmit.Srshr_V, typeof(AOpCodeSimdShImm));
SetA64("0100111101xxxxxx001001xxxxxxxxxx", AInstEmit.Srshr_V, typeof(AOpCodeSimdShImm));
SetA64("0>001110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Sshl_V, typeof(AOpCodeSimdReg));
@ -412,6 +424,7 @@ namespace ChocolArm64
SetA64("0100111101xxxxxx000001xxxxxxxxxx", AInstEmit.Sshr_V, typeof(AOpCodeSimdShImm));
SetA64("0x00111100>>>xxx000101xxxxxxxxxx", AInstEmit.Ssra_V, typeof(AOpCodeSimdShImm));
SetA64("0100111101xxxxxx000101xxxxxxxxxx", AInstEmit.Ssra_V, typeof(AOpCodeSimdShImm));
SetA64("0x001110<<1xxxxx001000xxxxxxxxxx", AInstEmit.Ssubl_V, typeof(AOpCodeSimdReg));
SetA64("0x001110<<1xxxxx001100xxxxxxxxxx", AInstEmit.Ssubw_V, typeof(AOpCodeSimdReg));
SetA64("0x00110000000000xxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs));
SetA64("0x001100100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs));
@ -445,10 +458,13 @@ namespace ChocolArm64
SetA64("011111100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_S, typeof(AOpCodeSimd));
SetA64("0x1011100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_V, typeof(AOpCodeSimd));
SetA64("0x101110<<1xxxxx000001xxxxxxxxxx", AInstEmit.Uhadd_V, typeof(AOpCodeSimdReg));
SetA64("0x101110<<1xxxxx001001xxxxxxxxxx", AInstEmit.Uhsub_V, typeof(AOpCodeSimdReg));
SetA64("0x101110<<1xxxxx011001xxxxxxxxxx", AInstEmit.Umax_V, typeof(AOpCodeSimdReg));
SetA64("0x101110<<1xxxxx101001xxxxxxxxxx", AInstEmit.Umaxp_V, typeof(AOpCodeSimdReg));
SetA64("0x101110<<1xxxxx011011xxxxxxxxxx", AInstEmit.Umin_V, typeof(AOpCodeSimdReg));
SetA64("0x101110<<1xxxxx101011xxxxxxxxxx", AInstEmit.Uminp_V, typeof(AOpCodeSimdReg));
SetA64("0x101110<<1xxxxx100000xxxxxxxxxx", AInstEmit.Umlal_V, typeof(AOpCodeSimdReg));
SetA64("0x101110<<1xxxxx101000xxxxxxxxxx", AInstEmit.Umlsl_V, typeof(AOpCodeSimdReg));
SetA64("0x001110000xxxxx001111xxxxxxxxxx", AInstEmit.Umov_S, typeof(AOpCodeSimdIns));
SetA64("0x101110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Umull_V, typeof(AOpCodeSimdReg));
SetA64("01111110xx1xxxxx000011xxxxxxxxxx", AInstEmit.Uqadd_S, typeof(AOpCodeSimdReg));
@ -457,6 +473,7 @@ namespace ChocolArm64
SetA64("0>101110<<1xxxxx001011xxxxxxxxxx", AInstEmit.Uqsub_V, typeof(AOpCodeSimdReg));
SetA64("01111110<<100001010010xxxxxxxxxx", AInstEmit.Uqxtn_S, typeof(AOpCodeSimd));
SetA64("0x101110<<100001010010xxxxxxxxxx", AInstEmit.Uqxtn_V, typeof(AOpCodeSimd));
SetA64("0x101110<<1xxxxx000101xxxxxxxxxx", AInstEmit.Urhadd_V, typeof(AOpCodeSimdReg));
SetA64("0>101110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Ushl_V, typeof(AOpCodeSimdReg));
SetA64("0x10111100>>>xxx101001xxxxxxxxxx", AInstEmit.Ushll_V, typeof(AOpCodeSimdShImm));
SetA64("0111111101xxxxxx000001xxxxxxxxxx", AInstEmit.Ushr_S, typeof(AOpCodeSimdShImm));
@ -466,6 +483,7 @@ namespace ChocolArm64
SetA64("0>101110<<100000001110xxxxxxxxxx", AInstEmit.Usqadd_V, typeof(AOpCodeSimd));
SetA64("0x10111100>>>xxx000101xxxxxxxxxx", AInstEmit.Usra_V, typeof(AOpCodeSimdShImm));
SetA64("0110111101xxxxxx000101xxxxxxxxxx", AInstEmit.Usra_V, typeof(AOpCodeSimdShImm));
SetA64("0x101110<<1xxxxx001000xxxxxxxxxx", AInstEmit.Usubl_V, typeof(AOpCodeSimdReg));
SetA64("0x101110<<1xxxxx001100xxxxxxxxxx", AInstEmit.Usubw_V, typeof(AOpCodeSimdReg));
SetA64("0>001110<<0xxxxx000110xxxxxxxxxx", AInstEmit.Uzp1_V, typeof(AOpCodeSimdReg));
SetA64("0>001110<<0xxxxx010110xxxxxxxxxx", AInstEmit.Uzp2_V, typeof(AOpCodeSimdReg));

View file

@ -0,0 +1,328 @@
// https://www.intel.com/content/dam/doc/white-paper/advanced-encryption-standard-new-instructions-set-paper.pdf
using System;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
namespace ChocolArm64.Instruction
{
static class ACryptoHelper
{
#region "LookUp Tables"
private static byte[] SBox =
{
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
};
private static byte[] InvSBox =
{
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
};
private static byte[] GFMul_02 =
{
0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e,
0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e,
0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe,
0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe,
0x1b, 0x19, 0x1f, 0x1d, 0x13, 0x11, 0x17, 0x15, 0x0b, 0x09, 0x0f, 0x0d, 0x03, 0x01, 0x07, 0x05,
0x3b, 0x39, 0x3f, 0x3d, 0x33, 0x31, 0x37, 0x35, 0x2b, 0x29, 0x2f, 0x2d, 0x23, 0x21, 0x27, 0x25,
0x5b, 0x59, 0x5f, 0x5d, 0x53, 0x51, 0x57, 0x55, 0x4b, 0x49, 0x4f, 0x4d, 0x43, 0x41, 0x47, 0x45,
0x7b, 0x79, 0x7f, 0x7d, 0x73, 0x71, 0x77, 0x75, 0x6b, 0x69, 0x6f, 0x6d, 0x63, 0x61, 0x67, 0x65,
0x9b, 0x99, 0x9f, 0x9d, 0x93, 0x91, 0x97, 0x95, 0x8b, 0x89, 0x8f, 0x8d, 0x83, 0x81, 0x87, 0x85,
0xbb, 0xb9, 0xbf, 0xbd, 0xb3, 0xb1, 0xb7, 0xb5, 0xab, 0xa9, 0xaf, 0xad, 0xa3, 0xa1, 0xa7, 0xa5,
0xdb, 0xd9, 0xdf, 0xdd, 0xd3, 0xd1, 0xd7, 0xd5, 0xcb, 0xc9, 0xcf, 0xcd, 0xc3, 0xc1, 0xc7, 0xc5,
0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5
};
private static byte[] GFMul_03 =
{
0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11,
0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21,
0x60, 0x63, 0x66, 0x65, 0x6c, 0x6f, 0x6a, 0x69, 0x78, 0x7b, 0x7e, 0x7d, 0x74, 0x77, 0x72, 0x71,
0x50, 0x53, 0x56, 0x55, 0x5c, 0x5f, 0x5a, 0x59, 0x48, 0x4b, 0x4e, 0x4d, 0x44, 0x47, 0x42, 0x41,
0xc0, 0xc3, 0xc6, 0xc5, 0xcc, 0xcf, 0xca, 0xc9, 0xd8, 0xdb, 0xde, 0xdd, 0xd4, 0xd7, 0xd2, 0xd1,
0xf0, 0xf3, 0xf6, 0xf5, 0xfc, 0xff, 0xfa, 0xf9, 0xe8, 0xeb, 0xee, 0xed, 0xe4, 0xe7, 0xe2, 0xe1,
0xa0, 0xa3, 0xa6, 0xa5, 0xac, 0xaf, 0xaa, 0xa9, 0xb8, 0xbb, 0xbe, 0xbd, 0xb4, 0xb7, 0xb2, 0xb1,
0x90, 0x93, 0x96, 0x95, 0x9c, 0x9f, 0x9a, 0x99, 0x88, 0x8b, 0x8e, 0x8d, 0x84, 0x87, 0x82, 0x81,
0x9b, 0x98, 0x9d, 0x9e, 0x97, 0x94, 0x91, 0x92, 0x83, 0x80, 0x85, 0x86, 0x8f, 0x8c, 0x89, 0x8a,
0xab, 0xa8, 0xad, 0xae, 0xa7, 0xa4, 0xa1, 0xa2, 0xb3, 0xb0, 0xb5, 0xb6, 0xbf, 0xbc, 0xb9, 0xba,
0xfb, 0xf8, 0xfd, 0xfe, 0xf7, 0xf4, 0xf1, 0xf2, 0xe3, 0xe0, 0xe5, 0xe6, 0xef, 0xec, 0xe9, 0xea,
0xcb, 0xc8, 0xcd, 0xce, 0xc7, 0xc4, 0xc1, 0xc2, 0xd3, 0xd0, 0xd5, 0xd6, 0xdf, 0xdc, 0xd9, 0xda,
0x5b, 0x58, 0x5d, 0x5e, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46, 0x4f, 0x4c, 0x49, 0x4a,
0x6b, 0x68, 0x6d, 0x6e, 0x67, 0x64, 0x61, 0x62, 0x73, 0x70, 0x75, 0x76, 0x7f, 0x7c, 0x79, 0x7a,
0x3b, 0x38, 0x3d, 0x3e, 0x37, 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26, 0x2f, 0x2c, 0x29, 0x2a,
0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a
};
private static byte[] GFMul_09 =
{
0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77,
0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7,
0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
0xab, 0xa2, 0xb9, 0xb0, 0x8f, 0x86, 0x9d, 0x94, 0xe3, 0xea, 0xf1, 0xf8, 0xc7, 0xce, 0xd5, 0xdc,
0x76, 0x7f, 0x64, 0x6d, 0x52, 0x5b, 0x40, 0x49, 0x3e, 0x37, 0x2c, 0x25, 0x1a, 0x13, 0x08, 0x01,
0xe6, 0xef, 0xf4, 0xfd, 0xc2, 0xcb, 0xd0, 0xd9, 0xae, 0xa7, 0xbc, 0xb5, 0x8a, 0x83, 0x98, 0x91,
0x4d, 0x44, 0x5f, 0x56, 0x69, 0x60, 0x7b, 0x72, 0x05, 0x0c, 0x17, 0x1e, 0x21, 0x28, 0x33, 0x3a,
0xdd, 0xd4, 0xcf, 0xc6, 0xf9, 0xf0, 0xeb, 0xe2, 0x95, 0x9c, 0x87, 0x8e, 0xb1, 0xb8, 0xa3, 0xaa,
0xec, 0xe5, 0xfe, 0xf7, 0xc8, 0xc1, 0xda, 0xd3, 0xa4, 0xad, 0xb6, 0xbf, 0x80, 0x89, 0x92, 0x9b,
0x7c, 0x75, 0x6e, 0x67, 0x58, 0x51, 0x4a, 0x43, 0x34, 0x3d, 0x26, 0x2f, 0x10, 0x19, 0x02, 0x0b,
0xd7, 0xde, 0xc5, 0xcc, 0xf3, 0xfa, 0xe1, 0xe8, 0x9f, 0x96, 0x8d, 0x84, 0xbb, 0xb2, 0xa9, 0xa0,
0x47, 0x4e, 0x55, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x0f, 0x06, 0x1d, 0x14, 0x2b, 0x22, 0x39, 0x30,
0x9a, 0x93, 0x88, 0x81, 0xbe, 0xb7, 0xac, 0xa5, 0xd2, 0xdb, 0xc0, 0xc9, 0xf6, 0xff, 0xe4, 0xed,
0x0a, 0x03, 0x18, 0x11, 0x2e, 0x27, 0x3c, 0x35, 0x42, 0x4b, 0x50, 0x59, 0x66, 0x6f, 0x74, 0x7d,
0xa1, 0xa8, 0xb3, 0xba, 0x85, 0x8c, 0x97, 0x9e, 0xe9, 0xe0, 0xfb, 0xf2, 0xcd, 0xc4, 0xdf, 0xd6,
0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46
};
private static byte[] GFMul_0B =
{
0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69,
0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9,
0x7b, 0x70, 0x6d, 0x66, 0x57, 0x5c, 0x41, 0x4a, 0x23, 0x28, 0x35, 0x3e, 0x0f, 0x04, 0x19, 0x12,
0xcb, 0xc0, 0xdd, 0xd6, 0xe7, 0xec, 0xf1, 0xfa, 0x93, 0x98, 0x85, 0x8e, 0xbf, 0xb4, 0xa9, 0xa2,
0xf6, 0xfd, 0xe0, 0xeb, 0xda, 0xd1, 0xcc, 0xc7, 0xae, 0xa5, 0xb8, 0xb3, 0x82, 0x89, 0x94, 0x9f,
0x46, 0x4d, 0x50, 0x5b, 0x6a, 0x61, 0x7c, 0x77, 0x1e, 0x15, 0x08, 0x03, 0x32, 0x39, 0x24, 0x2f,
0x8d, 0x86, 0x9b, 0x90, 0xa1, 0xaa, 0xb7, 0xbc, 0xd5, 0xde, 0xc3, 0xc8, 0xf9, 0xf2, 0xef, 0xe4,
0x3d, 0x36, 0x2b, 0x20, 0x11, 0x1a, 0x07, 0x0c, 0x65, 0x6e, 0x73, 0x78, 0x49, 0x42, 0x5f, 0x54,
0xf7, 0xfc, 0xe1, 0xea, 0xdb, 0xd0, 0xcd, 0xc6, 0xaf, 0xa4, 0xb9, 0xb2, 0x83, 0x88, 0x95, 0x9e,
0x47, 0x4c, 0x51, 0x5a, 0x6b, 0x60, 0x7d, 0x76, 0x1f, 0x14, 0x09, 0x02, 0x33, 0x38, 0x25, 0x2e,
0x8c, 0x87, 0x9a, 0x91, 0xa0, 0xab, 0xb6, 0xbd, 0xd4, 0xdf, 0xc2, 0xc9, 0xf8, 0xf3, 0xee, 0xe5,
0x3c, 0x37, 0x2a, 0x21, 0x10, 0x1b, 0x06, 0x0d, 0x64, 0x6f, 0x72, 0x79, 0x48, 0x43, 0x5e, 0x55,
0x01, 0x0a, 0x17, 0x1c, 0x2d, 0x26, 0x3b, 0x30, 0x59, 0x52, 0x4f, 0x44, 0x75, 0x7e, 0x63, 0x68,
0xb1, 0xba, 0xa7, 0xac, 0x9d, 0x96, 0x8b, 0x80, 0xe9, 0xe2, 0xff, 0xf4, 0xc5, 0xce, 0xd3, 0xd8,
0x7a, 0x71, 0x6c, 0x67, 0x56, 0x5d, 0x40, 0x4b, 0x22, 0x29, 0x34, 0x3f, 0x0e, 0x05, 0x18, 0x13,
0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3
};
private static byte[] GFMul_0D =
{
0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b,
0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b,
0xbb, 0xb6, 0xa1, 0xac, 0x8f, 0x82, 0x95, 0x98, 0xd3, 0xde, 0xc9, 0xc4, 0xe7, 0xea, 0xfd, 0xf0,
0x6b, 0x66, 0x71, 0x7c, 0x5f, 0x52, 0x45, 0x48, 0x03, 0x0e, 0x19, 0x14, 0x37, 0x3a, 0x2d, 0x20,
0x6d, 0x60, 0x77, 0x7a, 0x59, 0x54, 0x43, 0x4e, 0x05, 0x08, 0x1f, 0x12, 0x31, 0x3c, 0x2b, 0x26,
0xbd, 0xb0, 0xa7, 0xaa, 0x89, 0x84, 0x93, 0x9e, 0xd5, 0xd8, 0xcf, 0xc2, 0xe1, 0xec, 0xfb, 0xf6,
0xd6, 0xdb, 0xcc, 0xc1, 0xe2, 0xef, 0xf8, 0xf5, 0xbe, 0xb3, 0xa4, 0xa9, 0x8a, 0x87, 0x90, 0x9d,
0x06, 0x0b, 0x1c, 0x11, 0x32, 0x3f, 0x28, 0x25, 0x6e, 0x63, 0x74, 0x79, 0x5a, 0x57, 0x40, 0x4d,
0xda, 0xd7, 0xc0, 0xcd, 0xee, 0xe3, 0xf4, 0xf9, 0xb2, 0xbf, 0xa8, 0xa5, 0x86, 0x8b, 0x9c, 0x91,
0x0a, 0x07, 0x10, 0x1d, 0x3e, 0x33, 0x24, 0x29, 0x62, 0x6f, 0x78, 0x75, 0x56, 0x5b, 0x4c, 0x41,
0x61, 0x6c, 0x7b, 0x76, 0x55, 0x58, 0x4f, 0x42, 0x09, 0x04, 0x13, 0x1e, 0x3d, 0x30, 0x27, 0x2a,
0xb1, 0xbc, 0xab, 0xa6, 0x85, 0x88, 0x9f, 0x92, 0xd9, 0xd4, 0xc3, 0xce, 0xed, 0xe0, 0xf7, 0xfa,
0xb7, 0xba, 0xad, 0xa0, 0x83, 0x8e, 0x99, 0x94, 0xdf, 0xd2, 0xc5, 0xc8, 0xeb, 0xe6, 0xf1, 0xfc,
0x67, 0x6a, 0x7d, 0x70, 0x53, 0x5e, 0x49, 0x44, 0x0f, 0x02, 0x15, 0x18, 0x3b, 0x36, 0x21, 0x2c,
0x0c, 0x01, 0x16, 0x1b, 0x38, 0x35, 0x22, 0x2f, 0x64, 0x69, 0x7e, 0x73, 0x50, 0x5d, 0x4a, 0x47,
0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97
};
private static byte[] GFMul_0E =
{
0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a,
0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba,
0xdb, 0xd5, 0xc7, 0xc9, 0xe3, 0xed, 0xff, 0xf1, 0xab, 0xa5, 0xb7, 0xb9, 0x93, 0x9d, 0x8f, 0x81,
0x3b, 0x35, 0x27, 0x29, 0x03, 0x0d, 0x1f, 0x11, 0x4b, 0x45, 0x57, 0x59, 0x73, 0x7d, 0x6f, 0x61,
0xad, 0xa3, 0xb1, 0xbf, 0x95, 0x9b, 0x89, 0x87, 0xdd, 0xd3, 0xc1, 0xcf, 0xe5, 0xeb, 0xf9, 0xf7,
0x4d, 0x43, 0x51, 0x5f, 0x75, 0x7b, 0x69, 0x67, 0x3d, 0x33, 0x21, 0x2f, 0x05, 0x0b, 0x19, 0x17,
0x76, 0x78, 0x6a, 0x64, 0x4e, 0x40, 0x52, 0x5c, 0x06, 0x08, 0x1a, 0x14, 0x3e, 0x30, 0x22, 0x2c,
0x96, 0x98, 0x8a, 0x84, 0xae, 0xa0, 0xb2, 0xbc, 0xe6, 0xe8, 0xfa, 0xf4, 0xde, 0xd0, 0xc2, 0xcc,
0x41, 0x4f, 0x5d, 0x53, 0x79, 0x77, 0x65, 0x6b, 0x31, 0x3f, 0x2d, 0x23, 0x09, 0x07, 0x15, 0x1b,
0xa1, 0xaf, 0xbd, 0xb3, 0x99, 0x97, 0x85, 0x8b, 0xd1, 0xdf, 0xcd, 0xc3, 0xe9, 0xe7, 0xf5, 0xfb,
0x9a, 0x94, 0x86, 0x88, 0xa2, 0xac, 0xbe, 0xb0, 0xea, 0xe4, 0xf6, 0xf8, 0xd2, 0xdc, 0xce, 0xc0,
0x7a, 0x74, 0x66, 0x68, 0x42, 0x4c, 0x5e, 0x50, 0x0a, 0x04, 0x16, 0x18, 0x32, 0x3c, 0x2e, 0x20,
0xec, 0xe2, 0xf0, 0xfe, 0xd4, 0xda, 0xc8, 0xc6, 0x9c, 0x92, 0x80, 0x8e, 0xa4, 0xaa, 0xb8, 0xb6,
0x0c, 0x02, 0x10, 0x1e, 0x34, 0x3a, 0x28, 0x26, 0x7c, 0x72, 0x60, 0x6e, 0x44, 0x4a, 0x58, 0x56,
0x37, 0x39, 0x2b, 0x25, 0x0f, 0x01, 0x13, 0x1d, 0x47, 0x49, 0x5b, 0x55, 0x7f, 0x71, 0x63, 0x6d,
0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d
};
private static byte[] SRPerm = { 0, 13, 10, 7, 4, 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3 };
private static byte[] ISRPerm = { 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11 };
#endregion
public static Vector128<float> AESInvMixColumns(Vector128<float> op)
{
byte[] InState = new byte[16];
byte[] OutState = new byte[16];
FromVectorToByteArray(InState, ref op);
for (int Columns = 0; Columns <= 3; Columns++)
{
int Idx = Columns << 2;
byte Row0 = InState[Idx + 0]; // A, E, I, M: [Row0, Col0-Col3]
byte Row1 = InState[Idx + 1]; // B, F, J, N: [Row1, Col0-Col3]
byte Row2 = InState[Idx + 2]; // C, G, K, O: [Row2, Col0-Col3]
byte Row3 = InState[Idx + 3]; // D, H, L, P: [Row3, Col0-Col3]
OutState[Idx + 0] = (byte)((uint)GFMul_0E[Row0] ^ GFMul_0B[Row1] ^ GFMul_0D[Row2] ^ GFMul_09[Row3]);
OutState[Idx + 1] = (byte)((uint)GFMul_09[Row0] ^ GFMul_0E[Row1] ^ GFMul_0B[Row2] ^ GFMul_0D[Row3]);
OutState[Idx + 2] = (byte)((uint)GFMul_0D[Row0] ^ GFMul_09[Row1] ^ GFMul_0E[Row2] ^ GFMul_0B[Row3]);
OutState[Idx + 3] = (byte)((uint)GFMul_0B[Row0] ^ GFMul_0D[Row1] ^ GFMul_09[Row2] ^ GFMul_0E[Row3]);
}
FromByteArrayToVector(OutState, ref op);
return op;
}
public static Vector128<float> AESInvShiftRows(Vector128<float> op)
{
byte[] InState = new byte[16];
byte[] OutState = new byte[16];
FromVectorToByteArray(InState, ref op);
for (int Idx = 0; Idx <= 15; Idx++)
{
OutState[ISRPerm[Idx]] = InState[Idx];
}
FromByteArrayToVector(OutState, ref op);
return op;
}
public static Vector128<float> AESInvSubBytes(Vector128<float> op)
{
byte[] InState = new byte[16];
byte[] OutState = new byte[16];
FromVectorToByteArray(InState, ref op);
for (int Idx = 0; Idx <= 15; Idx++)
{
OutState[Idx] = InvSBox[InState[Idx]];
}
FromByteArrayToVector(OutState, ref op);
return op;
}
public static Vector128<float> AESMixColumns(Vector128<float> op)
{
byte[] InState = new byte[16];
byte[] OutState = new byte[16];
FromVectorToByteArray(InState, ref op);
for (int Columns = 0; Columns <= 3; Columns++)
{
int Idx = Columns << 2;
byte Row0 = InState[Idx + 0]; // A, E, I, M: [Row0, Col0-Col3]
byte Row1 = InState[Idx + 1]; // B, F, J, N: [Row1, Col0-Col3]
byte Row2 = InState[Idx + 2]; // C, G, K, O: [Row2, Col0-Col3]
byte Row3 = InState[Idx + 3]; // D, H, L, P: [Row3, Col0-Col3]
OutState[Idx + 0] = (byte)((uint)GFMul_02[Row0] ^ GFMul_03[Row1] ^ Row2 ^ Row3);
OutState[Idx + 1] = (byte)((uint)Row0 ^ GFMul_02[Row1] ^ GFMul_03[Row2] ^ Row3);
OutState[Idx + 2] = (byte)((uint)Row0 ^ Row1 ^ GFMul_02[Row2] ^ GFMul_03[Row3]);
OutState[Idx + 3] = (byte)((uint)GFMul_03[Row0] ^ Row1 ^ Row2 ^ GFMul_02[Row3]);
}
FromByteArrayToVector(OutState, ref op);
return op;
}
public static Vector128<float> AESShiftRows(Vector128<float> op)
{
byte[] InState = new byte[16];
byte[] OutState = new byte[16];
FromVectorToByteArray(InState, ref op);
for (int Idx = 0; Idx <= 15; Idx++)
{
OutState[SRPerm[Idx]] = InState[Idx];
}
FromByteArrayToVector(OutState, ref op);
return op;
}
public static Vector128<float> AESSubBytes(Vector128<float> op)
{
byte[] InState = new byte[16];
byte[] OutState = new byte[16];
FromVectorToByteArray(InState, ref op);
for (int Idx = 0; Idx <= 15; Idx++)
{
OutState[Idx] = SBox[InState[Idx]];
}
FromByteArrayToVector(OutState, ref op);
return op;
}
private static void FromVectorToByteArray(byte[] State, ref Vector128<float> op)
{
ulong ULongLow = AVectorHelper.VectorExtractIntZx((op), (byte)0, 3);
ulong ULongHigh = AVectorHelper.VectorExtractIntZx((op), (byte)1, 3);
for (int Idx = 0; Idx <= 7; Idx++)
{
State[Idx + 0] = (byte)(ULongLow & 0xFFUL);
State[Idx + 8] = (byte)(ULongHigh & 0xFFUL);
ULongLow >>= 8;
ULongHigh >>= 8;
}
}
private static void FromByteArrayToVector(byte[] State, ref Vector128<float> op)
{
if (!Sse2.IsSupported)
{
throw new PlatformNotSupportedException();
}
op = Sse.StaticCast<byte, float>(Sse2.SetVector128(
State[15], State[14], State[13], State[12],
State[11], State[10], State[9], State[8],
State[7], State[6], State[5], State[4],
State[3], State[2], State[1], State[0]));
}
}
}

View file

@ -1032,6 +1032,11 @@ namespace ChocolArm64.Instruction
EmitAddLongPairwise(Context, Signed: true, Accumulate: true);
}
public static void Saddl_V(AILEmitterCtx Context)
{
EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Add));
}
public static void Saddlp_V(AILEmitterCtx Context)
{
EmitAddLongPairwise(Context, Signed: true, Accumulate: false);
@ -1042,6 +1047,28 @@ namespace ChocolArm64.Instruction
EmitVectorWidenRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Add));
}
public static void Shadd_V(AILEmitterCtx Context)
{
EmitVectorBinaryOpSx(Context, () =>
{
Context.Emit(OpCodes.Add);
Context.Emit(OpCodes.Ldc_I4_1);
Context.Emit(OpCodes.Shr);
});
}
public static void Shsub_V(AILEmitterCtx Context)
{
EmitVectorBinaryOpSx(Context, () =>
{
Context.Emit(OpCodes.Sub);
Context.Emit(OpCodes.Ldc_I4_1);
Context.Emit(OpCodes.Shr);
});
}
public static void Smax_V(AILEmitterCtx Context)
{
Type[] Types = new Type[] { typeof(long), typeof(long) };
@ -1181,6 +1208,25 @@ namespace ChocolArm64.Instruction
EmitVectorSaturatingNarrowOpSxZx(Context, () => { });
}
public static void Srhadd_V(AILEmitterCtx Context)
{
EmitVectorBinaryOpSx(Context, () =>
{
Context.Emit(OpCodes.Add);
Context.Emit(OpCodes.Ldc_I4_1);
Context.Emit(OpCodes.Add);
Context.Emit(OpCodes.Ldc_I4_1);
Context.Emit(OpCodes.Shr);
});
}
public static void Ssubl_V(AILEmitterCtx Context)
{
EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Sub));
}
public static void Ssubw_V(AILEmitterCtx Context)
{
EmitVectorWidenRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Sub));
@ -1303,12 +1349,40 @@ namespace ChocolArm64.Instruction
{
Context.Emit(OpCodes.Add);
Context.EmitLdc_I4(1);
Context.Emit(OpCodes.Ldc_I4_1);
Context.Emit(OpCodes.Shr_Un);
});
}
public static void Uhsub_V(AILEmitterCtx Context)
{
EmitVectorBinaryOpZx(Context, () =>
{
Context.Emit(OpCodes.Sub);
Context.Emit(OpCodes.Ldc_I4_1);
Context.Emit(OpCodes.Shr_Un);
});
}
public static void Umax_V(AILEmitterCtx Context)
{
Type[] Types = new Type[] { typeof(ulong), typeof(ulong) };
MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types);
EmitVectorBinaryOpZx(Context, () => Context.EmitCall(MthdInfo));
}
public static void Umaxp_V(AILEmitterCtx Context)
{
Type[] Types = new Type[] { typeof(ulong), typeof(ulong) };
MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types);
EmitVectorPairwiseOpZx(Context, () => Context.EmitCall(MthdInfo));
}
public static void Umin_V(AILEmitterCtx Context)
{
Type[] Types = new Type[] { typeof(ulong), typeof(ulong) };
@ -1327,22 +1401,22 @@ namespace ChocolArm64.Instruction
EmitVectorPairwiseOpZx(Context, () => Context.EmitCall(MthdInfo));
}
public static void Umax_V(AILEmitterCtx Context)
public static void Umlal_V(AILEmitterCtx Context)
{
Type[] Types = new Type[] { typeof(ulong), typeof(ulong) };
MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types);
EmitVectorBinaryOpZx(Context, () => Context.EmitCall(MthdInfo));
EmitVectorWidenRnRmTernaryOpZx(Context, () =>
{
Context.Emit(OpCodes.Mul);
Context.Emit(OpCodes.Add);
});
}
public static void Umaxp_V(AILEmitterCtx Context)
public static void Umlsl_V(AILEmitterCtx Context)
{
Type[] Types = new Type[] { typeof(ulong), typeof(ulong) };
MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types);
EmitVectorPairwiseOpZx(Context, () => Context.EmitCall(MthdInfo));
EmitVectorWidenRnRmTernaryOpZx(Context, () =>
{
Context.Emit(OpCodes.Mul);
Context.Emit(OpCodes.Sub);
});
}
public static void Umull_V(AILEmitterCtx Context)
@ -1380,6 +1454,20 @@ namespace ChocolArm64.Instruction
EmitVectorSaturatingNarrowOpZxZx(Context, () => { });
}
public static void Urhadd_V(AILEmitterCtx Context)
{
EmitVectorBinaryOpZx(Context, () =>
{
Context.Emit(OpCodes.Add);
Context.Emit(OpCodes.Ldc_I4_1);
Context.Emit(OpCodes.Add);
Context.Emit(OpCodes.Ldc_I4_1);
Context.Emit(OpCodes.Shr_Un);
});
}
public static void Usqadd_S(AILEmitterCtx Context)
{
EmitScalarSaturatingBinaryOpZx(Context, SaturatingFlags.Accumulate);
@ -1390,6 +1478,11 @@ namespace ChocolArm64.Instruction
EmitVectorSaturatingBinaryOpZx(Context, SaturatingFlags.Accumulate);
}
public static void Usubl_V(AILEmitterCtx Context)
{
EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub));
}
public static void Usubw_V(AILEmitterCtx Context)
{
EmitVectorWidenRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub));

View file

@ -0,0 +1,54 @@
using ChocolArm64.Decoder;
using ChocolArm64.Translation;
namespace ChocolArm64.Instruction
{
static partial class AInstEmit
{
public static void Aesd_V(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
Context.EmitLdvec(Op.Rd);
Context.EmitLdvec(Op.Rn);
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Decrypt));
Context.EmitStvec(Op.Rd);
}
public static void Aese_V(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
Context.EmitLdvec(Op.Rd);
Context.EmitLdvec(Op.Rn);
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Encrypt));
Context.EmitStvec(Op.Rd);
}
public static void Aesimc_V(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
Context.EmitLdvec(Op.Rn);
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.InverseMixColumns));
Context.EmitStvec(Op.Rd);
}
public static void Aesmc_V(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
Context.EmitLdvec(Op.Rn);
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.MixColumns));
Context.EmitStvec(Op.Rd);
}
}
}

View file

@ -106,6 +106,26 @@ namespace ChocolArm64.Instruction
}
}
public static void Fcvtns_S(AILEmitterCtx Context)
{
EmitFcvtn(Context, Signed: true, Scalar: true);
}
public static void Fcvtns_V(AILEmitterCtx Context)
{
EmitFcvtn(Context, Signed: true, Scalar: false);
}
public static void Fcvtnu_S(AILEmitterCtx Context)
{
EmitFcvtn(Context, Signed: false, Scalar: true);
}
public static void Fcvtnu_V(AILEmitterCtx Context)
{
EmitFcvtn(Context, Signed: false, Scalar: false);
}
public static void Fcvtps_Gp(AILEmitterCtx Context)
{
EmitFcvt_s_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Ceiling)));
@ -250,6 +270,54 @@ namespace ChocolArm64.Instruction
}
}
private static void EmitFcvtn(AILEmitterCtx Context, bool Signed, bool Scalar)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
int SizeF = Op.Size & 1;
int SizeI = SizeF + 2;
int Bytes = Op.GetBitsCount() >> 3;
int Elems = !Scalar ? Bytes >> SizeI : 1;
if (Scalar && (SizeF == 0))
{
EmitVectorZeroLowerTmp(Context);
}
for (int Index = 0; Index < Elems; Index++)
{
EmitVectorExtractF(Context, Op.Rn, Index, SizeF);
EmitRoundMathCall(Context, MidpointRounding.ToEven);
if (SizeF == 0)
{
AVectorHelper.EmitCall(Context, Signed
? nameof(AVectorHelper.SatF32ToS32)
: nameof(AVectorHelper.SatF32ToU32));
Context.Emit(OpCodes.Conv_U8);
}
else /* if (SizeF == 1) */
{
AVectorHelper.EmitCall(Context, Signed
? nameof(AVectorHelper.SatF64ToS64)
: nameof(AVectorHelper.SatF64ToU64));
}
EmitVectorInsertTmp(Context, Index, SizeI);
}
Context.EmitLdvectmp();
Context.EmitStvec(Op.Rd);
if ((Op.RegisterSize == ARegisterSize.SIMD64) || Scalar)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
private static void EmitFcvt_s_Gp(AILEmitterCtx Context, Action Emit)
{
EmitFcvt___Gp(Context, Emit, true);
@ -569,4 +637,4 @@ namespace ChocolArm64.Instruction
}
}
}
}
}

View file

@ -410,6 +410,42 @@ namespace ChocolArm64.Instruction
}
#endregion
#region "Aes"
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> Decrypt(Vector128<float> value, Vector128<float> roundKey)
{
if (!Sse.IsSupported)
{
throw new PlatformNotSupportedException();
}
return ACryptoHelper.AESInvSubBytes(ACryptoHelper.AESInvShiftRows(Sse.Xor(value, roundKey)));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> Encrypt(Vector128<float> value, Vector128<float> roundKey)
{
if (!Sse.IsSupported)
{
throw new PlatformNotSupportedException();
}
return ACryptoHelper.AESSubBytes(ACryptoHelper.AESShiftRows(Sse.Xor(value, roundKey)));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> InverseMixColumns(Vector128<float> value)
{
return ACryptoHelper.AESInvMixColumns(value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> MixColumns(Vector128<float> value)
{
return ACryptoHelper.AESMixColumns(value);
}
#endregion
#region "Sha256"
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> HashLower(Vector128<float> hash_abcd, Vector128<float> hash_efgh, Vector128<float> wk)

View file

@ -0,0 +1,68 @@
namespace Ryujinx.Graphics.Gal
{
public enum GalFrameBufferFormat
{
Bitmap = 0x1c,
Unknown1D = 0x1d,
RGBA32Float = 0xc0,
RGBA32Sint = 0xc1,
RGBA32Uint = 0xc2,
RGBX32Float = 0xc3,
RGBX32Sint = 0xc4,
RGBX32Uint = 0xc5,
RGBA16Unorm = 0xc6,
RGBA16Snorm = 0xc7,
RGBA16Sint = 0xc8,
RGBA16Uint = 0xc9,
RGBA16Float = 0xca,
RG32Float = 0xcb,
RG32Sint = 0xcc,
RG32Uint = 0xcd,
RGBX16Float = 0xce,
BGRA8Unorm = 0xcf,
BGRA8Srgb = 0xd0,
RGB10A2Unorm = 0xd1,
RGB10A2Uint = 0xd2,
RGBA8Unorm = 0xd5,
RGBA8Srgb = 0xd6,
RGBA8Snorm = 0xd7,
RGBA8Sint = 0xd8,
RGBA8Uint = 0xd9,
RG16Unorm = 0xda,
RG16Snorm = 0xdb,
RG16Sint = 0xdc,
RG16Uint = 0xdd,
RG16Float = 0xde,
BGR10A2Unorm = 0xdf,
R11G11B10Float = 0xe0,
R32Sint = 0xe3,
R32Uint = 0xe4,
R32Float = 0xe5,
BGRX8Unorm = 0xe6,
BGRX8Srgb = 0xe7,
B5G6R5Unorm = 0xe8,
BGR5A1Unorm = 0xe9,
RG8Unorm = 0xea,
RG8Snorm = 0xeb,
RG8Sint = 0xec,
RG8Uint = 0xed,
R16Unorm = 0xee,
R16Snorm = 0xef,
R16Sint = 0xf0,
R16Uint = 0xf1,
R16Float = 0xf2,
R8Unorm = 0xf3,
R8Snorm = 0xf4,
R8Sint = 0xf5,
R8Uint = 0xf6,
A8Unorm = 0xf7,
BGR5X1Unorm = 0xf8,
RGBX8Unorm = 0xf9,
RGBX8Srgb = 0xfa,
BGR5X1UnormUnknownFB = 0xfb,
BGR5X1UnormUnknownFC = 0xfc,
BGRX8UnormUnknownFD = 0xfd,
BGRX8UnormUnknownFE = 0xfe,
Y32UintUnknownFF = 0xff
}
}

View file

@ -1,25 +1,25 @@
namespace Ryujinx.Graphics.Gal
{
public struct GalTexture
public struct GalImage
{
public int Width;
public int Height;
public GalTextureFormat Format;
public GalImageFormat Format;
public GalTextureSource XSource;
public GalTextureSource YSource;
public GalTextureSource ZSource;
public GalTextureSource WSource;
public GalTexture(
public GalImage(
int Width,
int Height,
GalTextureFormat Format,
GalTextureSource XSource,
GalTextureSource YSource,
GalTextureSource ZSource,
GalTextureSource WSource)
GalImageFormat Format,
GalTextureSource XSource = GalTextureSource.Red,
GalTextureSource YSource = GalTextureSource.Green,
GalTextureSource ZSource = GalTextureSource.Blue,
GalTextureSource WSource = GalTextureSource.Alpha)
{
this.Width = Width;
this.Height = Height;

View file

@ -0,0 +1,204 @@
namespace Ryujinx.Graphics.Gal
{
//These are Vulkan-based enumerations, do not take them as Tegra values
public enum GalImageFormat
{
Undefined = 0,
R4G4_UNORM_PACK8 = 1,
R4G4B4A4_UNORM_PACK16 = 2,
B4G4R4A4_UNORM_PACK16 = 3,
R5G6B5_UNORM_PACK16 = 4,
B5G6R5_UNORM_PACK16 = 5,
R5G5B5A1_UNORM_PACK16 = 6,
B5G5R5A1_UNORM_PACK16 = 7,
A1R5G5B5_UNORM_PACK16 = 8,
R8_UNORM = 9,
R8_SNORM = 10,
R8_USCALED = 11,
R8_SSCALED = 12,
R8_UINT = 13,
R8_SINT = 14,
R8_SRGB = 15,
R8G8_UNORM = 16,
R8G8_SNORM = 17,
R8G8_USCALED = 18,
R8G8_SSCALED = 19,
R8G8_UINT = 20,
R8G8_SINT = 21,
R8G8_SRGB = 22,
R8G8B8_UNORM = 23,
R8G8B8_SNORM = 24,
R8G8B8_USCALED = 25,
R8G8B8_SSCALED = 26,
R8G8B8_UINT = 27,
R8G8B8_SINT = 28,
R8G8B8_SRGB = 29,
B8G8R8_UNORM = 30,
B8G8R8_SNORM = 31,
B8G8R8_USCALED = 32,
B8G8R8_SSCALED = 33,
B8G8R8_UINT = 34,
B8G8R8_SINT = 35,
B8G8R8_SRGB = 36,
R8G8B8A8_UNORM = 37,
R8G8B8A8_SNORM = 38,
R8G8B8A8_USCALED = 39,
R8G8B8A8_SSCALED = 40,
R8G8B8A8_UINT = 41,
R8G8B8A8_SINT = 42,
R8G8B8A8_SRGB = 43,
B8G8R8A8_UNORM = 44,
B8G8R8A8_SNORM = 45,
B8G8R8A8_USCALED = 46,
B8G8R8A8_SSCALED = 47,
B8G8R8A8_UINT = 48,
B8G8R8A8_SINT = 49,
B8G8R8A8_SRGB = 50,
A8B8G8R8_UNORM_PACK32 = 51,
A8B8G8R8_SNORM_PACK32 = 52,
A8B8G8R8_USCALED_PACK32 = 53,
A8B8G8R8_SSCALED_PACK32 = 54,
A8B8G8R8_UINT_PACK32 = 55,
A8B8G8R8_SINT_PACK32 = 56,
A8B8G8R8_SRGB_PACK32 = 57,
A2R10G10B10_UNORM_PACK32 = 58,
A2R10G10B10_SNORM_PACK32 = 59,
A2R10G10B10_USCALED_PACK32 = 60,
A2R10G10B10_SSCALED_PACK32 = 61,
A2R10G10B10_UINT_PACK32 = 62,
A2R10G10B10_SINT_PACK32 = 63,
A2B10G10R10_UNORM_PACK32 = 64,
A2B10G10R10_SNORM_PACK32 = 65,
A2B10G10R10_USCALED_PACK32 = 66,
A2B10G10R10_SSCALED_PACK32 = 67,
A2B10G10R10_UINT_PACK32 = 68,
A2B10G10R10_SINT_PACK32 = 69,
R16_UNORM = 70,
R16_SNORM = 71,
R16_USCALED = 72,
R16_SSCALED = 73,
R16_UINT = 74,
R16_SINT = 75,
R16_SFLOAT = 76,
R16G16_UNORM = 77,
R16G16_SNORM = 78,
R16G16_USCALED = 79,
R16G16_SSCALED = 80,
R16G16_UINT = 81,
R16G16_SINT = 82,
R16G16_SFLOAT = 83,
R16G16B16_UNORM = 84,
R16G16B16_SNORM = 85,
R16G16B16_USCALED = 86,
R16G16B16_SSCALED = 87,
R16G16B16_UINT = 88,
R16G16B16_SINT = 89,
R16G16B16_SFLOAT = 90,
R16G16B16A16_UNORM = 91,
R16G16B16A16_SNORM = 92,
R16G16B16A16_USCALED = 93,
R16G16B16A16_SSCALED = 94,
R16G16B16A16_UINT = 95,
R16G16B16A16_SINT = 96,
R16G16B16A16_SFLOAT = 97,
R32_UINT = 98,
R32_SINT = 99,
R32_SFLOAT = 100,
R32G32_UINT = 101,
R32G32_SINT = 102,
R32G32_SFLOAT = 103,
R32G32B32_UINT = 104,
R32G32B32_SINT = 105,
R32G32B32_SFLOAT = 106,
R32G32B32A32_UINT = 107,
R32G32B32A32_SINT = 108,
R32G32B32A32_SFLOAT = 109,
R64_UINT = 110,
R64_SINT = 111,
R64_SFLOAT = 112,
R64G64_UINT = 113,
R64G64_SINT = 114,
R64G64_SFLOAT = 115,
R64G64B64_UINT = 116,
R64G64B64_SINT = 117,
R64G64B64_SFLOAT = 118,
R64G64B64A64_UINT = 119,
R64G64B64A64_SINT = 120,
R64G64B64A64_SFLOAT = 121,
B10G11R11_UFLOAT_PACK32 = 122,
E5B9G9R9_UFLOAT_PACK32 = 123,
D16_UNORM = 124,
X8_D24_UNORM_PACK32 = 125,
D32_SFLOAT = 126,
S8_UINT = 127,
D16_UNORM_S8_UINT = 128,
D24_UNORM_S8_UINT = 129,
D32_SFLOAT_S8_UINT = 130,
BC1_RGB_UNORM_BLOCK = 131,
BC1_RGB_SRGB_BLOCK = 132,
BC1_RGBA_UNORM_BLOCK = 133,
BC1_RGBA_SRGB_BLOCK = 134,
BC2_UNORM_BLOCK = 135,
BC2_SRGB_BLOCK = 136,
BC3_UNORM_BLOCK = 137,
BC3_SRGB_BLOCK = 138,
BC4_UNORM_BLOCK = 139,
BC4_SNORM_BLOCK = 140,
BC5_UNORM_BLOCK = 141,
BC5_SNORM_BLOCK = 142,
BC6H_UFLOAT_BLOCK = 143,
BC6H_SFLOAT_BLOCK = 144,
BC7_UNORM_BLOCK = 145,
BC7_SRGB_BLOCK = 146,
ETC2_R8G8B8_UNORM_BLOCK = 147,
ETC2_R8G8B8_SRGB_BLOCK = 148,
ETC2_R8G8B8A1_UNORM_BLOCK = 149,
ETC2_R8G8B8A1_SRGB_BLOCK = 150,
ETC2_R8G8B8A8_UNORM_BLOCK = 151,
ETC2_R8G8B8A8_SRGB_BLOCK = 152,
EAC_R11_UNORM_BLOCK = 153,
EAC_R11_SNORM_BLOCK = 154,
EAC_R11G11_UNORM_BLOCK = 155,
EAC_R11G11_SNORM_BLOCK = 156,
ASTC_BEGIN = ASTC_4x4_UNORM_BLOCK,
ASTC_4x4_UNORM_BLOCK = 157,
ASTC_4x4_SRGB_BLOCK = 158,
ASTC_5x4_UNORM_BLOCK = 159,
ASTC_5x4_SRGB_BLOCK = 160,
ASTC_5x5_UNORM_BLOCK = 161,
ASTC_5x5_SRGB_BLOCK = 162,
ASTC_6x5_UNORM_BLOCK = 163,
ASTC_6x5_SRGB_BLOCK = 164,
ASTC_6x6_UNORM_BLOCK = 165,
ASTC_6x6_SRGB_BLOCK = 166,
ASTC_8x5_UNORM_BLOCK = 167,
ASTC_8x5_SRGB_BLOCK = 168,
ASTC_8x6_UNORM_BLOCK = 169,
ASTC_8x6_SRGB_BLOCK = 170,
ASTC_8x8_UNORM_BLOCK = 171,
ASTC_8x8_SRGB_BLOCK = 172,
ASTC_10x5_UNORM_BLOCK = 173,
ASTC_10x5_SRGB_BLOCK = 174,
ASTC_10x6_UNORM_BLOCK = 175,
ASTC_10x6_SRGB_BLOCK = 176,
ASTC_10x8_UNORM_BLOCK = 177,
ASTC_10x8_SRGB_BLOCK = 178,
ASTC_10x10_UNORM_BLOCK = 179,
ASTC_10x10_SRGB_BLOCK = 180,
ASTC_12x10_UNORM_BLOCK = 181,
ASTC_12x10_SRGB_BLOCK = 182,
ASTC_12x12_UNORM_BLOCK = 183,
ASTC_12x12_SRGB_BLOCK = 184,
ASTC_END = ASTC_12x12_SRGB_BLOCK,
REVERSED_BEGIN,
R4G4B4A4_UNORM_PACK16_REVERSED = REVERSED_BEGIN,
REVERSED_END
}
}

View file

@ -7,6 +7,8 @@
public bool Enabled;
public int Stride;
public long VboKey;
public bool Instanced;
public int Divisor;
public GalVertexAttrib[] Attribs;
}
@ -22,6 +24,8 @@
public float FlipX;
public float FlipY;
public int Instance;
public GalFrontFace FrontFace;
public bool CullFaceEnabled;

View file

@ -4,11 +4,13 @@ namespace Ryujinx.Graphics.Gal
{
R32G32B32A32 = 0x1,
R16G16B16A16 = 0x3,
R32G32 = 0x4,
A8B8G8R8 = 0x8,
A2B10G10R10 = 0x9,
R32 = 0xf,
BC6H_SF16 = 0x10,
BC6H_UF16 = 0x11,
A4B4G4R4 = 0x12,
A1B5G5R5 = 0x14,
B5G6R5 = 0x15,
BC7U = 0x17,
@ -23,6 +25,7 @@ namespace Ryujinx.Graphics.Gal
BC5 = 0x28,
Z24S8 = 0x29,
ZF32 = 0x2f,
ZF32_X24S8 = 0x30,
Astc2D4x4 = 0x40,
Astc2D5x5 = 0x41,
Astc2D6x6 = 0x42,

View file

@ -0,0 +1,13 @@
namespace Ryujinx.Graphics.Gal
{
public enum GalTextureType
{
Snorm = 1,
Unorm = 2,
Sint = 3,
Uint = 4,
Snorm_Force_Fp16 = 5,
Unorm_Force_Fp16 = 6,
Float = 7
}
}

View file

@ -0,0 +1,16 @@
namespace Ryujinx.Graphics.Gal
{
public enum GalZetaFormat
{
Z32Float = 0x0a,
Z16Unorm = 0x13,
S8Z24Unorm = 0x14,
Z24X8Unorm = 0x15,
Z24S8Unorm = 0x16,
Z24C8Unorm = 0x18,
Z32S8X24Float = 0x19,
Z24X8S8C8X16Unorm = 0x1d,
Z32X8C8X16Float = 0x1e,
Z32S8C8X16Float = 0x1f
}
}

View file

@ -4,9 +4,13 @@ namespace Ryujinx.Graphics.Gal
{
public interface IGalFrameBuffer
{
void Create(long Key, int Width, int Height);
void BindColor(long Key, int Attachment);
void Bind(long Key);
void UnbindColor(int Attachment);
void BindZeta(long Key);
void UnbindZeta();
void BindTexture(long Key, int Index);
@ -14,6 +18,8 @@ namespace Ryujinx.Graphics.Gal
void Set(byte[] Data, int Width, int Height);
void SetMap(int[] Map);
void SetTransform(bool FlipX, bool FlipY, int Top, int Left, int Right, int Bottom);
void SetWindowSize(int Width, int Height);
@ -40,7 +46,6 @@ namespace Ryujinx.Graphics.Gal
long Key,
int Width,
int Height,
GalTextureFormat Format,
byte[] Buffer);
}
}

View file

@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Gal
void ClearBuffers(
GalClearBufferFlags Flags,
int Attachment,
float Red, float Green, float Blue, float Alpha,
float Depth,
int Stencil);

View file

@ -11,8 +11,6 @@ namespace Ryujinx.Graphics.Gal
IEnumerable<ShaderDeclInfo> GetConstBufferUsage(long Key);
IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key);
void EnsureTextureBinding(string UniformName, int Value);
void Bind(long Key);
void Unbind(GalShaderType Type);

View file

@ -5,9 +5,11 @@ namespace Ryujinx.Graphics.Gal
void LockCache();
void UnlockCache();
void Create(long Key, byte[] Data, GalTexture Texture);
void Create(long Key, byte[] Data, GalImage Image);
bool TryGetCachedTexture(long Key, long DataSize, out GalTexture Texture);
void CreateFb(long Key, long Size, GalImage Image);
bool TryGetCachedTexture(long Key, long DataSize, out GalImage Image);
void Bind(long Key, int Index);

View file

@ -0,0 +1,279 @@
using System;
namespace Ryujinx.Graphics.Gal
{
public static class ImageFormatConverter
{
public static GalImageFormat ConvertTexture(
GalTextureFormat Format,
GalTextureType RType,
GalTextureType GType,
GalTextureType BType,
GalTextureType AType)
{
if (RType != GType || RType != BType || RType != AType)
{
throw new NotImplementedException("Per component types are not implemented");
}
GalTextureType Type = RType;
switch (Type)
{
case GalTextureType.Snorm:
switch (Format)
{
case GalTextureFormat.R16G16B16A16: return GalImageFormat.R16G16B16A16_SNORM;
case GalTextureFormat.A8B8G8R8: return GalImageFormat.A8B8G8R8_SNORM_PACK32;
case GalTextureFormat.A2B10G10R10: return GalImageFormat.A2B10G10R10_SNORM_PACK32;
case GalTextureFormat.G8R8: return GalImageFormat.R8G8_SNORM;
case GalTextureFormat.R16: return GalImageFormat.R16_SNORM;
case GalTextureFormat.R8: return GalImageFormat.R8_SNORM;
case GalTextureFormat.BC4: return GalImageFormat.BC4_SNORM_BLOCK;
case GalTextureFormat.BC5: return GalImageFormat.BC5_SNORM_BLOCK;
}
break;
case GalTextureType.Unorm:
switch (Format)
{
case GalTextureFormat.R16G16B16A16: return GalImageFormat.R16G16B16A16_UNORM;
case GalTextureFormat.A8B8G8R8: return GalImageFormat.A8B8G8R8_UNORM_PACK32;
case GalTextureFormat.A2B10G10R10: return GalImageFormat.A2B10G10R10_UNORM_PACK32;
case GalTextureFormat.A4B4G4R4: return GalImageFormat.R4G4B4A4_UNORM_PACK16_REVERSED;
case GalTextureFormat.A1B5G5R5: return GalImageFormat.A1R5G5B5_UNORM_PACK16;
case GalTextureFormat.B5G6R5: return GalImageFormat.B5G6R5_UNORM_PACK16;
case GalTextureFormat.BC7U: return GalImageFormat.BC7_UNORM_BLOCK;
case GalTextureFormat.G8R8: return GalImageFormat.R8G8_UNORM;
case GalTextureFormat.R16: return GalImageFormat.R16_UNORM;
case GalTextureFormat.R8: return GalImageFormat.R8_UNORM;
case GalTextureFormat.BC1: return GalImageFormat.BC1_RGBA_UNORM_BLOCK;
case GalTextureFormat.BC2: return GalImageFormat.BC2_UNORM_BLOCK;
case GalTextureFormat.BC3: return GalImageFormat.BC3_UNORM_BLOCK;
case GalTextureFormat.BC4: return GalImageFormat.BC4_UNORM_BLOCK;
case GalTextureFormat.BC5: return GalImageFormat.BC5_UNORM_BLOCK;
case GalTextureFormat.Z24S8: return GalImageFormat.D24_UNORM_S8_UINT;
case GalTextureFormat.ZF32_X24S8: return GalImageFormat.D32_SFLOAT_S8_UINT;
case GalTextureFormat.Astc2D4x4: return GalImageFormat.ASTC_4x4_UNORM_BLOCK;
case GalTextureFormat.Astc2D5x5: return GalImageFormat.ASTC_5x5_UNORM_BLOCK;
case GalTextureFormat.Astc2D6x6: return GalImageFormat.ASTC_6x6_UNORM_BLOCK;
case GalTextureFormat.Astc2D8x8: return GalImageFormat.ASTC_8x8_UNORM_BLOCK;
case GalTextureFormat.Astc2D10x10: return GalImageFormat.ASTC_10x10_UNORM_BLOCK;
case GalTextureFormat.Astc2D12x12: return GalImageFormat.ASTC_12x12_UNORM_BLOCK;
case GalTextureFormat.Astc2D5x4: return GalImageFormat.ASTC_5x4_UNORM_BLOCK;
case GalTextureFormat.Astc2D6x5: return GalImageFormat.ASTC_6x5_UNORM_BLOCK;
case GalTextureFormat.Astc2D8x6: return GalImageFormat.ASTC_8x6_UNORM_BLOCK;
case GalTextureFormat.Astc2D10x8: return GalImageFormat.ASTC_10x8_UNORM_BLOCK;
case GalTextureFormat.Astc2D12x10: return GalImageFormat.ASTC_12x10_UNORM_BLOCK;
case GalTextureFormat.Astc2D8x5: return GalImageFormat.ASTC_8x5_UNORM_BLOCK;
case GalTextureFormat.Astc2D10x5: return GalImageFormat.ASTC_10x5_UNORM_BLOCK;
case GalTextureFormat.Astc2D10x6: return GalImageFormat.ASTC_10x6_UNORM_BLOCK;
}
break;
case GalTextureType.Sint:
switch (Format)
{
case GalTextureFormat.R32G32B32A32: return GalImageFormat.R32G32B32A32_SINT;
case GalTextureFormat.R16G16B16A16: return GalImageFormat.R16G16B16A16_SINT;
case GalTextureFormat.R32G32: return GalImageFormat.R32G32_SINT;
case GalTextureFormat.A8B8G8R8: return GalImageFormat.A8B8G8R8_SINT_PACK32;
case GalTextureFormat.A2B10G10R10: return GalImageFormat.A2B10G10R10_SINT_PACK32;
case GalTextureFormat.R32: return GalImageFormat.R32_SINT;
case GalTextureFormat.G8R8: return GalImageFormat.R8G8_SINT;
case GalTextureFormat.R16: return GalImageFormat.R16_SINT;
case GalTextureFormat.R8: return GalImageFormat.R8_SINT;
}
break;
case GalTextureType.Uint:
switch (Format)
{
case GalTextureFormat.R32G32B32A32: return GalImageFormat.R32G32B32A32_UINT;
case GalTextureFormat.R16G16B16A16: return GalImageFormat.R16G16B16A16_UINT;
case GalTextureFormat.R32G32: return GalImageFormat.R32G32_UINT;
case GalTextureFormat.A8B8G8R8: return GalImageFormat.A8B8G8R8_UINT_PACK32;
case GalTextureFormat.A2B10G10R10: return GalImageFormat.A2B10G10R10_UINT_PACK32;
case GalTextureFormat.R32: return GalImageFormat.R32_UINT;
case GalTextureFormat.G8R8: return GalImageFormat.R8G8_UINT;
case GalTextureFormat.R16: return GalImageFormat.R16_UINT;
case GalTextureFormat.R8: return GalImageFormat.R8_UINT;
}
break;
case GalTextureType.Snorm_Force_Fp16:
//TODO
break;
case GalTextureType.Unorm_Force_Fp16:
//TODO
break;
case GalTextureType.Float:
switch (Format)
{
case GalTextureFormat.R32G32B32A32: return GalImageFormat.R32G32B32A32_SFLOAT;
case GalTextureFormat.R16G16B16A16: return GalImageFormat.R16G16B16A16_SFLOAT;
case GalTextureFormat.R32G32: return GalImageFormat.R32G32_SFLOAT;
case GalTextureFormat.R32: return GalImageFormat.R32_SFLOAT;
case GalTextureFormat.BC6H_SF16: return GalImageFormat.BC6H_SFLOAT_BLOCK;
case GalTextureFormat.BC6H_UF16: return GalImageFormat.BC6H_UFLOAT_BLOCK;
case GalTextureFormat.R16: return GalImageFormat.R16_SFLOAT;
case GalTextureFormat.BF10GF11RF11: return GalImageFormat.B10G11R11_UFLOAT_PACK32;
case GalTextureFormat.ZF32: return GalImageFormat.D32_SFLOAT;
}
break;
}
throw new NotImplementedException("0x" + ((int)Format).ToString("x2") + " " + Type.ToString());
}
public static GalImageFormat ConvertFrameBuffer(GalFrameBufferFormat Format)
{
switch (Format)
{
case GalFrameBufferFormat.R32Float: return GalImageFormat.R32_SFLOAT;
case GalFrameBufferFormat.RGB10A2Unorm: return GalImageFormat.A2B10G10R10_UNORM_PACK32;
case GalFrameBufferFormat.RGBA8Srgb: return GalImageFormat.A8B8G8R8_SRGB_PACK32;
case GalFrameBufferFormat.RGBA16Float: return GalImageFormat.R16G16B16A16_SFLOAT;
case GalFrameBufferFormat.R16Float: return GalImageFormat.R16_SFLOAT;
case GalFrameBufferFormat.R8Unorm: return GalImageFormat.R8_UNORM;
case GalFrameBufferFormat.RGBA8Unorm: return GalImageFormat.A8B8G8R8_UNORM_PACK32;
case GalFrameBufferFormat.R11G11B10Float: return GalImageFormat.B10G11R11_UFLOAT_PACK32;
case GalFrameBufferFormat.RGBA32Float: return GalImageFormat.R32G32B32A32_SFLOAT;
case GalFrameBufferFormat.RG16Snorm: return GalImageFormat.R16G16_SNORM;
case GalFrameBufferFormat.RG16Float: return GalImageFormat.R16G16_SFLOAT;
case GalFrameBufferFormat.RG8Snorm: return GalImageFormat.R8_SNORM;
case GalFrameBufferFormat.RGBA8Snorm: return GalImageFormat.A8B8G8R8_SNORM_PACK32;
case GalFrameBufferFormat.RG8Unorm: return GalImageFormat.R8G8_UNORM;
case GalFrameBufferFormat.BGRA8Unorm: return GalImageFormat.A8B8G8R8_UNORM_PACK32;
case GalFrameBufferFormat.BGRA8Srgb: return GalImageFormat.A8B8G8R8_SRGB_PACK32;
case GalFrameBufferFormat.RG32Float: return GalImageFormat.R32G32_SFLOAT;
case GalFrameBufferFormat.RG32Sint: return GalImageFormat.R32G32_SINT;
case GalFrameBufferFormat.RG32Uint: return GalImageFormat.R32G32_UINT;
}
throw new NotImplementedException(Format.ToString());
}
public static GalImageFormat ConvertZeta(GalZetaFormat Format)
{
switch (Format)
{
case GalZetaFormat.Z32Float: return GalImageFormat.D32_SFLOAT;
case GalZetaFormat.S8Z24Unorm: return GalImageFormat.D24_UNORM_S8_UINT;
case GalZetaFormat.Z16Unorm: return GalImageFormat.D16_UNORM;
case GalZetaFormat.Z32S8X24Float: return GalImageFormat.D32_SFLOAT_S8_UINT;
}
throw new NotImplementedException(Format.ToString());
}
public static bool HasColor(GalImageFormat Format)
{
switch (Format)
{
case GalImageFormat.R32G32B32A32_SFLOAT:
case GalImageFormat.R32G32B32A32_SINT:
case GalImageFormat.R32G32B32A32_UINT:
case GalImageFormat.R16G16B16A16_SFLOAT:
case GalImageFormat.R16G16B16A16_SINT:
case GalImageFormat.R16G16B16A16_UINT:
case GalImageFormat.R32G32_SFLOAT:
case GalImageFormat.R32G32_SINT:
case GalImageFormat.R32G32_UINT:
case GalImageFormat.A8B8G8R8_SNORM_PACK32:
case GalImageFormat.A8B8G8R8_UNORM_PACK32:
case GalImageFormat.A8B8G8R8_SINT_PACK32:
case GalImageFormat.A8B8G8R8_UINT_PACK32:
case GalImageFormat.A2B10G10R10_SINT_PACK32:
case GalImageFormat.A2B10G10R10_SNORM_PACK32:
case GalImageFormat.A2B10G10R10_UINT_PACK32:
case GalImageFormat.A2B10G10R10_UNORM_PACK32:
case GalImageFormat.R32_SFLOAT:
case GalImageFormat.R32_SINT:
case GalImageFormat.R32_UINT:
case GalImageFormat.BC6H_SFLOAT_BLOCK:
case GalImageFormat.BC6H_UFLOAT_BLOCK:
case GalImageFormat.A1R5G5B5_UNORM_PACK16:
case GalImageFormat.B5G6R5_UNORM_PACK16:
case GalImageFormat.BC7_UNORM_BLOCK:
case GalImageFormat.R16G16_SFLOAT:
case GalImageFormat.R16G16_SINT:
case GalImageFormat.R16G16_SNORM:
case GalImageFormat.R16G16_UNORM:
case GalImageFormat.R8G8_SINT:
case GalImageFormat.R8G8_SNORM:
case GalImageFormat.R8G8_UINT:
case GalImageFormat.R8G8_UNORM:
case GalImageFormat.R16_SFLOAT:
case GalImageFormat.R16_SINT:
case GalImageFormat.R16_SNORM:
case GalImageFormat.R16_UINT:
case GalImageFormat.R16_UNORM:
case GalImageFormat.R8_SINT:
case GalImageFormat.R8_SNORM:
case GalImageFormat.R8_UINT:
case GalImageFormat.R8_UNORM:
case GalImageFormat.B10G11R11_UFLOAT_PACK32:
case GalImageFormat.BC1_RGBA_UNORM_BLOCK:
case GalImageFormat.BC2_UNORM_BLOCK:
case GalImageFormat.BC3_UNORM_BLOCK:
case GalImageFormat.BC4_UNORM_BLOCK:
case GalImageFormat.BC5_UNORM_BLOCK:
case GalImageFormat.ASTC_4x4_UNORM_BLOCK:
case GalImageFormat.ASTC_5x5_UNORM_BLOCK:
case GalImageFormat.ASTC_6x6_UNORM_BLOCK:
case GalImageFormat.ASTC_8x8_UNORM_BLOCK:
case GalImageFormat.ASTC_10x10_UNORM_BLOCK:
case GalImageFormat.ASTC_12x12_UNORM_BLOCK:
case GalImageFormat.ASTC_5x4_UNORM_BLOCK:
case GalImageFormat.ASTC_6x5_UNORM_BLOCK:
case GalImageFormat.ASTC_8x6_UNORM_BLOCK:
case GalImageFormat.ASTC_10x8_UNORM_BLOCK:
case GalImageFormat.ASTC_12x10_UNORM_BLOCK:
case GalImageFormat.ASTC_8x5_UNORM_BLOCK:
case GalImageFormat.ASTC_10x5_UNORM_BLOCK:
case GalImageFormat.ASTC_10x6_UNORM_BLOCK:
case GalImageFormat.R4G4B4A4_UNORM_PACK16_REVERSED:
return true;
case GalImageFormat.D24_UNORM_S8_UINT:
case GalImageFormat.D32_SFLOAT:
case GalImageFormat.D16_UNORM:
case GalImageFormat.D32_SFLOAT_S8_UINT:
return false;
}
throw new NotImplementedException(Format.ToString());
}
public static bool HasDepth(GalImageFormat Format)
{
switch (Format)
{
case GalImageFormat.D24_UNORM_S8_UINT:
case GalImageFormat.D32_SFLOAT:
case GalImageFormat.D16_UNORM:
case GalImageFormat.D32_SFLOAT_S8_UINT:
return true;
}
//Depth formats are fewer than colors, so it's harder to miss one
//Instead of checking for individual formats, return false
return false;
}
public static bool HasStencil(GalImageFormat Format)
{
switch (Format)
{
case GalImageFormat.D24_UNORM_S8_UINT:
case GalImageFormat.D32_SFLOAT_S8_UINT:
return true;
}
return false;
}
}
}

View file

@ -0,0 +1,124 @@
using OpenTK.Graphics.OpenGL;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class ImageHandler
{
//TODO: Use a variable value here
public const int MaxBpp = 16;
private static int CopyBuffer = 0;
private static int CopyBufferSize = 0;
public GalImage Image { get; private set; }
public int Width => Image.Width;
public int Height => Image.Height;
public GalImageFormat Format => Image.Format;
public PixelInternalFormat InternalFormat { get; private set; }
public PixelFormat PixelFormat { get; private set; }
public PixelType PixelType { get; private set; }
public int Handle { get; private set; }
private bool Initialized;
public ImageHandler()
{
Handle = GL.GenTexture();
}
public ImageHandler(int Handle, GalImage Image)
{
this.Handle = Handle;
this.Image = Image;
}
public void EnsureSetup(GalImage Image)
{
if (Width != Image.Width ||
Height != Image.Height ||
Format != Image.Format ||
!Initialized)
{
(PixelInternalFormat InternalFormat, PixelFormat PixelFormat, PixelType PixelType) =
OGLEnumConverter.GetImageFormat(Image.Format);
GL.BindTexture(TextureTarget.Texture2D, Handle);
if (Initialized)
{
if (CopyBuffer == 0)
{
CopyBuffer = GL.GenBuffer();
}
int MaxWidth = Math.Max(Image.Width, Width);
int MaxHeight = Math.Max(Image.Height, Height);
int CurrentSize = MaxWidth * MaxHeight * MaxBpp;
GL.BindBuffer(BufferTarget.PixelPackBuffer, CopyBuffer);
GL.BindBuffer(BufferTarget.PixelUnpackBuffer, CopyBuffer);
if (CopyBufferSize < CurrentSize)
{
CopyBufferSize = CurrentSize;
GL.BufferData(BufferTarget.PixelPackBuffer, CurrentSize, IntPtr.Zero, BufferUsageHint.StreamCopy);
}
GL.GetTexImage(TextureTarget.Texture2D, 0, this.PixelFormat, this.PixelType, IntPtr.Zero);
GL.DeleteTexture(Handle);
Handle = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, Handle);
}
const int MinFilter = (int)TextureMinFilter.Linear;
const int MagFilter = (int)TextureMagFilter.Linear;
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, MinFilter);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, MagFilter);
const int Level = 0;
const int Border = 0;
GL.TexImage2D(
TextureTarget.Texture2D,
Level,
InternalFormat,
Image.Width,
Image.Height,
Border,
PixelFormat,
PixelType,
IntPtr.Zero);
if (Initialized)
{
GL.BindBuffer(BufferTarget.PixelPackBuffer, 0);
GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0);
}
this.Image = Image;
this.InternalFormat = InternalFormat;
this.PixelFormat = PixelFormat;
this.PixelType = PixelType;
Initialized = true;
}
}
public bool HasColor { get => ImageFormatConverter.HasColor(Format); }
public bool HasDepth { get => ImageFormatConverter.HasDepth(Format); }
public bool HasStencil { get => ImageFormatConverter.HasStencil(Format); }
}
}

View file

@ -36,12 +36,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public void SetData(long Key, long Size, IntPtr HostAddress)
{
if (!Cache.TryGetValue(Key, out OGLStreamBuffer Buffer))
if (Cache.TryGetValue(Key, out OGLStreamBuffer Buffer))
{
throw new InvalidOperationException();
Buffer.SetData(Size, HostAddress);
}
Buffer.SetData(Size, HostAddress);
}
public bool TryGetUbo(long Key, out int UboHandle)

View file

@ -125,40 +125,75 @@ namespace Ryujinx.Graphics.Gal.OpenGL
throw new ArgumentException(nameof(Type));
}
public static (PixelFormat, PixelType) GetTextureFormat(GalTextureFormat Format)
public static (PixelInternalFormat, PixelFormat, PixelType) GetImageFormat(GalImageFormat Format)
{
switch (Format)
{
case GalTextureFormat.R32G32B32A32: return (PixelFormat.Rgba, PixelType.Float);
case GalTextureFormat.R16G16B16A16: return (PixelFormat.Rgba, PixelType.HalfFloat);
case GalTextureFormat.A8B8G8R8: return (PixelFormat.Rgba, PixelType.UnsignedByte);
case GalTextureFormat.A2B10G10R10: return (PixelFormat.Rgba, PixelType.UnsignedInt2101010Reversed);
case GalTextureFormat.R32: return (PixelFormat.Red, PixelType.Float);
case GalTextureFormat.A1B5G5R5: return (PixelFormat.Rgba, PixelType.UnsignedShort5551);
case GalTextureFormat.B5G6R5: return (PixelFormat.Rgb, PixelType.UnsignedShort565);
case GalTextureFormat.G8R8: return (PixelFormat.Rg, PixelType.UnsignedByte);
case GalTextureFormat.R16: return (PixelFormat.Red, PixelType.HalfFloat);
case GalTextureFormat.R8: return (PixelFormat.Red, PixelType.UnsignedByte);
case GalTextureFormat.ZF32: return (PixelFormat.DepthComponent, PixelType.Float);
case GalTextureFormat.BF10GF11RF11: return (PixelFormat.Rgb, PixelType.UnsignedInt10F11F11FRev);
case GalTextureFormat.Z24S8: return (PixelFormat.DepthStencil, PixelType.UnsignedInt248);
case GalImageFormat.R32G32B32A32_SFLOAT: return (PixelInternalFormat.Rgba32f, PixelFormat.Rgba, PixelType.Float);
case GalImageFormat.R32G32B32A32_SINT: return (PixelInternalFormat.Rgba32i, PixelFormat.RgbaInteger, PixelType.Int);
case GalImageFormat.R32G32B32A32_UINT: return (PixelInternalFormat.Rgba32ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt);
case GalImageFormat.R16G16B16A16_SFLOAT: return (PixelInternalFormat.Rgba16f, PixelFormat.Rgba, PixelType.HalfFloat);
case GalImageFormat.R16G16B16A16_SINT: return (PixelInternalFormat.Rgba16i, PixelFormat.RgbaInteger, PixelType.Short);
case GalImageFormat.R16G16B16A16_UINT: return (PixelInternalFormat.Rgba16ui, PixelFormat.RgbaInteger, PixelType.UnsignedShort);
case GalImageFormat.R32G32_SFLOAT: return (PixelInternalFormat.Rg32f, PixelFormat.Rg, PixelType.Float);
case GalImageFormat.R32G32_SINT: return (PixelInternalFormat.Rg32i, PixelFormat.RgInteger, PixelType.Int);
case GalImageFormat.R32G32_UINT: return (PixelInternalFormat.Rg32ui, PixelFormat.RgInteger, PixelType.UnsignedInt);
case GalImageFormat.A8B8G8R8_SNORM_PACK32: return (PixelInternalFormat.Rgba8Snorm, PixelFormat.Rgba, PixelType.Byte);
case GalImageFormat.A8B8G8R8_UNORM_PACK32: return (PixelInternalFormat.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte);
case GalImageFormat.A8B8G8R8_SINT_PACK32: return (PixelInternalFormat.Rgba8i, PixelFormat.RgbaInteger, PixelType.Byte);
case GalImageFormat.A8B8G8R8_UINT_PACK32: return (PixelInternalFormat.Rgba8ui, PixelFormat.RgbaInteger, PixelType.UnsignedByte);
case GalImageFormat.A8B8G8R8_SRGB_PACK32: return (PixelInternalFormat.Srgb8Alpha8, PixelFormat.Rgba, PixelType.UnsignedByte);
case GalImageFormat.A2B10G10R10_UINT_PACK32: return (PixelInternalFormat.Rgb10A2ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt2101010Reversed);
case GalImageFormat.A2B10G10R10_UNORM_PACK32: return (PixelInternalFormat.Rgb10A2, PixelFormat.Rgba, PixelType.UnsignedInt2101010Reversed);
case GalImageFormat.R32_SFLOAT: return (PixelInternalFormat.R32f, PixelFormat.Red, PixelType.Float);
case GalImageFormat.R32_SINT: return (PixelInternalFormat.R32i, PixelFormat.Red, PixelType.Int);
case GalImageFormat.R32_UINT: return (PixelInternalFormat.R32ui, PixelFormat.Red, PixelType.UnsignedInt);
case GalImageFormat.A1R5G5B5_UNORM_PACK16: return (PixelInternalFormat.Rgb5A1, PixelFormat.Rgba, PixelType.UnsignedShort5551);
case GalImageFormat.B5G6R5_UNORM_PACK16: return (PixelInternalFormat.Rgba, PixelFormat.Rgb, PixelType.UnsignedShort565);
case GalImageFormat.R16G16_SFLOAT: return (PixelInternalFormat.Rg16f, PixelFormat.Rg, PixelType.HalfFloat);
case GalImageFormat.R16G16_SINT: return (PixelInternalFormat.Rg16i, PixelFormat.RgInteger, PixelType.Short);
case GalImageFormat.R16G16_SNORM: return (PixelInternalFormat.Rg16Snorm, PixelFormat.Rg, PixelType.Byte);
case GalImageFormat.R16G16_UNORM: return (PixelInternalFormat.Rg16, PixelFormat.Rg, PixelType.UnsignedShort);
case GalImageFormat.R8G8_SINT: return (PixelInternalFormat.Rg8i, PixelFormat.RgInteger, PixelType.Byte);
case GalImageFormat.R8G8_SNORM: return (PixelInternalFormat.Rg8Snorm, PixelFormat.Rg, PixelType.Byte);
case GalImageFormat.R8G8_UINT: return (PixelInternalFormat.Rg8ui, PixelFormat.RgInteger, PixelType.UnsignedByte);
case GalImageFormat.R8G8_UNORM: return (PixelInternalFormat.Rg8, PixelFormat.Rg, PixelType.UnsignedByte);
case GalImageFormat.R16_SFLOAT: return (PixelInternalFormat.R16f, PixelFormat.Red, PixelType.HalfFloat);
case GalImageFormat.R16_SINT: return (PixelInternalFormat.R16i, PixelFormat.RedInteger, PixelType.Short);
case GalImageFormat.R16_SNORM: return (PixelInternalFormat.R16Snorm, PixelFormat.Red, PixelType.Byte);
case GalImageFormat.R16_UINT: return (PixelInternalFormat.R16ui, PixelFormat.RedInteger, PixelType.UnsignedShort);
case GalImageFormat.R16_UNORM: return (PixelInternalFormat.R16, PixelFormat.Red, PixelType.UnsignedShort);
case GalImageFormat.R8_SINT: return (PixelInternalFormat.R8i, PixelFormat.RedInteger, PixelType.Byte);
case GalImageFormat.R8_SNORM: return (PixelInternalFormat.R8Snorm, PixelFormat.Red, PixelType.Byte);
case GalImageFormat.R8_UINT: return (PixelInternalFormat.R8ui, PixelFormat.RedInteger, PixelType.UnsignedByte);
case GalImageFormat.R8_UNORM: return (PixelInternalFormat.R8, PixelFormat.Red, PixelType.UnsignedByte);
case GalImageFormat.B10G11R11_UFLOAT_PACK32: return (PixelInternalFormat.R11fG11fB10f, PixelFormat.Rgb, PixelType.UnsignedInt10F11F11FRev);
case GalImageFormat.R4G4B4A4_UNORM_PACK16_REVERSED: return (PixelInternalFormat.Rgba4, PixelFormat.Rgba, PixelType.UnsignedShort4444Reversed);
case GalImageFormat.D24_UNORM_S8_UINT: return (PixelInternalFormat.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248);
case GalImageFormat.D32_SFLOAT: return (PixelInternalFormat.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float);
case GalImageFormat.D16_UNORM: return (PixelInternalFormat.DepthComponent16, PixelFormat.DepthComponent, PixelType.UnsignedShort);
case GalImageFormat.D32_SFLOAT_S8_UINT: return (PixelInternalFormat.Depth32fStencil8, PixelFormat.DepthStencil, PixelType.Float32UnsignedInt248Rev);
}
throw new NotImplementedException(Format.ToString());
}
public static InternalFormat GetCompressedTextureFormat(GalTextureFormat Format)
public static InternalFormat GetCompressedImageFormat(GalImageFormat Format)
{
switch (Format)
{
case GalTextureFormat.BC6H_UF16: return InternalFormat.CompressedRgbBptcUnsignedFloat;
case GalTextureFormat.BC6H_SF16: return InternalFormat.CompressedRgbBptcSignedFloat;
case GalTextureFormat.BC7U: return InternalFormat.CompressedRgbaBptcUnorm;
case GalTextureFormat.BC1: return InternalFormat.CompressedRgbaS3tcDxt1Ext;
case GalTextureFormat.BC2: return InternalFormat.CompressedRgbaS3tcDxt3Ext;
case GalTextureFormat.BC3: return InternalFormat.CompressedRgbaS3tcDxt5Ext;
case GalTextureFormat.BC4: return InternalFormat.CompressedRedRgtc1;
case GalTextureFormat.BC5: return InternalFormat.CompressedRgRgtc2;
case GalImageFormat.BC6H_UFLOAT_BLOCK: return InternalFormat.CompressedRgbBptcUnsignedFloat;
case GalImageFormat.BC6H_SFLOAT_BLOCK: return InternalFormat.CompressedRgbBptcSignedFloat;
case GalImageFormat.BC7_UNORM_BLOCK: return InternalFormat.CompressedRgbaBptcUnorm;
case GalImageFormat.BC1_RGBA_UNORM_BLOCK: return InternalFormat.CompressedRgbaS3tcDxt1Ext;
case GalImageFormat.BC2_UNORM_BLOCK: return InternalFormat.CompressedRgbaS3tcDxt3Ext;
case GalImageFormat.BC3_UNORM_BLOCK: return InternalFormat.CompressedRgbaS3tcDxt5Ext;
case GalImageFormat.BC4_SNORM_BLOCK: return InternalFormat.CompressedSignedRedRgtc1;
case GalImageFormat.BC4_UNORM_BLOCK: return InternalFormat.CompressedRedRgtc1;
case GalImageFormat.BC5_SNORM_BLOCK: return InternalFormat.CompressedSignedRgRgtc2;
case GalImageFormat.BC5_UNORM_BLOCK: return InternalFormat.CompressedRgRgtc2;
}
throw new NotImplementedException(Format.ToString());
@ -184,16 +219,31 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
switch (Wrap)
{
case GalTextureWrap.Repeat: return TextureWrapMode.Repeat;
case GalTextureWrap.MirroredRepeat: return TextureWrapMode.MirroredRepeat;
case GalTextureWrap.ClampToEdge: return TextureWrapMode.ClampToEdge;
case GalTextureWrap.ClampToBorder: return TextureWrapMode.ClampToBorder;
case GalTextureWrap.Clamp: return TextureWrapMode.Clamp;
case GalTextureWrap.Repeat: return TextureWrapMode.Repeat;
case GalTextureWrap.MirroredRepeat: return TextureWrapMode.MirroredRepeat;
case GalTextureWrap.ClampToEdge: return TextureWrapMode.ClampToEdge;
case GalTextureWrap.ClampToBorder: return TextureWrapMode.ClampToBorder;
case GalTextureWrap.Clamp: return TextureWrapMode.Clamp;
}
//TODO: Those needs extensions (and are currently wrong).
case GalTextureWrap.MirrorClampToEdge: return TextureWrapMode.ClampToEdge;
case GalTextureWrap.MirrorClampToBorder: return TextureWrapMode.ClampToBorder;
case GalTextureWrap.MirrorClamp: return TextureWrapMode.Clamp;
if (OGLExtension.HasTextureMirrorClamp())
{
switch (Wrap)
{
case GalTextureWrap.MirrorClampToEdge: return (TextureWrapMode)ExtTextureMirrorClamp.MirrorClampToEdgeExt;
case GalTextureWrap.MirrorClampToBorder: return (TextureWrapMode)ExtTextureMirrorClamp.MirrorClampToBorderExt;
case GalTextureWrap.MirrorClamp: return (TextureWrapMode)ExtTextureMirrorClamp.MirrorClampExt;
}
}
else
{
//Fallback to non-mirrored clamps
switch (Wrap)
{
case GalTextureWrap.MirrorClampToEdge: return TextureWrapMode.ClampToEdge;
case GalTextureWrap.MirrorClampToBorder: return TextureWrapMode.ClampToBorder;
case GalTextureWrap.MirrorClamp: return TextureWrapMode.Clamp;
}
}
throw new ArgumentException(nameof(Wrap));

View file

@ -8,6 +8,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
private static bool EnhancedLayouts;
private static bool TextureMirrorClamp;
public static bool HasEnhancedLayouts()
{
EnsureInitialized();
@ -15,6 +17,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return EnhancedLayouts;
}
public static bool HasTextureMirrorClamp()
{
EnsureInitialized();
return TextureMirrorClamp;
}
private static void EnsureInitialized()
{
if (Initialized)
@ -23,6 +32,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
}
EnhancedLayouts = HasExtension("GL_ARB_enhanced_layouts");
TextureMirrorClamp = HasExtension("GL_EXT_texture_mirror_clamp");
}
private static bool HasExtension(string Name)

View file

@ -1,10 +1,9 @@
using OpenTK.Graphics.OpenGL;
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal.OpenGL
{
public class OGLFrameBuffer : IGalFrameBuffer
class OGLFrameBuffer : IGalFrameBuffer
{
private struct Rect
{
@ -15,50 +14,26 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public Rect(int X, int Y, int Width, int Height)
{
this.X = X;
this.Y = Y;
this.Width = Width;
this.X = X;
this.Y = Y;
this.Width = Width;
this.Height = Height;
}
}
private class FrameBuffer
{
public int Width { get; set; }
public int Height { get; set; }
public int Handle { get; private set; }
public int RbHandle { get; private set; }
public int TexHandle { get; private set; }
public FrameBuffer(int Width, int Height, bool HasRenderBuffer)
{
this.Width = Width;
this.Height = Height;
Handle = GL.GenFramebuffer();
TexHandle = GL.GenTexture();
if (HasRenderBuffer)
{
RbHandle = GL.GenRenderbuffer();
}
}
}
private const int NativeWidth = 1280;
private const int NativeHeight = 720;
private Dictionary<long, FrameBuffer> Fbs;
private const GalImageFormat RawFormat = GalImageFormat.A8B8G8R8_UNORM_PACK32;
private OGLTexture Texture;
private ImageHandler RawTex;
private ImageHandler ReadTex;
private Rect Viewport;
private Rect Window;
private FrameBuffer CurrFb;
private FrameBuffer CurrReadFb;
private FrameBuffer RawFb;
private bool FlipX;
private bool FlipY;
@ -67,111 +42,163 @@ namespace Ryujinx.Graphics.Gal.OpenGL
private int CropRight;
private int CropBottom;
public OGLFrameBuffer()
//This framebuffer is used to attach guest rendertargets,
//think of it as a dummy OpenGL VAO
private int DummyFrameBuffer;
//These framebuffers are used to blit images
private int SrcFb;
private int DstFb;
//Holds current attachments, used to avoid unnecesary calls to OpenGL
private int[] ColorAttachments;
private int DepthAttachment;
private int StencilAttachment;
public OGLFrameBuffer(OGLTexture Texture)
{
Fbs = new Dictionary<long, FrameBuffer>();
ColorAttachments = new int[8];
this.Texture = Texture;
}
public void Create(long Key, int Width, int Height)
public void BindColor(long Key, int Attachment)
{
if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
if (Texture.TryGetImage(Key, out ImageHandler Tex))
{
if (Fb.Width != Width ||
Fb.Height != Height)
{
SetupTexture(Fb.TexHandle, Width, Height);
EnsureFrameBuffer();
Fb.Width = Width;
Fb.Height = Height;
}
return;
Attach(ref ColorAttachments[Attachment], Tex.Handle, FramebufferAttachment.ColorAttachment0 + Attachment);
}
else
{
UnbindColor(Attachment);
}
Fb = new FrameBuffer(Width, Height, true);
SetupTexture(Fb.TexHandle, Width, Height);
GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle);
GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, Fb.RbHandle);
GL.RenderbufferStorage(
RenderbufferTarget.Renderbuffer,
RenderbufferStorage.Depth24Stencil8,
Width,
Height);
GL.FramebufferRenderbuffer(
FramebufferTarget.Framebuffer,
FramebufferAttachment.DepthStencilAttachment,
RenderbufferTarget.Renderbuffer,
Fb.RbHandle);
GL.FramebufferTexture(
FramebufferTarget.Framebuffer,
FramebufferAttachment.ColorAttachment0,
Fb.TexHandle,
0);
GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
Fbs.Add(Key, Fb);
}
public void Bind(long Key)
public void UnbindColor(int Attachment)
{
if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
{
GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle);
EnsureFrameBuffer();
CurrFb = Fb;
Attach(ref ColorAttachments[Attachment], 0, FramebufferAttachment.ColorAttachment0 + Attachment);
}
public void BindZeta(long Key)
{
if (Texture.TryGetImage(Key, out ImageHandler Tex))
{
EnsureFrameBuffer();
if (Tex.HasDepth && Tex.HasStencil)
{
if (DepthAttachment != Tex.Handle ||
StencilAttachment != Tex.Handle)
{
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.DepthStencilAttachment,
Tex.Handle,
0);
DepthAttachment = Tex.Handle;
StencilAttachment = Tex.Handle;
}
}
else if (Tex.HasDepth)
{
Attach(ref DepthAttachment, Tex.Handle, FramebufferAttachment.DepthAttachment);
Attach(ref StencilAttachment, 0, FramebufferAttachment.StencilAttachment);
}
else if (Tex.HasStencil)
{
Attach(ref DepthAttachment, 0, FramebufferAttachment.DepthAttachment);
Attach(ref StencilAttachment, Tex.Handle, FramebufferAttachment.StencilAttachment);
}
else
{
throw new InvalidOperationException();
}
}
else
{
UnbindZeta();
}
}
public void UnbindZeta()
{
EnsureFrameBuffer();
if (DepthAttachment != 0 ||
StencilAttachment != 0)
{
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.DepthStencilAttachment,
0,
0);
DepthAttachment = 0;
StencilAttachment = 0;
}
}
public void BindTexture(long Key, int Index)
{
if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
if (Texture.TryGetImage(Key, out ImageHandler Tex))
{
GL.ActiveTexture(TextureUnit.Texture0 + Index);
GL.BindTexture(TextureTarget.Texture2D, Fb.TexHandle);
GL.BindTexture(TextureTarget.Texture2D, Tex.Handle);
}
}
public void Set(long Key)
{
if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
if (Texture.TryGetImage(Key, out ImageHandler Tex))
{
CurrReadFb = Fb;
ReadTex = Tex;
}
}
public void Set(byte[] Data, int Width, int Height)
{
if (RawFb == null)
if (RawTex == null)
{
CreateRawFb(Width, Height);
RawTex = new ImageHandler();
}
if (RawFb.Width != Width ||
RawFb.Height != Height)
RawTex.EnsureSetup(new GalImage(Width, Height, RawFormat));
GL.BindTexture(TextureTarget.Texture2D, RawTex.Handle);
GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, Width, Height, RawTex.PixelFormat, RawTex.PixelType, Data);
ReadTex = RawTex;
}
public void SetMap(int[] Map)
{
if (Map != null && Map.Length > 0)
{
SetupTexture(RawFb.TexHandle, Width, Height);
DrawBuffersEnum[] Mode = new DrawBuffersEnum[Map.Length];
RawFb.Width = Width;
RawFb.Height = Height;
for (int i = 0; i < Map.Length; i++)
{
Mode[i] = DrawBuffersEnum.ColorAttachment0 + Map[i];
}
GL.DrawBuffers(Mode.Length, Mode);
}
else
{
GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
}
GL.ActiveTexture(TextureUnit.Texture0);
GL.BindTexture(TextureTarget.Texture2D, RawFb.TexHandle);
(PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8);
GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, Width, Height, Format, Type, Data);
CurrReadFb = RawFb;
}
public void SetTransform(bool FlipX, bool FlipY, int Top, int Left, int Right, int Bottom)
@ -208,60 +235,71 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public void Render()
{
if (CurrReadFb != null)
if (ReadTex == null)
{
int SrcX0, SrcX1, SrcY0, SrcY1;
if (CropLeft == 0 && CropRight == 0)
{
SrcX0 = 0;
SrcX1 = CurrReadFb.Width;
}
else
{
SrcX0 = CropLeft;
SrcX1 = CropRight;
}
if (CropTop == 0 && CropBottom == 0)
{
SrcY0 = 0;
SrcY1 = CurrReadFb.Height;
}
else
{
SrcY0 = CropTop;
SrcY1 = CropBottom;
}
float RatioX = MathF.Min(1f, (Window.Height * (float)NativeWidth) / ((float)NativeHeight * Window.Width));
float RatioY = MathF.Min(1f, (Window.Width * (float)NativeHeight) / ((float)NativeWidth * Window.Height));
int DstWidth = (int)(Window.Width * RatioX);
int DstHeight = (int)(Window.Height * RatioY);
int DstPaddingX = (Window.Width - DstWidth) / 2;
int DstPaddingY = (Window.Height - DstHeight) / 2;
int DstX0 = FlipX ? Window.Width - DstPaddingX : DstPaddingX;
int DstX1 = FlipX ? DstPaddingX : Window.Width - DstPaddingX;
int DstY0 = FlipY ? DstPaddingY : Window.Height - DstPaddingY;
int DstY1 = FlipY ? Window.Height - DstPaddingY : DstPaddingY;
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
GL.Viewport(0, 0, Window.Width, Window.Height);
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, CurrReadFb.Handle);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.BlitFramebuffer(
SrcX0, SrcY0, SrcX1, SrcY1,
DstX0, DstY0, DstX1, DstY1,
ClearBufferMask.ColorBufferBit, BlitFramebufferFilter.Linear);
return;
}
int SrcX0, SrcX1, SrcY0, SrcY1;
if (CropLeft == 0 && CropRight == 0)
{
SrcX0 = 0;
SrcX1 = ReadTex.Width;
}
else
{
SrcX0 = CropLeft;
SrcX1 = CropRight;
}
if (CropTop == 0 && CropBottom == 0)
{
SrcY0 = 0;
SrcY1 = ReadTex.Height;
}
else
{
SrcY0 = CropTop;
SrcY1 = CropBottom;
}
float RatioX = MathF.Min(1f, (Window.Height * (float)NativeWidth) / ((float)NativeHeight * Window.Width));
float RatioY = MathF.Min(1f, (Window.Width * (float)NativeHeight) / ((float)NativeWidth * Window.Height));
int DstWidth = (int)(Window.Width * RatioX);
int DstHeight = (int)(Window.Height * RatioY);
int DstPaddingX = (Window.Width - DstWidth) / 2;
int DstPaddingY = (Window.Height - DstHeight) / 2;
int DstX0 = FlipX ? Window.Width - DstPaddingX : DstPaddingX;
int DstX1 = FlipX ? DstPaddingX : Window.Width - DstPaddingX;
int DstY0 = FlipY ? DstPaddingY : Window.Height - DstPaddingY;
int DstY1 = FlipY ? Window.Height - DstPaddingY : DstPaddingY;
if (SrcFb == 0) SrcFb = GL.GenFramebuffer();
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0);
GL.Viewport(0, 0, Window.Width, Window.Height);
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, SrcFb);
GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, FramebufferAttachment.ColorAttachment0, ReadTex.Handle, 0);
GL.ReadBuffer(ReadBufferMode.ColorAttachment0);
GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
GL.Clear(ClearBufferMask.ColorBufferBit);
GL.BlitFramebuffer(
SrcX0, SrcY0, SrcX1, SrcY1,
DstX0, DstY0, DstX1, DstY1,
ClearBufferMask.ColorBufferBit, BlitFramebufferFilter.Linear);
EnsureFrameBuffer();
}
public void Copy(
@ -276,39 +314,80 @@ namespace Ryujinx.Graphics.Gal.OpenGL
int DstX1,
int DstY1)
{
if (Fbs.TryGetValue(SrcKey, out FrameBuffer SrcFb) &&
Fbs.TryGetValue(DstKey, out FrameBuffer DstFb))
if (Texture.TryGetImage(SrcKey, out ImageHandler SrcTex) &&
Texture.TryGetImage(DstKey, out ImageHandler DstTex))
{
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, SrcFb.Handle);
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DstFb.Handle);
if (SrcTex.HasColor != DstTex.HasColor ||
SrcTex.HasDepth != DstTex.HasDepth ||
SrcTex.HasStencil != DstTex.HasStencil)
{
throw new NotImplementedException();
}
GL.Clear(ClearBufferMask.ColorBufferBit);
GL.BlitFramebuffer(
SrcX0, SrcY0, SrcX1, SrcY1,
DstX0, DstY0, DstX1, DstY1,
ClearBufferMask.ColorBufferBit,
BlitFramebufferFilter.Linear);
if (SrcTex.HasColor)
{
CopyTextures(
SrcX0, SrcY0, SrcX1, SrcY1,
DstX0, DstY0, DstX1, DstY1,
SrcTex.Handle,
DstTex.Handle,
FramebufferAttachment.ColorAttachment0,
ClearBufferMask.ColorBufferBit,
true);
}
else if (SrcTex.HasDepth && SrcTex.HasStencil)
{
CopyTextures(
SrcX0, SrcY0, SrcX1, SrcY1,
DstX0, DstY0, DstX1, DstY1,
SrcTex.Handle,
DstTex.Handle,
FramebufferAttachment.DepthStencilAttachment,
ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit,
false);
}
else if (SrcTex.HasDepth)
{
CopyTextures(
SrcX0, SrcY0, SrcX1, SrcY1,
DstX0, DstY0, DstX1, DstY1,
SrcTex.Handle,
DstTex.Handle,
FramebufferAttachment.DepthAttachment,
ClearBufferMask.DepthBufferBit,
false);
}
else if (SrcTex.HasStencil)
{
CopyTextures(
SrcX0, SrcY0, SrcX1, SrcY1,
DstX0, DstY0, DstX1, DstY1,
SrcTex.Handle,
DstTex.Handle,
FramebufferAttachment.StencilAttachment,
ClearBufferMask.StencilBufferBit,
false);
}
else
{
throw new InvalidOperationException();
}
}
}
}
public void GetBufferData(long Key, Action<byte[]> Callback)
{
if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
if (Texture.TryGetImage(Key, out ImageHandler Tex))
{
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, Fb.Handle);
byte[] Data = new byte[Tex.Width * Tex.Height * ImageHandler.MaxBpp];
byte[] Data = new byte[Fb.Width * Fb.Height * 4];
GL.BindTexture(TextureTarget.Texture2D, Tex.Handle);
(PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8);
GL.ReadPixels(
GL.GetTexImage(
TextureTarget.Texture2D,
0,
0,
Fb.Width,
Fb.Height,
Format,
Type,
Tex.PixelFormat,
Tex.PixelType,
Data);
Callback(Data);
@ -319,83 +398,99 @@ namespace Ryujinx.Graphics.Gal.OpenGL
long Key,
int Width,
int Height,
GalTextureFormat Format,
byte[] Buffer)
{
if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
if (Texture.TryGetImage(Key, out ImageHandler Tex))
{
GL.BindTexture(TextureTarget.Texture2D, Fb.TexHandle);
GL.BindTexture(TextureTarget.Texture2D, Tex.Handle);
const int Level = 0;
const int Border = 0;
const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba;
(PixelFormat GlFormat, PixelType Type) = OGLEnumConverter.GetTextureFormat(Format);
GL.TexImage2D(
TextureTarget.Texture2D,
Level,
InternalFmt,
Tex.InternalFormat,
Width,
Height,
Border,
GlFormat,
Type,
Tex.PixelFormat,
Tex.PixelType,
Buffer);
}
}
private void CreateRawFb(int Width, int Height)
private void EnsureFrameBuffer()
{
if (RawFb == null)
if (DummyFrameBuffer == 0)
{
RawFb = new FrameBuffer(Width, Height, false);
DummyFrameBuffer = GL.GenFramebuffer();
}
SetupTexture(RawFb.TexHandle, Width, Height);
RawFb.Width = Width;
RawFb.Height = Height;
GL.BindFramebuffer(FramebufferTarget.Framebuffer, RawFb.Handle);
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DummyFrameBuffer);
}
private void Attach(ref int OldHandle, int NewHandle, FramebufferAttachment FbAttachment)
{
if (OldHandle != NewHandle)
{
GL.FramebufferTexture(
FramebufferTarget.Framebuffer,
FramebufferAttachment.ColorAttachment0,
RawFb.TexHandle,
FramebufferTarget.DrawFramebuffer,
FbAttachment,
NewHandle,
0);
GL.Viewport(0, 0, Width, Height);
OldHandle = NewHandle;
}
}
private void SetupTexture(int Handle, int Width, int Height)
private void CopyTextures(
int SrcX0,
int SrcY0,
int SrcX1,
int SrcY1,
int DstX0,
int DstY0,
int DstX1,
int DstY1,
int SrcTexture,
int DstTexture,
FramebufferAttachment Attachment,
ClearBufferMask Mask,
bool Color)
{
GL.BindTexture(TextureTarget.Texture2D, Handle);
if (SrcFb == 0) SrcFb = GL.GenFramebuffer();
if (DstFb == 0) DstFb = GL.GenFramebuffer();
const int MinFilter = (int)TextureMinFilter.Linear;
const int MagFilter = (int)TextureMagFilter.Linear;
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, SrcFb);
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DstFb);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, MinFilter);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, MagFilter);
GL.FramebufferTexture(
FramebufferTarget.ReadFramebuffer,
Attachment,
SrcTexture,
0);
(PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8);
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
Attachment,
DstTexture,
0);
const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba;
if (Color)
{
GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
}
const int Level = 0;
const int Border = 0;
GL.Clear(Mask);
GL.TexImage2D(
TextureTarget.Texture2D,
Level,
InternalFmt,
Width,
Height,
Border,
Format,
Type,
IntPtr.Zero);
GL.BlitFramebuffer(
SrcX0, SrcY0, SrcX1, SrcY1,
DstX0, DstY0, DstX1, DstY1,
Mask,
Color ? BlitFramebufferFilter.Linear : BlitFramebufferFilter.Nearest);
EnsureFrameBuffer();
}
}
}

View file

@ -25,23 +25,41 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ GalVertexAttribSize._11_11_10, 3 }
};
private static Dictionary<GalVertexAttribSize, VertexAttribPointerType> AttribTypes =
private static Dictionary<GalVertexAttribSize, VertexAttribPointerType> SignedAttribTypes =
new Dictionary<GalVertexAttribSize, VertexAttribPointerType>()
{
{ GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.Int },
{ GalVertexAttribSize._32_32_32, VertexAttribPointerType.Int },
{ GalVertexAttribSize._16_16_16_16, VertexAttribPointerType.Short },
{ GalVertexAttribSize._32_32, VertexAttribPointerType.Int },
{ GalVertexAttribSize._16_16_16, VertexAttribPointerType.Short },
{ GalVertexAttribSize._8_8_8_8, VertexAttribPointerType.Byte },
{ GalVertexAttribSize._16_16, VertexAttribPointerType.Short },
{ GalVertexAttribSize._32, VertexAttribPointerType.Int },
{ GalVertexAttribSize._8_8_8, VertexAttribPointerType.Byte },
{ GalVertexAttribSize._8_8, VertexAttribPointerType.Byte },
{ GalVertexAttribSize._16, VertexAttribPointerType.Short },
{ GalVertexAttribSize._8, VertexAttribPointerType.Byte },
{ GalVertexAttribSize._10_10_10_2, VertexAttribPointerType.Int }, //?
{ GalVertexAttribSize._11_11_10, VertexAttribPointerType.Int } //?
{ GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.Int },
{ GalVertexAttribSize._32_32_32, VertexAttribPointerType.Int },
{ GalVertexAttribSize._16_16_16_16, VertexAttribPointerType.Short },
{ GalVertexAttribSize._32_32, VertexAttribPointerType.Int },
{ GalVertexAttribSize._16_16_16, VertexAttribPointerType.Short },
{ GalVertexAttribSize._8_8_8_8, VertexAttribPointerType.Byte },
{ GalVertexAttribSize._16_16, VertexAttribPointerType.Short },
{ GalVertexAttribSize._32, VertexAttribPointerType.Int },
{ GalVertexAttribSize._8_8_8, VertexAttribPointerType.Byte },
{ GalVertexAttribSize._8_8, VertexAttribPointerType.Byte },
{ GalVertexAttribSize._16, VertexAttribPointerType.Short },
{ GalVertexAttribSize._8, VertexAttribPointerType.Byte },
{ GalVertexAttribSize._10_10_10_2, VertexAttribPointerType.Int2101010Rev }
};
private static Dictionary<GalVertexAttribSize, VertexAttribPointerType> UnsignedAttribTypes =
new Dictionary<GalVertexAttribSize, VertexAttribPointerType>()
{
{ GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.UnsignedInt },
{ GalVertexAttribSize._32_32_32, VertexAttribPointerType.UnsignedInt },
{ GalVertexAttribSize._16_16_16_16, VertexAttribPointerType.UnsignedShort },
{ GalVertexAttribSize._32_32, VertexAttribPointerType.UnsignedInt },
{ GalVertexAttribSize._16_16_16, VertexAttribPointerType.UnsignedShort },
{ GalVertexAttribSize._8_8_8_8, VertexAttribPointerType.UnsignedByte },
{ GalVertexAttribSize._16_16, VertexAttribPointerType.UnsignedShort },
{ GalVertexAttribSize._32, VertexAttribPointerType.UnsignedInt },
{ GalVertexAttribSize._8_8_8, VertexAttribPointerType.UnsignedByte },
{ GalVertexAttribSize._8_8, VertexAttribPointerType.UnsignedByte },
{ GalVertexAttribSize._16, VertexAttribPointerType.UnsignedShort },
{ GalVertexAttribSize._8, VertexAttribPointerType.UnsignedByte },
{ GalVertexAttribSize._10_10_10_2, VertexAttribPointerType.UnsignedInt2101010Rev },
{ GalVertexAttribSize._11_11_10, VertexAttribPointerType.UnsignedInt10F11F11FRev }
};
private GalPipelineState Old;
@ -108,9 +126,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
BindVertexLayout(New);
if (New.FlipX != Old.FlipX || New.FlipY != Old.FlipY)
if (New.FlipX != Old.FlipX || New.FlipY != Old.FlipY || New.Instance != Old.Instance)
{
Shader.SetFlip(New.FlipX, New.FlipY);
Shader.SetExtraData(New.FlipX, New.FlipY, New.Instance);
}
//Note: Uncomment SetFrontFace and SetCullFace when flipping issues are solved
@ -272,8 +290,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
private void BindConstBuffers(GalPipelineState New)
{
//Index 0 is reserved
int FreeBinding = 1;
int FreeBinding = OGLShader.ReservedCbufCount;
void BindIfNotNull(OGLShaderStage Stage)
{
@ -326,7 +343,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
bool Unsigned =
Attrib.Type == GalVertexAttribType.Unorm ||
Attrib.Type == GalVertexAttribType.Uint ||
Attrib.Type == GalVertexAttribType.Uint ||
Attrib.Type == GalVertexAttribType.Uscaled;
bool Normalize =
@ -341,7 +358,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
}
else
{
Type = AttribTypes[Attrib.Size] + (Unsigned ? 1 : 0);
if (Unsigned)
{
Type = UnsignedAttribTypes[Attrib.Size];
}
else
{
Type = SignedAttribTypes[Attrib.Size];
}
}
int Size = AttribElements[Attrib.Size];
@ -360,6 +384,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Binding.Stride, Offset);
}
if (Binding.Instanced && Binding.Divisor != 0)
{
GL.VertexAttribDivisor(Attrib.Index, 1);
}
else
{
GL.VertexAttribDivisor(Attrib.Index, 0);
}
}
}
}

View file

@ -3,7 +3,7 @@ using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
public class OGLRasterizer : IGalRasterizer
class OGLRasterizer : IGalRasterizer
{
private int[] VertexBuffers;
@ -44,36 +44,29 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public void ClearBuffers(
GalClearBufferFlags Flags,
int Attachment,
float Red, float Green, float Blue, float Alpha,
float Depth,
int Stencil)
{
ClearBufferMask Mask = ClearBufferMask.ColorBufferBit;
GL.ColorMask(
Flags.HasFlag(GalClearBufferFlags.ColorRed),
Flags.HasFlag(GalClearBufferFlags.ColorGreen),
Flags.HasFlag(GalClearBufferFlags.ColorBlue),
Flags.HasFlag(GalClearBufferFlags.ColorAlpha));
GL.ClearBuffer(ClearBuffer.Color, Attachment, new float[] { Red, Green, Blue, Alpha });
if (Flags.HasFlag(GalClearBufferFlags.Depth))
{
Mask |= ClearBufferMask.DepthBufferBit;
GL.ClearBuffer(ClearBuffer.Depth, 0, ref Depth);
}
if (Flags.HasFlag(GalClearBufferFlags.Stencil))
{
Mask |= ClearBufferMask.StencilBufferBit;
GL.ClearBuffer(ClearBuffer.Stencil, 0, ref Stencil);
}
GL.ClearColor(Red, Green, Blue, Alpha);
GL.ClearDepth(Depth);
GL.ClearStencil(Stencil);
GL.Clear(Mask);
GL.ColorMask(true, true, true, true);
}

View file

@ -23,7 +23,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
Buffer = new OGLConstBuffer();
FrameBuffer = new OGLFrameBuffer();
Texture = new OGLTexture();
FrameBuffer = new OGLFrameBuffer(Texture as OGLTexture);
Rasterizer = new OGLRasterizer();
@ -31,8 +33,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
Pipeline = new OGLPipeline(Buffer as OGLConstBuffer, Rasterizer as OGLRasterizer, Shader as OGLShader);
Texture = new OGLTexture();
ActionsQueue = new ConcurrentQueue<Action>();
}

View file

@ -9,6 +9,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
class OGLShader : IGalShader
{
public const int ReservedCbufCount = 1;
private const int ExtraDataSize = 4;
public OGLShaderProgram Current;
private ConcurrentDictionary<long, OGLShaderStage> Stages;
@ -96,16 +100,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return Enumerable.Empty<ShaderDeclInfo>();
}
public void EnsureTextureBinding(string UniformName, int Value)
{
BindProgram();
int Location = GL.GetUniformLocation(CurrentProgramHandle, UniformName);
GL.Uniform1(Location, Value);
}
public unsafe void SetFlip(float X, float Y)
public unsafe void SetExtraData(float FlipX, float FlipY, int Instance)
{
BindProgram();
@ -113,14 +108,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.BindBuffer(BufferTarget.UniformBuffer, ExtraUboHandle);
float* Data = stackalloc float[4];
Data[0] = X;
Data[1] = Y;
float* Data = stackalloc float[ExtraDataSize];
Data[0] = FlipX;
Data[1] = FlipY;
Data[2] = BitConverter.Int32BitsToSingle(Instance);
//Invalidate buffer
GL.BufferData(BufferTarget.UniformBuffer, 4 * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw);
GL.BufferData(BufferTarget.UniformBuffer, ExtraDataSize * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw);
GL.BufferSubData(BufferTarget.UniformBuffer, IntPtr.Zero, 4 * sizeof(float), (IntPtr)Data);
GL.BufferSubData(BufferTarget.UniformBuffer, IntPtr.Zero, ExtraDataSize * sizeof(float), (IntPtr)Data);
}
public void Bind(long Key)
@ -188,6 +184,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
CheckProgramLink(Handle);
BindUniformBlocks(Handle);
BindTextureLocations(Handle);
Programs.Add(Current, Handle);
}
@ -205,7 +202,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.BindBuffer(BufferTarget.UniformBuffer, ExtraUboHandle);
GL.BufferData(BufferTarget.UniformBuffer, 4 * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw);
GL.BufferData(BufferTarget.UniformBuffer, ExtraDataSize * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw);
GL.BindBufferBase(BufferRangeTarget.UniformBuffer, 0, ExtraUboHandle);
}
@ -227,8 +224,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.UniformBlockBinding(ProgramHandle, ExtraBlockindex, 0);
//First index is reserved
int FreeBinding = 1;
int FreeBinding = ReservedCbufCount;
void BindUniformBlocksIfNotNull(OGLShaderStage Stage)
{
@ -258,6 +254,34 @@ namespace Ryujinx.Graphics.Gal.OpenGL
BindUniformBlocksIfNotNull(Current.Fragment);
}
private void BindTextureLocations(int ProgramHandle)
{
int Index = 0;
void BindTexturesIfNotNull(OGLShaderStage Stage)
{
if (Stage != null)
{
foreach (ShaderDeclInfo Decl in Stage.TextureUsage)
{
int Location = GL.GetUniformLocation(ProgramHandle, Decl.Name);
GL.Uniform1(Location, Index);
Index++;
}
}
}
GL.UseProgram(ProgramHandle);
BindTexturesIfNotNull(Current.Vertex);
BindTexturesIfNotNull(Current.TessControl);
BindTexturesIfNotNull(Current.TessEvaluation);
BindTexturesIfNotNull(Current.Geometry);
BindTexturesIfNotNull(Current.Fragment);
}
private static void CheckProgramLink(int Handle)
{
int Status = 0;

View file

@ -4,26 +4,13 @@ using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
public class OGLTexture : IGalTexture
class OGLTexture : IGalTexture
{
private class TCE
{
public int Handle;
public GalTexture Texture;
public TCE(int Handle, GalTexture Texture)
{
this.Handle = Handle;
this.Texture = Texture;
}
}
private OGLCachedResource<TCE> TextureCache;
private OGLCachedResource<ImageHandler> TextureCache;
public OGLTexture()
{
TextureCache = new OGLCachedResource<TCE>(DeleteTexture);
TextureCache = new OGLCachedResource<ImageHandler>(DeleteTexture);
}
public void LockCache()
@ -36,73 +23,71 @@ namespace Ryujinx.Graphics.Gal.OpenGL
TextureCache.Unlock();
}
private static void DeleteTexture(TCE CachedTexture)
private static void DeleteTexture(ImageHandler CachedImage)
{
GL.DeleteTexture(CachedTexture.Handle);
GL.DeleteTexture(CachedImage.Handle);
}
public void Create(long Key, byte[] Data, GalTexture Texture)
public void Create(long Key, byte[] Data, GalImage Image)
{
int Handle = GL.GenTexture();
TextureCache.AddOrUpdate(Key, new TCE(Handle, Texture), (uint)Data.Length);
TextureCache.AddOrUpdate(Key, new ImageHandler(Handle, Image), (uint)Data.Length);
GL.BindTexture(TextureTarget.Texture2D, Handle);
const int Level = 0; //TODO: Support mipmap textures.
const int Border = 0;
if (IsCompressedTextureFormat(Texture.Format))
if (IsCompressedTextureFormat(Image.Format))
{
InternalFormat InternalFmt = OGLEnumConverter.GetCompressedTextureFormat(Texture.Format);
InternalFormat InternalFmt = OGLEnumConverter.GetCompressedImageFormat(Image.Format);
GL.CompressedTexImage2D(
TextureTarget.Texture2D,
Level,
InternalFmt,
Texture.Width,
Texture.Height,
Image.Width,
Image.Height,
Border,
Data.Length,
Data);
}
else
{
if (Texture.Format >= GalTextureFormat.Astc2D4x4)
if (Image.Format >= GalImageFormat.ASTC_BEGIN && Image.Format <= GalImageFormat.ASTC_END)
{
int TextureBlockWidth = GetAstcBlockWidth(Texture.Format);
int TextureBlockHeight = GetAstcBlockHeight(Texture.Format);
int TextureBlockWidth = GetAstcBlockWidth(Image.Format);
int TextureBlockHeight = GetAstcBlockHeight(Image.Format);
Data = ASTCDecoder.DecodeToRGBA8888(
Data,
TextureBlockWidth,
TextureBlockHeight, 1,
Texture.Width,
Texture.Height, 1);
Image.Width,
Image.Height, 1);
Texture.Format = GalTextureFormat.A8B8G8R8;
Image.Format = GalImageFormat.A8B8G8R8_UNORM_PACK32;
}
const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba;
(PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(Texture.Format);
(PixelInternalFormat InternalFormat, PixelFormat Format, PixelType Type) = OGLEnumConverter.GetImageFormat(Image.Format);
GL.TexImage2D(
TextureTarget.Texture2D,
Level,
InternalFmt,
Texture.Width,
Texture.Height,
InternalFormat,
Image.Width,
Image.Height,
Border,
Format,
Type,
Data);
}
int SwizzleR = (int)OGLEnumConverter.GetTextureSwizzle(Texture.XSource);
int SwizzleG = (int)OGLEnumConverter.GetTextureSwizzle(Texture.YSource);
int SwizzleB = (int)OGLEnumConverter.GetTextureSwizzle(Texture.ZSource);
int SwizzleA = (int)OGLEnumConverter.GetTextureSwizzle(Texture.WSource);
int SwizzleR = (int)OGLEnumConverter.GetTextureSwizzle(Image.XSource);
int SwizzleG = (int)OGLEnumConverter.GetTextureSwizzle(Image.YSource);
int SwizzleB = (int)OGLEnumConverter.GetTextureSwizzle(Image.ZSource);
int SwizzleA = (int)OGLEnumConverter.GetTextureSwizzle(Image.WSource);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleR, SwizzleR);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleG, SwizzleG);
@ -110,76 +95,100 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleA, SwizzleA);
}
private static int GetAstcBlockWidth(GalTextureFormat Format)
public void CreateFb(long Key, long Size, GalImage Image)
{
if (!TryGetImage(Key, out ImageHandler CachedImage))
{
CachedImage = new ImageHandler();
TextureCache.AddOrUpdate(Key, CachedImage, Size);
}
CachedImage.EnsureSetup(Image);
}
public bool TryGetImage(long Key, out ImageHandler CachedImage)
{
if (TextureCache.TryGetValue(Key, out CachedImage))
{
return true;
}
CachedImage = null;
return false;
}
private static int GetAstcBlockWidth(GalImageFormat Format)
{
switch (Format)
{
case GalTextureFormat.Astc2D4x4: return 4;
case GalTextureFormat.Astc2D5x5: return 5;
case GalTextureFormat.Astc2D6x6: return 6;
case GalTextureFormat.Astc2D8x8: return 8;
case GalTextureFormat.Astc2D10x10: return 10;
case GalTextureFormat.Astc2D12x12: return 12;
case GalTextureFormat.Astc2D5x4: return 5;
case GalTextureFormat.Astc2D6x5: return 6;
case GalTextureFormat.Astc2D8x6: return 8;
case GalTextureFormat.Astc2D10x8: return 10;
case GalTextureFormat.Astc2D12x10: return 12;
case GalTextureFormat.Astc2D8x5: return 8;
case GalTextureFormat.Astc2D10x5: return 10;
case GalTextureFormat.Astc2D10x6: return 10;
case GalImageFormat.ASTC_4x4_UNORM_BLOCK: return 4;
case GalImageFormat.ASTC_5x5_UNORM_BLOCK: return 5;
case GalImageFormat.ASTC_6x6_UNORM_BLOCK: return 6;
case GalImageFormat.ASTC_8x8_UNORM_BLOCK: return 8;
case GalImageFormat.ASTC_10x10_UNORM_BLOCK: return 10;
case GalImageFormat.ASTC_12x12_UNORM_BLOCK: return 12;
case GalImageFormat.ASTC_5x4_UNORM_BLOCK: return 5;
case GalImageFormat.ASTC_6x5_UNORM_BLOCK: return 6;
case GalImageFormat.ASTC_8x6_UNORM_BLOCK: return 8;
case GalImageFormat.ASTC_10x8_UNORM_BLOCK: return 10;
case GalImageFormat.ASTC_12x10_UNORM_BLOCK: return 12;
case GalImageFormat.ASTC_8x5_UNORM_BLOCK: return 8;
case GalImageFormat.ASTC_10x5_UNORM_BLOCK: return 10;
case GalImageFormat.ASTC_10x6_UNORM_BLOCK: return 10;
}
throw new ArgumentException(nameof(Format));
}
private static int GetAstcBlockHeight(GalTextureFormat Format)
private static int GetAstcBlockHeight(GalImageFormat Format)
{
switch (Format)
{
case GalTextureFormat.Astc2D4x4: return 4;
case GalTextureFormat.Astc2D5x5: return 5;
case GalTextureFormat.Astc2D6x6: return 6;
case GalTextureFormat.Astc2D8x8: return 8;
case GalTextureFormat.Astc2D10x10: return 10;
case GalTextureFormat.Astc2D12x12: return 12;
case GalTextureFormat.Astc2D5x4: return 4;
case GalTextureFormat.Astc2D6x5: return 5;
case GalTextureFormat.Astc2D8x6: return 6;
case GalTextureFormat.Astc2D10x8: return 8;
case GalTextureFormat.Astc2D12x10: return 10;
case GalTextureFormat.Astc2D8x5: return 5;
case GalTextureFormat.Astc2D10x5: return 5;
case GalTextureFormat.Astc2D10x6: return 6;
case GalImageFormat.ASTC_4x4_UNORM_BLOCK: return 4;
case GalImageFormat.ASTC_5x5_UNORM_BLOCK: return 5;
case GalImageFormat.ASTC_6x6_UNORM_BLOCK: return 6;
case GalImageFormat.ASTC_8x8_UNORM_BLOCK: return 8;
case GalImageFormat.ASTC_10x10_UNORM_BLOCK: return 10;
case GalImageFormat.ASTC_12x12_UNORM_BLOCK: return 12;
case GalImageFormat.ASTC_5x4_UNORM_BLOCK: return 4;
case GalImageFormat.ASTC_6x5_UNORM_BLOCK: return 5;
case GalImageFormat.ASTC_8x6_UNORM_BLOCK: return 6;
case GalImageFormat.ASTC_10x8_UNORM_BLOCK: return 8;
case GalImageFormat.ASTC_12x10_UNORM_BLOCK: return 10;
case GalImageFormat.ASTC_8x5_UNORM_BLOCK: return 5;
case GalImageFormat.ASTC_10x5_UNORM_BLOCK: return 5;
case GalImageFormat.ASTC_10x6_UNORM_BLOCK: return 6;
}
throw new ArgumentException(nameof(Format));
}
public bool TryGetCachedTexture(long Key, long DataSize, out GalTexture Texture)
public bool TryGetCachedTexture(long Key, long DataSize, out GalImage Image)
{
if (TextureCache.TryGetSize(Key, out long Size) && Size == DataSize)
{
if (TextureCache.TryGetValue(Key, out TCE CachedTexture))
if (TextureCache.TryGetValue(Key, out ImageHandler CachedImage))
{
Texture = CachedTexture.Texture;
Image = CachedImage.Image;
return true;
}
}
Texture = default(GalTexture);
Image = default(GalImage);
return false;
}
public void Bind(long Key, int Index)
{
if (TextureCache.TryGetValue(Key, out TCE CachedTexture))
if (TextureCache.TryGetValue(Key, out ImageHandler CachedImage))
{
GL.ActiveTexture(TextureUnit.Texture0 + Index);
GL.BindTexture(TextureTarget.Texture2D, CachedTexture.Handle);
GL.BindTexture(TextureTarget.Texture2D, CachedImage.Handle);
}
}
@ -208,18 +217,20 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureBorderColor, Color);
}
private static bool IsCompressedTextureFormat(GalTextureFormat Format)
private static bool IsCompressedTextureFormat(GalImageFormat Format)
{
switch (Format)
{
case GalTextureFormat.BC6H_UF16:
case GalTextureFormat.BC6H_SF16:
case GalTextureFormat.BC7U:
case GalTextureFormat.BC1:
case GalTextureFormat.BC2:
case GalTextureFormat.BC3:
case GalTextureFormat.BC4:
case GalTextureFormat.BC5:
case GalImageFormat.BC6H_UFLOAT_BLOCK:
case GalImageFormat.BC6H_SFLOAT_BLOCK:
case GalImageFormat.BC7_UNORM_BLOCK:
case GalImageFormat.BC1_RGBA_UNORM_BLOCK:
case GalImageFormat.BC2_UNORM_BLOCK:
case GalImageFormat.BC3_UNORM_BLOCK:
case GalImageFormat.BC4_SNORM_BLOCK:
case GalImageFormat.BC4_UNORM_BLOCK:
case GalImageFormat.BC5_SNORM_BLOCK:
case GalImageFormat.BC5_UNORM_BLOCK:
return true;
}

View file

@ -41,10 +41,15 @@ namespace Ryujinx.Graphics.Gal.Shader
public const string ExtraUniformBlockName = "Extra";
public const string FlipUniformName = "flip";
public const string InstanceUniformName = "instance";
public const string ProgramName = "program";
public const string ProgramAName = ProgramName + "_a";
public const string ProgramBName = ProgramName + "_b";
public const string BasicBlockName = "bb";
public const string BasicBlockAName = BasicBlockName + "_a";
public const string BasicBlockBName = BasicBlockName + "_b";
public const int SsyStackSize = 16;
public const string SsyStackName = "ssy_stack";
public const string SsyCursorName = "ssy_cursor";
private string[] StagePrefixes = new string[] { "vp", "tcp", "tep", "gp", "fp" };
@ -93,13 +98,34 @@ namespace Ryujinx.Graphics.Gal.Shader
m_Preds = new Dictionary<int, ShaderDeclInfo>();
}
public GlslDecl(ShaderIrBlock[] Blocks, GalShaderType ShaderType) : this(ShaderType)
public GlslDecl(ShaderIrBlock[] Blocks, GalShaderType ShaderType, ShaderHeader Header)
: this(ShaderType)
{
StagePrefix = StagePrefixes[(int)ShaderType] + "_";
if (ShaderType == GalShaderType.Fragment)
{
m_Gprs.Add(0, new ShaderDeclInfo(FragmentOutputName, 0, false, 0, 4));
int Index = 0;
for (int Attachment = 0; Attachment < 8; Attachment++)
{
for (int Component = 0; Component < 4; Component++)
{
if (Header.OmapTargets[Attachment].ComponentEnabled(Component))
{
m_Gprs.TryAdd(Index, new ShaderDeclInfo(GetGprName(Index), Index));
Index++;
}
}
}
if (Header.OmapDepth)
{
Index = Header.DepthRegister;
m_Gprs.TryAdd(Index, new ShaderDeclInfo(GetGprName(Index), Index));
}
}
foreach (ShaderIrBlock Block in Blocks)
@ -148,6 +174,11 @@ namespace Ryujinx.Graphics.Gal.Shader
return Combined;
}
public static string GetGprName(int Index)
{
return GprName + Index;
}
private static void Merge(
Dictionary<int, ShaderDeclInfo> C,
Dictionary<int, ShaderDeclInfo> A,
@ -311,9 +342,9 @@ namespace Ryujinx.Graphics.Gal.Shader
case ShaderIrOperGpr Gpr:
{
if (!Gpr.IsConst && !HasName(m_Gprs, Gpr.Index))
if (!Gpr.IsConst)
{
string Name = GprName + Gpr.Index;
string Name = GetGprName(Gpr.Index);
m_Gprs.TryAdd(Gpr.Index, new ShaderDeclInfo(Name, Gpr.Index));
}

View file

@ -120,8 +120,8 @@ namespace Ryujinx.Graphics.Gal.Shader
Blocks = ShaderDecoder.Decode(Memory, VpAPosition);
BlocksB = ShaderDecoder.Decode(Memory, VpBPosition);
GlslDecl DeclVpA = new GlslDecl(Blocks, ShaderType);
GlslDecl DeclVpB = new GlslDecl(BlocksB, ShaderType);
GlslDecl DeclVpA = new GlslDecl(Blocks, ShaderType, Header);
GlslDecl DeclVpB = new GlslDecl(BlocksB, ShaderType, HeaderB);
Decl = GlslDecl.Merge(DeclVpA, DeclVpB);
@ -136,7 +136,7 @@ namespace Ryujinx.Graphics.Gal.Shader
Blocks = ShaderDecoder.Decode(Memory, Position);
BlocksB = null;
Decl = new GlslDecl(Blocks, ShaderType);
Decl = new GlslDecl(Blocks, ShaderType, Header);
return Decompile();
}
@ -155,18 +155,19 @@ namespace Ryujinx.Graphics.Gal.Shader
PrintDeclOutAttributes();
PrintDeclGprs();
PrintDeclPreds();
PrintDeclSsy();
if (BlocksB != null)
{
PrintBlockScope(Blocks[0], null, null, "void " + GlslDecl.ProgramAName + "()", IdentationStr);
PrintBlockScope(Blocks, GlslDecl.BasicBlockAName);
SB.AppendLine();
PrintBlockScope(BlocksB[0], null, null, "void " + GlslDecl.ProgramBName + "()", IdentationStr);
PrintBlockScope(BlocksB, GlslDecl.BasicBlockBName);
}
else
{
PrintBlockScope(Blocks[0], null, null, "void " + GlslDecl.ProgramName + "()", IdentationStr);
PrintBlockScope(Blocks, GlslDecl.BasicBlockName);
}
SB.AppendLine();
@ -241,10 +242,15 @@ namespace Ryujinx.Graphics.Gal.Shader
{
if (Decl.ShaderType == GalShaderType.Vertex)
{
SB.AppendLine("layout (std140) uniform " + GlslDecl.ExtraUniformBlockName + "{");
//Memory layout here is [flip_x, flip_y, instance, unused]
//It's using 4 bytes, not 8
SB.AppendLine("layout (std140) uniform " + GlslDecl.ExtraUniformBlockName + " {");
SB.AppendLine(IdentationStr + "vec2 " + GlslDecl.FlipUniformName + ";");
SB.AppendLine(IdentationStr + "int " + GlslDecl.InstanceUniformName + ";");
SB.AppendLine("};");
}
@ -304,7 +310,17 @@ namespace Ryujinx.Graphics.Gal.Shader
private void PrintDeclOutAttributes()
{
if (Decl.ShaderType != GalShaderType.Fragment)
if (Decl.ShaderType == GalShaderType.Fragment)
{
for (int Attachment = 0; Attachment < 8; Attachment++)
{
if (Header.OmapTargets[Attachment].Enabled)
{
SB.AppendLine("layout (location = " + Attachment + ") out vec4 " + GlslDecl.FragmentOutputName + Attachment + ";");
}
}
}
else
{
SB.AppendLine("layout (location = " + GlslDecl.PositionOutAttrLocation + ") out vec4 " + GlslDecl.PositionOutAttrName + ";");
}
@ -342,6 +358,13 @@ namespace Ryujinx.Graphics.Gal.Shader
PrintDecls(Decl.Preds, "bool");
}
private void PrintDeclSsy()
{
SB.AppendLine("uint " + GlslDecl.SsyCursorName + ";");
SB.AppendLine("uint " + GlslDecl.SsyStackName + "[" + GlslDecl.SsyStackSize + "];" + Environment.NewLine);
}
private void PrintDecls(IReadOnlyDictionary<int, ShaderDeclInfo> Dict, string CustomType = null, string Suffix = "")
{
foreach (ShaderDeclInfo DeclInfo in Dict.Values.OrderBy(DeclKeySelector))
@ -352,9 +375,9 @@ namespace Ryujinx.Graphics.Gal.Shader
{
Name = CustomType + " " + DeclInfo.Name + Suffix + ";";
}
else if (DeclInfo.Name == GlslDecl.FragmentOutputName)
else if (DeclInfo.Name.Contains(GlslDecl.FragmentOutputName))
{
Name = "layout (location = 0) out vec4 " + DeclInfo.Name + Suffix + ";" + Environment.NewLine;
Name = "layout (location = " + DeclInfo.Index / 4 + ") out vec4 " + DeclInfo.Name + Suffix + ";";
}
else
{
@ -417,14 +440,16 @@ namespace Ryujinx.Graphics.Gal.Shader
}
}
SB.AppendLine(IdentationStr + "uint pc;");
if (BlocksB != null)
{
SB.AppendLine(IdentationStr + GlslDecl.ProgramAName + "();");
SB.AppendLine(IdentationStr + GlslDecl.ProgramBName + "();");
PrintProgram(Blocks, GlslDecl.BasicBlockAName);
PrintProgram(BlocksB, GlslDecl.BasicBlockBName);
}
else
{
SB.AppendLine(IdentationStr + GlslDecl.ProgramName + "();");
PrintProgram(Blocks, GlslDecl.BasicBlockName);
}
if (Decl.ShaderType != GalShaderType.Geometry)
@ -432,9 +457,62 @@ namespace Ryujinx.Graphics.Gal.Shader
PrintAttrToOutput();
}
if (Decl.ShaderType == GalShaderType.Fragment)
{
if (Header.OmapDepth)
{
SB.AppendLine(IdentationStr + "gl_FragDepth = " + GlslDecl.GetGprName(Header.DepthRegister) + ";");
}
int GprIndex = 0;
for (int Attachment = 0; Attachment < 8; Attachment++)
{
string Output = GlslDecl.FragmentOutputName + Attachment;
OmapTarget Target = Header.OmapTargets[Attachment];
for (int Component = 0; Component < 4; Component++)
{
if (Target.ComponentEnabled(Component))
{
SB.AppendLine(IdentationStr + Output + "[" + Component + "] = " + GlslDecl.GetGprName(GprIndex) + ";");
GprIndex++;
}
}
}
}
SB.AppendLine("}");
}
private void PrintProgram(ShaderIrBlock[] Blocks, string Name)
{
const string Ident1 = IdentationStr;
const string Ident2 = Ident1 + IdentationStr;
const string Ident3 = Ident2 + IdentationStr;
const string Ident4 = Ident3 + IdentationStr;
SB.AppendLine(Ident1 + "pc = " + GetBlockPosition(Blocks[0]) + ";");
SB.AppendLine(Ident1 + "do {");
SB.AppendLine(Ident2 + "switch (pc) {");
foreach (ShaderIrBlock Block in Blocks)
{
string FunctionName = Block.Position.ToString("x8");
SB.AppendLine(Ident3 + "case 0x" + FunctionName + ": pc = " + Name + "_" + FunctionName + "(); break;");
}
SB.AppendLine(Ident3 + "default:");
SB.AppendLine(Ident4 + "pc = 0;");
SB.AppendLine(Ident4 + "break;");
SB.AppendLine(Ident2 + "}");
SB.AppendLine(Ident1 + "} while (pc != 0);");
}
private void PrintAttrToOutput(string Identation = IdentationStr)
{
foreach (KeyValuePair<int, ShaderDeclInfo> KV in Decl.OutAttributes)
@ -468,193 +546,145 @@ namespace Ryujinx.Graphics.Gal.Shader
}
}
private void PrintBlockScope(
ShaderIrBlock Block,
ShaderIrBlock EndBlock,
ShaderIrBlock LoopBlock,
string ScopeName,
string Identation,
bool IsDoWhile = false)
private void PrintBlockScope(ShaderIrBlock[] Blocks, string Name)
{
string UpIdent = Identation.Substring(0, Identation.Length - IdentationStr.Length);
foreach (ShaderIrBlock Block in Blocks)
{
SB.AppendLine("uint " + Name + "_" + Block.Position.ToString("x8") + "() {");
if (IsDoWhile)
{
SB.AppendLine(UpIdent + "do {");
}
else
{
SB.AppendLine(UpIdent + ScopeName + " {");
}
PrintNodes(Block, Block.GetNodes());
while (Block != null && Block != EndBlock)
{
ShaderIrNode[] Nodes = Block.GetNodes();
Block = PrintNodes(Block, EndBlock, LoopBlock, Identation, Nodes);
}
if (IsDoWhile)
{
SB.AppendLine(UpIdent + "} " + ScopeName + ";");
}
else
{
SB.AppendLine(UpIdent + "}");
SB.AppendLine("}" + Environment.NewLine);
}
}
private ShaderIrBlock PrintNodes(
ShaderIrBlock Block,
ShaderIrBlock EndBlock,
ShaderIrBlock LoopBlock,
string Identation,
params ShaderIrNode[] Nodes)
private void PrintNode(ShaderIrBlock Block, ShaderIrNode Node, string Identation)
{
/*
* Notes about control flow and if-else/loop generation:
* The code assumes that the program has sane control flow,
* that is, there's no jumps to a location after another jump or
* jump target (except for the end of an if-else block), and backwards
* jumps to a location before the last loop dominator.
* Such cases needs to be transformed on a step before the GLSL code
* generation to ensure that we have sane graphs to work with.
* TODO: Such transformation is not yet implemented.
*/
string NewIdent = Identation + IdentationStr;
ShaderIrBlock LoopTail = GetLoopTailBlock(Block);
if (LoopTail != null && LoopBlock != Block)
if (Node is ShaderIrCond Cond)
{
//Shoock! kuma shock! We have a loop here!
//The entire sequence needs to be inside a do-while block.
ShaderIrBlock LoopEnd = GetDownBlock(LoopTail);
string IfExpr = GetSrcExpr(Cond.Pred, true);
PrintBlockScope(Block, LoopEnd, Block, "while (false)", NewIdent, IsDoWhile: true);
if (Cond.Not)
{
IfExpr = "!(" + IfExpr + ")";
}
return LoopEnd;
SB.AppendLine(Identation + "if (" + IfExpr + ") {");
if (Cond.Child is ShaderIrOp Op && Op.Inst == ShaderIrInst.Bra)
{
SB.AppendLine(Identation + IdentationStr + "return " + GetBlockPosition(Block.Branch) + ";");
}
else
{
PrintNode(Block, Cond.Child, Identation + IdentationStr);
}
SB.AppendLine(Identation + "}");
}
foreach (ShaderIrNode Node in Nodes)
else if (Node is ShaderIrAsg Asg)
{
if (Node is ShaderIrCond Cond)
if (IsValidOutOper(Asg.Dst))
{
string IfExpr = GetSrcExpr(Cond.Pred, true);
string Expr = GetSrcExpr(Asg.Src, true);
if (Cond.Not)
{
IfExpr = "!(" + IfExpr + ")";
}
Expr = GetExprWithCast(Asg.Dst, Asg.Src, Expr);
if (Cond.Child is ShaderIrOp Op && Op.Inst == ShaderIrInst.Bra)
{
//Branch is a loop branch and would result in infinite recursion.
if (Block.Branch.Position <= Block.Position)
{
SB.AppendLine(Identation + "if (" + IfExpr + ") {");
SB.AppendLine(Identation + IdentationStr + "continue;");
SB.AppendLine(Identation + "}");
continue;
}
string SubScopeName = "if (!" + IfExpr + ")";
PrintBlockScope(Block.Next, Block.Branch, LoopBlock, SubScopeName, NewIdent);
ShaderIrBlock IfElseEnd = GetUpBlock(Block.Branch).Branch;
if (IfElseEnd?.Position > Block.Branch.Position)
{
PrintBlockScope(Block.Branch, IfElseEnd, LoopBlock, "else", NewIdent);
return IfElseEnd;
}
return Block.Branch;
}
else
{
SB.AppendLine(Identation + "if (" + IfExpr + ") {");
PrintNodes(Block, EndBlock, LoopBlock, NewIdent, Cond.Child);
SB.AppendLine(Identation + "}");
}
SB.AppendLine(Identation + GetDstOperName(Asg.Dst) + " = " + Expr + ";");
}
else if (Node is ShaderIrAsg Asg)
}
else if (Node is ShaderIrOp Op)
{
switch (Op.Inst)
{
if (IsValidOutOper(Asg.Dst))
case ShaderIrInst.Bra:
{
string Expr = GetSrcExpr(Asg.Src, true);
SB.AppendLine(Identation + "return " + GetBlockPosition(Block.Branch) + ";");
Expr = GetExprWithCast(Asg.Dst, Asg.Src, Expr);
break;
}
SB.AppendLine(Identation + GetDstOperName(Asg.Dst) + " = " + Expr + ";");
}
}
else if (Node is ShaderIrOp Op)
{
if (Op.Inst == ShaderIrInst.Bra)
{
if (Block.Branch.Position <= Block.Position)
{
SB.AppendLine(Identation + "continue;");
}
}
else if (Op.Inst == ShaderIrInst.Emit)
case ShaderIrInst.Emit:
{
PrintAttrToOutput(Identation);
SB.AppendLine(Identation + "EmitVertex();");
break;
}
else
case ShaderIrInst.Ssy:
{
string StackIndex = GlslDecl.SsyStackName + "[" + GlslDecl.SsyCursorName + "]";
int TargetPosition = (Op.OperandA as ShaderIrOperImm).Value;
string Target = "0x" + TargetPosition.ToString("x8") + "u";
SB.AppendLine(Identation + StackIndex + " = " + Target + ";");
SB.AppendLine(Identation + GlslDecl.SsyCursorName + "++;");
break;
}
case ShaderIrInst.Sync:
{
SB.AppendLine(Identation + GlslDecl.SsyCursorName + "--;");
string Target = GlslDecl.SsyStackName + "[" + GlslDecl.SsyCursorName + "]";
SB.AppendLine(Identation + "return " + Target + ";");
break;
}
default:
SB.AppendLine(Identation + GetSrcExpr(Op, true) + ";");
}
}
else if (Node is ShaderIrCmnt Cmnt)
{
SB.AppendLine(Identation + "// " + Cmnt.Comment);
}
else
{
throw new InvalidOperationException();
break;
}
}
return Block.Next;
}
private ShaderIrBlock GetUpBlock(ShaderIrBlock Block)
{
return Blocks.FirstOrDefault(x => x.EndPosition == Block.Position);
}
private ShaderIrBlock GetDownBlock(ShaderIrBlock Block)
{
return Blocks.FirstOrDefault(x => x.Position == Block.EndPosition);
}
private ShaderIrBlock GetLoopTailBlock(ShaderIrBlock LoopHead)
{
ShaderIrBlock Tail = null;
foreach (ShaderIrBlock Block in LoopHead.Sources)
else if (Node is ShaderIrCmnt Cmnt)
{
if (Block.Position >= LoopHead.Position)
{
if (Tail == null || Tail.Position < Block.Position)
{
Tail = Block;
}
}
SB.AppendLine(Identation + "// " + Cmnt.Comment);
}
else
{
throw new InvalidOperationException();
}
}
private void PrintNodes(ShaderIrBlock Block, ShaderIrNode[] Nodes)
{
foreach (ShaderIrNode Node in Nodes)
{
PrintNode(Block, Node, IdentationStr);
}
return Tail;
if (Nodes.Length > 0)
{
ShaderIrNode Last = Nodes[Nodes.Length - 1];
bool UnconditionalFlowChange = false;
if (Last is ShaderIrOp Op)
{
switch (Op.Inst)
{
case ShaderIrInst.Bra:
case ShaderIrInst.Exit:
case ShaderIrInst.Kil:
case ShaderIrInst.Sync:
UnconditionalFlowChange = true;
break;
}
}
if (!UnconditionalFlowChange)
{
SB.AppendLine(IdentationStr + "return " + GetBlockPosition(Block.Next) + ";");
}
}
}
private bool IsValidOutOper(ShaderIrNode Node)
@ -779,7 +809,7 @@ namespace Ryujinx.Graphics.Gal.Shader
switch (Abuf.Offs)
{
case GlslDecl.VertexIdAttr: return "gl_VertexID";
case GlslDecl.InstanceIdAttr: return "gl_InstanceID";
case GlslDecl.InstanceIdAttr: return GlslDecl.InstanceUniformName;
}
}
else if (Decl.ShaderType == GalShaderType.TessEvaluation)
@ -829,8 +859,11 @@ namespace Ryujinx.Graphics.Gal.Shader
{
return "gl_PointSize";
}
}
throw new InvalidOperationException();
if (DeclInfo.Index >= 16)
{
throw new InvalidOperationException($"Shader attribute offset {Abuf.Offs} is invalid.");
}
if (Decl.ShaderType == GalShaderType.Geometry)
@ -876,7 +909,7 @@ namespace Ryujinx.Graphics.Gal.Shader
private string GetNameWithSwizzle(IReadOnlyDictionary<int, ShaderDeclInfo> Dict, int Index)
{
int VecIndex = Index >> 2;
int VecIndex = Index & ~3;
if (Dict.TryGetValue(VecIndex, out ShaderDeclInfo DeclInfo))
{
@ -961,7 +994,7 @@ namespace Ryujinx.Graphics.Gal.Shader
private string GetCnumExpr(ShaderIrOp Op) => GetUnaryCall(Op, "!isnan");
private string GetExitExpr(ShaderIrOp Op) => "return";
private string GetExitExpr(ShaderIrOp Op) => "return 0u";
private string GetFcosExpr(ShaderIrOp Op) => GetUnaryCall(Op, "cos");
@ -993,7 +1026,31 @@ namespace Ryujinx.Graphics.Gal.Shader
return "int(uint(" + GetOperExpr(Op, Op.OperandA) + "))";
}
private string GetIpaExpr(ShaderIrOp Op) => GetSrcExpr(Op.OperandA);
private string GetIpaExpr(ShaderIrOp Op)
{
ShaderIrMetaIpa Meta = (ShaderIrMetaIpa)Op.MetaData;
ShaderIrOperAbuf Abuf = (ShaderIrOperAbuf)Op.OperandA;
if (Meta.Mode == ShaderIpaMode.Pass)
{
int Index = Abuf.Offs >> 4;
int Elem = (Abuf.Offs >> 2) & 3;
if (Decl.ShaderType == GalShaderType.Fragment && Index == GlslDecl.GlPositionVec4Index)
{
switch (Elem)
{
case 0: return "gl_FragCoord.x";
case 1: return "gl_FragCoord.y";
case 2: return "gl_FragCoord.z";
case 3: return "1";
}
}
}
return GetSrcExpr(Op.OperandA);
}
private string GetKilExpr(ShaderIrOp Op) => "discard";
@ -1306,5 +1363,17 @@ namespace Ryujinx.Graphics.Gal.Shader
throw new ArgumentException(nameof(Node));
}
private static string GetBlockPosition(ShaderIrBlock Block)
{
if (Block != null)
{
return "0x" + Block.Position.ToString("x8") + "u";
}
else
{
return "0u";
}
}
}
}

View file

@ -1,4 +1,4 @@
namespace Ryujinx.Graphics.Gal.Shader
{
delegate void ShaderDecodeFunc(ShaderIrBlock Block, long OpCode);
delegate void ShaderDecodeFunc(ShaderIrBlock Block, long OpCode, long Position);
}

View file

@ -6,32 +6,32 @@ namespace Ryujinx.Graphics.Gal.Shader
{
static partial class ShaderDecode
{
public static void Bfe_C(ShaderIrBlock Block, long OpCode)
public static void Bfe_C(ShaderIrBlock Block, long OpCode, long Position)
{
EmitBfe(Block, OpCode, ShaderOper.CR);
}
public static void Bfe_I(ShaderIrBlock Block, long OpCode)
public static void Bfe_I(ShaderIrBlock Block, long OpCode, long Position)
{
EmitBfe(Block, OpCode, ShaderOper.Imm);
}
public static void Bfe_R(ShaderIrBlock Block, long OpCode)
public static void Bfe_R(ShaderIrBlock Block, long OpCode, long Position)
{
EmitBfe(Block, OpCode, ShaderOper.RR);
}
public static void Fadd_C(ShaderIrBlock Block, long OpCode)
public static void Fadd_C(ShaderIrBlock Block, long OpCode, long Position)
{
EmitFadd(Block, OpCode, ShaderOper.CR);
}
public static void Fadd_I(ShaderIrBlock Block, long OpCode)
public static void Fadd_I(ShaderIrBlock Block, long OpCode, long Position)
{
EmitFadd(Block, OpCode, ShaderOper.Immf);
}
public static void Fadd_I32(ShaderIrBlock Block, long OpCode)
public static void Fadd_I32(ShaderIrBlock Block, long OpCode, long Position)
{
ShaderIrNode OperA = GetOperGpr8 (OpCode);
ShaderIrNode OperB = GetOperImmf32_20(OpCode);
@ -49,47 +49,47 @@ namespace Ryujinx.Graphics.Gal.Shader
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
}
public static void Fadd_R(ShaderIrBlock Block, long OpCode)
public static void Fadd_R(ShaderIrBlock Block, long OpCode, long Position)
{
EmitFadd(Block, OpCode, ShaderOper.RR);
}
public static void Ffma_CR(ShaderIrBlock Block, long OpCode)
public static void Ffma_CR(ShaderIrBlock Block, long OpCode, long Position)
{
EmitFfma(Block, OpCode, ShaderOper.CR);
}
public static void Ffma_I(ShaderIrBlock Block, long OpCode)
public static void Ffma_I(ShaderIrBlock Block, long OpCode, long Position)
{
EmitFfma(Block, OpCode, ShaderOper.Immf);
}
public static void Ffma_RC(ShaderIrBlock Block, long OpCode)
public static void Ffma_RC(ShaderIrBlock Block, long OpCode, long Position)
{
EmitFfma(Block, OpCode, ShaderOper.RC);
}
public static void Ffma_RR(ShaderIrBlock Block, long OpCode)
public static void Ffma_RR(ShaderIrBlock Block, long OpCode, long Position)
{
EmitFfma(Block, OpCode, ShaderOper.RR);
}
public static void Fmnmx_C(ShaderIrBlock Block, long OpCode)
public static void Fmnmx_C(ShaderIrBlock Block, long OpCode, long Position)
{
EmitFmnmx(Block, OpCode, ShaderOper.CR);
}
public static void Fmnmx_I(ShaderIrBlock Block, long OpCode)
public static void Fmnmx_I(ShaderIrBlock Block, long OpCode, long Position)
{
EmitFmnmx(Block, OpCode, ShaderOper.Immf);
}
public static void Fmnmx_R(ShaderIrBlock Block, long OpCode)
public static void Fmnmx_R(ShaderIrBlock Block, long OpCode, long Position)
{
EmitFmnmx(Block, OpCode, ShaderOper.RR);
}
public static void Fmul_I32(ShaderIrBlock Block, long OpCode)
public static void Fmul_I32(ShaderIrBlock Block, long OpCode, long Position)
{
ShaderIrNode OperA = GetOperGpr8 (OpCode);
ShaderIrNode OperB = GetOperImmf32_20(OpCode);
@ -99,62 +99,62 @@ namespace Ryujinx.Graphics.Gal.Shader
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
}
public static void Fmul_C(ShaderIrBlock Block, long OpCode)
public static void Fmul_C(ShaderIrBlock Block, long OpCode, long Position)
{
EmitFmul(Block, OpCode, ShaderOper.CR);
}
public static void Fmul_I(ShaderIrBlock Block, long OpCode)
public static void Fmul_I(ShaderIrBlock Block, long OpCode, long Position)
{
EmitFmul(Block, OpCode, ShaderOper.Immf);
}
public static void Fmul_R(ShaderIrBlock Block, long OpCode)
public static void Fmul_R(ShaderIrBlock Block, long OpCode, long Position)
{
EmitFmul(Block, OpCode, ShaderOper.RR);
}
public static void Fset_C(ShaderIrBlock Block, long OpCode)
public static void Fset_C(ShaderIrBlock Block, long OpCode, long Position)
{
EmitFset(Block, OpCode, ShaderOper.CR);
}
public static void Fset_I(ShaderIrBlock Block, long OpCode)
public static void Fset_I(ShaderIrBlock Block, long OpCode, long Position)
{
EmitFset(Block, OpCode, ShaderOper.Immf);
}
public static void Fset_R(ShaderIrBlock Block, long OpCode)
public static void Fset_R(ShaderIrBlock Block, long OpCode, long Position)
{
EmitFset(Block, OpCode, ShaderOper.RR);
}
public static void Fsetp_C(ShaderIrBlock Block, long OpCode)
public static void Fsetp_C(ShaderIrBlock Block, long OpCode, long Position)
{
EmitFsetp(Block, OpCode, ShaderOper.CR);
}
public static void Fsetp_I(ShaderIrBlock Block, long OpCode)
public static void Fsetp_I(ShaderIrBlock Block, long OpCode, long Position)
{
EmitFsetp(Block, OpCode, ShaderOper.Immf);
}
public static void Fsetp_R(ShaderIrBlock Block, long OpCode)
public static void Fsetp_R(ShaderIrBlock Block, long OpCode, long Position)
{
EmitFsetp(Block, OpCode, ShaderOper.RR);
}
public static void Iadd_C(ShaderIrBlock Block, long OpCode)
public static void Iadd_C(ShaderIrBlock Block, long OpCode, long Position)
{
EmitIadd(Block, OpCode, ShaderOper.CR);
}
public static void Iadd_I(ShaderIrBlock Block, long OpCode)
public static void Iadd_I(ShaderIrBlock Block, long OpCode, long Position)
{
EmitIadd(Block, OpCode, ShaderOper.Imm);
}
public static void Iadd_I32(ShaderIrBlock Block, long OpCode)
public static void Iadd_I32(ShaderIrBlock Block, long OpCode, long Position)
{
ShaderIrNode OperA = GetOperGpr8 (OpCode);
ShaderIrNode OperB = GetOperImm32_20(OpCode);
@ -168,97 +168,101 @@ namespace Ryujinx.Graphics.Gal.Shader
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
}
public static void Iadd_R(ShaderIrBlock Block, long OpCode)
public static void Iadd_R(ShaderIrBlock Block, long OpCode, long Position)
{
EmitIadd(Block, OpCode, ShaderOper.RR);
}
public static void Iadd3_C(ShaderIrBlock Block, long OpCode)
public static void Iadd3_C(ShaderIrBlock Block, long OpCode, long Position)
{
EmitIadd3(Block, OpCode, ShaderOper.CR);
}
public static void Iadd3_I(ShaderIrBlock Block, long OpCode)
public static void Iadd3_I(ShaderIrBlock Block, long OpCode, long Position)
{
EmitIadd3(Block, OpCode, ShaderOper.Imm);
}
public static void Iadd3_R(ShaderIrBlock Block, long OpCode)
public static void Iadd3_R(ShaderIrBlock Block, long OpCode, long Position)
{
EmitIadd3(Block, OpCode, ShaderOper.RR);
}
public static void Imnmx_C(ShaderIrBlock Block, long OpCode)
public static void Imnmx_C(ShaderIrBlock Block, long OpCode, long Position)
{
EmitImnmx(Block, OpCode, ShaderOper.CR);
}
public static void Imnmx_I(ShaderIrBlock Block, long OpCode)
public static void Imnmx_I(ShaderIrBlock Block, long OpCode, long Position)
{
EmitImnmx(Block, OpCode, ShaderOper.Imm);
}
public static void Imnmx_R(ShaderIrBlock Block, long OpCode)
public static void Imnmx_R(ShaderIrBlock Block, long OpCode, long Position)
{
EmitImnmx(Block, OpCode, ShaderOper.RR);
}
public static void Ipa(ShaderIrBlock Block, long OpCode)
public static void Ipa(ShaderIrBlock Block, long OpCode, long Position)
{
ShaderIrNode OperA = GetOperAbuf28(OpCode);
ShaderIrNode OperB = GetOperGpr20 (OpCode);
ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Ipa, OperA, OperB);
ShaderIpaMode Mode = (ShaderIpaMode)((OpCode >> 54) & 3);
ShaderIrMetaIpa Meta = new ShaderIrMetaIpa(Mode);
ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Ipa, OperA, OperB, null, Meta);
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
}
public static void Iscadd_C(ShaderIrBlock Block, long OpCode)
public static void Iscadd_C(ShaderIrBlock Block, long OpCode, long Position)
{
EmitIscadd(Block, OpCode, ShaderOper.CR);
}
public static void Iscadd_I(ShaderIrBlock Block, long OpCode)
public static void Iscadd_I(ShaderIrBlock Block, long OpCode, long Position)
{
EmitIscadd(Block, OpCode, ShaderOper.Imm);
}
public static void Iscadd_R(ShaderIrBlock Block, long OpCode)
public static void Iscadd_R(ShaderIrBlock Block, long OpCode, long Position)
{
EmitIscadd(Block, OpCode, ShaderOper.RR);
}
public static void Iset_C(ShaderIrBlock Block, long OpCode)
public static void Iset_C(ShaderIrBlock Block, long OpCode, long Position)
{
EmitIset(Block, OpCode, ShaderOper.CR);
}
public static void Iset_I(ShaderIrBlock Block, long OpCode)
public static void Iset_I(ShaderIrBlock Block, long OpCode, long Position)
{
EmitIset(Block, OpCode, ShaderOper.Imm);
}
public static void Iset_R(ShaderIrBlock Block, long OpCode)
public static void Iset_R(ShaderIrBlock Block, long OpCode, long Position)
{
EmitIset(Block, OpCode, ShaderOper.RR);
}
public static void Isetp_C(ShaderIrBlock Block, long OpCode)
public static void Isetp_C(ShaderIrBlock Block, long OpCode, long Position)
{
EmitIsetp(Block, OpCode, ShaderOper.CR);
}
public static void Isetp_I(ShaderIrBlock Block, long OpCode)
public static void Isetp_I(ShaderIrBlock Block, long OpCode, long Position)
{
EmitIsetp(Block, OpCode, ShaderOper.Imm);
}
public static void Isetp_R(ShaderIrBlock Block, long OpCode)
public static void Isetp_R(ShaderIrBlock Block, long OpCode, long Position)
{
EmitIsetp(Block, OpCode, ShaderOper.RR);
}
public static void Lop_I32(ShaderIrBlock Block, long OpCode)
public static void Lop_I32(ShaderIrBlock Block, long OpCode, long Position)
{
int SubOp = (int)(OpCode >> 53) & 3;
@ -292,22 +296,22 @@ namespace Ryujinx.Graphics.Gal.Shader
}
}
public static void Lop_C(ShaderIrBlock Block, long OpCode)
public static void Lop_C(ShaderIrBlock Block, long OpCode, long Position)
{
EmitLop(Block, OpCode, ShaderOper.CR);
}
public static void Lop_I(ShaderIrBlock Block, long OpCode)
public static void Lop_I(ShaderIrBlock Block, long OpCode, long Position)
{
EmitLop(Block, OpCode, ShaderOper.Imm);
}
public static void Lop_R(ShaderIrBlock Block, long OpCode)
public static void Lop_R(ShaderIrBlock Block, long OpCode, long Position)
{
EmitLop(Block, OpCode, ShaderOper.RR);
}
public static void Mufu(ShaderIrBlock Block, long OpCode)
public static void Mufu(ShaderIrBlock Block, long OpCode, long Position)
{
int SubOp = (int)(OpCode >> 20) & 0xf;
@ -336,7 +340,7 @@ namespace Ryujinx.Graphics.Gal.Shader
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
}
public static void Psetp(ShaderIrBlock Block, long OpCode)
public static void Psetp(ShaderIrBlock Block, long OpCode, long Position)
{
bool NegA = ((OpCode >> 15) & 1) != 0;
bool NegB = ((OpCode >> 32) & 1) != 0;
@ -390,47 +394,47 @@ namespace Ryujinx.Graphics.Gal.Shader
Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode));
}
public static void Rro_C(ShaderIrBlock Block, long OpCode)
public static void Rro_C(ShaderIrBlock Block, long OpCode, long Position)
{
EmitRro(Block, OpCode, ShaderOper.CR);
}
public static void Rro_I(ShaderIrBlock Block, long OpCode)
public static void Rro_I(ShaderIrBlock Block, long OpCode, long Position)
{
EmitRro(Block, OpCode, ShaderOper.Immf);
}
public static void Rro_R(ShaderIrBlock Block, long OpCode)
public static void Rro_R(ShaderIrBlock Block, long OpCode, long Position)
{
EmitRro(Block, OpCode, ShaderOper.RR);
}
public static void Shl_C(ShaderIrBlock Block, long OpCode)
public static void Shl_C(ShaderIrBlock Block, long OpCode, long Position)
{
EmitAluBinary(Block, OpCode, ShaderOper.CR, ShaderIrInst.Lsl);
}
public static void Shl_I(ShaderIrBlock Block, long OpCode)
public static void Shl_I(ShaderIrBlock Block, long OpCode, long Position)
{
EmitAluBinary(Block, OpCode, ShaderOper.Imm, ShaderIrInst.Lsl);
}
public static void Shl_R(ShaderIrBlock Block, long OpCode)
public static void Shl_R(ShaderIrBlock Block, long OpCode, long Position)
{
EmitAluBinary(Block, OpCode, ShaderOper.RR, ShaderIrInst.Lsl);
}
public static void Shr_C(ShaderIrBlock Block, long OpCode)
public static void Shr_C(ShaderIrBlock Block, long OpCode, long Position)
{
EmitAluBinary(Block, OpCode, ShaderOper.CR, GetShrInst(OpCode));
}
public static void Shr_I(ShaderIrBlock Block, long OpCode)
public static void Shr_I(ShaderIrBlock Block, long OpCode, long Position)
{
EmitAluBinary(Block, OpCode, ShaderOper.Imm, GetShrInst(OpCode));
}
public static void Shr_R(ShaderIrBlock Block, long OpCode)
public static void Shr_R(ShaderIrBlock Block, long OpCode, long Position)
{
EmitAluBinary(Block, OpCode, ShaderOper.RR, GetShrInst(OpCode));
}
@ -442,7 +446,7 @@ namespace Ryujinx.Graphics.Gal.Shader
return Signed ? ShaderIrInst.Asr : ShaderIrInst.Lsr;
}
public static void Vmad(ShaderIrBlock Block, long OpCode)
public static void Vmad(ShaderIrBlock Block, long OpCode, long Position)
{
ShaderIrNode OperA = GetOperGpr8(OpCode);
@ -477,22 +481,22 @@ namespace Ryujinx.Graphics.Gal.Shader
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Final), OpCode));
}
public static void Xmad_CR(ShaderIrBlock Block, long OpCode)
public static void Xmad_CR(ShaderIrBlock Block, long OpCode, long Position)
{
EmitXmad(Block, OpCode, ShaderOper.CR);
}
public static void Xmad_I(ShaderIrBlock Block, long OpCode)
public static void Xmad_I(ShaderIrBlock Block, long OpCode, long Position)
{
EmitXmad(Block, OpCode, ShaderOper.Imm);
}
public static void Xmad_RC(ShaderIrBlock Block, long OpCode)
public static void Xmad_RC(ShaderIrBlock Block, long OpCode, long Position)
{
EmitXmad(Block, OpCode, ShaderOper.RC);
}
public static void Xmad_RR(ShaderIrBlock Block, long OpCode)
public static void Xmad_RR(ShaderIrBlock Block, long OpCode, long Position)
{
EmitXmad(Block, OpCode, ShaderOper.RR);
}

View file

@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.Gal.Shader
{
static partial class ShaderDecode
{
public static void Bra(ShaderIrBlock Block, long OpCode)
public static void Bra(ShaderIrBlock Block, long OpCode, long Position)
{
if ((OpCode & 0x20) != 0)
{
@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gal.Shader
Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Bra, Imm), OpCode));
}
public static void Exit(ShaderIrBlock Block, long OpCode)
public static void Exit(ShaderIrBlock Block, long OpCode, long Position)
{
int CCode = (int)OpCode & 0x1f;
@ -34,9 +34,34 @@ namespace Ryujinx.Graphics.Gal.Shader
}
public static void Kil(ShaderIrBlock Block, long OpCode)
public static void Kil(ShaderIrBlock Block, long OpCode, long Position)
{
Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Kil), OpCode));
}
public static void Ssy(ShaderIrBlock Block, long OpCode, long Position)
{
if ((OpCode & 0x20) != 0)
{
//This reads the target offset from the constant buffer.
//Almost impossible to support with GLSL.
throw new NotImplementedException();
}
int Offset = ((int)(OpCode >> 20) << 8) >> 8;
int Target = (int)(Position + Offset);
ShaderIrOperImm Imm = new ShaderIrOperImm(Target);
Block.AddNode(new ShaderIrOp(ShaderIrInst.Ssy, Imm));
}
public static void Sync(ShaderIrBlock Block, long OpCode, long Position)
{
//TODO: Implement Sync condition codes
Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Sync), OpCode));
}
}
}

View file

@ -35,13 +35,6 @@ namespace Ryujinx.Graphics.Gal.Shader
(int)(OpCode >> 20) & 0x3fff);
}
public static ShaderIrOperCbuf GetOperCbuf36(long OpCode)
{
return new ShaderIrOperCbuf(
(int)(OpCode >> 36) & 0x1f,
(int)(OpCode >> 22) & 0x3fff, GetOperGpr8(OpCode));
}
public static ShaderIrOperGpr GetOperGpr8(long OpCode)
{
return new ShaderIrOperGpr((int)(OpCode >> 8) & 0xff);

View file

@ -31,7 +31,7 @@ namespace Ryujinx.Graphics.Gal.Shader
{ RGB_, RG_A, R_BA, _GBA, RGBA, ____, ____, ____ }
};
public static void Ld_A(ShaderIrBlock Block, long OpCode)
public static void Ld_A(ShaderIrBlock Block, long OpCode, long Position)
{
ShaderIrNode[] Opers = GetOperAbuf20(OpCode);
@ -50,25 +50,37 @@ namespace Ryujinx.Graphics.Gal.Shader
}
}
public static void Ld_C(ShaderIrBlock Block, long OpCode)
public static void Ld_C(ShaderIrBlock Block, long OpCode, long Position)
{
int Type = (int)(OpCode >> 48) & 7;
int CbufPos = (int)(OpCode >> 22) & 0x3fff;
int CbufIndex = (int)(OpCode >> 36) & 0x1f;
int Type = (int)(OpCode >> 48) & 7;
if (Type > 5)
{
throw new InvalidOperationException();
}
ShaderIrOperGpr Temp = ShaderIrOperGpr.MakeTemporary();
Block.AddNode(new ShaderIrAsg(Temp, GetOperGpr8(OpCode)));
int Count = Type == 5 ? 2 : 1;
for (int Index = 0; Index < Count; Index++)
{
ShaderIrOperCbuf OperA = GetOperCbuf36(OpCode);
ShaderIrOperGpr OperD = GetOperGpr0 (OpCode);
ShaderIrOperCbuf OperA = new ShaderIrOperCbuf(CbufIndex, CbufPos, Temp);
ShaderIrOperGpr OperD = GetOperGpr0(OpCode);
OperA.Pos += Index;
OperD.Index += Index;
if (!OperD.IsValidRegister)
{
break;
}
ShaderIrNode Node = OperA;
if (Type < 4)
@ -85,7 +97,7 @@ namespace Ryujinx.Graphics.Gal.Shader
}
}
public static void St_A(ShaderIrBlock Block, long OpCode)
public static void St_A(ShaderIrBlock Block, long OpCode, long Position)
{
ShaderIrNode[] Opers = GetOperAbuf20(OpCode);
@ -101,7 +113,7 @@ namespace Ryujinx.Graphics.Gal.Shader
}
}
public static void Texq(ShaderIrBlock Block, long OpCode)
public static void Texq(ShaderIrBlock Block, long OpCode, long Position)
{
ShaderIrNode OperD = GetOperGpr0(OpCode);
ShaderIrNode OperA = GetOperGpr8(OpCode);
@ -120,12 +132,12 @@ namespace Ryujinx.Graphics.Gal.Shader
Block.AddNode(GetPredNode(new ShaderIrAsg(OperA, Op1), OpCode)); //Is this right?
}
public static void Tex(ShaderIrBlock Block, long OpCode)
public static void Tex(ShaderIrBlock Block, long OpCode, long Position)
{
EmitTex(Block, OpCode, GprHandle: false);
}
public static void Tex_B(ShaderIrBlock Block, long OpCode)
public static void Tex_B(ShaderIrBlock Block, long OpCode, long Position)
{
EmitTex(Block, OpCode, GprHandle: true);
}
@ -190,12 +202,12 @@ namespace Ryujinx.Graphics.Gal.Shader
}
}
public static void Texs(ShaderIrBlock Block, long OpCode)
public static void Texs(ShaderIrBlock Block, long OpCode, long Position)
{
EmitTexs(Block, OpCode, ShaderIrInst.Texs);
}
public static void Tlds(ShaderIrBlock Block, long OpCode)
public static void Tlds(ShaderIrBlock Block, long OpCode, long Position)
{
EmitTexs(Block, OpCode, ShaderIrInst.Txlf);
}

View file

@ -25,67 +25,67 @@ namespace Ryujinx.Graphics.Gal.Shader
F64 = 3
}
public static void F2f_C(ShaderIrBlock Block, long OpCode)
public static void F2f_C(ShaderIrBlock Block, long OpCode, long Position)
{
EmitF2f(Block, OpCode, ShaderOper.CR);
}
public static void F2f_I(ShaderIrBlock Block, long OpCode)
public static void F2f_I(ShaderIrBlock Block, long OpCode, long Position)
{
EmitF2f(Block, OpCode, ShaderOper.Immf);
}
public static void F2f_R(ShaderIrBlock Block, long OpCode)
public static void F2f_R(ShaderIrBlock Block, long OpCode, long Position)
{
EmitF2f(Block, OpCode, ShaderOper.RR);
}
public static void F2i_C(ShaderIrBlock Block, long OpCode)
public static void F2i_C(ShaderIrBlock Block, long OpCode, long Position)
{
EmitF2i(Block, OpCode, ShaderOper.CR);
}
public static void F2i_I(ShaderIrBlock Block, long OpCode)
public static void F2i_I(ShaderIrBlock Block, long OpCode, long Position)
{
EmitF2i(Block, OpCode, ShaderOper.Immf);
}
public static void F2i_R(ShaderIrBlock Block, long OpCode)
public static void F2i_R(ShaderIrBlock Block, long OpCode, long Position)
{
EmitF2i(Block, OpCode, ShaderOper.RR);
}
public static void I2f_C(ShaderIrBlock Block, long OpCode)
public static void I2f_C(ShaderIrBlock Block, long OpCode, long Position)
{
EmitI2f(Block, OpCode, ShaderOper.CR);
}
public static void I2f_I(ShaderIrBlock Block, long OpCode)
public static void I2f_I(ShaderIrBlock Block, long OpCode, long Position)
{
EmitI2f(Block, OpCode, ShaderOper.Imm);
}
public static void I2f_R(ShaderIrBlock Block, long OpCode)
public static void I2f_R(ShaderIrBlock Block, long OpCode, long Position)
{
EmitI2f(Block, OpCode, ShaderOper.RR);
}
public static void I2i_C(ShaderIrBlock Block, long OpCode)
public static void I2i_C(ShaderIrBlock Block, long OpCode, long Position)
{
EmitI2i(Block, OpCode, ShaderOper.CR);
}
public static void I2i_I(ShaderIrBlock Block, long OpCode)
public static void I2i_I(ShaderIrBlock Block, long OpCode, long Position)
{
EmitI2i(Block, OpCode, ShaderOper.Imm);
}
public static void I2i_R(ShaderIrBlock Block, long OpCode)
public static void I2i_R(ShaderIrBlock Block, long OpCode, long Position)
{
EmitI2i(Block, OpCode, ShaderOper.RR);
}
public static void Isberd(ShaderIrBlock Block, long OpCode)
public static void Isberd(ShaderIrBlock Block, long OpCode, long Position)
{
//This instruction seems to be used to translate from an address to a vertex index in a GS
//Stub it as such
@ -95,50 +95,50 @@ namespace Ryujinx.Graphics.Gal.Shader
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), GetOperGpr8(OpCode)), OpCode));
}
public static void Mov_C(ShaderIrBlock Block, long OpCode)
public static void Mov_C(ShaderIrBlock Block, long OpCode, long Position)
{
ShaderIrOperCbuf Cbuf = GetOperCbuf34(OpCode);
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Cbuf), OpCode));
}
public static void Mov_I(ShaderIrBlock Block, long OpCode)
public static void Mov_I(ShaderIrBlock Block, long OpCode, long Position)
{
ShaderIrOperImm Imm = GetOperImm19_20(OpCode);
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Imm), OpCode));
}
public static void Mov_I32(ShaderIrBlock Block, long OpCode)
public static void Mov_I32(ShaderIrBlock Block, long OpCode, long Position)
{
ShaderIrOperImm Imm = GetOperImm32_20(OpCode);
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Imm), OpCode));
}
public static void Mov_R(ShaderIrBlock Block, long OpCode)
public static void Mov_R(ShaderIrBlock Block, long OpCode, long Position)
{
ShaderIrOperGpr Gpr = GetOperGpr20(OpCode);
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Gpr), OpCode));
}
public static void Sel_C(ShaderIrBlock Block, long OpCode)
public static void Sel_C(ShaderIrBlock Block, long OpCode, long Position)
{
EmitSel(Block, OpCode, ShaderOper.CR);
}
public static void Sel_I(ShaderIrBlock Block, long OpCode)
public static void Sel_I(ShaderIrBlock Block, long OpCode, long Position)
{
EmitSel(Block, OpCode, ShaderOper.Imm);
}
public static void Sel_R(ShaderIrBlock Block, long OpCode)
public static void Sel_R(ShaderIrBlock Block, long OpCode, long Position)
{
EmitSel(Block, OpCode, ShaderOper.RR);
}
public static void Mov_S(ShaderIrBlock Block, long OpCode)
public static void Mov_S(ShaderIrBlock Block, long OpCode, long Position)
{
Block.AddNode(new ShaderIrCmnt("Stubbed."));

View file

@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Gal.Shader
{
static partial class ShaderDecode
{
public static void Out_R(ShaderIrBlock Block, long OpCode)
public static void Out_R(ShaderIrBlock Block, long OpCode, long Position)
{
//TODO: Those registers have to be used for something
ShaderIrOperGpr Gpr0 = GetOperGpr0(OpCode);

View file

@ -50,17 +50,29 @@ namespace Ryujinx.Graphics.Gal.Shader
{
ShaderIrNode LastNode = Current.GetLastNode();
ShaderIrOp Op = GetInnermostOp(LastNode);
ShaderIrOp InnerOp = GetInnermostOp(LastNode);
if (Op?.Inst == ShaderIrInst.Bra)
if (InnerOp?.Inst == ShaderIrInst.Bra)
{
int Offset = ((ShaderIrOperImm)Op.OperandA).Value;
int Offset = ((ShaderIrOperImm)InnerOp.OperandA).Value;
long Target = Current.EndPosition + Offset;
Current.Branch = Enqueue(Target, Current);
}
foreach (ShaderIrNode Node in Current.Nodes)
{
if (Node is ShaderIrOp CurrOp && CurrOp.Inst == ShaderIrInst.Ssy)
{
int Offset = ((ShaderIrOperImm)CurrOp.OperandA).Value;
long Target = Offset;
Current.Branch = Enqueue(Target, Current);
}
}
if (NodeHasNext(LastNode))
{
Current.Next = Enqueue(Current.EndPosition);
@ -157,7 +169,7 @@ namespace Ryujinx.Graphics.Gal.Shader
{
int Offset = ((int)(OpCode >> 20) << 8) >> 8;
long Target = Position + Offset;
long Target = Position + Offset - Beginning;
DbgOpCode += " (0x" + Target.ToString("x16") + ")";
}
@ -170,7 +182,7 @@ namespace Ryujinx.Graphics.Gal.Shader
continue;
}
Decode(Block, OpCode);
Decode(Block, OpCode, Position);
}
while (!IsFlowChange(Block.GetLastNode()));

View file

@ -1,5 +1,30 @@
namespace Ryujinx.Graphics.Gal.Shader
using System;
namespace Ryujinx.Graphics.Gal.Shader
{
struct OmapTarget
{
public bool Red;
public bool Green;
public bool Blue;
public bool Alpha;
public bool Enabled => Red || Green || Blue || Alpha;
public bool ComponentEnabled(int Component)
{
switch (Component)
{
case 0: return Red;
case 1: return Green;
case 2: return Blue;
case 3: return Alpha;
}
throw new ArgumentException(nameof(Component));
}
}
class ShaderHeader
{
public const int PointList = 1;
@ -30,6 +55,10 @@
public int StoreReqStart { get; private set; }
public int StoreReqEnd { get; private set; }
public OmapTarget[] OmapTargets { get; private set; }
public bool OmapSampleMask { get; private set; }
public bool OmapDepth { get; private set; }
public ShaderHeader(IGalMemory Memory, long Position)
{
uint CommonWord0 = (uint)Memory.ReadInt32(Position + 0);
@ -61,6 +90,50 @@
MaxOutputVertexCount = ReadBits(CommonWord4, 0, 12);
StoreReqStart = ReadBits(CommonWord4, 12, 8);
StoreReqEnd = ReadBits(CommonWord4, 24, 8);
//Type 2 (fragment?) reading
uint Type2OmapTarget = (uint)Memory.ReadInt32(Position + 72);
uint Type2Omap = (uint)Memory.ReadInt32(Position + 76);
OmapTargets = new OmapTarget[8];
for (int i = 0; i < OmapTargets.Length; i++)
{
int Offset = i * 4;
OmapTargets[i] = new OmapTarget
{
Red = ReadBits(Type2OmapTarget, Offset + 0, 1) != 0,
Green = ReadBits(Type2OmapTarget, Offset + 1, 1) != 0,
Blue = ReadBits(Type2OmapTarget, Offset + 2, 1) != 0,
Alpha = ReadBits(Type2OmapTarget, Offset + 3, 1) != 0
};
}
OmapSampleMask = ReadBits(Type2Omap, 0, 1) != 0;
OmapDepth = ReadBits(Type2Omap, 1, 1) != 0;
}
public int DepthRegister
{
get
{
int Count = 0;
for (int Index = 0; Index < OmapTargets.Length; Index++)
{
for (int Component = 0; Component < 4; Component++)
{
if (OmapTargets[Index].ComponentEnabled(Component))
{
Count++;
}
}
}
// Depth register is always two registers after the last color output
return Count + 1;
}
}
private static int ReadBits(uint Word, int Offset, int BitWidth)

View file

@ -0,0 +1,10 @@
namespace Ryujinx.Graphics.Gal.Shader
{
enum ShaderIpaMode
{
Pass = 0,
None = 1,
Constant = 2,
Sc = 3
}
}

View file

@ -84,6 +84,8 @@ namespace Ryujinx.Graphics.Gal.Shader
Bra,
Exit,
Kil,
Ssy,
Sync,
Emit,
Cut

View file

@ -0,0 +1,12 @@
namespace Ryujinx.Graphics.Gal.Shader
{
class ShaderIrMetaIpa : ShaderIrMeta
{
public ShaderIpaMode Mode { get; private set; }
public ShaderIrMetaIpa(ShaderIpaMode Mode)
{
this.Mode = Mode;
}
}
}

View file

@ -6,11 +6,18 @@ namespace Ryujinx.Graphics.Gal.Shader
public bool IsConst => Index == ZRIndex;
public bool IsValidRegister => (Index <= ZRIndex);
public int Index { get; set; }
public ShaderIrOperGpr(int Index)
{
this.Index = Index;
}
public static ShaderIrOperGpr MakeTemporary(int Index = 0)
{
return new ShaderIrOperGpr(0x100 + Index);
}
}
}

View file

@ -112,7 +112,9 @@ namespace Ryujinx.Graphics.Gal.Shader
Set("0100110000101x", ShaderDecode.Shr_C);
Set("0011100x00101x", ShaderDecode.Shr_I);
Set("0101110000101x", ShaderDecode.Shr_R);
Set("1110001010010x", ShaderDecode.Ssy);
Set("1110111111110x", ShaderDecode.St_A);
Set("1111000011111x", ShaderDecode.Sync);
Set("110000xxxx111x", ShaderDecode.Tex);
Set("1101111010111x", ShaderDecode.Tex_B);
Set("1101111101001x", ShaderDecode.Texq);

View file

@ -154,16 +154,12 @@ namespace Ryujinx.HLE.Gpu.Engines
}
else if (IsDstFb)
{
//Texture -> Frame Buffer copy.
const GalTextureFormat Format = GalTextureFormat.A8B8G8R8;
byte[] Buffer = TextureReader.Read(Vmm, SrcTexture());
Gpu.Renderer.FrameBuffer.SetBufferData(
DstKey,
DstWidth,
DstHeight,
Format,
Buffer);
}
else

View file

@ -27,6 +27,8 @@ namespace Ryujinx.HLE.Gpu.Engines
private List<long>[] UploadedKeys;
private int CurrentInstance = 0;
public NvGpuEngine3d(NvGpu Gpu)
{
this.Gpu = Gpu;
@ -102,7 +104,14 @@ namespace Ryujinx.HLE.Gpu.Engines
SetAlphaBlending(State);
SetPrimitiveRestart(State);
SetFrameBuffer(Vmm, 0);
for (int FbIndex = 0; FbIndex < 8; FbIndex++)
{
SetFrameBuffer(Vmm, 0);
}
SetZeta(Vmm);
SetRenderTargets();
long[] Keys = UploadShaders(Vmm);
@ -149,9 +158,11 @@ namespace Ryujinx.HLE.Gpu.Engines
int Stencil = ReadRegister(NvGpuEngine3dReg.ClearStencil);
SetFrameBuffer(Vmm, FbIndex);
SetZeta(Vmm);
Gpu.Renderer.Rasterizer.ClearBuffers(
Flags,
FbIndex,
Red, Green, Blue, Alpha,
Depth,
Stencil);
@ -161,6 +172,15 @@ namespace Ryujinx.HLE.Gpu.Engines
{
long VA = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + FbIndex * 0x10);
int Format = ReadRegister(NvGpuEngine3dReg.FrameBufferNFormat + FbIndex * 0x10);
if (VA == 0 || Format == 0)
{
Gpu.Renderer.FrameBuffer.UnbindColor(FbIndex);
return;
}
long Key = Vmm.GetPhysicalAddress(VA);
FrameBuffers.Add(Key);
@ -168,11 +188,11 @@ namespace Ryujinx.HLE.Gpu.Engines
int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10);
int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10);
float TX = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateX + FbIndex * 4);
float TY = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateY + FbIndex * 4);
float TX = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateX + FbIndex * 8);
float TY = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateY + FbIndex * 8);
float SX = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNScaleX + FbIndex * 4);
float SY = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNScaleY + FbIndex * 4);
float SX = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNScaleX + FbIndex * 8);
float SY = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNScaleY + FbIndex * 8);
int VpX = (int)MathF.Max(0, TX - MathF.Abs(SX));
int VpY = (int)MathF.Max(0, TY - MathF.Abs(SY));
@ -180,12 +200,48 @@ namespace Ryujinx.HLE.Gpu.Engines
int VpW = (int)(TX + MathF.Abs(SX)) - VpX;
int VpH = (int)(TY + MathF.Abs(SY)) - VpY;
Gpu.Renderer.FrameBuffer.Create(Key, Width, Height);
Gpu.Renderer.FrameBuffer.Bind(Key);
GalImageFormat ImageFormat = ImageFormatConverter.ConvertFrameBuffer((GalFrameBufferFormat)Format);
GalImage Image = new GalImage(Width, Height, ImageFormat);
long Size = TextureHelper.GetTextureSize(Image);
Gpu.Renderer.Texture.CreateFb(Key, Size, Image);
Gpu.Renderer.FrameBuffer.BindColor(Key, FbIndex);
Gpu.Renderer.FrameBuffer.SetViewport(VpX, VpY, VpW, VpH);
}
private void SetZeta(NvGpuVmm Vmm)
{
long ZA = MakeInt64From2xInt32(NvGpuEngine3dReg.ZetaAddress);
int Format = ReadRegister(NvGpuEngine3dReg.ZetaFormat);
bool ZetaEnable = (ReadRegister(NvGpuEngine3dReg.ZetaEnable) & 1) != 0;
if (ZA == 0 || Format == 0 || !ZetaEnable)
{
Gpu.Renderer.FrameBuffer.UnbindZeta();
return;
}
long Key = Vmm.GetPhysicalAddress(ZA);
int Width = ReadRegister(NvGpuEngine3dReg.ZetaHoriz);
int Height = ReadRegister(NvGpuEngine3dReg.ZetaVert);
GalImageFormat ImageFormat = ImageFormatConverter.ConvertZeta((GalZetaFormat)Format);
GalImage Image = new GalImage(Width, Height, ImageFormat);
long Size = TextureHelper.GetTextureSize(Image);
Gpu.Renderer.Texture.CreateFb(Key, Size, Image);
Gpu.Renderer.FrameBuffer.BindZeta(Key);
}
private long[] UploadShaders(NvGpuVmm Vmm)
{
long[] Keys = new long[5];
@ -366,6 +422,33 @@ namespace Ryujinx.HLE.Gpu.Engines
}
}
private void SetRenderTargets()
{
bool SeparateFragData = (ReadRegister(NvGpuEngine3dReg.RTSeparateFragData) & 1) != 0;
if (SeparateFragData)
{
uint Control = (uint)(ReadRegister(NvGpuEngine3dReg.RTControl));
uint Count = Control & 0xf;
int[] Map = new int[Count];
for (int i = 0; i < Count; i++)
{
int Shift = 4 + i * 3;
Map[i] = (int)((Control >> Shift) & 7);
}
Gpu.Renderer.FrameBuffer.SetMap(Map);
}
else
{
Gpu.Renderer.FrameBuffer.SetMap(null);
}
}
private void UploadTextures(NvGpuVmm Vmm, GalPipelineState State, long[] Keys)
{
long BaseShPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress);
@ -393,8 +476,6 @@ namespace Ryujinx.HLE.Gpu.Engines
UploadTexture(Vmm, TexIndex, TextureHandle);
Gpu.Renderer.Shader.EnsureTextureBinding(DeclInfo.Name, TexIndex);
TexIndex++;
}
}
@ -442,15 +523,15 @@ namespace Ryujinx.HLE.Gpu.Engines
}
else
{
GalTexture NewTexture = TextureFactory.MakeTexture(Vmm, TicPosition);
GalImage NewImage = TextureFactory.MakeTexture(Vmm, TicPosition);
long Size = (uint)TextureHelper.GetTextureSize(NewTexture);
long Size = (uint)TextureHelper.GetTextureSize(NewImage);
bool HasCachedTexture = false;
if (Gpu.Renderer.Texture.TryGetCachedTexture(Key, Size, out GalTexture Texture))
if (Gpu.Renderer.Texture.TryGetCachedTexture(Key, Size, out GalImage Image))
{
if (NewTexture.Equals(Texture) && !QueryKeyUpload(Vmm, Key, Size, NvGpuBufferType.Texture))
if (NewImage.Equals(Image) && !QueryKeyUpload(Vmm, Key, Size, NvGpuBufferType.Texture))
{
Gpu.Renderer.Texture.Bind(Key, TexIndex);
@ -462,7 +543,7 @@ namespace Ryujinx.HLE.Gpu.Engines
{
byte[] Data = TextureFactory.GetTextureData(Vmm, TicPosition);
Gpu.Renderer.Texture.Create(Key, Data, NewTexture);
Gpu.Renderer.Texture.Create(Key, Data, NewImage);
}
Gpu.Renderer.Texture.Bind(Key, TexIndex);
@ -575,10 +656,25 @@ namespace Ryujinx.HLE.Gpu.Engines
long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4);
long VertexEndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 2);
long VboKey = Vmm.GetPhysicalAddress(VertexPosition);
int VertexDivisor = ReadRegister(NvGpuEngine3dReg.VertexArrayNDivisor + Index * 4);
bool Instanced = (ReadRegister(NvGpuEngine3dReg.VertexArrayNInstance + Index) & 1) != 0;
int Stride = Control & 0xfff;
if (Instanced && VertexDivisor != 0)
{
VertexPosition += Stride * (CurrentInstance / VertexDivisor);
}
if (VertexPosition > VertexEndPos)
{
//Instance is invalid, ignore the draw call
continue;
}
long VboKey = Vmm.GetPhysicalAddress(VertexPosition);
long VbSize = (VertexEndPos - VertexPosition) + 1;
bool VboCached = Gpu.Renderer.Rasterizer.IsVboCached(VboKey, VbSize);
@ -590,10 +686,12 @@ namespace Ryujinx.HLE.Gpu.Engines
Gpu.Renderer.Rasterizer.CreateVbo(VboKey, (int)VbSize, DataAddress);
}
State.VertexBindings[Index].Enabled = true;
State.VertexBindings[Index].Stride = Stride;
State.VertexBindings[Index].VboKey = VboKey;
State.VertexBindings[Index].Attribs = Attribs[Index].ToArray();
State.VertexBindings[Index].Enabled = true;
State.VertexBindings[Index].Stride = Stride;
State.VertexBindings[Index].VboKey = VboKey;
State.VertexBindings[Index].Instanced = Instanced;
State.VertexBindings[Index].Divisor = VertexDivisor;
State.VertexBindings[Index].Attribs = Attribs[Index].ToArray();
}
}
@ -604,6 +702,25 @@ namespace Ryujinx.HLE.Gpu.Engines
GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff);
bool InstanceNext = ((PrimCtrl >> 26) & 1) != 0;
bool InstanceCont = ((PrimCtrl >> 27) & 1) != 0;
if (InstanceNext && InstanceCont)
{
throw new InvalidOperationException("GPU tried to increase and reset instance count at the same time");
}
if (InstanceNext)
{
CurrentInstance++;
}
else if (!InstanceCont)
{
CurrentInstance = 0;
}
State.Instance = CurrentInstance;
Gpu.Renderer.Pipeline.Bind(State);
if (IndexCount != 0)
@ -631,22 +748,43 @@ namespace Ryujinx.HLE.Gpu.Engines
WriteRegister(NvGpuEngine3dReg.IndexBatchCount, 0);
}
private enum QueryMode
{
WriteSeq,
Sync,
WriteCounterAndTimestamp
}
private void QueryControl(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
{
WriteRegister(PBEntry);
long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.QueryAddress);
int Seq = Registers[(int)NvGpuEngine3dReg.QuerySequence];
int Ctrl = Registers[(int)NvGpuEngine3dReg.QueryControl];
int Mode = Ctrl & 3;
QueryMode Mode = (QueryMode)(Ctrl & 3);
if (Mode == 0)
switch (Mode)
{
//Write mode.
Vmm.WriteInt32(Position, Seq);
}
case QueryMode.WriteSeq: Vmm.WriteInt32(Position, Seq); break;
WriteRegister(PBEntry);
case QueryMode.WriteCounterAndTimestamp:
{
//TODO: Implement counters.
long Counter = 1;
long Timestamp = (uint)Environment.TickCount;
Timestamp = (long)(Timestamp * 615384.615385);
Vmm.WriteInt64(Position + 0, Counter);
Vmm.WriteInt64(Position + 8, Timestamp);
break;
}
}
}
private void CbData(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
@ -754,4 +892,4 @@ namespace Ryujinx.HLE.Gpu.Engines
return Vmm.IsRegionModified(Key, Size, Type);
}
}
}
}

View file

@ -22,7 +22,16 @@ namespace Ryujinx.HLE.Gpu.Engines
StencilBackFuncRef = 0x3d5,
StencilBackMask = 0x3d6,
StencilBackFuncMask = 0x3d7,
RTSeparateFragData = 0x3eb,
ZetaAddress = 0x3f8,
ZetaFormat = 0x3fa,
ZetaBlockDimensions = 0x3fb,
ZetaLayerStride = 0x3fc,
VertexAttribNFormat = 0x458,
RTControl = 0x487,
ZetaHoriz = 0x48a,
ZetaVert = 0x48b,
ZetaArrayMode = 0x48c,
DepthTestEnable = 0x4b3,
IBlendEnable = 0x4b9,
DepthTestFunction = 0x4c3,
@ -44,6 +53,8 @@ namespace Ryujinx.HLE.Gpu.Engines
StencilFrontFuncMask = 0x4e6,
StencilFrontMask = 0x4e7,
VertexArrayElemBase = 0x50d,
VertexArrayInstBase = 0x50e,
ZetaEnable = 0x54e,
TexHeaderPoolOffset = 0x55d,
TexSamplerPoolOffset = 0x557,
StencilTwoSideEnable = 0x565,
@ -60,6 +71,7 @@ namespace Ryujinx.HLE.Gpu.Engines
IndexArrayFormat = 0x5f6,
IndexBatchFirst = 0x5f7,
IndexBatchCount = 0x5f8,
VertexArrayNInstance = 0x620,
CullFaceEnable = 0x646,
FrontFace = 0x647,
CullFace = 0x648,

View file

@ -6,11 +6,16 @@ namespace Ryujinx.HLE.Gpu.Texture
{
static class TextureFactory
{
public static GalTexture MakeTexture(NvGpuVmm Vmm, long TicPosition)
public static GalImage MakeTexture(NvGpuVmm Vmm, long TicPosition)
{
int[] Tic = ReadWords(Vmm, TicPosition, 8);
GalTextureFormat Format = (GalTextureFormat)(Tic[0] & 0x7f);
GalTextureType RType = (GalTextureType)((Tic[0] >> 7) & 7);
GalTextureType GType = (GalTextureType)((Tic[0] >> 10) & 7);
GalTextureType BType = (GalTextureType)((Tic[0] >> 13) & 7);
GalTextureType AType = (GalTextureType)((Tic[0] >> 16) & 7);
GalImageFormat Format = ImageFormatConverter.ConvertTexture((GalTextureFormat)(Tic[0] & 0x7f), RType, GType, BType, AType);
GalTextureSource XSource = (GalTextureSource)((Tic[0] >> 19) & 7);
GalTextureSource YSource = (GalTextureSource)((Tic[0] >> 22) & 7);
@ -20,7 +25,7 @@ namespace Ryujinx.HLE.Gpu.Texture
int Width = (Tic[4] & 0xffff) + 1;
int Height = (Tic[5] & 0xffff) + 1;
return new GalTexture(
return new GalImage(
Width,
Height,
Format,

View file

@ -30,117 +30,155 @@ namespace Ryujinx.HLE.Gpu.Texture
throw new NotImplementedException(Texture.Swizzle.ToString());
}
public static int GetTextureSize(GalTexture Texture)
public static int GetTextureSize(GalImage Image)
{
switch (Texture.Format)
switch (Image.Format)
{
case GalTextureFormat.R32G32B32A32:
return Texture.Width * Texture.Height * 16;
case GalImageFormat.R32G32B32A32_SFLOAT:
case GalImageFormat.R32G32B32A32_SINT:
case GalImageFormat.R32G32B32A32_UINT:
return Image.Width * Image.Height * 16;
case GalTextureFormat.R16G16B16A16:
return Texture.Width * Texture.Height * 8;
case GalImageFormat.R16G16B16A16_SFLOAT:
case GalImageFormat.R16G16B16A16_SINT:
case GalImageFormat.R16G16B16A16_SNORM:
case GalImageFormat.R16G16B16A16_UINT:
case GalImageFormat.R16G16B16A16_UNORM:
case GalImageFormat.D32_SFLOAT_S8_UINT:
case GalImageFormat.R32G32_SFLOAT:
case GalImageFormat.R32G32_SINT:
case GalImageFormat.R32G32_UINT:
return Image.Width * Image.Height * 8;
case GalTextureFormat.A8B8G8R8:
case GalTextureFormat.A2B10G10R10:
case GalTextureFormat.R32:
case GalTextureFormat.ZF32:
case GalTextureFormat.BF10GF11RF11:
case GalTextureFormat.Z24S8:
return Texture.Width * Texture.Height * 4;
case GalImageFormat.A8B8G8R8_SINT_PACK32:
case GalImageFormat.A8B8G8R8_SNORM_PACK32:
case GalImageFormat.A8B8G8R8_UINT_PACK32:
case GalImageFormat.A8B8G8R8_UNORM_PACK32:
case GalImageFormat.A8B8G8R8_SRGB_PACK32:
case GalImageFormat.A2B10G10R10_SINT_PACK32:
case GalImageFormat.A2B10G10R10_SNORM_PACK32:
case GalImageFormat.A2B10G10R10_UINT_PACK32:
case GalImageFormat.A2B10G10R10_UNORM_PACK32:
case GalImageFormat.R16G16_SFLOAT:
case GalImageFormat.R16G16_SINT:
case GalImageFormat.R16G16_SNORM:
case GalImageFormat.R16G16_UINT:
case GalImageFormat.R16G16_UNORM:
case GalImageFormat.R32_SFLOAT:
case GalImageFormat.R32_SINT:
case GalImageFormat.R32_UINT:
case GalImageFormat.D32_SFLOAT:
case GalImageFormat.B10G11R11_UFLOAT_PACK32:
case GalImageFormat.D24_UNORM_S8_UINT:
return Image.Width * Image.Height * 4;
case GalTextureFormat.A1B5G5R5:
case GalTextureFormat.B5G6R5:
case GalTextureFormat.G8R8:
case GalTextureFormat.R16:
return Texture.Width * Texture.Height * 2;
case GalImageFormat.B4G4R4A4_UNORM_PACK16:
case GalImageFormat.A1R5G5B5_UNORM_PACK16:
case GalImageFormat.B5G6R5_UNORM_PACK16:
case GalImageFormat.R8G8_SINT:
case GalImageFormat.R8G8_SNORM:
case GalImageFormat.R8G8_UINT:
case GalImageFormat.R8G8_UNORM:
case GalImageFormat.R16_SFLOAT:
case GalImageFormat.R16_SINT:
case GalImageFormat.R16_SNORM:
case GalImageFormat.R16_UINT:
case GalImageFormat.R16_UNORM:
case GalImageFormat.D16_UNORM:
return Image.Width * Image.Height * 2;
case GalTextureFormat.R8:
return Texture.Width * Texture.Height;
case GalImageFormat.R8_SINT:
case GalImageFormat.R8_SNORM:
case GalImageFormat.R8_UINT:
case GalImageFormat.R8_UNORM:
return Image.Width * Image.Height;
case GalTextureFormat.BC1:
case GalTextureFormat.BC4:
case GalImageFormat.BC1_RGBA_UNORM_BLOCK:
case GalImageFormat.BC4_SNORM_BLOCK:
case GalImageFormat.BC4_UNORM_BLOCK:
{
return CompressedTextureSize(Texture.Width, Texture.Height, 4, 4, 8);
return CompressedTextureSize(Image.Width, Image.Height, 4, 4, 8);
}
case GalTextureFormat.BC6H_SF16:
case GalTextureFormat.BC6H_UF16:
case GalTextureFormat.BC7U:
case GalTextureFormat.BC2:
case GalTextureFormat.BC3:
case GalTextureFormat.BC5:
case GalTextureFormat.Astc2D4x4:
case GalImageFormat.BC6H_SFLOAT_BLOCK:
case GalImageFormat.BC6H_UFLOAT_BLOCK:
case GalImageFormat.BC7_UNORM_BLOCK:
case GalImageFormat.BC2_UNORM_BLOCK:
case GalImageFormat.BC3_UNORM_BLOCK:
case GalImageFormat.BC5_SNORM_BLOCK:
case GalImageFormat.BC5_UNORM_BLOCK:
case GalImageFormat.ASTC_4x4_UNORM_BLOCK:
{
return CompressedTextureSize(Texture.Width, Texture.Height, 4, 4, 16);
return CompressedTextureSize(Image.Width, Image.Height, 4, 4, 16);
}
case GalTextureFormat.Astc2D5x5:
case GalImageFormat.ASTC_5x5_UNORM_BLOCK:
{
return CompressedTextureSize(Texture.Width, Texture.Height, 5, 5, 16);
return CompressedTextureSize(Image.Width, Image.Height, 5, 5, 16);
}
case GalTextureFormat.Astc2D6x6:
case GalImageFormat.ASTC_6x6_UNORM_BLOCK:
{
return CompressedTextureSize(Texture.Width, Texture.Height, 6, 6, 16);
return CompressedTextureSize(Image.Width, Image.Height, 6, 6, 16);
}
case GalTextureFormat.Astc2D8x8:
case GalImageFormat.ASTC_8x8_UNORM_BLOCK:
{
return CompressedTextureSize(Texture.Width, Texture.Height, 8, 8, 16);
return CompressedTextureSize(Image.Width, Image.Height, 8, 8, 16);
}
case GalTextureFormat.Astc2D10x10:
case GalImageFormat.ASTC_10x10_UNORM_BLOCK:
{
return CompressedTextureSize(Texture.Width, Texture.Height, 10, 10, 16);
return CompressedTextureSize(Image.Width, Image.Height, 10, 10, 16);
}
case GalTextureFormat.Astc2D12x12:
case GalImageFormat.ASTC_12x12_UNORM_BLOCK:
{
return CompressedTextureSize(Texture.Width, Texture.Height, 12, 12, 16);
return CompressedTextureSize(Image.Width, Image.Height, 12, 12, 16);
}
case GalTextureFormat.Astc2D5x4:
case GalImageFormat.ASTC_5x4_UNORM_BLOCK:
{
return CompressedTextureSize(Texture.Width, Texture.Height, 5, 4, 16);
return CompressedTextureSize(Image.Width, Image.Height, 5, 4, 16);
}
case GalTextureFormat.Astc2D6x5:
case GalImageFormat.ASTC_6x5_UNORM_BLOCK:
{
return CompressedTextureSize(Texture.Width, Texture.Height, 6, 5, 16);
return CompressedTextureSize(Image.Width, Image.Height, 6, 5, 16);
}
case GalTextureFormat.Astc2D8x6:
case GalImageFormat.ASTC_8x6_UNORM_BLOCK:
{
return CompressedTextureSize(Texture.Width, Texture.Height, 8, 6, 16);
return CompressedTextureSize(Image.Width, Image.Height, 8, 6, 16);
}
case GalTextureFormat.Astc2D10x8:
case GalImageFormat.ASTC_10x8_UNORM_BLOCK:
{
return CompressedTextureSize(Texture.Width, Texture.Height, 10, 8, 16);
return CompressedTextureSize(Image.Width, Image.Height, 10, 8, 16);
}
case GalTextureFormat.Astc2D12x10:
case GalImageFormat.ASTC_12x10_UNORM_BLOCK:
{
return CompressedTextureSize(Texture.Width, Texture.Height, 12, 10, 16);
return CompressedTextureSize(Image.Width, Image.Height, 12, 10, 16);
}
case GalTextureFormat.Astc2D8x5:
case GalImageFormat.ASTC_8x5_UNORM_BLOCK:
{
return CompressedTextureSize(Texture.Width, Texture.Height, 8, 5, 16);
return CompressedTextureSize(Image.Width, Image.Height, 8, 5, 16);
}
case GalTextureFormat.Astc2D10x5:
case GalImageFormat.ASTC_10x5_UNORM_BLOCK:
{
return CompressedTextureSize(Texture.Width, Texture.Height, 10, 5, 16);
return CompressedTextureSize(Image.Width, Image.Height, 10, 5, 16);
}
case GalTextureFormat.Astc2D10x6:
case GalImageFormat.ASTC_10x6_UNORM_BLOCK:
{
return CompressedTextureSize(Texture.Width, Texture.Height, 10, 6, 16);
return CompressedTextureSize(Image.Width, Image.Height, 10, 6, 16);
}
}
throw new NotImplementedException("0x" + Texture.Format.ToString("x2"));
throw new NotImplementedException(Image.Format.ToString());
}
public static int CompressedTextureSize(int TextureWidth, int TextureHeight, int BlockWidth, int BlockHeight, int Bpb)

View file

@ -12,6 +12,7 @@ namespace Ryujinx.HLE.Gpu.Texture
{
case GalTextureFormat.R32G32B32A32: return Read16Bpp (Memory, Texture);
case GalTextureFormat.R16G16B16A16: return Read8Bpp (Memory, Texture);
case GalTextureFormat.R32G32: return Read8Bpp (Memory, Texture);
case GalTextureFormat.A8B8G8R8: return Read4Bpp (Memory, Texture);
case GalTextureFormat.A2B10G10R10: return Read4Bpp (Memory, Texture);
case GalTextureFormat.R32: return Read4Bpp (Memory, Texture);
@ -19,6 +20,7 @@ namespace Ryujinx.HLE.Gpu.Texture
case GalTextureFormat.Z24S8: return Read4Bpp (Memory, Texture);
case GalTextureFormat.A1B5G5R5: return Read5551 (Memory, Texture);
case GalTextureFormat.B5G6R5: return Read565 (Memory, Texture);
case GalTextureFormat.A4B4G4R4: return Read2Bpp (Memory, Texture);
case GalTextureFormat.G8R8: return Read2Bpp (Memory, Texture);
case GalTextureFormat.R16: return Read2Bpp (Memory, Texture);
case GalTextureFormat.R8: return Read1Bpp (Memory, Texture);
@ -31,6 +33,7 @@ namespace Ryujinx.HLE.Gpu.Texture
case GalTextureFormat.BC4: return Read8Bpt4x4 (Memory, Texture);
case GalTextureFormat.BC5: return Read16BptCompressedTexture(Memory, Texture, 4, 4);
case GalTextureFormat.ZF32: return Read4Bpp (Memory, Texture);
case GalTextureFormat.ZF32_X24S8: return Read8Bpp (Memory, Texture);
case GalTextureFormat.Astc2D4x4: return Read16BptCompressedTexture(Memory, Texture, 4, 4);
case GalTextureFormat.Astc2D5x5: return Read16BptCompressedTexture(Memory, Texture, 5, 5);
case GalTextureFormat.Astc2D6x6: return Read16BptCompressedTexture(Memory, Texture, 6, 6);
@ -47,7 +50,7 @@ namespace Ryujinx.HLE.Gpu.Texture
case GalTextureFormat.Astc2D10x6: return Read16BptCompressedTexture(Memory, Texture, 10, 6);
}
throw new NotImplementedException("0x" + Texture.Format.ToString("x2"));
throw new NotImplementedException("0x" + ((int)Texture.Format).ToString("x2"));
}
private unsafe static byte[] Read1Bpp(IAMemory Memory, TextureInfo Texture)

View file

@ -174,39 +174,39 @@ namespace Ryujinx.HLE.HOS.Ipc
return 0;
}
public (long Position, long Size) GetBufferType0x21()
public (long Position, long Size) GetBufferType0x21(int Index = 0)
{
if (PtrBuff.Count != 0 &&
PtrBuff[0].Position != 0 &&
PtrBuff[0].Size != 0)
if (PtrBuff.Count > Index &&
PtrBuff[Index].Position != 0 &&
PtrBuff[Index].Size != 0)
{
return (PtrBuff[0].Position, PtrBuff[0].Size);
return (PtrBuff[Index].Position, PtrBuff[Index].Size);
}
if (SendBuff.Count != 0 &&
SendBuff[0].Position != 0 &&
SendBuff[0].Size != 0)
if (SendBuff.Count > Index &&
SendBuff[Index].Position != 0 &&
SendBuff[Index].Size != 0)
{
return (SendBuff[0].Position, SendBuff[0].Size);
return (SendBuff[Index].Position, SendBuff[Index].Size);
}
return (0, 0);
}
public (long Position, long Size) GetBufferType0x22()
public (long Position, long Size) GetBufferType0x22(int Index = 0)
{
if (RecvListBuff.Count != 0 &&
RecvListBuff[0].Position != 0 &&
RecvListBuff[0].Size != 0)
if (RecvListBuff.Count > Index &&
RecvListBuff[Index].Position != 0 &&
RecvListBuff[Index].Size != 0)
{
return (RecvListBuff[0].Position, RecvListBuff[0].Size);
return (RecvListBuff[Index].Position, RecvListBuff[Index].Size);
}
if (ReceiveBuff.Count != 0 &&
ReceiveBuff[0].Position != 0 &&
ReceiveBuff[0].Size != 0)
if (ReceiveBuff.Count > Index &&
ReceiveBuff[Index].Position != 0 &&
ReceiveBuff[Index].Size != 0)
{
return (ReceiveBuff[0].Position, ReceiveBuff[0].Size);
return (ReceiveBuff[Index].Position, ReceiveBuff[Index].Size);
}
return (0, 0);

View file

@ -9,9 +9,9 @@ namespace Ryujinx.HLE.HOS.Kernel
{
private void SvcSetHeapSize(AThreadState ThreadState)
{
long Size = (long)ThreadState.X1;
ulong Size = ThreadState.X1;
if ((Size & 0x1fffff) != 0 || Size != (uint)Size)
if ((Size & 0xFFFFFFFE001FFFFF) != 0)
{
Device.Log.PrintWarning(LogClass.KernelSvc, $"Heap size 0x{Size:x16} is not aligned!");
@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
long Result = Process.MemoryManager.TrySetHeapSize(Size, out long Position);
long Result = Process.MemoryManager.TrySetHeapSize((long)Size, out long Position);
ThreadState.X0 = (ulong)Result;

View file

@ -7,13 +7,13 @@ using static Ryujinx.HLE.HOS.ErrorCode;
namespace Ryujinx.HLE.HOS.Services.Acc
{
class IAccountServiceForApplication : IpcService
class IAccountService : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IAccountServiceForApplication()
public IAccountService()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{

View file

@ -3,7 +3,10 @@ using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Logging;
using Ryujinx.HLE.Utilities;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
namespace Ryujinx.HLE.HOS.Services.Acc
@ -16,15 +19,21 @@ namespace Ryujinx.HLE.HOS.Services.Acc
private UserProfile Profile;
private Stream ProfilePictureStream;
public IProfile(UserProfile Profile)
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, Get },
{ 1, GetBase }
{ 0, Get },
{ 1, GetBase },
{ 10, GetImageSize },
{ 11, LoadImage },
};
this.Profile = Profile;
ProfilePictureStream = Assembly.GetCallingAssembly().GetManifestResourceStream("Ryujinx.HLE.RyujinxProfileImage.jpg");
}
public long Get(ServiceCtx Context)
@ -54,5 +63,28 @@ namespace Ryujinx.HLE.HOS.Services.Acc
return 0;
}
private long LoadImage(ServiceCtx Context)
{
long BufferPosition = Context.Request.ReceiveBuff[0].Position;
long BufferLen = Context.Request.ReceiveBuff[0].Size;
byte[] ProfilePictureData = new byte[BufferLen];
ProfilePictureStream.Read(ProfilePictureData, 0, ProfilePictureData.Length);
Context.Memory.WriteBytes(BufferPosition, ProfilePictureData);
Context.ResponseData.Write(ProfilePictureStream.Length);
return 0;
}
private long GetImageSize(ServiceCtx Context)
{
Context.ResponseData.Write(ProfilePictureStream.Length);
return 0;
}
}
}

View file

@ -32,6 +32,8 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
{ 14, Connect },
{ 18, Listen },
{ 21, SetSockOpt },
{ 24, Write },
{ 25, Read },
{ 26, Close }
};
}
@ -122,15 +124,15 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
int SocketId = Context.RequestData.ReadInt32();
int SocketFlags = Context.RequestData.ReadInt32();
byte[] ReceivedBuffer = new byte[Context.Request.ReceiveBuff[0].Size];
(long ReceivePosition, long ReceiveLength) = Context.Request.GetBufferType0x22();
byte[] ReceivedBuffer = new byte[ReceiveLength];
try
{
int BytesRead = Sockets[SocketId].Handle.Receive(ReceivedBuffer);
//Logging.Debug("Received Buffer:" + Environment.NewLine + Logging.HexDump(ReceivedBuffer));
Context.Memory.WriteBytes(Context.Request.ReceiveBuff[0].Position, ReceivedBuffer);
Context.Memory.WriteBytes(ReceivePosition, ReceivedBuffer);
Context.ResponseData.Write(BytesRead);
Context.ResponseData.Write(0);
@ -150,13 +152,12 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
int SocketId = Context.RequestData.ReadInt32();
int SocketFlags = Context.RequestData.ReadInt32();
byte[] SentBuffer = Context.Memory.ReadBytes(Context.Request.SendBuff[0].Position,
Context.Request.SendBuff[0].Size);
(long SentPosition, long SentSize) = Context.Request.GetBufferType0x21();
byte[] SentBuffer = Context.Memory.ReadBytes(SentPosition, SentSize);
try
{
//Logging.Debug("Sent Buffer:" + Environment.NewLine + Logging.HexDump(SentBuffer));
int BytesSent = Sockets[SocketId].Handle.Send(SentBuffer);
Context.ResponseData.Write(BytesSent);
@ -180,8 +181,9 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
byte[] SentBuffer = Context.Memory.ReadBytes(Context.Request.SendBuff[0].Position,
Context.Request.SendBuff[0].Size);
byte[] AddressBuffer = Context.Memory.ReadBytes(Context.Request.SendBuff[1].Position,
Context.Request.SendBuff[1].Size);
(long AddressPosition, long AddressSize) = Context.Request.GetBufferType0x21(Index: 1);
byte[] AddressBuffer = Context.Memory.ReadBytes(AddressPosition, AddressSize);
if (!Sockets[SocketId].Handle.Connected)
{
@ -200,8 +202,6 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
try
{
//Logging.Debug("Sent Buffer:" + Environment.NewLine + Logging.HexDump(SentBuffer));
int BytesSent = Sockets[SocketId].Handle.Send(SentBuffer);
Context.ResponseData.Write(BytesSent);
@ -221,7 +221,7 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
{
int SocketId = Context.RequestData.ReadInt32();
long AddrBufferPtr = Context.Request.ReceiveBuff[0].Position;
(long AddrBufferPosition, long AddrBuffSize) = Context.Request.GetBufferType0x22();
Socket HandleAccept = null;
@ -246,7 +246,7 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
{
IpAddress = ((IPEndPoint)Sockets[SocketId].Handle.LocalEndPoint).Address,
RemoteEP = ((IPEndPoint)Sockets[SocketId].Handle.LocalEndPoint),
Handle = HandleAccept
Handle = HandleAccept
};
Sockets.Add(NewBsdSocket);
@ -265,7 +265,7 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
Writer.Write(IpAddress);
Context.Memory.WriteBytes(AddrBufferPtr, MS.ToArray());
Context.Memory.WriteBytes(AddrBufferPosition, MS.ToArray());
Context.ResponseData.Write(Sockets.Count - 1);
Context.ResponseData.Write(0);
@ -286,8 +286,9 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
{
int SocketId = Context.RequestData.ReadInt32();
byte[] AddressBuffer = Context.Memory.ReadBytes(Context.Request.SendBuff[0].Position,
Context.Request.SendBuff[0].Size);
(long AddressPosition, long AddressSize) = Context.Request.GetBufferType0x21();
byte[] AddressBuffer = Context.Memory.ReadBytes(AddressPosition, AddressSize);
try
{
@ -310,8 +311,9 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
{
int SocketId = Context.RequestData.ReadInt32();
byte[] AddressBuffer = Context.Memory.ReadBytes(Context.Request.SendBuff[0].Position,
Context.Request.SendBuff[0].Size);
(long AddressPosition, long AddressSize) = Context.Request.GetBufferType0x21();
byte[] AddressBuffer = Context.Memory.ReadBytes(AddressPosition, AddressSize);
try
{
@ -359,8 +361,8 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
{
int SocketId = Context.RequestData.ReadInt32();
SocketOptionLevel SocketLevel = (SocketOptionLevel)Context.RequestData.ReadInt32();
SocketOptionName SocketOptionName = (SocketOptionName)Context.RequestData.ReadInt32();
SocketOptionLevel SocketLevel = (SocketOptionLevel)Context.RequestData.ReadInt32();
SocketOptionName SocketOptionName = (SocketOptionName)Context.RequestData.ReadInt32();
byte[] SocketOptionValue = Context.Memory.ReadBytes(Context.Request.PtrBuff[0].Position,
Context.Request.PtrBuff[0].Size);
@ -383,6 +385,60 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
return 0;
}
//(u32 socket, buffer<i8, 0x21, 0> message) -> (i32 ret, u32 bsd_errno)
public long Write(ServiceCtx Context)
{
int SocketId = Context.RequestData.ReadInt32();
(long SentPosition, long SentSize) = Context.Request.GetBufferType0x21();
byte[] SentBuffer = Context.Memory.ReadBytes(SentPosition, SentSize);
try
{
//Logging.Debug("Wrote Buffer:" + Environment.NewLine + Logging.HexDump(SentBuffer));
int BytesSent = Sockets[SocketId].Handle.Send(SentBuffer);
Context.ResponseData.Write(BytesSent);
Context.ResponseData.Write(0);
}
catch (SocketException Ex)
{
Context.ResponseData.Write(-1);
Context.ResponseData.Write(Ex.ErrorCode - 10000);
}
return 0;
}
//(u32 socket) -> (i32 ret, u32 bsd_errno, buffer<i8, 0x22, 0> message)
public long Read(ServiceCtx Context)
{
int SocketId = Context.RequestData.ReadInt32();
(long ReceivePosition, long ReceiveLength) = Context.Request.GetBufferType0x22();
byte[] ReceivedBuffer = new byte[ReceiveLength];
try
{
int BytesRead = Sockets[SocketId].Handle.Receive(ReceivedBuffer);
Context.Memory.WriteBytes(ReceivePosition, ReceivedBuffer);
Context.ResponseData.Write(BytesRead);
Context.ResponseData.Write(0);
}
catch (SocketException Ex)
{
Context.ResponseData.Write(-1);
Context.ResponseData.Write(Ex.ErrorCode - 10000);
}
return 0;
}
//(u32 socket) -> (i32 ret, u32 bsd_errno)
public long Close(ServiceCtx Context)
{
@ -413,7 +469,7 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
int Size = Reader.ReadByte();
int Family = Reader.ReadByte();
int Port = EndianSwap.Swap16(Reader.ReadInt16());
int Port = EndianSwap.Swap16(Reader.ReadUInt16());
string IpAddress = Reader.ReadByte().ToString() + "." +
Reader.ReadByte().ToString() + "." +
@ -421,8 +477,7 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
Reader.ReadByte().ToString();
Sockets[SocketId].IpAddress = IPAddress.Parse(IpAddress);
Sockets[SocketId].RemoteEP = new IPEndPoint(Sockets[SocketId].IpAddress, Port);
Sockets[SocketId].RemoteEP = new IPEndPoint(Sockets[SocketId].IpAddress, Port);
}
}

View file

@ -22,9 +22,9 @@ namespace Ryujinx.HLE.HOS.Services.Lm
public long Log(ServiceCtx Context)
{
byte[] LogBuffer = Context.Memory.ReadBytes(
Context.Request.PtrBuff[0].Position,
Context.Request.PtrBuff[0].Size);
(long BufPos, long BufSize) = Context.Request.GetBufferType0x21();
byte[] LogBuffer = Context.Memory.ReadBytes(BufPos, BufSize);
using (MemoryStream MS = new MemoryStream(LogBuffer))
{
@ -50,20 +50,36 @@ namespace Ryujinx.HLE.HOS.Services.Lm
string FieldStr = string.Empty;
if (Field == LmLogField.Skip)
if (Field == LmLogField.Start)
{
Reader.ReadByte();
Reader.ReadBytes(Size);
continue;
}
else if (Field == LmLogField.Stop)
{
break;
}
else if (Field == LmLogField.Line)
{
FieldStr = Field + ": " + Reader.ReadInt32();
}
else
else if (Field == LmLogField.DropCount)
{
FieldStr = Field + ": " + Reader.ReadInt64();
}
else if (Field == LmLogField.Time)
{
FieldStr = Field + ": " + Reader.ReadInt64() + "s";
}
else if (Field < LmLogField.Count)
{
FieldStr = Field + ": \"" + Encoding.UTF8.GetString(Reader.ReadBytes(Size)) + "\"";
}
else
{
FieldStr = "Field" + Field + ": \"" + Encoding.UTF8.GetString(Reader.ReadBytes(Size)) + "\"";
}
SB.AppendLine(" " + FieldStr);
}

View file

@ -2,12 +2,17 @@ namespace Ryujinx.HLE.HOS.Services.Lm
{
enum LmLogField
{
Skip = 1,
Message = 2,
Line = 3,
Filename = 4,
Function = 5,
Module = 6,
Thread = 7
Start = 0,
Stop = 1,
Message = 2,
Line = 3,
Filename = 4,
Function = 5,
Module = 6,
Thread = 7,
DropCount = 8,
Time = 9,
ProgramName = 10,
Count
}
}

View file

@ -187,7 +187,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS
{
Left = Middle + 1;
LtRg = Rg;
if ((ulong)Position > Rg.Start)
{
LtRg = Rg;
}
}
}

View file

@ -31,7 +31,10 @@ namespace Ryujinx.HLE.HOS.Services
switch (Name)
{
case "acc:u0":
return new IAccountServiceForApplication();
return new IAccountService();
case "acc:u1":
return new IAccountService();
case "aoc:u":
return new IAddOnContentManager();

View file

@ -13,6 +13,14 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<None Remove="RyujinxProfileImage.jpg" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="RyujinxProfileImage.jpg" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View file

@ -2,7 +2,7 @@
{
static class EndianSwap
{
public static short Swap16(short Value) => (short)(((Value >> 8) & 0xff) | (Value << 8));
public static ushort Swap16(ushort Value) => (ushort)(((Value >> 8) & 0xff) | (Value << 8));
public static int Swap32(int Value)
{

View file

@ -0,0 +1,28 @@
using System;
namespace Ryujinx.Tests.Unicorn
{
public class IndexedProperty<TIndex, TValue>
{
readonly Action<TIndex, TValue> SetAction;
readonly Func<TIndex, TValue> GetFunc;
public IndexedProperty(Func<TIndex, TValue> getFunc, Action<TIndex, TValue> setAction)
{
this.GetFunc = getFunc;
this.SetAction = setAction;
}
public TValue this[TIndex i]
{
get
{
return GetFunc(i);
}
set
{
SetAction(i, value);
}
}
}
}

View file

@ -0,0 +1,13 @@
using System;
namespace Ryujinx.Tests.Unicorn
{
public enum MemoryPermission
{
NONE = 0,
READ = 1,
WRITE = 2,
EXEC = 4,
ALL = 7,
}
}

View file

@ -0,0 +1,296 @@
using System;
namespace Ryujinx.Tests.Unicorn.Native
{
public enum ArmRegister
{
INVALID = 0,
X29,
X30,
NZCV,
SP,
WSP,
WZR,
XZR,
B0,
B1,
B2,
B3,
B4,
B5,
B6,
B7,
B8,
B9,
B10,
B11,
B12,
B13,
B14,
B15,
B16,
B17,
B18,
B19,
B20,
B21,
B22,
B23,
B24,
B25,
B26,
B27,
B28,
B29,
B30,
B31,
D0,
D1,
D2,
D3,
D4,
D5,
D6,
D7,
D8,
D9,
D10,
D11,
D12,
D13,
D14,
D15,
D16,
D17,
D18,
D19,
D20,
D21,
D22,
D23,
D24,
D25,
D26,
D27,
D28,
D29,
D30,
D31,
H0,
H1,
H2,
H3,
H4,
H5,
H6,
H7,
H8,
H9,
H10,
H11,
H12,
H13,
H14,
H15,
H16,
H17,
H18,
H19,
H20,
H21,
H22,
H23,
H24,
H25,
H26,
H27,
H28,
H29,
H30,
H31,
Q0,
Q1,
Q2,
Q3,
Q4,
Q5,
Q6,
Q7,
Q8,
Q9,
Q10,
Q11,
Q12,
Q13,
Q14,
Q15,
Q16,
Q17,
Q18,
Q19,
Q20,
Q21,
Q22,
Q23,
Q24,
Q25,
Q26,
Q27,
Q28,
Q29,
Q30,
Q31,
S0,
S1,
S2,
S3,
S4,
S5,
S6,
S7,
S8,
S9,
S10,
S11,
S12,
S13,
S14,
S15,
S16,
S17,
S18,
S19,
S20,
S21,
S22,
S23,
S24,
S25,
S26,
S27,
S28,
S29,
S30,
S31,
W0,
W1,
W2,
W3,
W4,
W5,
W6,
W7,
W8,
W9,
W10,
W11,
W12,
W13,
W14,
W15,
W16,
W17,
W18,
W19,
W20,
W21,
W22,
W23,
W24,
W25,
W26,
W27,
W28,
W29,
W30,
X0,
X1,
X2,
X3,
X4,
X5,
X6,
X7,
X8,
X9,
X10,
X11,
X12,
X13,
X14,
X15,
X16,
X17,
X18,
X19,
X20,
X21,
X22,
X23,
X24,
X25,
X26,
X27,
X28,
V0,
V1,
V2,
V3,
V4,
V5,
V6,
V7,
V8,
V9,
V10,
V11,
V12,
V13,
V14,
V15,
V16,
V17,
V18,
V19,
V20,
V21,
V22,
V23,
V24,
V25,
V26,
V27,
V28,
V29,
V30,
V31,
//> pseudo registers
PC, // program counter register
CPACR_EL1,
ESR,
//> thread registers
TPIDR_EL0,
TPIDRRO_EL0,
TPIDR_EL1,
PSTATE, // PSTATE pseudoregister
//> floating point control and status registers
FPCR,
FPSR,
ENDING, // <-- mark the end of the list of registers
//> alias registers
IP0 = X16,
IP1 = X17,
FP = X29,
LR = X30,
}
}

View file

@ -0,0 +1,68 @@
using System;
using System.Runtime.InteropServices;
using Ryujinx.Tests.Unicorn;
namespace Ryujinx.Tests.Unicorn.Native
{
public class Interface
{
public static void Checked(UnicornError error)
{
if (error != UnicornError.UC_ERR_OK)
{
throw new UnicornException(error);
}
}
public static void MarshalArrayOf<T>(IntPtr input, int length, out T[] output)
{
var size = Marshal.SizeOf(typeof(T));
output = new T[length];
for (int i = 0; i < length; i++)
{
IntPtr item = new IntPtr(input.ToInt64() + i * size);
output[i] = Marshal.PtrToStructure<T>(item);
}
}
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
public static extern uint uc_version(out uint major, out uint minor);
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
public static extern UnicornError uc_open(uint arch, uint mode, out IntPtr uc);
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
public static extern UnicornError uc_close(IntPtr uc);
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr uc_strerror(UnicornError err);
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
public static extern UnicornError uc_reg_write(IntPtr uc, int regid, byte[] value);
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
public static extern UnicornError uc_reg_read(IntPtr uc, int regid, byte[] value);
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
public static extern UnicornError uc_mem_write(IntPtr uc, ulong address, byte[] bytes, ulong size);
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
public static extern UnicornError uc_mem_read(IntPtr uc, ulong address, byte[] bytes, ulong size);
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
public static extern UnicornError uc_emu_start(IntPtr uc, ulong begin, ulong until, ulong timeout, ulong count);
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
public static extern UnicornError uc_mem_map(IntPtr uc, ulong address, ulong size, uint perms);
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
public static extern UnicornError uc_mem_unmap(IntPtr uc, ulong address, ulong size);
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
public static extern UnicornError uc_mem_protect(IntPtr uc, ulong address, ulong size, uint perms);
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
public static extern UnicornError uc_mem_regions(IntPtr uc, out IntPtr regions, out uint count);
}
}

View file

@ -0,0 +1,16 @@
using System;
namespace Ryujinx.Tests.Unicorn.Native
{
public enum UnicornArch
{
UC_ARCH_ARM = 1, // ARM architecture (including Thumb, Thumb-2)
UC_ARCH_ARM64, // ARM-64, also called AArch64
UC_ARCH_MIPS, // Mips architecture
UC_ARCH_X86, // X86 architecture (including x86 & x86-64)
UC_ARCH_PPC, // PowerPC architecture (currently unsupported)
UC_ARCH_SPARC, // Sparc architecture
UC_ARCH_M68K, // M68K architecture
UC_ARCH_MAX,
}
}

View file

@ -0,0 +1,13 @@
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.Tests.Unicorn.Native
{
[StructLayout(LayoutKind.Sequential)]
public struct UnicornMemoryRegion
{
public UInt64 begin; // begin address of the region (inclusive)
public UInt64 end; // end address of the region (inclusive)
public UInt32 perms; // memory permissions of the region
}
}

View file

@ -0,0 +1,34 @@
using System;
namespace Ryujinx.Tests.Unicorn.Native
{
public enum UnicornMode
{
UC_MODE_LITTLE_ENDIAN = 0, // little-endian mode (default mode)
UC_MODE_BIG_ENDIAN = 1 << 30, // big-endian mode
// arm / arm64
UC_MODE_ARM = 0, // ARM mode
UC_MODE_THUMB = 1 << 4, // THUMB mode (including Thumb-2)
UC_MODE_MCLASS = 1 << 5, // ARM's Cortex-M series (currently unsupported)
UC_MODE_V8 = 1 << 6, // ARMv8 A32 encodings for ARM (currently unsupported)
// mips
UC_MODE_MICRO = 1 << 4, // MicroMips mode (currently unsupported)
UC_MODE_MIPS3 = 1 << 5, // Mips III ISA (currently unsupported)
UC_MODE_MIPS32R6 = 1 << 6, // Mips32r6 ISA (currently unsupported)
UC_MODE_MIPS32 = 1 << 2, // Mips32 ISA
UC_MODE_MIPS64 = 1 << 3, // Mips64 ISA
// x86 / x64
UC_MODE_16 = 1 << 1, // 16-bit mode
UC_MODE_32 = 1 << 2, // 32-bit mode
UC_MODE_64 = 1 << 3, // 64-bit mode
// ppc
UC_MODE_PPC32 = 1 << 2, // 32-bit mode (currently unsupported)
UC_MODE_PPC64 = 1 << 3, // 64-bit mode (currently unsupported)
UC_MODE_QPX = 1 << 4, // Quad Processing eXtensions mode (currently unsupported)
// sparc
UC_MODE_SPARC32 = 1 << 2, // 32-bit mode
UC_MODE_SPARC64 = 1 << 3, // 64-bit mode
UC_MODE_V9 = 1 << 4, // SparcV9 mode (currently unsupported)
// m68k
}
}

View file

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
<PackageReference Include="System.Runtime.Intrinsics.Experimental" Version="4.5.0-rc1" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,315 @@
using System;
using System.Diagnostics.Contracts;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
namespace Ryujinx.Tests.Unicorn
{
public class UnicornAArch64
{
internal readonly IntPtr uc;
public IndexedProperty<int, ulong> X
{
get
{
return new IndexedProperty<int, ulong>(
(int i) => GetX(i),
(int i, ulong value) => SetX(i, value));
}
}
public IndexedProperty<int, Vector128<float>> Q
{
get
{
return new IndexedProperty<int, Vector128<float>>(
(int i) => GetQ(i),
(int i, Vector128<float> value) => SetQ(i, value));
}
}
public ulong LR
{
get { return GetRegister(Native.ArmRegister.LR); }
set { SetRegister(Native.ArmRegister.LR, value); }
}
public ulong SP
{
get { return GetRegister(Native.ArmRegister.SP); }
set { SetRegister(Native.ArmRegister.SP, value); }
}
public ulong PC
{
get { return GetRegister(Native.ArmRegister.PC); }
set { SetRegister(Native.ArmRegister.PC, value); }
}
public uint Pstate
{
get { return (uint)GetRegister(Native.ArmRegister.PSTATE); }
set { SetRegister(Native.ArmRegister.PSTATE, (uint)value); }
}
public int Fpcr
{
get { return (int)GetRegister(Native.ArmRegister.FPCR); }
set { SetRegister(Native.ArmRegister.FPCR, (uint)value); }
}
public int Fpsr
{
get { return (int)GetRegister(Native.ArmRegister.FPSR); }
set { SetRegister(Native.ArmRegister.FPSR, (uint)value); }
}
public bool OverflowFlag
{
get { return (Pstate & 0x10000000u) != 0; }
set { Pstate = (Pstate & ~0x10000000u) | (value ? 0x10000000u : 0u); }
}
public bool CarryFlag
{
get { return (Pstate & 0x20000000u) != 0; }
set { Pstate = (Pstate & ~0x20000000u) | (value ? 0x20000000u : 0u); }
}
public bool ZeroFlag
{
get { return (Pstate & 0x40000000u) != 0; }
set { Pstate = (Pstate & ~0x40000000u) | (value ? 0x40000000u : 0u); }
}
public bool NegativeFlag
{
get { return (Pstate & 0x80000000u) != 0; }
set { Pstate = (Pstate & ~0x80000000u) | (value ? 0x80000000u : 0u); }
}
public UnicornAArch64()
{
Native.Interface.Checked(Native.Interface.uc_open((uint)Native.UnicornArch.UC_ARCH_ARM64, (uint)Native.UnicornMode.UC_MODE_LITTLE_ENDIAN, out uc));
SetRegister(Native.ArmRegister.CPACR_EL1, 0x00300000);
}
~UnicornAArch64()
{
Native.Interface.Checked(Native.Interface.uc_close(uc));
}
public void RunForCount(ulong count)
{
Native.Interface.Checked(Native.Interface.uc_emu_start(uc, this.PC, 0xFFFFFFFFFFFFFFFFu, 0, count));
}
public void Step()
{
RunForCount(1);
}
internal static Native.ArmRegister[] X_registers = new Native.ArmRegister[31]
{
Native.ArmRegister.X0,
Native.ArmRegister.X1,
Native.ArmRegister.X2,
Native.ArmRegister.X3,
Native.ArmRegister.X4,
Native.ArmRegister.X5,
Native.ArmRegister.X6,
Native.ArmRegister.X7,
Native.ArmRegister.X8,
Native.ArmRegister.X9,
Native.ArmRegister.X10,
Native.ArmRegister.X11,
Native.ArmRegister.X12,
Native.ArmRegister.X13,
Native.ArmRegister.X14,
Native.ArmRegister.X15,
Native.ArmRegister.X16,
Native.ArmRegister.X17,
Native.ArmRegister.X18,
Native.ArmRegister.X19,
Native.ArmRegister.X20,
Native.ArmRegister.X21,
Native.ArmRegister.X22,
Native.ArmRegister.X23,
Native.ArmRegister.X24,
Native.ArmRegister.X25,
Native.ArmRegister.X26,
Native.ArmRegister.X27,
Native.ArmRegister.X28,
Native.ArmRegister.X29,
Native.ArmRegister.X30,
};
internal static Native.ArmRegister[] Q_registers = new Native.ArmRegister[32]
{
Native.ArmRegister.Q0,
Native.ArmRegister.Q1,
Native.ArmRegister.Q2,
Native.ArmRegister.Q3,
Native.ArmRegister.Q4,
Native.ArmRegister.Q5,
Native.ArmRegister.Q6,
Native.ArmRegister.Q7,
Native.ArmRegister.Q8,
Native.ArmRegister.Q9,
Native.ArmRegister.Q10,
Native.ArmRegister.Q11,
Native.ArmRegister.Q12,
Native.ArmRegister.Q13,
Native.ArmRegister.Q14,
Native.ArmRegister.Q15,
Native.ArmRegister.Q16,
Native.ArmRegister.Q17,
Native.ArmRegister.Q18,
Native.ArmRegister.Q19,
Native.ArmRegister.Q20,
Native.ArmRegister.Q21,
Native.ArmRegister.Q22,
Native.ArmRegister.Q23,
Native.ArmRegister.Q24,
Native.ArmRegister.Q25,
Native.ArmRegister.Q26,
Native.ArmRegister.Q27,
Native.ArmRegister.Q28,
Native.ArmRegister.Q29,
Native.ArmRegister.Q30,
Native.ArmRegister.Q31,
};
internal ulong GetRegister(Native.ArmRegister register)
{
byte[] value_bytes = new byte[8];
Native.Interface.Checked(Native.Interface.uc_reg_read(uc, (int)register, value_bytes));
return (ulong)BitConverter.ToInt64(value_bytes, 0);
}
internal void SetRegister(Native.ArmRegister register, ulong value)
{
byte[] value_bytes = BitConverter.GetBytes(value);
Native.Interface.Checked(Native.Interface.uc_reg_write(uc, (int)register, value_bytes));
}
internal Vector128<float> GetVector(Native.ArmRegister register)
{
byte[] value_bytes = new byte[16];
Native.Interface.Checked(Native.Interface.uc_reg_read(uc, (int)register, value_bytes));
unsafe
{
fixed (byte* p = &value_bytes[0])
{
return Sse.LoadVector128((float*)p);
}
}
}
internal void SetVector(Native.ArmRegister register, Vector128<float> value)
{
byte[] value_bytes = new byte[16];
unsafe
{
fixed (byte* p = &value_bytes[0])
{
Sse.Store((float*)p, value);
}
}
Native.Interface.Checked(Native.Interface.uc_reg_write(uc, (int)register, value_bytes));
}
public ulong GetX(int index)
{
Contract.Requires(index <= 30, "invalid register");
return GetRegister(X_registers[index]);
}
public void SetX(int index, ulong value)
{
Contract.Requires(index <= 30, "invalid register");
SetRegister(X_registers[index], value);
}
public Vector128<float> GetQ(int index)
{
Contract.Requires(index <= 31, "invalid vector");
return GetVector(Q_registers[index]);
}
public void SetQ(int index, Vector128<float> value)
{
Contract.Requires(index <= 31, "invalid vector");
SetVector(Q_registers[index], value);
}
public byte[] MemoryRead(ulong address, ulong size)
{
byte[] value = new byte[size];
Native.Interface.Checked(Native.Interface.uc_mem_read(uc, address, value, size));
return value;
}
public byte MemoryRead8 (ulong address) { return MemoryRead(address, 1)[0]; }
public UInt16 MemoryRead16(ulong address) { return (UInt16)BitConverter.ToInt16(MemoryRead(address, 2), 0); }
public UInt32 MemoryRead32(ulong address) { return (UInt32)BitConverter.ToInt32(MemoryRead(address, 4), 0); }
public UInt64 MemoryRead64(ulong address) { return (UInt64)BitConverter.ToInt64(MemoryRead(address, 8), 0); }
public void MemoryWrite(ulong address, byte[] value)
{
Native.Interface.Checked(Native.Interface.uc_mem_write(uc, address, value, (ulong)value.Length));
}
public void MemoryWrite8 (ulong address, byte value) { MemoryWrite(address, new byte[]{value}); }
public void MemoryWrite16(ulong address, Int16 value) { MemoryWrite(address, BitConverter.GetBytes(value)); }
public void MemoryWrite16(ulong address, UInt16 value) { MemoryWrite(address, BitConverter.GetBytes(value)); }
public void MemoryWrite32(ulong address, Int32 value) { MemoryWrite(address, BitConverter.GetBytes(value)); }
public void MemoryWrite32(ulong address, UInt32 value) { MemoryWrite(address, BitConverter.GetBytes(value)); }
public void MemoryWrite64(ulong address, Int64 value) { MemoryWrite(address, BitConverter.GetBytes(value)); }
public void MemoryWrite64(ulong address, UInt64 value) { MemoryWrite(address, BitConverter.GetBytes(value)); }
public void MemoryMap(ulong address, ulong size, MemoryPermission permissions)
{
Native.Interface.Checked(Native.Interface.uc_mem_map(uc, address, size, (uint)permissions));
}
public void MemoryUnmap(ulong address, ulong size)
{
Native.Interface.Checked(Native.Interface.uc_mem_unmap(uc, address, size));
}
public void MemoryProtect(ulong address, ulong size, MemoryPermission permissions)
{
Native.Interface.Checked(Native.Interface.uc_mem_protect(uc, address, size, (uint)permissions));
}
public void DumpMemoryInformation()
{
Native.Interface.Checked(Native.Interface.uc_mem_regions(uc, out IntPtr regions_raw, out uint length));
Native.Interface.MarshalArrayOf<Native.UnicornMemoryRegion>(regions_raw, (int)length, out var regions);
foreach (var region in regions)
{
Console.WriteLine("region: begin {0:X16} end {1:X16} perms {2:X8}", region.begin, region.end, region.perms);
}
}
public static bool IsAvailable()
{
try
{
Native.Interface.uc_version(out uint major, out uint minor);
return true;
}
catch (DllNotFoundException)
{
return false;
}
}
}
}

View file

@ -0,0 +1,30 @@
using System;
namespace Ryujinx.Tests.Unicorn
{
public enum UnicornError
{
UC_ERR_OK = 0, // No error: everything was fine
UC_ERR_NOMEM, // Out-Of-Memory error: uc_open(), uc_emulate()
UC_ERR_ARCH, // Unsupported architecture: uc_open()
UC_ERR_HANDLE, // Invalid handle
UC_ERR_MODE, // Invalid/unsupported mode: uc_open()
UC_ERR_VERSION, // Unsupported version (bindings)
UC_ERR_READ_UNMAPPED, // Quit emulation due to READ on unmapped memory: uc_emu_start()
UC_ERR_WRITE_UNMAPPED, // Quit emulation due to WRITE on unmapped memory: uc_emu_start()
UC_ERR_FETCH_UNMAPPED, // Quit emulation due to FETCH on unmapped memory: uc_emu_start()
UC_ERR_HOOK, // Invalid hook type: uc_hook_add()
UC_ERR_INSN_INVALID, // Quit emulation due to invalid instruction: uc_emu_start()
UC_ERR_MAP, // Invalid memory mapping: uc_mem_map()
UC_ERR_WRITE_PROT, // Quit emulation due to UC_MEM_WRITE_PROT violation: uc_emu_start()
UC_ERR_READ_PROT, // Quit emulation due to UC_MEM_READ_PROT violation: uc_emu_start()
UC_ERR_FETCH_PROT, // Quit emulation due to UC_MEM_FETCH_PROT violation: uc_emu_start()
UC_ERR_ARG, // Inavalid argument provided to uc_xxx function (See specific function API)
UC_ERR_READ_UNALIGNED, // Unaligned read
UC_ERR_WRITE_UNALIGNED, // Unaligned write
UC_ERR_FETCH_UNALIGNED, // Unaligned fetch
UC_ERR_HOOK_EXIST, // hook for this event already existed
UC_ERR_RESOURCE, // Insufficient resource: uc_emu_start()
UC_ERR_EXCEPTION // Unhandled CPU exception
}
}

View file

@ -0,0 +1,23 @@
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.Tests.Unicorn
{
public class UnicornException : Exception
{
public readonly UnicornError Error;
internal UnicornException(UnicornError error)
{
Error = error;
}
public override string Message
{
get
{
return Marshal.PtrToStringAnsi(Native.Interface.uc_strerror(Error));
}
}
}
}

View file

@ -0,0 +1,3 @@
The pre-compiled dynamic libraries in this directory are licenced under the GPLv2.
The source code for windows/unicorn.dll is available at: https://github.com/MerryMage/UnicornDotNet/tree/299451c02d9c810d2feca51f5e9cb6d8b2f38960

Binary file not shown.

View file

@ -4,6 +4,8 @@ using ChocolArm64.State;
using NUnit.Framework;
using Ryujinx.Tests.Unicorn;
using System;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
@ -25,10 +27,22 @@ namespace Ryujinx.Tests.Cpu
private AMemory Memory;
private AThread Thread;
private static bool UnicornAvailable;
private UnicornAArch64 UnicornEmu;
static CpuTest()
{
UnicornAvailable = UnicornAArch64.IsAvailable();
if (!UnicornAvailable)
{
Console.WriteLine("WARNING: Could not find unicorn");
}
}
[SetUp]
public void Setup()
{
Position = 0x0;
Position = 0x1000;
Size = 0x1000;
EntryPoint = Position;
@ -38,6 +52,13 @@ namespace Ryujinx.Tests.Cpu
Memory = new AMemory(RamPointer);
Memory.Map(Position, 0, Size);
Thread = new AThread(Translator, Memory, EntryPoint);
if (UnicornAvailable)
{
UnicornEmu = new UnicornAArch64();
UnicornEmu.MemoryMap((ulong)Position, (ulong)Size, MemoryPermission.READ | MemoryPermission.EXEC);
UnicornEmu.PC = (ulong)EntryPoint;
}
}
[TearDown]
@ -46,6 +67,7 @@ namespace Ryujinx.Tests.Cpu
Marshal.FreeHGlobal(RamPointer);
Memory = null;
Thread = null;
UnicornEmu = null;
}
protected void Reset()
@ -57,6 +79,10 @@ namespace Ryujinx.Tests.Cpu
protected void Opcode(uint Opcode)
{
Thread.Memory.WriteUInt32(Position, Opcode);
if (UnicornAvailable)
{
UnicornEmu.MemoryWrite32((ulong)Position, Opcode);
}
Position += 4;
}
@ -81,6 +107,24 @@ namespace Ryujinx.Tests.Cpu
Thread.ThreadState.Negative = Negative;
Thread.ThreadState.Fpcr = Fpcr;
Thread.ThreadState.Fpsr = Fpsr;
if (UnicornAvailable)
{
UnicornEmu.X[0] = X0;
UnicornEmu.X[1] = X1;
UnicornEmu.X[2] = X2;
UnicornEmu.X[3] = X3;
UnicornEmu.SP = X31;
UnicornEmu.Q[0] = V0;
UnicornEmu.Q[1] = V1;
UnicornEmu.Q[2] = V2;
UnicornEmu.OverflowFlag = Overflow;
UnicornEmu.CarryFlag = Carry;
UnicornEmu.ZeroFlag = Zero;
UnicornEmu.NegativeFlag = Negative;
UnicornEmu.Fpcr = Fpcr;
UnicornEmu.Fpsr = Fpsr;
}
}
protected void ExecuteOpcodes()
@ -93,6 +137,11 @@ namespace Ryujinx.Tests.Cpu
Thread.Execute();
Wait.WaitOne();
}
if (UnicornAvailable)
{
UnicornEmu.RunForCount((ulong)(Position - EntryPoint - 8) / 4);
}
}
protected AThreadState GetThreadState()
@ -117,24 +166,124 @@ namespace Ryujinx.Tests.Cpu
return GetThreadState();
}
protected void CompareAgainstUnicorn()
{
if (!UnicornAvailable)
{
return;
}
Assert.That(Thread.ThreadState.X0, Is.EqualTo(UnicornEmu.X[0]));
Assert.That(Thread.ThreadState.X1, Is.EqualTo(UnicornEmu.X[1]));
Assert.That(Thread.ThreadState.X2, Is.EqualTo(UnicornEmu.X[2]));
Assert.That(Thread.ThreadState.X3, Is.EqualTo(UnicornEmu.X[3]));
Assert.That(Thread.ThreadState.X4, Is.EqualTo(UnicornEmu.X[4]));
Assert.That(Thread.ThreadState.X5, Is.EqualTo(UnicornEmu.X[5]));
Assert.That(Thread.ThreadState.X6, Is.EqualTo(UnicornEmu.X[6]));
Assert.That(Thread.ThreadState.X7, Is.EqualTo(UnicornEmu.X[7]));
Assert.That(Thread.ThreadState.X8, Is.EqualTo(UnicornEmu.X[8]));
Assert.That(Thread.ThreadState.X9, Is.EqualTo(UnicornEmu.X[9]));
Assert.That(Thread.ThreadState.X10, Is.EqualTo(UnicornEmu.X[10]));
Assert.That(Thread.ThreadState.X11, Is.EqualTo(UnicornEmu.X[11]));
Assert.That(Thread.ThreadState.X12, Is.EqualTo(UnicornEmu.X[12]));
Assert.That(Thread.ThreadState.X13, Is.EqualTo(UnicornEmu.X[13]));
Assert.That(Thread.ThreadState.X14, Is.EqualTo(UnicornEmu.X[14]));
Assert.That(Thread.ThreadState.X15, Is.EqualTo(UnicornEmu.X[15]));
Assert.That(Thread.ThreadState.X16, Is.EqualTo(UnicornEmu.X[16]));
Assert.That(Thread.ThreadState.X17, Is.EqualTo(UnicornEmu.X[17]));
Assert.That(Thread.ThreadState.X18, Is.EqualTo(UnicornEmu.X[18]));
Assert.That(Thread.ThreadState.X19, Is.EqualTo(UnicornEmu.X[19]));
Assert.That(Thread.ThreadState.X20, Is.EqualTo(UnicornEmu.X[20]));
Assert.That(Thread.ThreadState.X21, Is.EqualTo(UnicornEmu.X[21]));
Assert.That(Thread.ThreadState.X22, Is.EqualTo(UnicornEmu.X[22]));
Assert.That(Thread.ThreadState.X23, Is.EqualTo(UnicornEmu.X[23]));
Assert.That(Thread.ThreadState.X24, Is.EqualTo(UnicornEmu.X[24]));
Assert.That(Thread.ThreadState.X25, Is.EqualTo(UnicornEmu.X[25]));
Assert.That(Thread.ThreadState.X26, Is.EqualTo(UnicornEmu.X[26]));
Assert.That(Thread.ThreadState.X27, Is.EqualTo(UnicornEmu.X[27]));
Assert.That(Thread.ThreadState.X28, Is.EqualTo(UnicornEmu.X[28]));
Assert.That(Thread.ThreadState.X29, Is.EqualTo(UnicornEmu.X[29]));
Assert.That(Thread.ThreadState.X30, Is.EqualTo(UnicornEmu.X[30]));
Assert.That(Thread.ThreadState.X31, Is.EqualTo(UnicornEmu.SP));
Assert.That(Thread.ThreadState.V0, Is.EqualTo(UnicornEmu.Q[0]));
Assert.That(Thread.ThreadState.V1, Is.EqualTo(UnicornEmu.Q[1]));
Assert.That(Thread.ThreadState.V2, Is.EqualTo(UnicornEmu.Q[2]));
Assert.That(Thread.ThreadState.V3, Is.EqualTo(UnicornEmu.Q[3]));
Assert.That(Thread.ThreadState.V4, Is.EqualTo(UnicornEmu.Q[4]));
Assert.That(Thread.ThreadState.V5, Is.EqualTo(UnicornEmu.Q[5]));
Assert.That(Thread.ThreadState.V6, Is.EqualTo(UnicornEmu.Q[6]));
Assert.That(Thread.ThreadState.V7, Is.EqualTo(UnicornEmu.Q[7]));
Assert.That(Thread.ThreadState.V8, Is.EqualTo(UnicornEmu.Q[8]));
Assert.That(Thread.ThreadState.V9, Is.EqualTo(UnicornEmu.Q[9]));
Assert.That(Thread.ThreadState.V10, Is.EqualTo(UnicornEmu.Q[10]));
Assert.That(Thread.ThreadState.V11, Is.EqualTo(UnicornEmu.Q[11]));
Assert.That(Thread.ThreadState.V12, Is.EqualTo(UnicornEmu.Q[12]));
Assert.That(Thread.ThreadState.V13, Is.EqualTo(UnicornEmu.Q[13]));
Assert.That(Thread.ThreadState.V14, Is.EqualTo(UnicornEmu.Q[14]));
Assert.That(Thread.ThreadState.V15, Is.EqualTo(UnicornEmu.Q[15]));
Assert.That(Thread.ThreadState.V16, Is.EqualTo(UnicornEmu.Q[16]));
Assert.That(Thread.ThreadState.V17, Is.EqualTo(UnicornEmu.Q[17]));
Assert.That(Thread.ThreadState.V18, Is.EqualTo(UnicornEmu.Q[18]));
Assert.That(Thread.ThreadState.V19, Is.EqualTo(UnicornEmu.Q[19]));
Assert.That(Thread.ThreadState.V20, Is.EqualTo(UnicornEmu.Q[20]));
Assert.That(Thread.ThreadState.V21, Is.EqualTo(UnicornEmu.Q[21]));
Assert.That(Thread.ThreadState.V22, Is.EqualTo(UnicornEmu.Q[22]));
Assert.That(Thread.ThreadState.V23, Is.EqualTo(UnicornEmu.Q[23]));
Assert.That(Thread.ThreadState.V24, Is.EqualTo(UnicornEmu.Q[24]));
Assert.That(Thread.ThreadState.V25, Is.EqualTo(UnicornEmu.Q[25]));
Assert.That(Thread.ThreadState.V26, Is.EqualTo(UnicornEmu.Q[26]));
Assert.That(Thread.ThreadState.V27, Is.EqualTo(UnicornEmu.Q[27]));
Assert.That(Thread.ThreadState.V28, Is.EqualTo(UnicornEmu.Q[28]));
Assert.That(Thread.ThreadState.V29, Is.EqualTo(UnicornEmu.Q[29]));
Assert.That(Thread.ThreadState.V30, Is.EqualTo(UnicornEmu.Q[30]));
Assert.That(Thread.ThreadState.V31, Is.EqualTo(UnicornEmu.Q[31]));
Assert.That(Thread.ThreadState.V31, Is.EqualTo(UnicornEmu.Q[31]));
Assert.That(Thread.ThreadState.Fpcr, Is.EqualTo(UnicornEmu.Fpcr));
Assert.That(Thread.ThreadState.Fpsr & 0x08000000, Is.EqualTo(UnicornEmu.Fpsr & 0x08000000));
Assert.That(Thread.ThreadState.Overflow, Is.EqualTo(UnicornEmu.OverflowFlag));
Assert.That(Thread.ThreadState.Carry, Is.EqualTo(UnicornEmu.CarryFlag));
Assert.That(Thread.ThreadState.Zero, Is.EqualTo(UnicornEmu.ZeroFlag));
Assert.That(Thread.ThreadState.Negative, Is.EqualTo(UnicornEmu.NegativeFlag));
}
protected static Vector128<float> MakeVectorE0(double E0)
{
if (!Sse2.IsSupported)
{
throw new PlatformNotSupportedException();
}
return Sse.StaticCast<long, float>(Sse2.SetVector128(0, BitConverter.DoubleToInt64Bits(E0)));
}
protected static Vector128<float> MakeVectorE0E1(double E0, double E1)
{
return Sse.StaticCast<long, float>(Sse2.SetVector128(BitConverter.DoubleToInt64Bits(E1),
BitConverter.DoubleToInt64Bits(E0)));
if (!Sse2.IsSupported)
{
throw new PlatformNotSupportedException();
}
return Sse.StaticCast<long, float>(
Sse2.SetVector128(BitConverter.DoubleToInt64Bits(E1), BitConverter.DoubleToInt64Bits(E0)));
}
protected static Vector128<float> MakeVectorE1(double E1)
{
if (!Sse2.IsSupported)
{
throw new PlatformNotSupportedException();
}
return Sse.StaticCast<long, float>(Sse2.SetVector128(BitConverter.DoubleToInt64Bits(E1), 0));
}
protected static double VectorExtractDouble(Vector128<float> Vector, byte Index)
{
if (!Sse41.IsSupported)
{
throw new PlatformNotSupportedException();
}
long Value = Sse41.Extract(Sse.StaticCast<float, long>(Vector), Index);
return BitConverter.Int64BitsToDouble(Value);
@ -142,26 +291,51 @@ namespace Ryujinx.Tests.Cpu
protected static Vector128<float> MakeVectorE0(ulong E0)
{
if (!Sse2.IsSupported)
{
throw new PlatformNotSupportedException();
}
return Sse.StaticCast<ulong, float>(Sse2.SetVector128(0, E0));
}
protected static Vector128<float> MakeVectorE0E1(ulong E0, ulong E1)
{
if (!Sse2.IsSupported)
{
throw new PlatformNotSupportedException();
}
return Sse.StaticCast<ulong, float>(Sse2.SetVector128(E1, E0));
}
protected static Vector128<float> MakeVectorE1(ulong E1)
{
if (!Sse2.IsSupported)
{
throw new PlatformNotSupportedException();
}
return Sse.StaticCast<ulong, float>(Sse2.SetVector128(E1, 0));
}
protected static ulong GetVectorE0(Vector128<float> Vector)
{
if (!Sse41.IsSupported)
{
throw new PlatformNotSupportedException();
}
return Sse41.Extract(Sse.StaticCast<float, ulong>(Vector), (byte)0);
}
protected static ulong GetVectorE1(Vector128<float> Vector)
{
if (!Sse41.IsSupported)
{
throw new PlatformNotSupportedException();
}
return Sse41.Extract(Sse.StaticCast<float, ulong>(Vector), (byte)1);
}
}

View file

@ -45,6 +45,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("CLS <Wd>, <Wn>")]
@ -101,6 +102,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("CLZ <Wd>, <Wn>")]
@ -129,6 +131,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("RBIT <Xd>, <Xn>")]
@ -157,6 +160,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("RBIT <Wd>, <Wn>")]
@ -185,6 +189,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("REV16 <Xd>, <Xn>")]
@ -213,6 +218,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("REV16 <Wd>, <Wn>")]
@ -241,6 +247,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("REV32 <Xd>, <Xn>")]
@ -269,6 +276,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("REV <Wd>, <Wn>")]
@ -297,6 +305,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("REV64 <Xd>, <Xn>")]
@ -325,6 +334,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
#endif
}

View file

@ -61,6 +61,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
}
CompareAgainstUnicorn();
}
[Test, Description("ADD <Wd|WSP>, <Wn|WSP>, #<imm>{, <shift>}")]
@ -105,6 +106,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
}
CompareAgainstUnicorn();
}
[Test, Description("ADDS <Xd>, <Xn|SP>, #<imm>{, <shift>}")]
@ -156,6 +158,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("ADDS <Wd>, <Wn|WSP>, #<imm>{, <shift>}")]
@ -207,6 +210,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("AND <Xd|SP>, <Xn>, #<imm>")]
@ -240,6 +244,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
}
CompareAgainstUnicorn();
}
[Test, Description("AND <Xd|SP>, <Xn>, #<imm>")]
@ -273,6 +278,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
}
CompareAgainstUnicorn();
}
[Test, Description("AND <Wd|WSP>, <Wn>, #<imm>")]
@ -306,6 +312,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
}
CompareAgainstUnicorn();
}
[Test, Description("ANDS <Xd>, <Xn>, #<imm>")]
@ -343,6 +350,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("ANDS <Xd>, <Xn>, #<imm>")]
@ -380,6 +388,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("ANDS <Wd>, <Wn>, #<imm>")]
@ -417,6 +426,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("EOR <Xd|SP>, <Xn>, #<imm>")]
@ -450,6 +460,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
}
CompareAgainstUnicorn();
}
[Test, Description("EOR <Xd|SP>, <Xn>, #<imm>")]
@ -483,6 +494,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
}
CompareAgainstUnicorn();
}
[Test, Description("EOR <Wd>, <Wn>, #<imm>")]
@ -516,6 +528,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
}
CompareAgainstUnicorn();
}
[Test, Description("ORR <Xd|SP>, <Xn>, #<imm>")]
@ -549,6 +562,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
}
CompareAgainstUnicorn();
}
[Test, Description("ORR <Xd|SP>, <Xn>, #<imm>")]
@ -582,6 +596,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
}
CompareAgainstUnicorn();
}
[Test, Description("ORR <Wd|WSP>, <Wn>, #<imm>")]
@ -615,6 +630,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
}
CompareAgainstUnicorn();
}
[Test, Description("SUB <Xd|SP>, <Xn|SP>, #<imm>{, <shift>}")]
@ -659,6 +675,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
}
CompareAgainstUnicorn();
}
[Test, Description("SUB <Wd|WSP>, <Wn|WSP>, #<imm>{, <shift>}")]
@ -703,6 +720,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
}
CompareAgainstUnicorn();
}
[Test, Description("SUBS <Xd>, <Xn|SP>, #<imm>{, <shift>}")]
@ -754,6 +772,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("SUBS <Wd>, <Wn|WSP>, #<imm>{, <shift>}")]
@ -805,6 +824,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
#endif
}

View file

@ -51,6 +51,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("ADC <Wd>, <Wn>, <Wm>")]
@ -85,6 +86,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("ADCS <Xd>, <Xn>, <Xm>")]
@ -125,6 +127,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("ADCS <Wd>, <Wn>, <Wm>")]
@ -165,6 +168,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("ADD <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
@ -200,6 +204,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("ADD <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
@ -234,6 +239,7 @@ namespace Ryujinx.Tests.Cpu
else
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
CompareAgainstUnicorn();
}
}
@ -276,6 +282,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("ADDS <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
@ -317,6 +324,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("AND <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
@ -352,6 +360,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("AND <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
@ -387,6 +396,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("ANDS <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
@ -428,6 +438,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("ANDS <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
@ -469,6 +480,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("ASRV <Xd>, <Xn>, <Xm>")]
@ -501,6 +513,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("ASRV <Wd>, <Wn>, <Wm>")]
@ -533,6 +546,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("BIC <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
@ -568,6 +582,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("BIC <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
@ -603,6 +618,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("BICS <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
@ -644,6 +660,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("BICS <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
@ -685,6 +702,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("CRC32X <Wd>, <Wn>, <Xm>")]
@ -718,6 +736,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("CRC32W <Wd>, <Wn>, <Wm>")]
@ -749,6 +768,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("CRC32H <Wd>, <Wn>, <Wm>")]
@ -780,6 +800,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("CRC32B <Wd>, <Wn>, <Wm>")]
@ -811,6 +832,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("CRC32CX <Wd>, <Wn>, <Xm>")]
@ -844,6 +866,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("CRC32CW <Wd>, <Wn>, <Wm>")]
@ -875,6 +898,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("CRC32CH <Wd>, <Wn>, <Wm>")]
@ -906,6 +930,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("CRC32CB <Wd>, <Wn>, <Wm>")]
@ -937,6 +962,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("EON <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
@ -972,6 +998,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("EON <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
@ -1007,6 +1034,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("EOR <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
@ -1042,6 +1070,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("EOR <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
@ -1077,6 +1106,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("EXTR <Xd>, <Xn>, <Xm>, #<lsb>")]
@ -1111,6 +1141,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("EXTR <Wd>, <Wn>, <Wm>, #<lsb>")]
@ -1145,6 +1176,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("LSLV <Xd>, <Xn>, <Xm>")]
@ -1177,6 +1209,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("LSLV <Wd>, <Wn>, <Wm>")]
@ -1209,6 +1242,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("LSRV <Xd>, <Xn>, <Xm>")]
@ -1241,6 +1275,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("LSRV <Wd>, <Wn>, <Wm>")]
@ -1273,6 +1308,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("ORN <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
@ -1308,6 +1344,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("ORN <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
@ -1343,6 +1380,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("ORR <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
@ -1378,6 +1416,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("ORR <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
@ -1413,6 +1452,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("RORV <Xd>, <Xn>, <Xm>")]
@ -1445,6 +1485,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("RORV <Wd>, <Wn>, <Wm>")]
@ -1477,6 +1518,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("SBC <Xd>, <Xn>, <Xm>")]
@ -1511,6 +1553,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("SBC <Wd>, <Wn>, <Wm>")]
@ -1545,6 +1588,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("SBCS <Xd>, <Xn>, <Xm>")]
@ -1585,6 +1629,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("SBCS <Wd>, <Wn>, <Wm>")]
@ -1625,6 +1670,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("SDIV <Xd>, <Xn>, <Xm>")]
@ -1657,6 +1703,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("SDIV <Wd>, <Wn>, <Wm>")]
@ -1689,6 +1736,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("SUB <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
@ -1724,6 +1772,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("SUB <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
@ -1759,6 +1808,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("SUBS <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
@ -1800,6 +1850,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("SUBS <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
@ -1841,6 +1892,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("UDIV <Xd>, <Xn>, <Xm>")]
@ -1873,6 +1925,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("UDIV <Wd>, <Wn>, <Wm>")]
@ -1905,6 +1958,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
#endif
}

View file

@ -66,6 +66,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
}
CompareAgainstUnicorn();
}
[Test, Description("ADD <Xd|SP>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
@ -116,6 +117,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
}
CompareAgainstUnicorn();
}
[Test, Description("ADD <Xd|SP>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
@ -166,6 +168,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
}
CompareAgainstUnicorn();
}
[Test, Description("ADD <Xd|SP>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
@ -216,6 +219,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
}
CompareAgainstUnicorn();
}
[Test, Description("ADD <Wd|WSP>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
@ -266,6 +270,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
}
CompareAgainstUnicorn();
}
[Test, Description("ADD <Wd|WSP>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
@ -316,6 +321,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
}
CompareAgainstUnicorn();
}
[Test, Description("ADD <Wd|WSP>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
@ -366,6 +372,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
}
CompareAgainstUnicorn();
}
[Test, Description("ADDS <Xd>, <Xn|SP>, <X><m>{, <extend> {#<amount>}}")]
@ -410,6 +417,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("ADDS <Xd>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
@ -455,6 +463,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("ADDS <Xd>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
@ -500,6 +509,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("ADDS <Xd>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
@ -545,6 +555,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("ADDS <Wd>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
@ -590,6 +601,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("ADDS <Wd>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
@ -635,6 +647,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("ADDS <Wd>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
@ -680,6 +693,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("SUB <Xd|SP>, <Xn|SP>, <X><m>{, <extend> {#<amount>}}")]
@ -729,6 +743,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
}
CompareAgainstUnicorn();
}
[Test, Description("SUB <Xd|SP>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
@ -779,6 +794,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
}
CompareAgainstUnicorn();
}
[Test, Description("SUB <Xd|SP>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
@ -829,6 +845,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
}
CompareAgainstUnicorn();
}
[Test, Description("SUB <Xd|SP>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
@ -879,6 +896,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
}
CompareAgainstUnicorn();
}
[Test, Description("SUB <Wd|WSP>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
@ -929,6 +947,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
}
CompareAgainstUnicorn();
}
[Test, Description("SUB <Wd|WSP>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
@ -979,6 +998,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
}
CompareAgainstUnicorn();
}
[Test, Description("SUB <Wd|WSP>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
@ -1029,6 +1049,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
}
CompareAgainstUnicorn();
}
[Test, Description("SUBS <Xd>, <Xn|SP>, <X><m>{, <extend> {#<amount>}}")]
@ -1073,6 +1094,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("SUBS <Xd>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
@ -1118,6 +1140,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("SUBS <Xd>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
@ -1163,6 +1186,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("SUBS <Xd>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
@ -1208,6 +1232,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("SUBS <Wd>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
@ -1253,6 +1278,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("SUBS <Wd>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
@ -1298,6 +1324,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("SUBS <Wd>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
@ -1343,6 +1370,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
#endif
}

View file

@ -50,6 +50,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("BFM <Wd>, <Wn>, #<immr>, #<imms>")]
@ -83,6 +84,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("SBFM <Xd>, <Xn>, #<immr>, #<imms>")]
@ -114,6 +116,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("SBFM <Wd>, <Wn>, #<immr>, #<imms>")]
@ -145,6 +148,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("UBFM <Xd>, <Xn>, #<immr>, #<imms>")]
@ -176,6 +180,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("UBFM <Wd>, <Wn>, #<immr>, #<imms>")]
@ -207,6 +212,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
#endif
}

View file

@ -49,6 +49,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("CCMN <Wn>, #<imm>, #<nzcv>, <cond>")]
@ -81,6 +82,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("CCMP <Xn>, #<imm>, #<nzcv>, <cond>")]
@ -113,6 +115,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("CCMP <Wn>, #<imm>, #<nzcv>, <cond>")]
@ -145,6 +148,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
#endif
}

View file

@ -52,6 +52,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("CCMN <Wn>, <Wm>, #<nzcv>, <cond>")]
@ -87,6 +88,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("CCMP <Xn>, <Xm>, #<nzcv>, <cond>")]
@ -122,6 +124,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
[Test, Description("CCMP <Wn>, <Wm>, #<nzcv>, <cond>")]
@ -157,6 +160,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
});
CompareAgainstUnicorn();
}
#endif
}

View file

@ -54,6 +54,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("CSEL <Wd>, <Wn>, <Wm>, <cond>")]
@ -91,6 +92,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("CSINC <Xd>, <Xn>, <Xm>, <cond>")]
@ -128,6 +130,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("CSINC <Wd>, <Wn>, <Wm>, <cond>")]
@ -165,6 +168,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("CSINV <Xd>, <Xn>, <Xm>, <cond>")]
@ -202,6 +206,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("CSINV <Wd>, <Wn>, <Wm>, <cond>")]
@ -239,6 +244,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("CSNEG <Xd>, <Xn>, <Xm>, <cond>")]
@ -276,6 +282,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("CSNEG <Wd>, <Wn>, <Wm>, <cond>")]
@ -313,6 +320,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
#endif
}

View file

@ -46,6 +46,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("MOVK <Wd>, #<imm>{, LSL #<shift>}")]
@ -75,6 +76,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("MOVN <Xd>, #<imm>{, LSL #<shift>}")]
@ -102,6 +104,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("MOVN <Wd>, #<imm>{, LSL #<shift>}")]
@ -129,6 +132,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("MOVZ <Xd>, #<imm>{, LSL #<shift>}")]
@ -156,6 +160,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("MOVZ <Wd>, #<imm>{, LSL #<shift>}")]
@ -183,6 +188,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
#endif
}

View file

@ -53,6 +53,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("MADD <Wd>, <Wn>, <Wm>, <Wa>")]
@ -89,6 +90,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("MSUB <Xd>, <Xn>, <Xm>, <Xa>")]
@ -125,6 +127,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("MSUB <Wd>, <Wn>, <Wm>, <Wa>")]
@ -161,6 +164,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
}
CompareAgainstUnicorn();
}
[Test, Description("SMADDL <Xd>, <Wn>, <Wm>, <Xa>")]
@ -197,6 +201,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("UMADDL <Xd>, <Wn>, <Wm>, <Xa>")]
@ -233,6 +238,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("SMSUBL <Xd>, <Wn>, <Wm>, <Xa>")]
@ -269,6 +275,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("UMSUBL <Xd>, <Wn>, <Wm>, <Xa>")]
@ -305,6 +312,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("SMULH <Xd>, <Xn>, <Xm>")]
@ -337,6 +345,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
[Test, Description("UMULH <Xd>, <Xn>, <Xm>")]
@ -369,6 +378,7 @@ namespace Ryujinx.Tests.Cpu
{
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
}
CompareAgainstUnicorn();
}
#endif
}

File diff suppressed because it is too large Load diff

View file

@ -33,6 +33,7 @@ namespace Ryujinx.Tests.Cpu
V1: Sse.StaticCast<ulong, float>(Sse2.SetVector128(0, A)),
V2: Sse.StaticCast<ulong, float>(Sse2.SetVector128(0, B)));
Assert.AreEqual(Result, Sse41.Extract(Sse.StaticCast<float, ulong>(ThreadState.V0), 0));
CompareAgainstUnicorn();
}
[TestCase(0x80000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u)]
@ -59,6 +60,7 @@ namespace Ryujinx.Tests.Cpu
Assert.AreEqual(Result0, GetVectorE0(ThreadState.V0));
Assert.AreEqual(Result1, GetVectorE1(ThreadState.V0));
});
CompareAgainstUnicorn();
}
[TestCase(0x1E225820u, 0x0000000000000000ul, 0x0000000080000000ul, 0x0000000080000000ul)]
@ -85,6 +87,7 @@ namespace Ryujinx.Tests.Cpu
V1: Sse.StaticCast<ulong, float>(Sse2.SetVector128(0, A)),
V2: Sse.StaticCast<ulong, float>(Sse2.SetVector128(0, B)));
Assert.AreEqual(Result, Sse41.Extract(Sse.StaticCast<float, ulong>(ThreadState.V0), 0));
CompareAgainstUnicorn();
}
[TestCase(0x80000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x80000000u, 0x80000000u)]
@ -111,6 +114,7 @@ namespace Ryujinx.Tests.Cpu
Assert.AreEqual(Result0, GetVectorE0(ThreadState.V0));
Assert.AreEqual(Result1, GetVectorE1(ThreadState.V0));
});
CompareAgainstUnicorn();
}
[Test, Description("FMUL S6, S1, V0.S[2]")]
@ -121,6 +125,7 @@ namespace Ryujinx.Tests.Cpu
V0: Sse.SetVector128(0, B, 0, 0));
Assert.That(Sse41.Extract(ThreadState.V6, (byte)0), Is.EqualTo(A * B));
CompareAgainstUnicorn();
}
[TestCase(0x00000000u, 0x7F800000u)]
@ -135,6 +140,7 @@ namespace Ryujinx.Tests.Cpu
Vector128<float> V1 = MakeVectorE0(A);
AThreadState ThreadState = SingleOpcode(0x5EA1D820, V1: V1);
Assert.AreEqual(Result, GetVectorE0(ThreadState.V0));
CompareAgainstUnicorn();
}
[Test, Description("FRECPS D0, D1, D2")]
@ -145,6 +151,7 @@ namespace Ryujinx.Tests.Cpu
V2: MakeVectorE0(B));
Assert.That(VectorExtractDouble(ThreadState.V0, 0), Is.EqualTo(2 - (A * B)));
//CompareAgainstUnicorn(); // Not accurate enough
}
[Test, Description("FRECPS V4.4S, V2.4S, V0.4S")]
@ -163,6 +170,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(Sse41.Extract(ThreadState.V4, (byte)2), Is.EqualTo(Result));
Assert.That(Sse41.Extract(ThreadState.V4, (byte)3), Is.EqualTo(Result));
});
CompareAgainstUnicorn();
}
[TestCase(0x3FE66666u, false, 0x40000000u)]
@ -213,6 +221,7 @@ namespace Ryujinx.Tests.Cpu
Vector128<float> V1 = MakeVectorE0(A);
AThreadState ThreadState = SingleOpcode(0x1E264020, V1: V1, Fpcr: FpcrTemp);
Assert.AreEqual(Result, GetVectorE0(ThreadState.V0));
CompareAgainstUnicorn();
}
[TestCase(0x6E618820u, 0x3FF3333333333333ul, 0x3FF3333333333333ul, false, 0x3FF0000000000000ul, 0x3FF0000000000000ul)]
@ -240,6 +249,7 @@ namespace Ryujinx.Tests.Cpu
Assert.AreEqual(Result0, GetVectorE0(ThreadState.V0));
Assert.AreEqual(Result1, GetVectorE1(ThreadState.V0));
});
CompareAgainstUnicorn();
}
[TestCase(0x3FE66666u, 'N', false, 0x40000000u)]
@ -310,6 +320,7 @@ namespace Ryujinx.Tests.Cpu
Vector128<float> V1 = MakeVectorE0(A);
AThreadState ThreadState = SingleOpcode(0x1E27C020, V1: V1, Fpcr: FpcrTemp);
Assert.AreEqual(Result, GetVectorE0(ThreadState.V0));
CompareAgainstUnicorn();
}
[TestCase(0x6EE19820u, 0x3FF3333333333333ul, 0x3FF3333333333333ul, 'N', false, 0x3FF0000000000000ul, 0x3FF0000000000000ul)]
@ -376,6 +387,7 @@ namespace Ryujinx.Tests.Cpu
Assert.AreEqual(Result0, GetVectorE0(ThreadState.V0));
Assert.AreEqual(Result1, GetVectorE1(ThreadState.V0));
});
CompareAgainstUnicorn();
}
[TestCase(0x3FE66666u, false, 0x3F800000u)]
@ -426,6 +438,7 @@ namespace Ryujinx.Tests.Cpu
Vector128<float> V1 = MakeVectorE0(A);
AThreadState ThreadState = SingleOpcode(0x1E254020, V1: V1, Fpcr: FpcrTemp);
Assert.AreEqual(Result, GetVectorE0(ThreadState.V0));
CompareAgainstUnicorn();
}
[TestCase(0x4E619820u, 0x3FF3333333333333ul, 0x3FF3333333333333ul, false, 0x3FF0000000000000ul, 0x3FF0000000000000ul)]
@ -450,6 +463,7 @@ namespace Ryujinx.Tests.Cpu
Assert.AreEqual(Result0, GetVectorE0(ThreadState.V0));
Assert.AreEqual(Result1, GetVectorE1(ThreadState.V0));
});
CompareAgainstUnicorn();
}
[TestCase(0x3FE66666u, false, 0x40000000u)]
@ -500,6 +514,7 @@ namespace Ryujinx.Tests.Cpu
Vector128<float> V1 = MakeVectorE0(A);
AThreadState ThreadState = SingleOpcode(0x1E264020, V1: V1, Fpcr: FpcrTemp);
Assert.AreEqual(Result, GetVectorE0(ThreadState.V0));
CompareAgainstUnicorn();
}
[TestCase(0x4E618820u, 0x3FF3333333333333ul, 0x3FF3333333333333ul, false, 0x3FF0000000000000ul, 0x3FF0000000000000ul)]
@ -527,6 +542,7 @@ namespace Ryujinx.Tests.Cpu
Assert.AreEqual(Result0, GetVectorE0(ThreadState.V0));
Assert.AreEqual(Result1, GetVectorE1(ThreadState.V0));
});
CompareAgainstUnicorn();
}
[TestCase(0x3FE66666u, false, 0x40000000u)]
@ -577,6 +593,7 @@ namespace Ryujinx.Tests.Cpu
Vector128<float> V1 = MakeVectorE0(A);
AThreadState ThreadState = SingleOpcode(0x1E24C020, V1: V1, Fpcr: FpcrTemp);
Assert.AreEqual(Result, GetVectorE0(ThreadState.V0));
CompareAgainstUnicorn();
}
[TestCase(0x4EE18820u, 0x3FF3333333333333ul, 0x3FF3333333333333ul, false, 0x4000000000000000ul, 0x4000000000000000ul)]
@ -601,6 +618,7 @@ namespace Ryujinx.Tests.Cpu
Assert.AreEqual(Result0, GetVectorE0(ThreadState.V0));
Assert.AreEqual(Result1, GetVectorE1(ThreadState.V0));
});
CompareAgainstUnicorn();
}
[TestCase(0x3FE66666u, 'N', false, 0x40000000u)]
@ -671,6 +689,7 @@ namespace Ryujinx.Tests.Cpu
Vector128<float> V1 = MakeVectorE0(A);
AThreadState ThreadState = SingleOpcode(0x1E274020, V1: V1, Fpcr: FpcrTemp);
Assert.AreEqual(Result, GetVectorE0(ThreadState.V0));
CompareAgainstUnicorn();
}
[TestCase(0x6E619820u, 0x3FF3333333333333ul, 0x3FF3333333333333ul, 'N', false, 0x3FF0000000000000ul, 0x3FF0000000000000ul)]
@ -737,6 +756,7 @@ namespace Ryujinx.Tests.Cpu
Assert.AreEqual(Result0, GetVectorE0(ThreadState.V0));
Assert.AreEqual(Result1, GetVectorE1(ThreadState.V0));
});
CompareAgainstUnicorn();
}
[TestCase(0x41200000u, 0x3EA18000u)]
@ -745,6 +765,7 @@ namespace Ryujinx.Tests.Cpu
Vector128<float> V1 = MakeVectorE0(A);
AThreadState ThreadState = SingleOpcode(0x7EA1D820, V1: V1);
Assert.AreEqual(Result, GetVectorE0(ThreadState.V0));
CompareAgainstUnicorn();
}
}
}

View file

@ -52,6 +52,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp));
Assert.That(VectorExtractDouble(ThreadState.V0, (byte)1), Is.Zero);
});
CompareAgainstUnicorn();
}
[Test, Description("FCMEQ S0, S1, S2 | FCMGE S0, S1, S2 | FCMGT S0, S1, S2")]
@ -84,6 +85,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(Sse41.Extract(ThreadState.V0, (byte)2), Is.Zero);
Assert.That(Sse41.Extract(ThreadState.V0, (byte)3), Is.Zero);
});
CompareAgainstUnicorn();
}
[Test, Description("FCMEQ V0.2D, V1.2D, V2.2D | FCMGE V0.2D, V1.2D, V2.2D | FCMGT V0.2D, V1.2D, V2.2D")]
@ -113,6 +115,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp));
Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)1)), Is.EquivalentTo(Exp));
});
CompareAgainstUnicorn();
}
[Test, Description("FCMEQ V0.2S, V1.2S, V2.2S | FCMGE V0.2S, V1.2S, V2.2S | FCMGT V0.2S, V1.2S, V2.2S")]
@ -145,6 +148,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(Sse41.Extract(ThreadState.V0, (byte)2), Is.Zero);
Assert.That(Sse41.Extract(ThreadState.V0, (byte)3), Is.Zero);
});
CompareAgainstUnicorn();
}
[Test, Description("FCMEQ V0.4S, V1.4S, V2.4S | FCMGE V0.4S, V1.4S, V2.4S | FCMGT V0.4S, V1.4S, V2.4S")]
@ -176,6 +180,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)2)), Is.EquivalentTo(Exp));
Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)3)), Is.EquivalentTo(Exp));
});
CompareAgainstUnicorn();
}
[Test, Description("FCMGT D0, D1, #0.0 | FCMGE D0, D1, #0.0 | FCMEQ D0, D1, #0.0 | FCMLE D0, D1, #0.0 | FCMLT D0, D1, #0.0")]
@ -214,6 +219,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp));
Assert.That(VectorExtractDouble(ThreadState.V0, (byte)1), Is.Zero);
});
CompareAgainstUnicorn();
}
[Test, Description("FCMGT S0, S1, #0.0 | FCMGE S0, S1, #0.0 | FCMEQ S0, S1, #0.0 | FCMLE S0, S1, #0.0 | FCMLT S0, S1, #0.0")]
@ -254,6 +260,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(Sse41.Extract(ThreadState.V0, (byte)2), Is.Zero);
Assert.That(Sse41.Extract(ThreadState.V0, (byte)3), Is.Zero);
});
CompareAgainstUnicorn();
}
[Test, Description("FCMGT V0.2D, V1.2D, #0.0 | FCMGE V0.2D, V1.2D, #0.0 | FCMEQ V0.2D, V1.2D, #0.0 | FCMLE V0.2D, V1.2D, #0.0 | FCMLT V0.2D, V1.2D, #0.0")]
@ -291,6 +298,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp));
Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)1)), Is.EquivalentTo(Exp));
});
CompareAgainstUnicorn();
}
[Test, Description("FCMGT V0.2S, V1.2S, #0.0 | FCMGE V0.2S, V1.2S, #0.0 | FCMEQ V0.2S, V1.2S, #0.0 | FCMLE V0.2S, V1.2S, #0.0 | FCMLT V0.2S, V1.2S, #0.0")]
@ -331,6 +339,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(Sse41.Extract(ThreadState.V0, (byte)2), Is.Zero);
Assert.That(Sse41.Extract(ThreadState.V0, (byte)3), Is.Zero);
});
CompareAgainstUnicorn();
}
[Test, Description("FCMGT V0.4S, V1.4S, #0.0 | FCMGE V0.4S, V1.4S, #0.0 | FCMEQ V0.4S, V1.4S, #0.0 | FCMLE V0.4S, V1.4S, #0.0 | FCMLT V0.4S, V1.4S, #0.0")]
@ -370,6 +379,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)2)), Is.EquivalentTo(Exp));
Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)3)), Is.EquivalentTo(Exp));
});
CompareAgainstUnicorn();
}
}
}

View file

@ -0,0 +1,139 @@
// https://www.intel.com/content/dam/doc/white-paper/advanced-encryption-standard-new-instructions-set-paper.pdf
using ChocolArm64.State;
using NUnit.Framework;
using System.Runtime.Intrinsics;
namespace Ryujinx.Tests.Cpu
{
public class CpuTestSimdCrypto : CpuTest
{
[Test, Description("AESD <Vd>.16B, <Vn>.16B")]
public void Aesd_V([Values(0u)] uint Rd,
[Values(1u)] uint Rn,
[Values(0x7B5B546573745665ul)] ulong ValueH,
[Values(0x63746F725D53475Dul)] ulong ValueL,
[Random(2)] ulong RoundKeyH,
[Random(2)] ulong RoundKeyL,
[Values(0x8DCAB9BC035006BCul)] ulong ResultH,
[Values(0x8F57161E00CAFD8Dul)] ulong ResultL)
{
uint Opcode = 0x4E285800; // AESD V0.16B, V0.16B
Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
Vector128<float> V0 = MakeVectorE0E1(RoundKeyL ^ ValueL, RoundKeyH ^ ValueH);
Vector128<float> V1 = MakeVectorE0E1(RoundKeyL, RoundKeyH);
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1);
Assert.Multiple(() =>
{
Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(ResultL));
Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(ResultH));
});
Assert.Multiple(() =>
{
Assert.That(GetVectorE0(ThreadState.V1), Is.EqualTo(RoundKeyL));
Assert.That(GetVectorE1(ThreadState.V1), Is.EqualTo(RoundKeyH));
});
CompareAgainstUnicorn();
}
[Test, Description("AESE <Vd>.16B, <Vn>.16B")]
public void Aese_V([Values(0u)] uint Rd,
[Values(1u)] uint Rn,
[Values(0x7B5B546573745665ul)] ulong ValueH,
[Values(0x63746F725D53475Dul)] ulong ValueL,
[Random(2)] ulong RoundKeyH,
[Random(2)] ulong RoundKeyL,
[Values(0x8F92A04DFBED204Dul)] ulong ResultH,
[Values(0x4C39B1402192A84Cul)] ulong ResultL)
{
uint Opcode = 0x4E284800; // AESE V0.16B, V0.16B
Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
Vector128<float> V0 = MakeVectorE0E1(RoundKeyL ^ ValueL, RoundKeyH ^ ValueH);
Vector128<float> V1 = MakeVectorE0E1(RoundKeyL, RoundKeyH);
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1);
Assert.Multiple(() =>
{
Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(ResultL));
Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(ResultH));
});
Assert.Multiple(() =>
{
Assert.That(GetVectorE0(ThreadState.V1), Is.EqualTo(RoundKeyL));
Assert.That(GetVectorE1(ThreadState.V1), Is.EqualTo(RoundKeyH));
});
CompareAgainstUnicorn();
}
[Test, Description("AESIMC <Vd>.16B, <Vn>.16B")]
public void Aesimc_V([Values(0u)] uint Rd,
[Values(1u, 0u)] uint Rn,
[Values(0x8DCAB9DC035006BCul)] ulong ValueH,
[Values(0x8F57161E00CAFD8Dul)] ulong ValueL,
[Values(0xD635A667928B5EAEul)] ulong ResultH,
[Values(0xEEC9CC3BC55F5777ul)] ulong ResultL)
{
uint Opcode = 0x4E287800; // AESIMC V0.16B, V0.16B
Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
Vector128<float> V = MakeVectorE0E1(ValueL, ValueH);
AThreadState ThreadState = SingleOpcode(
Opcode,
V0: Rn == 0u ? V : default(Vector128<float>),
V1: Rn == 1u ? V : default(Vector128<float>));
Assert.Multiple(() =>
{
Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(ResultL));
Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(ResultH));
});
if (Rn == 1u)
{
Assert.Multiple(() =>
{
Assert.That(GetVectorE0(ThreadState.V1), Is.EqualTo(ValueL));
Assert.That(GetVectorE1(ThreadState.V1), Is.EqualTo(ValueH));
});
}
CompareAgainstUnicorn();
}
[Test, Description("AESMC <Vd>.16B, <Vn>.16B")]
public void Aesmc_V([Values(0u)] uint Rd,
[Values(1u, 0u)] uint Rn,
[Values(0x627A6F6644B109C8ul)] ulong ValueH,
[Values(0x2B18330A81C3B3E5ul)] ulong ValueL,
[Values(0x7B5B546573745665ul)] ulong ResultH,
[Values(0x63746F725D53475Dul)] ulong ResultL)
{
uint Opcode = 0x4E286800; // AESMC V0.16B, V0.16B
Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
Vector128<float> V = MakeVectorE0E1(ValueL, ValueH);
AThreadState ThreadState = SingleOpcode(
Opcode,
V0: Rn == 0u ? V : default(Vector128<float>),
V1: Rn == 1u ? V : default(Vector128<float>));
Assert.Multiple(() =>
{
Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(ResultL));
Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(ResultH));
});
if (Rn == 1u)
{
Assert.Multiple(() =>
{
Assert.That(GetVectorE0(ThreadState.V1), Is.EqualTo(ValueL));
Assert.That(GetVectorE1(ThreadState.V1), Is.EqualTo(ValueH));
});
}
CompareAgainstUnicorn();
}
}
}

View file

@ -35,6 +35,7 @@ namespace Ryujinx.Tests.Cpu
Assert.That(Sse41.Extract(Sse.StaticCast<float, uint>(ThreadState.V1), (byte)2), Is.EqualTo(Result));
Assert.That(Sse41.Extract(Sse.StaticCast<float, uint>(ThreadState.V1), (byte)3), Is.EqualTo(Result));
});
CompareAgainstUnicorn();
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -2685,6 +2685,154 @@ namespace Ryujinx.Tests.Cpu.Tester
V(d, result);
}
// fcvtns_advsimd.html#FCVTNS_asisdmisc_R
public static void Fcvtns_S(Bits sz, Bits Rn, Bits Rd)
{
const bool U = false;
const bool o2 = false;
const bool o1 = false;
/* Decode Scalar */
int d = (int)UInt(Rd);
int n = (int)UInt(Rn);
int esize = 32 << (int)UInt(sz);
int datasize = esize;
int elements = 1;
FPRounding rounding = FPDecodeRounding(Bits.Concat(o1, o2));
bool unsigned = (U == true);
/* Operation */
/* CheckFPAdvSIMDEnabled64(); */
Bits result = new Bits(datasize);
Bits operand = V(datasize, n);
Bits element;
for (int e = 0; e <= elements - 1; e++)
{
element = Elem(operand, e, esize);
Elem(result, e, esize, FPToFixed(esize, element, 0, unsigned, FPCR, rounding));
}
V(d, result);
}
// fcvtns_advsimd.html#FCVTNS_asimdmisc_R
public static void Fcvtns_V(bool Q, Bits sz, Bits Rn, Bits Rd)
{
const bool U = false;
const bool o2 = false;
const bool o1 = false;
/* Decode Vector */
int d = (int)UInt(Rd);
int n = (int)UInt(Rn);
/* if sz:Q == '10' then ReservedValue(); */
int esize = 32 << (int)UInt(sz);
int datasize = (Q ? 128 : 64);
int elements = datasize / esize;
FPRounding rounding = FPDecodeRounding(Bits.Concat(o1, o2));
bool unsigned = (U == true);
/* Operation */
/* CheckFPAdvSIMDEnabled64(); */
Bits result = new Bits(datasize);
Bits operand = V(datasize, n);
Bits element;
for (int e = 0; e <= elements - 1; e++)
{
element = Elem(operand, e, esize);
Elem(result, e, esize, FPToFixed(esize, element, 0, unsigned, FPCR, rounding));
}
V(d, result);
}
// fcvtnu_advsimd.html#FCVTNU_asisdmisc_R
public static void Fcvtnu_S(Bits sz, Bits Rn, Bits Rd)
{
const bool U = true;
const bool o2 = false;
const bool o1 = false;
/* Decode Scalar */
int d = (int)UInt(Rd);
int n = (int)UInt(Rn);
int esize = 32 << (int)UInt(sz);
int datasize = esize;
int elements = 1;
FPRounding rounding = FPDecodeRounding(Bits.Concat(o1, o2));
bool unsigned = (U == true);
/* Operation */
/* CheckFPAdvSIMDEnabled64(); */
Bits result = new Bits(datasize);
Bits operand = V(datasize, n);
Bits element;
for (int e = 0; e <= elements - 1; e++)
{
element = Elem(operand, e, esize);
Elem(result, e, esize, FPToFixed(esize, element, 0, unsigned, FPCR, rounding));
}
V(d, result);
}
// fcvtnu_advsimd.html#FCVTNU_asimdmisc_R
public static void Fcvtnu_V(bool Q, Bits sz, Bits Rn, Bits Rd)
{
const bool U = true;
const bool o2 = false;
const bool o1 = false;
/* Decode Vector */
int d = (int)UInt(Rd);
int n = (int)UInt(Rn);
/* if sz:Q == '10' then ReservedValue(); */
int esize = 32 << (int)UInt(sz);
int datasize = (Q ? 128 : 64);
int elements = datasize / esize;
FPRounding rounding = FPDecodeRounding(Bits.Concat(o1, o2));
bool unsigned = (U == true);
/* Operation */
/* CheckFPAdvSIMDEnabled64(); */
Bits result = new Bits(datasize);
Bits operand = V(datasize, n);
Bits element;
for (int e = 0; e <= elements - 1; e++)
{
element = Elem(operand, e, esize);
Elem(result, e, esize, FPToFixed(esize, element, 0, unsigned, FPCR, rounding));
}
V(d, result);
}
// neg_advsimd.html#NEG_asisdmisc_R
public static void Neg_S(Bits size, Bits Rn, Bits Rd)
{
@ -5122,6 +5270,57 @@ namespace Ryujinx.Tests.Cpu.Tester
V(d, result);
}
// saddl_advsimd.html
public static void Saddl_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
{
const bool U = false;
const bool o1 = false;
/* Decode */
int d = (int)UInt(Rd);
int n = (int)UInt(Rn);
int m = (int)UInt(Rm);
/* if size == '11' then ReservedValue(); */
int esize = 8 << (int)UInt(size);
int datasize = 64;
int part = (int)UInt(Q);
int elements = datasize / esize;
bool sub_op = (o1 == true);
bool unsigned = (U == true);
/* Operation */
/* CheckFPAdvSIMDEnabled64(); */
Bits result = new Bits(2 * datasize);
Bits operand1 = Vpart(datasize, n, part);
Bits operand2 = Vpart(datasize, m, part);
BigInteger element1;
BigInteger element2;
BigInteger sum;
for (int e = 0; e <= elements - 1; e++)
{
element1 = Int(Elem(operand1, e, esize), unsigned);
element2 = Int(Elem(operand2, e, esize), unsigned);
if (sub_op)
{
sum = element1 - element2;
}
else
{
sum = element1 + element2;
}
Elem(result, e, 2 * esize, sum.SubBigInteger(2 * esize - 1, 0));
}
V(d, result);
}
// saddw_advsimd.html
public static void Saddw_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
{
@ -5251,6 +5450,198 @@ namespace Ryujinx.Tests.Cpu.Tester
V(d, result);
}
// shadd_advsimd.html
public static void Shadd_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
{
const bool U = false;
/* Decode */
int d = (int)UInt(Rd);
int n = (int)UInt(Rn);
int m = (int)UInt(Rm);
/* if size == '11' then ReservedValue(); */
int esize = 8 << (int)UInt(size);
int datasize = (Q ? 128 : 64);
int elements = datasize / esize;
bool unsigned = (U == true);
/* Operation */
/* CheckFPAdvSIMDEnabled64(); */
Bits result = new Bits(datasize);
Bits operand1 = V(datasize, n);
Bits operand2 = V(datasize, m);
BigInteger element1;
BigInteger element2;
BigInteger sum;
for (int e = 0; e <= elements - 1; e++)
{
element1 = Int(Elem(operand1, e, esize), unsigned);
element2 = Int(Elem(operand2, e, esize), unsigned);
sum = element1 + element2;
Elem(result, e, esize, sum.SubBigInteger(esize, 1));
}
V(d, result);
}
// shsub_advsimd.html
public static void Shsub_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
{
const bool U = false;
/* Decode */
int d = (int)UInt(Rd);
int n = (int)UInt(Rn);
int m = (int)UInt(Rm);
/* if size == '11' then ReservedValue(); */
int esize = 8 << (int)UInt(size);
int datasize = (Q ? 128 : 64);
int elements = datasize / esize;
bool unsigned = (U == true);
/* Operation */
/* CheckFPAdvSIMDEnabled64(); */
Bits result = new Bits(datasize);
Bits operand1 = V(datasize, n);
Bits operand2 = V(datasize, m);
BigInteger element1;
BigInteger element2;
BigInteger diff;
for (int e = 0; e <= elements - 1; e++)
{
element1 = Int(Elem(operand1, e, esize), unsigned);
element2 = Int(Elem(operand2, e, esize), unsigned);
diff = element1 - element2;
Elem(result, e, esize, diff.SubBigInteger(esize, 1));
}
V(d, result);
}
// smlal_advsimd_vec.html
public static void Smlal_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
{
const bool U = false;
const bool o1 = false;
/* Decode */
int d = (int)UInt(Rd);
int n = (int)UInt(Rn);
int m = (int)UInt(Rm);
/* if size == '11' then ReservedValue(); */
int esize = 8 << (int)UInt(size);
int datasize = 64;
int part = (int)UInt(Q);
int elements = datasize / esize;
bool sub_op = (o1 == true);
bool unsigned = (U == true);
/* Operation */
/* CheckFPAdvSIMDEnabled64(); */
Bits result = new Bits(2 * datasize);
Bits operand1 = Vpart(datasize, n, part);
Bits operand2 = Vpart(datasize, m, part);
Bits operand3 = V(2 * datasize, d);
BigInteger element1;
BigInteger element2;
Bits product;
Bits accum;
for (int e = 0; e <= elements - 1; e++)
{
element1 = Int(Elem(operand1, e, esize), unsigned);
element2 = Int(Elem(operand2, e, esize), unsigned);
product = (element1 * element2).SubBigInteger(2 * esize - 1, 0);
if (sub_op)
{
accum = Elem(operand3, e, 2 * esize) - product;
}
else
{
accum = Elem(operand3, e, 2 * esize) + product;
}
Elem(result, e, 2 * esize, accum);
}
V(d, result);
}
// smlsl_advsimd_vec.html
public static void Smlsl_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
{
const bool U = false;
const bool o1 = true;
/* Decode */
int d = (int)UInt(Rd);
int n = (int)UInt(Rn);
int m = (int)UInt(Rm);
/* if size == '11' then ReservedValue(); */
int esize = 8 << (int)UInt(size);
int datasize = 64;
int part = (int)UInt(Q);
int elements = datasize / esize;
bool sub_op = (o1 == true);
bool unsigned = (U == true);
/* Operation */
/* CheckFPAdvSIMDEnabled64(); */
Bits result = new Bits(2 * datasize);
Bits operand1 = Vpart(datasize, n, part);
Bits operand2 = Vpart(datasize, m, part);
Bits operand3 = V(2 * datasize, d);
BigInteger element1;
BigInteger element2;
Bits product;
Bits accum;
for (int e = 0; e <= elements - 1; e++)
{
element1 = Int(Elem(operand1, e, esize), unsigned);
element2 = Int(Elem(operand2, e, esize), unsigned);
product = (element1 * element2).SubBigInteger(2 * esize - 1, 0);
if (sub_op)
{
accum = Elem(operand3, e, 2 * esize) - product;
}
else
{
accum = Elem(operand3, e, 2 * esize) + product;
}
Elem(result, e, 2 * esize, accum);
}
V(d, result);
}
// sqadd_advsimd.html#SQADD_asisdsame_only
public static void Sqadd_S(Bits size, Bits Rm, Bits Rn, Bits Rd)
{
@ -5651,6 +6042,95 @@ namespace Ryujinx.Tests.Cpu.Tester
V(d, result);
}
// srhadd_advsimd.html
public static void Srhadd_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
{
const bool U = false;
/* Decode */
int d = (int)UInt(Rd);
int n = (int)UInt(Rn);
int m = (int)UInt(Rm);
/* if size == '11' then ReservedValue(); */
int esize = 8 << (int)UInt(size);
int datasize = (Q ? 128 : 64);
int elements = datasize / esize;
bool unsigned = (U == true);
/* Operation */
/* CheckFPAdvSIMDEnabled64(); */
Bits result = new Bits(datasize);
Bits operand1 = V(datasize, n);
Bits operand2 = V(datasize, m);
BigInteger element1;
BigInteger element2;
for (int e = 0; e <= elements - 1; e++)
{
element1 = Int(Elem(operand1, e, esize), unsigned);
element2 = Int(Elem(operand2, e, esize), unsigned);
Elem(result, e, esize, (element1 + element2 + 1).SubBigInteger(esize, 1));
}
V(d, result);
}
// ssubl_advsimd.html
public static void Ssubl_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
{
const bool U = false;
const bool o1 = true;
/* Decode */
int d = (int)UInt(Rd);
int n = (int)UInt(Rn);
int m = (int)UInt(Rm);
/* if size == '11' then ReservedValue(); */
int esize = 8 << (int)UInt(size);
int datasize = 64;
int part = (int)UInt(Q);
int elements = datasize / esize;
bool sub_op = (o1 == true);
bool unsigned = (U == true);
/* Operation */
/* CheckFPAdvSIMDEnabled64(); */
Bits result = new Bits(2 * datasize);
Bits operand1 = Vpart(datasize, n, part);
Bits operand2 = Vpart(datasize, m, part);
BigInteger element1;
BigInteger element2;
BigInteger sum;
for (int e = 0; e <= elements - 1; e++)
{
element1 = Int(Elem(operand1, e, esize), unsigned);
element2 = Int(Elem(operand2, e, esize), unsigned);
if (sub_op)
{
sum = element1 - element2;
}
else
{
sum = element1 + element2;
}
Elem(result, e, 2 * esize, sum.SubBigInteger(2 * esize - 1, 0));
}
V(d, result);
}
// ssubw_advsimd.html
public static void Ssubw_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
{
@ -6092,6 +6572,57 @@ namespace Ryujinx.Tests.Cpu.Tester
V(d, result);
}
// uaddl_advsimd.html
public static void Uaddl_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
{
const bool U = true;
const bool o1 = false;
/* Decode */
int d = (int)UInt(Rd);
int n = (int)UInt(Rn);
int m = (int)UInt(Rm);
/* if size == '11' then ReservedValue(); */
int esize = 8 << (int)UInt(size);
int datasize = 64;
int part = (int)UInt(Q);
int elements = datasize / esize;
bool sub_op = (o1 == true);
bool unsigned = (U == true);
/* Operation */
/* CheckFPAdvSIMDEnabled64(); */
Bits result = new Bits(2 * datasize);
Bits operand1 = Vpart(datasize, n, part);
Bits operand2 = Vpart(datasize, m, part);
BigInteger element1;
BigInteger element2;
BigInteger sum;
for (int e = 0; e <= elements - 1; e++)
{
element1 = Int(Elem(operand1, e, esize), unsigned);
element2 = Int(Elem(operand2, e, esize), unsigned);
if (sub_op)
{
sum = element1 - element2;
}
else
{
sum = element1 + element2;
}
Elem(result, e, 2 * esize, sum.SubBigInteger(2 * esize - 1, 0));
}
V(d, result);
}
// uaddw_advsimd.html
public static void Uaddw_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
{
@ -6143,6 +6674,198 @@ namespace Ryujinx.Tests.Cpu.Tester
V(d, result);
}
// uhadd_advsimd.html
public static void Uhadd_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
{
const bool U = true;
/* Decode */
int d = (int)UInt(Rd);
int n = (int)UInt(Rn);
int m = (int)UInt(Rm);
/* if size == '11' then ReservedValue(); */
int esize = 8 << (int)UInt(size);
int datasize = (Q ? 128 : 64);
int elements = datasize / esize;
bool unsigned = (U == true);
/* Operation */
/* CheckFPAdvSIMDEnabled64(); */
Bits result = new Bits(datasize);
Bits operand1 = V(datasize, n);
Bits operand2 = V(datasize, m);
BigInteger element1;
BigInteger element2;
BigInteger sum;
for (int e = 0; e <= elements - 1; e++)
{
element1 = Int(Elem(operand1, e, esize), unsigned);
element2 = Int(Elem(operand2, e, esize), unsigned);
sum = element1 + element2;
Elem(result, e, esize, sum.SubBigInteger(esize, 1));
}
V(d, result);
}
// uhsub_advsimd.html
public static void Uhsub_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
{
const bool U = true;
/* Decode */
int d = (int)UInt(Rd);
int n = (int)UInt(Rn);
int m = (int)UInt(Rm);
/* if size == '11' then ReservedValue(); */
int esize = 8 << (int)UInt(size);
int datasize = (Q ? 128 : 64);
int elements = datasize / esize;
bool unsigned = (U == true);
/* Operation */
/* CheckFPAdvSIMDEnabled64(); */
Bits result = new Bits(datasize);
Bits operand1 = V(datasize, n);
Bits operand2 = V(datasize, m);
BigInteger element1;
BigInteger element2;
BigInteger diff;
for (int e = 0; e <= elements - 1; e++)
{
element1 = Int(Elem(operand1, e, esize), unsigned);
element2 = Int(Elem(operand2, e, esize), unsigned);
diff = element1 - element2;
Elem(result, e, esize, diff.SubBigInteger(esize, 1));
}
V(d, result);
}
// umlal_advsimd_vec.html
public static void Umlal_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
{
const bool U = true;
const bool o1 = false;
/* Decode */
int d = (int)UInt(Rd);
int n = (int)UInt(Rn);
int m = (int)UInt(Rm);
/* if size == '11' then ReservedValue(); */
int esize = 8 << (int)UInt(size);
int datasize = 64;
int part = (int)UInt(Q);
int elements = datasize / esize;
bool sub_op = (o1 == true);
bool unsigned = (U == true);
/* Operation */
/* CheckFPAdvSIMDEnabled64(); */
Bits result = new Bits(2 * datasize);
Bits operand1 = Vpart(datasize, n, part);
Bits operand2 = Vpart(datasize, m, part);
Bits operand3 = V(2 * datasize, d);
BigInteger element1;
BigInteger element2;
Bits product;
Bits accum;
for (int e = 0; e <= elements - 1; e++)
{
element1 = Int(Elem(operand1, e, esize), unsigned);
element2 = Int(Elem(operand2, e, esize), unsigned);
product = (element1 * element2).SubBigInteger(2 * esize - 1, 0);
if (sub_op)
{
accum = Elem(operand3, e, 2 * esize) - product;
}
else
{
accum = Elem(operand3, e, 2 * esize) + product;
}
Elem(result, e, 2 * esize, accum);
}
V(d, result);
}
// umlsl_advsimd_vec.html
public static void Umlsl_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
{
const bool U = true;
const bool o1 = true;
/* Decode */
int d = (int)UInt(Rd);
int n = (int)UInt(Rn);
int m = (int)UInt(Rm);
/* if size == '11' then ReservedValue(); */
int esize = 8 << (int)UInt(size);
int datasize = 64;
int part = (int)UInt(Q);
int elements = datasize / esize;
bool sub_op = (o1 == true);
bool unsigned = (U == true);
/* Operation */
/* CheckFPAdvSIMDEnabled64(); */
Bits result = new Bits(2 * datasize);
Bits operand1 = Vpart(datasize, n, part);
Bits operand2 = Vpart(datasize, m, part);
Bits operand3 = V(2 * datasize, d);
BigInteger element1;
BigInteger element2;
Bits product;
Bits accum;
for (int e = 0; e <= elements - 1; e++)
{
element1 = Int(Elem(operand1, e, esize), unsigned);
element2 = Int(Elem(operand2, e, esize), unsigned);
product = (element1 * element2).SubBigInteger(2 * esize - 1, 0);
if (sub_op)
{
accum = Elem(operand3, e, 2 * esize) - product;
}
else
{
accum = Elem(operand3, e, 2 * esize) + product;
}
Elem(result, e, 2 * esize, accum);
}
V(d, result);
}
// uqadd_advsimd.html#UQADD_asisdsame_only
public static void Uqadd_S(Bits size, Bits Rm, Bits Rn, Bits Rd)
{
@ -6339,6 +7062,95 @@ namespace Ryujinx.Tests.Cpu.Tester
V(d, result);
}
// urhadd_advsimd.html
public static void Urhadd_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
{
const bool U = true;
/* Decode */
int d = (int)UInt(Rd);
int n = (int)UInt(Rn);
int m = (int)UInt(Rm);
/* if size == '11' then ReservedValue(); */
int esize = 8 << (int)UInt(size);
int datasize = (Q ? 128 : 64);
int elements = datasize / esize;
bool unsigned = (U == true);
/* Operation */
/* CheckFPAdvSIMDEnabled64(); */
Bits result = new Bits(datasize);
Bits operand1 = V(datasize, n);
Bits operand2 = V(datasize, m);
BigInteger element1;
BigInteger element2;
for (int e = 0; e <= elements - 1; e++)
{
element1 = Int(Elem(operand1, e, esize), unsigned);
element2 = Int(Elem(operand2, e, esize), unsigned);
Elem(result, e, esize, (element1 + element2 + 1).SubBigInteger(esize, 1));
}
V(d, result);
}
// usubl_advsimd.html
public static void Usubl_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
{
const bool U = true;
const bool o1 = true;
/* Decode */
int d = (int)UInt(Rd);
int n = (int)UInt(Rn);
int m = (int)UInt(Rm);
/* if size == '11' then ReservedValue(); */
int esize = 8 << (int)UInt(size);
int datasize = 64;
int part = (int)UInt(Q);
int elements = datasize / esize;
bool sub_op = (o1 == true);
bool unsigned = (U == true);
/* Operation */
/* CheckFPAdvSIMDEnabled64(); */
Bits result = new Bits(2 * datasize);
Bits operand1 = Vpart(datasize, n, part);
Bits operand2 = Vpart(datasize, m, part);
BigInteger element1;
BigInteger element2;
BigInteger sum;
for (int e = 0; e <= elements - 1; e++)
{
element1 = Int(Elem(operand1, e, esize), unsigned);
element2 = Int(Elem(operand2, e, esize), unsigned);
if (sub_op)
{
sum = element1 - element2;
}
else
{
sum = element1 + element2;
}
Elem(result, e, 2 * esize, sum.SubBigInteger(2 * esize - 1, 0));
}
V(d, result);
}
// usubw_advsimd.html
public static void Usubw_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
{

View file

@ -5,21 +5,19 @@
// https://alastairreid.github.io/asl-lexical-syntax/
// | ------------------------|----------------------------------- |
// | ASL | C# |
// | ------------------------|----------------------------------- |
// | bit, bits(1); boolean | bool |
// | bits | Bits |
// | integer | BigInteger, int |
// | real | decimal |
// | ------------------------|----------------------------------- |
// | '0'; FALSE | false |
// | '1'; TRUE | true |
// | '010' | "010" |
// | bitsX IN {bitsY, bitsZ} | (bitsX == bitsY || bitsX == bitsZ) |
// | DIV | / |
// | MOD | % |
// | ------------------------|----------------------------------- |
// | ------------------------|-------------------------------- |
// | ASL | C# |
// | ------------------------|-------------------------------- |
// | bit, bits(1); boolean | bool |
// | bits | Bits |
// | integer | BigInteger, int |
// | real | decimal; double, float |
// | ------------------------|-------------------------------- |
// | '0'; FALSE | false |
// | '1'; TRUE | true |
// | '010' | "010" |
// | DIV, MOD | /, % |
// | ------------------------|-------------------------------- |
using System;
using System.Numerics;
@ -107,6 +105,7 @@ namespace Ryujinx.Tests.Cpu.Tester
/* SP_EL1 = bits(64) UNKNOWN; */
SP_EL1.SetAll(false);
FPCR.SetAll(false); // TODO: Add named fields.
FPSR.SetAll(false); // TODO: Add named fields.
}
@ -458,6 +457,7 @@ namespace Ryujinx.Tests.Cpu.Tester
#endregion
#region "instrs/vector/reduce/reduceop/"
// shared_pseudocode.html#impl-aarch64.Reduce.3
public static Bits Reduce(ReduceOp op, Bits input, int esize)
{
int N = input.Count;
@ -528,6 +528,7 @@ namespace Ryujinx.Tests.Cpu.Tester
SP_EL0 = new Bits(64, false);
SP_EL1 = new Bits(64, false);
FPCR = new Bits(32, false); // TODO: Add named fields.
FPSR = new Bits(32, false); // TODO: Add named fields.
PSTATE.N = false;
@ -817,6 +818,36 @@ namespace Ryujinx.Tests.Cpu.Tester
return (decimal)value;
}
/* */
public static float Real_32(BigInteger value)
{
if (value == BigInteger.Pow((BigInteger)2.0f, 1000))
{
return float.PositiveInfinity;
}
if (value == -BigInteger.Pow((BigInteger)2.0f, 1000))
{
return float.NegativeInfinity;
}
return (float)value;
}
/* */
public static double Real_64(BigInteger value)
{
if (value == BigInteger.Pow((BigInteger)2.0, 10000))
{
return double.PositiveInfinity;
}
if (value == -BigInteger.Pow((BigInteger)2.0, 10000))
{
return double.NegativeInfinity;
}
return (double)value;
}
// shared_pseudocode.html#impl-shared.ROR.2
public static Bits ROR(Bits x, int shift)
{
@ -881,6 +912,36 @@ namespace Ryujinx.Tests.Cpu.Tester
return (BigInteger)Decimal.Floor(x);
}
/* */
public static BigInteger RoundDown_32(float x)
{
if (float.IsPositiveInfinity(x))
{
return BigInteger.Pow((BigInteger)2.0f, 1000);
}
if (float.IsNegativeInfinity(x))
{
return -BigInteger.Pow((BigInteger)2.0f, 1000);
}
return (BigInteger)MathF.Floor(x);
}
/* */
public static BigInteger RoundDown_64(double x)
{
if (double.IsPositiveInfinity(x))
{
return BigInteger.Pow((BigInteger)2.0, 10000);
}
if (double.IsNegativeInfinity(x))
{
return -BigInteger.Pow((BigInteger)2.0, 10000);
}
return (BigInteger)Math.Floor(x);
}
// shared_pseudocode.html#impl-shared.RoundTowardsZero.1
public static BigInteger RoundTowardsZero(decimal x)
{
@ -1091,6 +1152,398 @@ namespace Ryujinx.Tests.Cpu.Tester
}
#endregion
#region "functions/float/fpdecoderounding/"
/* shared_pseudocode.html#impl-shared.FPDecodeRounding.1 */
public static FPRounding FPDecodeRounding(Bits rmode)
{
switch (rmode)
{
default:
case Bits bits when bits == "00":
return FPRounding.FPRounding_TIEEVEN; // N
case Bits bits when bits == "01":
return FPRounding.FPRounding_POSINF; // P
case Bits bits when bits == "10":
return FPRounding.FPRounding_NEGINF; // M
case Bits bits when bits == "11":
return FPRounding.FPRounding_ZERO; // Z
}
}
#endregion
#region "functions/float/fpexc/"
// shared_pseudocode.html#FPExc
public enum FPExc {FPExc_InvalidOp, FPExc_DivideByZero, FPExc_Overflow,
FPExc_Underflow, FPExc_Inexact, FPExc_InputDenorm};
#endregion
#region "functions/float/fpprocessexception/"
// shared_pseudocode.html#impl-shared.FPProcessException.2
public static void FPProcessException(FPExc exception, Bits _fpcr)
{
Bits fpcr = new Bits(_fpcr); // Clone.
int cumul;
// Determine the cumulative exception bit number
switch (exception)
{
default:
case FPExc.FPExc_InvalidOp: cumul = 0; break;
case FPExc.FPExc_DivideByZero: cumul = 1; break;
case FPExc.FPExc_Overflow: cumul = 2; break;
case FPExc.FPExc_Underflow: cumul = 3; break;
case FPExc.FPExc_Inexact: cumul = 4; break;
case FPExc.FPExc_InputDenorm: cumul = 7; break;
}
int enable = cumul + 8;
if (fpcr[enable])
{
// Trapping of the exception enabled.
// It is IMPLEMENTATION DEFINED whether the enable bit may be set at all, and
// if so then how exceptions may be accumulated before calling FPTrapException()
/* IMPLEMENTATION_DEFINED "floating-point trap handling"; */
throw new NotImplementedException();
}/*
else if (UsingAArch32())
{
// Set the cumulative exception bit
FPSCR<cumul> = '1';
}*/
else
{
// Set the cumulative exception bit
FPSR[cumul] = true;
}
}
#endregion
#region "functions/float/fprounding/"
// shared_pseudocode.html#FPRounding
public enum FPRounding {FPRounding_TIEEVEN, FPRounding_POSINF,
FPRounding_NEGINF, FPRounding_ZERO,
FPRounding_TIEAWAY, FPRounding_ODD};
#endregion
#region "functions/float/fptofixed/"
/* shared_pseudocode.html#impl-shared.FPToFixed.5 */
public static Bits FPToFixed(int M, Bits op, int fbits, bool unsigned, Bits _fpcr, FPRounding rounding)
{
int N = op.Count;
/* assert N IN {16,32,64}; */
/* assert M IN {16,32,64}; */
/* assert fbits >= 0; */
/* assert rounding != FPRounding_ODD; */
Bits fpcr = new Bits(_fpcr); // Clone.
if (N == 16)
{
throw new NotImplementedException();
}
else if (N == 32)
{
// Unpack using fpcr to determine if subnormals are flushed-to-zero
(FPType type, bool sign, float value) = FPUnpack_32(op, fpcr);
// If NaN, set cumulative flag or take exception
if (type == FPType.FPType_SNaN || type == FPType.FPType_QNaN)
{
FPProcessException(FPExc.FPExc_InvalidOp, fpcr);
}
// Scale by fractional bits and produce integer rounded towards minus-infinity
value = value * MathF.Pow(2.0f, fbits);
BigInteger int_result = RoundDown_32(value);
float error = value - Real_32(int_result);
if (float.IsNaN(error))
{
error = 0.0f;
}
// Determine whether supplied rounding mode requires an increment
bool round_up;
switch (rounding)
{
default:
case FPRounding.FPRounding_TIEEVEN:
round_up = (error > 0.5f || (error == 0.5f && int_result.SubBigInteger(0)));
break;
case FPRounding.FPRounding_POSINF:
round_up = (error != 0.0f);
break;
case FPRounding.FPRounding_NEGINF:
round_up = false;
break;
case FPRounding.FPRounding_ZERO:
round_up = (error != 0.0f && int_result < (BigInteger)0);
break;
case FPRounding.FPRounding_TIEAWAY:
round_up = (error > 0.5f || (error == 0.5f && int_result >= (BigInteger)0));
break;
}
if (round_up)
{
int_result = int_result + 1;
}
// Generate saturated result and exceptions
(Bits result, bool overflow) = SatQ(int_result, M, unsigned);
if (overflow)
{
FPProcessException(FPExc.FPExc_InvalidOp, fpcr);
}
else if (error != 0.0f)
{
FPProcessException(FPExc.FPExc_Inexact, fpcr);
}
return result;
}
else /* if (N == 64) */
{
// Unpack using fpcr to determine if subnormals are flushed-to-zero
(FPType type, bool sign, double value) = FPUnpack_64(op, fpcr);
// If NaN, set cumulative flag or take exception
if (type == FPType.FPType_SNaN || type == FPType.FPType_QNaN)
{
FPProcessException(FPExc.FPExc_InvalidOp, fpcr);
}
// Scale by fractional bits and produce integer rounded towards minus-infinity
value = value * Math.Pow(2.0, fbits);
BigInteger int_result = RoundDown_64(value);
double error = value - Real_64(int_result);
if (double.IsNaN(error))
{
error = 0.0;
}
// Determine whether supplied rounding mode requires an increment
bool round_up;
switch (rounding)
{
default:
case FPRounding.FPRounding_TIEEVEN:
round_up = (error > 0.5 || (error == 0.5 && int_result.SubBigInteger(0)));
break;
case FPRounding.FPRounding_POSINF:
round_up = (error != 0.0);
break;
case FPRounding.FPRounding_NEGINF:
round_up = false;
break;
case FPRounding.FPRounding_ZERO:
round_up = (error != 0.0 && int_result < (BigInteger)0);
break;
case FPRounding.FPRounding_TIEAWAY:
round_up = (error > 0.5 || (error == 0.5 && int_result >= (BigInteger)0));
break;
}
if (round_up)
{
int_result = int_result + 1;
}
// Generate saturated result and exceptions
(Bits result, bool overflow) = SatQ(int_result, M, unsigned);
if (overflow)
{
FPProcessException(FPExc.FPExc_InvalidOp, fpcr);
}
else if (error != 0.0)
{
FPProcessException(FPExc.FPExc_Inexact, fpcr);
}
return result;
}
}
#endregion
#region "functions/float/fptype/"
// shared_pseudocode.html#FPType
public enum FPType {FPType_Nonzero, FPType_Zero, FPType_Infinity,
FPType_QNaN, FPType_SNaN};
#endregion
#region "functions/float/fpunpack/"
/* shared_pseudocode.html#impl-shared.FPUnpack.2 */
/* shared_pseudocode.html#impl-shared.FPUnpackBase.2 */
/*public static (FPType, bool, real) FPUnpack_16(Bits fpval, Bits _fpcr)
{
int N = fpval.Count;
// assert N == 16;
Bits fpcr = new Bits(_fpcr); // Clone.
fpcr[26] = false;
return FPUnpackBase_16(fpval, fpcr);
}*/
public static (FPType, bool, float) FPUnpack_32(Bits fpval, Bits _fpcr)
{
int N = fpval.Count;
/* assert N == 32; */
Bits fpcr = new Bits(_fpcr); // Clone.
FPType type;
float value;
bool sign = fpval[31];
Bits exp32 = fpval[30, 23];
Bits frac32 = fpval[22, 0];
if (IsZero(exp32))
{
// Produce zero if value is zero or flush-to-zero is selected.
if (IsZero(frac32) || fpcr[24])
{
type = FPType.FPType_Zero;
value = 0.0f;
// Denormalized input flushed to zero
if (!IsZero(frac32))
{
FPProcessException(FPExc.FPExc_InputDenorm, fpcr);
}
}
else
{
type = FPType.FPType_Nonzero;
value = MathF.Pow(2.0f, -126) * (Real_32(UInt(frac32)) * MathF.Pow(2.0f, -23));
}
}
else if (IsOnes(exp32))
{
if (IsZero(frac32))
{
type = FPType.FPType_Infinity;
/* value = 2.0^1000000; */
value = MathF.Pow(2.0f, 1000);
}
else
{
type = frac32[22] ? FPType.FPType_QNaN : FPType.FPType_SNaN;
value = 0.0f;
}
}
else
{
type = FPType.FPType_Nonzero;
value = MathF.Pow(2.0f, (int)UInt(exp32) - 127) * (1.0f + Real_32(UInt(frac32)) * MathF.Pow(2.0f, -23));
}
if (sign)
{
value = -value;
}
return (type, sign, value);
}
public static (FPType, bool, double) FPUnpack_64(Bits fpval, Bits _fpcr)
{
int N = fpval.Count;
/* assert N == 64; */
Bits fpcr = new Bits(_fpcr); // Clone.
FPType type;
double value;
bool sign = fpval[63];
Bits exp64 = fpval[62, 52];
Bits frac64 = fpval[51, 0];
if (IsZero(exp64))
{
// Produce zero if value is zero or flush-to-zero is selected.
if (IsZero(frac64) || fpcr[24])
{
type = FPType.FPType_Zero;
value = 0.0;
// Denormalized input flushed to zero
if (!IsZero(frac64))
{
FPProcessException(FPExc.FPExc_InputDenorm, fpcr);
}
}
else
{
type = FPType.FPType_Nonzero;
value = Math.Pow(2.0, -1022) * (Real_64(UInt(frac64)) * Math.Pow(2.0, -52));
}
}
else if (IsOnes(exp64))
{
if (IsZero(frac64))
{
type = FPType.FPType_Infinity;
/* value = 2.0^1000000; */
value = Math.Pow(2.0, 10000);
}
else
{
type = frac64[51] ? FPType.FPType_QNaN : FPType.FPType_SNaN;
value = 0.0;
}
}
else
{
type = FPType.FPType_Nonzero;
value = Math.Pow(2.0, (int)UInt(exp64) - 1023) * (1.0 + Real_64(UInt(frac64)) * Math.Pow(2.0, -52));
}
if (sign)
{
value = -value;
}
return (type, sign, value);
}
/* shared_pseudocode.html#impl-shared.FPUnpackCV.2 */
/* shared_pseudocode.html#impl-shared.FPUnpackBase.2 */
/*public static (FPType, bool, real) FPUnpackCV_16(Bits fpval, Bits _fpcr)
{
int N = fpval.Count;
// assert N == 16;
Bits fpcr = new Bits(_fpcr); // Clone.
fpcr[19] = false;
return FPUnpackBase_16(fpval, fpcr);
}*/
public static (FPType, bool, float) FPUnpackCV_32(Bits fpval, Bits _fpcr)
{
return FPUnpack_32(fpval, _fpcr);
}
public static (FPType, bool, double) FPUnpackCV_64(Bits fpval, Bits _fpcr)
{
return FPUnpack_64(fpval, _fpcr);
}
#endregion
#region "functions/integer/"
/* shared_pseudocode.html#impl-shared.AddWithCarry.3 */
public static (Bits, Bits) AddWithCarry(int N, Bits x, Bits y, bool carry_in)
@ -1117,7 +1570,12 @@ namespace Ryujinx.Tests.Cpu.Tester
public static Bits SP_EL0;
public static Bits SP_EL1;
public static Bits FPCR; // TODO: Add named fields.
// [ 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 22 | 21 20 | 19 | 18 17 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 ]
// [ 0 | 0 | 0 | 0 | 0 | AHP | DN | FZ | RMode | Stride | FZ16 | Len | IDE | 0 | 0 | IXE | UFE | OFE | DZE | IOE | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 ]
public static Bits FPSR; // TODO: Add named fields.
// [ 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 ]
// [ N | Z | C | V | QC | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | IDC | 0 | 0 | IXC | UFC | OFC | DZC | IOC ]
#endregion
#region "functions/system/"
@ -1178,6 +1636,8 @@ namespace Ryujinx.Tests.Cpu.Tester
/* shared_pseudocode.html#impl-shared.HaveEL.1 */
public static bool HaveEL(Bits el)
{
// TODO: Implement ASL: "IN" as C#: "Bits.In()".
/* if el IN {EL1,EL0} then */
if (el == EL1 || el == EL0)
{
return true; // EL1 and EL0 must exist

View file

@ -5,6 +5,10 @@
<RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<OutputType>Exe</OutputType>
<IsPackable>false</IsPackable>
<TargetOS Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">windows</TargetOS>
<TargetOS Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">osx</TargetOS>
<TargetOS Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">linux</TargetOS>
</PropertyGroup>
<PropertyGroup>
@ -20,6 +24,13 @@
<ItemGroup>
<ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
<ProjectReference Include="..\Ryujinx.Tests.Unicorn\Ryujinx.Tests.Unicorn.csproj" />
</ItemGroup>
<Target Name="CopyUnicorn" AfterTargets="Build">
<Copy SourceFiles="..\Ryujinx.Tests.Unicorn\libs\$(TargetOS)\unicorn.dll" DestinationFolder="$(OutputPath)" ContinueOnError="true" />
<Copy SourceFiles="..\Ryujinx.Tests.Unicorn\libs\$(TargetOS)\libunicorn.dylib" DestinationFolder="$(OutputPath)" ContinueOnError="true" />
<Copy SourceFiles="..\Ryujinx.Tests.Unicorn\libs\$(TargetOS)\libunicorn.so" DestinationFolder="$(OutputPath)" ContinueOnError="true" />
</Target>
</Project>

View file

@ -1,5 +1,6 @@
using Ryujinx.HLE.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
@ -7,6 +8,10 @@ namespace Ryujinx
{
static class ConsoleLog
{
private static Thread MessageThread;
private static BlockingCollection<LogEventArgs> MessageQueue;
private static Dictionary<LogLevel, ConsoleColor> LogColors;
private static object ConsoleLock;
@ -21,15 +26,39 @@ namespace Ryujinx
{ LogLevel.Error, ConsoleColor.Red }
};
MessageQueue = new BlockingCollection<LogEventArgs>();
ConsoleLock = new object();
MessageThread = new Thread(() =>
{
while (!MessageQueue.IsCompleted)
{
try
{
PrintLog(MessageQueue.Take());
}
catch (InvalidOperationException)
{
// IOE means that Take() was called on a completed collection.
// Some other thread can call CompleteAdding after we pass the
// IsCompleted check but before we call Take.
// We can simply catch the exception since the loop will break
// on the next iteration.
}
}
});
MessageThread.IsBackground = true;
MessageThread.Start();
}
public static void PrintLog(object sender, LogEventArgs e)
private static void PrintLog(LogEventArgs e)
{
string FormattedTime = e.Time.ToString(@"hh\:mm\:ss\.fff");
string CurrentThread = Thread.CurrentThread.ManagedThreadId.ToString("d4");
string Message = FormattedTime + " | " + CurrentThread + " " + e.Message;
if (LogColors.TryGetValue(e.Level, out ConsoleColor Color))
@ -47,5 +76,13 @@ namespace Ryujinx
Console.WriteLine(Message);
}
}
public static void Log(object sender, LogEventArgs e)
{
if (!MessageQueue.IsAddingCompleted)
{
MessageQueue.Add(e);
}
}
}
}

View file

@ -21,10 +21,10 @@ namespace Ryujinx
Switch Device = new Switch(Renderer, AudioOut);
Config.Read(Device);
Device.Hid.InitializeJoycons();
Device.Log.Updated += ConsoleLog.PrintLog;
Device.Log.Updated += ConsoleLog.Log;
if (args.Length == 1)
{