sointu/src/sointu.asm
2020-05-25 13:44:30 +03:00

293 lines
9.6 KiB
NASM

%if BITS == 64
%define WRK rbp ; alias for unit workspace
%define VAL rsi ; alias for unit values (transformed/untransformed)
%define COM rbx ; alias for instrument opcodes
%define INP rdx ; alias for transformed inputs
%define _AX rax ; push and offsets have to be r* on 64-bit and e* on 32-bit
%define _BX rbx
%define _CX rcx
%define _DX rdx
%define _SP rsp
%define _SI rsi
%define _DI rdi
%define _BP rbp
%define PTRSIZE 8
%define PTRWORD qword
%define RESPTR resq
%define DPTR dq
%macro do 2
mov r9, qword %2
%1 r9
%endmacro
%macro do 3
mov r9, qword %2
%1 r9 %3
%endmacro
%macro do 4
mov r9, qword %2
%1 r9+%3 %4
%endmacro
%macro do 5
mov r9, qword %2
lea r9, [r9+%3]
%1 r9+%4 %5
%endmacro
%macro push_registers 1-*
%rep %0
push %1
%rotate 1
%endrep
%endmacro
%macro pop_registers 1-*
%rep %0
%rotate -1
pop %1
%endrep
%endmacro
%define PUSH_REG_SIZE(n) (n*8)
%else
%define WRK ebp ; alias for unit workspace
%define VAL esi ; alias for unit values (transformed/untransformed)
%define COM ebx ; alias for instrument opcodes
%define INP edx ; alias for transformed inputs
%define _AX eax
%define _BX ebx
%define _CX ecx
%define _DX edx
%define _SP esp
%define _SI esi
%define _DI edi
%define _BP ebp
%define PTRSIZE 4
%define PTRWORD dword
%define RESPTR resd
%define DPTR dd
%macro do 2
%1 %2
%endmacro
%macro do 3
%1 %2 %3
%endmacro
%macro do 4
%1 %2+%3 %4
%endmacro
%macro do 5
%1 %2+%3+%4 %5
%endmacro
%macro push_registers 1-*
pushad ; in 32-bit mode, this is the easiest way to store all the registers
%endmacro
%macro pop_registers 1-*
popad
%endmacro
%define PUSH_REG_SIZE(n) 32
%endif
struc su_stack ; the structure of stack _as the units see it_
.retaddr RESPTR 1
.voiceno RESPTR 1
.val RESPTR 1
.wrk RESPTR 1
%if BITS == 32 ; we dump everything with pushad, so this is unused in 32-bit
RESPTR 1
%endif
.com RESPTR 1
.synth RESPTR 1
.delaywrk RESPTR 1
%if BITS == 32 ; we dump everything with pushad, so this is unused in 32-bit
RESPTR 1
%endif
.retaddrvm RESPTR 1
%ifdef INCLUDE_POLYPHONY
.polyphony RESPTR 1
%endif
.output_sound
.rowtick RESPTR 1 ; which tick within this row are we at
.update_voices
.row RESPTR 1 ; which total row of the song are we at
.tick RESPTR 1 ; which total tick of the song are we at
.randseed RESPTR 1
%ifdef INCLUDE_MULTIVOICE_TRACKS
.voicetrack RESPTR 1
%endif
.render_epilogue
%if BITS == 32
RESPTR 8 ; registers
.retaddr_pl RESPTR 1
%elifidn __OUTPUT_FORMAT__,win64
RESPTR 4 ; registers
%else
RESPTR 2 ; registers
%endif
.bufferptr RESPTR 1
.size
endstruc
;===============================================================================
; 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.
;===============================================================================
SECT_DATA(suoptabl)
su_synth_commands DPTR OPCODES
;===============================================================================
; The number of transformed parameters each opcode takes
;===============================================================================
SECT_DATA(suparcnt)
su_opcode_numparams db NUMPARAMS
;-------------------------------------------------------------------------------
; Constants used by the common functions
;-------------------------------------------------------------------------------
SECT_DATA(suconst)
c_i128 dd 0.0078125
c_RandDiv dd 65536*32768
c_0_5 dd 0.5
c_24 dd 24
c_i12 dd 0x3DAAAAAA
EXPORT MANGLE_DATA(LFO_NORMALIZE)
dd DEF_LFO_NORMALIZE
;-------------------------------------------------------------------------------
; 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 command stream
; VAL : Pointer to value stream
; WRK : Pointer to the last workspace processed
; _DI : Voice number (0 = starting from first voice)
; Output: su_synth_obj.left : left sample
; su_synth_obj.right : right sample
; Dirty: everything
;-------------------------------------------------------------------------------
SECT_TEXT(surunvm)
EXPORT MANGLE_FUNC(su_run_vm,0)
push_registers _CX, _DX, COM, WRK, VAL, _DI ; save everything to stack
su_run_vm_loop: ; loop until all voices done
movzx eax, byte [COM] ; eax = command byte
inc COM ; move to next instruction
add WRK, su_unit.size ; move WRK to next unit
push _AX
shr eax,1
do {mov al, byte [},su_opcode_numparams,_AX,]
push _AX
call su_transform_values
pop _AX
shr eax,1
do call [,su_synth_commands,_AX*PTRSIZE,] ; call the function corresponding to the instruction
cmp dword [_SP+su_stack.voiceno-PTRSIZE],MAX_VOICES ; if (voice < MAX_VOICES)
jl su_run_vm_loop ; goto vm_loop
pop_registers _CX, _DX, COM, WRK, VAL, _DI ; pop everything from stack
ret
;-------------------------------------------------------------------------------
; su_transform_values function: transforms values and adds modulations
;-------------------------------------------------------------------------------
; Input: [esp] : number of bytes to transform
; VAL : pointer to byte stream
; Output: eax : last transformed byte (zero extended)
; edx : pointer to su_transformed_values, containing
; each byte transformed as x/128.0f+modulations
; VAL : updated to point after the transformed bytes
;-------------------------------------------------------------------------------
SECT_TEXT(sutransf)
su_transform_values:
xor ecx, ecx
xor eax, eax
mov INP, [_SP+su_stack.wrk+2*PTRSIZE]
add INP, su_voice.inputs
su_transform_values_loop:
cmp ecx, dword [_SP+PTRSIZE]
jnb su_transform_values_out
lodsb
push _AX
fild dword [_SP]
do fmul dword [,c_i128,]
fadd dword [WRK+su_unit.ports+_CX*4]
fstp dword [INP+_CX*4]
mov dword [WRK+su_unit.ports+_CX*4], 0
pop _AX
inc ecx
jmp su_transform_values_loop
su_transform_values_out:
ret PTRSIZE
;-------------------------------------------------------------------------------
; su_env_map function: computes 2^(-24*x) of the envelope parameter
;-------------------------------------------------------------------------------
; Input: eax : envelope parameter (0 = attac, 1 = decay...)
; edx : pointer to su_transformed_values
; Output: st0 : 2^(-24*x), where x is the parameter in the range 0-1
;-------------------------------------------------------------------------------
SECT_TEXT(supower)
%if ENVELOPE_ID > -1 ; TODO: compressor also uses this, so should be compiled if either
su_env_map:
fld dword [INP+_AX*4] ; x, where x is the parameter in the range 0-1
do fimul dword [,c_24,] ; 24*x
fchs ; -24*x
; flow into Power function, which outputs 2^(-24*x)
%endif
;-------------------------------------------------------------------------------
; su_power function: computes 2^x
;-------------------------------------------------------------------------------
; Input: st0 : x
; Output: st0 : 2^x
;-------------------------------------------------------------------------------
EXPORT MANGLE_FUNC(su_power,0)
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
;-------------------------------------------------------------------------------
; Include the rest of the code
;-------------------------------------------------------------------------------
%include "opcodes/arithmetic.asm"
%include "opcodes/flowcontrol.asm"
%include "opcodes/sources.asm"
%include "opcodes/sinks.asm"
; warning: at the moment effects has to be assembled after
; sources, as sources.asm defines SU_USE_WAVESHAPER
; if needed.
%include "opcodes/effects.asm"
%include "introspection.asm"
%include "player.asm"
%ifidn __OUTPUT_FORMAT__,win64
%include "win64/gmdls_win64.asm"
%endif
%ifidn __OUTPUT_FORMAT__,win32
%include "win32/gmdls_win32.asm"
%endif