汇编语言常见错误分析
作者简介:施志刚(1980.12— ) ,男,江苏南通人,南通航运职业技术学院信息系助教。
: Mov cx,3 Mov si,0 Mov al,0 add:Add al,[s1+si] Inc si Loop add : 还是上文提到的那个程序段,注意其中的标号 next 改成了 add。这样一改,肯定通不 过汇编了,原因就在于标号引用了指令的操作码。Loop 是循环指令,它的指令格式是 loop opr,其中 opr 为转向的目标地址,即标号,在上述指令序列中就是 add。明显和加法指令 add 同名了,这时只要把标号 add 改成 add1 即可以通过汇编。因此,对于初学者,今后在 编写程序时,尤其要注意标号不能引用指令的操作码。 1.1.5 指针寄存器未正确赋值之前就引用 在汇编语言中没有定义指针,但在 8086/8088 微处理器中有一些专用寄存器,比如 bx, bp,si 和 di,它们是用来存放地址的,通过地址实现对存储单元内容的访问。而在 c 语言 中定义了指针,它用来存放要访问的内存单元的地址,同样都存放地址,于是暂且可以把 bx,bp,si 和 di 同 c 语言中的指针关联起来,称为地址指针寄存器,这样就为理解和编写 程序带来了极大的方便。 地址指针寄存器通常用于循环程序设计中, 每执行完一次循环前都 要及时修改地址指针, 让其指向下一个将要访问的存储单元的地址。 因此在设计循环程序时, 都需要给在主程序中出现的地址指针寄存器赋初值。例如: Data segment 0per1 db 1,2,3 Data ends : Mov cx,3 Mov al,0 next:Add al,[oper1+si] Inc si Loop next : 上述指令序列,貌似看也是实现 1+2+3。在主程序中出现了指针寄存器 si,并且一次循 环结束前,也用 inc si 及时修改了指针值。但在循环的初始状态中并未对 si 赋初值,造成 循环语句无法正常运行。 因此对于初学者, 尤其要注意对将要在循环体中出现的指针寄存器 在程序的开始赋初值。 1.2 逻辑错误 1.2.1 循环语句中未规定循环结束条件 在汇编语言中, 循环程序通常由三部分组成: 循环的初始状态、 循环体和循环控制部分。 初学者在设计循环程序时, 经常忘记循环的初始化或忘记规定循环结束的条件, 从而造成循 环语句无法正常运行。 因此合理地选择结束条件就成为循环程序设计的关键问题。 通常设定 循环结束条件有两种方法: A.如果循环次数是已知的,可以用循环次数作为循环的控制条件。在循环程序中,cx 就是用来专门存放循环次数的寄存器。例如: Data segment
作者简介:施志刚(1980.12— ) ,男,江苏南通人,南通航运职业技术学院信息系助教。
如果把指令 inc si 去掉,则指针 si 永远指向 1 所在的存储单元,即实现 1+1+1。因此 一次循环结束前要及时修改地址指针的值。si 开始指向 1,执行了 inc si 后,指针 si 便指 向 2 所在的存储单元,依次下去,直至程序执行完毕。初学者在设计循环程序时,经常忘记 改变循环控制变量的值,从而造成循环语句无法正常运行,影响最终结果的正确性。 1.2.3lea 和 mov 指令使用不正确 Mov 是数据传送指令,传送存储单元的数据;而 lea 是地址传输指令,负责传送存储单 元的偏移地址。区别很明显,使用过程中却发现很多问题,例如: Data segment s1 db 1,2,3 Data ends : Mov cx,3 Mov si,0 Mov al,0 next:Add al,[s1+si] Inc si Loop next : 上述程序段中,si 是地址指针寄存器,存放存储单元的偏移地址。程序中 s1 对应存储 单元的偏移地址初始化为 0,存放在 si 中,因此 mov si,0 可以改成 lea si,s1。若改成 mov si,s1,意义就完全不同了。这时 si 中存放的就不是 s1 对应存储单元的地址了,而是 s1 对应存储单元的内容,运行结果显然不正确。若把 mov si,0 改成 lea si,s1,相当于 s1 对应存储单元的偏移地址被符号化了。这时指令中还有一处也必须改过来,才能保证程 序的正确性,即 next:后的指令应改为 add al,[si]。因此,初学者尤其要注意在阅读和 编写程序时,不要混淆这两条指令,并在程序设计中正确使用。 1.2.4jmp 指令的不正确使用 在程序中有时需要针对不同的情况或条件做出不同的处理, 这样的程序就要采用分支结 构。分支程序常常利用转移指令来实现。但程序中如果有多路分支,实现比较复杂时,初学 者往往就会忽视 JMP 指令,致使程序不能无条件跳转到另一个分支,当然汇编能通过,却输 出不正确的结果。下面的程序段用来统计正数和负数的个数: Data segment S1 db 1,-2,-1,-5,3,9,-7,5 Data ends :
作者简介:施志刚(1980.12— ) ,男,江苏南通人,南通航运职业技术学院信息系助教。
会出错。同理,第二条 mov 指令可以写成 mov al,byte ptr s2。 1.1.2 双操作数指令中两操作数同为存储器单元 在汇编语言中, 规定双操作数指令中两操作数不能同为存储器单元, 必须有一个是寄存 器。例如: Data segment s1 dw 1,2,3 s2 dw 10 dup(?) Data ends : Mov s2+6,s1+2 汇编语言在汇编这段程序时,能发现 mov 指令中的两操作数同为存储器单元。s1 和 s2 是在数据段定义的两个变量,类型均为字。dw 伪指令为其后跟着的数据存入指定的存储单 元,形成初始化数据;或者只分配存储空间而并不存入确定的数值,形成未初始化数据,例 如 dup。这里 s1 和 s2 是两个连续存储空间的起始地址,代表两个存储单元。因此要在两个 存储单元间传送数据,可以借助于寄存器。该条 mov 指令可以改成: Mov ax,oper1+2 Mov oper+6,ax 1.1.3 使用未定义的变量 在汇编语言中,要求对所有用到的变量作强制定义,即“先定义,后使用” 。初学者经 常在程序的执行部分直接使用没有定义过的变量。例如: Data segment s1 db 1,2,3 Data ends : Mov cx,3 Mov si,0 Mov al,0 Next:Add al,[s1+si] Inc si Loop next Mov sum,al : 不难看出,上述程序段实现的是 1+2+3,并最终把结果存入 sum 单元中。但是汇编语言 在汇编时, 发现指令中用到的变量 sum 在数据段中并没有定义, 于是报错使用了未定义的变 量。这主要表现为初学者编写程序的随意性,对在程序中可能要用到的变量,没有很好的预 见性和判断力。要使汇编通过,只要在数据段中为 sum 预留一个字节的存储空间就可以了, 即增加一条伪指令语句 sum db ?。 1.1.4 标号引用了指令的操作码 标号是可执行语句的符号地址,常常作为 jmp,call 和 loop 等指令的目的操作数,是 指令地址的符号表示。使用时后面必须跟有冒号,尤其要注意标号不能引用指令的操作码, 比如 add,sub,loop 等。例如: Data segment s1 db 1,2,3 Data ends
作者简介:施志刚(1980.12— ) ,男,江苏南通人,南通航运职业技术学院信息系助教。
s1 db 1,2,3 Data ends : Mov cx,3 Mov si,0 Mov al,0 next:Add al,[s1+si] Inc si Loop next : 在上述程序段中,循环体要被执行 3 次,这时可以在循环开始前使用 mov cx,3。在一 次循环结束时通过 loop next 来实现(cx)-1→(cx) ,如果(cx) 0,进入第二次循环,依 次下去,直到(cx)=0 退出循环。 B.若循环次数未知,就需要根据具体情况找出控制循环的结束条件。 例如: 设计一段程序求自然数的和, 若 1+2+3……<500, 停止运算, 并把结果存放在 sum 单元中。 该题可以用循环程序来设计, 但循环次数是未知的, 这时就要找出控制循环结束的条件, 即 1+2+3……<500。指令序列如下: Data segmeng Sum dw ? Data ends : Mov ax,0 Mov bx,1 Next:Add ax,bx Inc bx Cmp ax,500 Jb next Mov sum,ax : 在上述程序段中,Cmp ax,500 就是用来控制循环的。只要(ax)<500,循环体中的指 令被重复执行,否则就跳出循环,并将最终的结果存放在 sum 中。 对于初学者, 由于对循环次数不能正确预见或无意识设置, 并且循环结束条件设定也不 正确, 这些都会导致程序结果输出的不正确性。 在这里重点提出, 关键在于引起读者的重视。 1.2.2 循环语句中循环控制变量无变化 循环程序中初始状态主要包括设置控制循环的次数, 设置与循环有关的地址指针等。 这 里提到的循环控制变量无变化实际上就是地址指针无变化。 一次循环结束前, 都要在循环体 中修改有关的地址指针值, 为进入下次循环做好保障。 下面把上文 1+2+3 的程序段通过一存 储空间图给大家作简要描述。在执行第一次循环体时,地址指针寄存器 si 指向 1 所在的存 储单元,如图 1-(a)所示,在循环体中执行了加法指令后,要及时修改地址指针 si,即执行 inc si,在判断了(cx) 0 后,随即进入第二次循环,这时 si 指向 2,如图 1-(b)所示, 加法指令执行完,同时修改 si,判断(cx) 0,进入第三次循环,这时 si 指向 3,如图 1(c)所示,在执行完加法指令和修改了 si 后,判断(cx)=0,便跳出循环,程序执行完毕。
Shizhigang
(IT Department, Nantong Vocational & Technical Shipping College, Nantong Jiangsu 226010) Abstract: This article analysised conmmon errors in assembly language programming process through the concrete examples,including grammer errors and logic errors in programming,aimed at helping readers to prevent from the errors that this article refered to in programming. Key words: analysis; assembly language; grammer errors; logic errors 0 引言 汇编语言是计算机能够提供给用户使用的最快而又最有效的语言, 也是能够利用计算机 所有硬件特性并能直接控制硬件的惟一语言。 因而对程序的空间和时间要求很高的场合, 汇 编语言的应用是必不可少的。 至于很多需要直接控制硬件的应用场合, 则更是非用汇编语言 不可了。作为计算机专业及相关学科一门重要的基础课程,其重要性不言而喻。但是对于众 多初学汇编语言的人来说, 都普遍感到较难且不易理解和掌握, 难以入门, 看着有错的程序, 更是不知从何改起, 本人在汇编语言的教学过程中积累并总结了一些这方面的经验, 以供各 位读者借鉴和参考。 1 汇编语言常见错误 1.1 语法错误 1.1.1 数据类型不一致 在汇编语言中, 对于双操作数指令(比如 mov,add 等), 规定两操作数的数据类型必须一 致,所谓数据类型的一致是指两操作数必须同为字节或者同为字类型的数据。例如: Data segment s1 db 11h,22h s2 dw 3344h,7788h Data ends : Mov ax,s1+1 Mov al,s2 汇编程序在汇编这一段程序时,能发现两条 mov 指令的两个操作数的类型属性是不同 的:s1+1 为字节型属性,而 ax 为字类型属性;s2 为字类型属性,而 al 为字节类型属性。 因此,汇编程序将指示出错:两条 mov 指令中的两个操作数的类型不匹配。这时可以指定操 作数的类型属性,即使用 PTR 属性操作符,它赋予变量一个新的临时类型属性,类型可以是 byte,word 等,其本身并不分配存储单元。因此,第一条 mov 指令可以改成:mov ax,word ptr s1+1,这样就把 s1+1 的类型属性指定为字,两个操作数的属性也就一致了,汇编时不