Overworld graphics bank swap to Zelda 1 MMC3 conversion?

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

ShadowOne333
Posts: 12
Joined: Fri Sep 11, 2020 10:38 am

Re: Overworld graphics bank swap to Zelda 1 MMC3 conversion?

Post by ShadowOne333 »

Fiskbit wrote: Mon Dec 21, 2020 1:05 am I spent about 4 hours putting together a proof of concept using MMC1 and 32 KB of CHR-RAM, currently just animating the waterfall. It targets PRG0 (PRG1 will likely require changing some addresses). It uses 4 KB CHR mode and swaps the entire background table at once (between banks 1-4). The automap will theoretically work with it, but they may have conflicts that need to be resolved, and the automap graphics will have to be drawn into all 4 banks, which may be a pain when doing a scroll. I leave further work as an exercise to the reader. I'm maybe half confident the code is bug-free, but make no promises or guarantees. Anyone is free to use this code for any purpose, commercial or otherwise. I've included the targeted assembler, snarfblasm; I believe this is the version we use for Z1M1. Usage is "snarfblasm.exe animate.asm".

Edit: One thing that could prove to be an issue in the code as-is is the location I hijacked to do the bankswapping. It's actually further into rendering than I expected, though it does seem to only ever happen in the HUD. Might be best to do that earlier and with an explicit mode and pause check. It's not a problem now, but could be a pain depending on how you do the automap or how much code you add before the current swap.
This is really interesting! Thanks for sharing it, and for taking the time to code it.
I didn't know about this kind of NES 2.0 header of sorts, nor about the extra flags at the header's tail.

I took a quick glance and test at this code, and for sure it's something really interesting to see in execution.
I went ahead and converted your Snarfblasm ASM code to work with xkas, I have attached the xkas ready file in this post.
It should work out of the box with Zelda Redux's repository:
https://github.com/ShadowOne333/The-Leg ... elda-Redux

Simply put the animation.asm inside the gameplay/ folder, and add the line in the main.asm code, then run make.bat.

As for questions about your animation code:
  1. From what I was able to gather from your code, is that it allows for 256 bytes of data to be transferred over to the background PPU at once? Is that 256 bytes (as in 0x256) or just 256 (0xFF)?
  2. If I understood it right, you are basically transferring the specific waterfall tiles you want into the PPU $1890, correct? Not really the whole background tiles?
  3. As a last question. For the Automap tiles, would they need to be copied over to RAM in each animation frame? From what I see, it seems that the whole background page is being changed to that of other banks (which makes sense). Would another PPU transfer be needed specifically for the Automap tiles? Or could the whole top part of the background be possible to be left as is, and only modify the rest of the tiles starting on the stairs of the overworld tiles and onwards?
I'm kind of a noob when it comes to this, so any kind of clarification would help me to understand the code properly.
Attachments
animate.asm
(11.54 KiB) Downloaded 109 times
Fiskbit
Posts: 891
Joined: Sat Nov 18, 2017 9:15 pm

Re: Overworld graphics bank swap to Zelda 1 MMC3 conversion?

Post by Fiskbit »

iNES headers are insufficient for representing many hardware configurations, so emulators have to make a lot of incorrect assumptions and also use databases that override the header information. NES 2.0 attempts to fix this and makes it possible to represent a lot of additional necessary information (though there are ROM sets that have bad NES 2.0 headers, so emulators still need databases to fix these bad headers).


The hack works by having four 4 KB CHR-RAM banks used for the background. Bank 1 is handled by the game like normal and contains the graphics for the current area. The remaining three banks start out having the standard overworld graphics because I didn't want to expand the ROM to make room for four unique representations of the whole table. After that initial setup, these three banks are all updated with their unique frames of animation; that's where the 256 byte tables (decimal, not hex; prefixless means decimal) come into play. Each contiguous block of graphics uses 6 bytes to specify the source address in the current bank, the size, and the destination PPU address. This means each bank can get up to 42 contiguous blocks of custom graphics of any size (assuming the graphics fit in the bank) because of the 256 byte limit, which exists because it seemed large enough and just makes the code a bit simpler.

The automap uses a part of the table that is normally never updated. Its code updates just the part you scroll into, or the whole thing when loading a file. These updates simply need to be duplicated into the 3 new banks so the automap still has its graphics when the bank is changed (so you'd do the writes for one bank, then switch to the next bank and do the writes, and so on, ideally updating the one tile in all 4 banks in a single vblank). This only needs to be done once, not constantly. The point of this change is that effectively no work has to be done for animation, just like with CHR-ROM games. Because it's RAM, instead, you get the benefit of being able to update on the fly, but the animation is just done by swapping banks, not writing to VRAM like if there were only one bank.

Doing the automap properly is more complex if using CHR-ROM. IIRC, you can see shortcuts being taken in the RHDN thread where the automap is updated 8x8 pixels at a time instead of 4x4, which means entering a new screen will reveal other screens it shares a tile with. You can get around this, but it's more work.
ShadowOne333
Posts: 12
Joined: Fri Sep 11, 2020 10:38 am

Re: Overworld graphics bank swap to Zelda 1 MMC3 conversion?

Post by ShadowOne333 »

Fiskbit wrote: Thu Dec 24, 2020 12:27 amThe hack works by having four 4 KB CHR-RAM banks used for the background. Bank 1 is handled by the game like normal and contains the graphics for the current area. The remaining three banks start out having the standard overworld graphics because I didn't want to expand the ROM to make room for four unique representations of the whole table. After that initial setup, these three banks are all updated with their unique frames of animation; that's where the 256 byte tables (decimal, not hex; prefixless means decimal) come into play. Each contiguous block of graphics uses 6 bytes to specify the source address in the current bank, the size, and the destination PPU address. This means each bank can get up to 42 contiguous blocks of custom graphics of any size (assuming the graphics fit in the bank) because of the 256 byte limit, which exists because it seemed large enough and just makes the code a bit simpler.
Oh I thought the 256 bytes were actually the tile data bytes, not the ones at "dAnimatedGraphics".
That's neat. So there's no limit to how many tile data I transfer over? The only limit is the actual bytes used for Destination (big endian), source (little endian), size (little endian)?
If that's so, then I'll see what I can do about that.
I am thinking of probably adding the section of background tiles I need to have copied to each bank, starting from the Automap tiles, and up to the water tiles.

In the case the needed graphics do not fit in the specified banks, would expanding the the ROM and moving all the graphics to $20000 or above be possible for this? Or would it be better to have it all be in the first 3-4 banks?
Fiskbit wrote: Thu Dec 24, 2020 12:27 amThe automap uses a part of the table that is normally never updated. Its code updates just the part you scroll into, or the whole thing when loading a file. These updates simply need to be duplicated into the 3 new banks so the automap still has its graphics when the bank is changed (so you'd do the writes for one bank, then switch to the next bank and do the writes, and so on, ideally updating the one tile in all 4 banks in a single vblank). This only needs to be done once, not constantly. The point of this change is that effectively no work has to be done for animation, just like with CHR-ROM games. Because it's RAM, instead, you get the benefit of being able to update on the fly, but the animation is just done by swapping banks, not writing to VRAM like if there were only one bank.

Doing the automap properly is more complex if using CHR-ROM. IIRC, you can see shortcuts being taken in the RHDN thread where the automap is updated 8x8 pixels at a time instead of 4x4, which means entering a new screen will reveal other screens it shares a tile with. You can get around this, but it's more work.
To understand this better, by duplicating the Automap tiles into the other 3 new banks, does that mean they need to be in the EXACT same address as where it is right now?

The tiles for Automap are located here:

Code: Select all

bank 5; org $AD00	// 0x16D10-0x16F0C
// Automap tilemap (Filling map tiles)
	incbin code/gameplay/automap_tiles.bin
So that means I need to copy the tiles into $AD00 of each bank? Or can they simply be added alongside whatever other graphics will be transferred?
Like having the whole Automap tiles, water, waterfalls, etc. and then transferring to the PPU equivalent of where the Automap tiles would begin?
Fiskbit
Posts: 891
Joined: Sat Nov 18, 2017 9:15 pm

Re: Overworld graphics bank swap to Zelda 1 MMC3 conversion?

Post by Fiskbit »

You misunderstand. The automap in the corner specifies in the nametables what tiles it uses from the pattern tables, and that nametable data is fixed; it does not change as you explore. Instead, the automap takes advantage of CHR-RAM, filling in the graphics in the pattern tables as you explore instead of having all the graphics there up front. If the graphics are all there at the start, then you're basically doing a CHR-ROM solution, and doing the automap with no compromises becomes a much more involved problem. For CHR-RAM, if you're switching out the graphics in the table without updating the nametables, then the graphics necessarily come from the same location in the new table. Therefore, if you want them to stay the same as the tables are being swapped out, the exact same graphics must be in the exact same locations in each table. This has nothing to do with ROM data; the graphics should occur only once in the ROM. The automap tiles are also not copied into the table at the beginning.

You're correct that there is no code limit to the amount of animation graphics you can use, just a bank limit. The code limit is simply limiting you to 42 contiguous blocks of animation graphics, but those blocks can be any size you want.

Yes, you can update the code to pull animation graphics from another bank. The code to do the update can be moved to or duplicated into another bank, and the new bank's version is called for the banks that have data in the new bank. I think the easiest solution would be to make a duplicate of CopyAnimatedTiles in another bank, call that (with a loop so it does it for all 3 CHR-RAM banks) in addition to the old one, and split the graphics between the two ROM banks. If a particular RAM bank is not getting animated tiles in one of these ROM banks, its table should just be empty (ie jsut the #$FF terminator that signals the end of the table).
ShadowOne333
Posts: 12
Joined: Fri Sep 11, 2020 10:38 am

Re: Overworld graphics bank swap to Zelda 1 MMC3 conversion?

Post by ShadowOne333 »

I'm still a bit confused about the Automap stuff with the CHR-RAM animation.
For Automap to work properly being in the "same location", what exactly do you mean by that?
I usually relate that to ROM addresses, in the same bank, but you refer to being in the same locations in each table, but which table do you mean?
Sorry if I sound obnoxious, ASM is still dark magic to me sometimes :P

So indeed it is possible to move the entirety of the overworld graphics to bank 8 or further, and reserve those banks exclusively for the animation then?
Fiskbit
Posts: 891
Joined: Sat Nov 18, 2017 9:15 pm

Re: Overworld graphics bank swap to Zelda 1 MMC3 conversion?

Post by Fiskbit »

If the automap is at tiles $40-5F in one bank, the exact same graphics must be at tiles $40-5F in all the other banks.

The graphics can live in any ROM bank you want. The only limitation is your ability to modify the code. Most of the work is done.
bogaa
Posts: 8
Joined: Sat Oct 20, 2018 9:55 pm

Re: Overworld graphics bank swap to Zelda 1 MMC3 conversion?

Post by bogaa »

To say it simple. The automap code does copy the location of the map in the beginning. (nametable) Then does update the graphic (patterntable) when ever you explore that location. The graphic (pattern) needs to be on a fixed location to show up on the right place.

The MMC5 did handle it with rewriting the Automap to work with name tables instead. I was worried about that people might salvage MMC5 parts. Since the cards are rare I do think people might reconsider other options. Like flashing it on SD card solution to play on real hardware. Currently it would not be much effort to just port to MMC3 since the bank swapping stuff is done just needs to be rewritten to work on the MMC3 registers.

Still I do think the MMC5 is nice since you can have so many sprites and who knows if anyone would like to make a music engine that uses extra channels. OR use other features. I am glad you mentioned that people really do salvage parts for things like this :roll:

You got a interesting solution. I never did anything with CHR RAM before. I do think CHR RAM could be capable for some effects not seen yet on a NES. May be I will try myself for a demo for this. Also I did a solution before where I animated the waterfall with just using regular PPU RAM updates.

Next would be to do the animation with nametables. But then we would be done with solutions on how it could be animated on the NES.
*of course I did not think about sprite solutions..
*thinking again I did see nice effects with palettes too..
ShadowOne333
Posts: 12
Joined: Fri Sep 11, 2020 10:38 am

Re: Overworld graphics bank swap to Zelda 1 MMC3 conversion?

Post by ShadowOne333 »

Sorry was kinda busy during the holidays.
I took a stab at the code today, and managed to port the water and waterfall animations from the MMC5 version into the MMC1 using the Animation code you provided as the base, Fiskbit.

Here's how the animation looks atm:

Image

I basically took only the water tiles from all the frames, and then went ahead and did the proper PPU transfers for it.
The animated tiles work as expected.

However, there's still 3 issues to handle for a full implementation: For Point #2 (Cracked walls on OW), I did try to send the Visible secret tiles to the PPU $1540-$15FF for each bank frame, but that didn't seem to work at all. Same thing happened when I tried to do something similar just for testing for the Automap tiles (i know it isn't supposed to be done that way, but just wanted to check what it did). The Automap tiles being at $1300 and end at $14FF.
I am not sure if that's because the PPU transfers are locked to a certain PPU range to work with the animation frames (since the water tiles do work fine). What's the actual range for usage of animated frames in the PPU?

Another slight thing, the animation still goes on even when using the Flute for the pools that can be drained.
@Bogaa made a workaround for this on the MMC5 implementation, so the animation stops after the water has been drained, which could possibly be reimplemented for the MMC1 Animation as well:
https://github.com/ShadowOne333/The-Leg ... #L124-L140

Code: Select all

EndCHRAnimation:	
// Lake Drain
	lda.w $051A	// Lake Drain active on that screen.
	beq SkipLake
	cmp.b #$0C	// Lake Fully Drained = $0C
	bne DrainSpeed
	lda.b #$00	// Stop Animation
	sta.w $0700
DrainSpeed:
	lda.w $0700
	cmp.b #$04	// Check max frame. So it will animate every 4 frames
	bne SkipLake
	lda.b #$0F	// Set to trigger next frame
	sta.w $0700		
SkipLake:
	jsr $A0F6	// Hijackfix
	rts
With that said, here's the full code of what I currently have:
https://github.com/ShadowOne333/The-Leg ... /animation

(I have added you to the credits of the project as well for the MMC1 animation, Fiskbit)
Fiskbit
Posts: 891
Joined: Sat Nov 18, 2017 9:15 pm

Re: Overworld graphics bank swap to Zelda 1 MMC3 conversion?

Post by Fiskbit »

To make it stop animating in whatever your desired circumstances, update Hijack_HandleAnimation to simply skip the timer handling code in the cases you care about, just as it currently skips it if a dungeon is loaded.

The select patch causes this to crash because it changes the currently loaded bank from 2 to 5, while the call to Hijack_HandleAnimation at 07:EC7A expects bank 2 to be loaded. Hijack_HandleAnimation can be moved to bank 5, or alternatively bank 2 can be swapped back in. Moving it to bank 5 solves the problem with fewer cycles used, so it will contribute less to lag.

The overworld visible secrets (which feels like an oxymoron to me, but I guess people like this) aren't loading into the other banks because my code doesn't use the game's original code for loading the 3 new banks. Calling the normal graphics loading code would try to load sprite graphics, too, but I only want background. LoadAnimatedGraphicsBanks needs to be modified to call the new TileTransfer function for each bank.
bogaa
Posts: 8
Joined: Sat Oct 20, 2018 9:55 pm

Re: Overworld graphics bank swap to Zelda 1 MMC3 conversion?

Post by bogaa »

Here I made the RAM CHR work along automap and the hidden secret. There are many patches in the folder so you can test different configuration. https://cdn.discordapp.com/attachments/ ... _hints.zip
ShadowOne333
Posts: 12
Joined: Fri Sep 11, 2020 10:38 am

Re: Overworld graphics bank swap to Zelda 1 MMC3 conversion?

Post by ShadowOne333 »

Thanks to Bogaa's help, both the visible secrets and the item toggle are now fully working with the MMC1 animation.
The modified code for these can be checked here inside the "visible_secrets.asm" and "item_toggle.asm" files:
https://github.com/ShadowOne333/The-Leg ... e/gameplay

As for doing the Lake animation stop when the lake is drained, I got it working by porting and adjusting Bogaa's routine from the MMC5 animation for the MMC1 code:
https://github.com/ShadowOne333/The-Leg ... #L145-L164

Code: Select all

Hijack_HandleAnimation_Done:
// Animation Lake fix ported from  MMC5 animation (by Bogaa)
// Lake Drain
	lda.w $051A	// Lake Drain active on that screen.
	beq SkipLake
	cmp.b #$0C	// Lake Fully Drained = $0C
	bne DrainSpeed
	lda.b #$00	// Stop Animation
	sta.b {animation_timer}
DrainSpeed:
	inc.b {animation_timer} 	// Accelerate animation speed when the water is draining
	lda.b {animation_timer} 	// (Could possibly be removed later on for MMC1)
	cmp.b #$04	// Check max frame. So it will animate every 4 frames (could possibly be removed later on for MMC1)
	bne SkipLake
	lda.b #$0F	// Set to trigger next frame, (could possibly be removed later on for MMC1)
	sta.b {animation_timer}	// (Could possibly be removed later on for MMC1)
SkipLake:
// Perform the hijacked instruction.
	lda.w {has_clock}
	rts
However, I found something interesting with the MMC1 animation.
Whenever the player uses the Flute in the overworld, it seems the animation timer (RAM $4F) seems to pause completely when the music is playing, and then resumes once it is done. The frame counter at RAM $15 does continue normally. I tried looking around the animate.asm code to try to find the possible reason why this occurs, but couldn't find anything concrete. Do you know why this would happen, Fiskbit?

After this, the very last thing to do would be the Automap changes for MMC1 Animation with CHR-RAM (which I still haven't looked into) and that would be it!
Fiskbit
Posts: 891
Joined: Sat Nov 18, 2017 9:15 pm

Re: Overworld graphics bank swap to Zelda 1 MMC3 conversion?

Post by Fiskbit »

The animation hijack is in the gameplay mode after pausing is handled. However, the very first thing in the gameplay mode is the handling of the recorder timer at Bank7_EC1B. If the timer is set, it never makes it to the animation hijack.

I don't consider this to be a bad thing. The recorder halts the entire world; I would expect it to be desirable for animation to stop, too.
ShadowOne333
Posts: 12
Joined: Fri Sep 11, 2020 10:38 am

Re: Overworld graphics bank swap to Zelda 1 MMC3 conversion?

Post by ShadowOne333 »

Seems like the animation with the lake stuff works fine, so I'd call it a day for that.
As for the flute halt, yeah you got a point. Since everything else halts at that point, it makes sense the animation would as well.

If one were to make a hijack so it takes into account the flute timer, where would you suggest one to make it?
I gave it a small try just for fun today, and (iirc) I attempted a hijack at $1EC1F (replacing LDA $051E), and it completely removes the halting when playing the flute, allowing for Link to move around :P

--------------------------------------------------------------------------------------------------------

Aside from that, I attempted fixing the CHR-RAM automap for MMC1 animation, and while I couldn't get it to work properly, I think I narrowed down where the fix could be implemented:
https://github.com/ShadowOne333/The-Leg ... #L599-L607

Code: Select all

SendTileToPPU:    // $BCA2, 0x17CB2
    ldx.b #$00    // A2 00
l_BCA4:        // BCA4:
    lda.w {MapTileMacro}+3,x    // Load tile data byte of MapTileMacro+3,X
    sta.w $2007    // Write to PPU -> PPU_DATA = #$90
    inx
    cpx.b #$10    // Compare with $10
    bne l_BCA4    // D0 F5
    rts
I tried using Bogaa's fix from the visible secrets code, and I kinda got some tiles to be written to the other 4 banks, but not the proper tiles from Automap. It's some progress, I'll try doing more tests through the week.
Post Reply