It is currently Thu Dec 13, 2018 11:26 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 8 posts ] 
Author Message
PostPosted: Sun Oct 07, 2018 4:47 pm 
Offline

Joined: Fri Oct 28, 2016 12:37 pm
Posts: 54
Hi everyone, I'm starting a slightly more advanced project so I'm using CNROM and have some questions around how bank switching is actually working. I got a full working example going, swapping out the banks with button presses and assembling on ASM6. PPU on emulator looks correct during the switches.

I started with a CNROM template by tokumaru (Thanks for putting this together)
viewtopic.php?t=6160

And I read through CHR bank switching on Nerdy Nights
http://nintendoage.com/forum/messagevie ... adid=17074

I looked on the forums here and the wiki but found a lot of material to assume context is understood (more of reference material or material specific to mappers I'm not using).

First a code snippet:

Code:
   LDA #$01
   JSR Bankswitch

.......................................

Bankswitch:
   TAX
   STA Bankvalues, x
   RTS

Bankvalues:
   .db $00, $01, $02, $03

.......................................

   ; VECTORS
   .org $FFFA
   .dw NMI
   .dw RESET
   .dw IRQ

   ; CHR-ROM BANKS
   .incbin "test1.chr"
   .incbin "test2.chr"
   .incbin "test3.chr"
   .incbin "test4.chr"


How does this black magic work? I know that method is used to get around bus issues but that seems very obscure. How would trying to write to ROM, which cannot be written to, change which CHR bank I'm using? And per the template my CHR includes are at the bottom of my file so how does the NES associate $00 with the first, $01 with the second, etc?

Second question is the emulator seems to jump to the first bank, is that expected on CNROM?

Finally, is it safe to swamp CHR banks while the PPU is doing stuff?


Top
 Profile  
 
PostPosted: Sun Oct 07, 2018 4:58 pm 
Online

Joined: Sun Apr 13, 2008 11:12 am
Posts: 7821
Location: Seattle
casprog wrote:
How does this black magic work? I know that method is used to get around bus issues but that seems very obscure. How would trying to write to ROM, which cannot be written to, change which CHR bank I'm using?
If you look at an actual CNROM board you'll see one additional part over a plain NROM board.

This additional part (a "latch") holds onto values that the CPU writes to ROM. It happens to be in the same conceptual place as ROM, and the ROM doesn't even shut up while we're talking to it. But none-the-less, it still reacts to when the CPU writes.

Because the ROM doesn't shut up, the game has to include a small table ("Bankvalues") and has to write the same value that the ROM will itself show, in order to guarantee that the CPU and ROM don't get in a fight, and make the value seen by the latch be useful.

Quote:
And per the template my CHR includes are at the bottom of my file so how does the NES associate $00 with the first, $01 with the second, etc?
That's an artifact of how emulators work. The actual physical board has two separate ROMs, and the first bank is just the beginning of the ROM that holds CHR.

Quote:
Second question is the emulator seems to jump to the first bank, is that expected on CNROM?
CNROM doesn't support more than 32 KiB of program data; there's nothing but the first bank.

Quote:
Finally, is it safe to swamp CHR banks while the PPU is doing stuff?
Yes.


Top
 Profile  
 
PostPosted: Sun Oct 07, 2018 7:09 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11012
Location: Rio de Janeiro - Brazil
casprog wrote:
How would trying to write to ROM, which cannot be written to, change which CHR bank I'm using?

It's kind of hack: the CPU has an output pin that indicates whether it's writing to or reading from memory. The CPU doesn't know which parts of memory are ROM and which parts are RAM, so it will output this signal regardless of the kind of memory it's trying to access. But the designers of the cartridge know that $8000-$FFFF contains ROM (meaning that a write signal is useless), so they wired the cartridge in a way that causes the CPU write signal for that range to be repurposed, and interpreted as a mapper write instead.

The reason you have to write the value to a ROM location containing the same value is that the ROM is still active during the write, so it will keep outputting data, and there will be a conflict if the ROM output doesn't match the value the CPU is trying to write, because they're both going through the same wires. There are other mappers that will use the CPU write signal to disable the PRG-ROM when a write occurs, so that there are no conflicts, but this requires extra parts and Nintendo wanted to keep it simple/cheap with CNROM, UNROM, and many of its mappers.


Top
 Profile  
 
PostPosted: Mon Oct 08, 2018 7:19 pm 
Offline

Joined: Fri Oct 28, 2016 12:37 pm
Posts: 54
Interesting, so that's why it doesn't matter where you write to within the $8000 - $FFFF range? As soon as you try to write to ROM, by design of the board, the NES knows you are performing a mapping operation / bank switch.

So when you write #$00 to ROM at a location where #$00 already exists, the output is basically guaranteed to be #$00 and the 'latch' then intercepts this value? After a write does the NES immediately perform a read from that location in memory and see #$00?


When I declare the following for my header:

Code:
CHR_COUNT = 4


The NES will expect 32KB for CHR right? so is that how four .incbin directives load up all the chr data needed into CHR ROM auto-magically? I'm assuming the assembler is looking for '.chr'? And do these includes always need to be under .org $FFFA?


Top
 Profile  
 
PostPosted: Mon Oct 08, 2018 7:28 pm 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 20870
Location: NE Indiana, USA (NTSC)
The ROM can't tell whether you're doing a read or a write. It just sees "chip enabled" and "$8000", assumes everything is a read, and outputs $00 on the data bus. In order not to confuse the latch, the CPU also has to output $00 on the data bus.

(Some boards take special ROMs that can tell a read from a write. CNROM isn't one of them. AOROM is.)

The fact that the .incbin commands for CHR data come immediately after the vectors in your assembly language file is a consequence of the iNES format being defined such that CHR ROM comes after PRG ROM, and vectors being the last thing in the PRG ROM.


Top
 Profile  
 
PostPosted: Mon Oct 08, 2018 8:31 pm 
Offline

Joined: Fri Oct 28, 2016 12:37 pm
Posts: 54
Gotcha, appreciate the details. It helps much more to understand a little about how this works under the hood. Thanks!


Top
 Profile  
 
PostPosted: Mon Oct 08, 2018 10:15 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11012
Location: Rio de Janeiro - Brazil
casprog wrote:
Interesting, so that's why it doesn't matter where you write to within the $8000 - $FFFF range? As soon as you try to write to ROM, by design of the board, the NES knows you are performing a mapping operation / bank switch.

Exactly. More complex mappers with more than one register you can write to will snoop more address lines and select a register based on the address being accessed.

Quote:
So when you write #$00 to ROM at a location where #$00 already exists, the output is basically guaranteed to be #$00 and the 'latch' then intercepts this value?

Yes, if both the CPU and the ROM chip are putting the same value on the data bus, the result is guaranteed to be that value, and that's what'll get picked up by the latch.

Quote:
After a write does the NES immediately perform a read from that location in memory and see #$00?

Not "after", but "during". The CPU will fetch the write instruction from the ROM, and then output the target address to the address bus, then the value to the data bus, and will finally activate the write signal. The PRG-ROM completely ignores the write signal, and upon seeing the address on the address bus it will proceed to fetch the value at that address and output it AT THE SAME TIME as the CPU is outputting the value to be written. The mapper will then pick up on the write signal and copy the value on the data bus (which is a mix of what the CPU and the ROM are outputting) to the latch.

Quote:
When I declare the following for my header:

Code:
CHR_COUNT = 4


The NES will expect 32KB for CHR right?

I put that at the top to make a few configurable parameters easier to change, so you don't have to mess with the statements that generate the iNES header a few lines down, but that's exclusively for the header, and the header is only used by emulators. An emulator will use those size fields to know how much PRG and CHR to load from the .NES file, but a real NES doesn't "load" anything, the chips are just sitting there and they each have a certain capacity and that's that, there's no need to specify the size of anything in a real cartridge.

Quote:
so is that how four .incbin directives load up all the chr data needed into CHR ROM auto-magically? I'm assuming the assembler is looking for '.chr'?

An .NES file is just a "mirror" of the 2 chips contained in an NES cartridge, the PRG-ROM and the CHR-ROM, of the data that is permanently in those chips. Emulators have to load that data into their internal memory to simulate the chips, but on a real NES there's absolutely no "loading" of any kind.

Also, .CHR is only a file extension that helps us know what a file contains, but it doesn't enforce any particular format to a file... you could just as well use .BIN, .TXT, .EXE, whatever, as long as the actual contents were NES tiles. Even if you .incbin a file that doesn't contain NES tiles, as long as the size is right (exactly 8KB), the result will still be a valid NES ROM, but when run, the graphics will look like total garbage, because some other kind of arbitrary data will be interpreted as if it was CHR data.

Quote:
And do these includes always need to be under .org $FFFA?

Like tepples said, that's just because the vectors are the last thing in the PRG-ROM, and the CHR-ROM data immediately follows the PRG in the .NES format.


Top
 Profile  
 
PostPosted: Tue Oct 09, 2018 5:01 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 20870
Location: NE Indiana, USA (NTSC)
tokumaru wrote:
An emulator will use those size fields to know how much PRG and CHR to load from the .NES file, but a real NES doesn't "load" anything, the chips are just sitting there and they each have a certain capacity and that's that, there's no need to specify the size of anything in a real cartridge.

The one exception to this being CF or SD adapters like the PowerPak and EverDrive. They load the ROM from a file on the memory card and then emulate the cartridge using two RAMs and an FPGA. Though the cartridge is emulated, the Control Deck is not. Once the adapter's system software passes control to the game, the game likewise sees chips that "are just sitting there".


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 8 posts ] 

All times are UTC - 7 hours


Who is online

Users browsing this forum: Google Adsense [Bot] and 4 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group