/* * PicoDrive * (C) notaz, 2006 * (C) kub, 2020 added SSG-EG and simple output rate interpolation * * This work is licensed under the terms of MAME license. * See COPYING file in the top-level directory. */ @ this is a rewrite of MAME's ym2612 code, in particular this is only the main sample-generatin loop. @ it does not seem to give much performance increase (if any at all), so don't use it if it causes trouble. @ - notaz, 2006 @ vim:filetype=armasm #include @ very simple YM2612 output rate to sample rate adaption (~500k cycles @44100) #define INTERPOL #define SSG_EG .equiv SLOT1, 0 .equiv SLOT2, 2 .equiv SLOT3, 1 .equiv SLOT4, 3 .equiv SLOT_STRUCT_SIZE, 0x38 .equiv TL_TAB_LEN, 0x1A00 .equiv EG_ATT, 4 .equiv EG_DEC, 3 .equiv EG_SUS, 2 .equiv EG_REL, 1 .equiv EG_OFF, 0 .equiv EG_SH, 16 @ 16.16 fixed point (envelope generator timing) .equiv EG_TIMER_OVERFLOW, (3*(1<= (INT32) SLOT->sl ) strgeb r3, [r5,#0x17] @ state b 10f 4: @ EG_ATT subs r3, r3, #1 @ eg_inc_val_shift - 1 mvnpl r2, r0 movpl r2, r2, lsl r3 addpl r0, r0, r2, asr #4 cmp r0, #0 @ if (volume <= MIN_ATT_INDEX) bgt 10f ldr r2, [r5,#0x1c] mov r0, #0 cmp r2, #0 movne r3, #EG_DEC moveq r3, #EG_SUS strb r3, [r5,#0x17] @ state b 10f 2: @ EG_SUS mov r2, #1024 sub r2, r2, #1 @ r2 = MAX_ATT_INDEX cmp r0, r2 @ if ( volume >= MAX_ATT_INDEX ) movge r0, r2 b 10f 1: @ EG_REL mov r2, #1024 sub r2, r2, #1 @ r2 = MAX_ATT_INDEX cmp r0, r2 @ if ( volume >= MAX_ATT_INDEX ) movge r0, r2 movge r3, #EG_OFF strgeb r3, [r5,#0x17] @ state 10: @ finish ldrh r3, [r5,#0x18] @ tl strh r0, [r5,#0x1a] @ volume #if defined(SSG_EG) b 11f 9: @ SSG-EG mode ldrh r0, [r5,#0x1a] @ volume, unsigned (0-1023) cmp r2, #4 @ EG_ATT beq 4f cmp r0, #0x200 @ if ( volume < 0x200 ) movlt r0, #1 movlt r3, r0, lsl r3 ldrlth r0, [r5,#0x1a] @ volume, unsigned (0-1023) movlt r3, r3, lsr #1 @ eg_inc_val addlt r0, r0, r3, lsl #2 cmp r2, #2 blt 1f @ EG_REL beq 10f @ EG_SUS - nothing more to do 3: @ EG_DEC ldr r2, [r5,#0x1c] @ sl (can be 16bit?) mov r3, #EG_SUS cmp r0, r2 @ if ( volume >= (INT32) SLOT->sl ) strgeb r3, [r5,#0x17] @ state b 10f 4: @ EG_ATT subs r3, r3, #1 @ eg_inc_val_shift - 1 mvnpl r2, r0 movpl r2, r2, lsl r3 addpl r0, r0, r2, asr #4 cmp r0, #0 @ if (volume <= MIN_ATT_INDEX) bgt 10f ldr r2, [r5,#0x1c] @ sl mov r0, #0 cmp r2, #0 movne r3, #EG_DEC moveq r3, #EG_SUS strb r3, [r5,#0x17] @ state b 10f 1: @ EG_REL cmp r0, #0x200 @ if ( volume >= 0x200 ) movge r0, #1024 subge r0, #1 movge r3, #EG_OFF strgeb r3, [r5,#0x17] @ state 10: @ finish ldrb r2, [r5,#0x30] @ ssg ldrb r3, [r5,#0x17] @ state strh r0, [r5,#0x1a] @ volume cmp r2, #0x0c @ if ( ssg&0x04 && state > EG_REL ) cmpge r3, #EG_REL+1 ldrh r3, [r5,#0x18] @ tl rsbge r0, r0, #0x200 @ volume = (0x200-volume) & MAX_ATT lslge r0, r0, #22 lsrge r0, r0, #22 11: #endif add r0, r0, r3 @ volume += tl strh r0, [r5,#0x34] @ vol_out 0: @ EG_OFF .endm #if defined(SSG_EG) @ r5=slot, trashes: r0,r2,r3 .macro update_ssg_eg ldrh r0, [r5,#0x30] @ ssg+ssgn ldrb r2, [r5,#0x17] @ state ldrh r3, [r5,#0x1a] @ volume tst r0, #0x08 @ ssg enabled && beq 9f cmp r2, #EG_REL+1 @ state > EG_REL && cmpge r3, #0x200 @ volume >= 0x200? blt 9f orr r4, r4, #0x10 @ ssg_update tst r0, #0x01 beq 1f tst r0, #0x02 eorne r0, r0, lsr #8 @ ssg ^= ssgn ^ 4 eorne r0, r0, #0x4 orrne r0, r0, #0x400 @ ssgn = 4 strneh r0, [r5,#0x30] eor r0, r0, #0x4 @ if ( !(ssg&0x04) ) tst r0, #0x4 cmpne r2, #EG_ATT @ if ( state != EG_ATT ) movne r3, #0x400 subne r3, r3, #1 strneh r3, [r5,#0x1a] @ volume = MAX_ATT b 9f 1: tst r0, #0x02 eorne r0, r0, #0x4 @ ssg ^= 4 eorne r0, r0, #0x400 @ ssgn ^= 4 strneh r0, [r5,#0x30] moveq r0, #0 streq r0, [lr,#0x10] @ phase = 0 cmp r2, #EG_ATT @ if ( state != EG_ATT ) beq 9f ldr r0, [r5,#0x1c] @ sl mov r2, #EG_SUS @ state = sl==MIN_ATT ? EG_SUS:EG_DEC cmp r0, #0 ldrh r0, [r5,#0x32] @ ar+ksr movne r2, #EG_DEC cmp r0, #32+62 @ if ( ar+ksr >= 32+62 ) movge r3, #0 strgeh r3, [r5,#0x1a] @ volume = MIN_ATT bge 8f cmp r3, #0 movgt r2, #EG_ATT 8: strb r2, [r5,#0x17] @ state 9: .endm @ r5=slot, trashes: r0,r2,r3 .macro recalc_volout #if defined(INTERPOL) ldrh r0, [r5,#0x34] @ vol_out #endif ldrb r2, [r5,#0x30] @ ssg ldrb r3, [r5,#0x17] @ state #if defined(INTERPOL) strh r0, [r5,#0x36] @ vol_ipol #endif ldrh r0, [r5,#0x1a] @ volume @ and r2, r2, #0x0c cmp r2, #0x0c @ if ( ~ssg&0x0c && state > EG_REL ) cmpge r3, #EG_REL+1 ldrh r3, [r5,#0x18] @ tl rsbge r0, r0, #0x200 @ volume = (0x200-volume) & MAX_ATT lslge r0, r0, #22 lsrge r0, r0, #22 add r0, r0, r3 @ volume += tl strh r0, [r5,#0x34] @ vol_out .endm #endif @ r12=lfo_ampm[31:16], r1=lfo_cnt_old, r2=lfo_cnt, r3=scratch .macro advance_lfo_m mov r2, r2, lsr #LFO_SH cmp r2, r1, lsr #LFO_SH beq 0f and r3, r2, #0x3f cmp r2, #0x40 eorlt r3, r3, #0x3f bic r12,r12, #0xff000000 @ lfo_ampm &= 0xff orr r12,r12, r3, lsl #1+24 mov r2, r2, lsr #2 cmp r2, r1, lsr #LFO_SH+2 bicne r12,r12, #0xff0000 orrne r12,r12, r2, lsl #16 0: .endm @ result goes to r1, trashes r2 .macro make_eg_out slot tst r12, #8 tstne r12, #(1<<(\slot+8)) .if \slot == SLOT1 mov r1, r6, lsl #16 mov r1, r1, lsr #16 .elseif \slot == SLOT2 mov r1, r6, lsr #16 .elseif \slot == SLOT3 mov r1, r7, lsl #16 mov r1, r1, lsr #16 .elseif \slot == SLOT4 mov r1, r7, lsr #16 .endif andne r2, r12, #0xc0 movne r2, r2, lsr #6 addne r2, r2, #24 addne r1, r1, r12, lsr r2 bic r1, r1, #1 .endm @ \r=sin/result, r1=env, r3=ym_tl_tab .macro lookup_tl r tst \r, #0x100 eorne \r, \r, #0xff @ if (sin & 0x100) sin = 0xff - (sin&0xff); tst \r, #0x200 and \r, \r, #0xff orr \r, \r, r1, lsl #7 mov \r, \r, lsl #1 ldrh \r, [r3, \r] @ 2ci if ne rsbne \r, \r, #0 .endm @ lr=context, r12=pack (stereo, ssg_enabled, disabled, lfo_enabled | pan_r, pan_l, ams[2] | AMmasks[4] | FB[4] | lfo_ampm[16]) @ r0-r2=scratch, r3=sin_tab, r5=scratch, r6-r7=vol_out[4], r10=op1_out .macro upd_algo0_m @ SLOT3 make_eg_out SLOT3 cmp r1, #ENV_QUIET movcs r0, #0 bcs 0f ldr r2, [lr, #0x18] ldr r0, [lr, #0x38] @ mem (signed) mov r2, r2, lsr #16 add r0, r2, r0, lsr #1 lookup_tl r0 @ r0=c2 0: @ SLOT4 make_eg_out SLOT4 cmp r1, #ENV_QUIET movcs r0, #0 bcs 1f ldr r2, [lr, #0x1c] mov r0, r0, lsr #1 add r0, r0, r2, lsr #16 lookup_tl r0 @ r0=output smp 1: @ SLOT2 make_eg_out SLOT2 cmp r1, #ENV_QUIET movcs r2, #0 bcs 2f ldr r2, [lr, #0x14] @ 1ci mov r5, r10, lsr #17 add r2, r5, r2, lsr #16 lookup_tl r2 @ r2=mem 2: str r2, [lr, #0x38] @ mem .endm .macro upd_algo1_m @ SLOT3 make_eg_out SLOT3 cmp r1, #ENV_QUIET movcs r0, #0 bcs 0f ldr r2, [lr, #0x18] ldr r0, [lr, #0x38] @ mem (signed) mov r2, r2, lsr #16 add r0, r2, r0, lsr #1 lookup_tl r0 @ r0=c2 0: @ SLOT4 make_eg_out SLOT4 cmp r1, #ENV_QUIET movcs r0, #0 bcs 1f ldr r2, [lr, #0x1c] mov r0, r0, lsr #1 add r0, r0, r2, lsr #16 lookup_tl r0 @ r0=output smp 1: @ SLOT2 make_eg_out SLOT2 cmp r1, #ENV_QUIET movcs r2, #0 bcs 2f ldr r2, [lr, #0x14] @ 1ci mov r2, r2, lsr #16 lookup_tl r2 @ r2=mem 2: add r2, r2, r10, asr #16 str r2, [lr, #0x38] .endm .macro upd_algo2_m @ SLOT3 make_eg_out SLOT3 cmp r1, #ENV_QUIET movcs r0, #0 bcs 0f ldr r2, [lr, #0x18] ldr r0, [lr, #0x38] @ mem (signed) mov r2, r2, lsr #16 add r0, r2, r0, lsr #1 lookup_tl r0 @ r0=c2 0: add r0, r0, r10, asr #16 @ SLOT4 make_eg_out SLOT4 cmp r1, #ENV_QUIET movcs r0, #0 bcs 1f ldr r2, [lr, #0x1c] mov r0, r0, lsr #1 add r0, r0, r2, lsr #16 lookup_tl r0 @ r0=output smp 1: @ SLOT2 make_eg_out SLOT2 cmp r1, #ENV_QUIET movcs r2, #0 bcs 2f ldr r2, [lr, #0x14] mov r2, r2, lsr #16 @ 1ci lookup_tl r2 @ r2=mem 2: str r2, [lr, #0x38] @ mem .endm .macro upd_algo3_m @ SLOT3 make_eg_out SLOT3 cmp r1, #ENV_QUIET ldr r2, [lr, #0x38] @ mem (for future) mov r0, #0 bcs 0f ldr r0, [lr, #0x18] @ phase3 mov r0, r0, lsr #16 lookup_tl r0 @ r0=c2 0: add r0, r0, r2 @ SLOT4 make_eg_out SLOT4 cmp r1, #ENV_QUIET movcs r0, #0 bcs 1f ldr r2, [lr, #0x1c] mov r0, r0, lsr #1 add r0, r0, r2, lsr #16 lookup_tl r0 @ r0=output smp 1: @ SLOT2 make_eg_out SLOT2 cmp r1, #ENV_QUIET movcs r2, #0 bcs 2f ldr r2, [lr, #0x14] @ phase2 mov r5, r10, lsr #17 add r2, r5, r2, lsr #16 lookup_tl r2 @ r2=mem 2: str r2, [lr, #0x38] @ mem .endm .macro upd_algo4_m @ SLOT3 make_eg_out SLOT3 cmp r1, #ENV_QUIET movcs r0, #0 bcs 0f ldr r0, [lr, #0x18] mov r0, r0, lsr #16 @ 1ci lookup_tl r0 @ r0=c2 0: @ SLOT4 make_eg_out SLOT4 cmp r1, #ENV_QUIET movcs r0, #0 bcs 1f ldr r2, [lr, #0x1c] mov r0, r0, lsr #1 add r0, r0, r2, lsr #16 lookup_tl r0 @ r0=output smp 1: @ SLOT2 make_eg_out SLOT2 cmp r1, #ENV_QUIET bcs 2f ldr r2, [lr, #0x14] mov r5, r10, lsr #17 add r2, r5, r2, lsr #16 lookup_tl r2 add r0, r0, r2 @ add to smp 2: .endm .macro upd_algo5_m @ SLOT3 make_eg_out SLOT3 cmp r1, #ENV_QUIET movcs r0, #0 bcs 0f ldr r2, [lr, #0x18] ldr r0, [lr, #0x38] @ mem (signed) mov r2, r2, lsr #16 add r0, r2, r0, lsr #1 lookup_tl r0 @ r0=output smp 0: @ SLOT4 make_eg_out SLOT4 cmp r1, #ENV_QUIET bcs 1f ldr r2, [lr, #0x1c] mov r5, r10, lsr #17 add r2, r5, r2, lsr #16 lookup_tl r2 add r0, r0, r2 @ add to smp 1: @ SLOT2 make_eg_out SLOT2 cmp r1, #ENV_QUIET bcs 2f ldr r2, [lr, #0x14] mov r5, r10, lsr #17 add r2, r5, r2, lsr #16 lookup_tl r2 add r0, r0, r2 @ add to smp 2: mov r1, r10, asr #16 str r1, [lr, #0x38] @ mem .endm .macro upd_algo6_m @ SLOT3 make_eg_out SLOT3 cmp r1, #ENV_QUIET movcs r0, #0 bcs 0f ldr r0, [lr, #0x18] mov r0, r0, lsr #16 @ 1ci lookup_tl r0 @ r0=output smp 0: @ SLOT4 make_eg_out SLOT4 cmp r1, #ENV_QUIET bcs 1f ldr r2, [lr, #0x1c] mov r2, r2, lsr #16 @ 1ci lookup_tl r2 add r0, r0, r2 @ add to smp 1: @ SLOT2 make_eg_out SLOT2 cmp r1, #ENV_QUIET bcs 2f ldr r2, [lr, #0x14] mov r5, r10, lsr #17 add r2, r5, r2, lsr #16 lookup_tl r2 add r0, r0, r2 @ add to smp 2: .endm .macro upd_algo7_m @ SLOT3 make_eg_out SLOT3 cmp r1, #ENV_QUIET movcs r0, #0 bcs 0f ldr r0, [lr, #0x18] mov r0, r0, lsr #16 @ 1ci lookup_tl r0 @ r0=output smp 0: add r0, r0, r10, asr #16 @ SLOT4 make_eg_out SLOT4 cmp r1, #ENV_QUIET bcs 1f ldr r2, [lr, #0x1c] mov r2, r2, lsr #16 @ 1ci lookup_tl r2 add r0, r0, r2 @ add to smp 1: @ SLOT2 make_eg_out SLOT2 cmp r1, #ENV_QUIET bcs 2f ldr r2, [lr, #0x14] mov r2, r2, lsr #16 @ 1ci lookup_tl r2 add r0, r0, r2 @ add to smp 2: .endm .macro upd_slot1_m make_eg_out SLOT1 cmp r1, #ENV_QUIET movcs r10, r10, lsl #16 @ ct->op1_out <<= 16; // op1_out0 = op1_out1; op1_out1 = 0; bcs 0f ands r2, r12, #0xf000 moveq r0, #0 movne r2, r2, lsr #12 addne r0, r10, r10, lsl #16 movne r0, r0, asr #16 movne r0, r0, lsl r2 ldr r2, [lr, #0x10] @ phase1 add r0, r0, r2 mov r0, r0, lsr #16 lookup_tl r0 mov r10,r10,lsl #16 @ ct->op1_out <<= 16; mov r0, r0, lsl #16 orr r10,r10, r0, lsr #16 0: .endm @ lr=context, r12=pack (stereo, ssg_enabled, disabled, lfo_enabled | pan_r, pan_l, ams[2] | AMmasks[4] | FB[4] | lfo_ampm[16]) @ r0-r2=scratch, r3=sin_tab/scratch, r4=(length<<8)|unused[3],ssg_update,was_update,algo[3], r5=tl_tab/slot, @ r6-r7=vol_out[4], r8=eg_timer, r9=eg_timer_add[31:16], r10=op1_out, r11=buffer .global chan_render_loop @ chan_rend_context *ct, int *buffer, int length chan_render_loop: stmfd sp!, {r4-r11,lr} mov lr, r0 mov r4, r2, lsl #8 @ no more 24 bits here ldr r12, [lr, #0x4c] ldr r0, [lr, #0x50] mov r11, r1 and r0, r0, #7 orr r4, r4, r0 @ (length<<8)|algo ldr r8, [lr, #0x44] @ eg_timer ldr r9, [lr, #0x48] @ eg_timer_add ldr r10, [lr, #0x54] @ op1_out tst r12, #8 @ lfo? beq crl_loop crl_loop_lfo: ldr r1, [lr, #0x30] @ lfo_cnt ldr r2, [lr, #0x34] @ lfo_inc subs r4, r4, #0x100 bmi crl_loop_end add r2, r2, r1 str r2, [lr, #0x30] @ r12=lfo_ampm[31:16], r1=lfo_cnt_old, r2=lfo_cnt advance_lfo_m add r4, r4, #0x100 crl_loop: subs r4, r4, #0x100 bmi crl_loop_end ldr r5, [lr, #0x40] @ CH #if defined(SSG_EG) tst r12, #0x02 @ ssg_enabled? beq ssg_done @ -- SSG -- lsl r7, r8, #EG_SH add r7, r9, r7, lsr #EG_SH subs r7, r7, #1<>EG_SH)/2 bne 0f @ mix is vol_out ldr r6, [r5, #0x34] @ vol_out, vol_ipol for all slots ldr r2, [r5, #0x34+SLOT_STRUCT_SIZE*2] ldr r7, [r5, #0x34+SLOT_STRUCT_SIZE] ldr r3, [r5, #0x34+SLOT_STRUCT_SIZE*3] add r6, r6, r6, lsl #16 lsr r6, r6, #17 add r2, r2, r2, lsl #16 lsr r2, r2, #17 add r7, r7, r7, lsl #16 lsr r7, r7, #17 add r3, r3, r3, lsl #16 lsr r3, r3, #17 b 1f #else @ super-basic... just take value closest to sample point mov r3, r8, lsr #EG_SH-1 @ eg_timer, [0..3<>EG_SH) #endif 0: ldrgeh r6, [r5, #0x34] @ vol_out values for all slots ldrlth r6, [r5, #0x36] @ vol_ipol values for all slots ldrgeh r2, [r5, #0x34+SLOT_STRUCT_SIZE*2] ldrlth r2, [r5, #0x36+SLOT_STRUCT_SIZE*2] ldrgeh r7, [r5, #0x34+SLOT_STRUCT_SIZE] ldrlth r7, [r5, #0x36+SLOT_STRUCT_SIZE] ldrgeh r3, [r5, #0x34+SLOT_STRUCT_SIZE*3] ldrlth r3, [r5, #0x36+SLOT_STRUCT_SIZE*3] #else ldrh r6, [r5, #0x34] @ vol_out values for all slots ldrh r2, [r5, #0x34+SLOT_STRUCT_SIZE*2] ldrh r7, [r5, #0x34+SLOT_STRUCT_SIZE] ldrh r3, [r5, #0x34+SLOT_STRUCT_SIZE*3] #endif 1: orr r6, r6, r2, lsl #16 orr r7, r7, r3, lsl #16 @ -- SLOT1 -- PIC_LDR(r3, r2, ym_tl_tab) @ lr=context, r12=pack (stereo, ssg_enabled, disabled, lfo_enabled | pan_r, pan_l, ams[2] | AMmasks[4] | FB[4] | lfo_ampm[16]) @ r0-r2=scratch, r3=tl_tab, r5=scratch, r6-r7=vol_out[4], r10=op1_out upd_slot1_m @ -- SLOT2+ -- and r0, r4, #7 PIC_XB(,r0, lsl #2) nop PIC_BT(crl_algo0) PIC_BT(crl_algo1) PIC_BT(crl_algo2) PIC_BT(crl_algo3) PIC_BT(crl_algo4) PIC_BT(crl_algo5) PIC_BT(crl_algo6) PIC_BT(crl_algo7) .pool crl_algo0: upd_algo0_m b crl_algo_done .pool crl_algo1: upd_algo1_m b crl_algo_done .pool crl_algo2: upd_algo2_m b crl_algo_done .pool crl_algo3: upd_algo3_m b crl_algo_done .pool crl_algo4: upd_algo4_m b crl_algo_done .pool crl_algo5: upd_algo5_m b crl_algo_done .pool crl_algo6: upd_algo6_m b crl_algo_done .pool crl_algo7: upd_algo7_m crl_algo_done: @ -- WRITE SAMPLE -- tst r0, r0 beq ctl_sample_skip orr r4, r4, #8 @ have_output lsr r1, r0, #31 @ clip (saturate) sample to 14 bit adds r2, r1, r0, asr #13 subne r0, r1, #0x80000001 asrne r0, r0, #18 tst r12, #1 beq ctl_sample_mono tst r12, #0x20 @ L ldrne r1, [r11] addeq r11, r11, #4 addne r1, r0, r1 strne r1, [r11], #4 tst r12, #0x10 @ R ldrne r1, [r11] addeq r11, r11, #4 addne r1, r0, r1 strne r1, [r11], #4 b crl_do_phase ctl_sample_mono: ldr r1, [r11] add r1, r0, r1 str r1, [r11], #4 b crl_do_phase ctl_sample_skip: and r1, r12, #1 add r1, r1, #1 add r11,r11, r1, lsl #2 crl_do_phase: @ -- PHASE UPDATE -- add r5, lr, #0x10 ldmia r5, {r0-r3,r6-r7} add r0, r0, r6 add r1, r1, r7 ldr r6, [r5, #0x18] ldr r7, [r5, #0x1c] add r2, r2, r6 add r3, r3, r7 stmia r5, {r0-r3} tst r12, #8 bne crl_loop_lfo b crl_loop crl_loop_end: str r8, [lr, #0x44] @ eg_timer str r12, [lr, #0x4c] @ pack (for lfo_ampm) str r4, [lr, #0x50] @ was_update str r10, [lr, #0x54] @ op1_out ldmfd sp!, {r4-r11,pc} .pool @ vim:filetype=armasm