; ; terrain ; ; Created by Quinn Dunki on 7/29/17 ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; renderTerrain ; ; No stack operations permitted here! ; ; Trashes all registers ; renderTerrain: FASTGRAPHICS lda #0 sta SCRATCHL2 ; Row counter lda #$5f1f ; 4 Point stack to end of affected VRAM tcs ; 2 sec lda #compiledTerrainEnd-VISIBLETERRAINWINDOW-4 sbc mapScrollPos sta PARAML0 renderTerrainLoop: ; Note that DP register is normally $0000, so that is used as the BG/BG case lda #$0011 ; BG/FG ldx #$1111 ; FG/FG ldy #$1100 ; FG/BG jmp (PARAML0) renderRowComplete: lda PARAML0 sec sbc #COMPILEDTERRAINROW sta PARAML0 tsc clc adc #320 tcs lda SCRATCHL2 inc cmp lastCompiledTerrainY beq renderRowCont bcs renderTerrainDone renderRowCont: sta SCRATCHL2 bra renderTerrainLoop renderTerrainDone: SLOWGRAPHICS rts ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; craterTerrain ; ; PARAML0 = X pos of center in pixels from logical left terrain edge ; PARAML1 = Y pos of center in pixels from bottom terrain edge ; Y = Radius of circle, in pixels (minimum is 3) ; ; Trashes SCRATCHL craterTerrain: SAVE_AX stz craterTerrainAccumulator lda #TERRAINWIDTH ; Convert X pos to terrain-right byte count sec sbc PARAML0 sty SCRATCHL ; Center width in bytes sbc SCRATCHL sbc SCRATCHL and #$fffe ; Force even clc adc #terrainData sta PARAML0 tya asl tay lda circleTable,y ; Look up circle data sta SCRATCHL tya ; Iterate over diameter words asl tay craterTerrainLoop: dey dey bmi craterTerrainDone tya clc adc PARAML0 ; Clip to terrain edges cmp #terrainData bmi craterTerrainDone cmp #terrainDataEnd bpl craterTerrainLoop lda (SCRATCHL),y ; Fetch circle Y value clc adc PARAML1 ; Convert to terrain-space bmi craterTerrainZero sta SCRATCHL2 sec lda (PARAML0),y sbc SCRATCHL2 bmi craterTerrainLoop ; Circle value is lower, so use that clc adc craterTerrainAccumulator ; Track total crater material sta craterTerrainAccumulator lda SCRATCHL2 ; Replace terrain height with cratered value bmi craterTerrainZero ; Don't let it be negative sta (PARAML0),y bra craterTerrainLoop craterTerrainZero: ; Circle went negative so clip to 0 lda #0 sta (PARAML0),y bra craterTerrainLoop craterTerrainDone: lda #1 sta terrainDirty RESTORE_AX rts craterTerrainAccumulator: .word 0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; clipTerrain ; ; Saves the compiled terrain data that we overwrite into ; a buffer at $E04000. We do this by shadowing the stack ; to that area and pushing. ; clipTerrain: SAVE_AXY ; Shadow stack into $E04000 to save clipped data fast tsc sta STACKPTR lda #CLIPPEDTERRAINSTACK tcs sec lda #COMPILEDTERRAINROW*MAXTERRAINHEIGHT-4 sbc mapScrollPos tay ldx #0 clipTerrainLoop: lda compiledTerrain,y pha lda #$4cea ; NOP followed by JMP sta compiledTerrain,y iny iny lda compiledTerrain,y pha lda #renderRowComplete sta compiledTerrain,y tya sec sbc #COMPILEDTERRAINROW+2 tay inx cpx lastCompiledTerrainY bcc clipTerrainLoop beq clipTerrainLoop ; Put stack back where it belongs tsc sta clippedTerrainStackPtr lda STACKPTR tcs RESTORE_AXY rts ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; unclipTerrain ; ; Restores the compiled terrain data that we wrote ; to a buffer at $E04000. We do this by mapping the stack ; to $4000, then using stack-relative addressing to pull the data. ; We can't pop the data because it's all stored in reverse. ; ; On first move-left unclip every second row is unclipped incorrectly unclipTerrain: SAVE_AXY phd lda #(CLIPPEDTERRAINSTACK & $f000) pha pld ; Point direct page at our clip data sec lda #COMPILEDTERRAINROW*MAXTERRAINHEIGHT-4 sbc mapScrollPos tay sec ; Find stopping point for stack-relative addressing lda clippedTerrainStackPtr and #$0fff sbc #3 ; -3 for starting offset sta STACKPTR lda #$0fff-3 ; Start at the bottom of the stack tax unclipTerrainLoop: lda 2,x sta compiledTerrain,y iny iny lda 0,x sta compiledTerrain,y tya sec sbc #COMPILEDTERRAINROW+2 tay dex ; Walk up the stack to reverse the data dex dex dex cpx STACKPTR ; When x hits the top of the stack, we're done bne unclipTerrainLoop pld RESTORE_AXY rts ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; compileTerrain ; ; ; compileTerrain: SAVE_AY ldy #0 lda #compiledTerrain sta PARAML0 stz compileTerrainRowStart lda #TERRAINWIDTH sta compileTerrainRowEnd compileTerrainLoop: sty PARAML1 jsr compileTerrainRow iny cpy #MAXTERRAINHEIGHT beq compileTerrainDone clc lda #COMPILEDTERRAINROW adc PARAML0 sta PARAML0 bra compileTerrainLoop compileTerrainDone: RESTORE_AY rts ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; compileTerrainChunk ; ; compileTerrainRowStart = Start column to compile (logical terrainData index) ; compileTerrainRowEnd = Ending column to compile (logical terrainData index) ; ; Trashes A,Y, SCRATCHL, PARAML0, PARAML1 ; compileTerrainChunk: ; Clamp chunk to left and right terrain edges lda compileTerrainRowStart bmi compileTerrainChunkClampZero compileTerrainChunkClampResume: lda compileTerrainRowEnd cmp #TERRAINWIDTH bcs compileTerrainChunkClampHigh compileTerrainChunkResume: ldy #0 ldx #MAXTERRAINHEIGHT-1 stx SCRATCHL sty PARAML0 ; Compute the start of memory affected lda #COMPILEDTERRAINROW sta PARAML1 jsr mult16 clc adc #compiledTerrain sta PARAML0 compileTerrainChunkLoop: sty PARAML1 jsr compileTerrainRow iny cpy SCRATCHL beq compileTerrainChunkDone clc lda #COMPILEDTERRAINROW adc PARAML0 sta PARAML0 bra compileTerrainChunkLoop compileTerrainChunkDone: rts compileTerrainChunkClampZero: stz compileTerrainRowStart bra compileTerrainChunkClampResume compileTerrainChunkClampHigh: lda #TERRAINWIDTH-1 sta compileTerrainRowEnd bra compileTerrainChunkResume ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; compileTerrainRow ; ; PARAML0 = Start of compiled row data ; PARAML1 = Row index ; compileTerrainRowStart = Start column to compile (logical terrainData index) ; compileTerrainRowEnd = Ending column to compile (logical terrainData index) ; ; Note: DA = PHX = FG/FG ; 48 = PHA = FG/BG ; 5A = PHY = BG/FG ; 0B = PHD = BG/BG compileTerrainRow: SAVE_AXY lda compileTerrainRowStart ; Initialize horizontal counters lsr lsr and #%11111100 ; Word-align it tay asl ; Logical counter is 4x compiled counter asl tax lda compileTerrainRowEnd ; Convert end counter to compiled version lsr lsr and #%11111100 sta compileTerrainRowEndCompiled compileTerrainColumnLoop: stz compileTerrainOpcode ; Check column 0 lda terrainData,x cmp PARAML1 bcc compileTerrainColumn0BG beq compileTerrainColumn0BG ; Column 0 is FG, so check column 1 inx inx lda terrainData,x cmp PARAML1 bcc compileTerrainColumn1BG beq compileTerrainColumn1BG ; Columns 0 and 1 are FG/FG, so PHX lda #$00da compileTerrainColumn2: sta compileTerrainOpcode ; Cache results so far ; Check column 2 inx inx lda terrainData,x cmp PARAML1 bcc compileTerrainColumn2BG beq compileTerrainColumn2BG ; Column 2 is FG, so check column 3 inx inx lda terrainData,x cmp PARAML1 bcc compileTerrainColumn3BG beq compileTerrainColumn3BG ; Columns 2 and 3 are FG/FG, so PHX lda compileTerrainOpcode ora #$da00 compileTerrainColumnStore: sta (PARAML0),y inx inx iny iny cpy compileTerrainRowEndCompiled bne compileTerrainColumnLoop RESTORE_AXY rts compileTerrainColumn0BG: ; Column 0 is BG, so check column 1 inx inx lda terrainData,x cmp PARAML1 bcc compileTerrainColumn01BG beq compileTerrainColumn01BG ; Columns 0 and 1 are BG/FG, so PHA lda #$0048 bra compileTerrainColumn2 compileTerrainColumn01BG: ; Columns 0 and 1 are BG/BG, so PHD lda #$000b bra compileTerrainColumn2 compileTerrainColumn1BG: ; Columns 0 and 1 are FG/BG, so PHY lda #$005a bra compileTerrainColumn2 compileTerrainColumn2BG: ; Column 2 is BG, so check column 3 inx inx lda terrainData,x cmp PARAML1 bcc compileTerrainColumn23BG beq compileTerrainColumn23BG ; Columns 2 and 3 are BG/FG, so PHA lda compileTerrainOpcode ora #$4800 bra compileTerrainColumnStore compileTerrainColumn23BG: ; Columns 2 and 3 are BG, so PHD lda compileTerrainOpcode ora #$0b00 bra compileTerrainColumnStore compileTerrainColumn3BG: ; Columns 2 and 3 are FG/BG, so PHY lda compileTerrainOpcode ora #$5a00 bra compileTerrainColumnStore compileTerrainOpcode: .word 0 compileTerrainRowStart: .word 0 compileTerrainRowEnd: .word 640 compileTerrainRowEndCompiled: .word 0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; prepareRowRendering: ; ; Set SCBs to match rendering mode of each terrain line ; ; Trashes SCRATCHL, SCRATCHL2 ; prepareRowRendering: SAVE_AXY ldx #199 stz SCRATCHL2 stz lastCompiledTerrainY prepareRowRenderingLoop: lda #0 PLAYERPTR_Y sec lda playerData+GO_POSY,y sbc #GAMEOBJECTHEIGHT ; cmp SCRATCHL2 ; bcc prepareRowRenderingCompileMode ; beq prepareRowRenderingCompileMode ; jsr enableFillMode ; bra prepareRowRenderingLoopNext prepareRowRenderingCompileMode: jsr disableFillMode inc lastCompiledTerrainY prepareRowRenderingLoopNext: inc SCRATCHL2 dex cpx #200-MAXTERRAINHEIGHT bne prepareRowRenderingLoop prepareRowRenderingDone: RESTORE_AXY rts ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; generateTerrain ; ; Trashes everything ; generateTerrain: ldy #0 ldx #0 lda #terrainData sta SCRATCHL generateTerrainLoop: lda sineTable,x lsr lsr clc adc #30 sta (SCRATCHL),y iny iny inx inx inx inx txa and #$03ff tax cpy #TERRAINWIDTH bne generateTerrainLoop ; lda #1 ; Debug values ; sta terrainData ; lda #2 ; sta compiledTerrain-4 rts compiledTerrain: .repeat COMPILEDTERRAINROW * MAXTERRAINHEIGHT .byte $42 .endrepeat compiledTerrainEnd: clippedTerrainStackPtr: .word 0