News:

11 March 2016 - Forum Rules

Main Menu

Zelda 1 Bank Swapping?

Started by gzip, February 06, 2022, 09:23:15 PM

Previous topic - Next topic

gzip

So far I've managed to keep my hacks confined to the free space in whatever bank is being hacked so I haven't had to use any bank swapping. At this point I've largely filled up bank 5 of the Legend of Zelda but I could use more data space, possibly in bank 6. The problem seems to be that the relevant code to process that data is already in bank 5 and bank swapping doesn't really help unless I duplicate that code.

Here's my understanding, please correct as needed.

1) Bank 7 is the fixed bank and is always available in memory at C000-FFFF.
2) A bank swap makes the given bank available at 8000-BFFF.
3) Bank swapping is generally only used from the fixed bank.
4) Code and the data which it references are usually in the same bank (or in the fixed bank); e.g. bank 5 cannot access data from bank 6 unless that data is written somewhere to system RAM at 0000-1FFF.

I noticed something confusing in the Zelda code which makes me question my assumptions:

1) Why do banks 0, 1, 2, 5, and 6 all duplicate a portion of code that's already in bank 7 * (ISR reset, MMC1 control, and the bank swap routine)? I notice that they're always loaded into the same RAM location so the code can always be called reliably, but why not call the code in the fixed bank instead?
2) Why does said code perform a bank swap to bank 7? Does this result in the same code/data both in 8000-BFFF and C000-FFFF?
1FF90: A9 07 LDA #$07 ; A = 07
1FF92: 20 ACFF JSR $FFAC ; Switch to Bank A (7)

3) Can the duplicate code in those banks be removed and the relevant calls be redirected to bank 7 instead?
4) Do banks beside the fixed bank typically use bank swapping?

* Duplicated code in banks 0, 1, 5, 6 and 7 (ROM locations 3F50, 7F50, 17F50, 1BF50, and 1FF50):

1FF50: 78 SEI
1FF51: D8 CLD
1FF52: A9 00 LDA #$00 ; A = 00
1FF54: 8D 0020 STA $2000
1FF57: A2 FF LDX #$FF ; X = FF
1FF59: 9A TXS

1FF5A: AD 0220 LDA $2002
1FF5D: 29 80 AND #$80 ; keep bits x... ....
1FF5F: F0 F9 BEQ $1FF5A

1FF61: AD 0220 LDA $2002
1FF64: 29 80 AND #$80 ; keep bits x... ....
1FF66: F0 F9 BEQ $1FF61

1FF68: 09 FF ORA #$FF ; set  bits xxxx xxxx
1FF6A: 8D 0080 STA $8000
1FF6D: 8D 00A0 STA $A000
1FF70: 8D 00C0 STA $C000
1FF73: 8D 00E0 STA $E000

1FF76: A9 0F LDA #$0F ; A = 0F
1FF78: 20 98FF JSR $FF98

1FF7B: A9 00 LDA #$00 ; A = 00
1FF7D: 8D 00A0 STA $A000
1FF80: 4A LSR
1FF81: 8D 00A0 STA $A000
1FF84: 4A LSR
1FF85: 8D 00A0 STA $A000
1FF88: 4A LSR
1FF89: 8D 00A0 STA $A000
1FF8C: 4A LSR
1FF8D: 8D 00A0 STA $A000

1FF90: A9 07 LDA #$07 ; A = 07
1FF92: 20 ACFF JSR $FFAC ; Switch to Bank A (7)

1FF95: 4C 40E4 JMP $E440

----

1FF98: 8D 0080 STA $8000
1FF9B: 4A LSR
1FF9C: 8D 0080 STA $8000
1FF9F: 4A LSR
1FFA0: 8D 0080 STA $8000
1FFA3: 4A LSR
1FFA4: 8D 0080 STA $8000
1FFA7: 4A LSR
1FFA8: 8D 0080 STA $8000

1FFAB: 60 RTS

----

Bank Switch (Switch to Bank A)

1FFAC: 8D 00E0 STA $E000
1FFAF: 4A LSR
1FFB0: 8D 00E0 STA $E000
1FFB3: 4A LSR
1FFB4: 8D 00E0 STA $E000
1FFB7: 4A LSR
1FFB8: 8D 00E0 STA $E000
1FFBB: 4A LSR
1FFBC: 8D 00E0 STA $E000

1FFBF: 60 RTS


Cyneprepou4uk

#1
If you want to process a great amount of data, normally you put your routine inside a fixed bank, and swap banks with data. If your routine is bigger than fixed bank free space, you can convert to a mapper which is able to swap 8kb banks, and for example keep your routine in 8000-9FFF while swapping data at A000-BFFF.

Regarding your other questions read here http://wiki.nesdev.org/w/index.php/MMC1#Control_.28internal.2C_.248000-.249FFF.29

gzip

Quote from: Cyneprepou4uk on February 07, 2022, 10:52:40 AM
If you want to process a great amount of data, normally you put your routine inside a fixed bank, and swap banks with data. If your routine is bigger than fixed bank free space, you can convert to a mapper which is able to swap 8kb banks, and for example keep your routine in 8000-9FFF while swapping data at A000-BFFF.

That's what I suspected. I'm currently not interested in converting to a different mapper though. I believe another option is to dupe the relevant code to another bank which can also fit the necessary data. Not sure I like that idea either.

Quote from: Cyneprepou4uk
Regarding your other questions read here wiki.nesdev.org/w/index.php/MMC1#Control_.28internal.2C_.248000-.249FFF.29

The only relevant note I see there is that Zelda is using "PRG ROM Mode 3: fix last bank at $C000 and switch 16 KB bank at $8000". That verifies some of my observations but doesn't really answer any of my questions about why Zelda is duplicating code that's already in the fixed bank.

Cyneprepou4uk

#3
You haven't read what's written below the control register description.

If your routine is not too big, you can put it into SRAM. That's basically the same thing as keeping it in a fixed bank.

If none of the options are suitable, then duplicating code is your best bet.

Well, there is also a way to rearrange some code within banks with a disassembly, but I doubt you'll go for it since it will shift a lot of things and make your hack incompatible with others. Though depending on what you are trying to hack exactly, it may already be.

gzip

Quote from: Cyneprepou4uk on February 08, 2022, 07:34:07 AM
You haven't read what's written below the control register description.

Well I did, and just did again, but I fail to grasp how this helps understand why Zelda duplicates the code the way it does. I'm probably just being dense so if you care to explain...

Quote from: Cyneprepou4uk on February 08, 2022, 07:34:07 AM
If your routine is not too big, you can put it into SRAM. That's basically the same thing as keeping it in a fixed bank.

I was wondering about SRAM. The only problem there is it's unclear what's available. The RAM map on Data Crystal and Trax's RAM map don't really point out unused space. Maybe there's a better resource or an easy way to figure it out?

Quote from: Cyneprepou4uk on February 08, 2022, 07:34:07 AM
If none of the options are suitable, then duplicating code is your best bet.

Thanks for the clear answer.

Quote from: Cyneprepou4uk on February 08, 2022, 07:34:07 AM
Well, there is also a way to rearrange some code within banks with a disassembly, but I doubt you'll go for it since it will shift a lot of things and make your hack incompatible with others. Though depending on what you are trying to hack exactly, it may already be.

You're right, I'd like to keep the code as intact as possible for compatibility.

Cyneprepou4uk

It's unknown which bank will be loaded by default, that's why the game has dups in each bank just in case. So regardless of the bank the game will properly execute whatever it needs to anyway.

My notes say that 7EE1-7FFE are free.

gzip

Quote from: Cyneprepou4uk on February 08, 2022, 04:45:22 PM
It's unknown which bank will be loaded by default, that's why the game has dups in each bank just in case. So regardless of the bank the game will properly execute whatever it needs to anyway.

That makes sense but the confusing part is why not just call the same code which is in the fixed bank?

Quote from: Cyneprepou4uk on February 08, 2022, 04:45:22 PM
My notes say that 7EE1-7FFE are free.

Thanks for that.

Cyneprepou4uk

Because, like I told you, you don't know which bank will be loaded. If it will boot in 32kb mode and select a random bank, you're screwed.

gzip

Quote from: Cyneprepou4uk on February 08, 2022, 06:02:57 PM
Because, like I told you, you don't know which bank will be loaded. If it will boot in 32kb mode and select a random bank, you're screwed.

Ah, now I get it, it's a hardware issue. That's why I was being so dense. This is the relevant note from the link you sent:

Quote from: NesDevWikiAlthough some tests have found that all versions of the MMC1 seems to reliably power on in the last bank (by setting the "PRG ROM bank mode" to 3); other tests have found that this is fragile. Several commercial games have reset vectors every 32 KiB, but not every 16, so evidently PRG ROM bank mode 2 doesn't seem to occur randomly on power-up.

Quote from: Cyneprepou4uk on February 07, 2022, 10:52:40 AMIf your routine is bigger than fixed bank free space, you can convert to a mapper which is able to swap 8kb banks...

Regarding changing mappers, didn't you do some work for Infinite Hyrule to make the screen rendering more flexible? If so, do you have any code you can share from that?

Cyneprepou4uk

#9
Yes, I did. But I didn't convert it to another mapper. I've expanded the ROM and ended up duplicating a lot of original code in all 4 banks, because I wanted to make a quick hack and didn't bother with details.

I can give you a patch which converts the game to a default base for the randomizer. Only the very first area has proper background data, which starts at 0x2D210. Set execute breakpoint to 6BE0 to see code details.

https://www.dropbox.com/s/6zxd50chhyn8jnn/Zelda%20infinite%20hyrule.ips?dl=0

Relevant link http://www.romhacking.net/forum/index.php?topic=31345.msg401889#msg401889

gzip

Quote from: Cyneprepou4uk on February 08, 2022, 07:36:37 PM
Yes, I did. But I didn't convert it to another mapper. I've expanded the ROM and ended up duplicating a lot of original code in all 4 banks, because I wanted to make a quick hack and didn't bother with details.

Is there anything more to expansion than modifying the header, physically expanding the rom, and copying the fixed bank to then end?

Quote from: Cyneprepou4uk on February 08, 2022, 07:36:37 PM
I can give you a patch which converts the game to a default base for the randomizer.

Sweet, thanks! I finally had a chance to play around with it this afternoon and edit a few screens. It's nice to have free reign and not be constrained by the default columns.

I'm curious, what did you use to write the code? Did your disassembly come into play? Do you have anything compilable or just the patch?

Quote from: Cyneprepou4uk on February 08, 2022, 07:36:37 PM
Relevant link http://www.romhacking.net/forum/index.php?topic=31345.msg401889#msg401889

I read in the link that you added 8 new banks but only used 4. So I suppose the additional 4 banks could be used to create all new screens for the second quest as well?

Cyneprepou4uk

QuoteIs there anything more to expansion than modifying the header, physically expanding the rom, and copying the fixed bank to then end?

Usually no. But sometimes you need to insert new banks at the beginning of the file, not before the fixed bank. That depends on the game, in case it uses higher bits for banks ID's (probably an antihacking protection), which are originally ignored. In rare cases you need to mess around with those bytes a bit, which is not too hard.

QuoteSweet, thanks! I finally had a chance to play around with it this afternoon and edit a few screens. It's nice to have free reign and not be constrained by the default columns.

Glad you could have some fun with that.

QuoteI'm curious, what did you use to write the code? Did your disassembly come into play? Do you have anything compilable or just the patch?

Just manual hex editing. I've made my disassembly 5 months later, otherwise I would probably use it instead, which would also allow me to easily fit all the data in 2 banks only instead of 4.

QuoteI read in the link that you added 8 new banks but only used 4. So I suppose the additional 4 banks could be used to create all new screens for the second quest as well?

Well, yes, they can be used for anything you wish, just like any free space. I couldn't add only 4 new banks, because the number of banks must be a power of 2.


gzip

Just wanted to add some final thoughts here.

I'm thinking about expanding the ROM, duplicating all the banks (except for the fixed bank) and adding a bit of logic to the bank switching routine which checks the quest number first. If it's the second quest then add 8 to the bank number before switching. This would allow a completely new set of assets for the second quest, including graphics, screens, and enemies, both in the overworld and dungeons. Essentially an entirely new game would be possible for the second quest. All the current second quest logic and data would also be removed to allow for a bigger overworld and/or dungeons (funny how the second quest was only added due to a design mistake which only used half of the available space).

I'd also like to revisit the idea of scrapping the preset columns in the overworld and allowing freeform screen creation similar to what you did for Infinite Hyrule, only adding RLE compression to the screen definitions to reduce the space required (compressing rows instead of columns). It shouldn't be too hard to modify my Zelda Map Generator to run an experiment of converting and compressing the screens to see what that would look like space-wise.

Anyway, I have a few other projects I should wrap up before getting into something new. Really this would be along the same lines of what's already in the works, but first things first. Not sure if I should even open this can of worms since these ideas would most likely require new editing tools to be really useful.