2025年4月14日 星期一 乙巳(蛇)年 正月十五 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > 汇编

汇编语言定使用WHILE、REPEAT、FOR 和 FORC伪指令定义重复语句块

时间:03-05来源:作者:点击数:75

MASM 有许多循环伪指令用于生成重复的语句块:WHILE、REPEAT、FOR 和 FORC。与 LOOP 指令不同,这些伪指令只在汇编时起作用,并使用常量值作为循环条件和计数器:

  • WHILE 伪指令根据一个布尔表达式来重复语句块。
  • REPEAT 伪指令根据计数器的值来重复语句块。
  • FOR 伪指令通过遍历符号列表来重复语句块。
  • FORC 伪指令通过遍历字符串来重复语句块。

WHILE 伪指令

WHILE 伪指令重复一个语句块,直到特定的常量表达式为真。其语法如下:

WHILE constExpression
    statements
ENDM

下面的代码展示了如何在 1 到 F000 0000h 之间生成斐波那契 (Fibonacci) 数,作为汇编时常数序列:

  • .data
  • val1 = 1
  • val2 = 1
  • DWORD val1 ;前两个值
  • DWORD val2
  • val3 = val1 + val2
  • WHILE val3 LT 0F0000000h
  • DWORD val3
  • val1 = val2
  • val2 = val3
  • val3 = val1 + val2
  • ENDM

REEPEAT 伪指令

在汇编时,REPEAT 伪指令将一个语句块重复固定次数。其语法如下:

REPEAT constExpression
    statements
ENDM

constExpression 是一个无符号整数常量表达式,用于确定重复次数。

在创建数组时,REPEAT 的用法与 DUP 类似。在下面的例子中,WeatherReadings 结构含有一个地点字符串和一个包含了降雨量与湿度读数的数组:

  • WEEKS_PER_YEAR = 52
  • WeatherReadings STRUCT
  • location BYTE 50 DUP(0)
  • REPEAT WEEKS_PER_YEAR
  • LOCAL rainfall, humidity
  • rainfall DWORD ?
  • humidity DWORD ?
  • ENDM
  • WeatherReadings ENDS

由于汇编时循环会对降雨量和湿度重定义,使用 LOCAL 伪指令可以避免因其导致的错误。

FOR 伪指令

FOR 伪指令通过迭代用逗号分隔的符号列表来重复一个语句块。列表中的每个符号都会引发循环的一次迭代过程。其语法如下:

FOR parameter,<arg1,arg2,arg3,...>
    statements
ENDM

第一次循环迭代时,parameter 取 arg1 的值,第二次循环迭代时,parameter 取 arg2 的值; 以此类推,直到列表的最后一个实参。

【示例】现在创建一个学生注册的场景,其中,COURSE 结构含有课程编号和学分值;SEMESTER 结构包含一个有 6 门课程的数组和一个计数器 NumCourses:

  • COURSE STRUCT
  • Number BYTE 9 DUP(?)
  • Credits BYTE ?
  • COURSE ENDS
  • ;semester 含有一个课程数组。
  • SEMESTER STRUCT
  • Courses COURSE 6 DUP(<>)
  • NumCourses WORD ?
  • SEMESTER ENDS

使用 FOR 循环可以定义 4 个 SEMESTER 对象,每一个对象都从由尖括号括起的符号列表中选择一个不同的名称:

.data
    FOR semName,<Fall2013, Spring2014, Summer2014, Fall2014>
    semName SEMESTER <>
ENDM

如果查看列表文件就会发现如下变量:

.data
Fall2013 SEMESTER <>
Spring2014 SEMESTER <>
Summer2014 SEMESTER <>
Fall2014 SEMESTER <>

FORC 伪指令

FORC 伪指令通过迭代字符串来重复一个语句块。字符串中的每个字符都会引发循环的一次迭代过程。其语法如下:

FORC parameter, <string>
    statements
ENDM

第一次循环迭代时,parameter 等于字符串的第一个字符,第二次循环迭代时,parameter 等于字符串的第二个字符;以此类推,直到最后一个字符。

下面的例子创建了一个字符查找表,其中包含了一些非字母字符。注意,< 和 > 的前面必须有文字字符(!)运算符,以防它们违反FORC伪指令的语法:

Delimiters LABEL BYTE
FORC code, <@#$%^&*!<!>>
    BYTE "&code"
ENDM

生成的数据表如下所示,可以在列表文件中查看:

00000000  40  1  BYTE  "@"
00000001  23  1  BYTE  "#"
00000002  24  1  BYTE  "$"
00000003  25  1  BYTE  "%"
00000004  5E  1  BYTE  "^"
00000005  26  1  BYTE  "&"
00000006  2A  1  BYTE  "*"
00000007  3C  1  BYTE  "<"
00000008  3E  1  BYTE  ">"

示例:链表

结合结构声明与 REPEAT 伪指令以指示汇编器创建一个链表的数据结构是相当简单的。链表中的每个节点都含有一个数据域和一个链接域: 

在数据域中,一个或多个变量可以保存每个节点所特有的数据。在链接域中,一个指针包含了链表下一个节点的地址。最后一个节点的链接域通常是一个空指针。现在编写程序创建并显示一个简单链表。首先,程序定义一个节点,其中含有一个整数(数据)和一个指向下一个节点的指针:

ListNode STRUCT
    NodeData DWORD ?    ;节点的数据
    NextPtr DWORD ?        ;指向下一个节点的指针
ListNode ENDS

接着 REPEAT 伪指令创建了 ListNode 对象的多个实例。为了便于测试,NodeData 域含有一个整数常量,其范围为 1〜15,在循环内部,计数器加 1 并将值插入到 ListNode 域:

  • TotalNodeCount = 15
  • NULL = 0
  • Counter = 0
  • .data
  • LinkedList LABEL PTR ListNode
  • REPEAT TotalNodeCount
  • Counter = Counter + 1
  • ListNode <Counter, ($ + Counter * SIZEOF ListNode)>
  • ENDM

表达式 ($+Counter*SIZEOF ListNode) 告诉汇编器把计数值与 ListNode 的大小相乘,并将乘积与当前地址计数器相加。结果值插入结构内的 NextPtr 域。[注意一个有趣的现象:位置计数器的值 ($) 固定在表的第一节点上。]该表用尾节点 (tail node) 来标记末尾,其 NextPtr 域为空 (0):

ListNode <0,0>

当程序遍历该表时,它用下面的语句检索 NextPtr 域,并将其与 NULL 比较,以检查是否为表的末尾:

mov eax,(ListNode PTR [esi]).NextPtr
cmp eax,NULL

程序清单

完整的程序清单如下所示。在 main 中,一个循环遍历链表并显示全部的节点值。与使用固定计数值控制循环相比,程序检查是否为尾节点的空指针,若是则停止循环:

  • ; 创建一个链表 (List.asm)
  • INCLUDE Irvine32.inc
  • ListNode STRUCT
  • NodeData DWORD ?
  • NextPtr DWORD ?
  • ListNode ENDS
  • TotalNodeCount = 15
  • NULL = 0
  • Counter = 0
  • .data
  • LinkedList LABEL PTR ListNode
  • REPT TotalNodeCount
  • Counter = Counter + 1
  • ListNode <Counter, ($ + Counter * SIZEOF ListNode)>
  • ENDM
  • ListNode <0,0> ; tail node
  • .code
  • main PROC
  • mov esi,OFFSET LinkedList
  • ; 显示 NodeData 域的值
  • NextNode:
  • ; 检查是否为尾节点
  • mov eax,(ListNode PTR [esi]).NextPtr
  • cmp eax,NULL
  • je quit
  • ; 显示节点数据
  • mov eax,(ListNode PTR [esi]).NodeData
  • call WriteDec
  • call Crlf
  • ; 获取下一个节点的指针
  • mov esi,(ListNode PTR [esi]).NextPtr
  • jmp NextNode
  • quit:
  • exit
  • main ENDP
  • END main
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门