Help with horizontal sprite flip

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

User avatar
instantaphex
Posts: 30
Joined: Sat Sep 27, 2014 10:10 pm
Location: Houston, TX

Help with horizontal sprite flip

Post by instantaphex » Sat Jan 27, 2018 10:14 am

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: Select all

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?

User avatar
thefox
Posts: 3141
Joined: Mon Jan 03, 2005 10:36 am
Location: Tampere, Finland
Contact:

Re: Help with horizontal sprite flip

Post by thefox » Sat Jan 27, 2018 10:44 am

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

User avatar
instantaphex
Posts: 30
Joined: Sat Sep 27, 2014 10:10 pm
Location: Houston, TX

Re: Help with horizontal sprite flip

Post by instantaphex » Sat Jan 27, 2018 10:48 am

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?

User avatar
dougeff
Posts: 2600
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: Help with horizontal sprite flip

Post by dougeff » Sat Jan 27, 2018 11:46 am

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

User avatar
FrankenGraphics
Formerly WheelInventor
Posts: 2030
Joined: Thu Apr 14, 2016 2:55 am
Location: Gothenburg, Sweden
Contact:

Re: Help with horizontal sprite flip

Post by FrankenGraphics » Sat Jan 27, 2018 11:48 am

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

User avatar
instantaphex
Posts: 30
Joined: Sat Sep 27, 2014 10:10 pm
Location: Houston, TX

Re: Help with horizontal sprite flip

Post by instantaphex » Sat Jan 27, 2018 12:43 pm

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.

User avatar
pubby
Posts: 536
Joined: Thu Mar 31, 2016 11:15 am

Re: Help with horizontal sprite flip

Post by pubby » Sat Jan 27, 2018 2:00 pm

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.
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.
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.

User avatar
dougeff
Posts: 2600
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: Help with horizontal sprite flip

Post by dougeff » Sat Jan 27, 2018 2:08 pm

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

User avatar
instantaphex
Posts: 30
Joined: Sat Sep 27, 2014 10:10 pm
Location: Houston, TX

Re: Help with horizontal sprite flip

Post by instantaphex » Sat Jan 27, 2018 2:10 pm

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.

User avatar
instantaphex
Posts: 30
Joined: Sat Sep 27, 2014 10:10 pm
Location: Houston, TX

Re: Help with horizontal sprite flip

Post by instantaphex » Sat Jan 27, 2018 2:18 pm

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: Select all

; 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.

User avatar
instantaphex
Posts: 30
Joined: Sat Sep 27, 2014 10:10 pm
Location: Houston, TX

Re: Help with horizontal sprite flip

Post by instantaphex » Sat Jan 27, 2018 2:59 pm

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.

User avatar
dougeff
Posts: 2600
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: Help with horizontal sprite flip

Post by dougeff » Sat Jan 27, 2018 3:04 pm

No.

Here's a simplified version of the wiki example.

Code: Select all

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

User avatar
instantaphex
Posts: 30
Joined: Sat Sep 27, 2014 10:10 pm
Location: Houston, TX

Re: Help with horizontal sprite flip

Post by instantaphex » Sat Jan 27, 2018 3:21 pm

dougeff wrote:No.

Here's a simplified version of the wiki example.

Code: Select all

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: Select all

jsr Read_controllers
jsr Game_Logic
jsr Draw_Sprites_to_OAM_buffer
and mine looks like this:

Code: Select all

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.

User avatar
dougeff
Posts: 2600
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: Help with horizontal sprite flip

Post by dougeff » Sat Jan 27, 2018 3:51 pm

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

User avatar
rainwarrior
Posts: 7642
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Help with horizontal sprite flip

Post by rainwarrior » Sat Jan 27, 2018 6:46 pm

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.

Post Reply