This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

need help with a bit of understanding

need help with a bit of understanding
by on (#47345)
I need help in understanding a couple of things.

I'd first like to say that I have a fairly good fundamental understanding of processors behind me, and there's quite a bit of documentation about the NES out there (very helpful!), and I've read much of it, but there are still just a FEW little cracks that I'd like filled.

Okay, so apparently the PRG-ROM code gets copied into addresses $8000 - $FFFF. I just want to know - when you're writing the NES assembly code (or when my emulator READS this code, more accurately), and you are telling the program counter to go to an address (any way you want, let's say for example the easiest way -- JMP $xxxx, the jump absolute instruction), what address should you put in for the $xxxx?

That question was confusing. It's from an NES developer's point of view, if that helps. I'll try again: If I am developing an NES ROM, and I want to tell the program counter to go somewhere, shouldn't I ONLY be telling it to go to $8000 - $FFFF? Is this the ONLY place where program CODE, not RAM, is kept? And therefore I shouldn't be writing any JMPs to $05F0, in the RAM, or like $3000, in the I/O registers? I only ask because I disassembled a ROM file to make sure of this and I see a bunch of absolute jumps to RAM & I/O. Granted there are few so it could just be unused junk code, I can accept that. Just wondering if this is absolute -- ONLY there? And I'm also wondering what it does do with an instruction that leads to the program counter being invalid, like $0000 or something.

I guess another thing I'm wondering about is those memory mappers to switch the ROM banks .. first of all, is this done by code? Second of all, either way, how is it done??

Thanks a lot, I'd appreciate the help.

by on (#47346)
Welcome!

First of all, yes, you generally only want JMP or JSR instructions (or any PC relocation instruction for that matter) to point to $8000-$FFFF. You wouldn't really want to point it to $2000 or something, but you COULD point it to RAM if you copied code to RAM. I think a few games do this, actually, and I've done it myself. RAM is always $0000-$07FF, but if you have SRAM/WRAM (interchangeable terms, though usually SRAM refers to extra RAM that is battery packed and WRAM might indicate absence of battery) it is at $6000-$7FFF.

The bankswitching question is a little harder to answer. Basically, depending on the mapper you write to certain registers specific to that mapper (for example, $8001 for MMC3) to switch banks. These registers are usually ROM addresses, but writing to those addresses does not affect the data at that address. I'm not exactly sure how to explain that one, but if you read documentation on MMC1 or something it will explain more.
Re: need help with a bit of understanding
by on (#47348)
Raiden the Quick wrote:
apparently the PRG-ROM code gets copied into addresses $8000 - $FFFF.

No, it's not copied. There is no "copying" involved, the ROM contents are just seen at that location. No copying (can't say that enough).

Quote:
when you're writing the NES assembly code (or when my emulator READS this code, more accurately), and you are telling the program counter to go to an address (any way you want, let's say for example the easiest way -- JMP $xxxx, the jump absolute instruction), what address should you put in for the $xxxx?

The processor will try to execute whatever the program counter points to, anything from $0000 to $FFFF. It may not succeed if it doesn't find valid instruction, but it will try.

Quote:
It's from an NES developer's point of view, if that helps. I'll try again: If I am developing an NES ROM, and I want to tell the program counter to go somewhere, shouldn't I ONLY be telling it to go to $8000 - $FFFF? Is this the ONLY place where program CODE, not RAM, is kept? And therefore I shouldn't be writing any JMPs to $05F0, in the RAM, or like $3000, in the I/O registers?

As long as there is a program at the location the processor is trying to execute, it will succeed. Nothing stops the programmer from manually copying code to RAM and jumping to that code. This is usually done when modifiable logic is necessary. However, trying to execute registers will never result in anything good. Values returned by registers are usually not valid instruction, and even if they were they would not be part of any actual logic. You can JMP to registers, but don't expect anything meaningful to happen.

Quote:
I only ask because I disassembled a ROM file to make sure of this and I see a bunch of absolute jumps to RAM & I/O.

You are probably disassembling data. Remember that PRG-ROM doesn't hold only program code, it also holds level maps, animation scripts, and all sorts of data. When disassembling that, some of the output will look like instructions, but they were not put in the ROM as such, and under normal circunstances they are not executed.

Quote:
And I'm also wondering what it does do with an instruction that leads to the program counter being invalid, like $0000 or something.

There is no invalid value for the program counter. There are 16 bits in the address bus, for a total of 65536 possible addresses, and the CPU will try to execute every single one of them if it's pointed to them.

Quote:
I guess another thing I'm wondering about is those memory mappers to switch the ROM banks .. first of all, is this done by code? Second of all, either way, how is it done??

You'd know the answers to most of these questions if you knew more about computer architecture. When I first came here I had a lot of the same questions, but since then I learned a lot. i'll try to explain as quickly as I can:

Memory chips (ROM and RAM) have bytes stored in them. In order to access the bytes in them, the computer (in this case, the NES) tells the chip the address it wants to read through the address bus (16 wires that form a 16-bit address). After a little while the chip puts in the data bus (8 wires) the value stored at that location. To write to RAM the computer puts the address and the data on their respective buses, and enables a "write" line, so that the RAM chip knows it's supposed to save a value rather than output one.

Anyway, once you know that the address bus is just a group of 16 wires (or traces on a board, whatever) it's easy to understand how memory mapping works. If you understand binary numbers (if you don't you'll not get this) you'll understand that by looking at some of these wires/lines it's possible to know what address is being acced at a certain point in time.

Why do you think the memory map is conveniently divided in half (the upper half is ROM, the bottom half has RAM and registers)? Look at the binary representation of the addresses:

Code:
Lower half:
$0000 = %0000000000000000
(...)
$7FFF = %0111111111111111

Upper half:
$8000 = %1000000000000000
(...)
$FFFF = %1111111111111111

What is the difference you see between the two ranges? The highest bit, bit 15, indicates which half is being accessed (it's clear when accessing the lower half and set when accessing the upper half). The NES uses this bit to decide whether to access memory on the cart or it's own internal memory (RAM) and registers. This is why I told you nothing is "copied", the NES just makes sure the appropriate data is seen at each accessed location.

Bankswitching is also related to addresses. Only 32KB of memory are reserved for PRG-ROM, because the NES sends 15 address lines to the cart. So, 32KB is all the NES will be seeing at any given moment. However, carts can have much larger ROMs, with more than 15 address lines. Since the chip has more address lines than are actually available to the cart, what do we put in those lines? Well, this is bankswitching. These carts will also have a chip to hold what should be put into those extra address lines. By manipulating those values as the program runs, it's possible to see different sections of the large chip.

OK, tired of typing now. Gotta go.
Re: need help with a bit of understanding
by on (#47357)
tokumaru wrote:
Raiden the Quick wrote:
apparently the PRG-ROM code gets copied into addresses $8000 - $FFFF.

No, it's not copied. There is no "copying" involved, the ROM contents are just seen at that location. No copying (can't say that enough).

Unless, of course, your code is running on a PowerPak or a Famicom Disk System RAM cart.

by on (#47375)
Thanks a ton, that helps clear a lot of things up.

I had a feeling this was the case, just had to be sure: Nothing is absolute. They're just bytes. The programmer is responsible for deciding what to do with certain bytes in certain areas.

Also, for the memory mappers...thanks tokumaru, that's a good start for me, and I now see that there is lots of MMC# documentation on the main page of this site. I shall consume, thanks!

by on (#47377)
Raiden the Quick wrote:
Also, for the memory mappers...

You know what really made me understand memory mappers? Looking at an actual cart while making an UNROM dev cart. If you ever get the chance to look at an UNROM board, check online for the pinouts of the chips and look at how they are connected. Suddenly it all made sense to me.

by on (#47392)
Celius wrote:
The bankswitching question is a little harder to answer. Basically, depending on the mapper you write to certain registers specific to that mapper (for example, $8001 for MMC3) to switch banks. These registers are usually ROM addresses, but writing to those addresses does not affect the data at that address. I'm not exactly sure how to explain that one, but if you read documentation on MMC1 or something it will explain more.


I read some documentation and I now understand this. Certain areas in memory, when written to, act as, instead of writing data like you'd think, a way to control the memory mapping chip (bank switching, method of switching, which bank(s), etc.). Haha and ROM is read-only so it can't be written to anyway, so it makes sense that that was chosen as the method of control for some chips.

Thanks again for both of your help. It is pretty simple actually, just gotta learn the specifics of each chip (which is documented).

by on (#47657)
I'm just gonna flood this thread with questions, if no one minds.

What happens if you write to a memory location that maps to a read-only register, like $2002? Is it truly read-only or can you overwrite the data there if you decide?

What happens if you read from a memory location that maps to a write-only register, like $2005? What would it read, and what would happen to the register you're reading into (the X register, for example)?

Thanks.

by on (#47658)
Raiden the Quick wrote:
What happens if you write to a memory location that maps to a read-only register, like $2002? Is it truly read-only or can you overwrite the data there if you decide?


Nothing happens. The write just sits on the bus. It has no effect.

Quote:
What happens if you read from a memory location that maps to a write-only register, like $2005? What would it read, and what would happen to the register you're reading into (the X register, for example)?


You'd get open bus. X would be set to the last value that was placed on the bus. Often, this is the high byte of the address read from.

by on (#47660)
Thanks!

by on (#47694)
Few more questions, if you don't mind (you've all been extremely helpful, thanks)

Are the pattern tables ($0000 - $1FFF) read-only?

Does the CHR-ROM get mapped or actually copied into $0000 - $1FFF (whether it's being loaded in from startup, or memory mapped, or whatever)?

I guess I would initially think "no, the pattern tables are not read-only" since I read that you can write to them during execution. I would also initially think "no, it's not being copied, it's being mapped". However, how can you write to $0000 - $1FFF if the CHR-ROM banks are being mapped in and not copied? Does it disable you from writing to that location (or at least..nothing will happen, as discussed above) if CHR-ROM banks are being mapped in? Does it make the pattern tables read-only if CHR-ROM banks are being mapped in?

by on (#47696)
No, $0000-$1FFF of PPU memory isn't necessarily read only. Some games use CHR-RAM because the programmers thought it would be more beneficial to be able to write data directly to that memory region instead of having it all ROM. For example, some games have clouds and stuff move by shifting the graphical data within each tile manually instead of having it all pre-defined. But if you use CHR ROM, you can't write to $0000-$1FFF; it's all predefined, aka Read Only Memory.

CHR-ROM gets mapped to $0000-$1FFF just like PRG-ROM. If you use an 8k RAM chip in place of CHR ROM (aka CHR-RAM), that data will be mapped to $0000-$1FFF at all times, and you can do writes to that chip to change graphical data. It just depends on whether or not you have a ROM or RAM chip.

by on (#47697)
Okay, that helps, thanks.

So I'll say it's assumed that if CHR-ROM bank count is 0, put RAM there instead? Is this a safe assumption?

by on (#47699)
That's actually how the iNES header format works. If you make a NES ROM with 0 as the CHR ROM bank count, emulators will assume that the game has 8 kilobytes of CHR RAM. However, there has been talk that the iNES format is flawed, as games COULD have more than 8k of CHR RAM (I don't know if any mappers support this though). This might be a little difficult to understand, but basically that means it's just a RAM chip that's bigger than 8k so bankswitching can be performed to select different 8k sections to map to $0000-$1FFF. That way you can write to different 8k sections when the screen is completely shut off and just do an instant bankswitch to change graphical data immediately.

But yes, if you're relying on the current iNES header format, "0 CHR ROM banks" means "8k of CHR RAM".

by on (#47700)
Okay, cool.

No, that makes perfect sense actually. Just like ROM but instead RAM bankswitching.

by on (#47701)
Just a small addition to what was said about CHR memory:

Most game consoles back then used internal RAM for storing tiles, but the NES doesn't have any internal RAM dedicated for that (although through recent discussion it was concluded that it should be possible to use 1KB of name table memory as pattern memory, for a total of 64 tiles). Instead, all accesses to pattern memory are routed to the cartridge, which gives the people that make cartridges the option to use ROM or RAM for that memory.

Raiden the Quick wrote:
Just like ROM but instead RAM bankswitching.

Exactly. Though very few commercial games did this, it is still a possibility.

by on (#47703)
Celius wrote:
However, there has been talk that the iNES format is flawed, as games COULD have more than 8k of CHR RAM (I don't know if any mappers support this though).

Any mapper that can bankswitch CHR can bankswitch CHR RAM. For instance, CPROM (used only in Videomation) can bankswitch one of the 4 KB halves of the game's 16 KB CHR RAM. But I still don't know if any mappers used for more than one game during the NES's commercial era have more than 8 KB CHR RAM.