ASM6 and trouble with nametables...

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

JoeGtake2
Posts: 333
Joined: Tue Jul 01, 2014 4:02 pm

Re: ASM6 and trouble with nametables...

Post by JoeGtake2 »

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: Select all

	
	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)!
User avatar
rainwarrior
Posts: 8734
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: ASM6 and trouble with nametables...

Post by rainwarrior »

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

Re: ASM6 and trouble with nametables...

Post by tokumaru »

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

Re: ASM6 and trouble with nametables...

Post by JoeGtake2 »

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.
User avatar
rainwarrior
Posts: 8734
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: ASM6 and trouble with nametables...

Post by rainwarrior »

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

Re: ASM6 and trouble with nametables...

Post by JoeGtake2 »

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.
User avatar
rainwarrior
Posts: 8734
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: ASM6 and trouble with nametables...

Post by rainwarrior »

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

Re: ASM6 and trouble with nametables...

Post by tepples »

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

Re: ASM6 and trouble with nametables...

Post by Kasumi »

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

Re: ASM6 and trouble with nametables...

Post by JoeGtake2 »

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: Select all

;;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"
	
User avatar
rainwarrior
Posts: 8734
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: ASM6 and trouble with nametables...

Post by rainwarrior »

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_ ... _scrolling
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Re: ASM6 and trouble with nametables...

Post by Kasumi »

Be careful.

Code: Select all

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: Select all

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: Select all

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: Select all

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: Select all

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: Select all

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

Re: ASM6 and trouble with nametables...

Post by JoeGtake2 »

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: Select all


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

Re: ASM6 and trouble with nametables...

Post by Kasumi »

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

Re: ASM6 and trouble with nametables...

Post by JoeGtake2 »

Ah, thanks Kasumi! In context now, that makes more sense (from previous PM).

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