4.5.6 - Fun With Bank Switching

Bucket Mouse

Active member
In version 4.1 I had a game fill up the bank for monster AI before I got close with anything else. So with 4.5.6 I'm trying to think of a way that I could possibly store action step data in a separate bank if it came to it. In version 4.5.6, AI data is stored in bank 1C.

I made a copy of my NESMaker folder, so as not to alter anything important, and got to monkeying with the engine.

There are several parts in doHandleObjects that grab AI data from bank 1C. And Bank 1D appears to be completely empty. So.......what if I took half the sprite address data, and copied it into 1D, like this?


BANK 1C ALTERATIONS
Code:
ObjectLoSpriteAddressLo:
	.db #<GameItem0_Lo, #<GameItem1_Lo, #<GameItem2_Lo, #<GameItem3_Lo, #<GameItem4_Lo, #<GameItem5_Lo, #<GameItem6_Lo, #<GameItem7_Lo
	.db #<GameItem8_Lo, #<GameItem9_Lo,#<GameItem10_Lo,#<GameItem11_Lo,#<GameItem12_Lo,#<GameItem13_Lo,#<GameItem14_Lo,#<GameItem15_Lo
	
	.db #<Object0_Lo,#<Object1_Lo,#<Object2_Lo,#<Object3_Lo,#<Object4_Lo,#<Object5_Lo,#<Object6_Lo,#<Object7_Lo
	.db #<Object8_Lo,#<Object9_Lo,#<Object10_Lo,#<Object11_Lo,#<Object12_Lo,#<Object13_Lo,#<Object14_Lo,#<Object15_Lo
	.db #<Object16_Lo,#<Object17_Lo,#<Object18_Lo,#<Object19_Lo,#<Object20_Lo,#<Object21_Lo,#<Object22_Lo,#<Object23_Lo
	.db #<Object24_Lo,#<Object25_Lo
	
ObjectLoSpriteAddressHi:
	.db #>GameItem0_Lo, #>GameItem1_Lo, #>GameItem2_Lo, #>GameItem3_Lo, #>GameItem4_Lo, #>GameItem5_Lo, #>GameItem6_Lo, #>GameItem7_Lo
	.db #>GameItem8_Lo, #>GameItem9_Lo,#>GameItem10_Lo,#>GameItem11_Lo,#>GameItem12_Lo,#>GameItem13_Lo,#>GameItem14_Lo,#>GameItem15_Lo
	
	.db #>Object0_Lo,#>Object1_Lo,#>Object2_Lo,#>Object3_Lo,#>Object4_Lo,#>Object5_Lo,#>Object6_Lo,#>Object7_Lo
	.db #>Object8_Lo,#>Object9_Lo,#>Object10_Lo,#>Object11_Lo,#>Object12_Lo,#>Object13_Lo,#>Object14_Lo,#>Object15_Lo
	.db #>Object16_Lo,#>Object17_Lo,#>Object18_Lo,#>Object19_Lo,#>Object20_Lo,#>Object21_Lo,#>Object22_Lo,#>Object23_Lo
	.db #>Object24_Lo,#>Object25_Lo


ObjectHiSpriteAddressLo:
		.db #<GameItem0_Hi, #<GameItem1_Hi, #<GameItem2_Hi, #<GameItem3_Hi, #<GameItem4_Hi, #<GameItem5_Hi, #<GameItem6_Hi, #<GameItem7_Hi
	.db #<GameItem8_Hi, #<GameItem9_Hi,#<GameItem10_Hi,#<GameItem11_Hi,#<GameItem12_Hi,#<GameItem13_Hi,#<GameItem14_Hi,#<GameItem15_Hi
	
	.db #<Object0_Hi,#<Object1_Hi,#<Object2_Hi,#<Object3_Hi,#<Object4_Hi,#<Object5_Hi,#<Object6_Hi,#<Object7_Hi
	.db #<Object8_Hi,#<Object9_Hi,#<Object10_Hi,#<Object11_Hi,#<Object12_Hi,#<Object13_Hi,#<Object14_Hi,#<Object15_Hi
	.db #<Object16_Hi,#<Object17_Hi,#<Object18_Hi,#<Object19_Hi,#<Object20_Hi,#<Object21_Hi,#<Object22_Hi,#<Object23_Hi
	.db #<Object24_Hi,#<Object25_Hi
	
ObjectHiSpriteAddressHi:
	.db #>GameItem0_Hi, #>GameItem1_Hi, #>GameItem2_Hi, #>GameItem3_Hi, #>GameItem4_Hi, #>GameItem5_Hi, #>GameItem6_Hi, #>GameItem7_Hi
	.db #>GameItem8_Hi, #>GameItem9_Hi,#>GameItem10_Hi,#>GameItem11_Hi,#>GameItem12_Hi,#>GameItem13_Hi,#>GameItem14_Hi,#>GameItem15_Hi
	
	.db #>Object0_Hi,#>Object1_Hi,#>Object2_Hi,#>Object3_Hi,#>Object4_Hi,#>Object5_Hi,#>Object6_Hi,#>Object7_Hi
	.db #>Object8_Hi,#>Object9_Hi,#>Object10_Hi,#>Object11_Hi,#>Object12_Hi,#>Object13_Hi,#>Object14_Hi,#>Object15_Hi
	.db #>Object16_Hi,#>Object17_Hi,#>Object18_Hi,#>Object19_Hi,#>Object20_Hi,#>Object21_Hi,#>Object22_Hi,#>Object23_Hi
	.db #>Object24_Hi,#>Object25_Hi

BANK 1D ALTERATIONS
Code:
ObjectLoSpriteAddressLo2:
	
	.db #<Object26_Lo,#<Object27_Lo,#<Object28_Lo,#<Object29_Lo,#<Object30_Lo,#<Object31_Lo
	.db #<Object32_Lo,#<Object33_Lo,#<Object34_Lo,#<Object35_Lo,#<Object36_Lo,#<Object37_Lo,#<Object38_Lo,#<Object39_Lo
	.db #<Object40_Lo,#<Object41_Lo,#<Object42_Lo,#<Object43_Lo,#<Object44_Lo,#<Object45_Lo,#<Object46_Lo,#<Object47_Lo
	.db #<Object48_Lo,#<Object49_Lo,#<Object50_Lo,#<Object51_Lo,#<Object52_Lo,#<Object53_Lo,#<Object54_Lo,#<Object55_Lo
	.db #<Object56_Lo,#<Object57_Lo,#<Object58_Lo,#<Object59_Lo,#<Object60_Lo,#<Object61_Lo,#<Object62_Lo,#<Object63_Lo
	
ObjectLoSpriteAddressHi2:
	
	.db #>Object26_Lo,#>Object27_Lo,#>Object28_Lo,#>Object29_Lo,#>Object30_Lo,#>Object31_Lo
	.db #>Object32_Lo,#>Object33_Lo,#>Object34_Lo,#>Object35_Lo,#>Object36_Lo,#>Object37_Lo,#>Object38_Lo,#>Object39_Lo
	.db #>Object40_Lo,#>Object41_Lo,#>Object42_Lo,#>Object43_Lo,#>Object44_Lo,#>Object45_Lo,#>Object46_Lo,#>Object47_Lo
	.db #>Object48_Lo,#>Object49_Lo,#>Object50_Lo,#>Object51_Lo,#>Object52_Lo,#>Object53_Lo,#>Object54_Lo,#>Object55_Lo
	.db #>Object56_Lo,#>Object57_Lo,#>Object58_Lo,#>Object59_Lo,#>Object60_Lo,#>Object61_Lo,#>Object62_Lo,#>Object63_Lo


ObjectHiSpriteAddressLo2:
	
	.db #<Object26_Hi,#<Object27_Hi,#<Object28_Hi,#<Object29_Hi,#<Object30_Hi,#<Object31_Hi
	.db #<Object32_Hi,#<Object33_Hi,#<Object34_Hi,#<Object35_Hi,#<Object36_Hi,#<Object37_Hi,#<Object38_Hi,#<Object39_Hi
	.db #<Object40_Hi,#<Object41_Hi,#<Object42_Hi,#<Object43_Hi,#<Object44_Hi,#<Object45_Hi,#<Object46_Hi,#<Object47_Hi
	.db #<Object48_Hi,#<Object49_Hi,#<Object50_Hi,#<Object51_Hi,#<Object52_Hi,#<Object53_Hi,#<Object54_Hi,#<Object55_Hi
	.db #<Object56_Hi,#<Object57_Hi,#<Object58_Hi,#<Object59_Hi,#<Object60_Hi,#<Object61_Hi,#<Object62_Hi,#<Object63_Hi
	
ObjectHiSpriteAddressHi2:
	
	.db #>Object26_Hi,#>Object27_Hi,#>Object28_Hi,#>Object29_Hi,#>Object30_Hi,#>Object31_Hi
	.db #>Object32_Hi,#>Object33_Hi,#>Object34_Hi,#>Object35_Hi,#>Object36_Hi,#>Object37_Hi,#>Object38_Hi,#>Object39_Hi
	.db #>Object40_Hi,#>Object41_Hi,#>Object42_Hi,#>Object43_Hi,#>Object44_Hi,#>Object45_Hi,#>Object46_Hi,#>Object47_Hi
	.db #>Object48_Hi,#>Object49_Hi,#>Object50_Hi,#>Object51_Hi,#>Object52_Hi,#>Object53_Hi,#>Object54_Hi,#>Object55_Hi
	.db #>Object56_Hi,#>Object57_Hi,#>Object58_Hi,#>Object59_Hi,#>Object60_Hi,#>Object61_Hi,#>Object62_Hi,#>Object63_Hi

Note that I cut it in half at Object 25, but you don't have to cut it exactly there. If you do cut it a different way, be sure to change every instance of #$26 in the following code to the number you picked.

NESMaker won't know where to find the latter half of the object data if I don't tell it where I just put it. So open doDrawSprites.asm and find this comment line:
;; Next, we have to do a few indirect look ups to get the actual correct table read.
We're going to make these changes to it....

Code:
		LDA Object_type,x
CMP #$26
BCS +
		
		;; Next, we have to do a few indirect look ups to get the actual correct table read.
		LDY Object_type,x
		LDA ObjectLoSpriteAddressLo,y
		STA tempPointer_lo
		LDA ObjectLoSpriteAddressHi,y
		STA tempPointer_lo+1
		
		LDA ObjectHiSpriteAddressLo,y
		STA tempPointer_hi
		LDA ObjectHiSpriteAddressHi,y
		STA tempPointer_hi+1
		JMP ++
		
		+
		
		;; Next, we have to do a few indirect look ups to get the actual correct table read.
		LDY Object_type,x
		LDA ObjectLoSpriteAddressLo2,y
		STA tempPointer_lo
		LDA ObjectLoSpriteAddressHi2,y
		STA tempPointer_lo+1
		
		LDA ObjectHiSpriteAddressLo2,y
		STA tempPointer_hi
		LDA ObjectHiSpriteAddressHi2,y
		STA tempPointer_hi+1
		++


Next, I opened doHandleObjects and made the following alteration where the script first switches to 1C (stuff shifted to the left is what I added)...

Code:
                   JSR doHandleCreateState

LDA Object_type,x
CMP #$33
BCS laterobjects
                    SwitchBank #$1C
JMP earlierobjects
laterobjects:
SwitchBank #$1D
earlierobjects:
                        LDY Object_type,x
                        LDA ObjectFlags,y
                        STA Object_flags,x
                    ReturnBank

I did a search and found ten places overall where a call is made to switch to Bank 1C. Five of them are in doHandleObjects, and the other five appear in:
doCreateState_addExtraVariables_AdventureBase
doUpdateActionTimer
doClearAllMonsters
DoObjectAction
doHandleObjectCollisions

This is just for the Adventure module. If you're using Platform, there are several more. If you're unsure where to find them all, just open BASE 4.5 and do a Control-F search for 1C. They will all pop up.

I copy-pasted my SwitchBank workaround script into all of them, numbering the variables with each copy (that's how I knew I did it ten times). Then I hit "compile" and waited for the mess of errors.

I got none! The game went through! Rarely ever happens to me. The game is fully functional, and NES Space Tool shows that data is now stored in 1D, so as far as I know, this works. (Don't hold me to that -- it's fresh barely tested code, I won't be shocked if it causes a problem down the line).

The method I've used here could also be used to store other data in separate banks. Not this exact code, but the basic thinking behind it. If you've got a bank in danger of overflowing, and it is NOT the static bank, search for every instance of that bank being used and see if you can split it with an empty bank as I've done.
 

dale_coop

Moderator
Staff member
Yeah, you've done it well.
A lot of data or code can be moved in other unused bank. The most difficult (annoying part) is to find where are the bankswitch to modify them ;)
All depends of your projects (some projects have more gfx data or animation data... other projects have more code... )
Great work !
 

Pauldalyjr

Active member
Awesome work! I was trying to do this with the tiles in bank 18 but I cannot figure out how to do the CMP for a tile type like you did for the object type. Great job, happy you got this working!
 

Bucket Mouse

Active member
UPDATE: If this tutorial doesn't work for you, try this one:
 
Top Bottom