diff --git a/.gitignore b/.gitignore index 57794ad..0829c50 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /gscats.lst /GSCats.xcodeproj/xcuserdata/qd.xcuserdatad/xcdebugger /loader.lst +/terrain_e1.lst diff --git a/GSCats.xcodeproj/project.pbxproj b/GSCats.xcodeproj/project.pbxproj index 099a370..875780a 100644 --- a/GSCats.xcodeproj/project.pbxproj +++ b/GSCats.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 700F21E01F4A3A5500D7007D /* GenerateTrigTables.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = GenerateTrigTables.py; sourceTree = ""; }; 700FFAFB1F40F3BF00A442DE /* font.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = font.s; sourceTree = ""; }; 7059502B1F37A0BE00BBE90F /* GenerateVRAMTable.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = GenerateVRAMTable.py; sourceTree = ""; }; + 705AAFA920040B0D001BB0ED /* terrain_e1.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = terrain_e1.s; sourceTree = ""; }; 706DF1641F2D39F700AA6680 /* loader.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = loader.s; sourceTree = ""; }; 706DF1651F2D4A8100AA6680 /* terrain.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = terrain.s; sourceTree = ""; }; 7088096D1F2ECE8D00D4C950 /* GenerateRenderSpans.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = GenerateRenderSpans.py; sourceTree = ""; }; @@ -20,6 +21,8 @@ 7099E3841F41022100182A82 /* gameobject.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = gameobject.s; sourceTree = ""; }; 7099E3851F4107B100182A82 /* GenerateVRAMYOffset.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = GenerateVRAMYOffset.py; sourceTree = ""; }; 70A80FB01F43D7F200BD34C9 /* gamemanager.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = gamemanager.s; sourceTree = ""; }; + 70BDCBC92006AD5F00CB51F1 /* linkerConfig */ = {isa = PBXFileReference; lastKnownFileType = text; path = linkerConfig; sourceTree = ""; }; + 70BDCBCA200A99F200CB51F1 /* ParseMapFile.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = ParseMapFile.py; sourceTree = ""; }; 70C073091F5BAA3E009844A9 /* collision.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = collision.s; sourceTree = ""; }; 70E266E31F6F262D005AC7E4 /* circleTable.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = circleTable.s; sourceTree = ""; }; 70E554C41F807ADB00F3C871 /* spritebank.s */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; path = spritebank.s; sourceTree = ""; }; @@ -45,6 +48,7 @@ 706DF1641F2D39F700AA6680 /* loader.s */, 700FFAFB1F40F3BF00A442DE /* font.s */, 706DF1651F2D4A8100AA6680 /* terrain.s */, + 705AAFA920040B0D001BB0ED /* terrain_e1.s */, 70C073091F5BAA3E009844A9 /* collision.s */, 70F086A01F4230CB002446C3 /* utility.s */, 700C39C51F2E5CA800C24F9C /* tables.s */, @@ -55,12 +59,14 @@ 70E554C41F807ADB00F3C871 /* spritebank.s */, 70E9D8611F2BD95400555C19 /* gscats.s */, 70E9D8631F2BD95400555C19 /* Makefile */, + 70BDCBC92006AD5F00CB51F1 /* linkerConfig */, 70FE79D21F8814A600E0095C /* MerlinToCA65.sh */, 7088096D1F2ECE8D00D4C950 /* GenerateRenderSpans.py */, 7059502B1F37A0BE00BBE90F /* GenerateVRAMTable.py */, 7099E3851F4107B100182A82 /* GenerateVRAMYOffset.py */, 700F21E01F4A3A5500D7007D /* GenerateTrigTables.py */, 709175C01F60D263008FAFAB /* GenerateCircles.py */, + 70BDCBCA200A99F200CB51F1 /* ParseMapFile.py */, ); sourceTree = ""; }; diff --git a/GenerateSpanDataTable.py b/GenerateSpanDataTable.py new file mode 100755 index 0000000..1018c12 --- /dev/null +++ b/GenerateSpanDataTable.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 + +import sys + + +def main(argv): + + print ("terrainSpanDataPtr:",end="") + rowCount = 19 + MAXTERRAINHEIGHT = 100 # Must match assembly code! + + for jump in range(0,MAXTERRAINHEIGHT): + rowCount += 1 + if (rowCount==20): + print ("\n\t.word ", end="") + rowCount=0 + + print ("SPANROWSIZE*%d" % (jump), end="") + if (rowCount<19): + print (",", end="") + + +if __name__ == "__main__": + main(sys.argv[1:]) + diff --git a/GenerateVRAMYOffset.py b/GenerateVRAMYOffset.py index 6cfa08e..05b3be6 100755 --- a/GenerateVRAMYOffset.py +++ b/GenerateVRAMYOffset.py @@ -5,7 +5,6 @@ import sys def main(argv): - # Prologue print ("vramYOffset:",end="") rowCount = 19 @@ -19,6 +18,19 @@ def main(argv): if (rowCount<19): print (",", end="") + print ("\n\vramRowInvertedSpanLookup:",end="") + rowCount = 19 + + for jump in range(0,200): + rowCount += 1 + if (rowCount==20): + print ("\n\t.word ", end="") + rowCount=0 + + print ("$%04x" % ((200-jump)*160+8192), end="") + if (rowCount<19): + print (",", end="") + if __name__ == "__main__": main(sys.argv[1:]) diff --git a/Makefile b/Makefile index 2b99679..5093e9e 100644 --- a/Makefile +++ b/Makefile @@ -16,20 +16,23 @@ ADDR=800 PGM=gscats MRSPRITE=../MrSprite/mrsprite CHROMA=00ff00 -PALETTE=a4dffb 008800 886611 cc9933 eebb44 dd6666 ff99aa 000000 0e7db1 ffff00 ffff00 ffff00 ffff00 ffff00 ffff00 ffffff +PALETTE=a4dffb a4dffb 008800 886611 cc9933 eebb44 dd6666 ff99aa 0e7db1 000000 ffff00 ffff00 ffff00 ffff00 ffff00 ffffff SPRITES=SpriteBank +REMOTESYMBOLS=-Wl $(shell ./ParseMapFile.py *.map) -all: $(PGM) loader +all: terrain_e1 $(PGM) loader $(PGM): - @PATH=$(PATH):/usr/local/bin; $(CL65) -t apple2enh --cpu 65816 --start-addr 0000 -l$(PGM).lst $(PGM).s + @echo $(REMOTESYMBOLS) + @PATH=$(PATH):/usr/local/bin; $(CL65) -t apple2enh -C linkerConfig --cpu 65816 --start-addr 0000 -l$(PGM).lst $(REMOTESYMBOLS) $(PGM).s java -jar $(AC) -d $(PGM).2mg CODEBANK java -jar $(AC) -p $(PGM).2mg CODEBANK BIN 0x0000 < $(PGM) java -jar $(AC) -d $(PGM).2mg $(SPRITES)00 java -jar $(AC) -p $(PGM).2mg $(SPRITES)00 BIN 0x0000 < $(SPRITES)00.bin rm -f $(PGM) rm -f $(PGM).o + rm -f terrain_e1.map osascript V2Make.scpt $(PROJECT_DIR) $(PGM) loader: @@ -39,11 +42,21 @@ loader: rm -f loader rm -f loader.o +terrain_e1: + @PATH=$(PATH):/usr/local/bin; $(CL65) -t apple2enh -C linkerConfig --cpu 65816 --start-addr $(ADDR) -l -vm -m terrain_e1.map terrain_e1.s + java -jar $(AC) -d $(PGM).2mg CODEBANKE1 + java -jar $(AC) -p $(PGM).2mg CODEBANKE1 BIN 0x800 < terrain_e1 + rm -f terrain_e1 + rm -f terrain_e1.o + clean: rm -f $(PGM) rm -f $(PGM).o rm -f loader rm -f loader.o + rm -f terrain_e1.o + rm -f terrain_e1.map + rm -f terrain_e1 .PHONY: art art: diff --git a/ParseMapFile.py b/ParseMapFile.py new file mode 100755 index 0000000..b9ed593 --- /dev/null +++ b/ParseMapFile.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 + +# Outputs a list of exported symbols found in an ld65 link map file +# suitable for passing as a commandline option to define those +# same symbols in a cl65 compile/assemble operation. This allows +# sharing symbols between code banks without having to actually +# link them together. Sort of a low-budget DLL mechanism. :) + +import sys + + +def parseInputFile(filename,bank): + + inputFile = open(filename) + contents = inputFile.readlines() + output = "" + + # Find exported symbols + lineNum = 0 + for line in contents: + if line.find("Exports list") != -1: + break + lineNum+=1 + + # Found the exports table, so look for our symbol names + first=1 + for line in contents[lineNum+1:]: + if line.startswith("_") or line.startswith("-"): + continue + if line.startswith("Imports") or len(line)<2: + break + + if first!=1: + output += "," + first = 0 + + columns = line.split() + if len(columns) > 3: + output += ("-D,%s=0x%s,-D,%s=0x%s" % (columns[0],bank+columns[1][2:],columns[3],bank+columns[4][2:])) + else: + output += ("-D,%s=0x%s" % (columns[0],bank+columns[1][2:])) + + inputFile.close() + return output + + +def main(argv): + commandline = "" + first=1 + + for file in argv: + + if first!=1: + commandline += "," + first = 0 + + bank = file[-6:-4] # Assumes filename_XX.map + commandline += parseInputFile(file,bank) + + print (commandline) + + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/SpriteBank00.bin b/SpriteBank00.bin index f070860..6da7fb8 100644 Binary files a/SpriteBank00.bin and b/SpriteBank00.bin differ diff --git a/V2Make.scpt b/V2Make.scpt index 050b92c..dc60475 100644 Binary files a/V2Make.scpt and b/V2Make.scpt differ diff --git a/collision.s b/collision.s index 5ae9c71..91c0249 100644 --- a/collision.s +++ b/collision.s @@ -50,7 +50,7 @@ intersectRectTerrain: ; Check Y bottom intersectRectTerrainBottomLoop: - lda terrainData,y + lda terrainDataFar,y cmp rectParams+6 bpl intersectRectTerrainYep dey diff --git a/equates.s b/equates.s index 38589c5..6558ecf 100644 --- a/equates.s +++ b/equates.s @@ -26,6 +26,30 @@ SCRATCH1 = $1a SCRATCHL = $19 ; 16-bit version of scratch SCRATCHL2 = $67 ; Second 16-bit scratch PARAM24 = $67 ; 24-bit param (This is almost certainly messing up AppleSoft, but meh) +CACHEPTR = $6A ; General purpose cache pointer (This is almost certainly messing up AppleSoft, but meh) +CACHEDATA = $6C ; General purpose cache data (This is almost certainly messing up AppleSoft, but meh) +leftScreenEdge = $6E ; Reserved for leftScreenEdge (This is almost certainly messing up AppleSoft, but meh) 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 + + + +; Terrain constants +TERRAINWIDTH = 640 ; In pixels +MAXTERRAINHEIGHT = 100 ; In pixels +COMPILEDTERRAINROW = TERRAINWIDTH/4+4 ; In words, +4 to make room for clipping jump at end of row +VISIBLETERRAINWIDTH = TERRAINWIDTH/4 ; In words- width minus jump return padding +VISIBLETERRAINWINDOW = 80 ; In words +MAXSPANSPERROW = 15 +SPANROWBYTES = MAXSPANSPERROW*2 + 2 ; In bytes +CLIPPEDTERRAINSTACK = $3fff ; Location of stack where clipped terrain data lives + +; Terrain data, stored as height values 2 pixels wide (bytes) +terrainDataFar = $02f500 +terrainData = $f500 +; .repeat TERRAINWIDTH/2 +; .word 0 +; .endrepeat +terrainDataEnd = terrainData + (TERRAINWIDTH/2 * 2) diff --git a/gamemanager.s b/gamemanager.s index 19a7c2e..c3095a8 100644 --- a/gamemanager.s +++ b/gamemanager.s @@ -27,9 +27,8 @@ beginGameplay: jsr colorFill ; Generate, compile, and clip terrain + stz leftScreenEdge jsr generateTerrain - jsr compileTerrain - jsr clipTerrain ; Create players lda #56 @@ -45,21 +44,21 @@ beginGameplay: jsr protectPlayers jsr protectProjectiles + jsr prepareRowRendering + + jsr compileTerrain + jsr clipTerrain + jsl renderTerrainSpans gameplayLoop: - jsr syncVBL + BORDER_COLOR #$0 - ; Render the terrain if needed - lda terrainDirty - beq gameplayLoopKbd - jsr renderTerrain - stz terrainDirty - - ; Render players - jsr renderPlayers - -gameplayLoopKbd: + ;;;;;;;;;;; + ; Update + ; + lda #1 + sta projectilesDirty lda projectileActive bpl gameplayLoopShotTracking ; Skip input during shots @@ -67,8 +66,13 @@ gameplayLoopKbd: jsr kbdScan ; Check for pause - lda paused - bne gameplayLoopEndFrame +; lda paused +; bne gameplayLoopEndFrame + + bra gameplayLoopScroll + +gameplayLoopShotTracking: + jsr trackActiveShot gameplayLoopScroll: @@ -91,20 +95,44 @@ gameplayLoopPower: gameplayLoopFire: lda fireRequested - beq gameplayLoopProjectiles + beq gameplayLoopRender jsr fire -gameplayLoopProjectiles: +gameplayLoopRender: sta KBDSTROBE + + ;;;;;;;;;;; + ; Render + ; + + ; Render the terrain if needed + lda terrainDirty + beq gameplayLoopProjectiles + BORDER_COLOR #$3 + jsl renderTerrainSpans + jsr renderTerrain + + stz terrainDirty + BORDER_COLOR #$1 + + ; Render players + jsr renderPlayers + +gameplayLoopProjectiles: + lda projectilesDirty + beq gameplayLoopProjectilesSkip + jsr unrenderProjectiles jsr updateProjectilePhysics jsr protectProjectiles jsr renderProjectiles + +gameplayLoopProjectilesSkip: jsr updateProjectileCollisions - lda turnRequested - beq gameplayLoopVictoryCondition - jsr endTurn +; lda turnRequested +; beq gameplayLoopVictoryCondition +; jsr endTurn gameplayLoopVictoryCondition: lda gameOver @@ -113,12 +141,10 @@ gameplayLoopVictoryCondition: gameplayLoopEndFrame: lda quitRequested - beq gameplayLoop + beq gameplayLoopContinue jmp quitGame - -gameplayLoopShotTracking: - jsr trackActiveShot - bra gameplayLoopScroll +gameplayLoopContinue: + jmp gameplayLoop ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -218,9 +244,12 @@ endGame: ; scrollMap: jsr unclipTerrain + jsl unrenderTerrainSpans jsr unrenderPlayers jsr unrenderProjectiles + jsr updateProjectilePhysics ; Good idea? + sta mapScrollPos asl sta leftScreenEdge @@ -235,8 +264,10 @@ scrollMap: jsr protectPlayers jsr protectProjectiles jsr renderPlayers + jsr renderProjectiles ; Prevents flicker, but ads jitter to shot tracking lda #1 sta terrainDirty + stz projectilesDirty rts @@ -287,7 +318,7 @@ fire: basePalette: - .word $0aef,$0080,$0861,$0c93,$0eb4,$0d66,$0f9a,$0000,$07b,$0000,$0000,$0000,$0000,$0000,$0000,$0FFF + .word $0aef,$0aef,$0080,$0861,$0c93,$0eb4,$0d66,$0f9a,$007b,$0000,$0000,$0000,$0000,$0000,$0000,$0FFF @@ -305,6 +336,8 @@ turnRequested: .word $0000 terrainDirty: .word 1 +projectilesDirty: + .word 1 activePlayer: .word 0 currentPlayer: @@ -323,7 +356,7 @@ paused: ; c) Byte-distance from left edge of logical terrain to right edge of visible screen minus game object width in words mapScrollPos: .word 0 -leftScreenEdge: - .word 0 +;leftScreenEdge = $6E ; Moved to zero page for speed and cross-bank convenience +; .word 0 rightScreenEdge: .word 160-GAMEOBJECTWIDTH/4-1 diff --git a/graphics.s b/graphics.s index 110a7a1..782c527 100644 --- a/graphics.s +++ b/graphics.s @@ -80,6 +80,42 @@ initSCBsLoop: rts +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; enableFillMode +; Enables fill mode for a given scanline +; +; X = Scan line +; +enableFillMode: + SAVE_AXY + BITS8 + lda $e19d00,x + ora #%00100000 + sta $e19d00,x + BITS16 + RESTORE_AXY + rts + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; disableFillMode +; Disables fill mode for a given scanline +; +; X = Scan line +; +; Trashes A + +disableFillMode: + SAVE_AXY + BITS8 + lda $e19d00,x + and #%11011111 + sta $e19d00,x + BITS16 + RESTORE_AXY + rts + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; setPaletteColor ; Set a single color in a palette @@ -155,6 +191,22 @@ drawNumber: rts +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; BORDER_COLOR +; +; Trashes A +; +.macro BORDER_COLOR color + SAVE_AXY + BITS8 + lda BORDERCOLOR + and #$f0 + ora color + sta BORDERCOLOR + BITS16 + RESTORE_AXY +.endmacro + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Vertical blank checkers ; diff --git a/gscats.2mg b/gscats.2mg index 6c51db7..565db7d 100644 Binary files a/gscats.2mg and b/gscats.2mg differ diff --git a/gscats.s b/gscats.s index f133f25..b786fbe 100644 --- a/gscats.s +++ b/gscats.s @@ -8,12 +8,15 @@ .include "macros.s" .include "equates.s" +.import renderTerrainSpans +.import unrenderTerrainSpans +.import compileTerrainSpans + .org $0000 mainBank2: SYNCDBR - BITS8 lda #$f0 sta TEXTCOLOR @@ -34,10 +37,10 @@ quitGame: .include "graphics.s" .include "font.s" +.include "player.s" .include "terrain.s" .include "collision.s" .include "gameobject.s" -.include "player.s" .include "utility.s" .include "tables.s" .include "gamemanager.s" diff --git a/input.s b/input.s index 7ec13e4..ab71a24 100644 --- a/input.s +++ b/input.s @@ -48,7 +48,7 @@ kbdScanRightArrow: lda mapScrollPos cmp #VISIBLETERRAINWIDTH-VISIBLETERRAINWINDOW beq kbdScanDone - inc +; inc inc sta mapScrollRequested rts @@ -59,7 +59,7 @@ kbdScanLeftArrow: lda mapScrollPos beq kbdScanDone dec - dec +; dec sta mapScrollRequested rts diff --git a/linkerConfig b/linkerConfig new file mode 100644 index 0000000..4a1b127 --- /dev/null +++ b/linkerConfig @@ -0,0 +1,44 @@ +FEATURES { + STARTADDRESS: default = $0803; +} + +MEMORY { + ZP: start = $0080, size = $001A, define = yes; + HEADER: start = $0000, size = $0004, file = %O; + RAM: start = %S, size = $9600 - %S, file = %O, define = yes; + MOVE: start = $0000, size = $FFFF, file = %O, define = yes; + LC: start = $D400, size = $0C00, define = yes; +} + +SEGMENTS { + ZEROPAGE: load = ZP, type = zp; + EXEHDR: load = HEADER, type = ro; + STARTUP: load = RAM, type = ro; + LOWCODE: load = RAM, type = ro; + CODE: load = RAM, type = ro; + RODATA: load = RAM, type = ro; + DATA: load = RAM, type = rw; + ZPSAVE: load = RAM, type = bss, define = yes; + BSS: load = RAM, type = bss, define = yes; + INIT: load = MOVE, run = RAM, type = ro, define = yes; + HIGHCODE: load = MOVE, run = LC, type = ro, optional = yes; +} + +FEATURES { + CONDES: segment = INIT, + type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__; + CONDES: segment = RODATA, + type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__; + CONDES: type = interruptor, + segment = RODATA, + label = __INTERRUPTOR_TABLE__, + count = __INTERRUPTOR_COUNT__; +} +SYMBOLS { + __STACKSIZE__: value = $0800, weak = yes; +} + diff --git a/loader.s b/loader.s index 07c3a13..865cd83 100644 --- a/loader.s +++ b/loader.s @@ -40,10 +40,42 @@ main: ; Copy code into bank 2 ldx fileReadLen lda #2 + ldy #0 jsr copyBytes EMULATION +;;;;;;;;;;;;;;;;;;;;; + + ; Open the E1 code file + jsr PRODOS + .byte $c8 + .addr fileOpenCodeE1 + bne ioError + + ; Load the code into bank 0 + jsr PRODOS + .byte $ca + .addr fileRead + bne ioError + + ; Close the file + jsr PRODOS + .byte $cc + .addr fileClose + + NATIVE + + ; Copy code into bank E1 + ldx fileReadLen + lda #$E1 + ldy #$800 ; Must match terrain_e1 .org + jsr copyBytes + + EMULATION + +;;;;;;;;;;;;;;;;;;;;; + ; Open the sprite bank file jsr PRODOS .byte $c8 @@ -66,6 +98,7 @@ main: ; Copy sprites into bank 3 ldx fileReadLen lda #3 + ldy #0 jsr copyBytes ; Set up a long jump into bank 2, and @@ -73,6 +106,7 @@ main: ; properly to ProDOS 8 lda #returnToProDOS sta PRODOSRETURN + jml MAINENTRY returnToProDOS: @@ -90,9 +124,13 @@ ioError: ; bottom of any other bank. Must be in native mode. ; ; X = Length of data in bytes +; Y = Origin within bank ; A = Bank number of destination ; copyBytes: + sty copyBytesDest+1 + sty copyBytesDest2+1 + phx BITS8 sta copyBytesDest+3 @@ -135,6 +173,13 @@ fileOpenCode: .byte 0 ; Result (file handle) .byte 0 ; Padding +fileOpenCodeE1: + .byte 3 + .addr codePathE1 + .addr $9200 ; 1k below BASIC.SYSTEM + .byte 0 ; Result (file handle) + .byte 0 ; Padding + fileRead: .byte 4 .byte 1 ; File handle (we know it's gonna be 1) @@ -156,5 +201,7 @@ fileOpenSprites: codePath: pstring "/GSAPP/CODEBANK" +codePathE1: + pstring "/GSAPP/CODEBANKE1" spritePath: pstring "/GSAPP/SPRITEBANK00" diff --git a/macros.s b/macros.s index 98f413a..f085df1 100644 --- a/macros.s +++ b/macros.s @@ -30,7 +30,7 @@ .macro DBR bankNum BITS8 - lda bankNum + lda #bankNum pha plb BITS16 @@ -42,6 +42,15 @@ plb .endmacro +.macro SAVE_DBR + phb + phk + plb +.endmacro + +.macro RESTORE_DBR + plb +.endmacro .macro EMULATION sec ; Enable 8-bit mode @@ -100,7 +109,7 @@ .macro FASTGRAPHICS ;34 cycles, 12 bytes sei ;2 phd ;4 - sep #%00100000 ; 3 16-bit A only, to preserve X/Y + sep #%00100000 ; 3 8-bit A only, to preserve X/Y .a8 lda STACKCTL ;5 @@ -116,7 +125,7 @@ .macro SLOWGRAPHICS ;28 cycles, 12 bytes - sep #%00100000 ; 3 16-bit A only, to preserve X/Y + sep #%00100000 ; 3 8-bit A only, to preserve X/Y .a8 lda STACKREGISTER ;4 @@ -168,6 +177,25 @@ nobrk: pla .endmacro +.macro BREAK_NOSTACK + lda breakpoint + beq nobrk + lda #1 + sta $e1c029 + brk +nobrk: +.endmacro + + +.macro HARDBRK + pha + lda #1 + sta $e1c029 + pla + brk +.endmacro + + ;;;;;;;;;; ; Stack Macros diff --git a/terrain.s b/terrain.s index 1b1d9a3..58e3df4 100644 --- a/terrain.s +++ b/terrain.s @@ -5,29 +5,22 @@ ; -TERRAINWIDTH = 640 ; In pixels -MAXTERRAINHEIGHT = 100 ; In pixels -COMPILEDTERRAINROW = TERRAINWIDTH/4+3 ; In words, +2 to make room for clipping jump at end of row -VISIBLETERRAINWIDTH = TERRAINWIDTH/4 ; In words- width minus jump return padding -VISIBLETERRAINWINDOW = 80 ; In words - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; renderTerrain ; ; No stack operations permitted here! ; -; Current implementation: Unknown cycles per row ; Trashes all registers ; renderTerrain: FASTGRAPHICS - lda #MAXTERRAINHEIGHT + lda #0 sta SCRATCHL2 ; Row counter - lda #$9cff ; 4 Point stack to end of VRAM + lda #$5f1f ; 4 Point stack to end of affected VRAM tcs ; 2 sec - lda #compiledTerrainEnd-VISIBLETERRAINWINDOW-3 + lda #compiledTerrainEnd-VISIBLETERRAINWINDOW-4 sbc mapScrollPos sta PARAML0 @@ -39,13 +32,28 @@ renderTerrainLoop: jmp (PARAML0) renderRowComplete: + lda PARAML0 sec sbc #COMPILEDTERRAINROW sta PARAML0 - dec SCRATCHL2 - bne renderTerrainLoop + tsc + clc + adc #320 + tcs + + lda SCRATCHL2 + inc + cmp lastCompiledTerrainY + beq renderRowCont + bcs renderTerrainDone + +renderRowCont: + sta SCRATCHL2 + bra renderTerrainLoop + +renderTerrainDone: SLOWGRAPHICS rts @@ -120,47 +128,55 @@ craterTerrainDone: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 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-3 + lda #COMPILEDTERRAINROW*MAXTERRAINHEIGHT-4 sbc mapScrollPos tay - ldx #MAXTERRAINHEIGHT + + ldx #0 clipTerrainLoop: - clc ; Compute buffer to for saved data - txa - asl - asl - adc #clippedTerrainData-4 - sta PARAML0 - lda compiledTerrain,y - sta (PARAML0) ; Preserve data we're overwriting - inc PARAML0 - inc PARAML0 - - and #$ff00 - ora #$004c ; jmp in low byte + pha + lda #$4cea ; NOP followed by JMP sta compiledTerrain,y - iny + iny + iny lda compiledTerrain,y - sta (PARAML0) ; Preserve data we're overwriting + pha lda #renderRowComplete sta compiledTerrain,y tya sec - sbc #COMPILEDTERRAINROW+1 + sbc #COMPILEDTERRAINROW+2 tay - dex - bne clipTerrainLoop + inx + cpx lastCompiledTerrainY + bcc clipTerrainLoop + beq clipTerrainLoop + + ; Put stack back where it belongs + tsc + sta clippedTerrainStackPtr + lda STACKPTR + tcs RESTORE_AXY rts @@ -169,41 +185,53 @@ clipTerrainLoop: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 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 & $ff00) + pha + pld ; Point direct page at our clip data + sec - lda #COMPILEDTERRAINROW*MAXTERRAINHEIGHT-3 + lda #COMPILEDTERRAINROW*MAXTERRAINHEIGHT-4 sbc mapScrollPos tay - ldx #MAXTERRAINHEIGHT + + lda clippedTerrainStackPtr + and #$00ff + tax + inx unclipTerrainLoop: - clc ; Compute buffer that saved data is in - txa - asl - asl - adc #clippedTerrainData-4 - sta PARAML0 - - lda (PARAML0) + lda 2,x sta compiledTerrain,y - inc PARAML0 - inc PARAML0 + iny iny - lda (PARAML0) + lda 0,x sta compiledTerrain,y tya sec - sbc #COMPILEDTERRAINROW+1 + sbc #COMPILEDTERRAINROW+2 tay - dex + inx + inx + inx + inx + cpx #$100 ; When x hits the top of the stack, we're done bne unclipTerrainLoop + pld + RESTORE_AXY rts @@ -216,15 +244,16 @@ unclipTerrainLoop: compileTerrain: SAVE_AY - ldy #MAXTERRAINHEIGHT-1 + ldy #0 lda #compiledTerrain sta PARAML0 compileTerrainLoop: sty PARAML1 jsr compileTerrainRow - dey - bmi compileTerrainDone + iny + cpy #MAXTERRAINHEIGHT + beq compileTerrainDone clc lda #COMPILEDTERRAINROW @@ -234,6 +263,7 @@ compileTerrainLoop: bra compileTerrainLoop compileTerrainDone: + jsl compileTerrainSpans RESTORE_AY rts @@ -369,6 +399,48 @@ compileTerrainOpcode: +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 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 + + RESTORE_AXY + rts + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; generateTerrain ; @@ -382,7 +454,6 @@ generateTerrain: sta SCRATCHL generateTerrainLoop: - lda sineTable,x lsr @@ -414,23 +485,12 @@ generateTerrainLoop: rts - -; Terrain data, stored as height values 2 pixels wide (bytes) - -terrainData: - .repeat TERRAINWIDTH/2 - .word 0 - .endrepeat -terrainDataEnd: - compiledTerrain: .repeat COMPILEDTERRAINROW * MAXTERRAINHEIGHT - .byte 0 + .byte $00 .endrepeat compiledTerrainEnd: -clippedTerrainData: - .repeat MAXTERRAINHEIGHT - .byte 0,0,0,0 ; xx,jmp,addr - .endrepeat +clippedTerrainStackPtr: + .word 0 diff --git a/terrain_e1.s b/terrain_e1.s new file mode 100644 index 0000000..2ffd832 --- /dev/null +++ b/terrain_e1.s @@ -0,0 +1,306 @@ +; +; terrain (in E1 bank) +; +; Fill-mode terrain rendering code snippets +; that live in bank E1 for maximum speed. +; +; Created by Quinn Dunki on 7/29/17 +; + +.include "equates.s" +.include "macros.s" + +UNRENDERCACHESTACK = $7ff ; Stack downwards from bottom of VRAM. Mirrored from $01 into $E1! + +OP16 + +.org $800 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; renderTerrainSpans: +; +; +renderTerrainSpans: + SAVE_AXY + SAVE_DBR + FASTGRAPHICS + + lda #UNRENDERCACHESTACK ; Prepare unrender cache + tcs + + lda #MAXTERRAINHEIGHT-1 + sec + sbc lastCompiledTerrainY + +renderTerrainSpansLoop: + sta PARAML1 + + ; Find row data + lda PARAML1 + asl ; Shifts must match SPANROWBYTES + asl + asl + asl + asl + tay + lda terrainSpanData,y + sta SCRATCHL ; Track span color + + ; Find VRAM row + lda PARAML1 + asl + tax + lda vramRowInvertedSpanLookup,x + tax + + adc #160 + sta PARAML0 ; Watch for end of VRAM row + + ; Find span that intersects left logical edge + lda #0 + clc + +renderTerrainRowSpansFindLeftLoop: + adc terrainSpanData+2,y + cmp leftScreenEdge + bpl renderTerrainRowSpansFoundLeft + iny + iny + sta CACHEDATA + lda SCRATCHL + eor #%110000 ; Toggle span color cache + sta SCRATCHL + lda CACHEDATA + bra renderTerrainRowSpansFindLeftLoop + +renderTerrainRowSpansFoundLeft: + sec + sbc leftScreenEdge ; A now holds remainder of leftmost span + ; and Y points to leftmost span + +renderTerrainRowSpansLoop: + sta SCRATCHL2 + + ; Set fill mode byte for this span + lda SCRATCHL + sta VRAMBANK,x + + ; Cache the index we wrote to so we can erase later + phx + + ; Advance to end of span + clc + txa + adc SCRATCHL2 + cmp PARAML0 + bpl renderTerrainRowSpansDone + tax + + ; Prepare for next span + iny + iny + lda SCRATCHL + eor #%110000 ; Toggle span color cache + sta SCRATCHL + + lda terrainSpanData+2,y + bra renderTerrainRowSpansLoop + +renderTerrainRowSpansDone: + lda PARAML1 + dec + bne renderTerrainSpansLoop + + pea 0 ; Null-terminate the unrender cache + SLOWGRAPHICS + RESTORE_DBR + RESTORE_AXY + rtl + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; unrenderTerrainSpans: +; +; + +unrenderTerrainSpans: + SAVE_AXY + SAVE_DBR + phb + pea $e1e1 ; Work entirely in bank E1 for all local read/writes + plb + plb + + ldy #UNRENDERCACHESTACK-1 + +unrenderTerrainSpansLoop: + lda 0,y + beq unrenderTerrainSpansDone + tax + lda #0 + sta a:0,x + dey + dey + bra unrenderTerrainSpansLoop + +unrenderTerrainSpansDone: + plb + RESTORE_DBR + RESTORE_AXY + rtl + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; compileTerrainSpans: +; +; +compileTerrainSpans: + pha + SAVE_DBR + lda #0 + +compileTerrainSpansLoop: + sta PARAML1 + jsr compileTerrainSpansRow + lda PARAML1 + inc + cmp #MAXTERRAINHEIGHT + bne compileTerrainSpansLoop + + RESTORE_DBR + pla + rtl + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; compileTerrainSpansRow: +; +; PARAML1 = Row index (bottom relative) +; +; Trashes SCRATCHL +; +compileTerrainSpansRow: + SAVE_AXY + + lda PARAML1 + asl ; Shifts must match SPANROWBYTES + asl + asl + asl + asl + clc + adc #terrainSpanData + sta SCRATCHL + + ldy #0 + ldx #TERRAINWIDTH-2 + + lda terrainDataFar,x ; Figure out start value for row + cmp PARAML1 + bcs compileTerrainSpansRowInitGreen + +compileTerrainSpansRowInitBlack: + lda #$0010 ; First span is black + sta (SCRATCHL) ; Initialize the row + inc SCRATCHL + inc SCRATCHL + ldy #-1 + +compileTerrainSpansRowBlackStart: + iny + +compileTerrainSpansRowBlackLoop: + lda terrainDataFar,x + cmp PARAML1 + bcs compileTerrainSpansBlackEnd + dex + dex + iny + cpx #-2 + bne compileTerrainSpansRowBlackLoop + +compileTerrainSpansBlackEnd: + tya ; Store this span's length + sta (SCRATCHL) + inc SCRATCHL + inc SCRATCHL + + dex ; Begin searching for next span + dex + cpx #0 + bmi compileTerrainSpansRowDone + ldy #0 + bra compileTerrainSpansRowGreenStart + +compileTerrainSpansRowDone: + RESTORE_AXY + rts + +compileTerrainSpansRowInitGreen: + lda #$0020 ; First span is green + sta (SCRATCHL) ; Initialize the row + inc SCRATCHL + inc SCRATCHL + ldy #-1 + +compileTerrainSpansRowGreenStart: + iny + +compileTerrainSpansRowGreenLoop: + lda terrainDataFar,x + cmp PARAML1 + bcc compileTerrainSpansGreenEnd + dex + dex + iny + cpx #-2 + bne compileTerrainSpansRowGreenLoop + +compileTerrainSpansGreenEnd: + tya ; Store this span's length + sta (SCRATCHL) + inc SCRATCHL + inc SCRATCHL + + dex ; Begin searching for next span + dex + cpx #0 + bmi compileTerrainSpansRowDone + ldy #0 + bra compileTerrainSpansRowBlackStart + +terrainSpanData: + .repeat MAXTERRAINHEIGHT + .word 0 ; Start value (0=BG) + .repeat MAXSPANSPERROW + .word 0 ; Length (in bytes) + .endrepeat + .endrepeat + + +vramRowInvertedSpanLookup: + .word $9d00,$9c60,$9bc0,$9b20,$9a80,$99e0,$9940,$98a0,$9800,$9760,$96c0,$9620,$9580,$94e0,$9440,$93a0,$9300,$9260,$91c0,$9120 + .word $9080,$8fe0,$8f40,$8ea0,$8e00,$8d60,$8cc0,$8c20,$8b80,$8ae0,$8a40,$89a0,$8900,$8860,$87c0,$8720,$8680,$85e0,$8540,$84a0 + .word $8400,$8360,$82c0,$8220,$8180,$80e0,$8040,$7fa0,$7f00,$7e60,$7dc0,$7d20,$7c80,$7be0,$7b40,$7aa0,$7a00,$7960,$78c0,$7820 + .word $7780,$76e0,$7640,$75a0,$7500,$7460,$73c0,$7320,$7280,$71e0,$7140,$70a0,$7000,$6f60,$6ec0,$6e20,$6d80,$6ce0,$6c40,$6ba0 + .word $6b00,$6a60,$69c0,$6920,$6880,$67e0,$6740,$66a0,$6600,$6560,$64c0,$6420,$6380,$62e0,$6240,$61a0,$6100,$6060,$5fc0,$5f20 + .word $5e80,$5de0,$5d40,$5ca0,$5c00,$5b60,$5ac0,$5a20,$5980,$58e0,$5840,$57a0,$5700,$5660,$55c0,$5520,$5480,$53e0,$5340,$52a0 + .word $5200,$5160,$50c0,$5020,$4f80,$4ee0,$4e40,$4da0,$4d00,$4c60,$4bc0,$4b20,$4a80,$49e0,$4940,$48a0,$4800,$4760,$46c0,$4620 + .word $4580,$44e0,$4440,$43a0,$4300,$4260,$41c0,$4120,$4080,$3fe0,$3f40,$3ea0,$3e00,$3d60,$3cc0,$3c20,$3b80,$3ae0,$3a40,$39a0 + .word $3900,$3860,$37c0,$3720,$3680,$35e0,$3540,$34a0,$3400,$3360,$32c0,$3220,$3180,$30e0,$3040,$2fa0,$2f00,$2e60,$2dc0,$2d20 + .word $2c80,$2be0,$2b40,$2aa0,$2a00,$2960,$28c0,$2820,$2780,$26e0,$2640,$25a0,$2500,$2460,$23c0,$2320,$2280,$21e0,$2140,$20a0 + + + + +.export renderTerrainSpans +.export unrenderTerrainSpans +.export compileTerrainSpans + + +; Suppress some linker warnings - Must be the last thing in the file +.SEGMENT "ZPSAVE" +.SEGMENT "EXEHDR" +.SEGMENT "STARTUP" +.SEGMENT "INIT" +.SEGMENT "LOWCODE"