MMC2 question

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
MMC2 question
by on (#86186)
wiki wrote:
When the PPU reads from specific tiles in the pattern table during rendering, the MMC2 sets a latch that tells it to use a different 4 KB bank number. This has the effect of setting a different bank for all tiles to the right of a given tile.


Well, does "PPU reads" stand for "reading $2007" or "fetching tiles from the pattern table"?

by on (#86187)
"During rendering" means "fetching tiles from the pattern table". But I'd hazard a guess that the MMC2 looks only at the address bits PA12-PA4, so either rendering or a $2007 readback would trigger it.

by on (#86188)
You can manually trigger it to switch by reading from the pattern tables with $2007 or if the PPU itself reads the memory. Basically it's all about that memory being read from. If the CPU does it through $2007 or the PPU by rendering, both will make the MMC2/MMC4 bankswitch CHR-ROM.

by on (#86341)
Could someone clarify this thing, please?

Disch' docs wrote:
The swap occurs after the tile is fetched, not before. So if the latch is clear, and tile $FE is loaded,
tile $FE from the first reg (??) will be drawn to the screen, but the next tile drawn will be from the second reg.


What "1st reg" and "2nd reg" ??

by on (#86342)
"First reg" is before the swap
"second reg" is after the swap.

For example.... assuming:
- $B000=$00
- $C000=$01
- latch is set so that $B000 is used

When you come across tile $FE, it will be rendered from page $00. The next tile will be rendered from page $01

by on (#86345)
I didn't get it. :( Did you mean "latch is set so that $C000 is used"?

I'm trying to remove my working MMC2 "hack":
Code:
      bg_tile = bg_name[attrnum+1];
      if(bg_tile != last_tile)
      {
         if(ppu->bg_pat_table) mmc2_sync_chr(0x1000|(last_tile << 4));
         else mmc2_sync_chr(0x0000|(last_tile << 4));
         last_tile = bg_tile;
      }


Currently, the ring is glitched. It's ok for a few frames, but it glitches.

by on (#86347)
Zepper wrote:

Currently, the ring is glitched. It's ok for a few frames, but it glitches.


When I was debugging MMC2, I hacked things a tad so that the output was highlighted when the second bank was switched in. You might want to do this to see what bank is being switched in during rendering.

Famicom Wars (MMC4, the CHR switching is identical) switches banks many times per frame, and even switches banks for a few characters to render text.

You must use A5 to select which bank you're going to use, and A4-A13 to trigger the switch. The chip looks for 0P 1111 11XX xxxx (from A13 down to A0). Where P is the page#, XX is the XOR of these two bits, and xxxx are don't care bits.

This corresponds to:

Code:

// PPUREAD is true when the PPU has read the CHR data byte
// PPUADD is the 14 bit PPU address you're reading from
// bank0 returns 0/1 depending on which bank you need to use
// bank1 works similar to bank0

if (PPUREAD)
{
    switch(PPUADD & 0x3ff0)
    {
        case 0x0fd0 : bank0 = 0; break;
        case 0x0fe0 : bank0 = 1; break;
        case 0x1fd0 : bank1 = 0; break;
        case 0x1fe0 : bank1 = 1; break;
    }
}





bank0 indicates which of the two banks you must use for PPU addresses 0000-0fff (i.e. mapper register B000 or C000). 0 = B000, 1 = C000

bank1 indicates which of the two banks you must use for PPU addresses 1000-1fff (i.e. mapper register D000 or E000). 0 = D000, 1 = E000

NOTE: you cannot just set the bank inside the case statement. If you do this, it's possible that the game code can select one bank and then clear the screen, select another bank and write graphics without having an FD/FE character. This would result in the wrong bank being displayed, because the game updated the bank # without using switch characters (FD/FE).

by on (#86349)
Quote:
If you do this, it's possible that the game code can select one bank and then clear the screen, select another bank and write graphics without having an FD/FE character. This would result in the wrong bank being displayed, because the game updated the bank # without using switch characters (FD/FE).


Can you elaborate on this? I'm not sure I get what you mean.

by on (#86415)
Still not clear to me. I did a quick chat on irc about it (with him) and...

1. The PPU fetch a tile.
2. If this tile is $FD or $FE, the mapper "should" look at the PPU address ANDed with 3FF0. In short words, the latches are changed on $1FDx/$1FEx addresses.
3. He suggested to NOT swap banks during rendering in an emulator, basically it's possible "to break" the CHR pointers, of doing something you wouldn't like to do. So, he suggested to swap banks on $C000-$FFFF writes.

I'm out of ideas.

by on (#86418)
Quote:
He suggested to NOT swap banks during rendering in an emulator


Err.. don't you have to? The PPU reads during rendering are what triggers the latch change, which triggers the swap.

It's not that complicated. Here's another example. Given the below situation:

regs:
$C000 = $00
$D000 = $01

latch = $FD (use reg $C000)


Now let's say the PPU has to draw these tiles:

AA FE AA

Events occur as follows:

- PPU fetches lo CHR byte from $0AAx (mapper use bank $00 : $C000)
- PPU fetches hi CHR byte from $0AAx (mapper use bank $00 : $C000)
- PPU fetches lo CHR byte from $0FEx (mapper use bank $00 : $C000)
- PPU fetches hi CHR byte from $0FEx (mapper use bank $00 : $C000)
-- mapper changes latch to $FE, now it will use $D000 instead of $C000 --
- PPU fetches lo CHR byte from $0AAx (mapper use bank $01 : $D000)
- PPU fetches hi CHR byte from $0AAx (mapper use bank $01 : $D000)

by on (#86422)
Sorry, but I'm not understanding the things. :( Here's the relevant part.

Code:
[23:00] <@kevtris> the mapper specifically looks at the address bus of the PPU and nothing else
[23:00] <@kevtris> it knows a particular tile is being read, because its address is directly related to the tile # (or sprite tile # for that matter)
[23:01] <@kevtris> in both cases it's xTTx  where x is don't care and TT is the tile number.  i.e. 0331h, 0332h,  0339h, 033fh  are all reading tile 33h
[23:01] <@kevtris> each tile is 16 bytes exactly
[23:01] <@kevtris> thus, the lower 4 bits do not matter to the mapper and it does not look at them
[23:02] <@kevtris> that's why I chose 0fd0h  and 0fe0h and 1fd0/1fe0h  on the case statement
^C01[23:02] <Zepper> but you said "you cannot just set the bank inside the case statement".  That's why i asked you about bankswitching during rendering.
[23:02] <@kevtris> aah that
[23:02] <@kevtris> well that is an emulator caveat
[23:03] <@kevtris> the code I posted will only update bank0/bank1 when tile fd or fe is read
[23:03] <@kevtris> if you do something like this, it will not work:
[23:04] <@kevtris> switch (PPUADD & 0x3ff0)
[23:04] <@kevtris> {
[23:04] <@kevtris> case 0x0fe0 : ppubankhandle = rombanknumber[regc000];
[23:04] <@kevtris> i.e. if you bankswitch via adjusting pointers in the switch/case it will not work right
[23:05] <@kevtris> you adjust pointers in a routine that occurs when a switch happens OR the program writes to c000-ffff

by on (#86426)
I don't know how else to explain it. Maybe pseudocode?

Code:
if( tile_FD_was_just_drawn )
{
  latch = false;
  Sync();
}
else if( tile_FE_was_just_drawn )
{
  latch = true;
  Sync();
}

//...

void Sync()
{
  if(latch)
    SwapCHR( reg_D000 );
  else
    SwapCHR( reg_C000 );
}


Tiles FD and FE switch the latch so that the other CHR reg is used. Effectively causing CHR to automatically swap during rendering.

by on (#86427)
The ring and the boxer (not Mac) are glitched. Everything else is fine.
Image

The "PPU address" here (to set up latches/swap chr) is 2000_bg_pattern | (bg_fetched_tile << 4).
My gfx engine never touches the real PPU address, like incrementing or decrementing it on rendering.

by on (#86430)
Can you post some code?

by on (#86471)
It's possible that the game switches CHR banks during cycles 256~340. I'm still tracing my logs. From a preliminary analysis, the ring uses the high latch = 0, but the emu sets to 1, breaking the ring gfx banks.

by on (#86495)
Are you fetching the 34th tile? The game might rely on it, even if it is never rendered.

by on (#86502)
That's probably the golden point for bankswitching, somewhere between cycles 256~323. AFAIK, it never access $2007, as mentioned "there and there". :)

Update: the game does NOT glitch if a tile fetch occurs on dot 256 (257th pixel). Works fine. The game fetches tiles $FD->$FE->$FD->$FE->... and if the fetch at 256 isn't allowed, it glitches.

by on (#86505)
Every fetch is allowed. It doesn't matter when it is. Any fetch from anywhere (rendering or $2007) at any time will cause the swap.

Although I don't know why there would be a fetch at 256. Isn't that a NT fetch?

by on (#86506)
Someone else must try out. Perhaps it's a problem with my gfx engine.

by on (#86508)
The fetch should occur after the 8th and final pixel of the FD/FE tile is drawn. It's pretty straight forward in that respect. But I am not sure how you render graphics. Someway you need to implement so that after the tile pattern fetch for the 8 pixels of tile FD/FE for it to trigger the bankswitching. Switching apon reads through $2007 is not important to get PunchOut working properly as I remember.

by on (#86523)
My sprite code matches the hardware procedure perfectly; the BG render no. I just take the current ppu address and fetch the data for that pixel. I know that a rewrite should be required for fixing a few other issues, but I couldn't yet.

by on (#86538)
Well the boxers are rendered with sprites, so if they are messing up, then something is wrong with your MMC2 code or your sprite fetching.

by on (#87153)
i've got a problem with MMC2 in my emulator as well. it works absolutely perfectly and looks correct right up until it switches to the ring with the fight starting.

at that point, it looks like this:

Image



what is so different between before the fight and during the fight? the location of the tile that triggers the switching?

by on (#87154)
i partially fixed it myself, yeah i had to read the tile that was to the right of the screen. fetching that fixed the background, but i still have a problem with the sprites:

Image

by on (#87155)
I fixed the sprites by doing CHR bankswitches during cycles 256~320 of sprite fetches. Since the game uses 8x8 sprites, you should check the secondary OAM, the tile number stored there.

by on (#87194)
that helped, thanks! it works correctly now.

by on (#87198)
How exactly did you fix the ring?

by on (#87222)
i wasn't fetching the pattern data for the invisible tile just off the right side of the display, which the real NES accesses even if it isn't shown. that's the one that triggers the CHR swap.

by on (#91320)
miker00lz wrote:
i wasn't fetching the pattern data for the invisible tile just off the right side of the display, which the real NES accesses even if it isn't shown. that's the one that triggers the CHR swap.


I was kinda harsh with you guys, my apologies. Anyway, for my gfx engine, I had to render (but not displaying it) the 257th pixel, in order to fetch the 34th tile. Punch Out seems to be working ok.

by on (#91346)
That is one way to fix it and it doesn't really effect anything for the user, atleast not negatively.