In the NES (but not the Famicom), there is a /RESET signal, probably only intended for the CIC chip. I was wondering if this is an open-collector signal, and if I could actually drive it low to reset the NES. The reason I ask is if I put a RAM chip in a cart instead of the PRG-ROM, then had a microcontroller in there that would bootload the RAM chip from data stored on an SD card. I was thinking something like this could occur during reset, then releasing reset would run the game as normal. This seems like a way to make a simple/cheap/homebrew mapper-specific everdrive-like cartridge. This could then be expanded to do the same with CHR-ROM, or only support CHR-RAM games.
The only limitation I can see is holding the NES in suspended animation somehow during this bootload, and I am wondering if there is a simple way to do that.
Driving the CIC RESET won't have any effect on the console. That signal is an output from the console's CIC LOCK to synchronize the cartridge CIC KEY and nothing else.
The only way I'm aware of to effectively cause a reset from the cart is to direct the NES CPU to jump to the game's reset vector.
There are two ways to do it.
One is to disconnect the memory bus from the cart edge, spoonfeeding JMP $4C4C instructions to the CPU, then spoonfeed a JMP ($FFFC) at the end. Do level shifters have an enable?
Another is to have your MCU spoonfeed instructions to the CPU to copy the boot sector from SD to RAM. To minimize space in the MCU for this, you might want to adapt techniques from arbitrary code execution TASes of Pokémon Yellow Version.
tepples wrote:
Do level shifters have an enable?
Typically, yes they do.
infiniteneslives wrote:
to effectively cause a reset from the cart is to direct the NES CPU to jump to the game's reset vector.
If we had actual access to the OC /RESET input on the CPU, it would allow the cart to drive the address bus to fill the RAM on the cart without intervention from the CPU.
Unfortunately, when just jumping to the reset vector the 2A03 continues to drive the address bus at that time, and the 2C02 never stops driving the upper 6 bits of the address bus under any condition.
Sorry for not being more clear, I was trying to suggest what Tepples did a better job of explaining.
There actually is a way for the cart to fully reset the CPU, but it's pretty worthless. Cease CIC communications (or just drive CIC KEY DATA OUT high) to cause the LOCK CIC to reset the NES CPU every second repeatedly. But the only way out of that is to manually press reset on the console. Perhaps that could be of crude use somehow though as I'd expect the cart could drive the CPU address bus for ~1/2 second, at 1 second intervals while the CIC LOCK is holding the CPU in reset.
EDIT:
Quote:
the 2C02 never stops driving the upper 6 bits of the address bus under any condition.
Does that include the condition of while the CPU actually being held in reset?
Yes. That's why
Krzysiobal's hybrid NTSC/PAL console needed extra tristating buffers to isolate the two different PPUs.
Oh yes of course, my bad, I was confusing 2C02 & 2A03 while reading your post.
Setting up the PPU half of the cartridge can wait until the boot sector program finishes loading the next stage from the FAT file system. Then that stage can fill CHR RAM through the ordinary $2006/$2007 interface.
I wonder if the ~1/10th of a second before the CIC (front-loader)/capacitor(all top-loaders) release /RST is enough time to load a sufficiently large stub into cart RAM?
tepples wrote:
There are two ways to do it.
One is to disconnect the memory bus from the cart edge, spoonfeeding JMP $4C4C instructions to the CPU, then spoonfeed a JMP ($FFFC) at the end.
This is quite interesting. I always wanted to know how the guys at everdrive implemented the save feature in their cartridge - is it invoked by some rarely used button combination on the pad, like START+SELECT? Do the FPGA acts as a state machine and listens to all the write strobes of $4016 and reading of $4016 and if there is such read with proper bits received, it switches whole PRG area just before next opcode read to a custom code, providing JMP ($FFFC) opcode?
Feding constant byte $4c at random interval (which will finally result as $4c $4c $4c = JMP $4c4c might cause part of previous opcode to be executed improperly, with broken argument.
It seems like the simplest way to do saving would be to replace the NMI vector to point to your own code to read the controller. Then jump to the real NMI vector when done. NMI gives you a clean entry/exit point. I've been experimenting with this a bit, myself. The JMP $4C4C thing is more suited for replacing/delaying the reset vector, and/or forcing a reset.
Memblers wrote:
It seems like the simplest way to do saving would be to replace the NMI vector to point to your own code to read the controller. Then jump to the real NMI vector when done.
This would probably screw up most of my programs, since I often use every last cycle of vblank time.
tokumaru wrote:
Memblers wrote:
It seems like the simplest way to do saving would be to replace the NMI vector to point to your own code to read the controller. Then jump to the real NMI vector when done.
This would probably screw up most of my programs, since I often use every last cycle of vblank time.
The fake NMI can wait for VBL at the end of processing, then jump to the real one. Comes with a little overhead (10–20 cycles maybe?), but most games aren't that picky. Some tricks might be able to eliminate the latency entirely. (E.g., jump to the original NMI return address with an infinite loop patched in by hardware, restore NMI vector, and remove the patch when NMI happens. Probably won't work all that nice if the return address was in RAM, though...)