汇编语言实现模块化程序设计初探(一)

在这里我不会过多地描述基础知识,重点将在编程思想和方法技巧上,希望对看到这篇博文的你有所帮助。

模块化程序设计概述

在实际编程中,因为现实的问题比较复杂,对现实问题进行分析时,把它转化成相互联系、不同层次的子问题,是必须的解决方法,所以程序的模块化是必不可少的。而call与ret指令对这种分析方法提供了程序上的支持。利用call和ret指令,我们可以很简捷地实现多个相互联系、功能独立子程序来解决一个复杂的问题。

子程序概述

在汇编源程序中,子程序是一个具有一定功能的程序段
call和ret指令实现子程序的机制:call指令转去执行子程序之前,call指令后面的指令的地址将存储在栈中,所以可以在子程序的后面使用 ret 指令,用栈中的数据设置IP的值,从而转到 call 指令后面的代码处继续执行。

子程序的参数和结果的传递

  1. 用寄存器来存储参数和结果是最常使用的方法
    对于存放参数的寄存器和存放结果的寄存器,调用者和子程序的读写操作恰恰相反:调用者将参数送入参数寄存器,从结果寄存器中取到返回值;子程序从参数寄存器中取到参数,将返回值送入结果寄存器。
  2. 寄存器的数量终究有限,我们不可能简单地用寄存器来存放多个需要传递的数据。对于返回值,也有同样的问题。
    我们将批量数据放到内存中,然后将它们所在内存空间的首地址放在寄存器中,传递给需要的子程序。
    对于具有批量数据的返回结果,也可用同样的方法。

寄存器冲突的问题

子程序中使用的寄存器,很可能在主程序中也要使用,造成了寄存器使用上的冲突。
我们希望:
(1)编写调用了程序的程序的时候不必关心子程序到底使用了哪些寄存器;
(2)编写了程序的时候不必关心调用者使用了哪些寄存器;
(3)不会发生寄存器冲突。

解决这个问题的简捷方法是,在子程序的开始将子程序中所有用到的寄存器中的内容都保存起来,在子程序返回前再恢复。我们可以用栈来保存寄存器中的内容。

子程序设计

我们在编程的时候要注意形成良好的风格,对于程序应有详细的注释。子程序的注释信息应该包含对子程序的功能、参数和结果的说明。
我们写代码应该养成简洁明了的风格,汇编指令间根据意图留下空行方便以后阅读,不参杂额外的指令以免影响性能。
子程序中需要返回结果的寄存器不能进行保护处理,内容不发生改变的寄存器不需要进行保护处理。
具体详见以下实例

编程实例

设计一个子程序,将一个全是字母的字符串转化为大写。
分析说明:jcxz指令检测cx内容是否为零,我们用0来标记字符串的结束,就可用该指令结束字符串的读取。提到将字母转化为大写,我们首先想到的是先判断这个字母是不是大写,然后再考虑转化。虽然可行,但这样做比较麻烦。我们对字母的处理基于ASCII码,大写字母加上一个固定常数会转化为小写字母,反之亦然,再进一步观察字母的ASCII码,可发现大小写字母的区别就仅仅在位置5上(位置为0~7)。故我们将一个字母的第5位置零后必为大写字母,不需要再做其他处理。置零用and指令,具体见源码。
源码如下:

;将data段中的字符串全部转化为大写
assume cs:code

data segment
 db 'word',0
 db 'unix',0
 db 'wind',0
 db 'good',0
data ends

code segment
start: mov ax,data
 mov ds,ax
 mov bx,0

 mov cx,4
s: mov si,bx
 call capital
 add bx,5
 loop s

 mov ax,4c00h
 int 21h

 ;说明:将一个全是字母,以0结尾的字符串,转化为大写
 ;参数:ds:si指向字符串的首地址
 ;结果:没有返回值
 ;
capital: push cx
 push si

change: mov cl,[si]
 mov ch,0
 jcxz ok 			;如果(cx)=0,结束;如果不是0,处理
 and byte ptr [si],11011111b 	;将ds:si所指单元中的字母转化为大写
 inc si 			;ds:si指向下一个单元
 jmp short change

ok: pop si
 pop cx
 ret
code ends
end start

在程序执行效率上,汇编语言是其他高级语言不能比的,但汇编语言需要考虑更多细节,因此工作量会更大,汇编语言常用来写核心程序。

你可能感兴趣的:(汇编语言初级,汇编语言,程序设计,编程经验)