From 3199c94eb54add855106bd5a019fc14e6c92ae67 Mon Sep 17 00:00:00 2001 From: blondie7575 Date: Sat, 29 Jul 2023 16:31:17 -0700 Subject: [PATCH] Added some border effects to title screen Also added support for interactive stateful animations --- GSCats.xcodeproj/project.pbxproj | 2 +- animation.s | 120 +++++++++++++++- graphics.s | 235 ++++++++++++++++++++++++++++--- gscats.s | 2 + titleScreen.s | 33 ++++- 5 files changed, 363 insertions(+), 29 deletions(-) diff --git a/GSCats.xcodeproj/project.pbxproj b/GSCats.xcodeproj/project.pbxproj index 1266061..8b161f9 100644 --- a/GSCats.xcodeproj/project.pbxproj +++ b/GSCats.xcodeproj/project.pbxproj @@ -80,7 +80,6 @@ 70E266E31F6F262D005AC7E4 /* circleTable.s */, 70F0869F1F413A89002446C3 /* player.s */, 70BCB0CE2A4FC92D00B19803 /* sound.s */, - 709816A32A53B21800108D9C /* GenerateSoundBank.sh */, 705456882A4D336200A2B866 /* animation.s */, 700B5E6F2069831000B31C00 /* inventory.s */, 700F21DF1F4A364600D7007D /* projectile.s */, @@ -94,6 +93,7 @@ 7076E9222A57AED90006E295 /* CompileFont.py */, 70FE79D21F8814A600E0095C /* MerlinToCA65.sh */, 700F72872112428D00225B17 /* RenumberSpriteFiles.sh */, + 709816A32A53B21800108D9C /* GenerateSoundBank.sh */, 701E70912A6DA4D80030C35D /* GenerateRawImage.py */, 7088096D1F2ECE8D00D4C950 /* GenerateRenderSpans.py */, 7059502B1F37A0BE00BBE90F /* GenerateVRAMTable.py */, diff --git a/animation.s b/animation.s index 88cff4b..48923be 100644 --- a/animation.s +++ b/animation.s @@ -7,11 +7,115 @@ ANIMATION_SIZE_16x16=0 ANIMATION_SIZE_16x32=2 +MAX_TICKS = 180 + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; startAnimation +; +; Starts a stated animation, which allows interactivity, but requires ticking +; +; Y = Base sprite index +; X = Number of frames +; A = Animation size (use constants above) +; PARAML0 = Pointer to X,Y (16 bits each, Y is bottom relative) +; +; Trashes A,X,Y,PARAML0 +; +startAnimation: + sta animationState+AS_SIZE + sty animationState+AS_BASESPRITE + sty animationState+AS_CURRENTFRAME + + ; Compute final frame + stx animationState+AS_FINALFRAME + tya + clc + adc animationState+AS_FINALFRAME + sta animationState+AS_FINALFRAME + + jsr vramPtr + cpx #$ffff + beq startAnimationSkip + stx animationState+AS_VRAM + + lda #MAX_TICKS + sta animationState+AS_TICKS + + ; Preserve background + ldy animationState+AS_VRAM + ldx animationState+AS_SIZE + jsr (protectionRoutines,x) + + ; Render first animation frame + ldy animationState+AS_VRAM + lda animationState+AS_CURRENTFRAME + jsr drawSpriteBankSafe + +startAnimationSkip: + rts + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; tickAnimation +; +; Call this once per frame to update the current stated animation +; +; A->0 if finished +; +tickAnimation: + SAVE_XY + + ; Advance tick + dec animationState+AS_TICKS + bne tickAnimationSkip + + ; Reset tick count + lda #MAX_TICKS + sta animationState+AS_TICKS + + ; Advance frame + lda animationState+AS_CURRENTFRAME + inc + cmp animationState+AS_FINALFRAME + beq tickAnimationDone + sta animationState+AS_CURRENTFRAME + + ; Restore background + ldy animationState+AS_VRAM + ldx animationState+AS_SIZE + jsr (unrenderRoutines,x) + + ; Render new animation frame + ldy animationState+AS_VRAM + lda animationState+AS_CURRENTFRAME + jsr drawSpriteBankSafe + bra tickAnimationSkip + +tickAnimationDone: + + ; Restore background + ldy animationState+AS_VRAM + ldx animationState+AS_SIZE + jsr (unrenderRoutines,x) + + lda #$ffff + sta animationState+AS_CURRENTFRAME + lda #0 + RESTORE_XY + rts + +tickAnimationSkip: + lda #1 + RESTORE_XY + rts ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; renderAnimation ; +; Plays an entire animation statelessly, which is easier if you don't need interactivity +; ; Y = Base sprite index ; X = Number of frames ; A = Animation size (use constants above) @@ -25,7 +129,6 @@ renderAnimation: phx ; Calculate VRAM position jsr vramPtr -BREAK cpx #$ffff beq renderAnimationSkip stx SCRATCHL2 @@ -78,6 +181,21 @@ protectionRoutines: unrenderRoutines: .word unrenderAnimation16x16,unrenderAnimation16x32 +animationState: + .word $ffff ; Current frame ff = inactive + .word 0 ; Total Frames + .word 0 ; Ticks until next frame + .word 0 ; Animation size + .word 0 ; VRAM Position + .word 0 ; Base Sprite + +AS_CURRENTFRAME = 0 +AS_FINALFRAME = 2 ; Actually final frame +1 +AS_TICKS = 4 +AS_SIZE = 6 +AS_VRAM = 8 +AS_BASESPRITE = 10 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; protectAnimation16x16 diff --git a/graphics.s b/graphics.s index b0d2679..8d40198 100644 --- a/graphics.s +++ b/graphics.s @@ -82,16 +82,212 @@ bottomFillLoop: initSCBs: lda #0 - ldx #$0100 ;set all $100 scbs to A + ldx #200 ;set all 200 scbs to A initSCBsLoop: dex dex sta $e19d00,x bne initSCBsLoop + + jsr setScanLineInterruptVector + jsr setVBLInterruptVector rts +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; setScanLineInterruptVector +; +; Install scanline interrupt handler +; +setScanLineInterruptVector: + + ; Save current handler + lda $e10029 + sta scanlineHandlerCache + BITS8A + lda $e1002b + sta scanlineHandlerCache+2 + BITS16 + + ; Set our handler + lda #scanLineInterruptHandler + sta $e10029 + BITS8A + lda #2 ; This code bank + sta $e1002b + BITS16 + rts + +scanlineHandlerCache: + .byte 0,0,0 + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; unsetScanLineInterruptVector +; +; Remove scanline interrupt handler +; +unsetScanLineInterruptVector: + + ; Turn off scanline interrupts + BITS8A + lda $e0c023 ; Disable scaline interrupts + and #%11111101 + sta $e0c023 + BITS16 + + ; Restore saved handler + lda scanlineHandlerCache + sta $e10029 + BITS8A + lda scanlineHandlerCache+2 + sta $e1002b + BITS16 + + rts + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; setVBLInterruptVector +; +; Install VBL interrupt handler +; +setVBLInterruptVector: + + ; Save current handler + lda $e10031 + sta vblHandlerCache + BITS8A + lda $e10033 + sta vblHandlerCache+2 + BITS16 + + ; Set our handler + lda #vblInterruptHandler + sta $e10031 + BITS8A + lda #2 ; This code bank + sta $e10033 + BITS16 + + ; Enable VBL interrupts + lda $e0c041 + ora #%00001000 + sta $e0c041 + + rts + +vblHandlerCache: + .byte 0,0,0 + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; unsetVBLInterruptVector +; +; Remove VBL handler +; +unsetVBLInterruptVector: + + ; Turn off VBL interrupts + BITS8A + lda $e0c041 ; Disable VBL interrupts + and #%11110111 + sta $e0c041 + BITS16 + + ; Restore saved handler + lda vblHandlerCache + sta $e10031 + BITS8A + lda vblHandlerCache+2 + sta $e10033 + BITS16 + + rts + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; vblInterruptHandler +; +; Handler for IRQ.VBL (vector E1/0030-0033) +; +vblInterruptHandler: + OP8 + lda #0 ; Clear scanline interrupt source + sta $e0c047 + +; lda BORDERCOLOR +; and #$f0 +; ora #$7 ; Set to sky at bottom of screen +; sta BORDERCOLOR + + clc + rtl + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; setBorderAtScanLine +; +; X = Scan Line Number +; +; Trashes A +; +setBorderAtScanLine: + dex ; Scanline interrupts technically fire on the previous line + + BITS8A + lda $e19d00,x ; Enable interrupt on requested scanline + ora #%01000000 + sta $e19d00,x + +; lda $e19dc7 ; Enable interrupt on scanline 199 +; ora #%01000000 +; sta $e19dc7 + + lda $e0c023 ; Enable scaline interrupts, if needed + ora #%00000010 + sta $e0c023 + + BITS16 + rts + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; scanLineInterruptHandler +; +; Handler for IRQ.SCAN (vector E1/0028-002B) +; +scanLineInterruptHandler: + OP8 + lda $e0c032 ; Clear scanline interrupt source + and #%11011111 + sta $e0c032 + +; lda scanLineColorChangePhaseCounter +; beq scanLineInterruptHandler0 + +; lda BORDERCOLOR ; Set border color +; and #$f0 +; ora #$7 ; Set to sky at bottom of screen +; sta BORDERCOLOR +; dec scanLineColorChangePhaseCounter +; bra scanLineInterruptHandlerDone + +scanLineInterruptHandler0: + lda BORDERCOLOR ; Set border color to grass at given scanline + and #$f0 + ora #$4 + sta BORDERCOLOR +; inc scanLineColorChangePhaseCounter + +scanLineInterruptHandlerDone: + clc + rtl + +scanLineColorChangePhaseCounter: + .byte 0 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; setScanlinePalette ; Set the palette for a given scan line @@ -192,29 +388,26 @@ drawSpriteBankSafe: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; Alternate vertical blank checkers +; syncOverscanBottom ; - -.if 0 -; The Brutal Deluxe version, taken from LemminGS -; -nextVBL: - lda #75 - pha -nextVBL0: - lda $e0c02e - and #$7f - cmp 1,s - blt nextVBL0 - cmp #100 - bge nextVBL0 - pla -waitVBL: - lda $e0c018 - bpl waitVBL +; Waits for the bottom edge of the overscan region (give or take) +syncOverscanBottom: + BITS8 +syncOverscanBottom0: + ldaA $C02f + asl ; VA is now in the Carry flag + ldaA $C02e + rol ; Roll Carry into bit 0 + cmp #215 ; A now contains line number + blt syncOverscanBottom0 + BITS16 rts +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Alternate vertical blank checkers +; + ; The Apple version, taken from GS Tech Note 039 ; syncVBL: @@ -245,6 +438,6 @@ waitVBLToStart: BITS16 rts -.endif + .include "spritebank.s" diff --git a/gscats.s b/gscats.s index a1864d5..2adf80e 100644 --- a/gscats.s +++ b/gscats.s @@ -25,6 +25,8 @@ mainBank2: jmp titleScreen quitGame: + jsr unsetVBLInterruptVector + jsr unsetScanLineInterruptVector NORMALMEMORY CLASSICVIDEO jml (PRODOSRETURN) diff --git a/titleScreen.s b/titleScreen.s index edc9238..ededbe2 100644 --- a/titleScreen.s +++ b/titleScreen.s @@ -23,7 +23,6 @@ CAT1_VRAM = $5da0 ; Shows the title screen and main game menu ; titleScreen: - lda #%10000000 ; Set all SCBs to 320, no interrupts, palette 0 jsr initSCBs stz leftScreenEdge @@ -52,6 +51,9 @@ titleScreenCopyLoop: ; Render menu text jsr titleScreenRenderMenu + ldx #147 + jsr setBorderAtScanLine + ; Fade in lda #titlePalette sta PARAML2 @@ -63,44 +65,60 @@ titleScreenCopyLoop: titleScreenMainLoop: ; Track animations - jsr nextVBL + ;jsr syncOverscanBottom + jsr syncVBL + BORDER_COLOR #$7 + lda titleAnimationCounter inc cmp TITLE_ANIMATION_FRAMES jsr titleScreenResetAnimation - ; Time animations of cats + ; Update current animation if needed + lda animationState+AS_CURRENTFRAME + bmi titleScreenIdle + jsr tickAnimation + bra titleScreenKeyboard + +titleScreenIdle: + + ; Time start of cat 0 animation lda animationDelay0 dec bne titleScreenStillCat lda #CAT_DELAY sta animationDelay0 + ; Start new cat 0 animation ldy #CAT0_VRAM jsr unrenderAnimation16x16 - ; Render next frame of cats lda #titleAnimationPos0 sta PARAML0 ldx #TITLE_ANIMATION_FRAMES ldy #30 lda #ANIMATION_SIZE_16x32 - jsr renderAnimation + jsr startAnimation bra titleScreenNextCat titleScreenStillCat: + + ; Render idle cat 0 sta animationDelay0 ldy #CAT0_VRAM lda #29 jsr drawSpriteBankSafe titleScreenNextCat: + + ; Time start of cat 1 animation lda animationDelay1 dec bne titleScreenStillCat2 lda #CAT_DELAY sta animationDelay1 + ; Start new cat 1 animation ldy #CAT1_VRAM jsr unrenderAnimation16x16 @@ -109,16 +127,19 @@ titleScreenNextCat: ldx #TITLE_ANIMATION_FRAMES ldy #21 lda #ANIMATION_SIZE_16x32 - jsr renderAnimation + jsr startAnimation bra titleScreenKeyboard titleScreenStillCat2: + + ; Render idle cat 1 sta animationDelay1 ldy #CAT1_VRAM lda #20 jsr drawSpriteBankSafe titleScreenKeyboard: + ; Check for selection jsr kbdScanTitle jsr kbdScanDebug