MMC5 Hacking Adventures

Discuss hardware-related topics, such as development cartridges, CopyNES, PowerPak, EPROMs, or whatever.

Moderator: Moderators

User avatar
Ben Boldt
Posts: 1148
Joined: Tue Mar 22, 2016 8:27 pm
Location: Minnesota, USA

Re: MMC5 Hacking Adventures

Post by Ben Boldt »

New discovery:

It turns out that "RD Step 3" does not require a matching PPU address, or even a PPU address in range. In fact I set PPU address to 0x0000 and it still did the big loop around to the top. This finding removes 1 of the arrows from the diagram.
in_frame_bit_states_4.PNG
Edit:
I have checked all arrows now and I believe that this diagram is correct. Additional things not shown or tested in this diagram:
  • Any potential interaction with PPU /WR (none known)
  • Reading from each of the 2 V-blank interrupt vector bytes from each of the 6 green boxes
  • Any interaction with SL3 (pin 98), I am completely clueless on this one.

Edit 2:
I added the v-blank FFFA/B reads to the diagram. To make the arrows lay out better, I moved RD step 0.

Testing FFFA/B in each state divulged an interesting new connection from the 3rd /RD delay state back to the initial state. Normally, from state "RD step 3", falling edge of /RD always leads a 'good' sequence of green boxes (regardless of PPU address range), after which, another good or bad sequence of green boxes must also occur. However, if you sneak in a read from FFFA/B in "RD step 3", then it then it DOES matter if the PPU address is in range. It can go directly to the bad sequence of green boxes. This observation corresponds to a state change to the initial state.

The diagram did get a little more busy with the FFFA/B stuff added, but no previous findings were changed.
in_frame_bit_states_5.PNG
Edit 3:
There is still more going on here with reading $FFFA/B. Say that I am in a "step 0" green box. The PPU address is in range and I don't touch it. Then I read CPU bus FFFA. I switch the CPU bus back to reading the status register and the status bit went to 0, as expected. Then on the 3rd falling edge of PPU /RD, the status gets set again. Coincidentally, this is the same spot it would have been set again going through the normal process of /RD delay had none of this ever happened, which leads me to believe the /RD delay states operate independently of what is going on elsewhere in this diagram. I tested this by reading FFFA within a "step 1" box. Now it was the 2nd falling edge to set the status again, which again coincides where it would have been set with normal /RD delay. I think this proves that the state of the RD delay must be operating independently from the other stuff that is going on.

I am thinking that this is going to blow up a little more complicated until we start to notice more patterns, then we can simplify it back down into its more general state of existence. I really truly believe that this is just a counter or two and some gates.
User avatar
Ben Boldt
Posts: 1148
Joined: Tue Mar 22, 2016 8:27 pm
Location: Minnesota, USA

Re: MMC5 Hacking Adventures

Post by Ben Boldt »

It is still pretty early on this new theory but it is making a lot of sense. I finally got rid of the duplicated 3 green / 1 blue box rows.
in_frame_bit_states_6.PNG
Edit 1:
Issue found: The big top arrow on the gray boxes causes the /RD delay to always occur after any time the status bit is cleared. We have clearly observed the delay is skipped if the next PPU address is in range. So something is wrong with that arrow.

Edit 2:
Additional tweaks made:
in_frame_bit_states_7.PNG
Edit 3:
I am still feeling pretty good about this one, except 1 observation I had before that is not captured. With PPU /RD held low, I observed that the status bit could not be cleared with M2 toggles. But with PPU /RD held low, it could be cleared with reading 0xFFFA/B.

Edit 4:
More tweaks per edit 3:
in_frame_bit_states_8.PNG
User avatar
Ben Boldt
Posts: 1148
Joined: Tue Mar 22, 2016 8:27 pm
Location: Minnesota, USA

Re: MMC5 Hacking Adventures

Post by Ben Boldt »

Thanks for updating the arrows in my new MMC5 ASCII pinout in the wiki Lidnariq, I didn't realize how incomplete I left that!

Here are some tests that I did today to test the state diagram, especially focusing on reading from vblank interrupt vector address $FFFA. I am thinking that the diagram is just about exactly right now -- I am not able to find any example that disagrees with the diagram anymore. I have not tried anything with PPU /WR; maybe I should try toggling that pin from each state. I am open to any ideas for additional tests.

I am thinking it is quite likely that the clock of the top state machine is /RD 'and' M2. This would explain why /RD held low prevents M2 toggles from clearing status. If this is true, M2 step 1 should also not transition back to step 0 if M2 remains low after entering step 1, then /RD goes low and back high, and then finally M2 goes high. If true, 2 additional M2 falling edges with /RD high should clear the status in this case. I will test that tonight.

Test 1: RD state machine unaffected by reading FFFA/B, while in M2 step 0.

1. Set M2 = 1, R/W = 1, /RD = 1.

2. Get into this state:

*000
000*

- Set PPU address = $2000
- Set PPU /RD low and then high, 5 times

3. Set CPU address = $FFFA, then back to status register.

- Observation - status bit cleared.

Hypothetically, we are in this state:
000*
000*

4. Set /RD low (PPU address still $2000 and /RD still high since step 2.)

- Observation - status bit did set again right away.

*000
000*



Test 2: RD state machine unaffected by reading FFFA/B, while in M2 step 1.

1. Set M2 = 1, R/W = 1, /RD = 1.

2. Get into this state:

0*00
000*

- Set PPU address = $2000
- Set PPU /RD low and then high, 5 times
- Set M2 low and then high, once.

3. Set CPU address = $FFFA, then back to status register.

- Observation - status bit cleared.

Hypothetically, we are in this state:
000*
000*

4. Set /RD low (PPU address still $2000 and /RD still high since step 2.)

- Observation - status bit did set again right away.

*000
000*



Test 3: RD state machine unaffected by reading FFFA/B, while in M2 step 2.

1. Set M2 = 1, R/W = 1, /RD = 1.

2. Get into this state:

00*0
000*

- Set PPU address = $2000
- Set PPU /RD low and then high, 5 times
- Set M2 low and then high, twice.

3. Set CPU address = $FFFA, then back to status register.

- Observation - status bit cleared.

Hypothetically, we are in this state:
000*
000*

4. Set /RD low (PPU address still $2000 and /RD still high since step 2.)

- Observation - status bit did set again right away.

*000
000*



Test 4: RD state machine unaffected by reading FFFA/B, while in M2 step 3.

1. Set M2 = 1, R/W = 1, /RD = 1.

2. Get into this state:

000*
000*

- Set PPU address = $2000
- Set PPU /RD low and then high, 5 times
- Set M2 low and then high, 3 times.

- Observation - status bit on 3rd M2 falling edge.

3. Set CPU address = $FFFA, then back to status register.

- Observation - status bit stayed clear.

Hypothetically, we have remained in this state:
000*
000*

4. Set /RD low and then high (PPU address still $2000 and /RD still high since step 2.)

- Observation - status bit did set again right away.

*000
000*



Test 5: RD falling edge affecting step 3 in both state machines at the same time.

1. Set M2 = 1, R/W = 1, /RD = 1.

2. Get into this state:

*000
000*

- Set PPU address = $2000
- Set PPU /RD low and then high, 5 times

3. Set CPU address = $FFFA, then back to status register.

- Observation - status bit cleared.

Hypothetically, we have are in this state:
000*
000*

4. Set PPU address = $1FFF (different and out of range)

5. Set /RD low and then high (/RD had still been high since step 2.)

- In theory, the M2 state machine uses the "current state" of the RD state machine.
- Observation - status bit did set again.

Theory not disproved. Hypothetically, the current state is now:
*000
*000

6. Return to this state:
000*
*000

- Set CPU address = $FFFA, then back to status register.
- Observation - status bit cleared.

7. Set /RD low and then high (/RD had still been high since step 5, PPU address still $1FFF since step 4.)

- status remains cleared.

000*
*000

8. Set PPU address in range ($2000)

9. Set /RD low and then high 5 times

000*
0*00

000*
00*0

000*
000*

*000
000*

- Observation - Status became set on the 4th falling edge of /RD.
User avatar
Ben Boldt
Posts: 1148
Joined: Tue Mar 22, 2016 8:27 pm
Location: Minnesota, USA

Re: MMC5 Hacking Adventures

Post by Ben Boldt »

Okay, I am pretty sure I found something that does not match the "top state machine clocked by M2 'and' /RD" theory. :(

Test 1:
The test wrote: PPU address = $2000 for entire test.

1. Get into this state, /RD and M2 high:

*000
000*

2. Set M2 low and keep it low. State should now be:

0*00
000*

3. Toggle /RD lots of times, ending up high. If the clock of the top state machine is /RD 'or' M2, states should be unaffected:

0*00
000*

4. Set M2 back high. States still unaffected

0*00
000*

5. Set M2 low and back high.

00*0
000*

6. Set M2 low and back high a 2nd time

000*
000*
Sure enough, this test passed, status went low after 2nd M2 toggle at the end.

HOWEVER: if I modify the test so that test step 2 starts in M2 step 2:
The test wrote: PPU address = $2000 for entire test.

1. Get into this state, /RD and M2 high:

*000
000*

1b. Set skip to next state in top state machine by setting M2 low and back high once.

0*00
000*

2. Set M2 low and keep it low. State should now be:

00*0 (?)
000*

3. Toggle /RD lots of times, ending up high. If the clock of the top state machine is /RD 'or' M2, states should be unaffected:

00*0 (?)
000*

4. Set M2 back high. States still unaffected

00*0 (?)
000*

5. Set M2 low and back high.

000* <- status NOT cleared here, so it is not in this state.
000*

6. Set M2 low and back high a 2nd time

000* <- status did clear this time
000*
In this unrealistic situation, it takes 1 more M2 falling edge to clear the status bit than predicted. Though this condition can't probably happen in a real Famicom, it is still important because it shows us that our understanding is not yet complete.

Edit:
I don't feel good about it but here it is:
in_frame_bit_states_9.PNG
It just doesn't feel right, the previous one felt better. I think I need to think about how to interpret those results and take a fresh look at the test tomorrow and make sure I didn't goof it up somehow.
User avatar
Ben Boldt
Posts: 1148
Joined: Tue Mar 22, 2016 8:27 pm
Location: Minnesota, USA

Re: MMC5 Hacking Adventures

Post by Ben Boldt »

More testing is still necessary, but I have come up with a diagram that explains everything that I have observed so far, including 4 additional tests described below. This approach modified the top state machine to trigger on rising edge, and it left the bottom state machine triggering on falling edge. I did not yet test or think about the bottom state machine's edge in light of the changes to the top state machine. The triggering edge of M2 is tricky because you can't read the status with M2 low.
in_frame_bit_states_A.PNG
It now passes all of these tests (below). Test 1, 2, 3, and 4 failed with previous diagrams.

All tests start in this state by setting PPU address = $2000 and toggling PPU /RD lots of times:
*000
000*

Test begins in this state with M2 = 1, /RD = 1, status bit set.
Control Test (already passed) wrote:
  • M2 falls
  • M2 rises
  • M2 falls
  • M2 rises
  • M2 falls
  • M2 rises <- status bit got cleared here.
Test 1 wrote:
  • M2 falls
  • RD fall and rise lots of times, ending high
  • M2 rises
  • M2 falls
  • M2 rises
  • M2 falls
  • M2 rises <- status bit got cleared here.
Test 2 wrote:
  • M2 falls
  • M2 rises
  • M2 falls
  • RD fall and rise lots of times, ending high
  • M2 rises
  • M2 falls
  • M2 rises
  • M2 falls
  • M2 rises <- status bit got cleared here.
Test 3 wrote:
  • M2 falls
  • RD falls
  • M2 rises
  • RD rises
  • M2 falls
  • M2 rises
  • M2 falls
  • M2 rises
  • M2 falls
  • M2 rises <- status bit got cleared here.
Test 4 wrote:
  • M2 falls
  • M2 rises
  • M2 falls
  • RD falls
  • M2 rises
  • RD rises
  • M2 falls
  • M2 rises
  • M2 falls
  • M2 rises
  • M2 falls
  • M2 rises <- status bit got cleared here.
User avatar
Ben Boldt
Posts: 1148
Joined: Tue Mar 22, 2016 8:27 pm
Location: Minnesota, USA

Re: MMC5 Hacking Adventures

Post by Ben Boldt »

New problem. It seems like I have just fixed some complicated status clearing sequences, but one of the first things I knew about this was that PPU /RD falling edge sets the status, and that seems to have broken in this latest diagram. I need to verify that is true, and if so, make more changes to the diagram. I have been toying with the idea that the top state machine only clears and the bottom state machine only sets. I think I will think about that idea some more.
User avatar
krzysiobal
Posts: 1036
Joined: Sun Jun 12, 2011 12:06 pm
Location: Poland
Contact:

Re: MMC5 Hacking Adventures

Post by krzysiobal »

I am really impressed in the way you analyze this, but I personally thing that this complexity might be some kind of implementations's side effect how status setting was meant to work.

if I were you, I wouldn't spend on it so much time, there are many other interesting aspects of MMC5 to analyze and replicationg exactly that status bit behaviour will be hard too.
User avatar
Ben Boldt
Posts: 1148
Joined: Tue Mar 22, 2016 8:27 pm
Location: Minnesota, USA

Re: MMC5 Hacking Adventures

Post by Ben Boldt »

Thanks krzysiobal. I feel like I am still getting closer on this status bit -- as it is, I think this diagram is very good at explaining the behavior, but there are still some corner conditions that don't quite match up yet. I think that there is more I can do with this bit before feeling good about it and moving on to something else. These issues could potentially make graphical glitches if implemented this way. I want my trippy backgrounds to be perfect. :wink:

I mentioned my idea where the top state machine clears status and the bottom one sets status, but the interaction of PPU /RD falling edge causing a state change in both state machines at the same time is really integrated into my observations so far. That feature really explains a lot of what we see here, so it is hard to consider giving that up. A new idea I had over lunch today was that maybe all of the PPU /RD logic in the top state machine operates asynchronously, similar to reading $FFFA/B. I think I can test that theory like this:
  • Get into this state, just like recent tests:
    *000
    000*
    status = 1, /RD = 1, M2 = 1
  • Never touch M2 in this test.
  • Set PPU /RD low, leave it low.
  • Read $FFFA. It should put us into this state:
    000*
    000*
  • Read status. If /RD logic is asynchronous like $FFFA, status should automatically set and we should be in this state:
    *000
    000*
  • If status is still not set, rise /RD, check status again. If it got set, it waited for rising edge PPU /RD.
  • If status is still not set, fall /RD, check status again. If it got set (It definitely should be set by now), it waited for falling edge PPU /RD.
Will try it later tonight.

Edit:
It definitely waited until the last step to set the status bit. It apparently does wait for a PPU /RD falling edge, not asynchronous.

In other news, I recently hooked up an LED bargraph display to all 8 unknown pins. I keep an eye on it for any blinks/flashes during these tests now. All 8 have always been output high or open bus (can't tell the difference with this setup) so far.

Edit 2:
I looked into the /RD edge trigger of the top state machine. We know for a fact that exiting the blue box and going back to M2 step 0 happens on the falling edge of /RD. However, due to the 'and' logic with /RD when stepping from M2 step 0, 1, 2, it might not be possible to tell for certain which edge of /RD takes you from M2 step 1 to step 0, or M2 step 2 to step 0. It actually might not matter. We do know that /RD is falling edge when exiting the blue box, so in keeping with this, I made all /RD falling edge and all M2 rising edge. I feel pretty satisfied with this diagram now -- I think that it accurately describes the status bit. I think we are good enough with this bit now to proceed to other MMC5 things, definitely in agreement with you krzysiobal. Here is the final 'in frame' status bit diagram, for now anyway.
in_frame_bit_states_B.PNG
I think I could put this diagram in the wiki. What should I look at next?
User avatar
Quietust
Posts: 1918
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: MMC5 Hacking Adventures

Post by Quietust »

On the topic of the IRQ-related register at $5209, is there any possibility that it's actually a cycle counter initialized by writing to $5207-$5208? Having a register that just triggers an IRQ when you write to it seems slightly useless, at least in my opinion.
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.
User avatar
Ben Boldt
Posts: 1148
Joined: Tue Mar 22, 2016 8:27 pm
Location: Minnesota, USA

Re: MMC5 Hacking Adventures

Post by Ben Boldt »

I agree, that does seem sort of useless without some cycle counting or something, very similar to the DAC interrupt. Why would you need an interrupt if you already know it is going to happen immediately? It seems like there may be more to these things. I will try poking at it some time soon.
User avatar
Myask
Posts: 965
Joined: Sat Jul 12, 2014 3:04 pm

Re: MMC5 Hacking Adventures

Post by Myask »

I am interested to see this done.
User avatar
krzysiobal
Posts: 1036
Joined: Sun Jun 12, 2011 12:06 pm
Location: Poland
Contact:

Re: MMC5 Hacking Adventures

Post by krzysiobal »

Quietust wrote:On the topic of the IRQ-related register at $5209, is there any possibility that it's actually a cycle counter initialized by writing to $5207-$5208? Having a register that just triggers an IRQ when you write to it seems slightly useless, at least in my opinion.
Lol, you are correct. Writing value X to $5209 causes interrupt to trigger on X-th rising edge of M2. Because M2 needs to be toggled constantly, I did not realise that before. And writing value 0 never triggers interrupt, probably because it is triggered when counter clockes from 1 to 0.

Though, only 8 bit M2 counter seems to be hardly useful. I wonder where to write high byte ;)


Also, when M2 stops toggling, around 11us from that time MMC5 is bring to reset (causing interrupt line to go high)
Attachments
m2_stops_toggling.png
write_16_to_5209.png
User avatar
Ben Boldt
Posts: 1148
Joined: Tue Mar 22, 2016 8:27 pm
Location: Minnesota, USA

Re: MMC5 Hacking Adventures

Post by Ben Boldt »

Coooool! That is a super nice feature that nobody knew about! You could use that timer interrupt to write raw DAC samples!! That discovery is GOLDEN :shock:

My setup can't toggle M2 that fast so I would not have been able to test this. Did you try writing anything to $520A? In keeping with the multiplier result being in little endian, it seems that the high byte (if existing) might come after the low byte for this too.

Edit:
Additional questions about this register while the timer is running, before the IRQ happens (ex. writing a big value like $FF so you have time to try stuff):
  • What happens if you read register $5209? Is there a different flag to show that it is running?
  • How about reading register $5208 while it is running? Is it still reporting $C0?
  • What happens if you write value $01 - will it reload the counter and have the IRQ next rising edge of M2, or continue counting from the original value you wrote?
  • What happens if you write value $00 - does it cancel the timer and never generate the IRQ?
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: MMC5 Hacking Adventures

Post by tepples »

If it matters, I seem to remember there being multiplication algorithms that generate 2 bits per iteration. Multiplying two 8-bit numbers would converge in 4 iterations, which at 1 cycle per iteration is the time from a write to the following read.
User avatar
Ben Boldt
Posts: 1148
Joined: Tue Mar 22, 2016 8:27 pm
Location: Minnesota, USA

Re: MMC5 Hacking Adventures

Post by Ben Boldt »

I think that does matter tepples -- that method could save a lot of resources when trying to replicate MMC5 in hardware.

I was not able to ever observe an incomplete product, but I was testing extremely slow and I was toggling M2 during the test, which I am not sure is necessary or not. I didn't try it without toggling M2. Since, to your point, the NES could not possibly expect to read the result until some cycles later, it definitely seems reasonable to implement a multiply that relies on that fact -- whether or not a real MMC5 technically does it faster.
Post Reply