It is currently Tue Oct 23, 2018 8:02 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 17 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Sat Jan 27, 2018 10:14 am 
Offline
User avatar

Joined: Sat Sep 27, 2014 10:10 pm
Posts: 30
Location: Houston, TX
Hi Guys, I'm new to assembly as well as to NES dev so you'll have to excuse my ignorance on some things. I've been going through the nerdy nights tutorials on nintendo age and I was wondering about flipping a sprite horizontally. I'm using a fairly long method of reading the controller input for now, which I will soon fix, but in the mean time I'm going to run with it.

I have 4 sprites that make up big mario and I'm moving them all together in a loop. I'm using an or on the attribute bit to flip each sprite. The problem is that in addition to flipping the sprite, it flips their position as well.

Anyways, this is the code that I have:

Code:
ReadLeft:
   LDA $4016
   AND #%00000001
   BEQ ReadLeftDone

   LDX #$00
MoveLeftLoop:
   LDA $0203, x ; get sprite 1 x pos
   SEC               ; set carry
   SBC #$01     ; x -= 1
   STA $0203, x ; move all 4 sprites

   ; flip sprite horizontally
   LDA $0202, x     ; get attribute byte
   ORA #%01000000   ; flip horizontally
   STA $0202, x     ; store the flip

   INX
   INX
   INX
   INX
   CPX #$10
   BNE MoveLeftLoop


Does anyone know what I'm doing wrong?


Top
 Profile  
 
PostPosted: Sat Jan 27, 2018 10:44 am 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 3132
Location: Tampere, Finland
It's not enough to flip each individual 8x8 sprite in order to flip the whole metasprite. You need to also adjust the position of each 8x8 sprite.

I'd advise you to draw a small example (e.g., 4 8x8 sprites in a 2x2 grid) of this on paper to figure it out.

_________________
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi


Top
 Profile  
 
PostPosted: Sat Jan 27, 2018 10:48 am 
Offline
User avatar

Joined: Sat Sep 27, 2014 10:10 pm
Posts: 30
Location: Houston, TX
thefox wrote:
It's not enough to flip each individual 8x8 sprite in order to flip the whole metasprite. You need to also adjust the position of each 8x8 sprite.

I'd advise you to draw a small example (e.g., 4 8x8 sprites in a 2x2 grid) of this on paper to figure it out.


So I guess the flag will flip the sprites on their relative (0,0)? If I'm assuming correctly then i just need to position them relative to their flip location?


Top
 Profile  
 
PostPosted: Sat Jan 27, 2018 11:46 am 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2308
Location: DIGDUG
2 methods...

1. have 2 separate arrays for each flip state (faster to draw, but takes up more bytes of ROM) that already has the H flip bit set.

or

2. subtract each x coordinate from a central x point (slower, but needs 1/2 the ROM space)...and also set the H flip bit for each sprite.

central x - relative x = flipped x.

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Top
 Profile  
 
PostPosted: Sat Jan 27, 2018 11:48 am 
Offline
Formerly WheelInventor

Joined: Thu Apr 14, 2016 2:55 am
Posts: 1783
Location: Gothenburg, Sweden
Quote:
So I guess the flag will flip the sprites on their relative (0,0)? If I'm assuming correctly then i just need to position them relative to their flip location?


Yes, and the anchor point (top left corner) remains the same. So if you have two sprites at 0,0 and 8,0, flip their H mirror and want the whole metasprite to flip, they must be relocated to 8,0 and 0,0.

Edit: dougeff ninja'd and got into better detail.

_________________
http://www.frankengraphics.com - personal NES blog


Top
 Profile  
 
PostPosted: Sat Jan 27, 2018 12:43 pm 
Offline
User avatar

Joined: Sat Sep 27, 2014 10:10 pm
Posts: 30
Location: Houston, TX
dougeff wrote:
2 methods...

1. have 2 separate arrays for each flip state (faster to draw, but takes up more bytes of ROM) that already has the H flip bit set.

or

2. subtract each x coordinate from a central x point (slower, but needs 1/2 the ROM space)...and also set the H flip bit for each sprite.

central x - relative x = flipped x.


I'm not quite sure how #1 would work. So instead of 1 meta sprite, you have two? Do you just set that meta sprite so that each individual sprite has the correct relationship after the toggled flip? Do you then have to move sprite 1 off the screen and position sprite 2 correctly? Or is there some way to flip between sprite 1 and 2?

for #2, that makes sense. I would have to refactor so I'm not setting the position inside a loop, or checking if I'm on an odd or even sprite or something like that.


Top
 Profile  
 
PostPosted: Sat Jan 27, 2018 2:00 pm 
Offline
User avatar

Joined: Thu Mar 31, 2016 11:15 am
Posts: 391
It's very unusual to update sprites directly like that. Usually you update a variable referring to the player's x coordinate, and then use that variable to build the sprites at a later time. This might be the cause of some of your confusion.

Quote:
I'm not quite sure how #1 would work. So instead of 1 meta sprite, you have two?

Yes. You'd store data for two metasprites.

Quote:
Do you then have to move sprite 1 off the screen and position sprite 2 correctly? Or is there some way to flip between sprite 1 and 2?

? Again, it's very unusual to do sprites like the way you're doing them. In most games sprite memory is discarded and rebuilt each frame; there's no persistence. I think you're getting confused by not doing it this way.


Top
 Profile  
 
PostPosted: Sat Jan 27, 2018 2:08 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2308
Location: DIGDUG
I don't want to derail the conversation, but don't do logic inside the button reading code. There's other things to mention...another time...

But, I have 1 master subroutine for drawing all objects, and shuffling them (because of the 8 sprite per scanline limit). I clear all sprites off screen prior to this (by setting Y > 240).

To answer your question. 1 array for non-flipped. 1 array for flipped. Yes. Everyone who uses neslib and NES Screen Tool does this.

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Top
 Profile  
 
PostPosted: Sat Jan 27, 2018 2:10 pm 
Offline
User avatar

Joined: Sat Sep 27, 2014 10:10 pm
Posts: 30
Location: Houston, TX
pubby wrote:
It's very unusual to update sprites directly like that. Usually you update a variable referring to the player's x coordinate, and then use that variable to build the sprites at a later time. This might be the cause of some of your confusion.


Oh right, this makes sense. I was just following the beginning steps of the nerdy nights tutorial. Once I get this sorted out I'll try doing it that way instead.


Top
 Profile  
 
PostPosted: Sat Jan 27, 2018 2:18 pm 
Offline
User avatar

Joined: Sat Sep 27, 2014 10:10 pm
Posts: 30
Location: Houston, TX
dougeff wrote:
I don't want to derail the conversation, but don't do logic inside the button reading code. There's other things to mention...another time...

But, I have 1 master subroutine for drawing all objects, and shuffling them (because of the 8 sprite per scanline limit). I clear all sprites off screen prior to this (by setting Y > 240).

To answer your question. 1 array for non-flipped. 1 array for flipped. Yes. Everyone who uses neslib and NES Screen Tool does this.


Yeah, I definitely understand this point. Right now I'm just trying to wrap my head around the hardware as well as 6502 assembly. I'm just trying to get something working. I can refactor from there. I've actually been trying to split out the logic like this:

Code:
; heres my NMI code
NMI:
  JSR ReadController1
  JSR UpdatePlayer
  JSR DMADraw

  RTI        ; return from interrupt

; meanwhile in another file...
ReadController1:
; latch buttons player 1
  LDA #$01
  STA $4016
  LDA #$00
  STA $4016
  LDX #$08
ReadController1Loop:
  LDA $4016
  LSR A            ; Put controller pressed bool in carry
  ROL buttons1 ; put carry into buttons1
  DEX                ; x -= 1
  BNE ReadController1Loop  ; DEX will set zero flag if the count down is complete.  BNE will check zero flag
  RTS

UpdatePlayer:
  LDA buttons1
  AND #%10000000
  BEQ PlayerAPressed

  LDA buttons1
  AND #%01000000
  BEQ PlayerBPressed
   
  LDA buttons1
  AND #%00100000
  BEQ PlayerStartPressed

  LDA buttons1
  AND #%00010000
  BEQ PlayerSelectPressed

  LDA buttons1
  AND #%00001000
  BEQ PlayerUpPressed

  LDA buttons1
  AND #%00000100
  BEQ PlayerDownPressed

  LDA buttons1
  AND #%00000010
  BEQ PlayerLeftPressed

  LDA buttons1
  AND #%00000001
  BEQ PlayerRightPressed

  RTS   


So, hopefully this is a bit closer to what it should look like. This actually isn't working at all so I'm trying to work through that now.


Top
 Profile  
 
PostPosted: Sat Jan 27, 2018 2:59 pm 
Offline
User avatar

Joined: Sat Sep 27, 2014 10:10 pm
Posts: 30
Location: Houston, TX
Ok I just realized that the "switch" type statement that I wrote needed to have BNE instead of BEQ. Now I'll be trying to update my character using variables for tracking player x and y instead of updating the sprite directly.


Top
 Profile  
 
PostPosted: Sat Jan 27, 2018 3:04 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2308
Location: DIGDUG
No.

Here's a simplified version of the wiki example.

Code:
NMI:
     pha         ; back up registers (important)
     txa
     pha
     tya
     pha
     
       lda #0      ; do sprite DMA
       sta $2003   ; conditional via the 'needdma' flag
       lda #>oam
       sta $4014

       jsr PPU_Updates
       jsr Palette_Updates
       
       lda soft2001
       sta $2001
       lda soft2000
       sta $2000

       bit $2002
       lda xscroll    ; set X/Y scroll
       sta $2005
       lda yscroll
       sta $2005

     jsr MusicEngine

     pla            ; restore regs and exit
     tay
     pla
     tax
     pla
     rti



But, since you are doing nerdy nights, and doing "all in the NMI"...right after jsr MusicEngine, you would do...
jsr Read_controllers
jsr Game_Logic
jsr Draw_Sprites_to_OAM_buffer

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Top
 Profile  
 
PostPosted: Sat Jan 27, 2018 3:21 pm 
Offline
User avatar

Joined: Sat Sep 27, 2014 10:10 pm
Posts: 30
Location: Houston, TX
dougeff wrote:
No.

Here's a simplified version of the wiki example.

Code:
NMI:
     pha         ; back up registers (important)
     txa
     pha
     tya
     pha
     
       lda #0      ; do sprite DMA
       sta $2003   ; conditional via the 'needdma' flag
       lda #>oam
       sta $4014

       jsr PPU_Updates
       jsr Palette_Updates
       
       lda soft2001
       sta $2001
       lda soft2000
       sta $2000

       bit $2002
       lda xscroll    ; set X/Y scroll
       sta $2005
       lda yscroll
       sta $2005

     jsr MusicEngine

     pla            ; restore regs and exit
     tay
     pla
     tax
     pla
     rti



But, since you are doing nerdy nights, and doing "all in the NMI"...right after jsr MusicEngine, you would do...
jsr Read_controllers
jsr Game_Logic
jsr Draw_Sprites_to_OAM_buffer


No as in, no it shouldn't look like this? It's currently working like I want it to so at least there's that. I'm ok with not doing "all in the NMI", I just don't know another way. Like I said in the beginning, I'm completely new to assembly programming (as well as low level programming in general). Is there a tutorial you can point me to that would set me up for something more scalable?

Also, I think my NMI looks pretty much like what you've posted here. You've given:

Code:
jsr Read_controllers
jsr Game_Logic
jsr Draw_Sprites_to_OAM_buffer


and mine looks like this:

Code:
NMI:
   JSR ReadController1 ; same as Read_controllers
   JSR UpdatePlayer      ; since there isn't really a game to speak of yet... this is my game logic
   JSR DMADraw           

  RTI


Maybe I'm missing your point.


Top
 Profile  
 
PostPosted: Sat Jan 27, 2018 3:51 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2308
Location: DIGDUG
Some things NEED to happen during v-blank.

1.DMA Sprites
2.PPU Writes (and palette changes)
3.Setting the scroll and 2000/2001

These must come before anything else.

Saving the A,X,Y registers is optional IF you are doing an "all in the NMI" system.

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Top
 Profile  
 
PostPosted: Sat Jan 27, 2018 6:46 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6900
Location: Canada
dougeff wrote:
1. have 2 separate arrays for each flip state (faster to draw, but takes up more bytes of ROM) that already has the H flip bit set.
2. subtract each x coordinate from a central x point (slower, but needs 1/2 the ROM space)...and also set the H flip bit for each sprite.

So, FWIW, I prefer the latter option, mainly because if you have enough sprites the extra space becomes pretty significant.

Naively, a metasprite is 4 bytes per tile. Maybe a typical one has 4 tiles? 16 bytes per sprite for an extra flipped version adds up pretty fast.

On the other hand, a metasprite drawing routine is probably only ~60 bytes total. So, what I did is copy-paste my sprite routine and make a separate flipped version that's different in the following ways:

  • input X = X + 8 (+4 cycles per metasprite)
  • subtract metasprite tile X from input X instead of add (same number of cycles)
  • XOR the attribute byte for flip (+2 cycles per tile)

So, the "slower" aspect is really quite minor (~12 cycles per sprite), and the code/data trade begins paying off at only 4 sprites or so.

There's other ways of doing it, and if your game is not horizontally oriented you might want vertical flips as well, etc. but generally I find it pretty worthwhile to have two routines. If you want to spent a few more cycles per tile, you could even share most of the code between the two routines, saving even more space. You could even piggyback a palette swap option into the attribute XOR... there's lots of ways to customize this to suit your needs.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 17 posts ]  Go to page 1, 2  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: Bavi_H and 1 guest


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