It is currently Wed Sep 20, 2017 4:13 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 48 posts ]  Go to page 1, 2, 3, 4  Next
Author Message
PostPosted: Fri Aug 04, 2017 7:11 am 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 254
So...why not? May or may not implement this, but now I want to play with it. Generally, I've been preserving a subpalette for the menu area. I got the thought about possibly trying to swap the palette after the menu draw, since that area is always static, to maximize colors in gameplay area. Game has no scrolling. Started searching around the forums a bit and found some tips and tricks (and nightmares). I figured I'd toss it out there to see if anyone could help walk me through it.

I saw Tokumaru's responses about preloading A,X,Y with the values, that way you could set $2006 to $3fxx, bit the first write to %2007, then fire AXY as successive writes to $2007 to minimize time this would take. So...conceptually, I would...

1) Load palette with menubar subpal

2) Wait for sprite zero hit (which I'd put at the end of the menu bar), which would look something like:

Code:
checkSpriteZnotHit:
  bit PPUSTATUS
  bvs checkSpriteZnotHit

checkSpriteZhit:
  bit PPUSTATUS
  bvc checkSpriteZhit/code]


Then wait for hblank (how?)

3) Turn rendering off, then push the three new values to the subpal

Then wait for next hblank (how?)


4) Turn rendering on and reset scroll.


That seems...too easy. Granted, I'm not trying to do any sort of scrolling or anything. Is it really that easy? I'm reading about waiting for hBlank, but haven't ever had to do that. How does one know when the hBlank fires (understand the concept of an hBlank, but don't know how to know when it fires like vBlank, or if it's even necessary). Or is it that sprite0 hit *forces* an hblank or something?

Thoughts?

Thanks!


Top
 Profile  
 
PostPosted: Fri Aug 04, 2017 7:34 am 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 2945
Location: Tampere, Finland
JoeGtake2 wrote:
Then wait for hblank (how?)

There's no signal in NES which tells you that hblank has started. You're going to have to use a timed delay loop (well, it doesn't have to be a loop specifically) after the sprite 0 hit. How long a delay depends on your sprite's X position. (E.g., if your sprite is at X=200, you're going to have to wait approximately 256-200=56 pixels to reach the right side of the screen, i.e., the start of hblank. That comes down to 56/3 ≈ 18.67 CPU cycles on NTSC machines, so about 9-10 NOP instructions would do the job in this case.)

The debugger in Mesen has a pretty cool feature that lets you see the screen as its drawing (Options -> Draw Partial Frame in the debugger window). That could come in useful when debugging things like this. You can also see the PPU cycle count (0-341 -- anything over 256 means you're in hblank).

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


Top
 Profile  
 
PostPosted: Fri Aug 04, 2017 7:47 am 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 254
Thanks - yeah, that's getting into stuff I haven't really pushed into yet. Hm....

Alright, i could draw this sprite at, say, 240...then i'd have 16 pixels...5.3 cpu cycles...2-3 NOP instructions...

And then at that point, do the steps mentioned...but no need to wait until the next hblank in step 3, as it should all fit in that tiny hblank, correct?



Again...that kinda feels too easy...


Top
 Profile  
 
PostPosted: Fri Aug 04, 2017 7:49 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 18986
Location: NE Indiana, USA (NTSC)
Sprite 0 hit happens at a certain horizontal position on the scanline, and hblank begins at x=256. Each CPU cycle is worth three pixels. So if you have sprite 0 hit at (say) x=76, that means 256 - 76 = 180 pixels or 60 cycles from that point to the start of hblank. The end of your waiting loop will probably use 5 to 11 of those cycles.

Code:
  lda #$C0  ; $80 is vblank; $40 is sprite 0
  s0wait:
    ; The read and branch total 7 cycles, with the actual PPU port
    ; read on the fourth cycle of 7.
    bit PPUSTATUS
    beq s0wait

  ; At this point, one of two things has happened: either sprite 0 hit,
  ; or sprite 0 missed and vblank began instead.  Occasionally
  ; sprite 0 may miss in lag frames or in the first frame before the
  ; OAM DRAM controller becomes stable.  Distinguish these and skip
  ; mid-frame effects if it missed.
  bmi sprite0_missed

  ; Now we're 5 to 11.67 cycles (15 to 35 pixels) after sprite 0.
  ; Do sprite 0 stuff.


So there are two ways to buy enough cycles to set up the PPU port write sequence to happen during horizontal blanking: sprite 0 on the left side of the same scanline or on the right side of the previous scanline.


Top
 Profile  
 
PostPosted: Fri Aug 04, 2017 7:58 am 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 254
Awesome guys...good info!

Yeah, tepples, my thought was last pixel of former scanline.

Any suggestion as to generally *where* to place this in relation to other code in order to minimize potential for gremlin chaos? haha


Top
 Profile  
 
PostPosted: Fri Aug 04, 2017 8:01 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 18986
Location: NE Indiana, USA (NTSC)
There's a quirk of the PPU's pixel pipeline such that a hit on x=255 (and only x=255) won't register. If you're looking to use a whole line to prepare, put the overlap at x=252-254 or the like.


Top
 Profile  
 
PostPosted: Fri Aug 04, 2017 10:24 am 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 254
So, I'm getting hung up right out of the gate.

In my NMI, rendering is off. This code just causes the game to hang indefinitely:

Code:

WaitNotSprite0:
    LDA $2002
    AND #%01000000
    BNE WaitNotSprite0

WaitSprite0:
    LDA $2002
    #%01000000
    BEQ WaitSprite0

    LDX #$10 ;; arbitrary - variable
WaitScanline:
    DEX
    BNE WaitScanLine



I'd delved deeper but kept freezing, so i figured I'd just start whittling away. It's the WaitSprite0 that causes it to hang. I also made absolute sure, in case it made a difference, that Sprite0 was being drawn on the screen (by literally drawing it just prior to this code). It still froze, gray screen. This is without any sprite0 effect...just the above code inserted in the NMI just prior to palette loads in otherwise working code.

Any thoughts? Thanks!


Top
 Profile  
 
PostPosted: Fri Aug 04, 2017 10:41 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10008
Location: Rio de Janeiro - Brazil
This has nothing to do with the hanging, but here's a quick tip: to reduce the latency when detecting sprite 0 hits, use BIT instead of AND to test the flag:

Code:
WaitSprite0:
  BIT $2002
  BVC WaitSprite0

BIT copies bits 7 and 6 of the read value to flags N and V, respectively, so you can test those bits more quickly. But even when you need to test bits other than 6 and 7, BIT still results in a shorter loop since you can preload the bit mask, reducing the latency anyway:

Code:
  LDA #%00100000
WaitSpriteOverflow:
  BIT $2002
  BEQ WaitSpriteOverflow

This helps keeping your raster effects timed from sprite hits or overflows more stable.

As for the hanging, you do know that a sprite 0 hit only happens if an opaque sprite pixel overlaps an opaque background pixel, right? Transparent pixels (color 0) don't count.


Top
 Profile  
 
PostPosted: Fri Aug 04, 2017 10:56 am 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 254
Actually, I did know that, but I was absently not thinking about the fact that only my MainGame state was drawing sprite 0...forgot to check the game state before doing this call. Oops! Thanks for the tip. Now let's see if I can get it working :-)


Top
 Profile  
 
PostPosted: Fri Aug 04, 2017 12:07 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5613
Location: Canada
I would actually recommend testing both vblank and sprite 0 hit together in the second wait loop:
Code:
   lda #%11000000
   :
   bit $2002
   beq :-

If done correctly it should only ever exit the loop due to sprite 0 hit, but vblank is an appropriate fall-back in case some disaster causes that sprite 0 to miss. The fallback result wouldn't be pretty, probably it will drop to 50% framerate and be scrolled completely wrong, etc. but the game won't lock up, and if the player can manage to get themselves where it gets restored, they can get back on track without having to abandon and reset.

Just in case, might also be worth saying that you can have "opaque" pixels hit that are the same colour as the background, they just can't be colour index 0. Also the sprite background priority bit doesn't affect the hit test so you can put it "under" the background to hide it as well.


Top
 Profile  
 
PostPosted: Fri Aug 04, 2017 12:24 pm 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 254
I'm making a bit of progress...right now, in just trying to successfully read a hit without glitching or locking up.

I can visibly see where I'm getting a split (right now, ugly horizontal line that flickers...i'm sure that's a timing issue) but unfortunately, the bigger problem is that:

a) I get a push (like the scroll is getting messed up...can mess with that a bit later)
b) it seems like everything *above* the line is drawing from the wrong table, even though there haven't been any writes to 2000 (and even hard-writing something just before the sprite zero check doesn't help this).

Any ideas?


**** Actually, not sure what magic just happened but just going back through seemed to fix things. Huh. Ok. I think I'm right there.

********** Nope, nevermind. Here's what's happening.

(rendering is off)

Code:


;; checks game state...if not a screen where sprite 0 is drawn, skip this code

 WaitSpriteNot0:
     BIT $2002
     BVS WaitSpriteNot0

    LDA #%11000000
checkSprite0:
    bit $2002
    BEQ checkSprite0

;;;;;; some arbitrary numbers in a dummy loop here to try to get to the end of the scanline
 ;;;;; should change a color in the last sub palette.
;;;;;; this works with no issue if I don't do the sprite zero check
;;;;; so something with the sprite zero check is the problem?
DoSingColorSwap:
    LDA $2002
    LDA #$3f
    STA $2006
    LDA #$0c
    STA $2006
    LDA #$30
    STA $2007






If I skip the sprite 0 loop and just do the single color swap, no problem. Work's just fine, for the entire screen. But with the sprite 0 hit and trying to separate the screen, the color does not change, I get *animated junk* at the top, scroll value was pshed off, and it seems that in some spots it is drawing the right value (f5, which should be a blank tile) but from the wrong pattern table. I tried different versions of the sprite zero check discussed here. All with the same result.

Currently, i am drawing sprite zero at #$d0 if it helps (and it is definitely colliding with background).


Top
 Profile  
 
PostPosted: Fri Aug 04, 2017 1:18 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10008
Location: Rio de Janeiro - Brazil
JoeGtake2 wrote:
(rendering is off)

I find this statement a bit confusing, because rendering must be on for the sprite hit to happen, and off for the palette update to happen, so rendering must be turned off after the hit and before the palette update. Is this what you're doing? Also, after the update, the scroll has to be restored (since you trashed it when updating the palette), and rendering turned back on.

Can you show the complete code for the split, even if it contains temporary junk?


Top
 Profile  
 
PostPosted: Fri Aug 04, 2017 3:05 pm 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 254
Sorry - that was a misplaced note to myself. Yes, rendering is on for the eval.

Essentially, I load the palettes (all) then turn back on rendering, do the checks for sprite 0 hit, turn rendering back off, and then load the *other* palette* once the hit fires. I have tried zeroing out $2005 afterwards to no avail.

Right now, it looks like I'm getting closer, but my scroll is still off. Hm.


Top
 Profile  
 
PostPosted: Fri Aug 04, 2017 3:14 pm 
Offline
User avatar

Joined: Sat Aug 15, 2015 3:42 pm
Posts: 100
Location: France
You seem to be on the right path but this post may help you : viewtopic.php?f=2&t=13188


Top
 Profile  
 
PostPosted: Fri Aug 04, 2017 3:24 pm 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 254
Yeah, I looked at that for a lot of reference. Everything seems fine, except for the sprite 0 hit. If I take the sprite zero hit out, i can change the palettes no problem. But as soon as i do the sprite 0 hit stuff, I crash and get all sorts of funky things happening with scroll. Hm.


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

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 4 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