Changes to background tiles showing on all screens

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
User avatar
The Rook
Posts: 52
Joined: Mon Jul 13, 2015 8:04 am

Changes to background tiles showing on all screens

Post by The Rook » Fri Oct 16, 2020 7:52 pm

I am working on a game that has multiple screens: title, options, credits, and the main game screen.

I while on the main game screen I make changes to nametable tiles and attributes (palette ids). These changes are made using a vram buffer and set_vram_update(). When I come back from the main game screen to the title screen those changes are still there. Going back to the title screen I use vram_unrle() to uncompress the nametable for the title screen.

Why are the changes from the game screen still showing?

Each screen is saved using NES Screen Tool by doing Nametable->Save as C header

I change screens with the following code:

Code: Select all

void change_screens(unsigned char new_screen)
{
  fade_out(5);

  current_screen = new_screen;

  ppu_off();
  
  draw_screen();
	
  ppu_on_all();

  fade_in(5);
}

void draw_screen()
{
  vram_adr(NAMETABLE_A);
	
  switch (current_screen)
  {
    case SCREEN_TITLE: vram_unrle(title_screen_v2); break;
    case SCREEN_OPTIONS: vram_unrle(options_screen); break;
    case SCREEN_CREDITS_1: vram_unrle(credits_screen_01); break;
    case SCREEN_CREDITS_2: vram_unrle(credits_screen_02); break;
    case SCREEN_GAME: vram_unrle(game_screen); break;
    default: 
      current_screen = SCREEN_TITLE;
      vram_unrle(title_screen_v2);
      break;
  }
}
01_game_screen.jpg
The colored circles are the tiles that were changed.
02_title_screen.jpg
Here they are showing up on the title screen after exiting the main game screen.

User avatar
Quietust
Posts: 1623
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: Changes to background tiles showing on all screens

Post by Quietust » Fri Oct 16, 2020 8:11 pm

Does your VRAM update code clear the buffer after it's done writing the updates?
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.

User avatar
The Rook
Posts: 52
Joined: Mon Jul 13, 2015 8:04 am

Re: Changes to background tiles showing on all screens

Post by The Rook » Fri Oct 16, 2020 8:21 pm

Quietust wrote:
Fri Oct 16, 2020 8:11 pm
Does your VRAM update code clear the buffer after it's done writing the updates?
I make the changes to the buffer (vram_buffer) and then call set_vram_update(vram_buffer). This is currently triggered by controller input.

And then the changes are made when ppu_wait_nmi() is called in the main loop.

Code: Select all

  // Main loop.
  while (1)
  {
    // Clear all sprites from sprite buffer.
    oam_clear();

    // Check for input.
    check_input();

    // Draw sprites.
    draw_sprites();

    // Wait till beginning of the frame.
    ppu_wait_nmi();

    // Update the frame count.
    update_frame_count();
  }
So, I guess the answer is no? I'm not explicitly clearing anything. I'm just letting ppu_wait_nmi() handle the updates.

User avatar
The Rook
Posts: 52
Joined: Mon Jul 13, 2015 8:04 am

Re: Changes to background tiles showing on all screens

Post by The Rook » Fri Oct 16, 2020 8:33 pm

Looks like I need to call:

Code: Select all

flush_vram_update()
I can't call it instead of set_vram_update(). That causes the changes to get written immediately which causes the screen to flash.

What is a good approach to calling flush_vram_update()?

After ppu_wait_nmi() in my main loop?

Maybe I should move the vram_buffer variable out of the function and make it global. Then check if it needs flushed after calling ppu_wait_nmi() in the main loop.

Any recommendations?
Last edited by The Rook on Sun Oct 18, 2020 9:06 pm, edited 1 time in total.

User avatar
The Rook
Posts: 52
Joined: Mon Jul 13, 2015 8:04 am

Re: Changes to background tiles showing on all screens

Post by The Rook » Fri Oct 16, 2020 10:07 pm

I think I fixed it by calling flush_vram_update() with a buffer that just has a single NT_UPD_EOF.

I did notice though, that even though I only have 1 thing for the vram buffer, if I make the buffer of size 1 or 2, it craps out. It has to be 3 or more so I made it 4. I notice this when I call set_vram_update() too. The size of the buffer needs to be a few more than the number of commands being sent to it.

Code: Select all

void clear_vram()
{
  unsigned char vram_buffer[4];

  vram_buffer[0] = NT_UPD_EOF;

  flush_vram_update(vram_buffer);
}

void change_screens(unsigned char new_screen)
{
  fade_out(5);

  current_screen = new_screen;

  ppu_off();

  clear_vram();  

  draw_screen();

  ppu_on_all();

  fade_in(5);
}


User avatar
Quietust
Posts: 1623
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: Changes to background tiles showing on all screens

Post by Quietust » Sat Oct 17, 2020 7:26 am

I'm not familiar with any of the function names you're mentioning - are they part of some external library you're using? And if so, what does that library's documentation say about how they should be used?
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.

User avatar
The Rook
Posts: 52
Joined: Mon Jul 13, 2015 8:04 am

Re: Changes to background tiles showing on all screens

Post by The Rook » Sun Oct 18, 2020 9:03 pm

Quietust wrote:
Sat Oct 17, 2020 7:26 am
I'm not familiar with any of the function names you're mentioning - are they part of some external library you're using? And if so, what does that library's documentation say about how they should be used?
set_vram_update() and flush_vram_update() are from neslib

User avatar
Quietust
Posts: 1623
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: Changes to background tiles showing on all screens

Post by Quietust » Mon Oct 19, 2020 5:47 am

So I just took a look at neslib, and all I can say is that I don't know what the author was thinking:

1. Both "set_vram_update" and "flush_vram_update" permanently install a single VRAM update block - once it runs, the only way to make it stop is to call the function again with a NULL pointer.
2. The VRAM update won't actually take place until you call "ppu_wait_frame" or "ppu_wait_nmi", and as noted above, it will take place every time one of those two functions is called.
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.

dienben2020
Posts: 24
Joined: Wed Sep 30, 2020 2:19 pm

Re: Changes to background tiles showing on all screens

Post by dienben2020 » Mon Oct 19, 2020 9:43 am

Hi,

I'm not an expert of the neslib, which I learning, but as far as I understand it (https://nesdoug.com/2018/09/05/03-vram-buffer/):
Quietust wrote:
Mon Oct 19, 2020 5:47 am
1. Both "set_vram_update" and "flush_vram_update" permanently install a single VRAM update block - once it runs, the only way to make it stop is to call the function again with a NULL pointer.
It's exactly the way it is designed for: activate once...
Quietust wrote:
Mon Oct 19, 2020 5:47 am
2. The VRAM update won't actually take place until you call "ppu_wait_frame" or "ppu_wait_nmi", and as noted above, it will take place every time one of those two functions is called.
... and then update the buffer every ppu nmi

clear_vram_buffer(); must be reset every frame, in order to properly transfer the data


But I have the same 'issue': the background is not refreshed when I move from one screen to another.

Perhaps it's because it's slow to transfert the full background from the buffer to the PPU during the nmi, and so you load once and update only tiles every frame (no mention to the scrolling....)

But it's perhaps completly wrong....

Regards,

Ben

dienben2020
Posts: 24
Joined: Wed Sep 30, 2020 2:19 pm

Re: Changes to background tiles showing on all screens

Post by dienben2020 » Tue Oct 20, 2020 4:23 am

Hello,

Objectively, I don't fully understand how does it work.... so sorry. But, what I'm doing:

Code: Select all

ppu_off();
for (counter_1=0;counter_1<32;++counter_1)
	{
		for (counter_2=0;counter_2<30;++counter_2)
				{
					clear_vram_buffer(); // do each frame, and before putting anything in the buffer
					one_vram_buffer(128, NTADR_A(counter_1,counter_2));
					flush_vram_update_nmi();
				}	
	}
ppu_on_all();
Off course, do not use the 128 character, use a transparent one.... it's for testing purpose :D

Tested only in emulator for now. Will try tonight on 'real' hardware....

Regards,

Benoit

unregistered
Posts: 1098
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Re: Changes to background tiles showing on all screens

Post by unregistered » Tue Oct 20, 2020 8:54 am

Hmmm... hi The Rook,

It seems to me that your screen appearing weirdly, has to do with your turning rendering off, drawing a screen, and turning rendering on again. It seems your code does this over and over.

What I’ve learned, is that altering rendering, your ppu_off(or on), can be problematic. Problems, exactly like you are experiencing, can happen. A way to draw a screen without turning rendering off is called “double buffering”. To accomplish double buffering, draw a screen in sections, one section per vblank. That way rendering, after reset, never has to be altered for nametable writing. :)

Though, that seems like it would be rough with neslib bc you aren’t using assembly code... so cycle timing may be difficult. Vblank only lasts for a certain amount of cycles. Vblank happens between scanline 240 and scanline 260. Though, the entire scanline 240 is a waste bc the PPU doesn’t accept writes during scanline 240.

*Your game should fill a buffer with a section of screen outside of vblank, and then draw that section from the buffer while in vblank. Always drawing from the exact same section of memory reduces drawing code cycles, so a larger section of a screen can be drawn during our short vblank.


Obviously, drawing to PPU outside of vblank with rendering on, causes your screen display to become destroyed. But, a simple reset will start your game again... the screen destruction vanishes each reset, so feel free to attempt double buffering. :)


Note: With rendering enabled, it is perfectly ok to reach the end of vblank, the rti (assembly code), after scanline 260, IF AND ONLY IF your vblank code is NOT writing to the ppu beyond scanline 260. :)

dienben2020
Posts: 24
Joined: Wed Sep 30, 2020 2:19 pm

Re: Changes to background tiles showing on all screens

Post by dienben2020 » Tue Oct 20, 2020 9:30 am

Hi,

As far as I understand the comments in the NES Lib, it's exactly what the code is doing.

First you set the vram buffer (begining of the main):

Code: Select all

// we set the vram buffer
set_vram_buffer();
Then you 'push' in the buffer, anytime, using instruction like

Code: Select all

one_vram_buffer(128, NTADR_A(2+counter_1,2));
(counter in my example could vary)

then during the nmi the buffer is automatically flushed....

But, it is the limit of my understanding, writing in the buffer as above take a very long time, and you are not able to loop on the 960 tiles of a name table between two nmi (I have not yet try to understand the assembly code below).

So you are in the middle of the writing like that (it works fine when you have few tiles, 20 -25 max in my code):

Code: Select all

for (counter_1=0;counter_1<32;++counter_1)
	{
		for (counter_2=0;counter_2<30;++counter_2)
				{
					
					one_vram_buffer(128, NTADR_A(counter_1,counter_2));
					
				}	
	}

nmi occurs, the lib start to transfert the data and for a reason that I've not catched (yet I hope), it starts to be really messy in the screen.

I've supposed that it's due to the necessity to execute: clear_vram_buffer(); so I've put it in the middle of the loop for the next cycle between the nmi... without sucess....

Code: Select all


for (counter_1=0;counter_1<32;++counter_1)
	{
		for (counter_2=0;counter_2<30;++counter_2)
				{
					clear_vram_buffer(); // do each frame, and before putting anything in the buffer
					one_vram_buffer(128, NTADR_A(counter_1,counter_2));
					
				}	
	}

Reading the code from nesdoug site, I've finnaly use the following code, including flush_vram_update_nmi(); (but why??? I don't know)

And it doesn't work, the flush_vram_update_nmi(); needs ppu_off

Code: Select all


for (counter_1=0;counter_1<32;++counter_1)
	{
		for (counter_2=0;counter_2<30;++counter_2)
				{
					clear_vram_buffer(); // do each frame, and before putting anything in the buffer
					one_vram_buffer(128, NTADR_A(counter_1,counter_2));
					flush_vram_update_nmi();
				}	
	}

So finally I use the following code:

Code: Select all

ppu_off();
for (counter_1=0;counter_1<32;++counter_1)
	{
		for (counter_2=0;counter_2<30;++counter_2)
				{
					clear_vram_buffer(); // do each frame, and before putting anything in the buffer
					one_vram_buffer(128, NTADR_A(counter_1,counter_2));
					flush_vram_update_nmi();
				}	
	}
ppu_on_all();
If somebody has a better understanding of the arcane of the NesLib, it will help! :lol:

Regards,

Benoit

unregistered
Posts: 1098
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Re: Changes to background tiles showing on all screens

Post by unregistered » Tue Oct 20, 2020 1:14 pm

I’m sorry QT and others, I don’t understand why the buffer needs to be flushed. :?

The buffer can be in any section of memory, ROM or RAM, and it, that specific section of memory, is only used to store tile hex codes. And your game has a ton of non-vblank time to fill that tiny buffer (remember we just want to draw a section of A screen). Then, inside of vblank, you just create a small loop to read a value from that STATIC buffer and write it to $2007. (It’s a loop, so it will keep reading and writing those bytes to $2007 until the entire buffer has been written.)

There is no need to turn rendering off before that tiny loop AND, therefore, no need to turn it on again (it’s already on). :)


Obviously, this double buffering requires you to play with the size of your buffer until the game appears correctly. :)


edit: sry, I forgot that ROM can’t be written to after the game starts. :oops:

final-edit.

dienben2020
Posts: 24
Joined: Wed Sep 30, 2020 2:19 pm

Re: Changes to background tiles showing on all screens

Post by dienben2020 » Tue Oct 20, 2020 10:55 pm

Hello,

@unregistred: thanks for your comment. I completly agree with you, that's why I've said that I don't undertsand how it works (the neslib, I mean).

I've just tried things until it works.

What I see is, perhaps, the

Code: Select all

one_vram_buffer(128, NTADR_A(2+counter_1,2));
is very long (NTADR_A ???), need to check the assembler code....

But it seems that we all face the same issue. I will create a test project and share the code, so we could compare and test.

Regards,

Ben

Post Reply