It is currently Sat Dec 16, 2017 2:17 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 30 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Mon Mar 23, 2015 12:38 pm 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 260
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 1648 times ]

NT1:
Attachment:
NT01.bmp
NT01.bmp [ 30.12 KiB | Viewed 1648 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 1648 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!


Top
 Profile  
 
PostPosted: Mon Mar 23, 2015 1:06 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5899
Location: Canada
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?


Top
 Profile  
 
PostPosted: Mon Mar 23, 2015 1:11 pm 
Offline
User avatar

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


Last edited by tokumaru on Mon Mar 23, 2015 1:12 pm, edited 1 time in total.

Top
 Profile  
 
PostPosted: Mon Mar 23, 2015 1:11 pm 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 260
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.


Top
 Profile  
 
PostPosted: Mon Mar 23, 2015 1:14 pm 
Offline
User avatar

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


Top
 Profile  
 
PostPosted: Mon Mar 23, 2015 3:28 pm 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 260
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.


Top
 Profile  
 
PostPosted: Mon Mar 23, 2015 4:30 pm 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1046
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.

_________________
https://kasumi.itch.io/indivisible


Top
 Profile  
 
PostPosted: Mon Mar 23, 2015 6:14 pm 
Offline
User avatar

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


Top
 Profile  
 
PostPosted: Mon Mar 23, 2015 6:31 pm 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19348
Location: NE Indiana, USA (NTSC)
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.


Top
 Profile  
 
PostPosted: Tue Mar 24, 2015 5:59 am 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 260
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.


Top
 Profile  
 
PostPosted: Tue Mar 24, 2015 11:02 am 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3192
Location: Mountain View, CA, USA
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.


Top
 Profile  
 
PostPosted: Tue Mar 24, 2015 11:18 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19348
Location: NE Indiana, USA (NTSC)
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?


Top
 Profile  
 
PostPosted: Tue Mar 24, 2015 11:43 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5899
Location: Canada
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.


Last edited by rainwarrior on Tue Mar 24, 2015 11:44 am, edited 1 time in total.

Top
 Profile  
 
PostPosted: Tue Mar 24, 2015 11:43 am 
Offline
User avatar

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


Top
 Profile  
 
PostPosted: Tue Mar 24, 2015 12:39 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5899
Location: Canada
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


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 30 posts ]  Go to page 1, 2  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: Yahoo [Bot] and 2 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