DEBUG = 1
.include "..\header\nes_constants.h"
.include "..\header\CA65HL.h"
.include "..\header\memory.h"
.include "..\header\ppu.h"
.include "..\header\function_macros.h"
.include "..\header\ndxdebug.h"
.include "..\header\graphics.h"
OAM_ROTATION_START = 4 * OAM_ENTRY
OAM_ROTATION_AMOUNT = 160
METASPRITE_COUNT = 5 * 4 ; 4 bytes per
; --------------------------------------------------------------------------------------------
; --------------------------------------------------------------------------------------------
.scope scope_spriteTestSTATE
timerTitle 1, "metaspriteeval"
; setup dynamic memory:
.include "..\MEMCONFIG\MEMORY_CONFIG_spriteTestSTATE.inc"
IdentifyState "spriteTestSTATE"
; end memory
declareBSS
localJumpVector .addr
endBSS
declareBSS mSpriteMemory
yPos .byte ::METASPRITE_COUNT
att .byte ::METASPRITE_COUNT
mTile .byte ::METASPRITE_COUNT
xPos .byte ::METASPRITE_COUNT
endBSS
; --------------------------------------------------------------------------------------------
; EXPORTS:
.export spriteTestSTATE
; --------------------------------------------------------------------------------------------
; IMPORTS:
.import waitNMI
.import writeToVRAMbuffer
; .import split, yield, killThread
.importzp NMIdoVRAMupdate
.importzp NMIdoOAM
.importzp FrameCounter
.importZP PadPressed
.importzp PadNewPressed
.import VramBuffer1Offset
.import VramBuffer1
; --------------------------------------------------------------------------------------------
.pushseg
.segment "RODATA"
Metatiles:
; hepler constants:
X_CR = %00001000
X_DEFINE_PLUS8 = %00000100
X_DEFINE_NEXT = %00000000
X_LASTSPR = %00001100
Y_DEFINE_SAME = %00100000
Y_DEFINE_NEXT = %00000000
TILE_PLUS1 = %00010000
TILE_DEFINE_NEXT = %00000000
V_FLIP = %10000000
H_FLIP = %01000000
metaspriteNumbersLo: .byte <metasprite0, <metasprite1
metaspriteNumbersHi: .byte >metasprite0, >metasprite1
; first byte only has attribute, and tile byte
metasprite0:
; .byte X, Y, TILE, ATTRIB
; boo: 9 bytes
.byte $80, X_DEFINE_PLUS8 + Y_DEFINE_SAME + TILE_PLUS1 + 0
.byte X_CR + Y_DEFINE_SAME + TILE_PLUS1 + 0
.byte X_DEFINE_PLUS8 + Y_DEFINE_SAME + TILE_PLUS1 + 0
.byte X_CR + Y_DEFINE_SAME + TILE_PLUS1 + 0
.byte X_DEFINE_PLUS8 + Y_DEFINE_SAME + TILE_PLUS1 + 0
.byte X_CR + Y_DEFINE_SAME + TILE_PLUS1 + 0
.byte X_DEFINE_PLUS8 + Y_DEFINE_SAME + TILE_PLUS1 + 0
.byte X_LASTSPR
metasprite1:
; megaman: 13 bytes
.byte $90, X_DEFINE_PLUS8 + Y_DEFINE_SAME + TILE_PLUS1 + 1
.byte X_DEFINE_PLUS8 + Y_DEFINE_SAME + TILE_PLUS1 + 1
.byte X_CR + Y_DEFINE_SAME + TILE_PLUS1 + 1
.byte X_DEFINE_PLUS8 + Y_DEFINE_SAME + TILE_PLUS1 + 1
.byte X_DEFINE_PLUS8 + Y_DEFINE_SAME + TILE_PLUS1 + 1
.byte X_CR + Y_DEFINE_SAME + TILE_PLUS1 + 1
.byte X_DEFINE_PLUS8 + Y_DEFINE_SAME + TILE_PLUS1 + 1
.byte X_DEFINE_PLUS8 + Y_DEFINE_SAME + TILE_PLUS1 + 1
.byte X_DEFINE_NEXT + Y_DEFINE_NEXT + TILE_PLUS1 + 1
.byte <-6, <-10, X_LASTSPR + 2
.popseg
; --------------------------------------------------------------------------------------------
.proc spriteTestSTATE
; init state:
setSubState M::localJumpVector := #welcomeSTATE
nameTableInit 0
.pushseg
.SEGMENT "RODATA"
palette0:
.byte $3f,$00, 32
.byte $16,$0F,$05,$16,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
.byte $16,$0F,$20,$16,$16,$0F,$2C,$11,$0F,$0F,$30,$38,$0F,$0F,$0F,$0F
.popseg
call writeToVRAMbuffer, _a(palette0), #(32 + 3)
setFlag NMIdoVRAMupdate
; init sprite RAM
ldx #METASPRITE_COUNT
lda #$FF
repeat
sta mSpriteMemory::att, x
until dex == zero
;
; some test data:
mb mSpriteMemory[ 0 ]::yPos := #128
mb mSpriteMemory[ 0 ]::xPos := #128
mb mSpriteMemory[ 0 ]::att := #%00000000
mb mSpriteMemory[ 0 ]::mTile := #0
mb mSpriteMemory[ 1 ]::yPos := #(128+50)
mb mSpriteMemory[ 1 ]::xPos := #(15)
mb mSpriteMemory[ 1 ]::att := #%00000000
mb mSpriteMemory[ 1 ]::mTile := #1
mb mSpriteMemory[ 2 ]::yPos := #(128+50)
mb mSpriteMemory[ 2 ]::xPos := #(100)
mb mSpriteMemory[ 2 ]::att := #%00000000
mb mSpriteMemory[ 2 ]::mTile := #1
mb mSpriteMemory[ 3 ]::yPos := #(128+30)
mb mSpriteMemory[ 3 ]::xPos := #(50)
mb mSpriteMemory[ 3 ]::att := #%00000000
mb mSpriteMemory[ 3 ]::mTile := #1
PPU_ON
; end init
; --------------------------------------------------------------------------------------------
LOOP:
jsr waitNMI
jsri M::localJumpVector
jmp LOOP
.endproc
; ----------------------------------------------------------------------------------------------
.proc welcomeSTATE
setFlag NMIdoVRAMupdate
puts 8,12, "WELCOME!"
puts 2,13, "PRESS A FOR PRIORITY"
puts 2,14, "PRESS B AND SEL FOR FLIPPING"
puts 2,15, "PRESS START TO CHANGE SPRITE"
puts 2,16, "PRESS START TO CONTINUE"
if PadNewPressed & #BUTTON_START
setSubState M::localJumpVector := #subfunctionSTATE
endif
rts
.endproc
; --------------------------------------------------------------------------------------------
.macro addMspriteCoord coord, adder, attbyte, attbit
mb coord := coord + adder
if carry set
mb attbyte := attbyte ^ #(1 << attbit)
endif
.endmacro
; --------------------------------------------------------------------------------------------
.macro subMspriteCoord coord, adder, attbyte, attbit
mb coord := coord - adder
if carry clear
mb attbyte := attbyte ^ #(1 << attbit)
endif
.endmacro
; --------------------------------------------------------------------------------------------
.proc subfunctionSTATE
declareBSS
toggleX .byte
toggleY .byte
width .byte
height .byte
endBSS
if PadPressed & #BUTTON_UP
dec mSpriteMemory::yPos
if ldx mSpriteMemory::yPos : inx == zero ; if = #$FF
mb mSpriteMemory::att := mSpriteMemory::att ^ #%00000100
endif
endif
if PadPressed & #BUTTON_DOWN
inc mSpriteMemory::yPos
if zero
mb mSpriteMemory::att := mSpriteMemory::att ^ #%00000100
endif
endif
if PadPressed & #BUTTON_LEFT
dec mSpriteMemory::xPos
if ldx mSpriteMemory::xPos : inx == zero ; if = #$FF
mb mSpriteMemory::att := mSpriteMemory::att ^ #%00001000
endif
endif
if PadPressed & #BUTTON_RIGHT
inc mSpriteMemory::xPos
if zero
mb mSpriteMemory::att := mSpriteMemory::att ^ #%00001000
endif
endif
if PadNewPressed & #BUTTON_A
mb mSpriteMemory::att := mSpriteMemory::att ^ #%00100000
endif
if PadNewPressed & #BUTTON_B
mb mSpriteMemory::att := mSpriteMemory::att ^ #%10000000
if M::toggleY
dec M::toggleY
subMspriteCoord mSpriteMemory::yPos, M::height, mSpriteMemory::att, 2
else
inc M::toggleY
addMspriteCoord mSpriteMemory::yPos, M::height, mSpriteMemory::att, 2
endif
endif
if PadNewPressed & #BUTTON_SELECT
mb mSpriteMemory::att := mSpriteMemory::att ^ #%01000000
if M::toggleX
dec M::toggleX
subMspriteCoord mSpriteMemory::xPos, M::width, mSpriteMemory::att, 3
else
inc M::toggleX
addMspriteCoord mSpriteMemory::xPos, M::width, mSpriteMemory::att, 3
endif
endif
if PadNewPressed & #BUTTON_START
if mSpriteMemory::mTile = #1
dec mSpriteMemory::mTile
mb M::width := #( 16 - 8 )
mb M::height := #( 32 - 8 )
else
inc mSpriteMemory::mTile
mb M::width := #(24 - 8 )
mb M::height := #(24 - 8 )
endif
endif
ldaPPUmaskBits
staPPUmaskBits #MA_MONOCHROME, now
startTimer 1
jsr evalMetaSprites
stopTimer 1
ldaPPUmaskBits #<~MA_MONOCHROME
staPPUmaskBits , now
setFlag NMIdoOAM
rts
.endproc
; ----------------------------------------------------------------------------
.proc evalMetaSprites
; metasprite Object in RAM format, one record:
; should be organized as one array of each element
; (example, if there are $10 records, the first $10 bytes are all Ypos
; Ypos : byte
; Attribute modifier : byte
; vhbcxypp --- Attribute modifier byte (always)
; ||||||||
; ||||||++---- Sprite Colour toggle: EOR with metatile attribs
; |||||+------ Y clipping bit
; ||||+------- X clipping bit
; |||+-------- Clipping or wrap : 0: clip sprites at screen edge, 1: allow wrap
; ||+--------- Background priority bit
; |+---------- Horizontal flipping toggle: EOR metatile attribs
; +----------- Vertical flipping toggle: EOR metatile attribs
; Metatile number : byte
; Xpos : byte
; --------------------------------------------------------------------------------------------
; metasprite format to define metasprite in ROM data:
; xxxxxxxx --- Sprite X coordinate (if required)
; yyyyyyyy --- Sprite Y coordinate (if required)
; tttttttt --- Tile number (if required)
; vhytxxpp --- Attribute byte (always)
; ||||||||
; ||||||++---- Sprite Colour
; ||||++------ X position of next sprite (00 : required, 01 : X pos of this sprite + 8,
; |||| 10 : X pos of the first sprite, Y + 8 (CR/LF), 11 : Escape code for last sprite)
; |||+-------- Tile number of next sprite (0: required, 1 : Tile number of this sprite + 1
; ||+--------- Y position of next sprite (0 : required, 0 : unchanged, , skip if X control was LF)
; |+---------- Horizontal flipping
; +----------- Vertical flipping
; static
declareBSS
OAMoffset .byte
OAMusedLast .byte
endBSS
; temp use zeropage:
locals
mSprModifier .byte
sprAttrib .byte
sprTile .byte
sprXpos .byte
sprYpos .byte
sprXbit8 .byte
sprYbit8 .byte
metaspritePointerLo .byte
metaspritePointerHi .byte
sprAdder .byte
sprXposStart .byte
mSpriteOffset .byte
endlocals
metaspritePointer = local::metaspritePointerLo
; init:
ldy #$FF ; - 1
sty local::mSpriteOffset
; skip reserved sprites
if lda M::OAMoffset < #::OAM_ROTATION_START
lda #::OAM_ROTATION_START
endif
sta M::OAMoffset
sta local::sprAdder ; use as temp: save OAMoffset
DoNextmetasprite:
inc local::mSpriteOffset
ldx local::mSpriteOffset
mb a, local::mSprModifier := mSpriteMemory[ x ]::att ; load Attrib/meta data which is modifier for all
; if att is $FF then last entry, rotate and clear remaining shadow OAM
if tay : iny == zero
jmp clearRemainingOAM
endif
mb local::sprYbit8 := a >> 2 ; put Y 'bit8' into bit 0 local var (only bit0 matters)
mb local::sprXbit8 := a >> 1 ; put X 'bit8' into bit 0 local var (only bit0 matters)
mb local::sprYpos := mSpriteMemory[ x ]::yPos
mb local::sprXpos := mSpriteMemory[ x ]::xPos
sta local::sprXposStart
mb a, x := mSpriteMemory[ x ]::mTile ; load metatile number
; get metatile pointer
mb local::metaspritePointerLo := metaspriteNumbersLo[ x ]
mb local::metaspritePointerHi := metaspriteNumbersHi[ x ]
; very first tile we read attrib and tile only from metatile ROM data
; Y counts index into ROM data
ldy #0
mb local::sprTile := (metaspritePointer)[ y ]
iny
ldx local::sprAdder ; X now has OAMoffset
DoNextTile:
mb local::sprAttrib := (metaspritePointer)[ y ] ; read attrib and flags
iny
; if both offscreen bits are clear OR No clipping mode is on
if local::sprXbit8 | local::sprYbit8 >> 1 == carry clear || local::mSprModifier & #%00010000
mb OAM_SHADOW[ x + 2] := local::sprAttrib & #%11000011 ^ local::mSprModifier
mb OAM_SHADOW[ x + 3] := local::sprXpos
mb OAM_SHADOW[ x + 0] := local::sprYpos
mb OAM_SHADOW[ x + 1] := local::sprTile
if x := x + 4 == zero
ldx #::OAM_ROTATION_START
endif
endif
; -------------- X pos
mb local::sprAdder := #8 ; add 8 by default
mb a := local::sprAttrib & #%00001100
; process bit 3,2 as 0 to 3 value:
if a < #(2 << 2) ; 0 or 1:
; if offset needed:
if a = #0
mb local::sprAdder := (metaspritePointer)[ y ]
iny
endif
mb a := local::sprAdder
if { flagSet local::mSprModifier, 6 } ; if bit6 (h mirror)
; negate adder
mb a := a ^ #$FF + #1
endif
sta local::sprAdder
mb local::sprXpos := a + local::sprXpos
ror
if a ^ local::sprAdder == N set ; if carry and adder sign are different:
inc local::sprXbit8 ; toggle offscreen bit
endif
else
if a = #( 2 << 2 ) ; Reload metasprite X and Y position:
mb local::sprXpos := local::sprXposStart
mb local::sprXbit8 := local::mSprModifier >> 3
lda #8
jmp Add8toYPos
endif
; else:
stx local::sprAdder ; save OAM offset here
;---------------
jmp DoNextmetasprite ; if = 0 then bail and do next RAm based metasprite
endif
; -----------END X pos
; ----------- Y pos
; load Ypos? Skip all this if not 0
if ! local::sprAttrib & #%00100000
mb a := (metaspritePointer)[ y ]
iny
Add8toYPos: ; jump here if CR/LF code
if flagSet local::mSprModifier ; if bit7 (v mirror)
mb a := a ^ #$FF + #1
endif
sta local::sprAdder
clc
adc local::sprYpos
sta local::sprYpos
ror
if a ^ local::sprAdder == N set
inc local::sprYbit8
endif
endif
; -----------END Y pos
; -----------check tile:
; BIT4: check if 0: load tile, else use next tile
inc local::sprTile ; inc by default
if ! local::sprAttrib & #%00010000
mb local::sprTile := (metaspritePointer)[ y ]
iny
endif
; -------end tile processing
jmp DoNextTile
; --------------------------------------------------------------------------------------------
clearRemainingOAM:
OAMnext = local::sprAdder ; alias
ldx OAMnext
txa
mb a := a - M::OAMoffset ; bytes used
mb a := a >> 2 ; slots used
sta OAMnext ; use as temp, since this value is in reg X
mb y := #(64 - ( ::OAM_ROTATION_START / 4 ) ) - OAMnext ; total avaliable slots MINUS slots used
lda #$FF
repeat
sta OAM_SHADOW, x
mb x := x + 4
if x < #::OAM_ROTATION_START
ldx #::OAM_ROTATION_START
endif
until dey == zero
mb M::OAMoffset := M::OAMoffset + #::OAM_ROTATION_AMOUNT
rts
.endproc
; --------------------------------------------------------------------------------------------
.endscope
No comments:
Post a Comment