FMDIV_BITS equ 8
FMDIV_ENT equ (1 << FMDIV_BITS)
FMVOL_SFTBIT equ 4
SIN_BITS equ 11
EVC_BITS equ 10
ENV_BITS equ 16
KF_BITS equ 6
FREQ_BITS equ 21
ENVTBL_BIT equ 14
SINTBL_BIT equ 14
TL_BITS equ (FREQ_BITS+2)
OPM_OUTSB equ (TL_BITS + 2 - 16)
SIN_ENT equ (1 << SIN_BITS)
EVC_ENT equ (1 << EVC_BITS)
EC_ATTACK equ 0
EC_DECAY equ (EVC_ENT << ENV_BITS)
EC_OFF equ ((2 * EVC_ENT) << ENV_BITS)
EM_ATTACK equ 4
EM_DECAY1 equ 3
EM_DECAY2 equ 2
EM_RELEASE equ 1
EM_OFF equ 0
struc slot_t
.detune1 resd 1 ; 00
.totallevel resd 1 ; 04
.decaylevel resd 1 ; 08
.attack resd 1 ; 0c
.decay1 resd 1 ; 10
.decay2 resd 1 ; 14
.release resd 1 ; 18
.freq_cnt resd 1 ; 1c
.freq_inc resd 1 ; 20
.multiple resd 1 ; 24
.keyscale resb 1 ; 28
.env_mode resb 1 ; 29
.envraito resb 1 ; 2a
.ssgeg1 resb 1 ; 2b
.env_cnt resd 1 ; 2c
.env_end resd 1 ; 30
.env_inc resd 1 ; 34
.env_inc_attack resd 1 ; 38
.env_inc_decay1 resd 1 ; 3c
.env_inc_decay2 resd 1 ; 40
.env_inc_rel resd 1 ; 44
endstruc
struc ch_t
.slot resb (slot_t_size * 4)
.algorithm resb 1
.feedback resb 1
.playing resb 1
.outslot resb 1
.op1fb resd 1
.connect1 resd 1
.connect3 resd 1
.connect2 resd 1
.connect4 resd 1
.keynote resd 4
.keyfunc resb 4
.kcode resb 4
.pan resb 1
.extop resb 1
.stereo resb 1
resb 1
endstruc
struc opngen_t
.playchannels resd 1
.playing resd 1
.feedback2 resd 1
.feedback3 resd 1
.feedback4 resd 1
.outdl resd 1
.outdc resd 1
.outdr resd 1
.calcremain resd 1
.keyreg resb 12
endstruc
struc opncfg_t
.calc1024 resd 1
.fmvol resd 1
.ratebit resd 1
.vr_en resd 1
.vr_l resd 1
.vr_r resd 1
.sintable resd SIN_ENT
.envtable resd EVC_ENT
.envcurve resd (EVC_ENT*2 + 1)
endstruc
section .text
extern _opngen
extern _opnch
extern _opncfg
extern _sinshift
extern _envshift
ENVCURVE equ (_opncfg + opncfg_t.envcurve)
SINTABLE equ (_opncfg + opncfg_t.sintable)
ENVTABLE equ (_opncfg + opncfg_t.envtable)
global @opngen_getpcm@12
global @opngen_getpcmvr@12
%macro op_out 0
add eax, [edi + slot_t.freq_cnt]
shr eax, (FREQ_BITS - SIN_BITS)
and eax, (SIN_ENT - 1)
mov cl, [_sinshift + eax]
mov eax, [SINTABLE + eax*4]
add cl, [_envshift + edx]
imul eax, [ENVTABLE + edx*4]
sar eax, cl
%endmacro
%macro calcenv 2
mov eax, [edi + slot_t.freq_inc]
add [edi + slot_t.freq_cnt], eax
mov eax, [edi + slot_t.env_cnt]
add eax, [edi + slot_t.env_inc]
cmp eax, [edi + slot_t.env_end]
jae near %1
%2: mov [edi + slot_t.env_cnt], eax
shr eax, ENV_BITS
mov edx, [edi + slot_t.totallevel]
sub edx, [ENVCURVE + eax*4]
%endmacro
%macro setenv 3
%1: mov dl, [edi + slot_t.env_mode]
dec dl
je short %%setrr
dec dl
je short %%setd2
dec dl
je short %%setd1
dec dl
jne short %%envret
mov byte [edi + slot_t.env_mode], EM_DECAY1
mov eax, [edi + slot_t.decaylevel]
mov [edi + slot_t.env_end], eax
mov eax, [edi + slot_t.env_inc_decay1]
mov [edi + slot_t.env_inc], eax
mov eax, EC_DECAY
%%envret: jmp near %2
%%setd1: mov byte [edi + slot_t.env_mode], EM_DECAY2
mov dword [edi + slot_t.env_end], EC_OFF
mov eax, [edi + slot_t.env_inc_decay2]
mov [edi + slot_t.env_inc], eax
mov eax, [edi + slot_t.decaylevel]
jmp near %2
%%setrr: mov byte [edi + slot_t.env_mode], EM_OFF
%%setd2: mov dword [edi + slot_t.env_end], EC_OFF + 1
and dword [edi + slot_t.env_inc], byte 0
and byte [esi + ch_t.playing], ~(%3)
mov eax, EC_OFF
jmp near %2
%endmacro
align 16
@opngen_getpcm@12:
cmp dword [esp+4], byte 0
je near og_noupdate
cmp dword [_opngen + opngen_t.playing], byte 0
je near og_noupdate
push ebx
push esi
push edi
push ebp
sub esp, byte 8
OPN_SAMPL equ 0
OPN_SAMPR equ 4
OPN_LENG equ 16 + 8 + 4
mov ebp, edx
mov ebx, [_opngen + opngen_t.calcremain]
og_fmout_st: mov eax, ebx
imul ebx, [_opngen + opngen_t.outdl]
mov [esp + OPN_SAMPL], ebx
mov ebx, FMDIV_ENT
sub ebx, eax
imul eax, [_opngen + opngen_t.outdr]
mov [esp + OPN_SAMPR], eax
og_fmout_lp: mov [_opngen + opngen_t.calcremain], ebx
and dword [_opngen + opngen_t.playing], byte 0
and dword [_opngen + opngen_t.outdl], byte 0
and dword [_opngen + opngen_t.outdc], byte 0
and dword [_opngen + opngen_t.outdr], byte 0
mov ch, [_opngen + opngen_t.playchannels]
mov esi, _opnch
og_calcch_lp: mov cl, [esi + ch_t.outslot]
test cl, [esi + ch_t.playing]
je near og_calcch_nt
and dword [_opngen + opngen_t.feedback2], byte 0
and dword [_opngen + opngen_t.feedback3], byte 0
and dword [_opngen + opngen_t.feedback4], byte 0
mov edi, esi
calcenv envcalc1, envret1 ; slot1 calculate
jl near og_calcslot3
mov cl, [esi + ch_t.feedback]
test cl, cl
je short og_nofeed
mov eax, [esi + ch_t.op1fb] ; with feedback
mov ebx, eax
shr eax, cl
op_out
mov [esi + ch_t.op1fb], eax
add eax, ebx
sar eax, 1
jmp short og_algchk
og_nofeed: xor eax, eax ; without feedback
op_out
og_algchk: cmp byte [esi + ch_t.algorithm], 5
jne short og_calcalg5
mov [_opngen + opngen_t.feedback2], eax ; case ALG == 5
mov [_opngen + opngen_t.feedback3], eax
mov [_opngen + opngen_t.feedback4], eax
jmp short og_calcslot3
og_calcalg5: mov ebx, [esi + ch_t.connect1] ; case ALG != 5
add [ebx], eax
og_calcslot3: add edi, byte slot_t_size ; slot3 calculate
calcenv envcalc2, envret2
jl short og_calcslot2
mov eax, [_opngen + opngen_t.feedback2]
op_out
mov ebx, [esi + ch_t.connect2]
add [ebx], eax
og_calcslot2: add edi, byte slot_t_size ; slot2 calculate
calcenv envcalc3, envret3
jl short og_calcslot4
mov eax, [_opngen + opngen_t.feedback3]
op_out
mov ebx, [esi + ch_t.connect3]
add [ebx], eax
og_calcslot4: add edi, byte slot_t_size ; slot4 calculate
calcenv envcalc4, envret4
jl short og_calcsloted
mov eax, [_opngen + opngen_t.feedback4]
op_out
mov ebx, [esi + ch_t.connect4]
add [ebx], eax
og_calcsloted: inc dword [_opngen + opngen_t.playing]
og_calcch_nt: add esi, ch_t_size
dec ch
jne near og_calcch_lp
mov eax, [_opngen + opngen_t.outdc]
add [_opngen + opngen_t.outdl], eax
add [_opngen + opngen_t.outdr], eax
sar dword [_opngen + opngen_t.outdl], FMVOL_SFTBIT
sar dword [_opngen + opngen_t.outdr], FMVOL_SFTBIT
mov edx, [_opncfg + opncfg_t.calc1024]
mov ebx, [_opngen + opngen_t.calcremain]
mov eax, ebx
sub ebx, edx
jbe short og_nextsamp
mov [_opngen + opngen_t.calcremain], ebx
mov eax, edx
imul eax, [_opngen + opngen_t.outdl]
add [esp + OPN_SAMPL], eax
imul edx, [_opngen + opngen_t.outdr]
add [esp + OPN_SAMPR], edx
jmp near og_fmout_lp
og_nextsamp: neg ebx
mov [_opngen + opngen_t.calcremain], ebx
mov ecx, eax
imul eax, [_opngen + opngen_t.outdl]
add eax, [esp + OPN_SAMPL]
imul dword [_opncfg + opncfg_t.fmvol]
add [ebp], edx
mov eax, [_opngen + opngen_t.outdr]
imul ecx
add eax, [esp + OPN_SAMPR]
imul dword [_opncfg + opncfg_t.fmvol]
add [ebp+4], edx
add ebp, byte 8
dec dword [esp + OPN_LENG]
jne near og_fmout_st
add esp, byte 8
pop ebp
pop edi
pop esi
pop ebx
og_noupdate: ret 4
setenv envcalc1, envret1, 1
setenv envcalc2, envret2, 2
setenv envcalc3, envret3, 4
setenv envcalc4, envret4, 8
align 16
@opngen_getpcmvr@12:
cmp dword [_opncfg + opncfg_t.vr_en], byte 0
je near @opngen_getpcm@12
cmp dword [esp+4], byte 0
je near ogv_noupdate
push ebx
push esi
push edi
push ebp
sub esp, byte 8
OPNV_SAMPL equ 0
OPNV_SAMPR equ 4
OPNV_LENG equ 16 + 8 + 4
mov ebp, edx
mov ebx, [_opngen + opngen_t.calcremain]
ogv_fmout_st: mov eax, ebx
imul ebx, [_opngen + opngen_t.outdl]
mov [esp + OPNV_SAMPL], ebx
mov ebx, FMDIV_ENT
sub ebx, eax
imul eax, [_opngen + opngen_t.outdr]
mov [esp + OPNV_SAMPR], eax
ogv_fmout_lp: xor eax, eax
mov [_opngen + opngen_t.calcremain], ebx
mov [_opngen + opngen_t.outdl], eax
mov [_opngen + opngen_t.outdc], eax
mov [_opngen + opngen_t.outdr], eax
mov ch, [_opngen + opngen_t.playchannels]
mov edi, _opnch
ogv_calcch_lp: xor eax, eax
mov [_opngen + opngen_t.feedback2], eax
mov [_opngen + opngen_t.feedback3], eax
mov [_opngen + opngen_t.feedback4], eax
mov esi, edi
calcenv vrenvcalc1, vrenvret1 ; slot1 calculate
jl near ogv_calcslot3
mov cl, [esi + ch_t.feedback]
test cl, cl
je short ogv_nofeed
mov eax, [esi + ch_t.op1fb] ; with feedback
mov ebx, eax
shr eax, cl
op_out
mov [esi + ch_t.op1fb], eax
add eax, ebx
sar eax, 1
jmp short ogv_algchk
ogv_nofeed: xor eax, eax ; without feedback
op_out
ogv_algchk: cmp byte [esi + ch_t.algorithm], 5
jne short ogv_calcalg5
mov [_opngen + opngen_t.feedback2], eax ; case ALG == 5
mov [_opngen + opngen_t.feedback3], eax
mov [_opngen + opngen_t.feedback4], eax
jmp short ogv_calcslot3
ogv_calcalg5: mov ebx, [esi + ch_t.connect1] ; case ALG != 5
add [ebx], eax
ogv_calcslot3: add edi, byte slot_t_size ; slot3 calculate
calcenv vrenvcalc2, vrenvret2
jl short ogv_calcslot2
mov eax, [_opngen + opngen_t.feedback2]
op_out
mov ebx, [esi + ch_t.connect2]
add [ebx], eax
ogv_calcslot2: add edi, byte slot_t_size ; slot2 calculate
calcenv vrenvcalc3, vrenvret3
jl short ogv_calcslot4
mov eax, [_opngen + opngen_t.feedback3]
op_out
mov ebx, [esi + ch_t.connect3]
add [ebx], eax
ogv_calcslot4: add edi, byte slot_t_size ; slot4 calculate
calcenv vrenvcalc4, vrenvret4
jl short ogv_calcsloted
mov eax, [_opngen + opngen_t.feedback4]
op_out
mov ebx, [esi + ch_t.connect4]
add [ebx], eax
ogv_calcsloted: add edi, byte (ch_t_size - (slot_t_size * 3))
dec ch
jne near ogv_calcch_lp
mov eax, [_opngen + opngen_t.outdl]
mov edx, [_opngen + opngen_t.outdc]
imul eax, [_opncfg + opncfg_t.vr_l]
mov ebx, edx
sar eax, 5
add ebx, eax
sar eax, 2
add edx, eax
mov eax, [_opngen + opngen_t.outdr]
imul eax, [_opncfg + opncfg_t.vr_r]
sar eax, 5
add edx, eax
sar eax, 2
add ebx, eax
add [_opngen + opngen_t.outdl], edx
add [_opngen + opngen_t.outdr], ebx
sar dword [_opngen + opngen_t.outdl], FMVOL_SFTBIT
sar dword [_opngen + opngen_t.outdr], FMVOL_SFTBIT
mov edx, [_opncfg + opncfg_t.calc1024]
mov ebx, [_opngen + opngen_t.calcremain]
mov eax, ebx
sub ebx, edx
jbe short ogv_nextsamp
mov [_opngen + opngen_t.calcremain], ebx
mov eax, edx
imul eax, [_opngen + opngen_t.outdl]
add [esp + OPNV_SAMPL], eax
imul edx, [_opngen + opngen_t.outdr]
add [esp + OPNV_SAMPR], edx
jmp near ogv_fmout_lp
ogv_nextsamp: neg ebx
mov [_opngen + opngen_t.calcremain], ebx
mov ecx, eax
imul eax, [_opngen + opngen_t.outdl]
add eax, [esp + OPNV_SAMPL]
imul dword [_opncfg + opncfg_t.fmvol]
add [ebp], edx
mov eax, [_opngen + opngen_t.outdr]
imul ecx
add eax, [esp + OPNV_SAMPR]
imul dword [_opncfg + opncfg_t.fmvol]
add [ebp+4], edx
add ebp, byte 8
dec dword [esp + OPNV_LENG]
jne near ogv_fmout_st
add esp, byte 8
pop ebp
pop edi
pop esi
pop ebx
ogv_noupdate: ret 4
setenv vrenvcalc1, vrenvret1, 1
setenv vrenvcalc2, vrenvret2, 2
setenv vrenvcalc3, vrenvret3, 4
setenv vrenvcalc4, vrenvret4, 8
RetroPC.NET-CVS <cvs@retropc.net>