Some basic scrolling issues

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Some basic scrolling issues
by on (#143615)
Fortunately, the game is not heavily dependent on scrolling, but after toying with it and it stumping me, I'd still like to figure it out. I adapted some of the Nerdy Nights tutorials to a simple engine I slammed together for testing, and I do have scrolling 'working', but very wonkily.

Here are my two nametables:
NT0:
Attachment:
NT00.bmp
NT00.bmp [ 30.12 KiB | Viewed 2974 times ]

NT1:
Attachment:
NT01.bmp
NT01.bmp [ 30.12 KiB | Viewed 2974 times ]


But when I actually work them in code, the second, procedurally loaded nametable is drawn 'sideways' (tiles seem correct, but position is flipped and rotated...evidenced especially by the fact only 240 across are drawn). I have not yet worked on attributes so not worried about that issue yet, but this should demonstrate what I mean...see tile orientation here compared to the previous attachment:

scrolled to NT1:
Attachment:
NT2.png
NT2.png [ 1.67 KiB | Viewed 2974 times ]


Here is the current code, from the NMI forward:

Code:


NMI:
   ;first push whatever is in the accumulator to the stack
   PHA
   TXA
   PHA
   TYA
   PHA
   
NewColumnCheck:
   LDA hScroll
   AND #%00000111
   BNE NewColumnCheckDone

   JSR DrawNewColumn
   
   
NewColumnCheckDone:
   
   LDA #$00
   STA $2003
   LDA #$02
   STA $4014
   
   LDA #$00
   STA $2006
   STA $2006
   
   LDA hScroll
   STA $2005   ;reset scroll values to zero
   LDA vScroll
   STA $2005   ;reset scroll values to zero
   
   JSR GamePadCheck
   
   LDA #%10010000
   ORA nametable
   STA $2000
   LDA #%00011110
   STA $2001
   
   PLA
   TAY
   PLA
   TAX
   PLA
   ;; keep constant frame rate:
   DEC vBlankTimer
   ;;return from this interrupt
   
   RTI
   

StartGame:

   LDA $2002
   LDA #$3F
   STA $2006
   LDA #$00
   STA $2006
   LDX #$00
LoadPaletteLoop:
   LDA PaletteData,x
   STA $2007
   INX
   CPX #$20
   BNE LoadPaletteLoop

   LDA #$00
   STA hScroll
   STA vScroll
   LDA #$00
   STA newScreen
   LDA #$00
   STA nametable
   LDA #$00
   STA columnNumber
   LDA #$00
   STA $2000
   STA $2001
   JSR LoadNametable

   LDA #$10 ;; what row of pattern table to load
   LDX #$00
   LDY #$00 ;; what column
   JSR LoadChrRam
   
MainGameLoop:
   

   bit $2002
vBlankWait:
   bit $2002
   BPL vBlankWait
   

   LDA gamepad
   AND #PAD_R
   BEQ dontScrollRight

   INC hScroll
   LDA hScroll
   AND #%00000111
   BNE doneScroll
   INC columnNumber
   
   jmp doneScroll

   
dontScrollRight:

   
doneScroll:

   
   JMP MainGameLoop
   
   
DrawNewColumn:
   LDA hScroll
   LSR A
   LSR A
   LSR A
   STA columnLo
   
   LDA #$24
   STA columnHi

   LDA columnNumber
   ASL A
   ASL A
   ASL A
   ASL A
   ASL A
   STA sourceLo
   LDA columnNumber
   LSR A
   LSR A
   LSR A
   STA sourceHi
   
   LDA sourceLo
   CLC
   ADC #<Room1
   STA sourceLo
   LDA sourceHi
   ADC #>Room1
   STA sourceHi
   
DrawColumn:
   LDA #%0000100
   STA $2000
   LDA $2002
   LDA columnHi
   STA $2006
   LDA columnLo
   STA $2006
   LDX #$1E
   LDY #$00
DrawColumnLoop:
   LDA (sourceLo),y
   STA $2007
   INY
   DEX
   BNE DrawColumnLoop

   RTS
   
GamePadCheck:
   ;strobe the gamepad
   LDA #$01
   STA $4016
   LDA #$00
   STA $4016
   LDX #$08
ReadControllerBytesLoop:
   PHA
   LDA $4016
   AND #%00000011
   CMP #%00000001
   PLA
   ROR
   DEX
   BNE ReadControllerBytesLoop
   STA gamepad
   RTS
   
   
LoadNametable:
   LDA #$00
   STA $2001
   LDA $2002   
   LDA #$20
   STA $2006
   LDA #$00
   STA $2006
   LDA newScreen   
      
   TAX
   LDA NTPointerLo,x
   STA ntRam
   LDA NTPointerHi,x
   STA ntRam+1
   LDX #$04
   LDY #$00
LoadNametableLoop:
   LDA #$00
   STA $2001
   LDA (ntRam),y
   STA $2007
   INY
   BNE LoadNametableLoop
   INC ntRam+1
   DEX
   BNE LoadNametableLoop
   
   RTS
   
   

LoadRam:
   .include "LoadRamRoutine.asm"

CHRTileCount:
   .db #$50
CHRAddressLo:
   .db <bckTiles
CHRAddressHi:
   .db >bckTiles

bckTiles:
   .incbin "HudTiles.chr"
   

NTPointerLo:
   .db <Room0, <Room1
NTPointerHi:
   .db >Room0, >Room1
   
   
Room0:
   .incbin "MapNametables\NT00.nam"
Room1:
   .incbin "MapNametables\NT01.nam"
   





Any thoughts? Thanks!
Re: Some basic scrolling issues
by on (#143616)
Probably has to do with how you set bit 2 of $2000: $2000 / PPUCTRL

If this bit is 0, writing $2007 increments the address by 1 after each write (i.e. fill horizontally), but if this bit is 1 it will increment by 32 (fill vertically).

Edit: or if that's working the way you expect, I guess take a look at the way you're storing the columns to be copied?
Re: Some basic scrolling issues
by on (#143617)
Have you configured the PPU to increment the VRAM address by 32 (i.e column mode) instead of 1? That could explain the "rotated playfield"...

EDIT: Ninja'd.
Re: Some basic scrolling issues
by on (#143618)
Thanks - that was actually my first instinct. I tried changing this:

Code:
DrawColumn:
   LDA #%0000100
   STA $2000


To this:

Code:
DrawColumn:
   LDA #%0000000
   STA $2000


...prior to even posting, but that gives me just one 'row' of data, rather than one column...i'll have to play more with that part of it.
Re: Some basic scrolling issues
by on (#143620)
Do you want to draw in columns? If yes, you'll have to store (or at least read) the name table data in columns as well.
Re: Some basic scrolling issues
by on (#143627)
Hm - yeah, I had considered that might be where the issue was, but dismissed it. I tried this:

Code:
DrawColumnLoop:
    LDA (sourceLo),y
    STA $2007
    TYA             ; Changed
    CLC             ; changed
    ADC #$20   ; changed
    TAY             ; changed
    DEX             ; changed
    BNE DrawColumnLoop



I thought this would give me essentially 'columns', but just gave me wacky results - nothing that resembled the actual nametable. I'll have to take a closer look - just seeing if you guys saw something obvious I was missing (besides only having 7 bits in one spot, but that change was negligible to the outcome).

Thanks.
Re: Some basic scrolling issues
by on (#143631)
JoeGtake2 wrote:
I thought this would give me essentially 'columns', but just gave me wacky results - nothing that resembled the actual nametable.

sourceLo and sourceHi are 2 bytes. 16bits. Indirect,y effectively adds y to this address and lets you read from the resulting address. Y can be anything from 0 to 255, so this means you only have a range of 256 bytes from that address. 256/32 = 8. 8 rows. A screen is 30 rows tall. If you want to read further than that you have to change the source address.

Right now when you add 32 to y, it doesn't take into account that when Y goes above 256 and wraps, the high byte of the pointer needs to be incremented. So I imagine its just drawing the first 8 rows 4 times.

It's also possible your NMI is taking too long, which would result in even stranger behavior than that. I would get the tiles you want to draw outside the NMI, so that the loop in the NMI is as simple as possible.

Code:
 lda nmibuffer,y
 sta $2007
 iny
 dex
 bne DrawLoop

or, write the buffer backwards outside your nmi so it can be as simple as this:
Code:
 lda nmibuffer,y
 sta $2007
 dey
 bpl DrawLoop


There's also a lot of calculation as far as getting the address to write to which could be done outside the NMI. I'd actually be surprised if your current code is too slow, but definitely time it. Never forget that writes to $2007/$2006 are only safe for a small amount of time after the NMI begins.
Re: Some basic scrolling issues
by on (#143638)
I took a quick look at your code and have a few comments:

The most important thing was mentioned by Kasumi: there's a lot of logic and address calculation in your column drawing routine. VBlank time is really short, and you shouldn't waste it calculating stuff that could have been calculated beforehand. VRAM updates during VBlank should be all about the actual data transfers, not about setting up the transfers. This might not be a problem yet, but you should always make sure you're not extrapolating the VBlank time.

Second, there's no need to reset $2006 to 0. The writes to $2000 and $2005 are enough to fully set the scroll. $2006 should only be used for scrolling purposes if you need to change the vertical scroll mid-frame, which almost never is the case. Writing 0s to $2006 was something that desperate programmers started to disseminate because they didn't understand how scrolling worked. If these writes do affect the scrolling in any way, you're not using $2000 and $2005 correctly.

Third, put the controller reading after all PPU operations. The controllers can be read after the end of VBlank just fine, but the $2000 and $2001 writes you have there must happen before VBlank ends or you might get visual glitches. Always do the PPU-related tasks first, then you worry about controllers, music and whatever else that goes in the NMI handler.
Re: Some basic scrolling issues
by on (#143640)
tokumaru wrote:
Writing 0s to $2006 was something that desperate programmers started to disseminate because they didn't understand how scrolling worked.

It's called cargo cult programming (Wikipedia, citing The New Hacker's Dictionary). I wonder how many homebrewers got into this habit after reading recommendations found in a particular old doc.

Quote:
Always do the PPU-related tasks first, then you worry about controllers, music and whatever else that goes in the NMI handler.

In fact, I wouldn't read controllers in the NMI handler unless you're doing all game logic in the NMI handler. If press event detection (finding buttons pressed now that weren't pressed a frame ago) is in NMI, and the game starts to lag, it might miss button presses. If you do press event detection in an interrupt handler, such as if you're using a specialized controller that's sensitive to timing, make sure to remember press events until your main thread consumes them.
Re: Some basic scrolling issues
by on (#143658)
Hey guys - thanks for the tips. This was just a slammed together tester file to futz around with scrolling, so there probably are some quick and dirty (read: lazy, wonky) things in here, but also that info is very valuable. And yeah, Tepples - I'd say that some of this is Cargo Cult programming...generally my first attempts at things are, but I tinker with it and continue to research *working* things I don't understand until I develop a stronger understanding...it's pretty much how I've learned every programming language, and ASM (and its NES particulars) has been the same way. You guys are a continuing HUGE help with that, for sure!

Thanks for having a look, everyone...I really appreciate the feedback.
Re: Some basic scrolling issues
by on (#143675)
tepples wrote:
tokumaru wrote:
Writing 0s to $2006 was something that desperate programmers started to disseminate because they didn't understand how scrolling worked.

It's called cargo cult programming (Wikipedia, citing The New Hacker's Dictionary). I wonder how many homebrewers got into this habit after reading recommendations found in a particular old doc.

Both of you: please stop with the FUD. There are commercial games which do this (ex. Final Fantasy 2), so it is not a "homebrew thing". Whether or not it's "correct behaviour" is up to the programmer. Yes we know more about how the PPU works now than we did then, but it doesn't change the fact that this isn't a "homebrew thing". It may be more prolific there, but it isn't limited to there in the least.

Furthermore, you and I just had a conversation 1-2 weeks ago about code I was working on where you explicitly told me "You can just lda #$00 / sta $2006 / sta $2006 -- there's no need for the preceding lda #$3f / sta $2006 / lda #$00 / sta $2006" (which is Square's routine, not mine). For me to "make this work with $2000/2005" would have been painful mainly because of how $2000 is multi-purpose (the game does track what it sticks there, but it doesn't do a very good job).

Also, I noticed someone totally revamped the area of the NESdev wiki that explained this -- specifically the work I did to try and make it clear, giving real-world examples for people to understand -- turning it into incredibly hard to follow text. So apparently my attempts to improve the misunderstandings have been trumped once again. Therefore: until you guys write up a clear, concise, easy-to-follow explanation with real code (and not just some dumped crap into a <pre> block) with step-by-step explanations, you're going to continue to see this.
Re: Some basic scrolling issues
by on (#143677)
koitsu wrote:
tepples wrote:
tokumaru wrote:
Writing 0s to $2006 was something that desperate programmers started to disseminate because they didn't understand how scrolling worked.

It's called cargo cult programming (Wikipedia, citing The New Hacker's Dictionary). I wonder how many homebrewers got into this habit after reading recommendations found in a particular old doc.

Both of you: please stop with the FUD. There are commercial games which do this (ex. Final Fantasy 2), so it is not a "homebrew thing".

I'm aware of that. From the doc I linked: "Licensed programmers didn't have perfect docs either."

Quote:
Furthermore, you and I just had a conversation 1-2 weeks ago about code I was working on where you explicitly told me "You can just lda #$00 / sta $2006 / sta $2006 -- there's no need for the preceding lda #$3f / sta $2006 / lda #$00 / sta $2006" (which is Square's routine, not mine).

In that conversation, I was trying to preserve existing behavior. Other parts of the program might have called that subroutine before filling CHR RAM, for example.

Quote:
For me to "make this work with $2000/2005" would have been painful mainly because of how $2000 is multi-purpose (the game does track what it sticks there, but it doesn't do a very good job).

Perhaps I was unclear that patching commercial games and creating original programs from scratch are two separate scenarios.

Quote:
Also, I noticed someone totally revamped the area of the NESdev wiki that explained this -- specifically the work I did to try and make it clear, giving real-world examples for people to understand -- turning it into incredibly hard to follow text.

I'd like to try fixing this. Can you get me a diff of the edits that caused the problem?
Re: Some basic scrolling issues
by on (#143682)
Are you referring to this edit? The skinny on NES scrolling revision 5249

I removed that particular diagram because its pattern of writes can't set the fine-Y offset properly, and as such I thought it was inappropriate to use an example. I don't know if it's a real world example, but it's got a bug in it that would manifest if you were trying to do vertical scrolling with it.

The other diagram was kept. It's a very good example, I think.
Re: Some basic scrolling issues
by on (#143683)
koitsu wrote:
There are commercial games which do this (ex. Final Fantasy 2), so it is not a "homebrew thing".

I don't think anyone said it was a homebrew thing. Commercial games have all sorts of crap in them, professional game developers of the 80's weren't gods that knew everything about the systems they worked with.

If I tell you not to write to $2000 at the end of a scanline to avoid glitchy lines will you tell me to fuck off just because SMB did it?

Luckly, writing 0's to $2006 doesn't have any bad side effects, it's just superfluous if you do set the scroll as the designers of the PPU intended you to do.

However, it does cause a lot of confusion leading newcomers to believe that $2006 must be used in order to manipulate the scroll, which leads them to "The Skinny on NES Scrolling" and they end up thinking that scrolling is a 7-headed monster while it's actually quite simple (if you're not doing raster effects, which most newcomers aren't).

Also, suggesting newbies to write 0's to $2006 to fix their misaligned static screen games is plain poor advice, because they lose the ideal opportunity to learn how to make proper use of $2000/$2005, which will be necessary once they get involved with scrolling, and $2006 won't be of any help (it will actually get in the way).

Quote:
Whether or not it's "correct behaviour" is up to the programmer.

Unless the programmer is a newbie that doesn't know what he's doing, and is just doing what someone else told them to. There are legitimate uses to setting the scroll through $2006, but that's an advanced topic that shouldn't be frivolously suggested to beginners as the normal thing to do. I guess it's easier to say "Your screen is misaligned, huh? Write 0 to $2006 twice." as a quick fix than to actually explain how scrolling works.

Quote:
Furthermore, you and I just had a conversation 1-2 weeks ago about code I was working on where you explicitly told me "You can just lda #$00 / sta $2006 / sta $2006 -- there's no need for the preceding lda #$3f / sta $2006 / lda #$00 / sta $2006" (which is Square's routine, not mine). For me to "make this work with $2000/2005" would have been painful mainly because of how $2000 is multi-purpose (the game does track what it sticks there, but it doesn't do a very good job).

Hacking is a whole 'nother can of worms, you have to play along with what you've got. You can't take it upon yourself the godly task of fixing all the bad programming practices in the world, but you can make sure that your own work, which you have full control over, is as good as possible.

Quote:
Therefore: until you guys write up a clear, concise, easy-to-follow explanation with real code (and not just some dumped crap into a <pre> block) with step-by-step explanations, you're going to continue to see this.

You mean explaining how to use $2000/$2005 for scrolling? I thought this was documented to death, but I'll check the documentation and see if I have anything to add.
Re: Some basic scrolling issues
by on (#143687)
Quote:
Therefore: until you guys write up a clear, concise, easy-to-follow explanation with real code (and not just some dumped crap into a <pre> block) with step-by-step explanations, you're going to continue to see this.

I would hope this first example is easy to follow, but what do you suggest instead?
The skinny on NES scrolling: Single_scroll
Re: Some basic scrolling issues
by on (#143688)
In the middle of work right now (my work flow tends to vary, so sometimes I'm around actively and then I'll disappear for hours due to god knows what I have to work at my day job), but here are the responses:

@rainwarrior -- that is a part of the commit in question, yes, but not entirely. However it would take me probably an hour or so to go through the 50+ commits to that document made since January 8th 00:21 UTC. Note: this isn't questioning the validity of the information, it's that it's presented in such a way that is no longer easy for someone to understand. Maybe having both the text explanation and actual real-world examples would be best. I feel strongly that having examples that clearly outline what bits get changed where/how along with correlating 6502 code is beneficial universally. But I don't want it to end up looking like this (nightmare fuel).

JoeGtake2's situation/question here is almost literally what I went through nearly 20 years ago. If people are still asking these types of questions ~20 years later, then it is obvious that we (the royal "we") have not done a good job making it clear/concise to upcoming developers. I think this aspect of the PPU, hands down, is the most common stumbling block there is, and is understood fully by an elite few (I'd guess maybe 15 people). It's kinda like how proper implementation of 6502 ADC/SBC (re: two's complement) is the common stumbling block when doing 6502 emulation.

@tokumaru -- no they weren't gods (I'm left with the impression Nintendo Co. Ltd's own documentation given to commercial companies did not describe the existence of the PPU internal register at all), and my comment about it being a "homebrew thing" was mainly directed at tepples not you. Sorry about that.

And no, I wouldn't tell you to sod off in that case at all. :-) It's just that $2000/2005/2006 are actually very tricky for a multitude of reasons. It's all highly circumstantial (i.e. what the condition/scenario is greatly dictates which registers you should be touching, and more importantly, when they should be touched, and in what order).

We do have the "PPU scrolling" wiki page which I think helps a lot because it provides a visual demonstration of what's going on, but again we have a "wall of text" (helpful yes, practical not always). That page, combined with The frame and NMIs, and PPU rendering, and The skinny on NES scrolling page, are indeed what make this a 7-headed hydra. Respectfully: can you deny that all of that does not make it overwhelming and ridiculous for someone new (or even experienced) to the console? As I said above, it's a recurring sore subject for homebrewers and romhackers alike, and that means something.

And yes, it is easier to say "just write $00 to $2006 twice" if that solves/rectifies the problem for people. I learned the same thing with writing $00 to $2005 for certain situations as well. (Note: that is purely on me, i.e. a habitual thing). In fact, some of this was even solicited here on the forum -- I'll have to find the post, but at some point in a thread (discussing the skinny on NES scrolling) Disch flat out said something to the effect of "if you just want to play it safe no matter what, do this".

For example even in my revamped FF2e intro -- which does not involve screen scrolling at all -- there are some $00/$00 writes to $2005 and $00/$00 writes to $2006 which are absolutely needed else there are screen glitches (sometimes hard to notice unless you have a good eye, other times very noticeable). I can discuss these in another thread if you're curious. What we (programmers who don't have a grasp of how this actually works) end up doing is throwing in some code until things are fixed, simply because we do not understand the 7-headed hydra. We would like to understand this hydra, but the hydra is big, bulky, not clearly documented for folks who don't understand it, with few well-documented code examples alongside it. This is why it remains a hydra.

Edit: Here's further verification. ;-)

bunnyboy said something somewhat recently (on IRC I think), that there basically isn't enough training material/howtos for people that help with understanding stuff (he wasn't speaking about this specific matter, it was more of a general point, but I think it applies here greatly) and I'm in agreement. The "nerdy nights" tutorial is an example of something a lot of people latch on to because it's all that's out there -- and that information is a lot different than just "big Wiki dumps". Maybe I'd be a good candidate for such that material as well, because this is still a subject even I don't have a good grasp of ~20 years later.
Re: Some basic scrolling issues
by on (#143694)
When I was starting out I tried writing 0s to $2006 as well, but I remember why I ended up doing this:
1. I had a working regular setup with $2000, and writing 0s to $2005.
2. I added nametable updates to my NMI after the $2005 writes, not realizing the shared register situation.
3. Realizing that $2006 conflicted with the scroll somehow, I tried writing 0s to it in an attempt to "reset" it.
4. This looked fine until I needed to do some real scrolling, at which point I quickly realized I had more learning to do.

This was in 2010, though. The wiki was smaller, and didn't even have the detailed loopy info; that information was still trapped in loopy's document, which obviously a lot of people have a hard time with. My problem was just that I hadn't figured out where to look for the information I needed yet.

At present the skinny wiki article is a lot more readable, and more detailed than loopy's original document. It's a huge improvement and has several good-practice examples. The diagram you made of a proper X/Y split technique was fantastic, though it needed some accompanying explanation (which has since been added). I think we're doing a ton better, here, than when I started out.

People still do blanket writes to $2006 for many reasons. A big part of it is that it's just a natural thing to try when you realize $2006 is causing you a problem, and it seems to work alright at first (if you're not actually trying to scroll). It doesn't matter how well we document it, it's just something people are likely to try anyway. The other part of it is how much old and bad information is out there. You can't just look at one person making a mistake and blame it on the current state of the Wiki. It probably has nothing to do with the wiki (unless they are actually telling us it was due to misinformation in the wiki).


Why, exactly, do you keep bringing up Final Fantasy 2? If it's some weird edge case, it's definitely not the one we should offer as an example to beginners. We should be offering examples that are easy to understand, and robust. Hacks that just barely work, or weirdo advanced stuff should be relegated to footnotes and links to forum discussions.

Similarly, I don't think forum posts from 2009 shed much light on the current state of things. Yes of course people find loopy's document difficult. That's why the wiki article happened.
Re: Some basic scrolling issues
by on (#143697)
rainwarrior wrote:
Why, exactly, do you keep bringing up Final Fantasy 2? If it's some weird edge case, it's definitely not the one we should offer as an example to beginners.

The same sequence is found in Super Mario Bros., in the subroutine that doppelganger's disassembly calls UpdateScreen:
Code:
  lda #$3F
  sta $2006
  lda #$00
  sta $2006
  sta $2006
  sta $2006

This appears to operate on the (misguided) assumption that the CGRAM address and the VRAM address are separate pointers. This is true of Nintendo's later hardware but not of the NES.

Perhaps it's a conflict between what beginners are likely to encounter when developing original software from scratch and what they are likely to encounter when hacking a commercial game.

In any case, I want to help hack this hydra.
Re: Some basic scrolling issues
by on (#143702)
There's no reason to acknowledge silly stuff like that on the wiki. All games have weird or redundant code buried in them somewhere. Just because it's in a popular game doesn't mean it's a worthy example. Even the disassembly you're borrowing that from has a comment that points out how weird it is. It's just a benign code-tumour.
Re: Some basic scrolling issues
by on (#143703)
After looking at it again, I revised and restored koitsu's other diagram. I deleted it before because the way it was presented at the time made it look like an example of what to do, rather than an example of how the registers worked. (It was confusingly right next to the $2006/2005/2005/2006 diagram which was both kinds of example at once.) I've moved it into a "Summary" section under "Stuff that affects register contents" (which needs a rename, btw), where I think its presentation is a bit more clear.
http://wiki.nesdev.com/w/index.php/The_skinny_on_NES_scrolling#Summary
Re: Some basic scrolling issues
by on (#143708)
rainwarrior wrote:
After looking at it again, I revised and restored koitsu's other diagram. I deleted it before because the way it was presented at the time made it look like an example of what to do, rather than an example of how the registers worked. (It was confusingly right next to the $2006/2005/2005/2006 diagram which was both kinds of example at once.) I've moved it into a "Summary" section under "Stuff that affects register contents" (which needs a rename, btw), where I think its presentation is a bit more clear.
http://wiki.nesdev.com/w/index.php/The_skinny_on_NES_scrolling#Summary
Re: Some basic scrolling issues
by on (#143713)
koitsu wrote:
I'm left with the impression Nintendo Co. Ltd's own documentation given to commercial companies did not describe the existence of the PPU internal register at all

Yeah, it does look like the official documentation was pretty superficial. That actually seems to be the norm, and it's understandable that they wouldn't want to overwhelm developers with hardware details.

Quote:
And no, I wouldn't tell you to sod off in that case at all.

You know, I had to look up what "FUD" and "sod" meant, not being a native english speaker. Sorry if the expression "fuck off" was too much, I didn't give much thought to it when I wrote my last post.

Quote:
It's just that $2000/2005/2006 are actually very tricky for a multitude of reasons.

My opinion is that yes, it's tricky, IF you involve $2006. Ideally, IMO, the basic information about scrolling should say that $2000 selects a name table, and $2005 an offset within that name table, and shouldn't even include $2006, besides saying you shouldn't mess with $2006 after setting the scroll. At first, $2006 should only be presented as the address register used for writing/reading data to/from PPU memory, and its relationship with the scroll should be an advanced topic for a later time.

Quote:
Respectfully: can you deny that all of that does not make it overwhelming and ridiculous for someone new (or even experienced) to the console?

I completely agree with you, which is exactly why I propose leaving $2006 out of the basic scrolling information. Nobody should be reading "The Skinny on NES Scrolling" when they're starting out, and it baffles me that people keep suggesting that read when newbies ask about scrolling. I've seen even newbies that clearly don't understand it suggesting it to other newbies...!

It took me years to understand the original Skinny document, but before I did I was fully capable of making the screen scroll using $2000 and $2005. Finally understanding how scrolling really worked was incredibly enlightening, but we shouldn't expect newbies to get that right from the beginning.

Quote:
And yes, it is easier to say "just write $00 to $2006 twice" if that solves/rectifies the problem for people.

Ideally we'd change that advice to "just clear the lower 2 bits of $2000 and write $00 to $2005 twice"... it's a little bit longer, but at least it's using the registers that were meant to be used for scrolling.

Quote:
I learned the same thing with writing $00 to $2005 for certain situations as well.

At least numbers written to $2005 have a clear meaning that even newbies can understand (0 to 255 pixels), but the soup of bits that is $2006 will hardly make sense to anyone that doesn't fully understand how the PPU renders its graphics with values other than 0.

Quote:
Disch flat out said something to the effect of "if you just want to play it safe no matter what, do this".

Yes, I remember that, it happens from time to time. For the record, I'm against this kind of advice, I'm completely against "covering all fronts" (when it comes to software you're writing and have full control of). This is my personal preference, others might have different philosophies, but this kind of thing only serves to mask your own mistakes, making bugs harder to find. Your $2000/$2005 logic SHOULD have no mistakes, if it does, you shouldn't be HIDING the errors with $2006 writes, but instead making sure they are as EVIDENT as possible so you can DETECT and FIX them.

This is the exact same reason why I think clearing all RAM is evil. "Hey, my first level plays great, but once the second one starts the screen is all garbled...", "Have you initialized all variables necessary for the level to run?", "No, I relied on a bunch of them containing zeroes...", "Duh!"... My point here is that a game is not a linear program, like a PHP script that runs from top to bottom generating a webpage, it's a "living" thing composed by different parts (title screens, cut scenes, menus, gameplay, bonus levels, etc.) that run multiple times, in different orders under different circumstances, and if you expect them to work properly EVERY TIME, under EVERY circumstance, after whatever module finished running before, you have to fucking initialize ALL the relevant variables. Always. And if you are indeed initializing everything, a full RAM cleanup is completely unnecessary. All those zeros will be replaced with actual information, even if it's another 0. Being completely unnecessary, its presence only serves one purpose: hiding your own mistakes. Programming mistakes should be evident, so that they can be found and dealt with.

Sorry about the rant.

Quote:
For example even in my revamped FF2e intro -- which does not involve screen scrolling at all -- there are some $00/$00 writes to $2005 and $00/$00 writes to $2006 which are absolutely needed else there are screen glitches (sometimes hard to notice unless you have a good eye, other times very noticeable). I can discuss these in another thread if you're curious.

Like I said before, a hack is a hack. You can't have full control over someone else's code. In this case you'll often have to fix things afterwards, because you can't mess with the whole structure of the program.

Quote:
What we (programmers who don't have a grasp of how this actually works) end up doing is throwing in some code until things are fixed, simply because we do not understand the 7-headed hydra. We would like to understand this hydra, but the hydra is big, bulky, not clearly documented for folks who don't understand it, with few well-documented code examples alongside it. This is why it remains a hydra.

Get $2006 and mid-screen scroll changes out of the picture and it's not a hydra anymore.

Quote:

As far back as I can remember, I advocated the promotion of $2006 to an advanced topic, while $2000 and $2005 remained the basic scrolling registers.

Quote:
bunnyboy said something somewhat recently (on IRC I think), that there basically isn't enough training material/howtos for people that help with understanding stuff (he wasn't speaking about this specific matter, it was more of a general point, but I think it applies here greatly) and I'm in agreement.

Well, it does take years to understand the complicated stuff, it would be naive to think that a newbie could learn everything in a few weeks. Most people don't even stick around long enough to get there.

A complicated subject will remain complicated no matter how well it's documented or explained. I don't think that making "the Skinny" more accessible is the key to end all the confusion about scrolling. It will certainly help, but only after the programmer has mastered the basic knowledge that will allow them to make sense out of the complicated part.

That being said, I believe that the more complicated subjects should be split into different levels of difficulty. The introduction to scrolling should be all about $2000 and $2005, with no mention of temporary internal registers or any of that crap (which is probably how the official documentation was). Most newbies would be thrilled to find out that the information contained in this introduction is enough to make Super Mario Bros. Once they felt the need to implement something more complex, they'd naturally move on to the advanced documents, and would be better equipped to understand that new information.
Re: Some basic scrolling issues
by on (#143715)
I did some editing of the wiki today, trying to consolidate the various bits of information into just one article: PPU scrolling

Hopefully this is easier to find and follow. The simple/common case is front-loaded with a big attractive animated GIF highlighting it. Nobody needs to refer to "the skinny" anymore, it's just part of the plain PPU scrolling page.
Re: Some basic scrolling issues
by on (#143718)
I'm not entirely sure the merger was a good idea. Tell them "Read 'The common case' and ignore the rest" and reverse psychology will compel them to read the rest, only for them to become frustrated when they don't understand it.
Re: Some basic scrolling issues
by on (#143719)
Hey guys, Don't read the common case, only pay attention to the rest.
Re: Some basic scrolling issues
by on (#143721)
You don't have to tell anybody to ignore it. If you want to tell them about the common case, just tell them about it, and let them keep reading if they want.

I think the topic is graspable. It seems like a new emulator author manages to figure it out every week or so. Maybe if we can shake off the weird reputation of loopy's "skinny" document from this information, new homebrew developers will be more willing to approach it?
Re: Some basic scrolling issues
by on (#143728)
I know a lot of folk chiming in are vets here, where as I am still very much learning the ins-and-outs. I figured I'd offer some perspective here, in case it was of interest to those of you creating documentation (and I am creating documentation as I am going too...more like the layman's guide to NES programming or NES programming for dummies sort of thing - silly stacked next to the mountain of work you guys have done, but documentation none-the-less...sharing my 'aha' moments, so to speak).

The resources available are extensive, thorough, and surprisingly pretty easy to follow. You guys have done an incredible job. The problem lies more with the actual medium itself - for instance. I'm playing around with (as per suggestion) a RAM buffer so that I can do the math outside of the NMI and just use the streamlined data in the RAM buffer to write to the PPU when the flag is appropriately ticked. I get the concept. The logic on the documentation is perfectly sound and easy to understand. The *problem*, though, is inconsistencies in other parts of the code mucking up the works. For instance, pulling from the wrong address to populate the RAM buffer will lead to problematic results...at which time, I have no indicator which is the problem, a wrongly coded buffer, the populator, or something before or after that.

Like with the scrolling issue that began this thread - the Nerdy Nights tutorial was great. I was able to follow it, then adapt it to my needs. However, when it got to processing the actual nametable data, funky things happened and I wasn't sure why. My nametable data is being loaded differently than the tutorial's yielding wildly different results than expected...and as I work to tech that particular problem, I still find myself spinning around trying to figure out exactly which chunk is the culprit and how to rectify it. It's like there's no primer...the nametable load works, the basic scrolling works, the combination does not, and the problem could be in the NMI, it could be in the nametable load, it could be in the scrolling, it could be incongruency between them...so now, let's just say I came to the assumption that, either way, I should write the data to a buffer first to streamline the NMI. I program it soundly, but I still get bizarre results...now it's just another variable to add to that mix of potential problems.

I'm just venting the frustration about things you guys already understand, have no doubt gone through yourselves, and are a result of me still being only semi-literate in ASM/NES-speak. I'm still very early on in the process of 'earning my nes developer wings' for sure so feel free to completely disregard this message. It has no target at all...just getting it off my chest after staring at the same few lines of code for two days. I think this is the exact reason newcomers like me avoid or abandon more complex scrolling mechanics...someone here called it a Hydra. That's an apt way to put it. It's not that the concepts are, in and of themselves, all that difficult. It's much more that there's no visual feedback to determine when one proverbial head has been conquered, and there are so many components to it that have to work with congruency.

*done rant...thanks for humoring it*
Re: Some basic scrolling issues
by on (#143730)
JoeGtake2 wrote:
I'm playing around with (as per suggestion) a RAM buffer so that I can do the math outside of the NMI and just use the streamlined data in the RAM buffer to write to the PPU when the flag is appropriately ticked. [But]pulling from the wrong address to populate the RAM buffer will lead to problematic results...at which time, I have no indicator which is the problem, a wrongly coded buffer, the populator, or something before or after that.

For that, I tend to use the debugger in FCEUX for Windows. It lets me watch the populator run one instruction at a time. This came in handy when troubleshooting problems with the half dozen different data formats used in the RAM buffer of RHDE.

  1. Have your assembler output a listing or map file, which tells you the address where the populator starts.
  2. Open FCEUX's debugger and set a breakpoint on execution of that address.
  3. Your program will stop at the populator, with a disassembly on the left side of the screen.
  4. Click Step Into to see registers after each instruction, Step Out to run to the end of a subroutine, or Step Over to combine the two.
  5. Open the Hex Editor to see your RAM buffer.
  6. By the time your populator completes, if the RAM buffer contents do not match what you expect, there was a problem.
  7. To get the program working again, you can double-click to toggle it between enabled and disabled.

If your x86 PC runs something other than Windows, FCEUX for Windows runs mostly correctly in Wine.
Re: Some basic scrolling issues
by on (#143732)
JoeGtake2, debugging would indeed be the best option to avoid the frustration you're feeling. When we're coding, we usually emulate the CPU in our heads at a very rudimentary level, so it's common to make mistakes during this stage. Debugging allows us to see whether the CPU is actually doing what we expect it to.

Analyze the whole process from the beginning and you won't have to guess where the problem is. At each step you can see if the proper output is being generated, if it isn't, focus on finishing this step before moving on to the next.
Re: Some basic scrolling issues
by on (#143735)
Yeah, I started the routines from scratch and really tried to work it out logically; based everything around my existing nametable load routine. I'm sure it's terribly ugly, but it works (horizontally, at least). Now, onto the terrors of attributes...haha.

Should I post the solution for dissection? I'm sure it's less than pretty.