iigs-game-engine/demos/sprites/App.Main.s

655 lines
19 KiB
ArmAsm

; Test driver to exercise graphics routines.
REL
DSK MAINSEG
use Locator.Macs.s
use Misc.Macs.s
use EDS.GSOS.MACS.s
use Tool222.Macs.s
use Util.Macs.s
use CORE.MACS.s
use ../../src/GTE.s
use ../../src/Defs.s
mx %00
; Feature flags
NO_INTERRUPTS equ 0 ; turn off for crossrunner debugging
NO_MUSIC equ 1 ; turn music + tool loading off
; Keycodes
LEFT_ARROW equ $08
RIGHT_ARROW equ $15
UP_ARROW equ $0B
DOWN_ARROW equ $0A
; Typical init
phk
plb
jsl EngineStartUp
lda #^MyPalette ; Fill Palette #0 with our colors
ldx #MyPalette
ldy #0
jsl SetPalette
ldx #0 ; Mode 0 is full-screen
jsl SetScreenMode
; Set up our level data
jsr BG0SetUp
; jsr TileAnimInit
jsr SetLimits
; Allocate room to load data
; jsr MovePlayerToOrigin ; Put the player at the beginning of the map
jsr InitOverlay ; Initialize the status bar
stz frameCount
ldal OneSecondCounter
sta oldOneSecondCounter
; Initialize the sprite's global position (this is tracked outside of the tile engine)
lda #16
sta PlayerGlobalX
lda MaxGlobalY
sec
lda #40 ; 32 for tiles, 8 for sprite
sta PlayerGlobalY
stz PlayerXVel
stz PlayerYVel
; Add a sprite to the engine and save it's sprite ID
jsr UpdatePlayerLocal
lda #3 ; 8x8 sprite, tile ID = 3
ldx PlayerX
ldy PlayerY
jsl AddSprite
bcc :sprite_ok
brl Exit ; If we could not allocate a sprite, exit
:sprite_ok
sta PlayerID
; Draw the initial screen
lda #DIRTY_BIT_BG0_REFRESH ; Redraw all of the tiles on the next Render
tsb DirtyBits
jsl Render
; Set up a very specific test. First, we draw a sprite into the sprite plane, and then
; leave it alone. We are just testing the ability to merge sprite plane data into
; the play field tiles.
EvtLoop
jsl ReadControl
; Check the buttons first
pha
bit #$0100
beq :no_jump
lda PlayerStanding
beq :no_jump
lda #$FFF8
sta PlayerYVel
:no_jump
pla
and #$007F ; Ignore the buttons for now
cmp #'q'
bne :not_q
brl Exit
:not_q
cmp #'d'
bne :not_d
lda StartX
cmp MaxBG0X
bcs :do_render
inc
jsl SetBG0XPos
bra :do_render
:not_d
cmp #'a'
bne :not_a
lda StartX
beq :do_render
dec
jsl SetBG0XPos
bra :do_render
:not_a
cmp #'s'
bne :not_s
lda StartY
cmp MaxBG0Y
bcs :do_render
inc
jsl SetBG0YPos
bra :do_render
:not_s
cmp #'w'
bne :not_w
lda StartY
beq :do_render
dec
jsl SetBG0YPos
bra :do_render
:not_w
; Do j,l to move the character left/right
cmp #'j'
bne :not_j
lda PlayerXVel
bpl :pos_xvel
cmp #$FFFA
bcc :not_j
:pos_xvel dec
sta PlayerXVel
bra :do_render
:not_j
cmp #'l'
bne :not_l
lda PlayerXVel
bmi :neg_xvel
cmp #6
bcs :not_l
:neg_xvel inc
sta PlayerXVel
bra :do_render
:not_l
; Update the camera position
:do_render
; jsr UpdatePlayerPos ; Moves in global cordinates
; jsr UpdateCameraPos ; Moves the screen
; jsr UpdatePlayerLocal ; Gets local sprite coordinates
; lda PlayerID
; ldx PlayerX
; ldy PlayerY
; jsl UpdateSprite ; Move the sprite to this local position
; Draw the sprite in the sprite plane
; ldx PlayerX
; ldy PlayerY
; jsl GetSpriteVBuffAddr
; tax ; put in X
; ldy #3*128 ; draw the 3rd tile as a sprite
; stx PlayerLastPos ; save for erasure
; jsl DrawTileSprite
; Now the sprite has been drawn. Enqueue the dirty tiles. We blindly add the potential
; dirty tiles and rely on PushDirtyTile to elimate duplicates quickly
; ldx PlayerX
; ldy PlayerY
; jsr MakeDirtySprite8x8
; The dirty tile queue has been written to; apply it to the code field
; jsl ApplyTiles
; Let's see what it looks like!
jsl Render
; Update the performance counters
inc frameCount
ldal OneSecondCounter
cmp oldOneSecondCounter
beq :noudt
sta oldOneSecondCounter
jsr UdtOverlay
stz frameCount
:noudt
; Erase the sprites that moved
; ldx PlayerLastPos ; Delete the sprite because it moved
; jsl EraseTileSprite
; Add the tiles that the sprite was previously at as well.
; ldx PlayerXOld
; ldy PlayerYOld
; jsr MakeDirtyTile8x8
; tax
; ldy PlayerY
; lda PlayerID
; jsl UpdateSprite
; jsl DoTimers
; jsl Render
brl EvtLoop
; Exit code
Exit
jsl EngineShutDown
_QuitGS qtRec
bcs Fatal
Fatal brk $00
MyPalette dw $068F,$0EDA,$0000,$068F,$0BF1,$00A0,$0EEE,$0777,$01CE,$0FA4,$0F59,$0D40,$02E3,$09B9,$0F93,$0FD7
PlayerGlobalX ds 2
PlayerGlobalY ds 2
PlayerID ds 2
PlayerX ds 2
PlayerXOld ds 2
PlayerY ds 2
PlayerYOld ds 2
PlayerLastPos ds 2
PlayerXVel ds 2
PlayerYVel ds 2
KeyState ds 2
PlayerStanding ds 2
MaxGlobalX ds 2
MaxGlobalY ds 2
MaxBG0X ds 2
MaxBG0Y ds 2
oldOneSecondCounter ds 2
frameCount ds 2
PLAYER_X_MIN equ 0
PLAYER_X_MAX equ 160-4
PLAYER_Y_MIN equ 0
PLAYER_Y_MAX equ 200-8
EMPTY_TILE equ $0029 ; the tile that makes up the background
AdjustLocalX
clc
adc StartXMod164
cmp #164
bcc *+5
sbc #164
rts
AdjustLocalY
clc
adc StartYMod208
cmp #208
bcc *+5
sbc #208
rts
SetLimits
lda TileMapWidth
asl
asl
sta MaxGlobalX
sec
sbc ScreenWidth
sta MaxBG0X
lda TileMapHeight
asl
asl
asl
sta MaxGlobalY
sec
sbc ScreenHeight
sta MaxBG0Y
rts
; Set the scroll position based on the global cooridinate of the player
; Try to center the player on the screen
UpdateCameraPos
lda ScreenWidth
lsr
sta tmp0
lda PlayerGlobalX
sec
sbc tmp0
bpl :x_pos
lda #0
:x_pos cmp MaxBG0X
bcc :x_ok
lda MaxBG0X
:x_ok jsl SetBG0XPos
lda ScreenHeight
lsr
sta tmp0
lda PlayerGlobalY
sec
sbc tmp0
bpl :y_pos
lda #0
:y_pos cmp MaxBG0Y
bcc :y_ok
lda MaxBG0Y
:y_ok jsl SetBG0YPos
rts
; Convert the global coordinates to adjusted local coordinated (compensating for wrap-around)
UpdatePlayerLocal
lda PlayerGlobalX
sec
sbc StartX
jsr AdjustLocalX
sta PlayerX
lda PlayerGlobalY
sec
sbc StartY
jsr AdjustLocalY
sta PlayerY
rts
; Simple updates with gravity and collisions. It's important that eveything in this
; subroutine be done against
UpdatePlayerPos
stz PlayerStanding
lda PlayerYVel
bmi :no_ground_check
; Check if the player is standing on the ground at their current local position
ldx PlayerX
lda PlayerY
clc
adc #8
tay
jsr GetTileAt
cmp #EMPTY_TILE
beq :no_ground_check
lda PlayerGlobalY
and #$fff8
sta PlayerGlobalY
stz PlayerYVel
lda #1
sta PlayerStanding
:no_ground_check
lda PlayerGlobalY
clc
adc PlayerYVel
bpl *+5
lda #0
cmp MaxGlobalY
bcc *+5
lda MaxGlobalY
sta PlayerGlobalY
lda PlayerGlobalX
clc
adc PlayerXVel
bpl *+5
lda #0
cmp MaxGlobalX
bcc *+5
lda MaxGlobalX
sta PlayerGlobalX
lda PlayerXVel
beq :no_dxv
bpl :pos_dxv
inc
bra :no_dxv
:pos_dxv
dec
:no_dxv
sta PlayerXVel
lda PlayerYVel
inc
bmi :is_neg
cmp #4
bcs :too_fast
:is_neg
sta PlayerYVel
:too_fast
rts
; Takes a signed playfield position (including off-screen coordinates) and a size and marks
; the tiles that are impacted by this shape. The main job of this subroutine is to ensure
; that all of the tile coordinate s are within the valid bounds [0 - 40], [0 - 25].
;
; X = signed integer
; Y = signed integer
; A = sprite size (0 - 7)
SpriteWidths dw 4,4,8,8,12,8,12,16
SpriteHeights dw 8,16,8,16,16,24,24,24
; 000 - 8x8 (1x1 tile)
; 001 - 8x16 (1x2 tiles)
; 010 - 16x8 (2x1 tiles)
; 011 - 16x16 (2x2 tiles)
; 100 - 24x16 (3x2 tiles)
; 101 - 16x24 (2x3 tiles)
; 110 - 24x24 (3x3 tiles)
; 111 - 32x24 (4x3 tiles)
MarkTilesOut
ply
plx
sec
rts
MarkTiles
phx
phy
and #$0007
asl
tax
; First, do a bound check against the whole sprite. It it's totally off-screen, do nothing because
; there are no physical tiles to mark.
lda 1,s ; load the Y coordinate
bpl :y_pos
eor #$FFFF ; for a negative coordinate, see if it's equal to or larger than the sprite height
inc
cmp SpriteHeights,x
bcs MarkTilesOut
bra :y_ok
:y_pos cmp ScreenHeight
bcc :y_ok
bra MarkTilesOut
:y_ok
rts
; X = coordinate
; Y = coordinate
GetTileAt
txa
bmi :out
lsr
lsr
tax
tya
bmi :out
lsr
lsr
lsr
tay
jsl GetTileStoreOffset
tax
ldal TileStore+TS_TILE_ID,x
rts
:out
lda #EMPTY_TILE
rts
; X = coordinate
; Y = coordinate
MakeDirtySprite8x8
phx
phy
txa ; need to do a signed shift...
lsr
lsr
tax
tya
lsr
lsr
lsr
tay
jsr MakeDirtySpriteTile ; top-left
lda 3,s
clc
adc #3
lsr
lsr
tax
jsr MakeDirtySpriteTile ; top-right
lda 1,s
clc
adc #7
lsr
lsr
lsr
tay
jsr MakeDirtySpriteTile ; bottom-right
lda 3,s
lsr
lsr
tax
jsr MakeDirtySpriteTile ; bottom-left
ply
plx
rts
; X = coordinate
; Y = coordinate
MakeDirtyTile8x8
phx
phy
txa
lsr
lsr
tax
tya
lsr
lsr
lsr
tay
jsr MakeDirtyTile ; top-left
lda 3,s
clc
adc #3
lsr
lsr
tax
jsr MakeDirtyTile ; top-right
lda 1,s
clc
adc #7
lsr
lsr
lsr
tay
jsr MakeDirtyTile ; bottom-right
lda 3,s
lsr
lsr
tax
jsr MakeDirtyTile ; bottom-left
ply
plx
rts
MakeDirtyTile
phx
phy
jsl GetTileStoreOffset
jsl PushDirtyTile
ply
plx
rts
MakeDirtySpriteTile
phx
phy
txa
asl
asl
tax
tya
asl
asl
asl
tay
jsl GetSpriteVBuffAddr
pha
lda 3,s
tay
lda 5,s
tax
jsl GetTileStoreOffset
tax
lda #TILE_SPRITE_BIT
stal TileStore+TS_SPRITE_FLAG,x
pla
stal TileStore+TS_SPRITE_ADDR,x
txa
jsl PushDirtyTile
ply
plx
rts
; Position the screen with the botom-left corner of the tilemap visible
MovePlayerToOrigin
lda #0 ; Set the player's position
jsl SetBG0XPos
lda TileMapHeight
asl
asl
asl
sec
sbc ScreenHeight
jsl SetBG0YPos
rts
qtRec adrl $0000
da $00
PUT ../shell/Overlay.s
PUT gen/App.TileMapBG0.s
PUT gen/App.TileSetAnim.s
ANGLEBNK ENT