It is currently Thu May 23, 2019 2:23 pm

 All times are UTC - 7 hours

 Page 1 of 1 [ 12 posts ]
 Print view Previous topic | Next topic
Author Message
 Posted: Mon Dec 31, 2018 11:12 am

Joined: Tue Aug 28, 2018 8:54 am
Posts: 143
I'm working on 4 way scrolling right now. Working with X axis is nice and enjoyable experience, working with Y makes me cry in despair. I use 16-bit number for X where high 8-bit would be indicator of new nametable to load (or fill with \$00 tile if not found). For Y, scroll must wrap at 240 otherwise I will have teared picture. So what I do is use scroll x/y (copies in RAM, that put in PPU at NMI) as lower 8 bits. So my 16-bit Y coordinate has to wrap at 240 for low byte as well.

I don't have division (I know I can use loop and substract 240 until value left is less or equal to 239), and having high byte to be level number, makes it easier to work with.

Problem arises when I need to compare what is the difference between camera coordinates, and loaded coordinates. If I scroll Y-1 and loaded Y - camera Y == 8, I load new row. At certain point I will have loaded Y=\$XX00 and camera Y=\$XXE8, and I want to get 8 as the result. I am not going to show any code, as all of my attempt have bugs and I'm still not very comfortable with assembler.

Does any one knows good approach to deal with this, or is there better way to deal with Y coordinate?

Last edited by yaros on Mon Dec 31, 2018 12:00 pm, edited 1 time in total.

Top

 Posted: Mon Dec 31, 2018 11:38 am

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 7462
For scrolling, you probably don't need to scroll fast enough to need more than just an increment at a time, something like this:
Code:
low_y += scroll_rate_y
if low_y >= 240
low_y -= 240
high_y += 1

...and something similar for subtracting.

If you need to do a big jump, you can change that "if" into some sort of "while", perhaps, but what I'm getting at is that is that for moving short distances you don't need to do generic division. You can just accomplish division as a repeated subtraction, probably with more efficiency than a more generic division routine.

If you occasionally need to do a big jump, probably it won't matter on that particular frame if you take a few extra cycles. Even then, a subtraction loop is probably OK. How tall are your levels? 4 screens? 16 screens? 1000 screens? If the problem is small scale, a small scale solution may be sufficient.

For things that aren't scrolling but somehow need to be mapped to a screen, you can probably just keep the "real" coordinate of the camera position, and do a subtraction from that to find its position on the current screen. (Or at most they'll be one or two screens off, adjacently.)

Top

 Posted: Mon Dec 31, 2018 12:11 pm

Joined: Tue Aug 28, 2018 8:54 am
Posts: 143
I scroll 1px/frame and that is pretty much what I do and I will have at most 16x16 screens. Problem is not wrapping, but math after that. I want to know that we scrolled for 8 pixels, and 0-232!=8. But you are probably right, I should have an additional two variables to store scroll difference, and then use wrapped ones to find row to load.

I need to keep low byte wrapped, because if I have [0-255]:[0-240], high(scrollY) will be Y (+-1 for adjacent) for nametable (32 by 30) and low(scrollY)>>3 will be the row number to load from.

Top

 Posted: Mon Dec 31, 2018 12:38 pm

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 21397
Location: NE Indiana, USA (NTSC)
Keeping separate variables for "top of valid portion of tilemap in VRAM space" and "top of valid portion of tilemap in world space" can help. When you scroll, add 8 to or subtract 8 from both, and wrap world space mod 256 and VRAM space mod 240. Every time you update the tilemap or set the hardware scroll position, use additions and subtractions to convert world space to VRAM space.

_________________
Pin Eight | Twitter | GitHub | Patreon

Top

 Posted: Mon Dec 31, 2018 12:40 pm

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 7462
yaros wrote:
I need to keep low byte wrapped, because if I have [0-255]:[0-240], high(scrollY) will be Y (+-1 for adjacent) for nametable (32 by 30) and low(scrollY)>>3 will be the row number to load from.

What I was suggesting is to keep both at once for your camera. A "world" coordinate to subtract from everything else, and the wrapped "screen" coordinate for mapping to the screen.

Edit: tepples made the same suggestion while I was typing.

Top

 Posted: Mon Dec 31, 2018 12:59 pm

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11348
Location: Rio de Janeiro - Brazil
The trick is to not to use the same value to calculate the row number to load from and the one to write to. Instead, use 2 separate scroll values, one relative to the level map (wraps normally at 256), and the other relative to the name tables (wraps at 240). Just update them in sync (always add/subtract the same amounts to both) and handle the special wrapping accordingly.

When you need to do operations relative to the level map, such as loading rows/columns or keeping the camera from going past the edges of the level, use the scroll that's relative to the map. When you need to do operations relative to the screen, such as calculating the target NT address for rows/columns or setting the PPU scroll, use the scroll that's relative to the name tables.

During initialization, the separate scroll values don't even need to be aligned to each other in any particular way when it comes to map rows. That is, there's no need to divide the normal scroll by 240 to find the initial value of the special scroll to find out what it'd be both started at 0 and scrolled down. The row where the special scroll starts makes literally no difference, since both scrolls wrap around independently from each other. Only the "pixel" part of the scroll values must match. For example, say that your metatiles are 16x16 pixels, and the initial vertical scroll is 1317. The remainder of that by 16 is 5, so you can simply initialize the special scroll to 5, but everything would work just as well if it were 21, 37, 53, and so on (increments of 16 pixels).

EDIT: Basically what the guys above said.

Top

 Posted: Mon Dec 31, 2018 2:49 pm

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 21397
Location: NE Indiana, USA (NTSC)
The only restriction on the scroll position in VRAM space and world space is that their difference has to be a multiple of 16. Allowing them to become unaligned recently bit me.

_________________
Pin Eight | Twitter | GitHub | Patreon

Top

 Posted: Mon Dec 31, 2018 3:14 pm

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11348
Location: Rio de Janeiro - Brazil
Yeah, I may not have worded it very well when I said that "the pixel part must match", but that's what I meant. They need to be pixel aligned within the specific metatile they're in, but the NT space coordinate can point to any metatile row at the beginning.

Top

 Posted: Mon Dec 31, 2018 5:56 pm

Joined: Tue Aug 28, 2018 8:54 am
Posts: 143
Thank you all. I made it working now.

Edit: smaller gif

 Attachments: scroll2.gif [ 847 KiB | Viewed 2447 times ]
Top

 Posted: Tue Jan 01, 2019 2:13 am

Joined: Tue Oct 06, 2015 10:16 am
Posts: 938
I keep expecting the Skifree monster to show up in that gif, lol.

Top

 Posted: Wed Jan 02, 2019 8:28 am
 Formerly ~J-@D!~

Joined: Sun Mar 12, 2006 12:36 am
Posts: 488
Location: Rive nord de Montréal
Happy New Year everyone!
tepples wrote:
The only restriction on the scroll position in VRAM space and world space is that their difference has to be a multiple of 16. Allowing them to become unaligned recently bit me.

Care to say what kind of issue it causes? I'm curious, because I can't figure out the problem.

_________________
((λ (x) (x x)) (λ (x) (x x)))

Top

 Posted: Wed Jan 02, 2019 8:31 am

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 21397
Location: NE Indiana, USA (NTSC)
Once the lower 4 bits of the VRAM and world space coordinates become misaligned, the attribute grid no longer lines up, and parts of your background start having the wrong palette.

_________________
Pin Eight | Twitter | GitHub | Patreon

Top

 Display posts from previous: All posts1 day7 days2 weeks1 month3 months6 months1 year Sort by AuthorPost timeSubject AscendingDescending
 Page 1 of 1 [ 12 posts ]

 All times are UTC - 7 hours

#### Who is online

Users browsing this forum: No registered users and 8 guests

 You cannot post new topics in this forumYou cannot reply to topics in this forumYou cannot edit your posts in this forumYou cannot delete your posts in this forumYou cannot post attachments in this forum

Search for:
 Jump to:  Select a forum ------------------ NES / Famicom    NESdev    NESemdev    NES Graphics    NES Music    Homebrew Projects       2018 NESdev Competition       2017 NESdev Competition       2016 NESdev Competition       2014 NESdev Competition       2011 NESdev Competition    Newbie Help Center    NES Hardware and Flash Equipment       Reproduction    NESdev International       FCdev       NESdev China       NESdev Middle East Other    General Stuff    Membler Industries    Other Retro Dev       SNESdev       GBDev    Test Forum Site Issues    phpBB Issues    Web Issues    nesdevWiki