Trying to swap entire nametable

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

Post Reply
casprog
Posts: 70
Joined: Fri Oct 28, 2016 12:37 pm

Trying to swap entire nametable

Post by casprog »

Hi all

I'm able to initialize and render my first nametable just fine (start screen), but am having trouble trying to load a second nametable upon starting the game (play screen).

Here is what I'm doing:

1. During NMI I check for the start button press during my initial game state. If this happens I set a change state flag.
2. During my 'Forever' loop, outside of NMI, I check if the change flag is set, and if so, I update my game state then disable NMI:

Code: Select all

LDA #%00000000
STA $2000
3. I call a subroutine to repopulate my nametable. The code to populate is the same as my initial population code of the nametable, with the only difference being my pointer points to a new nametable dataset. (planning on making this subroutine reusable later by making what the pointer points to dynamic, for testing I'm just hardcoding it).

Code: Select all

LoadPlayNametable:
    LDA $2002             ; read PPU status to reset the high/low latch
    LDA #$20
    STA $2006             ; write the high byte of $2000 address
    LDA #$00
    STA $2006             ; write the low byte of $2000 address

    LDA #LOW(PlayNametable)
    STA pointerLow
    LDA #HIGH(PlayNametable)
    STA pointerHigh

    LDX #$00
    LDY #$00

LoadPlayNametableLoop:
        LDA [pointerLow], y
        STA $2007
        INY
        CPY #$00
        BNE LoadPlayNametableLoop
        INC pointerHigh
        INX
        CPX #$04
        BNE LoadPlayNametableLoop

    RTS
4. Upon return of this subroutine I re-enable NMI and sprites (not using sprites at the moment)

Code: Select all

LDA #%10010000
STA $2000
I can tell the logic is triggering, but my screen looks like the starting nametable just got mixed around. I see one tile from nametable 2, and I still see tiles from nametable 1.

I've been doing some reading on here and have learned something like this should not happen during NMI, and NMI should be off, so curious why this fails to work.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Trying to swap entire nametable

Post by tokumaru »

Apparently you're not disabling rendering before writing the new data to the PPU. You absolutely can't access VRAM while the PPU is rendering, if you try you'll get visual glitches and corrupted VRAM.

This is why we can only do visual updates (sprite DMA, pallette updates, small NT changes, etc.) during vblank, a time during which the PPU is not rendering images. For big updates, such as a whole NT, the vblank period isn't enough, so you have to completely disable background and sprite rendering (using register $2001) in order to have free access to VRAM. When you're done transferring the data, you can turn rendering back on.
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: Trying to swap entire nametable

Post by lidnariq »

tokumaru wrote:so you have to completely disable background and sprite rendering (using register $2001) in order to have free access to VRAM. When you're done transferring the data, you can turn rendering back on.
Or, schedule the updates over multiple vblanks. You may find yourself wanting a PPU update system anyway.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Trying to swap entire nametable

Post by tokumaru »

If you ever plan on implementing scrolling, background animations and the like, then yeah, it makes sense to start thinking of a flexible VRAM update system that makes use of the vblank time. If you prefer to start simple, turning rendering off between screens is conceptually simpler, and even people who have more advanced VRAM management may still prefer to turn rendering off during screen transitions and have unlimited VRAM access in order to simplify and speed up larger updates, that may need to replace not only name tables, but also pattern tables.
casprog
Posts: 70
Joined: Fri Oct 28, 2016 12:37 pm

Re: Trying to swap entire nametable

Post by casprog »

That was the missing piece! Turning off the bits I had in $2001, so close. Screen switch works perfectly now, thanks guys!
sdm
Posts: 412
Joined: Tue Apr 11, 2006 4:08 am
Location: Poland

Re: Trying to swap entire nametable

Post by sdm »

Is the write to $ 2001 (disable / enable screen) must be done only during NMI (VBLANK)? Because I guess I'm wrongly turning off / on the screen when I change the nametable. I would like to make sure on this. For example:

Code: Select all

LoadLevel:

	JSR DisablePPU              ;outside NMI/VBLANK
	JSR LoadSomeData
	JSR LoadSprites
	JSR LoadPalettes
	JSR LoadNametable
	JSR EnablePPU            ;outside NMI/VBLANK

	RTS

;------------------------------------

EnablePPU:              ;outside NMI/VBLANK
	LDA #%10010000
	STA $2000
	LDA #%00011110
	STA $2001
	RTS

;------------------------------------

DisablePPU:              ;outside NMI/VBLANK
	LDA #%00000000
	STA $2000
	LDA #%00000000
	STA $2001
	RTS
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: Trying to swap entire nametable

Post by lidnariq »

Best to only toggle it during NMI, yes.

Turning off rendering at the wrong time during rendering can cause sprites to be wrong during the next frame in which rendering is enabled again.

Turning on rendering during the would-be-active portion will most likely cause the contents of the screen to be wrong for the remainder of that frame.
sdm
Posts: 412
Joined: Tue Apr 11, 2006 4:08 am
Location: Poland

Re: Trying to swap entire nametable

Post by sdm »

So I always wrongly did it.

I have a problem modifying this code so that its meaning stays the same, but the "DisablePPU" and "EnablePPU" codes were executed during VBLANK. So the LoadLevel code stays out of the NMI, but the Disable / EnablePPU code is executed correctly.



Code: Select all

NMI:

 lda <PPU0
 sta PPUCTRL
 lda <PPU1
 sta PPUMASK

RTI

;------------------------------------

EnablePPU:
	LDA #%10010000
	STA  <PPU0
	LDA #%00011110
	STA  <PPU1
	RTS

;------------------------------------

DisablePPU:
	LDA #%00000000
	STA  <PPU0
	LDA #%00000000
	STA  <PPU1
	RTS
Will it be ok?
Post Reply