CHR-RAM in UNROM

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

Post Reply
User avatar
Diskover
Posts: 219
Joined: Thu Nov 24, 2011 7:16 am
Contact:

CHR-RAM in UNROM

Post by Diskover »

I'm doing tests at UNROM.

Compile with CC65.

I want to update a tile (for example, 0x00) of CHR 'on the fly' without turning the screen off.

How can I access the CHR-RAM to tell it to replace the 0x00 tile with another?
How can I access the CHR-RAM to tell it to move the graph to the left, right, up or down the tile 0x00?
User avatar
rainwarrior
Posts: 8732
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: CHR-RAM in UNROM

Post by rainwarrior »

I don't know what you mean by "move the graph", but each tile is 16 bytes (8 x 8 bits x 2 planes), so the PPU address of any given tile is just 16x its index.

E.g. tile $53 begins at $0530 or $1530, depending on which CHR page you're using.

So, to write a new tile, just set the address with two writes to $2006, and then write the 16 bytes to $2007.

To do it without turning rendering off, you simply need to do this during vblank (probably in your NMI handler), just like you would for nametable updates.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: CHR-RAM in UNROM

Post by tokumaru »

Do note that "updating tiles on the fly without turning the screen off" is a pretty slow processes, specially if you're not unrolling loops, so don't expect to be able to change a lot of tiles per frame. If you use the vblank interval exclusively for updating tiles (no pallette, NT, AT or sprite updates at all), you'll be able to update around 16 tiles at best.
na_th_an
Posts: 558
Joined: Mon May 27, 2013 9:40 am

Re: CHR-RAM in UNROM

Post by na_th_an »

If you are using neslib to do the updates you may use the common method you use to update the nametables, this is, the set_vram_update function and the update list array. Just be sure to use the NT_UPD_HORZ switch alongside the MSB and add the 16 bytes to be updated afterwards, something like this:

Code: Select all

// Before your main loop
set_vram_update (update_list);

[...]

// Calculate the address
gp_addr = pattern_number << 4; // Add 0x1000 for the second pattern table

// the ul pointer points to the current position in the update_list
*ul ++ = MSB (gp_addr) | NT_UPD_HORZ
*ul ++ = LSB (gp_addr);
*ul ++ = byte0;
...
*ul ++ = byte15;

// During the next VBLANK the bytes will be sent to VRAM
*ul = NT_UPD_EOF;
ppu_wait_update ();

[...]

I have never done this but I understand it should work. Note that, as mentioned, you will be able to update just a few patterns using this method (it could be as few as just a couple or three patterns!). It's better to add custom code to your NMI routine to handle this.
User avatar
Diskover
Posts: 219
Joined: Thu Nov 24, 2011 7:16 am
Contact:

Re: CHR-RAM in UNROM

Post by Diskover »

No, what I'm talking about is being able to change the tiles of CHR-RAM.

Modifying VRAM I have already been able to do something, but what I need is to change tiles in the CHR-RAM to be able to get animated effects.
calima
Posts: 1745
Joined: Tue Oct 06, 2015 10:16 am

Re: CHR-RAM in UNROM

Post by calima »

CHR-RAM is VRAM.
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: CHR-RAM in UNROM

Post by tepples »

It's mostly the same deal.
  • To write to nametables, you load an address $2000-$2FFF into PPU port $2006 and then start writing bytes through $2007.
  • To write to pattern tables, you load an address $0000-$1FFF into PPU port $2006 and then start writing bytes through $2007.
If you're using a generic update buffer system, it should work for both, with two exceptions:
  • If the buffer format uses bits 13 and 12 of the destination address for behavior flags instead of the destination address, it won't work.
  • If the buffer format uses an address $0000-$00FF for terminating (as Nintendo's does), you may not be able to replace the first 16 tiles of the first pattern table. You may, however, be able to write to $4000-$40FF, which the PPU mirrors to $0000-$00FF.
User avatar
Diskover
Posts: 219
Joined: Thu Nov 24, 2011 7:16 am
Contact:

Re: CHR-RAM in UNROM

Post by Diskover »

I do not know if it's a language problem, but I do not understand you very much.

For now I have managed to do this: Basically drawing on the tile, although I can not write on the tile I want, I always write in the 0x00 and sometimes it is also written in other tiles without much sense.

Code: Select all

//x starts at 0

if(joypad1 & PAD_LEFT)		--x;			
if(joypad1 & PAD_RIGHT)	++x;

PPU_ADDRESS = 0x20;       //Here I am writing $2000?

		if(x == 0){			
			PPU_DATA    = 0x80;
			PPU_DATA    = 0x80;
			PPU_DATA    = 0x80;
			PPU_DATA    = 0xFF;
			PPU_DATA    = 0x80;
			PPU_DATA    = 0x80;
			PPU_DATA    = 0x80;
			PPU_DATA    = 0x80;
			
		}
	
		if(x == 1){
			PPU_DATA    = 0x40;
			PPU_DATA    = 0x40;
			PPU_DATA    = 0x40;
			PPU_DATA    = 0xFF;
			PPU_DATA    = 0x40;
			PPU_DATA    = 0x40;
			PPU_DATA    = 0x40;
			PPU_DATA    = 0x40;
			
		}
		
		if(x == 2){
			PPU_DATA    = 0x20;
			PPU_DATA    = 0x20;
			PPU_DATA    = 0x20;
			PPU_DATA    = 0xFF;
			PPU_DATA    = 0x20;
			PPU_DATA    = 0x20;
			PPU_DATA    = 0x20;
			PPU_DATA    = 0x20;
			
		}
		
		if(x == 3){
			PPU_DATA    = 0x10;
			PPU_DATA    = 0x10;
			PPU_DATA    = 0x10;
			PPU_DATA    = 0xFF;
			PPU_DATA    = 0x10;
			PPU_DATA    = 0x10;
			PPU_DATA    = 0x10;
			PPU_DATA    = 0x10;
			
		}
		
		if(x == 4){
			PPU_DATA    = 0x08;
			PPU_DATA    = 0x08;
			PPU_DATA    = 0x08;
			PPU_DATA    = 0xFF;
			PPU_DATA    = 0x08;
			PPU_DATA    = 0x08;
			PPU_DATA    = 0x08;
			PPU_DATA    = 0x08;
			
		}
		
		if(x == 5){
			PPU_DATA    = 0x04;
			PPU_DATA    = 0x04;
			PPU_DATA    = 0x04;
			PPU_DATA    = 0xFF;
			PPU_DATA    = 0x04;
			PPU_DATA    = 0x04;
			PPU_DATA    = 0x04;
			PPU_DATA    = 0x04;
			
		}
		
		if(x == 6){
			PPU_DATA    = 0x02;
			PPU_DATA    = 0x02;
			PPU_DATA    = 0x02;
			PPU_DATA    = 0xFF;
			PPU_DATA    = 0x02;
			PPU_DATA    = 0x02;
			PPU_DATA    = 0x02;
			PPU_DATA    = 0x02;
			
		}
		
		if(x == 7){
			PPU_DATA    = 0x01;
			PPU_DATA    = 0x01;
			PPU_DATA    = 0x01;
			PPU_DATA    = 0xFF;
			PPU_DATA    = 0x01;
			PPU_DATA    = 0x01;
			PPU_DATA    = 0x01;
			PPU_DATA    = 0x01;
			
		}
		
		if(x == 8) x = 0;
		if(x == 255) x = 7;
		
		Reset_Scroll();
What I get is this:
Image


ROM:
prueba1.nes
(64.02 KiB) Downloaded 140 times
team_disposable
Posts: 129
Joined: Sat Oct 15, 2016 8:52 am

Re: CHR-RAM in UNROM

Post by team_disposable »

tl;dr - Just quickly looking at your code, you only seem to be writing the first part of the address to PPU_ADDRESS - you need to write the whole 16 bit address. Secondly, presumably because it's a demo, your code has movement functions being called before the update to CHR-RAM - I would recommend you don;t do this, and call CHR-RAM updates first, at the very top, as NMI update time is limitedand if it overruns, you will get horrible graphical glitches to your tiles.

Hi Diskover, the game you're porting looks ace! I look forward to playing it!

I've done quite a lot of CHR-RAM updating for animations -

First I made a lookup table of the address of the graphic I want to write in to CHR-RAM, and the address it should be written to - here's an excerpt:

Code: Select all

;address in rom high, address in rom low, destination address high, destination address low
tile_lookup:
.byte <spike1_chr, >spike1_chr, 22, 16
 .byte <spike2_chr, >spike2_chr, 22, 16
 .byte <spike3_chr, >spike3_chr, 22, 16
.byte <coin1_chr, >coin1_chr, 18, 16
.byte <coin2_chr, >coin2_chr, 18, 16
.byte <coin3_chr, >coin3_chr, 18, 16
.byte <coin4_chr, >coin4_chr, 18, 16
.byte <fivecoin1_chr, >fivecoin1_chr, 18, 32
.byte <fivecoin2_chr, >fivecoin2_chr, 18, 32
.byte <fivecoin3_chr, >fivecoin3_chr, 18, 32
.byte <fivecoin4_chr, >fivecoin4_chr, 18, 32
Secondly, I wrote a function in assembly to load the tile when given the name as a parameter:

Code: Select all

_copy_tile:
 tay
 lda tile_lookup, y ; load the source address into a pointer in zero page
 sta src
 iny
 lda tile_lookup,y
 sta src+1
 iny
 lda tile_lookup,y
  sta PPUADDR  ; load the destination address into the PPU
  iny
  lda tile_lookup,y
  sta PPUADDR
  ldy #0
  lda (src),y  ; copy one byte
  sta PPUDATA
  iny
  
  lda (src),y  ; copy one byte
  sta PPUDATA
  iny
  
  lda (src),y  ; copy one byte
  sta PPUDATA
  iny
  
  lda (src),y  ; copy one byte
  sta PPUDATA
  iny
  
  
  lda (src),y  ; copy one byte
  sta PPUDATA
  iny
	
  lda (src),y  ; copy one byte
  sta PPUDATA
  iny
  
  lda (src),y  ; copy one byte
  sta PPUDATA
  iny
  
  lda (src),y  ; copy one byte
  sta PPUDATA
  iny
  
  
  lda (src),y  ; copy one byte
  sta PPUDATA
  iny
	
  lda (src),y  ; copy one byte
  sta PPUDATA
  iny
  
  lda (src),y  ; copy one byte
  sta PPUDATA
  iny
  
  lda (src),y  ; copy one byte
  sta PPUDATA
  iny
  
  
  lda (src),y  ; copy one byte
  sta PPUDATA
  iny
	
  lda (src),y  ; copy one byte
  sta PPUDATA
  iny
  
  lda (src),y  ; copy one byte
  sta PPUDATA
  iny
  
  lda (src),y  ; copy one byte
  sta PPUDATA
  iny
  rts	
Thirdly, I then export this from the assembly, forthly I write a function header in C, and then finally call it in C like this:

Code: Select all

copy_tile(chr_heart1);
That's it - just make sure you're calling copy_tile in the NMI.

Hopefully this is of some help, if not let me know!
Post Reply