Various NES Questions (Mappers, Palettes, Pointers, oh my!)

Started by Polinym, November 17, 2019, 01:49:57 AM

Previous topic - Next topic

Polinym

So I've been working on localizing LaSalle Ishii and I've had some questions on how to do things that might improve the final product of what I'm making. I've searched around the internet for information, but haven't gotten every detail answered, or the guide (like that NesDev wiki) assumes you know a lot about how the NES functions at a hardware level. I'd like some more information in simpler, more practical terms. I know that things can work differently on different mappers and I'd like general info I can use with any ROM, but how stuff pertains to "Mapper 4" (LaSalle Ishii No Childs Quest) would be especially helpful! : )  But without further ado, here's my questions:

How do mappers/switching banks work?

What I've gathered is that the NES ROM has some number of banks, one of them always loaded into RAM or something like that. Somehow the game "switches out banks" by writing some number to RAM $8000. Can someone tell me more about how that works, like exactly how to "switch banks"? The ROM data seems to start exactly at $8000, so it seems like writing anything to $8000 is just going to put one byte there. I tried fixing a value at $8000 using FCEUX's cheat tool, but that didn't fix the bank at all! All it did was change some random map tile. If I could learn how to switch banks, I could use unused spaces in other banks to expand a game's text, right?

How does the game assign palette to things?

I want to know how the game tells what tile/sprite is going to use what palette. With backgrounds, I've figured out that some are like text strings where they have two bytes at the beginning of drawing a line that determine the position that tiles show up. (ex. [32][20][byte values for tiles like in a title screen]. But where do games decide what background tiles are going to get what palette?

I've included an example of what I mean in the above screenshot from Pop Star Debut (アイドル八犬伝), if it shows up. When I was working on giving Pop Czar Feld some pants, I noticed that not all the tiles that made up his leg used the same palette. Some parts of his leg use  red, others use brown. Knowing how to change the tiles' palettes will be helpful when going to make Patch #2.

I'd also like to know how the game assigns palettes to sprites, too. I'm still don't know at all how the game handles things like X/Y positons, animations, spawning sprites in general etc. If anyone could tell me how to change what palette a sprite (or part of a sprite) uses, that'd be useful to know! : )


Weird pointers I don't understand!

So I've learned through internet research that the way to calculate a pointer is to take the ROM address of the beginning of the text, subtract 10 from the last two bytes, then swap the two pairs of bytes. However, the LaSalle Ishii game doesn't seem to abide by that!

You can see in the below screenshot that the first line in this bank of text begins at 0xA070, so following the standard pointer formula, the pointer address ought to be 60A0, right?


But if we go to the pointer table for this text, (and yes, I've confirmed this IS its pointer table using FCEUX), you can see the first pointer listed is 6081! (I also may be off by 1 on the last byte pair because the game might somehow skip the first string's byte number that determines at what speed the line prints out at).


What's with this inconsistency, and how should I be calculating the pointer?

What can I do to enable SRAM?

Through research I've heard that you can write any values you want to get saved to the addresses $6000-8000. I think I understand that I just need to add a routine to some unused part of the ROM where I write values with STA, and have the game load them with LDA. However, I don't think I've found all the steps that you need to actually enable the game to USE SRAM. Trying to test out how SRAM works, I manually set some values in SRAM in Pop Star Debut (a game that doesn't save) through FCEUX's Hex Editor, reset the game, but the data was erased! I've heard something about change some byte in the header (couldn't figure out what!), and writing some value (don't know which) to $A001. What are the exact steps I need to do in order for a game to save, especially on a real NES cartridge?

As always, help is greatly appreciated! : D I hope it's okay to ask all these questions at once. I feel like it's be bad to start 4 new threads about each individual unrelated question.

Edit: I found a different place to store images, so they show up now. Woohoo!
"Where are you going, soldier? Get back to the battle!" - Some Battlefront Clone
My YouTube Channel: https://www.youtube.com/channel/UCJqSB4xw29TTKlia5E0NYtQ

Jiggers

Your images aren't showing up!

Here's some of what I learned:

There are 8 palettes, of 3 colours each. They all share the same background colour. Backgrounds then get 4 colours, while for sprites, that colour is transparent.

(I better preface this with: This is how it works in Final Fantasy 1 and I may be wrong about how it works in other games.)

Palette data is stored at $3F00 in RAM. It's $20 bytes (hex) total, or 4*4 decimal. The first 16 bytes are background colours, the second 16 bytes are sprite colours. 4 sets for each. So $3F15 is the first colour for the second set of sprite palettes. When loading them up, usually the game has backup palettes saved in a buffer, that it then copies over to $3F00 when its time to set them for real.

Sprite data is stored at $0200, known as OAM, and I don't know what that stands for. Each 8x8 block of pixels is its own sprite tile. This tile is in CHR RAM, and I can't say for certain where that is, because I think it can change depending on the mapper and what the game is doing with its graphics. Going with my Final Fantasy 1 knowledge, bot the background and sprite CHR have 256 tiles each, which usually get loaded in chunks beforehand. I don't know what CHR really stands for either! But re: sprites...

Each sprite has 4 bytes in OAM ($0200). The first byte is Y, vertical, position, in pixels. So $7F is like halfway down the screen or something. The second byte is the tile ID, that tells it what 8x8 graphic to use from the sprite CHR. The third byte is attributes, this gets a little messy, but if you just want to know about colours, then numbers 0-3 set the colour. $0 is the first set of sprite palettes, $1 is the second, $2 is the third, and $3 is the fourth. This only accounts for 2 BITS of the sprite's attribute byte. The other bits decide if its flipped upside-down or backwards, or if its behind or in front of background tiles. The third byte of the sprite's info is the X, or horizontal position, again in pixels.

Since the pixel positions can go up to 256 and the screen is only 240 pixels high (I forget how wide), setting an X or Y position of $F0 hides the sprite "off screen" Final Fantasy 1 just flood-fills the sprite RAM area with $F0 to basically tell the game not to draw any sprites this way.

So next time you look at $0200 in RAM, every 4 bytes is 1 8x8 pixel sprite tile. The next 4 are the next sprite, and so on. The game doesn't have to fill this in order, either. But every $10 bytes is a 4x4 sprite usually. To update sprite positions, new X and Y positions are written. FF1 likes to COMPLETELY wipe the sprites out every single frame and re-draw every single byte from a buffer! I think this is because this part of RAM decays faster or something, so if you don't keep updating even the tile ID, it will end up garbling itself?

Background tiles get their colour from the attribute table, which is an 8x8 grid. Each square of the grid is composed of 4 smaller squares, which can be filled with 4 8x8 pixel tiles. The easiest way to think of it is the blocks in Super Mario Bros. Every group of 4 tiles is its own colour. But to figure out what colour that is, the game has to do a bunch of gross math to figure out the colours of the other blocks that share its attribute box.

The best program for visualizing this is - https://www.romhacking.net/utilities/1087/

Each corner of the square can be a different palette set. That utility will use 1, 2, 3 and 4, but the game will probably refer to the palettes as 0, 1, 2, and 3. So NES games set 16 tile colours at once with just 1 byte of attribute data! Now I'm wondering how Tetris games do it, but I haven't played one in forever... I know the MMC5 mapper has a switch that gives up some CHR RAM for having a higher fidelity attribute table, where every 8x8 pixel tile can have its own colour set, but that's the only outlier I know of.

I hope that explained anything a little better... I'm basically re-learning this every month on my own, then I forget and have to read up on it again, so I hope I'm not forgetting something. Its all fresh in my mind so I could forget an obviously trippy bit...

The best learning tool to see how things are working is an emulator like Mesen, then open up the PPU Viewer and switch between the tabs, pausing and unpausing to see things change, mousing over tiles to see bits of information about them in the side panels. Really helps piece it all together.

For the pointers--see if you can set a breakpoint for when they're read? Then see what is happening after its loaded. It sounds to me like the game is reading the pointer, then maybe doing some extra math on it to fix it up right to point to the proper string. Its hard to tell without the pictures though...
I know exactly what I'm doing. I just don't know what effect it's going to have.

I wrote some NES music! Its a legal ROM file. - I got a Ko-Fi page too.

Cyneprepou4uk

#2
$8000 is an mmc3 register, and at the same time it is a cpu memory location. So your cheat doesn't change what is going to be written into $8000, it just changes a value that is currently located at $8000 address. If you want to create a valid cheat, you need to change a value that is written into $8000 by some instruction. However messing up with $8000 cheats will most likely break your game from the start.

Writing at $8000 is for selecting which area you are going to switch. Writting #00-#05 is for chr banks, and #06-#07 is for prg banks. Then, when you have selected switching area, you write a value into $8001 to select a particular bank number. More details on nesdev. Yes, you can use unused space (or create that space by rom expanding) if you learn how to switch banks.



Sprites are usually (but not necessary) in $0200-$02FF. Generally it is a 256 bytes area somewhere in ram. Here you can read about sprite attributes. To change sprite palette you need to change "Byte 2" value.

Background palette attributes are located in the end of the nametable right after tiles numbers, 64 bytes at $23C0 in ppu memory. Read more here.



To calculate pointers for 0xA070, first you substract $10. Then you add/subatract $2000 to get locations in a specific range that depends on mapper. Generally it is $8000-$FFFF range. So from 0xA070 we get possible locations $8060, $A060, $C060 and $E060.

Pointers are usually located above their objects they point at, code/data logger can help you see them. Pointers can be stored in various formats, one of them is a simple low + high bytes combinations. A sure way to find them is to set read breakpoints to these address, then check logs from trace logger after breakpoint hit.



Fceux allows you to write at $6000-$7FFF out of the box. But some emulators like Nestopia require to activate 7th bit at $A001 register beforehand, so you write #80 in there, and make sure other values are not written into that register. Sram is activated in header by adding #02 to 0x00006 address, however modern emulators don't honor that sram header bit nowdays, so I wouldn't worry about it.

Sram gets zero out afrer hard reset, I bet that's why your manual values were erased. Use soft reset.

If you want to teach the game to save itself, you need to write all important data to sram, like current level, lives counter, points and other shit, then force the game to read these values from sram when it loads. I'm not going to describe exact steps coz I doubt you'll actually be doing something.

Disch

QuoteHow do mappers/switching banks work?

I'm going to elaborate more here, as others only briefly touched on it.

NES addresses are only 2-bytes.  This means the address range is from $0000 to $FFFF.  This allows for 64K of addressable memory.  Some of that memory is RAM, some is PPU registers, some is APU registers, etc.  Generally, the $8000-FFFF range is where the actual game PRG-ROM is put.

But note that the $8000-FFFF range is only 32K of space.  Games will want to be much larger than that.  That's where bankswitching comes in.
(Note it sounds like you're working with MMC3 so I'm going to use the most common MMC3 configuration in my example, but not that other mappers work slightly differently)


For MMC3, the rom file itself is split into different "pages".  Each page is 8K ($2000 bytes).  So in your actual .nes ROM file can be thought of as looking like this:

- offset $00000-$0000F is the header
- offset $00010-$0200F is 'page 0' of PRG
- offset $02010-$0400F is 'page 1' of PRG
- offset $04010-$0600F is 'page 2' of PRG
etc

And as for the $8000-FFFF range in addressing space that the CPU sees, MMC3 splits that into 4 separate "slots".  Each slot is 8K in size:

$8000-9FFF  (swappable)
$A000-BFFF  (swappable)
$C000-DFFF  (fixed to always have the 2nd-to-last page of ROM)
$E000-FFFF  (fixed to always have the last page of ROM)



Those "slots" each have a different "page" put into them.  So what you get when you actually read from $8000 depends on what page is currently "swapped in":
- If page 0 is swapped in, you'll read from offset $00010
- If page 1 is swapped in, you'll read from offset $02010
etc


This allows games to be much bigger.  Because the ROM itself can be any size, it just changes which parts of it are "visible" at any given time.

As shown above, on MMC3 the last 2 slots ($C000-FFFF) are typically fixed to the last two pages of PRG ROM, but the other two slots are independently swappable.  This is accomplished by first writing to $8000 to indicate which slot you want to configure, then writing to $8001 to say which page you want to put in that slot:


LDA #$06
STA $8000   ; by writing 6, we're saying we want to configure $8000-9FFF slot
            ;    if we wrote 7, we'd be configuring the $A000-BFFF slot

LDA #$03
STA $8001   ; This will put page 3 (ROM offset $06010) into the $8000-9FFF slot


LDA $8010   ; reads from .nes file offset $06020 ... $10 bytes into page 3

Polinym

Thanks, all, for the info! This will be very useful for me.

But one more thing! I've set up the game I'm working with the header so it should be all ready for me to start saving things to SRAM. However, when I watch the values at $6000-8000 in the hex editor, ALL the bytes are switching constantly between 01 and FC! It looks like adding ASM to write values there isn't going to do a thing since FC or 01 will just overwrite them! Solution?



Same Mapper (#4), by the way. The bytes change regardless of what's in the header.
"Where are you going, soldier? Get back to the battle!" - Some Battlefront Clone
My YouTube Channel: https://www.youtube.com/channel/UCJqSB4xw29TTKlia5E0NYtQ

Disch

SRAM needs to be enabled and made writable by writing $80 to $A001 before it can be used.

If the game wasn't using SRAM previously, it likely isn't do that, so you'll have to add code to do that.

Cyneprepou4uk

Like I said before, sram in fceux works without that register.

What game is it? This bytes changing seems pointless

Disch

Quote from: Cyneprepou4uk on November 17, 2019, 03:57:14 PM
Like I said before, sram in fceux works without that register.

It shouldn't.

EDIT:

Snarkiness aside, even if you're right, if he wants this to work on any other emulator, $A001 is not optional and he absolutely should be writing to it.

The only other thing I can see causing weirdness is if FCEUX simply isn't giving the game RAM, but I find that extremely unlikely, especially if the SRAM bit in the header is set.

Pluvius

I've tested on my copy of the game (changing byte $06 from $#40 to $#42) and I don't see that behavior.  In fact, the RAM I'm seeing is exactly $#1000 bytes shifted from yours, so that I'm seeing that behavior occur from $5000-5FFF and the area from ~$4000 to $4FFF filled with $#FF.  $6000-7FFF is blank.

Polinym

Okay, so there must be something else weird going on... maybe it's a header issue? I had to change the header earlier because some graphical weirdness with incorrect mirroring made the game very difficult to test (http://www.romhacking.net/forum/index.php?topic=29344.0) I tried the suggested header, and using it fixed the issues. Here it is below: (Note that I changed E1 to E3 for SRAM)


The original ROM file I started with, clean and unchanged, had a header like this:


Side note: Any tips on how to find a good JMP routine to change to reroute to new SRAM-saving code? I'm worried I'll mess up the registers or something, use RTS/JMP wrong, something like that. Also, I tried adding code to Pop Star Debut from a JSR that wrote #80 to $A001, set A to whatever I skipped, then JSR'd to the original address, and all it did was crash the game. If all the code is structured right, then writing #80 to $A001 should never crash the game, right?
"Where are you going, soldier? Get back to the battle!" - Some Battlefront Clone
My YouTube Channel: https://www.youtube.com/channel/UCJqSB4xw29TTKlia5E0NYtQ

Cyneprepou4uk

Anything can crash the game if you don't know what are you doing. And I have no idea what are you doing here either. If you use fceux, forget about header and $A001 for a while

You won't get any particular answers if you just scream about your issues without any details.

Disch

That header is... not right.  You're changing the mapper from a standard MMC3 (mapper 4) to .... mapper 206?  Some weird Tengen/Namco mapper that mimics an MMC3?  No wonder you're having issues.  Aside from not having on-board PRG RAM, mapper 206 doesn't control mirroring or have IRQs, so I can't imagine this would work.


All you need to do is flip the SRAM bit on.


4E 45 53 1A 08 08 40 00 00  ...   original
4E 45 53 1A 08 08 42 00 00  ...   new
                   ^
                   |
             This is all you change


Reference on the header:
https://wiki.nesdev.com/w/index.php/INES



If you do this, and write $80 to $A001 some time shortly after startup, I see absolutely no reason why this wouldn't work.
I know Cynepropou4k is telling you not to worry about $A001, but I strongly disagree with that advice.


QuoteAny tips on how to find a good JMP routine to change to reroute to new SRAM-saving code?

For the $A001 code, that's something you'd want to do pretty much on startup.  So I would start looking at the disassembly by tracing from reset and see if there's somewhere you can squeeze a few extra bytes in.  Either by shrinking existing code, or finding some nearby unused space.  You'll only need 5 bytes so unless the game is VERY tightly packed you should be able to do it without TOO much hassle (depending on how confident you are on your 6502 knowledge).

For actually reading/writing stuff to SRAM, you'd just hook into whatever code is running when you want it to save/load a game.


QuoteAlso, I tried adding code to Pop Star Debut from a JSR that wrote #80 to $A001, set A to whatever I skipped, then JSR'd to the original address

If you're JSRing to get in, you'd want to RTS out.  RTS returns you back to the original JSR.
If you're JMPing in, then you'd need to JMP out.

If you're doing two JSRs the way you describe, you are kind of clobbering the stack.

Cyneprepou4uk

Quote from: Disch on November 18, 2019, 12:43:01 AM
I know Cynepropou4k is telling you not to worry about $A001, but I strongly disagree with that advice.

He is currently using fceux to test stuff. Even if he does manage to add a write at $A001, which he obviously can't right now because of lack of undersanding 6502, it won't make ANY difference for him.

So, on the contrary, don't listen to Disch  :police:  At least until you get... em, whatever you are trying to do... to work on fceux

KingMike

Well, Disch has written much of the mapper documents that NES-Dev is currently based on.
But Cyneprepou4uk's name is not familiar to me.
Just saying who I believe more presently. :)

Expecting SRAM to work without enabling it sounds like depending on emulator initialization to make your hack work, which is a bad idea, rather than adding "unnecessary" code to be SURE it is set how you want.
As I understand there are variations of MMC3 (and the Namco and Tengen mappers are related in that I THINK it was Namco that made the first variants and then they licensed it out to Tengen and Nintendo, or something like that), in that some supported SRAM, some did not, and with SRAM some had an enable switch and some always enabled).
Creating a problem with emulators which cannot tell which situation is correct (such as bus conflicts), with the feeling I got from Disch's doc is when it can not be determined which one a particular game should use: emulators should error on the side of compatibility (allow SRAM unless disabled by code), while writing code should error on the side of being precise (enable and disable SRAM as needed)?
"My watch says 30 chickens" Google, 2018

Bregalad

Quote from: KingMike on November 18, 2019, 02:12:07 AM
As I understand there are variations of MMC3 (and the Namco and Tengen mappers are related in that I THINK it was Namco that made the first variants and then they licensed it out to Tengen and Nintendo, or something like that), in that some supported SRAM, some did not, and with SRAM some had an enable switch and some always enabled).
If it's the Namco 108 mapper we're talking about (aka mapper #206), it was a very early very (1986) simplified pre-MMC3, which featuers less PRG- and CHR-ROM, no SRAM, no mirroring control nor scanline counter, etc...

But this mapper introduced writes to $8000 and $8001 to control bankswitching, and intorduced bankswitching in small amount (8kb PRG-ROM and 1kb CHR-ROM), which was unheard of before this came out. The concept were re-used and extended by Nintendo when they made their MMC3 in 1988. It's only used in a couple of games. Like Dish says, refrain from using this and use real MMC3 instead.

And like Cyneprepou4uk said, you should know what you're doing and not doing random stuff you don't understand.

Quoteemulators should error on the side of compatibility (allow SRAM unless disabled by code), while writing code should error on the side of being precise (enable and disable SRAM as needed)?
Most game just enable SRAM at some point in their start routine, it seems.

Supper

Quote from: Disch on November 18, 2019, 12:43:01 AM
That header is... not right.  You're changing the mapper from a standard MMC3 (mapper 4) to .... mapper 206?  Some weird Tengen/Namco mapper that mimics an MMC3?  No wonder you're having issues.  Aside from not having on-board PRG RAM, mapper 206 doesn't control mirroring or have IRQs, so I can't imagine this would work.

See this topic involving the mentioned problem with glitched graphics. The short version is that this game (LaSalle Ishii no Childs Quest) depends on having the correct hardwired nametable mirroring setup, and as it does in fact use mapper 206, I recommended changing both the header's mapper number and nametable mirroring bit to reflect the correct values for the cartridge.

But yes, if you're then going to be turning around and adding save RAM and a bunch of other whizz-bang features, you'll need to convert to the MMC3.

I'll leave the technical advice to the people who know the console best, but since you seem to be new to the concepts you're asking about, I would remark that the NES is really a terrible machine to try to learn them on. Sure, everyone loves the ol' Nintendo, but (thanks in part to that popularity) it's a complete mess of wildly differing mappers, crude and confusing hardware, emulator hacks, and what-have-you. I know "go do something else instead" isn't what you want to hear, but you'd have a much easier time learning the basics on something more orthogonal like the Game Boy and then using that as a basis for future work. Or at least in my case, figuring out the fundamentals on the Game Gear made it a lot easier to understand what was going on when I started working on the NES.

Disch

Quote from: Supper on November 18, 2019, 11:02:00 AM
See this topic involving the mentioned problem with glitched graphics. The short version is that this game (LaSalle Ishii no Childs Quest) depends on having the correct hardwired nametable mirroring setup, and as it does in fact use mapper 206, I recommended changing both the header's mapper number and nametable mirroring bit to reflect the correct values for the cartridge.

Okay, this makes a LOT more sense.  I apologize for getting all weirded out.  It looks like I was just missing the full context of what was going on.   :beer:

So it sounds like this isn't just adding SRAM to an existing game, this is also a full mapper conversion.

Polinym

Okay, so I think I'm going to worry about adding SRAM to this game until later, as its not essential. If you've got ideas on how to get that to work though, let me know! : ) I need to get back to work on the actual translating part of localization, and not get distracted by wanting to do technical stuff. Getting a bit tired of this stupid game, but it'll all be worth it in the end. : P Once I'm done with this, I think I'll look into doing GameBoy stuff like Supper suggested. Maybe I'll find some short, untranslated Game Boy JRPG and do that.
"Where are you going, soldier? Get back to the battle!" - Some Battlefront Clone
My YouTube Channel: https://www.youtube.com/channel/UCJqSB4xw29TTKlia5E0NYtQ