%define SU_LENGTH_IN_SAMPLES 2703680 %define SU_SAMPLE_RATE 44100 %define SU_BPM 137. ;------------------------------------------------------------------------------- ; unit struct ;------------------------------------------------------------------------------- struc su_unit .state resd 8 .ports resd 8 .size: endstruc ;------------------------------------------------------------------------------- ; voice struct ;------------------------------------------------------------------------------- struc su_voice .note resd 1 .sustain resd 1 .inputs resd 8 .reserved resd 6 ; this is done to so the whole voice is 2^n long, see polyphonic player .workspace resb 63 * su_unit.size .size: endstruc ;------------------------------------------------------------------------------- ; synthworkspace struct ;------------------------------------------------------------------------------- struc su_synthworkspace .curvoices resb 32 ; these are used by the multitrack player to store which voice is playing on which track .left resd 1 .right resd 1 .aux resd 6 ; 3 auxiliary signals .voices resb 32 * su_voice.size .size: endstruc ;------------------------------------------------------------------------------- ; su_delayline_wrk struct ;------------------------------------------------------------------------------- struc su_delayline_wrk .dcin resd 1 .dcout resd 1 .filtstate resd 1 .buffer resd 65536 .size: endstruc ;------------------------------------------------------------------------------- ; su_sample_offset struct ;------------------------------------------------------------------------------- struc su_sample_offset ; length conveniently 8 bytes, so easy to index .start resd 1 .loopstart resw 1 .looplength resw 1 .size: endstruc ;------------------------------------------------------------------------------- ; Uninitialized data: The synth object ;------------------------------------------------------------------------------- section .synth_object bss align=256 su_synth_obj: resb su_synthworkspace.size resb 40*su_delayline_wrk.size ;------------------------------------------------------------------------------- ; su_render_song function: the entry point for the synth ;------------------------------------------------------------------------------- ; Has the signature su_render_song(void *ptr), where ptr is a pointer to ; the output buffer. Renders the compile time hard-coded song to the buffer. ; Stack: output_ptr ;------------------------------------------------------------------------------- section .su_render_song code align=1 global _su_render_song@4 _su_render_song@4: pushad ; Stack: edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr xor eax, eax push 20752 ; Stack: VoiceTrackBitmask, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr push 1 ; Stack: RandSeed, VoiceTrackBitmask, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr push eax ; Stack: GlobalTick, RandSeed, VoiceTrackBitmask, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr su_render_rowloop: ; loop through every row in the song push eax ; Stack: Row, GlobalTick, RandSeed, VoiceTrackBitmask, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr call su_update_voices ; update instruments for the new row xor eax, eax ; ecx is the current sample within row su_render_sampleloop: ; loop through every sample in the row push eax ; Stack: Sample, Row, GlobalTick, RandSeed, VoiceTrackBitmask, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr push 4572 ; Stack: PolyphonyBitmask, Sample, Row, GlobalTick, RandSeed, VoiceTrackBitmask, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr ; does the next voice reuse the current opcodes? push 17 ; Stack: VoicesRemain, PolyphonyBitmask, Sample, Row, GlobalTick, RandSeed, VoiceTrackBitmask, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr mov edx, dword su_synth_obj ; edx points to the synth object mov ebx, dword su_patch_opcodes ; COM points to vm code mov esi, dword su_patch_operands ; VAL points to unit params mov ecx, dword su_synth_obj + su_synthworkspace.size - su_delayline_wrk.filtstate lea ebp, [edx + su_synthworkspace.voices] ; WRK points to the first voice call su_run_vm ; run through the VM code pop eax ; eax = VoicesRemain, Stack: PolyphonyBitmask, Sample, Row, GlobalTick, RandSeed, VoiceTrackBitmask, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr pop eax ; eax = PolyphonyBitmask, Stack: Sample, Row, GlobalTick, RandSeed, VoiceTrackBitmask, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr mov esi, [esp + 56] ; esi points to the output buffer mov edi, dword su_synth_obj+su_synthworkspace.left mov ecx, 2 output_sound16bit_loop: ; loop over two channels, left & right fld dword [edi] call su_clip fmul dword [FCONST_32767_0] push eax fistp dword [esp] pop eax mov word [esi],ax ; // store integer converted right sample xor eax,eax stosd add esi,2 loop output_sound16bit_loop mov [esp + 56], esi ; save esi back to stack ; *ptr++ = left, *ptr++ = right pop eax ; eax = Sample, Stack: Row, GlobalTick, RandSeed, VoiceTrackBitmask, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr inc dword [esp + 4] ; increment global time, used by delays inc eax cmp eax, 4828 jl su_render_sampleloop pop eax ; eax = Row, Stack: GlobalTick, RandSeed, VoiceTrackBitmask, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr ; Stack: pushad ptr inc eax cmp eax, 560 jl su_render_rowloop ; rewind the stack the entropy of multiple pop eax is probably lower than add pop eax ; eax = GlobalTick, Stack: RandSeed, VoiceTrackBitmask, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr pop eax ; eax = RandSeed, Stack: VoiceTrackBitmask, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr pop eax ; eax = VoiceTrackBitmask, Stack: edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr popad ; Popped: eax, ecx, edx, ebx, esp, ebp, esi, edi. Stack: retaddr_su_render_song, OutputBufPtr ret 4 ;------------------------------------------------------------------------------- ; su_update_voices function: polyphonic & chord implementation ;------------------------------------------------------------------------------- ; Input: eax : current row within song ; Dirty: pretty much everything ;------------------------------------------------------------------------------- section .su_update_voices code align=1 su_update_voices: ; The more complicated implementation: one track can trigger multiple voices xor edx, edx mov ebx, 16 ; we could do xor ebx,ebx; mov bl,PATTERN_SIZE, but that would limit patternsize to 256... div ebx ; eax = current pattern, edx = current row in pattern lea esi, [su_tracks+eax] ; esi points to the pattern data for current track xor eax, eax ; eax is the first voice of next track xor ebx, ebx ; ebx is the first voice of current track mov ebp, dword su_synth_obj ; ebp points to the current_voiceno array su_update_voices_trackloop: movzx eax, byte [esi] ; eax = current pattern imul eax, 16 ; eax = offset to current pattern data movzx eax,byte [su_patterns + eax + edx] ; eax = note push edx ; Stack: ptrnrow xor edx, edx ; edx=0 mov ecx, ebx ; ecx=first voice of the track to be done su_calculate_voices_loop: ; do { bt dword [esp + 16 + 4],ecx ; test voicetrack_bitmask// notice that the incs don't set carry inc edx ; edx++ // edx=numvoices inc ecx ; ecx++ // ecx=the first voice of next track jc su_calculate_voices_loop ; } while bit ecx-1 of bitmask is on push ecx ; Stack: next_instr ptrnrow cmp al, 1 ; anything but hold causes action je short su_update_voices_nexttrack mov cl, byte [ebp] mov edi, ecx add edi, ebx shl edi, 12 ; each unit = 64 bytes and there are 1<= num_voices) jl su_update_voices_skipreset xor ecx,ecx ; curvoice = 0 su_update_voices_skipreset: mov byte [ebp],cl add ecx, ebx shl ecx, 12 ; each unit = 64 bytes and there are 1<<6 units + small header lea edi,[su_synth_obj + su_synthworkspace.voices + ecx] stosd ; save note stosd ; save release mov ecx, (su_voice.size - su_voice.inputs)/4 xor eax, eax rep stosd ; clear the workspace of the new voice, retriggering oscillators su_update_voices_nexttrack: pop ebx ; ebx=first voice of next instrument, Stack: ptrnrow pop edx ; edx=patrnrow add esi, 35 inc ebp cmp ebp,su_synth_obj + 12 jl su_update_voices_trackloop ret ;------------------------------------------------------------------------------- ; su_run_vm function: runs the entire virtual machine once, creating 1 sample ;------------------------------------------------------------------------------- ; Input: su_synth_obj.left : Set to 0 before calling ; su_synth_obj.right : Set to 0 before calling ; _CX : Pointer to delay workspace (if needed) ; _DX : Pointer to synth object ; COM : Pointer to opcode stream ; VAL : Pointer to operand stream ; WRK : Pointer to the last workspace processed ; Output: su_synth_obj.left : left sample ; su_synth_obj.right : right sample ; Dirty: everything ;------------------------------------------------------------------------------- section .su_run_vm code align=1 su_run_vm: pushad ; Stack: edi, OperandStream, Voice, esp, OpcodeStream, Synth, DelayWorkSpace, eax, retaddr_su_run_vm, VoicesRemain, PolyphonyBitmask, Sample, Row, GlobalTick, RandSeed, VoiceTrackBitmask, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr su_run_vm_loop: ; loop until all voices done movzx edi, byte [ebx] ; edi = command byte inc ebx ; move to next instruction add ebp, su_unit.size ; move WRK to next unit shr edi, 1 ; shift out the LSB bit = stereo bit je su_run_vm_advance ; the opcode is zero, jump to advance mov edx, [esp + 8] ; reset INP to point to the inputs part of voice pushf ; push flags to save carry = stereo bit add edx, su_voice.inputs xor ecx, ecx ; counter = 0 xor eax, eax ; clear out high bits of eax, as lodsb only sets al su_transform_operands_loop: cmp cl, byte [su_vm_transformcounts-1+edi] ; compare the counter to the value in the param count table je su_transform_operands_out lodsb ; load the operand from VAL stream push eax ; push it to memory so FPU can read it fild dword [esp] ; load the operand value to FPU stack fmul dword [FCONST_0_00781250] ; divide it by 128 (0 => 0, 128 => 1.0) fadd dword [ebp+su_unit.ports+ecx*4] ; add the modulations in the current workspace fstp dword [edx+ecx*4] ; store the modulated value in the inputs section of voice xor eax, eax mov dword [ebp+su_unit.ports+ecx*4], eax ; clear out the modulation ports pop eax inc ecx jmp su_transform_operands_loop su_transform_operands_out: popf ; pop flags for the carry bit = stereo bit call [su_vm_jumptable-4+edi*4] ; call the function corresponding to the instruction jmp su_run_vm_loop su_run_vm_advance: mov ebp, [esp + 8] ; WRK points to start of current voice add ebp, su_voice.size ; move to next voice mov [esp + 8], ebp ; update the pointer in the stack to point to the new voice mov ecx, [esp + 36] ; ecx = how many voices remain to process dec ecx ; decrement number of voices to process bt dword [esp + 40], ecx ; if voice bit of su_polyphonism not set jnc su_op_advance_next_instrument ; goto next_instrument mov esi, [esp + 4] ; if it was set, then repeat the opcodes for the current voice mov ebx, [esp + 16] su_op_advance_next_instrument: mov [esp + 4], esi ; save current VAL as a checkpoint mov [esp + 16], ebx ; save current COM as a checkpoint su_op_advance_finish: mov [esp + 36], ecx jne su_run_vm_loop ; ZF was set by dec ecx popad ; Popped: eax, ecx = DelayWorkSpace, edx = Synth, ebx = OpcodeStream, esp, ebp = Voice, esi = OperandStream, edi. Stack: retaddr_su_run_vm, VoicesRemain, PolyphonyBitmask, Sample, Row, GlobalTick, RandSeed, VoiceTrackBitmask, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr ret ;------------------------------------------------------------------------------- ; ADD opcode: add the two top most signals on the stack ;------------------------------------------------------------------------------- ; Mono: a b -> a+b b ;------------------------------------------------------------------------------- section .su_op_add code align=1 su_op_add: fadd st1 ret ;------------------------------------------------------------------------------- ; ADDP opcode: add the two top most signals on the stack and pop ;------------------------------------------------------------------------------- ; Mono: a b -> a+b ; Stereo: a b c d -> a+c b+d ;------------------------------------------------------------------------------- section .su_op_addp code align=1 su_op_addp: faddp st1, st0 ret ;------------------------------------------------------------------------------- ; LOADNOTE opcode: load the current note, scaled to [-1,1] ;------------------------------------------------------------------------------- ; Mono: (empty) -> n, where n is the note ;------------------------------------------------------------------------------- section .su_op_loadnote code align=1 su_op_loadnote: fild dword [edx-su_voice.inputs+su_voice.note] fmul dword [FCONST_0_00781250] ; s=n/128.0 fsub dword [FCONST_0_500000] ; s-.5 fadd st0, st0 ; 2*s-1 ret ;------------------------------------------------------------------------------- ; MULP opcode: multiply the two top most signals on the stack and pop ;------------------------------------------------------------------------------- ; Mono: a b -> a*b ; Stereo: a b c d -> a*c b*d ;------------------------------------------------------------------------------- section .su_op_mulp code align=1 su_op_mulp: jnc su_op_mulp_mono fmulp st2, st0 fmulp st2, st0 ret su_op_mulp_mono: fmulp st1 ret ;------------------------------------------------------------------------------- ; FILTER opcode: perform low/high/band-pass/notch etc. filtering on the signal ;------------------------------------------------------------------------------- ; Mono: x -> filtered(x) ; Stereo: l r -> filtered(l) filtered(r) ;------------------------------------------------------------------------------- section .su_op_filter code align=1 su_op_filter: lodsb ; load the flags to al call su_effects_stereohelper fld dword [edx + 4] ; r x fld dword [edx]; f r x fmul st0, st0 ; f2 x (square the input so we never get negative and also have a smoother behaviour in the lower frequencies) fst dword [ebp+12] ; f2 r x fmul dword [ebp+8] ; f2*b r x fadd dword [ebp] ; f2*b+l r x fst dword [ebp] ; l'=f2*b+l r x fsubp st2, st0 ; r x-l' fmul dword [ebp+8] ; r*b x-l' fsubp st1, st0 ; x-l'-r*b fst dword [ebp+4] ; h'=x-l'-r*b fmul dword [ebp+12] ; f2*h' fadd dword [ebp+8] ; f2*h'+b fstp dword [ebp+8] ; b'=f2*h'+b fldz ; 0 test al, byte 0x40 jz short su_op_filter_skiplowpass fadd dword [ebp] su_op_filter_skiplowpass: test al, byte 0x10 jz short su_op_filter_skiphighpass fadd dword [ebp+4] su_op_filter_skiphighpass: test al, byte 0x08 jz short su_op_filter_skipnegbandpass fsub dword [ebp+8] su_op_filter_skipnegbandpass: test al, byte 0x04 jz short su_op_filter_skipneghighpass fsub dword [ebp+4] su_op_filter_skipneghighpass: ret ;------------------------------------------------------------------------------- ; PAN opcode: pan the signal ;------------------------------------------------------------------------------- ; Mono: s -> s*(1-p) s*p ; Stereo: l r -> l*(1-p) r*p ; ; where p is the panning in [0,1] range ;------------------------------------------------------------------------------- section .su_op_pan code align=1 su_op_pan: jc su_op_pan_do ; this time, if this is mono op... fld st0 ; ...we duplicate the mono into stereo first su_op_pan_do: fld dword [edx] ; p l r fld1 ; 1 p l r fsub st1 ; 1-p p l r fmulp st2 ; p (1-p)*l r fmulp st2 ; (1-p)*l p*r ret ;------------------------------------------------------------------------------- ; DELAY opcode: adds delay effect to the signal ;------------------------------------------------------------------------------- ; Mono: perform delay on ST0, using delaycount delaylines starting ; at delayindex from the delaytable ; Stereo: perform delay on ST1, using delaycount delaylines starting ; at delayindex + delaycount from the delaytable (so the right delays ; can be different) ;------------------------------------------------------------------------------- section .su_op_delay code align=1 su_op_delay: lodsw ; al = delay index, ah = delay count pushad ; Stack: edi, DelayVal, ebp, esp, DelayCom, edx, ecx, eax, retaddr_su_op_delay, edi, OperandStream, Voice, esp, OpcodeStream, Synth, DelayWorkSpace, eax, retaddr_su_run_vm, VoicesRemain, PolyphonyBitmask, Sample, Row, GlobalTick, RandSeed, VoiceTrackBitmask, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr movzx ebx, al lea ebx,[su_delay_times + ebx*2] ; BX now points to the right position within delay time table movzx esi, word [esp + 88] ; notice that we load word, so we wrap at 65536 mov ecx, dword [esp + 60] ; ebp is now the separate delay workspace, as they require a lot more space jnc su_op_delay_mono push eax ; save _ah (delay count) fxch ; r l call su_op_delay_do ; D(r) l process delay for the right channel pop eax ; restore the count for second run fxch ; l D(r) su_op_delay_mono: ; flow into mono delay call su_op_delay_do ; when stereo delay is not enabled, we could inline this to save 5 bytes, but I expect stereo delay to be farely popular so maybe not worth the hassle mov dword [esp + 60],ecx ; move delay workspace pointer back to stack. popad ; Popped: eax, ecx, edx, ebx = DelayCom, esp, ebp, esi = DelayVal, edi. Stack: retaddr_su_op_delay, edi, OperandStream, Voice, esp, OpcodeStream, Synth, DelayWorkSpace, eax, retaddr_su_run_vm, VoicesRemain, PolyphonyBitmask, Sample, Row, GlobalTick, RandSeed, VoiceTrackBitmask, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr ret ;------------------------------------------------------------------------------- ; su_op_delay_do: executes the actual delay ;------------------------------------------------------------------------------- ; Pseudocode: ; q = dr*x ; for (i = 0;i < count;i++) ; s = b[(t-delaytime[i+offset])&65535] ; q += s ; o[i] = o[i]*da+s*(1-da) ; b[t] = f*o[i] +p^2*x ; Perform dc-filtering q and output q ;------------------------------------------------------------------------------- section .su_op_delay_do code align=1 su_op_delay_do: ; x y fld st0 fmul dword [edx] ; p*x y fmul dword [edx] ; p*p*x y fxch ; y p*p*x fmul dword [edx + 4] ; dr*y p*p*x su_op_delay_loop: mov edi, esi sub di, word [ebx] ; we perform the math in 16-bit to wrap around fld dword [ecx+su_delayline_wrk.buffer+edi*4]; s dr*y p*p*x, where s is the sample from delay buffer fadd st1, st0 ; s dr*y+s p*p*x (add comb output to current output) fld1 ; 1 s dr*y+s p*p*x fsub dword [edx + 12] ; 1-da s dr*y+s p*p*x fmulp st1, st0 ; s*(1-da) dr*y+s p*p*x fld dword [edx + 12] ; da s*(1-da) dr*y+s p*p*x fmul dword [ecx+su_delayline_wrk.filtstate] ; o*da s*(1-da) dr*y+s p*p*x, where o is stored faddp st1, st0 ; o*da+s*(1-da) dr*y+s p*p*x fadd dword [FCONST_0_500000] ; add and sub small offset to prevent denormalization. WARNING: this is highly important, as the damp filters might denormalize and give 100x CPU penalty fsub dword [FCONST_0_500000] ; See for example: https://stackoverflow.com/questions/36781881/why-denormalized-floats-are-so-much-slower-than-other-floats-from-hardware-arch fst dword [ecx+su_delayline_wrk.filtstate] ; o'=o*da+s*(1-da), o' dr*y+s p*p*x fmul dword [edx + 8] ; f*o' dr*y+s p*p*x fadd st0, st2 ; f*o'+p*p*x dr*y+s p*p*x fstp dword [ecx+su_delayline_wrk.buffer+esi*4]; save f*o'+p*p*x to delay buffer add ebx,2 ; move to next index add ecx, su_delayline_wrk.size ; go to next delay delay workspace sub ah, 2 jg su_op_delay_loop ; if ah > 0, goto loop fstp st1 ; dr*y+s1+s2+s3+... ; DC-filtering fld dword [ecx+su_delayline_wrk.dcout] ; o s fmul dword [FCONST_0_99609375] ; c*o s fsub dword [ecx+su_delayline_wrk.dcin] ; c*o-i s fxch ; s c*o-i fst dword [ecx+su_delayline_wrk.dcin] ; i'=s, s c*o-i faddp st1 ; s+c*o-i fadd dword [FCONST_0_500000] ; add and sub small offset to prevent denormalization. WARNING: this is highly important, as low pass filters might denormalize and give 100x CPU penalty fsub dword [FCONST_0_500000] ; See for example: https://stackoverflow.com/questions/36781881/why-denormalized-floats-are-so-much-slower-than-other-floats-from-hardware-arch fst dword [ecx+su_delayline_wrk.dcout] ; o'=s+c*o-i ret ;------------------------------------------------------------------------------- ; OUT opcode: outputs and pops the signal ;------------------------------------------------------------------------------- ; Stereo: add ST0 to left out and ST1 to right out, then pop ;------------------------------------------------------------------------------- section .su_op_out code align=1 su_op_out: ; l r mov edi, [esp + 24] ; DI points to the synth object, use DI consistently in sinks/sources presumably to increase compression rate call su_op_out_mono add edi, 4 ; shift from left to right channel su_op_out_mono: fmul dword [edx] ; multiply by gain fadd dword [edi + su_synthworkspace.left] ; add current value of the output fstp dword [edi + su_synthworkspace.left] ; store the new value of the output ret ;------------------------------------------------------------------------------- ; OUTAUX opcode: outputs to main and aux1 outputs and pops the signal ;------------------------------------------------------------------------------- ; Mono: add outgain*ST0 to main left port and auxgain*ST0 to aux1 left ; Stereo: also add outgain*ST1 to main right port and auxgain*ST1 to aux1 right ;------------------------------------------------------------------------------- section .su_op_outaux code align=1 su_op_outaux: ; l r mov edi, [esp + 24] call su_op_outaux_mono add edi, 4 su_op_outaux_mono: fld st0 ; l l fmul dword [edx] ; g*l fadd dword [edi + su_synthworkspace.left] ; g*l+o fstp dword [edi + su_synthworkspace.left] ; o'=g*l+o fmul dword [edx + 4] ; h*l fadd dword [edi + su_synthworkspace.aux] ; h*l+a fstp dword [edi + su_synthworkspace.aux] ; a'=h*l+a ret ;------------------------------------------------------------------------------- ; SEND opcode: adds the signal to a port ;------------------------------------------------------------------------------- ; Mono: adds signal to a memory address, defined by a word in VAL stream ; Stereo: also add right signal to the following address ;------------------------------------------------------------------------------- section .su_op_send code align=1 su_op_send: lodsw mov ecx, [esp + 12] ; load pointer to voice test al, 0x8 ; if the SEND_POP bit is not set jnz su_op_send_skippush fld st0 ; duplicate the signal on stack: s s su_op_send_skippush: ; there is signal s, but maybe also another: s (s) fld dword [edx] ; a l (l) fsub dword [FCONST_0_500000] ; a-.5 l (l) fadd st0 ; g=2*a-1 l (l) and ah, 0x7f ; eax = send address, clear the global bit or al, 0x8 ; set the POP bit always, at the same time shifting to ports instead of wrk fmulp st1, st0 ; g*l (l) fadd dword [ecx + eax*4] ; g*l+L (l),where L is the current value fstp dword [ecx + eax*4] ; (l) ret ;------------------------------------------------------------------------------- ; ENVELOPE opcode: pushes an ADSR envelope value on stack [0,1] ;------------------------------------------------------------------------------- ; Mono: push the envelope value on stack ; Stereo: push the envelope valeu on stack twice ;------------------------------------------------------------------------------- section .su_op_envelope code align=1 su_op_envelope: jnc su_op_envelope_mono call su_op_envelope_mono fld st0 ret su_op_envelope_mono: mov eax, dword [edx-su_voice.inputs+su_voice.sustain] ; eax = su_instrument.sustain test eax, eax ; if (eax != 0) jne su_op_envelope_process ; goto process mov al, 3 ; [state]=RELEASE mov dword [ebp], eax ; note that mov al, XXX; mov ..., eax is less bytes than doing it directly su_op_envelope_process: mov eax, dword [ebp] ; al=[state] fld dword [ebp+4] ; x=[level] cmp al, 2 ; if (al==SUSTAIN) je short su_op_envelope_leave2 ; goto leave2 su_op_envelope_attac: cmp al, 0 ; if (al!=ATTAC) jne short su_op_envelope_decay ; goto decay call su_nonlinear_map ; a x, where a=attack faddp st1, st0 ; a+x fld1 ; 1 a+x fucomi st1 ; if (a+x<=1) // is attack complete? fcmovnb st0, st1 ; a+x a+x jbe short su_op_envelope_statechange ; else goto statechange su_op_envelope_decay: cmp al, 1 ; if (al!=DECAY) jne short su_op_envelope_release ; goto release call su_nonlinear_map ; d x, where d=decay fsubp st1, st0 ; x-d fld dword [edx + 8] ; s x-d, where s=sustain fucomi st1 ; if (x-d>s) // is decay complete? fcmovb st0, st1 ; x-d x-d jnc short su_op_envelope_statechange ; else goto statechange su_op_envelope_release: cmp al, 3 ; if (al!=RELEASE) jne short su_op_envelope_leave ; goto leave call su_nonlinear_map ; r x, where r=release fsubp st1, st0 ; x-r fldz ; 0 x-r fucomi st1 ; if (x-r>0) // is release complete? fcmovb st0, st1 ; x-r x-r, then goto leave jc short su_op_envelope_leave su_op_envelope_statechange: inc dword [ebp] ; [state]++ su_op_envelope_leave: fstp st1 ; x', where x' is the new value fst dword [ebp+4] ; [level]=x' su_op_envelope_leave2: fmul dword [edx + 16] ; [gain]*x' ret ;------------------------------------------------------------------------------- ; NOISE opcode: creates noise ;------------------------------------------------------------------------------- ; Mono: push a random value [-1,1] value on stack ; Stereo: push two (differeent) random values on stack ;------------------------------------------------------------------------------- section .su_op_noise code align=1 su_op_noise: lea ecx,[esp + 60] imul eax, [ecx],16007 mov [ecx],eax fild dword [ecx] fidiv dword [ICONST_2147483648] ; 65536*32768 fld dword [edx] call su_waveshaper fmul dword [edx + 4] ret ;------------------------------------------------------------------------------- ; OSCILLAT opcode: oscillator, the heart of the synth ;------------------------------------------------------------------------------- ; Mono: push oscillator value on stack ; Stereo: push l r on stack, where l has opposite detune compared to r ;------------------------------------------------------------------------------- section .su_op_oscillator code align=1 su_op_oscillator: lodsb ; load the flags fld dword [edx + 4] ; e, where e is the detune [0,1] fsub dword [FCONST_0_500000] ; e-.5 fadd st0, st0 ; d=2*e-.5, where d is the detune [-1,1] jnc su_op_oscillat_mono fld st0 ; d d add ebp, 4 ; move wrk... call su_op_oscillat_mono ; r d sub ebp, 4 ; ...restore wrk fxch ; d r fchs ; -d r, negate the detune for second round su_op_oscillat_mono: pushad ; Stack: edi, esi, OscWRK, esp, ebx, edx, ecx, , retaddr_su_op_oscillator, edi, OperandStream, Voice, esp, OpcodeStream, Synth, DelayWorkSpace, eax, retaddr_su_run_vm, VoicesRemain, PolyphonyBitmask, Sample, Row, GlobalTick, RandSeed, VoiceTrackBitmask, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr fldz ; 0 d fxch ; d a=0, "accumulated signal" su_op_oscillat_unison_loop: fst dword [esp] ; save the current detune, d. We could keep it in fpu stack but it was getting big. call su_op_oscillat_single ; s a faddp st1, st0 ; a+=s test al, 3 je su_op_oscillat_unison_out add ebp, 8 ; this is ok after all, as there's a pop in the end of unison loop fld dword [edx + 8] ; p s fadd dword [ICONST_1034594986] ; 1/12 p s, add some little phase offset to unison oscillators so they don't start in sync fstp dword [edx + 8] ; s note that this changes the phase for second, possible stereo run. That's probably ok fld dword [esp] ; d s fmul dword [FCONST_0_500000] ; .5*d s // negate and halve the detune of each oscillator fchs ; -.5*d s // negate and halve the detune of each oscillator dec eax jmp short su_op_oscillat_unison_loop su_op_oscillat_unison_out: popad ; Popped: eax = , ecx, edx, ebx, esp, ebp = OscWRK, esi, edi. Stack: retaddr_su_op_oscillator, edi, OperandStream, Voice, esp, OpcodeStream, Synth, DelayWorkSpace, eax, retaddr_su_run_vm, VoicesRemain, PolyphonyBitmask, Sample, Row, GlobalTick, RandSeed, VoiceTrackBitmask, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr ret su_op_oscillat_single: fld dword [edx] fsub dword [FCONST_0_500000] fdiv dword [FCONST_0_00781250] faddp st1 fiadd dword [edx-su_voice.inputs+su_voice.note] ; // st0 is note, st1 is t+d offset fmul dword [ICONST_1034594986] call su_power fmul dword [FCONST_9_269614em05] ; // st0 is now frequency su_op_oscillat_normalized: fadd dword [ebp] test al, byte 0x80 jz short su_op_oscillat_not_sample fst dword [ebp] ; for samples, we store the phase without mod(p,1) fadd dword [edx + 8] call su_oscillat_sample jmp su_op_oscillat_shaping ; skip the rest to avoid color phase normalization and colorloading su_op_oscillat_not_sample: fld1 ; we need to take mod(p,1) so the frequency does not drift as the float fadd st1, st0 ; make no mistake: without this, there is audible drifts in oscillator pitch fxch ; as the actual period changes once the phase becomes too big fprem ; we actually computed mod(p+1,1) instead of mod(p,1) as the fprem takes mod fstp st1 ; towards zero fst dword [ebp] ; store back the updated phase fadd dword [edx + 8] fld1 ; this is a bit stupid, but we need to take mod(x,1) again after phase modulations fadd st1, st0 ; as the actual oscillator functions expect x in [0,1] fxch fprem fstp st1 fld dword [edx + 12] ; // c p ; every oscillator test included if needed test al, byte 0x40 jz short su_op_oscillat_notsine call su_oscillat_sine su_op_oscillat_notsine: test al, byte 0x20 jz short su_op_oscillat_not_trisaw call su_oscillat_trisaw su_op_oscillat_not_trisaw: test al, byte 0x10 jz short su_op_oscillat_not_pulse call su_oscillat_pulse su_op_oscillat_not_pulse: su_op_oscillat_shaping: ; finally, shape the oscillator and apply gain fld dword [edx + 16] call su_waveshaper su_op_oscillat_gain: fmul dword [edx + 20] ret section .su_oscillat_pulse code align=1 su_oscillat_pulse: fucomi st1 ; // c p fld1 jnc short su_oscillat_pulse_up ; // +1 c p fchs ; // -1 c p su_oscillat_pulse_up: fstp st1 ; // +-1 p fstp st1 ; // +-1 ret section .su_oscillat_trisaw code align=1 su_oscillat_trisaw: fucomi st1 ; // c p jnc short su_oscillat_trisaw_up fld1 ; // 1 c p fsubr st2, st0 ; // 1 c 1-p fsubrp st1, st0 ; // 1-c 1-p su_oscillat_trisaw_up: fdivp st1, st0 ; // tp'/tc fadd st0 ; // 2*'' fld1 ; // 1 2*'' fsubp st1, st0 ; // 2*''-1 ret section .su_oscillat_sine code align=1 su_oscillat_sine: fucomi st1 ; // c p jnc short su_oscillat_sine_do fstp st1 fsub st0, st0 ; // 0 ret su_oscillat_sine_do: fdivp st1, st0 ; // p/c fldpi ; // pi p fadd st0 ; // 2*pi p fmulp st1, st0 ; // 2*pi*p fsin ; // sin(2*pi*p) ret section .su_oscillat_sample code align=1 su_oscillat_sample: pushad ; Stack: SampleDi, esi, ebp, esp, SampleBx, SampleDx, SampleCx, SampleAx, retaddr_su_oscillat_sample, retaddr_su_op_oscillator, edi, OperandStream, Voice, esp, OpcodeStream, Synth, DelayWorkSpace, eax, retaddr_su_run_vm, VoicesRemain, PolyphonyBitmask, Sample, Row, GlobalTick, RandSeed, VoiceTrackBitmask, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr ; edx must be saved, eax & ecx if this is stereo osc push eax mov al, byte [esi-4] ; reuse "color" as the sample number lea edi, [su_sample_offsets + eax*8]; edi points now to the sample table entry fmul dword [FCONST_84_28075] ; p*r fistp dword [esp] pop edx ; edx is now the sample number movzx ebx, word [edi + 4] ; ecx = loopstart sub edx, ebx ; if sample number < loop start jl su_oscillat_sample_not_looping ; then we're not looping yet mov eax, edx ; eax = sample number movzx ecx, word [edi + 6] ; edi is now the loop length xor edx, edx ; div wants edx to be empty div ecx ; edx is now the remainder su_oscillat_sample_not_looping: add edx, ebx ; sampleno += loopstart add edx, dword [edi] fild word [su_sample_table + edx*2] fdiv dword [FCONST_32767_0] popad ; Popped: eax = SampleAx, ecx = SampleCx, edx = SampleDx, ebx = SampleBx, esp, ebp, esi, edi = SampleDi. Stack: retaddr_su_oscillat_sample, retaddr_su_op_oscillator, edi, OperandStream, Voice, esp, OpcodeStream, Synth, DelayWorkSpace, eax, retaddr_su_run_vm, VoicesRemain, PolyphonyBitmask, Sample, Row, GlobalTick, RandSeed, VoiceTrackBitmask, edi, esi, ebp, esp, ebx, edx, ecx, eax, retaddr_su_render_song, OutputBufPtr ret ;------------------------------------------------------------------------------- ; IN opcode: inputs and clears a global port ;------------------------------------------------------------------------------- ; Mono: push the left channel of a global port (out or aux) ; Stereo: also push the right channel (stack in l r order) ;------------------------------------------------------------------------------- section .su_op_in code align=1 su_op_in: lodsb mov edi, [esp + 24] xor ecx, ecx ; we cannot xor before jnc, so we have to do it mono & stereo. LAHF / SAHF could do it, but is the same number of bytes with more entropy fld dword [edi + su_synthworkspace.right + eax*4] mov dword [edi + su_synthworkspace.right + eax*4], ecx fld dword [edi + su_synthworkspace.left + eax*4] mov dword [edi + su_synthworkspace.left + eax*4], ecx ret section .su_load_gmdls code align=1 global _su_load_gmdls@0 _su_load_gmdls@0: mov eax, su_sample_table ; these are the arguments for ReadFile push 0 ; NULL push eax ; &bytes_read, reusing sample table again; it does not matter that the first four bytes are trashed push 3440660 ; number of bytes to read push eax ; here we actually pass the sample table to readfile ; these are for OpenFile push 0 ; OF_READ push eax ; &ofstruct, blatantly reuse the sample table push su_gmdls_path1 ; path call dword [__imp__OpenFile@12]; eax = OpenFile(path,&ofstruct,OF_READ) push eax ; handle to file call dword [__imp__ReadFile@20] ; Readfile(handle,&su_sample_table,SAMPLE_TABLE_SIZE,&bytes_read,NULL) ret extern __imp__OpenFile@12 ; requires windows extern __imp__ReadFile@20 ; requires windows section .su_gmdls_path1 data align=1 su_gmdls_path1: db 'drivers/gm.dls',0 section .susamtable bss align=256 su_sample_table: resb 3440660 ; size of gmdls. ;------------------------------------------------------------------------------- ; su_nonlinear_map function: returns 2^(-24*x) of parameter number _AX ;------------------------------------------------------------------------------- ; Input: _AX : parameter number (e.g. for envelope: 0 = attac, 1 = decay...) ; INP : pointer to transformed operands ; Output: st0 : 2^(-24*x), where x is the parameter in the range 0-1 ;------------------------------------------------------------------------------- section .su_nonlinear_map code align=1 su_nonlinear_map: fld dword [edx+eax*4] ; x, where x is the parameter in the range 0-1 fimul dword [ICONST_24] ; 24*x fchs ; -24*x ;------------------------------------------------------------------------------- ; su_power function: computes 2^x ;------------------------------------------------------------------------------- ; Input: st0 : x ; Output: st0 : 2^x ;------------------------------------------------------------------------------- global _su_pow@0 _su_pow@0: su_power: fld1 ; 1 x fld st1 ; x 1 x fprem ; mod(x,1) 1 x f2xm1 ; 2^mod(x,1)-1 1 x faddp st1,st0 ; 2^mod(x,1) x fscale ; 2^mod(x,1)*2^trunc(x) x ; Equal to: ; 2^x x fstp st1 ; 2^x ret ;------------------------------------------------------------------------------- ; DISTORT opcode: apply distortion on the signal ;------------------------------------------------------------------------------- ; Mono: x -> x*a/(1-a+(2*a-1)*abs(x)) where x is clamped first ; Stereo: l r -> l*a/(1-a+(2*a-1)*abs(l)) r*a/(1-a+(2*a-1)*abs(r)) ; This is placed here to be able to flow into waveshaper & also include ; wave shaper if needed by some other function; need to investigate the ; best way to do this ;------------------------------------------------------------------------------- section .su_op_distort code align=1 su_op_distort: fld dword [edx] su_waveshaper: fld st0 ; a a x fsub dword [FCONST_0_500000] ; a-.5 a x fadd st0 ; 2*a-1 a x fld st2 ; x 2*a-1 a x fabs ; abs(x) 2*a-1 a x fmulp st1 ; (2*a-1)*abs(x) a x fld1 ; 1 (2*a-1)*abs(x) a x faddp st1 ; 1+(2*a-1)*abs(x) a x fsub st1 ; 1-a+(2*a-1)*abs(x) a x fdivp st1, st0 ; a/(1-a+(2*a-1)*abs(x)) x fmulp st1 ; x*a/(1-a+(2*a-1)*abs(x)) ret ;------------------------------------------------------------------------------- ; su_effects_stereohelper: moves the workspace to next, does the filtering for ; right channel (pulling the calling address from stack), rewinds the ; workspace and returns ;------------------------------------------------------------------------------- section .su_effects_stereohelper code align=1 su_effects_stereohelper: jnc su_effects_stereohelper_mono ; carry is still the stereo bit add ebp, 16 fxch ; r l call [esp] ; call whoever called me... fxch ; l r sub ebp, 16 ; move WRK back to where it was su_effects_stereohelper_mono: ret ; return to process l/mono sound section .su_clip code align=1 su_clip: fld1 ; 1 x a fucomi st1 ; if (1 <= x) jbe short su_clip_do ; goto Clip_Do fchs ; -1 x a fucomi st1 ; if (-1 < x) fcmovb st0, st1 ; x x a su_clip_do: fstp st1 ; x' a, where x' = clamp(x) ret ;------------------------------------------------------------------------------- ; The opcode table jump table. This is constructed to only include the opcodes ; that are used so that the jump table is as small as possible. ;------------------------------------------------------------------------------- section .su_vm_jumptable data align=1 su_vm_jumptable: dd su_op_envelope dd su_op_oscillator dd su_op_mulp dd su_op_noise dd su_op_filter dd su_op_addp dd su_op_pan dd su_op_outaux dd su_op_send dd su_op_distort dd su_op_delay dd su_op_add dd su_op_loadnote dd su_op_in dd su_op_out ;------------------------------------------------------------------------------- ; The number of transformed parameters each opcode takes ;------------------------------------------------------------------------------- section .su_vm_transformcounts data align=1 su_vm_transformcounts: db 5 db 6 db 0 db 2 db 2 db 0 db 1 db 2 db 1 db 1 db 4 db 0 db 0 db 0 db 1 ;------------------------------------------------------------------------------- ; Patterns ;------------------------------------------------------------------------------- section .su_patterns data align=1 su_patterns: db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 48,1,1,1,48,1,1,1,48,1,1,1,48,1,1,1 db 48,1,1,1,48,1,1,1,48,1,1,1,48,1,48,1 db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 db 1,1,1,1,58,1,1,1,1,1,1,1,58,1,1,1 db 0,0,0,0,72,1,1,1,1,1,1,1,72,1,1,1 db 1,1,1,1,72,1,1,1,1,1,1,1,72,1,1,1 db 0,38,50,38,0,38,50,38,0,38,50,38,0,38,50,50 db 0,46,58,46,0,46,58,46,0,46,58,46,0,46,58,58 db 0,48,60,48,0,48,60,48,0,48,60,48,0,48,60,60 db 0,41,53,41,0,41,53,41,0,41,53,41,0,41,53,53 db 0,48,60,48,0,48,60,48,0,48,60,55,0,53,52,53 db 59,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0 db 0,0,53,1,1,1,53,1,1,1,53,1,1,1,53,53 db 1,1,53,1,1,1,53,1,1,1,53,1,1,1,53,53 db 1,1,53,1,1,1,53,1,1,1,53,1,1,1,53,1 db 1,1,53,1,1,1,53,1,1,1,53,1,1,1,53,52 db 0,0,0,0,0,0,0,0,57,57,57,57,54,54,54,54 db 0,0,0,0,0,0,0,0,0,0,57,57,1,52,52,1 db 50,0,0,50,0,50,50,1,50,1,0,50,0,50,0,50 db 0,0,0,50,0,50,50,1,50,1,0,50,0,50,50,50 db 0,46,46,46,0,46,46,1,46,1,0,46,0,46,46,46 db 0,0,46,46,0,46,46,1,46,1,0,46,0,46,46,46 db 0,0,0,48,0,48,48,1,48,1,0,48,0,48,48,48 db 41,1,0,41,0,41,41,1,41,1,0,41,0,41,41,41 db 0,46,74,74,0,46,72,1,74,1,0,74,0,74,74,74 db 76,76,76,76,0,48,70,1,76,1,0,48,0,48,48,79 db 0,0,50,50,0,50,50,1,50,1,0,50,0,50,50,50 db 48,1,0,48,0,48,48,1,48,1,0,48,0,48,48,48 db 74,0,0,74,0,74,72,1,74,1,0,74,0,74,0,72 db 0,0,0,74,0,74,72,1,74,1,0,74,0,74,74,72 db 0,74,74,74,0,74,72,1,74,1,0,74,0,74,74,72 db 0,0,74,74,0,74,72,1,74,1,0,74,0,74,74,76 db 0,0,0,76,0,76,76,1,76,1,0,76,0,76,76,76 db 77,1,0,77,0,77,77,1,77,1,0,77,0,77,77,67 db 0,74,70,46,0,74,69,1,70,1,0,70,0,70,70,70 db 72,72,72,48,0,72,74,1,72,1,0,76,0,76,74,48 db 0,0,74,74,0,74,72,1,74,1,0,74,0,74,74,72 db 76,1,0,76,0,76,76,1,76,1,0,76,0,76,76,76 db 77,0,0,77,0,77,76,1,77,1,0,77,0,77,0,76 db 0,0,0,77,0,77,76,1,77,1,0,77,0,77,77,76 db 0,77,77,77,0,77,76,1,77,1,0,77,0,77,77,76 db 0,0,77,77,0,77,76,1,77,1,0,77,0,77,77,79 db 0,0,0,79,0,79,79,1,79,1,0,79,0,79,79,79 db 81,1,0,81,0,81,81,1,81,1,0,81,0,81,81,72 db 0,70,46,70,0,70,46,1,46,1,0,46,0,46,46,46 db 48,48,48,72,0,76,48,1,48,1,0,72,0,72,77,76 db 0,0,77,77,0,77,76,1,77,1,0,77,0,77,77,76 db 79,1,0,79,0,79,79,1,79,1,0,79,0,79,79,79 db 0,0,0,0,0,0,0,0,77,0,0,0,64,0,0,0 db 62,0,0,0,0,0,69,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,69,0,0,0,0,0,0,0 db 62,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,64,0,0,0,65,0,0,0 db 79,0,0,0,0,0,0,0,0,0,0,0,0,0,81,0 db 0,0,0,0,0,0,0,0,0,0,60,0,0,0,0,0 db 74,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 65,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0 db 79,1,1,1,1,1,1,1,0,0,0,0,0,0,84,0 db 0,0,0,0,0,0,0,0,70,0,0,0,69,0,0,0 db 74,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,65,0,0,0,76,0,0,0 db 74,0,0,0,0,0,81,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,81,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,76,0,0,0,77,0,0,0 db 67,0,0,0,0,0,0,0,0,0,0,0,0,0,69,0 db 0,0,0,0,0,0,0,0,0,0,72,0,0,0,0,0 db 62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 77,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0 db 67,1,1,1,1,1,1,1,0,0,0,0,0,0,72,0 db 0,0,0,0,0,0,0,0,82,0,0,0,81,0,0,0 ;------------------------------------------------------------------------------- ; Tracks ;------------------------------------------------------------------------------- section .su_tracks data align=1 su_tracks: db 0,0,0,0,0,0,0,0,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,3,3,3 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3 db 5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,3,3,3 db 0,0,0,0,0,0,0,0,7,7,8,8,9,10,8,9,7,7,8,8,9,10,8,11,7,7,8,8,9,10,8,9,0,0,0 db 0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0 db 13,14,14,14,14,14,14,14,14,14,14,15,15,14,15,15,16,15,15,15,16,15,15,15,16,15,15,15,16,15,15,15,3,3,3 db 0,0,0,0,0,0,0,17,0,0,0,0,0,0,0,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 19,20,21,22,23,24,25,26,19,27,21,22,28,24,25,26,19,27,21,22,28,24,25,26,19,27,21,22,28,24,25,26,0,0,0 db 29,30,31,32,33,34,35,36,29,37,31,32,38,34,35,36,29,37,31,32,38,34,35,36,29,37,31,32,38,34,35,36,0,0,0 db 39,40,41,42,43,44,45,46,39,47,41,42,48,44,45,46,39,47,41,42,48,44,45,46,39,47,41,42,48,44,45,46,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,50,51,52,53,54,55,56,49,50,51,57,53,58,59,60,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,61,62,63,60,64,65,66,67,61,62,63,68,64,69,70,52,0,0,0,0 ;------------------------------------------------------------------------------- ; Sample offsets ;------------------------------------------------------------------------------- section .su_sample_offsets data align=1 su_sample_offsets: dd 560606 dw 4276 dw 1 dd 641780 dw 2423 dw 1 dd 433554 dw 2615 dw 5676 dd 367477 dw 1343 dw 1 dd 1543725 dw 831 dw 104 dd 557335 dw 2374 dw 821 ;------------------------------------------------------------------------------- ; Delay times ;------------------------------------------------------------------------------- section .su_delay_times data align=1 su_delay_times: dw 1395,350,1744,1744,69,12550,24402,21265,32419,1116,1188,1276,1356,1422,1492,1556,1618,1140,1212,1300,1380,1446,1516,1580,1642 ;------------------------------------------------------------------------------- ; The code for this patch, basically indices to vm jump table ;------------------------------------------------------------------------------- section .su_patch_opcodes data align=1 su_patch_opcodes: db 2,4,6,2,8,10,10,6,12,10,10,14,17,2,18,0,2,4,20,6,2,8,6,10,10,12,14,17,2,18,2,18,0,2,22,8,6,2,22,8,6,12,2,4,6,12,10,20,14,17,0,2,18,18,2,4,4,6,4,12,6,10,10,14,17,0,3,3,7,5,7,15,17,0,3,5,7,11,11,15,17,0,2,4,6,14,17,2,18,0,2,4,4,12,8,12,6,2,4,4,12,8,12,6,22,15,11,11,17,0,2,2,6,4,24,4,12,4,12,6,10,10,14,23,17,26,18,0,29,23,31,0 ;------------------------------------------------------------------------------- ; The parameters / inputs to each opcode ;------------------------------------------------------------------------------- section .su_patch_operands data align=1 su_patch_operands: db 0,70,0,0,76,88,69,0,128,63,79,66,0,43,0,0,128,43,79,84,82,68,100,62,64,12,69,68,12,48,16,64,50,0,62,76,0,0,128,46,40,0,0,76,0,0,96,86,116,0,0,114,37,130,95,0,68,0,0,83,3,107,57,67,68,128,78,64,64,42,26,47,54,0,0,128,19,136,0,54,68,70,0,128,26,255,255,0,53,0,0,128,64,128,0,0,0,3,97,0,0,54,0,0,128,64,6,0,0,2,3,79,128,0,62,0,0,42,64,64,0,1,64,125,128,39,76,68,114,60,39,19,39,73,62,74,128,94,176,0,47,91,0,42,65,86,71,83,88,66,91,49,100,89,34,64,74,64,42,70,17,17,64,78,13,128,88,128,66,22,46,68,10,116,16,64,40,0,8,90,0,0,128,21,78,0,85,64,77,54,0,2,87,111,128,64,128,97,50,60,0,0,128,82,64,0,3,64,127,128,78,128,76,109,128,64,67,128,11,1,74,0,0,64,64,67,0,4,64,99,129,64,89,43,1,78,36,74,64,83,40,0,40,0,63,51,54,64,94,0,128,64,128,35,76,28,52,128,64,102,35,114,67,20,0,48,64,64,64,46,78,128,64,128,35,76,99,54,128,64,128,35,128,80,128,0,0,0,4,1,65,122,128,72,103,128,64,20,6,40,102,47,78,68,0,84,25,84,127,76,52,128,91,48,128,67,88,73,0,57,37,56,35,55,64,0,5,58,128,129,30,128,80,53,128,24,61,69,103,84,12,5,3,27,14,82,200,0,2,61,113,122,97,9,15,128 ;------------------------------------------------------------------------------- ; Constants ;------------------------------------------------------------------------------- section .constants data align=1 FCONST_32767_0 dd 0x46fffe00 FCONST_0_00781250 dd 0x3c000000 FCONST_0_500000 dd 0x3f000000 FCONST_0_99609375 dd 0x3f7f0000 FCONST_9_269614em05 dd 0x38c265dc FCONST_84_28075 dd 0x42a88fbe ICONST_2147483648 dd 0x80000000 ICONST_1034594986 dd 0x3daaaaaa ICONST_24 dd 0x18