;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;SNES Digital Signal Processor Emulator ; Copyright (C)1999-2006 Alpha-II Productions ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ CPU PPRO BITS 32 ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;Header files %include "../../Macro.Inc" %include "APU.Inc" %include "SPC700.Inc" %define INTERNAL %include "DSP.Inc" GLOBAL SetDSPReg_A ;Needed by SPC700.Asm %if DSPINTEG GLOBAL UpdateDSP ;Needed by SPC700.Asm GLOBAL SetEmuDSP ;Needed by APU.Asm %endif %if PROFILE EXTERN profile ;SPC700.Asm - Profiling information %endif ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;Equates ;Envelope precision ------------------------- E_SHIFT EQU 4 ;Amount to shift envelope to get 8-bit signed value ;Envelope adjustment rates ------------------ A_GAIN EQU (1 << E_SHIFT) ;Amount to adjust envelope values A_LINEAR EQU (128*A_GAIN)/64 ;Linear rate to increase/decrease envelope A_REL EQU (128*A_GAIN)/256 ;Rate to decrease envelope during release A_BENT EQU (128*A_GAIN)/256 ;Rate to increase envelope after bend A_NOATT EQU (64*A_GAIN) ;Rate to increase if attack rate is set to 0ms A_EXP EQU 0 ;Rate to decrease envelope exponentially (Not used) ;Envelope destination values ---------------- D_MAX EQU (128*A_GAIN)-1 ;Maximum envelope value D_ATTACK EQU (128*A_GAIN*63)/64 ;Destination of attack rate D_BENT EQU (128*A_GAIN*3)/4 ;First destination of bent line D_DECAY EQU (128*A_GAIN)/8 ;Minimum decay destination value D_MIN EQU 0 ;Minimum envelope value ;Array sizes -------------------------------- MIX_SIZE EQU 1024 ;Size of mixing buffer in samples FIRBUF EQU 2*2*64 ;stereo * ring loop * 256kHz / 32kHz ECHOBUF EQU 2*((192000*240)/1000) ;Size of echo buffer (stereo * 192kHz * 240ms) LOWTAPS EQU 32 ;Number of taps in low-pass FIR filter LOWBUF EQU 2*2*LOWTAPS VAATAPS EQU 16 ;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß ; Data SECTION .rdata ALIGN=32 ;12-bit Gaussian curve generated by SNES DSP gaussTab DW 5920,20880, 5984, 0, 5856,20880, 6048, 0, 5792,20864, 6096, 0, 5728,20864, 6160, 0 DW 5664,20864, 6224, 0, 5616,20864, 6288, 0, 5552,20864, 6352, 0, 5488,20848, 6416, 0 DW 5424,20848, 6480, 0, 5376,20848, 6560, 0, 5312,20832, 6624, 0, 5248,20832, 6688, 0 DW 5200,20816, 6752, 0, 5136,20800, 6816, 0, 5088,20800, 6880, 0, 5024,20784, 6944, 0 DW 4976,20768, 7024, 16, 4912,20752, 7088, 16, 4864,20752, 7152, 16, 4800,20736, 7216, 16 DW 4752,20720, 7296, 16, 4688,20704, 7360, 16, 4640,20688, 7424, 16, 4576,20672, 7504, 16 DW 4528,20656, 7568, 16, 4480,20640, 7632, 16, 4416,20608, 7712, 16, 4368,20592, 7776, 32 DW 4320,20576, 7856, 32, 4272,20544, 7920, 32, 4208,20528, 7984, 32, 4160,20512, 8064, 32 DW 4112,20480, 8128, 32, 4064,20464, 8208, 32, 4016,20432, 8272, 48, 3968,20400, 8352, 48 DW 3920,20384, 8432, 48, 3872,20352, 8496, 48, 3824,20320, 8576, 48, 3776,20304, 8640, 64 DW 3728,20272, 8720, 64, 3680,20240, 8800, 64, 3632,20208, 8864, 64, 3584,20176, 8944, 64 DW 3536,20144, 9008, 80, 3488,20112, 9088, 80, 3440,20080, 9168, 80, 3392,20048, 9232, 80 DW 3360,20016, 9312, 96, 3312,19968, 9392, 96, 3264,19936, 9472, 96, 3216,19904, 9536, 96 DW 3184,19856, 9616, 112, 3136,19824, 9696, 112, 3088,19792, 9776, 112, 3056,19744, 9840, 128 DW 3008,19712, 9920, 128, 2976,19664,10000, 128, 2928,19632,10080, 144, 2880,19584,10160, 144 DW 2848,19536,10240, 144, 2800,19504,10304, 160, 2768,19456,10384, 160, 2736,19408,10464, 160 DW 2688,19360,10544, 176, 2656,19312,10624, 176, 2608,19280,10704, 176, 2576,19232,10784, 192 DW 2544,19184,10848, 192, 2496,19136,10928, 208, 2464,19088,11008, 208, 2432,19040,11088, 224 DW 2400,18976,11168, 224, 2352,18928,11248, 240, 2320,18880,11328, 240, 2288,18832,11408, 240 DW 2256,18784,11488, 256, 2224,18720,11568, 256, 2192,18672,11648, 272, 2144,18624,11712, 272 DW 2112,18560,11792, 288, 2080,18512,11872, 304, 2048,18448,11952, 304, 2016,18400,12032, 320 DW 1984,18336,12112, 320, 1952,18288,12192, 336, 1920,18224,12272, 336, 1888,18176,12352, 352 DW 1872,18112,12432, 368, 1840,18048,12512, 368, 1808,18000,12592, 384, 1776,17936,12672, 384 DW 1744,17872,12752, 400, 1712,17808,12832, 416, 1696,17744,12896, 432, 1664,17696,12976, 432 DW 1632,17632,13056, 448, 1600,17568,13136, 464, 1584,17504,13216, 464, 1552,17440,13296, 480 DW 1520,17376,13376, 496, 1504,17312,13456, 512, 1472,17248,13536, 512, 1440,17184,13616, 528 DW 1424,17120,13680, 544, 1392,17056,13760, 560, 1376,16976,13840, 576, 1344,16912,13920, 576 DW 1328,16848,14000, 592, 1296,16784,14080, 608, 1280,16720,14144, 624, 1248,16640,14224, 640 DW 1232,16576,14304, 656, 1216,16512,14384, 672, 1184,16432,14464, 688, 1168,16368,14528, 704 DW 1136,16304,14608, 720, 1120,16224,14688, 736, 1104,16160,14768, 752, 1072,16080,14832, 768 DW 1056,16016,14912, 784, 1040,15952,14992, 800, 1024,15872,15056, 816, 992,15808,15136, 832 DW 976,15728,15216, 848, 960,15648,15280, 864, 944,15584,15360, 880, 928,15504,15440, 896 DW 896,15440,15504, 928, 880,15360,15584, 944, 864,15280,15648, 960, 848,15216,15728, 976 DW 832,15136,15808, 992, 816,15056,15872, 1024, 800,14992,15952, 1040, 784,14912,16016, 1056 DW 768,14832,16080, 1072, 752,14768,16160, 1104, 736,14688,16224, 1120, 720,14608,16304, 1136 DW 704,14528,16368, 1168, 688,14464,16432, 1184, 672,14384,16512, 1216, 656,14304,16576, 1232 DW 640,14224,16640, 1248, 624,14144,16720, 1280, 608,14080,16784, 1296, 592,14000,16848, 1328 DW 576,13920,16912, 1344, 576,13840,16976, 1376, 560,13760,17056, 1392, 544,13680,17120, 1424 DW 528,13616,17184, 1440, 512,13536,17248, 1472, 512,13456,17312, 1504, 496,13376,17376, 1520 DW 480,13296,17440, 1552, 464,13216,17504, 1584, 464,13136,17568, 1600, 448,13056,17632, 1632 DW 432,12976,17696, 1664, 432,12896,17744, 1696, 416,12832,17808, 1712, 400,12752,17872, 1744 DW 384,12672,17936, 1776, 384,12592,18000, 1808, 368,12512,18048, 1840, 368,12432,18112, 1872 DW 352,12352,18176, 1888, 336,12272,18224, 1920, 336,12192,18288, 1952, 320,12112,18336, 1984 DW 320,12032,18400, 2016, 304,11952,18448, 2048, 304,11872,18512, 2080, 288,11792,18560, 2112 DW 272,11712,18624, 2144, 272,11648,18672, 2192, 256,11568,18720, 2224, 256,11488,18784, 2256 DW 240,11408,18832, 2288, 240,11328,18880, 2320, 240,11248,18928, 2352, 224,11168,18976, 2400 DW 224,11088,19040, 2432, 208,11008,19088, 2464, 208,10928,19136, 2496, 192,10848,19184, 2544 DW 192,10784,19232, 2576, 176,10704,19280, 2608, 176,10624,19312, 2656, 176,10544,19360, 2688 DW 160,10464,19408, 2736, 160,10384,19456, 2768, 160,10304,19504, 2800, 144,10240,19536, 2848 DW 144,10160,19584, 2880, 144,10080,19632, 2928, 128,10000,19664, 2976, 128, 9920,19712, 3008 DW 128, 9840,19744, 3056, 112, 9776,19792, 3088, 112, 9696,19824, 3136, 112, 9616,19856, 3184 DW 96, 9536,19904, 3216, 96, 9472,19936, 3264, 96, 9392,19968, 3312, 96, 9312,20016, 3360 DW 80, 9232,20048, 3392, 80, 9168,20080, 3440, 80, 9088,20112, 3488, 80, 9008,20144, 3536 DW 64, 8944,20176, 3584, 64, 8864,20208, 3632, 64, 8800,20240, 3680, 64, 8720,20272, 3728 DW 64, 8640,20304, 3776, 48, 8576,20320, 3824, 48, 8496,20352, 3872, 48, 8432,20384, 3920 DW 48, 8352,20400, 3968, 48, 8272,20432, 4016, 32, 8208,20464, 4064, 32, 8128,20480, 4112 DW 32, 8064,20512, 4160, 32, 7984,20528, 4208, 32, 7920,20544, 4272, 32, 7856,20576, 4320 DW 32, 7776,20592, 4368, 16, 7712,20608, 4416, 16, 7632,20640, 4480, 16, 7568,20656, 4528 DW 16, 7504,20672, 4576, 16, 7424,20688, 4640, 16, 7360,20704, 4688, 16, 7296,20720, 4752 DW 16, 7216,20736, 4800, 16, 7152,20752, 4864, 16, 7088,20752, 4912, 16, 7024,20768, 4976 DW 0, 6944,20784, 5024, 0, 6880,20800, 5088, 0, 6816,20800, 5136, 0, 6752,20816, 5200 DW 0, 6688,20832, 5248, 0, 6624,20832, 5312, 0, 6560,20848, 5376, 0, 6480,20848, 5424 DW 0, 6416,20848, 5488, 0, 6352,20864, 5552, 0, 6288,20864, 5616, 0, 6224,20864, 5664 DW 0, 6160,20864, 5728, 0, 6096,20864, 5792, 0, 6048,20880, 5856, 0, 5984,20880, 5920 ;Registers that require special handling depending on output type dspRegsI DD RVolL, RVolR, RMVolL, RMVolR, REVolL, REVolR, REFB, RFCI ;Integer dspRegsM DD RVolLM, RVolRM, RMVolLM, RMVolRM, REVolLM, REVolRM, REFBM, RFCI ;Monaural dspRegsF DD RVolLF, RVolRF, RMVolLF, RMVolRF, REVolLF, REVolRF, REFBF, RFCF ;Floating-point ;Pointers to interpolation functions for each mixing routine intRout DD NoneI, LinearI, CubicI, GaussI, SincI DD NoneX, LinearX, CubicX, GaussX, SincX DD NoneF, LinearF, CubicF, GaussF, SincF DD 0 ;Pointers to each mixing routine: none, integer (386), mmx, and float mixRout DD EmuDSPN, EmuDSPI, EmuDSPX, EmuDSPF ;Masks for MMX gaussian interpolation ---- loSmp DD 00000FFFFh,00000FFFFh hiSmp DD 0FFFF0000h,0FFFF0000h ;Frequency table ------------------------- freqTab DW 0 DW 2048, 1536, 1280 ;Number of samples between updates. Used to determine DW 1024, 768, 640 ;envelope rates and noise frequencies DW 512, 384, 320 DW 256, 192, 160 DW 128, 96, 80 DW 64, 48, 40 DW 32, 24, 20 DW 16, 12, 10 DW 8, 6, 5 DW 4, 3 DW 2 DW 1 ;Floating point constants ---------------- fn2_5 DD -2.5 fp0_5 DD 0.5 Scale32 fp64k,16 Scale32 fpShR7,-7 ;Voice volume Scale32 fpShR15,-15 ;Interpolation Scale32 fpShR16,-16 Scale32 fpShR23,-23 ;Echo feedback ;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß ; Variables %ifndef WIN32 SECTION .bss ALIGN=256 %else SECTION .bss ALIGN=64 ;The BSS must be aligned on at least a 256-byte boundary. This is tricky in Windows because the ;win32 object files can only specify 64-byte alignment. To enforce that the BSS is properly ;aligned, a debug breakpoint has been inserted into InitDSP that will be triggered if alignment is ;not strict enough. In such an event, try padding with multiples of 64 until it works. resb 192 ;Force page alignment %endif ;Don't touch! All arrays are carefully aligned on large boundaries to facillitate easier indexing ;and better cache utilization. Add new variables at the end or in empty slots. ;DSP Core ---------------------------- [0] mix resb 8*128 ; Mixing settings for each voice dsp resb 128 ; DSP registers ;Look-up Tables -------------------- [480] rateTab resd 32 ;Update Rate Table cubicTab resq 256 ;Cubic interpolation sincTab resq 256*2 ;Sinc interpolation regTab resd 128 ;Jump table for DSP register writes ;Integration with SPC700 ---------- [1F00] %define _Out(v) ESI + (out %+ v - output) output: outPBuf resd 1 ;-> output buffer outLeft resd 1 ;Number of samples left to fill output buffer outCnt resd 1 ;t64 count at last call to EmuDSP outDec resd 1 ;Fractional number of samples to be generated outRate resd 1 ;Output sample rate set by APU.Asm ;Globals -------------------------- [1F14] songLen resd 1 ;Length of song (in ticks) fadeLen resd 1 ;Length of fade (in ticks) pTrace resd 1 ;-> Debugging vector dbgOpt resb 1 ;Debugging options konLate resb 1 resb 2 resd 1 ;DSP Options ---------------------- [1F28] procType resb 1 ;Type of host CPU (returned by GetProcType) dspMix resb 1 ;Mixing routine dspChn resb 1 ;Number of channels being output dspSize resb 1 ;Size of samples in bytes dspRate resd 1 ;Output sample rate pitchBas resd 1 ;Base sample rate pitchAdj resd 1 ;Amount to adjust pitch rates [16.16] pInter resd 1 ;-> interpolation function dspInter resb 1 ;Interpolation method voiceMix resb 1 ;Voices that are currently being mixed dspOpts resb 1 ;Option flags passed to SetDSPOpt surround resb 1 ;Turn on surround sound pDecomp resd 1 ;-> sample decompression routine ;Volume --------------------------- [1F44] volAmp resd 1 ;Amplification [16.16] volAtt resd 1 ;Global volume attenuation [1.16] volAdj resd 1 ;Amount to adjust main volumes [-15.16] volMainL resd 1 ;Main volume volMainR resd 1 volEchoL resd 1 ;Echo volume volEchoR resd 1 mMaxL resd 1 ;Maximum absolute sample output mMaxR resd 1 volRamp resd 2 ;Amount to ramp volume per sample (+/-) volSepar resd 1 ;Stereo separation ;Noise ---------------------------- [1F74] nRate resd 1 ;Noise sample rate reciprocal [.32] nAcc resd 1 ;Noise accumulator [.32] (>= 1 generate a new sample) nSmp resd 1 ;Current Noise sample ;Echo filtering ------------------- [1F80] firTaps resd 2*8 ;Filter coefficents (doubled for stereo MMX) firCur resd 1 ;Index of the first sample to feed into the filter firRate resd 1 ;Rate to feed samples into filter firDec resd 1 ;Temp firEnabl resb 1 ;0 if filtering is disabled (each bit C0-C7 != 0) disEcho resb 1 ;0 if echo is enabled resb 2 ; [0] - FIR coefficients are all 0 ; [4] - User has disabled echo ; [5] - Echo is disabled in FLG register ;Echo ----------------------------- [1FD0] echoDel resd 1 ;Size of delay (in bytes) echoCur resd 1 ;Current sample in echo area resd 1 efbct resd 1 ;User specified echo feedback crosstalk echoFB resd 2 ;Echo feedback echoFBCT resd 2 ;Echo feedback crosstalk ;Single source playback ----------- [1FF0] %define _Src(v) EBX + (src %+ v - src) src: resw 8 ;Temporary buffer for single sound playback srcBuf resw 16 ; tBuf must start on a 64-byte boundary srcIdx resd 1 srcRate resd 1 srcDec resd 1 srcBlk resd 1 srcLoop resd 1 srcP1 resw 1 srcP2 resw 1 srcBRR resb 8 ;Temporary buffer for storing BRR block ;Low-pass filter ------------------ [2040] realRate resd 1 ;Real output sample rate, not emulated rate realOutS resd 1 ;Real size of output buffer lowAmp resd 1 ;Amplification to apply during filtering lowCur resd 1 lowRate resd 1 ;Rate to feed samples into filter [16.16] lowDec resd 1 ;Current sample fraction [0.16] lowRFI resd 2 ;Current RFI simulation sample vaaCut resd 1 ;Cutoff rate for voice AA filtering ;Auto Amplification Reduction ----- [2064] %define _AAR(v) ESI + (aar %+ v - aar) aar: aarCnt resd 1 ;t64Cnt at last amp increase aarMin resd 1 ;Minimum value to decrease to aarMax resd 1 ;Maximum value to increase to aarMMaxL resd 1 ;Maximum absolute main output aarMMaxR resd 1 aarThrsh resd 1 ;Attenuation threshold aarType resb 1 ;Type of AAR resb 3 ;Space for new variables ---------- [2080] resd 32 ;<-- room for new variables here ;Storage buffers ------------------ [2100] mixBuf resd 2*2*MIX_SIZE ;Temporary mixing buffer (2 main, 2 echo) ;Ring buffers are reversed, meaning that buffer[0] contains the most recent sample, buffer[1] ;contains the previous sample, and so forth. startBuf: echoBuf resd ECHOBUF ;External echo memory (240ms @ 192kHz) firBuf resd FIRBUF ;Unaltered echo samples fed into FIR filter lowBuf resd LOWBUF ;Interpolated samples fed into low-pass FIR filter vaaBuf resw 8*VAATAPS endBuf: lowTaps resd LOWTAPS ;Filter coefficients used for low-pass filter vaaTaps resd 8*VAATAPS ;Filter coefficients for voice anti-aliasing filters ;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß ; Code SECTION .text ALIGN=16 ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Get Processor Information PROC GetProcInfo USES ECX,EDX,EBX ;Test for CPUID instruction -------------- PushFD Mov EDX,[ESP] ;EDX = EFLAGS XOr dword [ESP],200000h ;Flip CPUID support flag PopFD ;Restore EFLAGS PushFD Pop EAX ;EAX = New EFLAGS XOr EAX,EDX ;Does processor support the CPUID instruction? JZ .Done ; No, Return 0 XOr EAX,EAX ;Get the number of extended functions CPUID Test EAX,EAX ;Does CPU have any? JZ .Done ; No, Return 0 ;Test for MMX support -------------------- Mov EAX,1 ;EAX = Get extended information CPUID XOr EAX,EAX BT EDX,23 ;Does CPU have MMX support? JNC .Done ; No, Return 0 ;Check processor manufacturer ------------ XOr EAX,EAX CPUID Cmp EBX,"Genu" ;Is processor an Intel? SetE AL Cmp EDX,"ineI" SetE AH Or AL,AH Cmp ECX,"ntel" SetE AH Or AL,AH JZ .NotIntel Mov EAX,1 CPUID BT EDX,25 ;Does CPU have SIMD support? SetC AL ; Yes MovZX EAX,AL ShL EAX,2 Or AL,1 ;Return MMX support as well RetS .NotIntel: Cmp EBX,"Auth" ;Is processor an AMD? SetE AL Cmp EDX,"enti" SetE AH Or AL,AH Cmp ECX,"cAMD" SetE AH Or AL,AH JZ .NotAMD Mov EAX,80000001h CPUID BT EDX,31 ;Does CPU have 3DNow! support? SetC AL ; Yes MovZX EAX,AL Add EAX,EAX Or AL,1 RetS .NotAMD: Mov EAX,1 .Done: ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Calculate Power of e ; ;Desc: ; Calculates e to the power of x by using the formula: ; ; 2^(x*log2(e)) ; ; Where 2^x is calculated by: ; ; 2^int(x) * 2^frac(x) ; ;In: ; ST = x ; ;Out: ; ST = e^x ;PROC Exp ; ; FStCW [ESP-6] ;Save control state ; FStCW [ESP-4] ; FStCW [ESP-2] ; ; And word [ESP-4],0F0FFh ; And word [ESP-6],0F0FFh ; Or word [ESP-4],0300h ;Enable extended double precision ; Or word [ESP-6],0C00h ; " " w/ rounding towards 0 ; FLdCW [ESP-4] ; ; FLdL2e ; |x Log2(e) ; FMulP ST1,ST ; |x*Log2(e) ; ; FLd ST1 ; |ex ex ; FLdCW [ESP-6] ; FRndInt ;Get the integer portion |ex int(ex) ; FLdCW [ESP-4] ; FSub ST1,ST ;Get the fractional portion|ex-iex iex ; ; FLd1 ; |fex iex 1 ; FScale ; |fex iex 1<Cubic array |FPU Stack after execution .NextC: ;x1=(n/256) x2=(n/256)^2 x3=(n/256)^3 FILd dword [%$ipD] ;Load (int) delta |D FDiv dword [%$fp256] ;Divide delta by 256 |D/256=X1 FLd ST ;Copy top of stack |X1 X1 FMul ST,ST1 ;Square point |X1 X1*X1=X2 FLd ST ; |X1 X2 X2 FMul ST,ST2 ;Cube point |X1 X2 X2*X1=X3 ;s[-1] *= -.5(x^3) + (x^2) - .5x ------ FLd dword [%$fn0_5] ; |X1 X2 X3 -0.5 FMul ST,ST3 ; |X1 X2 X3 -0.5*X1=T1 FAdd ST,ST2 ; |X1 X2 X3 T1+X2 FLd dword [%$fn0_5] ; |X1 X2 X3 T1 -0.5 FMul ST,ST2 ; |X1 X2 X3 T1 -0.5*X3=T2 FAddP ST1,ST ; |X1 X2 X3 T1+T2 FMul dword [%$fp32km1] ;Convert to fixed point (-.15) FIStP word [EDI] ;Store value in cubicTab |X1 X2 X3 ;s[0] *= 1.5(x^3) - 2.5(x^2) + 1 ------ FLd dword [fn2_5] ; |X1 X2 X3 -2.5 FMul ST,ST2 ; |X1 X2 X3 -2.5*X2=T1 FLd dword [%$fp1_5] ; |X1 X2 X3 T1 1.5 FMul ST,ST2 ; |X1 X2 X3 T1 1.5*X3=T2 FLd1 ; |X1 X2 X3 T1 T2 1.0 FAddP ST1,ST ; |X1 X2 X3 T1 T2+1 FAddP ST1,ST ; |X1 X2 X3 T1+T2 FMul dword [%$fp32km1] FIStP word [2+EDI] ; |X1 X2 X3 ;s[1] *= -1.5(x^3) + 2(x^2) + .5x ----- FLd dword [fp0_5] ; |X1 X2 X3 0.5 FMul ST,ST3 ; |X1 X2 X3 0.5*X1=T1 FLd ST2 ; |X1 X2 X3 T1 X2 FAdd ST,ST3 ; |X1 X2 X3 T1 X2+X2=T2 FLd dword [%$fn1_5] ; |X1 X2 X3 T1 T2 -1.5 FMul ST,ST3 ; |X1 X2 X3 T1 T2 -1.5*X3=T3 FAddP ST1,ST ; |X1 X2 X3 T1 T2+T3 FAddP ST1,ST ; |X1 X2 X3 T1+T2 FMul dword [%$fp32km1] FIStP word [4+EDI] ; |X1 X2 X3 ;s[2] *= .5(x^3) - .5(x^2) ------------ FLd dword [%$fn0_5] ; |X1 X2 X3 -0.5 FMul ST,ST2 ; |X1 X2 X3 -0.5*X2=T1 FLd dword [fp0_5] ; |X1 X2 X3 T1 0.5 FMul ST,ST2 ; |X1 X2 X3 T1 0.5*X3=T2 FAddP ST1,ST ; |X1 X2 X3 T1+T2 FMul dword [%$fp32km1] FIStP word [6+EDI] ; |X1 X2 X3 Add EDI,8 FStP ST ;Pop X's off stack |X1 X3 FStP ST ; |X3 FStP ST ; |(empty) Inc byte [%$ipD] JNZ .NextC ;Build a look-up table for 8-point sinc interpolation with a Hann window ; ; sin((n/2)pi x) ; -------------- * (0.5 + 0.5cos(pi x)) ; (n/2)pi x ; ;n is the number of points of interpolation (8) Mov EDI,sincTab XOr EAX,EAX ;If ipD were initialized to -768 (-3.0), a divide by Mov [EDI],EAX ; zero error would occur when building the table. Mov [4+EDI],EAX ; So we manually initialize the first row, which is Mov [8+EDI],EAX ; easy to do. Mov [12+EDI],EAX Mov word [6+EDI],32767 ;Set first row to 0 0 0 1 0 0 0 0 Add EDI,16 Mov dword [%$ipD],-769 ;Fill remaining rows -769 to -1023 (-3.004 to -3.996) Mov CH,255 .NextS: Mov CL,8 .NextSS: FILd dword [%$ipD] ;(x >> 10) * 4pi |x FMul dword [%$fpShR8] ; |x>>8 FLdPi ; |x pi FMulP ST1,ST ; |x*pi FLd ST ; |x x FSin ;Sinc function |x sin(x) FDivRP ST1,ST ;sin(x) / x |x/sin(x) FILd dword [%$ipD] ;Hann window |sinc x FMul dword [%$fpShR10] ;cos((x >> 10) * pi) |sinc x>>10 FLdPi ; |sinc x pi FMulP ST1,ST ; |sinc x*pi FCos ; |sinc cos(x*pi) FLd1 ;(1.0 + cos) * 0.5 |sinc cos 1.0 FAddP ST1,ST ; |sinc cos+1 FMul dword [fp0_5] ; |sinc cos*0.5 FMulP ST1,ST ;Multiply by window |sinc*window FMul dword [%$fp32k] ;Convert to integer |sinc<<15 FIStP word [EDI] ;Store |(empty) Add EDI,2 Add dword [%$ipD],256 ;Move to next point of interpolation (x += 256) Dec CL JNZ .NextSS Sub dword [%$ipD],801h Dec CH JNZ .NextS Call GetProcInfo Mov [procType],AL Call SetDSPOpt,1,2,16,32000,INT_GAUSS,0 Call SetDSPDbg,0,0 Call SetDSPAAR,AAR_TOFF,32768,16384,32768 ;No AAR, threshold = 0dB, min = -6dB, max = 0dB Call SetDSPDbg,0,0 ;Disable debugging ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Reset DSP Settings PROC ResetDSP USES ECX,EBX,EDI XOr EAX,EAX ;Erase DSP Registers --------------------- Mov EDI,dsp Mov ECX,128/4 Rep StoSD Mov byte [dsp+flg],0E0h ;Place DSP in power up mode ;Erase internal mixing settings ---------- Mov AH,8 Mov EBX,dsp Mov EDI,mix .ClrMix: Mov CL,mFlg Rep StoSB And byte [EDI],MFLG_MUTE ;Leave voice muted Or byte [EDI],MFLG_OFF ;Voice is inactive Inc EDI Mov CL,7Fh-mFlg Rep StoSB Mov [EDI+pDSPV-80h],EBX Add EBX,10h Dec AH JNZ short .ClrMix ;Erase global volume settings ------------ Mov [volMainL],EAX Mov [volMainR],EAX Mov [volEchoL],EAX Mov [volEchoR],EAX ;Erase noise settings -------------------- Mov [nRate],EAX Mov [nAcc],EAX Mov [nSmp],EAX ;Reset buffers --------------------------- Mov EDI,startBuf Mov ECX,(endBuf-startBuf) / 4 Rep StoSD ;Echo region ----------------------------- Mov [echoCur],EAX ;Reset echo variables Mov dword [echoDel],8 ;Delay 1 sample Mov [echoFB],EAX Mov [4+echoFB],EAX Mov [echoFBCT],EAX Mov [4+echoFBCT],EAX ;Echo filter ----------------------------- Mov EDI,firTaps ;Reset filter coefficients Mov CL,2*8 Rep StoSD Mov [firCur],EAX ;Reset filter variables Mov [firDec],EAX Mov [firEnabl],AL ;Reset low-pass filter ------------------- Mov dword [lowCur],lowBuf Mov [lowDec],EAX Mov [lowRFI],EAX Mov [4+lowRFI],EAX ;Disable voices -------------------------- Mov [voiceMix],AL ;Reset times ----------------------------- Mov [outLeft],EAX Mov [outCnt],EAX Mov [outDec],EAX Mov [konLate],AL Mov dword [songLen],-1 Mov dword [fadeLen],1 ;Reset AAR ------------------------------- Mov [mMaxL],EAX Mov [mMaxR],EAX Mov [aarCnt],EAX Mov [aarMMaxL],EAX Mov [aarMMaxR],EAX ;Reset fade volume ----------------------- Call SetDSPVol,10000h And byte [dbgOpt],~DSP_HALT ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Calculate Anti-Aliasing Filter Coefficients ; ;Adapted from code in the SoundTouch sound processing library. ;If aaCutoff is greater than the Nyquist frequency, the center tap is 1 with the remaining taps at 0 ;Taps are written out as integers. Use aaScale to adjust the taps for fixed point. ; ;In: ; aaBuf -> Buffer to store taps in ; aaNum = Number of taps to generate ; aaRate = Sample rate ; aaCutoff = Cutoff frequency ; aaScale = Number to scale taps by (float) ; ;Destroys: ; EAX PROC AACoeffs, pBuf, num, rate, cutoff, scale LOCALS cnt,fp0_46,fp0_54 USES ECX,EDI Mov EAX,[%$cutoff] Add EAX,EAX Cmp EAX,[%$rate] JAE .NoFilter Mov dword [%$fp0_46],3EEB851Fh Mov dword [%$fp0_54],3F0A3D71h FLdZ ; |sum FILd dword [%$cutoff] ;fc2 = 2.0 * Cutoff |sum cutoff FIDiv dword [%$rate] ; |sum cutoff/rate FAdd ST,ST ; |sum ratio*2.0 FLdPi ;wc = Pi * fc2 |sum fc2 Pi FMul ST,ST1 ; |sum fc2 Pi*fc2 FLdPi ;co = 2*PI / NumTaps |sum fc2 wc Pi FAdd ST,ST ; |sum fc2 wc 2*Pi FIDiv dword [%$num] ; |sum fc2 wc 2Pi/num Mov EAX,[%$num] Mov ECX,EAX ;ECX = Tap index ShR EAX,1 Inc EAX Mov [%$cnt],EAX Mov EDI,[%$pBuf] .Next: Dec dword [%$cnt] FILd dword [%$cnt] ;cnt = i - NumTaps / 2 |sum fc2 wc co cnt FLd1 ;h = 1.0 |sum fc2 wc co cnt 1.0 JZ short .Zero ;if (cnt != 0) |sum fc2 wc co cnt 1.0 FStP ST ; |sum fc2 wc co cnt FLd ST ;temp = cnt * wc |sum fc2 wc co cnt cnt FMul ST,ST3 ; |sum fc2 wc co cnt cnt*wc FLd ST ;sin(temp) * fc2 / temp |sum fc2 wc co cnt temp temp FSin ; |sum fc2 wc co cnt temp sin(temp) FMul ST,ST5 ; |sum fc2 wc co cnt temp sin*fc2 FDivRP ST1,ST ; |sum fc2 wc co cnt sin/temp .Zero: Dec ECX FXch ST1 ;Hamming window |sum fc2 wc co h cnt FMul ST,ST2 ;0.54+0.46*cos(co*cnt) |sum fc2 wc co h cnt*co FCos ; |sum fc2 wc co h cos(cnt) FMul dword [%$fp0_46] ; |sum fc2 wc co h w*0.46 FAdd dword [%$fp0_54] ; |sum fc2 wc co h w+0.54 FMulP ST1,ST ;tap = w * h; |sum fc2 wc co h*w FSt dword [ECX*4+EDI] FAddP ST4,ST ;sum += temp |sum+tap fc2 wc co JNZ short .Next FStP ST ; |sum fc2 wc FStP ST ; |sum fc2 FStP ST ; |sum ;Normalize coefficients ------------------ FLd1 FDivRP ST1,ST FMul dword [%$scale] Mov ECX,[%$num] .Norm: Dec ECX FLd dword [ECX*4+EDI] FMul ST1 FIStP dword [ECX*4+EDI] JNZ short .Norm FStP ST ; |(empty) RetS .NoFilter: XOr EAX,EAX Mov EDI,[%$pBuf] Mov ECX,[%$num] Rep StoSD Mov EDI,[%$pBuf] Mov ECX,[%$num] ShR ECX,1 FLd dword [%$scale] FIStP dword [ECX*4+EDI] ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Set DSP Options PROC SetDSPOpt, mixType, chn, bits, rate, inter, opts LOCALS fixVol,eraseBuf,ftemp USES ALL Mov byte [%$fixVol],0 Mov byte [%$eraseBuf],0 ;========================================= ;Verify parameters ;mixType --------------------------------- MovZX EAX,byte [dspMix] Mov EDX,[%$mixType] Cmp EDX,-1 JE short .DefMix Mov AL,3 Cmp EDX,MIX_FLOAT JAE short .DefMix Test byte [procType],CPU_MMX SetNZ AL Inc AL Cmp EDX,MIX_INT JAE short .DefMix XOr EAX,EAX .DefMix: Mov [%$mixType],EAX ;numChn ---------------------------------- MovZX EAX,byte [dspChn] Mov EDX,[%$chn] Cmp EDX,-1 JE short .DefChn Mov EAX,EDX Cmp EDX,1 JE short .DefChn Cmp EDX,2 JE short .DefChn Cmp EDX,4 JE short .DefChn Mov EAX,2 .DefChn: Mov [%$chn],EAX ;bits ------------------------------------ MovZX EAX,byte [dspSize] Mov EDX,[%$bits] Cmp EDX,-1 JE short .DefBits Mov EAX,EDX SAR EAX,3 Cmp EDX,8 JE short .DefBits Cmp EDX,16 JE short .DefBits Cmp EDX,24 JE short .DefBits Cmp EDX,32 JE short .DefBits Cmp EDX,-32 JE short .DefBits Mov EAX,2 .DefBits: Mov [%$bits],EAX ;rate ------------------------------------ Mov EAX,[dspRate] Test byte [dspOpts],OPT_ANALOG JZ short .NoLow1 Mov EAX,[realRate] .NoLow1: Mov EDX,[%$rate] Cmp EDX,-1 JE short .DefRate Mov EAX,EDX XOr ECX,ECX Cmp EDX,8000 SetB CL Cmp EDX,192000 SetA CH Test ECX,ECX JZ short .DefRate Mov EAX,32000 .DefRate: Mov [%$rate],EAX ;inter ----------------------------------- MovZX EAX,byte [dspInter] Mov EDX,[%$inter] Cmp EDX,-1 JE short .DefInter Mov EAX,EDX Cmp EDX,4 JBE .DefInter Mov EAX,3 .DefInter: Mov [%$inter],EAX ;opts ------------------------------------ MovZX EAX,byte [dspOpts] Mov EDX,[%$opts] Cmp EDX,-1 JE short .DefOpts Mov EAX,EDX .DefOpts: Mov [%$opts],EAX ;Verify mix type is capable of producing requested output Cmp byte [%$mixType],0 ;MIX_NONE is unaffected by output settings JZ .MixOK Cmp byte [%$bits],2 ;ifn (bits >= 16-bit && numChn >= stereo && SetAE AL ; mixType >= MMX) Cmp byte [%$chn],2 SetAE AH And AL,AH Cmp byte [%$mixType],2 SetAE AH And AL,AH JNZ short .MixOK Mov byte [%$mixType],1 ;Force mix type to 386 Cmp byte [%$bits],1 ;if (bits == 8-bit) mixType = 386 JE short .MixOK Cmp byte [%$chn],1 ;if (numChn == mono) mixType = 386; bits = 16-bit JA short .Stereo Mov byte [%$bits],2 Jmp short .MixOK .Stereo: Cmp byte [%$bits],2 ;if (bits != 16-bit) mixType = float JE short .MixOK Mov byte [%$mixType],3 .MixOK: ;========================================= ;Options Mov DL,[%$opts] ;Select ADPCM routine -------------------- Mov dword [pDecomp],UnpackBRROld Test DL,OPT_OLDSMP JNZ short .OldSmp Mov dword [pDecomp],UnpackBRR .OldSmp: ;Pseudo surround sound ------------------- Mov AL,[surround] XOr AL,DL And AL,OPT_SURND Or [%$fixVol],AL ;Did surround sound flag change? Test DL,OPT_SURND SetZ AL Dec AL Mov [surround],AL ;Reverse stereo -------------------------- Mov AL,[dspOpts] XOr AL,DL And AL,OPT_REVERSE Or [%$fixVol],AL ;Did reverse flag change? ;Disable echo ---------------------------- Test DL,OPT_NOECHO SetZ AL Dec AL And AL,OPT_NOECHO And byte [disEcho],~OPT_NOECHO Or [disEcho],AL Mov [dspOpts],DL ;Save option flags ;========================================= ;Interpolation method MovZX EAX,byte [%$inter] ;Save interpolation type Mov [dspInter],AL MovZX EDX,byte [%$mixType] ;if (mixType != MIX_NONE) Test EDX,EDX JZ short .NoMix Dec EDX LEA EDX,[EDX*5] Add EDX,EAX Mov EAX,[EDX*4+intRout] Mov [pInter],EAX .NoMix: ;========================================= ;Calculate sample rate change Mov EAX,[%$rate] Mov [realRate],EAX Test byte [dspOpts],OPT_ANALOG JZ short .NoLow And byte [dspOpts],~OPT_ANALOG ;Clear flag, incase filter can't be implemented Cmp byte [%$mixType],2 ;if (mixType == MMX && rate >= 32000 && JNE short .NoLow ; (opts & OPT_LOW)) Cmp dword [%$rate],32000 JB short .NoLow XOr EDX,EDX Mov EAX,32000 << 16 Mov [lowDec],EDX Div dword [%$rate] Mov [lowRate],EAX Mov dword [%$rate],32000 Or byte [dspOpts],OPT_ANALOG Call AACoeffs,lowTaps,LOWTAPS,[realRate],13000,4F000000h ;2^31 .NoLow: Mov EAX,[%$rate] Cmp EAX,[dspRate] ;Has sample rate changed? JE .SameRate ; No Mov [dspRate],EAX ; Yes, Adjust a lot of items ;Calculate amount to adjust DSP pitch values XOr EDX,EDX ;EDX:EAX = Base pitch << 20 Mov EAX,[pitchBas] ShLD EDX,EAX,20 ShL EAX,20 Div dword [dspRate] Mov [pitchAdj],EAX ;Calculate update rate for envelopes and noise Mov ESI,freqTab Mov EDI,rateTab Mov EBX,32000 Mov ECX,31 .CalcRT: Mov AX,[ECX*2+ESI] ShL EAX,16 Mul dword [dspRate] Div EBX Cmp EAX,10000h JAE short .RTOK Mov EAX,10000h .RTOK: Mov [ECX*4+EDI],EAX Dec ECX JNZ short .CalcRT Mov [EDI],ECX ;Volume ramping rate ------------------ Mov dword [%$ftemp],32000 FILd dword [%$ftemp] FIDiv dword [dspRate] Mov dword [%$ftemp],3A000000h ;>> 11 FMul dword [%$ftemp] FSt dword [volRamp] FChS FStP dword [4+volRamp] ;Reset FIR info ----------------------- XOr EAX,EAX Mov [firDec],EAX Mov [firCur],EAX Mov [lowDec],EAX Mov EAX,[dspRate] MovZX EDX,word [2+dspRate] ShL EAX,16 Mov ECX,32000 Div ECX Mov [firRate],EAX ;firRate = (dspRate<<16) / 32kHz ;Adjust voice rates ------------------- Mov EDX,1 XOr EAX,EAX Div dword [%$rate] Mov ECX,EAX Mov EAX,[%$rate] ;Set voice cutoff to 3/4ths the Nyquist frequency or LEA EAX,[EAX*3] ; 18kHz, whichever is lower ShR EAX,3 Cmp EAX,18000 JB short .COffOK Mov EAX,18000 .COffOK: Mul ECX ShRD EAX,EDX,16 Mov [vaaCut],EAX Mov ESI,7*16 ;Adjust the current rates in each voice incase the Mov EBX,7*80h ; sample rate is being changed during emulation .Voice: MovZX EAX,word [ESI+dsp+pitch] ;Set pitch And AH,3Fh Mul dword [pitchAdj] ShRD EAX,EDX,16 Mov [EBX+mix+mRate],EAX MovZX EDI,byte [EBX+mix+eRIdx] ;Set envelope adjustment Mov EAX,[EDI*4+rateTab] Mov [EBX+mix+eRate],EAX Mov [EBX+mix+eCnt],EAX Sub ESI,16 Add EBX,-80h JNS short .Voice ;Adjust echo delay -------------------- MovZX EAX,byte [dsp+edl] ShL AL,4 Mul dword [dspRate] Mov ECX,1000 Div ECX Test EAX,EAX SetZ CL Or AL,CL ShL EAX,3 Mov [echoDel],EAX Mov byte [%$eraseBuf],1 .SameRate: ;========================================= ;Set sample size Mov AL,[%$bits] Cmp AL,[dspSize] ;If the sample size has changed, CL = 1 JE short .SameBits Mov [dspSize],AL .SameBits: ;========================================= ;Set number of channels Mov AL,[%$chn] Cmp AL,[dspChn] ;If the number of channels has changed, CL = 1 SetNE CL Or [%$fixVol],CL Mov [dspChn],AL ;========================================= ;Update areas affected by the mix type Mov AL,[%$mixType] Cmp AL,[dspMix] JE .SameMix Mov [dspMix],AL Mov byte [%$fixVol],1 ;Force volumes to be recalculated Mov byte [%$eraseBuf],1 .SameMix: ;========================================= ;Erase sample buffers Test byte [%$eraseBuf],-1 JZ short .NoErase XOr EAX,EAX Mov EDI,echoBuf Mov ECX,ECHOBUF Rep StoSD Mov EDI,firBuf Mov ECX,FIRBUF Rep StoSD Mov EDI,lowBuf Mov ECX,LOWBUF Rep StoSD Mov [aarMMaxL],EAX Mov [aarMMaxR],EAX Mov [mMaxL],EAX Mov [mMaxR],EAX .NoErase: ;========================================= ;Fixup volume handlers Test byte [%$fixVol],-1 JZ .Done ;Setup DSP register jump table -------- Mov ESI,dspRegsF ;ESI -> floating point register handlers Cmp byte [dspMix],3 JE .SetRegs Mov ESI,dspRegsM ;ESI -> mono integer register handlers Cmp byte [dspChn],1 JE .SetRegs Mov ESI,dspRegsI ;ESI -> stereo integer register handlers .SetRegs: Mov EDI,regTab Mov EAX,[ESI] ;EAX -> VOLL handler Mov EDX,[4+ESI] ;EDX -> VOLR handler Mov ECX,[28+ESI] ;ECX -> FC handler Test byte [dspOpts],OPT_REVERSE ;If reversed, swap pointers to left and right volume JZ short .NoRev1 XChg EAX,EDX .NoRev1: Mov EBX,70h .CopyVol: Mov [volL*4+EBX*4+EDI],EAX Mov [volR*4+EBX*4+EDI],EDX Mov [fc*4+EBX*4+EDI],ECX Sub BL,10h JNS short .CopyVol Mov EAX,[8+ESI] ;EAX -> MVOLL handler Mov EDX,[12+ESI] ;EDX -> MVOLR handler Mov EBX,[16+ESI] ;EBX -> EVOLL handler Mov ECX,[20+ESI] ;ECX -> EVOLR handler Test byte [dspOpts],OPT_REVERSE JZ short .NoRev2 XChg EAX,EDX XChg EBX,ECX .NoRev2: Mov [mvolL*4+EDI],EAX Mov [mvolR*4+EDI],EDX Mov [evolL*4+EDI],EBX Mov [evolR*4+EDI],ECX Mov EAX,[24+ESI] ;EAX -> EFB handler Mov [efb*4+EDI],EAX ;Reinitialize registers --------------- Mov ECX,70h .NextVoice: LEA EBX,[ECX+volL] Call InitReg LEA EBX,[ECX+volR] Call InitReg Mov EDX,mix Mov EAX,[ECX*8+EDX+mTgtL] Mov EBX,[ECX*8+EDX+mTgtR] Mov [ECX*8+EDX+mChnL],EAX Mov [ECX*8+EDX+mChnR],EBX LEA EBX,[ECX+fc] Call InitReg Sub CL,10h JNC short .NextVoice Mov BL,efb Call InitReg .Done: Mov EAX,[volAmp] ;Fixup after any changes that could affect main volumes Call SetDSPAmpB ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Debug DSP PROC SetDSPDbg, pTraceFunc, opts USES EDX Mov AL,[%$opts] Cmp AL,-1 JE short .NoOpts And AL,DSP_HALT Mov [dbgOpt],AL .NoOpts: Mov EAX,[pTrace] Mov EDX,[%$pTraceFunc] Cmp EDX,-1 JE short .NoFunc Mov [pTrace],EDX .NoFunc: ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Fix DSP After Loading Saved State PROC FixDSPLoad USES ALL ;Enable voices currently keyed on -------- Mov byte [voiceMix],0 Mov BL,kon Call InitReg ;Setup global paramaters ----------------- Mov BL,mvolL Call InitReg Mov BL,mvolR Call InitReg Mov BL,evolL Call InitReg Mov BL,evolR Call InitReg Mov BL,flg Call InitReg Mov BL,efb Call InitReg Mov BL,edl Call InitReg Mov ECX,70h .NextTap: LEA EBX,[ECX+fc] Call InitReg Sub CL,10h JNC short .NextTap ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Fix DSP After Seeking PROC FixDSPSeek, reset USES ECX,EDI Mov AL,[%$reset] Test AL,AL JZ .NoReset ;Turn off all voices ------------------ Mov byte [dsp+endx],-1 ;Mark all playing voices as ended XOr EAX,EAX Mov [dsp+kon],AL ;Reset key registers Mov [dsp+kof],AL Mov [voiceMix],AL Mov CL,8 Mov EDI,mix .ResetMix: Mov [EDI+eVal],EAX Mov [EDI+mOut],EAX And byte [EDI+mFlg],MFLG_MUTE Or byte [EDI+mFlg],MFLG_OFF Sub EDI,-80h Dec CL JNZ short .ResetMix Mov CL,8 Mov EDI,dsp .ResetDSP: Mov [EDI+envx],AL Mov [EDI+outx],AL Add EDI,10h Dec CL JNZ short .ResetDSP .NoReset: ;Erase buffers --------------------------- XOr EAX,EAX Mov EDI,startBuf Mov ECX,(endBuf-startBuf) / 4 Rep StoSD Call SetFade ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Save DSP's Current State of Operation PROC SaveDSP, pState USES ALL Mov EBX,[%$pState] ;DSP Registers --------------------------- Mov EDI,[EBX+DSPState.pReg] Test EDI,EDI JZ .NoRegs Mov ESI,dsp Mov ECX,128/4 Rep MovSD .NoRegs: ;Internal Voice Structures --------------- Mov EDI,[EBX+DSPState.pVoice] Test EDI,EDI JZ .NoMix Mov ESI,mix ;Copy structure array Mov ECX,8*128/4 Rep MovSD Mov ESI,[EBX+DSPState.pReg] Sub ESI,dsp Mov EDI,[EBX+DSPState.pVoice] Mov ECX,80h*7 .NextVoice: Add [ECX+EDI+pDSPV],ESI ;Point pDSPV to the correct place in memory Mov EAX,[pAPURAM] Sub [ECX+EDI+bCur],EAX MovZX EAX,byte [ECX+EDI+eRIdx] ;Adjust rate to 32kHz Mov AX,[EAX*2+freqTab] ShL EAX,16 Mov [ECX+EDI+eRate],EAX Mov EAX,32000 ;Adjust sample counter to 32kHz Mul dword [ECX+EDI+eCnt] Div dword [dspRate] Mov [ECX+EDI+eCnt],EAX Mov EAX,EDI ;Point sIdx to the correct place Sub EAX,mix Add [ECX+EDI+sIdx],EAX Add ECX,-80h JNS .NextVoice .NoMix: ;Echo region ----------------------------- Mov EDI,[EBX+DSPState.pEcho] Test EDI,EDI JZ .NoEcho LEA ESI,[EAX+echoBuf] ;Copy from the current echo position to the end of the Mov ECX,[echoDel] ; buffer Sub ECX,[echoCur] ShR ECX,2 Rep MovSD Mov ESI,echoBuf ;Copy remaing buffer Mov ECX,[echoCur] ShR ECX,2 Rep MovSD .NoEcho: ;Volume ---------------------------------- Mov AL,[aarType] ShL EAX,28 Or EAX,[volAmp] Mov [EBX+DSPState.amp],EAX ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Restore DSP's State of Operation PROC RestoreDSP, pState USES ALL Mov EBX,[%$pState] ;DSP Registers --------------------------- Mov ESI,[EBX+DSPState.pReg] Test ESI,ESI JZ .NoRegs Mov EDI,dsp Mov ECX,128/4 Rep MovSD .NoRegs: ;Internal Voice Structures --------------- Mov ESI,[EBX+DSPState.pVoice] Test ESI,ESI JZ .NoMix Mov EDI,mix ;Copy structure array Mov ECX,8*128/4 Rep MovSD Mov EDI,mix Mov EAX,dsp Mov CL,8 .FixDSPV: Mov [EDI+pDSPV],EAX Sub EDI,-80h Add EAX,10h Dec CL JNZ short .FixDSPV Mov byte [voiceMix],0 Mov EDI,mix XOr ECX,ECX .ChkVoice: Test byte [EDI+mFlg],MFLG_OFF JNZ .Inactive ShR ECX,7 BTS [voiceMix],ECX ShL ECX,7 Mov EAX,[pAPURAM] Add [EDI+bCur],EAX Mov EAX,[EDI+bCur] Mov AL,[EAX] Mov [EDI+bHdr],AL ;Envelope -------------------------- Mov ESI,32000 Mov EAX,[EDI+eCnt] Mul dword [dspRate] Div ESI Mov [EDI+eCnt],EAX Cmp dword [EDI+eRate],-1 JNE short .KeepEnv Push EBX Mov EBX,ECX Call ChgEnv Pop EBX .KeepEnv: MovZX EAX,byte [EDI+eRIdx] Mov EAX,[EAX*4+rateTab] Mov [EDI+eRate],EAX ;Sound source ---------------------- Mov EAX,EDI Sub EAX,ECX Sub EAX,[EBX+DSPState.pVoice] Add [EDI+sIdx],EAX ;Mixing ---------------------------- Cmp dword [EDI+mRate],-1 JNE short .KeepMix Push EBX LEA EBX,[EDI+pitch] Call InitReg Mov dword [EDI+mDec],0 LEA EBX,[EDI+volL] Call InitReg LEA EBX,[EDI+volR] Call InitReg Pop EBX .KeepMix: .Inactive: Add EDI,80h Sub ECX,-80h Cmp CH,4 JB .ChkVoice .NoMix: ;========================================= ;Restore Global Settings ;Volume ---------------------------------- Mov EAX,[EBX+DSPState.amp] And EAX,0FFFFFFFh Call SetDSPAmp, EAX ;SetDSPAmp will reset main and echo volumes Mov AL,[3+EBX+DSPState.amp] ShR AL,4 Mov [aarType],AL Call GetSPCTime XOr EDX,EDX Mov ECX,8000 Div ECX IMul EAX,ECX Mov [aarCnt],EAX ;DSP registers --------------------------- Mov BL,flg Call InitReg Mov BL,efb Call InitReg Mov BL,edl Call InitReg Mov ECX,70h .NextTap: LEA EBX,[ECX+fc] Call InitReg Sub ECX,10h JNC short .NextTap ;Variables ------------------------------- XOr EAX,EAX Mov [konLate],AL Mov [mMaxL],EAX Mov [mMaxR],EAX Mov [aarMMaxL],EAX Mov [aarMMaxR],EAX Mov dword [lowCur],lowBuf Mov [lowDec],EAX Mov dword [songLen],-1 Mov dword [fadeLen],1 ;========================================= ;Reset Buffers ;Echo region ----------------------------- Mov [echoCur],EAX Mov EBX,[%$pState] Mov ESI,[EBX+DSPState.pEcho] Mov EDI,echoBuf Test ESI,ESI JZ .NoEcho Mov ECX,[echoDel] ShR ECX,4 Rep MovSD Jmp .HasEcho .NoEcho: XOr EAX,EAX Mov ECX,ECHOBUF Rep StoSD .HasEcho: ;Restore FIR ring buffer ----------------- Mov EDI,firBuf Mov ESI,echoBuf Mov ECX,FIRBUF Rep MovSD Mov ESI,echoBuf Mov ECX,FIRBUF Rep MovSD Mov dword [firCur],0 ;Erase low-pass filter buffer ------------ XOr EAX,EAX Mov EDI,lowBuf Mov ECX,LOWBUF Rep StoSD ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Set Amplification Level PROC SetDSPAmpB USES ECX,EDX,EBX ;Multiply by volume ---------------------- Test byte [dspOpts],OPT_ANALOG JZ short .NoAmp ShR EAX,8 Neg EAX Mov [lowAmp],EAX Mov EAX,10000h .NoAmp: Mul dword [volAtt] ShRD EAX,EDX,16 Mov [volAdj],EAX ;Update global volumes ------------------- Mov BL,mvolL Call InitReg Mov BL,mvolR Call InitReg Mov BL,evolL Call InitReg Mov BL,evolR Call InitReg ENDP PROC SetDSPAmp, amp And byte [aarType],~AAR_ON ;Disable AAR Mov EAX,[%$amp] CDQ ;if amp<0 amp=0 Not EDX And EAX,EDX Mov [volAmp],EAX Call SetDSPAmpB ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Get Amplification Level PROC GetDSPAmp Mov EAX,[volAmp] ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Set Automatic Amplification Reduction PROC SetDSPAAR, type, thresh, min, max USES ESI Mov ESI,aar Cmp dword [%$max],-1 JE short .NoMax Mov EAX,[%$max] Mov [_AAR(Max)],EAX .NoMax: Cmp dword [%$min],-1 JE short .NoMin Mov EAX,[%$min] Mov [_AAR(Min)],EAX .NoMin: Cmp dword [%$thresh],-1 JE short .NoThresh Mov EAX,[%$thresh] Mov [_AAR(Thrsh)],EAX .NoThresh: Cmp byte [%$type],-1 JE short .NoType Mov AL,[%$type] And AL,AAR_TYPE Mov [_AAR(Type)],AL .NoType: XOr EAX,EAX Mov [_AAR(MMaxL)],EAX Mov [_AAR(MMaxR)],EAX Mov [mMaxL],EAX Mov [mMaxR],EAX Mov AL,[_AAR(Type)] And AL,AAR_TYPE Retc Z Or byte [_AAR(Type)],AAR_ON Cmp AL,AAR_TINC Retc B Or byte [_AAR(Type)],AAR_INC ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Process Auto Amplification Reduction PROC ProcessAAR USES ECX,EDX,ESI Mov ESI,aar ;Update max output for visualization ----- Mov EAX,[mMaxL] Sub EAX,[_AAR(MMaxL)] CDQ And EAX,EDX Sub [mMaxL],EAX Mov EAX,[mMaxR] Sub EAX,[_AAR(MMaxR)] CDQ And EAX,EDX Sub [mMaxR],EAX Mov EAX,[_AAR(MMaxL)] ;ECX = Max(maxL, maxR) Mov ECX,EAX Sub EAX,[_AAR(MMaxR)] CDQ And EAX,EDX Sub ECX,EAX XOr EAX,EAX Mov [_AAR(MMaxL)],EAX Mov [_AAR(MMaxR)],EAX ;Process AAR ----------------------------- Test byte [_AAR(Type)],AAR_ON JZ .Done Mov EAX,[_AAR(Thrsh)] Cmp ECX,EAX ;Has volume breached threshold? JBE short .Increase ; Nope, Check for increase Mul dword [volAmp] ;EAX = Amp * (MMax / Threshold) Div ECX Cmp EAX,[_AAR(Min)] ;if (EAX < Min) disable AAR JA short .NotMin Mov EAX,[_AAR(Min)] ;Clamp amp level And byte [_AAR(Type)],~AAR_ON ;Disable AAR .NotMin: And byte [_AAR(Type)],~AAR_INC ;Disable auto increase Mov [volAmp],EAX Call SetDSPAmpB ;Set new amplification RetS .Increase: Test byte [_AAR(Type)],AAR_INC ;Is auto increase enabled? Retc Z Call GetSPCTime ;Has 1/8th of a second gone by? Sub EAX,[_AAR(Cnt)] Cmp EAX,8000 Retc B Mov dword [ESP-4],3F817CBFh ;2^(1/60) (or 60th root of 2) FILd dword [volAmp] ;Increase amp level by 0.1dB FMul dword [ESP-4] FIStP dword [volAmp] Add [_AAR(Cnt)],EAX Mov EAX,[volAmp] ;Has amp reached max level? Cmp EAX,[_AAR(Max)] JB short .NotMax Mov EAX,[_AAR(Max)] And byte [_AAR(Type)],~AAR_INC Mov [volAmp],EAX .NotMax: Call SetDSPAmpB .Done: ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Set DSP Volume ; ;This value attenuates the output and was implemented to allow songs to be faded out. ResetDSP sets ;this value to 65536 (no attenuation). ; ;In: ; vol = Volume [-1.16] (0.0 to 1.0, negative values act as 0) ; ;Destroys: ; EAX PROC SetDSPVol, vol USES ECX,EDX,EBX Mov EAX,[%$vol] ;if EAX<0 EAX=0 CDQ Not EDX And EAX,EDX Mov [volAtt],EAX Test byte [dspOpts],OPT_ANALOG JNZ short .NoAmp Mul dword [volAmp] ShRD EAX,EDX,16 .NoAmp: Mov [volAdj],EAX ;Update global volumes ------------------- Mov BL,mvolL Call InitReg Mov BL,mvolR Call InitReg Mov BL,evolL Call InitReg Mov BL,evolR Call InitReg ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Set Fade Volume ; ;Calls SetDSPVol to fade the song out based on t64Cnt, songLen, and fadeLen. PROC SetFade USES EDX Call GetSPCTime ;EDX = T64Cnt - songLen; Mov EDX,EAX Sub EDX,[songLen] JBE .Done XOr EAX,EAX ;if (EAX > fadeLen) EAX = fadeLen; Cmp EDX,[fadeLen] SetA AL Dec EAX And EDX,EAX Not EAX And EAX,[fadeLen] Or EDX,EAX XOr EAX,EAX ;EDX = 65536 - ((EDX << 16) / fadeLen); ShRD EAX,EDX,16 ShR EDX,16 Div dword [fadeLen] Mov EDX,65536 Sub EDX,EAX Call SetDSPVol,EDX ;SetDSPVol(EDX); .Done: ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Set Song Length PROC SetDSPLength, song, fade USES EDX Mov EDX,[%$fade] XOr EAX,EAX ;if (fadeLen == 0) fadeLen = 1; Test EDX,EDX ;0 will cause a division error SetZ AL Or EDX,EAX Mov [fadeLen],EDX Mov EAX,[%$song] Add EDX,EAX Mov [songLen],EAX Call GetSPCTime ;if (t64Cnt < songLen) Cmp EAX,[%$song] JAE short .SetFade Call SetDSPVol,10000h RetS EDX .SetFade: Call SetFade ;If song is in fade mode, set fade volume Mov EAX,EDX ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;DSP Pitch Adjustment PROC SetDSPPitch, base USES EDX,EBX,ESI ;Calculate amount to adjust DSP pitch values XOr EDX,EDX Mov EAX,[%$base] Mov [pitchBas],EAX ShLD EDX,EAX,20 ShL EAX,20 Div dword [dspRate] Mov [pitchAdj],EAX ;Adjust voice rates to new pitch --------- Mov ESI,7*16 ;Adjust the current rates in each voice incase the Mov EBX,7*80h ; sample rate is being changed during emulation .Voice: Mov EAX,[EBX+mix+pDSPV] MovZX EAX,word [EAX+pitch] And AH,3Fh Mul dword [pitchAdj] ShRD EAX,EDX,16 Mov [EBX+mix+mRate],EAX Sub ESI,16 Add EBX,-80h JNS short .Voice ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Adjust Voice Volume for Stereo Separation ; ;A big nasty function to adjust the left and right channel volumes for stereo separation control ; ;In: ; EBX = Indexes current voice %if STEREO PROC ChnSep RVolL: RVolR: LOCALS fp128,fpShR7,fp0_5,ftemp USES ECX,EBX Mov dword [%$fp128],43000000h Mov dword [%$fpShR7],3C000000h Mov dword [%$fp0_5],3F000000h ShR EBX,3 Test byte [dspOpts],OPT_REVERSE JNZ short .Reverse MovSX EAX,byte [EBX+dsp+volL] MovSX EDX,byte [EBX+dsp+volR] Jmp short .Normal .Reverse: MovSX EAX,byte [EBX+dsp+volR] MovSX EDX,byte [EBX+dsp+volL] .Normal: LEA EBX,[EBX*8+mix] Mov [EBX+mChnL],EAX ;Save the sign of each channel Mov [EBX+mTgtL],EAX Mov [EBX+mChnR],EDX Mov [EBX+mTgtR],EDX Cmp EAX,EDX ;If both volumes are 0, an exception will be generated JE .Done Mov ECX,[volSepar] ;Don't waste time if no separation is applied Test ECX,ECX JZ .Done SAR EAX,31 SAR EDX,31 ;Convert left/right into vol/pan --------- ; _____________ ; | 2 2 ; | L R ; V = _ | --- + --- ; \| 128 128 ; ; 2 ; R ; P = --- - 0+5 ; V ; ; V is 0.0 to 1.414 (-inf to +3 dB) ; P is -0.5 to 0.5 (left to right) FILd dword [EBX+mChnR] ;V = sqrt((L/128)^2+(R/128)^2) |chnR FMul dword [%$fpShR7] ;Change vol to float |chnR/128=R FLd ST ; |R R FMul ST,ST ; |R R^2 FILd dword [EBX+mChnL] ; |R R chnL FMul dword [%$fpShR7] ; |R R chnL/128=L FMul ST,ST ; |R R L^2 FAddP ST1,ST ; |R R+L=V FSqrt ; |R sqrt(V) FXch ;P = (R/V)^2 - 0+5 |V R FAbs ; |V |R| FDiv ST,ST1 ; |V R/V FMul ST,ST ; |V R^2 FSub dword [%$fp0_5] ; |V R-0+5=P ;Adjust panning -------------------------- ; S is -1.0 to 0 (pan toward center): ; ; P = P + (P * S) ; ; ; S is 0 to 1.0 (pan toward outside): ; ; Left ; P = P + ((0.5 + P) * S) ; ; Right ; P = P + ((0.5 - P) * S) FLd ST ; |V P D Test byte [3+volSepar],80h ;Is panning adjusted toward center? JNZ short .ToCenter FSt dword [%$ftemp] FLd dword [%$fp0_5] ; |V P D 0.5 Test byte [3+%$ftemp],80h ;Is pan on left side? JZ short .Right FChS ;Negate 0.5 |V P D -0.5 .Right: FSubRP ST1,ST ;Get distance from side |V P 0.5-D .ToCenter: FMul dword [volSepar] ;Get fraction of distance |V P D*volSepar FAddP ST1,ST ;Add fraction to pan |V P+D FLd ST ;Copy new panning value |V P P ;Convert vol/pan back into left/right ---- ; _________ ; L = V * \| 0.5 - P * 128 ; _________ ; R = V * \| 0.5 + P * 128 FAdd dword [%$fp0_5] ;R = V*sqrt(+5+P)*128 |V P P+0.5 FSqrt ; |V P sqrt(P) FMul ST,ST2 ; |V P P*V=R FMul dword [%$fp128] ; |V P R*128 FIStP dword [EBX+mChnR] ; |V P XOr [EBX+mChnR],EDX ;Set the correct sign Sub [EBX+mChnR],EDX FSubR dword [%$fp0_5] ;L = V*sqrt(+5-P)*128 |V 0.5-P FSqrt ; |V sqrt(P) FMulP ST1,ST ; |V*P=L FMul dword [%$fp128] ; |L*128 FIStP dword [EBX+mChnL] ; |(empty) XOr [EBX+mChnL],EAX Sub [EBX+mChnL],EAX Mov EDX,[EBX+mChnR] Mov EAX,[EBX+mChnL] Mov [EBX+mTgtR],EDX Mov [EBX+mTgtL],EAX .Done: XOr EAX,EAX ENDP ;Channel separator for floating-point routines PROC ChnSepF RVolLF: RVolRF: LOCALS fpShR7,fp0_5,ftemp USES ECX,EBX Mov dword [%$fpShR7],3C000000h Mov dword [%$fp0_5],3F000000h ShR EBX,3 Test byte [dspOpts],OPT_REVERSE JNZ short .Reverse MovSX EAX,byte [EBX+dsp+volL] MovSX EDX,byte [EBX+dsp+volR] Jmp short .Normal .Reverse: MovSX EAX,byte [EBX+dsp+volR] MovSX EDX,byte [EBX+dsp+volL] .Normal: LEA EBX,[EBX*8+mix] Mov [EBX+mTgtL],EAX Mov [EBX+mTgtR],EDX Cmp EAX,EDX JE .NoSep Mov ECX,[volSepar] Test ECX,ECX JZ .NoSep And AL,80h ;Save sign bit of each volume And DL,80h ShL EAX,24 ShL EDX,24 ;Convert left/right into vol/pan --------- FILd dword [EBX+mTgtR] FMul dword [%$fpShR7] FLd ST FMul ST,ST FILd dword [EBX+mTgtL] FMul dword [%$fpShR7] FMul ST,ST FAddP ST1,ST FSqrt FXch FAbs FDiv ST,ST1 FMul ST,ST FSub dword [%$fp0_5] ;Adjust panning -------------------------- FLd ST Test byte [3+volSepar],80h JNZ short .ToCenterF FSt dword [%$ftemp] FLd dword [%$fp0_5] Test byte [3+%$ftemp],80h JZ short .RightF FChS .RightF: FSubRP ST1,ST .ToCenterF: FMul dword [volSepar] FAddP ST1,ST FLd ST ;Convert vol/pan back into left/right ---- FAdd dword [%$fp0_5] FSqrt FMul ST,ST2 FStP dword [EBX+mTgtR] Or [EBX+mTgtR],EDX FSubR dword [%$fp0_5] FSqrt FMulP ST1,ST FStP dword [EBX+mTgtL] Or [EBX+mTgtL],EAX XOr EAX,EAX RetS .NoSep: FILd dword [EBX+mTgtL] FMul dword [%$fpShR7] FStP dword [EBX+mTgtL] FILd dword [EBX+mTgtR] FMul dword [%$fpShR7] FStP dword [EBX+mTgtR] XOr EAX,EAX ENDP %endif ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Convert Stereo Volume to Monaural ; ;Attenuates the volume so hard panned values will sound the same as they would be percieved in a ;stereo system. PROC GetMonoVol, left, right USES ECX Mov EAX,[%$right] ;if (abs(mvLeft) == abs(mvRight)) return mvLeft; CDQ XOr EAX,EDX Sub EAX,EDX Mov ECX,EAX Mov EAX,[%$left] CDQ XOr EAX,EDX Sub EAX,EDX Cmp EAX,ECX Retc Z Mov dword [ESP-4],3F3504F3h ;2^-0.5 (or 1/sqrt(2)) FILd dword [%$left] ; |left FAbs ; ||left| FLd dword [ESP-4] ; |left 2^-0.5 FLd1 ; |left .707 1.0 FIld dword [%$right] ; |left .707 1.0 right FAbs ; |left .707 1.0 |right| ;if (abs(left) < abs(right)) swap values JA short .NoSwap FXCh ST,ST3 ; |right .707 1.0 left .NoSwap: ;Get the percentage of difference of the volumes ;diff = (vol - otherVol) / vol FSubR ST,ST3 ; |vol .707 1.0 vol-other FDiv ST,ST3 ; |vol .707 1.0 diff/vol ;Convert the linear percentage into a logarithmic value ;Hard panned volumes get attenuated -3dB ;vol = vol * (0.707 + (sqrt(1.0 - diff) * (1.0 - 0.707))) FSubR ST,ST1 ; |vol .707 1.0 1.0-diff FSqrt ; |vol .707 1.0 sqrt(diff) FXCh ST1 ; |vol .707 sqrt 1.0 FSub ST2 ; |vol .707 sqrt 1.0-0.707 FMulP ST1,ST ; |vol .707 sqrt*.293 FAddP ST1,ST ; |vol .707+att FMulP ST1,ST ; |vol*att FIStP dword [%$left] ; |(empty) Mov EAX,[%$left] ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Set Stereo Separation PROC SetDSPStereo, sep USES EDX,EBX %if STEREO Sub dword [%$sep],32768 ;Convert fixed point unsigned value to signed float FILd dword [%$sep] FMul dword [fpShR15] FStP dword [volSepar] ;Update each voice with new separation --- Mov EBX,7*80h Cmp byte [dspMix],3 JE .Float .Voice: Call ChnSep Add EBX,-80h JNS short .Voice RetS .Float: Call ChnSepF Mov EAX,[EBX+mix+mTgtL] Mov EDX,[EBX+mix+mTgtR] Mov [EBX+mix+mChnL],EAX Mov [EBX+mix+mChnR],EDX Add EBX,-80h JNS short .Float %endif ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Set Echo Stereo Separation PROC SetDSPEFBCT, leak USES EDX,EBX Mov EAX,[%$leak] Add EAX,32768 ;Unsign crosstalk Mov [efbct],EAX ;Update echo feedback -------------------- Mov BL,efb Call InitReg ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Set Voice Mute PROC SetDSPVoiceMute, voice, state USES EDX Mov EDX,[%$voice] MovZX EAX,byte [%$state] And EDX,7 ShL EDX,7 Add EDX,mix+mFlg Cmp AL,MUTE_OFF JE short .Off Cmp AL,MUTE_ON JE short .On Cmp AL,MUTE_SOLO JE short .Solo Cmp AL,MUTE_TOGGLE JE short .Toggle Mov AL,MFLG_MUTE And AL,[EDX] RetN .Off: And byte [EDX],~MFLG_MUTE RetN .On: Or byte [EDX],MFLG_MUTE RetN .Solo: Mov AL,MFLG_MUTE Or [000h+mix+mFlg],AL Or [080h+mix+mFlg],AL Or [100h+mix+mFlg],AL Or [180h+mix+mFlg],AL Or [200h+mix+mFlg],AL Or [280h+mix+mFlg],AL Or [300h+mix+mFlg],AL Or [380h+mix+mFlg],AL Not AL And [EDX],AL Mov AL,0 RetN .Toggle: Mov AL,MFLG_MUTE XOr [EDX],AL And AL,[EDX] ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Start Sound Source Decompression ; ;Called when a voice is keyed on to set up the internal data for waveform mixing and decompress the ;first block. ; ;In: ; EBX-> mix[voice] ; ESI-> dsp.voice[voice] ; ;Out: ; nothing ; ;Destroys: ; EAX,EDX PROC StartSrc USES ESI,EDI,EBP MovZX EAX,byte [ESI+srcn] Mov ESI,[pAPURAM] ShL EAX,2 Add AH,[dsp+dir] Mov SI,[EAX+ESI] ;ESI -> First block of waveform LEA EDI,[EBX+sBuf] ;EDI -> Uncompressed sample buffer Mov [EBX+bCur],ESI ;Save physical pointers to wave data Mov [EBX+sIdx],EDI ;Decompress first block ------------------ Mov AL,[ESI] Push EBX Mov [EBX+bHdr],AL ;Save block header MovSX EDX,word [EBX+sP1] MovSX EBX,word [EBX+sP2] Call [pDecomp] Mov EAX,EBX Pop EBX Mov [EBX+sP1],DX Mov [EBX+sP2],AX ;Initialize interpolation ---------------- XOr EAX,EAX Mov [EBX+sBuf-16],EAX Mov [EBX+sBuf-12],EAX Mov [EBX+sBuf-8],EAX Mov [EBX+sBuf-4],EAX Test byte [dspOpts],OPT_FILTER JZ short .NoFilter Mov EDI,EBX Sub EDI,mix ShR EDI,2 Add EDI,vaaBuf Mov [EDI],EAX Mov [4+EDI],EAX Mov [8+EDI],EAX Mov [12+EDI],EAX Mov [16+EDI],EAX Mov [20+EDI],EAX Mov [24+EDI],EAX Mov [28+EDI],EAX Push ECX Mov CL,12 Sub byte [EBX+sIdx],2 Call FilterVoice Pop ECX Jmp short .NoInter .NoFilter: Cmp byte [dspInter],2 ;Is interpolation enabled? JB short .NoInter Add byte [EBX+sIdx],6 ;Update sample index .NoInter: ENDP ;In: ; EBX -> mix[voice] ; CL = Number of samples to increase ; ;Out: ; mix.sIdx = New index ; CL = Number of samples left to increase ; ;Destroys: ; EAX,EDX,ESI PROC FilterVoice USES EDI,EBP Mov DL,[EBX+sIdx] ;AL indexes current sample And DL,3Fh Cmp DL,1Eh ;If we're at the end of the buffer, quit Retc E ShL DL,2 SAR DL,2 Mov AL,30 MovSX ESI,DL Sub AL,DL ShR AL,1 Sub CL,AL JA short .ok Add AL,CL Mov CL,0 .ok: Push ECX Mov CL,AL Mov EDI,EBX Sub EDI,mix ShR EDI,2 LEA EDX,[EDI*2+vaaTaps] ;EDX -> filter taps for voice Add EDI,vaaBuf ;EDI -> buffer containing unfiltered samples .Sample: Add ESI,2 ;Move to next sample Mov AX,[ESI+EBX+sBuf] ;Get unfiltered sample and store it in the filter buffer Mov [ESI+EDI],AX XOr EBP,EBP ;Push 16 samples through the filter. ESI will wrap around. Mov CH,VAATAPS .Next: MovSX EAX,word [ESI+EDI] Sub ESI,2 IMul EAX,dword [EDX] And ESI,1Fh Add EDX,4 Add EBP,EAX Dec CH JNZ .Next ShR EBP,16 Add EBP,EBP Sub EDX,4*VAATAPS Mov [ESI+EBX+sBuf],BP ;Store filtered sample Dec CL JNZ .Sample LEA ESI,[ESI+EBX+sBuf] Mov [EBX+sIdx],ESI Pop ECX ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Start Envelope ; ;Called when a voice is keyed on to set up the internal data to begin envelope modification based on ;the values in ADSR/Gain. ; ;In: ; EBX-> mix[voice] ; ESI-> dsp.voice[voice] ; ;Out: ; mix.e??? = correct values for envelope routine in mixer ; dsp.voice.envx = 0 ; ;Destroys: ; EAX,EDX PROC StartEnv XOr EAX,EAX Mov [EBX+eVal],EAX ;Envelope starts at 0 Mov [ESI+envx],AL ;Reset envelope height Mov byte [EBX+eMode],E_ATT << 4 ;If envelope gets switched out of gain mode, start ADSR Test byte [ESI+adsr1],80h ;Is the envelope in ADSR mode? JZ ChgGain ; No, It's in gain mode ChgAtt: Mov AL,byte [ESI+adsr1] And EAX,0Fh Add EAX,EAX ;Adjust EAX to index rateTab Inc EAX Mov [EBX+eRIdx],AL Mov EDX,[EAX*4+rateTab] ;EDX = Rate of adjustment Mov [EBX+eRate],EDX Mov [EBX+eCnt],EDX Mov byte [EBX+eMode],E_ATT ;Set envelope mode to attack Cmp AL,1Fh ;Is there an attack? JE .NoAtt Mov dword [EBX+eAdj],A_LINEAR ;Set adjustment rate to linear Mov dword [EBX+eDest],D_ATTACK ;Set destination to 63/64ths RetN ;Exit .NoAtt: Mov dword [EBX+eAdj],A_NOATT ;Set adjustment rate to 1/2 Mov dword [EBX+eDest],D_MAX ;Set destination to 1.0 RetN ALIGN 16 ChgDec: Mov dword [EBX+eAdj],A_EXP ;Set adjustment rate to exponential MovZX EAX,byte [ESI+adsr2] ShR EAX,5 Inc EAX IMul EAX,D_DECAY Mov [EBX+eDest],EAX ;Set destination to AL/8 Mov AL,[ESI+adsr1] And EAX,70h ShR EAX,3 Add EAX,10h ;Adjust AL to index rateTab Mov [EBX+eRIdx],AL Mov EDX,[EAX*4+rateTab] Mov [EBX+eRate],EDX ;Set rate of adjustment Mov [EBX+eCnt],EDX Mov byte [EBX+eMode],E_DECAY ;Set envelope mode to decay RetN ;Exit ALIGN 16 ChgSus: Mov AL,[ESI+adsr2] And EAX,1Fh Mov [EBX+eRIdx],AL Mov EDX,[EAX*4+rateTab] SetZ AL ShL AL,7 ;E_IDLE Mov [EBX+eRate],EDX Mov [EBX+eCnt],EDX Mov dword [EBX+eDest],D_MIN ;Set destination to 0 Or AL,E_SUST Mov [EBX+eMode],AL ;Set envelope mode to sustain RetN ;Exit ALIGN 16 ChgGain: .SetGain: Mov AL,[ESI+gain] Test AL,80h ;Is gain direct? JNZ short .GainMode ; No, Program envelope And EAX,7Fh ;Isolate direct value ShL EAX,E_SHIFT ;Adjust value for internal precision Mov [EBX+eVal],EAX Mov AL,[EBX+eMode] And AL,70h Or AL,E_DIRECT|E_IDLE Mov [EBX+eMode],AL ;Set envelope mode to direct RetN ALIGN 16 .GainMode: Mov DL,AL And EAX,1Fh ;Is index zero? Mov [EBX+eRIdx],AL Mov EAX,[EAX*4+rateTab] Mov [EBX+eRate],EAX ;Set rate of change Mov [EBX+eCnt],EAX SetZ AL RoR AL,1 ;E_IDLE Mov AH,[EBX+eMode] ;Preserve ADSR mode And AH,70h Or AL,AH Test DL,60h ;Jump to the right mode JZ .GainDec Test DL,40h JZ short .GainExp Test DL,20h JZ short .GainInc .GainBent: Mov dword [EBX+eAdj],A_LINEAR Mov dword [EBX+eDest],D_BENT Or AL,E_BENT ;Set mode to bent line increase Mov [EBX+eMode],AL RetS ALIGN 16 .GainInc: Mov dword [EBX+eAdj],A_LINEAR Mov dword [EBX+eDest],D_MAX Or AL,E_INC ;Set mode to linear increase Mov [EBX+eMode],AL RetS ALIGN 16 .GainExp: Mov dword [EBX+eAdj],A_EXP Mov dword [EBX+eDest],D_MIN Or AL,E_EXP ;Set mode to exponential decrease Mov [EBX+eMode],AL RetS ALIGN 16 .GainDec: Mov dword [EBX+eAdj],A_LINEAR Mov dword [EBX+eDest],D_MIN Or AL,E_DEC ;Set mode to linear decrease Mov [EBX+eMode],AL EnvDone: ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Change Envelope ; ;Called when the ADSR or GAIN registers are written to while an envelope is in progress. Control is ;transfered to StartEnv where the envelope is updated. ; ;In: ; EBX = Voice << 4 ; ;Destroys: ; EAX,EDX,EBX PROC ChgEnv Push ESI ;ESI will get popped on return from StartEnv LEA ESI,[EBX+dsp] LEA EBX,[EBX*8+mix] Mov DL,[EBX+eMode] And DL,0Fh Push .Return Cmp DL,E_ATT ;If the envelope isn't in attack, decay, or sustain JE ChgAtt ; mode, changes to the ADSR registers have no effect Cmp DL,E_DECAY JE ChgDec Cmp DL,E_SUST JE ChgSus Test DL,E_ADSR JZ ChgGain Pop EAX .Return: Pop ESI ;No changes were made, pop ESI and return ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;DSP Data Port ;-------------------------------------------- ;External procedure for users of SNESAPU.DLL PROC SetDSPReg, reg, val USES ECX,EDX,EBX MovZX EBX,byte [%$reg] MovZX EAX,byte [%$val] Mov CL,0 ;CL = Don't emulate DSP Call SetDSPReg_B ;Process register write without calling debug function Mov EBX,[pAPURAM] ;Update DSP Data function register in APU RAM Mov DL,[0F2h+EBX] And EDX,7Fh Mov DL,[EDX+dsp] Mov [0F3h+EBX],DL ENDP ;-------------------------------------------- ;Internal procedure for initialzing DSP registers ; ;In: ; BL = Register ; ;Destroys: ; EAX,EDX,EBX PROC InitReg USES ECX MovZX EBX,BL Mov AL,[EBX+dsp] Mov CL,0 ;CL = Don't emulate DSP Call SetDSPReg_C ;Process register regardless of current register value ENDP ;-------------------------------------------- ;Procedure for writing to the DSP from the SPC700 ; ;Destroys: ; EAX,CL,EDX,EBX PROC SetDSPReg_A %if DEBUG Mov EDX,[pTrace] Test EDX,EDX JZ short .NoDbg MovZX EAX,AL Add EBX,dsp Push EAX ;Pass these as parameters Push EBX Call EDX Pop EBX Pop EAX MovZX EBX,BL .NoDbg: %endif %if DSPINTEG Mov CL,1 ;CL = Emulate DSP to catch up to current state %endif SetDSPReg_B: Mov DL,BL ;Do nothing if DSP is suspended or if writes are to Or DL,[dbgOpt] ; 80-FFh JS short DSPQuit Cmp BL,kon JE RKOn Cmp BL,kof ;Check for registers that can have duplicate data JE short RKOf ; written Cmp BL,endx JE short REndX Cmp AL,[EBX+dsp] ;Is the new data the same as the current data? JZ short DSPQuit ; Yes, Don't bother updating SetDSPReg_C: Mov EDX,EBX Mov AH,BL And EBX,70h Not AH ShL EBX,3 ;EBX indexes mix (needed by some handlers) And AH,MFLG_OFF ;AH = 08h if the register is in dsp.voice Test [EBX+mix+mFlg],AH ;Is the voice inactive? JNZ short DSPDone ; Yes, Don't bother updating %if DSPINTEG Test CL,CL ;If write was from SPC700, emulate DSP before JZ short .NoOutput ; processing new register data Call UpdateDSP .NoOutput: %endif Mov [EDX+dsp],AL ;Update DSP RAM Jmp [EDX*4+regTab] DSPDone: Mov [EDX+dsp],AL DSPQuit: XOr EAX,EAX ;DSP state didn't change ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;DSP Register Handlers ;============================================ ;End block decoded ALIGN 16 REndX: %if DSPINTEG Test CL,CL ;If write was from SPC700, emulate DSP before JZ short .NoOutput ; processing new register data Call UpdateDSP .NoOutput: %endif XOr EAX,EAX Or AL,[dsp+endx] Mov [dsp+endx],AH ;Reset the ENDX register SetNZ AL Ret ;============================================ ;Key On/Off ALIGN 16 RKOf: %if DSPINTEG Test CL,CL ;If write was from SPC700, emulate DSP before JZ short .NoOutput ; processing new register data Call UpdateDSP .NoOutput: %endif XOr EDX,EDX Mov [dsp+kof],AL And AL,[voiceMix] ;Only check voices that are currently playing JZ .Done Mov EDX,[31*4+rateTab] Mov EBX,mix Jmp short .Start .KeyOff: Mov byte [EBX+eRIdx],31 ;Place envelope in release mode Mov dword [EBX+eRate],EDX Mov dword [EBX+eCnt],EDX Mov dword [EBX+eAdj],A_REL Mov dword [EBX+eDest],D_MIN Mov byte [EBX+eMode],E_REL Or byte [EBX+mFlg],MFLG_KOFF ;Flag voice as keying off .NoKOff: Sub EBX,-80h .Start: ShR AL,1 JC short .KeyOff JNZ short .NoKOff XOr EDX,EDX ;Mark DSP state as having changed Inc EDX .Done: ;Problem: ;The DSP processes the KON register before the KOF register. Because of this, voices that have ;their key off flag set can't be keyed on. However, some games (like Super Turrican 2) write to ;the KON register then immediately write to the KOF register. This works on the SNES because the ;DSP processes the key registers every other sample frame, I think, and the two writes happen ;within that time frame. ; ;Here in the emulation code I process the registers as soon as they're written to, and so games ;that write to the KOF register after the KON register will be missing notes. ; ;Solution: ;If any voices weren't keyed on with the last KON write, check them against the new value of KOF ;and see if they can be keyed on. Mov CL,[dsp+kof] Not CL And CL,[konLate] JNZ .KOn Mov EAX,EDX Ret .KOn: Mov [konLate],AL ;Reset konLate Push EDX Call RKOn2 Pop EDX Or EAX,EDX ;If KON or KOF changed DSP, return true Ret ALIGN 16 RKOn: %if DSPINTEG Test CL,CL JZ short .NoOutput Call UpdateDSP .NoOutput: %endif Mov CL,AL And AL,[dsp+kof] Mov [konLate],AL ;Save the voices that won't be keyed on XOr CL,AL ;Remove those voices from the kon register JZ KDone RKOn2: Push ECX,ESI,EDI Mov CH,1 Mov EBX,mix Mov ESI,dsp Mov EDI,vaaTaps Jmp .Start .KeyOn: And byte [EBX+mFlg],MFLG_MUTE ;Reset flags ;Set voice volume --------------------- Sub EBX,mix Mov AL,[ESI+volL] Call [regTab] ;If stereo controls are enabled, setting the left %if STEREO=0 ; volume will also set the right and vise versa Mov AL,[ESI+volR] Call [4+regTab] %endif Add EBX,mix ;We don't want the channel volume to be ramped in on Mov EAX,[EBX+mTgtL] ; a key on. Set current volume to target level. Mov [EBX+mChnL],EAX Mov EAX,[EBX+mTgtR] Mov [EBX+mChnR],EAX ;Set pitch ---------------------------- MovZX EAX,word [ESI+pitch] And AH,3Fh Mul dword [pitchAdj] ShRD EAX,EDX,16 AdC EAX,0 Mov [EBX+mRate],EAX Mov word [EBX+mDec],0 Test byte [dspOpts],OPT_FILTER JZ short .NoFilter Call AACoeffs,EDI,VAATAPS,EAX,[vaaCut],47000000h ;32768.0 .NoFilter: Call StartSrc ;Start waveform decompression Call StartEnv ;Start envelope Or [voiceMix],CH ;Mark voice as being on internally .NoKey: Add CH,CH Add ESI,10h Sub EBX,-80h Add EDI,VAATAPS*4 .Start: ShR CL,1 JC .KeyOn JNZ short .NoKey Pop EDI,ESI,ECX XOr EAX,EAX Inc EAX Ret KDone: XOr EAX,EAX Ret ;============================================ ;Voice volume %if STEREO=0 ALIGN 16 RVolL: MovSX EAX,AL ;Sign extend volume to 32-bits Mov [EBX+mix+mChnL],EAX Mov [EBX+mix+mTgtL],EAX XOr EAX,EAX Inc EAX Ret ALIGN 16 RVolLF: MovSX EAX,AL Mov [ESP-4],EAX FILd dword [ESP-4] FMul dword [fpShR7] ;Convert volume from fixed to floating-point FStP dword [EBX+mix+mTgtL] XOr EAX,EAX Inc EAX Ret %endif ALIGN 16 RVolLM: ShR EBX,3 MovSX EAX,AL MovSX EDX,byte [EBX+dsp+volR] ShL EBX,3 Call GetMonoVol,EAX,EDX Add EBX,mix Mov [EBX+mChnL],EAX Mov [EBX+mTgtL],EAX Mov [EBX+mChnR],EAX Mov [EBX+mTgtR],EAX XOr EAX,EAX Inc EAX Ret %if STEREO=0 ALIGN 16 RVolR: MovSX EAX,AL Mov [EBX+mix+mChnR],EAX Mov [EBX+mix+mTgtR],EAX XOr EAX,EAX Inc EAX Ret ALIGN 16 RVolRF: MovSX EAX,AL Mov [ESP-4],EAX FILd dword [ESP-4] FMul dword [fpShR7] FStP dword [EBX+mix+mTgtR] XOr EAX,EAX Inc EAX Ret %endif ALIGN 16 RVolRM: ShR EBX,3 MovSX EAX,AL MovSX EDX,byte [EBX+dsp+volL] ShL EBX,3 Call GetMonoVol,EDX,EAX Add EBX,mix Mov [EBX+mChnL],EAX Mov [EBX+mTgtL],EAX Mov [EBX+mChnR],EAX Mov [EBX+mTgtR],EAX XOr EAX,EAX Inc EAX Ret ;============================================ ;Pitch ALIGN 16 RPitch: Mov EAX,[EBX+mix+pDSPV] MovZX EAX,word [EAX+pitch] And AH,3Fh Mul dword [pitchAdj] ;Convert the pitch into a more meaningful value ShRD EAX,EDX,16 ;Remove 16-bit fraction from pitchAdj AdC EAX,0 Mov [EBX+mix+mRate],EAX Test byte [dspOpts],OPT_FILTER JZ short .NoFilter ShR EBX,1 Add EBX,vaaTaps Call AACoeffs,EBX,VAATAPS,EAX,[vaaCut],47000000h ;32768.0 .NoFilter: XOr EAX,EAX Inc EAX Ret ;============================================ ;Envelope ALIGN 16 RADSR: XOr EAX,EAX Test byte [EBX+mix+mFlg],MFLG_KOFF ;Is voice in key off mode? JNZ short .NoChg ; Yes, Envelope setting can't be changed now Mov AL,[EBX+mix+eMode] ;AL = ADSR or Gain mode ShR EBX,3 And AL,E_ADSR Mov AH,[EBX+dsp+adsr1] And AH,80h Or AL,AH Test AL,80h + E_ADSR JZ short .NoChg ;Envelope is already in gain mode, do nothing Test AL,80h JZ short .SetGain ;Switched from ADSR to Gain Test AL,E_ADSR JNZ .Change ;Envelope is in ADSR mode, update settings Mov AL,[EBX*8+mix+eMode] ;Switched from Gain to ADSR, restore previous ADSR ShR AL,4 ; state then update settings Or AL,E_ADSR Mov [EBX*8+mix+eMode],AL .Change: Call ChgEnv XOr EAX,EAX Inc EAX .NoChg: Ret .SetGain: ShL EBX,3 ShL byte [EBX+mix+eMode],4 ;Save ADSR state, ChgGain will set bits 7 and 3-0 ; Mov AL,[EBX+mix+eMode] ;If envelope switches to gain while in attack mode and ; Cmp AL,E_ATT << 4 ; and attack time is 0ms, then force envelope to max. ; JNE short RGain ; Cmp dword [ebx+mix+eAdj],A_NOATT ; JNE short RGain ; Mov dword [EBX+mix+eVal],D_MAX Jmp short RGain ALIGN 16 RGain: XOr EAX,EAX Test byte [EBX+mix+mFlg],MFLG_KOFF ;Is voice in key off mode? JNZ short .NoChg ; Yes, Envelope setting can't be changed now ShR EBX,3 Test byte [EBX+dsp+adsr1],80h ;Is envelope in gain mode? JNZ short .NoChg ; No, Setting gain register has no effect Call ChgEnv XOr EAX,EAX Inc EAX .NoChg: Ret ;============================================ ;Main volumes ALIGN 16 RMVolL: MovSX EAX,AL IMul EAX,[volAdj] SAR EAX,16 ;Remove fraction from multiplication Mov [volMainL],EAX XOr EAX,EAX Inc EAX Ret ALIGN 16 RMVolLM: MovSX EAX,AL MovSX EDX,byte [dsp+mvolR] Call GetMonoVol,EAX,EDX IMul dword [volAdj] SAR EAX,16 Mov [volMainL],EAX Mov [volMainR],EAX XOr EAX,EAX Inc EAX Ret ALIGN 16 RMVolLF: MovSX EAX,AL Mov [ESP-4],EAX FILd dword [ESP-4] FIMul dword [volAdj] FMul dword [fpShR7] ;>> 7 to turn MVOL into a float FStP dword [volMainL] ;Leave the 16-bits added by volAdj so the final XOr EAX,EAX ; output will be 32-bit instead of 16-bit Inc EAX Ret ALIGN 16 RMVolR: XOr AL,[surround] ;Invert right output, if surround is enabled Sub AL,[surround] MovSX EAX,AL IMul EAX,[volAdj] SAR EAX,16 Mov [volMainR],EAX XOr EAX,EAX Inc EAX Ret ALIGN 16 RMVolRM: MovSX EAX,AL MovSX EDX,byte [dsp+mvolL] Call GetMonoVol,EDX,EAX IMul dword [volAdj] SAR EAX,16 Mov [volMainL],EAX Mov [volMainR],EAX XOr EAX,EAX Inc EAX Ret ALIGN 16 RMVolRF: XOr AL,[surround] Sub AL,[surround] MovSX EAX,AL Mov [ESP-4],EAX FILd dword [ESP-4] FIMul dword [volAdj] FMul dword [fpShR7] FStP dword [volMainR] XOr EAX,EAX Inc EAX Ret ALIGN 16 REVolL: MovSX EAX,AL IMul EAX,[volAdj] SAR EAX,16 ;Remove fraction Mov [volEchoL],EAX XOr EAX,EAX Inc EAX Ret ALIGN 16 REVolLM: MovSX EDX,byte [dsp+evolR] MovSX EAX,AL Call GetMonoVol,EAX,EDX IMul dword [volAdj] SAR EAX,16 Mov [volEchoL],EAX Mov [volEchoR],EAX XOr EAX,EAX Inc EAX Ret ALIGN 16 REVolLF: MovSX EAX,AL Mov [ESP-4],EAX FILd dword [ESP-4] FIMul dword [volAdj] FMul dword [fpShR7] FStP dword [volEchoL] XOr EAX,EAX Inc EAX Ret ALIGN 16 REVolR: XOr AL,[surround] Sub AL,[surround] MovSX EAX,AL IMul EAX,[volAdj] SAR EAX,16 Mov [volEchoR],EAX XOr EAX,EAX Inc EAX Ret ALIGN 16 REVolRM: MovSX EAX,AL MovSX EDX,byte [dsp+evolL] Call GetMonoVol,EDX,EAX IMul dword [volAdj] SAR EAX,16 Mov [volEchoL],EAX Mov [volEchoR],EAX XOr EAX,EAX Inc EAX Ret ALIGN 16 REVolRF: XOr AL,[surround] Sub AL,[surround] MovSX EAX,AL Mov [ESP-4],EAX FILd dword [ESP-4] FIMul dword [volAdj] FMul dword [fpShR7] FStP dword [volEchoR] XOr EAX,EAX Inc EAX Ret ;============================================ ;Echo settings ALIGN 16 REFB: %if STEREO MovSX EAX,AL Mov EDX,EAX IMul EAX,[efbct] SAR EAX,16 Mov [echoFB],EAX Mov [4+echoFB],EAX Mov EAX,10000h ;Calculate crosstalk Sub EAX,[efbct] IMul EAX,EDX SAR EAX,16 Mov [echoFBCT],EAX Mov [4+echoFBCT],EAX %else MovSX EAX,AL Mov [echoFB],EAX Mov [4+echoFB],EAX %endif XOr EAX,EAX Inc EAX Ret ALIGN 16 REFBM: MovSX EAX,AL Mov [echoFB],EAX Mov [4+echoFB],EAX %if STEREO XOr EAX,EAX Mov [echoFBCT],EAX Mov [4+echoFBCT],EAX %endif XOr EAX,EAX Inc EAX Ret ALIGN 16 REFBF: MovSX EAX,AL Mov [ESP-4],EAX FILd dword [ESP-4] %if STEREO FLd ST FIMul dword [efbct] FMul dword [fpShR23] ;Convert from fixed to floating-point FSt dword [echoFB] ;7-bits (efb) + 16-bits (efbct) = 23-bits FStP dword [4+echoFB] FLd dword [fp64k] FISub dword [efbct] FMulP ST1,ST FMul dword [fpShR23] FSt dword [echoFBCT] FStP dword [4+echoFBCT] %else FMul dword [fpShR7] FSt dword [echoFB] FStP dword [4+echoFB] %endif XOr EAX,EAX Inc EAX Ret ALIGN 16 REDl: And EAX,0Fh ShL EAX,9 ;EAX = Number of samples to delay SetZ CL ;if (EDL == 0) EAX = 1 Or AL,CL Mul dword [dspRate] ;EAX *= Rate / 32kHz Mov EBX,32000 Div EBX Test EAX,EAX ;if EAX = 0, EAX = 1 SetZ CL Or AL,CL ShL EAX,3 ;Multiply by 8, since echo is stored in 32-bit stereo Mov [echoDel],EAX XOr EAX,EAX Inc EAX Ret ALIGN 16 RFCI: ShR EBX,4 MovSX EAX,AL ;Sign extend filter coefficient Mov [EBX+firTaps],EAX ;Store for left and right (for parallel instructions) Mov [4+EBX+firTaps],EAX Test EBX,EBX JZ short CheckFC1 Jmp short CheckFC ;Check if filter coefficients do anything ALIGN 16 RFCF: ShR EBX,4 MovSX EAX,AL Mov [ESP-4],EAX FILd dword [ESP-4] FMul dword [fpShR7] FSt dword [EBX+firTaps] FStP dword [4+EBX+firTaps] Test EBX,EBX JNZ short CheckFC CheckFC1: Cmp AL,7Fh ;If first tap is 127, pretend it's 0 SetE AH Dec AH And AL,AH CheckFC: Test AL,AL SetNZ AL ShR EBX,3 Mov AH,1 Mov CL,BL ShL EAX,CL Not AH And [firEnabl],AH ;Set corresponding bit if tap is non-zero Or [firEnabl],AL ;Disable echo if all taps are 0 ---------- Mov AL,[firEnabl] ;Mask for all taps Or AL,[dsp+fc] ;Value of first tap (here, 127 doesn't count as 0) SetZ AL ;If all taps are 0, including first one, set AL And byte [disEcho],~1 Or [disEcho],AL ;Set first bit to disable echo XOr EAX,EAX ;DSP state changed if echo was enabled Inc EAX Ret ;============================================ ;Other ALIGN 16 RPMOn: And AL,0FEh ;Disable LSB Mov [dsp+pmon],AL ;Reset all pitch on all voices ----------- Mov EBX,mix Mov CL,8 .Next: Mov EAX,[EBX+pDSPV] MovZX EAX,word [EAX+pitch] And AH,3Fh Mul dword [pitchAdj] ShRD EAX,EDX,16 AdC EAX,0 Mov [EBX+mRate],EAX Sub EBX,-80h Dec CL JNZ short .Next XOr EAX,EAX Inc EAX Ret ALIGN 16 RFlg: Test AL,80h ;Has a soft reset been initialized? JZ short .NoSRst ; No Mov EBX,dsp And AL,~80h Or AL,60h ;Turn on mute and disable echo Mov [EBX+flg],AL Mov byte [EBX+endx],BL ;Clear end block flags Mov byte [EBX+kon],BL Mov byte [EBX+kof],BL Mov byte [voiceMix],BL ;Reset internal voice settings -------- Mov EBX,mix+mFlg Mov CL,8 .MFlg: And byte [EBX],MFLG_MUTE ;Preserve mute flag Or byte [EBX],MFLG_OFF ;Set voice to inactive Sub EBX,-80h Dec CL JNZ .MFlg .NoSRst: ;Disable echo ---------------------------- Mov DL,AL And DL,20h ;Isolate "disable echo" flag And byte [disEcho],~20h Or [disEcho],DL ;Store it ;Update noise clock ---------------------- Mov dword [nRate],0 And EAX,1Fh JZ short .NoNoise Mov EBX,EAX Or EAX,-1 Mov EDX,65535 Div dword [EBX*4+rateTab] Mov [nRate],EAX .NoNoise: XOr EAX,EAX Inc EAX Ret ;============================================ ;Null register ALIGN 16 RNull: XOr EAX,EAX Ret ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Interpolation Functions ; ;All interpolation functions, except Gaussian, return the original sample if a 0 delta is used. ; ;In: ; ESI-> Samples to interpolate between ; EAX = Delta between samples (0 - 65535) ; ;Out: ; EAX or ST = Interpolated sample ; ;Destroys: ; LinearI and LinearX destroys EDX ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;No Interpolation PROC NoneI MovSX EAX,word [ESI] ;EAX = Current sample ENDP PROC NoneX MovSX EAX,word [ESI] ENDP PROC NoneF FILd word [ESI] ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Linear Interpolation PROC LinearI Push ESI MovSX EDX,word [ESI] MovSX ESI,word [ESI-2] Sub EDX,ESI SAR EDX,1 IMul EDX,EAX SAR EDX,15 LEA EAX,[EDX+ESI] Pop ESI ENDP PROC LinearX Push ESI MovSX EDX,word [ESI] MovSX ESI,word [ESI-2] Sub EDX,ESI SAR EDX,1 IMul EDX,EAX SAR EDX,15 LEA EAX,[EDX+ESI] And EAX,-2 Pop ESI ENDP PROC LinearF FILd word [ESI-2] FILd word [ESI] Mov [ESP-4],EAX FSub ST,ST1 ;Difference between samples FIMul dword [ESP-4] ;Multiply by delta x from last sample FMul dword [fpShR16] FAddP ST1,ST ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Cubic Interpolation PROC CubicI Push ECX,EBX ShR EAX,8 LEA EBX,[EAX*8+cubicTab] ;EBX -> Cubic table value Mov AX,[ESI-6] ;Get first sample IMul word [EBX+0] ;Multiply by cubic Mov AX,[ESI-4] Mov CX,DX ;DX = Partial sample >> 1 IMul word [EBX+2] Mov AX,[ESI-2] Add CX,DX IMul word [EBX+4] Mov AX,[ESI-0] Add CX,DX IMul word [EBX+6] Add CX,DX Pop EBX MovSX EAX,CX ;EAX = New sample Pop ECX Add EAX,EAX ;15-bit to 16-bit ENDP PROC CubicX MovQ MM7,[ESI-6] ;Load all four samples (isn't MMX nice?) ShR EAX,8 ;EAX indexes interpolation table value PMAddWD MM7,[EAX*8+cubicTab] ;Multiply samples by table PSRAD MM7,15 MovQ MM6,MM7 PSLLQ MM7,32 PAddD MM7,MM6 ;Add two dwords together and store in upper dword of MM7 PackSSDW MM7,MM0 ;Pack dwords into words (EmuDSPX requires 16-bit samples) MovD EAX,MM7 SAR EAX,16 ;Sign extend 16-bit result to 32-bits And EAX,-2 ENDP PROC CubicF Mov [ESP-4],EAX FILd dword [ESP-4] ;delta x = mDec>>16 FMul dword [fpShR16] ; |x FLd ST ; |x x FMul ST,ST1 ; |x x^2 FLd ST ; |x x2 x2 FMul ST,ST2 ; |x x2 x^3 FLd ST ; |x x2 x3 x3 FMul dword [fp0_5] ; |x x2 x3 x3*0.5 FAdd ST1,ST ; |x x2 1.5*x3 .5x3 FXch ST3 ; |.5x3 x2 1.5x3 x FMul dword [fp0_5] ; |.5x3 x2 1.5x3 x*0.5 ;s[-1] = -.5(x^3) + (x^2) - .5x + 0 FLd ST2 ; |.5x3 x2 1.5x3 .5x x2 FSub ST,ST4 ; |.5x3 x2 1.5x3 .5x x2-.5x3 FSub ST,ST1 ; |.5x3 x2 1.5x3 .5x D0-.5x ;s[1] = -1.5(x^3) + 2(x^2) + .5x + 0 FXch ST1 ; |.5x3 x2 1.5x3 D0 .5x FAdd ST,ST3 ; |.5x3 x2 1.5x3 D0 .5x+x2 FAdd ST,ST3 ; |.5x3 x2 1.5x3 D0 D2+x2 FSub ST,ST2 ; |.5x3 x2 1.5x3 D0 D2-1.5x3 ;s[0] = 1.5(x^3) - 2.5(x^2) + 0x + 1 FXch ST2 ; |.5x3 x2 D2 D0 1.5x3 FLd1 FAddP ST1,ST FLd ST3 FMul dword [fn2_5] FAddP ST1,ST ; |.5x3 x2 D2 D0 D1 ;s[2] = .5(x^3) - .5(x^2) + 0x + 0 FXch ST3 ; |.5x3 D1 D2 D0 x2 FMul dword [fp0_5] ; |.5x3 D1 D2 D0 x2*0.5 FSubP ST4,ST ; |.5x3-.5x2 D1 D2 D0 FIMul word [ESI-6] ; |D3 D1 D2 D0*S FXch ST3 ; |SO D1 D2 D3 FIMul word [ESI-0] ; |SO D1 D2 D3*S FAddP ST3,ST ; |S0+S2 D1 D2 FIMul word [ESI-2] ; |S D1 D2*S FAddP ST2,ST ; |S+S2 D1 FIMul word [ESI-4] ; |S D1*S FAddP ST1,ST ; |S+S1 ; ;Use lookup table ------------------------ ; ShR EAX,8 ; LEA EDI,[EAX*8+cubicTab] ; FILd word [ESI-6] ; FIMul word [EDX+0] ; FILd word [ESI-4] ; FIMul word [EDX+2] ; FILd word [ESI-2] ; FIMul word [EDX+4] ; FILd word [ESI] ; FIMul word [EDX+6] ; FAddP ST1,ST ; FAddP ST1,ST ; FAddP ST1,ST ; FMul dword [fpShR15] ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;4-point Gaussian Interpolation PROC GaussI Push ECX,EBX ShR EAX,8 LEA EBX,[EAX*8+gaussTab] ;EBX -> Gaussian table value Mov AX,[ESI-6] ;Get first sample IMul word [EBX+0] ;Multiply by cubic Mov AX,[ESI-4] Mov CX,DX ;DX = Partial sample >> 1 IMul word [EBX+2] Mov AX,[ESI-2] Add CX,DX IMul word [EBX+4] Mov AX,[ESI-0] Add CX,DX IMul word [EBX+6] Add CX,DX Pop EBX MovSX EAX,CX ;EAX = New sample Pop ECX Add EAX,EAX ;15-bit to 16-bit ENDP PROC GaussX MovQ MM6,[ESI-6] ShR EAX,8 ;EAX indexes interpolation table value MovQ MM7,MM6 PAnd MM6,[loSmp] ;MM6 = 00 s2 00 s0 PAnd MM7,[hiSmp] ;MM7 = s3 00 s1 00 PMAddWD MM6,[EAX*8+gaussTab] ;Multiply samples by table PMAddWD MM7,[EAX*8+gaussTab] PSRAD MM6,15 PSRAD MM7,15 PAddD MM7,MM6 MovQ MM6,MM7 PSRLQ MM6,32 PAddD MM7,MM6 PackSSDW MM7,MM0 MovD EAX,MM7 MovSX EAX,AX And EAX,-2 ENDP PROC GaussF ShR EAX,8 ;EAX indexes interpolation table value LEA EAX,[EAX*8+gaussTab] FILd word [ESI-6] ;Get first sample FIMul word [EAX+0] FILd word [ESI-4] FIMul word [EAX+2] FILd word [ESI-2] FIMul word [EAX+4] FILd word [ESI] FIMul word [EAX+6] FAddP ST2,ST FAddP ST2,ST FAddP ST1,ST FMul dword [fpShR15] ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;8-point Sinc Interpolation PROC SincI Push ECX,EBX ShR EAX,4 And EAX,-16 LEA EBX,[EAX+sincTab] Mov AX,[ESI-14] IMul word [EBX+0] Mov AX,[ESI-12] Mov CX,DX IMul word [EBX+2] Mov AX,[ESI-10] Add CX,DX IMul word [EBX+4] Mov AX,[ESI-8] Add CX,DX IMul word [EBX+6] Mov AX,[ESI-6] Add CX,DX IMul word [EBX+8] Mov AX,[ESI-4] Add CX,DX IMul word [EBX+10] Mov AX,[ESI-2] Add CX,DX IMul word [EBX+12] Mov AX,[ESI-0] Add CX,DX IMul word [EBX+14] Add CX,DX Pop EBX MovSX EAX,CX ;EAX = New sample Pop ECX Add EAX,EAX ;15-bit to 16-bit ENDP PROC SincX MovQ MM6,[ESI-14] MovQ MM7,[ESI-6] ShR EAX,4 And EAX,-16 PMAddWD MM6,[EAX+sincTab] PMAddWD MM7,[8+EAX+sincTab] PSRAD MM6,15 PSRAD MM7,15 PAddD MM6,MM7 MovQ MM7,MM6 PSLLQ MM6,32 PAddD MM7,MM6 PackSSDW MM7,MM0 MovD EAX,MM7 SAR EAX,16 And EAX,-2 ENDP PROC SincF ShR EAX,4 ;EAX indexes interpolation table value And EAX,-16 Add EAX,sincTab FILd word [ESI-14] FIMul word [EAX+0] FILd word [ESI-12] FIMul word [EAX+2] FILd word [ESI-10] FIMul word [EAX+4] FILd word [ESI-8] FIMul word [EAX+6] FILd word [ESI-6] FIMul word [EAX+8] FILd word [ESI-4] FIMul word [EAX+10] FILd word [ESI-2] FIMul word [EAX+12] FILd word [ESI-0] FIMul word [EAX+14] FAddP ST4,ST FAddP ST4,ST FAddP ST4,ST FAddP ST4,ST FAddP ST2,ST FAddP ST2,ST FAddP ST1,ST FMul dword [fpShR15] ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Noise Generator ; ;Generates white noise samples ; ;Out: ; nSmp = Random 16-bit sample ; ;Destroys: ; EAX,EDX %macro NoiseGen 0 Mov EAX,[nRate] Add [nAcc],EAX JNC short %%NoNInc IMul EAX,[nSmp],27865 ;X=(AX+C)%M Where: X> 15 ; ;Pitch modulation in the SNES uses the full 16-bit sample value, not the 8-bit value in OUTX as ;previously believed. ; ;In: ; CH = Bitmask for current voice ; EBX-> Current voice in 'mix' ; ;Destroys: ; EAX,EDX %macro PitchMod 0 Test [dsp+pmon],CH ;Is pitch modulation enabled? JZ short %%NoPMod ; No, Pitch doesn't need to be adjusted ;Adjust pitch by sample value --------- Mov EDX,[EBX+pDSPV] Mov EAX,[EBX+mOut-80h] ;EAX = Wave height of last voice (-16.15) MovZX EDX,word [EDX+pitch] Add EAX,32768 ;Unsign sample And DH,3Fh IMul EAX,EDX ;Apply sample height to pitch SAR EAX,15 ;Clamp pitch to 14-bits --------------- Mov EDX,EAX SAR EDX,14 JZ short %%PitchOK SetS AL And EAX,1 Dec EAX And EAX,3FFFh %%PitchOK: ;Convert pitch to sample rate --------- Mul dword [pitchAdj] ShRD EAX,EDX,16 Mov [EBX+mRate],EAX %%NoPMod: %endmacro ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Process Sound Source ; ;Updates the current sample position and decompresses the next block if necessary ; ;In: ; CH = Bitmask for current voice ; EBX-> Current voice in 'mix' ; ;Destroys: ; EAX,EDX,CL,ESI PROC ProcessSrc And byte [EBX+sIdx],~20h ;Adjust sample index for wrap around Mov EAX,[EBX+sBuf+16] ;Copy last four samples of buffer Mov EDX,[EBX+sBuf+20] ; (needed for interpolation) Mov [EBX+sBuf-16],EAX Mov [EBX+sBuf-12],EDX Mov EAX,[EBX+sBuf+24] Mov EDX,[EBX+sBuf+28] Mov [EBX+sBuf-8],EAX Mov [EBX+sBuf-4],EDX Add word [EBX+bCur],9 ;Move to next sample block Test byte [EBX+bHdr],BRR_END ;Was this the end block? JZ short .NotEndB ; No, Decompress next block Or [dsp+endx],CH ;Set flag in ENDX Test byte [EBX+bHdr],BRR_LOOP ;Is this source looped? JNZ short .LoopB ; Yes, Start over at loop point ;End voice playback ------------------- .EndPlay: Not CH And [voiceMix],CH ;Don't include voice in mixing process Not CH Mov dword [EBX+eVal],0 ;Reset envelope and wave height Mov dword [EBX+mOut],0 Or byte [EBX+mFlg],MFLG_OFF ;Mark voice as inactive And byte [EBX+mFlg],~MFLG_KOFF RetS ;Restart loop ------------------------- .LoopB: Mov EAX,[EBX+pDSPV] MovZX EDX,byte [EAX+srcn] ;EDX = Source number Mov EAX,[pAPURAM] ShL EDX,2 Add DH,[dsp+dir] ;EDX indexes source directory Mov AX,[2+EDX+EAX] Mov [EBX+bCur],EAX ;Store loop point in current block pointer ;Decompress next block ---------------- .NotEndB: Mov ESI,[EBX+bCur] ;ESI -> Current sample block Push EDI,EBX Mov AL,[ESI] ;Get block header LEA EDI,[EBX+sBuf] ;EDI -> location to store samples Mov [EBX+bHdr],AL ;Save header byte MovSX EDX,word [EBX+sP1] ;Load previous two samples MovSX EBX,word [EBX+sP2] Call [pDecomp] ;Call user selected decompression routine Mov EAX,EBX Pop EBX,EDI Mov [EBX+sP1],DX ;Save last two samples in 32-bit form Mov [EBX+sP2],AX Test byte [dspOpts],OPT_FILTER JZ short .NoFilter2 Sub byte [EBX+sIdx],CL ;Undo addition of CL from the top of the macro ShR CL,1 ;Playback will end when the filter still has eight Call FilterVoice ; samples in queue. So it's unnecessary to zero out RetS ; the last eight samples. .NoFilter2: Mov AL,[EBX+bHdr] And AL,3 Cmp AL,1 Retc NE XOr EAX,EAX ;The SNES ends playback of one-shot sounds about Mov [16+EBX+sBuf],EAX ; half-way through the last block. To simulate this, Mov [20+EBX+sBuf],EAX ; zero out last eight samples. Fixes EWJ popping. Mov [24+EBX+sBuf],EAX Mov [28+EBX+sBuf],EAX ENDP %macro UpdateSrc 0 ;Update sample index --------------------- Mov CL,0 ;CL = Number of whole samples to increase index by Mov EAX,[EBX+mRate] ;AX = Fraction of sample to increase index by Add [EBX+mDec],AX ;Add AX to the decimal counter AdC CL,[EBX+mRate+2] ;Add carry, if any, to increase amount JZ short %%NoSInc ;If the amount is zero, index didn't increase ;Check for end of block ------------------ Test byte [dspOpts],OPT_FILTER JZ short %%NoFilter Call FilterVoice %%NoFilter: Add CL,CL ;CL <<= 1 (for 16-bit samples) Add [EBX+sIdx],CL ;Increase sample index Test byte [EBX+sIdx],20h ;Have we reached the end of the block? JZ short %%NoSInc ; Nope Call ProcessSrc %%NoSInc: %endmacro ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Calculate Envelope Modification ; ;Changes the current height of the volume envelope based on its programming ; ;In: ; EBX-> Current voice in mix ; CH = Current voice bit mask ; ;Destroys: ; EAX,CL,EDX,ESI PROC ProcessEnv Mov EAX,[EBX+eRate] ;Restore sample counter Add [EBX+eCnt],EAX ;Adjust Envelope ------------------------- Test CL,E_ADJ ;Is the adjustment an exponential decrease? JZ short .AdjLin ; No, Go to linear Mov EAX,[EBX+eVal] Mov EDX,[EBX+eDest] ;Get destination (SL or 0) Neg EAX SAR EAX,8 Add [EBX+eVal],EAX ;Subtract 1/256th of envelope height Cmp EDX,[EBX+eVal] ;Has height reached destination? JB .Done ; No, Quit Test EDX,EDX ;If destination isn't 0, change to sustain mode JNZ short .AdjDone Or byte [EBX+eMode],E_IDLE ;Otherwise mark envelope as no longer changing RetN .AdjLin: Test CL,E_DIR ;Is the adjustment up or down? JZ short .AdjDec Mov EAX,[EBX+eVal] Add EAX,[EBX+eAdj] ;Add adjustment to height Mov [EBX+eVal],EAX Cmp EAX,[EBX+eDest] ;Has height reached destination? JB .Done ; No, Quit Cmp EAX,D_MAX ;Clamp height to D_MAX JBE short .AdjDone Mov dword [EBX+eVal],D_MAX Jmp short .AdjDone .AdjDec: ;Linear decrease Mov EAX,[EBX+eAdj] Sub [EBX+eVal],EAX ;Decrease envelope height JA .Done ; Jump if envelope is > 0 (destination is always 0) Mov dword [EBX+eVal],D_MIN Mov AL,[EBX+eMode] ;If the envelope started out in ADSR mode, but was And AL,~70h ; switched to Gain w/ linear decrease, the ADSR state Or AL,E_SUST << 4 ; will become sustain if ADSR is re-enabled. Mov [EBX+eMode],AL Mov AL,[EBX+mFlg] ;If the voice was getting keyed off, set MFLG_OFF to And AL,MFLG_KOFF ; mark the voice as now being inactive Add AL,AL SetZ AH Or [EBX+mFlg],AL And byte [EBX+mFlg],~MFLG_KOFF Dec AH And AH,CH Not AH And [voiceMix],AH ;Disable voice mixing if keyed off Or byte [EBX+eMode],E_IDLE ;Envelope is no longer changing RetN .AdjDone: ;Change adjustment mode ------------------ ;(see StartEnv) Test CL,E_ADSR ;Is envelope in ADSR mode? JZ short .EnvGain ; Nope, Jump to Gain Mov ESI,EBX And CL,0Fh Sub ESI,mix XOr EAX,EAX ShR ESI,3 ;ESI indexes current voice in dsp Add ESI,dsp Cmp CL,E_DECAY ;Switch to next mode JE short .EnvSust .EnvDecay: Mov dword [EBX+eAdj],A_EXP ;see StartEnv Mov AL,[ESI+adsr2] ShR AL,5 Inc AL IMul EAX,D_DECAY Mov [EBX+eDest],EAX MovZX EAX,byte [ESI+adsr1] And AL,70h ShR AL,3 Add AL,10h Mov [EBX+eRIdx],AL Mov ESI,[EAX*4+rateTab] Mov [EBX+eRate],ESI Mov [EBX+eCnt],ESI Mov byte [EBX+eMode],E_DECAY RetS .EnvSust: Mov AL,[ESI+adsr2] ;see StartEnv And EAX,1Fh Mov [EBX+eRIdx],AL Mov ESI,[EAX*4+rateTab] SetZ AL Mov [EBX+eRate],ESI Mov [EBX+eCnt],ESI RoR AL,1 Mov dword [EBX+eDest],D_MIN Or AL,E_SUST Mov [EBX+eMode],AL RetS .EnvGain: Or byte [EBX+eMode],E_IDLE ;Envelope is now constant Test CL,E_DEST ;If gain is in "bent line" mode and line has reached Retc Z ; bend point, adjust envelope settings, otherwise ; envelope is done. Cmp dword [EBX+eDest],D_MAX Retc E And byte [EBX+eMode],~E_IDLE ;Undo idle flag Mov dword [EBX+eAdj],A_BENT ;Slow down increase rate Mov dword [EBX+eDest],D_MAX ;Set destination to max .Done: ENDP %macro UpdateEnv 0 Mov CL,[EBX+eMode] Test CL,E_IDLE ;Is the envelope constant? JNZ short %%Quit ; Yes, Quit Dec word [2+EBX+eCnt] ;Decrease sample counter, is it zero? JNZ short %%Quit ; No, Quit Call ProcessEnv %%Quit: %endmacro ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Finite Impulse Response Echo Filter ; ;Filters the echo using an eight tap FIR filter: ; ; 7 ; --- ; x = \ c * s ; / n n ; --- ; n=0 ; ; x = output sample ; c = filter coefficient (-.7) ; s = unfiltered sample ; n = 0 is the oldest sample and 7 is the most recent (opposite most FIR filter implementations) ; ;FIR filters are based on the sample rate. This was fine in the SNES, because the sample rate was ;always 32kHz, but in the case of an emulator the sample rate can change. So measures have to be ;taken to ensure the filter will have the same effect, regardless of the output sample rate. ; ;To overcome this problem, I figured each tap of the filter is applied every 31250ns. So the ;solution is to calculate when 31250ns have gone by, and use the sample at that point. Of course ;this method really only works if the output rate is a multiple of 32k. In order to get accurate ;results, some sort of interpolation method needs to be introduced. I went the cheap route and used ;linear interpolation. ;In: ; EAX,EDX = Input samples ; ;Out: ; EAX,EDX = Filtered samples ; EDI -> Echo buffer ; ;Destroys: ; ECX,EBX %macro FIRFilterI 0 Sub byte [firCur],4 ;Move index back one sample. (Index will wrap around Mov EDI,[firCur] ; after 64 samples, enough for up to 256kHz output.) LEA EDI,[EDI*2+firBuf] ;EDI -> Current sample in filter buffer Mov [EDI],EAX ;Store new samples in buffer Mov [4+EDI],EDX Mov [FIRBUF*2+EDI],EAX Mov [FIRBUF*2+4+EDI],EDX Push ESI,EBP XOr EBX,EBX XOr ECX,ECX Mov ESI,firTaps+56 ;ESI -> Filter taps Mov [firDec],EBX ;Reset decimal overflow, so filtering is consistant Mov EBP,8 ;8-tap FIR filter %%Tap: Mov EAX,[8+EDI] ;Interpolate left sample Sub EAX,[EDI] IMul dword [firDec] ShRD EAX,EDX,16 Add EAX,[EDI] IMul EAX,[ESI] ;Multiply by filter tap SAR EAX,7 Add EBX,EAX ;Accumulate result Mov EAX,[12+EDI] ;Right sample Sub EAX,[4+EDI] IMul dword [firDec] ShRD EAX,EDX,16 Add EAX,[4+EDI] IMul EAX,[ESI] SAR EAX,7 Add ECX,EAX Mov EDX,[firDec] ;Determine next sample to use in filter Add EDX,[firRate] Mov [firDec],DX ShR EDX,16 LEA EDI,[EDX*8+EDI] ;EDI -> Sample to use in filter Sub ESI,8 ;ESI -> Next filter tap Dec EBP JNZ short %%Tap Pop EBP,ESI MovSX EAX,BX MovSX EDX,CX And EAX,~1 And EDX,~1 Mov EDI,[echoCur] ;Restore EDI Add EDI,echoBuf %endmacro ;In: ; MM2 = Input samples ; ;Out: ; MM2 = Filtered samples ; EDI-> Echo buffer ; ;Destroys: ; EAX,EDX,CL,MM5-7 %macro FIRFilterX 0 Sub byte [firCur],4 Mov EDI,[firCur] LEA EDI,[EDI*2+firBuf] MovQ [EDI],MM2 MovQ [FIRBUF*2+EDI],MM2 Push ESI PXOr MM2,MM2 XOr EAX,EAX XOr EDX,EDX Mov ESI,firTaps+56 Mov CL,8 %%Tap: MovD MM7,EDX ;MM7 = Delta between samples MovQ MM5,[EDI] ;MM5 = Current echo samples MovQ MM6,[8+EDI] ;MM6 = Next echo samples PUnpckLDQ MM7,MM7 PSRAW MM5,1 ;Clear LSB in samples PSRAW MM6,1 PSRLD MM7,1 PSubW MM6,MM5 ;Get difference between samples PMAddWD MM6,MM7 ;Multiply by delta PAddD MM6,MM6 ;Restore bit lost in multiplication PSRLD MM6,16 ;Shift result down into lower word and clear upper PAddW MM6,MM5 ;Add current samples to interpolated value PAddW MM6,MM6 ;Restore LSB PMAddWD MM6,[ESI] ;Apply filter tap PSRAD MM6,7 PAddD MM2,MM6 Mov EAX,EDX Add EAX,[firRate] Mov DX,AX ShR EAX,16 Sub ESI,8 LEA EDI,[EAX*8+EDI] Dec CL JNZ short %%Tap Pop ESI ;Enabling this seems to break some songs ; PSRAD MM2,1 ;Clamp to 17 bits ; PackSSDW MM2,MM0 ; PUnpckLWD MM2,MM0 ; PAddW MM2,MM2 ;Clear LSB PackSSDW MM2,MM0 ;Clamp to 16 bits PUnpckLWD MM2,MM0 ;(The SNES clamps to 17 bits, but this breaks DQK. PSRAW MM2,1 ; More investigation is needed.) PAddW MM2,MM2 ;Clear LSB Mov EDI,[echoCur] Add EDI,echoBuf %endmacro ;In: ; ST0,1 = Input samples ; ;Out: ; ST0,1 = Filtered samples ; ;Destroys: ; EAX,EDX,EBX,CL %macro FIRFilterF 0 Sub byte [firCur],4 Mov EBX,[firCur] LEA EBX,[EBX*2+firBuf] FSt dword [EBX] FStP dword [FIRBUF*2+EBX] FSt dword [4+EBX] FStP dword [FIRBUF*2+4+EBX] Mov dword [firDec],0 FLdZ FLdZ Mov ECX,firTaps+56 %%Tap: FILd dword [firDec] FMul dword [fpShR16] FLd dword [8+EBX] FSub dword [EBX] FMul ST1 FAdd dword [EBX] FMul dword [ECX] FAddP ST2,ST FLd dword [12+EBX] FSub dword [4+EBX] FMulP ST1,ST FAdd dword [4+EBX] FMul dword [ECX] FAddP ST2,ST Sub ECX,8 Mov EAX,[firDec] Add EAX,[firRate] Mov [firDec],AX ShR EAX,16 LEA EBX,[EAX*8+EBX] Cmp ECX,firTaps JAE short %%Tap %endmacro PROC AAFilter USES ECX,ESI,EDI Mov EAX,[ESI+8] Sub EAX,[ESI] IMul EBP ShRD EAX,EDX,16 Add EAX,[ESI] Mov [EBX],EAX Mov [LOWBUF*2+EBX],EAX Mov EAX,[ESI+12] Sub EAX,[ESI+4] IMul EBP ShRD EAX,EDX,16 Add EAX,[ESI+4] Mov [EBX+4],EAX Mov [LOWBUF*2+EBX+4],EAX Add EBX,8 And EBX,~(LOWBUF*2) Mov ECX,LOWTAPS-1 XOr ESI,ESI XOr EDI,EDI .Filter: Mov EAX,[ECX*8+EBX] IMul dword [ECX*4+lowTaps] Add ESI,EDX Mov EAX,[ECX*8+EBX+4] IMul dword [ECX*4+lowTaps] Add EDI,EDX Dec ECX JNS short .Filter Mov EAX,ESI Mov EDX,EDI ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Emulate DSP %if DSPINTEG ;-------------------------------------------- ;Emulate DSP up to Current Point in Time PROC UpdateDSP USES ESI,EDX %if PROFILE Inc dword [profile+Profile.update] %endif Push EAX Mov ESI,output Call GetSPCTime ;EAX = number of ticks that have passed Sub EAX,[_Out(Cnt)] Add [_Out(Cnt)],EAX Mul dword [_Out(Rate)] ;EAX = number of samples to generate Add EAX,[_Out(Dec)] AdC EDX,0 Mov [_Out(Dec)],AX ShRD EAX,EDX,16 JZ short .Done Sub [_Out(Left)],EAX ;Subtract sample count from samples left JNC short .Okay Add EAX,[_Out(Left)] Mov dword [_Out(Left)],0 .Okay: %if PROFILE Inc dword [8+profile+Profile.update] %endif Call EmuDSP,[_Out(PBuf)],EAX Mov [_Out(PBuf)],EAX .Done: Pop EAX ENDP ;-------------------------------------------- ;Set Automatic EmuDSP Parameters ; ;pBufD -> Buffer to store samples ;numD = Number of samples to generate ;rateD = Sample rate ; rateD may be less or more than dspRate if the APU ; is trying to speed up or slow down the music PROC SetEmuDSP, pBuf, num, rate USES ESI Mov EAX,[%$rate] Mov ESI,output Test EAX,EAX JZ short .Final Push ECX,EDX XOr EDX,EDX ShLD EDX,EAX,16 ShL EAX,16 Mov ECX,64000 Div ECX Mov [_Out(Rate)],EAX Pop EDX,ECX Mov EAX,[%$num] Mov [_Out(Left)],EAX Mov EAX,[%$pBuf] Mov [_Out(PBuf)],EAX Call GetSPCTime Mov [_Out(Cnt)],EAX Mov dword [_Out(Dec)],0 RetS .Final: Call EmuDSP,[_Out(PBuf)],[_Out(Left)] Mov [_Out(PBuf)],EAX Mov dword [_Out(Left)],0 ENDP %endif PROC EmuDSP, pBuf, num USES ALL %if PROFILE Call StartAPUProfile,Profile.dspTSC %endif Mov EAX,[%$pBuf] XOr EBX,EBX ;EBX = 0 if output pointer is null, otherwise it indexes Test EAX,EAX ; the emulation routine SetZ BL Dec BL And BL,[dspMix] .Next: ;Verify output buffer length ---------- Mov EDX,[%$num] Test EDX,EDX ;Is num > 0? JLE .Quit ; No, Quit Cmp EDX,MIX_SIZE ;Is num <= size of internal buffer? JBE short .NSmpOK Mov EDX,MIX_SIZE .NSmpOK: Sub [%$num],EDX Test byte [dbgOpt],DSP_HALT ;Do nothing if DSP is suspended JNZ short .Mute ;Call emulation routine --------------- Test byte [dspOpts],OPT_ANALOG JZ short .NoLow Mov [realOutS],EDX IMul EDX,[lowRate] Add EDX,[lowDec] ShR EDX,16 .NoLow: Call [EBX*4+mixRout],EAX,EDX JC short .Next ;Quit, if emulation produced output .Mute: ;Output silence ----------------------- Mov EDI,EAX XOr EAX,EAX ;EDX = Size of output buffer in bytes MovSX AX,[dspSize] XOr AL,AH Sub AL,AH Mul byte [dspChn] IMul EDX,EAX Cmp byte [dspSize],1 ;EAX = 80h if samples are unsigned, 0 otherwise SetNE AL Dec EAX And EAX,80808080h Mov ECX,EDX ;Fill output buffer with baseline samples And EDX,3 ShR ECX,2 Rep StoSD Mov ECX,EDX Rep StoSB Mov EAX,EDI Jmp .Next .Quit: Push EAX %if MMETER Call ProcessAAR %endif Call SetFade ;Update ENVX and OUTX registers ---------- Mov EBX,7*10h Mov ESI,mix Mov EDI,dsp .XRegs: ; XOr EAX,EAX ; Mov DL,[EBX+EDI+adsr1] ; Or DL,[EBX+EDI+gain] ; JNS short .NoEnvX Mov EAX,[EBX*8+ESI+eVal] ShR EAX,E_SHIFT ; .NoEnvX: Mov [EBX+EDI+envx],AL Mov AL,[EBX*8+ESI+mOut+1] Mov [EBX+EDI+outx],AL Sub EBX,10h JAE short .XRegs ;Update DSP data register in APU RAM ----- Mov EDI,[pAPURAM] Mov DL,[0F2h+EDI] And EDX,7Fh Mov DL,[EDX+dsp] Mov [0F3h+EDI],DL %if PROFILE Call EndAPUProfile,Profile.dspTSC %endif Pop EAX ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Emulate DSP (Integer) ; ;Emulates the DSP of the SNES using standard 80386 instructions. ;This routine is provided for historical purposes. It's the only routine that produces 8-bit or ;monaural output. ; ;In: ; pBuf-> Buffer to store output ; num = Number of samples to create (1 - MIX_SIZE) ; ;Out: ; CF = Set, samples were created ; EAX-> End of buffer ; ; CF = Clear, DSP is muted ; EDI = pBuf ; EDX = num ; ;Destroys: ; ECX,EDX,ESI,EDI PROC EmuDSPI, pBuf, num LOCALS count USES EBX Mov EAX,[%$num] ;Save output size as counter Mov [%$count],EAX Mov EDI,mixBuf ;EDI -> Temporary mixing buffer ALIGN 16 .NextEmu: ;Generate Noise ======================= NoiseGen ;Voice Loop =========================== XOr ECX,ECX Mov EBX,mix ;EBX -> Internal mixing structure Mov [EDI],ECX ;Erase current samples in buffer Mov [4+EDI],ECX Mov [8+EDI],ECX Mov [12+EDI],ECX Mov CH,1 ;CH = Voice bitmask (start with first voice) ALIGN 16 .VoiceMix: XOr EAX,EAX XOr EDX,EDX Test [voiceMix],CH ;Is the current voice active? JZ .VoiceDone ; No, Goto next voice PitchMod ;Apply pitch modulation UpdateEnv ;Update envelope ;Get sample ======================== Mov EAX,[nSmp] ;Load noise Test [dsp+non],CH ;Is noise enabled? JNZ short .Noise ; Yes, Use noise sample Mov ESI,[EBX+sIdx] ;ESI -> Samples Mov EAX,[EBX+mDec] ;EAX = Sample delta decimal Call [pInter] .Noise: ;Mixing ============================ IMul EAX,[EBX+eVal] ;Multiply envelope height SAR EAX,E_SHIFT+7 ;Remove the effects of envelope multiplication Mov [EBX+mOut],EAX ;Store sample for pitch modulation Test byte [EBX+mFlg],MFLG_MUTE ;Is voice muted by user? JNZ .VoiceOff ; Yes, Don't add to master Mov EDX,EAX ;Duplicate sample for right side IMul EAX,[EBX+mChnL] ;Apply left and right volumes IMul EDX,[EBX+mChnR] SAR EAX,7 ;Remove the effects of volume multiplication SAR EDX,7 Add [EDI],EAX ;Add to master samples Add [4+EDI],EDX Test [dsp+eon],CH ;Is echo turned on for this voice? JZ short .NoChEcho ; No, Move to next voice Add [8+EDI],EAX ;Add to master samples Add [12+EDI],EDX .NoChEcho: %if VMETER ;Save greatest sample output ---- Mov ESI,EDX ;Save right sample CDQ ;eax = abs(eax) XOr EAX,EDX Sub EAX,EDX Sub EAX,[EBX+vMaxL] ;if (EAX>mix.vMaxL) mix.vMaxL=EAX CDQ Not EDX And EDX,EAX Add [EBX+vMaxL],EDX Mov EAX,ESI CDQ ;|Right| XOr EAX,EDX Sub EAX,EDX Sub EAX,[EBX+vMaxR] CDQ Not EDX And EAX,EDX Add [EBX+vMaxR],EAX %endif .VoiceOff: UpdateSrc ;Update sample position .VoiceDone: Sub EBX,-80h ;Increase base pointer Add CH,CH ;Move bitmask over JNZ .VoiceMix ; No, Loop Add EDI,16 Dec dword [%$count] JNZ .NextEmu ;Create final output ===================== Mov EAX,[%$num] Mov [%$count],EAX Mov ESI,mixBuf ALIGN 16 NextSmp: ;Multiply samples by main volume ------ Mov EAX,[ESI] ;Load main samples Mov EDX,[4+ESI] IMul EAX,[volMainL] ;Multiply by main volume IMul EDX,[volMainR] SAR EAX,7 SAR EDX,7 Mov [ESI],EAX ;Store samples back into memory Mov [4+ESI],EDX ;Echo --------------------------------- Test byte [disEcho],-1 ;Is echo enabled? JNZ .NoEcho ; No, Jump past expensive code XOr EBX,EBX Sub dword [echoCur],8 ;Move to next sample in echo buffer SetNC BL Dec EBX And EBX,[echoDel] Add [echoCur],EBX ;Start over, if end of buffer was reached Mov EDI,echoBuf ;EDI -> Delayed sample Add EDI,[echoCur] Mov EAX,[EDI] ;Get current samples from echo buffer Mov EDX,[4+EDI] ;Filter echo ----------------------- Test byte [firEnabl],-1 ;Does song use a filter? JZ .NoFilter ; No, Skip more expensive code FIRFilterI .NoFilter: Mov ECX,EAX Mov EBX,EDX IMul EAX,[volEchoL] ;Multiply delayed samples by echo volume IMul EDX,[volEchoR] SAR EAX,7 SAR EDX,7 Add [ESI],EAX ;Add to main output Add [4+ESI],EDX %if STEREO Mov EDX,EBX Mov EAX,ECX IMul ECX,[echoFB] ;Multiply delayed samples by feedback level IMul EDX,[echoFBCT] ;Multiply delayed samples by crosstalk level IMul EBX,[echoFB] IMul EAX,[echoFBCT] Add ECX,EDX ;Add crosstalk Add EBX,EAX %else IMul ECX,[echoFB] IMul EBX,[echoFB] %endif SAR ECX,7 SAR EBX,7 Add ECX,[8+ESI] ;Add delayed samples to echo samples Add EBX,[12+ESI] Mov [EDI],ECX ;Store feedback back into echo buffer Mov [4+EDI],EBX .NoEcho: %if MMETER Mov EAX,[ESI] ;See VMETER above CDQ XOr EAX,EDX Sub EAX,EDX Sub EAX,[aarMMaxL] CDQ Not EDX And EAX,EDX Add [aarMMaxL],EAX Mov EAX,[4+ESI] CDQ XOr EAX,EDX Sub EAX,EDX Sub EAX,[aarMMaxR] CDQ Not EDX And EAX,EDX Add [aarMMaxR],EAX %endif Add ESI,16 Dec dword [%$count] JNZ NextSmp ;========================================= ; Store output Test byte [dsp+flg],40h ;Is the DSP muted? JNZ .MuteI ; Yes, Return silence Mov EBX,32768 Mov ECX,[%$num] Mov [%$count],ECX Mov ESI,mixBuf Mov EDI,[%$pBuf] Mov AL,[dspSize] And AL,1 Mov AH,[dspChn] And AH,2 Or AL,AH Test AL,3 JZ .R16M Test AL,1 JZ .R16S Test AL,2 JZ .R8M ;8-bit Stereo ---------------------------- .R8S: Mov EAX,[ESI] Mov EDX,[4+ESI] Add EAX,EBX ;Unsign sample Add EDX,EBX SHR EAX,8 ;Convert to 8-bit Test AH,AH ;Is sample negative or more than 255? JZ short .SmpOKL SetS AL Dec AL ;AL = 0 or 255, based on sign .SmpOKL: SHR EDX,8 Test DH,DH JZ short .SmpOKR SetS DL Dec DL .SmpOKR: Mov AH,DL Mov [EDI],AX Add ESI,16 Add EDI,2 Dec ECX JNZ short .R8S StC RetN EDI ;16-bit Stereo --------------------------- .R16S: Mov EDX,[ESI] Mov EAX,[4+ESI] ;Clamp samples ------------------------ LEA ECX,[EDX+EBX] ;Convert sample to unsigned, for test SAR ECX,16 ;Is sample negative or more than 32767? JZ short .SmpL ; No Mov EDX,EBX ;EDX = -32768 SetS DL ;Set DL based on sign Dec EDX ;EDX = -32768 or 32767 .SmpL: LEA ECX,[EAX+EBX] SAR ECX,16 JZ short .SmpR Mov EAX,EBX SetS AL Dec EAX .SmpR: Mov [EDI],DX ;Store samples Mov [2+EDI],AX Add ESI,16 Add EDI,4 Dec dword [%$count] JNZ short .R16S StC ;Set carry, since output was made RetS EDI ;Return pointer to end of output ;8-bit Mono ------------------------------ .R8M: Mov AX,[1+ESI] Sub AX,-128 ;Test for clipping Test AH,AH JZ short .Smp8OK SetS AL Dec AL .Smp8OK: Mov [EDI],AL Add ESI,16 Inc EDI Dec ECX JNZ short .R8M StC RetS EDI ;16-bit Mono ----------------------------- .R16M: Mov EAX,[ESI] LEA EDX,[EBX+EAX] ;Test for clipping SAR EDX,16 JZ short .Smp16OK Mov EAX,8000h SetS AL Dec EAX .Smp16OK: Mov [EDI],AX Add ESI,16 Add EDI,2 Dec ECX JNZ short .R16M StC RetS EDI .MuteI: Mov EAX,[%$pBuf] Mov EDX,[%$num] ClC ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Emulate DSP (MMX) ; ;Emulates the DSP of the SNES using instructions from the Intel Multi-Media Extensions ;This mixing routine is coded for original hardware accuracy. It's the only routine that supports ;OPT_ANALOG. All output is stereo. ; ;In: ; pBuf-> Buffer to store output ; num = Number of samples to create (1 - MIX_SIZE) ; ;Out: ; CF = Set, samples were created ; EAX-> End of buffer ; ; CF = Clear, DSP is muted ; EDI = pBuf ; EDX = num ; ;Destroys: ; ECX,EDX,ESI,EDI ; MM0 = 0 ; MM1 = Main ; MM2 = Echo ; MM3 = VMax for main output ; MM4 = temp ; MM5 = temp ; MM6 = temp ; MM7 = temp PROC EmuDSPX, pBuf, num LOCALS count,8,fpShR30,ftemp USES EBX PXOr MM0,MM0 ;MM0 = 0, always %if MMETER MovQ MM3,[aarMMaxL] %endif ;========================================= ;Mix voices Mov EAX,[%$num] Test EAX,EAX ;num can == 0 when OPT_ANALOG is enabled JZ .Store Mov [%$count],EAX XOr EAX,EAX Test byte [dspOpts],OPT_ANALOG SetNZ AL ShL EAX,6 LEA EDI,[EAX+mixBuf] ALIGN 16 .NextEmu: ;Generate noise ----------------------- NoiseGen ;Process single voice ----------------- XOr ECX,ECX Mov EBX,mix PXOr MM1,MM1 PXOr MM2,MM2 Mov CH,1 ALIGN 16 .VoiceMix: XOr EAX,EAX XOr EDX,EDX Test [voiceMix],CH JZ .VoiceDone PitchMod ;Apply pitch modulation UpdateEnv ;Update envelope ;Get sample ------------------------ Mov EAX,[nSmp] Test [dsp+non],CH JNZ short .Noise Mov ESI,[EBX+sIdx] Mov EAX,[EBX+mDec] Call [pInter] .Noise: ;Mixing ---------------------------- IMul EAX,[EBX+eVal] SAR EAX,E_SHIFT+7+1 Add EAX,EAX Mov [EBX+mOut],EAX Test byte [EBX+mFlg],MFLG_MUTE JNZ .VoiceOff MovZX EAX,AX ;Truncate sample to 16-bits and dupilcate in MM5 MovD MM4,EAX PUnpckLDQ MM4,MM4 PMAddWD MM4,[EBX+mChnL] ;Multiply by channel volume PSRAD MM4,7 %if VMETER ;Save greatest sample output ---- MovQ MM7,[EBX+vMax] ;Get max voice output so far MovQ MM6,MM4 ;Copy current voice output MovQ MM5,MM4 PSRAD MM6,31 ;Create a mask from negative samples PXOr MM5,MM6 ;Invert negative samples PSubD MM5,MM6 ;Add 1 to negatives, |MM5| MovQ MM6,MM7 PCmpGTD MM7,MM5 ;Create a mask from greater samples PAnd MM6,MM7 ;Save greater samples PAndN MM7,MM5 ;Clear lesser samples POr MM7,MM6 ;MM7 = Greatest output so far MovQ [EBX+vMax],MM7 %endif PAddSW MM1,MM4 ;Add to main output Test [dsp+eon],CH JZ short .NoChEcho PAddSW MM2,MM4 ;Add to echo .NoChEcho: .VoiceOff: UpdateSrc ;Update sample position .VoiceDone: Sub EBX,-80h Add CH,CH JNZ .VoiceMix PSLLD MM1,16 PSLLD MM2,16 PSRLD MM1,17 PSRLD MM2,17 PAddW MM1,MM1 PAddW MM2,MM2 MovQ [EDI],MM1 MovQ [8+EDI],MM2 Add EDI,16 Dec dword [%$count] JNZ .NextEmu ;========================================= ;Apply main volumes and mix in echo Mov EAX,[%$num] Mov [%$count],EAX XOr EAX,EAX Test byte [dspOpts],OPT_ANALOG SetNZ AL ShL EAX,6 LEA ESI,[EAX+mixBuf] ALIGN 16 .NextSmp: ;Multiply samples by main volume ------ MovQ MM1,[ESI] PMAddWD MM1,[volMainL] PSRAD MM1,7 Test byte [disEcho],-1 JNZ .NoEcho XOr EAX,EAX Sub dword [echoCur],8 SetNC AL Dec EAX And EAX,[echoDel] Add [echoCur],EAX Mov EDI,echoBuf Add EDI,[echoCur] MovQ MM2,[EDI] ;Load MM2 with echo samples incase filtering is off ;Filter echo ----------------------- FIRFilterX MovQ MM5,MM2 PMAddWD MM5,[volEchoL] PSRAD MM5,7 PAddD MM1,MM5 %if STEREO MovQ MM7,MM2 PSRLQ MM7,32 ;MM7 = PSwapD MM2 PUnpckLDQ MM7,MM2 PMAddWD MM2,[echoFB] ;Multiply old samples by feedback PMAddWD MM7,[echoFBCT] PAddD MM2,MM7 %else PMAddWD MM2,[echoFB] %endif PSLLD MM2,9 ;Shift result into upper 16-bits PSRLD MM2,17 ;Zero fill upper word and clear LSB PAddW MM2,MM2 ; " " PAddSW MM2,[8+ESI] ;Add current samples and clamp result MovQ [EDI],MM2 .NoEcho: %if MMETER MovQ MM6,MM1 ;See VMETER above MovQ MM7,MM1 PSRAD MM6,31 PXOr MM7,MM6 PSubD MM7,MM6 MovQ MM6,MM3 PCmpGTD MM3,MM7 PAnd MM6,MM3 PAndN MM3,MM7 POr MM3,MM6 %endif PackSSDW MM1,MM0 MovD [ESI],MM1 Add ESI,16 Dec dword [%$count] JNZ .NextSmp %if MMETER MovQ [aarMMaxL],MM3 %endif ;========================================= ; Store output .Store: Test byte [dsp+flg],40h JNZ MuteX Mov ESI,mixBuf Mov EDI,[%$pBuf] Mov ECX,[%$num] Test byte [dspOpts],OPT_ANALOG JNZ .UseLow Cmp byte [dspSize],2 JE .R16 Cmp byte [dspSize],3 JE .R24 Cmp byte [dspSize],4 JE .R32 EMMS .RF: FILd word [ESI] FMul dword [fpShR15] FILd word [2+ESI] FMul dword [fpShR15] FStP dword [4+EDI] FStP dword [EDI] Add ESI,16 Add EDI,8 Dec ECX JNZ short .RF StC RetN EDI .R16: Mov EAX,[ESI] Mov [EDI],EAX Add ESI,16 Add EDI,4 Dec ECX JNZ short .R16 EMMS StC RetN EDI .R24: MovZX EAX,word [ESI] Mov DX,[2+ESI] ShL EAX,8 Mov [EDI],EAX Mov [4+EDI],DX Add ESI,16 Add EDI,6 Dec ECX JNZ short .R24 EMMS StC RetN EDI .R32: MovQ MM1,MM0 PUnpckLWD MM1,[ESI] MovQ [EDI],MM1 Add ESI,16 Add EDI,8 Dec ECX JNZ short .R32 EMMS StC RetN EDI .UseLow: Test ECX,ECX JZ .NoUnpack Mov EAX,17491 ;Initialize white noise generator Mov EDX,1999 MovD MM6,EAX MovD MM7,EDX PUnpckLDQ MM6,MM6 PUnpckLDQ MM7,MM7 MovQ MM5,[lowRFI] Mov EBX,[lowAmp] Mov EAX,07E87A09h ;Clamp to -0.1dB (28-bits signed) MovD MM4,EAX PUnpckLDQ MM4,MM4 %if MMETER MovQ MM3,[aarMMaxL] PSLLD MM3,12 %endif Mov EDI,32 .Unpack: PUnpckLWD MM1,[EDI*2+ESI] ;Get 16-bit samples PSRAD MM1,3 ;Add -72dB of white noise PMulLW MM5,MM6 ;<-- causes a DC offset, maybe fix this some day PAddW MM5,MM7 PAddD MM1,MM5 PSRAD MM1,9 ;Reduce to 20 bits MovD EAX,MM1 ;Multiply by volAmp PSRLQ MM1,32 MovD EDX,MM1 IMul EAX,EBX IMul EDX,EBX MovD MM1,EAX MovD MM2,EDX PUnpckLDQ MM1,MM2 %if MMETER MovQ MM2,MM1 PSRAD MM2,31 PXOr MM2,MM1 MovQ MM0,MM3 PCmpGTD MM3,MM2 PAnd MM0,MM3 PAndN MM3,MM2 POr MM3,MM0 %endif ;Clamp samples --------------------- PAddD MM1,MM4 ;Unsign samples PSRAD MM1,1 MovQ MM2,MM1 ;Clamp < 0 PSRAD MM1,31 PAndN MM1,MM2 MovQ MM2,MM4 ;Clamp > MM4 PCmpGTD MM2,MM1 PAnd MM1,MM2 PAndN MM2,MM4 POr MM1,MM2 PAddD MM1,MM1 ;Sign samples PSubD MM1,MM4 PSLLD MM1,3 MovQ [EDI+ESI],MM1 Add EDI,8 Dec ECX JNZ short .Unpack Mov EDI,[%$pBuf] MovQ [lowRFI],MM5 %if MMETER PSRAD MM3,12 MovQ [aarMMaxL],MM3 PXOr MM0,MM0 %endif .NoUnpack: Push EBP Mov ECX,[realOutS] Mov EBP,[lowDec] Mov EBX,[lowCur] Cmp byte [dspSize],2 JE .RL16 Cmp byte [dspSize],3 JE .RL24 Cmp byte [dspSize],4 JE .RL32 Mov dword [%$fpShR30],30800000h EMMS .RLF: Call AAFilter Mov [%$ftemp],EAX Mov [4+%$ftemp],EDX FILd dword [%$ftemp] FILd dword [4+%$ftemp] FMul dword [%$fpShR30] FStP dword [4+EDI] FMul dword [%$fpShR30] FStP dword [EDI] Mov [EDI],EAX Mov [4+EDI],EDX XOr EAX,EAX Add BP,[lowRate] AdC AL,[2+lowRate] LEA ESI,[EAX*8+ESI] Add EDI,8 Dec ECX JNZ .RLF Jmp .DoneL .RL16: Call AAFilter ShR EAX,14 ShR EDX,14 Mov [EDI],AX Mov [2+EDI],DX XOr EAX,EAX Add BP,[lowRate] AdC AL,[2+lowRate] LEA ESI,[EAX*8+ESI] Add EDI,4 Dec ECX JNZ .RL16 Jmp .DoneL .RL24: Call AAFilter ShL EAX,2 ShR EDX,6 ShRD EAX,EDX,8 ShR EDX,8 Mov [EDI],EAX Mov [4+EDI],DX XOr EAX,EAX Add BP,[lowRate] AdC AL,[2+lowRate] LEA ESI,[EAX*8+ESI] Add EDI,6 Dec ECX JNZ .RL24 Jmp .DoneL .RL32: Call AAFilter ShL EAX,2 ShL EDX,2 Mov [EDI],EAX Mov [4+EDI],EDX XOr EAX,EAX Add BP,[lowRate] AdC AL,[2+lowRate] LEA ESI,[EAX*8+ESI] Add EDI,8 Dec ECX JNZ .RL32 .DoneL: Mov [lowCur],EBX Mov [lowDec],EBP Pop EBP Mov EDX,[%$num] ShL EDX,3 JZ short .Blah MovQ MM1,[EDX+mixBuf] MovQ MM2,[8+EDX+mixBuf] MovQ MM3,[16+EDX+mixBuf] MovQ MM4,[24+EDX+mixBuf] MovQ [mixBuf],MM1 MovQ [8+mixBuf],MM2 MovQ [16+mixBuf],MM3 MovQ [24+mixBuf],MM4 .Blah: EMMS ;Empty MMX regs to keep FP operations from breaking StC RetS EDI MuteX: Mov EAX,[%$pBuf] Mov EDX,[%$num] EMMS ClC ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Emulate DSP (Float) ; ;Emulates the DSP of the SNES using floating-point instructions. ;This routine is coded for numerical accuracy. ; ;In: ; pBuf-> Buffer to store output ; num = Number of samples to create (1 - MIX_SIZE) ; ;Out: ; CF = Set, samples were created ; EAX-> End of buffer ; ; CF = Clear, DSP is muted ; EDI = pBuf ; EDX = num ; ;Destroys: ; ECX,EDX,ESI,EDI,ST0-ST7 ;----------------------------------------- ;Clamp a single precision float ; ;In: ; ESI -> Float to clamp ; EBX = +/- float value to clamp to ; ;Out: ; ST0 = Clamped value %macro ClampF32 0 Mov EAX,[ESI] XOr EDX,EDX Add EAX,EAX ;Get absolute value of sample by shifting off sign bit RCL EBX,1 ;Save sign of sample Sub EAX,EBX SetA DL ;if (sample < EBX) EDX = -1 Dec EDX And EAX,EDX ;Clamp sample Add EAX,EBX ShR EBX,1 ;Restore sign RCR EAX,1 Mov [ESI],EAX FLd dword [ESI] %endmacro PROC EmuDSPF, pBuf, num LOCALS count,vMaxL,vMaxR,fSize,fpEShR USES EBX Mov dword [%$fpEShR],(127-(E_SHIFT+7)) << 23 ;========================================= ; Mix voices Mov EAX,[%$num] Mov [%$count],EAX Mov EDI,mixBuf ALIGN 16 .NextEmu: ;Generate Noise ======================= NoiseGen ;Voice Loop =========================== FLdZ XOr ECX,ECX Mov EBX,mix FSt qword [EDI] FStP qword [8+EDI] Mov CH,1 .VoiceMix: XOr EAX,EAX XOr EDX,EDX Test [voiceMix],CH JZ .VoiceDone PitchMod ;Apply pitch modulation UpdateEnv ;Update envelope ;Get sample ======================== Mov ESI,[EBX+sIdx] Mov EAX,[EBX+mDec] Call [pInter] Test [dsp+non],CH JZ short .NoNoise FStP ST FILd dword [nSmp] .NoNoise: ;Mixing ============================ FIMul dword [EBX+eVal] FMul dword [%$fpEShR] FISt dword [EBX+mOut] Test byte [EBX+mFlg],MFLG_MUTE JNZ .VoiceOff ;Adjust volumes to match target - Mov EAX,[EBX+mTgtL] XOr EDX,EDX Cmp EAX,[EBX+mChnL] JZ short .NoRampL ;Choose ramp direction ------- SetL DL ;DL = Ramp direction (0/1, up/down) Test EAX,[EBX+mChnL] ;If mTgtL and mChnL are both negative, reverse the SetS CL ; result of the comparison/direction XOr DL,CL FLd dword [EBX+mChnL] ;Load current volume FLd dword [EDX*4+volRamp] ;Load +/- ramp amount ;Ramp volume ----------------- FAddP ST1,ST FStP dword [EBX+mChnL] Test EAX,[EBX+mChnL] SetS CL ;Reverse comparison if both are negative XOr CL,DL ;Has target has been hit? ---- Sub EAX,[EBX+mChnL] SetG DL ;DL = 1, if volume hasn't reached destination XOr DL,CL ;Reverse comparison if volume is moving down Dec EDX And EAX,EDX Add [EBX+mChnL],EAX .NoRampL: Mov EAX,[EBX+mTgtR] XOr EDX,EDX Cmp EAX,[EBX+mChnR] JZ short .NoRampR SetL DL Test EAX,[EBX+mChnR] SetS CL XOr DL,CL FLd dword [EBX+mChnR] FLd dword [EDX*4+volRamp] FAddP ST1,ST FStP dword [EBX+mChnR] Test EAX,[EBX+mChnR] SetS CL XOr CL,DL Sub EAX,[EBX+mChnR] SetG DL XOr DL,CL Dec EDX And EAX,EDX Add [EBX+mChnR],EAX .NoRampR: ;Apply channel volumes ---------- FLd ST FMul dword [EBX+mChnR] FXCh ST1 FMul dword [EBX+mChnL] %if VMETER FLd ST1 FAbs FLd ST1 FAbs FIStP dword [%$vMaxL] FIStP dword [%$vMaxR] %endif Test [dsp+eon],CH JNZ short .VoiceEcho FAdd dword [EDI] FStP dword [EDI] FAdd dword [4+EDI] FSt dword [4+EDI] Jmp short .NoVoiceEcho .VoiceEcho: FLd ST FAdd dword [EDI] FStP dword [EDI] FAdd dword [8+EDI] FStP dword [8+EDI] FLd ST FAdd dword [4+EDI] FStP dword [4+EDI] FAdd dword [12+EDI] FSt dword [12+EDI] .NoVoiceEcho: %if VMETER ;Save greatest sample output ---- Mov EAX,[EBX+vMaxL] Sub EAX,[%$vMaxL] CDQ And EAX,EDX Sub [EBX+vMaxL],EAX Mov EAX,[EBX+vMaxR] Sub EAX,[%$vMaxR] CDQ And EAX,EDX Sub [EBX+vMaxR],EAX %endif .VoiceOff: FStP ST UpdateSrc ;Update sample position .VoiceDone: Sub EBX,-80h Add CH,CH JNC .VoiceMix Add EDI,16 Dec dword [%$count] JNZ .NextEmu ;========================================= ; Apply main volumes and mix in echo %if MMETER XOr EAX,EAX Mov [%$vMaxL],EAX Mov [%$vMaxR],EAX %endif MovZX EBX,byte [dspChn] ShL EBX,2 Mov [%$fSize],EBX Mov EAX,[%$num] Mov [%$count],EAX Mov ESI,mixBuf Mov EDI,mixBuf ALIGN 16 .NextSmp: ;Multiply samples by main volume ------ FLd dword [ESI] FMul dword [volMainL] FStP dword [EDI] FLd dword [4+ESI] FMul dword [volMainR] FStP dword [4+EDI] Test byte [disEcho],-1 JNZ .NoEcho ;Advance echo sample pointer ------- Mov EDX,[echoCur] XOr EAX,EAX Sub EDX,8 SetNC AL Dec EAX And EAX,[echoDel] Add EDX,EAX Mov [echoCur],EDX Add EDX,echoBuf FLd dword [4+EDX] ;FBR FLd dword [EDX] ;FBR FBL ;Filter echo ----------------------- FIRFilterF FLd ST1 ;FBR FBL FBR FLd ST1 ;FBR FBL FBR FBL ;Calculate echo feedback ----------- %if STEREO FLd ST ;FBR FBL FBR FBL FBL FMul dword [echoFB] ;FBR FBL FBR FBL FBL*EchoFB FLd ST2 ;FBR FBL FBR FBL EFBL FBR FMul dword [echoFBCT] ;FBR FBL FBR FBL EFBL FBR*EchoFBCT FAddP ST1,ST ;FBR FBL FBR FBL EFBL+EFBCR FAdd dword [8+ESI] ;FBR FBL FBR FBL EFBL+EL FStP dword [EDX] ;FBR FBL FBR FBL FMul dword [echoFBCT] ;FBR FBL FBR FBL*EchoFBCT FXCh ST1 ;FBR FBL EFBCL FBR FMul dword [echoFB] ;FBR FBL EFBCL FBR*EchoFB FAddP ST1,ST ;FBR FBL EFBCL+EFBR FAdd dword [12+ESI] ;FBR FBL EFBR+ER FStP dword [4+EDX] ;FBR FBL %else FMul dword [echoFB] ;FBR FBL FBR FBL*EchoFB FAdd dword [8+ESI] ;FBR FBL FBR EFBL+EL FStP dword [EDX] ;FBR FBL FBR FMul dword [echoFB] ;FBR FBL FBR*EchoFB FAdd dword [12+ESI] ;FBR FBL EFBR+ER FStP dword [4+EDX] ;FBR FBL %endif ;Add echo to main output ----------- FMul dword [volEchoL] ;FBR FBL*EVolL FXCh ST1 ;EchoL FBR FMul dword [volEchoR] ;EchoL FBR*EVolR Cmp byte [dspChn],4 JB short .Stereo FStP dword [12+EDI] FStP dword [8+EDI] Jmp short .ChDone .Stereo: FAdd dword [4+EDI] ;EchoL EchoR+MainR FStP dword [4+EDI] ;EchoL FAdd dword [EDI] ;EchoL+MainL FStP dword [EDI] ;(empty) .ChDone: .NoEcho: Add ESI,16 %if MMETER Mov EDX,[EDI] ;if (abs(out) > MMaxL) MMaxL = abs(out); Mov EAX,[%$vMaxL] Add EDX,EDX ;EDX = |Left| ShR EDX,1 Sub EAX,EDX CDQ And EAX,EDX Sub [%$vMaxL],EAX Mov EDX,[4+EDI] Mov EAX,[%$vMaxR] Add EDX,EDX ;EDX = |Right| ShR EDX,1 Sub EAX,EDX CDQ And EAX,EDX Sub [%$vMaxR],EAX %endif Add EDI,[%$fSize] Dec dword [%$count] JNZ .NextSmp ;========================================= ; Store output Test byte [dsp+flg],40h JNZ MuteF Mov AL,[dspSize] MovZX ECX,byte [dspChn] Mov ESI,mixBuf Mov EDI,[%$pBuf] IMul ECX,[%$num] Cmp AL,-4 JE .OutFloat Mov EBX,4EFFFE00h ;EBX = 2147418112.0 (32767 << 16) Cmp AL,3 JE short .Next24 Cmp AL,4 JE short .Next32 FLd dword [fpShR16] .Next16: ClampF32 Add ESI,4 FMul ST1 FIStP word [EDI] Add EDI,2 Dec ECX JNZ short .Next16 FStP ST Jmp .Done .Next24: ClampF32 FIStP dword [ESI] Mov DL,[1+ESI] Mov AX,[2+ESI] Add ESI,4 Mov [0+EDI],DL Mov [1+EDI],AX Add EDI,3 Dec ECX JNZ short .Next24 Jmp .Done .Next32: ClampF32 Add ESI,4 FIStP dword [EDI] Add EDI,4 Dec ECX JNZ short .Next32 Jmp .Done ; ;*** Slower floating-point method for clamping *** ; FLd [fp2M] ;Load clamping range |Fp2M ; ; FLd dword [ESI] ;Load sample |Fp2M Left ; FLd ST ; |Fp2M Left Left ; FAbs ;Get absolute value |Fp2M Left |Left| ; FComP ST2 ;Cmp |Left|,Fp2M and pop |Fp2M Left ; FNStSW AX ;Store FPU flags in AX ; SAHF ;Store AH in x86 flags ; JBE short .SmpL ; FStP [fTemp] ; |Fp2M ; FLd ST ; |Fp2M Fp2M ; Test byte [3+ESI],80h ;Is sample negative? ; JZ short .SmpL ; No, Leave ST positive ; FChS ; Yes, Negate ST |Fp2M -Fp2M ; .SmpL: ; FIStP dword [EDI] ;Save sample as an int |Fp2M ; ; FStP ST ;Pop clipping range ;32-bit floating-point ------------------- .OutFloat: Rep MovSD .Done: ;Save maximum output --------------------- Mov EAX,[aarMMaxL] FLd dword [%$vMaxL] FMul dword [fpShR16] FIStP dword [%$vMaxL] Sub EAX,[%$vMaxL] CDQ And EAX,EDX Sub [aarMMaxL],EAX Mov EAX,[aarMMaxR] FLd dword [%$vMaxR] FMul dword [fpShR16] FIStP dword [%$vMaxR] Sub EAX,[%$vMaxR] CDQ And EAX,EDX Sub [aarMMaxR],EAX StC RetS EDI MuteF: Mov EAX,[%$pBuf] Mov EDX,[%$num] ClC ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Emulate DSP (no mixing) ; ;Emulates the DSP of the SNES, except for pitch modulation, noise generator, and mixing. ; ;Notes: ; OutBuf can be null, in which case no output is created. If OutBuf is not null, silence will be ; returned. ; ;In: ; pBuf-> Buffer that would store output ; num = Number of samples to not create (1-3072) ; ;Out: ; EAX = outBuf ; CF = True, if EAX is null ; ;Destroys: ; ECX,EDX,ESI,EDI PROC EmuDSPN, pBuf, num LOCALS count USES EBX Mov EAX,[%$num] Mov [%$count],EAX Mov EBX,mix Mov CH,1 ;CH = Voice bitmask ALIGN 16 .NextVoice: ;Voice Loop --------------------------- Test [voiceMix],CH ;Is the current voice on? JZ .VoiceDone ; No ;Figure new sample position ======== Mov EAX,[EBX+mRate] Mul dword [%$count] Add [EBX+mDec],AX SetC CL ShRD EAX,EDX,16 MovZX EDX,CL Add EAX,EDX Mov EDX,EAX ShR EDX,4 ;EDX = Number of sample blocks to increase And EAX,0Fh Add EAX,EAX ;EAX = Position in sample RAM Add [EBX+sIdx],AL ;Increase current index Test byte [EBX+sIdx],20h ;Have we moved into the next block? JZ short .NoSInc ; No And byte [EBX+sIdx],~20h ;Correct index in Inc EDX .NoSInc: Test EDX,EDX ;Do any blocks need to be decompressed? JZ .NoBInc ; No Mov ESI,[EBX+bCur] ;ESI -> Current block .NextBlk: Mov AL,[ESI] Test AL,1 ;Is this the end block? JZ short .NotEnd ; Not yet Or [dsp+endx],CH ;Set end flag Test AL,2 JNZ short .LoopB Not CH ;Turn off voice And [voiceMix],CH Not CH Mov dword [EBX+eVal],0 Mov dword [EBX+mOut],0 Or byte [EBX+mFlg],MFLG_OFF And byte [EBX+mFlg],~MFLG_KOFF Jmp .VoiceDone .LoopB: Mov EDI,[EBX+pDSPV] Mov ESI,[pAPURAM] MovZX EAX,byte [EDI+srcn] ShL EAX,2 Add AH,[dsp+dir] Mov SI,[2+EAX+ESI] .NotEnd: Push EDX,EBX ;Decompress block LEA EDI,[EBX+sBuf] MovSX EDX,word [EBX+sP1] MovSX EBX,word [EBX+sP2] Call [pDecomp] Mov EAX,EBX Pop EBX Mov [EBX+sP1],DX Pop EDX Mov [EBX+sP2],AX Dec EDX JNZ short .NextBlk Sub SI,9 ;Save block header and pointer for last block decomp Mov AL,[ESI] Mov [EBX+bCur],ESI Mov [EBX+bHdr],AL .NoBInc: XOr EAX,EAX Mov [EBX+sBuf-4],EAX Mov [EBX+sBuf-8],EAX ;Adjust envelope height ============ Mov EDI,[%$count] ALIGN 16 .VoiceEnv: Mov CL,[EBX+eMode] Test CL,E_IDLE ;Is envelope constant? JNZ .EnvDone ; Yes, Skip height adjustment Mov EAX,EDI Cmp DI,[2+EBX+eCnt] ;EAX = min(eCnt, countN) JB short .EmuS Mov AX,[2+EBX+eCnt] .EmuS: Sub EDI,EAX Dec EAX Sub [2+EBX+eCnt],AX UpdateEnv ;Calculate envelope change Test EDI,EDI JNZ .VoiceEnv .EnvDone: ;Update ENVX and OUTX registers ==== Mov ESI,dword [EBX+sIdx] MovSX EAX,word [ESI] ;EAX = Current sample IMul EAX,[EBX+eVal] SAR EAX,E_SHIFT+7 ;Reduce sample to 16-bit number Mov [EBX+mOut],EAX .VoiceDone: Sub EBX,-80h ;Increase base pointer Add CH,CH ;Move bitmask over JNC .NextVoice Mov EAX,[%$pBuf] ;Set carry if OutBuf is null, so EmuDSP doesn't crash Cmp EAX,1 ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Play Sound Source PROC PlaySrc, pSrc, loop, rate LOCALS pInter,inRAM,fp32k USES ALL Mov EBX,src Mov EAX,[%$rate] Test EAX,EAX JNZ .New Mov EAX,[_Src(Blk)] Test EAX,EAX JZ .Done Mov dword [%$fp32k],47000000h ;32768.0 ShR EAX,16 Cmp AX,[2+pAPURAM] SetNE [%$inRAM] ;Select interpolation function ----------- MovZX EDX,byte [dspMix] MovZX EAX,byte [dspInter] XOr ECX,ECX Test EDX,EDX SetNZ CL Sub EDX,ECX ;If dspMix == MIX_NONE, default to MIX_INT LEA EDX,[EDX*5] Add EDX,EAX Mov EAX,[EDX*4+intRout] Mov [%$pInter],EAX Mov EAX,[%$loop] .NextBuf: ;===================================== ;Fill mixBuf Mov ECX,2*2*MIX_SIZE Mov EDI,mixBuf Mov ESI,[_Src(Idx)] Sub EAX,ECX Mov [%$loop],EAX CDQ And EAX,EDX Add ECX,EAX Mov [%$rate],ECX .NextSmp: Mov EAX,[_Src(Dec)] ;Has sample index increased? Add EAX,[_Src(Rate)] Mov [_Src(Dec)],AX ShR EAX,16 JZ .NoSInc LEA ESI,[EAX*2+ESI] ;Have we reached the end of the block? Test ESI,20h JZ .NoSInc And ESI,~20h Mov EAX,[_Src(Blk)] Mov AL,[EAX] Test AL,1 JNZ short .Stop Test AL,2 JNZ short .LoopB Jmp short .NotEndB .NoSInc: ;Get sample ======================== Mov EAX,[_Src(Dec)] Call [%$pInter] ShL EAX,15 Mov [EDI],EAX Cmp byte [dspMix],MIX_FLOAT JNE short .NoStore FMul dword [%$fp32k] FIStP dword [EDI] .NoStore: Add EDI,4 Dec ECX JNZ short .NextSmp Mov [_Src(Idx)],ESI Jmp .Reduce ;Stop playback ------------------------ .Stop: Mov dword [_Src(Blk)],0 Mov dword [%$loop],0 Sub [%$rate],ECX Jmp .Reduce ;Restart sample loop ------------------ .LoopB: Push ESI Mov ESI,[_Src(Loop)] Mov [_Src(Blk)],ESI Jmp short .Decomp ;Move to next BRR block --------------- .NotEndB: Push ESI XOr EAX,EAX Add word [_Src(Blk)],9 SetC AL ;If increase wrapped around 64KB... And AL,[%$inRAM] ; ...and pointer is not in APU RAM, Add [1+_Src(Blk)],AX ; then increase upper word Mov ESI,[_Src(Blk)] ;Decompress sample block -------------- .Decomp: Mov EAX,[16+_Src(Buf)] Mov EDX,[20+_Src(Buf)] Mov [_Src(Buf)-16],EAX Mov [_Src(Buf)-12],EDX Mov EAX,[24+_Src(Buf)] Mov EDX,[28+_Src(Buf)] Mov [_Src(Buf)-8],EAX Mov [_Src(Buf)-4],EDX Push EDI,EBX Mov AL,[ESI] Test byte [%$inRAM],1 JZ short .InRAM Mov EDI,[1+ESI] Mov EDX,[5+ESI] LEA ESI,[_Src(BRR)-1] Mov [1+ESI],EDI Mov [5+ESI],EDX .InRAM: LEA EDI,[_Src(Buf)] MovSX EDX,word [_Src(P1)] MovSX EBX,word [_Src(P2)] Call [pDecomp] Mov EAX,EBX Pop EBX,EDI Mov [_Src(P1)],DX Mov [_Src(P2)],AX Pop ESI Jmp .NoSInc ;===================================== ;Reduce samples .Reduce: Mov EDX,[%$rate] ;EDX = number of samples generated this loop Mov ESI,mixBuf ;ESI-> buffer containing samples Mov EDI,[%$pSrc] ;EDI-> location to store output LEA ECX,[EDX-1] Cmp byte [dspChn],1 Mov AL,[dspSize] JE .Mono Cmp AL,2 JE .S16 Cmp AL,3 JE .S24 .S32: Mov EAX,[ECX*4+ESI] Mov [0+ECX*8+EDI],EAX Mov [4+ECX*8+EDI],EAX Dec ECX JNS short .S32 LEA EDI,[EDX*8+EDI] Jmp .Converted .S24: Mov AX,[2+ECX*4+ESI] Mov [1+EDI],AX Mov [4+EDI],AX Mov AL,[1+ECX*4+ESI] Mov [0+EDI],AL Mov [3+EDI],AL Add EDI,6 Dec ECX JNS short .S24 LEA EDX,[EDX*3] LEA EDI,[EDX*2+EDI] Jmp .Converted .S16: Mov AX,[2+ECX*4+ESI] Mov [0+ECX*4+EDI],AX Mov [2+ECX*4+EDI],AX Dec ECX JNS short .S16 LEA EDI,[EDX*4+EDI] Jmp short .Converted .Mono Cmp AL,1 JE .M8 .M16: Mov AX,[2+ECX*4+ESI] Mov [ECX*2+EDI],AX Dec ECX JNS short .M16 LEA EDI,[EDX*2+EDI] Jmp short .Converted .M8: Mov AL,[3+ECX*4+ESI] XOr AL,80h Mov [ECX+EDI],AL Dec ECX JNS short .M8 Add EDI,EDX Jmp short .Converted .Converted: Mov [%$pSrc],EDI Mov EAX,[%$loop] Test EAX,EAX JG .NextBuf .Done: RetN [%$pSrc] ;========================================= ;Initialize source playback .New: Mov EDX,EAX ;Calculate playback rate ShL EAX,16 ShR EDX,16 Div dword [dspRate] Cmp EAX,100000h JBE short .RateOK Mov EAX,100000h .RateOK: Mov [_Src(Rate)],EAX Mov dword [_Src(Dec)],0 Mov ESI,[%$pSrc] Mov EAX,[%$loop] ;Convert loop block into physical pointer LEA EAX,[EAX*9] Add EAX,ESI Test word [2+%$pSrc],-1 ;Determine if pointer is an index into APU RAM JZ short .InAPURAM Mov CX,[2+pAPURAM] Cmp CX,[2+%$pSrc] JNE short .HavePtr .InAPURAM: Or ESI,[pAPURAM] MovZX EAX,AX Or EAX,[pAPURAM] .HavePtr Mov [_Src(Blk)],ESI Mov [_Src(Loop)],EAX LEA EDI,[_Src(Buf)] Mov [_Src(Idx)],EDI XOr EAX,EAX Mov [EDI-16],EAX ;Erase interpolation buffer Mov [EDI-12],EAX Mov [EDI-8],EAX Mov [EDI-4],EAX Mov ECX,ESI Mov AL,[ESI] ;Decompress first block ShR ECX,16 Cmp CX,[2+pAPURAM] JE short .InAPURAM2 Mov ECX,[1+ESI] Mov EDX,[5+ESI] LEA ESI,[_Src(BRR)-1] Mov [1+ESI],ECX Mov [5+ESI],EDX .InAPURAM2: Call [pDecomp] Mov [srcP1],DX Mov [srcP2],BX Mov EAX,[dspRate] ;Return actual logical sample rate Mul dword [srcRate] ShRD EAX,EDX,16 ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Unpack BRR Block ; ;Decompresses a 9-byte bit-rate reduced block into 16 16-bit PCM samples ; ;In: ; AL = Block header ; ESI-> Sample Block ; EDI-> Output buffer ; EDX = Last sample of previous block ; EBX = Next to last sample ; ;Out: ; ESI-> Next Block ; EDI-> After last sample ; EDX = Last sample ; EBX = Next to last sample ; ;Destroys: ; EAX ALIGN 16 UnpackBRR: Push ECX,EBP MovZX ECX,AL Inc SI ;Inc SI so pointer will wrap around a 16-bit value ;Expand delta values --------------------- RoR ECX,4 Sub CL,12 Mov EBP,0F000F000h Mov CH,8 JA short .Invalid ;Check for invalid range Push EDX Mov EBP,0FFFEFFFEh Neg CL .Unpack: MovSX EAX,byte [ESI] Inc SI Mov DL,AL And EAX,-16 ShL EAX,8 ;Left justify nybbles ShL EDX,12 ShR EAX,CL ;Shift right with sign extension SAR DX,CL And EAX,EBP ;Clear LSB And EDX,EBP Mov [EDI],AX Mov [2+EDI],DX Add EDI,4 Dec CH JNZ short .Unpack Pop EDX Jmp short .Unpacked .Invalid: Mov CL,[ESI] Inc SI ShR CL,4 ;Check sign bit of nybble SbB EAX,EAX ShR CL,4 SbB AX,AX And EAX,EBP ;-4096 for negative nybbles, 0 for positive Mov [EDI],EAX Add EDI,4 Dec CH JNZ short .Invalid ;Apply filters to ADPCM data ------------- .Unpacked: ShLD EAX,ECX,4 ;Get filter type Mov CL,8 ;Decompress 8 bytes (16 nybbles) Test AL,0Ch ;Does block use ADPCM compression? JZ short .NoFilter ; No Sub EDI,32 Test AL,04h JZ short .Filter2 Test AL,08h JNZ .Filter3 ALIGN 16 ;[Delta]+[Smp-1](15/16) ------------------ .Filter1: Mov EBX,EDX Neg EDX SAR EDX,5 LEA EAX,[EDX*2+EBX] ;EAX = ((-p1 >> 4) & ~1) + p1 Add AX,[EDI] ;EAX += delta MovSX EDX,AX Mov [EDI],AX Add EDI,2 Mov EBX,EDX Neg EDX SAR EDX,5 LEA EAX,[EDX*2+EBX] Add AX,[EDI] MovSX EDX,AX Mov [EDI],AX Add EDI,2 Dec CL JNZ short .Filter1 Pop EBP,ECX Ret .NoFilter: MovSX EDX,word [EDI-2] MovSX EBX,word [EDI-4] Pop EBP,ECX Ret ALIGN 16 ;[Delta]+[Smp-1](61/32)-[Smp-2](15/16) --- .Filter2: MovSX EBP,word [EDI] ;Subtract 15/16 of second sample ------ Mov EAX,EBX Neg EBX SAR EAX,5 LEA EAX,[EAX*2+EBX] ;s = ((p2 >> 4) & ~1) + -p2 Mov EBX,EDX ;Add 61/32 of last sample ------------- LEA EAX,[EDX*2+EAX] ;s += 2 * p1 LEA EDX,[EDX*3] Neg EDX Add EAX,EBP ;s += delta SAR EDX,6 LEA EAX,[EDX*2+EAX] ;s += ((-3 * p1) >> 5) & ~1 MovSX EDX,AX ;Clamp 16-bit sample to a 17-bit value Add EAX,65536 ;if s >= -65536 && s <= 65534 SAR EAX,17 JZ short .F2AOK SetS DL ;if s > 65534 then s = -2 MovZX EDX,DL ;if s < -65536 then s = 0 Dec EDX Add EDX,EDX .F2AOK: MovSX EBP,word [2+EDI] Mov [EDI],EDX Mov EAX,EBX Neg EBX SAR EAX,5 LEA EAX,[EAX*2+EBX] Mov EBX,EDX LEA EAX,[EDX*2+EAX] LEA EDX,[EDX*3] Neg EDX Add EAX,EBP SAR EDX,6 LEA EAX,[EDX*2+EAX] MovSX EDX,AX Add EAX,65536 SAR EAX,17 JZ short .F2BOK SetS DL MovZX EDX,DL Dec EDX Add EDX,EDX .F2BOK: Mov [2+EDI],DX Add EDI,4 Dec CL JNZ .Filter2 Pop EBP,ECX Ret ALIGN 16 ;[Delta]+[Smp-1](115/64)-[Smp-2](13/16) -- .Filter3: MovSX EBP,word [EDI] ;Subtract 52/64 of second sample ------ Mov EAX,EBX LEA EBX,[EBX*3] SAR EBX,5 Neg EAX LEA EAX,[EBX*2+EAX] ;s = -p2 + (((p2 * 3) >> 4) & ~1) Mov EBX,EDX ;Add 115/64 of last sample ------------ LEA EAX,[EDX*2+EAX] ;s += p1 * 2 LEA EDX,[EBX*5] LEA EDX,[EBX*8+EDX] Add EAX,EBP Neg EDX SAR EDX,7 LEA EAX,[EDX*2+EAX] ;s += ((-13 * p1) >> 6) & ~1 MovSX EDX,AX Add EAX,65536 SAR EAX,17 JZ short .F3AOK SetS DL MovZX EDX,DL Dec EDX Add EDX,EDX .F3AOK: MovSX EBP,word [2+EDI] Mov [EDI],EDX Mov EAX,EBX LEA EBX,[EBX*3] SAR EBX,5 Neg EAX LEA EAX,[EBX*2+EAX] Mov EBX,EDX LEA EAX,[EDX*2+EAX] LEA EDX,[EBX*5] LEA EDX,[EBX*8+EDX] Add EAX,EBP Neg EDX SAR EDX,7 LEA EAX,[EDX*2+EAX] MovSX EDX,AX Add EAX,65536 SAR EAX,17 JZ short .F3BOK SetS DL MovZX EDX,DL Dec EDX Add EDX,EDX .F3BOK: Mov [2+EDI],DX Add EDI,4 Dec CL JNZ .Filter3 Pop EBP,ECX Ret ; ;SSE version ----------------------------- ; MovQ XMM4,[ESI] ; MovDQA XMM6,XMM4 ; PSRLQ XMM4,4 ; PUnpckLBW XMM4,XMM6 ; ; MovDQA XMM5,XMM4 ; PUnpckLBW XMM4,XMM0 ; PUnpckHBW XMM5,XMM0 ; ; PSLLW XMM4,12 ; PSLLW XMM5,12 ; PSRAW XMM4,XMM7 ; PSRAW XMM5,XMM7 ; ; MovDQA [EDI],XMM4 ; MovDQA [16+EDI],XMM5 ; ;MMX version ----------------------------- ; MovQ MM4,[ESI] ; MovQ MM5,MM4 ; PSRLQ MM4,4 ; MovQ MM6,MM4 ; PUnpckLBW MM4,MM5 ; PUnpckHBW MM6,MM5 ; ; MovQ MM5,MM4 ; MovQ MM7,MM6 ; ; PUnpckLBW MM4,MM0 ; PUnpckHBW MM5,MM0 ; PUnpckLBW MM6,MM0 ; PUnpckHBW MM7,MM0 ; ; PSLLW MM4,12 ; PSLLW MM5,12 ; PSLLW MM6,12 ; PSLLW MM7,12 ; ; PSRAW MM4,MM3 ; PSRAW MM5,MM3 ; PSRAW MM6,MM3 ; PSRAW MM7,MM3 ; ; MovQ [00+EDI],MM4 ; MovQ [08+EDI],MM5 ; MovQ [16+EDI],MM6 ; MovQ [24+EDI],MM7 ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Unpack BRR Block (Old school method) ALIGN 16 UnpackBRROld: Push ECX ;Get range ------------------------------- Mov CL,0CFh Inc SI Sub CL,AL ;CL = 12 - Range (change range from << to >>) SetNC AH ;If result is negative (invalid range) add 3 Dec AH And AH,30h Add CL,AH ShR CL,4 Mov CH,8 Test AL,0Ch JZ short .Filter0 Add CL,10 ;Values will be shifted right from 32-bit values Test AL,8 JZ short .Filter1 Test AL,4 JZ .Filter2 Jmp .Filter3 ALIGN 16 ;[Smp] ----------------------------------- .Filter0: XOr EAX,EAX XOr EDX,EDX Mov AH,[ESI] Mov DH,AH And AH,0F0h ShL DH,4 SAR AX,CL SAR DX,CL Mov [EDI],AX Mov [2+EDI],DX Add EDI,4 Inc SI Dec CH JNZ short .Filter0 MovSX EDX,DX MovSX EBX,AX Pop ECX Ret ALIGN 16 ;[Delta]+[Smp-1](15/16) ------------------ .Filter1: Mov EBX,[ESI] And BL,0F0h ShL EBX,24 SAR EBX,CL Mov EAX,EDX IMul EAX,60 Add EBX,EAX SAR EBX,6 Mov [EDI],EBX Mov EDX,[ESI] ShL EDX,28 SAR EDX,CL Mov EAX,EBX IMul EAX,60 Add EDX,EAX SAR EDX,6 Mov [2+EDI],DX Add EDI,4 Inc SI Dec CH JNZ short .Filter1 Pop ECX Ret ALIGN 16 ;[Delta]+[Smp-1](61/32)-[Smp-2](30/32) --- .Filter2: Mov EAX,[ESI] And AL,0F0h ShL EAX,24 SAR EAX,CL ;Subtract 15/16 of second sample ------ IMul EBX,60 Sub EAX,EBX Mov EBX,EDX ;Add 61/32 of last sample ------------- IMul EDX,122 Add EAX,EDX SAR EAX,6 Mov [EDI],EAX Mov EDX,[ESI] ShL EDX,28 SAR EDX,CL IMul EBX,60 Sub EDX,EBX Mov EBX,EAX IMul EAX,122 Add EDX,EAX SAR EDX,6 Mov [2+EDI],DX Add EDI,4 Inc SI Dec CH JNZ .Filter2 Pop ECX Ret ALIGN 16 ;[Delta]+[Smp-1](115/64)-[Smp-2](52/64) -- .Filter3: Mov EAX,[ESI] And AL,0F0h ShL EAX,24 SAR EAX,CL ;Subtract 13/16 of second sample ------ IMul EBX,52 Sub EAX,EBX Mov EBX,EDX ;Add 115/64 of last sample ------------ IMul EDX,115 Add EAX,EDX SAR EAX,6 Mov [EDI],EAX Mov EDX,[ESI] ShL EDX,28 SAR EDX,CL IMul EBX,52 Sub EDX,EBX Mov EBX,EAX IMul EAX,115 Add EDX,EAX SAR EDX,6 Mov [2+EDI],DX Add EDI,4 Inc SI Dec CH JNZ .Filter3 Pop ECX Ret ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Unpack Sound Source PROC UnpackSrc, pSmp, pBlk, num, opts, prev1, prev2 LOCALS method, blocks, temp USES ALL ;Initialize local variables -------------- Mov EDX,[%$opts] ;Select unpacking method based on option XOr EAX,EAX Test DL,BRR_OLDSMP SetZ AL Dec EAX And EAX,UnpackBRROld-UnpackBRR Add EAX,UnpackBRR Mov [%$method],EAX XOr EAX,EAX Mov [%$blocks],EAX Mov [%$temp],EAX ;Initialize previous samples ------------- LEA EBX,[%$temp] XOr EAX,EAX ;if (prev2==NULL) prev2=&temp Cmp [%$prev2],EAX SetNE AL Dec EAX And EAX,EBX Or [%$prev2],EAX XOr EAX,EAX ;if (prev1==NULL) prev1=&temp Cmp [%$prev1],EAX SetNE AL Dec EAX And EAX,EBX Or [%$prev1],EAX Mov EBX,[%$prev1] ;EDX = *prev1 Mov EDX,[EBX] Mov EBX,[%$prev2] ;EBX = *prev2 Mov EBX,[EBX] Mov ECX,[%$num] Mov EDI,[%$pSmp] Mov AX,[2+%$pBlk] Cmp AX,[2+pAPURAM] JE short .InRAM Test AX,AX JZ short .InRAM ;Decompress samples not in APU RAM ------- .NextC: Mov ESI,[%$pBlk] Add dword [%$pBlk],9 Mov AL,[8+ESI] ;Copy BRR block into 'mixBuf' Mov [8+mixBuf],AL Mov EAX,[4+ESI] Mov [4+mixBuf],EAX Mov EAX,[ESI] Mov [mixBuf],EAX Mov ESI,mixBuf ;Decompress block Call [%$method] Inc dword [%$blocks] Test byte [ESI-9],1 JNZ short .Done Dec ECX JNZ short .NextC Jmp short .Done ;Decompress samples in APU RAM ----------- .InRAM: Mov ESI,[pAPURAM] Mov SI,[%$pBlk] .Next: Mov AL,[ESI] Push EAX Call [%$method] Inc dword [%$blocks] Pop EAX Test EAX,1 ;Have we reached the end block? JNZ short .Done Mov EAX,ESI ;Has sample pointer wrapped around? Sub EAX,[%$pBlk] Cmp AX,-9 JG short .Done Dec ECX JNZ short .Next .Done: ;Return previous samples ----------------- Mov EAX,[%$prev2] Mov [EAX],EBX Mov EBX,[%$prev1] Mov [EBX],EDX Mov EAX,[%$blocks] ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Pack PCM into BRR Block ; ;In: ; pIn -> Samples ; pOut -> Output buffer ; opts = Options ; BRR_LINEAR = Force linear compression (filter 0) ; BRR_LOOP = Set loop flag in block header ; prev1 = Last sample of previous block ; prev2 = Next to last sample ; ;Out: ; EDX = Last sample ; EBX = Next to last sample ; ;Destroys: ; nothing STRUC Blk .brr resb 16 ; [0] .range resb 1 ;[10] resb 3 .diff resd 1 ;[14] .p1 resd 1 ;[18] .p2 resd 1 ;[1C] ENDSTRUC %define _Blk(v) EDI + Blk. %+ v PROC PackBRR, pOut, pIn, opts, prev1, prev2 LOCALS 128,blocks USES ECX,ESI,EDI LEA EDI,[%$blocks] Or dword [32+_Blk(diff)],-1 Or dword [64+_Blk(diff)],-1 Or dword [96+_Blk(diff)],-1 Push EAX ;========================================= ;Linear Compression (Filter 0) ;Find maximum sample value --------------- Mov ESI,[%$pIn] Mov CL,16 XOr EBX,EBX .Next0: MovSX EAX,word [ESI] CDQ XOr EAX,EDX Add ESI,2 BSR EAX,EAX Cmp BL,AL CMovB EBX,EAX Dec CL JNZ short .Next0 Sub ESI,32 Sub BL,2 SetS CL Dec CL And CL,BL Mov [_Blk(range)],CL ;Reduce samples to 4-bits ---------------- XOr EAX,EAX XOr EBX,EBX Mov [_Blk(diff)],EAX Mov CH,16 .Test0: Mov AX,[ESI] And AL,~1 SAR AX,CL Mov [EBX+_Blk(brr)],AL Inc EBX ShL AX,CL Sub AX,[ESI] Add ESI,2 CWD XOr AX,DX Sub AX,DX Add [_Blk(diff)],EAX Dec CH JNZ short .Test0 MovSX EAX,byte [14+_Blk(brr)] ;Create previous samples ShL EAX,CL Mov [_Blk(p2)],EAX MovSX EAX,byte [15+_Blk(brr)] ShL EAX,CL Mov [_Blk(p1)],EAX Add EDI,32 Test byte [%$opts],BRR_LINEAR JNZ .Done ;========================================= ;Filter 1 Mov CL,0 .Range1: Mov ESI,[%$pIn] Mov EAX,[%$prev1] Mov EDX,[%$prev2] Mov [_Blk(p1)],EAX Mov [_Blk(p2)],EDX XOr EBX,EBX Mov [_Blk(diff)],EBX Mov CH,16 .Next1: Mov EDX,[_Blk(p1)] Mov EAX,EDX Neg EDX SAR EDX,5 LEA EDX,[EDX*2+EAX] MovSX EAX,word [ESI] And AL,~1 Sub EAX,EDX SAR EAX,CL Cmp EAX,7 JG .Broken1 Cmp EAX,-8 JL .Broken1 Mov [EBX+_Blk(brr)],AL Inc EBX ShL EAX,CL Add EAX,EDX Cmp EAX,32767 JG .Broken1 Cmp EAX,-32768 JL .Broken1 Mov EDX,[_Blk(p1)] Mov [_Blk(p2)],EDX Mov [_Blk(p1)],EAX MovSX EDX,word [ESI] Add ESI,2 Sub EAX,EDX CDQ XOr EAX,EDX Sub EAX,EDX Add [_Blk(diff)],EAX Dec CH JNZ short .Next1 Mov [_Blk(range)],CL Jmp short .Good1 .Broken1: Inc CL Cmp CL,12 JBE .Range1 Or dword [_Blk(diff)], -1 .Good1: Add EDI,32 ;========================================= ;Filter 2 Mov CL,0 .Range2: Mov ESI,[%$pIn] Mov EAX,[%$prev1] Mov EDX,[%$prev2] Mov [_Blk(p1)],EAX Mov [_Blk(p2)],EDX XOr EBX,EBX Mov [_Blk(diff)],EBX Mov CH,16 .Next2: Mov EAX,[_Blk(p2)] Mov EDX,EAX Neg EAX SAR EDX,5 LEA EDX,[EDX*2+EAX] Mov [_Blk(p2)],EDX Mov EAX,[_Blk(p1)] Mov EDX,EAX Add EAX,EAX Neg EDX LEA EDX,[EDX*2+EDX] SAR EDX,6 LEA EDX,[EDX*2+EAX] MovSX EAX,word [ESI] And AL,~1 Sub EAX,EDX Sub EAX,[_Blk(p2)] SAR EAX,CL Cmp EAX,7 JG .Broken2 Cmp EAX,-8 JL .Broken2 Mov [EBX+_Blk(brr)],AL Inc EBX ShL EAX,CL Add EAX,EDX Add EAX,[_Blk(p2)] Cmp EAX,32767 JG .Broken2 Cmp EAX,-32768 JL .Broken2 Mov EDX,[_Blk(p1)] Mov [_Blk(p2)],EDX Mov [_Blk(p1)],EAX MovSX EDX,word [ESI] Add ESI,2 Sub EAX,EDX CDQ XOr EAX,EDX Sub EAX,EDX Add [_Blk(diff)],EAX Dec CH JNZ .Next2 Mov [_Blk(range)],CL Jmp short .Good2 .Broken2: Inc CL Cmp CL,12 JBE .Range2 Or dword [_Blk(diff)],-1 .Good2: Add EDI,32 ;========================================= ;Filter 3 Mov CL,0 .Range3: Mov ESI,[%$pIn] Mov EAX,[%$prev1] Mov EDX,[%$prev2] Mov [_Blk(p1)],EAX Mov [_Blk(p2)],EDX XOr EBX,EBX Mov [_Blk(diff)],EBX Mov CH,16 .Next3: Mov EDX,[_Blk(p2)] Mov EAX,[_Blk(p2)] LEA EDX,[EDX*3] SAR EDX,5 Neg EAX LEA EAX,[EDX*2+EAX] Mov [_Blk(p2)],EAX Mov EDX,[_Blk(p1)] LEA EAX,[EDX*5] LEA EAX,[EDX*8+EAX] Add EDX,EDX Neg EAX SAR EAX,7 LEA EDX,[EAX*2+EDX] MovSX EAX,word [ESI] And AL,~1 Sub EAX,EDX Sub EAX,[_Blk(p2)] SAR EAX,CL Cmp EAX,7 JG .Broken3 Cmp EAX,-8 JL .Broken3 Mov [EBX+_Blk(brr)],AL Inc EBX ShL EAX,CL Add EAX,EDX Add EAX,[_Blk(p2)] Cmp EAX,32767 JG .Broken3 Cmp EAX,-32768 JL .Broken3 Mov EDX,[_Blk(p1)] Mov [_Blk(p2)],EDX Mov [_Blk(p1)],EAX MovSX EDX,word [ESI] Add ESI,2 Sub EAX,EDX CDQ XOr EAX,EDX Sub EAX,EDX Add [_Blk(diff)],EAX Dec CH JNZ .Next3 Mov [_Blk(range)],CL Jmp short .Good3 .Broken3: Inc CL Cmp CL,12 JBE .Range3 Or dword [_Blk(diff)],-1 .Good3: .Done: ;========================================= ;Store Final Block ;Find best filter ------------------------ XOr EDX,EDX ;EDX = Filter LEA ESI,[%$blocks] ;ESI -> First filter block Mov EAX,[Blk.diff+ESI] ;EAX = Total difference for first filter Mov EBX,ESI ;EBX -> Array of block stuctures Mov CL,1 .NextB: Add EBX,32 Cmp EAX,[Blk.diff+EBX] ;Is this a better filter? JBE short .Better ; Nope, keep what we have Mov EAX,[Blk.diff+EBX] Mov ESI,EBX Mov DL,CL .Better: Inc CL Cmp CL,4 JB short .NextB ;Create header byte ---------------------- Mov EDI,[%$pOut] Mov AL,[Blk.range+ESI] ;Get BRR range ShL AL,4 ShL DL,2 ;Get filter number Or AL,DL Mov DL,[%$opts] ;Set looping flag And DL,BRR_LOOP Or AL,DL Mov [EDI],AL ;Store header byte Inc EDI ;Pack bytes into nybbles ----------------- Mov ECX,7 .Copy: Mov AL,[ECX*2+ESI] Mov DL,[1+ECX*2+ESI] ShL AL,4 And DL,0Fh ;Remove sign bits Or AL,DL Mov [ECX+EDI],AL Dec ECX JNS short .Copy ;Return previous samples ----------------- Mov EDX,[Blk.p1+ESI] Mov EBX,[Blk.p2+ESI] Pop EAX ENDP ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Pack PCM Samples into BRR Blocks ;-------------------------------------------- ;Copies samples from an input buffer into a temporary buffer. If the input buffer doesn't contain ;enough samples to fill the temp, the remaining samples in the temp buffer are filled with silence. ; ;In: ; EDI-> Buffer to store samples in ; ESI-> Buffer containing samples ; EAX = Number of samples in input buffer ; ;Out: ; EAX = Number of samples left in input buffer ; ESI-> Next samples PROC CopySmp USES ECX,EDX,EDI Push EAX Sub EAX,16 ;if (EAX >= 16) EAX = 16 CDQ And EAX,EDX Add EAX,16 Mov ECX,EAX ;Copy samples to temp buffer Mov EDX,EAX Rep MovSW Mov EAX,ECX ;Fill remaning samples with 0 Or ECX,16 Sub ECX,EDX Rep StoSW Pop EAX Sub EAX,EDX ;Subtract copied samples from remaining count ENDP ;-------------------------------------------- ;Interpolates samples using sinc ; ;In: ; pDest -> Buffer to store sixteen interpolated samples ; pSrc -> Buffer containing samples to interpolate from ; pInter-> Buffer containing eight samples used for interpolation function ; rate = Rate of sample increase ; delta = Interpolation delta ; ;Out: ; EAX = New delta ; EBX = New input pointer PROC Interpolate, pDest, pSrc, pInter, rate, delta LOCALS cnt USES ECX,EDX,ESI,EDI Mov EDX,[%$rate] Mov EDI,[%$pDest] Test EDX,EDX JZ short .Copy Mov EBX,[%$pSrc] Mov ESI,[%$pInter] Mov byte [%$cnt],16 .Next: Add [%$delta],EDX JNC short .NotNext Add EBX,2 Mov EAX,[2+ESI] Mov ECX,[6+ESI] Mov [ESI],EAX Mov [4+ESI],ECX Mov EAX,[10+ESI] Mov CX,[14+ESI] Mov [8+ESI],EAX Mov [12+ESI],ECX Mov AX,[EBX] Mov [14+ESI],AX .NotNext: MovZX EAX,word [2+%$delta] Call SincF FIStP word [EDI] Add EDI,2 Dec byte [%$cnt] JNZ short .Next RetS [%$delta] .Copy: Mov ESI,[%$pSrc] LEA ECX,[EDX+8] Mov EAX,[%$delta] Rep MovSD Mov EBX,ESI ENDP PROC PackSrc, pBlk, pSmp, pLen, pLoop, opts, prev1, prev2 LOCALS len,loop,delta,rate,p1,p2,32,smp,32,inter USES ALL ;Clear temp buffers ---------------------- XOr EAX,EAX LEA EDI,[%$smp] LEA ECX,[EAX+8] Rep StoSD Mov [%$inter],EAX ;Reset the first three samples in the interpolation Mov [4+%$inter],EAX ; buffer incase there's no preloop section. ;Get previous samples -------------------- Mov EBX,[%$prev2] Mov EDX,[%$prev1] Test EBX,EBX ;If prev1 or prev2 is NULL, use default sample values SetZ CH Test EDX,EDX SetZ CL Test ECX,ECX JNZ short .NoPrev Mov EBX,[EBX] Mov EDX,[EDX] Mov [%$p2],EBX Mov [%$p1],EDX Or byte [%$opts],BRR_CONT Jmp short .GotPrev .NoPrev: Mov [%$p2],EAX Mov [%$p1],EAX Mov [%$prev1],EAX .GotPrev: ;Copy input length ----------------------- Mov EBX,[%$pLen] Mov EDX,[EBX] ;EDX = length of input Test EDX,EDX JZ .Error Mov [%$len],EDX Mov [EBX],EAX ;Set output length to 0 ;Create initial block of silence --------- Test byte [%$opts],BRR_NOINIT|BRR_CONT JNZ short .NoInit Mov EDI,[%$pBlk] Mov byte [EDI],0C0h Mov [1+EDI],EAX Mov [5+EDI],EAX Add dword [%$pBlk],9 Inc dword [EBX] ;Increase output length .NoInit: ;Change options based on prev1 ---------- Test byte [%$opts],BRR_CONT JZ short .NoCont Mov [%$pLoop],EAX ;Ignore pLoop if continuing compression Jmp short .Cont .NoCont: Or byte [%$opts],BRR_END ;Force end block flag to be set .Cont: Or EAX,[%$pLoop] JNZ short .Loops ;One-shot sounds ====================== LEA EAX,[EDX+15] ;Store final length ShR EAX,4 Add [EBX],EAX Mov ESI,[%$pSmp] ;Location of input samples LEA EDI,[%$smp] ;Location to store temp samples (used by CopySmp) Mov ECX,[%$opts] Mov AL,BRR_CONT|BRR_NOINIT And AL,CL Cmp AL,BRR_CONT|BRR_NOINIT JE short .NoLinearO Cmp AL,BRR_NOINIT JNE short .NoLinearO Or CL,BRR_LINEAR ;Force linear compression on the first block .NoLinearO: Mov EAX,EDX Mov EDX,[%$p1] Mov EBX,[%$p2] .NextO: Test EAX,EAX JZ .Done Call CopySmp Call PackBRR,[%$pBlk],EDI,ECX,EDX,EBX Add dword [%$pBlk],9 Mov ECX,[%$opts] Jmp short .NextO .Loops: ;Looping sounds ======================= Mov [%$loop],EAX Mov ECX,EDX Sub ECX,EAX ;Loop must start before end of input JBE .Error Cmp ECX,16 ;Looping section must be at least 16 samples JL .Error LEA EBX,[EAX+15] ShR EBX,4 Mov ESI,[%$pLen] Mov EDI,[%$pLoop] Add EBX,[ESI] Mov [EDI],EBX ;pLoop -> Loop starting block Add ECX,15 ShR ECX,4 Add EBX,ECX Mov [ESI],EBX Mov ECX,[%$opts] Test CL,BRR_NOINIT JE short .NoLinearL Or ECX,BRR_LINEAR ;Force linear compression on the first block .NoLinearL: ;Preloop section ---------------------- Test EAX,EAX JZ short .NoPreLoop XOr EDI,EDI ;If preloop is not a mutiple of 16, insert silence Sub EDI,EAX And EDI,0Fh LEA EDI,[EDI*2+%$smp] Mov ESI,[%$pSmp] Push ECX ;Copy samples to fill 16-sample buffer Mov ECX,EAX And EAX,-16 And ECX,0Fh Rep MovSW Pop ECX LEA EDI,[%$smp] Mov EDX,[%$p1] Mov EBX,[%$p2] Jmp short .StartPL .NextPL: ;Pack preloop samples Call CopySmp .StartPL: Call PackBRR,[%$pBlk],EDI,ECX,EDX,EBX Add dword [%$pBlk],9 Mov ECX,[%$opts] Test EAX,EAX JNZ short .NextPL Mov [%$pSmp],ESI ;Save values Mov [%$p1],EDX Mov [%$p2],EBX Mov EAX,[ESI-6] Mov EDX,[ESI-2] Mov [%$inter],EAX Mov [4+%$inter],DX .NoPreLoop: ;Looping section ---------------------- Mov ESI,[%$pSmp] Mov EAX,[ESI] Mov EDX,[4+ESI] Mov BX,[8+ESI] Mov [6+%$inter],EAX Mov [10+%$inter],EDX Mov [14+%$inter],BX XOr EDX,EDX Mov [%$delta],EDX Mov EAX,[%$len] Sub EAX,[%$loop] Test AL,0Fh ;If the loop length is a multiple of 16, no JZ short .NoInter ; interpolation is needed LEA EBX,[EAX+15] Mov EDX,EAX And EBX,-16 ;EBX = length rounded up to next 16 sample boundary XOr EAX,EAX ;EDX:EAX = length << 32 Div EBX Mov EDX,EAX ;EDX = [0.32] interpolation rate .NoInter: Mov [%$rate],EDX LEA EDI,[%$smp] LEA ESI,[%$inter] XOr EAX,EAX Sub EAX,EDX Mov EBX,[%$pSmp] .NextL: Call Interpolate,EDI,[%$pSmp],ESI,[%$rate],[%$delta] Mov [%$pSmp],EBX Mov [%$delta],EAX Call PackBRR,[%$pBlk],EDI,ECX,[%$p1],[%$p2] Mov [%$p1],EDX Mov [%$p2],EBX Add dword [%$pBlk],9 Mov ECX,[%$opts] Jmp short .NextL Mov EDI,[%$pBlk] Or byte [EDI-9],BRR_LOOP ;TODO: Move this to the end of the function .Done: ;Return previous samples ----------------- Mov CL,[%$opts] Test CL,BRR_CONT JZ short .NoPrevs Mov EAX,[%$prev1] Mov [EAX],EDX Mov EAX,[%$prev2] Mov [EAX],EBX .NoPrevs: ;Set end block flag ---------------------- Mov EAX,[%$pBlk] ;EAX -> end of block buffer Test CL,BRR_END ;Check for user to specify end flag Retc Z Or byte [EAX-9],BRR_END .Error: ENDP PackSrc ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;VMax to Decible PROC VMax2dBf, pList LOCALS ftemp USES ECX,EDX,ESI,EDI Mov EDI,[%$pList] ;EDI -> Output array Mov ESI,mix+vMaxL ;ESI -> vMax values XOr EAX,EAX Mov CL,8 ;Eight voices Mov dword [%$ftemp],40C00000h ;6.0 FLd dword [%$ftemp] Mov dword [%$ftemp],42B40000h ;90.0 FLd dword [%$ftemp] Mov EDX,42C00000h ;EDX = -96.0 (negative infinity decibles) ;Floating-point Format =================== ; ; dB = 6.0 (Log vMax) - 90.0 ; 2 ;Voice output ---------------------------- .Voice: Mov [EDI],EDX ;Default to -96.0 (-inf dB) Cmp EAX,[ESI] ;Is vMaxL > -inf dB? JE short .NoRangeL ; No, Move onto vMaxR FLd ST1 FILd dword [ESI] ;Load vMaxL into FPU FYL2X ;Compute 6.0*Log2(vMaxL) FSub ST1 ;Subtract 90.0 from result FStP dword [EDI] ;Store dB as floating-point Mov [ESI],EAX ;Reset vMaxL .NoRangeL: Mov [4+EDI],EDX ;Do the same thing for vMaxR Cmp EAX,[4+ESI] JE short .NoRangeR FLd ST FILd dword [4+ESI] FYL2X FSub ST1 FStP dword [4+EDI] Mov [4+ESI],EAX .NoRangeR: Add EDI,8 Sub ESI,-128 Dec CL JNZ short .Voice ;Main output (mMax) ---------------------- Mov ESI,mMaxL Mov [EDI],EDX Cmp EAX,[ESI] JE short .NoRangeML FLd ST1 FILd dword [ESI] FYL2X FSub ST1 FStP dword [EDI] Mov [ESI],EAX .NoRangeML: Mov ESI,mMaxR Mov [4+EDI],EDX Cmp EAX,[ESI] JE short .NoRangeMR FLd ST1 FILd dword [ESI] FYL2X FSub ST1 FStP dword [4+EDI] Mov [ESI],EAX .NoRangeMR: FStP ST FStP ST ENDP VMax2dBf PROC VMax2dBi, pList LOCALS ftemp USES ECX,EDX,ESI,EDI Mov EDI,[%$pList] ;EDI -> Output array Mov ESI,mix+vMaxL ;ESI -> vMax values XOr EAX,EAX Mov CL,8 ;Eight voices Mov dword [%$ftemp],40C00000h ;6.0 FLd dword [%$ftemp] Mov dword [%$ftemp],47800000h ;65536.0 FLd dword [%$ftemp] ;Integer format ========================== ; ; dB = (6.0 (Log vMax) + 6.0) * 65536 ; 2 ;Voice output ---------------------------- .Voice: Mov [EDI],EAX ;Default to 0 (negative infinity) Cmp EAX,[ESI] ;Is vMaxL > -inf dB? JE short .NoRangeL ; No, Move onto vMaxR FLd ST1 FILd dword [ESI] ;Load vMaxL into FPU FYL2X ;Compute 6.0*Log2(vMaxL) FAdd ST,ST2 ;Add 6.0 FMul ST1 ;Multiply by 65536 FIStP dword [EDI] ;Store dB as 16.16 integer Mov [ESI],EAX ;Reset vMaxL .NoRangeL: Mov [4+EDI],EAX ;Do the same thing for vMaxR Cmp EAX,[4+ESI] JE short .NoRangeR FLd ST1 FILd dword [4+ESI] FYL2X FAdd ST,ST2 FMul ST1 FIStP dword [4+EDI] Mov [4+ESI],EAX .NoRangeR: Add EDI,8 Sub ESI,-128 Dec CL JNZ short .Voice ;Main output (mMax) ---------------------- Mov ESI,mMaxL Mov [EDI],EAX Cmp EAX,[ESI] JE short .NoRangeML FLd ST1 FILd dword [ESI] FYL2X FAdd ST,ST2 FMul ST1 FIStP dword [EDI] Mov [ESI],EAX .NoRangeML: Mov ESI,mMaxR Mov [4+EDI],EAX Cmp EAX,[ESI] JE short .NoRangeMR FLd ST1 FILd dword [ESI] FYL2X FAdd ST,ST2 FMul ST1 FIStP dword [4+EDI] Mov [ESI],EAX .NoRangeMR: FStP ST FStP ST ENDP VMax2dBi