Usually I do a banksel before every GPR access but I find that I have many unecessary lines. So, the question is this. I do some calls to subroutines that access diferent memory banks than the calling routine. Is it a good idea to save in a GPR, the STATUS register, for maintaining the current bank and switch back to it before returning or what is the optimal way of doing this?
3 Answers
There are many ways of going about this, and alot will depend on how clever you want to be, how much code space you want to save, and how time critical your code needs to be. There's a trade off between trying to save code space / time, and then running the risk of being in the wrong bank at the wrong time accidentally.
I prefer the safe option here...
What I do is, for any Sub that is likely to change the Bank, then I write a small "Calling Sub" which backs up the GPR status. The "Calling Sub" then calls the "Real Sub", which can change the Bank as much as it likes. When the "Real Sub" exits, it returns to the "Calling Sub", which then restores the Bank back to what it was, before returning to your program loop
Psuedo Code:
CALL_MYSUB BACKUP STATUS
BACKUP GPR
CALL MYSUB
RESTORE GPR
RESTORE STATUS
RETURN
You can get clever with the above and write a small Macro that replaces the Storing and Retrieving code, which simplifies how your code looks, and improves maintainability... It uses the same code space of course mind you.
Psuedo Code:
CALL_MYSUB BACKUP_MACRO
CALL MYSUB
RESTORE_MACRO
RETURN
In theory, if you are likely to have any more than two instructions to backup, and two to Restore, then instead of a macro, you'd make a single Backup Routine, and a Single Restore Routine, and call these instead;
Psuedo Code:
BACKUP_CONTEXT BACKUP STATUS
BACKUP GPR
BACKUP OTHER THINGS
BACKUP MORE STUFF
RETURN
BACKUP_CONTEXT BACKUP STATUS
BACKUP GPR
BACKUP OTHER THINGS
BACKUP MORE STUFF
RETURN
RESTORE_CONTEXT RESTORE MORE STUFF
RESTORE OTHER THINGS
RESTORE GPR
RESTORE STATUS
RETURN
CALL_MYSUB BACKUP_CONTEXT
CALL MYSUB
RESTORE_CONTEXT
RETURN
You could of course, incorporate the above directly into each sub which requires it. But I preferred the above, as it allows you to call the sub without the Backup and Restore if you want, keeps your sub about what it is trying to do, allows you to place all of the similar calls in one place, allowing you to make changes in one place, and makes it obvious that the sub is going to change the GPR.
The only thing to be careful of here is making sure you don't have too many nested Calls, which would overflow the stack of course!
I hope this helps....
EDIT:
If all you need to do is backup your Bank Select Bits and nothing else, You may try the following...
Store only Bank Select Bits;
MOVF STATUS,w ;Get the Current Bank Select bits
ANDLW B'01100000' ;Remove all but the Bank Select bits
MOVWF TEMP1 ;Store in a Temporary Register
Retrieve only Bank Select Bits;
MOVLW B'10011111'
ANDWF STATUS,1 ; Clear the Bank Select Bits
MOVF TEMP1,w ; Get the Old Bank Select Bits
XORWF STATUS,1 ; Restore Old Bank Select Bits
However, I've not got any of the environment here, so I can't confirm if this works 100%, but the maths is fine I think!
I have also assumed you are using the 16c77, which has RP1 at STATUS Bit 6 and RP0 at STATUS Bit 5.
Of course, if you need the IRP bit as well, then you'll need to store STAUTS Bit 7 - IRP aswell
You'll also need to make sure that TEMP1 is stored in GPR, so that it is unbanked.

- 3,123
- 2
- 26
- 37
-
Thanks for your answer. I don't like the idea of performing an other call for saving the STATUS reg, as it affects the STACK. Since I am currently using a 16F mcu, I only have 8 levels of stack and I can't spare I single byte. I am thinking of incorporating a macro in each subroutine that switches banks to do the job. The ugly thing about it, is that I can't just save the STATUS register and then restore it as I need to leave other bits unchanged so I have to use several lines to check bit RP1 and RP0 and then restore them properly. What do you think? – tcop Jan 27 '13 at 22:51
-
I understand... I did alot of work with the 16C77, thankfully I was able to move upto the 18F452 as it was Pin for Pin, and almost Code for Code compatible (Aside from the addressing change). I've edited my answer to show the Bank Select Bits only version.. – PGallagher Jan 27 '13 at 23:08
-
Thanks again. I used a pair of macros to do the job. I can't touch W reg, as it passes data to subroutines. – tcop Jan 27 '13 at 23:24
-
Your solution is just fine I think... Of course, if you don't return anything from the sub in the wreg, you can save some code using my Restore code (assuming that my code doesn't use more Bytes!)... But, if you need to preserve the wreg both in and out, then your solution is the best way... Sorry I couldn't be more help... – PGallagher Jan 27 '13 at 23:37
-
1;) You was very helpful by showing me the right way and by validating my thinking. cheers – tcop Jan 27 '13 at 23:56
I would suggest that the simplest approach is probably to state as global policy that unless specified otherwise, each routine will assume that RP0 and RP1 are clear on entry; IRP is set as appropriate for whatever is loaded in FSR if anything, and otherwise unspecified. On exit, unless specified otherwise, every routine should leave RP0 and RP1 clear and may do whatever it wants with IRP (though if a routine will never do anything with IRP, it may be useful for it to document that fact).
Code which "sits" in bank 0 will thus not have to do anything with the banking bits except when using FSR. Code which accesses another bank will have to reselect bank 0 before making any subroutine calls or returning to the caller.
In some cases it may be useful to have a few special methods which specify different behavior, but this "formula" should be pretty good in many cases. The key thing is to have a rule that most methods will follow, and make clear that any methods which don't use that rule are clearly documented.

- 77,689
- 9
- 166
- 211
-
thanks. I was following the same policy until a had a routine that accessed Bank1 and called an other routine that accessed Bank2. Then I had to change banks back and forth. Imagine the "caller" to use Bank1. The called subroutine used bank2 and returned to bank0 on exit, by following the global policy. Then the caller had to switch back to bank1. Whenever a call was made, a banksel 0x01 had to be made again and this was UGLY. – tcop Jan 28 '13 at 00:07
-
1@tcop: It's not perfect, but no policy is going to be perfect. Having a called routine save and restore the bank registers is generally going to add four instructions; having the caller set the bank registers to whatever it needs following the return will be faster. As to whether it's better to specify bank 0 on entry and exit, such an assumption saves an instruction if the last thing accessed before the call/return and the first thing accessed after are both bank 0. It's a wash if one is bank 0 and the other not. It costs an instruction if neither is bank 0. – supercat Jan 28 '13 at 00:17
-
@tcop: You may think redundant banking instructions are ugly, but they're far less ugly than incorrectly-omitted ones! – supercat Jan 28 '13 at 00:18
I used two macros:
m_SaveBank macro Var
clrf Var
btfsc STATUS,RP1
bsf Var,RP1
btfsc STATUS,RP0
bsf Var,RP0
endm
m_RestoreBank macro Var
bcf STATUS,RP1
bcf STATUS,RP0
btfsc Var,RP1
bsf STATUS,RP1
btfsc Var,RP0
bsf STATUS,RP0
endm
The subroutine must first saves STATUS.RP1 & RP0 and then restore them on exit. W register stays untouched as most times carries data for the subroutine and STATUS bits other than RP0 & RP1 stay unaffected too. I spare eleven words this way, but i don't think there is any other alternative.

- 381
- 1
- 2
- 11