It is currently Wed Dec 12, 2018 1:02 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 14 posts ] 
Author Message
PostPosted: Wed Nov 14, 2018 7:35 am 
Offline
User avatar

Joined: Tue Jun 24, 2008 8:38 pm
Posts: 2125
Location: Fukuoka, Japan
I have been quite busy with my engine these days to the point that my code and data combined is starting to spill from the fixed banks and even from the selected ones. I need to trim some fat, somehow :lol:

Because of the current issue, I'm starting to investigate where I can switch bank(s) and how useful in that scenario it could be but I found one possible issue: one case where I may need to call the code from another bank and come back after since I may need to change more than 1 bank to do the operation. I guess this is all trampolines are about.

I understand the concept and why it would be useful but my only concern is if there are place where you have to be careful to use trampoline or not. For example, I may have to use some trampoline code to jump to the sound driver, inside the nmi, then continue once finished but is it dangerous to do so from NMI? If the sound driver is not over, the NMI won't return yet, right?

What are the "gotcha" that I have to be careful when using such techniques?


Top
 Profile  
 
PostPosted: Wed Nov 14, 2018 7:43 am 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2353
Location: DIGDUG
What mapper are you using?

I think that switching banks for music code is a standard thing to do.

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Top
 Profile  
 
PostPosted: Wed Nov 14, 2018 7:55 am 
Offline
User avatar

Joined: Thu Mar 31, 2016 11:15 am
Posts: 441
It's safe so long as the trampoline backs up the previous bank on the stack.


Top
 Profile  
 
PostPosted: Wed Nov 14, 2018 8:11 am 
Offline
User avatar

Joined: Tue Jun 24, 2008 8:38 pm
Posts: 2125
Location: Fukuoka, Japan
@dougeff

I'm using an MMC3. I had the sound driver and code/data for current level selected at all time and was using general code/data in both fixed bank but now I reached a point in complexity that it's not enough anymore. I was aware of people mentioning about switching the sound driver but didn't have the need "yet". Now, it's knocking on the door big time ^^;;


@pubby

The reason I was concerned is that I'm using dynamically set NMI, which means the code reside inside the stage related bank and is set at runtime. Which means, if I need to switch the stage bank to get the sound driver and maybe the soundtrack, then I was afraid of the possibles issues. I think I need to try, no more choice now :lol:

Once I have something more concrete I will be more than happy to share it but for now it's not worth it, yet :D


Top
 Profile  
 
PostPosted: Wed Nov 14, 2018 8:39 am 
Offline
User avatar

Joined: Fri Nov 12, 2004 2:49 pm
Posts: 7604
Location: Chexbres, VD, Switzerland
Back in 2012 I tested using trampoline code for a demo, to switch among 32kb PRG-ROM banks. The usage was purely artificial, as the demo would have fit in 32kb PRG, but I wanted to test the technique. I don't remember the details, but basically if bank A would call a subroutine in bank B, it would call some trampoline code which puts a strategic return adress on the stack. When subroutine in bank B does rts, instead of returning directly to caller it goes to the second part of trampoline code switching bank A back and returning.

This could work as well in a situation where there is a fixed bank, and the trampoline code would be in the fixed bank. The "magic" is that the calee doesn't have to know if it was called directly (from the same bank) or not, just doing "rts" will automatically switch the caller's bank back if necessary.


Top
 Profile  
 
PostPosted: Wed Nov 14, 2018 10:30 am 
Offline

Joined: Tue Aug 28, 2018 8:54 am
Posts: 108
Location: Edmonton, Canada
Bregalad wrote:
The "magic" is that the calee doesn't have to know if it was called directly (from the same bank) or not, just doing "rts" will automatically switch the caller's bank back if necessary.


Sorry, I can't check the code right now. Were you putting address of "epilogue" subroutine that switches banks on the stack, to make "rts" work?


Top
 Profile  
 
PostPosted: Wed Nov 14, 2018 11:45 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11011
Location: Rio de Janeiro - Brazil
You don't normally need to manipulate the stack explicitly to implement trampolines. The basic implementation goes something like this:

Code:
;in the (real or virtual) fixed bank:
CallDoSomething:
  lda CurrentBank ;remember the current bank's index
  pha
  lda #.bank(DoSomething) ;switch to the subroutine's bank
  ;OMITTED: bank switch code (depends on mapper)
  jsr DoSomething ;call the subroutine
  pla ;restore the previous bank
  ;OMITTED: bank switch code
  rts ;return to the original caller

Then, whenever you need to call DoSomething from another bank, just call CallDoSomething instead. DoSomething can still be called directly by code that runs from the same bank it does.

You can also implement a generic trampoline, to which you can supply a bank index and the destination address somehow (via registers, RAM, ROM, the stack, whatever works best for you), but that'll obviously be slower than a dedicated trampoline.

A dedicated trampoline can also have extra code to preserve parameters and return values (which would normally be trashed by the bankswitch code), which might be important in some cases.


Top
 Profile  
 
PostPosted: Wed Nov 14, 2018 12:11 pm 
Offline

Joined: Tue Aug 28, 2018 8:54 am
Posts: 108
Location: Edmonton, Canada
tokumaru wrote:
You don't normally need to manipulate the stack explicitly to implement trampolines.

You're correct. I've been thinking about something else.


Top
 Profile  
 
PostPosted: Wed Nov 14, 2018 12:35 pm 
Offline
User avatar

Joined: Sat Jan 09, 2016 9:21 pm
Posts: 518
Location: Central Illinois, USA
tokumaru wrote:
You don't normally need to manipulate the stack explicitly to implement trampolines. The basic implementation goes something like this....


And a significant amount of the time, it can even be simpler than that, if you know that you only call a function from a certain bank. (ie I always call function FOO from bank 0). Then you can skip saving the current bank, and just hardcode the return bankswitch.

_________________
My games: http://www.bitethechili.com


Top
 Profile  
 
PostPosted: Wed Nov 14, 2018 2:11 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11011
Location: Rio de Janeiro - Brazil
That's true. At least in my programs, most banked subroutines are only called from the main engine, meaning that the return bank is constant and doesn't need to be saved on the stack.


Top
 Profile  
 
PostPosted: Wed Nov 14, 2018 3:03 pm 
Offline
User avatar

Joined: Sat Jan 09, 2016 9:21 pm
Posts: 518
Location: Central Illinois, USA
tokumaru wrote:
That's true. At least in my programs, most banked subroutines are only called from the main engine, meaning that the return bank is constant and doesn't need to be saved on the stack.


That's my experience. In the one or two exceptions, it's sometimes easier just to make a new trampoline (ie CallFooFromBank1) than to bother with making a more generalized trampoline.

_________________
My games: http://www.bitethechili.com


Top
 Profile  
 
PostPosted: Wed Nov 14, 2018 6:16 pm 
Offline
User avatar

Joined: Tue Jun 24, 2008 8:38 pm
Posts: 2125
Location: Fukuoka, Japan
All the comments gave me a lot of ideas for my current project.

Since I want to try to switch the driver in nmi and the nmi code is inside the stage bank, since I know the bank will always be the same in that case, I can hardcode it.

Will try it soon and will sure have fun at first, failing doing it properly :lol:

Some crazy idea came out of nowhere but will mention it anyway since we are on the trampoline subject. If I wanted, I could write a template code dynamically in memory. Before calling the code, I update the bank and when the method would come back, it would be back at the original code, right? It seems convoluted but seems the best way to avoid bank issues while switching since memory is not bankswitched anyway and you could keep the template in ram at anytime. Why do I think those ideas, I guess I love pain ^^;;;


Top
 Profile  
 
PostPosted: Wed Nov 14, 2018 6:48 pm 
Offline

Joined: Mon Apr 07, 2008 6:08 pm
Posts: 334
Location: Missouri
Balloon Kid on GB mixes both approaches that are discussed here. The main program resides in the lower, fixed bank 0 and always keeps the upper, switchable bank at 1 by default. When it does any task that involves switching to another bank, it switches back to 1 afterward. However, since the call to the music engine is in the vblank interrupt, it saves the current bank on the stack for that call only.

It can probably keep things simple because each bank is dedicated to a certain task (music, level data, sprite animation data, etc. all have their own bank).

There are some trampoline calls that seem pointless. It's possible the programmer planned on having the same call at a fixed address across several banks but it's redundant now.


Top
 Profile  
 
PostPosted: Wed Nov 14, 2018 7:25 pm 
Offline
User avatar

Joined: Tue Jun 24, 2008 8:38 pm
Posts: 2125
Location: Fukuoka, Japan
@strat

I see, I never thought about it this way since my test have always been very limited and with not much data so I never had to think about micro-managing my data up to now. For example, my player's metasprite data was in the second fixed bank since I was sure that I would not overflow with code, am I, and that's the most important data ^^;;; Now, I realize I don't need that data in every state of the game (intro, menus, etc) so I should fix that.

This means, in my main loop for the stage, I would process input, prepare state for entity/player, check collisions, switch bank to get meta sprite data and put it the OAM buffer, switch bank to get map data if necessary and while in NMI, switch banks for soundriver and current stage music and revert previous bank. Rinse and repeat.

I guess before continuing I should test how to implement it. With ca65, I just need to change where the data is located and it will be fast to test.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 14 posts ] 

All times are UTC - 7 hours


Who is online

Users browsing this forum: zeroone and 4 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group