Hi,
I post this here because I feel like the problem might be something very easy...
however, I'm currently initializing MMC1 emulation by setting reg0 to 0xf, other regs to 0 and finally refreshing NMT, PRG and CHR like I would during emulation.
the result is that at start I get bank 0 mapped in 0x8000-0xbfff and bank 0xf mapped in 0xc000-0xffff.
Most games work, but Snow Bros US and JP both refuse to start: they would need both regions to be set at bank 0, because they seem to only write registers to swith the lower bank once emulation is started and they require (at least in MESS) bank 0 in 0xc000-0xffff...
On the other hand, I have games which require high 16k bank to be initialized to 0xf (e.g. Rescue The Embassy Mission also only switches the lower bank but needs to start wit 0xf in the higher bank to work)
As a result, I am really puzzled!!
Where would you look for a bug? could a timing bug cause MMC1 to write in the wrong reg (or the wrong bankswitch value)?
Any other suggestion I might try?
weird MMC1 PRG init problem...
Moderator: Moderators
-
doppelganger
- Posts: 183
- Joined: Tue Apr 05, 2005 7:30 pm
If I remember correctly, MMC1 has no original power-up state and therefore reset code and interrupt vectors must be put into every PRG bank to accommodate for this random behavior. (edit: this seems to be true mainly for the original MMC1, revisions may exhibit slightly different power-up state behavior)
I stepped through Snow Bros at the beginning. Apparently it performs either an INC $FFFF or an INC $FFF9. The value in these locations is either $C0 (the first eight banks), $FF (the seven after that), and $FE (the last one).
As many nesdevvers know, writing a value of 1 to d7 of any MMC1 register causes the load register value at $8000-$9FFF to be ORed with $0C...this causes the PRG bankswitching mode to be set to 16k, and locks the upper bank at $C000-$FFFF to the last bank. This behavior occurs on the very first write of 1 to d7.
Now, obviously, when the values $C0 and FE are INCed and written to the MMC1, d7 will be set, causing this reset behavior.
It will seem intuitive that, if the value $FF is the one INCed, the value $00 will be written to MMC1 causing no reset behavior. And while it *is* true that this value is written, it is only the second of *two* writes the 6502 does on read-modify-write instructions. A properly emulated 6502, if executing INC $FFF9 where $FFF9 = $FF will do the following on each cycle:
Be sure you are emulating this behavior properly.
One additional thing: MMC1 may not acknowledge two writes so close together, in which case it will only acknowledge the first, pre-op write and ignore the second.
I stepped through Snow Bros at the beginning. Apparently it performs either an INC $FFFF or an INC $FFF9. The value in these locations is either $C0 (the first eight banks), $FF (the seven after that), and $FE (the last one).
As many nesdevvers know, writing a value of 1 to d7 of any MMC1 register causes the load register value at $8000-$9FFF to be ORed with $0C...this causes the PRG bankswitching mode to be set to 16k, and locks the upper bank at $C000-$FFFF to the last bank. This behavior occurs on the very first write of 1 to d7.
Now, obviously, when the values $C0 and FE are INCed and written to the MMC1, d7 will be set, causing this reset behavior.
It will seem intuitive that, if the value $FF is the one INCed, the value $00 will be written to MMC1 causing no reset behavior. And while it *is* true that this value is written, it is only the second of *two* writes the 6502 does on read-modify-write instructions. A properly emulated 6502, if executing INC $FFF9 where $FFF9 = $FF will do the following on each cycle:
Code: Select all
cycle operation
----------------
1 fetch the opcode ($EE which is INC absolute)
2 fetch operand address low ($F9)
3 fetch operand address high ($FF)
4 read from operand address ($FFF9 gives $FF)
5 write pre-operation result ($FF is written, thus
causing MMC1 reset) and perform operation
6 write post-operation result ($00 is written, possibly
not acknowledged by MMC1, see note below)
One additional thing: MMC1 may not acknowledge two writes so close together, in which case it will only acknowledge the first, pre-op write and ignore the second.
Last edited by doppelganger on Tue Jun 22, 2010 2:42 pm, edited 2 times in total.
And now I finally understand why cc65's NES link script has a 12-byte VECTORS segment starting at $FFF4 instead of the 6 bytes at $FFFA that one might expect, so that the following code can be placed in all banks:
Code: Select all
.segment "VECTORS"
resetStub:
inc $FFFC ; location contains $F4, the low byte of resetStub's address
jmp $C000 ; start of real reset code
.addr nmiHandler, resetStub, irqHandler
I'm pretty sure MMC1A and MMC1B variants fixed that issue, OR-ing R0 with $0c at power-up somehow. I'm absolutely sure Final Fantasy II has only one reset vector in the last bank, and I did a bootleg of it using MMC1A, and it worked fine.
Has anyone actually tested a MMC1 that did't switch last PRG bank at $c000-$ffff on power up ?
Has anyone actually tested a MMC1 that did't switch last PRG bank at $c000-$ffff on power up ?
Useless, lumbering half-wits don't scare us.
thanks a lot guys for the answers (especially doppelganger for stepping through the code)
it seems I have to add Snow Bros to the list (already including AD&D Hillsfar and Bill & Ted) of games failing if MMC1 detects INC 0xffff... it seems I will need to fix this problem sooner than I hoped (MAME/MESS core is not super friendly if you need to track the # of cycles between accesses)
it seems I have to add Snow Bros to the list (already including AD&D Hillsfar and Bill & Ted) of games failing if MMC1 detects INC 0xffff... it seems I will need to fix this problem sooner than I hoped (MAME/MESS core is not super friendly if you need to track the # of cycles between accesses)