Convert UNROM to SNROM for SRAM Saving....Possible?

Started by 8.bit.fan, November 22, 2017, 02:27:11 PM

Previous topic - Next topic

8.bit.fan

Hi Everyone! :)
I'm back to hacking more games to give them SRAM Saving function!
However, there are a handful of games that uses mappers(such as UNROM, etc.) that do not have SRAM saving function capabilities.

For example, right now, I'm trying to add SRAM Saving to 'Majou Densetsu II -Daimashikyou Galious (J) [!].nes', but the game uses the UNROM mapper. So, even if I am able to write instructions to store save info in $6000-$7FFF, it wouldn't work because that section of the RAM keeps changing...like this:


So my question is:
Is it possible to convert a game that uses UNROM to something like SNROM so I can add SRAM Saving function?
Or, is there a way to save to SRAM for a game that uses UNROM mapper?

Any help would be greatly appreciated! :thumbsup:

Thank you!! :beer:

8-bit fan
In the year of 200X, a super robot named Mega Man...
http://www.8bitfan.info/
FF4 Ultima Discord: https://discord.gg/4MqjwJt

Disch

I don't think there are any UxROM boards which have on-board RAM (nesdev wiki says the board doesn't support it).  I wouldn't think most emulators or emu-on-a-carts like the PowerPak would care and would've put RAM at the $6000-7FFF range anyway for compatibility reasons -- but apparently I'm wrong!!  Interesting.

The constantly changing values in the hex editor is definitely a bug in FCEUX, though... and a weird one.  Maybe a stray/uninitialized pointer?  In any case, when I tested and read from that range I didn't get the values it showed... instead I got open bus as I'd expect.


Anyway, to answer your questions:

1)  Yes, it is possible to do a mapper conversion.  A UxROM to <insert other mapper here> mapper hack isn't different from any other mapper hack.  Just pick a mapper that has similar swapping abilities as well as SRAM support (MMC1 could work), then find all the points at which the game does mapper stuff and replace it with the MMC1 equivalent.   The only tricky thing with UxROM is that since the swap code is so small, many games do inline swaps without JSRing to a separate swap routine -- which means it might be hard to track down ALL the mapper code.

2)  No, there's not a reliable way to add SRAM to UxROM.  You'd probably be able to get some emulators to work, but others will choke.  Best to switch mappers.

8.bit.fan

Quote from: Disch on November 22, 2017, 03:42:55 PM
I don't think there are any UxROM boards which have on-board RAM (nesdev wiki says the board doesn't support it).  I wouldn't think most emulators or emu-on-a-carts like the PowerPak would care and would've put RAM at the $6000-7FFF range anyway for compatibility reasons -- but apparently I'm wrong!!  Interesting.

The constantly changing values in the hex editor is definitely a bug in FCEUX, though... and a weird one.  Maybe a stray/uninitialized pointer?  In any case, when I tested and read from that range I didn't get the values it showed... instead I got open bus as I'd expect.


Anyway, to answer your questions:

1)  Yes, it is possible to do a mapper conversion.  A UxROM to <insert other mapper here> mapper hack isn't different from any other mapper hack.  Just pick a mapper that has similar swapping abilities as well as SRAM support (MMC1 could work), then find all the points at which the game does mapper stuff and replace it with the MMC1 equivalent.   The only tricky thing with UxROM is that since the swap code is so small, many games do inline swaps without JSRing to a separate swap routine -- which means it might be hard to track down ALL the mapper code.

2)  No, there's not a reliable way to add SRAM to UxROM.  You'd probably be able to get some emulators to work, but others will choke.  Best to switch mappers.
Hey Disch!
Thanks so much for the reply and thorough explanation! Much appreciated!! :)

But yeah, I figured as much, that the best way to go about this is to convert the game to a different mapper(MMC1) to do the SRAM hack. However, this is an undertaking that is pretty much beyond what I'm capable of. For example, I have no clue what a swap code is, etc. So with that said, would you happen to know any FAQs or Step-by-Step instructions on how I would go about converting mappers?

Thanks again for all your help! :)

8-bit fan
In the year of 200X, a super robot named Mega Man...
http://www.8bitfan.info/
FF4 Ultima Discord: https://discord.gg/4MqjwJt

Disch

To do a mapper hack you need to know 3 things:

1)  6502 assembly  (which I assume you are reasonably proficient in if you are adding SRAM to a game)
2)  How the original mapper works (In this case, UxROM)
3)  How the new mapper works (In this case, MMC1, I assume, unless you decide on something different).


Most mappers do more or less the same thing.  So doing a mapper hack involves figuring out what the game is doing in one mapper, and doing the equivalent thing in another mapper.

In this case, UxROM only has 1 thing to do:  Swap PRG.  It does that with code similar to the following:


; Swap to bank 'some_bank_number'
LDA #some_bank_number
TAX
STA $C000,X    ; address may vary -- must be between $8000-FFFF


The equivalent code on MMC1 (to swap PRG) would be the below:

LDA #some_bank_number
STA $F000   ; address may vary -- must be between $E000-FFFF
LSR A
STA $F000
LSR A
STA $F000
LSR A
STA $F000
LSR A
STA $F000



Both of these bits of code might seem strange at first, but what they're doing is very simple.


UxROM
To Swap PRG on UxROM, you just have to write the desired page number to any address in the $8000-FFFF range.  The thing that makes it complicated is that most boards have bus conflicts, so the value you write must match EXACTLY to the value that is read from that address.  IE, if you want to swap to page $05... you'd need to write $05 to an address in the $8000-FFFF range that already contains a $05.

Games often do this with a lookup table that just looks like this:
At address C000:
  00 01 02 03 04 05 06 07


The game then uses the page number as the index to the lookup table to write:


LDA #5        ; want to swap to bank 5
TAX           ; A and X are now both 5
STA $C000, X  ; Since X=5, we will write to $C005, and we know $C005 contains 5 because
              ;   that's the point of the lookup table.



Note that this is just an example, and not a definitive rule.  A game might do something like this:

LDA #$02
STA $8016

... if it just happens to know that $8016 contains a 2.  There isn'te specific routine to do this -- a game can do it any number of ways. It's up to you to look at the code and figure out what it's doing.

But you can know for sure that every single write the original game does to the $8000-FFFF range is the game attempting to swap PRG to a specific page.


MMC1

MMC1 is weirder.  To Swap PRG here, you just have to write the desired page number to any address in the $E000-FFFF range.  The plus side is there's no bus conflicts, so you don't have to worry about writing to an address that contains a certain value.  The down side is MMC1 registers can only be written 1 bit at a time.

MMC1 regs are also 5 bits wide, which means each register write actually takes 5 writes (one for each bit)

Hence the LSR's between each STA.  The low bit is written first, then you shift right to move the next bit into position 0, so it will be written on the next STA.


MMC1 also has additional functionality besides just PRG swapping.  You don't have to use it, since the game you're hacking doesn't need it... however you still have to initialize everything.  Common best practice is to write to all registers once at powerup to put everything in a known state.  So take a look at MMC1's register list:

https://wiki.nesdev.com/w/index.php/MMC1#Control_.28internal.2C_.248000-.249FFF.29

There are 4 registers:

1)  Control ($8000-9FFF).  This sets the mirroring mode, as well as the swap modes.  To match UxROM, you want 8K CHR and 16K PRG swappable at $8000... and either horizontal or vertical mirroring depending on what the game is (I'm too lazy to check).  So you'll want to write either $0E (vertical) or $0F (horizontal).  But again, gotta write 1 bit at a time, so when I say "write" I mean "perform 5 STA's"

2)  CHR bank 0 ($A000-BFFF).  Since you'll be using CHR-RAM, the CHR swapped in doesn't really matter.  You can probably get away with not writing to this reg at all, but if you want to be strict you can write $00.

3)  CHR bank 1 ($C000-DFFF).  Ditto.  Although since you'll be using 8K CHR swapping this reg will actually be ignored, so you REALLY don't need to write to it at all.

4)  PRG bank ($E000-FFFF).  This is what controls the PRG swapping.  Basically just write to this reg whatever the original game does when it bankswaps.

8.bit.fan

Quote from: Disch on November 22, 2017, 07:11:28 PM
To do a mapper hack you need to know 3 things:

1)  6502 assembly  (which I assume you are reasonably proficient in if you are adding SRAM to a game)
2)  How the original mapper works (In this case, UxROM)
3)  How the new mapper works (In this case, MMC1, I assume, unless you decide on something different).


Most mappers do more or less the same thing.  So doing a mapper hack involves figuring out what the game is doing in one mapper, and doing the equivalent thing in another mapper.

In this case, UxROM only has 1 thing to do:  Swap PRG.  It does that with code similar to the following:


; Swap to bank 'some_bank_number'
LDA #some_bank_number
TAX
STA $C000,X    ; address may vary -- must be between $8000-FFFF


The equivalent code on MMC1 (to swap PRG) would be the below:

LDA #some_bank_number
STA $F000   ; address may vary -- must be between $E000-FFFF
LSR A
STA $F000
LSR A
STA $F000
LSR A
STA $F000
LSR A
STA $F000



Both of these bits of code might seem strange at first, but what they're doing is very simple.


UxROM
To Swap PRG on UxROM, you just have to write the desired page number to any address in the $8000-FFFF range.  The thing that makes it complicated is that most boards have bus conflicts, so the value you write must match EXACTLY to the value that is read from that address.  IE, if you want to swap to page $05... you'd need to write $05 to an address in the $8000-FFFF range that already contains a $05.

Games often do this with a lookup table that just looks like this:
At address C000:
  00 01 02 03 04 05 06 07


The game then uses the page number as the index to the lookup table to write:


LDA #5        ; want to swap to bank 5
TAX           ; A and X are now both 5
STA $C000, X  ; Since X=5, we will write to $C005, and we know $C005 contains 5 because
              ;   that's the point of the lookup table.



Note that this is just an example, and not a definitive rule.  A game might do something like this:

LDA #$02
STA $8016

... if it just happens to know that $8016 contains a 2.  There isn'te specific routine to do this -- a game can do it any number of ways. It's up to you to look at the code and figure out what it's doing.

But you can know for sure that every single write the original game does to the $8000-FFFF range is the game attempting to swap PRG to a specific page.


MMC1

MMC1 is weirder.  To Swap PRG here, you just have to write the desired page number to any address in the $E000-FFFF range.  The plus side is there's no bus conflicts, so you don't have to worry about writing to an address that contains a certain value.  The down side is MMC1 registers can only be written 1 bit at a time.

MMC1 regs are also 5 bits wide, which means each register write actually takes 5 writes (one for each bit)

Hence the LSR's between each STA.  The low bit is written first, then you shift right to move the next bit into position 0, so it will be written on the next STA.


MMC1 also has additional functionality besides just PRG swapping.  You don't have to use it, since the game you're hacking doesn't need it... however you still have to initialize everything.  Common best practice is to write to all registers once at powerup to put everything in a known state.  So take a look at MMC1's register list:

https://wiki.nesdev.com/w/index.php/MMC1#Control_.28internal.2C_.248000-.249FFF.29

There are 4 registers:

1)  Control ($8000-9FFF).  This sets the mirroring mode, as well as the swap modes.  To match UxROM, you want 8K CHR and 16K PRG swappable at $8000... and either horizontal or vertical mirroring depending on what the game is (I'm too lazy to check).  So you'll want to write either $0E (vertical) or $0F (horizontal).  But again, gotta write 1 bit at a time, so when I say "write" I mean "perform 5 STA's"

2)  CHR bank 0 ($A000-BFFF).  Since you'll be using CHR-RAM, the CHR swapped in doesn't really matter.  You can probably get away with not writing to this reg at all, but if you want to be strict you can write $00.

3)  CHR bank 1 ($C000-DFFF).  Ditto.  Although since you'll be using 8K CHR swapping this reg will actually be ignored, so you REALLY don't need to write to it at all.

4)  PRG bank ($E000-FFFF).  This is what controls the PRG swapping.  Basically just write to this reg whatever the original game does when it bankswaps.
Wow thanks so much Disch!
This is some amazing info!  :o :)

I'll have to take some time to read and understand this.
But right off, one thing I need to really grasp is bank swapping...both usage/concept and syntax. Sounds super useful also.

Thanks again for your help and I'll give an update if I make any progress! :)

Cheers!!  :D :beer:

8-bit fan
In the year of 200X, a super robot named Mega Man...
http://www.8bitfan.info/
FF4 Ultima Discord: https://discord.gg/4MqjwJt

Disch


8.bit.fan

#6
Quote from: Disch on November 23, 2017, 12:09:30 PM
I threw this together to help explain bankswapping:

https://imgur.com/a/I002Y
Disch you are awesome!! :)
That image spells it out very nicely and doesn't get much clearer than that! Thank you SO much and I'll definitely read it more later after some turkey first! :)

I'll report back later!

Thanks again and have a nice Holiday!! :D

Cheers!! :D  :beer:

8-bit fan

November 27, 2017, 03:28:47 PM - (Auto Merged - Double Posts are not allowed before 7 days.)

Quote from: Disch on November 23, 2017, 12:09:30 PM
I threw this together to help explain bankswapping:

https://imgur.com/a/I002Y
Hi Disch,
So I got the chance to look at what you wrote more thoroughly. Your image needs to be stickied btw :)

But yeah, so this one is going to actually take me a while. As I'll need to first convert the game into a different mapper(which would take a while if at all, not sure if I am capable of this), then adding the SRAM saving function...which would be the easy part as I've done enough of that already.

If anyone can lend a hand in converting the game to MMC1 or MMC3, that would be awesome! It'll be a collaborative project and will give credit to each and everyone that contributes! :)

Until then,  :beer:

8-bit fan
In the year of 200X, a super robot named Mega Man...
http://www.8bitfan.info/
FF4 Ultima Discord: https://discord.gg/4MqjwJt

Disch

Quote from: 8.bit.fan on November 23, 2017, 06:22:23 PM
But yeah, so this one is going to actually take me a while. As I'll need to first convert the game into a different mapper(which would take a while if at all, not sure if I am capable of this), then adding the SRAM saving function...which would be the easy part as I've done enough of that already.

You're building a mental wall.  Doing a mapper hack isn't as hard as you think.  In fact, I'd argue that doing an SRAM hack is harder, as it actually takes understanding of what information the game is handling.

The only thing you really need to do for a mapper hack is find all the code that swaps banks and change it to a 'JSR' that does the MMC1 version of a swap.  It's little more than find/replace.  Time consuming, but very easy.

Don't let it scare you.  You definitely should be able to do it.

Bregalad

Quote from: Disch on November 22, 2017, 03:42:55 PM
I don't think there are any UxROM boards which have on-board RAM (nesdev wiki says the board doesn't support it).

[...]

2)  No, there's not a reliable way to add SRAM to UxROM.  You'd probably be able to get some emulators to work, but others will choke.  Best to switch mappers.
UxROM are Nintendo boards and definitely have no SRAM. iNES mapper #2 is however a super-set of UxROM which is implementation-agnostic, and allows swapping far more banks (8 bits instead of 3 or 4), and allows for SRAM.

So if you're going to hack a game and require SRAM I'd say first do that part of the hack. If in the end, staying with mapper #2 is problematic for whathever reason (it shouldn't be unless you want to run on a real cart, but remind you that it means more people bootlegging your hack and selling repros) - then if this is a problem you can always switch to mapper #1 later. As Disch says this is a super simple thing to do and the only real requirement is to have some extra free ROM as the bankswitch routines will be longer - but this is a requirement for almost any romhack.

8.bit.fan

Quote from: Disch on November 28, 2017, 10:33:09 AM
You're building a mental wall.  Doing a mapper hack isn't as hard as you think.  In fact, I'd argue that doing an SRAM hack is harder, as it actually takes understanding of what information the game is handling.

The only thing you really need to do for a mapper hack is find all the code that swaps banks and change it to a 'JSR' that does the MMC1 version of a swap.  It's little more than find/replace.  Time consuming, but very easy.

Don't let it scare you.  You definitely should be able to do it.
Thanks Disch for the encouragement! :)
Ok I'll definitely look into this when I have the time then! :)


November 28, 2017, 03:47:37 PM - (Auto Merged - Double Posts are not allowed before 7 days.)

Quote from: Bregalad on November 28, 2017, 11:40:15 AM
UxROM are Nintendo boards and definitely have no SRAM. iNES mapper #2 is however a super-set of UxROM which is implementation-agnostic, and allows swapping far more banks (8 bits instead of 3 or 4), and allows for SRAM.

So if you're going to hack a game and require SRAM I'd say first do that part of the hack. If in the end, staying with mapper #2 is problematic for whathever reason (it shouldn't be unless you want to run on a real cart, but remind you that it means more people bootlegging your hack and selling repros) - then if this is a problem you can always switch to mapper #1 later. As Disch says this is a super simple thing to do and the only real requirement is to have some extra free ROM as the bankswitch routines will be longer - but this is a requirement for almost any romhack.
Thanks for the reply Bregalad! :)
Yeah, I'm not concerned about people selling repros of my hacks, in fact, the more people get to play them the better! That's why I make them :)
But yeah, the ROM for this game seems to have some free space(unlike Guardian Legend) so it shouldn't be a problem as long as I can figure this whole thing out.

Thanks again for the advice! :thumbsup:

8-bit fan
In the year of 200X, a super robot named Mega Man...
http://www.8bitfan.info/
FF4 Ultima Discord: https://discord.gg/4MqjwJt

Disch

Quote from: Bregalad on November 28, 2017, 11:40:15 AM
UxROM are Nintendo boards and definitely have no SRAM. iNES mapper #2 is however a super-set of UxROM which is implementation-agnostic, and allows swapping far more banks (8 bits instead of 3 or 4), and allows for SRAM.

FCEUX at the very least seems to be putting open bus at $6000-7FFF for mapper 2.  Though maybe it wouldn't if the battery bit in the header was set?

Either way -- you're playing with fire at this point and are making your hack have questionable emu compatibility.