MMC1 simulated in Logisim

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

Moderator: Moderators

User avatar
infiniteneslives
Posts: 2104
Joined: Mon Apr 04, 2011 11:49 am
Location: WhereverIparkIt, USA
Contact:

Post by infiniteneslives »

Okay well the more I dig around the more interesting things I find... It also appears this is stirring things up a bit more around here than I expected. As of now my plan is to keep probing away at this thing. I'll post what I find here, but keep in mind this is still experimental data. I could have broke something inside my MMC1 at some point, have a bad connection somewhere, or anything really. At risk of being wrong about something I'll still post it here. If I should start a new thread just let me know. But I don't see why to right now.

Once I figure things out as best I can if someone wants to write a test rom to verify these things on other MMC1's while running on the NES etc I would think it's a good idea before considering any of this official. I'm just performing experiments to try and figure out how this thing is actually built. Once things have been verified, I'll release what my official design is of the MMC1 that is as accurate as I can tell. With all that said hopefully no body gets their panties in a wad...

To answer your question Bregalad. Like we discussed earlier if the double write ignore isn't intentional then they wouldn't have added extra logic to check for double (or more than double) consecutive writes and only acknowledge the first. So some optimization or 'don't care' would have had to be made use of if it was unintentional. There are only a few ways to do this. Here are some possibilities that I disproved in my experiments.

The biggest possibility I saw was the fact that PRG R/W stays low between sequential writes. It will only go high when a read is done. So maybe they are clocking something with PRG R/W (even though it sounds like a bad idea). With my tests I was able to actually pull PRG R/W high between the two writes without changing M2 or PRG /CE. Even if I did this, the follow on writes were ignored.

Another idea is some bad circuit or something that a capacitance or something that requires time to discharge so the second write is ignored. But since I can wait seconds between writes this is disproved because it didn't matter how much time I wait between writes (usually 50msec for my rig).

The real kicker for me was the question, "what if I perform 3, 4, 5 or more writes concecutively?" What I found, only the FIRST will be acknowledged. ALL follow on writes are ignored.

Then I asked, "what if I write to ROM (MMC1), then WRAM, then ROM? Even though it's impossible to do with the NES, It turns out only the first will still be acknowledged. Even if you write below where the MMC1 can see ($0000-$5FFF) for several cycles but then come back and write to ROM. It still only acknowledges the first write.

I then asked "Well if it isn't checking PRG R/W solely (based on my earlier statement.) How is it sensing the follow on writes?" I the only thing I could figure is that it uses M2 and only checks PRG R/W when clocked. But how to check this? Well what if we DON'T clock M2 but still do everything else normally. It turns out that consecutive writes WILL be acknowledged if we do this. Regardless if M2 is held high or low. This also further proves that PRG R/W can be held low between writes and still get each write acknowledged. This also means M2 is used for more than just WRAM CE. It's not used for clocking the Shift register, but is used for checking for consecutive writes.

And if they did this, then the MUST have ignored consecutive writes on purpose. Unless they somehow accidentally threw in an extra flipflop to create an 'enable write' signal by clocking PRG R/W each CPU clock cycle. Which I think everyone will agree isn't plausible.

One question I have yet to answer yet though is, "will blocked writes with D7=1 cause resets or not?" So really this could also be asked, "will DEC $00 cause a reset or cause a 0 to be loaded into the shift register with no reset." I'll get around to this at some point.


I also did some testing today to check into the details of how only the address of the last write matters as stated in Kevtris' docs. Turns out this is only true to a certain point. I found this out accidentally at first, but it turns out it's actually possible to write TWO Registers at ONCE. Well almost, You can write to one full register and bits 4-0 of a second. If one were to implement this the 5th bit of the second register appears to always be set to 1. Now the exact details of this I'm not 100% certain of yet but here's what it looks like to me.

So according to kevtris:

Code: Select all

LDA #[data to load]
STA 08000h  ;It does not matter where these first 4 writes occur. only the last write matters.
LSR A
STA 08000h
LSR A
STA 08000h
LSR A
STA 08000h
LSR A
STA 0E000h  ;NOTE: register 3 is what gets loaded!
But what I've found is that if the last 'STA 0E000h' instruction is located in $E000-FFFF this is true. However if this instruction were in $8000-9FFF, I believe that in addition to reg3 getting loaded as normal, bits 4-0 (F, H, and M1, M0) would also get loaded with the same value that we loaded reg3 bits 4-0 with. The one thing I'm not certain of is what the 5th bit gets set to. I did some similar testing with CHR registers (Reg1 and Reg2) and the 5th bit always gets set to 1.

I found this by only changing the address the last byte is written to, and keeping the address of the previous cycle's read the same as the previous writes. Implementing this on the NES the read that occurs the cycle before the write of a STA is from the address of the STA instruction. So wherever that read is performed from, the associated register gets partially written to. Looking at the opcodes this is only true for addressing modes that read from PC (+) before the write. Looks like you might get something funky for some addressing modes.

So it would seem this is somewhat of a bug and I wouldn't think it's intentional. But it gives hits as to how it's constructed. Looks like the copying from the Shift register to the other registers gets enabled a little early as a result of an optimization from not caring about this issue.

Well I thats all I've got for now, at this point I'm not sure how much I care to dig deeper. I should have enough info to get mine working atleast. It's been fun solving some of this puzzle but I don't think it's very fruitful to dig around much deeper. Most of this register stuff could be checked with ROM testing on the NES it just takes a lot longer to check each step compared to my set up. If someone wants something specific tested let me know. I can also burn eproms and test on the NES also.
User avatar
Bregalad
Posts: 8055
Joined: Fri Nov 12, 2004 2:49 pm
Location: Divonne-les-bains, France

Post by Bregalad »

OK so what does happen if I write for example at $2000, then immediately I write at $8000. Is the second write ignored by the MMC1, or is it taken in account.

Also you didn't mention the possibility for the shift register to be clocked by R/W AND /ROMSEL

If this were to be the case, R/W would not go high between two consecutive writes, so this means the shift register is not clocked. But if you do what you experimented, that is, if I understood it well, toggle both M2 and R/W (but keep R/W low during write cycles when M2 is high) then the shift register will ignore writes even though R/W is cycling, because /ROMSEL is held low.

I didn't understand the last bug you mentionned at all. It sounds completely new to me.

I'll definitely fix my logisim model when this MMC1 behavior will get definitive.
Useless, lumbering half-wits don't scare us.
User avatar
infiniteneslives
Posts: 2104
Joined: Mon Apr 04, 2011 11:49 am
Location: WhereverIparkIt, USA
Contact:

Post by infiniteneslives »

Bregalad wrote:OK so what does happen if I write for example at $2000, then immediately I write at $8000. Is the second write ignored by the MMC1, or is it taken in account.
My guess is yes it will be ignored. But I do have that down as one thing to check.
Also you didn't mention the possibility for the shift register to be clocked by R/W logically AND-ed with /ROMSEL.

If this were to be the case, R/W would not go high between two consecutive writes, so this means the shift register is not clocked. But if you do what you experimented, that is, if I understood it well, toggle both M2 and R/W (but keep R/W low during write cycles when M2 is high) then the shift register will ignore writes even though R/W is cycling, because /ROMSEL is held low.
Actually I did account for this. I didn't explicitly state it but it's what I was implying when I said this:
Well what if we DON'T clock M2 but still do everything else normally (for the writes to the MMC1 shift register). It turns out that consecutive writes WILL be acknowledged if we do this. Regardless if M2 is held high or low. This also further proves that PRG R/W can be held low between writes and still get each write acknowledged.
So for that test I operated PRG /CE and PRG R/W naturally and did a test with M2 held high and another with it low. Basically if you disconnect M2 you can get it ACKNOWLEDGE consecutive writes. PRG R/W stayed low for the entirety of several sequential writes and ALL of those writes were acknowledged even though they occured one 'cycle' (less M2 toggling) after another.
I didn't understand the last bug you mentionned at all. It sounds completely new to me.
Basically it appears to me that the shift register to reg0-3 coping happens partially (bits 4-0) on the STA instruction read the CPU clock cycle preceeding the STA's write cycle. Then on the write cycle the same bits4-0 plus the final 5th bit from that cycle's write are copied as well. Additionally It looks like the 5th unaccounted for bit of the first copy during the read cycle is always a 1. So if the STA instruction is read from a different ROM location (A14-13) than the register location you're trying to write to; you'll end up writting to BOTH registers with that single (5 bit) MMC1 write operation. Sorry I know it's a bit confusing I'll have to come up with an example...

Yes I've never heard of it either, I think it's safe to say this is the first time it's came to light. But it's definitely there to some degree, I need to further characterize it. Once I modify my design a bit it may make more sense as to why it's happening. I tested it with writing to reg0 and reg3 at the same time. And additionally I tested it with reg1 and reg2 with similar behavior (I don't know enough to say it's identical for all registers yet but I'm guessing so).
User avatar
infiniteneslives
Posts: 2104
Joined: Mon Apr 04, 2011 11:49 am
Location: WhereverIparkIt, USA
Contact:

Post by infiniteneslives »

Good news. I got my MMC1 working :) Thanks for making your logisim version Bregalad. I didn't copy it exactly with some of my differences noted previously. But your design was a great reference and starting point to help me to understand how things worked when the docs left details to be desired.

I'll post my design and everything soon in another thread.
User avatar
infiniteneslives
Posts: 2104
Joined: Mon Apr 04, 2011 11:49 am
Location: WhereverIparkIt, USA
Contact:

Post by infiniteneslives »

Well I've still got a bug to figure out with my MMC1 before I publish my design. Something is wrong when I test out zelda, other SNROM games are fine like metroid but zelda has issues when drawing the first map screen with the cave and everything.

I decided to jump into MMC3 and found something that may be wrong with your MMC3 Bregalad. I think it's due to some ambiguity in all the MMC3 docs (wiki, kevtris, & disch). The issue is with the first two CHR registers/bank numbers.

I made the same assumption as Bregalad that those two 7bit wide registers ignore the MSB from PRG D7. Which is similar to the PRG banks which are only 6 bits wide. But it appears that they ignore the LSB (PRG D0) as they use CHR A10 in it's place. All the docs I found were ambiguous as to which 7 of the 8 bits are used for holding the bank number. I did some agreement with what I'm saying in the mapper 118 wiki impling that D7 is always mapped to CHR A17. Thinking about it like this makes a little sense now. Basically Bregalad's design shouldn't shift the first two CHR banks by 1 bit, they should stay as is. The value of D7 always corresponds to CHR A17. And all the games I'm testing with look MUCH better after I implemented this change so I'm pretty sure I'm not mistaken.

I know you made mention of it Bregalad, it's probably common knowledge to most, but I think it's worth pointing out that your design WILL NOT WORK as you've posted it because you're clocking your scanline counter with each positive edge of CHR A12. This behavior really isn't pointed out in Kevtris' docs well and your design led me further down the garden path. I didn't realize how the MMC3 ignores closely timed CHR A12 posedges until checking things with disch's docs, so a more explanatory note would have been helpful to me at least.
User avatar
Bregalad
Posts: 8055
Joined: Fri Nov 12, 2004 2:49 pm
Location: Divonne-les-bains, France

Post by Bregalad »

Yeah - in other words the 2kb CHR bank select registers select a 2kb bank, but with the corresponding 1kb bank number, ignoring the LSB, just like the MMC1 in 8kb CHR bank modes uses 4kb page numbers.

This make sense as it simplifies the inside logic.

Only the MMC5 does it the complex way.
Useless, lumbering half-wits don't scare us.
mog123
Posts: 27
Joined: Mon Jan 22, 2007 11:12 am
Location: Poland

Re: MMC1 simulated in Logisim

Post by mog123 »

I'm sorry for bringing this old topic back up, but I'm actually interested in designing a pcb for these, as there are no know, cheap mmc1 replacements and making one involves using cpld's, which maybe aren't that expensive, but learning to implement those would require a bit of time, which i currently don't have much. My question is: I noticed that you're using a lot of splitters out of d-type flipflops, how is that realised? shift registers? Then where is the latch bit?
Post Reply