12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460 |
- @ created by ~ipatix~
- @ revision 2.1
-
- /* globals */
- .global main_mixer
- .global main_mixer_end
-
- /* game code definitions */
- .equ GAME_BPED, 0
- .equ GAME_BPEE, 1
- .equ GAME_BPRE, 2
- .equ GAME_KWJ6, 3
- .equ GAME_AE7E, 4
- .equ GAME_BPRD, 5
-
- /* 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 BUFFER_IRAM_BPE, 0x03001AA8
- .equ BUFFER_IRAM_BPR, 0x030028E0
- .equ BUFFER_IRAM_KWJ, 0x03005840
- .equ BUFFER_IRAM_AE7, 0x03006D60 @ PUT THE WORKBUFFER ADDRESS FOR FIRE EMBLEM HERE!!!
-
- /* stack variables */
- .equ ARG_FRAME_LENGTH, 0x0 @ TODO actually use this variable
- .equ ARG_REMAIN_CHN, 0x4 @ This is the channel count variable
- .equ ARG_BUFFER_POS, 0x8 @ stores the current output buffer pointer
- .equ ARG_LOOP_START_POS, 0xC @ stores wave loop start position in channel loop
- .equ ARG_LOOP_LENGTH, 0x10 @ '' '' '' end position
- @ .equ ARG_UKNOWN, 0x14
- .equ ARG_VAR_AREA, 0x18 @ pointer to engine the main work area
-
- /* channel struct */
- .equ CHN_STATUS, 0x0 @ [byte] channel status bitfield
- .equ CHN_MODE, 0x1 @ [byte] channel mode bitfield
- .equ CHN_VOL_1, 0x2 @ [byte] volume right
- .equ CHN_VOL_2, 0x3 @ [byte] volume left
- .equ CHN_ATTACK, 0x4 @ [byte] wave attack summand
- .equ CHN_DECAY, 0x5 @ [byte] wave decay factor
- .equ CHN_SUSTAIN, 0x6 @ [byte] wave sustain level
- .equ CHN_RELEASE, 0x7 @ [byte] wave release factor
- .equ CHN_ADSR_LEVEL, 0x9 @ [byte] current envelope level
- .equ CHN_FINAL_VOL_1, 0xA @ [byte] not used anymore!
- .equ CHN_FINAL_VOL_2, 0xB @ [byte] not used anymore!
- .equ CHN_ECHO_VOL, 0xC @ [byte] pseudo echo volume
- .equ CHN_ECHO_REMAIN, 0xD @ [byte] pseudo echo length
- .equ CHN_POSITION_REL, 0x18 @ [word] sample countdown in mixing loop
- .equ CHN_FINE_POSITION, 0x1C @ [word] inter sample position (23 bits)
- .equ CHN_FREQUENCY, 0x20 @ [word] sample rate (in Hz)
- .equ CHN_WAVE_OFFSET, 0x24 @ [word] wave header pointer
- .equ CHN_POSITION_ABS, 0x28 @ [word] points to the current position in the wave data (relative offset for compressed samples)
- .equ CHN_BLOCK_COUNT, 0x3C @ [word] only used for compressed samples: contains the value of the block that is currently decoded
-
- /* wave header struct */
- .equ WAVE_LOOP_FLAG, 0x3 @ [byte] 0x0 = oneshot; 0x40 = looped
- .equ WAVE_FREQ, 0x4 @ [word] pitch adjustment value = mid-C samplerate * 1024
- .equ WAVE_LOOP_START, 0x8 @ [word] loop start position
- .equ WAVE_LENGTH, 0xC @ [word] loop end / wave end position
- .equ WAVE_DATA, 0x10 @ [byte array] actual wave data
-
- /* pulse wave synth configuration offset */
- .equ SYNTH_BASE_WAVE_DUTY, 0x1 @ [byte]
- .equ SYNTH_WIDTH_CHANGE_1, 0x2 @ [byte]
- .equ SYNTH_MOD_AMOUNT, 0x3 @ [byte]
- .equ SYNTH_WIDTH_CHANGE_2, 0x4 @ [byte]
-
- /* CHN_STATUS flags - 0x0 = OFF */
- .equ FLAG_CHN_INIT, 0x80 @ [bit] write this value to init a channel
- .equ FLAG_CHN_RELEASE, 0x40 @ [bit] write this value to release (fade out) the channel
- .equ FLAG_CHN_COMP, 0x20 @ [bit] is wave being played compressed (yes/no)
- .equ FLAG_CHN_LOOP, 0x10 @ [bit] loop (yes/no)
- .equ FLAG_CHN_ECHO, 0x4 @ [bit] echo phase
- .equ FLAG_CHN_ATTACK, 0x3 @ [bit] attack phase
- .equ FLAG_CHN_DECAY, 0x2 @ [bit] decay phase
- .equ FLAG_CHN_SUSTAIN, 0x1 @ [bit] sustain phase
-
- /* CHN_MODE flags */
- .equ MODE_FIXED_FREQ, 0x8 @ [bit] set to disable resampling (i.e. playback with output rate)
- .equ MODE_REVERSE, 0x10 @ [bit] set to reverse sample playback
- .equ MODE_COMP, 0x30 @ [bit] is wave being played compressed or reversed (TODO: rename flag)
- .equ MODE_SYNTH, 0x40 @ [bit] READ ONLY, indicates synthzied output
-
- /* variables of the engine work area */
- .equ VAR_REVERB, 0x5 @ [byte] 0-127 = reverb level
- .equ VAR_MAX_CHN, 0x6 @ [byte] maximum channels to process
- .equ VAR_MASTER_VOL, 0x7 @ [byte] PCM master volume
- .equ VAR_DEF_PITCH_FAC, 0x18 @ [word] this value get's multiplied with the samplerate for the inter sample distance
- .equ VAR_FIRST_CHN, 0x50 @ [CHN struct] relative offset to channel array
-
- /* just some more defines */
- .equ REG_DMA3_SRC, 0x040000D4
- .equ ARM_OP_LEN, 0x4
-
- @#######################################
- @*********** GAME CONFIGS **************
- @ add the game's name above to the ASM .equ-s before creating new configs
- @#######################################
-
-
- @*********** IF GERMAN POKEMON EMERALD
- .if USED_GAME==GAME_BPED
-
- .equ hq_buffer, BUFFER_IRAM_BPE
- .equ decoder_buffer_target, DECODER_BUFFER_BPE
- .equ ALLOW_PAUSE, 1
- .equ DMA_FIX, 1
- .equ ENABLE_DECOMPRESSION, 1
- .equ PREVENT_CLIP, 1
-
- .endif
- @*********** IF ENGLISH POKEMON FIRE RED
- .if USED_GAME==GAME_BPRD
-
- .equ hq_buffer, 0x03002830
- .equ decoder_buffer_target, DECODER_BUFFER_BPR
- .equ ALLOW_PAUSE, 1
- .equ DMA_FIX, 1
- .equ ENABLE_DECOMPRESSION, 1
- .equ PREVENT_CLIP, 1
-
- .endif
- @*********** IF ENGLISH POKEMON EMERALD
- .if USED_GAME==GAME_BPEE
-
- .equ hq_buffer, BUFFER_IRAM_BPE
- .equ decoder_buffer_target, DECODER_BUFFER_BPE
- .equ ALLOW_PAUSE, 1
- .equ DMA_FIX, 1
- .equ ENABLE_DECOMPRESSION, 1
- .equ PREVENT_CLIP, 1
-
- .endif
- @*********** IF ENGLISH POKEMON FIRE RED
- .if USED_GAME==GAME_BPRE
-
- .equ hq_buffer, BUFFER_IRAM_BPR
- .equ decoder_buffer_target, DECODER_BUFFER_BPR
- .equ ALLOW_PAUSE, 1
- .equ DMA_FIX, 1
- .equ ENABLE_DECOMPRESSION, 1
- .equ PREVENT_CLIP, 1
-
- .endif
- @*********** IF KAWAs JUKEBOX 2006
- .if USED_GAME==GAME_KWJ6
-
- .equ hq_buffer, BUFFER_IRAM_KWJ
- .equ decoder_buffer_target, DECODER_BUFFER_KWJ
- .equ ALLOW_PAUSE, 0
- .equ DMA_FIX, 0
- .equ ENABLE_DECOMPRESSION, 0
- .equ PREVENT_CLIP, 1
-
- .endif
- @*********** IF US FIRE EMBLEM
- .if USED_GAME==GAME_AE7E
-
- .equ hq_buffer, BUFFER_IRAM_AE7
- .equ ALLOW_PAUSE, 0
- .equ DMA_FIX, 0
- .equ ENABLE_DECOMPRESSION, 0
- .equ PREVENT_CLIP, 0
- .endif
- @***********
-
- .thumb
-
- main_mixer:
- /* load Reverb level and check if we need to apply it */
- LDRB R3, [R0, #VAR_REVERB]
- LSR R3, R3, #2
- BEQ clear_buffer
-
- ADR R1, do_reverb
- BX R1
-
- .align 2
- .arm
-
- do_reverb:
-
- /*
- * reverb is calculated by the following: new_sample = old_sample * reverb_level / 127
- * note that reverb is mono (both sides get mixed together)
- *
- * reverb get's applied to the frame we are currently looking at and the one after that
- * the magic below simply calculateds the pointer for the one after the current one
- */
-
- CMP R4, #2
- ADDEQ R7, R0, #0x350
- ADDNE R7, R5, R8
- MOV R4, R8
- ORR R3, R3, R3, LSL#16
- STMFD SP!, {R8, LR}
- LDR LR, hq_buffer_label
-
- reverb_loop:
- /* This loop does the reverb processing */
- LDRSB R0, [R5, R6]
- LDRSB R1, [R5], #1
- LDRSB R2, [R7, R6]
- LDRSB R8, [R7], #1
- LDRSB R9, [R5, R6]
- LDRSB R10, [R5], #1
- LDRSB R11, [R7, R6]
- LDRSB R12, [R7], #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
- /* end of loop */
- LDMFD SP!, {R8, LR}
- ADR R0, (adsr_setup+1)
- BX R0
-
- .thumb
-
- clear_buffer:
- /* Incase reverb is disabled the buffer get's set to zero */
- LDR R3, hq_buffer_label
- MOV R1, R8
- MOV R4, #0
- MOV R5, #0
- MOV R6, #0
- MOV R7, #0
- /*
- * Setting the buffer to zero happens in a very efficient loop
- * Depending on the alignment of the buffer length, twice or quadruple the amount of bytes
- * get cleared at once
- */
- 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:
- /* This repeats until the buffer has been cleared */
- 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
- /* loop end */
- adsr_setup:
- /*
- * okay, before the actual mixing starts
- * the volume and envelope calculation happens
- */
- MOV R4, R8 @ R4 = buffer length
- /* this buffers the buffer length to a backup location
- * TODO: Move this variable to stack
- */
- ADR R0, hq_buffer_length_label
- STR R4, [R0]
- /* init channel loop */
- LDR R4, [SP, #ARG_VAR_AREA] @ R4 = main work area pointer
- LDR R0, [R4, #VAR_DEF_PITCH_FAC] @ R0 = samplingrate pitch factor
- MOV R12, R0 @ --> R12
- LDRB R0, [R4, #VAR_MAX_CHN] @ load MAX channels to R0
- ADD R4, #VAR_FIRST_CHN @ R4 = Base channel Offset (Channel #0)
-
- mixer_entry:
- /* this is the main channel processing loop */
- STR R0, [SP, #ARG_REMAIN_CHN]
- LDR R3, [R4, #CHN_WAVE_OFFSET]
- LDRB R6, [R4, #CHN_STATUS]
- 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 @ skip channel
- /* check channel flags */
- LSL R0, R6, #25 @ shift over the FLAG_CHN_INIT to CARRY
- BCC adsr_echo_check @ continue with normal channel procedure
- /* check leftmost bit */
- BMI stop_channel_handler @ if the channel is initiated but on release it gets turned off immediatley
- /* channel init procedure */
- MOVS R6, #FLAG_CHN_ATTACK @ set the channel status to ATTACK
- MOVS R0, R3 @ R0 = CHN_WAVE_OFFSET
- ADD R0, #WAVE_DATA @ R0 = wave data offset
-
- /* Pokemon games seem to init channels differently than other m4a games */
- .if ALLOW_PAUSE==0
- STR R0, [R4, #CHN_POSITION_ABS]
- LDR R0, [R3, #WAVE_LENGTH]
- STR R0, [R4, #CHN_POSITION_REL]
- .else
- LDR R1, [R4, #CHN_POSITION_REL]
- ADD R0, R0, R1
- STR R0, [R4, #CHN_POSITION_ABS]
- LDR R0, [R3, #WAVE_LENGTH]
- SUB R0, R0, R1
- STR R0, [R4, #CHN_POSITION_REL]
- .endif
-
- MOVS R5, #0 @ initial envelope = #0
- STRB R5, [R4, #CHN_ADSR_LEVEL]
- STR R5, [R4, #CHN_FINE_POSITION]
- LDRB R2, [R3, #WAVE_LOOP_FLAG]
- LSR R0, R2, #6
- BEQ adsr_attack_handler @ if loop disabled --> branch
- /* loop enabled here */
- MOVS R0, #FLAG_CHN_LOOP
- ORR R6, R0 @ update channel status
- B adsr_attack_handler
-
- adsr_echo_check:
- /* this is the normal ADSR procedure without init */
- LDRB R5, [R4, #CHN_ADSR_LEVEL]
- LSL R0, R6, #29 @ echo flag --> bit 31
- BPL adsr_release_check @ PL == false
- /* pseudo echo handler */
- LDRB R0, [R4, #CHN_ECHO_REMAIN]
- SUB R0, #1
- STRB R0, [R4, #CHN_ECHO_REMAIN]
- BHI channel_vol_calc @ if echo still on --> branch
-
- stop_channel_handler:
-
- MOVS R0, #0
- STRB R0, [R4, #CHN_STATUS]
-
- return_channel_null:
- /* go to end of the channel loop */
- B check_remain_channels
-
- adsr_release_check:
- LSL R0, R6, #25 @ bit 31 = release bit
- BPL adsr_decay_check @ if release == 0 --> branch
- /* release handler */
- LDRB R0, [R4, #CHN_RELEASE]
- @SUB R0, #0xFF @ linear decay; TODO make option for triggering it
- @SUB R0, #1
- @ADD R5, R5, R0
- MUL R5, R5, R0 @ default release algorithm
- LSR R5, R5, #8
- @BMI adsr_released_handler @ part of linear decay
- BEQ adsr_released_handler @ release gone down to #0 --> branch
- /* pseudo echo init handler */
- LDRB R0, [R4, #CHN_ECHO_VOL]
- CMP R5, R0
- BHI channel_vol_calc @ if release still above echo level --> branch
-
- adsr_released_handler:
- /* if volume released to #0 */
- LDRB R5, [R4, #CHN_ECHO_VOL] @ TODO: replace with MOV R5, R0
- CMP R5, #0
- BEQ stop_channel_handler @ if pseudo echo vol = 0 --> branch
- /* pseudo echo volume handler */
- MOVS R0, #FLAG_CHN_ECHO
- ORR R6, R0 @ set the echo flag
- B adsr_update_status
-
- adsr_decay_check:
- /* check if decay is active */
- MOVS R2, #3
- AND R2, R6 @ seperate phase status bits
- CMP R2, #FLAG_CHN_DECAY
- BNE adsr_attack_check @ decay not active --> branch
- /* decay handler */
- LDRB R0, [R4, #CHN_DECAY]
- MUL R5, R0
- LSR R5, R5, #8
- LDRB R0, [R4, #CHN_SUSTAIN]
- CMP R5, R0
- BHI channel_vol_calc @ sample didn't decay yet --> branch
- /* sustain handler */
- MOVS R5, R0 @ current level = sustain level
- BEQ adsr_released_handler @ sustain level #0 --> branch
- /* step to next phase otherweise */
- B adsr_switchto_next
-
- adsr_attack_check:
- /* attack handler */
- CMP R2, #FLAG_CHN_ATTACK
- BNE channel_vol_calc @ if it isn't in attack attack phase, it has to be in sustain (no adsr change needed) --> branch
-
- adsr_attack_handler:
- /* apply attack summand */
- LDRB R0, [R4, #CHN_ATTACK]
- ADD R5, R5, R0
- CMP R5, #0xFF
- BCC adsr_update_status
- /* cap attack at 0xFF */
- MOVS R5, #0xFF
-
- adsr_switchto_next:
- /* switch to next adsr phase */
- SUB R6, #1
-
- adsr_update_status:
- /* store channel status */
- STRB R6, [R4, #CHN_STATUS]
-
- channel_vol_calc:
- /* store the calculated ADSR level */
- STRB R5, [R4, #CHN_ADSR_LEVEL]
- /* apply master volume */
- LDR R0, [SP, #ARG_VAR_AREA]
- LDRB R0, [R0, #VAR_MASTER_VOL]
- ADD R0, #1
- MUL R5, R0, R5
- /* left side volume */
- LDRB R0, [R4, #CHN_VOL_2]
- MUL R0, R5
- LSR R0, R0, #13
- MOV R10, R0 @ R10 = left volume
- /* right side volume */
- LDRB R0, [R4, #CHN_VOL_1]
- MUL R0, R5
- LSR R0, R0, #13
- MOV R11, R0 @ R11 = right volume
- /*
- * Now we get closer to actual mixing:
- * For looped samples some additional operations are required
- */
- MOVS R0, #FLAG_CHN_LOOP
- AND R0, R6
- BEQ mixing_loop_setup @ TODO: This label should rather be called "skip_loop_setup"
- /* loop setup handler */
- ADD R3, #WAVE_LOOP_START
- LDMIA R3!, {R0, R1} @ R0 = loop start, R1 = loop end
- ADD R3, R0, R3 @ R3 = loop start position (absolute)
- STR R3, [SP, #ARG_LOOP_START_POS] @ backup loop start
- SUB R0, R1, R0
-
- mixing_loop_setup:
- /* do the rest of the setup */
- STR R0, [SP, #ARG_LOOP_LENGTH] @ if loop is off --> R0 = 0x0
- LDR R5, hq_buffer_label
- LDR R2, [R4, #CHN_POSITION_REL] @ remaining samples for channel
- LDR R3, [R4, #CHN_POSITION_ABS] @ current stream position (abs)
- LDRB R0, [R4, #CHN_MODE]
- ADR R1, mixing_arm_setup
- BX R1
-
- .align 2
- hq_buffer_label:
- .word hq_buffer
- hq_buffer_length_label: @ TODO: Replace with variable on stack
- .word 0xFFFFFFFF
-
- .arm
- mixing_arm_setup:
- /* frequency and mixing loading routine */
- LDR R8, hq_buffer_length_label
- ORRS R11, R10, R11, LSL#16 @ R11 = 00RR00LL
- BEQ switchto_thumb @ volume #0 --> branch and skip channel processing
- /* normal processing otherwise */
- TST R0, #MODE_FIXED_FREQ
- BNE fixed_mixing_setup
- TST R0, #MODE_COMP
- BNE special_mixing @ compressed? --> branch
- /* same here */
- STMFD SP!, {R4, R9, R12}
- /*
- * This mixer supports 4 different kind of synthesized sounds
- * They are triggered when the loop end = 0
- * This get's checked below
- */
- MOVS R2, R2
- ORREQ R0, R0, #MODE_SYNTH
- STREQB R0, [R4, #CHN_MODE]
- ADD R4, R4, #CHN_FINE_POSITION
- LDMIA R4, {R7, LR} @ R7 = Fine Position, LR = Frequency
- MUL R4, R12, LR @ R4 = inter sample steps = output rate factor * samplerate
- /* now the first samples get loaded */
- LDRSB R6, [R3], #1
- LDRSB R12, [R3]
- TST R0, #MODE_SYNTH
- BNE init_synth
- /* incase no synth mode should be used, code contiues here */
- SUB R12, R12, R6 @ R12 = DELTA
- /*
- * Mixing goes with volume ranges 0-127
- * They come in 0-255 --> divide by 2
- */
- MOVS R11, R11, LSR#1
- ADC R11, R11, #0x8000
- BIC R11, R11, #0xFF00
- MOV R1, R7 @ R1 = inter sample position
- /*
- * There is 2 different mixing codepaths for uncompressed data
- * path 1: fast mixing, but doesn't supports loop or stop
- * path 2: not so fast but supports sample loops / stop
- * This checks if there is enough samples aviable for path 1.
- * important: R0 is expected to be #0
- */
- UMLAL R1, R0, R4, R8
- MOV R1, R1, LSR#23
- ORR R0, R1, R0, LSL#9
- CMP R2, R0 @ actual comparison
- BLE split_sample_loading @ if not enough samples are available for path 1 --> branch
- /*
- * This is the mixer path 1.
- * The interesting thing here is that the code will
- * buffer enough samples on stack if enough space
- * on stack is available (or goes over the limit of 0x400 bytes)
- */
- SUB R2, R2, R0
- LDR R10, stack_capacity
- ADD R10, R10, R0
- CMP R10, SP
- ADD R10, R3, R0
- ADR R9, custom_stack_3
- /*
- * R2 = remaining samples
- * R10 = final sample position
- * SP = original stack location
- * These values will get reloaded after channel processing
- * due to the lack of registers.
- */
- STMIA R9, {R2, R10, SP}
- CMPCC R0, #0x400 @ > 0x400 bytes --> read directly from ROM rather than buffered
- BCS select_mixing_mode @ TODO rename
- /*
- * The code below inits the DMA to read word aligned
- * samples from ROM to stack
- */
- BIC R1, R3, #3
- MOV R9, #0x04000000
- ADD R9, R9, #0xD4
- ADD R0, R0, #7
- MOV R0, R0, LSR#2
- SUB SP, SP, R0, LSL#2
- AND R3, R3, #3
- ADD R3, R3, SP
- ORR LR, R0, #0x84000000
- STMIA R9, {R1, SP, LR} @ actually starts the DMA
-
- /* Somehow is neccesary for some games not to break */
- .if DMA_FIX==1
- MOV R0, #0
- MOV R1, R0
- MOV R2, R1
- STMIA R9, {R0, R1, R2}
- .endif
-
- select_mixing_mode:
- /*
- * This code decides which piece of code to load
- * depending on playback-rate / default-rate ratio.
- * Modes > 1.0 run with different volume levels.
- */
- SUBS R4, R4, #0x800000
- MOVPL R11, R11, LSL#1
- ADR R0, math_resources @ loads the base pointer of the code
- ADDPL R0, R0, #(ARM_OP_LEN*6) @ 6 instructions further
- SUBPLS R4, R4, #0x800000
- ADDPL R0, R0, #(ARM_OP_LEN*6)
- ADDPL R4, R4, #0x800000 @ TODO how does restoring for > 2.0 ratios work?
- LDR R2, function_pointer
- CMP R0, R2 @ code doesn't need to be reloaded if it's already in place
- BEQ mixing_init
- /* This loads the needed code to RAM */
- STR R0, function_pointer
- LDMIA R0, {R0-R2, R8-R10} @ load 6 opcodes
- ADR LR, runtime_created_routine
-
- create_routine_loop:
- /* paste code to destination, see below for patterns */
- STMIA LR, {R0, R1}
- ADD LR, LR, #0x98
- STMIA LR, {R0, R1}
- SUB LR, LR, #0x8C
- STMIA LR, {R2, R8-R10}
- ADD LR, LR, #0x98
- STMIA LR, {R2, R8-R10}
- SUB LR, LR, #0x80
- ADDS R5, R5, #0x40000000 @ do that for 4 blocks
- BCC create_routine_loop
-
- LDR R8, hq_buffer_length_label
-
- mixing_init:
- MOV R2, #0xFF000000 @ load the fine position overflow bitmask
- mixing_loop:
- /* This is the actual processing and interpolation code loop; NOPs will be replaced by the code above */
- LDMIA R5, {R0, R1, R10, LR} @ load 4 stereo samples to Registers
- MUL R9, R7, R12
- 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
- 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
- /* restore previously saved values */
- ADR R12, custom_stack_3
- 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, [R3, #1]!
- 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, [R3, #1]!
- LDRSB R12, [R3, #1]!
- 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, [R3]
- LDRSB R12, [R3, #1]!
- 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, [R5, -R8, LSL#2] @ load sample from buffer
- MLANE R0, R11, R9, R0 @ add it to the buffer sample
- STRNE R0, [R5, -R8, LSL#2] @ 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, [R3, R9]! @ load new sample
- LDRSB R12, [R3, #1]! @ 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, [R4, #CHN_FINE_POSITION] @ 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, [SP, #ARG_LOOP_LENGTH+0x8] @ load the loop length value
- MOVS R6, R2 @ copy it to R6 and check if loop is disabled
- LDRNE R3, [SP, #ARG_LOOP_START_POS+0x8] @ 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, [R4] @ 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, [R5] @ load sample from buffer
- LDRSB R6, [R3], #1 @ load sample from ROM ro R6
- MLA R0, R11, R6, R0 @ write the sample to the buffer
- STR R0, [R5], #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, [R4, #CHN_POSITION_REL] @ store relative and absolute sample position
- STR R3, [R4, #CHN_POSITION_ABS]
-
- switchto_thumb:
-
- ADR R0, (check_remain_channels+1) @ load the label offset and switch to thumb
- BX R0
-
- .thumb
-
- check_remain_channels:
-
- LDR R0, [SP, #ARG_REMAIN_CHN] @ 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, [SP, #ARG_VAR_AREA] @ load the main var area to R0
- LDR R3, mixer_finished_status @ load some status indication value to R3
- STR R3, [R0] @ 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, [SP, #ARG_BUFFER_POS]
- 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, [R10], #2
- LDRSH R0, [R10], #2
- LDRSH R3, [R10], #2
- LDRSH R1, [R10], #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, [R9, R7]
- STRH R0, [R9], #2
-
- SUBS R8, #2
- BGT downsampler_loop
-
- .else
- downsampler_loop:
-
- LDRH R4, [R10], #2
- LDRH R0, [R10], #2
- LDRH R5, [R10], #2
- LDRH R1, [R10], #2
- LDRH R6, [R10], #2
- LDRH R2, [R10], #2
- LDRH R7, [R10], #2
- LDRH R3, [R10], #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, [R9, #0x630]
- STR R0, [R9], #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, [R3, #SYNTH_WIDTH_CHANGE_1] @ for saw wave -> 0xF0 (base duty cycle change)
- ADD R2, R2, R6, LSL#24 @ add it to the current synt
- LDRB R6, [R3, #SYNTH_WIDTH_CHANGE_2] @ 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, [R3, #SYNTH_MOD_AMOUNT] @ for saw wave -> 0xE0
- LDRB R0, [R3, #SYNTH_BASE_WAVE_DUTY] @ 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)
-
- 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
-
- 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, [R4, #CHN_WAVE_OFFSET] @ load the wave header offset to R6
- LDRB R0, [R4]
- 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, [R4] @ store the channel status
- LDRB R0, [R4, #CHN_MODE] @ 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, [R6, #WAVE_LENGTH] @ 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, [R4, #CHN_POSITION_ABS] @ store the final seek position
-
- determine_compression:
-
- LDRH R0, [R6] @ 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, [R4, #CHN_POSITION_ABS] @ store the inital position (relative, not absolute)
-
- setup_compressed_mixing_frequency:
-
- LDR R0, [R6, #WAVE_LOOP_START]
- STR R0, [SP, #ARG_LOOP_START_POS]
-
- 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, [R4, #CHN_FINE_POSITION] @ load the fine position
- LDR R1, [R4, #CHN_FREQUENCY] @ load the channel frequency
- LDRB R0, [R4, #CHN_MODE] @ 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, [R6] @ 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, [R4, #CHN_BLOCK_COUNT] @ write the last decoded block into the channel vars
- LDRB R0, [R4, #CHN_MODE] @ 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, [R5, -R8, LSL#2] @ 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, [R5, -R8, LSL#2] @ 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, [R3, R9]!
- BL bdpcm_decoder
- MOV R6, R12
-
- compressed_mixing_base_load_skip:
-
- ADD R3, R3, #1 @ equivalent to LDRSB R12, [R3, #1]!
- 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, [R5, -R8, LSL#2] @ 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, [R5, -R8, LSL#2] @ 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, [R4, #1] @ 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, [R3, #-1]! @ load first negative sample
- LDRSB R12, [R3, #-1] @ 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, [R5, -R8, LSL#2] @ 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, [R5, -R8, LSL#2] @ 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, [R3, -R9]!
- LDRSB R12, [R3, #-1] @ 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, [R4, #CHN_BLOCK_COUNT] @ check if the current sample position is at the beginning of the current block
- CMP R0, R12
- BEQ bdpcm_decoder_return
-
- STR R0, [R4, #CHN_BLOCK_COUNT] @ 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, [R4, #CHN_WAVE_OFFSET] @ 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, [R2], #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, [R5], #1 @ write the sample to the decoder buffer
- LDRB R12, [R2], #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, [R2], #1 @ load the next 2 samples to get decoded
- MOV R0, R12, LSR#4 @ seperate the current samples' bits
- LDRSB R0, [R6, 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, [R5], #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, [R6, 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, [R5], #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, [R5, R0] @ 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
-
- main_mixer_end:
-
- .end
|