|
1 | 1 | ;;; |
2 | 2 | ;;; nullsound - modular sound driver |
3 | | -;;; Copyright (c) 2024 Damien Ciabrini |
| 3 | +;;; Copyright (c) 2024-2025 Damien Ciabrini |
4 | 4 | ;;; This file is part of ngdevkit |
5 | 5 | ;;; |
6 | 6 | ;;; ngdevkit is free software: you can redistribute it and/or modify |
|
23 | 23 |
|
24 | 24 | .include "ym2610.inc" |
25 | 25 | .include "struct-fx.inc" |
| 26 | + .include "pipeline.inc" |
26 | 27 |
|
27 | 28 | .area CODE |
28 | 29 |
|
29 | 30 |
|
30 | | -;;; Enable volume slide effect for the current channel |
31 | | -;;; TODO: handle slide up |
| 31 | +;;; Setup the fixed-point increment for the slide |
32 | 32 | ;;; ------ |
33 | | -;;; ix : state for channel |
34 | | -;;; a : slide direction: 0 == up, 1 == down |
35 | | -;;; bc : volume increment |
36 | | -;;; d : max volume |
37 | | -;;; [ hl ]: speed (4bits) |
38 | | -;;; [ hl modified ] |
39 | | -vol_slide_init:: |
40 | | - ;; a: speed |
41 | | - ld a, (hl) |
42 | | - inc hl |
| 33 | +;;; c : increment size (increment = 1/2^c) |
| 34 | +;;; e : speed (increments) |
| 35 | +;;; bc, de modified |
| 36 | +vol_slide_init_increment:: |
| 37 | + |
| 38 | + ;; de: inc16 = speed / 2^c |
| 39 | + ld d, e |
| 40 | + ld e, #0 |
| 41 | +__vol_slide_divide: |
| 42 | + srl d |
| 43 | + rr e |
| 44 | + dec c |
| 45 | + jr nz, __vol_slide_divide |
| 46 | + |
| 47 | + ;; store absolute speed (no direction) |
| 48 | + ld VOL_SLIDE_INC16(ix), e |
| 49 | + ld VOL_SLIDE_INC16+1(ix), d |
43 | 50 |
|
44 | | - ;; 0 speed means 'disable FX' |
45 | | - cp #0 |
46 | | - jr nz, _setup_vol_slide |
47 | | - res BIT_FX_VOL_SLIDE, FX(ix) |
48 | 51 | ret |
49 | | -_setup_vol_slide: |
50 | | - ;; setup FX |
51 | | - ld a, #0x40 |
52 | | - ld VOL_SLIDE_INC16(ix), c |
53 | | - ld VOL_SLIDE_INC16+1(ix), b |
54 | | - ld a, #0 |
55 | | - ld VOL_SLIDE_POS16(ix), a |
56 | | - ld VOL_SLIDE_POS16+1(ix), a |
57 | | - ld a, #15 |
58 | | - ld VOL_SLIDE_END(ix), d |
59 | 52 |
|
60 | | - ;; enable FX |
61 | | - set BIT_FX_VOL_SLIDE, FX(ix) |
| 53 | + |
| 54 | +;;; Initialize the source volume for the slide |
| 55 | +;;; ------ |
| 56 | +;;; ix : state for channel |
| 57 | +;;; b : current volume |
| 58 | +vol_slide_init_source:: |
| 59 | + ;; init slide position if no volume slide FX is ongoing |
| 60 | + bit BIT_FX_VOL_SLIDE, FX(ix) |
| 61 | + jr nz, _vol_post_init_slide |
| 62 | + ld VOL_SLIDE_POS16(ix), #0 |
| 63 | + ld VOL_SLIDE_POS16+1(ix), b |
| 64 | +_vol_post_init_slide: |
| 65 | + ret |
| 66 | + |
| 67 | + |
| 68 | +;;; Initialize a target volume for the slide |
| 69 | +;;; ------ |
| 70 | +;;; ix : state for channel |
| 71 | +;;; b : current volume |
| 72 | +;;; d : target offset (signed) |
| 73 | +vol_slide_init_target:: |
| 74 | + ;; init slide direction |
| 75 | + res BIT_SLIDE_DIRECTION, VOL_SLIDE_CFG(ix) |
| 76 | + |
| 77 | + ;; slide target is the current position + new displacement |
| 78 | + ;; a: target note |
| 79 | + ld a, b |
| 80 | + add d |
| 81 | + ;; down: we also need to go one seminote below, to account |
| 82 | + ;; for the fractional parts of the slide. |
| 83 | + bit 7, d |
| 84 | + jr z, _post_neg_vol_slide |
| 85 | + set BIT_SLIDE_DIRECTION, VOL_SLIDE_CFG(ix) |
| 86 | + dec a |
| 87 | +_post_neg_vol_slide: |
| 88 | + ld VOL_SLIDE_END(ix), a |
62 | 89 |
|
63 | 90 | ret |
64 | 91 |
|
65 | 92 |
|
66 | | -;;; Update the volume slide for the current channel by one increment |
| 93 | +;;; Increment current fixed point displacement and |
| 94 | +;;; stop effects when the target displacement is reached |
67 | 95 | ;;; ------ |
68 | 96 | ;;; IN: |
69 | | -;;; ix: state for the current channel |
70 | | -eval_vol_slide_step:: |
71 | | - push hl |
72 | | - push bc |
73 | | - ld l, VOL_SLIDE_POS16(ix) |
74 | | - ld h, VOL_SLIDE_POS16+1(ix) |
| 97 | +;;; ix : state for channel |
| 98 | +;;; bc, de, hl modified |
| 99 | +eval_vol_slide_step: |
| 100 | + ;; bc: increment |
75 | 101 | ld c, VOL_SLIDE_INC16(ix) |
76 | 102 | ld b, VOL_SLIDE_INC16+1(ix) |
| 103 | + ;; hl: decimal note (FX copy) |
| 104 | + ld l, VOL_SLIDE_POS16(ix) |
| 105 | + ld h, VOL_SLIDE_POS16+1(ix) |
| 106 | + |
| 107 | + ;; c: 0 slide up, 1 slide down |
| 108 | + bit BIT_SLIDE_DIRECTION, VOL_SLIDE_CFG(ix) |
| 109 | + jr nz, _vol_slide_sub_inc |
| 110 | + |
| 111 | +_vol_slide_add_inc: |
| 112 | + ;; de: pos16+inc16 |
77 | 113 | add hl, bc |
78 | | - ld a, h |
79 | | - cp VOL_SLIDE_END(ix) |
80 | | - jr c, _post_vol_slide_clamp |
81 | | - ;; the slide FX reached past its end, clamp it |
| 114 | + ex de, hl |
| 115 | + ;; bc: 00xx (end vol) |
| 116 | + ld c, VOL_SLIDE_END(ix) |
| 117 | + ld b, VOL_SLIDE_END+1(ix) |
| 118 | + ;; lh: new pos16 MSB |
| 119 | + ld l, d |
| 120 | + ld h, #0 |
| 121 | + jr _vol_slide_cp |
| 122 | +_vol_slide_sub_inc: |
| 123 | + ;; de: pos16-inc16 |
| 124 | + or a |
| 125 | + sbc hl, bc |
| 126 | + ex de, hl |
| 127 | + ;; bc: new pos16 MSB |
| 128 | + ld c, d |
| 129 | + ld b, #0 |
| 130 | + ;; lh: 00xx (end vol) or ffff (-1) |
| 131 | + ld l, VOL_SLIDE_END(ix) |
| 132 | + ld h, VOL_SLIDE_END+1(ix) |
| 133 | + |
| 134 | + ;; have we reached the end of the slide? |
| 135 | + ;; slide up: continue if cur < end |
| 136 | + ;; slide down: continue if end < cur |
| 137 | + ;; note: we don't check for equality because the increment |
| 138 | + ;; can go past the target note |
| 139 | +_vol_slide_cp: |
| 140 | + or a |
| 141 | + sbc hl, bc |
| 142 | + jp m, _vol_slide_intermediate |
| 143 | + |
| 144 | + ;; slide has reached its target |
| 145 | + |
| 146 | + ;; hl: clamp the last slide pos to the target displacement |
82 | 147 | ld h, VOL_SLIDE_END(ix) |
83 | | -_post_vol_slide_clamp: |
84 | | - ld VOL_SLIDE_POS16(ix), l |
85 | | - ld VOL_SLIDE_POS16+1(ix), h |
86 | | - pop bc |
| 148 | + ld l, #0 |
| 149 | + |
| 150 | + ;; for slide down, we finish one note below the real target to play |
| 151 | + ;; all ticks with fractional parts. Adjust the end displacement back if needed |
| 152 | + bit BIT_SLIDE_DIRECTION, VOL_SLIDE_CFG(ix) |
| 153 | + jr z, _vol_slide_post_sub_clamp |
| 154 | + inc h |
| 155 | +_vol_slide_post_sub_clamp: |
| 156 | + ;; slide is finished, but only stop the effect if requested |
| 157 | + bit BIT_SLIDE_KEEP_RUNNING, VOL_SLIDE_CFG(ix) |
| 158 | + jr nz, _vol_slide_intermediate |
| 159 | + res BIT_FX_VOL_SLIDE, FX(ix) |
| 160 | + |
| 161 | +_vol_slide_intermediate: |
| 162 | + ;; effect is still running |
| 163 | + ;; CHECK: when we stop the slide midway, we can only keep the |
| 164 | + ;; integer part of the slide, which may not match Furnace's semantics |
| 165 | + ld VOL_SLIDE_POS16(ix), e |
| 166 | + ld VOL_SLIDE_POS16+1(ix), d |
| 167 | + ld VOL(ix), d |
| 168 | + |
| 169 | + ret |
| 170 | + |
| 171 | + |
| 172 | +;;; Enable volume slide effect for the current channel |
| 173 | +;;; ------ |
| 174 | +;;; ix : state for channel |
| 175 | +;;; a : slide direction: 0 == up, 1 == down |
| 176 | +;;; [ hl ]: increment |
| 177 | +vol_slide_init:: |
| 178 | + ;; FX configuration |
| 179 | + ld VOL_SLIDE_CFG(ix), a |
| 180 | + |
| 181 | + push bc |
| 182 | + push de |
| 183 | + |
| 184 | + ;; e: increment |
| 185 | + ld e, (hl) |
| 186 | + inc hl |
| 187 | + push hl |
| 188 | + |
| 189 | + ;; c: increment size: 1/4 volume |
| 190 | + ld c, #2 |
| 191 | + call vol_slide_init_increment |
| 192 | + |
| 193 | + ;; b: current volume |
| 194 | + ld b, VOL(ix) |
| 195 | + |
| 196 | + ;; init volume slide source (absolute value) |
| 197 | + call vol_slide_init_source |
| 198 | + |
| 199 | + ;; set up a default target bound, as per definition, vol slides |
| 200 | + ;; do not have one (they rely on the stop FX in the music) |
| 201 | + ld bc, #-1 |
| 202 | + bit BIT_SLIDE_DIRECTION, VOL_SLIDE_CFG(ix) |
| 203 | + jr nz, _vol_slide_init_direction |
| 204 | + ld b, #0 |
| 205 | + ld c, VOL_SLIDE_MAX(ix) |
| 206 | +_vol_slide_init_direction: |
| 207 | + ld VOL_SLIDE_END(ix), c |
| 208 | + ld VOL_SLIDE_END+1(ix), b |
| 209 | + set BIT_FX_VOL_SLIDE, FX(ix) |
| 210 | + |
87 | 211 | pop hl |
| 212 | + pop de |
| 213 | + pop bc |
| 214 | + |
| 215 | + ld a, #1 |
88 | 216 | ret |
| 217 | + |
| 218 | + |
| 219 | +;;; Update an ongoing volume slide effect with a new volume |
| 220 | +;;; ------ |
| 221 | +;;; ix : state for channel |
| 222 | +;;; a : new volume for channel |
| 223 | +vol_slide_update:: |
| 224 | + bit BIT_SLIDE_PORTAMENTO, VOL_SLIDE_CFG(ix) |
| 225 | + jr nz, _vol_slide_update_target |
| 226 | + |
| 227 | +_vol_slide_update_src: |
| 228 | + ld VOL_SLIDE_POS16(ix), #0 |
| 229 | + ld VOL_SLIDE_POS16+1(ix), a |
| 230 | + ret |
| 231 | + |
| 232 | +_vol_slide_update_target: |
| 233 | + ;; b: current volume |
| 234 | + ld b, a |
| 235 | + ;; d: displacement (new vol - current vol) |
| 236 | + sub VOL(ix) |
| 237 | + ld d, a |
| 238 | + call vol_slide_init_target |
| 239 | + ret |
| 240 | + |
| 241 | + |
| 242 | +;;; VOL_SLIDE_OFF |
| 243 | +;;; Stop the volume slide effect in progress for the current channel |
| 244 | +;;; ------ |
| 245 | +vol_slide_off:: |
| 246 | + ;; set the new volume from current slide position |
| 247 | + ld a, VOL_SLIDE_POS16+1(ix) |
| 248 | + ld VOL(ix), a |
| 249 | + ;; since we disable the FX outside of the pipeline process |
| 250 | + ;; make sure to load this new volume at next pipeline run |
| 251 | + res BIT_FX_VOL_SLIDE, FX(ix) |
| 252 | + set BIT_LOAD_VOL, PIPELINE(ix) |
| 253 | + ld a, #1 |
| 254 | + ret |
| 255 | + |
| 256 | + |
| 257 | +;;; VOL_SLIDE_UP |
| 258 | +;;; Enable volume slide up effect for the current channel |
| 259 | +;;; ------ |
| 260 | +;;; [ hl ]: increment |
| 261 | +vol_slide_up:: |
| 262 | + ld a, #0 |
| 263 | + set BIT_SLIDE_KEEP_RUNNING, a |
| 264 | + jp vol_slide_init |
| 265 | + |
| 266 | + |
| 267 | +;;; VOL_SLIDE_DOWN |
| 268 | +;;; Enable volume slide down effect for the current channel |
| 269 | +;;; ------ |
| 270 | +;;; [ hl ]: increment |
| 271 | +vol_slide_down:: |
| 272 | + ld a, #1 |
| 273 | + set BIT_SLIDE_KEEP_RUNNING, a |
| 274 | + jp vol_slide_init |
0 commit comments