diff options
| author | Miquel Sabaté Solà <mssola@mssola.com> | 2026-04-13 16:34:11 +0200 |
|---|---|---|
| committer | Miquel Sabaté Solà <mssola@mssola.com> | 2026-04-13 16:34:11 +0200 |
| commit | aef156ac6216ed157b06a2872ba051af43317e93 (patch) | |
| tree | a6daca2bc0353e6b59823bc6bf019362ecb02507 /src | |
| parent | c3c6f3b4469bf9eadff5763fa16dbbbf15637e57 (diff) | |
| download | jetpac.nes-aef156ac6216ed157b06a2872ba051af43317e93.tar.gz jetpac.nes-aef156ac6216ed157b06a2872ba051af43317e93.zip | |
Add sound effects
Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
Diffstat (limited to 'src')
| -rw-r--r-- | src/bullets.s | 3 | ||||
| -rw-r--r-- | src/driver.s | 9 | ||||
| -rw-r--r-- | src/enemies.s | 3 | ||||
| -rw-r--r-- | src/interrupts.s | 5 | ||||
| -rw-r--r-- | src/items.s | 9 | ||||
| -rw-r--r-- | src/jetpac.s | 12 | ||||
| -rw-r--r-- | src/sound.s | 171 |
7 files changed, 212 insertions, 0 deletions
diff --git a/src/bullets.s b/src/bullets.s index 1072a8f..9e40546 100644 --- a/src/bullets.s +++ b/src/bullets.s @@ -133,6 +133,9 @@ beq @find_free_bullet_bucket @initialize_bucket: + ;; Play the bullet sound if appropiate. + jsr Sound::play_bullet_maybe + ;; We found a free bucket. Initialize the first byte to 0 since it has ;; not moved yet. The heading is taken from the player's state. lda Player::zp_state diff --git a/src/driver.s b/src/driver.s index 2bc78ca..4e54e8c 100644 --- a/src/driver.s +++ b/src/driver.s @@ -240,6 +240,9 @@ sta zp_next_bullet_cycle sta zp_next_enemy_cycle + ;; And make the sound for entering into a level. + SOUND_ENTER_LEVEL + @game: ;; Has the player died? lda Globals::zp_flags @@ -818,6 +821,9 @@ ora #%01100000 sta Globals::zp_flags + ;; Make some noise! + START_TAKE_OFF_SOUND + rts .endproc @@ -928,6 +934,9 @@ ora #%01100000 sta Globals::zp_flags + ;; Stop the sound effect for take off. + STOP_TAKE_OFF_SOUND + rts @set: diff --git a/src/enemies.s b/src/enemies.s index 2208758..76f3ddf 100644 --- a/src/enemies.s +++ b/src/enemies.s @@ -695,6 +695,9 @@ ldx Enemies::zp_pool_index sta Enemies::zp_pool_base + 3, x + ;; Play an explosion effect. + SOUND_EXPLOSION + ;; Restore back the value for the 'y' register. pla tay diff --git a/src/interrupts.s b/src/interrupts.s index 7026223..658f6a3 100644 --- a/src/interrupts.s +++ b/src/interrupts.s @@ -161,6 +161,11 @@ sta PPU::m_scroll sta PPU::m_scroll + ;; The sound effect from bullets follow a frame rule. Tick the frame count + ;; from sound as the very last thing to be done before unblocking the main + ;; code. + jsr Sound::tick + ;; Unblock the main code. lda #%01111111 and Globals::zp_flags diff --git a/src/items.s b/src/items.s index d8d1391..1e8a111 100644 --- a/src/items.s +++ b/src/items.s @@ -591,6 +591,9 @@ ;; Increase the number of collected items. inc Items::zp_collected + ;; Make the "item drop" sound + SOUND_ITEM_DROP + ;; Now we unset the 'S' bit, which is unconditionally true regardless of ;; the collection state. That being said, if we still need to collect ;; more fuel tanks (the rocket has all its parts and we have not filled @@ -679,6 +682,9 @@ @set_modes: sta Items::zp_pool_base, x + ;; Make the sound for grabbing an item. + SOUND_ITEM_PICKUP + ;; Mark the player's to be already grabbing an item. lda Items::zp_state ora #$80 @@ -988,6 +994,9 @@ ADD_ITEM_SCORE ldx Globals::zp_tmp2 + ;; Make the sound for item pickup. + SOUND_ITEM_PICKUP + rts .endproc diff --git a/src/jetpac.s b/src/jetpac.s index f2e28b2..a011a45 100644 --- a/src/jetpac.s +++ b/src/jetpac.s @@ -42,6 +42,7 @@ .include "assets.s" .include "background.s" .include "prng.s" +.include "sound.s" .include "explosions.s" .include "score.s" .include "items.s" @@ -53,6 +54,14 @@ .include "driver.s" .include "interrupts.s" +;; Sanity check for some constant values. 'cl65' fails at this, so I'm only +;; doing the check on 'nasm'. +.ifdef __NASM__ + .if Bullets::BULLET_TIMER_VALUE > Sound::BULLET_SFX_FRAME_COUNT + .error "Sound::BULLET_SFX_FRAME_COUNT must be smaller than Bullets::BULLET_TIMER_VALUE" + .endif +.endif + ;; Pretty standard reset function, nothing crazy. .proc reset ;; Disable interrupts and decimal mode. @@ -73,6 +82,9 @@ stx PPU::m_mask stx APU::m_dmc + ;; Initialize the APU. + jsr Sound::init + ;; First PPU wait. bit PPU::m_status @vblankwait1: diff --git a/src/sound.s b/src/sound.s new file mode 100644 index 0000000..a824898 --- /dev/null +++ b/src/sound.s @@ -0,0 +1,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 |
