[4.5.9] Flash Saving

JamesNES

Well-known member
I've seen this asked a bit and since I figured it out a while back, and wanted to put it into the thing I'm working on now, so thought it'd be a good time to do a quick walkthrough as I went. This isn't super hard or anything, but you should know how to do things in NESMaker like add scripts, I'm not going to explain basic stuff like that.

I've attached edited version of Flash.asm and FlashSave.asm since there are variable name conflicts with 4.5.9 in there by default.

Thanks heaps to Kasumi's original posts in this thread. Couldn't have figured it out without that write up.

Mesen has to be the latest version - the one that comes with NESMaker by default doesn't support flash saving. It won't crash, just nothing will happen.

Doing it this way will take up 4% of the static bank; the saving might not have to go there but I'm not up for trying at the moment.

First you want to include Flash.asm and FlashSave.asm in LoadAllSubroutines.asm. (After adding them in NESMaker's script window) Flash.asm has to be included before FlashSave.asm just like

Code:
	.include SCR_FLASH
	.include SCR_FLASH_SAVE

In MemoryMap.asm, delete or comment out:

Code:
FlashRamPage 		= $0300 ;; because collision data will not be used on saving/loading of data.
							;; this should always be utilized on a non-screen, and reload the screen when saving/loading is finished.

This isn't used by default in NESMaker. If you use $0300 for flashing it'll mess up your collision table.

It doesn't matter where, just that they're in that order.

I'm using bank 1E for this as it starts basically empty, so you want to put the loading part in there so it looks like this now:

Code:
SaveSegmentStart:


.pad $8100

TriggerSegmentStart:



.pad $8FFF
userPoweredOffFlash:;This MUST be the last value saved to. It is used to detect if the user turned the game off while we were saving
	.db $00	

Flash_Load:
	
	LDY #$00
		

		LDA SaveSegmentStart,y
		STA camScreen
		INY 
		LDA SaveSegmentStart,y 
		STA warpMap
		

		LDY #$00
		triggerLoadLoop:
			LDA TriggerSegmentStart,y 
			STA screenTriggers,y
			
			INY
			CPY #$20
			BNE triggerLoadLoop
RTS



HudTiles:
	.incbin "Graphics\Backgrounds\CHR_HudTiles.chr"

For the example I'm saving your screen/map so when you load, it'll warp you to the warp in spot on the screen you saved on. It also saves screen triggers.

To test it out you can bind these scripts to start/select to save/load:

Code:
JSR SaveSubroutine

RTS

Code:
SwitchBank #$1E
jsr Flash_Load
ReturnBank
	
WarpToScreen warpMap, camScreen, #$01

RTS

This should work off the bat.

To save different variables, you want to edit FlashSave.asm. You basically want to repeat this for each variable:

Code:
LDA warpMap
CallWriteByte
INY

So to save the player's x position, you'd add:

Code:
LDA Object_x_hi
CallWriteByte
INY

and then to load it, in 1E you'd add:

Code:
LDA SaveSegmentStart,y 
STA Object_x_hi
INY

The important thing is that the variables are written/read in the same order, as it's just storing them in a straight sequence.

Hopefully this is enough to get you going.
 

Attachments

  • FlashSave.zip
    2.6 KB · Views: 55
Last edited:

Subotai

Active member
Humm I've followed the tutorial (without adding any other variables).

I've put the SaveSubroutine on a tile.

CPX player1_object
BNE notPlayer2
JSR SaveSubroutine
RTS
notPlayer2:


When the player hit the tile, it crash the game.
 

Subotai

Active member
Even if that's crashing the game.. The save function work. Because, when I restart MESEN (latest 0.9.9), I've setuped a load option at the start menu, the player come back a the same screen as the save.

So Save/Load work perfectly except for the crash when hitting the SaveTile.
 

JamesNES

Well-known member
If you're adding it to a tile you'll want to change the tile's collision to something else after the first save otherwise it'll keep trying to save every frame they're touching it and I'd imagine that would crash the game (and ruin your cartridge).
 

Subotai

Active member
I've made some test with a tile script and a pickup script.
It keeps crashing.

Even if I add into the tile script,
- ChangeTileAtCollision #$00, #$00
- variable SaveWait (initial value 0) +1 at the end just to make sure that the script process once.

Code:
    CPX player1_object
    BEQ checkPad11
     JMP notPlayer2
    checkPad11:
    LDA gamepad
    AND #%00010000
    CMP #%00010000
    BEQ continuCheck11
     JMP notPlayer2
    continuCheck11:
     LDA SaveWait
     CMP #$00
     BEQ SaveEnfin
        JMP +passaver
     SaveEnfin:

        PlaySound #sfx_pickupA
        JSR SaveSubroutine
        
        
        ChangeTileAtCollision #$00, #$00
         LDA $01
         STA SaveWait
    notPlayer2:
    +passaver:


It keeps crashing the CPU.

- Same test with a pickup script.


I think that when the player hit the tile or pickup, the player cannot hit only one point on the collision table.
So I've changed the doHandleTileCollisions_ArcadePlatformerBase to 3 points (tempA tempB tempC) just to have tempD remaining when player is touching the tile. Same crash.


The only way I found to do not crash the game, is to assign the SaveSubroutine with an input button.
 

Subotai

Active member
@JamesNES : I managed to transfert the 'Checkpoint' and 'Saving' functionnality when talking to a NPC (in fact a campfire in my case).
The player in my game is a vagrant after all.

campfire.PNG

Thanks for your help James and your tutorial (s). (CHR Switching is GREAT !!!!)
 

JamesNES

Well-known member
I should've realised this sooner, but tiles run in bank 18 and the save script uses a bank switch to 1E.
@JamesNES : I managed to transfert the 'Checkpoint' and 'Saving' functionnality when talking to a NPC (in fact a campfire in my case).
The player in my game is a vagrant after all.

View attachment 5865

Thanks for your help James and your tutorial (s). (CHR Switching is GREAT !!!!)

You're welcome! Sorry for not replying sooner but if you want to put it in a tile, you need to do a workaround since saving needs access to bank 1E and tiles (and pickups) are in bank 18. So somewhere in your static bank you want to put something like:

Code:
TileSave:

	jsr SaveSubroutine
	SwitchBank #$18


RTS

and then jump to there from your tile script. I just tried this and it works. Also something to consider, if you're making a single screen game you can use the other collision table for your flash ram so the sprites don't glitch out for that split second. I'm doing a single screen at the moment and just completely got rid of the second collision table since it's redundant, it frees up a whole 256 extra bytes of ram. No idea why it wasn't taken out of the non-scrolling modules, it's super wasteful.

Edit: OH and when I was trying this out I found ChangeTileAtCollision didn't work either so I had to do a

Code:
LDX #$00
-collLoop:
	LDA collisionTable,x 
	CMP #$0C
	BNE +
		LDA #$00
		STA collisionTable,x
		JMP +gotIt
	+
	INX
	CPX #$F0
	BNE -collLoop
+gotIt

in the tile script to overwrite my save tile (#$0C). Not sure if that's just me or what but you can look in the memory viewer to see if it's actually doing its job.
 
Last edited:

crazygrouptrio

Active member
Thanks for sharing! I'll be needing this feature in my next project just to store some variables (though I won't be able to test it for awhile).
Keep up the good work!
 

Bucket Mouse

Active member
"if you want to put it in a tile"

Like....if you touched the tile the game would save? I would not do this if you plan to make a physical version of your game, as the game will save on EVERY FRAME the player stands on the tile (60 saves a second). Flash saving has a limit before it bugs out, and while the number is high, something like this would get you there in no time -- and then the cart is bricked.
 

JamesNES

Well-known member
You could change the collision to #$00 so it doesn't keep saving, then trigger the screen and not save off that condition but yeah... better to do it with a button.
 

baardbi

Well-known member
This is absolutely amazing! I have been waiting for something like this for four years. I couldn't figure it out for myself. Thank you so much for posting this tutorial. I will definitely be using this in my next game.
 
Static Bank is Bank 1F correct? I can't find the script named 1F haha. can you help me with that? I need to save tile type in-between multiple screens
so I can transition back and forth between battle map & overworld map
(this is for the FE-like moveable tiles game):

I also already have an Idea on how to reset the save if you fail the mission, reset the NES during it, ETC so don'tworry about that though! :)
 

JamesNES

Well-known member
You'll need to keep that info in variables, ie RAM, and restore them when you go back to the map screen. You might want to watch all the tutorials (not the beginner ones), even for game styles you're not interested in, as there's a lot of just straight up "how to program something" in there.
 
You'll need to keep that info in variables, ie RAM, and restore them when you go back to the map screen. You might want to watch all the tutorials (not the beginner ones), even for game styles you're not interested in, as there's a lot of just straight up "how to program something" in there.
Thanks for not saying "beginner" I'm basically somewhere middle ground between you & dale level of skill and the beginners (forgive me if I forgot anyone above me)
Here's some funni proof of my skill:


a sorta first tutorial. maybe I wrote the tutorial wrong because I am only two months over brain surgery if you saw my update video on m and my friend's let's playish
channel a short of my tutorial part is... but IT DOES WORK! you can create a Rythm game this way I Promise.

anyway yeah sorry for the somewhat unrelated sponsership of myself. I'll check out the tutorials (not the beginner ones) ;)
 
Top Bottom