【改版基础教程#2】ipatix的高音质混音器
本帖最后由 jiangzhengwenjz 于 2017-1-19 18:45 编辑首先告诫大家一下,发帖一定要备份不要在线弄,一不小心之前写完的又都消掉了。
原贴:https://www.pokecommunity.com/showthread.php?t=324673
标题:ipatix' High Quality Sound Mixer
作者:ipatix
本帖不得转载,转载英文原贴务必自觉注明网址!
1. 介绍
大量GBA游戏中混音系统都是用的sdk中的引擎,但这个引擎的噪音现象较为严重,虽然在火红中体现不明显,不过如果扩充了directsound的音轨数量的话这个缺陷就不容忽视了,所以ipatix自己开发了一个混音器。
2. 如何实现
首先要先编译二楼的源码并写入空位(这个是稍微修改过后的,原先那个说起来比较麻烦,这个可用mixer_size标签)(发在二楼是因为字数限制)
写入完了后,搜索ROM中所有0x3005F50的指针并改为0x203E000的指针,以作为新的PCM工作区。(要用这个空位须先取消帮助系统,在0x13b8c2写入1D E0)
接下来在0x1DD0B4写入编译好的混音器源码的指针,不用+1。
搜索ROM中所有0x30028E0和0x30028E1的指针并分别改为0x3005F50和0x3005F51的指针,这个是利用了已经不被使用的IWRAM空间。
之后在0x1DD0BC写入混音器源码编译后的字节数/4,或者直接用mixer_size的数值。应当为0x2C9(C9 02)
最后在0x1DD0C9写入CC以扩充directsound音轨数量为12。
这样就全部完成了,可进入游戏试听,效果还是比较明显的。下篇教程将会介绍些音乐修改方面的基础,不过想深入原理性的东西短期内应该做不到。
3. 感谢
Sturmvogel整理源码
有任何错误请回帖指出
@ created by ~ipatix~
.global mixer_size
.global main_mixer
.global main_mixer_end
.set mixer_size,(main_mixer_end - main_mixer) / 4
.equ GAME_BPED, 0
.equ GAME_BPEE, 1
.equ GAME_BPRE, 2
.equ GAME_KWJ6, 3
@ SELECT USED GAME HERE
.equ USED_GAME, GAME_BPRE @ CHOOSE YOUR GAME
.equ FRAME_LENGTH_5734, 0x60
.equ FRAME_LENGTH_7884, 0x84 @ THIS MODE IS NOT SUPPORTED BY THIS ENGINE BECAUSE IT DOESN'T USE AN 8 ALIGNED BUFFER LENGTH
.equ FRAME_LENGTH_10512, 0xB0
.equ FRAME_LENGTH_13379, 0xE0 @ DEFAULT
.equ FRAME_LENGTH_15768, 0x108
.equ FRAME_LENGTH_18157, 0x130
.equ FRAME_LENGTH_21024, 0x160
.equ FRAME_LENGTH_26758, 0x1C0
.equ FRAME_LENGTH_31536, 0x210
.equ FRAME_LENGTH_36314, 0x260
.equ FRAME_LENGTH_40137, 0x2A0
.equ FRAME_LENGTH_42048, 0x2C0
.equ DECODER_BUFFER_BPE, 0x03001300
.equ DECODER_BUFFER_BPR, 0x03002088
.equ DECODER_BUFFER_KWJ, 0x03005800
.equ FREE_IRAM_BPE, 0x03001AA8
.equ FREE_IRAM_BPR, 0x030028E0
.equ FREE_IRAM_KWJ, 0x03005840
.equ ARG_FRAME_LENGTH, 0x0
.equ ARG_REMAIN_CHN, 0x4
.equ ARG_BUFFER_POS, 0x8
.equ ARG_LOOP_START_POS, 0xC
.equ ARG_LOOP_LENGTH, 0x10
.equ ARG_VAR_AREA, 0x18
.equ CHN_STATUS, 0x0
.equ CHN_MODE, 0x1
.equ CHN_VOL_1, 0x2
.equ CHN_VOL_2, 0x3
.equ CHN_ATTACK, 0x4
.equ CHN_DECAY, 0x5
.equ CHN_SUSTAIN, 0x6
.equ CHN_RELEASE, 0x7
.equ CHN_ADSR_LEVEL, 0x9
.equ CHN_FINAL_VOL_1, 0xA @ not used anymore
.equ CHN_FINAL_VOL_2, 0xB @ not used anymore
.equ CHN_ECHO_VOL, 0xC
.equ CHN_ECHO_REMAIN, 0xD
.equ CHN_POSITION_REL, 0x18 @ RELATIVE FOR COMPRESSED SAMPLES (decrementing)
.equ CHN_FINE_POSITION, 0x1C
.equ CHN_FREQUENCY, 0x20
.equ CHN_WAVE_OFFSET, 0x24
.equ CHN_POSITION_ABS, 0x28 @ RELATIVE FOR COMPRESSED SAMPLES (incrementing)
.equ CHN_BLOCK_COUNT, 0x3C
.equ WAVE_LOOP_FLAG, 0x3
.equ WAVE_FREQ, 0x4
.equ WAVE_LOOP_START, 0x8
.equ WAVE_LENGTH, 0xC
.equ SYNTH_BASE_WAVE_DUTY, 0x1
.equ SYNTH_WIDTH_CHANGE_1, 0x2
.equ SYNTH_MOD_AMOUNT, 0x3
.equ SYNTH_WIDTH_CHANGE_2, 0x4
.equ FLAG_CHN_INIT, 0x80
.equ FLAG_CHN_RELEASE, 0x40
.equ FLAG_CHN_COMP, 0x20
.equ FLAG_CHN_LOOP, 0x10
.equ FLAG_CHN_ECHO, 0x4
.equ FLAG_CHN_ATTACK, 0x3
.equ FLAG_CHN_DECAY, 0x2
.equ FLAG_CHN_SUSTAIN, 0x1
.equ MODE_FIXED_FREQ, 0x8
.equ MODE_REVERSE, 0x10
.equ MODE_COMP, 0x30
.equ MODE_SYNTH, 0x40
.equ VAR_REVERB, 0x5
.equ VAR_MAX_CHN, 0x6
.equ VAR_MASTER_VOL, 0x7
.equ VAR_DEF_PITCH_FAC, 0x18
.equ VAR_FIRST_CHN, 0x50
.equ REG_DMA3_SRC, 0x040000D4
@#######################################
@*********** GAME CONFIGS **************
@ add the game's name above to the ASM .equ-s before creating new configs
@#######################################
@*********** IF BPED
.if USED_GAME==GAME_BPED
.equ hq_buffer, FREE_IRAM_BPE
.equ decoder_buffer_target, DECODER_BUFFER_BPE
.equ ALLOW_PAUSE, 1
.equ DMA_FIX, 1
.equ ENABLE_DECOMPRESSION, 1
.equ ENABLE_FM, 0
.equ PREVENT_CLIP, 0
.endif
@*********** IF BPEE
.if USED_GAME==GAME_BPEE
.equ hq_buffer, FREE_IRAM_BPE
.equ decoder_buffer_target, DECODER_BUFFER_BPE
.equ ALLOW_PAUSE, 1
.equ DMA_FIX, 1
.equ ENABLE_DECOMPRESSION, 1
.equ ENABLE_FM, 0
.equ PREVENT_CLIP, 1
.endif
@*********** IF BPRE
.if USED_GAME==GAME_BPRE
.equ hq_buffer, FREE_IRAM_BPR
.equ decoder_buffer_target, DECODER_BUFFER_BPR
.equ ALLOW_PAUSE, 1
.equ DMA_FIX, 1
.equ ENABLE_DECOMPRESSION, 1
.equ ENABLE_FM, 0
.equ PREVENT_CLIP, 1
.endif
@*********** IF KWJ6
.if USED_GAME==GAME_KWJ6
.equ hq_buffer, FREE_IRAM_KWJ
.equ decoder_buffer_target, DECODER_BUFFER_KWJ
.equ ALLOW_PAUSE, 0
.equ DMA_FIX, 0
.equ ENABLE_DECOMPRESSION, 0
.equ ENABLE_FM, 0
.equ PREVENT_CLIP, 1
.endif
@***********
.thumb
main_mixer:
LDRB R3,
LSR R3, R3, #2
@CMP R3, #0
BEQ clear_buffer
ADR R1, do_reverb
BX R1
.align 2
.arm
do_reverb:
CMP R4, #2
ADDEQ R7, R0, #0x350
ADDNE R7, R5, R8
MOV R4, R8
ORR R3, R3, R3, LSL#16 @ turn on reverb for both s
STMFD SP!, {R8, LR}
LDR LR, hq_buffer_label
@ R00: VAR AREA (not used) ... OK
@ R01: Function Pointer (not used) ... OK
@ R02: not consistent (not used) ... OK
@ R03: Reverb ;NEEDED!!!
@ R04: Countdown Sample ;NEEDED!!!
@ R05: Sample Pointer, low Res Buffer ;NEEDED!!!
@ R06: Buffer Spacing; not needed ; NEEDED!!!
@ R07: previous buffer pointer, low Res Buffer ;NEEDED!!!
@ R08: Frame Length (not used) (PUSH) ... OK
@ R09: not used ... OK
@ R10: not used ... OK
@ R11: not used ... OK
@ R12: not used ... OK
@ LR: not needed ... OK
reverb_loop:
LDRSB R0,
LDRSB R1, , #1
LDRSB R2,
LDRSB R8, , #1
LDRSB R9,
LDRSB R10, , #1
LDRSB R11,
LDRSB R12, , #1
ADD R0, R0, R1
ADD R0, R0, R2
ADDS R0, R0, R8
ADDMI R0, R0, #0x4
ADD R1, R9, R10
ADD R1, R1, R11
ADDS R1, R1, R12
ADDMI R1, R1, #0x4
MUL R0, R3, R0
MUL R1, R3, R1
STMIA LR!, {R0, R1}
SUBS R4, R4, #2
BGT reverb_loop
LDMFD SP!, {R8, LR}
ADR R0, (adsr_setup+1)
BX R0
.thumb
clear_buffer:
LDR R3, hq_buffer_label
MOV R1, R8
MOV R4, #0
MOV R5, #0
MOV R6, #0
MOV R7, #0
LSR R1, #3
BCC clear_buffer_align_8
STMIA R3!, {R4, R5, R6, R7}
clear_buffer_align_8:
LSR R1, #1
BCC clear_buffer_align_16
STMIA R3!, {R4, R5, R6, R7}
STMIA R3!, {R4, R5, R6, R7}
clear_buffer_align_16:
STMIA R3!, {R4, R5, R6, R7}
STMIA R3!, {R4, R5, R6, R7}
STMIA R3!, {R4, R5, R6, R7}
STMIA R3!, {R4, R5, R6, R7}
SUB R1, #1
BGT clear_buffer_align_16
adsr_setup:
MOV R4, R8
ADR R0, hq_buffer_length_label
STR R4,
@adsr_setup:
LDR R4, @ load ARG_0x18 (main var area) to R4
LDR R0, @ load samplingrate pitch factor value to R0
MOV R12, R0 @ copy factor to R12
LDRB R0, @ load MAX channels to R0
ADD R4, #VAR_FIRST_CHN @ R4 == Base channel Offset (Channel 0)
mixer_entry:
STR R0, @ store the channels to work with on stack (probably given by calling function)
LDR R3, @ load the Wave Data Offset to R3
LDRB R6, @ get the channel status indicator to R6
MOVS R0, #0xC7 @ check if any of the channel status flags is set
TST R0, R6 @ check if none of the flags is set
BEQ return_channel_null @ end the channel loop and disable the channel
LSL R0, R6, #0x19 @ shift over the channel status by 0x19/25 Bits
BCC adsr_echo_check @ continue with normal channel procedure
BMI stop_channel_handler @ if the channel is initiated but on release it gets turned off immediatley
MOVS R6, #FLAG_CHN_ATTACK @ set the channel status to ATTACK
MOVS R0, R3 @ copy the wave offset to R0
ADD R0, #0x10 @ Add 0x10 to result the actual beginning of the wave data
@**************** Other Games ******************@
.if ALLOW_PAUSE==0
STR R0, @ store the absolute channel position to the CHN Vars
LDR R0, @ load the wave length to R0
STR R0, @ store it as the remaining samples in the CHN Vars
.endif
@************** End Other Games ****************@
@*************** Pokemon Games *****************@
.if ALLOW_PAUSE==1
LDR R1, @ load sample position into R1
ADD R0, R0, R1 @ add it to the base offset
STR R0, @ write the current sample position to 0x28 of channel in array
LDR R0, @ load sample length into R0
SUB R0, R0, R1 @ R0 = Samples left (if it's 0 the end of sample is reached)
STR R0, @ Write samples left into Relative Channel Position Variable in Channel
.endif
@************* End Pokemon Games ***************@
MOVS R5, #0 @ set the initial envelope to #0
STRB R5, @ write it to the adsr level slot
STR R5, @ set the fine position to #0 aswell
LDRB R2, @ load the loop indicator to R2
LSR R0, R2, #6 @ if loop is disabled result is EQ
BEQ adsr_attack_handler
MOVS R0, #FLAG_CHN_LOOP @ load the channel loop flag to R0
ORR R6, R0 @ set the Loop Flag in the channel status
B adsr_attack_handler
adsr_echo_check:
LDRB R5, @ load the current ADSR Level
LSL R0, R6, #0x1D @ shift the echo flag to the leftmost bit
BPL adsr_release_check @ if the echo bit is NOT set go to label
LDRB R0, @ load the remaining echo to R0
SUB R0, #1 @ reduce the remaining echo by #1
STRB R0, @ store it again
BHI channel_vol_calc @ if the echo has not gone to #0 go over to vol calc
stop_channel_handler:
MOVS R0, #0 @ load NULL channel status to R0
STRB R0, @ store it to the channel status
return_channel_null:
B check_remain_channels
adsr_release_check:
LSL R0, R6, #0x19 @ move the release flag bit over to the leftmost bit
BPL adsr_decay_check @ if the bit is NOT set go over to regular envelope handler
LDRB R0, @ load the release value
@SUB R0, #0xFF
@SUB R0, #1 @ reduce the release value by 256
@ADD R5, R5, R0 @ add the result to the adsr level (linear decay)
MUL R5, R5, R0 @ default release algorithm
LSR R5, R5, #8
@BMI adsr_released_handler
BEQ adsr_released_handler @ neccessary for the other release algorithm
LDRB R0, @ load echo vol to R0
CMP R5, R0 @ check if the adsr level has fallen below the echo vol
BHI channel_vol_calc
adsr_released_handler:
LDRB R5, @ load the echo vol again
CMP R5, #0 @ check if it's zero
BEQ stop_channel_handler @ stop the channel if it isn't used
MOVS R0, #FLAG_CHN_ECHO @ load the echo flag to R0
ORR R6, R0 @ set the echo flag
B adsr_update_status
adsr_decay_check:
MOVS R2, #3 @ seperate phase bits
AND R2, R6 @
CMP R2, #FLAG_CHN_DECAY @ check if channel NOT in decay state
BNE adsr_attack_check @ goto if decay NOT active
LDRB R0, @ load the decay value to R0
MUL R5, R0 @ apply the decay
LSR R5, R5, #8
LDRB R0, @ load the sutain level
CMP R5, R0 @ has the sample not fully decayed yet?
BHI channel_vol_calc @ if it didn't decay yet goto
MOVS R5, R0 @ move the sustin level on underflow to adsr level and check if the result is #0 (i.e. channel = off)
BEQ adsr_released_handler
B adsr_switchto_next
adsr_attack_check:
CMP R2, #FLAG_CHN_ATTACK @ check if attack is enabled
BNE channel_vol_calc @ if it isn't in attack attack phase, it has to be in sustain (no adsr change needed) -> go over to vol calc
adsr_attack_handler:
LDRB R0, @ load the attack value to R0
ADD R5, R5, R0 @ add the attack value to the adsr value
CMP R5, #0xFF @ check if the adsr level is below 0xFF
BCC adsr_update_status @ store the value if it hasn't overflowed
MOVS R5, #0xFF @ write MAX level to adsr level if it did overflow
adsr_switchto_next:
SUB R6, #1 @ switch to the next adsr state
adsr_update_status:
STRB R6, @ store the channel state
channel_vol_calc:
STRB R5, @ store the adsr level
LDR R0, @ apply master volume
LDRB R0,
ADD R0, #1
MUL R5, R0, R5
LDRB R0, @ load volume #1 to R0
MUL R0, R5 @ mutiply it with the adsr level
LSR R0, R0, #13 @ convert it to an 8 bit volume level
MOV R10, R0 @ store vol #1 to R10
LDRB R0, @ load volume #2 to R0
MUL R0, R5 @ apply adsr to vol #2
LSR R0, R0, #13 @ scale it down to an 8bit value
MOV R11, R0 @ store it into R11 (vol#2)
MOVS R0, #FLAG_CHN_LOOP @ ist loop enabled?
AND R0, R6 @
BEQ mixing_loop_setup @ skip the loop setup procedure if it's disabled
ADD R3, #8 @ add 8 to the sample pointer (loop start)
LDMIA R3!, {R0, R1} @ load loop start position to R0 and sample end to R1
ADD R3, R0, R3 @ R3 = loop start position (absolute)
STR R3, @ store loop start position
SUB R0, R1, R0
mixing_loop_setup:
STR R0, @ store the loop length (if loop is off ==> = 0x0)
LDR R5, hq_buffer_label @ load the hq_buffer offset
LDR R2, @ load the remaining samples for channel
LDR R3, @ load current stream position (abs)
LDRB R0, @ load channel mode to R0
ADR R1, mixing_arm_setup
BX R1 @ jump to the arm procedure
.align 2
hq_buffer_label:
.word hq_buffer
hq_buffer_length_label:
.word 0xFFFFFFFF
.arm
mixing_arm_setup:
LDR R8, hq_buffer_length_label @ write MAX samples per frame to R8
ORRS R11, R10, R11, LSL#16 @ write Vol #2 to the high 16 bits and combine them in one word
BEQ switchto_thumb @ if volume is #0 for both channels we don't need to do any mixing and skip the mixing loop
TST R0, #MODE_FIXED_FREQ @ check if fixed freq mode is enabled
BNE fixed_mixing_setup @ if the flag is set enter the fixed freq mixing mode
TST R0, #MODE_COMP @ ### added compression check
BNE special_mixing @ goto the special mixing handler if reverse playback ir compression is enabled
STMFD SP!, {R4, R9, R12} @ free up some registers
MOVS R2, R2 @ if remaining samples == 2
ORREQ R0, R0, #MODE_SYNTH @ set the SYNTH flag if sample header is both 0
STREQB R0, @ store the channel mode again
ADD R4, R4, #0x1C @ add 0x1C to channel pointer
LDMIA R4, {R7, LR} @ R7 = Fine Position, LR = Frequency
MUL R4, R12, LR @ calc final frequency steps by multiplying the Default Frequency Facotr from R12 with the actual frequency
LDRSB R6, , #1 @ load the first sample and increment the sample pointer
LDRSB R12, @ load the next sample for interpolation
TST R0, #MODE_SYNTH @ is the Synth Mode enabled?
BNE init_synth
SUB R12, R12, R6 @ R12 = DELTA
MOVS R11, R11, LSR#1 @ move the volume level over one bit to the right
ADC R11, R11, #0x8000 @ halve both volume levels
BIC R11, R11, #0xFF00 @ remove bit 15 if it is set
MOV R1, R7 @ save the fine position in R1
UMLAL R1, R0, R4, R8 @ multiply the sample amount per frame with the step frequency
MOV R1, R1, LSR#23 @ divide the result by 0x800000 LONG DIVISION
ORR R0, R1, R0, LSL#9 @ Are less samples needed than there is actually needed to fill the buffer
CMP R2, R0 @ can we load a chunk of data without interruption to fill the buffer?
BLE split_sample_loading @ always jump
SUB R2, R2, R0 @ calculate remaining samples after this channel processing (to write it back to memory)
LDR R10, stack_capacity @ load some work area address to R10
ADD R10, R10, R0 @ place the work area pointer at the actual staart position
CMP R10, SP @ check if R10 is within the stack
ADD R10, R3, R0 @ place the sample pointer end in R10
ADR R9, custom_stack_3 @ load the custom stack to R9
STMIA R9, {R2, R10, SP} @ backup the sample block length, the end of sample and SP
CMPCC R0, #0x400 @ check if the block length is above #0x400 (too long)
BCS select_mixing_mode
BIC R1, R3, #3 @ write a word aligned address to R1
MOV R9, #0x04000000 @ write the DMA3 Sourceaddress
ADD R9, R9, #0xD4 @
ADD R0, R0, #7 @ add #7 to block length
MOV R0, R0, LSR#2 @ do a word alignment ont R0
SUB SP, SP, R0, LSL#2 @ calculate the final destination location for the DMA
AND R3, R3, #3 @ restore the alignment error to R3
ADD R3, R3, SP @ store the sample start position from the dma buffer in R3
ORR LR, R0, #0x84000000 @ add the block length + #0x84000000 (DMA INIT Value) to LR
STMIA R9, {R1, SP, LR} @ write the DMA values to the IO registers and copy data
.if DMA_FIX==1 @ ### DMA Fix
MOV R0, #0
MOV R1, R0
MOV R2, R1
STMIA R9, {R0, R1, R2}
.endif
select_mixing_mode:
SUBS R4, R4, #0x800000 @ check if sampling factor is lower than default rate
MOVPL R11, R11, LSL#1 @ multiply volume levels by #2 if condition is ture
ADR R0, math_resources @ load a functiom pointer
ADDPL R0, R0, #0x18 @ add 0x18 if we need to load more than 1 sample per step
SUBPLS R4, R4, #0x800000 @ do we need to always load 2 samples?
ADDPL R0, R0, #0x18 @ extend the pointer even further
ADDPL R4, R4, #0x800000 @ restore the original value
LDR R2, function_pointer @ load the function pointer variable
CMP R0, R2 @ check if the pointer is the same as in the variable
BEQ mixing_init @ if the user created routine already contains the right function we can skip the setup process
STR R0, function_pointer @ store the pointer to VAR
LDMIA R0, {R0-R2, R8-R10} @ load 6 opcodes from R0 to Registers
ADR LR, runtime_created_routine @ load the routine offset that is gonna be custom made in real time
create_routine_loop:
STMIA LR, {R0, R1} @ write the first 2 instructions
ADD LR, LR, #0x98 @ extend the function pointer to Instr #38
STMIA LR, {R0, R1} @ write the same instruction here
SUB LR, LR, #0x8C @ go to Instr #3
STMIA LR, {R2, R8-R10} @ write #4 more instructions
ADD LR, LR, #0x98 @ jump to Instr #41
STMIA LR, {R2, R8-R10} @ write those instructions aswell
SUB LR, LR, #0x80 @ jump to next instruction block
ADDS R5, R5, #0x40000000 @ do a loop until all 4 blocks are written to instruction block
BCC create_routine_loop
LDR R8, hq_buffer_length_label @ load buffer length ins samples to R8
mixing_init:
MOV R2, #0xFF000000 @ load the fine position overflow bitmask
mixing_loop:
LDMIA R5, {R0, R1, R10, LR} @ load 4 stereo samples to Registers
MUL R9, R7, R12 @ multiply DELTA with fine position
runtime_created_routine:
NOP @ Block #1
NOP
MLANE R0, R11, R9, R0
NOP
NOP
NOP
NOP
BIC R7, R7, R2, ASR#1
MULNE R9, R7, R12
NOP @ Block #2
NOP
MLANE R1, R11, R9, R1
NOP
NOP
NOP
NOP
BIC R7, R7, R2, ASR#1
MULNE R9, R7, R12
NOP @ Block #3
NOP
MLANE R10, R11, R9, R10
NOP
NOP
NOP
NOP
BIC R7, R7, R2, ASR#1
MULNE R9, R7, R12
NOP @ Block #4
NOP
MLANE LR, R11, R9, LR
NOP
NOP
NOP
NOP
BIC R7, R7, R2, ASR#1
STMIA R5!, {R0, R1, R10, LR} @ write 4 stereo samples
LDMIA R5, {R0, R1, R10, LR} @ load the next 4 stereo samples
MULNE R9, R7, R12 @ calc next DELTA
NOP @ Block #1
NOP
MLANE R0, R11, R9, R0
NOP
NOP
NOP
NOP
BIC R7, R7, R2, ASR#1
MULNE R9, R7, R12
NOP @ Block #2
NOP
MLANE R1, R11, R9, R1
NOP
NOP
NOP
NOP
BIC R7, R7, R2, ASR#1
MULNE R9, R7, R12
NOP @ Block #3
NOP
MLANE R10, R11, R9, R10
NOP
NOP
NOP
NOP
BIC R7, R7, R2, ASR#1
MULNE R9, R7, R12
NOP @ Block #4
NOP
MLANE LR, R11, R9, LR
NOP
NOP
NOP
NOP
BIC R7, R7, R2, ASR#1
STMIA R5!, {R0, R1, R10, LR} @ write 4 stereo samples
SUBS R8, R8, #8 @ subtract 8 from the sample count
BGT mixing_loop
ADR R12, custom_stack_3 @ reload saved values
LDMIA R12, {R2, R3, SP}
B mixing_end_func
@ work variables
.align 2
custom_stack_3:
.word 0x0, 0x0, 0x0
stack_capacity:
.word 0x03007910
function_pointer:
.word 0x0
@ math resources, not directly used
math_resources:
MOV R9, R9, ASR#22 @ Frequency Lower than default Frequency
ADDS R9, R9, R6, LSL#1
ADDS R7, R7, R4
ADDPL R6, R12, R6
LDRPLSB R12, !
SUBPLS R12, R12, R6
ADDS R9, R6, R9, ASR#23 @ Frequency < 2x && Frequency > default frequency
ADD R6, R12, R6
ADDS R7, R7, R4
LDRPLSB R6, !
LDRSB R12, !
SUBS R12, R12, R6
ADDS R9, R6, R9, ASR#23 @ Frequency >= 2x higher than default Frequency
ADD R7, R7, R4
ADD R3, R3, R7, LSR#23
LDRSB R6,
LDRSB R12, !
SUBS R12, R12, R6
split_sample_loading:
ADD R5, R5, R8, LSL#2 @ R5 = End of HQ buffer
uncached_mixing_loop:
MUL R9, R7, R12 @ calc interpolated DELTA
MOV R9, R9, ASR#22 @ scale down the DELTA
ADDS R9, R9, R6, LSL#1 @ Add to Base Sample (upscaled to 8 bits again)
LDRNE R0, @ load sample from buffer
MLANE R0, R11, R9, R0 @ add it to the buffer sample
STRNE R0, @ write the sample
ADD R7, R7, R4 @ add the step size to the fine position
MOVS R9, R7, LSR#23 @ write the overflow amount to R9
BEQ uncached_mixing_load_skip @ skip the mixing load if it isn't required
SUBS R2, R2, R7, LSR#23 @ remove the overflow count from the remaning samples
BLLE loop_end_sub @ if the loop end is reached call the loop handler
SUBS R9, R9, #1 @ remove #1 from the overflow count
ADDEQ R6, R12, R6 @ new base sample is previous sample + DELTA
@RETURN LOCATION FROM LOOP HANDLER
LDRNESB R6, ! @ load new sample
LDRSB R12, ! @ load the delta sample (always required)
SUB R12, R12, R6 @ calc new DELTA
BIC R7, R7, #0x3F800000 @ clear the overflow from the fine position by using the bitmask
uncached_mixing_load_skip:
SUBS R8, R8, #1 @ reduce the sample count for the buffer by #1
BGT uncached_mixing_loop
mixing_end_func:
SUB R3, R3, #1 @ reduce sample pointer by #1
LDMFD SP!, {R4, R9, R12} @ pop values from stack
STR R7, @ store the fine position
B store_coarse_sample_pos @ jump over to code to store coarse channel position
loop_end_sub:
ADD R3, SP, #ARG_LOOP_START_POS+0xC @ prepare sample loop start loading and lopo length loading (0xC due to the pushed stack pointer)
LDMIA R3, {R3, R6} @ R3 = Loop Start; R6 = Loop Length
CMP R6, #0 @ check if loop is enabled; if Loop is enabled R6 is != 0
RSBNE R9, R2, #0 @ the sample overflow from the resampling needs to get subtracted so the remaining samples is slightly less
ADDNE R2, R6, R2 @ R2 = add the loop length
ADDNE PC, LR, #8 @ return from the subroutine to 2 instructions after the actual return location
LDMFD SP!, {R4, R9, R12} @ restore registers from stack
B update_channel_status
fixed_freq_loop_end_handler:
LDR R2, @ load the loop length value
MOVS R6, R2 @ copy it to R6 and check if loop is disabled
LDRNE R3, @ reset the sample pointer to the loop start position
BXNE LR @ if it loops return to mixing function, if it doesn't go on and end mixing
LDMFD SP!, {R4, R9}
update_channel_status:
STRB R6, @ if loop ist disabled R6 = 0 and we can disable the channel by writing R6 to R4 (channel area)
B switchto_thumb @ switch to thumb
fixed_math_resource: @ not exectued, used to create mixing function
MOVS R6, R10, LSL#24
MOVS R6, R6, ASR#24
MOVS R6, R10, LSL#16
MOVS R6, R6, ASR#24
MOVS R6, R10, LSL#8
MOVS R6, R6, ASR#24
MOVS R6, R10, ASR#24
LDMIA R3!, {R10} @ load chunk of samples
MOVS R6, R10, LSL#24
MOVS R6, R6, ASR#24
MOVS R6, R10, LSL#16
MOVS R6, R6, ASR#24
MOVS R6, R10, LSL#8
MOVS R6, R6, ASR#24
LDMFD SP!, {R4, R9, R12}
fixed_mixing_setup:
STMFD SP!, {R4, R9} @ backup the channel pointer and
fixed_mixing_check_length:
MOV LR, R2 @ move absolute sample position to LR
CMP R2, R8 @
MOVGT LR, R8 @ if there is less samples than the buffer to process write the smaller sample amount to LR
SUB LR, LR, #1 @ shorten samples to process by #1
MOVS LR, LR, LSR#2 @ calculate the amount of words to process (-1/4)
BEQ fixed_mixing_process_unaligned @ process the unaligned samples if there is <= 3 samples to process
SUB R8, R8, LR, LSL#2 @ subtract the amount of samples we need to process from the buffer length
SUB R2, R2, LR, LSL#2 @ subtract the amount of samples we need to process from the remaining samples
ADR R1, fixed_mixing_custom_routine
ADR R0, fixed_math_resource @ load the 2 pointers to create function (@R0) by instructions from R1
MOV R9, R3, LSL#30 @ move sample alignment bits to the leftmost position
ADD R0, R0, R9, LSR#27 @ alignment * 8 + resource offset = new resource offset
LDMIA R0!, {R6, R7, R9, R10} @ load 4 instructions
STMIA R1, {R6, R7} @ write the 1st 2 instructions
ADD R1, R1, #0xC @ move label pointer over to the next slot
STMIA R1, {R9, R10} @ write 2nd block
ADD R1, R1, #0xC @ move label pointer to next block
LDMIA R0, {R6, R7, R9, R10} @ load instructions for block #3 and #4
STMIA R1, {R6, R7} @ write block #3
ADD R1, R1, #0xC @ ...
STMIA R1, {R9, R10} @ write block #4
LDMIA R3!, {R10} @ write read 4 samples from ROM
fixed_mixing_loop:
LDMIA R5, {R0, R1, R7, R9} @ load 4 samples from hq buffer
fixed_mixing_custom_routine:
NOP
NOP
MLANE R0, R11, R6, R0 @ add new sample if neccessary
NOP
NOP
MLANE R1, R11, R6, R1
NOP
NOP
MLANE R7, R11, R6, R7
NOP
NOP
MLANE R9, R11, R6, R9
STMIA R5!, {R0, R1, R7, R9} @ write the samples to the work area buffer
SUBS LR, LR, #1 @ countdown the sample blocks to process
BNE fixed_mixing_loop @ if the end wasn't reached yet, repeat the loop
SUB R3, R3, #4 @ reduce sample position by #4, we'll need to load the samples again
fixed_mixing_process_unaligned:
MOV R1, #4 @ we need to repeat the loop #4 times to completley get rid of alignment errors
fixed_mixing_unaligned_loop:
LDR R0, @ load sample from buffer
LDRSB R6, , #1 @ load sample from ROM ro R6
MLA R0, R11, R6, R0 @ write the sample to the buffer
STR R0, , #4
SUBS R2, R2, #1 @ reduce alignment error by #1
BLEQ fixed_freq_loop_end_handler
SUBS R1, R1, #1
BGT fixed_mixing_unaligned_loop @ repeat the loop #4 times
SUBS R8, R8, #4 @ reduce the sample amount we wrote to the buffer by #1
BGT fixed_mixing_check_length @ go up to repeat the mixing procedure until the buffer is filled
LDMFD SP!, {R4, R9} @ pop registers from stack
store_coarse_sample_pos:
STR R2, @ store relative and absolute sample position
STR R3,
switchto_thumb:
ADR R0, (check_remain_channels+1) @ load the label offset and switch to thumb
BX R0
.thumb
check_remain_channels:
LDR R0, @ load the remaining channels
SUB R0, #1 @ reduce the amount by #1
BLE mixer_return @ end the mixing when finished processing all channels
ADD R4, #0x40
B mixer_entry
mixer_return:
ADR R0, downsampler
BX R0
downsampler_return:
LDR R0, @ load the main var area to R0
LDR R3, mixer_finished_status @ load some status indication value to R3
STR R3, @ store this value to the main var area
ADD SP, SP, #0x1C
POP {R0-R7}
MOV R8, R0
MOV R9, R1
MOV R10, R2
MOV R11, R3
POP {R3}
BX R3
.align 2
mixer_finished_status:
.word 0x68736D53
.arm
downsampler:
LDR R10, hq_buffer_label
LDR R9,
LDR R8, hq_buffer_length_label
MOV R11, #0xFF
.if PREVENT_CLIP==1
MOV R12, #0xFFFFFFFF
MOV R12, R12, LSL#14
MOV R7, #0x630
downsampler_loop:
LDRSH R2, , #2
LDRSH R0, , #2
LDRSH R3, , #2
LDRSH R1, , #2
CMP R0, #0x4000
MOVGE R0, #0x3F80
CMP R0, #-0x4000
MOVLT R0, R12
CMP R1, #0x4000
MOVGE R1, #0x3F80
CMP R1, #-0x4000
MOVLT R1, R12
CMP R2, #0x4000
MOVGE R2, #0x3F80
CMP R2, #-0x4000
MOVLT R2, R12
CMP R3, #0x4000
MOVGE R3, #0x3F80
CMP R3, #-0x4000
MOVLT R3, R12
AND R0, R11, R0, ASR#7
AND R1, R11, R1, ASR#7
AND R2, R11, R2, ASR#7
AND R3, R11, R3, ASR#7
ORR R2, R2, R3, LSL#8
ORR R0, R0, R1, LSL#8
STRH R2,
STRH R0, , #2
SUBS R8, #2
BGT downsampler_loop
.else
downsampler_loop:
LDRH R4, , #2
LDRH R0, , #2
LDRH R5, , #2
LDRH R1, , #2
LDRH R6, , #2
LDRH R2, , #2
LDRH R7, , #2
LDRH R3, , #2
AND R0, R11, R0, LSR#7
AND R1, R11, R1, LSR#7
AND R2, R11, R2, LSR#7
AND R3, R11, R3, LSR#7
AND R4, R11, R4, LSR#7
AND R5, R11, R5, LSR#7
AND R6, R11, R6, LSR#7
AND R7, R11, R7, LSR#7
ORR R4, R4, R5, LSL#8
ORR R4, R4, R6, LSL#16
ORR R4, R4, R7, LSL#24
ORR R0, R0, R1, LSL#8
ORR R0, R0, R2, LSL#16
ORR R0, R0, R3, LSL#24
STR R4,
STR R0, , #4
SUBS R8, #4
BGT downsampler_loop
.endif
ADR R0, (downsampler_return+1)
BX R0
.align 2
init_synth:
CMP R12, #0 @ $030057C4
BNE check_synth_type
LDRB R6, @ for saw wave -> 0xF0 (base duty cycle change)
ADD R2, R2, R6, LSL#24 @ add it to the current synt
LDRB R6, @ for saw wave -> 0x80 (base duty cycle change #2)
ADDS R6, R2, R6, LSL#24 @ add this to the synth state aswell but keep the old value in R2 and put the new one in R6
MVNMI R6, R6 @ negate if duty cycle is > 50%
MOV R10, R6, LSR#8 @ dividide the final duty cycle by 8 to R10
LDRB R1, @ for saw wave -> 0xE0
LDRB R0, @ for saw wave -> 0x10 (base duty cycle offset)
MOV R0, R0, LSL#24 @ convert it to a usable duty cycle
MLA R6, R10, R1, R0 @ calculate the final duty cycle with the offset, and intensity * rotating duty cycle amount
STMFD SP!, {R2, R3, R9, R12}
synth_type_0_loop:
LDMIA R5, {R0-R3, R9, R10, R12, LR} @ load 8 samples
CMP R7, R6 @ Block #1
ADDCC R0, R0, R11, LSL#6
SUBCS R0, R0, R11, LSL#6
ADDS R7, R7, R4, LSL#3
CMP R7, R6 @ Block #2
ADDCC R1, R1, R11, LSL#6
SUBCS R1, R1, R11, LSL#6
ADDS R7, R7, R4, LSL#3
CMP R7, R6 @ Block #3
ADDCC R2, R2, R11, LSL#6
SUBCS R2, R2, R11, LSL#6
ADDS R7, R7, R4, LSL#3
CMP R7, R6 @ Block #4
ADDCC R3, R3, R11, LSL#6
SUBCS R3, R3, R11, LSL#6
ADDS R7, R7, R4, LSL#3
CMP R7, R6 @ Block #5
ADDCC R9, R9, R11, LSL#6
SUBCS R9, R9, R11, LSL#6
ADDS R7, R7, R4, LSL#3
CMP R7, R6 @ Block #6
ADDCC R10, R10, R11, LSL#6
SUBCS R10, R10, R11, LSL#6
ADDS R7, R7, R4, LSL#3
CMP R7, R6 @ Block #7
ADDCC R12, R12, R11, LSL#6
SUBCS R12, R12, R11, LSL#6
ADDS R7, R7, R4, LSL#3
CMP R7, R6 @ Block #8
ADDCC LR, LR, R11, LSL#6
SUBCS LR, LR, R11, LSL#6
ADDS R7, R7, R4, LSL#3
STMIA R5!, {R0-R3, R9, R10, R12, LR} @ write 8 samples
SUBS R8, R8, #8 @ remove #8 from sample count
BGT synth_type_0_loop
LDMFD SP!, {R2, R3, R9, R12}
B mixing_end_func
check_synth_type:
SUBS R12, R12, #1 @ remove #1 from the synth type byte and check if it's #0
BNE synth_type_2 @ if it still isn't it's synth type 2 (smooth pan flute)
.if ENABLE_FM==1
B setup_fm
.else
MOV R6, #0x300 @ R6 = 0x300
MOV R11, R11, LSR#1 @ halve the volume
BIC R11, R11, #0xFF00 @ clear bad bits from division
MOV R12, #0x70 @ R12 = 0x70
synth_type_1_loop:
LDMIA R5, {R0, R1, R10, LR} @ load 4 samples from memory
ADDS R7, R7, R4, LSL#3 @ Block #1 (some oscillator type code)
RSB R9, R12, R7, LSR#24
MOV R6, R7, LSL#1
SUB R9, R9, R6, LSR#27
ADDS R2, R9, R2, ASR#1
MLANE R0, R11, R2, R0
ADDS R7, R7, R4, LSL#3 @ Block #2
RSB R9, R12, R7, LSR#24
MOV R6, R7, LSL#1
SUB R9, R9, R6, LSR#27
ADDS R2, R9, R2, ASR#1
MLANE R1, R11, R2, R1
ADDS R7, R7, R4, LSL#3 @ Block #3
RSB R9, R12, R7, LSR#24
MOV R6, R7, LSL#1
SUB R9, R9, R6, LSR#27
ADDS R2, R9, R2, ASR#1
MLANE R10, R11, R2, R10
ADDS R7, R7, R4, LSL#3 @ Block #4
RSB R9, R12, R7, LSR#24
MOV R6, R7, LSL#1
SUB R9, R9, R6, LSR#27
ADDS R2, R9, R2, ASR#1
MLANE LR, R11, R2, LR
STMIA R5!, {R0, R1, R10, LR}
SUBS R8, R8, #4
BGT synth_type_1_loop
B mixing_end_func @ goto end
.endif
synth_type_2:
MOV R6, #0x80 @ write base values to the registers
MOV R12, #0x180
synth_type_2_loop:
LDMIA R5, {R0, R1, R10, LR} @ load samples from work buffer
ADDS R7, R7, R4, LSL#3 @ Block #1
RSBPL R9, R6, R7, ASR#23
SUBMI R9, R12, R7, LSR#23
MLA R0, R11, R9, R0
ADDS R7, R7, R4, LSL#3 @ Block #2
RSBPL R9, R6, R7, ASR#23
SUBMI R9, R12, R7, LSR#23
MLA R1, R11, R9, R1
ADDS R7, R7, R4, LSL#3 @ Block #3
RSBPL R9, R6, R7, ASR#23
SUBMI R9, R12, R7, LSR#23
MLA R10, R11, R9, R10
ADDS R7, R7, R4, LSL#3 @ Block #4
RSBPL R9, R6, R7, ASR#23
SUBMI R9, R12, R7, LSR#23
MLA LR, R11, R9, LR
STMIA R5!, {R0, R1, R10, LR} @ store the samples back to the buffer
SUBS R8, R8, #4 @ subtract #4 from the remainging samples
BGT synth_type_2_loop
B mixing_end_func
@****************** SPECIAL MIXING ******************@
.if ENABLE_DECOMPRESSION==1
special_mixing: @ $03006BF8
LDR R6, @ load the wave header offset to R6
LDRB R0,
TST R0, #FLAG_CHN_COMP @ check if the channel is initialized
BNE setup_compressed_mixing_frequency @ skip the setup procedure if it's running in compressed mode already
ORR R0, R0, #FLAG_CHN_COMP @ enable the flag in the channel status
STRB R0, @ store the channel status
LDRB R0, @ load the channel mode byte
TST R0, #MODE_REVERSE @ check if reverse mode is not enabled
BEQ determine_compression @ if Reverse Mode isn't enabled we can directly check if the sample has to get decoded
LDR R1, @ load the amount of samples
ADD R1, R1, R6, LSL#1 @ do some start position calculation
ADD R1, R1, #0x20
SUB R3, R1, R3
STR R3, @ store the final seek position
determine_compression:
LDRH R0, @ load the compression flag from the sample header
CMP R0, #0 @ check if the compression is not enabled
BEQ setup_compressed_mixing_frequency @ skip the compression handler
SUB R3, R3, R6 @ calc initial position
SUB R3, R3, #0x10
STR R3, @ store the inital position (relative, not absolute)
setup_compressed_mixing_frequency:
STMFD SP!, {R4, R9, R12}
MOVS R11, R11, LSR#1 @ divide master volume by 2
ADC R11, R11, #0x8000
BIC R11, R11, #0xFF00
LDR R7, @ load the fine position
LDR R1, @ load the channel frequency
LDRB R0, @ load the channel mode again
TST R0, #MODE_FIXED_FREQ @ check if fixed frequency mode is enabled
MOVNE R1, #0x800000 @ ### SAMPLE STEP FREQUENCY CHANGED TO R7
MULEQ R1, R12, R1 @ default rate factor * frequency = sample steps
ADD R5, R5, R8, LSL#2 @ set the buffer pointer to the end of the channel
LDRH R0, @ load the codec type
CMP R0, #0 @ check if compression is disabled
BEQ uncompressed_mixing_reverse_check
MOV R0, #0xFF000000 @ set the current decoding block to "something very high" so that the first block always gets decoded
STR R0, @ write the last decoded block into the channel vars
LDRB R0, @ check again if reverse mode is enabled
TST R0, #MODE_REVERSE @ test if reverse mode is enabled
BNE compressed_mixing_reverse_init @ check again of reverse mixing is enabled
BL bdpcm_decoder @ load a sample from the stream to R12
MOV R6, R12 @ move the base sample to R6
ADD R3, R3, #1 @ increase stream position by #1
BL bdpcm_decoder @ load the delta sample and calculate delta value
SUB R12, R12, R6
@***** MIXING LOOP REGISTER USAGE ***********@
@ R0: Sample to modify from buffer
@ R1: sample steps (MOVED FROM R4)
@ R2: remaining samples before loop/end
@ R3: sample position
@ R4: channel pointer
@ R5: pointer to the end of buffer
@ R6: Base sample
@ R7: fine position
@ R8: remaining samples for current buffer
@ R9: interpolated sample
@ R10: not used
@ R11: volume
@ R12: Delta Sample
@ LR: not used
@********************************************@
compressed_mixing_loop:
MUL R9, R7, R12 @ delta sample * fine position = interpolated DELTA
MOV R9, R9, ASR#22 @ scale down the sample
ADDS R9, R9, R6, LSL#1 @ double the base sample and add it to the interpolated downscaled DELTA
LDRNE R0, @ if the sample is NOT 0 load the sample from buffer and store the calulated value
MLANE R0, R11, R9, R0 @ add the sample to the buffer sample and apply volume
STRNE R0, @ store the sample if it's not Zero
ADD R7, R7, R1 @ ### changed from R4 to R1
MOVS R9, R7, LSR#23 @ check if there is new samples to load
BEQ compressed_mixing_load_skip @ no new samples need to be loaded
SUBS R2, R2, R7, LSR#23 @ remove the sample overflow from the remaining samples
BLLE loop_end_sub @ call the loop/ending handler if the countdown reached zero or something negative
SUBS R9, R9, #1 @ check if only one sample has to get loaded
ADDEQ R6, R12, R6 @ if this is the case we can calculate the new base sample
BEQ compressed_mixing_base_load_skip
ADD R3, R3, R9 @ these opcodes are equivalent to LDRNESB R6, !
BL bdpcm_decoder
MOV R6, R12
compressed_mixing_base_load_skip:
ADD R3, R3, #1 @ equivalent to LDRSB R12, !
BL bdpcm_decoder
SUB R12, R12, R6
BIC R7, R7, #0x3F800000 @ clear the overflow bits by using the according bitmask
compressed_mixing_load_skip:
SUBS R8, R8, #1 @ remove #1 from the remaining samples
BGT compressed_mixing_loop
@SUB R3, R3, #1 @ sample pointer -1 ; ALREADY DONE BY mixing_end_func
B mixing_end_func
compressed_mixing_reverse_init:
SUB R3, R3, #1 @ subtract one from the reverse playback location initially
BL bdpcm_decoder @ fetch a sample from stream
MOV R6, R12 @ bdpcm_decoder returns base sample in R12 --> R6
SUB R3, R3, #1 @ seek one sample further backwards
BL bdpcm_decoder @ detch the DELTA sample
SUB R12, R12, R6 @ calc the Delta value
compressed_mixing_reverse_loop:
MUL R9, R7, R12 @ delta sample * fine position = interpolated DELTA
MOV R9, R9, ASR#22 @ scale down the sample
ADDS R9, R9, R6, LSL#1 @ double the base sample and add it to the interpolated downscaled DELTA
LDRNE R0, @ if the sample is NOT 0 load the sample from buffer and store the calulated value
MLANE R0, R11, R9, R0 @ add the sample to the buffer sample and apply volume
STRNE R0, @ store the sample if it's not Zero
ADD R7, R7, R1 @ ### changed from R4 to R1
MOVS R9, R7, LSR#23 @ check if there is new samples to load
BEQ compressed_mixing_reverse_load_skip @ skip sample loading if we don't need to load new samples from ROM
SUBS R2, R2, R7, LSR#23 @ remove the overflowed samples from the remaining samples
BLLE loop_end_sub @ if the sample playback finished go to end handler
SUBS R9, R9, #1 @ remove sample overflow count by #1
ADDEQ R6, R12, R6 @ make the previous delta sample the new base sample if only #1 sample needs to get loaded
BEQ compressed_mixing_reverse_base_load_skip @skip base sample loading
SUB R3, R3, R9 @
BL bdpcm_decoder @
MOV R6, R12 @
compressed_mixing_reverse_base_load_skip:
SUB R3, R3, #1
BL bdpcm_decoder
SUB R12, R12, R6 @ load next samples
BIC R7, R7, #0x3F800000 @ clear overflow bits
compressed_mixing_reverse_load_skip:
SUBS R8, R8, #1
BGT compressed_mixing_reverse_loop
@ADD R3, R3, #2 @ copied from original code
ADD R3, R3, #3
B mixing_end_func
uncompressed_mixing_reverse_check:
LDRB R0, @ load the channel mode =$03006D84
TST R0, #MODE_REVERSE @ check if reverse mode is even enabled
BEQ mixing_end_func @ skip the channel if the mode is "akward"
LDRSB R6, ! @ load first negative sample
LDRSB R12, @ load the DELTA sample
SUB R12, R12, R6 @ calculate DELTA
reverse_mixing_loop:
MUL R9, R7, R12 @ delta sample * fine position = interpolated DELTA
MOV R9, R9, ASR#22 @ scale down the sample
ADDS R9, R9, R6, LSL#1 @ double the base sample and add it to the interpolated downscaled DELTA
LDRNE R0, @ if the sample is NOT 0 load the sample from buffer and store the calulated value
MLANE R0, R11, R9, R0 @ add the sample to the buffer sample and apply volume
STRNE R0, @ store the sample if it's not Zero
ADD R7, R7, R1 @ ### changed from R4 to R1
MOVS R9, R7, LSR#23 @ check if there is new samples to load
BEQ reverse_mixing_load_skip
SUBS R2, R2, R7, LSR#23 @ blablabla, all same as above
BLLE loop_end_sub
MOVS R9, R9 @ check if sample
ADDEQ R6, R12, R6
LDRNESB R6, !
LDRSB R12, @ load samples dependent on conditions
SUB R12, R12, R6
BIC R7, R7, #0x3F800000 @ cut off overflow count to get new fine position
reverse_mixing_load_skip:
SUBS R8, R8, #1 @ remaining samples -1
BGT reverse_mixing_loop @ continue lopo if there is still samples to process
@ADD R3, R3, #1 @ copied from original code
ADD R3, R3, #2 @ =$03006DE8
B mixing_end_func
@**************** SPECIAL MIXING END ****************@
@************** SPECIAL MIXING LOOPING **************@
compressed_loop_end_sub:
@************ SPECIAL MIXING LOOPING END ************@
@****************** BDPCM DEOCODER ******************@
bdpcm_decoder: @ RETURNS SAMPLE FROM POSITION XXX in R12
STMFD SP!, {R0, R2, R5-R7, LR} @ push registers to make them free to use: R0, R2, R5, R6, R7, LR
MOV R0, R3, LSR#6 @ shift the relative position over to clip of every but the block offset
LDR R12, @ check if the current sample position is at the beginning of the current block
CMP R0, R12
BEQ bdpcm_decoder_return
STR R0, @ store the block position to Channel Vars
MOV R12, #0x21 @ load decoding byte count to R1 (1 Block = 0x21 Bytes)
MUL R2, R12, R0 @ multiply the block count with the block length to calc actual byte position of current block
LDR R12, @ load the wave data offset to R1
ADD R2, R2, R12 @ add the wave data offset and 0x10 to get the actual position in ROM
ADD R2, R2, #0x10 @
LDR R5, decoder_buffer @ load the decoder buffer pointer to R5
ADR R6, delta_lookup_table @ load the lookup table pointer to R6
MOV R7, #0x40 @ load the block sample count (0x40) to R7
LDRB LR, , #1 @ load the first byte & sample from the wave data to LR (each block starts with a signed 8 bit pcm sample) LDRSB not necessary due to the 24 high bits being cut off anyway
STRB LR, , #1 @ write the sample to the decoder buffer
LDRB R12, , #1 @ load the next 2 samples to R1 (to get decoded) --- LSBits is decoded first and MSBits last
B bdpcm_decoder_lsb
bdpcm_decoder_msb:
LDRB R12, , #1 @ load the next 2 samples to get decoded
MOV R0, R12, LSR#4 @ seperate the current samples' bits
LDRSB R0, @ load the differential value from the lookup table
ADD LR, LR, R0 @ add the decoded value to the previous sample value to calc the current samples' level
STRB LR, , #1 @ write the output sample to the decoder buffer and increment buffer pointer
bdpcm_decoder_lsb:
AND R0, R12, #0xF @ seperate the 4 LSBits
LDRSB R0, @ but the 4 bit value into the lookup table and save the result to R0
ADD LR, LR, R0 @ add the value from the lookup table to the previous value to calc the new one
STRB LR, , #1 @ store the decoded sample to the decoding buffer
SUBS R7, R7, #2 @ decrease the block sample counter by 2 (2 samples each byte) and check if it is still above 0
BGT bdpcm_decoder_msb @ if there is still samples to decode jump to the MSBits
bdpcm_decoder_return:
LDR R5, decoder_buffer @ reload the decompressor buffer offset to R5
AND R0, R3, #0x3F @ cut off the main position bits to read data from short buffer
LDRSB R12, @ read the decoded sample from buffer
LDMFD SP!, {R0, R2, R5-R7, PC} @ pop registers and return to the compressed sample mixer
@**************** END BDPCM DECODER *****************@
decoder_buffer:
.word decoder_buffer_target
delta_lookup_table:
.byte 0x0, 0x1, 0x4, 0x9, 0x10, 0x19, 0x24, 0x31, 0xC0, 0xCF, 0xDC, 0xE7, 0xF0, 0xF7, 0xFC, 0xFF
.endif
.if ENABLE_FM==1
sin_lookup:
.byte 0x00, 0x02, 0x03, 0x05, 0x06, 0x08, 0x09, 0x0B, 0x0C, 0x0E, 0x10, 0x11, 0x13, 0x14, 0x16, 0x17
.byte 0x19, 0x1A, 0x1C, 0x1D, 0x1F, 0x20, 0x22, 0x23, 0x25, 0x26, 0x28, 0x29, 0x2B, 0x2C, 0x2E, 0x2F
.byte 0x31, 0x32, 0x33, 0x35, 0x36, 0x38, 0x39, 0x3A, 0x3C, 0x3D, 0x3F, 0x40, 0x41, 0x43, 0x44, 0x45
.byte 0x47, 0x48, 0x49, 0x4A, 0x4C, 0x4D, 0x4E, 0x4F, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x58, 0x59
.byte 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69
.byte 0x6A, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x6F, 0x70, 0x71, 0x71, 0x72, 0x73, 0x73, 0x74, 0x75
.byte 0x75, 0x76, 0x76, 0x77, 0x78, 0x78, 0x79, 0x79, 0x7A, 0x7A, 0x7A, 0x7B, 0x7B, 0x7C, 0x7C, 0x7C
.byte 0x7D, 0x7D, 0x7D, 0x7D, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F
.byte 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7D, 0x7D, 0x7D
.byte 0x7D, 0x7C, 0x7C, 0x7C, 0x7B, 0x7B, 0x7A, 0x7A, 0x7A, 0x79, 0x79, 0x78, 0x78, 0x77, 0x76, 0x76
.byte 0x75, 0x75, 0x74, 0x73, 0x73, 0x72, 0x71, 0x71, 0x70, 0x6F, 0x6F, 0x6E, 0x6D, 0x6C, 0x6B, 0x6A
.byte 0x6A, 0x69, 0x68, 0x67, 0x66, 0x65, 0x64, 0x63, 0x62, 0x61, 0x60, 0x5F, 0x5E, 0x5D, 0x5C, 0x5B
.byte 0x5A, 0x59, 0x58, 0x56, 0x55, 0x54, 0x53, 0x52, 0x51, 0x4F, 0x4E, 0x4D, 0x4C, 0x4A, 0x49, 0x48
.byte 0x47, 0x45, 0x44, 0x43, 0x41, 0x40, 0x3F, 0x3D, 0x3C, 0x3A, 0x39, 0x38, 0x36, 0x35, 0x33, 0x32
.byte 0x31, 0x2F, 0x2E, 0x2C, 0x2B, 0x29, 0x28, 0x26, 0x25, 0x23, 0x22, 0x20, 0x1F, 0x1D, 0x1C, 0x1A
.byte 0x19, 0x17, 0x16, 0x14, 0x13, 0x11, 0x10, 0x0E, 0x0C, 0x0B, 0x09, 0x08, 0x06, 0x05, 0x03, 0x02
.byte 0x00, 0xFE, 0xFD, 0xFB, 0xFA, 0xF8, 0xF7, 0xF5, 0xF4, 0xF2, 0xF0, 0xEF, 0xED, 0xEC, 0xEA, 0xE9
.byte 0xE7, 0xE6, 0xE4, 0xE3, 0xE1, 0xE0, 0xDE, 0xDD, 0xDB, 0xDA, 0xD8, 0xD7, 0xD5, 0xD4, 0xD2, 0xD1
.byte 0xCF, 0xCE, 0xCD, 0xCB, 0xCA, 0xC8, 0xC7, 0xC6, 0xC4, 0xC3, 0xC1, 0xC0, 0xBF, 0xBD, 0xBC, 0xBB
.byte 0xB9, 0xB8, 0xB7, 0xB6, 0xB4, 0xB3, 0xB2, 0xB1, 0xAF, 0xAE, 0xAD, 0xAC, 0xAB, 0xAA, 0xA8, 0xA7
.byte 0xA6, 0xA5, 0xA4, 0xA3, 0xA2, 0xA1, 0xA0, 0x9F, 0x9E, 0x9D, 0x9C, 0x9B, 0x9A, 0x99, 0x98, 0x97
.byte 0x96, 0x96, 0x95, 0x94, 0x93, 0x92, 0x91, 0x91, 0x90, 0x8F, 0x8F, 0x8E, 0x8D, 0x8D, 0x8C, 0x8B
.byte 0x8B, 0x8A, 0x8A, 0x89, 0x88, 0x88, 0x87, 0x87, 0x86, 0x86, 0x86, 0x85, 0x85, 0x84, 0x84, 0x84
.byte 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82, 0x82, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81
.byte 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83
.byte 0x83, 0x84, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86, 0x86, 0x87, 0x87, 0x88, 0x88, 0x89, 0x8A, 0x8A
.byte 0x8B, 0x8B, 0x8C, 0x8D, 0x8D, 0x8E, 0x8F, 0x8F, 0x90, 0x91, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96
.byte 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5
.byte 0xA6, 0xA7, 0xA8, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB1, 0xB2, 0xB3, 0xB4, 0xB6, 0xB7, 0xB8
.byte 0xB9, 0xBB, 0xBC, 0xBD, 0xBF, 0xC0, 0xC1, 0xC3, 0xC4, 0xC6, 0xC7, 0xC8, 0xCA, 0xCB, 0xCD, 0xCE
.byte 0xCF, 0xD1, 0xD2, 0xD4, 0xD5, 0xD7, 0xD8, 0xDA, 0xDB, 0xDD, 0xDE, 0xE0, 0xE1, 0xE3, 0xE4, 0xE6
.byte 0xE7, 0xE9, 0xEA, 0xEC, 0xED, 0xEF, 0xF0, 0xF2, 0xF4, 0xF5, 0xF7, 0xF8, 0xFA, 0xFB, 0xFD, 0xFE
setup_fm:
LDRB R0, @ load frequency scaling factor
LDR R1, fm_freq_scaling_resource @ load base opcode
ORR R1, R1, R0, LSL#7 @ modify the left shift to match the setting
LDRB R0,
LDR R10, fm_intensity_resource @ same for the intensity
ORR R10, R10, R0, LSL#7
ADR LR, fm_loop_parse
fm_setup_loop:
STR R1,
STR R10,
ADD LR, LR, #32
ADDS R8, R8, #0x40000000
BCC fm_setup_loop
ADR R12, sin_lookup
fm_loop:
LDMIA R5, {R0, R1, R10, LR} @ load 4 samples from memory
fm_loop_parse:
ADDS R7, R7, R4, LSL#3
ADDS R2, R2, R4, LSL#6 @ Modulation Frequency Scaling Factor (LSL#3 = Bias Level)
MOV R9, R2, LSR#23
LDRSB R6,
ADDS R7, R7, R6, LSL#20 @ Modulation Intensity
MOV R9, R7, LSR#23
LDRSB R6,
MLA R0, R11, R6, R0
ADDS R7, R7, R4, LSL#3
ADDS R2, R2, R4, LSL#6
MOV R9, R2, LSR#23
LDRSB R6,
ADDS R7, R7, R6, LSL#20
MOV R9, R7, LSR#23
LDRSB R6,
MLA R1, R11, R6, R1
ADDS R7, R7, R4, LSL#3
ADDS R2, R2, R4, LSL#6
MOV R9, R2, LSR#23
LDRSB R6,
ADDS R7, R7, R6, LSL#20
MOV R9, R7, LSR#23
LDRSB R6,
MLA R10, R11, R6, R10
ADDS R7, R7, R4, LSL#3
ADDS R2, R2, R4, LSL#6
MOV R9, R2, LSR#23
LDRSB R6,
ADDS R7, R7, R6, LSL#20
MOV R9, R7, LSR#23
LDRSB R6,
MLA LR, R11, R6, LR
STMIA R5!, {R0, R1, R10, LR}
SUBS R8, R8, #4
BGT fm_loop
B mixing_end_func
fm_freq_scaling_resource:
ADDS R2, R2, R4
fm_intensity_resource:
ADDS R7, R7, R6
.endif
main_mixer_end:
.end 弄完测试加载到耿鬼VS尼多力诺动画那里死机了 本帖最后由 jiangzhengwenjz 于 2017-2-12 22:26 编辑
sfsefsdfsd 发表于 2017-2-12 14:13
弄完测试加载到耿鬼VS尼多力诺动画那里死机了
以前实测过没问题,可能是教程哪里写错了?我依据的是这个:.org 0x080007B4
.word0x0203E000 // new PCM work area
.org 0x081DD0B4
.wordmain_mixer // new mixer ROM location
.word0x03005F50 // new mixer RAM location (used for loading)
.halfword mixer_size
.halfword 0x400 // CpuSet, copy code by 32 bit units
.word0x0203E000 // new PCM work area
.org 0x081DD0C8
// set correct sound driver operation mode
// 12 channels at 26758 Hz samplerate
.byte0x00, 0xCC, 0x98, 0x00
.org 0x081DC094
.word0x03005F50+1 // new mixer RAM location (used for branch)其中唯一不同的恐怕也就是0x1DD0C8的位置吧,不过我之前姑且把它当做修改sample rate了。
.org就是写入的地址,.word,.halfword是写入4,2字节(反写Little Endian),.byte是依次写入一字节
页:
[1]