通常我在每次 GPR 访问之前都会做一次银行,但我发现我有很多不必要的线路。所以,问题是这样的。我对访问不同内存库的子例程进行了一些调用,而不是调用例程。将 GPR 保存在 STATUS 寄存器中是否是一个好主意,以维护当前的银行并在返回之前切换回它,或者这样做的最佳方式是什么?
3 回答
有很多方法可以解决这个问题,很大程度上取决于你想变得多么聪明,你想节省多少代码空间,以及你的代码对时间的要求有多高。在尝试节省代码空间/时间和冒着在错误的时间意外进入错误银行的风险之间需要权衡取舍。
我更喜欢这里的安全选项...
我所做的是,对于任何可能改变银行的 Sub,然后我写一个小的“Calling Sub”来备份 GPR 状态。“Calling Sub”然后调用“Real Sub”,它可以随心所欲地改变银行。当“Real Sub”退出时,它返回到“Calling Sub”,然后将 Bank 恢复到原来的状态,然后返回到您的程序循环
伪代码:
CALL_MYSUB BACKUP STATUS
BACKUP GPR
CALL MYSUB
RESTORE GPR
RESTORE STATUS
RETURN
您可以通过上面的方法变得聪明,并编写一个小宏来替换存储和检索代码,从而简化代码的外观,并提高可维护性......它当然使用相同的代码空间,请注意。
伪代码:
CALL_MYSUB BACKUP_MACRO
CALL MYSUB
RESTORE_MACRO
RETURN
理论上,如果您可能有两个以上的备份指令和两个还原指令,那么您将创建一个备份例程和一个还原例程,而不是宏,然后调用它们;
伪代码:
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
当然,您可以将上述内容直接合并到需要它的每个子项中。但我更喜欢上面的方法,因为它允许您在没有备份和恢复的情况下调用子程序,让您的子程序知道它正在尝试做什么,允许您将所有类似的调用放在一个地方,允许您在一个地方进行更改,并明确表明该潜艇将更改 GPR。
这里唯一需要注意的是确保没有太多嵌套调用,这当然会溢出堆栈!
我希望这有帮助....
编辑:
如果您只需要备份您的 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
仅检索银行选择位;
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
但是,我这里没有任何环境,所以我无法确认这是否 100% 有效,但我认为数学很好!
我还假设您使用的是 16c77,它的 RP1 位于 STATUS 位 6,RP0 位于 STATUS 位 5。
当然,如果您还需要 IRP 位,那么您还需要存储 STAUTS 位 7 - IRP
您还需要确保 TEMP1 存储在 GPR 中,以便它没有银行账户。
我建议最简单的方法可能是声明为全局策略,除非另有说明,否则每个例程都会假设 RP0 和 RP1 在进入时是明确的;IRP 设置为适用于 FSR 中加载的任何内容(如果有),否则未指定。在退出时,除非另有说明,否则每个例程都应该保持 RP0 和 RP1 为空,并且可以对 IRP 做任何它想做的事情(尽管如果一个例程永远不会对 IRP 做任何事情,那么记录这一事实可能对它很有用)。
因此,除非使用 FSR,否则“位于”bank 0 中的代码不必对 bank 位做任何事情。访问另一个银行的代码必须在进行任何子程序调用或返回给调用者之前重新选择银行 0。
在某些情况下,使用一些指定不同行为的特殊方法可能很有用,但这个“公式”在许多情况下应该是相当不错的。关键是要有一个大多数方法都遵循的规则,并明确说明任何不使用该规则的方法都被清楚地记录在案。
我使用了两个宏:
m_SaveBank 宏 Var
clrf Var
btfsc STATUS,RP1
bsf Var,RP1
btfsc STATUS,RP0
bsf Var,RP0
端
m_RestoreBank 宏 Var
bcf STATUS,RP1
bcf STATUS,RP0
btfsc Var,RP1
bsf STATUS,RP1
btfsc Var,RP0
bsf STATUS,RP0
端
子程序必须首先保存 STATUS.RP1 和 RP0,然后在退出时恢复它们。W 寄存器保持不变,因为大多数时候承载子程序的数据,除 RP0 和 RP1 之外的 STATUS 位也不受影响。我这样省了十一个字,但我认为没有其他选择。