Simple vertical scrolling with status bar

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

User avatar
DRW
Posts: 2225
Joined: Sat Sep 07, 2013 2:59 pm

Re: Simple vertical scrolling with status bar

Post by DRW »

Thanks for all the help.

I did this after the sprite 0 split and it worked:

Code: Select all

; Write (NameTable << 2) to PPUADDR
LDA NameTable
ASL
ASL
STA $2006

; Write VerticalScrollingPosition to PPUSCROLL
LDA VerticalScrollingPosition
STA $2005

; Save ((VerticalScrollingPosition & %11111000) << 2)
; to a temporary variable.
AND #%11111000
ASL
ASL
STA MergedScrollingValue

; Write ((HorizontalScrollingPosition >> 3) | ((VerticalScrollingPosition & %11111000) << 2))
; to the temporary variable.
LDA HorizontalScrollingPosition
LSR
LSR
LSR
ORA MergedScrollingValue
STA MergedScrollingValue

; TODO: Wait for hblank.

; Write HorizontalScrollingPosition to PPUSCROLL
LDA HorizontalScrollingPosition
STA $2005

; Write the temporary value to PPUADDR
LDA MergedScrollingValue
STA $2006
Is this all correct so far?

One more question:
tepples wrote:Writes 3 and 4 must happen in horizontal blanking between X=256 and X=320.
How do I make sure that these writes are done in hblank?
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Simple vertical scrolling with status bar

Post by tokumaru »

Looks correct, but you don't need to calculate these values in real time if you're doing this only for the status bar. Since the status bar is always displayed relative to its top left corner, you can just use hardcoded values. The dynamic version is good if you plan on scrolling the area after the split, though.
DRW wrote:How do I make sure that these writes are done in hblank?
You could buffer the last 2 values in different registers so you can write them as fast as possible (without a load instruction between them), making them even easier to fit within the appropriate window of time, and then set a breakpoint on the first write to check when it triggers, in an emulator that has this feature. FCEUX will tell you the current scanline and pixel, so you can easily tell when the writes are happening.
User avatar
DRW
Posts: 2225
Joined: Sat Sep 07, 2013 2:59 pm

Re: Simple vertical scrolling with status bar

Post by DRW »

Yeah, the scrolling is indeed for the area after the split.
Top: Status bar.
Bottom: Playfield.
tokumaru wrote:You could buffer the last 2 values in different registers so you can write them as fast as possible (without a load instruction between them), making them even easier to fit within the appropriate window of time
Thanks. I altered the last part of the code accordingly:

Code: Select all

; Write ((HorizontalScrollingPosition >> 3) | ((VerticalScrollingPosition & %11111000) << 2))
; to the X register.
LDA HorizontalScrollingPosition
LSR
LSR
LSR
ORA MergedScrollingValue
TAX

; Write HorizontalScrollingPosition to the A register
LDA HorizontalScrollingPosition

; HBlank

; Write HorizontalScrollingPosition to PPUSCROLL
STA $2005

; Write the temporary value to PPUADDR
STX $2006
tokumaru wrote:and then set a breakpoint on the first write to check when it triggers, in an emulator that has this feature.
So, there's no flag inside the NES' architecture that I can check? I simply have to experiment?

I tried to include a counter before writing to $2005 and $2006:

Code: Select all

LDY #3
@counter:
DEY
BNE @counter
But no matter what value I use, it is never exactly correct. Either some pixels are missing from the status bar (in every frame it's a different number, so the missing pixels are jumping) or the whole screen is shaking.
Attachments
Screenshot.png
Screenshot.png (3.96 KiB) Viewed 2757 times
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Simple vertical scrolling with status bar

Post by tokumaru »

DRW wrote:Yeah, the scrolling is indeed for the area after the split.
Top: Status bar.
Bottom: Playfield.
Oh, OK then. Somehow I missed that and thought the status bar was at the bottom.
So, there's no flag inside the NES' architecture that I can check? I simply have to experiment?
Nope, no flag. You need to have the sprite 0 hit happen at a consistent time every frame and have the split take place a fixed number of cycles after that. You may need to add NOPs or a dummy loop so the writes happen at the correct time.
But no matter what value I use, it is never exactly correct. Either some pixels are missing from the status bar (in every frame it's a different number, so the missing pixels are jumping) or the whole screen is shaking.
Yes, there will always be some wobbling where the split happens, because on the NES it's not possible to perfectly align the CPU and the PPU (you can get close with a humongous amount of work and trickery, but it's not really practical), but as long as the variation isn't bigger than 64 pixels (~21 CPU cycles) you should be able to fit it into the appropriate window.

If pixels are missing from the status bar and the bottom part shakes, that means that sometimes the writes are happening too early, so you still have to push them back a bit. You have to check in the debugger for several frames to see if the writes in all possible alignments are taking within the allowed range of time (pixel > 256 and < 320). If the split happens too early, the last scanline of the status bar will glitch and the bottom part will jump, if it happens too late, the first scanline of the bottom part will glitch.
User avatar
DRW
Posts: 2225
Joined: Sat Sep 07, 2013 2:59 pm

Re: Simple vertical scrolling with status bar

Post by DRW »

Yeah, today I noticed for the first time that "The Legend of Zelda" has exactly the same problem. I guess I might put a few sprites in the status bar to cover the timing issues.

Next thing I'll do is probably learn how to handle MMC-1. Because my next game is supposed to be a screen-by-screen scrolling game like "Zelda". And the MMC-1 has the ability to switch the mirroring. So, with it I can do horizontal and vertical scrolling without graphical glitches at the screen borders.
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Simple vertical scrolling with status bar

Post by tokumaru »

You haven't shown your code for testing the sprite 0 hit, so I don't know if it's optimized already, but the timing variation is smaller when it is. This would be a naive approach:

Code: Select all

wait:
  lda $2002
  and #%01000000
  beq wait
This takes 9 cycles per iteration, so if the hit flag changes right after a $2002 read, it will take you 9 more cycles to detect the change. If you do this however:

Code: Select all

wait:
  bit $2002
  bvc wait
The iteration time is reduced to 7 cycles, which effectively reduces the timing variation.
DRW wrote:Yeah, today I noticed for the first time that "The Legend of Zelda" has exactly the same problem. I guess I might put a few sprites in the status bar to cover the timing issues.
You don't need to. You have a ~21 cycle window to fit 5 cycles (last cycle of the first write + 4 cycles of the second write), it's perfectly doable, even with a variation of 16 cycles (48 pixels).
And the MMC-1 has the ability to switch the mirroring. So, with it I can do horizontal and vertical scrolling without graphical glitches at the screen borders.
You can do this with plain old vertical mirroring too, the only "catch" is that you might need to move the status bar from one name table to the other when scrolling vertically, to clear the space for scrolling. The screen layout change the MMC1 does might require you to move the status bar anyway, so... Do what you're more comfortable with, but know that glitchless alternating horizontal/vertical scrolling with a status bar is possible even without switching mirroring.
User avatar
DRW
Posts: 2225
Joined: Sat Sep 07, 2013 2:59 pm

Re: Simple vertical scrolling with status bar

Post by DRW »

tokumaru wrote:

Code: Select all

wait:
  bit $2002
  bvc wait
What does this code do? And how do I differentiate whether I want to check for sprite 0 or another flag, like the nine sprites overflow flag?
tokumaru wrote:Do what you're more comfortable with, but know that glitchless alternating horizontal/vertical scrolling with a status bar is possible even without switching mirroring.
So, which mapper would you suggest?
The new game will probably not fit into NROM since I need to switch graphics in certain locations and there will be a lot of data, like the map definition and text dialogs.
The actual game logic will probably be relatively small, i.e. if the game had only 10 screens and a small set of monsters, it would fit into NROM (if I'm really able to do a glitch-less vertical scrolling with a status bar).
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Simple vertical scrolling with status bar

Post by tokumaru »

DRW wrote:
tokumaru wrote:

Code: Select all

wait:
  bit $2002
  bvc wait
What does this code do? And how do I differentiate whether I want to check for sprite 0 or another flag, like the nine sprites overflow flag?
BIT does the same as AND but without storing the result in A (none of this is of use here), but it conveniently copies bit 7 of the memory value to the N flag and bit 6 to the V flag, so you can use BPL/BMI and BVC/BVS to test the vblank and sprite 0 flags, respectively. This doesn't help with the sprite overflow flag (which's returned in bit 5), but you can still make the loop faster when testing it by preloading the mask and checking the result of the AND operation:

Code: Select all

  lda #%00100000
wait:
  bit $2002
  beq wait
So, which mapper would you suggest?
I definitely wouldn't use the MMC1 just for this, since its register interface is maddening (1 bit at a time!) and the same result can be achieved without mirroring switching.

I always felt like the logical step up from NROM is UNROM, if you don't need fast CHR switching.
if I'm really able to do a glitch-less vertical scrolling with a status bar
I'll try to draw some images showing you how it could be done and post them in this thread later.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Simple vertical scrolling with status bar

Post by tokumaru »

tokumaru wrote:I'll try to draw some images showing you how it could be done and post them in this thread later.
Here are some images showing one way to do Zelda-like scrolling using vertical mirroring.

You start with this layout, that allows you to move freely from left to right:
scroll-1.png
scroll-1.png (1.25 KiB) Viewed 2715 times
Then, when the camera is aligned to a name table and you need to scroll up, you first draw the status bar on the other name table, but below where it was vertically, and then you scroll up:
scroll-2.png
scroll-2.png (1.13 KiB) Viewed 2715 times
You'll end up with this layout after having scrolled up:
scroll-3.png
scroll-3.png (1015 Bytes) Viewed 2715 times
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Simple vertical scrolling with status bar

Post by tokumaru »

You can again scroll freely sideways using this new layout:
scroll-4.png
scroll-4.png (1.28 KiB) Viewed 2710 times
To sum it up, every time you need to scroll vertically, you have to move the status bar in the opposite direction you're scrolling by it's height, and switch it to the opposite name table if it's in the same one where the scrolling is about to happen. The resulting layout will not be neatly aligned to the name tables anymore, but that's expected, since the amount you scroll is less than the full screen height.

If that alignment problem bothers you too much, one solution (similar to what's done in Mega Man, if I'm not mistaken) is to always draw at the status bar at the very top, and then draw the new screen on top of (or below) the current screen as you normally would, but also draw a copy of it that's correctly aligned to the status bar on the other name table, and when you're finished scrolling, switch over to the copy.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Simple vertical scrolling with status bar

Post by tokumaru »

Here's how the alternative works. Instead of moving the status bar down, you keep it at the top:
scroll-2a.png
scroll-2a.png (1.1 KiB) Viewed 2706 times
Then, when scrolling up, you draw the new screen right above the current one, but also on the hidden name table:
scroll-3a.png
scroll-3a.png (4.08 KiB) Viewed 2706 times
Then you finally switch to showing the other name table (which should look identical to the other side, so the switch isn't noticeable, but it's properly aligned to the name table and the status bar), and you become able to scroll left and right freely again:
scroll-4a.png
scroll-4a.png (970 Bytes) Viewed 2706 times
EDIT: In case it wasn't clear, the white border represents the scroll, and it's usually broken below the status bar and at the top of the gameplay window because of the scroll split.
User avatar
DRW
Posts: 2225
Joined: Sat Sep 07, 2013 2:59 pm

Re: Simple vertical scrolling with status bar

Post by DRW »

O.k., I'll have a closer look at your graphics when I extend my test program to actually load different screens during scrolling.

So, UNROM is much easier to program than MMC-1? O.k., this might be an argument then.

I did the CNROM chapter from the Nerdy Nights tutorial, but this one was nothing more than switching between two sets of graphics data.

Is there a similar explanation that teaches you how to do UNROM? I mean, I know how to do NES programming in general. But those technical data in
https://wiki.nesdev.com/w/index.php/UxROM
don't really help me yet. At first, I'd need a little textual step by step explanation that tells what UNROM can do and how to do it.
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Rahsennor
Posts: 479
Joined: Thu Aug 20, 2015 3:09 am

Re: Simple vertical scrolling with status bar

Post by Rahsennor »

There is a full tutorial with example code on the wiki.
User avatar
DRW
Posts: 2225
Joined: Sat Sep 07, 2013 2:59 pm

Re: Simple vertical scrolling with status bar

Post by DRW »

Alright, I'll check whether that explanation is sufficient for my understanding.
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
calima
Posts: 1745
Joined: Tue Oct 06, 2015 10:16 am

Re: Simple vertical scrolling with status bar

Post by calima »

Very useful pictures, thanks.
Post Reply