/****************************************/ /* Low-level control of the EMU8000 */ /* (c) Grinus/ToM, 1996 */ /* Written for Borland C++ 3.1 */ /****************************************/ #include #include #include #include "awe.h" // --------------- D a t a --------------- // WORD Port620 = 0; // EMU base port address (0x620 resp. 0x640) WORD PortE22; // secondary port address WORD TbEffectCmd[4] = { AWE_28, AWE_2A, AWE_2C, AWE_2E }; WORD TbEffData1[128] = { 0x03FF, 0x0030, 0x07FF, 0x0130, 0x0BFF, 0x0230, 0x0FFF, 0x0330, 0x13FF, 0x0430, 0x17FF, 0x0530, 0x1BFF, 0x0630, 0x1FFF, 0x0730, 0x23FF, 0x0830, 0x27FF, 0x0930, 0x2BFF, 0x0A30, 0x2FFF, 0x0B30, 0x33FF, 0x0C30, 0x37FF, 0x0D30, 0x3BFF, 0x0E30, 0x3FFF, 0x0F30, 0x43FF, 0x0030, 0x47FF, 0x0130, 0x4BFF, 0x0230, 0x4FFF, 0x0330, 0x53FF, 0x0430, 0x57FF, 0x0530, 0x5BFF, 0x0630, 0x5FFF, 0x0730, 0x63FF, 0x0830, 0x67FF, 0x0930, 0x6BFF, 0x0A30, 0x6FFF, 0x0B30, 0x73FF, 0x0C30, 0x77FF, 0x0D30, 0x7BFF, 0x0E30, 0x7FFF, 0x0F30, 0x83FF, 0x0030, 0x87FF, 0x0130, 0x8BFF, 0x0230, 0x8FFF, 0x0330, 0x93FF, 0x0430, 0x97FF, 0x0530, 0x9BFF, 0x0630, 0x9FFF, 0x0730, 0xA3FF, 0x0830, 0xA7FF, 0x0930, 0xABFF, 0x0A30, 0xAFFF, 0x0B30, 0xB3FF, 0x0C30, 0xB7FF, 0x0D30, 0xBBFF, 0x0E30, 0xBFFF, 0x0F30, 0xC3FF, 0x0030, 0xC7FF, 0x0130, 0xCBFF, 0x0230, 0xCFFF, 0x0330, 0xD3FF, 0x0430, 0xD7FF, 0x0530, 0xDBFF, 0x0630, 0xDFFF, 0x0730, 0xE3FF, 0x0830, 0xE7FF, 0x0930, 0xEBFF, 0x0A30, 0xEFFF, 0x0B30, 0xF3FF, 0x0C30, 0xF7FF, 0x0D30, 0xFBFF, 0x0E30, 0xFFFF, 0x0F30 }; WORD TbEffData2[128] = { 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254, 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234, 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x06E7, 0x229E, 0x7224, 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x07F6, 0x2C28, 0x7254, 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x0F02, 0x1341, 0x7264, 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x0FA9, 0x3EB5, 0x7294, 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0x44C3, 0x3EBB, 0x45C3, 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x0671, 0x14FD, 0x0287, 0x3EBC, 0xE610, 0x3EC8, 0x0C7B, 0x031A, 0x07E6, 0x3EC8, 0x86F7, 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x021F, 0x3ECA, 0x0386, 0x3EC1, 0x0C03, 0x3EC9, 0x031E, 0x3ECA, 0x8C4C, 0x3EBF, 0x0C55, 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x0EAD, 0x3EC8, 0xD308, 0x3EC2, 0x8F7E, 0x3ECB, 0x0219, 0x3ECB, 0xD26E, 0x3EC5, 0x031F, 0x3EC6, 0xC308, 0x3EC3, 0x32FF, 0x3EC9, 0x0265, 0x3EC9, 0x8319, 0x1342, 0xD36E, 0x3EC7, 0x33FF, 0x0000, 0x8365, 0x1420, 0x9570 }; CHORUS_TYPE TbChorusTypes[8] = { 0xE600, 0x03F6, 0xBC2C, 0, 0x006D, // Chorus 1 0xE608, 0x031A, 0xBC6E, 0, 0x017C, // Chorus 2 0xE610, 0x031A, 0xBC84, 0, 0x0083, // Chorus 3 0xE620, 0x0269, 0xBC6E, 0, 0x017C, // Chorus 4 0xE680, 0x04D3, 0xBCA6, 0, 0x005B, // Feedback Delay 0xE6E0, 0x044E, 0xBC37, 0, 0x0026, // Flanger 0xE600, 0x0B06, 0xBC00, 0x6E000, 0x0083, // Short Delay 0xE6C0, 0x0B06, 0xBC00, 0x6E000, 0x0083 // Short Delay FB }; BYTE TbReverbReg[28] = { 0x03, 0x05, 0x7F, 0x07, 0x34, 0x36, 0x0F, 0x17, 0x1F, 0x27, 0x2F, 0x37, 0x3D, 0x3F, 0x41, 0x43, 0x09, 0x0B, 0x11, 0x13, 0x19, 0x1B, 0x21, 0x23, 0x29, 0x2B, 0x31, 0x33 }; WORD TbReverbData[8*28] = { 0xB488, 0xA450, 0x9550, 0x84B5, 0x383A, 0x3EB5, 0x72F4, // Room 1 0x72A4, 0x7254, 0x7204, 0x7204, 0x7204, 0x4416, 0x4516, 0xA490, 0xA590, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, 0xB488, 0xA458, 0x9558, 0x84B5, 0x383A, 0x3EB5, 0x7284, // Room 2 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548, 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, 0xB488, 0xA460, 0x9560, 0x84B5, 0x383A, 0x3EB5, 0x7284, // Room 3 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4416, 0x4516, 0xA490, 0xA590, 0x842C, 0x852C, 0x842C, 0x852C, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A, 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7284, // Hall 1 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548, 0xA440, 0xA540, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529, 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7254, // Hall 2 0x7234, 0x7224, 0x7254, 0x7264, 0x7294, 0x44C3, 0x45C3, 0xA404, 0xA504, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, 0xB4FF, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7234, // Plate 0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x4448, 0x4548, 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, 0xB4FF, 0xA470, 0x9500, 0x84B5, 0x333A, 0x39B5, 0x7204, // Delay 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500, 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0xB4FF, 0xA490, 0x9590, 0x8474, 0x333A, 0x39B5, 0x7204, // Panning Delay 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500, 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520 }; WORD TbAweBass[12*3] = { 0xD26A, 0xD36A, 0x0000, // -12 dB 0xD25B, 0xD35B, 0x0000, // -8 0xD24C, 0xD34C, 0x0000, // -6 0xD23D, 0xD33D, 0x0000, // -4 0xD21F, 0xD31F, 0x0000, // -2 0xC208, 0xC308, 0x0001, // 0 (HW default) 0xC219, 0xC319, 0x0001, // +2 0xC22A, 0xC32A, 0x0001, // +4 0xC24C, 0xC34C, 0x0001, // +6 0xC26E, 0xC36E, 0x0001, // +8 0xC248, 0xC348, 0x0002, // +10 0xC26A, 0xC36A, 0x0002 // +12 dB }; WORD TbAweTreble[12*9] = { 0x821E, 0xC26A, 0x031E, 0xC36A, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001, // -12 dB 0x821E, 0xC25B, 0x031E, 0xC35B, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001, 0x821E, 0xC24C, 0x031E, 0xC34C, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001, 0x821E, 0xC23D, 0x031E, 0xC33D, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001, 0x821E, 0xC21F, 0x031E, 0xC31F, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001, 0x821E, 0xD208, 0x031E, 0xD308, 0x021E, 0xD208, 0x831E, 0xD308, 0x0002, 0x821E, 0xD208, 0x031E, 0xD308, 0x021D, 0xD219, 0x831D, 0xD319, 0x0002, 0x821E, 0xD208, 0x031E, 0xD308, 0x021C, 0xD22A, 0x831C, 0xD32A, 0x0002, 0x821E, 0xD208, 0x031E, 0xD308, 0x021A, 0xD24C, 0x831A, 0xD34C, 0x0002, 0x821E, 0xD208, 0x031E, 0xD308, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002, // +8 (HW default) 0x821D, 0xD219, 0x031D, 0xD319, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002, 0x821C, 0xD22A, 0x031C, 0xD32A, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002 // +12 dB }; // --------------- C o d e --------------- // /* Read the EMU base address from the BLASTER variable */ WORD GetBaseFromEnv() { char *p; WORD w; if (!(p = getenv("BLASTER")) || !(p = strstr(p, " E")) || sscanf(p+2, "%x", &w ) !=1 ) return 0; // ERROR return w; // address } // --------------- Low-level EMU8000 routines --------------- // /* Write the 16-bit EMU register */ void AweWrW(WORD reg, WORD data) { asm { mov ax, reg // HighBYTE = register index, LowBYTE = G-chan number mov cx,ax mov cl,ch and cx,0x6002 shr ch,3 add cx,Port620 // CX=port2 and ax,0x1C1F shl ah,3 or al,ah xor ah,ah // AX=data1 mov dx,PortE22 // DX=port1 cli // start of critical section out dx,ax mov dx,cx // DX=port2 mov ax, data // AX=data2 out dx,ax sti // end of critical section } } /* Read the 16-bit EMU register */ WORD AweRdW(WORD reg) { asm { mov ax, reg mov cx,ax mov cl,ch and cx,0x6002 shr ch,3 add cx,Port620 and ax,0x1C1F shl ah,3 or al,ah xor ah,ah mov dx,PortE22 cli out dx,ax mov dx,cx in ax,dx sti } #pragma warn -rvl } #pragma warn +rvl /* Write the 32-bit EMU register */ void AweWrD(WORD reg, WORD hdata, WORD ldata ) { asm { mov ax, reg mov cx,ax mov cl,ch and cx,0x6002 shr ch,3 add cx,Port620 and ax,0x1C1F shl ah,3 or al,ah xor ah,ah mov dx,PortE22 cli out dx,ax mov dx,cx mov ax,ldata out dx,ax inc dx inc dx mov ax,hdata out dx,ax sti } } /* Read the 32-bit EMU register */ DWORD AweRdD(WORD reg) { asm { mov ax, reg mov cx,ax mov cl,ch and cx,0x6002 shr ch,3 add cx,Port620 and ax,0x1C1F shl ah,3 or al,ah xor ah,ah mov dx,PortE22 cli out dx,ax mov dx,cx in ax,dx mov cx,ax inc dx inc dx in ax,dx sti mov dx,ax mov ax,cx } #pragma warn -rvl } #pragma warn +rvl /* Detect the presence of the EMU8000 */ int AweDetect1() { BYTE b; WORD w; asm mov dx,Port620 asm add dx,0x802 asm mov PortE22,dx asm in al,dx asm in al,dx asm not al asm mov b,al asm out dx,al // invert the LowByte asm in al,dx if ( _AL != b) return 0; // ERROR asm in ax,dx asm mov w,ax for (b = 128; 1; b--) { asm in ax,dx if (_AX != w) break; // is it changing? if (b==0) return 0; // if not, then ERROR } if ( (AweRdW(AWE_5C) & 0x8E) != 0x0C ) return 0; b = ~AweRdW(0x241E) & 0x06; w = ~AweRdW(0x241F) & 0x1F; AweWrW(0x241E, w | b<<4); if ( (AweRdW(0x241E) & 0x06) != b) return 0; AweWrW(0x241E, w | (~b<<4 & 0x60) ); return 1; // ok } /* Determine the EMU8000 base port */ int AweDetect() { if (Port620 == 0) // the address is not forced ? Port620 = GetBaseFromEnv(); // then let's try the BLASTER variable if (Port620 == 0) { // still unknown? Port620 = 0x620; // Try 0x620 if (AweDetect1()) return 1; // ok Port620 += 0x20; // Try 0x640 } return AweDetect1(); } /* Wait for some EMU clocks (1/44100 sec) */ void AweWait(WORD delay) { WORD t0 = AweRdW(AWE_CLK); while ( (WORD)( AweRdW(AWE_CLK) - t0 ) < delay ) ; } void InitEnvelopeEngine() { for (WORD gchan = 0; gchan<32; gchan++) { AweWrW( AWE_Env2Su_Dcy|gchan, 0x0080 ); AweWrW( AWE_Env1Ho_Att|gchan, 0 ); AweWrW( AWE_Env1Su_Dcy|gchan, 0 ); AweWrW( AWE_Pitch |gchan, 0xD000 ); AweWrW( AWE_FC_Vol |gchan, 0xFF00 ); AweWrW( AWE_Env1tP_tF |gchan, 0 ); AweWrW( AWE_Lfo1tP_tF |gchan, 0 ); AweWrW( AWE_Lfo1tV_F |gchan, 0x0018 ); AweWrW( AWE_Lfo2tP_F |gchan, 0x0018 ); AweWrW( AWE_58 |gchan, 0 ); AweWrW( AWE_Lfo2Dly |gchan, 0 ); AweWrW( AWE_Lfo1Dly |gchan, 0 ); AweWrW( AWE_Env2Ho_Att|gchan, 0 ); AweWrW( AWE_Env2Dly |gchan, 0 ); AweWrW( AWE_Env1Dly |gchan, 0 ); } } void InitSoundEngine() { AweWrD(AWE_RdAdrD, 0x8000, 0x0000 ); AweWrD(AWE_RdAdrD+1, 0x8000, 0x0000 ); AweWrD(AWE_WrAdrD, 0x8000, 0x0000 ); AweWrD(AWE_WrAdrD+1, 0x8000, 0x0000 ); for (WORD gchan = 0; gchan<32; gchan++) { AweWrD( AWE_DP_Rev_Pan |gchan, 0, 0 ); AweWrD( AWE_DestV_FC |gchan, 0, 0xFFFF ); AweWrD( AWE_CurrV_FC |gchan, 0, 0xFFFF ); AweWrD( AWE_Pan_Loops |gchan, 0, 0 ); AweWrD( AWE_Cho_Loope |gchan, 0, 0 ); AweWrD( AWE_CurrPitch |gchan, 0, 0 ); AweWrD( AWE_Flt_Start |gchan, 0, 0 ); AweWrD( AWE_14 |gchan, 0, 0 ); AweWrD( AWE_10 |gchan, 0, 0 ); AweWrW( AWE_Env1Su_Dcy |gchan, 0x807F ); AweWrW( AWE_Env2Su_Dcy |gchan, 0x807F ); } } void InitEffects1(WORD near *data) { int i; for (i=0; i<128; i++) AweWrW( (i & 0x1F) | TbEffectCmd[i>>5 & 0x07], data[i] ); } void InitEffects2(WORD near *data) { int i; for (i=0; i<128; i++) AweWrW( (i & 0x1F) | TbEffectCmd[i>>5 & 0x07], data[i] | i<<15 ); } void InitEffectsEngine() { int i,j; InitEffects1(TbEffData1); AweWait(0x400); for (i=0; i<0x14; i++) AweWrD(0x2400|i, 0, 0 ); InitEffects2(TbEffData1); InitEffects2(TbEffData2); AweWrD(0x2409, 0, 0 ); AweWrD(0x240A, 0, 0x0083 ); AweWrD(0x240D, 0, 0x8000 ); AweWrD(0x240E, 0, 0 ); InitEffects1(TbEffData2); } /* Prepare the last two channels for DRAM refresh and producing the reverb and chorus effects for Yamaha OPL-3 synthesizer. */ void InitFMEffects(BYTE reverb, BYTE chorus) { AweWrW(AWE_Env2Su_Dcy |30, 0x0080 ); AweWrD(AWE_Pan_Loops |30, 0xFFFF, 0xFFE0 ); AweWrD(AWE_Cho_Loope |30, 0x00FF | (WORD)chorus<<8, 0xFFE8 ); AweWrD(AWE_DP_Rev_Pan |30, 0, (WORD)reverb<<8 ); AweWrD(AWE_CurrPitch |30, 0, 0 ); AweWrD(AWE_Flt_Start |30, 0x00FF, 0xFFE3 ); AweWrW(AWE_Env2Su_Dcy |31, 0x0080 ); AweWrD(AWE_Pan_Loops |31, 0x00FF, 0xFFF0 ); AweWrD(AWE_Cho_Loope |31, 0x00FF | (WORD)chorus<<8, 0xFFF8 ); AweWrD(AWE_DP_Rev_Pan |31, 0, 0x00FF | (WORD)reverb<<8 ); AweWrD(AWE_CurrPitch |31, 0, 0x8000); AweWrD(AWE_Flt_Start |31, 0x00FF, 0xFFF3 ); asm cli // disable interrupts asm mov dx,PortE22 asm mov ax,0x003E asm out dx,ax asm xchg bx,dx asm mov dx,Port620 asm xor ax,ax asm out dx,ax asm xchg bx,dx // 0xE22 loop1: asm in ax,dx asm test ah,0x10 asm jz loop1 loop2: asm in ax,dx asm test ah,0x10 asm jnz loop2 // wait for an edge asm mov dx,bx asm add dx,2 // 0x622 asm mov ax,0x4828 asm out dx,ax asm mov dx,PortE22 asm mov ax,0x003C asm out dx,ax asm mov dx,bx asm add dh,4 // 0xE20 asm xor ax,ax asm out dx,ax asm sti // enable interrupts AweWrD(AWE_DestV_FC |30, 0x8000, 0xFFFF ); AweWrD(AWE_DestV_FC |31, 0x8000, 0xFFFF ); } /* Prepare some G-channels for the sample upload/download rmode = 1 -> odd G-channels for reading rmode = 0 -> all G-channels for writing */ void AweEnableRam(int rmode) { WORD gchan; for (gchan=0; gchan<30; gchan++) AweWrW( AWE_Env2Su_Dcy |gchan, 0x0080 ); for (gchan=0; gchan<30; gchan++) { AweWrD( AWE_DestV_FC |gchan, 0, 0 ); AweWrD( AWE_CurrV_FC |gchan, 0, 0 ); AweWrD( AWE_Pan_Loops |gchan, 0, 0 ); AweWrD( AWE_Cho_Loope |gchan, 0, 0 ); AweWrD( AWE_DP_Rev_Pan |gchan, 0x4000, 0x0000 ); AweWrD( AWE_CurrPitch |gchan, 0x4000, 0x0000 ); // Read (4) / Write (6) mode AweWrD( AWE_Flt_Start |gchan, (gchan & rmode) ? 0x0400 : 0x0600, 0 ); } } void AweDisableRam() { for (WORD gchan=0; gchan<30; gchan++) AweWrD( AWE_Flt_Start |gchan, 0, 0 ), AweWrW( AWE_Env2Su_Dcy |gchan, 0x807F ); } /* Check the sample RAM size This routine does not restore the changed sample data! */ WORD RamCheck() { WORD DRAMsize; // [KB] AweEnableRam(1); // we need both to read and write AweWrD(AWE_WrAdrD, TOPRAM>>16, TOPRAM&0xFFFF ); // 1-st addr in RAM AweWrW(AWE_DataW, 0xE5A7 ); AweWrW(AWE_DataW, 0x1A58 ); // put a mark there for (DRAMsize=0; DRAMsize<28*1024; ) { AweWait(2); AweWrD(AWE_RdAdrD, TOPRAM>>16, TOPRAM&0xFFFF ); // back to 1-st addr AweRdW(AWE_DataW); // skip the 1-st word if (AweRdW(AWE_DataW)!=0xE5A7) break; if (AweRdW(AWE_DataW)!=0x1A58) // is the mark present? break; DRAMsize += 32; AweWrD(AWE_WrAdrD, (TOPRAM>>16) + (DRAMsize>>7), (TOPRAM&0xFFFF) + (DRAMsize<<9) ); AweWrW(AWE_DataW, 0xFFFF); // clear the mark } AweDisableRam(); return DRAMsize; } /* Initialization of the EMU8000 (except the Equalizer and Effect settings) */ int AweInitHw() { WORD DRAMsize; if ( (AweRdW(AWE_5C) & 0x0E) != 0x0C) return 0; // ERROR AweWrW( 0x241D, 0x0059 ); AweWrW( 0x241E, 0x0020 ); AweWrW( 0x241F, 0 ); InitEnvelopeEngine(); InitSoundEngine(); InitEffectsEngine(); InitFMEffects( 102, 51 ); // Reverb 40%, Chorus 20% DRAMsize = RamCheck(); AweWrW( 0x241F, 0x0006 ); if (~AweRdW(0x241E) & 0x40) return 0; // ERROR return DRAMsize; // ok } /* EMU clean-up routine (called from 'exit' function) */ void AweTerminate() { for (WORD gchan=30; gchan--; ) AweWrW( AWE_Env2Su_Dcy |gchan, 0x807F ); // All Sounds Off AweWait(2205); // wait 50ms to avoid the reverb-click InitFMEffects( 102, 51 ); // Reverb & chorus for OPL3 synth } /* Set the chorus type */ void AweChorusType(int type) { CHORUS_TYPE *t = TbChorusTypes + type; // type = 0..7 AweWrW(AWE_2C|0x09, t->FbkLevel ); AweWrW(AWE_2C|0x0C, t->Delay ); AweWrW(AWE_2E|0x03, t->LfoDepth ); AweWrD(AWE_24|0x09, HWORD(t->DelayR), (WORD)t->DelayR ); AweWrD(AWE_24|0x0A, HWORD(t->LfoFreq), (WORD)t->LfoFreq ); AweWrD(AWE_24|0x0D, 0, 0x8000 ); AweWrD(AWE_24|0x0E, 0, 0 ); } /* Set the reverb type */ void AweReverbType(int type) { BYTE b; for (int i=0; i<28; i++) b = TbReverbReg[i], AweWrW( TbEffectCmd[b>>5] | (b & 0x1F), TbReverbData[type*28+i] ); } /* Send the values of EMU8000 digital equalizer */ void AweTrebleBass(int bass, int treble) { // a range is 0..11 (-12dB .. +12dB) WORD w; WORD *pb = TbAweBass + bass*3; WORD *pt = TbAweTreble + treble*9; AweWrW(AWE_2E | 0x01, pb[0] ); AweWrW(AWE_2E | 0x11, pb[1] ); AweWrW(AWE_2C | 0x11, pt[0] ); AweWrW(AWE_2C | 0x13, pt[1] ); AweWrW(AWE_2C | 0x1B, pt[2] ); AweWrW(AWE_2E | 0x07, pt[3] ); AweWrW(AWE_2E | 0x0B, pt[4] ); AweWrW(AWE_2E | 0x0D, pt[5] ); AweWrW(AWE_2E | 0x17, pt[6] ); AweWrW(AWE_2E | 0x19, pt[7] ); w = pb[2] + pt[8]; AweWrW(AWE_2E | 0x15, w + 0x0263 ); AweWrW(AWE_2E | 0x1D, w - 0x7C9D ); } /* Send the block of 16-bit signed samples to the sample RAM, provided that the sample RAM address is already set */ void AweWrBlock(WORD far *buf, WORD num_samps) { asm mov cx,num_samps asm jcxz retdlr asm mov dx,PortE22 asm mov ax,0x003A asm out dx,ax asm mov dx,Port620 asm add dh,4 asm push ds asm lds si,buf asm cld loopdlr16: asm lodsw asm out dx,ax asm loop loopdlr16 asm pop ds retdlr: } /* Send the NoteOff cmd (in fact, it just starts the release phase) */ void NoteOff(int gChan) { // Start the fastest decay to a zero level AweWrW( AWE_Env2Su_Dcy |gChan, 0x807F ); /* ENV1 was not used AweWrW( AWE_Env1Su_Dcy |gChan, 0x807F ); */ } /* The fastest possible destroing of the note It can cause a click! */ void CutNote(int gChan) { AweWrW(AWE_Env2Su_Dcy|gChan, 0x0080); // disable the volume envelope AweWrD(AWE_DestV_FC |gChan, 0, 0xFFFF); // dest. volume = 0 } /* Start the new note It expects that all necessary values are already prepared. */ void AweNoteOn( int gChan, // 0..29 BYTE Volume, // log. 255..0 (min..max) BYTE Pan, // 00..FF (left..right) BYTE Reverb, // lin. 00..FF BYTE Chorus, // lin. 00..FF WORD AwePitch, // log. 0000..FFFF WORD AweLinPitch, // lin. 0000..FFFF DWORD Start, // 000000..FFFFFF DWORD LoopS, DWORD LoopE // 000000..FFFFFF ) { // Send the G-channel params AweWrD( AWE_DestV_FC |gChan, 0, 0xFFFF ); // CurrVol=0, CurrFC=0xFFFF AweWrW( AWE_Env2Ho_Att |gChan, 0x7F7E); // ENV2 Hold = 0, Attack = 6ms AweWrW( AWE_Lfo1Dly |gChan, 0x8000 ); // LFO1 Delay = 0 AweWrW( AWE_Env1Ho_Att |gChan, 0x7F7F ); // ENV1 Hold & Attack = 0 AweWrW( AWE_Env1Su_Dcy |gChan, 0x807F ); // ENV1 Sustain & Decay = 0 AweWrW( AWE_Lfo2Dly |gChan, 0x8000 ); // LFO2 Delay = 0 AweWrW( AWE_Pitch |gChan, AwePitch ); // Pitch AweWrW( AWE_FC_Vol |gChan, 0xFF00|Volume); // FiltCutoff & Volume AweWrW( AWE_Env1tP_tF |gChan, 0x0000 ); // ENV1 ToPitch & ToFilter AweWrW( AWE_Lfo1tP_tF |gChan, 0x0000 ); // LFO1 ToPitch & ToFilter AweWrW( AWE_Lfo1tV_F |gChan, 0x0000 ); // LFO1 ToVolume & Freq AweWrW( AWE_Lfo2tP_F |gChan, 0x0000 ); // LFO2 ToPitch & Freq AweWrW( AWE_Env1Dly |gChan, 0x8000 ); // ENV1 Delay = 0 AweWrW( AWE_Env2Dly |gChan, 0x8000 ); // ENV2 Delay = 0 AweWrD( AWE_CurrV_FC |gChan, 0, 0xFFFF ); // CurrVol=0, CurrFC=max AweWrD( AWE_DP_Rev_Pan |gChan, AweLinPitch, // DestPitch (WORD)Reverb<<8 // ReverbLevel | Pan ); // RightPan AweWrD( AWE_Pan_Loops |gChan, (WORD)(~Pan) <<8 // LeftPan | HWORD(LoopS), (WORD)LoopS ); // LoopS AweWrD( AWE_Cho_Loope |gChan, (WORD)Chorus<<8 // Chorus | HWORD(LoopE), (WORD)LoopE ); // LoopE AweWrD( AWE_Flt_Start |gChan, 0 // FilterQ=0, RamMode=Normal | HWORD(Start), (WORD)Start ); // Start AweWrD( AWE_14 |gChan, 0, 0 ); // ??? AweWrD( AWE_10 |gChan, 0, 0 ); // ??? AweWrD( AWE_DestV_FC |gChan, 0, 0xFF00 ); // DestVol=0, DestFC=0xFF00 AweWrD( AWE_CurrV_FC |gChan, 0, 0xFF00 ); // CurrVol=0, CurrFC=0xFF00 AweWrD( AWE_CurrPitch |gChan, AweLinPitch, 0);// CurrPitch // Now start the note and envelopes AweWrW( AWE_Env2Su_Dcy |gChan, 0x7F01 ); // ENV2 Sustain=max & Decay=min }