diff --git a/GSCats.xcodeproj/project.pbxproj b/GSCats.xcodeproj/project.pbxproj index a3bfa78..50dcf15 100644 --- a/GSCats.xcodeproj/project.pbxproj +++ b/GSCats.xcodeproj/project.pbxproj @@ -35,6 +35,8 @@ 70E9D8611F2BD95400555C19 /* gscats.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = gscats.s; sourceTree = ""; }; 70E9D8621F2BD95400555C19 /* macros.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = macros.s; sourceTree = ""; }; 70E9D8631F2BD95400555C19 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; }; + 70F011D023B91B2900C8873F /* dirt.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = dirt.s; sourceTree = ""; }; + 70F011D123B989B800C8873F /* random.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = random.s; sourceTree = ""; }; 70F0869F1F413A89002446C3 /* player.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = player.s; sourceTree = ""; }; 70F086A01F4230CB002446C3 /* utility.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = utility.s; sourceTree = ""; }; 70FE79D21F8814A600E0095C /* MerlinToCA65.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = MerlinToCA65.sh; sourceTree = ""; }; @@ -49,11 +51,13 @@ 70E9D8601F2BD95400555C19 /* graphics.s */, 70E9D8621F2BD95400555C19 /* macros.s */, 7099E3841F41022100182A82 /* gameobject.s */, + 70F011D123B989B800C8873F /* random.s */, 706DF1641F2D39F700AA6680 /* loader.s */, 700FFAFB1F40F3BF00A442DE /* font.s */, 7002647320CD78C40015B184 /* smallNumbers.s */, 706DF1651F2D4A8100AA6680 /* terrain.s */, 705AAFA920040B0D001BB0ED /* terrain_e1.s */, + 70F011D023B91B2900C8873F /* dirt.s */, 70C073091F5BAA3E009844A9 /* collision.s */, 70F086A01F4230CB002446C3 /* utility.s */, 700C39C51F2E5CA800C24F9C /* tables.s */, @@ -114,6 +118,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, ); mainGroup = 70E9D8541F2BD8EF00555C19; diff --git a/dirt.s b/dirt.s new file mode 100644 index 0000000..46a1897 --- /dev/null +++ b/dirt.s @@ -0,0 +1,298 @@ +; +; dirt +; +; Created by Quinn Dunki on 12/29/19 +; + +MAXPARTICLES = 64 + +.macro PARTICLEPTR_XY + txa ; Pointer to particle structure from index + asl + asl + asl + asl + tay +.endmacro + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; createDirtExplosion +; +; Simulate a bunch of dirt flying everywhere +; +; PARAML0 = X pos of circle left edge +; PARAML1 = Y pos of circle center +; PARAML2 = Radius (16 bits) +; +; Trashes SCRATCHL,SCRATCHL2,CACHEDATA +; +createDirtExplosion: + SAVE_AXY + + lda #1 + sta dirtExplosionActive + + lda #120 + sta PARAML0 + lda #100 + sta PARAML1 + lda #3 + sta PARAML2 + asl + sta CACHEDATA ; Cache diameter + + ldx #0 + ldy #0 +createDirtExplosionLoop: + jsr createDirtExplosionColumn + inc PARAML0 + inc PARAML0 + iny + cpy CACHEDATA + bne createDirtExplosionLoop + + RESTORE_AXY + rts + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; createDirtExplosionColumn +; +; Create one column of a starting dirt explosion circle +; X = Particle index to start filling from +; Y = Local X index of column +; PARAML0 = X pos of column +; PARAML1 = Y pos of circle center +; PARAML2 = Radius (16 bits) +; +; X=> Last particle index we filled in +1 +; Trashse SCRATCHL,SCRATCHL2 +; +createDirtExplosionColumn: + SAVE_AY + lda PARAML2 + pha + + stx SCRATCHL2 ; Cache particle index + + phy ; Look up circle table for our radius + lda PARAML2 + asl + tay + lda circleTable,y + sta SCRATCHL + + ; Find starting Y from circle table + pla + asl + tay + lda (SCRATCHL),y + tax ; Row counter + pha + eor #$ffff + inc + sta PARAML2 ; Row counter end + + pla + clc + adc PARAML1 + sta SCRATCHL ; Current Y position + +createDirtExplosionColumnLoop: + phx ; Find pointer to next particle to fill out + ldx SCRATCHL2 + PARTICLEPTR_XY + plx + + ; X position - Always the same + lda PARAML0 + sta dirtParticles+DP_POSX,y + asl ; Convert to 12.4 + asl + asl + asl + sta dirtParticles+DP_PRECISEX,y + + ; Y position - Iterates from computed cache + lda SCRATCHL + sta dirtParticles+DP_POSY,y + asl ; Convert to 12.4 + asl + asl + asl + sta dirtParticles+DP_PRECISEY,y + + ; X velocity. Need half to be negative. I feel like there + ; should be a clever branchless way to do this, but I couldn't + ; come up with it. + jsr random + lda RANDOM + bmi createDirtExplosionColumnLoopNegX + and #$00ff + bra createDirtExplosionColumnLoopNowY + +createDirtExplosionColumnLoopNegX: + ora #$ff00 + +createDirtExplosionColumnLoopNowY: + sta dirtParticles+DP_VX,y + + ; Y velocity + jsr random + lda RANDOM + and #$00ff + ora #$0100 + sta dirtParticles+DP_VY,y + + ; Advance to next particle + inc SCRATCHL2 + inc SCRATCHL + inx + cpx PARAML2 + bmi createDirtExplosionColumnLoop + + ; Gather return values + ldx SCRATCHL2 + + pla + sta PARAML2 + RESTORE_AY + rts + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; renderDirtExplosion +; +; Update and render the current dirt explosion +; +renderDirtExplosion: + SAVE_AXY + lda dirtExplosionActive + beq renderDirtExplosionDone + + ldx #0 +renderDirtExplosionLoop: + PARTICLEPTR_XY + jsr updateDirtParticle + inx ; Advance array pointer to next particle + cpx #MAXPARTICLES + bne renderDirtExplosionLoop + +renderDirtExplosionDone: + RESTORE_AXY + rts + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; updateDirtParticle +; +; Y = Offset to particle in array +; Update and render the current dirt explosion +; +; Trashes SCRATCHL,PARAML0 + +updateDirtParticle: + SAVE_AXY + + lda dirtParticles+DP_POSX,y + bmi updateDirtParticleDone ; Particle is dead + + ; Erase old position + lda #dirtParticles ; Calculate pointer to struct + sta PARAML0 + clc + tya + adc PARAML0 + sta PARAML0 + jsr vramPtr + lda #00 + sta SHADOWVRAMBANK,x + + ; Integrate gravity over velocity + lda dirtParticles+DP_VY,y + clc + adc #GRAVITY + sta dirtParticles+DP_VY,y + + ; Integrate X velocity over position + lda dirtParticles+DP_VX,y + ; Convert 8.8 to 12.4 + cmp #$8000 + ror + cmp #$8000 + ror + cmp #$8000 + ror + cmp #$8000 + ror + clc + adc dirtParticles+DP_PRECISEX,y + sta dirtParticles+DP_PRECISEX,y + + ; Convert to integer for rendering + lsr + lsr + lsr + lsr + sta dirtParticles+DP_POSX,y + + ; Integrate Y velocity over position + lda dirtParticles+DP_VY,y + ; Convert 8.8 to 12.4 + cmp #$8000 + ror + cmp #$8000 + ror + cmp #$8000 + ror + cmp #$8000 + ror + clc + adc dirtParticles+DP_PRECISEY,y + sta dirtParticles+DP_PRECISEY,y + + ; Convert to integer for rendering + lsr + lsr + lsr + lsr + sta dirtParticles+DP_POSY,y + + ; Draw new position + jsr vramPtr ; PARAML0 still holds struct pointer + cpx #$ffff + beq updateDirtParticleKill + lda #$11 + sta SHADOWVRAMBANK,x + +updateDirtParticleDone: + RESTORE_AXY + rts + +updateDirtParticleKill: + lda #-1 + sta dirtParticles+DP_POSX,y + bra updateDirtParticleDone + +dirtExplosionActive: + .word 0 + +dirtParticles: +.repeat MAXPARTICLES + .word -1 ; Pos X in pixels + .word 100 ; Pos Y in pixels + .word $a00 ; Pos X (12.4 fixed point) + .word $640 ; Pos Y (12.4 fixed point) + .word $ff00 ; Velocity X (8.8 fixed point) + .word $100 ; Velocity Y (8.8 fixed point) + .word 0,0 ; Pad to 16 bytes +.endrepeat + + +DP_POSX = 0 ; Byte offsets into dirtParticles data structure +DP_POSY = 2 +DP_PRECISEX = 4 +DP_PRECISEY = 6 +DP_VX = 8 +DP_VY = 10 diff --git a/equates.s b/equates.s index d4d907c..557cad0 100644 --- a/equates.s +++ b/equates.s @@ -21,6 +21,7 @@ PARAM2 = $08 PARAM3 = $09 PARAML0 = $06 ; 16-bit versions of params PARAML1 = $08 +PARAML2 = $d0 SCRATCH0 = $19 SCRATCH1 = $1a SCRATCHL = $19 ; 16-bit version of scratch @@ -33,7 +34,9 @@ STACKPTR = $70 ; Cache for stack pointer in fast graphics SHADOWREGISTER = $72 ; Cache for shadow register in fast graphics STACKREGISTER = $73 ; Cache for stack register in fast graphics lastCompiledTerrainY = $75 ; The highest Y value that the compiled renderer must handle - +RANDOM = $ce ; 16 bit random number +RANDOML = $ce ; Low byte of random number generator +RANDOMH = $cf ; High byte of random number generator ; Terrain constants diff --git a/gamemanager.s b/gamemanager.s index 30f9cc8..ead409e 100644 --- a/gamemanager.s +++ b/gamemanager.s @@ -9,6 +9,9 @@ NUMPLAYERS = 2 beginGameplay: + ; Initialize random numbers + lda #1 + jsr seedRandom ; Set up palette for terrain and players lda #basePalette @@ -132,7 +135,7 @@ gameplayLoopRender: ; Render the terrain if needed lda terrainDirty - beq gameplayLoopProjectiles + beq gameplayLoopExplosions ; jsl renderTerrainSpans ; Part of the now disabled fill-mode renderer jsr renderTerrain stz terrainDirty @@ -140,6 +143,10 @@ gameplayLoopRender: ; Render players jsr renderPlayers +gameplayLoopExplosions: + ; Render explosions + jsr renderDirtExplosion + gameplayLoopProjectiles: ; BORDER_COLOR #$3 diff --git a/gameobject.s b/gameobject.s index 253923e..4e205b2 100644 --- a/gameobject.s +++ b/gameobject.s @@ -58,7 +58,7 @@ placeGameObjectOnTerrain: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; vramPtr ; -; PARAML0 = Pointer to X,Y (16 bits each) +; PARAML0 = Pointer to X,Y (16 bits each, Y is bottom relative) ; X => Offset to upper left corner of VRAM, or ffff if offscreen ; ; Trashes SCRATCHL diff --git a/gscats.2mg b/gscats.2mg index 1967dc9..9b21d7b 100644 Binary files a/gscats.2mg and b/gscats.2mg differ diff --git a/gscats.s b/gscats.s index ba773c6..ad4dec5 100644 --- a/gscats.s +++ b/gscats.s @@ -35,6 +35,7 @@ quitGame: jml (PRODOSRETURN) +.include "random.s" .include "graphics.s" .include "font.s" .include "smallNumbers.s" @@ -49,7 +50,7 @@ quitGame: .include "fan.s" .include "projectile.s" .include "inventory.s" - +.include "dirt.s" endMainBank2: diff --git a/projectile.s b/projectile.s index 2bba117..ae0ce53 100644 --- a/projectile.s +++ b/projectile.s @@ -222,6 +222,9 @@ allocProjectileDone: ; ; fireProjectile: + jsr createDirtExplosion + rts + SAVE_AXY ; Allocate a projectile diff --git a/random.s b/random.s new file mode 100644 index 0000000..2c0325d --- /dev/null +++ b/random.s @@ -0,0 +1,52 @@ +; +; random.s +; GSCats +; +; Created by Quinn Dunki on 12/29/19. +; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; random +; A => Random 8 bits +; Y => Less random 8 bits +; +; Taken from https://codebase64.org/doku.php?id=base:16bit_xorshift_random_generator +; This routine is not very fast, but it'll do for now. It's intended +; for 6502 so we're making due with it. +; +rng_zp_low = RANDOML +rng_zp_high = RANDOMH + +random: + SAVE_AXY + EMULATION + LDA rng_zp_high + LSR + LDA rng_zp_low + ROR + EOR rng_zp_high + STA rng_zp_high ; high part of x ^= x << 7 done + ROR ; A has now x >> 9 and high bit comes from low byte + EOR rng_zp_low + STA rng_zp_low ; x ^= x >> 9 and the low part of x ^= x << 7 done + EOR rng_zp_high + STA rng_zp_high ; x ^= x << 8 done + NATIVE + RESTORE_AXY + RTS + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; seedRandom +; A = Seed +; +; Taken from https://codebase64.org/doku.php?id=base:16bit_xorshift_random_generator +; +seedRandom: + SAVE_AXY + EMULATION + STA rng_zp_low + LDA #0 + STA rng_zp_high + NATIVE + RESTORE_AXY + rts