工大后院

 找回密码
 加入后院

扫一扫,访问微社区

QQ登录

只需一步,快速开始

搜索
查看: 1531|回复: 4

汇编辅导-基本程序结构

[复制链接]
发表于 2003-9-7 10:41 | 显示全部楼层 |阅读模式
1、顺序程序(略)

2、分枝程序

1)简单分支程序

将比较指令(或其它能使标志位发生变化的指令)和条件转移指令结合,可实现分支程序。简单分支程序有两种形式:



在汇编语言中,采用图a的结构容易出错,例如把GB0中的十六进制数转换成 ASCII 码可分成两段, 0~9 的 ASCII 码是 30H~39H ,即加 30H ;A~F 的ASCII 码是 41H~45H ,即加 37 H。两种结构的程序如下:

图a结构  图b结构
  
CPL GR0,C10   CPL GR0,C10
JMI L1   JMI L1
ADD GR0,C37   ADD GR0,C7
JMP L2  L1 ADD GR0,C30
L1 ADD GR0,C30    …  
L2 …   C10 DC 10
C10 DC 10  C7 DC 7
C37 DC #37  C30 DC #30
C30 DC #30     

若采用图a的结构,很容易把JMP L2漏掉,变成A~F 的ASCII 码加了67H。

2)多岔分支程序

可以用多条比较指令和条件转移指令实现多岔分支。

但用散转表实现多岔分支则程序更为简练,其思路是在散转表中存放各个分支程序的入口地址,用查表方法将入口地址放入变址寄存器,然后用JMP指令或CALL指令的间接寻址方式使程序转到此入口。

【例1】根据键盘输入命令转入各命令处理程序


SB START   
AGAIN  IN ALABLE,NLABLE
;输入一个字符串
LD GR1,NLABLE ;字符串长度
LEA GR1,0,GR1  
JZE AGAIN ;若字符串长度 =0,重输
LD GR1,ALABLE ;将第一个字符放到GR1中
LEA GR1,-65,GR1 ;减去"A"的ASCII码
JMI AGAIN ;若该字符<"A",重输
CPA GR1,C4  
JPZ AGAIN ;若该字符>"D",重输
LD GR2,ENTRY,GR1 ;将散转表中的一项地址放入GR2
CALL  0,GR2  ;转入地址所指子程序
JMP  AGAIN   
ALABLE DS  5  ;输入字符串缓冲区  
NLABLE DS  1  ;输入字符串长度  
C4 DC  4  ;常数 5  
ENTRY DC  ASUBR  ;子程序A 入口地址  
DC  BSUBR  ;子程序B 入口地址  
DC  CSUBR  ;子程序C 入口地址  
DC  DSUBR  ;子程序D 入口地址  
END   

在散转表中也可存放转移到各个分支程序入口的转移指令,然后用变址寻址方式的JMP指令或CALL指令使程序转到此入口。
【例2】

SB START   
AGAIN  IN ALABLE,NLABLE
;输入一个字符串
LD GR1,NLABLE ;字符串长度
LEA GR1,0,GR1  
JZE AGAIN ;若字符串长度 =0,重输
LD GR1,ALABLE ;将第一个字符放到GR1中
LEA GR1,-65,GR1 ;减去"A"的ASCII码
JMI AGAIN ;若该字符<"A",重输
CPA GR1,C4  
JPZ AGAIN ;若该字符>"D",重输
SLL  GR1,1  ;散转表每项占2 字  
JMP  ENTRY,GR1   
ALABLE DS  5   
NLABLE DS  1   
C4  DC  4   
ENTRY JMP  ASUBR   
JMP  BSUBR  
JMP  CSUBR  
JMP  DSUBR   
END   

3、循环程序

循环程序也是用转移指令实现,循环的退出一般用循环计数器实现,也可用其它条件来控制退出。

1)用循环计数器控制

【例1】将SD处的100个数搬到TD处(搬家程序)

SB START   
LEA  GR1,100  ;循环计数器  
LEA  GR2,0  ;偏移地址指针  
LOOP LD  GR0,SD,GR2  ;源操作数→GR0  
ST  GR0,TD,GR2  ;GR0→目的操作数  
LEA  GR2,1,GR2  ;修正偏移地址指针  
LEA  GR1,-1,GR1  ;搬完否?  
JNZ  LOOP  
EXIT   
SD DS  100   
TD  DS  100  
END   

当源操作数和目的操作数有重迭时,若源操作数在目的操作数的前面,则必须采用从下往上的搬法,即先搬最后一个,再一个一个往前搬。
【例2】将 SD 处的 100 个数搬到 TD 处(有重迭)

SB START   
LEA  GR1,100  ;循环计数器  
LEA  GR2,99  ;偏移地址指针  
LOOP LD  GR0,SD,GR2  ;源操作数→GR0  
ST  GR0,TD,GR2  ;GR0→目的操作数  
LEA  GR2,-1,GR2  ;修正偏移地址指针  
LEA  GR1,-1,GR1  ;搬完否?  
JNZ  LOOP  
EXIT   
SD DS  1   
TD  DS  100  
END   

2)循环次数可能为 0 的循环程序
前面的例子无法实现循环次数可能为0的循环程序(如用连加实现两个数相乘的算法),因为循环计数器预置为0时,实际的循环次数是65536。

【例2】将 A 和 B 两个整数相乘和放到 C 中(不考虑溢出)


SB START   
LD  GR1,B  ;乘数作循环计数器  
LEA  GR0,0  ;乘积清0  
LOOP LEA  GR1,0,GR1   
JZE  L1  ;若乘数为0,退出循环  
ADD  GR0,A  ;被乘数加到乘积中  
LEA  GR1,-1,GR1  ;加完否?  
JMP  LOOP  
L1  ST  GR0,C   ;乘积→C  
EXIT   
A  DS  1  
B  DS  1  
C DS  1  
END   

3)循环次数不定的循环程序
不用循环计数器,而用其它方法控制循环的退出。
【例4】测试字符串STR的长度,并将它保存到L中


SB START   
LEA  GR1,0  ;字符串长度计数器(兼地址指针)清0  
LOOP  LD  GR0,STR,GR1  ;取一字符  
CPL  GR0,FEND   
JZE  L1  ;若是结束符,退出循环  
LEA  GR1,-1,GR1  ;长度计数器+1  
JMP  LOOP   
L1 ST  GR0,L ;长度→L
EXIT   
L DS  1   
FEND  DC  '$'  
STR  DC  'This is a sample$'  
END   

4)多重循环

【例5】冒泡排序程序。

一组有符号数al(i=1,2,…,100),存放在Al开始的连续单元中。下面一程序将这组数在原来的内存区中由大到小重新排序。

SB START   
LEA  GR1,99   
ST  GR1,CN  ;外循环计数器初值  
L1 LEA  GR1,0  ;外循环指针  
LEA  CR2,1  ;内循环指针兼计数器初值  
LEA  GR3,0  ;交换标志置0  
L2 LD  GR0,A1,GR1   
CPA  GR0,A1,GR2  
JPZ   L3  ;内循环体,若Ai≥Ai+1,不动  
LD  GR4,A1,GR2  ;若Ai<Ai+1,交换  
ST  GR4,A1,GR1  
ST  GR0,A1,GR2   
LEA GR3,1  ;交换标志置1  
L3 LEA GR1,1,GR1  ;内循环调整  
LEA GR2,1,GR2   
CPL GR2,CN   
JZE L2  ;内循环控制  
JMI L2   
LEA CR3,0,CR3   
JZE L4  ;本次内循环交换标志为0,排序结束  
LD GR4,CN  ;外循环计数减1  
LEA GR4,-1,GR4   
JZE L4  ;排序结束  
ST  GR4,CN   
JMP L1   
L4 EXIT   
A1 DS 100   
CN DS 1   
END   

  这是一个二重循环结构的程序。程序中采用冒泡排序的方法,首先从第1个单元开始,依次把相邻两个元素比较,即第1个单元内的数与第2个单元内的数比较,第2个单元内的数与第3个单元内的数比较,…第99个单元与第100个单元的数相比较。每次把较大者放在前面单元,较小者放在后面单元。这样第一次遍历做了n-1次比较,最后最小的元素放在第100个单元。

第二次遍历再从头开始,依次将相邻两个元素进行比较,把n-1个元素遍历一次需做n-2次比较,这样,第99个单元存放全部元素中次小元素。

如此反复,小的元素往下,大的元素向上犹如汽泡上浮,因此这种排序的方法给它一个形象的名字,叫做冒泡算法。在程序中还设GR3为交换标志,若本次内循环结束交换标志为0,说明没有交换。即所有的Ai≥Ai+1,排序结束。
4、子程序

所谓子程序是指完成一定功能的指令序列,在主程序的若干地方可以用CALL指令对它调用,子程序结束时通过RET指令返回主程序。在子程序中,如果没有特别说明,则子程序中所要使用到的寄存器要保护。在子程序的开始用PUSH指令将子程序中要使用的寄存器保护,在返回前用POP指令恢复。主程序与子程序可以处在同一个程序中,此时,主程序对子程序的调用称为程序内的调用,CASL中也允许主程序与子程序不在同一个程序中,这样的调用就称为程序间凋用。

在子程序中一般不将GR4作为通用寄存器使用,因为GR4作为栈顶指针。如果其它通用寄存器不够用,要使用GR4,则先将GR4中的栈顶指针值保存,然后使用GR4,GR4使用完后,立即恢复栈指针的值。

1)参数传递

主程序调用子程序时,通常要向子程序传递参数(入口参数)。子程序结束,也会有数据送回主程序(返回参数)。当参数较少时,主程序将参数存于寄存器(GR0~GR3)传递给子程序;也可放于内存给子程序;当传递参数较多时,通常存于内存(参数表),并将参数表的首地址存于寄存器传递给子程序;也可以通过堆栈传递参数值。

【例1】寄存器传递参数

MAIN START   
LD  GR0,A  ;被加数送GR0  
LEA  GR1,B  ;加数送GR1  
CALL  SUBA   
ST  GR0,C  ;结果回送  
……   
EXIT   
SUBA  ADD  GR0,0,GR1   
RET   
A  DC  56   
B  DC  89   
C  DS  1   
D  DC  186   
E  DC  567   
F  DS  1   
END   

【例2】内存传递参数

MAIN START   
LD  GR0,A  ;被加数送GR0  
LD  GR1,B   
ST  GR1,BUF  ;加数送BUF内存单元  
CALL  SUBA   
ST  GR0,C  ;结果回送  
……   
EXIT   
SUBA  ADD  GR0,BUF   
RET   
BUF  DS  1   
A  DC  56   
B  DC  89   
C  DS  1   
D  DC  186   
E  DC  567   
F  DS  1   
END   

【例3】参数表传递参

MAIN START   
LEA   GR1,LIST  ;参数表首址送GR1  
CALL  SUBA   
EXIT   
SUBA  LD  GR0,0,GR1  ;被加数  
ADD  GR0,1,GR1  ;与加数相加  
ST  GR0,2,GR1  ;结果回送  
RET   
LIST  DC  56   
DC  89   
DS  1   
END   

【例4】堆栈传递参数

MAIN START   
LD  GR1,A   
PUSH  0,GR1  ;被加数压入堆栈  
LD  GR1,B   
PUSH  0,GR1  ;加数压入堆栈  
LEA  GR1,C   
PUSH  0,GR1  ;结果地址压入堆栈  
CALL  SUBA   
LEA  GR4,3,GR4  ;堆栈指针退回  
EXIT   
SUBA  LD  GR0,3,GR4  ;被加数  
ADD  GR0,2,GR4  ;与加数相加  
LD  GR1,1,GR4  ;结果地址  
ST  GR0,0,GR1  ;结果回送  
RET   
A  DC  56   
B  DC  89   
C  DS  1   
END   

2)子程序的返回

子程序通过RET指令返回主程序。RET指令的功能是将栈顶的返回地址置入PC。一般情况,应该保证返回时的堆栈指针SP(GR4)与调用时一致。以保证正确返回。但CASL中,GR4兼作通用寄存器,变址器和栈顶指针,因此在子程序中可以通过GR4修改返回地址,这样子程序返回时,就不一定返回到主程序的调用点下面的一条指令。在早期的程序员CASL试题中常常利用这一技巧。

3)现场保护

通常情况下,子程序都应该保护它所使用过的寄存器。因PUSH指令不能保护GR0,所以GR0用ST指令保护,用LD指令恢复。

【例4】无符号乘法。

有两个无符号数N1,N2,下面一子程序实现N1×N2返回。调用子程序时GR1存放参数N1、N2和相乘结果的存放区域的首地址,如下图所示。设相乘过程不会产生溢出。

GR1+0 N1
+1 N2
+2 结果

MULT START   
ST GR0,SAVE  
PUSH 0,GR2 ;保护寄存器
PUSH 0,GR3  
LEA GR2,0 ;部分积清零
LEA GR3,16 ;循环计算器
LD GR0,1,GR1 ;取出乘数N2
LOOP SLL GR2,1 ;部分积左移一位
SLL GR0,0 ;测乘数N2最高位是否为1
JPZ L1 ;若为0,跳过这一位
ADD GR2,0,GR1 ;若为1,部分积加上被乘数
L1  SLL GR0,1 ;乘数左移一位,算下一位
LEA GR3,-1,GR3  ;16位算完否?
JNZ LOOP   
ST  GR2,2,GR1  ;存放乘积  
LD  GR0,SAVE  ;恢复寄存器的值  
POP  GR2   
RET   
SAVE DS  1   
END   

象手算乘法一样,由于两个数是二进制数,所以唯一的问题是乘0还是乘1;显然,乘0结果为0,而乘1则结果与开始的数(被乘数)相同。因此在二进制乘法中每一步都可以简化成下列运算:
如果乘数中现有位为1,则将被乘数加到部分积上,为0则不加。剩下的问题是部分积和被乘数每次相加要进行移位,保证每次相加都正确对准。乘法过程可分成以下几步:

(1)积清0,设置计数器初值16。

(2)积左移1位。

(3)乘数最高位是否为1。

(4)若为1,则被乘数加积。

(5)乘数左移1位,计数器减1,若不为0转(2)。

程序中GR0存乘数,GR2存积,GR3为计数器

 楼主| 发表于 2003-9-7 18:52 | 显示全部楼层
晕 ~~没主意 忘了把图贴出来 算了 大家应该可以看得懂了
不要扔我鸡蛋阿 哈哈
回复

使用道具 举报

发表于 2003-9-7 21:00 | 显示全部楼层
谢谢你哦
回复

使用道具 举报

 楼主| 发表于 2003-9-8 04:52 | 显示全部楼层
不必客气,大家都是广工的
回复

使用道具 举报

发表于 2003-10-28 02:25 | 显示全部楼层
还没开始学呢?!!!!!!!!!!!!!!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 加入后院

本版积分规则

QQ|Archiver|手机版|小黑屋|广告业务Q|工大后院 ( 粤ICP备10013660号 )

GMT+8, 2024-5-30 07:56

Powered by Discuz! X3.5

Copyright © 2001-2024 Tencent Cloud.

快速回复 返回顶部 返回列表