diff --git a/src/apple2/audio.inc b/src/apple2/audio.inc index 079831e..09d12b5 100644 --- a/src/apple2/audio.inc +++ b/src/apple2/audio.inc @@ -8,6 +8,7 @@ ;----------------------------------------------------------------------------- .segment "CODE" +;----------------------------------------------------------------------------- .proc serviceAudio lda audioMask ; get the active channels @@ -50,7 +51,7 @@ explosion: rts bombDrop: - ldy #$50 + ldy #$30 ldx #$10 jmp playNote @@ -77,8 +78,8 @@ engine: jmp playNote ui: - ldy #$80 - ldx #$10 + ldy #$40 + ldx #$08 jmp playNote .endproc @@ -91,7 +92,7 @@ ui: sty delay loop: - lda speaker_toggle + lda SPEAKER ldy delay : nop diff --git a/src/apple2/defs.inc b/src/apple2/defs.inc index b7caa79..e43fa41 100644 --- a/src/apple2/defs.inc +++ b/src/apple2/defs.inc @@ -7,19 +7,28 @@ ;----------------------------------------------------------------------------- ; System locations -.include "apple2.inc" +PATHNAME = $0280 -ram_layer0 = $2000 ; HGR Pages -ram_layer1 = $4000 +ram_layer0 = $2000 ; HGR Pages +ram_layer1 = $4000 -speaker_toggle = $C030 +MLI = $BF00 ; ProDOS API +SPEAKER = $C030 ; Access to toggle the speaker +PADDL0 = $C064 ; Read to get POT +PTRIG = $C070 ; Reset PADDLE values -PADDL0 = $C064 -PTRIG = $C070 +;----------------------------------------------------------------------------- +; MLI call type bytes +QUIT_CALL = $65 +OPEN_CALL = $C8 +READ_CALL = $CA +WRITE_CALL = $CB +CLOSE_CALL = $CC +CREATE_CALL = $C0 ;----------------------------------------------------------------------------- ; self-modifying address marker -PLACEHOLDER = $FFFF +PLACEHOLDER = $FFFF ;----------------------------------------------------------------------------- ; modes @@ -44,7 +53,7 @@ KEY_BOMB = %10000000 ;----------------------------------------------------------------------------- ; game constants XINSET = 4 ; By how many BYTE cols the display is offset into X -XSIZE = (80 - (4 * XINSET)) ; columns on screen (1 BYTE col = 2 game cols) +XSIZE = (80 - (4 * XINSET)) ; columns on screen (1 BYTE col = 2 game cols) YSIZE = 192 ; lines on screen WORLD_START = $08 ; top line where world is drawn diff --git a/src/apple2/edit.inc b/src/apple2/edit.inc index 9566a77..17fd267 100644 --- a/src/apple2/edit.inc +++ b/src/apple2/edit.inc @@ -49,6 +49,11 @@ loop: bcc notnum sbc #'1' ; stages are 0 based sta stage + + lda #AUDIO_UI_TICK ; make a sound + sta audioFrame + jsr serviceAudio + lda #0 ; reset where the buffers will start sta bufferInsert jsr drawClearScreen @@ -58,6 +63,11 @@ loop: notnum: cmp #$1b ; ESC key bne notQuit + + lda #AUDIO_UI_TICK ; make a sound + sta audioFrame + jsr serviceAudio + lda #1 sta pause ; abuse pause to signal quit rts @@ -69,6 +79,10 @@ notQuit: bne notHelp help: + lda #AUDIO_UI_TICK ; make a sound + sta audioFrame + jsr serviceAudio + jmp uiShowEditHelp notHelp: @@ -78,6 +92,10 @@ notHelp: bne notSave save: + lda #AUDIO_UI_TICK ; make a sound + sta audioFrame + jsr serviceAudio + lda #0 ; a 0 is save jsr uiFileLoadSave jmp editLoop @@ -89,6 +107,10 @@ notSave: bne done load: + lda #AUDIO_UI_TICK ; make a sound + sta audioFrame + jsr serviceAudio + lda #1 ; a 1 is load jsr uiFileLoadSave jmp editLoop @@ -235,8 +257,8 @@ missile: sta zCollision sta stopScrolling sta bufferInsert + sta dangerTickIdx sta pause - sta terrainOrigin tax ; start at 0 for the buffers : @@ -257,6 +279,10 @@ missile: lda #$ff sta lastInput ; set no keys down - all 1's + sta nextStage + + lda #DANGER_TICKS + sta dangerTickCount lda #(XSIZE/2) ; middle of the screen in X sta playerShipX ; is where the cursor is diff --git a/src/apple2/file.inc b/src/apple2/file.inc index c119d77..8af0584 100644 --- a/src/apple2/file.inc +++ b/src/apple2/file.inc @@ -5,71 +5,180 @@ ; Stefan Wessels, 2019 ; This is free and unencumbered software released into the public domain. -;----------------------------------------------------------------------------- -PATHNAME = $0280 -MLI = $BF00 - -QUIT_CALL = $65 -OPEN_CALL = $C8 -READ_CALL = $CA -WRITE_CALL = $CB -CLOSE_CALL = $CC -CREATE_CALL = $C0 ;----------------------------------------------------------------------------- .segment "DATA" + createParam: - .byte $07 ;PARAM_COUNT - .addr PATHNAME ;PATHNAME - .byte $C3 ;ACCESS - .byte $06 ;FILE_TYPE (6 is binay) - .word $0000 ;AUX_TYPE - .byte $01 ;STORAGE_TYPE - .word $0000 ;CREATE_DATE - .word $0000 ;CREATE_TIME + .byte $07 ; param_count +createName: + .addr PATHNAME ; pathname + .byte $C3 ; access + .byte $06 ; file_type (6 is binay) + .word $0000 ; aux_type + .byte $01 ; storage_type + .word $0000 ; create_date + .word $0000 ; create_time openParam: - .byte $03 ;PARAM_COUNT - .addr PATHNAME ;PATHNAME - .addr $2000-$400 ;IO_BUFFER + .byte $03 ; param_count +openName: + .addr PATHNAME ; pathname + .addr $2000-$400 ; io_buffer openRef: - .byte $00 ;REF_NUM + .byte $00 ; ref_num readParam: - .byte $04 ;PARAM_COUNT + .byte $04 ; param_count readRef: - .byte $00 ;REF_NUM - .addr worldDataStart ;DATA_BUFFER - .word $FFFF ;REQUEST_COUNT - .word $0000 ;TRANS_COUNT + .byte $00 ; ref_num +readAddress: + .addr worldDataStart ; data_buffer +readLength: + .word $FFFF ; request_count + .word $0000 ; trans_count writeParam: - .byte $04 ;PARAM_COUNT + .byte $04 ; param_count writeRef: - .byte $00 ;REF_NUM - .addr worldDataStart ;DATA_BUFFER - .word worldDataEnd-worldDataStart ;REQUEST_COUNT - .word $0000 ;TRANS_COUNT + .byte $00 ; ref_num +writeAddress: + .addr worldDataStart ; data_buffer +writeLength: + .word worldDataEnd-worldDataStart ; request_count + .word $0000 ; trans_count closeParam: - .byte $01 ;PARAM_COUNT + .byte $01 ; param_count closeRef: - .byte $00 ;REF_NUM + .byte $00 ; ref_num - ;----------------------------------------------------------------------------- .segment "CODE" ;----------------------------------------------------------------------------- -.proc fileSave +.proc saveWorld - jsr MLI ; create the file - .byte CREATE_CALL - .word createParam - bcc :+ - jmp error + lda #worldDataStart + sta writeAddress + 1 + + lda #<(worldDataEnd-worldDataStart) ; set the size + sta writeLength + lda #>(worldDataEnd-worldDataStart) + sta writeLength + 1 + + jmp saveFile ; save + +.endproc + +;----------------------------------------------------------------------------- +.proc loadWorld + + lda #worldDataStart + sta readAddress + 1 + + lda #<(worldDataEnd-worldDataStart) ; set the length (size) + sta readLength + lda #>(worldDataEnd-worldDataStart) + sta readLength + 1 + + jmp loadFile ; load + +.endproc + +;----------------------------------------------------------------------------- +.proc saveHighScores + + jsr setHighScoreFileNames ; set the name + + lda #scoresTable + sta writeAddress + 1 + + lda #<(scoresTableEnd-scoresTable) ; set the size + sta writeLength + lda #>(scoresTableEnd-scoresTable) + sta writeLength + 1 + + jmp saveFile ; save + +.endproc + +;----------------------------------------------------------------------------- +.proc loadHighScores + + jsr setHighScoreFileNames ; set the name + + lda #scoresTable + sta readAddress + 1 + + lda #<(scoresTableEnd-scoresTable) ; set the size + sta readLength + lda #>(scoresTableEnd-scoresTable) + sta readLength + 1 + + jmp loadFile ; load + +.endproc + +;----------------------------------------------------------------------------- +.proc setHighScoreFileNames + + ldx pathPos ; append to the end of the path + ldy #0 ; from the 1st char pf the name +: + lda hihgScoreFileName, y ; copy name to path + sta PATHNAME, x + beq :+ + iny + inx + bne :- : - jsr MLI + stx PATHNAME + dec PATHNAME ; don't count the trailing null + + rts + +.endproc + +;----------------------------------------------------------------------------- +.proc setWorldFileName + + zaEntryL = zWorldPtr ; internal - ptr to a string being entered (abusing world ptr) + + tya ; add the file length to the path length + clc + adc pathPos + tax + stx PATHNAME + dec PATHNAME ; don't count the trailing null + +: + lda (zaEntryL), y ; copy the file name to the end of the path + sta PATHNAME, x + dex + dey + bpl :- + + rts + +.endproc + +;----------------------------------------------------------------------------- +.proc saveFile + + jsr MLI ; create the file, ignoring errors + .byte CREATE_CALL + .word createParam + + jsr MLI ; open the (now hopefully existing) file .byte OPEN_CALL .word openParam bcc :+ @@ -96,7 +205,7 @@ error: .endproc ;----------------------------------------------------------------------------- -.proc fileLoad +.proc loadFile jsr MLI .byte OPEN_CALL diff --git a/src/apple2/game.inc b/src/apple2/game.inc index f3cea96..dc6fcbb 100644 --- a/src/apple2/game.inc +++ b/src/apple2/game.inc @@ -41,16 +41,6 @@ preamble: ; bring terrain on-screen with n wait #$40 ; give the user a moment to get ready loop: - lda runAudio ; alternate between game and audio - eor #1 - sta runAudio - beq :+ ; run the game when runAudio eq 0 - jsr serviceAudio ; run the audio - jmp loop ; go back and flip to the game -: - lda playerDead ; check if the input should run - bne skip ; and skip if not - jsr inputGame ; read joystick and move player, check for pause key cmp #$9b ; check for ESC bne skip ; if not, check for pause @@ -90,12 +80,13 @@ delay: lda victory ; when set, either a nuke or back home bne win + jsr serviceAudio ; run the audio lda playerDead beq loop ; playerDead = 0 means alive so keep going died: dec playerDead ; the counter holds the explosion state - bne loop ; don't get user input + bne skip ; don't get user input jsr gameNextPlayer ; switch players if a 2p game jmp outerloop @@ -153,7 +144,6 @@ win: sta dangerTickIdx sta moveHorz sta moveVert - sta runAudio sta flipFlop sta pause sta fireCoolDown diff --git a/src/apple2/input.inc b/src/apple2/input.inc index aabfc6d..9574452 100644 --- a/src/apple2/input.inc +++ b/src/apple2/input.inc @@ -288,8 +288,11 @@ joyDone: sta rawEor ; and save this state as the key state Button_B: - bit Bit7Mask ; Button_B + bit Bit7Mask ; Button_B beq Button_A + lda #AUDIO_UI_TICK ; make a sound + sta audioFrame + jsr serviceAudio lda brushType bne radar ora #KEY_RIGHT ; when pressed, move right as well @@ -305,6 +308,9 @@ radar: Button_A: bit Bit8Mask ; Button_A beq up + lda #AUDIO_UI_TICK ; make a sound + sta audioFrame + jsr serviceAudio lda brushType bne missile ora #KEY_RIGHT @@ -356,6 +362,9 @@ Button_Start: and lastInput ; and debounce bit Bit5Mask ; test if Start button down beq joyDone + lda #AUDIO_UI_TICK ; make a sound + sta audioFrame + jsr serviceAudio lda brushType ; if down, toggle the brush type between terrain and enemies eor #1 sta brushType diff --git a/src/apple2/penetrator.asm b/src/apple2/penetrator.asm index 236d645..cf9f2ec 100644 --- a/src/apple2/penetrator.asm +++ b/src/apple2/penetrator.asm @@ -18,6 +18,7 @@ jmp main ; This ends up at $080d (sys 2061's target) ;----------------------------------------------------------------------------- +.include "apple2.inc" ; Apple II locations from cc65 .include "defs.inc" ; constants .include "macros.inc" ; vpoke, vpeek, print* & wait. .include "zpvars.inc" ; Zero Page usage (variables) @@ -39,12 +40,15 @@ jmp main ; This ends up at $080d (sys 206 ;----------------------------------------------------------------------------- .segment "CODE" + +;----------------------------------------------------------------------------- .proc main jsr mainGameSetup : jsr inputCheckForInput ; wait for user interaction beq :- + jsr drawClearScreen jsr drawPresent : jsr uiTitleScreen @@ -56,6 +60,27 @@ jmp main ; This ends up at $080d (sys 206 ;----------------------------------------------------------------------------- .proc mainGameSetup + lda #$0 + sta backLayer ; set back layer to 0 + sta audioFrame ; set all audio channels to off (0) + sta numPlayers ; Init initially to 0 (not set for training) + + lda PATHNAME ; length of file path + tax ; put in index +: + lda PATHNAME, x ; get character + cmp #'/' ; look backwards for directory seperator + beq :+ + dex + bne :- + +: + inx ; 1 or 1 past / is where the file name starts + stx pathPos ; save that location + + jsr loadHighScores ; load high scores from disc + bcc cont ; on success, skip the init + ldx #((textHSEnd-textHS) - 1) ; empty the high score names to spaces store: lda textHS, x @@ -66,19 +91,16 @@ store: dex bpl 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 numPlayers ; Init initially to 0 (not set for training) + ldx #((highScoresEnd-highScores) - 1) ; set high score table scores to 0 : sta highScores, x dex bpl :- - ldx #(AUDIO_EXPLOSION | AUDIO_BOMB_DROP | AUDIO_FIRE | AUDIO_UI_TICK) - - stx audioMask ; set the mask to all channels on ($ff) +cont: + lda #(AUDIO_EXPLOSION | AUDIO_BOMB_DROP | AUDIO_FIRE | AUDIO_UI_TICK) + sta audioMask ; set the mask for default ON channels ldx #((BitMasksEnd - BitMasks) - 1) : diff --git a/src/apple2/text.inc b/src/apple2/text.inc index 7a22198..ba692aa 100644 --- a/src/apple2/text.inc +++ b/src/apple2/text.inc @@ -38,6 +38,7 @@ textTrain: .asciiz "\"T\" FOR TRAINING CONTROL CENTER" textEdit: .asciiz "\"E\" FOR THE LANDSCAPE EDITOR" textLoad: .asciiz "\"L\" TO LOAD ANOTHER LANDSCAPE" textSirens: .asciiz "\"S\" SET AUDIO: " +textQuit: .asciiz "\"Q\" QUIT" ; main menu audio options textAudio1: .asciiz "ENGINE, SFX" @@ -102,28 +103,7 @@ textFileSuccess: .asciiz "SUCCESS." textFileThe: .asciiz "The" textFileFailed: .asciiz "Failed. Error Code" -;----------------------------------------------------------------------------- -.segment "DATA" - -textHS: -textHighScore1: .asciiz "123456" -textHighScore2: .asciiz "123456" -textHighScore3: .asciiz "123456" -textHighScore4: .asciiz "123456" -textHighScore5: .asciiz "123456" -textHSEnd: - -textStage: .asciiz "1" -textPlayerNum: .asciiz "1" -textNumber: .asciiz "1234567" ; score display 10x score - extra digit needed -textDangerBar: .asciiz "123" -textEnd: - -textFileName: .asciiz "PENEWORLD01" ; default file name -textFilePad: .asciiz "" ; This makes the null terminator on textFileName viable and a 12 char filename -textFileNameEnd: -szHex: .asciiz " " - +hihgScoreFileName:.asciiz "PENSCORES" ;----------------------------------------------------------------------------- .segment "CODE" diff --git a/src/apple2/ui.inc b/src/apple2/ui.inc index db00229..fb2c8de 100644 --- a/src/apple2/ui.inc +++ b/src/apple2/ui.inc @@ -199,6 +199,9 @@ show: ; a high-score was set, so show width = tempBlock + 14 height = tempBlock + 15 + lda #0 + sta audioExplFrame ; use this as a delay counter for logo sound + lda #((dataLogoLinesEnd - dataLogoLines) / 4) sta remain @@ -215,6 +218,8 @@ show: ; a high-score was set, so show loop: dec remain ; iterate over all lines bpl :+ + lda #0 + sta audioExplFrame rts : ldx index @@ -285,6 +290,18 @@ plot: lda skip ; if key/button pressed - fast draw logo bne :+ + lda audioMask + beq nosnd +snd: + ldy audioExplFrame + ldx #4 + jsr playNote ; play the note + dec audioExplFrame ; alter the note + dec audioExplFrame + bmi nosnd ; variable decay so that the duration + dec audioExplFrame ; is a close match to when the logo ends + dec audioExplFrame +nosnd: jsr inputCheckForInput ; check for user interaction sta skip bne :+ @@ -357,6 +374,7 @@ redraw: print textEdit , #(02), #(8*13) print textLoad , #(01), #(8*14) print textSirens , #(05), #(8*15) + print textQuit , #(12), #(8*16) lda audioMask beq none @@ -416,7 +434,10 @@ load: jsr uiFileLoadSave ; call the code to do file name and load jmp uiMainMenu -sirens: +sirens: ; For Apple II this is the "set sound type" + cpx #5 + bne quit + lda audioMask beq audioSetAll bit Bit8432Mask @@ -431,10 +452,23 @@ audioSetAll: lda #%01111111 : sta audioMask - jmp redraw ; siren code instead of this + jmp redraw +quit: + inc PWREDUP ; Make sure this isn't a power vector -options: .byte "12TELS" + jsr MLI ; call the prodos mli ($bf00) - never returns here + .byte QUIT_CALL ; call type = quit + .addr quitParam ; pointer to parameter table + +quitParam: + .byte 4 ; number of parameters is 4 + .byte 0 ; quit type + .word 0000 ; reserved + .byte 0 ; reserved + .word 0000 ; reserved + +options: .byte "12TELSQ" optionsEnd: .endproc @@ -778,7 +812,8 @@ copyScore: dey bpl :- - jmp uiGetHighScoreName ; now get the new name + jsr uiGetHighScoreName ; now get the new name + jmp saveHighScores .endproc @@ -981,13 +1016,7 @@ enter: lda #0 ; and null terminate the string sta (zaEntryL), y -setPathName: - sty PATHNAME -: - lda (zaEntryL), y - sta PATHNAME+1,y - dey - bpl :- + jsr setWorldFileName lda #1 ; enter has been pressed - it's all over sta allOver ; time to attempt a disc action, but just redraw once more @@ -996,13 +1025,13 @@ setPathName: disc: lda forLoad ; load or save beq save - - jsr fileLoad ; load the file by the name + + jsr loadWorld ; load the file by the name bcs fail ; if carry set then there's an error jmp success ; no error - all good save: - jsr fileSave ; save the file + jsr saveWorld ; save the file bcs fail ; if carry set then there's an error success: diff --git a/src/apple2/variables.inc b/src/apple2/variables.inc index 556ef0b..d7fcae2 100644 --- a/src/apple2/variables.inc +++ b/src/apple2/variables.inc @@ -20,6 +20,9 @@ rsEnd: playerStats: .res ((rsEnd-rsStart) * 2) ; 2 copies of stats, for p1 and p2 is 2p game +;----------------------------------------------------------------------------- +; High scores table saved to disc +scoresTable: highScores: highScore1: .res 3 highScore2: .res 3 @@ -28,6 +31,29 @@ highScore4: .res 3 highScore5: .res 3 highScoresEnd: +textHS: +textHighScore1: .asciiz "123456" +textHighScore2: .asciiz "123456" +textHighScore3: .asciiz "123456" +textHighScore4: .asciiz "123456" +textHighScore5: .asciiz "123456" +textHSEnd: +scoresTableEnd: + +;----------------------------------------------------------------------------- +; user facing text area +textStage: .asciiz "1" +textPlayerNum: .asciiz "1" +textNumber: .asciiz "1234567" ; score display 10x score - extra digit needed +textDangerBar: .asciiz "123" +textEnd: + +textFileName: .asciiz "PENEWORLD01" ; default file name +textFilePad: .asciiz "" ; This makes the null terminator on textFileName viable and a 12 char filename +textFileNameEnd: +szHex: .asciiz " " + +;----------------------------------------------------------------------------- ; per "run" buffers bombX: .res NUM_BOMBS ; 2 bombs allowed, their x values bombY: .res NUM_BOMBS ; Y values for 2 bombs (0=available) diff --git a/src/apple2/zpvars.inc b/src/apple2/zpvars.inc index 562ac81..1fb9c13 100644 --- a/src/apple2/zpvars.inc +++ b/src/apple2/zpvars.inc @@ -57,7 +57,6 @@ 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,7 +66,9 @@ enemyHit: .res 1 ; the flags of the enemy destroy enemyHitType: .res 1 ; 0 = nuke, 1 = missile, 2 = monster, 3 = radar, $ff - nothing lastInput: .res 1 ; holds the value of the prev joystick frame pause: .res 1 ; <> 0 when the game is paused -fireCoolDown: .res 1 +fireCoolDown: .res 1 ; decay to zero before fire allowed +pathPos: .res 1 ; index to start of file name in path + ; audio variables audioMask: .res 1 ; which audio "channels" are active (and with audioFrame)