updating which subroutine is used [4.5]

jataka5000

New member
Hello!

I want to change which subroutine is used on the fly.

I have successfully created a "flag" variable that can be either #$01 or #$00, and I can make a small change in the way that a subroutine works. However, if I want to change a subroutine in several places, the game does not compile -- either I get a "branch out of range" error or "Routines\BASE_4_5\System\Vectors.asm(1): Value out of range." I suspect that I am breaking it because it doesn't want as many or as complex amount of branches as I have put in.

As an alternative, I'd like to have my flag variable allow one of two doHandlePhysics scripts to be called on the fly, depending on the state of the variable. Problem is I don't know where the doHandlePhysics routine is called each time as the code will loop through calling all the subroutines etc...
Note that this skill could be generalized to a variety of situations -- e.g. having a one-way scroller change to a two-way scroller, etc.

I picture modifying the code in a way like this:

Code:
LDA myFlag

BNE doSecondSubroutine
	[call for first subroutine, doHandlePhysics1]
	JMP CarryOnWithProgram
	
	doSecondSubroutine:
	[call for second subroutine , doHandlePhysics2]

CarryOnWithProgram:
...
[rest of subroutine calls here]
...

Thank you for any insights on how I can accomplish executing more than one doHandlePhysics scripts!

-j5k
 

dale_coop

Moderator
Staff member
When a "branch out of range" error, it means the BNE or BEQ tries to access a label (or subroutine) that is too far.
So, the fix is to just reverse the condition and use a JMP, for example a:
Code:
	LDA myVar
	BNE goDoThis
	;; else do that
Could be changed into:
Code:
	LDA myVar
	BEQ +
	JMP goDoThis
	+
	;; else do that

The "Vectors.asm(1): Value out of range." error means that your bank is full, you added too much code. You need to reduce your scripts, optimize your code... remove the scripts that are not necessary (in your "Project Settings > Script Settings" maybe... or under the scripts > Input Scripts maybe you have old unused scripts...
 

mouse spirit

Well-known member
Yeah maybe remove unused tiles,inputs,monster a.i's. Aslo if your jmp is still not far enough,jmp to another jmp to really stretch it.
I see that it s a lot of code especially doubled up so you can then switch. Maybe group up the parts you change, then use the flag only for changed stuff, if you already havnt.


To super duper jump,heres an example

Code:
JMP superjump


;lots of code here

JMP+           ;;;as to not trigger superduper jump normally
superjump:
JMP duperjump
+

;lots of code here



duperjump:
 

jataka5000

New member
These are good ideas!

Dale, one issue with the code you proposed is that ASM marches through everything line by line, which means that the following example will accomplish both the doThis and doThat steps if the flag is set to #$00 (ultimately loads #$06 into the accumulator in both cases, as run e.g. in https://skilldrick.github.io/easy6502/):

Code:
LDA myFlag
BEQ DoThis
JMP doThat
	DoThis:
	;; else do that
	LDA #$03
doThat:
LDA #$06

It would be great if there was a simpler way to to an if/then/else, but this doesn't seem available. Best I can do:

Code:
LDA myFlag
BEQ DoThis
JMP doThat
	DoThis:
	;; else do that
	LDA #$03
	JMP Carryon
doThat:
LDA #$06
Carryon:

Note how that adds a minimum of 7 lines of code in ASM just to do a simple if/else... Is there a more compact ways to do an if/then or if/else in asm?

I tried removing all scripts that were not necessary (Note: actually *removing* leads to many headaches, I think setting them to blank.asm is the appropriate action), and included only the absolute minimum modification of switches in doHandlePhysics. @mouse_spirit, I think that it was setting the monster action scripts to blank.asm that allowed me to add the ~10 more lines of code needed to modify my doHandlePhysics and avoid the "Vectors.asm(1): Value out of range" error. Thank you both for the suggestions!

So the net result is that I have successfully made a switch that can reverse gravity on the fly by modifying three scripts:
doHandlePhysics_Gravity_LeftBoundsSolid_vulnerabilityBit_oneWayPlat_prizeBlock
stopOnSolid_Platformer_prizeBlock
jump_throughPlat

and I introduced a new script that reverses the flag when called:

Code:
;; change flag for gravity
	Inc gravFlag
	LDA gravFlag
	AND #$01
	STA gravFlag



In case anyone wants more gory details, I made a personal video that just tracks progress and links to the files I updated....
https://youtu.be/FRmNjPSye2c

It will be probably really tricky to start adding monsters and their behavior -- sounds like I'm already up against the limits of what can be done. :(
Are there ways to increase the memory, so I could play it as a ROM on a computer, but knowing that I wouldn't be able to flash it to an actual NES cartridge? It seems like I only have like ~50 more lines of code to include before I see "Vectors.asm(1): Value out of range" error again!
 

mouse spirit

Well-known member
Sounds great! So lets say you cant shorten the code. Simply make a game based on that gimmick alone. But i bet it can be squeezed down to a more comfortable size, so lets hope for that.

So could you group all things together that you switch on and off? So you only have to ask once if its gravity normal or reversed? I may be incorrect but it sounds like you are asking for each individual case. If so, it would be great to only ask once so let me think. I understand i may be way off but i'll run it by ya.

So which is less, asking every instance,but only at key parts (if possible)? Or writing the same code twice(one reversed obviously) and asking to switch once(if possible)?

Could you put the code somewhere else?

Last ones a doosey, dont even know if its a thing but....
what about using constants or variables or a small subroutine to do things like change + to - (or INC DEC stuff)?
What i mean is, maybe dont rewright code to reverse gravity, maybe reverse the symbols in the equasion(if possible) when you wanna reverse gravity, and jump height and speed?
So in the original code can you use a variable instead of a + sign (or DEC INC stuff) and so on, then later when you wanna change it all, have a code that changes the symbols in the equasions where you have those variables? That way the code only needs to be there once, and a simple variable change could change what it means.Iiiif it's a possible thing to do with the code. I will try to investigate.
 

CutterCross

Active member
jataka5000 said:
It will be probably really tricky to start adding monsters and their behavior -- sounds like I'm already up against the limits of what can be done. :(
Are there ways to increase the memory, so I could play it as a ROM on a computer, but knowing that I wouldn't be able to flash it to an actual NES cartridge? It seems like I only have like ~50 more lines of code to include before I see "Vectors.asm(1): Value out of range" error again!

A bit late on this, but you might have a bit of a misunderstanding on how much ROM storage space you actually have to work with, and how the NES handles reading from ROM. To clarify...

NESmaker exports ROMs built on Mapper#30 / UNROM512, which gives you 512 KB of total ROM space to work with. That is much more than most NES games had back in the NES's heyday, The total ROM capacity is divided into multiple sections of memory called banks. Each PRG (program) ROM bank is 16 KB, and Mapper#30 has 32 PRG banks in total.

The NES can only "see" 2 PRG banks at once, so it has a window of 32 KB for how much PRG ROM it can use at a time. To use the rest of the total ROM, one bank can be swapped out for another, while the other bank has to remain in place and holds the actual bankswitching code. This "fixed" bank holds your main game loop, initialization stuff, and any other mechanics that require their routines to be constantly in available memory.

NESmaker's fixed bank is Bank $1F, which is pretty packed by default since it has to hold a lot of necessary routines for many types of games. You're trying to pack more code into that 16 KB bank than what's available, thus the value for the Vectors' place in memory gets overflowed since they're stored at the end of the static bank. (Hence, Vector value out of range.)

NESmaker also reserves other banks by default for things that don't need to constantly be in available memory, like text, graphics, monster actions, palettes, etc. Shiru's NES Space Checker is an invaluable tool to see how much of your game's banks are filled.

I suppose what I'm trying to say at the end of this long tangent is that increasing the total ROM size alone wouldn't help your issue, as that only adds more banks, it doesn't increase each bank size. So you'd run into the same problem either way.
 

jataka5000

New member
Thanks for the responses!

@CutterCross, not late at all. That's really helpful..
NESmaker's fixed bank is Bank $1F, which is pretty packed by default since it has to hold a lot of necessary routines for many types of games. You're trying to pack more code into that 16 KB bank than what's available, thus the value for the Vectors' place in memory gets overflowed since they're stored at the end of the static bank. (Hence, Vector value out of range.)

So I'm guessing that doHandlePhysics and other subroutines are by default in the 16 KB fixed bank?

Curious, if I'm typically limited to 2 x 16 KB of ROM, then what are the other 30 banks typically used for? Is this where graphics and music are generally stored? I'm sort of confused, because subroutines such as "doUpdateCamera" and "doDrawBox" are already larger than 10 kb, and the initialization itself is a 5 kb file. Is it that these asm files are not always part of the bank, and are just included when needed?

@mouse_spirit:
So which is less, asking every instance,but only at key parts (if possible)? Or writing the same code twice(one reversed obviously) and asking to switch once(if possible)?

I think that the former case is shorter -- to ask only at key parts.

It would be great to put the code somewhere else, but according to CutterCross, it sounds like there isn't any other place, I'll always be limited by the 32 kb limit that would be in the 2 banks...

@mouse_spirit:
what about using constants or variables or a small subroutine to do things like change + to - (or INC DEC stuff)?

I like where you're going with that! It might be way more efficient than using ADC and SBC. Good thinking!
 

CutterCross

Active member
jataka5000 said:
So I'm guessing that doHandlePhysics and other subroutines are by default in the 16 KB fixed bank?

Curious, if I'm typically limited to 2 x 16 KB of ROM, then what are the other 30 banks typically used for? Is this where graphics and music are generally stored? I'm sort of confused, because subroutines such as "doUpdateCamera" and "doDrawBox" are already larger than 10 kb, and the initialization itself is a 5 kb file. Is it that these asm files are not always part of the bank, and are just included when needed?

Remember that the ASM text files are compiled to data that the NES can actually use. So the size of the ASM files aren't the size that's actually included in ROM.

I can't exactly vounch for 4.5 since I haven't looked deep into it yet, but these are what information is stored in each bank in 4.1.5 for reference:


00-07 - Overworld screen data
08-0F - Underworld screen data
10 - Background graphics 01
11 - Background graphics 02
12 - Background graphics 03
13 - Monster object graphics 01
14 - Databank1
15 - Monster object graphics 00
16 - Palette data and screen pointers
17 - Text data
18-1A - Empty
1B - Music engine and song / SFX data
1C - Game/Monster object animation data
1D - Game object, HUD, and special screen graphics
1E - Special screen nametable data
1F - Static bank - main game loop, NMI, and initialization stuff


All of these banks (aside from 1F) are bankswitched into memory when needed.
 

jataka5000

New member
Remember that the ASM text files are compiled to data that the NES can actually use. So the size of the ASM files aren't the size that's actually included in ROM.

good point! I thought about that right after I sent the message :)

Thanks for the hints. I think I'll be able to look into how banks are switched into memory. Seems like something as fundamental as the game physics (probably bank 1C) is something that will have to be in there most of the time...
 

CutterCross

Active member
jataka5000 said:
good point! I thought about that right after I sent the message :)

Thanks for the hints. I think I'll be able to look into how banks are switched into memory. Seems like something as fundamental as the game physics (probably bank 1C) is something that will have to be in there most of the time...

Another thing to keep in mind that most things that are bankswitched in are copied to RAM so that bank slot can be switched out safely. Things like graphics have their bank swapped in, copied to CHR RAM and PPU RAM, then are bankswitched out for another bank during screen load. Then they can be used from RAM during game logic while the bank they're originally loaded from isn't visible in the NES's memory map anymore. Same for most dynamically swapped banks like that.
 
Top Bottom