I have a question about using Sprite #0 for split screen side scrolling (horizontal scrolling)
I have what looks like workng code, but I see a small graphical artifact along the right edge of the screen on the same scanline as my sprite #0 is located. (I am using Nintendulator). My sprite #0 is a horizontal line of 6 pixels (1 pixel in height) , and the artifact resembles a missing region of pixels. (1 pixel high but the width varies up to 6 pixels)
Is this considered normal (acceptable) since I believe the rightmost tiles are not normally visible anyways, or am I missing something fundamental in my split screen routine.
Basically here is all I do in my NMI to handle split screen
Code:
nmi:
; store A,X,Y on stack (omitting code here)
lda $2002 ;reset latch
lda #%10001000 ; the nametable with my status bar at the top
sta $2000
lda #$00 ; reset scrolling
sta $2005
sta $2005
;do sprite DMA
lda #$02
sta $4014
; now wait for sprite #0 hit
: lda $2002
AND #%01000000
BNE :-
; sprite #0 was hit, now wait for hblank
: lda $2002
AND #%01000000
BEQ :-
; Now adjust the scroll values nametable for the area below the status bar
lda currentNameTableData ; I keep a variable elsewhere with the active nametable
sta $2000
lda SCROLL_POSITION ; I keep a variable with the X scroll position
sta $2005
lda #$00
sta $2005
; continue with the rest of the NMI...
Thanks,
Al
Once thing is that you do the hit detection wrong. I'm pretty should first wait the flag to be clear, then set (and you do the other way arround). That is because the flag is clear at the end of VBlank, and that your NMI code finish before that, so you wait the flag to be clear. And then you wait the flag to be set again, when set sprite zero hit occured. And wait for the flag to be clear wait the end of the VBlank, it don't have anything to do with HBlank.
When you change the horizontal scroll via $2005, bits 0-2 take effect immediately, while bits 3-7 don't take effect until H-Blank. When writing to $2000, bit 0 (horizontal nametable) takes effect at H-Blank.
To avoid glitching at the right side of the screen, make sure the second nametable is filled with transparent tiles in the area covered by the status bar.
BTW, your sprite 0 code can be optimized if you use BIT:
Code:
WaitClear:
bit $2002
bvs WaitClear
WaitSet:
bit $2002
bvc WaitSet
There is no "wait for hblank". Instead, you delay cycles between the X position of the sprite, and the right side of the screen.
I'm thinking that your response is for a status bar at the bottom of the screen. I should have mentioned mine is at the top of the screen.
Here's a pic of the glitch. The black at the bottom right of the status area varies in width as I scroll. Anywhere from 0 to 6 pixels in width.
http://i51.photobucket.com/albums/f388/ ... glitch.jpg
The sprite I am using is in the middle (horizontally) on the bottom of the blue status area at the top. I included a screen shot showing the sprite too (although its just a horizontal line).
Al
Someone make sure my math is right here...
Code:
lda #$40
wait1:
;wait for sprite hit to turn off
bit $2002
beq wait1
wait2:
bit $2002 ;4, read takes place on last cycle
bne wait2 ;3 taken, 2 not
;okay so now we're 3-9 cycles after hit?
;value of X should be ( (226-sprite_X)/15)
ldx #xx ;2 cycles
wait3:
;takes X*15-3 pixels
dex ;2
bne wait ;3 taken, 2 not
post edited again
After you are in hblank, do your 2005 and 2006 writes.
Write X to 2005, then write x>>3 | (Y & 0xF8) << 2 to 2006.
Your Y is 32, so you take x>>3 | 0x80.
You are most likely writing a bit too early to $2005, I just suggest getting the value by writing a small waiting loop and try different values for the timing (I always do it that way, almost faster than actually calculate the value that is supposed to go here, try, note that you calculated it wrong and do it again, etc...). To convert to PAL, you only have to multiply that said value by the fration 15/16. In fact I used to often set my sprite some 8 or so scanline above the point where I change scolling, and wait 8.5 or so scanline before writing to $2005 (trying different values until you find the best one).
Alternatively, as the other guy already said, another approach is to change the horizontal scolling on a blank line where the line is blank on BOTH nametables, so that no matter where the scrolling is changed, the scanline will always be blank and no glitches will ever be visible. This of course works as long as you never touch $2006 to change vertical scrolling.
Thanks guys, I'll give it a try. Although my second nametable is transparent in that upper region.
I didnt realize that there was "timing" involved. I knew I had to get a few things done in a short period of time, I just thought I could detect an hblank for sprite #0
I'll let you know how it goes.
Al
It appears that if I just position my sprite #0 so its X position is 248 (the far right of the screen) all my timing issues go away
For me, I was only planning on using sprite 0 for the status bar, not as a specific sprite to be seen, so for my purposes, this seems to work in Nintendulator for NTSC and PAL.
Based on the info provided, my sprite detection portion gets much smaller and more efficient.
LDA #$40
: BIT $2002
BVS :-
: BIT $2002
BVC :-
Thanks for clearing things up for me about hblank and timing. I'll keep it in mind for future projects.
Al
That LDA isn't required. BIT sets V according to bit 6 of the read value -- the contents of A are only relevent for setting the Z flag.
Quote:
Code:
bne wait ;3 taken, 2 not
A little tip for easier to total timing comments:
Code:
foo:
...
lda $2002 ; 4
bne foo ; 3
; -1
This lets you just scan down with a calculator totaling the values for the code path in question. If you're following the path for branch not taken, you hit the -1 and get 2 for the branch. Very minor. (don't kill me if I got the lda timing wrong, as I've been doing too much GB-Z80 coding lately)
Thanks guys.
Edit-
Even though I have it working, I'd still like to understand sprite #0 better
Here's what I have read, please correct me if I am wrong:
Most of this is from NES Architecture document 2.6 dated Jan 2005.
1) Sprite #0 must intersect the background on a pixel where neither the background or the sprite is using color index 0 (or a multiple of 4)
2) (I did not get this from the above mentioned document) The sprite/intersection must be at least 2 contigous pixels. Meaning I cannot simply make my sprite #0 a single pixel in size, and I cannot make it just 2 pixels with a space between them. (I am pretty sure this is wrong.)
3) Bit 6 of $2002 is set when the first intersected pixel intersection occurs. (so this already contradicts #2 above) It doesnt go down until the PPU starts refreshing the screen (I have no idea what "PPU starts refreshing the screen" means). Does it mean when it gets to the next sprite, next scanline, next vblank, etc..
4) Reading from $2002 has no effect on bit 6 (Hit flag)
Al
Wizards and Warriors 3 uses a single pixel. In fact, it does some crazy and neat tricks to ensure that there will always be a pixel in the location where it collides. I'm sure rare wouldn't have done it that way if a single pixel didn't work.
Note #3 basically means that it clears the hitflag on scanline #0. (otherwise it would stay on all the time)
The flag is SET each time a non-stanspaent (i.e. not color 00) sprite pixel hit one non-transparent (i.e. not color 00) background pixel. The flag is CLEAR each time the PPU start the rendering (finish VBlank).
If you want to wait until the screen reach where the sprite zero is, you have to wait the flag to be clear, then wait for it being set again. You can also, for any reason, want to have a VBlank with variable lenght, but still synch to the screen to do some timed code (from rendered scanline 0) to do some graphical effect. If you want to do that, just wait the flag to be clear. Then it doesn't really matter where sprite #0 is, it just matters that it is somewhere and hit with the background at least one time.
1, 3 and 4 sounds right. 2 definitely sound wrong, however, you should also note that if the hit happens only horizontal posistion 255, the hit will not be taken in account ! (or something like that)
I remebered W&W3 used a 1x2 pixel sprite (that's 2 pixels vertically) but I'm not that sure right now.
I think the misconception of "rule" 2 comes from the following test suite: a one pixel overlap at X=255, followed by a two pixel overlap at X=254 and X=255.
I think I'm still not grasping one important concept. That is, when does the hit flag (bit 6) of $2002 go OFF.
The reason I am not grasping this, despite very good responses listed above, is that this piece of code invoked within the NMI works.
: BIT $2002
BVS :-
: BIT $2002
BVC :-
The Overflow flag gets set (BVS) and I loop again until it is cleared (BVC).
If I am on scanline 32, x position around 240 when sprite #0 is hit (the first loop, bit 6 gets set)
then does that means my second loop is waiting until the END of the NMI. Youch!!!
When I use this code, I can clearly see the screen being split properly, which doesnt jive with the responses I am reading, so I'm pretty sure I am clearly misundertanding what takes place during the second loop.
Al
The first loop waits for sprite 0 hit to clear (turn off). This happens at the end of VBlank.
The second loop actually waits for sprite 0 to hit.
The reason you need the first loop is because if you try to wait for sprite 0 hit when it's still in VBlank, the flag may still be set from the previous frame -- so you'll get a "false positive" so to speak and will split the screen at the very top instead of mid-frame like you want.
You seem to be getting BVS and BVC mixed up. BVS will branch when V (sprite 0 hit flag) is set. When put in a loop like the ones you're using, that means it will keep looping as long as V is set -- therefore, BVS is really waiting for V to clear -- not waiting for it to become set.
Ah OK.
I was mis-understanding 2 very important things.
1) I was miunderstanding the branches. I was mistakenly thinking they were branching forwards, lol. Thats just me being a fool.
2) I was misunderstanding thinking that the hit flag was cleared at the time the NMI was fired. This was a huge mis-understanding on my part, because if I thought it was clear then my first loop wouldnt even have been needed (and obviously it is).
The first loop waits until scanline #0 occurs.
The second loop waits until sprite #0 intersection occurs.
Got it. Time to update my code comments
I really appreciate all the responses. It greatly clarifies things for me.
Al