第五章 程序设计方法和上机调试

52
第第第 第第第第第第第第第第第 5.1 第第第第第第 5.2 第第第第第第第第第第

description

第五章 程序设计方法和上机调试. 5.1 程序设计方法 5.2 汇编语言上机调试过程. 5.1 程序设计方法. 编写汇编语言程序步骤 分析实际问题,抽象描述问题的模型 确定解决模型的算法 按算法画出程序流程图 按流程图编写程序 上机调试 , 运行程序. 判断程序质量的标准 程序的正确性 程序的可读性 程序的执行时间 程序所占内存大小. 流程图的组成. 执行框 判别框 开始结束框 连接点 指向线. - PowerPoint PPT Presentation

Transcript of 第五章 程序设计方法和上机调试

第五章 程序设计方法和上机调试

5.1 程序设计方法

5.2 汇编语言上机调试过程

编写汇编语言程序步骤

分析实际问题,抽象描述问题的模型

确定解决模型的算法

按算法画出程序流程图

按流程图编写程序

上机调试 , 运行程序

5.1 程序设计方法

判断程序质量的标准

程序的正确性

程序的可读性

程序的执行时间

程序所占内存大小

执行框 判别框 开始结束框 连接点 指向线

用流程图表示算法直观形象,比较清楚地显示出各个框之间的逻辑关系。常用的还有 N-S 结构化流程图。程序编制人员都应当掌握传统流程图,会看会画。

流程图的组成

顺序程序结构是一种最简单的程序结构。在流程图中,处理框一个接一个执行,既无分支,也无循环和转移。是一种简单的程序结构。

S 1

S2

S3

一、顺序结构

例 1 : 内存中 TABLE 开始存放 0~9 的平方值,通过人机对话,当任给定一个数 X(0~9) ,查表得 X 的平方值,放在 AL 中。

DATA SEGMENTTABLE DB 0,1,4,9,…81

BUF DB ‘Please input one number(0~9):’,0DH, 0AH, ‘$’DATA ENDS

STACK SEGMENT STACK ‘STACK’ DB 50 DUP (?) TOP LABEL WORD

STACK ENDS

CODE SEGMENTMAIN PROC FAR

ASSUME CS:CODE,DS:DATA,SS:STACK

START: MOV AX,DATAMOV DS,AXMOV AX,STACKMOV SS,AXMOV SP,OFFSET TOPMOV BX,OFFSET TABLE

NEXT: MOV DX,OFFSET BUFMOV AH,9; 输出提示INT 21HMOV AH,1 ;键入一个数INT 21HMOV AH,0AND AL,0FHADD BX,AXMOV AL,[BX]MOV AH,4CHINT 21HRET

MAIN ENDPCODE ENDS

END START

▲补充:有关字符、数码转换的处理

1. 计算机处理字符时,常用的字符编码是 ASCII 码。

2. 数字和字母的 ASCII 码是一个有序序列数字 0~9 : 30H ~ 39H

大写字母 A~Z : 41H ~ 5AH

小写字母 a~z : 61H ~ 7AH

3. 计算机处理信息时,其对象都是二进制数。 外设 ( 显示器、打印机、键盘等 ) 用 ASCII 码与 CPU 进行信息传送。

例如: 1) 在键盘上按下某一字符键 ( 如’ 9’) ,键盘接口向 键盘缓冲区送去的是该字符的 ASCII 码 ( 如 39H) , 不是送数字 09H 。

2) 在文本方式下,要在显示器上显示某一字符 ( 如’ A’), 须将该字符的 ASCII 码 ( 如 41H) 送显示缓冲区, 不是送数字 0AH 。

▲ 计算机要利用显示器、键盘等外设时,须据程序设计的需要进行有关转换。

例 将 CPU 运算的结果通过显示器显示时,如二进制数“ A9” 需将结果转换成其对应的 ASCII 码才能进行显示。

显示缓冲区存放的内容

(字符的ASCII 码)

CPU运算后所得的二进制数1 0 1 0 1 0 0 1

十六进制形式显示 41h,39h 显示 ’A9’

十进制形式显示 31h,36h,39h

显示 ’169’ ( 无符号数)

2Dh, 38h, 37h 显示 ’ -87 ’ (带符号数)

二进制形式显示31h, 30h, 31h, 30h, 31h, 30h, 30h, 31h

显示 ’10101001’

例 CPU 要处理从键盘输入的某一数据 , 如键入“ 1234” 需将该数据串进行转换后才能应用。

CPU处理时 从键盘输入’ 1234 ’, 缓冲区存放31H, 32H, 33H, 34H

看作十六进制数输入 1234H

看作十进制数输入 04D2H

例 将 BX 寄存器中的内容以十六进制形式显示出来。

▲ BX 是一个 16 位寄存器二进制 1010 1001 0011 1110

▲ 用十六进制显示时,每 4 位用一个字符显示,共 4 个其中 0000 →’0’ 30H , 1010 →’A’ 41H

0001 →’1’ 31H , 1011 →’B’ 42H

、、 、、 1001 →’9’ 39H , 1111 →’F’ 46H

?十六进制 A 9 3 E 屏幕上的显示 ‘ A’ ‘9’ ‘3’ ‘E’

对应的 ASCII 41H 39H 33H 45H

算法 :

取出要显示的某 4 位,转换为对应的 ASCII 码 ,

再调用 DOS 系统功能进行显示。

1) 对于 0000~1001 ( 0~9 ) , 先扩展成一个字节,高 4 位清 0 , 加上 30H 后 , 即可得字符’ 0’~’9’ 对应的 ASCII 码。 0000 0001B + 30H= 31H 0000 1001B + 30H=39H

0001B ‘1’ 1001B ‘9’

2) 对于 1010~1111 ( A~F ) , 先扩展成一个字节,高 4 位清 0 , 加上 30H 后 , 还要再加上 07H ,才能得到’ A’~’F’ 对应的ASCII 码 0000 1010B+30H+07H = 41H 0000 1111B+30H+07H =46H

1010B ‘A’ 1111B ‘F’

code SEGMENT ASSUME CS:codestart: MOV CH, 4 ; 字符个数 MOV CL, 4 ; 循环移位次数 next: ROL BX, CL ; 取显示位的值 MOV DL, BL ; 保存在 DL 中 AND DL, 0FH ; 清除高 4 位 ADD DL, 30H ; 转变为数字的 ASCII

CMP DL, 39H ; 大于 39H, 则应转变 JLE print ; 为字母 A~F 的 ASCII

ADD DL, 07H print: MOV AH, 2H ; 显示 DL 中的字符 INT 21H DEC CH ; 显示结束? JNZ next MOV AH, 4CH ; 返回 DOS

INT 21Hcode ENDS END start

显示字符个数 CH=4循环移位次数 CL=4

BX 循环左移 4 位,将要显示的值移至低 4 位,保存在 DL

中清 DL 的高 4 位,只保留要显示位的值

DL ← DL+30H完成数值 0~9 的 ASCII 码转

Y

N

DL←DL+07H完成数值 A~F 的 ASCII 码转

换用 02 功能显示 DL 中的字符

Y

N

返回 DOS

DL 超出 39H ?

CH←CH-1 结束?

开始

1 、 分支:分支程序结构要求程序在运行过程中需要根据不同的情况或条件作出判断,并转向相应的处理程序。分支是通过条件转移指令实现的。

1 (X >0)

0 (X=0)

- 1 (X <0)

Y =

例 2 :根据 BUFFER 中的数 (X) 对符号函数 BX(Y) 赋值。

二、分支结构程序设计

- 1

1 X

Y

(AX)=0?

(AX) <0?

0FFH→BX 0→BX1→BX

Y

JE

N

JNS(AX )= 0(AX) > 0

ZEROPLUS

X→AX并将 AX 的特征反映到 FLAG

N

Y

例 3 :在以 BUF 为首地址的内存,存放着一个长度为N ( N<256) 的字符串,编程统计其中数字,字母和其它字符的个数,统计数存放在串后三个单元中。

初始化

取一字符送 AL

(AL)≥30H

(AL)>39H

(AL) ≥41H

(AL)>5AH

DH+1

DL+1

(CX)=0

修改 BX 及 (CX)-1

N

N

N

N

N

Y

Y

Y

Y

Y

A

DH 送内存

A

DL 送内存

AL=N-DH-DL

AL 送内存

RET初始化: N 送 CX ; 0 送 DX ; 0 送 BX;BX 为串偏移量,MOV AL , BUF[BX]

‘0’—‘9’ 30H—39H

‘A’—’Z’ 41H—5AH

先介绍第二种 DOS 返回方法:采用下面的程序框架

code SEGMENT ASSUME CS:code

main PROC FAR ; 使 RET 为远返回 start: PUSH DS ; 入栈保存地址( DS 的值 = CS 的值)

MOV AX, 0 ; 程序段前缀的首地址 (IP)=0

PUSH AX

、、 ; 程序主体部分 、、 RET ; 取程序段前缀首地址main ENDP

code ENDS

END start

DATA SEGMENTBUF DB N

DB 01H,38H,…76HNUM DB 3 DUP (?)DATA ENDSCODE SEGMENTMAIN PROC FAR

ASSUME CS:CODE,DS:DATASTART:

PUSH DSSUB AX,AXPUSH AXMOV AX,DATAMOV DS,AXMOV CH,NMOV BX,1MOV DX,0

LP: MOV AH,BUF[BX]CMP AH,30HJL NEXT ; 小于‘ 0’ 转CMP AH,39H

JG ABC ;大于‘ 9’ 转INC DHJMP NEXT

ABC: CMP AH,41HJL NEXTCMP AH,5AHJG NEXTINC DL

NEXT: INC BXDEC CHJNZ LPMOV BUF[BX],DHMOV BUF[BX+1],DLMOV AH,NSUB AH,DHSUB AH,DLMOV BUF[BX+2],AHRET

MAIN ENDPCODE ENDSEND START

2 、多分支

有的分支结构为多分支,可依次测试条件是否满足,若满足条件则转入相应分支入口,若不满足继续向下测试,直到全部测试完。简单,直观,速度慢。

例:有 8 个加工子程序,入口地址分别为 P1 , P2 ,… P8 。编程实现检测键盘输入命令,使系统分别转向 8 个加工子程序。

MOV AH , 1 INT 21H ; 1 号功能调用,键盘输入 CMP AL ,‘ 1’ JE P1 ;键值为 1 ,转 P1 CMP AL ,‘ 2’ JE P2 ;键值为 2 ,转 P2

: CMP AL ,‘ 8’ JE P8 ;键值为 8 ,转 P8 JMP ST ;非法键,转停机P1 :… ; 1 号键加工子程序 P2 :… ; 2 号键加工子程序

P8 :… ; 8 号键加工子程序 ST : HLT

局限性?—— 利用跳转表实现多分支,可克服该方法的缺点。

3 、跳转表实现多分支

利用跳转表实现多分支,可以直接找到相应入口,利用该法需建立一个跳转表,表中含每个分支的入口地址。

( 1 )根据表内地址分支

跳转表中存放了每个分支程序的入口地址,只要找到表地址,再将其内容取出,即可得到每个分支程序的入口地址。

表地址 = 跳转表首址 + 偏移地址

教材 P173 例 5-4

输入键值

开始

表首地址 BX

求偏移量

计算地址

散转

AND AL , 0FH ADD AL , AL

ADD BX , AX

JMP WORD PTR [BX]跳转表在内存中的存放方法

P0L

P0H

P1L

P1H

...

...

...

BASE+0

BASE+2

P0

P1

MOV BX , OFFSET BASE

跳转表中存放着转移指令,查表后程序执行转移指令将转到相应的子程序去。教材 P174 例 5-5

P1L

P1H

P2L

P2H

BASE

...

...

...

P1

P2

JMP

JMP

MOV AH , 1 ;键入到 AL

INT 21H

AND AL , 0FH

MOV AH , 0

MOV BL , AL

ADD AL , AL

ADD AL , BL ;偏移量 = 键值*3

MOV BX , OFFSET BASE

ADD BX , AX ;得转移地址

JMP BX ;转入转移指令

( 2 )根据表内指令分支

转移指令跳转表

( 3 )根据关键字分支

P1L

P1H

P2L

P2H

BASE

...

...

...

P1

P2

31

32输入关键字

开始

首地址送 BX

与关键字比较

指针加 3 转移

关键字 =0

相等

Y

Y

N

N

关键字跳转表

分支流程图

跳转表中存放关键字及相应分支地址。

三、循环程序设计

循环结束判断:每一次循环要有二个出口。根据循环工作调整的情况,判断是否满足结束条件。若满足结束条件,退出循环;若不满足结束条件,继续循环。

循环体:重复执行的一段程序。 循环工作部分: 循环程序的核心。 循环工作调整:重复执行的环境调整。

循环初始化: 循环程序工作单元的初始化赋值。

N循环结束判断

结 束

循环体

循环工作部分

循环工作调整

循环初始化

循环程序可以有两种结构形式: DO WHILE 结构、 DO UNTIL 结构

循环结构

循环初始设置

循环体

循环条件判断 ?Y

N

Y

N

循环初始设置

循环体

循环条件判断 ?

当型循环(当条件成立进入循环 )

直到型循环(直到条件成立退出循环 )

循环程序组成部分: 1、循环初始化2、循环体3、循环修改4、循环控制

循环程序设计举例

例 设计一个程序,完成从 1 连加到 100 (即 1+2+…+99+100 )的操作,结果保存在数据段的 SUM单元。

分析:这样的问题如果采用顺序程序设计至少要一百条指令,并且程序的结构性和可读性差,而采用循环程序设计就会简洁明了,结果显示可考虑采用过程调用方式,程序清单如下:

DATA SEGMENTSUM DW ?DATA ENDSCODE SEGMENT

ASSUME CS:CODE , DS:DATASTART: MOV AX, DATA

MOV DS , AX ;数据段寄存器赋初值 ;循环初始化 SUB AX,AX ;工作寄存器清零 MOV CX, 100 ;计数器赋初值 CLC ;清除进位标志

LP: INC AX ;循环体 ADC SUM, AX DEC CX ;循环修改 JNZ LP ;循环控制 ; ******** ;插入显示程序的地方

( 预留位置 ) HLT

CODE ENDS END START

注意:

① 本程序段采用了第三种退出方式,程序运行结束将由于执

行 HLT指令而进入停机状态,当键入 Ctrl+Break组合键 ( 键

盘中断 )后,返回 DOS现场。

② 用 DEBUG跟踪,会发现 (SUM)=13BAH。

四、子程序

主程序

CALLSUB1

子程序

SUB1 PROC

断点地址

转向子程序

RET返回主程序

子程序是编程中常用的重要方法,它只需编写一次,测试一次而可以多次重复使用。把这些相同的计算机操作编成一个子程序 ( 过程 PROC) 。使用 CALL 或 RET 指令调用或返回。

在编写子程序时,应写一个子程序说明,使子程序的模块结构及功能一目了然。

* 子程序的名称,功能及性能

* 子程序中用到的寄存器和存储单元

* 子程序的入口参数,出口参数

* 子程序中调用其它子程序的名称

教材: P182 例 5-13 说明文档

注释改错 !

1 、 子程序在调用和返回时注意堆栈的正确使用。

2 、 现场保护与现场恢复 为避免出错,在进入子程序后应把子程序中用到的寄存器内容保存到堆栈中。在退出子程序前再将其恢复。例: SUBT PROC

PUSH CXPUSH DX

:POP DXPOP CXRETSUBT ENDP

什么情况需要现场保护?• 保护和恢复的对象:在子程序中需要使用的内部寄

存器。• (1) 一定要保护:子程序中使用的寄存器;在返回后主程序需继续使用的寄存器。

• (2) 不用保护:作为子程序的结果传送给主程序的寄存器。

• (3) 可随意处理:子程序中使用,返回主程序后不再使用的寄存器。

1) 用寄存器传递:适合于参数较少的情况,传递速度较快。

(例 1 listbx子程序用 BX 寄存器传递参数)

2) 用定义的变量(存储器)传递:适合于参数较多的情况。

(教材: P183 例 5-14 )

3) 用堆栈传递:适合于参数较多的情况,在子程序嵌套与递归调用的情况下使用,不容易出错。

(教材: P184 例 5-15 )

3 、 子程与主程的参数传递

listbx PROC MOV CH, 4

MOV CL, 4next: ROL BX, CL

MOV DL, BL AND DL, 0FH ADD DL, 30H CMP DL, 39H JLE print ADD DL,07H

print: MOV AH,2H INT 21H DEC CH JNZ next

MOV DL, 20H ; 显示空格符 MOV AH, 02H INT 21H

RET ; 子程返回listbx ENDPcode ENDS END start

listbx PROC PUSH CX ; 保存寄存器 PUSH BX PUSH DX PUSH AX PUSHF

POPF ;恢复寄存器 POP AX POP DX POP BX POP CX RET ; 子程返回listbx ENDP

例 1 :用十六进制显示 BX 内容子程(用寄存器传递参数)

例 5-14 :编程实现数组段分别求和(不计溢出)。 ( 利用存储器来传递参数)提示:教材上有错误

DATA SEGMENT ARY1 DW 100 DUP (?) SUM1 DW ? ARY2 DW 100 DUP (?) SUM2 DW ?DATA ENDSSTACK SEGMENT STACK SA DW 50 DUP (?) TOP EQU LENGTH SASTACK ENDSCODE SEGMENT ASSUME CS : CODE , DS : DATA , SS :STACKMAIN PROC FARSTART : PUSH DS SUB AX , AX PUSH AX MOV AX , DATA MOV DS , AX MOV AX , STACK MOV SS , AX MOV SP , TOP

LEA SI , ARY1 MOV CX , LENGTH ARY1 CALL SUM LEA SI , ARY2 MOV CX , LENGTH ARY2 CALL SUM RETMAIN ENDPSUM PROC NEAR XOR AX , AXL1 : ADD AX , WORD PTR[SI] INC SI INC SI LOOP L1 MOV WORD PTR [SI] , AX RETSUM ENDPCODE ENDS END START

本例是利用存储器来传递参数,调用前将数组的偏移地址放入 SI 中,在过程中通过寄存器间址就可取得存储器中的操作数,并通过存储器返回。

教材上错误第二种改法:

DATA SEGMENTARY1 DW 100 DUP (?)SUM1 DW ?ARY2 DW 100 DUP (?)SUM2 DW ?

DATA ENDS: ;堆栈段省略

CODE SEGMENTASSUME CS:CODE,DS:DATA

START: MOV AX,DATAMOV DS,AXMOV AX,STACKMOV SS,AXMOV SP,TOPLEA SI, ARY1MOV CX, LENGTH ARY1CALL SUM

LEA SI,ARY2MOV CX,LENGTH ARY2CALL SUMMOV AH,4CHINT 21H

SUM PROC NEARXOR AX,AX

L1: ADD AX,WORD PTR [SI]INC SIINC SILOOP L1MOV WORD PTR [SI],AXRET

SUM ENDP

CODE ENDSEND START

例 5-15 :编程实现十进制数组求和,段间调用。 ( 用堆栈传递) MDATA SEGMENT

ARY1 DB 20 DUP (?) SUM1 DW ? ARY2 DW 100 DUP (?) SUM2 DW ?MDATA ENDSMSTACK SEGMENT STACK SB DW 100 DUP (?) TOP EQU LENGTH SBMSTACK ENDSMCODE SEGMENT ASSUME CS : MCODE , DS : MDATA , SS : MSTACKMAIN PROC FARSTART : PUSH DS SUB AX , AX PUSH AX MOV AX , MDATA MOV DS , AX MOV AX , MSTACK MOV SS , AX MOV SP , TOP

MOV AX , OFFSET ARY1 PUSH AX MOV AX , SIZE ARY1 PUSH AX CALL FAR PTR PADD MOV AX , OFFSET ARY2 PUSH AX MOV AX , SIZE ARY2 PUSH AX CALL FAR PTR PADD RETMAIN ENDPMCODE ENDSPCODE SEGMENT ASSUME CS : PCODE , DS : MDATA , SS : MSTACKPADD PROC FAR PUSH BX PUSH CX PUSH BP MOV BP , SP PUSHF

SP

ARY1

SIZE1

CS

IP

F

BP

CX

BX

SP-8

SP-4

SP-2

SP-C

SP-6

SP-A

SP-E

SP-10

MOV CX , [BP+10] MOV BX , [BP+12] MOV AX , 0NEXT : ADD AL , [BX] DAA MOV DL , AL MOV AL , 0 ADC AL , AH DAA MOV AH , AL MOV AL , DL INC BX LOOP NEXT MOV [BX] , AX POPF POP BP POP CX POP BX RET 4PADD ENDPPCODE ENDS END START

SP

ARY1

SIZE1

CS

IP

F

BP

CX

BX

SP-8

SP-4

SP-2

SP-C

SP-6

SP-A

SP-E

SP-10

4、子程序嵌套 子程序调用其他子程序,称子程序嵌套,子程序可多重嵌套调用。 *递归调用:子程序调用子程序本身。

例 设从 BUF 开始存放若干无符号字节数据,找出其中的最小值并以十六进制形式输出。 分析:用子程序 SEARCH 来求最小值并输出。

DATA SEGMENTBUF DB 13,25,23,100,423,78,90,134CNT EQU $-BUF

DATA ENDSCODESEGMENT

ASSUME CS:CODE,DS:DATA

SATRT : MOV AX , DATA

MOV DS , AXMOV CX, CNT-1MOV SI, OFFSET

BUFCALL SEARCHMOV AH, 4CHINT 21H

SEARCH PROC NEARMOV BL , [SI]

SEAR1:INC SICMP BL , [SI]JBE SEAR2MOV BL , [SI]

SEAR2:DEC CXJNZ SEAR1MOV DL , BLMOV CL , 4SHR DL , CLCALL DISP;嵌套

调用

MOVDL , BL

ANDDL , 0FH CALLDISP

RETSEARCH ENDP

DISP PROC NEARCMP DL,9JBE DISP1ADD DL,7

DISP1: ADD DL,30HMOV AH,2INT 21HRET

DISP ENDP

CODE ENDSEND START

教材 例 5-16 :两个无符号求和并转换成十六进制数在屏幕上显示。 (嵌套调用 )

主程序:DATA SEGMENT P DW 125 , 368 SUM DW ?DATA ENDS

MOV SI , OFFSET P

CALL PADD

子程序 PADD :AX , BX , CX , DX 入栈MOV AX , [SI] ;取数ADD AX , [SI+2] ;加法MOV SUM , AX ;存数CALL DISP ;AX , BX , CX , DX 出栈RET

子程序 DISP :显示数送 BX ;显示位数 04H 送 CH ;取四位二进制数转换成 ASCII ;送显示;四位数显示完?未完继续循环;RET

5 、编写子程序的注意事项 : 注意子程中 PUSH 、 POP 应成对,否则易造成死机。

data SEGMENT string DB ‘Hello’,’$’ data ENDS code SEGMENT ASSUME CS:code, DS:data start: MOV AX, data

MOV DS, AX CALL input MOV AH, 4CH INT 21H

input PROC PUSH AX LEA DX, string MOV AH, 09H INT 21H RET

input ENDP code ENDS END start

执行 call 前SS:SP

(AX)

执行 call 后SS:SP

(IP)

执行 push 后SS:SP

执行 ret 后SS:SP (IP)

思考如下程序的执行流程 :code SEGMENTstart: 、、

CALL sub 、、 CALL sub 、、; MOV AH, 4CH; INT 21H

sub PROC 、、 、、 RET

sub ENDP

MOV AH, 4CH INT 21H

code ENDS END start

程序执行不到返回 DOS 功能调用处 ,最后的结果是死机 .

5.2 汇编语言上机调试过程(略)

上机环境

硬件

系统软件 应用软件

操作系统: DOS 系统编辑器: EDIT.exe编程序: MASM.exe连接程序: LINK.exe调试程序: DEBUG.exe

用户开发的程序: ABC.exe 等

CPU 、存储器 (ROM 、 RAM) 、 I/O 接口、输入、输出设备

汇编语言上机过程

D:>EDIT ABC.asm

D:>MASM ABC;

有语法错,回 EDIT 下改该程序D:>LINK ABC;

有错,回在 EDIT 下改程序D:>ABC

运行结果错,回 EDIT 下改程序 或在 DEBUG 下调试,找原因。D:>DEBUG ABC.exe

编辑源程序 EDIT ABC.ASM

汇编源程序 MASM ABC.ASM

形成目标程序 ABC.OBJ

连接目标程序 LINK ABC.OBJ

有连接错误信息?

形成可执行程序 ABC.EXE

装入可执行程序到内存,并执行D:\>sub>ABC

下一程序

用 DEBUG 调试可执行程序D:\>sub>DEBUG ABC.EXE

找到原因

Y

N

Y

N

N

Y

N

有汇编错误信息 ?

Y运行结果正确?

用 DEBUG调试程序查错 ?

作 业

P199

8 、 12 、