///////////////////////////////////////////////////////////////////////////// // // spucore - Emulates a single SPU CORE // ///////////////////////////////////////////////////////////////////////////// #ifndef EMU_COMPILE #error "Hi I forgot to set EMU_COMPILE" #endif #include "spucore.h" //////////////////////////////////////////////////////////////////////////////// /* ** Key-on defer ** ** Bit of a hack I did to stop the clicking in Final Fantasy Tactics ** If there's a key-on while the envelope is still active, the key-on is ** deferred by this many samples while the existing channel is microramped ** down to zero (works best if it's a power of 2) */ //#define KEYON_DEFER_SAMPLES (64) #define KEYON_DEFER_SAMPLES (64) // // Render max samples // #define RENDERMAX (200) //////////////////////////////////////////////////////////////////////////////// /* ** Static information */ static const sint16 gauss_shuffled_reverse_table[1024] = { 0x12c7, 0x59b3, 0x1307, 0xffff, 0x1288, 0x59b2, 0x1347, 0xffff, 0x1249, 0x59b0, 0x1388, 0xffff, 0x120b, 0x59ad, 0x13c9, 0xffff, 0x11cd, 0x59a9, 0x140b, 0xffff, 0x118f, 0x59a4, 0x144d, 0xffff, 0x1153, 0x599e, 0x1490, 0xffff, 0x1116, 0x5997, 0x14d4, 0xffff, 0x10db, 0x598f, 0x1517, 0xffff, 0x109f, 0x5986, 0x155c, 0xffff, 0x1065, 0x597c, 0x15a0, 0xffff, 0x102a, 0x5971, 0x15e6, 0xffff, 0x0ff1, 0x5965, 0x162c, 0xffff, 0x0fb7, 0x5958, 0x1672, 0xffff, 0x0f7f, 0x5949, 0x16b9, 0xffff, 0x0f46, 0x593a, 0x1700, 0xffff, 0x0f0f, 0x592a, 0x1747, 0x0000, 0x0ed7, 0x5919, 0x1790, 0x0000, 0x0ea1, 0x5907, 0x17d8, 0x0000, 0x0e6b, 0x58f4, 0x1821, 0x0000, 0x0e35, 0x58e0, 0x186b, 0x0000, 0x0e00, 0x58cb, 0x18b5, 0x0000, 0x0dcb, 0x58b5, 0x1900, 0x0000, 0x0d97, 0x589e, 0x194b, 0x0001, 0x0d63, 0x5886, 0x1996, 0x0001, 0x0d30, 0x586d, 0x19e2, 0x0001, 0x0cfd, 0x5853, 0x1a2e, 0x0001, 0x0ccb, 0x5838, 0x1a7b, 0x0002, 0x0c99, 0x581c, 0x1ac8, 0x0002, 0x0c68, 0x57ff, 0x1b16, 0x0002, 0x0c38, 0x57e2, 0x1b64, 0x0003, 0x0c07, 0x57c3, 0x1bb3, 0x0003, 0x0bd8, 0x57a3, 0x1c02, 0x0003, 0x0ba9, 0x5782, 0x1c51, 0x0004, 0x0b7a, 0x5761, 0x1ca1, 0x0004, 0x0b4c, 0x573e, 0x1cf1, 0x0005, 0x0b1e, 0x571b, 0x1d42, 0x0005, 0x0af1, 0x56f6, 0x1d93, 0x0006, 0x0ac4, 0x56d1, 0x1de5, 0x0007, 0x0a98, 0x56ab, 0x1e37, 0x0007, 0x0a6c, 0x5684, 0x1e89, 0x0008, 0x0a40, 0x565b, 0x1edc, 0x0009, 0x0a16, 0x5632, 0x1f2f, 0x0009, 0x09eb, 0x5609, 0x1f82, 0x000a, 0x09c1, 0x55de, 0x1fd6, 0x000b, 0x0998, 0x55b2, 0x202a, 0x000c, 0x096f, 0x5585, 0x207f, 0x000d, 0x0946, 0x5558, 0x20d4, 0x000e, 0x091e, 0x5529, 0x2129, 0x000f, 0x08f7, 0x54fa, 0x217f, 0x0010, 0x08d0, 0x54ca, 0x21d5, 0x0011, 0x08a9, 0x5499, 0x222c, 0x0012, 0x0883, 0x5467, 0x2282, 0x0013, 0x085d, 0x5434, 0x22da, 0x0015, 0x0838, 0x5401, 0x2331, 0x0016, 0x0813, 0x53cc, 0x2389, 0x0018, 0x07ef, 0x5397, 0x23e1, 0x0019, 0x07cb, 0x5361, 0x2439, 0x001b, 0x07a7, 0x532a, 0x2492, 0x001c, 0x0784, 0x52f3, 0x24eb, 0x001e, 0x0762, 0x52ba, 0x2545, 0x0020, 0x0740, 0x5281, 0x259e, 0x0021, 0x071e, 0x5247, 0x25f8, 0x0023, 0x06fd, 0x520c, 0x2653, 0x0025, 0x06dc, 0x51d0, 0x26ad, 0x0027, 0x06bb, 0x5194, 0x2708, 0x0029, 0x069b, 0x5156, 0x2763, 0x002c, 0x067c, 0x5118, 0x27be, 0x002e, 0x065c, 0x50da, 0x281a, 0x0030, 0x063e, 0x509a, 0x2876, 0x0033, 0x061f, 0x505a, 0x28d2, 0x0035, 0x0601, 0x5019, 0x292e, 0x0038, 0x05e4, 0x4fd7, 0x298b, 0x003a, 0x05c7, 0x4f95, 0x29e7, 0x003d, 0x05aa, 0x4f52, 0x2a44, 0x0040, 0x058e, 0x4f0e, 0x2aa1, 0x0043, 0x0572, 0x4ec9, 0x2aff, 0x0046, 0x0556, 0x4e84, 0x2b5c, 0x0049, 0x053b, 0x4e3e, 0x2bba, 0x004d, 0x0520, 0x4df7, 0x2c18, 0x0050, 0x0506, 0x4db0, 0x2c76, 0x0054, 0x04ec, 0x4d68, 0x2cd4, 0x0057, 0x04d2, 0x4d20, 0x2d33, 0x005b, 0x04b9, 0x4cd7, 0x2d91, 0x005f, 0x04a0, 0x4c8d, 0x2df0, 0x0063, 0x0488, 0x4c42, 0x2e4f, 0x0067, 0x0470, 0x4bf7, 0x2eae, 0x006b, 0x0458, 0x4bac, 0x2f0d, 0x006f, 0x0441, 0x4b5f, 0x2f6c, 0x0074, 0x042a, 0x4b13, 0x2fcc, 0x0078, 0x0413, 0x4ac5, 0x302b, 0x007d, 0x03fc, 0x4a77, 0x308b, 0x0082, 0x03e7, 0x4a29, 0x30ea, 0x0087, 0x03d1, 0x49d9, 0x314a, 0x008c, 0x03bc, 0x498a, 0x31aa, 0x0091, 0x03a7, 0x493a, 0x3209, 0x0096, 0x0392, 0x48e9, 0x3269, 0x009c, 0x037e, 0x4898, 0x32c9, 0x00a1, 0x036a, 0x4846, 0x3329, 0x00a7, 0x0356, 0x47f4, 0x3389, 0x00ad, 0x0343, 0x47a1, 0x33e9, 0x00b3, 0x0330, 0x474e, 0x3449, 0x00ba, 0x031d, 0x46fa, 0x34a9, 0x00c0, 0x030b, 0x46a6, 0x3509, 0x00c7, 0x02f9, 0x4651, 0x3569, 0x00cd, 0x02e7, 0x45fc, 0x35c9, 0x00d4, 0x02d6, 0x45a6, 0x3629, 0x00db, 0x02c4, 0x4550, 0x3689, 0x00e3, 0x02b4, 0x44fa, 0x36e8, 0x00ea, 0x02a3, 0x44a3, 0x3748, 0x00f2, 0x0293, 0x444c, 0x37a8, 0x00fa, 0x0283, 0x43f4, 0x3807, 0x0101, 0x0273, 0x439c, 0x3867, 0x010a, 0x0264, 0x4344, 0x38c6, 0x0112, 0x0255, 0x42eb, 0x3926, 0x011b, 0x0246, 0x4292, 0x3985, 0x0123, 0x0237, 0x4239, 0x39e4, 0x012c, 0x0229, 0x41df, 0x3a43, 0x0135, 0x021b, 0x4185, 0x3aa2, 0x013f, 0x020d, 0x412a, 0x3b00, 0x0148, 0x0200, 0x40d0, 0x3b5f, 0x0152, 0x01f2, 0x4074, 0x3bbd, 0x015c, 0x01e5, 0x4019, 0x3c1b, 0x0166, 0x01d9, 0x3fbd, 0x3c79, 0x0171, 0x01cc, 0x3f62, 0x3cd7, 0x017b, 0x01c0, 0x3f05, 0x3d35, 0x0186, 0x01b4, 0x3ea9, 0x3d92, 0x0191, 0x01a8, 0x3e4c, 0x3def, 0x019c, 0x019c, 0x3def, 0x3e4c, 0x01a8, 0x0191, 0x3d92, 0x3ea9, 0x01b4, 0x0186, 0x3d35, 0x3f05, 0x01c0, 0x017b, 0x3cd7, 0x3f62, 0x01cc, 0x0171, 0x3c79, 0x3fbd, 0x01d9, 0x0166, 0x3c1b, 0x4019, 0x01e5, 0x015c, 0x3bbd, 0x4074, 0x01f2, 0x0152, 0x3b5f, 0x40d0, 0x0200, 0x0148, 0x3b00, 0x412a, 0x020d, 0x013f, 0x3aa2, 0x4185, 0x021b, 0x0135, 0x3a43, 0x41df, 0x0229, 0x012c, 0x39e4, 0x4239, 0x0237, 0x0123, 0x3985, 0x4292, 0x0246, 0x011b, 0x3926, 0x42eb, 0x0255, 0x0112, 0x38c6, 0x4344, 0x0264, 0x010a, 0x3867, 0x439c, 0x0273, 0x0101, 0x3807, 0x43f4, 0x0283, 0x00fa, 0x37a8, 0x444c, 0x0293, 0x00f2, 0x3748, 0x44a3, 0x02a3, 0x00ea, 0x36e8, 0x44fa, 0x02b4, 0x00e3, 0x3689, 0x4550, 0x02c4, 0x00db, 0x3629, 0x45a6, 0x02d6, 0x00d4, 0x35c9, 0x45fc, 0x02e7, 0x00cd, 0x3569, 0x4651, 0x02f9, 0x00c7, 0x3509, 0x46a6, 0x030b, 0x00c0, 0x34a9, 0x46fa, 0x031d, 0x00ba, 0x3449, 0x474e, 0x0330, 0x00b3, 0x33e9, 0x47a1, 0x0343, 0x00ad, 0x3389, 0x47f4, 0x0356, 0x00a7, 0x3329, 0x4846, 0x036a, 0x00a1, 0x32c9, 0x4898, 0x037e, 0x009c, 0x3269, 0x48e9, 0x0392, 0x0096, 0x3209, 0x493a, 0x03a7, 0x0091, 0x31aa, 0x498a, 0x03bc, 0x008c, 0x314a, 0x49d9, 0x03d1, 0x0087, 0x30ea, 0x4a29, 0x03e7, 0x0082, 0x308b, 0x4a77, 0x03fc, 0x007d, 0x302b, 0x4ac5, 0x0413, 0x0078, 0x2fcc, 0x4b13, 0x042a, 0x0074, 0x2f6c, 0x4b5f, 0x0441, 0x006f, 0x2f0d, 0x4bac, 0x0458, 0x006b, 0x2eae, 0x4bf7, 0x0470, 0x0067, 0x2e4f, 0x4c42, 0x0488, 0x0063, 0x2df0, 0x4c8d, 0x04a0, 0x005f, 0x2d91, 0x4cd7, 0x04b9, 0x005b, 0x2d33, 0x4d20, 0x04d2, 0x0057, 0x2cd4, 0x4d68, 0x04ec, 0x0054, 0x2c76, 0x4db0, 0x0506, 0x0050, 0x2c18, 0x4df7, 0x0520, 0x004d, 0x2bba, 0x4e3e, 0x053b, 0x0049, 0x2b5c, 0x4e84, 0x0556, 0x0046, 0x2aff, 0x4ec9, 0x0572, 0x0043, 0x2aa1, 0x4f0e, 0x058e, 0x0040, 0x2a44, 0x4f52, 0x05aa, 0x003d, 0x29e7, 0x4f95, 0x05c7, 0x003a, 0x298b, 0x4fd7, 0x05e4, 0x0038, 0x292e, 0x5019, 0x0601, 0x0035, 0x28d2, 0x505a, 0x061f, 0x0033, 0x2876, 0x509a, 0x063e, 0x0030, 0x281a, 0x50da, 0x065c, 0x002e, 0x27be, 0x5118, 0x067c, 0x002c, 0x2763, 0x5156, 0x069b, 0x0029, 0x2708, 0x5194, 0x06bb, 0x0027, 0x26ad, 0x51d0, 0x06dc, 0x0025, 0x2653, 0x520c, 0x06fd, 0x0023, 0x25f8, 0x5247, 0x071e, 0x0021, 0x259e, 0x5281, 0x0740, 0x0020, 0x2545, 0x52ba, 0x0762, 0x001e, 0x24eb, 0x52f3, 0x0784, 0x001c, 0x2492, 0x532a, 0x07a7, 0x001b, 0x2439, 0x5361, 0x07cb, 0x0019, 0x23e1, 0x5397, 0x07ef, 0x0018, 0x2389, 0x53cc, 0x0813, 0x0016, 0x2331, 0x5401, 0x0838, 0x0015, 0x22da, 0x5434, 0x085d, 0x0013, 0x2282, 0x5467, 0x0883, 0x0012, 0x222c, 0x5499, 0x08a9, 0x0011, 0x21d5, 0x54ca, 0x08d0, 0x0010, 0x217f, 0x54fa, 0x08f7, 0x000f, 0x2129, 0x5529, 0x091e, 0x000e, 0x20d4, 0x5558, 0x0946, 0x000d, 0x207f, 0x5585, 0x096f, 0x000c, 0x202a, 0x55b2, 0x0998, 0x000b, 0x1fd6, 0x55de, 0x09c1, 0x000a, 0x1f82, 0x5609, 0x09eb, 0x0009, 0x1f2f, 0x5632, 0x0a16, 0x0009, 0x1edc, 0x565b, 0x0a40, 0x0008, 0x1e89, 0x5684, 0x0a6c, 0x0007, 0x1e37, 0x56ab, 0x0a98, 0x0007, 0x1de5, 0x56d1, 0x0ac4, 0x0006, 0x1d93, 0x56f6, 0x0af1, 0x0005, 0x1d42, 0x571b, 0x0b1e, 0x0005, 0x1cf1, 0x573e, 0x0b4c, 0x0004, 0x1ca1, 0x5761, 0x0b7a, 0x0004, 0x1c51, 0x5782, 0x0ba9, 0x0003, 0x1c02, 0x57a3, 0x0bd8, 0x0003, 0x1bb3, 0x57c3, 0x0c07, 0x0003, 0x1b64, 0x57e2, 0x0c38, 0x0002, 0x1b16, 0x57ff, 0x0c68, 0x0002, 0x1ac8, 0x581c, 0x0c99, 0x0002, 0x1a7b, 0x5838, 0x0ccb, 0x0001, 0x1a2e, 0x5853, 0x0cfd, 0x0001, 0x19e2, 0x586d, 0x0d30, 0x0001, 0x1996, 0x5886, 0x0d63, 0x0001, 0x194b, 0x589e, 0x0d97, 0x0000, 0x1900, 0x58b5, 0x0dcb, 0x0000, 0x18b5, 0x58cb, 0x0e00, 0x0000, 0x186b, 0x58e0, 0x0e35, 0x0000, 0x1821, 0x58f4, 0x0e6b, 0x0000, 0x17d8, 0x5907, 0x0ea1, 0x0000, 0x1790, 0x5919, 0x0ed7, 0x0000, 0x1747, 0x592a, 0x0f0f, 0xffff, 0x1700, 0x593a, 0x0f46, 0xffff, 0x16b9, 0x5949, 0x0f7f, 0xffff, 0x1672, 0x5958, 0x0fb7, 0xffff, 0x162c, 0x5965, 0x0ff1, 0xffff, 0x15e6, 0x5971, 0x102a, 0xffff, 0x15a0, 0x597c, 0x1065, 0xffff, 0x155c, 0x5986, 0x109f, 0xffff, 0x1517, 0x598f, 0x10db, 0xffff, 0x14d4, 0x5997, 0x1116, 0xffff, 0x1490, 0x599e, 0x1153, 0xffff, 0x144d, 0x59a4, 0x118f, 0xffff, 0x140b, 0x59a9, 0x11cd, 0xffff, 0x13c9, 0x59ad, 0x120b, 0xffff, 0x1388, 0x59b0, 0x1249, 0xffff, 0x1347, 0x59b2, 0x1288, 0xffff, 0x1307, 0x59b3, 0x12c7, }; static sint32 ratelogtable[32+128]; /* // v1.10 coefs - normalized from v1.04 static const sint32 reverb_lowpass_coefs[8] = { (int)((-0.036346113709214548)*(2048.0)), (int)(( 0.044484956332843419)*(2048.0)), (int)(( 0.183815456609675380)*(2048.0)), (int)(( 0.308045700766695740)*(2048.0)), (int)(( 0.308045700766695740)*(2048.0)), (int)(( 0.183815456609675380)*(2048.0)), (int)(( 0.044484956332843419)*(2048.0)), (int)((-0.036346113709214548)*(2048.0)) }; */ /* // test coefs - ganked from LAME's blackman function // (11-point filter, but only a few points were nonzero) static const sint32 reverb_new_lowpass_coefs[3] = { (int)((-0.02134438446523164)*(2048.0)), // skip one (int)(( 0.27085135668587779)*(2048.0)), (int)(( 0.50098605555870768)*(2048.0)) // rest are symmetrical }; */ // PS1 reverb downsampling coefficients(as best as I could extract them at the moment, some of the even(non-zero/non-16384) ones *might* be off by 1. // ------------- #if 1 static const sint32 reverb_psx_lowpass_coefs[11] = { -1, // 0, 2, // 0, -10, // 0, 35, // 0, -103, // 0, 266, // 0, -616, // 0, 1332, // 0, -2960, // 0, 10246, 16384 }; #else static const sint32 reverb_psx_lowpass_coefs[48] = { -1, 0, 2, 0, -10, 0, 35, 0, -103, 0, 266, 0, -616, 0, 1332, 0, -2960, 0, 10246, 16384, 10246, 0, -2960, 0, 1332, 0, -616, 0, 266, 0, -103, 0, 35, 0, -10, 0, 2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; #endif static const int noisetable[] = { 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1 }; /* ** Static init */ sint32 EMU_CALL spucore_init(void) { sint32 i; memset(ratelogtable, 0, sizeof(ratelogtable)); ratelogtable[32-8] = 1; ratelogtable[32-7] = 1; ratelogtable[32-6] = 1; ratelogtable[32-5] = 1; ratelogtable[32-4] = 2; ratelogtable[32-3] = 2; ratelogtable[32-2] = 3; ratelogtable[32-1] = 3; ratelogtable[32+0] = 4; ratelogtable[32+1] = 5; ratelogtable[32+2] = 6; ratelogtable[32+3] = 7; for(i = 4; i < 128; i++) { uint32 n = 2*ratelogtable[32+i-4]; if(n > 0x20000000) n = 0x20000000; ratelogtable[32+i] = n; } return 0; } //////////////////////////////////////////////////////////////////////////////// /* ** State information */ #define SPUCORESTATE ((struct SPUCORE_STATE*)(state)) #define SAMPLE_STATE_OFF (0) #define SAMPLE_STATE_ENDING (1) #define SAMPLE_STATE_ON (2) struct SPUCORE_SAMPLE { uint8 state; uint8 array_cleared; sint32 array[32]; uint32 phase; uint32 block_addr; uint32 start_block_addr; //uint32 start_loop_block_addr; uint32 loop_block_addr; }; #define ENVELOPE_STATE_OFF (0) #define ENVELOPE_STATE_ATTACK (1) #define ENVELOPE_STATE_DECAY (2) #define ENVELOPE_STATE_SUSTAIN (3) #define ENVELOPE_STATE_RELEASE (4) struct SPUCORE_ENVELOPE { uint32 reg_ad; uint32 reg_sr; sint32 level; sint32 delta; int state; sint32 cachemax; }; //////////////////////////////////////////////////////////////////////////////// /* ** Volumes with increase/decrease modes */ struct SPUCORE_VOLUME { uint16 mode; sint32 level; }; static EMU_INLINE void EMU_CALL volume_setmode(struct SPUCORE_VOLUME *vol, uint16 mode) { vol->mode = mode; if(mode & 0x8000) { } else { vol->level = mode; vol->level <<= 17; vol->level >>= 1; } } static EMU_INLINE uint16 EMU_CALL volume_getmode(struct SPUCORE_VOLUME *vol) { return vol->mode; } static EMU_INLINE sint32 EMU_CALL volume_getlevel(struct SPUCORE_VOLUME *vol) { return (vol->level) >> 15; } //////////////////////////////////////////////////////////////////////////////// struct SPUCORE_CHAN { struct SPUCORE_VOLUME vol[2]; uint32 voice_pitch; struct SPUCORE_SAMPLE sample; struct SPUCORE_ENVELOPE env; int samples_until_pending_keyon; }; /* ** Reverb resample state */ struct SPUCORE_REVERB_RESAMPLER { sint32 in_queue_l[64]; sint32 in_queue_r[64]; sint32 out_queue_l[16]; sint32 out_queue_r[16]; int queue_index; }; struct SPUCORE_REVERB { uint32 FB_SRC_A ; uint32 FB_SRC_B ; uint16 IIR_ALPHA ; uint16 ACC_COEF_A ; uint16 ACC_COEF_B ; uint16 ACC_COEF_C ; uint16 ACC_COEF_D ; uint16 IIR_COEF ; uint16 FB_ALPHA ; uint16 FB_X ; uint32 IIR_DEST_A0; uint32 IIR_DEST_A1; uint32 ACC_SRC_A0 ; uint32 ACC_SRC_A1 ; uint32 ACC_SRC_B0 ; uint32 ACC_SRC_B1 ; uint32 IIR_SRC_A0 ; uint32 IIR_SRC_A1 ; uint32 IIR_DEST_B0; uint32 IIR_DEST_B1; uint32 ACC_SRC_C0 ; uint32 ACC_SRC_C1 ; uint32 ACC_SRC_D0 ; uint32 ACC_SRC_D1 ; uint32 IIR_SRC_B1 ; uint32 IIR_SRC_B0 ; uint32 MIX_DEST_A0; uint32 MIX_DEST_A1; uint32 MIX_DEST_B0; uint32 MIX_DEST_B1; uint16 IN_COEF_L ; uint16 IN_COEF_R ; uint32 start_address; uint32 end_address; sint32 current_address; sint32 safe_start_address; sint32 safe_end_address; sint32 safe_size; struct SPUCORE_REVERB_RESAMPLER resampler; }; struct SPUCORE_STATE { uint32 flags; sint32 memsize; struct SPUCORE_CHAN chan[24]; struct SPUCORE_REVERB reverb; struct SPUCORE_VOLUME mvol[2]; sint16 evol[2]; sint16 avol[2]; sint16 bvol[2]; uint32 kon; uint32 koff; uint32 fm; uint32 noise; uint32 vmix[2]; uint32 vmixe[2]; uint32 irq_address; uint32 noiseclock; uint32 noisecounter; sint32 noiseval; uint32 irq_decoder_clock; uint32 irq_triggered_cycle; }; struct SPUCORE_IRQ_STATE { uint32 offset; uint32 triggered_cycle; }; uint32 EMU_CALL spucore_get_state_size(void) { return(sizeof(struct SPUCORE_STATE)); } /* ** Initialize SPU CORE state */ void EMU_CALL spucore_clear_state(void *state) { /* ** Clear to zero */ memset(state, 0, sizeof(struct SPUCORE_STATE)); /* ** Set other defaults */ SPUCORESTATE->memsize = 0x80000; spucore_setreg(state, SPUREG_EEA, 0x7FFFF, 0xFFFFFFFF); spucore_setreg(state, SPUREG_VMIX, 0x00FFFFFF, 0xFFFFFFFF); spucore_setflag(state, SPUREG_FLAG_MSNDL , 1); spucore_setflag(state, SPUREG_FLAG_MSNDR , 1); spucore_setflag(state, SPUREG_FLAG_MSNDEL, 1); spucore_setflag(state, SPUREG_FLAG_MSNDER, 1); spucore_setflag(state, SPUREG_FLAG_SINL, 1); spucore_setflag(state, SPUREG_FLAG_SINR, 1); SPUCORESTATE->irq_triggered_cycle = 0xFFFFFFFF; } void EMU_CALL spucore_set_mem_size(void *state, uint32 size) { SPUCORESTATE->memsize = (sint32)size; spucore_setreg(state, SPUREG_EEA, size-1, 0xFFFFFFFF); } //////////////////////////////////////////////////////////////////////////////// static void EMU_CALL make_safe_reverb_addresses(struct SPUCORE_STATE *state) { struct SPUCORE_REVERB *r = &(state->reverb); sint32 sa = r->start_address; sint32 ea = r->end_address; //EMUTRACE2("[sa,ea=%X,%X]\n",sa,ea); ea += 0x20000; ea &= (~0x1FFFF); sa &= (~1); if(ea > state->memsize) ea = state->memsize; if(ea < 0x20000) ea = 0x20000; if(sa > ea) { sa &= 0x1FFFE; sa += ea; sa -= 0x20000; } r->safe_start_address = sa; r->safe_end_address = ea; r->safe_size = ea-sa; r->current_address &= (~1); if( (r->current_address < r->safe_start_address) || (r->current_address >= r->safe_end_address) ) { r->current_address = r->safe_start_address; } } //////////////////////////////////////////////////////////////////////////////// /* ** ADPCM sample decoding */ /* #define SPUCORE_PREDICT_SKEL(prednum,coef1,coef2) \ static void EMU_CALL spucore_predict_##prednum(uint8 *src, sint32 *dest, uint32 shift) { \ uint32 i; \ sint32 p_a = dest[-2]; \ sint32 p_b = dest[-1]; \ shift += 16; \ for(i = 0; i < 14; i++) { \ sint32 a = src[i^(EMU_ENDIAN_XOR(1))]; \ sint32 b = (a&0xF0)<<24; \ a = a << 28; b >>= shift; a >>= shift; \ a += ( ( ((coef1)*p_b) + ((coef2)*p_a) )+32)>>6; \ if(a > 32767) { a = 32767; } if(a < (-32768)) { a = (-32768); } \ *dest++ = a; \ b += ( ( ((coef1)* a ) + ((coef2)*p_b) )+32)>>6; \ if(b > 32767) { b = 32767; } if(b < (-32768)) { b = (-32768); } \ *dest++ = b; \ p_a = a; p_b = b; \ } \ } */ #define SPUCORE_PREDICT_SKEL(prednum,coef1,coef2) \ static void EMU_CALL spucore_predict_##prednum(uint16 *src, sint32 *dest, uint32 shift) { \ uint32 i; \ sint32 p_a = dest[-2]; \ sint32 p_b = dest[-1]; \ shift += 16; \ for(i = 0; i < 7; i++) { \ sint32 a = *src++; \ sint32 b = (a&0x00F0)<<24; \ sint32 c = (a&0x0F00)<<20; \ sint32 d = (a&0xF000)<<16; \ a <<= 28; \ a >>= shift; \ b >>= shift; \ c >>= shift; \ d >>= shift; \ a += ( ( ((coef1)*p_b) + ((coef2)*p_a) )+32)>>6; \ if(a > 32767) { a = 32767; } if(a < (-32768)) { a = (-32768); } \ *dest++ = a; \ b += ( ( ((coef1)* a ) + ((coef2)*p_b) )+32)>>6; \ if(b > 32767) { b = 32767; } if(b < (-32768)) { b = (-32768); } \ *dest++ = b; \ c += ( ( ((coef1)* b ) + ((coef2)* a ) )+32)>>6; \ if(b > 32767) { b = 32767; } if(b < (-32768)) { b = (-32768); } \ *dest++ = c; \ d += ( ( ((coef1)* c ) + ((coef2)* b ) )+32)>>6; \ if(b > 32767) { b = 32767; } if(b < (-32768)) { b = (-32768); } \ *dest++ = d; \ p_a = c; p_b = d; \ } \ } SPUCORE_PREDICT_SKEL(0,0,0) SPUCORE_PREDICT_SKEL(1,60,0) SPUCORE_PREDICT_SKEL(2,115,-52) SPUCORE_PREDICT_SKEL(3,98,-55) SPUCORE_PREDICT_SKEL(4,122,-60) typedef void (EMU_CALL * spucore_predict_callback_t)(uint16*, sint32*, uint32); static spucore_predict_callback_t spucore_predict[8] = { spucore_predict_0, spucore_predict_1, spucore_predict_2, spucore_predict_3, spucore_predict_4, spucore_predict_1, spucore_predict_2, spucore_predict_3 }; static void EMU_CALL decode_sample_block( uint16 *ram, uint32 memmax, struct SPUCORE_SAMPLE *sample, int skip ) { // uint32 i; if(sample->state != SAMPLE_STATE_ON) { if(!sample->array_cleared) { memset(sample->array, 0, sizeof(sample->array)); sample->array_cleared = 1; } sample->state = SAMPLE_STATE_OFF; } else { /* temporary hack to avoid memory problems */ sample->block_addr &= (memmax-1); if((sample->block_addr + 0x10) > memmax) sample->block_addr -= 0x10; ram += sample->block_addr >> 1; /* decode */ if(skip) { if(!sample->array_cleared) { memset(sample->array, 0, sizeof(sample->array)); sample->array_cleared = 1; } } else { sample->array[0] = sample->array[28]; sample->array[1] = sample->array[29]; sample->array[2] = sample->array[30]; sample->array[3] = sample->array[31]; (spucore_predict[(ram[0]>>4)&7])( ram + 1, sample->array + 4, ram[0] & 0xF ); } /* set loop start address if necessary */ if(ram[0]&0x0400) { sample->loop_block_addr = sample->block_addr; } /* sample end? */ if(ram[0]&0x0100) { /* loop? */ if(ram[0]&0x0200) { sample->block_addr = sample->loop_block_addr; /* no loop, just end */ } else { sample->state = SAMPLE_STATE_ENDING; } } else { /* advance to next block */ sample->block_addr += 16; } } } //////////////////////////////////////////////////////////////////////////////// // // Returns the number of samples actually generated // If dest is null, it means "fast forward" - don't render anything // // // output of resampler() is guaranteed clipped, as long as the output of // decode_sample_block() is // static uint32 EMU_CALL resampler( uint16 *ram, uint32 memmax, struct SPUCORE_SAMPLE *sample, sint32 *dest, uint32 n, uint32 phase_inc, struct SPUCORE_IRQ_STATE *irq_state ) { uint32 irq_triggered_cycle = 0xFFFFFFFF; uint32 s; uint32 ph; //, phl; ph = sample->phase; if(!dest) { ph += phase_inc * n; s = 0; while(ph >= 0x1C000) { if(sample->state == SAMPLE_STATE_OFF) break; if(irq_state && irq_state->offset - sample->block_addr < 16 && irq_triggered_cycle == 0xFFFFFFFF) { irq_triggered_cycle = s; } decode_sample_block(ram, memmax, sample, 1); s += phase_inc * 28; ph -= 0x1C000; } s = n; } else { uint32 t = 0; for(s = 0; s < n; s++) { sint32 *source_signal; sint16 *mygauss; sint32 sum; if(ph >= 0x1C000) { if(sample->state == SAMPLE_STATE_OFF) break; if(irq_state && irq_state->offset - sample->block_addr < 16 && irq_triggered_cycle == 0xFFFFFFFF) { irq_triggered_cycle = t; } decode_sample_block(ram, memmax, sample, 0); ph -= 0x1C000; } source_signal = sample->array + (ph >> 12); mygauss = (sint16*) (((uint8*)gauss_shuffled_reverse_table) + ((ph & 0xFF0) >> 1)); { sum = (source_signal[0] * mygauss[0]) + (source_signal[1] * mygauss[1]) + (source_signal[2] * mygauss[2]) + (source_signal[3] * mygauss[3]); } sum >>= 15; *dest++ = sum; ph += phase_inc; t += phase_inc; } } if(irq_state && irq_triggered_cycle != 0xFFFFFFFF) { irq_triggered_cycle = (irq_triggered_cycle * 768) >> 12; if(irq_triggered_cycle < irq_state->triggered_cycle) irq_state->triggered_cycle = irq_triggered_cycle; } sample->phase = ph; return s; } //////////////////////////////////////////////////////////////////////////////// static uint32 EMU_CALL resampler_modulated( uint16 *ram, uint32 memmax, struct SPUCORE_SAMPLE *sample, sint32 *dest, uint32 n, uint32 phase_inc, sint32 *fmbuf, struct SPUCORE_IRQ_STATE *irq_state ) { uint32 irq_triggered_cycle = 0xFFFFFFFF; uint32 s, t = 0; uint32 ph; //, phl; uint32 pimod; ph = sample->phase; if(!dest) { for(s = 0; s < n; s++) { pimod = ((*fmbuf++ + 32768) * phase_inc) / 32768; if(pimod > 0x3FFF) pimod = 0x3FFF; else if(pimod < 1) pimod = 1; ph += pimod; while(ph >= 0x1C000) { if(sample->state == SAMPLE_STATE_OFF) break; if(irq_state && irq_state->offset - sample->block_addr < 16 && irq_triggered_cycle == 0xFFFFFFFF) { irq_triggered_cycle = t; } decode_sample_block(ram, memmax, sample, 1); ph -= 0x1C000; } t += pimod; } } else { for(s = 0; s < n; s++) { sint32 *source_signal; sint16 *mygauss; sint32 sum; if(ph >= 0x1C000) { if(sample->state == SAMPLE_STATE_OFF) break; if(irq_state && irq_state->offset - sample->block_addr < 16 && irq_triggered_cycle == 0xFFFFFFFF) { irq_triggered_cycle = t; } decode_sample_block(ram, memmax, sample, 0); ph -= 0x1C000; } source_signal = sample->array + (ph >> 12); mygauss = (sint16*) (((uint8*)gauss_shuffled_reverse_table) + ((ph & 0xFF0) >> 1)); { sum = (source_signal[0] * mygauss[0]) + (source_signal[1] * mygauss[1]) + (source_signal[2] * mygauss[2]) + (source_signal[3] * mygauss[3]); } sum >>= 15; *dest++ = sum; pimod = ((*fmbuf++ + 32768) * phase_inc) / 32768; if(pimod > 0x3FFF) pimod = 0x3FFF; else if(pimod < 1) pimod = 1; ph += pimod; t += pimod; } } if(irq_state && irq_triggered_cycle != 0xFFFFFFFF) { irq_triggered_cycle = (irq_triggered_cycle * 768) >> 12; if(irq_triggered_cycle < irq_state->triggered_cycle) irq_state->triggered_cycle = irq_triggered_cycle; } sample->phase = ph; return s; } //////////////////////////////////////////////////////////////////////////////// #define MY_AM (((env->reg_ad)>>15)&0x01) #define MY_AR (((env->reg_ad)>> 8)&0x7F) #define MY_DR (((env->reg_ad)>> 4)&0x0F) #define MY_SL (((env->reg_ad)>> 0)&0x0F) #define MY_SM (((env->reg_sr)>>15)&0x01) #define MY_SD (((env->reg_sr)>>14)&0x01) #define MY_SR (((env->reg_sr)>> 6)&0x7F) #define MY_RM (((env->reg_sr)>> 5)&0x01) #define MY_RR (((env->reg_sr)>> 0)&0x1F) /* ** - Sets the current envelope slope ** - Returns the max number of samples that can be processed at the current ** slope */ static EMU_INLINE sint32 EMU_CALL envelope_do(struct SPUCORE_ENVELOPE *env) { sint32 target = 0; /* ** Clip envelope value in case it wrapped around */ switch(((env->level) >> 30) & 3) { case 2: env->level = 0x7FFFFFFF; break; case 3: env->level = 0x00000000; break; } switch(env->state) { case ENVELOPE_STATE_ATTACK : goto attack; case ENVELOPE_STATE_DECAY : goto decay; case ENVELOPE_STATE_SUSTAIN: goto sustain; case ENVELOPE_STATE_RELEASE: goto release; default: env->state = ENVELOPE_STATE_OFF; env->level = 0; env->delta = 0; return 1; } attack: if(env->level == 0x7FFFFFFF) { env->state = ENVELOPE_STATE_DECAY; goto decay; } /* log */ if(MY_AM) { if(env->level < 0x60000000) { target = 0x60000000; env->delta = ratelogtable[32+(MY_AR^0x7F)-0x10]; } else { target = 0x7FFFFFFF; env->delta = ratelogtable[32+(MY_AR^0x7F)-0x18]; } /* linear */ } else { target = 0x7FFFFFFF; env->delta = ratelogtable[32+(MY_AR^0x7F)-0x10]; } goto domax; decay: if((((env->level)>>27)&0xF) <= ((sint32)(MY_SL))) { env->state = ENVELOPE_STATE_SUSTAIN; goto sustain; } target = env->level & (~0x07FFFFFF); switch(((env->level)>>28)&0x7) { case 0: env->delta = -ratelogtable[32+(4*(MY_DR^0x1F))-0x18+0]; break; case 1: env->delta = -ratelogtable[32+(4*(MY_DR^0x1F))-0x18+4]; break; case 2: env->delta = -ratelogtable[32+(4*(MY_DR^0x1F))-0x18+6]; break; case 3: env->delta = -ratelogtable[32+(4*(MY_DR^0x1F))-0x18+8]; break; case 4: env->delta = -ratelogtable[32+(4*(MY_DR^0x1F))-0x18+9]; break; case 5: env->delta = -ratelogtable[32+(4*(MY_DR^0x1F))-0x18+10]; break; case 6: env->delta = -ratelogtable[32+(4*(MY_DR^0x1F))-0x18+11]; break; case 7: env->delta = -ratelogtable[32+(4*(MY_DR^0x1F))-0x18+12]; break; } goto domax; sustain: if(!MY_SD) { if(env->level == 0x7FFFFFFF) { env->delta = 0; return 0x7FFFFFFF; } /* log */ if(MY_SM) { if(env->level < 0x60000000) { target = 0x60000000; env->delta = ratelogtable[32+(MY_SR^0x7F)-0x10]; } else { target = 0x7FFFFFFF; env->delta = ratelogtable[32+(MY_SR^0x7F)-0x18]; } /* linear */ } else { target = 0x7FFFFFFF; env->delta = ratelogtable[32+(MY_SR^0x7F)-0x10]; } } else { if(env->level == 0x00000000) { env->delta = 0; return 0x7FFFFFFF; } /* log */ if(MY_SM) { target = ((env->level)&(~0x0FFFFFFF)); switch(((env->level)>>28)&0x7) { case 0: env->delta = -ratelogtable[32+(MY_SR^0x7F)-0x1B+0]; break; case 1: env->delta = -ratelogtable[32+(MY_SR^0x7F)-0x1B+4]; break; case 2: env->delta = -ratelogtable[32+(MY_SR^0x7F)-0x1B+6]; break; case 3: env->delta = -ratelogtable[32+(MY_SR^0x7F)-0x1B+8]; break; case 4: env->delta = -ratelogtable[32+(MY_SR^0x7F)-0x1B+9]; break; case 5: env->delta = -ratelogtable[32+(MY_SR^0x7F)-0x1B+10]; break; case 6: env->delta = -ratelogtable[32+(MY_SR^0x7F)-0x1B+11]; break; case 7: env->delta = -ratelogtable[32+(MY_SR^0x7F)-0x1B+12]; break; } /* linear */ } else { target = 0x00000000; env->delta = -ratelogtable[32+(MY_SR^0x7F)-0x0F]; } } goto domax; release: if(env->level == 0) { env->delta = 0; env->state = ENVELOPE_STATE_OFF; return 1; } /* log */ if(MY_RM) { target = ((env->level) & (~0x0FFFFFFF)); switch(((env->level)>>28)&0x7) { case 0: env->delta = -ratelogtable[32+(4*(MY_RR^0x1F))-0x18+0]; break; case 1: env->delta = -ratelogtable[32+(4*(MY_RR^0x1F))-0x18+4]; break; case 2: env->delta = -ratelogtable[32+(4*(MY_RR^0x1F))-0x18+6]; break; case 3: env->delta = -ratelogtable[32+(4*(MY_RR^0x1F))-0x18+8]; break; case 4: env->delta = -ratelogtable[32+(4*(MY_RR^0x1F))-0x18+9]; break; case 5: env->delta = -ratelogtable[32+(4*(MY_RR^0x1F))-0x18+10]; break; case 6: env->delta = -ratelogtable[32+(4*(MY_RR^0x1F))-0x18+11]; break; case 7: env->delta = -ratelogtable[32+(4*(MY_RR^0x1F))-0x18+12]; break; } /* linear */ } else { target = 0; env->delta = -ratelogtable[32+(4*(MY_RR^0x1F))-0x0C]; } goto domax; domax: { sint32 max; if(env->delta) { max = (target - (env->level)) / (env->delta); } else { max = 0x7FFFFFFF; } max--; if(max < 1) max = 1; return max; } } //////////////////////////////////////////////////////////////////////////////// static void EMU_CALL envelope_prime(struct SPUCORE_ENVELOPE *env) { // if(env->state != ENVELOPE_STATE_OFF) { //if(env->state!=ENVELOPE_STATE_RELEASE){ // OutputDebugString("envelope re-prime\n"); //} // } //EMUTRACE2("[eprime %04X %04X]",env->reg_ad_x,env->reg_sr_x); env->level = 1; env->state = ENVELOPE_STATE_ATTACK; env->delta = 1; env->cachemax = 0; } static void EMU_CALL envelope_release(struct SPUCORE_ENVELOPE *env) { if(env->state != ENVELOPE_STATE_OFF) { env->state = ENVELOPE_STATE_RELEASE; } env->cachemax = 0; } //////////////////////////////////////////////////////////////////////////////// static void EMU_CALL sample_prime(struct SPUCORE_SAMPLE *sample) { sample->state = SAMPLE_STATE_ON; memset(sample->array, 0, sizeof(sample->array)); sample->array_cleared = 1; sample->phase = 0x1C000; // ensure it grabs the first block right away //EMUTRACE2("[sprime %08X %08X]", sample->start_block_addr, sample->start_loop_block_addr); sample->block_addr = sample->start_block_addr; //sample->loop_block_addr = sample->start_loop_block_addr; } //////////////////////////////////////////////////////////////////////////////// static void EMU_CALL voice_on(struct SPUCORE_CHAN *c) { // FOR DEBUGGING PURPOSES ONLY. // if(c->sample.state == SAMPLE_STATE_ON)return; // if(c->env.state != ENVELOPE_STATE_OFF)return; /* ** Defer if already on */ if(c->env.state != ENVELOPE_STATE_OFF) { //EMUTRACE0("alreadyon:"); if(!(c->samples_until_pending_keyon)) { //EMUTRACE0("defer"); c->samples_until_pending_keyon = KEYON_DEFER_SAMPLES; } else { //EMUTRACE0("was already deferred"); } } else { // EMUTRACE0("prime"); sample_prime(&(c->sample)); envelope_prime(&(c->env)); } // EMUTRACE0("\n"); } static void EMU_CALL voice_off(struct SPUCORE_CHAN *c) { //EMUTRACE0("release"); envelope_release(&(c->env)); //EMUTRACE0("\n"); } //////////////////////////////////////////////////////////////////////////////// static void EMU_CALL voices_on(struct SPUCORE_STATE *state, uint32 bits) { int a; for(a = 0; a < 24; a++) { if(bits & 1) { // EMUTRACE1("voiceon %2d:",a); //EMUTRACE2("[vol %08X %08X]",state->chan[a].vol[0].level,state->chan[a].vol[1].level); //EMUTRACE2("[mvol %08X %08X]",state->mvol[0].level,state->mvol[1].level); ////EMUTRACE2("[avol %08X %08X]",state->avol[0],state->avol[1]); //EMUTRACE2("[evol %08X %08X]",state->evol[0],state->evol[1]); // sint32 extvol_l = ((sint16)(state->avol[0])); // sint32 extvol_r = ((sint16)(state->avol[1])); voice_on(state->chan + a); } bits >>= 1; } } static void EMU_CALL voices_off(struct SPUCORE_STATE *state, uint32 bits) { int a; for(a = 0; a < 24; a++) { if(bits & 1) { //EMUTRACE1("voiceoff %2d:",a); voice_off(state->chan + a); } bits >>= 1; } } //////////////////////////////////////////////////////////////////////////////// /* ** - Scales samples in a buffer using an envelope ** - Returns the actual number of samples modified ** ** All UNMODIFIED samples must then be discarded ** (they're supposed to be zero) */ static int EMU_CALL enveloper( struct SPUCORE_ENVELOPE *env, sint32 *buf, int samples ) { int i = 0; while(i < samples) { sint32 e, d, max; if(env->state == ENVELOPE_STATE_OFF) break; max = env->cachemax; if(!max) { max = envelope_do(env); env->cachemax = max; } e = env->level; d = env->delta; if(max < 1) max = 1; if(max > (samples - i)) { max = samples - i; } env->cachemax -= max; if(!buf) { i += max; e += max * d; } else { while(max--) { sint32 b = buf[i]; b *= (e >> 16); b >>= 15; buf[i] = b; e += d; i++; } } env->level = e; env->delta = d; } return i; } //////////////////////////////////////////////////////////////////////////////// // // - Calls the resampler and enveloper // - Returns the number of samples actually generated // (can be 0 if the channel is silent) // // If buf is null, it means "fast forward" - don't render anything // // // output of render_channel_raw() is guaranteed clipped as long as // resampler() and enveloper() are also // static int EMU_CALL render_channel_raw( uint16 *ram, uint32 memmax, struct SPUCORE_CHAN *c, sint32 *buf, sint32 *fmbuf, sint32 *nbuf, int samples, struct SPUCORE_IRQ_STATE *irq_state ) { int r = samples; /* If the envelope is dead, don't bother anyway */ if((c->env.state) == ENVELOPE_STATE_OFF) return 0; /* Do resampling */ if (fmbuf) r = resampler_modulated(ram, memmax, &(c->sample), buf, r, c->voice_pitch, fmbuf, irq_state); else r = resampler(ram, memmax, &(c->sample), buf, r, c->voice_pitch, irq_state); if(nbuf) { r = samples; if(buf) memcpy(buf, nbuf, 4 * r); } /* Do enveloping */ r = enveloper(&(c->env), buf, r); /* If we were cut short by _either_, then the envelope state must be set ** to OFF */ if(r < samples) c->env.state = ENVELOPE_STATE_OFF; return r; } // // This is the new render_channel which processes deferred key-ons // // // output of render_channel_mono() is guaranteed clipped as long as // render_channel_raw() is also // static int EMU_CALL render_channel_mono( uint16 *ram, uint32 memmax, struct SPUCORE_CHAN *c, sint32 *buf, sint32 *fmbuf, sint32 *nbuf, sint32 samples, struct SPUCORE_IRQ_STATE *irq_state ) { sint32 n; sint32 r, r2; sint32 defer_remaining; struct SPUCORE_IRQ_STATE spare_state; //top: n = c->samples_until_pending_keyon; if(!n) { return render_channel_raw(ram, memmax, c, buf, fmbuf, nbuf, samples, irq_state); } // // Don't defer key-on if fast-forwarding // this caused a STUCK-NOTE BUG. should not use. // // if(!buf) { // c->samples_until_pending_keyon = 0; // goto top; // } if(n > samples) { n = samples; } /* ** r = how many samples we actually will process */ r = render_channel_raw(ram, memmax, c, buf, fmbuf, nbuf, n, irq_state); defer_remaining = c->samples_until_pending_keyon; if(buf) { sint32 i; for(i = 0; i < r; i++) { (*buf) = ((*buf)*defer_remaining) / KEYON_DEFER_SAMPLES; buf++; defer_remaining--; } } else { defer_remaining -= r; } if(fmbuf) fmbuf += r; if(nbuf) nbuf += r; /* ** if render_channel_raw got cut short, then we're done anyway. */ if(r < n) defer_remaining = 0; c->samples_until_pending_keyon = defer_remaining; /* ** Process the key-on if necessary */ if(!defer_remaining) { sample_prime(&(c->sample)); envelope_prime(&(c->env)); } /* ** we already handled r samples */ samples -= r; /* ** if there are any remaining samples to render, do them here */ r2 = 0; if(samples) { struct SPUCORE_IRQ_STATE *s = NULL; if(irq_state) { s = &spare_state; spare_state.offset = irq_state->offset; } r2 = render_channel_raw(ram, memmax, c, buf, fmbuf, nbuf, samples, s); if(irq_state && irq_state->triggered_cycle == 0xFFFFFFFF && spare_state.triggered_cycle != 0xFFFFFFFF) irq_state->triggered_cycle = spare_state.triggered_cycle + r * 768; } return r + r2; } //////////////////////////////////////////////////////////////////////////////// // // Generates a buffer worth of noise data, ganked from Eternal SPU // static void EMU_CALL render_noise( struct SPUCORE_STATE *state, sint32 *buf, sint32 samples ) { int n; uint32 noisecounter = state->noisecounter; sint32 noiseval = state->noiseval; uint32 noiseinc = (uint16)(0x8000 >> (state->noiseclock << 6)); for(n = 0; n < samples; n++) { noisecounter += noiseinc; noiseval += noisecounter + noisecounter + noisetable[(noisecounter >> 10) & 63]; if (noiseval < -32767) noiseval = -32767; else if (noiseval > 32767) noiseval = 32767; if (buf) *buf++ = noiseval; } state->noisecounter = noisecounter; state->noiseval = noiseval; } //////////////////////////////////////////////////////////////////////////////// #define MAKE_SINT32_COEF(x) ((sint32)((sint16)(state->reverb.x))) #define MAKE_REVERB_OFFSET(x) (state->reverb.x) #define NORMALIZE_REVERB_OFFSET(x) {while(x>=state->reverb.safe_end_address){x-=state->reverb.safe_size;}while(xreverb.safe_start_address){x+=state->reverb.safe_size;}} #define RAM_PCM_SAMPLE(x) (*((sint16*)(((uint8*)ram) + (x)))) #define RAM_SINT32_SAMPLE(x) ((sint32)(RAM_PCM_SAMPLE(x))) /* ** Multiplying -32768 by -32768 and scaling by 15 bits is not safe ** (the sign would get flipped) ** so we should clip to -32767 instead */ #define CLIP_PCM_1(x) {if(x>32767){x=32767;}else if(x<(-32767)){x=(-32767);}} #define CLIP_PCM_2(a,b) {CLIP_PCM_1(a);CLIP_PCM_1(b);} #define CLIP_PCM_4(a,b,c,d) {CLIP_PCM_1(a);CLIP_PCM_1(b);CLIP_PCM_1(c);CLIP_PCM_1(d);} //#define CLIP_PCMDBL_1(x) {if(x>(32767*2)){x=(32767*2);}else if(x<(-(32767*2))){x=(-(32767*2));}} //#define CLIP_PCMDBL_2(a,b) {CLIP_PCMDBL_1(a);CLIP_PCMDBL_1(b);} //#define CLIP_PCMDBL_4(a,b,c,d) {CLIP_PCMDBL_1(a);CLIP_PCMDBL_1(b);CLIP_PCMDBL_1(c);CLIP_PCMDBL_1(d);} //////////////////////////////////////////////////////////////////////////////// /* ** 22KHz reverb steady state step */ static void EMU_CALL reverb_steadystate22(struct SPUCORE_STATE *state, uint16 *ram, sint32 input_l, sint32 input_r) { /* ** Current reverb offset */ sint32 current = state->reverb.current_address; /* ** Reverb registers */ sint32 fb_src_a = MAKE_REVERB_OFFSET(FB_SRC_A); sint32 fb_src_b = MAKE_REVERB_OFFSET(FB_SRC_B); sint32 iir_alpha = MAKE_SINT32_COEF(IIR_ALPHA); sint32 acc_coef_a = MAKE_SINT32_COEF(ACC_COEF_A); sint32 acc_coef_b = MAKE_SINT32_COEF(ACC_COEF_B); sint32 acc_coef_c = MAKE_SINT32_COEF(ACC_COEF_C); sint32 acc_coef_d = MAKE_SINT32_COEF(ACC_COEF_D); sint32 iir_coef = MAKE_SINT32_COEF(IIR_COEF); sint32 fb_alpha = MAKE_SINT32_COEF(FB_ALPHA); sint32 fb_x = MAKE_SINT32_COEF(FB_X); sint32 iir_dest_a0 = MAKE_REVERB_OFFSET(IIR_DEST_A0); sint32 iir_dest_a1 = MAKE_REVERB_OFFSET(IIR_DEST_A1); sint32 acc_src_a0 = MAKE_REVERB_OFFSET(ACC_SRC_A0); sint32 acc_src_a1 = MAKE_REVERB_OFFSET(ACC_SRC_A1); sint32 acc_src_b0 = MAKE_REVERB_OFFSET(ACC_SRC_B0); sint32 acc_src_b1 = MAKE_REVERB_OFFSET(ACC_SRC_B1); sint32 iir_src_a0 = MAKE_REVERB_OFFSET(IIR_SRC_A0); sint32 iir_src_a1 = MAKE_REVERB_OFFSET(IIR_SRC_A1); sint32 iir_dest_b0 = MAKE_REVERB_OFFSET(IIR_DEST_B0); sint32 iir_dest_b1 = MAKE_REVERB_OFFSET(IIR_DEST_B1); sint32 acc_src_c0 = MAKE_REVERB_OFFSET(ACC_SRC_C0); sint32 acc_src_c1 = MAKE_REVERB_OFFSET(ACC_SRC_C1); sint32 acc_src_d0 = MAKE_REVERB_OFFSET(ACC_SRC_D0); sint32 acc_src_d1 = MAKE_REVERB_OFFSET(ACC_SRC_D1); sint32 iir_src_b1 = MAKE_REVERB_OFFSET(IIR_SRC_B1); sint32 iir_src_b0 = MAKE_REVERB_OFFSET(IIR_SRC_B0); sint32 mix_dest_a0 = MAKE_REVERB_OFFSET(MIX_DEST_A0); sint32 mix_dest_a1 = MAKE_REVERB_OFFSET(MIX_DEST_A1); sint32 mix_dest_b0 = MAKE_REVERB_OFFSET(MIX_DEST_B0); sint32 mix_dest_b1 = MAKE_REVERB_OFFSET(MIX_DEST_B1); sint32 in_coef_l = MAKE_SINT32_COEF(IN_COEF_L); sint32 in_coef_r = MAKE_SINT32_COEF(IN_COEF_R); /* ** Alternate buffer positions */ sint32 fb_src_a0; sint32 fb_src_a1; sint32 fb_src_b0; sint32 fb_src_b1; sint32 iir_dest_a0_plus; sint32 iir_dest_a1_plus; sint32 iir_dest_b0_plus; sint32 iir_dest_b1_plus; /* ** Intermediate results */ sint32 acc0; sint32 acc1; sint32 iir_input_a0; sint32 iir_input_a1; sint32 iir_input_b0; sint32 iir_input_b1; sint32 iir_a0; sint32 iir_a1; sint32 iir_b0; sint32 iir_b1; sint32 fb_a0; sint32 fb_a1; sint32 fb_b0; sint32 fb_b1; sint32 mix_a0; sint32 mix_a1; sint32 mix_b0; sint32 mix_b1; /* ** Offsets */ mix_dest_a0 += current; NORMALIZE_REVERB_OFFSET(mix_dest_a0); mix_dest_a1 += current; NORMALIZE_REVERB_OFFSET(mix_dest_a1); mix_dest_b0 += current; NORMALIZE_REVERB_OFFSET(mix_dest_b0); mix_dest_b1 += current; NORMALIZE_REVERB_OFFSET(mix_dest_b1); fb_src_a0 = mix_dest_a0 - fb_src_a; NORMALIZE_REVERB_OFFSET(fb_src_a0); fb_src_a1 = mix_dest_a1 - fb_src_a; NORMALIZE_REVERB_OFFSET(fb_src_a1); fb_src_b0 = mix_dest_b0 - fb_src_b; NORMALIZE_REVERB_OFFSET(fb_src_b0); fb_src_b1 = mix_dest_b1 - fb_src_b; NORMALIZE_REVERB_OFFSET(fb_src_b1); acc_src_a0 += current; NORMALIZE_REVERB_OFFSET(acc_src_a0); acc_src_a1 += current; NORMALIZE_REVERB_OFFSET(acc_src_a1); acc_src_b0 += current; NORMALIZE_REVERB_OFFSET(acc_src_b0); acc_src_b1 += current; NORMALIZE_REVERB_OFFSET(acc_src_b1); acc_src_c0 += current; NORMALIZE_REVERB_OFFSET(acc_src_c0); acc_src_c1 += current; NORMALIZE_REVERB_OFFSET(acc_src_c1); acc_src_d0 += current; NORMALIZE_REVERB_OFFSET(acc_src_d0); acc_src_d1 += current; NORMALIZE_REVERB_OFFSET(acc_src_d1); iir_src_a0 += current; NORMALIZE_REVERB_OFFSET(iir_src_a0); iir_src_a1 += current; NORMALIZE_REVERB_OFFSET(iir_src_a1); iir_src_b0 += current; NORMALIZE_REVERB_OFFSET(iir_src_b0); iir_src_b1 += current; NORMALIZE_REVERB_OFFSET(iir_src_b1); iir_dest_a0 += current; NORMALIZE_REVERB_OFFSET(iir_dest_a0); iir_dest_a1 += current; NORMALIZE_REVERB_OFFSET(iir_dest_a1); iir_dest_b0 += current; NORMALIZE_REVERB_OFFSET(iir_dest_b0); iir_dest_b1 += current; NORMALIZE_REVERB_OFFSET(iir_dest_b1); iir_dest_a0_plus = iir_dest_a0 + 2; NORMALIZE_REVERB_OFFSET(iir_dest_a0_plus); iir_dest_a1_plus = iir_dest_a1 + 2; NORMALIZE_REVERB_OFFSET(iir_dest_a1_plus); iir_dest_b0_plus = iir_dest_b0 + 2; NORMALIZE_REVERB_OFFSET(iir_dest_b0_plus); iir_dest_b1_plus = iir_dest_b1 + 2; NORMALIZE_REVERB_OFFSET(iir_dest_b1_plus); /* ** IIR */ CLIP_PCM_2(input_l,input_r); input_l *= in_coef_l; input_r *= in_coef_r; #define OPPOSITE_IIR_ALPHA (32768-iir_alpha) iir_input_a0 = ((RAM_SINT32_SAMPLE(iir_src_a0) * iir_coef) + input_l) >> 15; iir_input_a1 = ((RAM_SINT32_SAMPLE(iir_src_a1) * iir_coef) + input_r) >> 15; iir_input_b0 = ((RAM_SINT32_SAMPLE(iir_src_b0) * iir_coef) + input_l) >> 15; iir_input_b1 = ((RAM_SINT32_SAMPLE(iir_src_b1) * iir_coef) + input_r) >> 15; CLIP_PCM_4(iir_input_a0,iir_input_a1,iir_input_b0,iir_input_b1); iir_a0 = ((iir_input_a0 * iir_alpha) + (RAM_SINT32_SAMPLE(iir_dest_a0) * (OPPOSITE_IIR_ALPHA))) >> 15; iir_a1 = ((iir_input_a1 * iir_alpha) + (RAM_SINT32_SAMPLE(iir_dest_a1) * (OPPOSITE_IIR_ALPHA))) >> 15; iir_b0 = ((iir_input_b0 * iir_alpha) + (RAM_SINT32_SAMPLE(iir_dest_b0) * (OPPOSITE_IIR_ALPHA))) >> 15; iir_b1 = ((iir_input_b1 * iir_alpha) + (RAM_SINT32_SAMPLE(iir_dest_b1) * (OPPOSITE_IIR_ALPHA))) >> 15; CLIP_PCM_4(iir_a0,iir_a1,iir_b0,iir_b1); RAM_PCM_SAMPLE(iir_dest_a0_plus) = iir_a0; RAM_PCM_SAMPLE(iir_dest_a1_plus) = iir_a1; RAM_PCM_SAMPLE(iir_dest_b0_plus) = iir_b0; RAM_PCM_SAMPLE(iir_dest_b1_plus) = iir_b1; /* ** Accumulators */ acc0 = ((RAM_SINT32_SAMPLE(acc_src_a0) * acc_coef_a) >> 15) + ((RAM_SINT32_SAMPLE(acc_src_b0) * acc_coef_b) >> 15) + ((RAM_SINT32_SAMPLE(acc_src_c0) * acc_coef_c) >> 15) + ((RAM_SINT32_SAMPLE(acc_src_d0) * acc_coef_d) >> 15); acc1 = ((RAM_SINT32_SAMPLE(acc_src_a1) * acc_coef_a) >> 15) + ((RAM_SINT32_SAMPLE(acc_src_b1) * acc_coef_b) >> 15) + ((RAM_SINT32_SAMPLE(acc_src_c1) * acc_coef_c) >> 15) + ((RAM_SINT32_SAMPLE(acc_src_d1) * acc_coef_d) >> 15); CLIP_PCM_2(acc0,acc1); /* ** Feedback */ fb_a0 = RAM_SINT32_SAMPLE(fb_src_a0); fb_a1 = RAM_SINT32_SAMPLE(fb_src_a1); fb_b0 = RAM_SINT32_SAMPLE(fb_src_b0); fb_b1 = RAM_SINT32_SAMPLE(fb_src_b1); mix_a0 = acc0 - ((fb_a0*fb_alpha)>>15); mix_a1 = acc1 - ((fb_a1*fb_alpha)>>15); mix_b0 = fb_alpha*acc0; mix_b1 = fb_alpha*acc1; fb_alpha = ((sint32)((sint16)(((sint16)fb_alpha)^0x8000))); mix_b0 -= fb_a0*fb_alpha; mix_b1 -= fb_a1*fb_alpha; mix_b0 -= fb_b0*fb_x; mix_b1 -= fb_b1*fb_x; mix_b0>>=15; mix_b1>>=15; CLIP_PCM_4(mix_a0,mix_a1,mix_b0,mix_b1); RAM_PCM_SAMPLE(mix_dest_a0) = mix_a0; RAM_PCM_SAMPLE(mix_dest_a1) = mix_a1; RAM_PCM_SAMPLE(mix_dest_b0) = mix_b0; RAM_PCM_SAMPLE(mix_dest_b1) = mix_b1; } //////////////////////////////////////////////////////////////////////////////// /* ** 22KHz reverb engine */ static void EMU_CALL reverb_engine22(struct SPUCORE_STATE *state, uint16 *ram, sint32 *l, sint32 *r) { sint32 input_l = *l; sint32 input_r = *r; sint32 output_l; sint32 output_r; sint32 mix_dest_a0 = state->reverb.current_address + MAKE_REVERB_OFFSET(MIX_DEST_A0); sint32 mix_dest_a1 = state->reverb.current_address + MAKE_REVERB_OFFSET(MIX_DEST_A1); sint32 mix_dest_b0 = state->reverb.current_address + MAKE_REVERB_OFFSET(MIX_DEST_B0); sint32 mix_dest_b1 = state->reverb.current_address + MAKE_REVERB_OFFSET(MIX_DEST_B1); NORMALIZE_REVERB_OFFSET(mix_dest_a0); NORMALIZE_REVERB_OFFSET(mix_dest_a1); NORMALIZE_REVERB_OFFSET(mix_dest_b0); NORMALIZE_REVERB_OFFSET(mix_dest_b1); /* ** (Scale these down for now - avoids some clipping) */ input_l *= 2; input_r *= 2; input_l /= 3; input_r /= 3; /* ** Execute steady state step if necessary */ if(state->flags & SPUREG_FLAG_REVERB_ENABLE) { reverb_steadystate22(state, ram, input_l, input_r); } /* ** Retrieve wet out L/R ** (pretty certain this is done AFTER the steady state step) */ { int al = RAM_SINT32_SAMPLE(mix_dest_a0); int ar = RAM_SINT32_SAMPLE(mix_dest_a1); int bl = RAM_SINT32_SAMPLE(mix_dest_b0); int br = RAM_SINT32_SAMPLE(mix_dest_b1); output_l = al + bl; output_r = ar + br; } *l = output_l; *r = output_r; /* ** Advance reverb buffer position */ state->reverb.current_address += 2; if(state->reverb.current_address >= state->reverb.safe_end_address) { state->reverb.current_address = state->reverb.safe_start_address; } } //////////////////////////////////////////////////////////////////////////////// static void EMU_CALL reverb_process(struct SPUCORE_STATE *state, uint16 *ram, sint32 *buf, int samples) { int q = state->reverb.resampler.queue_index; /* ** Sample loop */ while(samples--) { /* ** Get an input sample */ sint32 l = buf[0]; sint32 r = buf[1]; /* ** Put it in the input queue */ state->reverb.resampler.in_queue_l[q & 63] = l; state->reverb.resampler.in_queue_r[q & 63] = r; /* ** If we're ready to create another output sample... */ if(q & 1) { /* ** Lowpass/downsample */ #if 1 l = (state->reverb.resampler.in_queue_l[(q - 38) & 63]) * reverb_psx_lowpass_coefs[0] + (state->reverb.resampler.in_queue_l[(q - 36) & 63]) * reverb_psx_lowpass_coefs[1] + (state->reverb.resampler.in_queue_l[(q - 34) & 63]) * reverb_psx_lowpass_coefs[2] + (state->reverb.resampler.in_queue_l[(q - 32) & 63]) * reverb_psx_lowpass_coefs[3] + (state->reverb.resampler.in_queue_l[(q - 30) & 63]) * reverb_psx_lowpass_coefs[4] + (state->reverb.resampler.in_queue_l[(q - 28) & 63]) * reverb_psx_lowpass_coefs[5] + (state->reverb.resampler.in_queue_l[(q - 26) & 63]) * reverb_psx_lowpass_coefs[6] + (state->reverb.resampler.in_queue_l[(q - 24) & 63]) * reverb_psx_lowpass_coefs[7] + (state->reverb.resampler.in_queue_l[(q - 22) & 63]) * reverb_psx_lowpass_coefs[8] + (state->reverb.resampler.in_queue_l[(q - 20) & 63]) * reverb_psx_lowpass_coefs[9] + (state->reverb.resampler.in_queue_l[(q - 19) & 63]) * reverb_psx_lowpass_coefs[10] + (state->reverb.resampler.in_queue_l[(q - 18) & 63]) * reverb_psx_lowpass_coefs[9] + (state->reverb.resampler.in_queue_l[(q - 16) & 63]) * reverb_psx_lowpass_coefs[8] + (state->reverb.resampler.in_queue_l[(q - 14) & 63]) * reverb_psx_lowpass_coefs[7] + (state->reverb.resampler.in_queue_l[(q - 12) & 63]) * reverb_psx_lowpass_coefs[6] + (state->reverb.resampler.in_queue_l[(q - 10) & 63]) * reverb_psx_lowpass_coefs[5] + (state->reverb.resampler.in_queue_l[(q - 8) & 63]) * reverb_psx_lowpass_coefs[4] + (state->reverb.resampler.in_queue_l[(q - 6) & 63]) * reverb_psx_lowpass_coefs[3] + (state->reverb.resampler.in_queue_l[(q - 4) & 63]) * reverb_psx_lowpass_coefs[2] + (state->reverb.resampler.in_queue_l[(q - 2) & 63]) * reverb_psx_lowpass_coefs[1] + (state->reverb.resampler.in_queue_l[(q - 0) & 63]) * reverb_psx_lowpass_coefs[0]; r = (state->reverb.resampler.in_queue_r[(q - 38) & 63]) * reverb_psx_lowpass_coefs[0] + (state->reverb.resampler.in_queue_r[(q - 36) & 63]) * reverb_psx_lowpass_coefs[1] + (state->reverb.resampler.in_queue_r[(q - 34) & 63]) * reverb_psx_lowpass_coefs[2] + (state->reverb.resampler.in_queue_r[(q - 32) & 63]) * reverb_psx_lowpass_coefs[3] + (state->reverb.resampler.in_queue_r[(q - 30) & 63]) * reverb_psx_lowpass_coefs[4] + (state->reverb.resampler.in_queue_r[(q - 28) & 63]) * reverb_psx_lowpass_coefs[5] + (state->reverb.resampler.in_queue_r[(q - 26) & 63]) * reverb_psx_lowpass_coefs[6] + (state->reverb.resampler.in_queue_r[(q - 24) & 63]) * reverb_psx_lowpass_coefs[7] + (state->reverb.resampler.in_queue_r[(q - 22) & 63]) * reverb_psx_lowpass_coefs[8] + (state->reverb.resampler.in_queue_r[(q - 20) & 63]) * reverb_psx_lowpass_coefs[9] + (state->reverb.resampler.in_queue_r[(q - 19) & 63]) * reverb_psx_lowpass_coefs[10] + (state->reverb.resampler.in_queue_r[(q - 18) & 63]) * reverb_psx_lowpass_coefs[9] + (state->reverb.resampler.in_queue_r[(q - 16) & 63]) * reverb_psx_lowpass_coefs[8] + (state->reverb.resampler.in_queue_r[(q - 14) & 63]) * reverb_psx_lowpass_coefs[7] + (state->reverb.resampler.in_queue_r[(q - 12) & 63]) * reverb_psx_lowpass_coefs[6] + (state->reverb.resampler.in_queue_r[(q - 10) & 63]) * reverb_psx_lowpass_coefs[5] + (state->reverb.resampler.in_queue_r[(q - 8) & 63]) * reverb_psx_lowpass_coefs[4] + (state->reverb.resampler.in_queue_r[(q - 6) & 63]) * reverb_psx_lowpass_coefs[3] + (state->reverb.resampler.in_queue_r[(q - 4) & 63]) * reverb_psx_lowpass_coefs[2] + (state->reverb.resampler.in_queue_r[(q - 2) & 63]) * reverb_psx_lowpass_coefs[1] + (state->reverb.resampler.in_queue_r[(q - 0) & 63]) * reverb_psx_lowpass_coefs[0]; #else l = 0; r = 0; for (n = 47; n >= 0; n -= 8) { l += (state->reverb.resampler.in_queue_l[(q - n + 0) & 63]) * reverb_psx_lowpass_coefs[n - 0] + (state->reverb.resampler.in_queue_l[(q - n + 1) & 63]) * reverb_psx_lowpass_coefs[n - 1] + (state->reverb.resampler.in_queue_l[(q - n + 2) & 63]) * reverb_psx_lowpass_coefs[n - 2] + (state->reverb.resampler.in_queue_l[(q - n + 3) & 63]) * reverb_psx_lowpass_coefs[n - 3] + (state->reverb.resampler.in_queue_l[(q - n + 4) & 63]) * reverb_psx_lowpass_coefs[n - 4] + (state->reverb.resampler.in_queue_l[(q - n + 5) & 63]) * reverb_psx_lowpass_coefs[n - 5] + (state->reverb.resampler.in_queue_l[(q - n + 6) & 63]) * reverb_psx_lowpass_coefs[n - 6] + (state->reverb.resampler.in_queue_l[(q - n + 7) & 63]) * reverb_psx_lowpass_coefs[n - 7]; r += (state->reverb.resampler.in_queue_r[(q - n + 0) & 63]) * reverb_psx_lowpass_coefs[n - 0] + (state->reverb.resampler.in_queue_r[(q - n + 1) & 63]) * reverb_psx_lowpass_coefs[n - 1] + (state->reverb.resampler.in_queue_r[(q - n + 2) & 63]) * reverb_psx_lowpass_coefs[n - 2] + (state->reverb.resampler.in_queue_r[(q - n + 3) & 63]) * reverb_psx_lowpass_coefs[n - 3] + (state->reverb.resampler.in_queue_r[(q - n + 4) & 63]) * reverb_psx_lowpass_coefs[n - 4] + (state->reverb.resampler.in_queue_r[(q - n + 5) & 63]) * reverb_psx_lowpass_coefs[n - 5] + (state->reverb.resampler.in_queue_r[(q - n + 6) & 63]) * reverb_psx_lowpass_coefs[n - 6] + (state->reverb.resampler.in_queue_r[(q - n + 7) & 63]) * reverb_psx_lowpass_coefs[n - 7]; } #endif l >>= 15; r >>= 15; /* l = (state->reverb.resampler.in_queue_l[(q - 6) & 7]) * reverb_new_lowpass_coefs[0] + (state->reverb.resampler.in_queue_l[(q - 4) & 7]) * reverb_new_lowpass_coefs[1] + (state->reverb.resampler.in_queue_l[(q - 3) & 7]) * reverb_new_lowpass_coefs[2] + (state->reverb.resampler.in_queue_l[(q - 2) & 7]) * reverb_new_lowpass_coefs[1] + (state->reverb.resampler.in_queue_l[(q - 0) & 7]) * reverb_new_lowpass_coefs[0]; l >>= 11; r = (state->reverb.resampler.in_queue_r[(q - 6) & 7]) * reverb_new_lowpass_coefs[0] + (state->reverb.resampler.in_queue_r[(q - 4) & 7]) * reverb_new_lowpass_coefs[1] + (state->reverb.resampler.in_queue_r[(q - 3) & 7]) * reverb_new_lowpass_coefs[2] + (state->reverb.resampler.in_queue_r[(q - 2) & 7]) * reverb_new_lowpass_coefs[1] + (state->reverb.resampler.in_queue_r[(q - 0) & 7]) * reverb_new_lowpass_coefs[0]; r >>= 11; */ //l = state->reverb.resampler.in_queue_l[q & 7]; //r = state->reverb.resampler.in_queue_r[q & 7]; /* ** Run the reverb engine */ reverb_engine22(state, ram, &l, &r); /* ** Put the new stuff into the output queue */ state->reverb.resampler.out_queue_l[q & 15] = l; state->reverb.resampler.out_queue_r[q & 15] = r; } /* ** Upsample */ /* ** Upsample using the same technique as for ADPCM samples ** (may or may not be right, sounds okay though) */ #define gauss_table_0x000 gauss_shuffled_reverse_table[0x200] #define gauss_table_0x100 gauss_shuffled_reverse_table[0x201] #define gauss_table_0x200 gauss_shuffled_reverse_table[0x202] #define gauss_table_0x300 gauss_shuffled_reverse_table[0x203] #define gauss_table_0x080 gauss_shuffled_reverse_table[0x000] #define gauss_table_0x180 gauss_shuffled_reverse_table[0x001] #define gauss_table_0x280 gauss_shuffled_reverse_table[0x002] #define gauss_table_0x380 gauss_shuffled_reverse_table[0x003] if(q & 1) { l = (state->reverb.resampler.out_queue_l[(q - 6) & 15]) * gauss_table_0x080 + (state->reverb.resampler.out_queue_l[(q - 4) & 15]) * gauss_table_0x180 + (state->reverb.resampler.out_queue_l[(q - 2) & 15]) * gauss_table_0x280 + (state->reverb.resampler.out_queue_l[(q - 0) & 15]) * gauss_table_0x380; l >>= 15; r = (state->reverb.resampler.out_queue_r[(q - 6) & 15]) * gauss_table_0x080 + (state->reverb.resampler.out_queue_r[(q - 4) & 15]) * gauss_table_0x180 + (state->reverb.resampler.out_queue_r[(q - 2) & 15]) * gauss_table_0x280 + (state->reverb.resampler.out_queue_r[(q - 0) & 15]) * gauss_table_0x380; r >>= 15; } else { l = (state->reverb.resampler.out_queue_l[(q - 7) & 15]) * gauss_table_0x000 + (state->reverb.resampler.out_queue_l[(q - 5) & 15]) * gauss_table_0x100 + (state->reverb.resampler.out_queue_l[(q - 3) & 15]) * gauss_table_0x200 + (state->reverb.resampler.out_queue_l[(q - 1) & 15]) * gauss_table_0x300; l >>= 15; r = (state->reverb.resampler.out_queue_r[(q - 7) & 15]) * gauss_table_0x000 + (state->reverb.resampler.out_queue_r[(q - 5) & 15]) * gauss_table_0x100 + (state->reverb.resampler.out_queue_r[(q - 3) & 15]) * gauss_table_0x200 + (state->reverb.resampler.out_queue_r[(q - 1) & 15]) * gauss_table_0x300; r >>= 15; } /* ** Advance queue position, write output, all that good stuff */ q++; buf[0] = l; buf[1] = r; buf += 2; } state->reverb.resampler.queue_index = q; } //////////////////////////////////////////////////////////////////////////////// //int spucore_frq[RENDERMAX]; /* ** Renderer */ static void EMU_CALL render(struct SPUCORE_STATE *state, uint16 *ram, sint16 *buf, sint16 *extinput, sint32 samples, uint8 mainout, uint8 effectout) { uint32 chanbit; uint32 maskmain_l; uint32 maskmain_r; uint32 maskverb_l; uint32 maskverb_r; uint32 masknoise; uint32 maskfm; sint32 ibuf [ RENDERMAX]; sint32 ibufmix[2*RENDERMAX]; sint32 ibufrvb[2*RENDERMAX]; sint32 ibufn [ RENDERMAX]; sint32 ibuffm [ RENDERMAX]; int ch, i; sint32 m_v_l; sint32 m_v_r; sint32 r_v_l; sint32 r_v_r; struct SPUCORE_IRQ_STATE irq_state; struct SPUCORE_IRQ_STATE *irq_state_ptr; //spucore_frq[samples]++; irq_state_ptr = NULL; irq_state.triggered_cycle = 0xFFFFFFFF; if (state->flags & SPUREG_FLAG_IRQ_ENABLE) { if (state->memsize == 0x80000 && state->irq_address < 0x1000) { uint32 irq_address_masked = state->irq_address & 0x3FF; uint32 irq_sample_offset = irq_address_masked - state->irq_decoder_clock; if(irq_sample_offset > 0x3FF) irq_sample_offset += 0x400; if (irq_sample_offset < (uint32)samples) { irq_state.triggered_cycle = irq_sample_offset * 768; } } else { irq_state_ptr = &irq_state; irq_state.offset = state->irq_address; } } state->irq_decoder_clock = (state->irq_decoder_clock + samples) & 0x3FF; memset(ibufmix, 0, 8 * samples); if(effectout) { memset(ibufrvb, 0, 8 * samples); } maskmain_l = 0; maskmain_r = 0; maskverb_l = 0; maskverb_r = 0; if(state->flags & SPUREG_FLAG_MSNDL) maskmain_l = state->vmix[0] & 0xFFFFFF; if(state->flags & SPUREG_FLAG_MSNDR) maskmain_r = state->vmix[1] & 0xFFFFFF; if(effectout) { if(state->flags & SPUREG_FLAG_MSNDEL) maskverb_l = state->vmixe[0] & 0xFFFFFF; if(state->flags & SPUREG_FLAG_MSNDER) maskverb_r = state->vmixe[1] & 0xFFFFFF; } masknoise = state->noise; render_noise(state, masknoise ? ibufn : NULL, samples); maskfm = state->fm & 0xFFFFFE; if(!mainout) { maskmain_l = 0; maskmain_r = 0; } for(ch = 0, chanbit = 1; ch < 24; ch++, chanbit <<= 1) { int r; sint32 v_l, v_r; uint32 main_l = chanbit & maskmain_l; uint32 main_r = chanbit & maskmain_r; uint32 verb_l = chanbit & maskverb_l; uint32 verb_r = chanbit & maskverb_r; sint32 *b = buf ? ibuf : NULL; sint32 *fm = (chanbit & maskfm) ? ibuffm : NULL; sint32 *noise = (chanbit & masknoise) ? ibufn : NULL; if(!(main_l | main_r | verb_l | verb_r)) b = NULL; r = render_channel_mono( ram, state->memsize, state->chan + ch, b, fm, noise, samples, irq_state_ptr ); if(!b) { memset(ibuffm, 0, 4 * samples); continue; } memcpy(ibuffm, ibuf, 4 * r); if(r < samples) memset(ibuffm + r, 0, 4 * (samples-r)); v_l = volume_getlevel(state->chan[ch].vol+0); v_r = volume_getlevel(state->chan[ch].vol+1); for(i = 0; i < r; i++) { sint32 q_l = (v_l * ibuf[i]) >> 16; sint32 q_r = (v_r * ibuf[i]) >> 16; if(main_l) ibufmix[2*i+0] += q_l; if(main_r) ibufmix[2*i+1] += q_r; if(verb_l) ibufrvb[2*i+0] += q_l; if(verb_r) ibufrvb[2*i+1] += q_r; } } state->irq_triggered_cycle = irq_state.triggered_cycle; if(!buf) return; /* ** Mix in external input, if it exists */ if(extinput) { sint32 extvol_l = ((sint16)(state->avol[0])); sint32 extvol_r = ((sint16)(state->avol[1])); if(extvol_l == -0x8000) extvol_l = -0x7FFF; if(extvol_r == -0x8000) extvol_r = -0x7FFF; for(i = 0; i < samples; i++) { sint32 sin_l = extinput[2*i+0]; sint32 sin_r = extinput[2*i+1]; sin_l *= extvol_l; sin_r *= extvol_r; sin_l >>= 15; sin_r >>= 15; if(state->flags & SPUREG_FLAG_SINL ) ibufmix[2*i+0] += sin_l; if(state->flags & SPUREG_FLAG_SINR ) ibufmix[2*i+1] += sin_r; if(state->flags & SPUREG_FLAG_SINEL) ibufrvb[2*i+0] += sin_l; if(state->flags & SPUREG_FLAG_SINER) ibufrvb[2*i+1] += sin_r; } } /* ** Do reverb ** This handles both writing into the buffer and retrieving ** values out of it, resampling to/from 22KHz, etc. */ if(effectout) { reverb_process(state, ram, ibufrvb, samples); } /* ** */ m_v_l = volume_getlevel(state->mvol+0); m_v_r = volume_getlevel(state->mvol+1); r_v_l = ((sint16)(state->evol[0])); r_v_r = ((sint16)(state->evol[1])); if(!effectout) { for(i = 0; i < samples; i++) { sint64 q_l = ibufmix[2*i+0]; sint64 q_r = ibufmix[2*i+1]; q_l *= (sint64)m_v_l; q_r *= (sint64)m_v_r; q_l >>= 15; q_r >>= 15; CLIP_PCM_2(q_l,q_r); *buf++ = (sint16)q_l; *buf++ = (sint16)q_r; } } else { for(i = 0; i < samples; i++) { sint64 q_l = ibufmix[2*i+0]; sint64 q_r = ibufmix[2*i+1]; sint64 r_l = ibufrvb[2*i+0]; sint64 r_r = ibufrvb[2*i+1]; q_l *= (sint64)m_v_l; q_r *= (sint64)m_v_r; r_l *= (sint64)r_v_l; r_r *= (sint64)r_v_r; q_l >>= 15; q_r >>= 15; r_l >>= 15; r_r >>= 15; q_l += r_l; q_r += r_r; CLIP_PCM_2(q_l, q_r); *buf++ = (sint16)q_l; *buf++ = (sint16)q_r; } } } //////////////////////////////////////////////////////////////////////////////// /* ** Externally-accessible renderer */ void EMU_CALL spucore_render(void *state, uint16 *ram, sint16 *buf, sint16 *extinput, uint32 samples, uint8 mainout, uint8 effectout) { while(samples > RENDERMAX) { samples -= RENDERMAX; render(SPUCORESTATE, ram, buf, extinput, RENDERMAX, mainout, effectout); if(buf ) buf += 2 * RENDERMAX; if(extinput) extinput += 2 * RENDERMAX; } if(samples) render(SPUCORESTATE, ram, buf, extinput, samples, mainout, effectout); } //////////////////////////////////////////////////////////////////////////////// /* ** Flag get/set */ int EMU_CALL spucore_getflag(void *state, uint32 n) { return !!(SPUCORESTATE->flags & n); } void EMU_CALL spucore_setflag(void *state, uint32 n, int value) { if(value) { SPUCORESTATE->flags |= n; } else { SPUCORESTATE->flags &= ~n; } } //////////////////////////////////////////////////////////////////////////////// /* ** Register get/set */ uint32 EMU_CALL spucore_getreg(void *state, uint32 n) { switch(n) { case SPUREG_MVOLL: return volume_getmode (SPUCORESTATE->mvol+0) & 0x0000FFFF; case SPUREG_MVOLR: return volume_getmode (SPUCORESTATE->mvol+1) & 0x0000FFFF; case SPUREG_MVOLXL: return volume_getlevel(SPUCORESTATE->mvol+0) & 0x0000FFFF; case SPUREG_MVOLXR: return volume_getlevel(SPUCORESTATE->mvol+1) & 0x0000FFFF; case SPUREG_EVOLL: return SPUCORESTATE->evol[0] & 0x0000FFFF; case SPUREG_EVOLR: return SPUCORESTATE->evol[1] & 0x0000FFFF; case SPUREG_AVOLL: return SPUCORESTATE->avol[0] & 0x0000FFFF; case SPUREG_AVOLR: return SPUCORESTATE->avol[1] & 0x0000FFFF; case SPUREG_BVOLL: return SPUCORESTATE->bvol[0] & 0x0000FFFF; case SPUREG_BVOLR: return SPUCORESTATE->bvol[1] & 0x0000FFFF; case SPUREG_KON: return SPUCORESTATE->kon & 0x00FFFFFF; case SPUREG_KOFF: return SPUCORESTATE->koff & 0x00FFFFFF; case SPUREG_FM: return SPUCORESTATE->fm & 0x00FFFFFF; case SPUREG_NOISE: return SPUCORESTATE->noise & 0x00FFFFFF; case SPUREG_VMIXE: return (SPUCORESTATE->vmixe[0] | SPUCORESTATE->vmixe[1]) & 0xFFFFFF; case SPUREG_VMIX: return (SPUCORESTATE->vmix[0] | SPUCORESTATE->vmix[1] ) & 0xFFFFFF; case SPUREG_VMIXEL: return SPUCORESTATE->vmixe[0] & 0x00FFFFFF; case SPUREG_VMIXER: return SPUCORESTATE->vmixe[1] & 0x00FFFFFF; case SPUREG_VMIXL: return SPUCORESTATE->vmix[0] & 0x00FFFFFF; case SPUREG_VMIXR: return SPUCORESTATE->vmix[1] & 0x00FFFFFF; case SPUREG_ESA: return SPUCORESTATE->reverb.start_address; case SPUREG_EEA: return SPUCORESTATE->reverb.end_address; case SPUREG_EAX: return SPUCORESTATE->reverb.current_address; case SPUREG_IRQA: return SPUCORESTATE->irq_address; case SPUREG_NOISECLOCK: return SPUCORESTATE->noiseclock; #define SPUCORE_REVERB_GET(x) case SPUREG_REVERB_##x:return SPUCORESTATE->reverb.x; SPUCORE_REVERB_GET(FB_SRC_A) SPUCORE_REVERB_GET(FB_SRC_B) SPUCORE_REVERB_GET(IIR_ALPHA) SPUCORE_REVERB_GET(ACC_COEF_A) SPUCORE_REVERB_GET(ACC_COEF_B) SPUCORE_REVERB_GET(ACC_COEF_C) SPUCORE_REVERB_GET(ACC_COEF_D) SPUCORE_REVERB_GET(IIR_COEF) SPUCORE_REVERB_GET(FB_ALPHA) SPUCORE_REVERB_GET(FB_X) SPUCORE_REVERB_GET(IIR_DEST_A0) SPUCORE_REVERB_GET(IIR_DEST_A1) SPUCORE_REVERB_GET(ACC_SRC_A0) SPUCORE_REVERB_GET(ACC_SRC_A1) SPUCORE_REVERB_GET(ACC_SRC_B0) SPUCORE_REVERB_GET(ACC_SRC_B1) SPUCORE_REVERB_GET(IIR_SRC_A0) SPUCORE_REVERB_GET(IIR_SRC_A1) SPUCORE_REVERB_GET(IIR_DEST_B0) SPUCORE_REVERB_GET(IIR_DEST_B1) SPUCORE_REVERB_GET(ACC_SRC_C0) SPUCORE_REVERB_GET(ACC_SRC_C1) SPUCORE_REVERB_GET(ACC_SRC_D0) SPUCORE_REVERB_GET(ACC_SRC_D1) SPUCORE_REVERB_GET(IIR_SRC_B1) SPUCORE_REVERB_GET(IIR_SRC_B0) SPUCORE_REVERB_GET(MIX_DEST_A0) SPUCORE_REVERB_GET(MIX_DEST_A1) SPUCORE_REVERB_GET(MIX_DEST_B0) SPUCORE_REVERB_GET(MIX_DEST_B1) SPUCORE_REVERB_GET(IN_COEF_L) SPUCORE_REVERB_GET(IN_COEF_R) } return 0; } void EMU_CALL spucore_setreg(void *state, uint32 n, uint32 value, uint32 mask) { value &= mask; switch(n) { /* TODO: the increase/decrease modes, etc. */ case SPUREG_MVOLL: volume_setmode(SPUCORESTATE->mvol+0, value); break; case SPUREG_MVOLR: volume_setmode(SPUCORESTATE->mvol+1, value); break; case SPUREG_EVOLL: SPUCORESTATE->evol[0] = value; break; case SPUREG_EVOLR: SPUCORESTATE->evol[1] = value; break; case SPUREG_AVOLL: SPUCORESTATE->avol[0] = value; break; case SPUREG_AVOLR: SPUCORESTATE->avol[1] = value; break; case SPUREG_BVOLL: SPUCORESTATE->bvol[0] = value; break; case SPUREG_BVOLR: SPUCORESTATE->bvol[1] = value; break; case SPUREG_KON: SPUCORESTATE->kon &= ~mask; SPUCORESTATE->kon |= value; voices_on(state, value); break; case SPUREG_KOFF: SPUCORESTATE->koff &= ~mask; SPUCORESTATE->koff |= value; voices_off(state, value); break; case SPUREG_FM: SPUCORESTATE->fm &= ~mask; SPUCORESTATE->fm |= value; break; case SPUREG_NOISE: SPUCORESTATE->noise &= ~mask; SPUCORESTATE->noise |= value; break; case SPUREG_VMIXE: SPUCORESTATE->vmixe[0] &= ~mask; SPUCORESTATE->vmixe[0] |= value; SPUCORESTATE->vmixe[1] &= ~mask; SPUCORESTATE->vmixe[1] |= value; break; case SPUREG_VMIX: SPUCORESTATE->vmix[0] &= ~mask; SPUCORESTATE->vmix[0] |= value; SPUCORESTATE->vmix[1] &= ~mask; SPUCORESTATE->vmix[1] |= value; break; case SPUREG_VMIXEL: SPUCORESTATE->vmixe[0] &= ~mask; SPUCORESTATE->vmixe[0] |= value; break; case SPUREG_VMIXER: SPUCORESTATE->vmixe[1] &= ~mask; SPUCORESTATE->vmixe[1] |= value; break; case SPUREG_VMIXL: SPUCORESTATE->vmix[0] &= ~mask; SPUCORESTATE->vmix[0] |= value; break; case SPUREG_VMIXR: SPUCORESTATE->vmix[1] &= ~mask; SPUCORESTATE->vmix[1] |= value; break; case SPUREG_ESA: SPUCORESTATE->reverb.start_address &= ~mask; SPUCORESTATE->reverb.start_address |= value; make_safe_reverb_addresses(state); SPUCORESTATE->reverb.current_address = SPUCORESTATE->reverb.safe_start_address; break; case SPUREG_EEA: SPUCORESTATE->reverb.end_address &= ~mask; SPUCORESTATE->reverb.end_address |= value; make_safe_reverb_addresses(state); break; case SPUREG_IRQA: /* TODO: actual implementation of IRQs */ SPUCORESTATE->irq_address &= ~mask; SPUCORESTATE->irq_address |= value; break; case SPUREG_NOISECLOCK: SPUCORESTATE->noiseclock = value & 0x3F; break; #define SPUCORE_REVERB_SET(x) case SPUREG_REVERB_##x:SPUCORESTATE->reverb.x&=(~mask);SPUCORESTATE->reverb.x|=value;break; SPUCORE_REVERB_SET(FB_SRC_A) SPUCORE_REVERB_SET(FB_SRC_B) SPUCORE_REVERB_SET(IIR_ALPHA) SPUCORE_REVERB_SET(ACC_COEF_A) SPUCORE_REVERB_SET(ACC_COEF_B) SPUCORE_REVERB_SET(ACC_COEF_C) SPUCORE_REVERB_SET(ACC_COEF_D) SPUCORE_REVERB_SET(IIR_COEF) SPUCORE_REVERB_SET(FB_ALPHA) SPUCORE_REVERB_SET(FB_X) SPUCORE_REVERB_SET(IIR_DEST_A0) SPUCORE_REVERB_SET(IIR_DEST_A1) SPUCORE_REVERB_SET(ACC_SRC_A0) SPUCORE_REVERB_SET(ACC_SRC_A1) SPUCORE_REVERB_SET(ACC_SRC_B0) SPUCORE_REVERB_SET(ACC_SRC_B1) SPUCORE_REVERB_SET(IIR_SRC_A0) SPUCORE_REVERB_SET(IIR_SRC_A1) SPUCORE_REVERB_SET(IIR_DEST_B0) SPUCORE_REVERB_SET(IIR_DEST_B1) SPUCORE_REVERB_SET(ACC_SRC_C0) SPUCORE_REVERB_SET(ACC_SRC_C1) SPUCORE_REVERB_SET(ACC_SRC_D0) SPUCORE_REVERB_SET(ACC_SRC_D1) SPUCORE_REVERB_SET(IIR_SRC_B1) SPUCORE_REVERB_SET(IIR_SRC_B0) SPUCORE_REVERB_SET(MIX_DEST_A0) SPUCORE_REVERB_SET(MIX_DEST_A1) SPUCORE_REVERB_SET(MIX_DEST_B0) SPUCORE_REVERB_SET(MIX_DEST_B1) SPUCORE_REVERB_SET(IN_COEF_L) SPUCORE_REVERB_SET(IN_COEF_R) } } //////////////////////////////////////////////////////////////////////////////// /* ** Voice register get/set */ uint32 EMU_CALL spucore_getreg_voice(void *state, uint32 voice, uint32 n) { switch(n) { case SPUREG_VOICE_VOLL : return volume_getmode(SPUCORESTATE->chan[voice].vol+0); case SPUREG_VOICE_VOLR : return volume_getmode(SPUCORESTATE->chan[voice].vol+1); case SPUREG_VOICE_VOLXL: return volume_getlevel(SPUCORESTATE->chan[voice].vol+0); case SPUREG_VOICE_VOLXR: return volume_getlevel(SPUCORESTATE->chan[voice].vol+1); case SPUREG_VOICE_PITCH: return SPUCORESTATE->chan[voice].voice_pitch; case SPUREG_VOICE_ADSR1: return SPUCORESTATE->chan[voice].env.reg_ad; case SPUREG_VOICE_ADSR2: return SPUCORESTATE->chan[voice].env.reg_sr; case SPUREG_VOICE_ENVX : if(SPUCORESTATE->chan[voice].env.state == ENVELOPE_STATE_OFF) return 0; return (SPUCORESTATE->chan[voice].env.level) >> 16; case SPUREG_VOICE_SSA : return SPUCORESTATE->chan[voice].sample.start_block_addr; case SPUREG_VOICE_LSAX : return SPUCORESTATE->chan[voice].sample.loop_block_addr; case SPUREG_VOICE_NAX : return SPUCORESTATE->chan[voice].sample.block_addr; } return 0; } void EMU_CALL spucore_setreg_voice(void *state, uint32 voice, uint32 n, uint32 value, uint32 mask) { value &= mask; switch(n) { /* TODO: the increase/decrease modes */ case SPUREG_VOICE_VOLL : volume_setmode(SPUCORESTATE->chan[voice].vol+0, value); break; case SPUREG_VOICE_VOLR : volume_setmode(SPUCORESTATE->chan[voice].vol+1, value); break; case SPUREG_VOICE_PITCH: SPUCORESTATE->chan[voice].voice_pitch = value; break; case SPUREG_VOICE_ADSR1: SPUCORESTATE->chan[voice].env.reg_ad = value; SPUCORESTATE->chan[voice].env.cachemax = envelope_do(&SPUCORESTATE->chan[voice].env); break; case SPUREG_VOICE_ADSR2: SPUCORESTATE->chan[voice].env.reg_sr = value; SPUCORESTATE->chan[voice].env.cachemax = envelope_do(&SPUCORESTATE->chan[voice].env); break; case SPUREG_VOICE_SSA: SPUCORESTATE->chan[voice].sample.start_block_addr &= ~mask; SPUCORESTATE->chan[voice].sample.start_block_addr |= value; break; case SPUREG_VOICE_LSAX: SPUCORESTATE->chan[voice].sample.loop_block_addr &= ~mask; SPUCORESTATE->chan[voice].sample.loop_block_addr |= value; break; } } //////////////////////////////////////////////////////////////////////////////// /* ** IRQ checking */ uint32 EMU_CALL spucore_cycles_until_interrupt(void *state, uint16 *ram, uint32 samples) { uint32 r; void *backup; if (!(SPUCORESTATE->flags & SPUREG_FLAG_IRQ_ENABLE)) return 0xFFFFFFFF; backup = malloc(spucore_get_state_size()); if (!backup) return 0xFFFFFFFF; memcpy(backup, state, spucore_get_state_size()); state = backup; SPUCORESTATE->irq_triggered_cycle = 0xFFFFFFFF; r = 0; while(samples > RENDERMAX) { samples -= RENDERMAX; render(SPUCORESTATE, ram, NULL, NULL, RENDERMAX, 0, 0); if (SPUCORESTATE->irq_triggered_cycle != 0xFFFFFFFF) break; r += RENDERMAX * 768; } if(samples && SPUCORESTATE->irq_triggered_cycle == 0xFFFFFFFF) render(SPUCORESTATE, ram, NULL, NULL, samples, 0, 0); r = (SPUCORESTATE->irq_triggered_cycle == 0xFFFFFFFF) ? 0xFFFFFFFF : SPUCORESTATE->irq_triggered_cycle + r; free(backup); return r; } ////////////////////////////////////////////////////////////////////////////////