Points of Required Attention™
Please chime in on a proposed restructuring of the ROM hacking sections.
Views: 88,434,112
Main | FAQ | Uploader | IRC chat | Radio | Memberlist | Active users | Latest posts | Calendar | Stats | Online users | Search 04-19-24 03:22 AM
Guest: Register | Login

0 users currently in ROM Hacking | 1 guest | 2 bots

Main - ROM Hacking - CHR-RAM Trouble New thread | New reply


RetroRain
Posted on 06-02-10 06:57 PM Link | Quote | ID: 131626


Fuzz Ball
Level: 66

Posts: 510/994
EXP: 2436715
Next: 25136

Since: 09-30-07

Last post: 1927 days
Last view: 950 days
Surprisingly, after doing lots of searching, there aren't many documents that explain about dealing with CHR-RAM. I am hacking Megaman 4, but it doesn't have any VROM in it. All of the graphics are in PRG. So, I have no choice but to deal with CHR-RAM. The thing is, I'm always up for learning new things, and I don't like giving up on a problem.



I want to do my first CHR-RAM hack here. So, I'll do it with something simple, like rain. I want the rain to fall down. In other words, I want the tiles to shift downwards.

Now, I read that when dealing with CHR-RAM, you have to deal with VRAM, and you have to deal with $2006 and $2007. But that doesn't tell me much.

I made a small NES demo one time, and I used $2006 and $2007 to put graphics on the screen (Name Table). But, I don't know how to use those addresses to write to the PPU Table itself.

But what I can't get over is that there is not one single document on the net that goes into it. They cover CHR-ROM and VROM, but nothing on CHR-RAM.

So, how would I go about doing this?

Here is what I have done so far.

I set a breakpoint of $95, which is one of the frame counters in Megaman 4. It is the manual frame counter. I also found some free space. So, the code beneath the INC $95 I relocated it to the free space, I will insert my new code, and then I will RTS.

Now, what to put there.........

However, I am impressed that the CHR pages work just like a game that has VROM. I can switch 2K CHR pages just like that, by writing to $8000 and $8001. It works the same way.

But how the hell do I get those tiles to shift downward? I will probably have to "shift the bits" right, like using a LSR?

I want to know how to select the pattern table tiles $48, $49, $58, and $59 themselves.

I know that this piece of code right here:

LDA $20
STA $2006
LDA $00
STA $2006

This will set the scanline to the upper-left part of the Name Table, and then you can select a tile, such as $48, and write it to that location

LDA $48
STA $2007.

But I don't want to write to the Name Table. I want to write to the PPU itself.

Any help and advice would greatly be appreciated. I want to learn. Thanks.

____________________
My YouTube Channel

never-obsolete
Posted on 06-02-10 08:38 PM (rev. 2 of 06-02-10 08:39 PM) Link | Quote | ID: 131627


Rat
Level: 24

Posts: 64/96
EXP: 74452
Next: 3673

Since: 02-22-07
From: Phoenix, AZ

Last post: 2589 days
Last view: 2589 days
All of vram is written to the same way. Setting PPU_ADDR ($2006) to $0000 will point to tile #$00 of pattern table #0, $1000 will point to tile #$00 of pattern table #1.

RetroRain
Posted on 06-03-10 06:55 PM Link | Quote | ID: 131668


Fuzz Ball
Level: 66

Posts: 511/994
EXP: 2436715
Next: 25136

Since: 09-30-07

Last post: 1927 days
Last view: 950 days
So, to point to tile $48, I have to set $2006 to that using:

LDA #$00
STA $2006
LDA #$48
STA $2006

But if it is then pointing to tile $48, then what does $2007 do when it comes to the PPU table?

____________________
My YouTube Channel

never-obsolete
Posted on 06-03-10 07:18 PM Link | Quote | ID: 131674


Rat
Level: 24

Posts: 65/96
EXP: 74452
Next: 3673

Since: 02-22-07
From: Phoenix, AZ

Last post: 2589 days
Last view: 2589 days
Tiles are 16 bytes, so tile #$48 would be either $0480 or $1480 depending on which pattern table you want to write to.

Once you set PPU_ADDR ($2006) you have to write to PPU_DATA ($2007) 16 times to write a full tile. This has to been done during vblank (or while rendering is disabled) or else you'll have some graphical glitches.

RetroRain
Posted on 06-03-10 07:19 PM Link | Quote | ID: 131675


Fuzz Ball
Level: 66

Posts: 512/994
EXP: 2436715
Next: 25136

Since: 09-30-07

Last post: 1927 days
Last view: 950 days
Oh alright. Thank you for your help. I will give it a try later and let you know how it goes.

____________________
My YouTube Channel

RetroRain
Posted on 06-04-10 11:07 PM Link | Quote | ID: 131728


Fuzz Ball
Level: 66

Posts: 513/994
EXP: 2436715
Next: 25136

Since: 09-30-07

Last post: 1927 days
Last view: 950 days
Okay. First problem. Despite the fact that I have turned the display off and then on again, I am still getting glitches. I even used this piece of code:

vwait:
LDA $2002
BPL vwait

As you can see in the image, I am using:

LDA #$16
STA $2001

to turn the display off, and then:

LDA #$1E
STA $2001

to turn it back on again.

I even used both the vwait code and the $2001 code. And this is the result I'm getting.



However, I have experimented with $2007, and I was able to mess with the PPU tiles I wanted to change.

But I'm confused about $2007. Does $2007 write a single pixel? Is that what it's supposed to do?

You mentioned that you have to write $2007 16 times to write a full time, but I'm a little confused about that because a single tile is 8x8 pixels, which means there are 64 pixels in a single tile. So why would it be 16, and not 64? (yes, I'm a little inexperienced when it comes to bit stuff, but I do understand how binary works).

By the way, never-obsolete, I checked out your web page. I am fascinated by that "Cat Killer" game you have on there. I remember playing Rodent's Revenge on my very first computer. I remember those hectic days of Windows 95 and that slow Pentium processor, but that little game was fun. I like the things I see on your site. Keep up the good work.

____________________
My YouTube Channel

never-obsolete
Posted on 06-05-10 02:15 AM (rev. 2 of 06-05-10 02:17 AM) Link | Quote | ID: 131743


Rat
Level: 24

Posts: 66/96
EXP: 74452
Next: 3673

Since: 02-22-07
From: Phoenix, AZ

Last post: 2589 days
Last view: 2589 days
$2007 is just a data transfer register. Its use (combined with $2006) is to be able to move data from whats mapped into cpu's address space to the ppu's and vice versa. So the use really depends on where you're writing to or reading from.

A tile is 16 bytes because NES graphics are limited to 2 bits per pixel. Here's a better visual.

Now for the bigger problem. The reason for all that is because $2006 also has another use. During rendering, it's used by the ppu to draw the screen. You have to turn the screen off to use it, but before you turn it back on, you have to reset $2006 to the correct address. Even if you do that, you'll still have a portion of the screen be blanked because you turned it off mid-frame.

What you want to do is find out how the game buffers its vram updates and write all your data to the buffer. Then let the game take care of writing to vram on the next vblank.

I played a fair amount of RR back in the day too, thanks for the comments.

Hamtaro126
Posted on 06-05-10 06:14 AM (rev. 2 of 06-05-10 06:15 AM) Link | Quote | ID: 131744


Cheep-cheep
Level: 33

Posts: 138/194
EXP: 212712
Next: 16467

Since: 05-02-07
From: Shelton, WA

Last post: 2465 days
Last view: 2308 days
Here is a old topic from the NESDEV BBS:

http://nesdev.parodius.com/bbs/viewtopic.php?t=4921&postdays=0&postorder=asc&start=0

At the very last post is animation code by Celius. Also, Celius included a demonstration of the explained code. The code might need to be modified for your own prefrence.

____________________
Mah boi, romhacking is what all true warriors strive for!

I wonder what's for dinner?

RetroRain
Posted on 06-13-10 09:39 PM Link | Quote | ID: 131981


Fuzz Ball
Level: 66

Posts: 515/994
EXP: 2436715
Next: 25136

Since: 09-30-07

Last post: 1927 days
Last view: 950 days
Posted by never-obsolete
What you want to do is find out how the game buffers its vram updates and write all your data to the buffer. Then let the game take care of writing to vram on the next vblank.
I took a break from this, because it was too difficult for me.

When you are writing a single tile, you say you have to write to $2007 16 times. But you are loading #$FF into the accumulator, and storing it to $2007 16 times right? I did that, and noticed it got the whole tile.

Now, when you say write to the VRAM buffer, am I going to the buffer itself, and simply jumping to some free space, putting all of my code there, and then returning?

Matrixz has most of MM4's data documented, and it has the addresses of the VRAM buffer stuff. But I have no clue which one I should be using. I'm assuming it could be $19 or $1A because the name of that section is Graphics updates.

Last question. Once I have a single tile written by $2007, do I have to use a LSR or ROR to make the tile shift downwards, to give the appearance that the rain is falling?

LDY #$10
Again:
LDA #$FF
STA $2007
DEY
BNE Again:

; Should the shifting code look like this?

; (Tile $48)
LDA #$04
STA $2006
LDA #$80
STA $2006
LSR ; Shift tile down one pixel

Matrixz' MM4 Data


Graphics updates

19 : VRAM Buffer - Write Flag (if != 0, then write VRAM Buffer to VRAM)

1A : VRAM Buffer - Write Flag (Vertical NameTable Updates) (if != 0, then write VRAM Buffer to VRAM, NameTable writes are done in Vertical direction. )

58-59 : Graphics being loaded - Dest. VRAM Address Pointer

9A : VRAM update suspend flag. Used to synchronize sprite DMA updates and VRAM updates.

780-7FE : VRAM Buffer.
(Used to write to Name Table VRAM, either when
screen is off or in NMI when screen is on.)
Format:
The buffer is divided in chunks of strings.
If the first byte in the chunk is $80 or more (usually $FF),
it means to stop reading chunks from the buffer.
Format of string chunk:
byte 0: PPU VRAM Address High (if >= $80, stop reading buffer)
byte 1: PPU VRAM Address Low
byte 2: Number of Letters/Bytes to write (minus 1; if
the value is 0, it means to write 1 tile, if value is 1,
it means to write 2 tiles, and so on..)
From byte 3 follows the variable-length string of bytes to write
in sequence, (length is the value of [Byte 2] + 1)
This is the end of the string chunk.
Format, explained by interpretion in C code style: {
unsigned char a, x, y;
unsigned char vram_hi, vram_lo;
x = 0;
loop:
a = *(0x780 + x);
if ((a & 0x80) == 0x80) { return (0); }
vram_hi = a;
a = *(0x781 + x);
vram_lo = a;
y = *(0x782 + x);
for ( ; y >= 0; y--) {
a = *(0x783 + x);
NES_PPU_VRAM[(vram_hi << 8) | vram_lo] = a;
x++;
}
x += 3;
goto loop;
}


____________________
My YouTube Channel

RetroRain
Posted on 06-14-10 10:48 PM Link | Quote | ID: 132009


Fuzz Ball
Level: 66

Posts: 516/994
EXP: 2436715
Next: 25136

Since: 09-30-07

Last post: 1927 days
Last view: 950 days
(Double post, but relevant)

Would my code look something like this?

; Code to copy and shift a single PPU tile.  In this case, PPU tile $48.


; Turn screen off
LDA $16
STA $2001

; Select PPU tile $48
LDA $04
STA $2006
LDA $80
STA $2006

; Write to $2007 sixteen times to copy all of tile $48
LDY $10
Read:
STA $2007
DEY
BNE Read

; Increase tile Y position and write pixels
LDY $0F
LDA $04
STA $2006
LDX $81
STX $2006
Write:
LDA $04
STA $2006
STX $2006
STA $2007
INX
DEY
BNE Write

; Restore scanlines
LDA $20
STA $2006
LDA $00
STA $2006

; Turn screen back on
LDA $1E
STA $2001

; Return from sub-routine
RTS


____________________
My YouTube Channel

kuja killer
Posted on 06-15-10 03:37 AM Link | Quote | ID: 132014


Level: 55

Posts: 196/628
EXP: 1243147
Next: 71042

Since: 03-20-07
From: Lake Havasu City, Arizona

Last post: 274 days
Last view: 22 hours
i personally really hate that whole CHR-ram deal with mm4, the coding is way too complicated for me to even understand, and because it's very slow and how it requires like freaking 16 writes as someone else mentioned, just to change 1 little single time or something, ridiculous

CHR-ROM mode is a million times easier and simpler and stuff, except that it's more limited though un-fortantely.

RetroRain
Posted on 06-15-10 04:06 AM Link | Quote | ID: 132015


Fuzz Ball
Level: 66

Posts: 517/994
EXP: 2436715
Next: 25136

Since: 09-30-07

Last post: 1927 days
Last view: 950 days
Yeah but is giving up, the answer? Because I've been seriously thinking about moving my project over to Multimedia Fusion 2, where I will not have anymore limitations, and I can do the things I really want to do. I can make my game the way I want it, without having to figure out how the original game works, and then struggle just to make it do the things I want.

But I don't like giving up, because I have done that a lot in the past, and it gets you no where. Just because I don't know how to do CHR-RAM swapping, doesn't mean I should just discard the ROM. I won't learn anything, and not knowing how to do something drives me nuts, when other more experienced hackers out there can do it no problem.

Do you know how to do CHR-RAM swapping? I know you converted MM4 over to CHR-ROM completely. How the heck did you do that?

Why should I struggle with ROM hacking when I can just move my game over to MMF2 and then freely do what I want without anymore limitations? Part of me wants people to be able to play the thing on the real NES, and its an actual Nintendo game, not some PC game. That is part of the reason, but is it worth the hassle?

____________________
My YouTube Channel

never-obsolete
Posted on 06-15-10 06:10 AM Link | Quote | ID: 132016


Rat
Level: 24

Posts: 67/96
EXP: 74452
Next: 3673

Since: 02-22-07
From: Phoenix, AZ

Last post: 2589 days
Last view: 2589 days


Now, when you say write to the VRAM buffer, am I going to the buffer itself, and simply jumping to some free space, putting all of my code there, and then returning?

Matrixz has most of MM4's data documented, and it has the addresses of the VRAM buffer stuff. But I have no clue which one I should be using. I'm assuming it could be $19 or $1A because the name of that section is Graphics updates.



You'll be writing your tile data to the buffer instead of directly to vram. The code to do this will be wherever you have free space.

From the notes you posted, it looks like you'll need to write your data to $780-$7FE. $19 will need to be set (to 1?), but their might be other things.



Last question. Once I have a single tile written by $2007, do I have to use a LSR or ROR to make the tile shift downwards, to give the appearance that the rain is falling?



Shifts and rotates will work for moving it left and right. To shit up and down you write each byte off by one every frame and wrap after 7 for each plane.

Trax
Posted on 06-15-10 06:11 AM (rev. 2 of 06-15-10 06:14 AM) Link | Quote | ID: 132017


Yellow Stalfos
Level: 71

Posts: 969/1145
EXP: 3033987
Next: 133127

Since: 07-06-07
From: Québec

Last post: 3619 days
Last view: 2871 days
It's just a question of whether your tiles are neatly preassembled in CHR banks, or just loaded using other means. If you have compressed graphics, you will have to feed the PPU by hand using specific routines. Contra works that way. Graphics chunks are scattered throughout the ROM, and each level has its own set of graphics chunks. Each chunk is compressed in a mix of RLE and literal bytes. When a level is loaded, the chunks are decompressed and sent to the PPU...

RetroRain, your thinking is not bad, but I don't think it's necessary to go for such precision of the bytes that compose your tiles. You could simply locate some free space in the ROM, store your tile data there and access it as needed. To make a rain tile, 3 or 4 frames are enough. So make your 256 bytes (16x4x4) in advance, keep track of your frame number (0-3) and use a X or Y index when loading the tile...

Steps are like:

- Place the PPU writing head to whatever location of your rain tiles.
- Load your frame number (0-3) and multiply it by 64 (ASL 6 times).
- Transfer the bytes form your custom table to the PPU, using the frame number as an index.


Main - ROM Hacking - CHR-RAM Trouble New thread | New reply

Acmlmboard 2.1+4δ (2023-01-15)
© 2005-2023 Acmlm, blackhole89, Xkeeper et al.

Page rendered in 0.026 seconds. (339KB of memory used)
MySQL - queries: 97, rows: 131/131, time: 0.017 seconds.