It is currently Mon Dec 11, 2017 2:44 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 51 posts ]  Go to page Previous  1, 2, 3, 4  Next
Author Message
PostPosted: Thu Jan 12, 2017 2:36 pm 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1509
Thanks for all the help.

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

Code:
; 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?

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
PostPosted: Thu Jan 12, 2017 2:54 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10164
Location: Rio de Janeiro - Brazil
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.


Top
 Profile  
 
PostPosted: Thu Jan 12, 2017 3:35 pm 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1509
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:
; 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:
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 730 times ]

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg
Top
 Profile  
 
PostPosted: Thu Jan 12, 2017 4:20 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10164
Location: Rio de Janeiro - Brazil
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.

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

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


Top
 Profile  
 
PostPosted: Thu Jan 12, 2017 4:29 pm 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1509
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.

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
PostPosted: Thu Jan 12, 2017 4:43 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10164
Location: Rio de Janeiro - Brazil
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:
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:
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).

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


Top
 Profile  
 
PostPosted: Thu Jan 12, 2017 4:55 pm 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1509
tokumaru wrote:
Code:
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).

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
PostPosted: Thu Jan 12, 2017 5:08 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10164
Location: Rio de Janeiro - Brazil
DRW wrote:
tokumaru wrote:
Code:
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:
  lda #%00100000
wait:
  bit $2002
  beq wait

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

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


Top
 Profile  
 
PostPosted: Thu Jan 12, 2017 8:45 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10164
Location: Rio de Janeiro - Brazil
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:
Attachment:
scroll-1.png
scroll-1.png [ 1.25 KiB | Viewed 688 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:
Attachment:
scroll-2.png
scroll-2.png [ 1.13 KiB | Viewed 688 times ]

You'll end up with this layout after having scrolled up:
Attachment:
scroll-3.png
scroll-3.png [ 1015 Bytes | Viewed 688 times ]


Top
 Profile  
 
PostPosted: Thu Jan 12, 2017 8:54 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10164
Location: Rio de Janeiro - Brazil
You can again scroll freely sideways using this new layout:
Attachment:
scroll-4.png
scroll-4.png [ 1.28 KiB | Viewed 683 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.


Top
 Profile  
 
PostPosted: Thu Jan 12, 2017 9:11 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10164
Location: Rio de Janeiro - Brazil
Here's how the alternative works. Instead of moving the status bar down, you keep it at the top:
Attachment:
scroll-2a.png
scroll-2a.png [ 1.1 KiB | Viewed 679 times ]

Then, when scrolling up, you draw the new screen right above the current one, but also on the hidden name table:
Attachment:
scroll-3a.png
scroll-3a.png [ 4.08 KiB | Viewed 679 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:
Attachment:
scroll-4a.png
scroll-4a.png [ 970 Bytes | Viewed 679 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.


Top
 Profile  
 
PostPosted: Fri Jan 13, 2017 2:13 am 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1509
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.

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
PostPosted: Fri Jan 13, 2017 4:00 am 
Offline

Joined: Thu Aug 20, 2015 3:09 am
Posts: 295
There is a full tutorial with example code on the wiki.


Top
 Profile  
 
PostPosted: Fri Jan 13, 2017 4:58 am 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1509
Alright, I'll check whether that explanation is sufficient for my understanding.

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
PostPosted: Fri Jan 13, 2017 5:14 am 
Offline

Joined: Tue Oct 06, 2015 10:16 am
Posts: 598
Very useful pictures, thanks.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 51 posts ]  Go to page Previous  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