It is currently Sun May 27, 2018 5:14 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 31 posts ]  Go to page Previous  1, 2, 3  Next
Author Message
PostPosted: Mon Jul 07, 2014 8:29 pm 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 285
The Fox - yup. I landed on that one...thanks for the suggestion. I managed to figure that out through trial and error.

Also, Memblers and Tokumaru - the solution was in a combination of what you two said. I WAS doing the write to the PPU register correct to read from $1000, but that was never being seen because the NMI was never being reached! I did a shadow register variable (though I'm sure I did it 'wrong'...even still it was enough for me to find the issue in the logic). I did this early in the code (which is redundant, I know)...

Code:
   
   LDA #%10010000
   STA v2000
   STA $2000


Which ENABLED the NMI, and then made sure to write that value in the NMI as well. And voila - everything looked the way that i expected it to.

MAN that was a long day to do something so small! Haha. I feel like I've learned quite a bit of some of those quirky things that logic wouldn't dictate, though, so it's been worth it.

Do me a favor - if you get the chance could one of you go into a little more detail (maybe with a simple code example) of how you use the shadow register for $2000 and $2001? I get the general concept, but my brain is fried on this right now.

You all rock, thanks so much for helping and pointing me in the right direction(s)!


Top
 Profile  
 
PostPosted: Mon Jul 07, 2014 10:18 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6294
Location: Canada
One of the reasons I wrote this example was to demonstrate proper use of the PPU. It might be helpful. There are a lot of quirky issues to do with how to time turning the renderer on and off, and the steps to take when doing it, etc. It took me about two years of working with the NES to come to a point where I could write NES render code that doesn't have any strange 1-frame glitches somewhere.


Top
 Profile  
 
PostPosted: Mon Jul 07, 2014 11:10 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10484
Location: Rio de Janeiro - Brazil
JoeGtake2 wrote:
could one of you go into a little more detail (maybe with a simple code example) of how you use the shadow register for $2000 and $2001?

The shadow registers are just buffers... They aren't absolutely necessary, and should be the least of your problems for now. The only real advantage of buffering $2000/$2001 writes is that, since you're saving new $2000/$2001 values to RAM and only copying the values to the actual registers during VBlank, you don't get weird visual changes mid-screen, like a quick "shake" when rendering is turned on. All changes will happen during VBlank and will be invisible to the player. There's really no reason for you to worry about this now, since those small glitches are really quick and not permanent... once you have a better knowledge of the flow of an NES program, this will come naturally to you.

The problem you faced was that you tried to buffer the very write that enables NMIs, which prevented NMIs from ever happening, so the shadow registers were never copied to the actual registers. The $2000 write that enables NMIs shouldn't be buffered (but you should do this write during VBlank). I believe most programs don't constantly enable and disable NMIs, in most cases NMIs are turned on from the very beginning, and a variable, which is checked by the NMI handler, is used to indicate what kind of task it's supposed to perform, if any.

In my programs, I enable NMIs right after waiting the 2 PPU warm up frames, while I know the PPU is still in VBlank. The variable that controls what tasks will be done in the NMI should already be initialized by this time, as should the shadow registers if you are indeed copying them to the real registers in the NMI handler.

EDIT: I just remembered that SMB1 is one game that keeps enabling/disabling NMIs, and this results in visual glitches from time to time.


Top
 Profile  
 
PostPosted: Tue Jul 08, 2014 4:47 am 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 285
Wow - Rainwarrior, that example has a ton of great stuff in it! Thanks so much for that, I'm really going to dissect it. Most of it makes perfect sense to me as I read it sort of casually...which is good! Haha.

Tokumaru, thanks for clarifying.

All in all, this problem is officially solved thanks to your guys' help. More importantly, I have some more foundational knowledge which I didn't have before and some more things to directly study! Much appreciated.


Top
 Profile  
 
PostPosted: Tue Jul 08, 2014 6:38 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6294
Location: Canada
In the project I'm working on, I set up the first screen, and then I enable NMI and never need to turn it off. Between screen transitions, I turn off rendering, but the NMI routine continues playing music, for example. As long as the NMI routine itself can be prevented from messing with $2005/2006/2007 while rendering is off, it's perfectly valid to have it running while you're updating the hidden screen.

Another thing: it's good practice to only ever upload palette data in the NMI handler. If done at other times, the writes to the palette will manifest visually as a change of background colour and you'll get a scanline or two of "rainbow" glitch. I found this very counter-intuitive, but it's sort of because the background colour is still active when rendering is off. Pointing the PPU address at any of the palette bytes will cause it to display the colour at that address, for some reason.

Similarly, but for a different reason, don't use the OAM DMA outside of the NMI routine either. Most emulators don't simulate this, but when sprite rendering is off the data in the OAM begins to decay after a short time (30 scanlines or so), so there's not much point to update the OAM while rendering is off, as the data would likely be corrupt by the time you get rendering back on.


Top
 Profile  
 
PostPosted: Tue Jul 08, 2014 6:59 am 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 285
Thanks Rainwarrior - later today, I'm going to attempt to iterate through a basic file structure and try to build it from scratch and referencing your link as little as possible. If I have pressing questions while I do, would you mind if I PM you? I appreciate the help and the lengthy explanation. You're the third person on this forum to be simply awesome about that! 100% of people helping, 0% of people impatiently trolling. I can't stress enough how amazing I find that.


Top
 Profile  
 
PostPosted: Tue Jul 08, 2014 9:03 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6294
Location: Canada
Sure, PM me if you'd like, no need to ask. It's usually better to ask questions in the open, though. You'll get better answers if more people see the question, and also other people with the same problem may come by to read this thread.


Top
 Profile  
 
PostPosted: Tue Jul 08, 2014 10:27 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 20072
Location: NE Indiana, USA (NTSC)
That and if you post in public, then anyone else can use Google or Bing to search for the question and end up here, where (as you've noticed) we don't bite newcomers.


Top
 Profile  
 
PostPosted: Tue Jul 08, 2014 12:48 pm 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1145
rainwarrior wrote:
It's usually better to ask questions in the open, though. You'll get better answers if more people see the question, and also other people with the same problem may come by to read this thread.

This. Also because if the person you PM doesn't know the answer, you're out of luck until you get another set of eyes on the problem anyway.

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


Top
 Profile  
 
PostPosted: Tue Jul 08, 2014 2:42 pm 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 285
Gotcha - will absolutely do that, then!

So last night, we fixed the issue I was having. Today, I completely rewrote my code (to better align with what I'm understanding of a good file structure, and commenting things out to better help me understand what is happening).

Rainwarrior, you suggested putting palette updates in the NMI, and I noticed that your example file also put nametable updates in the NMI. I thought I would also try this. I got funky results, for sure. If I put the nametable update in the NMI, everything is off as far as the scroll, however I HAVE to reset the scroll data in the NMI for it to not be wonky (explained in the code).

Anyhow, this code works as it is. Hoorah. Before moving forward, I wanted you guys to take a glance and see if the file structure looks alright or if I'm heading for a cliff anywhere, and also help me figure out why the scroll data must be in the NMI like this for it to work correctly.

I feel like this post alone has taught me a lot, and hopefully it shows in this updated code / is evident in the comments.

Code:
;;AN ATTEMPT TO DEVELOP A GOOD FILE STRUCTURE
;;TO BE ASSEMBLED WITH ASM6.

;; ======1. HEADER - taken care of in header file

;; ======2. Establish variables and constants.
   .enum $0000         ; variabl section must open with this on ASM6
   addrLo .dsb 1
   addrHi .dsb 2
   .ende            ; variable section must close with this on ASM6
;; ======3. Where should the code start?
   .org $c000

;; ======4. RESET

RESET:
   SEI               ; Ignore Interupts
   LDA #$00         ; Load zero.
   STX $2000         ; Store it to $2000 (PPU - graphics) - disable NMI
   STX $2001         ; Store it to $2001 (PPU - graphics) - disable rendering
   STX $4010         ; Store it to $4010 (DMC IRQ) - disable DMC IRQ
   STX $4015         ; Store it to $4015 (APU) - disable APU sound
   LDA #$40         ; Load 64
   STA $4017         ; Store it to $4017 - disables APU IRQ
   CLD               ; disable decimal mode
   LDX #$FF         ; Load 255
   TXS               ; Initialize the stack
   
;; ======5. First vblank wait
   bit $2002
vbwait1:   
   bit $2002
   BPL vbwait1
   
;; =====6. Clear all ram except stack ($100)
   LDA #$00         ; Load zero to A
   LDX #$00         ; Load zero to X
clrMem:
   STA $0000,x
   STA $0200,x
   STA $0300,x
   STA $0400,x
   STA $0500,x
   STA $0600,x
   STA $0700,x         ; if you want to clear on reset
   INX
   BNE clrMem
   
;; =====7. Second vblank wait
vbwait2:
   bit $2002
   BPL vbwait2
   
;; =====8. Enable NMI for graphics updates
   LDA #%10010000      ; turn on NMI, set sprites to $0000/bkg to $1000
   STA $2000         ; write it to $2000
                     ; load palette data
   

;; =====9. Load background/ nametable
                  ;; load background
                  ;; this does not work
                  ;; inside NMI.  Find out why(?)
   LDA #$00
   STA $2001
   LDA $2002
   LDA #$20
   STA $2006
   LDA #$00
   STA $2006
                  ;; load nametable
   LDA #<nametable0
   STA addrLo
   LDA #>nametable0
   STA addrHi
   jsr LoadBkg         ;; jump to LoadBkg, return below
                  ;;IF I RESET SCROLL DATA HERE
                  ;;SCROLL IS WRONG!
                  ;; Only if I put the Scroll data
                  ;;in NMI does it work.
                  ;; End of Background code that does
                  ;; not work in NMI.
                  

   JMP main
   
;; =====10. Set up NMI - much to do with this section.
NMI:
   PHA               ; push accumulator values to the stack.
   TXA
   PHA
   TYA
   PHA
                  ; set up PPU
   LDA #%10010000
   STA $2000
   LDA #%00011110
   STA $2001

                  ;; set oal dma (?) - look into this
   LDA #$00
   STA $2003
   LDA #$02
   STA $4014

                  ;; load palettes
   LDA $2002
   LDA #$3F
   STA $2006
   LDA #$00
   STA $2006
   LDX #$00
LoadPal:
   LDA PaletteData,x
   STA $2007
   INX
   CPX #$20
   BNE LoadPal
   
                  ;;SCROLL DATA ONLY SEEMS TO GIVE
                  ;;PREDICTABLE OUTCOME IF PUT
                  ;;HERE.  PUTTING IT AFTER THE
                  ;;RETURN FROM LoadBkg SUB ROUTINE
                  ;;ABOVE gives strange results,
                  ;;as does making it the end of
                  ;;the sub routine...
                  
                  ;;but putting the nametable stuff
                  ;;here gives wonky results as well...
                  
   LDA $2000
   LDA #$00         ;; load zero to accumulator
   STA $2006         ;;
   STA $2006
   STA $2005         ;reset scroll
   STA $2005         ;reset scroll

                  ; re-pull accumulator values from the stack.
   PLA
   TAY
   PLA
   TAX
   PLA
   
   RTI               ; Return from whence you came.

;; ===== 11. Main Loop   
main:
   
   JMP main

;; ===== 12. Sub routines
   
LoadBkg:
   LDX #$04
   LDY #$00
   
LoadNametableLoop:
   LDA #$00
   STA $2001
   LDA (addrLo),y
   STA $2007
   INY
   BNE LoadNametableLoop
   inc addrHi
   DEX
   BNE LoadNametableLoop
   LDA #$00
   STA $2005
   LDA #%00011110
   STA $2001
   
   RTS               ;return to setting up nametables.
   
;; ===== 13. Load binaries
PaletteData:
   .incbin "charPal.pal"
   .incbin "bkgPal.pal"
   
nametable0:
   .incbin "mainBkg.nam"
   
;; ===== 14. Vectors
   .org $fffa
   .dw NMI
   .dw RESET
   .dw 0
   
;; ===== 15. load Chr
   .incbin "main.chr"
   


Top
 Profile  
 
PostPosted: Tue Jul 08, 2014 4:21 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6294
Location: Canada
Whenever you write to $2006 or $2007, you overwrite whatever is currently in the scroll registers, because $2005 and $2006 are accessing the same internal registers, just in different ways.

This is why you must reset scroll (write $2000, $2005, $2005) after you are finished making any updates via $2007.

It is not necessary to write 0 to both $2006 and $2005. This is redundant, as the writes go to the same place.

See my example at "@scroll:" and note that this step that sets the scroll registers is always done after all the other PPU updates. It is critical that it happen after other PPU updates (i.e. $2006/2007 writes) and before the end of vblank, where the hardware will pick up the last scroll values written.

If you want to know a lot more about it, there is detail here: http://wiki.nesdev.com/w/index.php/The_skinny_on_NES_scrolling


Top
 Profile  
 
PostPosted: Tue Jul 08, 2014 4:34 pm 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1145
Be careful.
Code:
RESET:
   SEI               ; Ignore Interupts
   LDA #$00         ; Load zero.
   STX $2000         ; Store it to $2000 (PPU - graphics) - disable NMI
   STX $2001         ; Store it to $2001 (PPU - graphics) - disable rendering
   STX $4010         ; Store it to $4010 (DMC IRQ) - disable DMC IRQ
   STX $4015         ; Store it to $4015 (APU) - disable APU sound

You're storing X which has an undefined value at this point. Replace those STXs with STA so you're storing zero and not whatever was in X before you reset the game.

Don't mess with the scroll ($2005) outside of the NMI until you really know what you're doing.
Code:
BNE LoadNametableLoop
   LDA #$00
   STA $2005
   LDA #%00011110
   STA $2001


What's more $2005 (and $2006) are double-write registers. Typically if you stored the same value to the same location (like... say RAM) twice in a row, it wouldn't be different than doing it once. This is not true for double write registers, where writing to them twice in a row will make the same value do different things. There's no real way to know which write will affect which thing unless you reset them, which you do by reading $2002.

Code:
lda #$00
sta $2005;This will affect either the X or Y scroll value.
lda #$20
sta $2005;This will affect which scroll value that the previous write DIDN'T. (If the first write changed X, this would change Y. If the first write changed Y, this would change X.)


So with the above code alone, you might end up with an X scroll value of $00 and a Y scroll value of $20, or you might end up with an X scroll value of $20 and a Y scroll value of $00. To avoid this Schrodinger's code, read $2002 first and always write to $2006/$2005 twice.

Code:
bit $2002;(This reads from $2002 without changing A. If you don't care if A is changed, LDA will work too)
lda #$00
sta $2005;This will set the X scroll to $00.
lda #$20
sta $2005;This will set the Y scroll to $20


You are doing this properly with your $2006 code in your NMI:
Code:
LDA $2002
   LDA #$3F
   STA $2006
   LDA #$00
   STA $2006

You read $2002 which makes sure the first write will set the high byte of the address. Another interesting thing is that $2005 and $2006 share the same "toggle". So if after the above code, you wrote a value to $2005, it would reliably write the X scroll. You would not need to read $2002 again to ensure this.

Therefore, this would be fine:

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

   lda (whatevervalue you need for $2000)
   sta $2000
   lda #$00
   STA $2005         ;X scroll is zero
   STA $2005         ;Y scroll is zero

As stated by rainwarrior, $2005/$2000 need to be rewritten after any writes to $2006/$2007 are made, and they need to be done after those writes are complete.

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


Top
 Profile  
 
PostPosted: Tue Jul 08, 2014 6:05 pm 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 285
Thank you very much. I have played with these ideas from the posts, but the result is exactly the same, unfortunately.

Let me show you how I have essentially put the Nametable Load into the NMI and made the adjustments you all suggested: The scroll is still off. Now, all of this 'problem' is contained to the NMI - no more jump to a sub routine or anything.

Any other thoughts?

**NOTE: Still, with the new adjustments, if I load the nametable atthe end of the second vblank, use the subroutine, and do all the updates to $2000 and $2005 in the NMI, everything still works as expected, but when put into the NMI as shown below, there is still a strange scroll issue...**

Code:

NMI:
   PHA               ; push accumulator values to the stack.
   TXA
   PHA
   TYA
   PHA

                  ;; set oal dma (?) - look into this
   LDA #$00
   STA $2003
   LDA #$02
   STA $4014

                  ;; load palettes
   LDA $2002
   LDA #$3F
   STA $2006
   LDA #$00
   STA $2006
   LDX #$00
LoadPal:
   LDA PaletteData,x
   STA $2007
   INX
   CPX #$20
   BNE LoadPal
            ;; end load palette - load background

;;I HAVE ALSO TRIED PLACING A WRITE TO
;;$2000 and $2005 here thinking maybe
;;literally after every write to $2007
;;this would need to written to
;; but this did not help.

   LDA $2002
   LDA #$20
   STA $2006
   LDA #$00
   STA $2006
;;;;;; BOTH ABOVE AND BELOW HAVE
;;;;;; SAME RESULTS.
   ;LDA #$00
   ;STA $2001
   ;LDA $2002
   ;LDA #$20
   ;STA $2006
   ;LDA #$00
   ;STA $2006

                  ;; load nametable
   LDA #<nametable0
   STA addrLo
   LDA #>nametable0
   STA addrHi
   
LoadBkg:
   LDX #$04
   LDY #$00
   
LoadNametableLoop:
   LDA #$00
   STA $2001
   LDA (addrLo),y
   STA $2007
   INY
   BNE LoadNametableLoop
   inc addrHi
   DEX
   BNE LoadNametableLoop
               ;; end load nametable
   
   LDA #%10010000   ;; after a write to $2007 like above
   STA $2000      ;; must reanable          
   LDA #%00011110   ;;write to $2000
   STA $2001            
   LDA #$00      ;; And set the
   STA $2005      ;; x scroll data
   STA $2005      ;; and y scroll data
   
                  ; re-pull accumulator values from the stack.
   PLA
   TAY
   PLA
   TAX
   PLA
   
   RTI               ; Return from whence you came.


Top
 Profile  
 
PostPosted: Tue Jul 08, 2014 6:25 pm 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1145
That'd be because (unlike the code in your last post) you're trying to update the entire background in NMI. (In the code in your post before the one you're asking about, you did it below reset, before your main loop, and outside your NMI which is correct.)

I said in a PM a while ago that you only have a very limited time to update the background after vblank happens, and if you write to $2006/$2007 after this period (and rendering is enabled), the game will crash. You seem to disable rendering before you write the backgrounds, which means it won't crash... BUT the code that updates the nametables is still taking up actual time.

The scroll registers ($2005/$2000) need to be set before this window of time is over, or they'll only be set to the right values for the scanlines (a horizontal row of pixels) that haven't already been rendered. That is that $2005 is SAFE to write outside vblank time even when rendering is enabled, but it will only affect a portion of the screen UNLESS it is done during vblank time. (Which is why the Kirby's Adventure HUD never moves. The scroll for those scanlines is set after the rest are rendered with a different scroll value.)

Edit: As well, writes to $2006/$2007 still affect the scroll even if done while rendering is disabled.

There is only enough time to update about two rows and two columns of the screen in the tiny period you have between when rendering begins and your NMI. This is how games scroll without disabling rendering. They update only the new rows/columns that have been scrolled in while keeping rendering enabled. If you want to draw an entire new screen, you must disable rendering and draw it all then. You cannot do this every frame like you're trying to do without all kinds of weird stuff happening. Once the background is drawn, you only have to draw what's changed anyway, because the rest of it will stay on the PPU's memory map.

Background updates are a tricky thing about NES dev.

Edit: To fix it, move loadBKG back where it was. The rest of the post explains why that needs to happen.

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


Top
 Profile  
 
PostPosted: Tue Jul 08, 2014 6:40 pm 
Offline

Joined: Tue Jul 01, 2014 4:02 pm
Posts: 285
Ah, thanks Kasumi! In context now, that makes more sense (from previous PM).

I shall trudge ever forward. Thanks all for the assistance!


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

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 6 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