It is currently Tue Feb 19, 2019 3:37 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 5 posts ] 
Author Message
PostPosted: Sun Feb 10, 2019 7:11 pm 
Offline

Joined: Sun Jan 02, 2011 9:58 pm
Posts: 9
I'm trying to learn how to make entity lists and for now I'm starting with a projectile list for Player 1. My projectiles worked fine when everything was just hardcoded and static but that's a bad way of actually making something functional so I wanted to learn how to do it 'properly'. The problem I'm having is that my projectiles aren't showing up and I can't figure out what's going on, any help would be greatly appreciated. FWIW I'm using ASM6 compiler.

Entity List Variables:
Code:
.enum $0400               ;start lists in RAM ($0400-$07FF) - 1024 bytes

;Player 1's projectile variables (8x5 = 40 bytes)
;Bytes = Is Active, Type, Level, Xpos, Ypos
p1_projectile1 .dsb 5      ;Player 1's projectiles
p1_projectile2 .dsb 5
p1_projectile3 .dsb 5
p1_projectile4 .dsb 5
p1_projectile5 .dsb 5
p1_projectile6 .dsb 5
p1_projectile7 .dsb 5
p1_projectile8 .dsb 5

.ende      ;end Entity lists


Player 1 - Fire Vulcan Subroutine
Gets called when the player presses the A button
Note: prj_vulcan_lvl0_sprite is the metasprite data
Code:
p1_fireVulcan:
   
   LDX #$00
   LDY #$00
p1_fireVulcanLoop:
   LDA p1_projectile1, x
   CMP #PRJ_INACTIVE
   BEQ p1_initVulcanProjectile      ;check for an inactive projectile slot, then spawn a projectile
   INX                        ;Increment X 5 times because each projectile slot is 5 bytes wide
   INX
   INX
   INX
   INX
   CPX #PLAYER_NUM_PRJ_BYTES      ;Compare X to 40
   BNE p1_fireVulcanLoop
   
   JMP p1_fireVulcanDone         ;IF no inactive projectile slot is found skip the rest of the subroutine
   
p1_initVulcanProjectile:
   ;SET projectile to ACTIVE
   LDA #PRJ_ACTIVE
   STA p1_projectile1, x
   
   ;SET projectile type to VULCAN
   INX
   LDA #PRJ_VULCAN
   STA p1_projectile1, x
   
   ;Determine & SET projectile level (which metasprite to use)
   ;For now just default to a Level 0 projectile
   INX
   LDA #PRJ_LVL0
   STA p1_projectile1, x

   ;Determine & SET Xpos
   INX
   LDY #$03
   LDA p1_x
   CLC
   ADC prj_vulcan_lvl0_sprite, y
   STA p1_projectile1, x
   
   ;Determine & SET Ypos
   INX
   LDY #$00
   LDA p1_y
   SEC
   SBC prj_vulcan_lvl0_sprite, y
   STA p1_projectile1, x
   
p1_fireVulcanDone:
   RTS      ;end p1_fireVulcan


Main Part of loop which updates sprite stuff once per frame
Note: JSR buildSpriteTable just calls p1_updateProjectiles subroutine for now so I didn't include it's code.
Code:
;Setup sprite pointer - For now always draw player 1, so the pointer always begins pointing to the first available slot after the player 1 metasprite
   LDA #PLAYER_NUMSPRITETILES
   STA sprite_pointer
   LDA #$02
   STA sprite_pointer + #$01      ;Write $0209 to sprite pointer
   
   ;setup the sprite counter - For now always draw player 1, start the counter at 36 (9 sprites x 4 bytes)
   LDA #$24
   STA sprite_counter
   
   JSR buildSpriteTable


Update Player 1's Projectiles
Should be updating any active projectiles and copying their sprite data into the sprite table (which is getting DMA'd to the PPU every frame)
Code:
;Player Projectile Data -> Is Active, Type, Level, Xpos, Ypos
p1_updateProjectiles:

   ;Loop through entity list
   ;Check each projectile for ACTIVE, update if TRUE
   
   LDX #$00
   LDY #$00
p1_checkPrjActiveLoop:
   LDA p1_projectile1, x
   CMP #PRJ_ACTIVE
   BEQ p1_updateCurrentProjectile   ;check for active projectile, IF TRUE then update it
p1_continuePrjActive:
   INX                        ;Increment X 5 times because each projectile slot is 5 bytes wide
   INX
   INX
   INX
   INX
   CPX #PLAYER_NUM_PRJ_BYTES      ;Compare X to 40
   BNE p1_checkPrjActiveLoop      ;If not 40, continue loop
   BEQ p1_updateProjectilesDone   ;If 40, end updates

   
p1_updateCurrentProjectile:

   TXA
   TAY      ;Transfer X to Y so I can update projectiles without affecting X (which is used in the loop)
   
   INY      ;INY to check TYPE
   ;Check TYPE here ->skip to appropriate label (Either VULCAN or SIDE)
   ;For now don't check type, only testing with Vulcan
   
   INY      ;INY to check LEVEL
   ;Check LEVEL here ->skip to appropriate label
   ;For now don't check level, assume it's level 0 since that's what I'm testing with

   INY      ;INY to check X pos
   ;Move projectile:
   ;For now only move X (which is what Vulcan Projectiles do)
   ;Check if it's within screen bounds (IF not, set it inactive and go back through loop)
   LDA p1_projectile1, y
   CLC
   ADC #PRJ_VULCAN_SPEED
   BCS p1_updateCurrentProjectile_setInactive      ;IF A rolled over, set the projectile inactive. IF NOT continue
   STA p1_projectile1, y
   
   ;Check for collision with enemies here
   ;IF TRUE deal damage to enemy, spawn explosion, set Inactive
   
   ;Copy metasprite data to sprite table
   ;For now just copy a Vulcan Level 0 metasprite to the sprite table for testing
p1_updateCurrentProjectile_copy:
   TXA
   TAY
   ;Player Projectile Data = 5 bytes -> Is Active, Type, Level, Xpos, Ypos
   ;Sprite = 4 bytes -> Ypos, Tile Number, Attributes, Xpos
   INY
   INY
   INY
   INY
   LDA p1_projectile1, y
   STA (sprite_pointer)      ;Store sprite Ypos to sprite table
   INC sprite_pointer
   
   LDA prj_vulcan_lvl0_sprite + #$01
   STA (sprite_pointer)      ;Store sprite TileNum to sprite table
   INC sprite_pointer
   
   LDA prj_vulcan_lvl0_sprite + #$02
   STA (sprite_pointer)      ;Store sprite Attributes to sprite table
   INC sprite_pointer
   
   DEY
   LDA p1_projectile1, y
   STA (sprite_pointer)      ;Store Xpos to sprite table
   INC sprite_pointer
   
   LDA sprite_counter
   CLC
   ADC #$04
   STA sprite_counter         ;Add 4 to sprite_counter (used to keep track of used sprite slots, and for blanking unused sprite slots)
   
   JMP p1_continuePrjActive

p1_updateCurrentProjectile_setInactive:
   TXA
   TAY
   LDA #PRJ_INACTIVE         ;Set projectile to inactive state
   STA p1_projectile1, y
   
   INY
   LDA #PRJ_VULCAN            ;For now just set it to vulcan
   STA p1_projectile1, y
   
   INY
   LDA #PRJ_LVL0            ;For now set the level to 0
   STA p1_projectile1, y
   
   INY
   LDA #$FF
   STA p1_projectile1, y
   INY
   STA p1_projectile1, y      ;Set the X&Ypos to 255

   JMP p1_continuePrjActive

p1_updateProjectilesDone:
   RTS      ;end p1_updateProjectiles


Top
 Profile  
 
PostPosted: Sun Feb 10, 2019 8:49 pm 
Offline

Joined: Tue Feb 07, 2017 2:03 am
Posts: 680
Code:
   STA (sprite_pointer)      ;Store sprite Ypos to sprite table
   INC sprite_pointer
   
   LDA prj_vulcan_lvl0_sprite + #$01
   STA (sprite_pointer)      ;Store sprite TileNum to sprite table
   INC sprite_pointer
   
   LDA prj_vulcan_lvl0_sprite + #$02
   STA (sprite_pointer)      ;Store sprite Attributes to sprite table
   INC sprite_pointer
   
   DEY
   LDA p1_projectile1, y
   STA (sprite_pointer)      ;Store Xpos to sprite table

This is illegal and should not even assemble, hard to know what it is doing
STA (sprite_pointer) can't be done it either needs to be
STA (sprite_pointer,x) or more commonly STA (sprite_pointer) ,y which I guess is assembler is doing for you, which is not what you want.

There are two ways to greatly improve this code..
first step.
Don't do
iny
lda thing,y to get the next field
keep y or x to the base of the "struct" then do
lda thing+1,y
lda thing+4,y this allows you to read any field you want without needing to adjust x or y each time, iny dey get brittle fast.

However the even better way to do it is to make a struct of arrays so
Prj_enable .dsb 8
Prj_level .dsb 8
Prj_x .dsb 8
etc
the if X or Y is the projectile number you want
lda Prj_enable,x
lda Prj_level,x
this way you need to need to mul x or y by 5 each time, and just do a single inx/dex to move through them.


Top
 Profile  
 
PostPosted: Sun Feb 10, 2019 9:31 pm 
Offline

Joined: Sun Jan 02, 2011 9:58 pm
Posts: 9
Thank you very much! That fixed the problem. Now that I know how it works I'll try and rebuild it how you suggest in option #2.


Top
 Profile  
 
PostPosted: Sun Feb 10, 2019 10:59 pm 
Online
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11166
Location: Rio de Janeiro - Brazil
Oziphantom wrote:
This is illegal and should not even assemble, hard to know what it is doing

Another ASM6 quirk, I guess... The parentheses are probably being interpreted as precedence modifiers, rather than representing indirection, which isn't supported on the vanilla 6502 without an index register. I guess 6502 assemblers shouldn't allow parentheses around entire expressions, to avoid this sort of confusion.


Top
 Profile  
 
PostPosted: Sun Feb 10, 2019 11:42 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3866
Location: A world gone mad
Oziphantom wrote:
... This is illegal and should not even assemble, hard to know what it is doing ... which I guess is assembler is doing for you, which is not what you want.

Thankfully given the simplicity of the assembler and its listing capability, it's easy to find out what it's doing, so let's look. I made some assumptions as to what some of the variables or equates referred to, given what I think might have been intended by code.

The below is strictly about asm6 syntax and so on.

Code:
D:\downloads\x>type test.lst

                                sprite_pointer         = $33
                                prj_vulcan_lvl0_sprite = $0456

                                  .enum $0400
00400                           p1_projectile1 .dsb 5
00405                             .ende


                                  .org $8000
08000
08000 85 33                        STA (sprite_pointer)      ;Store sprite Ypos to sprite table
08002 E6 33                        INC sprite_pointer
08004
08004 AD 57 04                     LDA prj_vulcan_lvl0_sprite + #$01
08007 85 33                        STA (sprite_pointer)      ;Store sprite TileNum to sprite table
08009 E6 33                        INC sprite_pointer
0800B
0800B AD 58 04                     LDA prj_vulcan_lvl0_sprite + #$02
0800E 85 33                        STA (sprite_pointer)      ;Store sprite Attributes to sprite table
08010 E6 33                        INC sprite_pointer
08012
08012 88                           DEY
08013 B9 00 04                     LDA p1_projectile1, y
08016 85 33                        STA (sprite_pointer)      ;Store Xpos to sprite table

Summarised results:

STA (sprite_pointer), which isn't a valid addressing mode on 6502 (but is on 65816), gets assembled to 85 33, which is sta zp. I suspect the parser is interpreting the parenthesis as part of the expression (i.e. expands to the ZP address of sprite_pointer). If we change sprite_pointer into a 16-bit value and reassemble, we get 8d {lo} {hi}, which is sta abs; this generally supports the theory. tokumaru had the same suspicion.

LDA prj_vulcan_lvl0_sprite + #$01, which is "almost syntactically valid but not quite" -- the #$01 should just be 1 or $01 or whatever. The use of # for immediate addressing should throw a syntax error. But instead, we see the results ad {lo+1} {hi}, which is lda abs. The expression is doing the right thing by adding 1 to the equate value ($0456+1 == $0457), but really the issue here is about enforcing a consistent syntax. Either way, it generates what I believe is the correct code. People using # erroneously like this is a result of them not understanding the difference between literals and addressing modes (specifically immediates).

I always urge people to generate code listings if they suspect something is anomalous; compare what gets generated bytecode-wise to what you expect. I've been saying this for years and am starting to sound like a broken record. Good assemblers support generating clear code listings, and despite several asm6 parser/syntax oddities, it's still a good assembler.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 5 posts ] 

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 5 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group