Need instruction on how to save.

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Need instruction on how to save.
by on (#129691)
I have been working on a game and I need it to save. I am not new to programming but I am newer to 6502 and may not be familiar with all the terminology you use in your explanation. I don't understand what needs to happen to read and write from a save file.
Re: Need instruction on how to save.
by on (#129693)
In order to save data in an NES game you need to set bit 1 of byte 6 in the header of your ROM. Doing so enables 8k of expanded RAM at $6000-$7FFF to be saved to a file when the emulator closes and is restored when you open it up with that ROM.
Re: Need instruction on how to save.
by on (#129694)
How do I set bit 1 of byte 6 in the header?

Is the only information saved from $6000 - $7FFF ?
Re: Need instruction on how to save.
by on (#129695)
An NES game stored in the iNES format will, if the battery bit is set in the header (i.e. the 2s bit of the 6th byte of the file is set), save the memory to disk when the emulator quits, and restore from disk when the emulator starts.

Note that this a comparatively expensive addition to the cartridge, and many original hardware never actually supported it.

Most of the games that originally came with battery backed memory (with the notable exception of Family Basic) provide the ability to disable writes to memory to prevent corruption of the save memory. For more specifics, you should tell us which mapper you're using.
Re: Need instruction on how to save.
by on (#129697)
I'm not sure, but this is how I have things initially set up.

.inesprg 1 ; 1x 16KB PRG code
.ineschr 1 ; 1x 8KB CHR data
.inesmap 0 ; mapper 0 = NROM, no bank swapping
.inesmir 1 ; background mirroring

Does this answer your question? I'm new at this so I still don't know what commands set bit 1 of byte 6 of the header. Also, will the only info that is saved be $6000 - $7FFF ?
Re: Need instruction on how to save.
by on (#129698)
You could always use a password system instead. :D
Re: Need instruction on how to save.
by on (#129699)
raydempsey wrote:
How do I set bit 1 of byte 6 in the header?

If you're hacking an existing ROM, use an hex editor. If you're assembling from source, look for the iNEs header and change the bit. Some programmers include the iNES header as a binary file (in which case you'd also need an hex editor), others have it defined in editable form in the source code. It varies.

Quote:
Is the only information saved from $6000 - $7FFF ?

Yes, but since it's RAM you can put anything you want in there. This was Nintendo's standard solution for saving games: a battery backed 8KB RAM chip mapped at $6000-$7FFF (right before the start of ROM, which is at $8000). In a ROM, all you need to do is use the header to specify that this area of memory should be preserved (i.e. it's free), but a real cart that doesn't already have SRAM must be significantly modified in order to support a game that expects this memory to be present.

Another important thing you have to consider when saving games is that data corruption does happen. This makes it important that you implement some for of error detection, to verify that the saved data is present and valid. The presence of saved data is often signaled by a few signature bytes, a sequence of specific values that isn't likely to appear at random. For example, a 4-byte signature has a chance of 1 in 18,446,744,073,709,551,616 of being created by accident, so that should be pretty safe. As for the validity of the saved data, a common approach is to checksum the data (when saving, add all bytes together, ignoring the carry, and save the resulting byte - when loading, add them all again and compare against the saved checksum). This is clearly not 100% safe, because the errors could coincidentally nullify themselves and still generate the same checksum, but that's somewhat unlikely.

BTW, the 8KB of extra RAM doesn't have to be used exclusively for saving, it can also be used as regular RAM. This means that by adding this memory to your game you not only get to save games, but you also bump the amount of RAM you have available from 2KB to 10KB.

raydempsey wrote:
.inesprg 1 ; 1x 16KB PRG code
.ineschr 1 ; 1x 8KB CHR data
.inesmap 0 ; mapper 0 = NROM, no bank swapping
.inesmir 1 ; background mirroring

I see you're using NESASM. I looked at the documentation and couldn't find anything related to enabling battery-backed SRAM. Apparently there are only these 4 directives controlling the iNES header, which sucks. Honestly I wouldn't be surprised if NESASM didn't support this, it isn't a very good or particularly well planned program. You can always run the final ROM through an hex editor, but that would be a pin in the ass to do every time. I suggest you start to assemble the ROM without a header and use an external 16-byte binary file containing your header, and use copy /b header.bin + rom.bin rom.nes (if you're running windows) to generate the final ROM.

Movax12 wrote:
You could always use a password system instead. :D

That is indeed a good option if the amount of data you have to save isn't that large.
Re: Need instruction on how to save.
by on (#129700)
What is the best environment to develop NES games in? I write the code in GVIM and have it compiled in NESASM3. Is there a better way to do this that I am unaware of?
Re: Need instruction on how to save.
by on (#129701)
The most popular assemblers around here seem to be ca65 and ASM6. ASM6 is simpler; ca65 is more flexible. I have a project file that will get you started with ca65 on SNROM (common board with save, using MMC1).

Password systems can comfortably save up to 32 bits of data and uncomfortably up to 96 or so.
Re: Need instruction on how to save.
by on (#129702)
NEASM3 has a lot of users because it's often used in tutorials, but it has some flaws and limitations that sometimes get in the way if you don't have the experience to know what's happening. It uses non standard 6502 syntax for some things (i.e. indirection is [], not (), like in every other 6502 assembler in existence), and is known to assemble corrupt binaries without any warnings if your source code contains certain errors (e.g. wrong addressing modes and such). It also makes the somewhat annoying assumption that all code must be broken up in 8KB banks. Apparently you can't let the code/data smoothly spill into the next bank, you have to explicitly use a .bank directive to specify the beginning of every bank, even if the specific mapper you're using doesn't deal with 8KB PRG-ROM banks.

ASM6 is as simple as NESASM to use, it's just different. It has no special directives to create the iNES header, but you can create one just as easily with .db statements. Other than that, it's practically NESASM without the annoyances.

CA65 appears to be the definitive assembler with tons of advanced features, and because of this, getting started with it is a significantly more complex process. You can't just give it an ASM file and get an NES file on the other end, first you have to write a config file detailing how the memory is supposed to be mapped (which changes from mapper to mapper), then you assemble the code and get an object file, not a ROM, which you have to link (whatever that is) using a separate program to finally get a working ROM. You can of course get config files made by other people, but if you don't understand how the process works you're bound to hit a wall sooner or later.
Re: Need instruction on how to save.
by on (#129707)
Saying ASM6 Is easier to use is complete BS. I write with NESASM3, but the ASM6 code to set up a ROM is ridiculousness for an assembler. The NES community is large enough, we need our own NES assembler. I'm working on one I'll open source when it's done and add features and such or let users do that. It's time we get something not a POS to make NES games with the NES mappers and such in mind. No assembler does that better than NESASM3, but YES it does have tons of limitations. My end point was RAM code, too much of a bitch to do, so time to make my own tools.

Still, all you do is put memory somewhere and read it later in the 6000-7FFF range on a cart with a mapper. That can also be used as normal RAM. But you also can just use passwords, like others said.
Re: Need instruction on how to save.
by on (#129709)
I believe that in NESASM, the INESMIR command is used for that purpose; add 2 to the number given to set bit1 (so, put 3 instead of 1).

Saving is then done in a range $6000-$7FFF; you can use part of it as ordinary RAM too, if you like to do so.

I myself prefer a variant of NESASM (I have made various changes myself). I prefer the non-standard syntax used in MagicKit. But, you are free to make your own, and let's see how well it is, too! Perhaps it is good, at least for some people.

Also, Family BASIC does come with the ability to disable writes to battery RAM, although it is a physical switch rather than software-controlled.
Re: Need instruction on how to save.
by on (#129711)
3gengames wrote:
Saying ASM6 Is easier to use is complete BS.

I didn't say it was easier, I said it just as as easy as NESASM. It really is, it just uses different words to build the basic structure of the ROM, but the amount of typing isn't that different and the assembling process is the same (supply .asm file, get .nes file).

Quote:
the ASM6 code to set up a ROM is ridiculousness for an assembler.

Eh? I don't see what's so ridiculous. Ridiculous is having to put markers every 8KB and losing code without warnings if you fail to do so.

Quote:
The NES community is large enough, we need our own NES assembler.

I disagree. I like to code for other 6502 machines, such as the Atari 2600, and I'm perfectly fine with using an assembler that is versatile enough to handle code for the different machines I like to work with. I don't want to use a different assembler for each machine I work with, and have to deal with different peculiarities and limitations if the language is essentially the same! I already find it bad enough that I have to use different assemblers for different CPUs! Ideally I'd have a single assembler, with a set of features that I could use consistently across all the CPUs I write code for, with only the instructions themselves differing from CPU to CPU.
Re: Need instruction on how to save.
by on (#129714)
3gengames wrote:
we need our own NES assembler


What will it be able to do that current assemblers cannot?
Re: Need instruction on how to save.
by on (#129716)
Movax12 wrote:
3gengames wrote:
we need our own NES assembler


What will it be able to do that current assemblers cannot?


Output NES ROMS and include iNES 2.0+iNES 1 options right as assembler directives in clear text, for beginners. After that, it'll be able to do whatever anyone else wants to make it do. Js....expect it to be done in a long time, I'm just pointing out not everyone likes using assemblers that are dynamic, but that makes them insanely hard to use and follow. Do you see NESASM3 templated around? No, because it's not as difficult to get a more NES-focused assembler working from nothing but the readme.
Re: Need instruction on how to save.
by on (#129717)
NESASM:
Code:
.inesprg 2 ; 16k PRG
.ineschr 1 ; 8k CHR
.inesmir 1 ; 0 = horizontal mirroring, 1 = vertical mirroring
.inesmap 0 ; 0 = NROM


CA65:
Code:
.segment "HEADER"

INES_MAPPER = 0 ; 0 = NROM
INES_MIRROR = 1 ; 0 = horizontal mirroring, 1 = vertical mirroring
INES_SRAM   = 0 ; 1 = battery backed SRAM at $6000-7FFF

.byte 'N', 'E', 'S', $1A ; ID
.byte $02 ; 16k PRG bank count
.byte $01 ; 8k CHR bank count
.byte INES_MIRROR | (INES_SRAM << 1) | ((INES_MAPPER & $f) << 4)
.byte (INES_MAPPER & %11110000)
.byte $0, $0, $0, $0, $0, $0, $0, $0 ; padding


I don't think we need to write a whole new assembler just to explore the difference between these two pieces of code.

Creating an iNES header with your assembler or with a hex editor is about the most trivial thing one can possibly do in NES programming. If a beginner can't get through this, especially since it can be solved by a CTRL-C, CTRL-V from the myriad of available examples, there may be no hope at all. There are many things that are difficult to learn about NES and 6502 programming, but this isn't one of them.
Re: Need instruction on how to save.
by on (#129718)
Of course, there's always the option of using a separate utility to combine the header, PRG, and CHR into a .NES file afterwards, which is what I do. This also tends to be useful if, for some reason, you intend to produce both .NES and .UNIF outputs.
Re: Need instruction on how to save.
by on (#129719)
3gengames wrote:
Output NES ROMS

Every 6502 assembler I know of can do this.

Quote:
and include iNES 2.0+iNES 1 options right as assembler directives in clear text, for beginners.

So you feel the need to write whole new parsing/assembling/macro engines because of a mere 16 bytes (which in the absolute worst case can be written by hand) that go in the beginning of the ROM? OK.

Quote:
Do you see NESASM3 templated around?

Yes, in the newbie tutorials most beginners use.

rainwarrior wrote:
CA65:
Code:
.segment "HEADER"

INES_MAPPER = 0 ; 0 = NROM
INES_MIRROR = 1 ; 0 = horizontal mirroring, 1 = vertical mirroring
INES_SRAM   = 0 ; 1 = battery backed SRAM at $6000-7FFF

.byte 'N', 'E', 'S', $1A ; ID
.byte $02 ; 16k PRG bank count
.byte $01 ; 8k CHR bank count
.byte INES_MIRROR | (INES_SRAM << 1) | ((INES_MAPPER & $f) << 4)
.byte (INES_MAPPER & %11110000)
.byte $0, $0, $0, $0, $0, $0, $0, $0 ; padding

Yes, headers can be created like this in practically any assembler. This is something you get done in the first 5 minutes of coding a game and never look at again during the remaining several months/years of development. Not really worth coding another buggy assembler, is it?
Re: Need instruction on how to save.
by on (#129730)
rainwarrior wrote:
NESASM:
Code:
.inesprg 2 ; 16k PRG
.ineschr 1 ; 8k CHR
.inesmir 1 ; 0 = horizontal mirroring, 1 = vertical mirroring
.inesmap 0 ; 0 = NROM


CA65:
Code:
.segment "HEADER"

INES_MAPPER = 0 ; 0 = NROM
INES_MIRROR = 1 ; 0 = horizontal mirroring, 1 = vertical mirroring
INES_SRAM   = 0 ; 1 = battery backed SRAM at $6000-7FFF

.byte 'N', 'E', 'S', $1A ; ID
.byte $02 ; 16k PRG bank count
.byte $01 ; 8k CHR bank count
.byte INES_MIRROR | (INES_SRAM << 1) | ((INES_MAPPER & $f) << 4)
.byte (INES_MAPPER & %11110000)
.byte $0, $0, $0, $0, $0, $0, $0, $0 ; padding


I don't think we need to write a whole new assembler just to explore the difference between these two pieces of code.

Creating an iNES header with your assembler or with a hex editor is about the most trivial thing one can possibly do in NES programming. If a beginner can't get through this, especially since it can be solved by a CTRL-C, CTRL-V from the myriad of available examples, there may be no hope at all. There are many things that are difficult to learn about NES and 6502 programming, but this isn't one of them.


Using NESASM3, the only change you have is .inesprg 2 instead of .inesprg 1 like I have it. When I changed it, the screen shows only grey. After other experimentation, it still doesn't allow me to write to $6000. So no, hope is not lost because I can't do this one thing. My game is up and running flawlessly besides this it just doesn't have the ability to save. I also tried .inesmir 3 instead of inesmir 1 like I have it. No change. Messed around with .inesmap too. So I'll ask the question again, what do I have to do to make it save?
Re: Need instruction on how to save.
by on (#129733)
raydempsey wrote:
Using NESASM3, the only change you have is .inesprg 2 instead of .inesprg 1 like I have it
Rainwarrior's example had nothing to do with your problem, and everything to do with 3gengames's comment.

You should try what zzo38 said earlier in this thread.

If it doesn't work, your only choices are to use a less awkward assembler than NESASM, or to manually edit the file after NESASM makes it.
Re: Need instruction on how to save.
by on (#129750)
I apologize for getting dragged off topic. I lost sight of what the OP's question was.
Re: Need instruction on how to save.
by on (#129753)
So to sum up:
  1. Make sure the battery bit is set in the header. How to do this varies based on which assembler you use, and there are holy wars on this BBS over which assembler is best.
  2. You may have to write to the mapper to make save memory writable.
  3. Anything you write to $6000-$7FFF will be restored next time you power on.
  4. Stray writes during a power cycle may occasionally corrupt data, as will use of a cartridge for the first time. Use some sort of error detection, such as a modulo 256 sum or a CRC16, so that the game doesn't end up loading bad data.
  5. Save RAM will add to the cost of replication of your game on a cartridge.
  6. If you can pack your saved game into 32 to 64 bits, you can avoid this with passwords.
Re: Need instruction on how to save.
by on (#129754)
Is RAM at $6000-7FFF with NROM universally supported by emulators?
Re: Need instruction on how to save.
by on (#129757)
Any emulator that supports the Famicom keyboard should support at least 2048 bytes there, which Family BASIC requires.
Re: Need instruction on how to save.
by on (#129759)
Nerdy Nights explains how to enable WRAM using NESASM. I still don't quite understand. What I think is happening is the iNES header numbers are altered somehow, and then you have to write something 5 times to the PRG bank register, which I vaguely understand. If anyone could explain how to make this work by telling me what to change the iNES header to and what code to add, it would be much appreciated.

http://www.nintendoage.com/forum/messag ... adid=33260
Re: Need instruction on how to save.
by on (#129760)
Nerdy Nights is giving an example using the MMC1, which only matters to you if you're planning on using the MMC1.

Otherwise, this quote is the relevant bit, and is what zzo38 had said to do:
Quote:
To add a battery to the WRAM, set the .inesmir directive to 2 in the iNES header. Now read and write to the WRAM normally. When power is removed the RAM contents will remain. The emulator will create an 8KB .sav file for the WRAM, however some emulators will not do this unless you have done some WRAM access.
Re: Need instruction on how to save.
by on (#129777)
I tried simply switching inesmir to 2 but that did nothing. I believe it has something to do with writing to $8000 and $E000 but I don't know. I've tried a lot of things but it won't allow me to write to $6000.
Re: Need instruction on how to save.
by on (#129779)
I promise you, the writes to $E000 and $8000 only are relevant if you're using the MMC1 (i.e. ".inesmap 1").

You really shouldn't have to use MMC1 to get an emulation of battery-backed RAM from $6000-$7FFF; what emulator are you testing with?
Re: Need instruction on how to save.
by on (#129785)
tokumaru wrote:
Eh? I don't see what's so ridiculous. Ridiculous is having to put markers every 8KB and losing code without warnings if you fail to do so.
Well, I have corrected this problem. You still need to specify that two adjacent banks should be joined together, but after you do that, it is not necessary to keep track of every 8K; it will do that automatically.

3gengames wrote:
Output NES ROMS and include iNES 2.0+iNES 1 options right as assembler directives in clear text, for beginners.
I have corrected this too. If you use any NES 2.0 exclusive commands, or you specify a mapper number exceeding 255, then it will create a NES 2.0 header.

Quote:
After that, it'll be able to do whatever anyone else wants to make it do.
Perhaps you can specify some examples? Some people pay have more use for some features than what other people prefer. I have implemented many features which is what I use; I find counted labels the least useful of them although most people seem to find counted labels to be the most useful of the features I have added.

Use whatever assembler you prefer though; it is really a matter of preference.

raydempsey wrote:
Using NESASM3, the only change you have is .inesprg 2 instead of .inesprg 1 like I have it.
That changes the PRG ROM size, not the flags. You changed it from 16K to 32K, meaning that the 16K is no longer mirrored to the $C000-$FFFF area, therefore there is no reset vector and it will not work. Use .inesmir to set the flag.

Quote:
To add a battery to the WRAM, set the .inesmir directive to 2 in the iNES header.
Yes, this is how it is done. However, if you want vertical nametable mirroring (horizontal arrangement) then you need .inesmir to be set to 3 instead of 2; the relevant numbers are added together.
Re: Need instruction on how to save.
by on (#129805)
I still can't get saving to work. This is how I start my program: (compiled using NESASM)

.inesprg 1 ; 1x 16KB PRG code
.ineschr 1 ; 1x 8KB CHR data
.inesmap 0 ; mapper 0 = NROM, no bank swapping
.inesmir 3 ; background mirroring

.bank 0
.org $C000

RESET:
SEI ; disable IRQs
CLD ; disable decimal mode
LDX #$40
STX $4017 ; disable APU frame IRQ
LDX #$FF
TXS ; Set up stack
INX ; now X = 0
STX $2000 ; disable NMI
STX $2001 ; disable rendering
STX $4010 ; disable DMC IRQs

After this point, I have been trying to save values into $6000 and it won't respond. I believe I have this set up for MMC1 mapper which is sufficient for what I'm doing (not very familiar with mappers). How do I get it to save!?
Re: Need instruction on how to save.
by on (#129806)
Can you post your NES file? Have you verified that .inesmir 3 actually does what you think it does?

.inesmap 0 sets mapper 0 (NROM), if you want MMC1 you want to use .inesmap 1, but we should verify that you're doing the correct thing for NROM before we resort to MMC1.
Re: Need instruction on how to save.
by on (#129810)
Possibilities:
  1. NESASM isn't writing the battery bit correctly. Do what rainwarrior says first.
  2. You have not yet configured a writable folder in which to save saved games. Do any other games save correctly?
  3. A particular emulator doesn't save on mapper 0 unless the NES 2.0 header is present and specifies RAM. I don't know how to get NESASM to write NES 2.0 headers.
  4. A particular emulator uses a database to correct headers of commercial games' ROM images, and it doesn't save on mapper 0 unless the hash value of the PRG ROM matches that of Family BASIC.
Re: Need instruction on how to save.
by on (#129856)
tepples wrote:
a. NESASM isn't writing the battery bit correctly. Do what rainwarrior says first.
Check in the hex editor or hex dump if you have any, what it is set. Some emulators will also display the header to you.

Quote:
b. You have not yet configured a writable folder in which to save saved games. Do any other games save correctly?
This is definitely important to check.

Quote:
c. A particular emulator doesn't save on mapper 0 unless the NES 2.0 header is present and specifies RAM. I don't know how to get NESASM to write NES 2.0 headers.
Standard NESASM has no such capability. Unofficial MagicKit (my own version) does support it, if you use any of the NES2* commands, or if you specify a mapper number outside of the range 0-255. Of course, make sure the emulator you are using supports NES 2.0 format!

Quote:
d. A particular emulator uses a database to correct headers of commercial games' ROM images, and it doesn't save on mapper 0 unless the hash value of the PRG ROM matches that of Family BASIC.
This corresponds to a bad feature of many emulators (point of advice: if you implement it, please ignore the database in case of NES 2.0 header present!). However, if the database is external, you may be able to add records to it from NES 2.0 headers.

To work-around both cases c and d, you could use a different mapper, but that ought not to be required.