From d34e0fabfca9deb5049936eefaf9df43ffcfc811 Mon Sep 17 00:00:00 2001 From: StewBC Date: Wed, 15 Jan 2020 14:33:49 -0800 Subject: [PATCH] Added Audio Added UI, engine and sfx audio --- src/apple2/audio.inc | 112 ++++++++++++++++++++++++++++++++++++++ src/apple2/defs.inc | 10 ++++ src/apple2/draw.inc | 7 ++- src/apple2/game.inc | 23 +++++++- src/apple2/input.inc | 23 +++++++- src/apple2/penetrator.asm | 9 ++- src/apple2/rodata.inc | 5 ++ src/apple2/text.inc | 7 ++- src/apple2/ui.inc | 42 +++++++++++++- src/apple2/zpvars.inc | 6 ++ 10 files changed, 235 insertions(+), 9 deletions(-) create mode 100644 src/apple2/audio.inc diff --git a/src/apple2/audio.inc b/src/apple2/audio.inc new file mode 100644 index 0000000..3a8349b --- /dev/null +++ b/src/apple2/audio.inc @@ -0,0 +1,112 @@ +;----------------------------------------------------------------------------- +; audio.inc +; Part of penetrator, the zx spectrum game, made for Apple II +; +; Stefan Wessels, 2020 +; This is free and unencumbered software released into the public domain. + +;----------------------------------------------------------------------------- +.segment "CODE" + +.proc serviceAudio + + lda audioMask ; get the active channels + and audioFrame ; see if any channel needs to play this frame + bne makeSounds ; make a sound if a channel needs audio + rts + +makeSounds: + bit Bit7Mask ; AUDIO_EXPLOSION + bne bombDrop + + bit Bit6Mask ; AUDIO_FIRE + bne fire + + bit Bit5Mask ; AUDIO_EXPLOSION + bne explosion + + bit Bit8432Mask ; AUDIO_ENGINE - Not that bit 8 is erroneusly checked + bne engine ; only because I didn't want to make a new constant + + bit Bit1Mask ; AUDIO_UI_TICK + bne ui + + rts + +explosion: + ldy audioExplFrame ; use the frame as an index + lda explDelay, y + tax + ldy #$6 + jsr playNote + inc audioExplFrame + lda audioExplFrame + cmp #%00000011 + bcc :+ + stx audioFrame ; turn off the explosion + rts +: + lda #AUDIO_EXPLOSION + sta audioFrame + rts + +bombDrop: + ldy #$50 + ldx #$10 + jmp playNote + +fire: + ldy #$20 + ldx #$10 + jmp playNote + +engine: + bit Bit4Mask ; AUDIO_MOTOR_FWD + beq :+ + ldy #$25 + ldx #$08 + jmp playNote +: + bit Bit3Mask ; AUDIO_MOTOR_REW + beq :+ + ldy #$35 + ldx #$08 + jmp playNote +: + ldy #$30 + ldx #$08 + jmp playNote + +ui: + ldy #$80 + ldx #$10 + jmp playNote + +.endproc + +;----------------------------------------------------------------------------- +.proc playNote + + delay = tempBlock + 21 + + sty delay + +loop: + lda speaker_toggle + ldy delay +: + nop + nop + nop + nop + dey + bne :- ; hold for the duration in y + dex + bne loop ; retrigger + + lda #AUDIO_EXPLOSION ; reset the audio frame (serviced) + and audioFrame ; but keep explosion if it was set + sta audioFrame + rts + +.endproc diff --git a/src/apple2/defs.inc b/src/apple2/defs.inc index 7488ebe..d7da727 100644 --- a/src/apple2/defs.inc +++ b/src/apple2/defs.inc @@ -11,6 +11,7 @@ ram_layer0 = $2000 ; HGR Pages ram_layer1 = $4000 +speaker_toggle = $C030 ;----------------------------------------------------------------------------- ; self-modifying address marker @@ -56,6 +57,15 @@ DANGER_FONT = 4 ; Number of font characters that DANGER_LENGTH = ((textEnd-1)-textDangerBar) ; how many characters in the danger column (skip trailing 0) DANGER_TICKS = ((((worldDataEnd-worldDataStart) / 3) / DANGER_LENGTH) / DANGER_FONT) +; audio constants +AUDIO_BOMB_DROP = %01000000 ; Releasing a bomb +AUDIO_FIRE = %00100000 ; Firing the gun +AUDIO_EXPLOSION = %00010000 ; An enemy exploding +AUDIO_MOTOR_FWD = %00001000 ; Speeding up +AUDIO_MOTOR_REW = %00000100 ; slowing down +AUDIO_MOTOR_PLAY = %00000010 ; flying at a constant speed +AUDIO_UI_TICK = %00000001 ; UI selection + ; The enemyBuffer is filled with these values, which break down as: ; ; Bits 1 & 2 - Type. 0 - monster, 1 - missile, 2 - radar & 3 - nuke diff --git a/src/apple2/draw.inc b/src/apple2/draw.inc index c20091e..67a0a20 100644 --- a/src/apple2/draw.inc +++ b/src/apple2/draw.inc @@ -352,7 +352,12 @@ explode: stx explFrame : - lda #3 ; set a nonsense width to make the explosio + stx audioExplFrame + lda audioFrame + ora #AUDIO_EXPLOSION + sta audioFrame + + lda #3 ; set a nonsense width to make the explosion sta zaDataWidth ; not so recognizable lda #3 ; set the dimensions of the explosion diff --git a/src/apple2/game.inc b/src/apple2/game.inc index 947b13b..3b1ede9 100644 --- a/src/apple2/game.inc +++ b/src/apple2/game.inc @@ -41,6 +41,19 @@ preamble: ; bring terrain on-screen with n wait #$40 ; give the user a moment to get ready loop: + lda runAudio + eor #1 + sta runAudio + bne :+ + jsr serviceAudio + lda audioFrame + ora #AUDIO_MOTOR_PLAY + sta audioFrame + beq loop +: + lda playerDead + bne skip + jsr inputGame ; read joystick and move player, check for pause key cmp #$9b ; check for ESC bne skip ; if not, check for pause @@ -85,7 +98,7 @@ delay: died: dec playerDead ; the counter holds the explosion state - bne skip ; don't get user input + bne loop ; don't get user input jsr gameNextPlayer ; switch players if a 2p game jmp outerloop @@ -146,6 +159,7 @@ win: sta flipFlop sta pause sta fireCoolDown + sta audioFrame tax ; clear the buffers for tracking objects : @@ -1122,6 +1136,13 @@ continue: and #%00000011 ; mask column (or strip of the sprite) sta zaSprColumn ; save it + lda audioFrame + ora #AUDIO_EXPLOSION ; Add explosion to audio frame + sta audioFrame + lda #0 ; start the explosion at frame 0 + sta audioExplFrame + + lda zaEnemyColLocal ; see where the sprite starts sec ; by subtracting the strip number (index) sbc zaSprColumn ; from the current world column diff --git a/src/apple2/input.inc b/src/apple2/input.inc index 162b6c2..07f173f 100644 --- a/src/apple2/input.inc +++ b/src/apple2/input.inc @@ -142,7 +142,10 @@ left: dex ; move left one column bmi preBomb ; if off-screen to the left, ignore stx playerShipX ; save the movement - bpl preBomb + lda audioFrame + ora #AUDIO_MOTOR_REW + sta audioFrame + bne preBomb right: bit Bit1Mask ; bit set when right joystick active @@ -153,6 +156,9 @@ right: cpx #(((XSIZE/3)*2) - SHIP_WIDTH - 1) ; limit to two-thirds toward the right bcs preBomb ; if over limit, ignore stx playerShipX ; save movement + lda audioFrame + ora #AUDIO_MOTOR_FWD + sta audioFrame preBomb: lda zaRawInput @@ -184,6 +190,9 @@ drop: sta bombY, x lda #BOMB_FRWRD_FRMS ; set it to fly forward sta bombDir, x + lda audioFrame + ora #AUDIO_BOMB_DROP + sta audioFrame nobomb: lda zaRawInput @@ -208,6 +217,9 @@ fire: sta bulletsBuffer, x ; put height into 3 columns lda #3 sta fireCoolDown + lda audioFrame + ora #AUDIO_FIRE + sta audioFrame joyDone: lda zaRawInput ; remember the key-state for next time @@ -380,10 +392,17 @@ joyDone: bne :- : txa - rts + ; rts done: and #$7f + beq :+ + pha + lda #AUDIO_UI_TICK + sta audioFrame + jsr serviceAudio + pla +: rts ; upon return 0 means no interaction, anything else is a user input .endproc diff --git a/src/apple2/penetrator.asm b/src/apple2/penetrator.asm index 34bfe65..503769a 100644 --- a/src/apple2/penetrator.asm +++ b/src/apple2/penetrator.asm @@ -21,6 +21,7 @@ jmp main ; This ends up at $080d (sys 206 .include "defs.inc" ; constants .include "macros.inc" ; vpoke, vpeek, print* & wait. .include "zpvars.inc" ; Zero Page usage (variables) +.include "audio.inc" ; Play notes fro audio events .include "input.inc" ; Keyboard/Joystick routines .include "ui.inc" ; The front-end code .include "edit.inc" ; In-game editor @@ -34,7 +35,7 @@ jmp main ; This ends up at $080d (sys 206 .include "fontdata.inc" ; The ZA Spectrum font as 2bpp, 2 bytes/char .include "logodata.inc" ; Lines to spell Penetrator and intro graphic .include "rodata.inc" ; Read Only (RODATA segment sprites, etc) -.include "logo.inc" +.include "logo.inc" ; The include to include the HGR image ;----------------------------------------------------------------------------- .segment "CODE" @@ -67,12 +68,16 @@ store: ldx #((highScoresEnd-highScores) - 1) ; set high score table scores to 0 lda #$0 + sta backLayer ; set back layer to 0 + sta audioFrame ; set all audio channels to off (0) : sta highScores, x dex bpl :- - sta backLayer ; set back layer to 0 + ldx #(AUDIO_EXPLOSION | AUDIO_BOMB_DROP | AUDIO_FIRE | AUDIO_UI_TICK) + + stx audioMask ; set the mask to all channels on ($ff) ldx #((BitMasksEnd - BitMasks) - 1) : diff --git a/src/apple2/rodata.inc b/src/apple2/rodata.inc index dd9eca6..399f5b5 100644 --- a/src/apple2/rodata.inc +++ b/src/apple2/rodata.inc @@ -181,6 +181,11 @@ BitMasks: .byte %11110000 BitMasksEnd: +;----------------------------------------------------------------------------- +; Audio delay values +explDelay: + .byte $04, $08, $06, $04 + ;----------------------------------------------------------------------------- ; Sprite Information ; Images to the right in human form, not Apple II form (i.e. bytes don't diff --git a/src/apple2/text.inc b/src/apple2/text.inc index 4d509e7..b7227d3 100644 --- a/src/apple2/text.inc +++ b/src/apple2/text.inc @@ -37,7 +37,12 @@ textOneTwo: .asciiz "\"1\" OR \"2\" FOR NUMBER OF PLAYERS" textTrain: .asciiz "\"T\" FOR TRAINING CONTROL CENTER" textEdit: .asciiz "\"E\" FOR THE LANDSCAPE EDITOR" textLoad: .asciiz "\"L\" TO LOAD ANOTHER LANDSCAPE" -textSirens: .asciiz "\"S\" TO DISABLE SIRENS" +textSirens: .asciiz "\"S\" SET AUDIO: " + +; main menu audio options +textAudio1: .asciiz "ENGINE, SFX" +textAudio2: .asciiz "SFX " +textAudio3: .asciiz "NONE " ; training mode menu textTrainingMode: .asciiz "TRAINING MODE" diff --git a/src/apple2/ui.inc b/src/apple2/ui.inc index 40ec87c..360de47 100644 --- a/src/apple2/ui.inc +++ b/src/apple2/ui.inc @@ -83,7 +83,7 @@ loop: jmp loop done: - rts + rts .endproc @@ -348,6 +348,7 @@ plot: jsr drawClearScreen +redraw: print textInstructions, #(10), #(8*04) print textPress , #(13), #(8*08) print textUnderline , #(13), #(8*09) @@ -357,6 +358,22 @@ plot: print textLoad , #(01), #(8*14) print textSirens , #(05), #(8*15) + lda audioMask + beq none + + bit Bit8432Mask + bne :+ + print textAudio2 , #(20), #(8*15) + jmp present + +: + print textAudio1 , #(20), #(8*15) + jmp present + +none: + print textAudio3 , #(20), #(8*15) + +present: jsr drawPresent lda #GAME_MODE_PLAY ; assume the player will play (play is 0) @@ -400,7 +417,21 @@ load: jmp uiMainMenu sirens: - jmp loop ; siren code instead of this + lda audioMask + beq audioSetAll + bit Bit8432Mask + bne :+ + lda #0 + beq :++ +: + lda #(AUDIO_EXPLOSION | AUDIO_BOMB_DROP | AUDIO_FIRE | AUDIO_UI_TICK) + bne :+ + +audioSetAll: + lda #%01111111 +: + sta audioMask + jmp redraw ; siren code instead of this options: .byte "12TELS" @@ -1030,6 +1061,13 @@ loop: bit Bit8Mask beq loop bit KBDSTRB + + pha + lda #AUDIO_UI_TICK + sta audioFrame + jsr serviceAudio + pla + and #$7F cmp #$0D ; enter key then done beq enter diff --git a/src/apple2/zpvars.inc b/src/apple2/zpvars.inc index 0b29c98..562ac81 100644 --- a/src/apple2/zpvars.inc +++ b/src/apple2/zpvars.inc @@ -57,6 +57,7 @@ dangerTickCount: .res 1 ; how often a dot is added to th dangerTickIdx: .res 1 ; the index into the text that holds the danger line of dots moveHorz: .res 1 ; keeps track of horizontal movement - only moves every 2nd frame moveVert: .res 1 ; keeps track of vertical movement - moves 4 rows but over 2 frames +runAudio: .res 1 ; flip-flop for service audio or run a game frame ; helper variables updateHUD: .res 1 ; 0 - don't draw, 1+ - clean and redraw @@ -67,3 +68,8 @@ enemyHitType: .res 1 ; 0 = nuke, 1 = missile, 2 = mon lastInput: .res 1 ; holds the value of the prev joystick frame pause: .res 1 ; <> 0 when the game is paused fireCoolDown: .res 1 + +; audio variables +audioMask: .res 1 ; which audio "channels" are active (and with audioFrame) +audioFrame: .res 1 ; prioritized mask for sounds to make (msb to lsb) +audioExplFrame: .res 1 ; explosion frame index, for sound \ No newline at end of file