Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Assembly s/360汇编:如何实现调用堆栈_Assembly_Mainframe_Callstack_Zos_Reentrancy - Fatal编程技术网

Assembly s/360汇编:如何实现调用堆栈

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是标准的。如果子例程随后调用另一个子例程,则可以保存调用方的地址,然后在以后还原:

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

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

指令集具有以下指令:

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栏)
这个骨架例程是可重入的,因此它也可以递归使用,正如您所提到的(需要注意的是,重新调用错误代码本身最终会导致存储区外异常终止)。代码需要一个基址寄存器,但我建议使用称为“baseless”的现代编码方法,其中代码本身不使用基址寄存器,因为它使用分支的相对和立即指令。(数据始终需要基址寄存器。)

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

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

可重入程序是系统级z/体系结构编程的重要组成部分,因为它实际上是多任务环境所必需的。这是一项重要的技术,要了解人们是否会(在一般意义上)编写超出典型批处理程序范围的应用程序

如果您在LE环境(COBOL、PL/I、C/C++或汇编程序主程序)中,则应使用LE提供的CEEENTRY和CEETERM宏。编译器生成的程序通常是可重入的(甚至是带有NORENT选项的COBOL)。确保在CEEENTRY宏上编写MAIN=NO,否则会出现各种性能问题和可能的逻辑错误。LE-stack机制是我在上面演示的技术的高级版本,最显著的是分配了一个专门用于堆栈使用的大区域,这样后续调用就不会有调用操作系统以获取存储的开销

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


请用问题进行评论,我将更新此示例以进一步澄清。

这里有一些超出已发布内容的想法

首先,任何成熟的汇编程序程序员都会考虑子程序链接,并在多年的时间里建立起自己的偏好。除了“有效”的东西外,您还需要高效、简单且易于调试的东西。解决方案包括三个部分:

  • 保存/恢复状态的方式和位置
  • 如何定位和调用子例程并从中获取结果
  • 您需要在被调用的子例程中执行的操作 保存状态 保存状态是一个复杂的问题。上面的“zarchasmpgmr”示例展示了链式存储区域的经典方法,如果您调用的是您无法控制的内容,您应该熟悉其工作原理。但是,有时这不是最好的方法,因为您在示例中看到的“存储获取”是一个系统服务,在执行效率方面不是“免费的”。换句话说,如果你调用了很多子例程,它们都获得/释放了工作区域,那么
    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