Assembly s/360汇编:如何实现调用堆栈
我想写一个函数(在hlasm中)来调用自身和其他函数 在x86或z80(可能还有其他)上,您只需调用函数,并在函数末尾执行ret。然后,处理器将存储和检索返回地址 指令集具有以下指令:Assembly s/360汇编:如何实现调用堆栈,assembly,mainframe,callstack,zos,reentrancy,Assembly,Mainframe,Callstack,Zos,Reentrancy,我想写一个函数(在hlasm中)来调用自身和其他函数 在x86或z80(可能还有其他)上,您只需调用函数,并在函数末尾执行ret。然后,处理器将存储和检索返回地址 指令集具有以下指令: BAL reg,func …它将回信地址存储在reg中,然后在最后您可以执行BR reg跳转到该回信地址。另一个问题是,显然没有push/pop指令。如果它是您自己的程序中的一个子例程,那么BAS(分支和保存)/BR是标准的。如果子例程随后调用另一个子例程,则可以保存调用方的地址,然后在以后还原:
BAL reg,func
…它将回信地址存储在reg中,然后在最后您可以执行
BR reg
跳转到该回信地址。另一个问题是,显然没有push/pop指令。如果它是您自己的程序中的一个子例程,那么BAS(分支和保存)/BR是标准的。如果子例程随后调用另一个子例程,则可以保存调用方的地址,然后在以后还原:
<some code>
BAS R10,SUBRTN1 .Branch to subroutine
<some mode code>
.
.
.
*
SUBRTN1 DS 0H
ST R10,R10SAVE .Save Return address
BAS R10,SUBRTN2 .Call subroutine 2
L R10,R10SAVE .Restore return address
BR R10 .and return
*
SUBRTN2 DS 0H
<some code>
BR R10 .Return to caller
BAS R10,SUBRTN1.到子程序的分支
.
.
.
*
子RTN1 DS 0H
ST R10,R10保存。保存返回地址
BAS R10,SUBRTN2.调用子程序2
L R10,R10保存。还原返回地址
BR R10.然后返回
*
子RTN2 DS 0H
BR R10.返回到调用方
但是,这是针对“内部”子例程的,您必须注意子例程不会覆盖主程序使用的寄存器
如果这是一种风险,那么您可能希望使用标准链接约定,其中一个程序调用另一个程序,并且调用程序为被调用程序提供一个保存区域,以保存System/360和后续操作系统中的调用者寄存器等,实现这一点的基础设施是所谓的可重入编程的一部分。基本概念是R13指向的存储区域的存储是从操作系统获得的(想想C中的malloc)。获取存储的系统宏调用在程序开始时使用。同样,将存储器返回到操作系统的系统宏调用在程序出口处进行编码 您没有提到您正在使用的操作系统。我将对此示例代码进行以下假设:
- 这适用于z/OS或其前身操作系统(OS/390、MVS、OS/VSn、OS/360)李>
- 代码为AMODE 24或31,处于非访问寄存器模式(引入AMODE 64或ARs更改保存区格式)李>
- 这不是在语言环境(LE)情况下运行李>
- 注册等同于已定义的Rn形式李>
- 我还没有计算评论的栏数(因此它们可能会徘徊到72栏)
请用问题进行评论,我将更新此示例以进一步澄清。这里有一些超出已发布内容的想法 首先,任何成熟的汇编程序程序员都会考虑子程序链接,并在多年的时间里建立起自己的偏好。除了“有效”的东西外,您还需要高效、简单且易于调试的东西。解决方案包括三个部分:
WORKAREA DSECT , Reentrant work area (like C stack)
DS 18F Save area
FIELD1 DS F Some variable
FIELD2 DS F Another variable
WORKLEN EQU *-WORKAREA Length of reentrant work area
SUBRTN1 RSECT , HLASM will perform reentrant checking
STM R14,R12,12(R13) Save registers at entry
LR R12,R15 Set code base register
USING SUBRTN1,R12 Establish code addressability
LGHI R0,WORKLEN Get length of reentrant work area
STORAGE OBTAIN, Obtain reentrant work area X
LENGTH=(0) ..Length is in R0
ST R1,8(,R13) Forward chain in prev save area
ST R13,4(,R1) Backward chain in next save area
L R14,20(,R13) Get R1 at entry (parameters)
LR R13,R1 Set up new save area/reentrant workarea
USING WORKAREA,R13 Establish work area addressability
LM R2,R3,0(R14) Get addresses of parameters
STM R2,R3,FIELD1 Save parameter addresses for later
…
*** Logic goes here
…
LR R1,R13 Address to be released
L R13,4(,R13) Address of prior save area
LGHI R0,WORKLEN Length of storage to release
STORAGE RELEASE, Release reentrant work area X
ADDRESS=(1), ..Address in R1 X
LENGTH=(0) ..Length in R0
LM R14,R12,12(R13) Restore registers
OI 15(R13),X'01' This bit on means this save area is inactive
BR R14 Return to caller
WORKAREA DSECT ,
STACK DS 10CL80 A 10-entry subroutine save stack
DS 80X'FF' End of stack marker
*
* Call the subroutine
*
LA R1,some_parameter R1 -> subroutine parameter
L R15,=A(Subrtn) R15 -> subroutine address
BALR R14,R15 Call the subroutine
*
* Called subroutine
*
PUSH USING Guarantees no "stale" USINGs here
DROP ,
Subrtn CSECT , Allows subroutines in a different object
STCK 0(R13) Saves the timestamp in the stack
STM R0,R15,8(R13) Save the caller's registers
LA R13,80(R13) R13 -> next stack frame
LR Rx,R15 Setup a local base register
USING Subrtn,Rx
. . .
. . . (Subroutine does it's work here)
. . .
SubExit EQU *
*
* Return to the caller here R0/R1 have our result, R15 = return code
*
SLR R15,R15 (we'll assume a good return code)
SHI R13,80 Back up to prior stack frame
LM R2,R14,16(R13) Re-load caller's R2/R14
BR R14 Return to the caller
LTORG , Force subroutine literal pool here