 GET TimLib:hdr.System
 GET TimLib:hdr.Macros
 GET TimLib:hdr.Division
 GET TimLib:hdr.SeqF
 GET hdr.GlobHdr
 GET hdr.MemUtils
 GET hdr.Notes
 IMPORT Convert_Cutoff

 EXPORT CRandom
 EXPORT CNote_FillMonitorBuffer
 EXPORT CITFilter_Params

; default area assignment

 AREA |Assembler|,READONLY,CODE

 _GENRETURNS

;-------------------------------------------------------------------------------
; Random
;
; In -  R0  global mem ptr
;
; Out - R0 = random value
;-------------------------------------------------------------------------------
 ALIGN
rand_mul
 DCD &343FD
rand_seed
 DCD &269E00

CRandom
 MOV     R3,R0
 LDR     R0,[R3,#glbmem_random]
 LDR     R1,rand_mul
 LDR     R2,rand_seed
 ; R0 = rnd value
 MLA     R0,R1,R0,R2
 MOV     R0,R0,LSR #16
 STR     R0,[R3,#glbmem_random]
 MOV     PC,R14

;-------------------------------------------------------------------------------
; CNote_FillMonitorBuffer
;
; In  - [R0+&00] glbmem ptr
;       [R0+&04] note ptr
;       [R0+&08] buffer ptr or 0
;       [R0+&0C] buffer len (bytes)
;
; Out - [R0+&10] buffer peak value [0, &7FFF]
;       [R0+&14] buffer mean value [0, &7FFF]
;-------------------------------------------------------------------------------
 ALIGN

CNote_FillMonitorBuffer
 _DEFPROC "R0-R11"
 MOV     R11,R0
 LDR     R12,[R11,#0]
 LDR     R7,[R11,#4]
 LDR     R0,[R11,#8]
 LDR     R1,[R11,#12]
 BL      Note_FillMonitorBuffer
 STR     R4,[R11,#16]
 STR     R5,[R11,#20]
 _ENDPROC

;-------------------------------------------------------------------------------
; Note_FillMonitorBuffer
;
; In  - R0  16-bit buffer ptr or 0
;       R1  buffer len (bytes)
;       R7  note ptr
;       R12 glbmem ptr
;
; Out - R4  buffer peak value [0, &7FFF]
;       R5  buffer mean value [0, &7FFF]
;-------------------------------------------------------------------------------
 ALIGN

Note_FillMonitorBuffer
 _DEFPROC "R0-R2,R6,R8-R11"

 MOV     R4,#0
 MOV     R5,#0

 CMP     R7,#0
 BEQ     Notes_FillMonitorBuffer_Clear

 LDR     R2,[R7,#note_volume]

 LDR     R9,[R7,#stream_pbuffer]
 CMP     R9,#0
 BEQ     Notes_FillMonitorBuffer_Clear

 MOV     R10,R0
 BL      Seq_GetSampleSize
 MOV     R8,R0
 MOV     R0,R10
 MOV     R8,R8,LSL #1
 CMP     R0,#0
 CMPHI   R8,R1
 MOVHI   R8,R1
 CMP     R8,#0
 BEQ     Notes_FillMonitorBuffer_Clear
 ADD     R10,R9,R8

 ; Loop on buffer to get peak and mean value
 ; and scale with channel volume

Note_FillMonitorBuffer_BufLoop
 LDR     R11,[R9],#4
 MOV     R6,R11,ASR #16
 MUL     R6,R2,R6
 MOV     R11,R11,LSL #16
 MOV     R11,R11,ASR #16
 MUL     R11,R2,R11
 MOV     R6,R6,LSR #16
 MOV     R11,R11,LSR #16
 ORR     R14,R11,R6,LSL #16
 CMP     R0,#0
 STRNE   R14,[R0],#4
 TST     R11,#&8000
 RSBNE   R11,R11,#&10000
 CMP     R4,R11
 MOVLO   R4,R11
 ADD     R5,R5,R11
 TST     R6,#&8000
 RSBNE   R6,R6,#&10000
 CMP     R4,R6
 MOVLO   R4,R6
 ADD     R5,R5,R6
 CMP     R9,R10
 BLO     Note_FillMonitorBuffer_BufLoop

 ; Compute mean value

 SUBS    R1,R1,R8
 MOVLT   R1,#0
 MOV     R8,R8,LSR #1
 _DIVIDE R11,R5,R8,R6
 MOV     R5,R11

Notes_FillMonitorBuffer_Clear
 CMP     R0,#0
 BLNE    Mem_Clear

 _ENDPROC

;-------------------------------------------------------------------------------
; ITFilter_Params
;  Requires SMULL
;
; In  - R0  mixing frequency [5000-192000], 0 if no SMULL support
;       R7  note ptr
;
; The following formulas are used:
; R = Fmix / ( (2*PI*110*POW(2, .25) * POW(2, realcutoff/24*256) )
;     range [234 ... 0.155]
; L = POW(10, -24*resonance / (128*20))
;     (= 2*damping and not damping as seen in some formulas)
;     range [1 ... 0.064]
;
; D'= (1 - L) / R
;     I see sometimes code with "if (D' > 2) D' = 2"
;     which I guess is a protection for when reaching Nyquist's limit
; D = (L - D') * R
;   = L*R + L - 1     if D' is not clipped
;   = L*R - 2*R       if D' is clipped
; E = R * R
;
; A0 = 1 / (1 + D + E)
; B1 = (D + 2*E) / (1 + D + E)           = 1 - A0 - B2
; B2 = -E / (1 + D + E)                  = -E * A0
; As you can see A0 + B1 + B2 = 1, hence the rewrite on the right.
;-------------------------------------------------------------------------------
 ALIGN

CITFilter_Params
 _DEFPROC "R7"
 MOV     R7,R0
 MOV     R0,R1
 BL      ITFilter_Params
 _ENDPROC

ITFilter_Params
 _DEFPROC "R0-R6"
 CMP     R0,#0
 BEQ     ITFilter_Params_NoFilter
 MOV     R3,R0 ; Fmix
 LDRB    R0,[R7,#note_cutoff]
 LDRB    R1,[R7,#note_resonance]
 CMP     R0,#127
 BGT     ITFilter_Params_NoFilter
 CMPEQ   R1,#0
 BEQ     ITFilter_Params_NoFilter
 CMP     R1,#127
 BGT     ITFilter_Params_NoFilter

ITFilter_Params_UseFilter
 ; X = 1/POW(2, realcutoff/24*256) = POW(2, -realcutoff/24*256)
 LDR     R0,[R7,#note_realcutoff]
 _PUSH   "R1-R3" ; C call
 BL      Convert_Cutoff; in 2.30
 _PULL   "R1-R3" ; C call
 ; R = Fmix / (2*PI*110*POW(2, .25)*POW(2, realcutoff/24*256))
 ; R = Fmix * NaturalPeriod * X
 LDR     R2,NaturalPeriod
 MOV     R3,R3,LSL #13
 UMULL   R4,R5,R3,R2       ; in 12.52 (19.13 * 0.39)
 UMULL   R3,R2,R5,R0       ; R in 14.50 (12.20 * 2.30)
 MOV     R2,R2,LSL #5
 ORRS    R2,R2,R3,LSR #27  ; R in 9.23
 ADC     R2,R2,#0
 ADR     R5,Resonance_Table
 LDR     R1,[R5,R1,LSL #2] ; L in 9.23
 UMULL   R3,R4,R1,R2       ; L*R in 18.46
 RSB     R0,R1,#(1<<23)    ; (1-L) in 9.23
 CMP     R0,R2,LSL #1      ; (1-L) > 2R ?
 MOVHI   R0,R2,LSL #1      ; ?(1-L,2R) in 9.23
 SUBS    R3,R3,R0,LSL #23
 SBC     R4,R4,R0,ASR #9   ; D = L*R + ?(L-1,-2R) in 18.46
 UMULL   R5,R6,R2,R2       ; E = R*R in 18.46
 ADDS    R3,R5,R3
 ADC     R4,R6,R4
 ADD     R4,R4,#(1<<14)    ; (1+D+E) in 18.46
 MOV     R0,#0
 MOV     R1,#0             ; shift
 CMP     R0,R4,LSR #15     ; not 16 as UDIV64 does not allow high bit
 CMPEQ   R0,R6,LSR #15
 BNE     no_lshift
 MOV     R4,R4,LSL #16
 MOV     R6,R6,LSL #16
 ORRS    R4,R4,R3,LSR #16
 ADC     R4,R4,#0          ; (1+D+E) << shift in 18.14
 ORR     R6,R6,R5,LSR #16
 MOV     R5,R5,LSL #16     ; E << shift in 18.49
 MOV     R1,#16
 B       no_shift
no_lshift
 CMP     R0,R4,LSR #23     ; not 24 as UDIV64 does not allow high bit
 CMPEQ   R0,R6,LSR #23
 BNE     no_shift
 MOV     R4,R4,LSL #8
 MOV     R6,R6,LSL #8
 ORRS    R4,R4,R3,LSR #24
 ADC     R4,R4,#0          ; (1+D+E) << shift in 18.14
 ORR     R6,R6,R5,LSR #24
 MOV     R5,R5,LSL #8      ; E << shift in 18.49
 MOV     R1,#8
no_shift
 ADD     R1,R1,#10
 MOV     R3,#1
 MOV     R3,R3,LSL R1
 MOV     R2,#0             ; (1 << shift) in 22.42
 _UDIV64 R0,R2,R3,R4,R1    ; A0 = 1/(1+D+E) in 4.28
 MOV     R5,R5,LSR #4
 ORR     R5,R5,R6,LSL #28
 MOV     R6,R6,ASR #4      ; (E << shift) in 22.42
 _UDIV64 R2,R5,R6,R4,R1    ; E/(1+D+E) in 4.28
 RSB     R2,R2,#0          ; B2 = -E*A0 in 4.28
 RSB     R1,R0,#(1<<28)
 SUB     R1,R1,R2          ; B1 = 1-B2-A0 in 4.28
 STR     R0,[R7,#note_stream+stream_filter_a0]
 STR     R1,[R7,#note_stream+stream_filter_b1]
 STR     R2,[R7,#note_stream+stream_filter_b2]
 LDRB    R1,[R7,#note_stream+stream_flags]
 TST     R1,#stream_flag_filter
 ;       preload
 LDREQ   R0,[R7,#note_stream+stream_last_smp_value]
 STREQ   R0,[R7,#note_stream+stream_filter_y1]
 STREQ   R0,[R7,#note_stream+stream_filter_y2]
 ORR     R1,R1,#stream_flag_filter
 STRB    R1,[R7,#note_stream+stream_flags]
 _ENDPROC

ITFilter_Params_NoFilter
 LDRB    R1,[R7,#note_stream+stream_flags]
 BIC     R1,R1,#stream_flag_filter
 STRB    R1,[R7,#note_stream+stream_flags]

 _ENDPROC

NaturalPeriod ; fixed point 0.39 of 1/(2*PI*110*POW(2, .25))
 DCD &27DE19C5
Resonance_Table ; fixed point 9.23 of POW(10,-(24*Resonance)/(128*20))
 DCD &800000, &7D4439, &7A9765, &77F930, &75694C, &72E769, &70733B, &6E0C78
 DCD &6BB2D6, &69660C, &6725D6, &64F1ED, &62CA10, &60ADFC, &5E9D70, &5C982E
 DCD &5A9DF7, &58AE90, &56C9BE, &54EF46, &531EEF, &515884, &4F9BCD, &4DE894
 DCD &4C3EA8, &4A9DD4, &4905E6, &4776AF, &45EFFE, &4471A5, &42FB77, &418D46
 DCD &4026E7, &3EC82F, &3D70F5, &3C210F, &3AD855, &3996A1, &385BCB, &3727AE
 DCD &35FA26, &34D30F, &33B244, &3297A5, &31830E, &307460, &2F6B79, &2E683B
 DCD &2D6A86, &2C723C, &2B7F3F, &2A9173, &29A8BB, &28C4FB, &27E618, &270BF8
 DCD &263680, &256597, &249924, &23D110, &230D40, &224DA0, &219217, &20DA8F
 DCD &2026F3, &1F772C, &1ECB27, &1E22CD, &1D7E0D, &1CDCD1, &1C3F06, &1BA49A
 DCD &1B0D7B, &1A7995, &19E8D8, &195B32, &18D093, &1848EA, &17C426, &174238
 DCD &16C310, &1646A0, &15CCD8, &1555A9, &14E107, &146EE1, &13FF2C, &1391DA
 DCD &1326DD, &12BE29, &1257B2, &11F36A, &119147, &11313D, &10D33F, &107743
 DCD &101D3F, &0FC526, &0F6EEF, &0F1A8F, &0EC7FD, &0E772E, &0E2819, &0DDAB4
 DCD &0D8EF6, &0D44D6, &0CFC4C, &0CB54E, &0C6FD4, &0C2BD6, &0BE94C, &0BA82D
 DCD &0B6873, &0B2A15, &0AED0C, &0AB150, &0A76DC, &0A3DA6, &0A05AA, &09CEDF
 DCD &099940, &0964C7, &09316C, &08FF2A, &08CDFA, &089DD8, &086EBC, &0840A3

 END
