s/360 assembly:如何实现调用堆栈

Fol*_*den 2 assembly callstack mainframe reentrancy zos

我想编写一个调用自身和其他函数的函数(在hlasm中).

在x86或z80(可能还有其他)上,您只需调用函数,并在其结尾处进行返回.然后处理器将存储和检索返回地址.

指令集有以下指令:BAL reg,func ...将寄存器中的寄存器地址存储在寄存器中,最后你可以将BR寄存器跳转到该寄存器地址.另一个问题是显然没有推/弹指令.

所以我现在有点st ..

问候

zar*_*gmr 5

在System/360和后继操作系统中,执行此操作的基础结构是所谓的可重入编程的一部分.基本概念是R13指向的保存区域的存储是从操作系统获得的(想想C中的malloc).在程序的最开始使用系统宏调用来获取存储.同样,系统宏调用将存储返回到操作系统,并在程序的出口处进行编码

您还没有提到您正在使用的操作系统.我将对此示例代码进行以下假设:

  • 这适用于z/OS或前身操作系统(OS/390,MVS,OS/VSn,OS/360);
  • 代码是AMODE 24或31,处于非访问寄存器模式(引入AMODE 64或ARs更改保存区域格式);
  • 这不是在语言环境(LE)情况下运行;
  • 已经定义了Rn形式的寄存器等式;
  • 我没有计算评论的栏目(所以他们可能会徘徊到72).

这个骨架例程是可重入的,因此它也可以递归使用,正如你所提到的那样(有警告说重新调用自身的坏代码最终会导致存储异常终止).代码需要一个基址寄存器,但我建议使用称为"baseless"的现代编码方法,其中代码本身不使用基址寄存器,因为它使用分支的相对和立即指令.(您始终需要一个基本寄存器来存储数据.)

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
Run Code Online (Sandbox Code Playgroud)

这是一个非常基本的示例,并介绍了一些高级汇编程序概念,如DSECT(虚拟部分),它描述了一个区域,但没有在程序中实际分配存储.RSECT是汇编程序通过在程序试图修改自身时发出警告来强化重入的一种方式.(还有一个汇编程序选项,RENT,但它适用于整个源代码; RSECT仅用于该部分.)

关于这个例子要记住的关键是你有两个基址寄存器,一个寻址代码,一个寻址数据.这与早期的x86架构相似,包括代码段和数据段.在这种情况下,数据段也用作堆栈段.

可重入程序是系统级z/Architecture编程的重要组成部分,因为它实际上是多任务环境所必需的.这是一种重要的技术,可以理解是否可以编写超出典型批处理程序的应用程序(一般意义上).

如果您处于LE环境(COBOL,PL/I,C/C++或汇编程序主程序)中,则应使用LE提供的CEEENTRY和CEETERM宏.LE编译器生成的程序通常是可重入的(即使是带有NORENT选项的COBOL).确保在CEEENTRY宏上编码MAIN = NO,否则会出现各种性能问题和可能的逻辑错误.LE堆栈机制是我在上面演示的技术的高级版本,最值得注意的是分配专用于堆栈使用的大区域,以便后续调用没有调用操作系统来获取存储的开销.

如果您在z/VSE或z/VM环境或非IBM BS2000/OSD环境中工作,我可以修改上面的示例.

请对问题进行评论,我将更新此示例以进一步说明.

  • 还有“BAKR”和“PR”指令,但我从未使用过它们。 (2认同)