MASM中子程序(Procedures)的写法

  在MASM 5(Microsoft Micro Assembler)的汇编体系中,子程序(Procedures)的定义和调用是非常重要的,就像C、pascal等的函数和方法一样;且对深入理解高级语言里函数的底层原理极其重要,如函数的参数传递、栈、变长参数等。但在网络上许多教程及代码都极其不规范且语焉不详;有的代码虽然可以运行,但都存在潜在Bug。我仔细阅读了Microsoft的相关手册后,像子程序的严格定义及调用分享如下:

  一、子程序(Procedure)的定义和调用

  方式1

  定义:

  label Proc [Far | Near]

  ...(语句)

  Ret [n]

  label Endp

  调用:

  Call label

  方式2

  定义:

  label:

  ...(语句)

  Retn [n]

  调用:

  Call Near Ptr label

  方式3

  label LABEL far(这里2个label不要搞混了,前一个是子程序名称,后一个是关键字)

  ...(语句)

  Retf [n]

  调用:

  Call Far Ptr label

  注意事项:

  1、方式1是官方推荐的,Call时不用关心near(段内调用)和far(跨段调用),编译器会根据定义自动生成near call或far call。但forward-referenced,如果proc定义为far,就必须call far ptr。

  2、方式2和方式3定义中Retn和Retf一定要与Call Near Ptr和Call Far Ptr对应,否则栈数据会被破坏

  3、从MASM 6后还可通过Proc的定义中声明参数(相当于C语言中函数的定义)。

  4、ret、retn、retf后面的n(整数)表示调用返回后sp还需要加n(即还需要弹出n个字节),主要用于传递参数。

  二、Call的语法补充

  语法:Call oprd(操作数),可分为直接调用间接调用

  1、直接调用

  oprd为子程序的地址,则可通过call [near或far]调用,本文之前的调用都是直接调用,英文label编译后就会替换成地址。

  2、间接调用

  oprd为保存子程序的地址的寄存器或内存地址:

  段内调用(near call)则可通过call bx或call word ptr [bx+si+20];

  跨段调用(far call)则可通过call dword ptr [bx+si+20];

  3、Jmp指令

  Jmp指令与Call指令的调用方式完全相同,但Jmp有一个Jmp short label,编译器只生成-128~127的偏移量。

  这里估计会有读者感到奇怪,为什么要有这么多方式了,全部改成segment:offset的方式不就行了么,jmp far跳转segment改变,jmp near跳转segment不变,完全没必要有其他方式了,masm还要弄一个jmp short。

  这里只要看一下编译器生成的汇编代码便可理解,早期计算机内存空间极其宝贵,生成的代码应尽量简短。short生成的代码比near更短,同理far。即,实现相同的功能,运行代码所需内存空间能少则少,这种理念,在嵌入式开发中普遍应用。

  三、同段调用和跨段调用并存

  代码调用存在near和far,如果某个proc,同段调用为near,跨段调用为far,那这个proc到底定义成near还是far呢?

  这里就涉及代码的组织问题,尽量将程序放在同段。如果条件不允许,只能将部分proc定义到另一个段中。call near只是为了提升执行速度,实在不行,把所有pro都定义为far,同段的子程序也可以用far调用。

  JMP也有同样的问题,即使是同段也可以jmp far ptr labelName。

posted @ 2024-04-25 12:31  美洲象  阅读(83)  评论(0编辑  收藏  举报