aboutsummaryrefslogtreecommitdiff
path: root/src/sound.s
blob: a824898966ae9eefd7485954d843cc47735ddbfd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
.segment "CODE"

;; The sound for this game is extremely simple, coming from a system that only
;; allowed for 1-bit beeps. Here the sound is a bit different due to a vastly
;; different audio hardware, and we make use of three channels:
;;
;;   1. Square 1: used on level entry and bullets.
;;   2. Square 2: used for item collection or dropping.
;;   3. Noise: enemy explosion and rocket launch.
.scope Sound
    ;; Bullets can go super fast, and if we delivered the sound effect for each
    ;; bullet it could potentially be annoying. Moreover, because of the
    ;; limitations from the ZX Spectrum, the original game didn't spit a sound
    ;; effect for each bullet either. Hence, wait for some frames before
    ;; producing a sound. This is why we call Sound::tick() on NMI code, and why
    ;; the sound effect for bullet is called via Sound::play_bullet_maybe().
    ;;
    ;; NOTE: the maximum count value is supposed to be bigger than the timer for
    ;; bullets creation. This is guaranteed in 'jetpac.s'.
    zp_frame_count = $DA
    BULLET_SFX_FRAME_COUNT = HZ / 10

    ;; Period values for square channels.
    .ifdef PAL
        BULLET_SFX_LOW  = $29
        BULLET_SFX_HIGH = $00
        ENTER_SFX_LOW   = $4D
        ENTER_SFX_HIGH  = $01
        PICKUP_SFX_LOW  = $61
        PICKUP_SFX_HIGH = $01
        DROP_SFX_LOW    = $3A
        DROP_SFX_HIGH   = $01
    .else
        BULLET_SFX_LOW  = $2C
        BULLET_SFX_HIGH = $00
        ENTER_SFX_LOW   = $67
        ENTER_SFX_HIGH  = $01
        PICKUP_SFX_LOW  = $7C
        PICKUP_SFX_HIGH = $01
        DROP_SFX_LOW    = $52
        DROP_SFX_HIGH   = $01
    .endif

    ;; Initialize all the sound channels which are needed and reset some
    ;; register values.
    .proc init
        ;; Enable square 1, 2; and noise.
        lda #%00001011
        sta APU::m_status

        ;; Reset sweep registers and frame count.
        lda #0
        sta APU::m_square_1_sweep
        sta APU::m_square_2_sweep
        sta Sound::zp_frame_count

        ;; Silence channels.
        lda #0
        sta APU::m_noise_envelope
        lda #$30
        sta APU::m_square_1_envelope
        sta APU::m_square_2_envelope

        rts
    .endproc

    ;; Tick the internal frame count for sound effects.
    ;;
    ;; NOTE: expected to only be called at the end of NMI code.
    .proc tick
        ;; If there is no bullet sound effect to be delivered, don't even sweat
        ;; it.
        lda Sound::zp_frame_count
        beq @end

        ;; Increase the frame counter and check the limit. If we reached that
        ;; limit, reset it so we don't tick until the next bullet sfx request
        ;; comes in.
        clc
        adc #1
        cmp #Sound::BULLET_SFX_FRAME_COUNT
        beq @reset
        sta Sound::zp_frame_count
        rts

    @reset:
        lda #0
        sta Sound::zp_frame_count

    @end:
        rts
    .endproc

    ;; Play the bullet sound effect if we can (i.e. the frame count allows us to
    ;; do it).
    .proc play_bullet_maybe
        ;; If we cannot play the sound yet, skip this altogether.
        lda Sound::zp_frame_count
        bne @end
        inc Sound::zp_frame_count

        lda #$01
        sta APU::m_square_1_envelope
        lda #%10000001
        sta APU::m_square_1_sweep
        lda #BULLET_SFX_LOW
        sta APU::m_square_1_low
        lda #BULLET_SFX_HIGH
        sta APU::m_square_1_high

    @end:
        rts
    .endproc
.endscope

;; Make an explosion sound via the noise channel.
.macro SOUND_EXPLOSION
    lda #$03
    sta APU::m_noise_envelope
    lda #$8F
    sta APU::m_noise_mode
    lda #$F8
    sta APU::m_noise_counter
.endmacro

;; Make a small beep, suitable for level entry.
.macro SOUND_ENTER_LEVEL
    lda #%10000100
    sta APU::m_square_1_envelope
    lda #Sound::ENTER_SFX_LOW
    sta APU::m_square_1_low
    lda #Sound::ENTER_SFX_HIGH
    sta APU::m_square_1_high
.endmacro

;; Make a small beep for item pickup.
.macro SOUND_ITEM_PICKUP
    lda #%10000100
    sta APU::m_square_2_envelope
    lda #Sound::PICKUP_SFX_LOW
    sta APU::m_square_2_low
    lda #Sound::PICKUP_SFX_HIGH
    sta APU::m_square_2_high
.endmacro

;; Make a small beep for item collection in the droppping zone (i.e. fuel tanks
;; and shuttle parts making into the shuttle).
.macro SOUND_ITEM_DROP
    lda #%10000100
    sta APU::m_square_2_envelope
    lda #Sound::DROP_SFX_LOW
    sta APU::m_square_2_low
    lda #Sound::DROP_SFX_HIGH
    sta APU::m_square_2_high
.endmacro

;; Start the sound effect for the rocket take off animation.
.macro START_TAKE_OFF_SOUND
    lda #$38
    sta APU::m_noise_envelope
    lda #$0F
    sta APU::m_noise_mode
    lda #0
    sta APU::m_noise_counter
.endmacro

;; Stop the sound effect for the rocket take off animation.
.macro STOP_TAKE_OFF_SOUND
    lda #0
    sta APU::m_noise_envelope
.endmacro