节选1.0 和 1.1: 1.0简介 许多人认为汇编程序难于阅读。虽然大家有这种感觉有许多原因,但最主要的还是汇编语言难以使得程序员写出易读的程序。这并不表示不可能编写出易读的程序,只是它要花费汇编程序员一部分额外的工作来写出易读代码。 为了示范汇编程序的一些公共问题,决定使用下面的程序或程序段。这些都是在Internet上找到的真正用汇编语言编写的程序。每个例子都示范了一个单独的问题。(顺便一提:选择这些程序并不是有意让原作者难吭。这些程序都是在网上找到的汇编代码特例)。
代码:
1.1ADDHEX.ASM %title "两个16进制数相加" IDEAL DOSSEG MODEL small STACK 256 DATASEG exitCode db 0 prompt1 db 'Enter value 1: ', 0 prompt2 db 'Enter value 2: ', 0 string db 20 DUP (?) CODESEG EXTRN StrLength:proc EXTRN StrWrite:proc, StrRead:proc, NewLine:proc EXTRN AscToBin:proc, BinToAscHex:proc Start: mov ax,@data mov ds,ax mov es,ax mov di, offset prompt1 call Getvalue push ax mov di, offset prompt2 call Getvalue pop bx add ax,bx mov cx,4 mov di, offset string
call BinToAscHex call StrWrite Exit: mov ah,04Ch mov al,[exitCode] int 21h PROC Getvalue call StrWrite mov di, offset string mov cl,4 call StrRead call NewLine call StrLength mov bx,cx mov [word bx + di], 'h' call AscToBin ret ENDP Getvalue END Start
|
|
好了,这个程序的最大问题还是相当明显的-除了标题之外完全没有其它注释(译注:国内有太多这样没注释的程序了)。另一个实际的问题则是用来提示用户的字符串出现在程序的一端但用来打印这些字符串的调用代码却出现在另外一个地方(译注:即指代码中的StrWrite等三个函数)。尽管这是个经典的汇编语言编程方法,但却导致了代码难于阅读。另外,相对次要的一个问题是该程序使用了TASM的 “less-than”IDEAL 语法(注:一些一直使用TASM的人会认为这没什么不好。但个别人就不这么认为了,由于他们不熟悉TASM的古怪语言,有时会导致被程序中的几条语句搞混淆) 这个程序也使用了MASM/TASM的“简单化”段定义。微软声称的这个典型特性却给一个“简单”的工程增加了复杂性。如果该程序转换为标准的段定义格式将会更加易读。 (注:使用简单段定义虽然更容易编写高级语言接口的汇编程序,但无论如何,他们都只会使单一的程序问题变得复杂) 在扔掉它之前,该程序还是有两点值得称赞的(遵循了可读性)。第一,该程序员为过程名和本程序使用的变量选择了一组合理的名称(我假定这段代码的作者同时也是该程序调用的库的作者)。程序另一个值得肯定的是助记性好和操作数之间有良好的对齐。 OK,在抱怨这份代码如何难读后,为什么不来个更易读的版本?下面的程序便是,可证明,该版本比上面的版本更加易读。可证明,因为该版本使用了UCR标准库v2.O 并假定读者熟悉该库的细节和特性。
代码:
;************************************************** ; ; AddHex- ; ; 这个简单的程序从用户那里获取两个整数值,计算它们的和, ; 并在屏幕上打印结果。 ; ; 这个例子使用了“80x86汇编程序员的UCR标准库v2.0” ; ; Randall Hyde ; 12/13/96 title AddHex .xlist include ucrlib.a includelib ucrlib.lib .list cseg segment para public 'code' assume cs:cseg ; GetInt- ; ; 这个函数从键盘读取一个整数并将结果返回到AX寄存器中 ; ; 该程序捕获无效的值(太大或无效数字)并需要用户重新输入一个数值。 ;
GetInt textequ <call GetInt_p> GetInt_p proc push dx ;DX 保存错误代码 GetIntLoop: mov dx, false ;假定没错误 try ;捕获任何可能的错误 FlushGetc ;清空输入来换新行 geti ;读入整数值 except $Conversion ;捕获错误字符 print "Illegal numeric conversion, please re-enter", nl mov dx, true except $Overflow ;捕获数值太大 print "value out of range, please re-enter.",nl mov dx, true endtry cmp dx, true je GetIntLoop pop dx ret GetInt_p endp
Main proc InitExcept print 'Enter value 1: ' GetInt mov bx, ax print 'Enter value 2: ' GetInt print cr, lf, 'The sum of the two values is ' add ax, bx puti putcr Quit: CleanUpEx ExitPgm ;退出程序的DOS宏 Main endp
cseg ends
sseg segment para stack 'stack' stk db 256 dup (?) sseg ends
zzzzzzseg segment para public 'zzzzzz' LastBytes db 16 dup (?) zzzzzzseg ends end Main
|
|
这里要特别指出的是这份代码比原来的那个AddHex程序要大一点。在细节上,它验证用户的输入;而在原程序上并没有这么做。如果那位想更严密地模范原程序,下面的简单代码即是:
代码:
print nl, 'Enter value 1: ' Geti mov bx, ax print nl, 'Enter value 2: ' Geti add ax, bx putcr puti putcr
|
|
在这个例子中,两个简单的解决方案改良了程序的可读性:增加注释、将程序格式化得更好点,而且使用了UCR标准库的高级特性来使得编码更为简单并使得打印输出字符串的语句保持它们的字面意思。
|
|