I have a save file from an emulator I'd like to transfer to a retail cart, but I don't have a SRAM writer.
What I do have is a super everdrive, a GQ-4X Programmer, and a Teensy 2.0.
How do I go about doing this?
Also, can I possibly use my teensy to do SRAM backups in preparation for battery replacements?
If you disable the CIC on your SNES, you can hotswap the flash cart to the retail cart, save the SRAM into internal RAM, hotswap the retail cart out while you swap the battery, then hotswap it back in and rewrite. Tricky since a crash on the SNES would mess the process up.
With the Teensy you can hook it to the controller port, run a bootloader from the flash cart (which I assume you can program from your PC), hotswap to the retail cart, and read/write SRAM from the PC via the Teensy.
How well does hot swapping work on the SNES though? I recall someone mentioning having DMA transfers going tends to reduce the chance of a crash.
Either way there is no ready to go way to do what you want with the materials you have on hand. Older style Copiers like the Game Doctor SF can do what you want though.
Hotswapping works reliably for me, yes, using 64K DMAs to make the CPU unlikely to crash. The only tricky parts are disabling the CIC (lift one pin) with a soldering iron and hooking the Teensy to the controller port. Agreed that unless you want to do some significant SNES stuff, a copier that can do this directly or sending the boards to someone who can makes more sense (remove them from cart cases and pay maybe $10 to send several cart boards round-trip, at least in the USA).
You could build an awful contraption like I did for an N64 when I had to give four copies of Super Smash Bros 64 the same fully unlocked save file:
However, this worked by playing both carts from a fresh state and progressing through them identically, so there weren't conflicts between the two (potentially mismatched) sets of SRAM data lines being merged into the one.
I guess you could do something like that but disable SRAM reading on one cartridge temporarily? unfortunately I don't know if that 's a reasonable thing, since without knowing better I'd expect it to be bidirectional and not have specific lines for reading and writing.
blargg wrote:
Hotswapping works reliably for me, yes, using 64K DMAs to make the CPU unlikely to crash.
Interesting way to misuse DMA for hotswapping. A different way is to store up to 8x12 bytes of program code in the DMA registers at 4300h-437Fh, and let the CPU execute that code, doing that should be absolutely stable and make it impossible to crash the CPU.
Have you tested and confirmed that?
I currently use the 64K DMA trick to avoid CPU crashes, and it's somewhat unreliable. About a 25% failure rate.
I mean the theory is sound, but code executing out of WRAM shouldn't crash when you swap carts either, and yet it does.
If this really works, I sure would have liked to have known about it before dumping 750 cartridges using the DMA trick
Putting a cart in with those big caps puts quite a surge on the power supply. That's probably what causes it to crash somewhat often.
Thanks, DMA registers work. I first put the program that flashes the screen for a while, then runs the serial bootloader out of WRAM, but WRAM was apparently getting corrupted when I inserted/removed cartridge. So I rewrote the serial bootloader to fit entirely into DMA registers and it seems uncrashable/uncorruptible. It was a tad tricky to break the code into 12-byte chunks (with branches to the next) and get the timing right. The bootloader checks for a 3-byte header and does a checksum on received data, so there's no way it can get tricked by insertions while it's reading the controller port for data. It barely fit.
Code:
; Receives data as 57600-bps serial on controller port 2
; Waits for 256-byte block beginning with $DC $4B $D2
; Writes it to 0-$FF in memory (zero-page)
; Verifies checksum in fourth byte (see code at end for preparing block and checksum)
; If all check out, starts executing at $0004, otherwise resumes waiting for header
; The following must be run before code is executed:
sep #$30 ; make A, X, and Y 8 bits
; It works in emulation or native mode.
; The code below must be copied to DMA registers and executed from them (JMP $4300)
4300:A2 00 LDX #$00 ; X = bytes received
4302:A9 01 LDA #$01 ; A = mask
4304:2C 17 40 BIT $4017 ; Wait for start bit
4307:F0 FB BEQ $4304
4309:80 05 BRA $4310
430B:FF FF FF FF FF
4310:A9 01 LDA #$01 ; A = initial delay to middle of start bit
4312:A0 FF LDY #$FF ; Y = received bits
4314:3A DEC A ; Delay A*30
4315:D0 FD BNE $4314
4317:A9 01 LDA #$01 ; A = mask
4319:80 05 BRA $4320
431B:FF FF FF FF FF
4320:2D 17 40 AND $4017 ; Read in middle of bit
4323:49 FF EOR #$FF ; un-invert
4325:C9 FF CMP #$FF ; carry = bit
4327:98 TYA ; Rotate into bits received
4328:6A ROR
4329:80 BRA $4330
432B:FF FF FF FF FF
4330:95 00 STA $00,X ; Store byte (also acts as delay)
4332:90 0C BCC $4340 ; Done with byte if start bit was shifted out
4334:A8 TAY
4335:95 00 STA $00,X ; delay
4337:EB XBA
4338:A9 04 LDA #$04 ; delay between bits
433A:80 D8 BRA $4314
433C:FF FF FF D8
4340:E0 03 CPX #$03 ; First 3 bytes are signature
4342:B0 0C BCS $4350
4344:7D 77 43 ADC $4376,X ; Add negated correct byte, =0 if correct
4347:D0 B7 BNE $4300
4349:80 05 BRA $4350
434B:FF FF FF FF FF
4350:A9 07 LDA #$07 ; delay until middle of stop bit
4352:3A DEC A
4353:D0 FD BNE $4352
4355:E8 INX
4356:D0 AA BNE $4302 ; more bytes
4358:80 06 BRA $4360
435A:FF FF FF FF FF FF
4360:8A TXA ; Calculate checksum (XOR and add)
4361:55 00 EOR $00,X
4363:0A ASL
4364:69 99 ADC #$99
4366:E8 INX
4367:D0 F8 BNE $4361
4369:80 05 BRA $4370
436B:FF FF FF FF FF
4370:AA TAX ; if CRC != 0, go back and wait for new block
4371:D0 8D BNE $4300
4373:4C 04 00 JMP $0004 ; Run the program!
4376:24 B5 2E ; negated header bytes
/* Converts 256-byte block of user code (beginning at offset 4)
into 256-byte program block ready to send to boot loader */
enum { boot_block_offset = 4 };
enum { boot_block_size = 0x100 };
static void make_boot_block( unsigned char block [boot_block_size] )
{
int i, crc = 0;
for ( i = boot_block_size - 1; i >= boot_block_offset - 1; i-- )
{
crc += 0x100 - 0x99;
crc = (crc << 7 & 0x80) | (crc >> 1 & 0x7F);
crc ^= block [i];
}
block [0] = 0xDC;
block [1] = 0x4B;
block [2] = 0xD2;
block [3] ^= crc ^ 0xCB;
}
EDIT: scope shows cartridge data bus quiet while executing above code out of DMA registers, so this does look like it will be uncrashable. Now to examine the +5V rail droop due to the cart's capacitors charging when hotswapped.
Nope, am not seeing any power dips on cartridge insertion. Also after doing all this scoping while code was running, it hadn't crashed.
The programmer that is used for the Super Flash Cart from Tototek will allow you to write to the SRAM on retail carts.
http://tototek.com/store/index.php?main ... ucts_id=39You can buy the programmer separately for only $20 (scroll down to bottom of page) but the catch is you need an EPP parallel port for it.
I'm currently working on a Teensy++ based cart reader, should be a fun project.
I really should finish that hardware re-spin of the controller-port serial connection too... so many projects, so little time. No, wait, that's not true... so many projects, so *much* time... so much Netflix >_<
qwertymodo wrote:
so many projects, so *much* time... so much Netflix >_<
Just ask yourself which you'd like to have done in a year: watch Netflix a lot yet still only watched a tiny fraction of what's available, or completed several projects.
If a game project involves creating an original setting, one may need to watch Netflix to get inspiration.
blargg wrote:
qwertymodo wrote:
so many projects, so *much* time... so much Netflix >_<
Just ask yourself which you'd like to have done in a year: watch Netflix a lot yet still only watched a tiny fraction of what's available, or completed several projects.
To be fair, I tend to work on my projects *while* watching Netflix. And really, Netflix isn't so much the problem as my ADD is, I have so many half-completed projects that I just got distracted by a new idea and set aside...
nocash wrote:
A different way is to store up to 8x12 bytes of program code in the DMA registers at 4300h-437Fh, and let the CPU execute that code, doing that should be absolutely stable and make it impossible to crash the CPU.
blargg wrote:
Thanks, DMA registers work. I first put the program that flashes the screen for a while, then runs the serial bootloader out of WRAM, but WRAM was apparently getting corrupted when I inserted/removed cartridge. So I rewrote the serial bootloader to fit entirely into DMA registers and it seems uncrashable/uncorruptible. It was a tad tricky to break the code into 12-byte chunks (with branches to the next) and get the timing right. The bootloader checks for a 3-byte header and does a checksum on received data, so there's no way it can get tricked by insertions while it's reading the controller port for data. It barely fit.
Executing code that's sitting on DMA registers!
This is
insanely cool.
Thinking of it (and reading nocash's docs again), I guess that implementing an "SPC was played" flag in the PowerPak firmware might be possible after all ... I'm definitely going to try!
Thanks a lot, nocash & blargg!
Ramsis wrote:
Thinking of it (and reading nocash's docs again), I guess that implementing an "SPC was played" flag in the PowerPak firmware might be possible after all
A flag that's preserved on reset? Can't it just as well be in WRAM or even VRAM, or are those lost on reset?
blargg wrote:
Ramsis wrote:
Thinking of it (and reading nocash's docs again), I guess that implementing an "SPC was played" flag in the PowerPak firmware might be possible after all
A flag that's preserved on reset? Can't it just as well be in WRAM or even VRAM, or are those lost on reset?
I guess not, but isn't the state of WRAM/VRAM undetermined upon initial power-up? So a (small?) risk of an unwanted flag value would remain. DMA registers, on the other hand, are all $FF upon power-up, at least according to Fullsnes.
If you stick a very particular value somewhere in RAM, the chances that it would be that particular value on power-up can be made very small.
Even moreso if you put many particular values in many locations. I'd trust RAM more than DMA register preservation on all revs of the SNES.
So then is it true that swapping a cart corrupts WRAM arbitrarily -even if- we are currently executing code out of the DMA registers during the entire swap?
Putting the entire uploader into the DMA registers is much less fun than just putting in a long wait loop.
My version of synchronous serial would suffer a bit from the 4-byte gaps, as its unrolled form bangs the joypad registers as fast as possible.
Okay, did some testing on my end.
It appears completely stable now to swap carts by executing a wait loop inside the DMA registers. WRAM is not getting corrupted as far as I can tell, so long as you swap while inside the DMA loop.
So this code should suffice, and it allows you to use different transfer methods (like my USART, which when unrolled doesn't fit into the DMA registers without a performance penalty):
Code:
ldy #$0040; ldx #$0000 //works out to about six seconds, roughly
lda #$ca; sta $004300 //dex
lda #$d0; sta $004301 //bne $fd
lda #$fd; sta $004302 //...
lda #$88; sta $004303 //dey
lda #$d0; sta $004304 //bne $fa
lda #$fa; sta $004305 //...
lda #$6b; sta $004306 //rtl
jsl $004300
So as long as you keep your upload transfer routine resident in memory (at $7ffxxx or so), this is all you'll need.
Really great trick, appreciate you pointing it out, nocash.
I should do a checksum test where WRAM is filled with random values, then verified after lots of swapping. Not sure why my code was crashing before for me when going back to WRAM. Good it doesn't all have to fit into DMA registers
That's pretty awesome if you can indeed give yourself 6 seconds of time to cart swap with no risk of CPU crash.
Why do you say your WRAM code needs to be at $7FFxxx? What significance is there opposed to just anywhere in WRAM?
In theory with this setup, couldn't you pop some kind of SRAM in a Cartridge and the loader in another on an EPROM and make your own SRAM cart + uploader? Not for any particular reason, just because you could.
I think you mentioned using a cartridge swap method for dumping carts when you were redumping carts didn't you?
You can keep the uploader on the SRAM cart itself, and reprogram it when it gets erased. In practice it doesn't get erased all that often. Most code you don't need run right at reset, and for that which you do, you can have it run the bootloader after the code, thus continuing the cycle of not having to boot off your EPROM (or second SRAM) cart with an intact bootloader.
Quote:
I should do a checksum test where WRAM is filled with random values, then verified after lots of swapping.
That would be great, if you were bored and didn't mind. I found it curious that the fake DMA transfer could corrupt WRAM anyway, but apparently something was happening.
My test only relies on 0x400 bytes of WRAM remaining in-tact, so it's possible it's still not 100% safe, so confirmation would be great.
Quote:
Why do you say your WRAM code needs to be at $7FFxxx? What significance is there opposed to just anywhere in WRAM?
It can be anywhere, it's just where I put it. Keep it out of the way so uploaded programs have more WRAM to use.
Quote:
You can keep the uploader on the SRAM cart itself, and reprogram it when it gets erased. In practice it doesn't get erased all that often.
I am hoping it will get erased a lot less now with this nicer swap method. It's quite a hassle when it does get erased and you have to break out the floppy disk and the copier.
Quote:
thus continuing the cycle of not having to boot off your EPROM (or second SRAM) cart with an intact bootloader.
Yep, I was usually able to dump 1 - 10 carts in a row before a swap would fail and I'd have to use the original boot cart again. From there, I could usually pull off about 10 swaps from the boot cart before I'd kill the SRAM.
Also lots of ways to accidentally kill the boot cart SRAM: pull before or after the start of the cart swap, pull it at a really odd angle, put it in backwards (:P), sometimes when the card edge doesn't get good contact with all pins weird stuff can happen and wipe it, and so forth.
It would actually be really nice to get a FlashROM cart to run the bootloader on.
I was hoping for a more stable hardware solution.
Additionally, I'm not familiar enough with SNES assembly, so programming DMA transfers and a SRAM transfer program is a bit outside my skill set for now.
I guess the tototek card and a PCI express Parallel card with EPP is my solution.
Thanks.
I was kind of hoping to just be able to wire up a battery circuit to the SRAM, desolder it, pop it in the willem and write a file to it.
byuu wrote:
It would actually be really nice to get a FlashROM cart to run the bootloader on.
If you need a FlashROM programmer, I have an extra copy of my Teensy<->FlashROM adapter PCB...
DNSDies wrote:
I was kind of hoping to just be able to wire up a battery circuit to the SRAM, desolder it, pop it in the willem and write a file to it.
You could probably do that... or, for a more stable solution, you could replace the SRAM with F-RAM, so you don't have to worry about maintaining Vcc, but then you'd need a little extra glue logic to properly drive the /CS pin.
qwertymodo wrote:
You could probably do that... or, for a more stable solution, you could replace the SRAM with F-RAM, so you don't have to worry about maintaining Vcc, but then you'd need a little extra glue logic to properly drive the /CS pin.
Apparently the willem I have (GQ-4X) doesn't support writing to SRAM, only testing it.
There has been some discussion on other boards abotu using other ROM profiles to attempt to force a write, but it doesn't work out usually.
How would you preserve the SRAM's contents when removing from the programmer?
Some kind of clip on the top to provide the battery power circuit maybe? Either way he says his programmer won't write SRAM chips so it hardly matters. However, if he could cart swap, programming an eprom you could make a swapping program that would let you swap carts and then would program a SRAM image which was stored in EPROM and loaded to WRAM along with the code to upload it to the swapped in cart. It actually doesn't even sound that hard. The earlier DMA register 6 second swap loop is simple enough. All you need is some init code, maybe throw some graphics in the screen, copy your SRAM image and code to copy that back to a cartridge into WRAM and that's about it. It actually seems very practical if you have access to an eprom programmer and a cartridge preferably with a chip socket.
blargg wrote:
How would you preserve the SRAM's contents when removing from the programmer?
That's probably why it isn't supported, but that's also why I suggested F-RAM.
I'm mighty interested in this whole F-RAM proposal.
It's kinda off topic, but it would be useful to know this in case I want to replace the battery backed S-RAM of some game with a long term stable solution.
Would be especially useful for the pokemon games with RTC.
DNSDies wrote:
I'm mighty interested in this whole F-RAM proposal.
It's kinda off topic, but it would be useful to know this in case I want to replace the battery backed S-RAM of some game with a long term stable solution.
Would be especially useful for the pokemon games with RTC.
Gameboy games are easy. Literally a drop-in replacement. For Pokemon, you want the
Ramtron (now Cypress) FM18W08. If the battery dies, you still lose the RTC, but you won't lose your save, and you can replace the battery at your leisure (or not, and just set the clock every time you start playing...). For SNES, it requires some glue logic on the /CS line. I've worked out a PCB that should work for the 64k and 256k RAM chips as a drop-in replacement, using the Ramtron FM18W08 (same as for the Gameboy) for 256k, or the FM16W08 for 64k, and a 74HTC00 for the /CS logic. If you're interested, once I get PCB's in, I'll probably sell a couple, as I don't really have that many SNES games that I want to modify this way.
DNSDies wrote:
I'm mighty interested in this whole F-RAM proposal.
It's kinda off topic, but it would be useful to know this in case I want to replace the battery backed S-RAM of some game with a long term stable solution.
Would be especially useful for the pokemon games with RTC.
Battery backed SRAM *is* a long term stable solution. There are Legend of Zelda cartridge with their original batteries from the late 1980s that still retain their SRAM contents. That's pretty long term.
byuu wrote:
Quote:
I should do a checksum test where WRAM is filled with random values, then verified after lots of swapping.
That would be great, if you were bored and didn't mind. I found it curious that the fake DMA transfer could corrupt WRAM anyway, but apparently something was happening.
I did a test and found no corruption in the 128K DRAM after swapping a few carts in and out for about two minutes. Didn't test the first $500 bytes since the test code was there.
I don't know whether I got corruption before. If so, it may have been because my loop-in-DMA was writing to some registers (INIDISP and MDMEN) in the loop. Er and the other 7 DMA channels were continuously writing to a PPU register. So constant /WR assertion on the bus. For the test I just did, I used a delay loop like yours without any writes.
MottZilla wrote:
DNSDies wrote:
I'm mighty interested in this whole F-RAM proposal.
It's kinda off topic, but it would be useful to know this in case I want to replace the battery backed S-RAM of some game with a long term stable solution.
Would be especially useful for the pokemon games with RTC.
Battery backed SRAM *is* a long term stable solution. There are Legend of Zelda cartridge with their original batteries from the late 1980s that still retain their SRAM contents. That's pretty long term.
True enough, but starting from today, it's no longer a long-term solution if you have one of those Zelda carts with its save intact and the original battery... so long-term relative to its existence? Absolutely. Long-term in terms of how much longer will it last? Maybe not so much.
My point is you can backup the data and replace the battery and be good for another long term of time. Which is more simple than replacing the SRAM with a non-volatile memory like FRAM. No to mention in the case of certain games, the battery backed RAM also serves as expanded general purpose RAM which may wear out certain non-volatile replacements.
Primary lithium coin cells and solder can also be safely assumed to be manufactured for the rest of all time; the same is not true of ICs. Even if the FRAM will exceed the life of one battery, it won't exceed the total repairability.
(Counterargument: Who knows how long NES/SNES/PCBs will remain viable?)
I have hope that by the time NES consoles start failing hard, there will be an open-source gate-for-gate replica of the NES and all mappers in Verilog. The Visual 2A03 and Visual 2C02 increase my faith in this, as in theory a netlist could be extracted from that. At that point it'll become easy to build an FPGA NES or (with enough up-front investment for tooling) to get a Chinese company to build a dead-on accurate NOAC.
F-RAM is an "infinite write cycles" memory, unlike Flash, so at least that isn't an issue. Also, you may be interested in
this project re:FPGA NES.
F-RAM has no cycle limit? So you could make a programmable cartridge out of it like you normally do for Flash memory?
So then FeRAM is something different? I recall Sonic 3 using something like that, and it certainly did have a limited write cycle lifespan.
The technology's evidently come a looooong way in the past 50 years...
It's not "unlimited", it's just "so huge you can't really quantify it"—wikipedia claims 10¹⁶, or, at NES speeds, one byte being hammered on continuously would expire in roughly 200 years.
Yeah, that's why I said "unlimited write cycles" in quotes... maybe it should have been unlimited* write cycles
However, it looks like the higher-density and/or lower voltage parts are *only* 10^14 (100 trillion) writes.
F-RAM = FeRAM, and while I'm sure you could make a cart out of it, the highest density I've seen is 8Mbit, and that's a BGA-only part. You can get 4Mbit in TSOP, but it's 16-bit wide, and only 3v.