微机原理与接口技术-NUAA-课内实验报告

写在最前

应邀开始正式填坑,考完就可以肆无忌惮的发挥优良传统啦!姿持下互联网精神好不好啦!


目录

写在最前

实验1:调试工具DEBUG的应用

一、DEBUG 命令使用

二、8086常用指令练习

1、传送指令

2、加减法指令:

3、带进位加减法:

* 4、BCD码加减法(选作)

实验二、 内存操作数及寻址方法

一、内存操作数及各种寻址方式使用

二、求累加和程序

三、总结:

实验三 、数据串传送和查表程序

一、利用查表方法显示内存单元的内容

二、数据串传送程序

实验四 、完整段定义程序实现

实验步骤

总结:

1.segment是段,是段定义的伪指令。在汇编中有数据段,代码段,堆栈段和附加段

2. 在汇编语言源程序中可以定义多个段,每个段都要与一“个段寄 存器建立- - 种对应关系。建立这种对应关系的说明语句格式如下:

3. 伪指令END表示源程序到此为止,汇编程序对该语句之后的任何内容都不作处理.所以,通常情况下,伪指令END是源程序的最后- -条语句。伪指令END后面可附带-一个在程序中已定义的标号,由该标号指明程序的启动位置。如果源程序是-一个独立的程序或主模块,那么,伪指令END后面一“定要附带- - 个标号;如果源程序仅是一一个普通模块,那么,其END后面就一-定 不能附带标号:

4. MOV AX,@DATA;取数据段段值

实验五 、分支结构程序设计

实验步骤

总结:

无条件转移指令JMP(Unconditional Jump): JMP指令的- -般形式:

2.无符号数的条件转移指令(JumpsBased on Unsigned (Logic) Data)

3. 在编写分支程序时,要尽可能避免编写“头重脚轻”的结构,即:当前分支条件成立时,将执行一系列指令,而条件不成立时,所执行的指令很少。这样就使后-一个分支离分支点较远,有时甚至会遗忘编写后--分支程序。这种分支方式不仅不利于程序的阅读,而且也不便将来的维护。

4. 在屏幕上从当前光标位置开始显示--个字符串:

实验六 、分支与循环混合结构程序设计

实验步骤

总结:

1.选择结构

2.条件跳转指令

3.嵌套if结构

4.case 结构

5.无符号跳转指令

6.迭代结构

实验七 、多重循环及过程的应用

实验步骤

总结:

1. 循环指令(Loop Until Complete)

2.返回指令(RET)

3. 寄存器的保护与恢复

实验八 、结构的应用及宏程序设计

实验步骤

1、输入下列程序,汇编并链接通过。

2、运行程序,可以看到显示结果如下:

3、利用MASM工具汇编生成列表文件,并察看列表文件内容,注意其中的宏调用和展开过程,注意局部标号的重命名规律。

4.附上修改后源码:

实验附录 汇编语言常用出错信息

全文知识点总结:

【debug命令】总结:

【cpu内部寄存器】状态的解释:

【DAA】指令:

【PTR】说明

【DOS(INT 21H)功能调用表】(部分)

【汇编语言---跳转指令】ja、jb、jl

【汇编简化段】定义

 1. 存储模型伪指令

2. 简化的段伪指令

3.与简化段定义有关的预定义符号


实验1:调试工具DEBUG的应用

内容及步骤

注:本次实验可以参照教材上关于DEBUG的叙述内容进行。

一、DEBUG 命令使用

  • 1、开机后,切换到命令提示符窗口下,出现提示符后键入命令DEBUG,进入调试环境, 显示提示符 '- '。
  • 2、用命令 F 200 220 'AB' 将'AB'的两个ASCII码循环填入内存。

注:第一个参数200是当前段的起始偏移地址,第二个参数220是终了偏移地址,第三个参数‘AB’是被填入的数值,若不给出第二个参数则填入128(8行)个字节。

  • 3、用命令 D200 观察内存中的十六进制码及屏幕右边的ASCII字符。
微机原理与接口技术-NUAA-课内实验报告_第1张图片

  • 4、用命令 F230 23F 12 重复上二项实验,观察结果并比较。
微机原理与接口技术-NUAA-课内实验报告_第2张图片

  • 5、用命令 E200 41 42 43 44 45将A-E的ASCII码写入地址为200开始的内存单元中,再用D命令观察结果,看键入的十六进制数和ASCII码的对应关系。
微机原理与接口技术-NUAA-课内实验报告_第3张图片

(这里注意 输完一个字符以后 按下空格表示 继续输入 继续修改下面的字符)

  • 6、用H命令检查下列各组十六进制数的和与差(补码表示):

                (1)56H,34H (2)23H,45H (3)AB,3045H

注:输入 H 12 34 则在下一行显示0046 FFDE,即二者的补码和与差。在DEBUG环境下所有数据和地址都是按16进制处理,所以不要加后面的H标志。

微机原理与接口技术-NUAA-课内实验报告_第4张图片
  • 7、用R命令检查各寄存器内容,特别注意AX,BX,CX,DX,IP及标志位中ZF,CF和AF的内容。

注:若在DEBUG32环境下用R16和R32命令分别显示16位和32位寄存器内容。

  • 8、用R命令将AX,BX内容改写为1050H及23A8H。
微机原理与接口技术-NUAA-课内实验报告_第5张图片

二、8086常用指令练习

1、传送指令

  • 用A命令在内存400H处键入下列内容:

-A 0400
****:0400   MOV   AX,1234
****:0403   MOV	 BX,5678
****:0406   XCHG	 AX,BX
****:0408   MOV	 AH,10
****:040A   MOV	 AL,20
****:040C   MOV	 CX,89AB
****:040F   XCHG	 AX,CX
****:0411   
-

注:****为段寄存器CS的当前值,内容是不一定的,每行命令以回车键结束。

  • 用U命令检查键入的程序并记录,特别注意左边的机器码与指令的对应关系。

-U  0400
微机原理与接口技术-NUAA-课内实验报告_第6张图片

  • 用T命令逐条运行这些指令,每运行一行指令观察各寄存器及IP的变化情况,并注意标志位的变化情况。

-T =0400 (注:=400是表示从偏移地址400处开始单步执行)

-T (不给出地址,则表示接续上一条指令执行)

-T
微机原理与接口技术-NUAA-课内实验报告_第7张图片

可以发现都正常运行了

2、加减法指令:

1)用A命令在内存100H处键入下列内容:

MOV AH,11

MOV AL,22

ADD AL,AH

SUB AL,33

MOV CX,1234

MOV DX,5678

ADD CX,DX

SUB CX,AX

SUB CX,CX

2)用U命令检查键入的程序及对应的机器码。

3)用T命令逐条运行这些指令,检查并记录有关寄存器及ZF情况。

运行情况:

微机原理与接口技术-NUAA-课内实验报告_第8张图片

 微机原理与接口技术-NUAA-课内实验报告_第9张图片

微机原理与接口技术-NUAA-课内实验报告_第10张图片

通过上面可以发现当出现0的结果时候,出现ZR 意味着 z f=1

3、带进位加减法:

1)用A命令在内存200H处键入下列内容,并用U命令检查:

MOV AH,12
MOV AL,84
MOV CH,56
MOV CL,78
ADD AL,CL
ADC AH,CH
MOV DH,A7
MOV DL,58
SUB DL,7F
SBB DH,34

微机原理与接口技术-NUAA-课内实验报告_第11张图片

微机原理与接口技术-NUAA-课内实验报告_第12张图片

发现在上一步的运算中po->pe代表1个数为偶数,下一步又变了回去。

微机原理与接口技术-NUAA-课内实验报告_第13张图片

解释下运行SUB DL,7F的过程:58-7F 对上面有进位,在低四位上会向高四位借位所有AF=1 ,高四位也有借位,CF=1

PL(positive):符号位为0,NG(negative):符号为1

NA:(NotAuxiliary carry)没有辅助进位,AC:(AuxiliaryCarry)有辅助就进位

PE:偶数标志位,PO:1个数为奇数。

NC:not carry 没有进位,CY(carry yes):有进位。

解释下:为什么下面的SUB出现了溢出

微机原理与接口技术-NUAA-课内实验报告_第14张图片

到这里细心的人可能发现DX结果不对,哈哈哈哈是因为最后的SBB写成了SUB,这样就导致结果DH中结果少减了CF=1结果应该是72D9。针对这一错误,在后面的16位运算中将给出矫正!

2)用T命令逐条运行这些指令,检查并记录有寄存器及CF内容。

3)上面这段程序若改用16位操作指令达到同样结果,怎么改?试修改并运行之。

微机原理与接口技术-NUAA-课内实验报告_第15张图片

微机原理与接口技术-NUAA-课内实验报告_第16张图片

(针对上面的问题做出了修改)对比发现与先前的结果是一样的!

* 4、BCD码加减法(选作)

1)内容:

MOV AL,65
ADD AL,16
DAA

2)要求:用A 命令键入,U命令检查,T命令逐条运行并记录有关寄存器及AF内容。

注:相加后AL值本来是7B,经DAA指令调整后变为81,即65和16两个BCD码的和。

微机原理与接口技术-NUAA-课内实验报告_第17张图片

(即原来是按照16进制进行二进制的加法运算,add调整为两个BCD数视为BCD加法)

af=ac 证明存在辅助进位,也就是bcd码运算时候存在的辅助进位.

实验二、 内存操作数及寻址方法

一、内存操作数及各种寻址方式使用

程序内容:

MOV AX,2000 (立即寻址)

MOV [200],AX ;(直接寻址)

MOV BX,210

MOV BYTEPTR[BX],50 ;(寄存器间接寻址)

MOV CL,40

INC BX

MOV [BX],CL;(寄存器间接寻址)

DEC CL

MOV SI,5

MOV [BX+SI],CL ;(基址加变址)

MOV [BX+SI+1],CL ;(基址加变址相对寻址)

MOV WORDPTR[BX+SI+2],1234 ;(基址加变址相对寻址)

操作步骤

1)用A命令键入上述程序,并用T命令逐条运行。

2)每运行一条有关内存操作数的指令,要用D命令检查并记录有关内存单元的内容并注意是什么寻址方式。

高位数据放在高地址。

微机原理与接口技术-NUAA-课内实验报告_第18张图片

微机原理与接口技术-NUAA-课内实验报告_第19张图片

微机原理与接口技术-NUAA-课内实验报告_第20张图片

注:D命令显示结果时,双字节数在内存的存放是高地址对应高数据位;

指令中出现的BYTE PTR及WORD PTR是因为操作数的宽度必须一致。

二、求累加和程序

程序内容:

MOV BX,200

MOV CX,9

XOR AX,AX

ADD AL,[BX] ;按字节相加

ADC AH,0 ;若有进位则到AH中

INC BX

LOOP 108

INT3

操作步骤:

1)进入DEBUG环境。

2)用命令F 200 L10 40 在内存200H-20FH地址处填入一系列值40H。

3)用命令A 100 将上述程序键入到100H开始的内存中。

注:LOOP指令用到108H号地址,即为ADD指令的当前地址,构成一个循环。

4)用命令G=100 执行该程序段,程序运行后停在INT 3 指令上,此时观察AX寄存器

的值为240H,即为9个40H的和。

注:INT 3指令是一条断点中断指令,程序遇到该指令则停止。

5)用T=100命令单步执行,观察IP、CX及AX寄存器的值,分析程序执行过程。

写好代码

代码在0779:100

微机原理与接口技术-NUAA-课内实验报告_第21张图片

数据在0760:0200

微机原理与接口技术-NUAA-课内实验报告_第22张图片

结果如下:

微机原理与接口技术-NUAA-课内实验报告_第23张图片

针对里面特殊点,给出说明如下图:

微机原理与接口技术-NUAA-课内实验报告_第24张图片

(每四次运算AH+=1 九次运算就是AX=0200H+0040H=0240H)

三、总结:

  • D命令显示结果时,双字节数在内存的存放是高地址对应高数据位:

  • 为了使指令中存储单元操作数具有明确的属性,我们可以使用强制属性操作符PTR.

  1. 其一般格式为:

            a)   数据类型PTR 地址表达式
  2. 其中:数据类型是前面所学的各种数据类型,常用的数据类型有: BYTE、WORD、DWORD、NEAR和FAR等:在指令中用操作符PTR强制后,不管其后的地址表达式原数据类型是什么,在本指令中就以PTR前面的类型为准。该强制属性只在本指令有效,是一种临时性的属性,它不会改变原内存单元的定义属性:

  • G命令等号后的地址指定程序段运行的起始地址:如 g=200

  • 中断指令INT:断指令INT的- -般格式如下: INT Imm

        其中:立即数Imm是一个0^ OFFH范围内的整数。该指令执行完后,CPU将转去执行中断服务程序。程序遇到断点(实际上就是断点中断指令INT 3),停止执行,并显示当前所有寄存器和标志位的内容、以及下一条将要执行的指令(显示内容同R命令),以便观察程序运行到此的情况。程序正常结束,将显示“Program terminated normally" ;

  • 在作单步操作或用G命令设段点操作时,在暂停处,可以检查所有寄存器和有关内存单元的内容,这不会影响程序的继续执行:

  • 若想把源数据块重新换- -批数据,可以用DEBUG的Fill命令填充新的数据。例如:

-F 1000: 00L1F 33 从1000: 0000H 开始的长度为(L也就是这个意思)1F(H)=31个字节被替换成33H. (-f 命令可跳转到 这里 观看)

  • 最后求累加和之所以没有出现上周良老师说的问题,是因为我的数据段和代码段间隔很远,并没有出现数据段和代码段相互覆盖的情况,所以最后的结果也是正确无误的。

实验三 、数据串传送和查表程序

实验内容及步骤

一、利用查表方法显示内存单元的内容

1、编辑下列程序:

.model small

.stack

.data

str1 db 'ABCDEFGHIJ' ;待显示的内存区内容

str2 db 'Please input thenumber you will display:',10,13,'$'

.code

.startup

mov ah,9

mov dx,offset str2

int 21h ;显示STR2字符串的内容,即提示信息

mov ah,1

int 21h ;输入待显示的字符序号(0-9)

mov bx,offset str1

sub al,30h

xlat ;查STR1表,对应序号的字符ASCII码进入AL

mov dl,al

mov ah,2

int 21h ;显示对应字符

.exit 0

End

2、程序汇编通过后,在运行过程中输入0-9的任意数字,显示STR1字符串中对应位置 的字符。

3、在DEBUG环境中,用P命令调试执行该程序,察看AL寄存器的变化情况及结果的输出, 分析其执行过程。

代码反汇编如下:可以发现数据段在0773

CLI 指令 : 禁止中断

STI :允许中断

微机原理与接口技术-NUAA-课内实验报告_第25张图片

微机原理与接口技术-NUAA-课内实验报告_第26张图片

利用 -p命令执行过程如下:

微机原理与接口技术-NUAA-课内实验报告_第27张图片

微机原理与接口技术-NUAA-课内实验报告_第28张图片

微机原理与接口技术-NUAA-课内实验报告_第29张图片

微机原理与接口技术-NUAA-课内实验报告_第30张图片

微机原理与接口技术-NUAA-课内实验报告_第31张图片

二、数据串传送程序

1、编辑下列程序:

.model small

.stack

.data

str1 db'abcdefghijklmn' ; 源串定义

lengs equ$-str1 ;lengs 代表了 str1的长度

;动态自动修改

str2 dblengs dup (?),'$' ; 目标串

.code

.startup

mov ax,ds

moves,ax ; 使DS和ES为同一个段

cld

lea si,str1

lea di,str2

mov cx,lengs

rep movsb ; 串复制

mov ah,9

mov dx,offsetstr2

int 21h ; 显示目标串

.exit 0

End
  1. 先说说MOVSB(MOVe String Byte):即字符串传送指令,这条指令按字节传送数据(MOVSW为以字为单位传送字符串)。通过SI和DI这两个寄存器控制字符串的源地址和目标地址,比如DS:SI这段地址的N个字节复制到ES:DI指向的地址,复制后DS:SI的内容保持不变。
  2. 而REP(REPeat)指令就是“重复”的意思,术语叫做“重复前缀指令”。既然是传递字符串,如果程序设计者写成一个字(节)一个字(节)地传送,会显得相当繁琐。因此有必要用一个寄存器来控制串长度,用少量的代码达到一次性传送的目的。这个寄存器就是CX,指令每次执行前都会判断CX的值是否为0(为0结束重复,不为0,CX的值减1),以此来设定重复执行的次数。因此设置好CX的值之后就可以用REPMOVSB了。
  3. CLD(CLear Direction flag)则是清方向标志位,也就是使DF的值为0,在执行串操作时,使地址按递增的方式变化,调整当前指针的执行方向。
  4. 这条指令与STD(SeTDirection flag)的执行结果相反(指针方向递减),即置DF的值为1。

2、程序汇编通过后,运行程序察看输出结果。

3、在DEBUG环境中,用P命令调试执行该程序,察看SI、DI寄存器及相应内存单元的变化情况,分析其执行过程。

反汇编代码:

微机原理与接口技术-NUAA-课内实验报告_第32张图片

微机原理与接口技术-NUAA-课内实验报告_第33张图片

代码执行:

微机原理与接口技术-NUAA-课内实验报告_第34张图片

微机原理与接口技术-NUAA-课内实验报告_第35张图片

微机原理与接口技术-NUAA-课内实验报告_第36张图片

微机原理与接口技术-NUAA-课内实验报告_第37张图片

4、如果把源串的小写字母复制到目标串的同时再修改为对应的大写字母,该怎么修改程序?

可以把串复制部分修改,先从源串中取出字符,修改后送到目标串即可。

程序改为:

.model small

.stack

.data

str1 db' abcdefghijklmn' ;源串定义

lengs equ $-str1

str2 db lengs dup (?),'$' ; 目标串

.code

.startup

mov ax, ds

mov es, ax ;使DS和ES为同一个段

lea si, str1

lea di, str2

mov cx, lengs

lp: ;循环开始的标志

mov al, [si] ;将源串的字符取出


sub al, 20H;将小写改为大写


mov [di],al ;将修改后的字符送到STR2

inc si

inc di

loop lp


mov ah, 9

mov dx, offset str2 ; 或者lea dx, str2

int 21h;显示目标串

int 3

.exit 0

End

运行结果:

运行前:

微机原理与接口技术-NUAA-课内实验报告_第38张图片

运行后:

微机原理与接口技术-NUAA-课内实验报告_第39张图片

实验四 、完整段定义程序实现

实验内容

要求程序中实现对键盘输入的字符作如下处理:

  • 如果输入的是小写字母则转换成对应的大写字母并显示在屏幕上;

  • 如果输入的是大写字母则转换成对应的小写字母并显示在屏幕上;

  • 如果输入的是键盘上的Esc键则退出程序执行;

  • 按其它任意可显示键,则不作处理,直接显示输出。

实验步骤

1、编辑下列程序:

​
DATA SEGMENT ;数据段定义

MESSAGE DB 'Please input your key!',0DH,0AH,'$'

DATA ENDS

STACK SEGMENT PARA STACK 'STACK' ;堆栈段定义

; 汇编segment伪指令-CSDN博客

DB 50 DUP(?)

STACK ENDS

CODE SEGMENT ;代码段

ASSUME CS:CODE,DS:DATA,SS:STACK ;分配段寄存器

START: MOV AX,DATA

MOV DS,AX

MOV DX,OFFSET MESSAGE

MOV AH,9

INT 21H ;显示提示信息(字符串)

AGAIN:

MOV AH,1

INT 21H ;读入一个键盘按键

CMP AL,1BH ;按的是ESC键(ASCII码为1BH)则退出程序

JE EXIT

CMP AL,61H

JB NEXT

CMP AL,7AH ;如果超过了大写字母的范围

JA NEXT ;无符号大于就跳转

SUB AL,20H ;ASCII码在61H和7AH之间(小写字母)则转换为大写字母

JMP DISP

NEXT: ;在97以下(A)

CMP AL,41H

JB DISP

CMP AL,5AH

JA DISP

ADD AL,20H ;ASCII码在41H和5AH之间(大写字母)则转换为小写字母

DISP: ;比 a还小

MOV DL,AL

MOV AH,2

INT 21H ;显示当前字符

JMP AGAIN ;循环

EXIT:

MOV AH,4CH ;结束程序

INT 21H

CODE ENDS

END START ;指定START标号为程序入口地址

​

2、把上述程序保存为ASM源文件,利用MASM根据对源文件进行汇编,产生.OBJ文件,若汇编时提示有错,编辑工具修改源程序后重新汇编,直至通过。

3、用LINK将.OBJ文件连接成可执行的.EXE文件。

微机原理与接口技术-NUAA-课内实验报告_第40张图片

4、在DOS状态下运行LINK产生的.EXE文件,按过键盘之后在屏幕上显示实验要求的字符,按ESC键可返回DOS。

注:汇编过程中若出现错误,可参阅本章最后的实验附录。

微机原理与接口技术-NUAA-课内实验报告_第41张图片

5.把上述程序改为简化段定义格式,再汇编,查看执行结果。

.model small

.stack 50

.data

MESSAGE DB 'Please input your key!',0DH,0AH,'$'

.code

.startup

MOV AX,@DATA

MOV DS,AX

MOV DX,OFFSET MESSAGE

MOV AH,9

INT 21H ;显示提示信息(字符串)

AGAIN:

MOV AH,1

INT 21H ;读入一个键盘按键

CMP AL,1BH ;按的是ESC键(ASCII码为1BH)则退出程序

JE EXIT

CMP AL,61H

JB NEXT

CMP AL,7AH ;如果超过了大写字母的范围

JA NEXT ;无符号大于就跳转

SUB AL,20H ;ASCII码在61H和7AH之间(小写字母)则转换为大写字母

JMP DISP

NEXT: ;在97以下(A)

CMP AL,41H

JB DISP

CMP AL,5AH

JA DISP

ADD AL,20H ;ASCII码在41H和5AH之间(大写字母)则转换为小写字母

DISP: ;比 a还小

MOV DL,AL

MOV AH,2

INT 21H ;显示当前字符

JMP AGAIN ;循环

EXIT:

MOV AH,4CH ;结束程序

INT 21H

.EXIT 0

END

微机原理与接口技术-NUAA-课内实验报告_第42张图片

总结:

1.segment是段,是段定义的伪指令。在汇编中有数据段,代码段,堆栈段和附加段

[格式]

segment_name SEGMENT 【定位(对齐)类型】 【组合类型】 【类别名】

···;here is yourcode

segment_name ends

注:【】都是在英文输入法下的符号,此处无法打出所以才用了中文状态下的【】

参数解释:

【定位类型】

指明段开始的边界,如para,它使段定位在小段的边界,段首地址正好能被16整除,定位类型未指定时默认为para

段的定位类型有4种,分别是:

page(页起始),起始地址以00H结尾,能被256整除

para (节起始),起始地址以0H结尾,能被16整除

word(字起始),起始地址末位为0,是偶地址

byte (字节起始),起始地址为任意边界

【组合类型】

决定本段是否要和其他段组合在一起,组合类型有:stack,common,public

【类别名】

连接时用于相关段组合在一起,如代码段’code’,数据段’Data’,堆栈段’Stack’

例子: stack segmentpara stack ‘stack’

解释: para表明该段起始地址对齐到para。 1 para= 16 bytes。stack声明该段是堆栈段,这样在最后的链接link时,会将该最终创建的exe文件头部的SS:SP域指向该段的末尾

‘stack’表明该段的组合名为’stack’。同一程序不同模块中,相同组合名的段会组合到一起。组合名也决定了最后exe文件中各个段的排列顺序。

  • 2. 在汇编语言源程序中可以定义多个段,每个段都要与一“个段寄 存器建立- - 种对应关系。建立这种对应关系的说明语句格式如下:

ASSUME段寄存器名:段名[,段寄存器名:段.......

其中:段寄存器是CS.DS、ES.SS、FS和GS,段名是在段定义语句说明时的段名。

在一条ASSUME语句中可建立多组段寄存器与段之间的关系,每种对应关系要用逗号

分隔。例如,

ASSUME CS:CODE1, DS:DATA1 .

上面的语句说明了: CS对应于代码段CODEI,DS对应于数据段DATA1.

在ASSUME语句中,还可以用关键字NOTHING来说明某个段寄存器不与任何段相对

应。下面语句说明了段寄存器ES不与某段相对应。

ASSUME ES:NOTHING

3. 伪指令END表示源程序到此为止,汇编程序对该语句之后的任何内容都不作处理.所以,通常情况下,伪指令END是源程序的最后- -条语句。伪指令END后面可附带-一个在程序中已定义的标号,由该标号指明程序的启动位置。如果源程序是-一个独立的程序或主模块,那么,伪指令END后面一“定要附带- - 个标号;如果源程序仅是一一个普通模块,那么,其END后面就一-定 不能附带标号:

4. MOV AX,@DATA;取数据段段值

实验五 、分支结构程序设计

实验内容

实现从键盘输入一个月份数值(1-12),根据输入的月份数值显示相应的月份英文单词缩写,如输入5则显示May。

实验步骤

1、编辑下列程序:

.model small

.stack

.data

mon db 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'

msg1 db 'Pleaseinput a month(1-12) :',13,10,'$'

msg2 db 'Inputerror! Now try again...',13,10,'$'

buffer labelbyte ;定义输入月份的输入缓冲区 LABEL的功能是定义变量或标号的类型,

;而变量或标号的段属性和偏移属性由该语句所处的位置确定。

maxlen db3 ;最多2个数字,包括一个回车

actlen db? ;保存实际输入的字符个数

string db3 dup(?) ;保存输入的月份数字内容(ASCII码)

.code

.startup

shuru: ;开始输入月份编号

leadx,msg1

movah,09h

int21h ;显示提示信息

leadx,buffer

movah,0ah

int21h ;输入月份数值 DS:DX=缓冲区首地址 以下全部用12月举例



cmpactlen,0 ;若没有输入任何字符则转出错处理

je shuruerr

lea di,string

cmp actlen,2

je da10 ;若输入的是2位数月份值则转到da10标号处执行



andal,0fh ;把ASCII码转换为对应数字

jmpjisuan

da10:

moval,string ;al经过这一步拿到的是 月份的十位

andal,0fh ;把月份数值十位的ASCII码转换为对应数字(如12月的1字)

;31 对应0010 0001 经过处理后变成了 0000 0001



mov bl,10

mulbl ; ax=bl*al 无符号乘法 这里应该是变成了000A(AX)

andstring[1],0fh ;把月份数值个位的ASCII码转换为对应数字

addal,string[1] ;十位加上个位 (如12月) 这里al变成了0C 整个AX=000C (12月份)

;上面一行执行完了直接执行,jisuan中的代码,汇编代码是连在一起的 不会跳回去

jisuan: ;以下计算偏移地址

cmpal,1 ;比1小是非法月份

jbshuruerr ;若月份值小于1则转出错处理

cmp al,12

jashuruerr ;比12大也是非法月份



subal,1 ;月份值减1 因为0位置对应1月



shl al,1

shlal,1 ;月份再乘4对应了MON字符串中从首地址开始的字符相对位置,因为jan,占了4个,类比下去

xorah,ah ;1月份从0位置开始即JAN,...5月份从位置16开始即MAY

leasi,mon ;找到被显示月份字符的位置

add si,ax

movcx,3 ;只有三个对应了相应的单词,所以输出三个,这里也是接着执行了output的代码,不存在返回

output: mov dl,[si] ;输出对应月份英文缩写

mov ah,2

int 21h

inc si

loopoutput

.exit 0

shuruerr: lea dx,msg2 ;输入出错时提示出错,并转到程序起始处重新执行

mov ah,09h

int 21h

jmp shuru

end

2、程序汇编通过后,运行程序察看输出结果。

微机原理与接口技术-NUAA-课内实验报告_第43张图片

微机原理与接口技术-NUAA-课内实验报告_第44张图片

3、程序运行中不输入月份值(直接回车),或输入非法月份值,观察程序执行的情况。

微机原理与接口技术-NUAA-课内实验报告_第45张图片

总结:

  1. 无条件转移指令JMP(Unconditional Jump): JMP指令的- -般形式:

JMP标号/Reg/Mem

JMP指令是从程序当前执行的地方无条件转移到另一个地方执行。

这种转移可以是一个短(short)转移(偏移量在[-128,127]范围内),近(near)转移(偏移量在[-32K,32K]范围内)或远(far)转移(在不同的代码段之间转移)。

短和近转移是段内转移,JMP指令只把目标指令位置的偏移量赋值指令指针寄存器IP,

从而实现转移功能。但远转移是段间转移,JMP指令不仅会改变指令指针寄存器IP的值,

而且还会改变代码段寄存器CS的值。

该转移指令的执行不影响任何标志位。

2.无符号数的条件转移指令(JumpsBased on Unsigned (Logic) Data)

微机原理与接口技术-NUAA-课内实验报告_第46张图片

3. 在编写分支程序时,要尽可能避免编写“头重脚轻”的结构,即:当前分支条件成立时,将执行一系列指令,而条件不成立时,所执行的指令很少。这样就使后-一个分支离分支点较远,有时甚至会遗忘编写后--分支程序。这种分支方式不仅不利于程序的阅读,而且也不便将来的维护。

所以,在编写分支结构时,一般先处理简单的分支,再处理较复杂的分支。对多分支的情况,也可遵循“由易到难”的原则。因为简单的分支只需要较少的指令就能处理完,一旦处理完这种情况后,在后面的编程过程中就可集中考虑如何处理复杂的分支;

4. 在屏幕上从当前光标位置开始显示--个字符串:

MOV DX, OFFSET STRING ;取字符串的首地址

MOVAH,09H;系统调用功能号送AH

INT 21H

此调用的入口参数为内存缓冲区中字符串的首地址,该字符必须以‘$’结尾,出口参

数无,调用执行结果是在屏幕上显示-一个字符串

实验六 、分支与循环混合结构程序设计

实验内容

实现成绩单的分等级统计功能,在数据段中有成绩单的定义,并包含有若干个分数,程序根据成绩单数据的个数循环分别统计个分数段成绩的个数,最后输出个等级段的分数个数。要求:成绩单数据少于100字节,输出的每个档次统计值按两位数显示。

实验步骤

1、编辑下列程序:

.model small

.stack

.data

HAN DB 42,62,68,70,79,82,85,90,95,99,66,88,78,90,98,200 ;成绩单

count equ$-han ;记录数据个数

MEM DB 6dup(0) ;分别用于存放各分数段成绩的个数

scoree db10,13,'Score "E" count is:$' ;定义各分数段的输出提示信息

scored db10,13,'Score "D" count is:$'

scorec db 10,13,'Score "C" count is:$'

scoreb db10,13,'Score "B" count is:$'

scorea db10,13,'Score "A" count is:$'

scoreo db10,13,'Score "Other" count is:$'

table dwscoreo,scoree,scored,scorec,scoreb,scorea ;定义地址表

.code

.startup

MOV CX,count

mov si,0

begin: ;统计各个分数段的成绩个数

.ifhan[si]<60

addmem[1],1

.elseifhan[si]>=60 && han[si]<=69

addmem[2],1

.elseifhan[si]>=70 && han[si]<=79

addmem[3],1

.elseifhan[si]>=80 && han[si]<=89

addmem[4],1

.elseif han[si]>=90 && han[si]<=99

addmem[5],1

.else

addmem[0],1

.endif

inc si

loop begin ;cx=0了自己就停下了



mov cx,6 ;一共要输出6项信息

.repeat ;开始循环

movsi,cx

decsi

addsi,si ;计算地址表中对应地址项与提示信息的对应关系

mov dx,table[si]

mov ah,9

int21h ;显示输出提示信息



xorah,ah ;接下来该输出具体的数据了,先清空下ah



mov si,cx

MOVal,mem[si-1] ;读出统计好的一个数值(此处只能处理99以下的)

movbl,10

div bl ;ax/10

movmem[si-1],ah ;AH为余数是个位,暂时存放回该内存单元

movdl,AL ;al为商是十位

add dl,30h ;转换为ASCII码

MOVAH,2

INT 21H ;以十进制形式显示十位

MOVAH,2

movdl,mem[si-1]

adddl,30h

INT21H ;再显示个位

.untilcxz ;CX自动减1,减到0结束循环

.exit0

END

2、程序汇编通过后,运行程序察看输出结果。

微机原理与接口技术-NUAA-课内实验报告_第47张图片

3、修改成绩单的内容再重新汇编和运行程序,观察程序的结果变化情况。

微机原理与接口技术-NUAA-课内实验报告_第48张图片

4、利用DEBUG工具的U命令反汇编可执行程序,观察.IF语句及.REPEAT语句所对应的汇编指令情况。

微机原理与接口技术-NUAA-课内实验报告_第49张图片

微机原理与接口技术-NUAA-课内实验报告_第50张图片

微机原理与接口技术-NUAA-课内实验报告_第51张图片

5、利用MASM的命令行,汇编源文件并生成.LST列表文件,查看列表文件内容。

注:实际上高级程序结构控制语句在汇编时,都转换成相应的比较指令和条件转移指令了。高级程序结构控制语句使得程序设计、理解都变得非常方便。

微机原理与接口技术-NUAA-课内实验报告_第52张图片

微机原理与接口技术-NUAA-课内实验报告_第53张图片

微机原理与接口技术-NUAA-课内实验报告_第54张图片

总结:

1.选择结构

if-then结构

C语言版本

if(count == 10)

{

count --;

i++;

}

MASM汇编

.if count==10

dec count

inc i

.endif

cmp指令,该指令用于比较两个参数大小

cmp mem, imm 比较内存mem和立即数imm大小

cmp reg, imm 比较寄存器reg和立即数imm大小

cmp reg, mem 比较寄存器reg和内存mem大小

cmp mem, reg 比较内存mem和寄存器reg大小

cmp imm, reg 比较立即数和寄存器reg大小

cmp imm, mem比较立即数和内存mem大小

cmp reg, reg比较两个寄存器数据大小

.if 内部是通过cmp实现的,.if和cmp都不能比较两个内存中数据大小

2.条件跳转指令

je 相等时跳转

jne 不相等时跳转

je 和jne可以用于无符号数比较跳转,也可用于符号数比较跳转

符号数据跳转指令

jg 大于时跳转

jge 大于或等于时跳转

jl 小于是跳转

jle 小于或等于时跳转

jnle 不小于且不等于时跳转,即大于时跳转

jnl 不小于时跳转

jnge 不大于且不等于时跳转

jng 不大于时跳转

这类指令可用上面的jg,jge,jl,jle替代,尽量不要使用,

不容易理解。

.if-.end汇编指令可用上面的cmp指令和条件跳转指令替代

.if number == 0

dec number

.endif

用条件跳转指令实现为:

if01: cmp number, 0

jne endif01

then01: dec number

endif01: nop

if-then-else结构

汇编指令的结构为

.if 条件

;执行语句

.else

;执行语句

.endif

举例说明,C语言

if (x>=y)

x--;

else

y--;

汇编语言

if01: mov eax,x

cmp eax,y

jl else01

then03: dec x

jmp endif01

else01: dec y

endif01: nop

采用汇编指令

mov eax , x

.if eax >= y

dec x

.else

dec y

.endif

3.嵌套if结构

C语言

if(x < 50)

y++;

else

if(x <= 100)

y = 0;

else

y--;

汇编语言

采用汇编指令

.if x < 50

inc y

.elseif x <= 100

mov y, 0

.else

dec y

.endif

通过跳转指令实现上述步骤

if01: cmp eax, 50

jge else01

then01: inc y

jmp endif01

else01: nop

if02: cmp x, 100

jg else02

then02: mov y,0

jmp endif01

else02: dec y

endif01: nop

4.case 结构

C语言

switch(w)

{

    case 1: x++;

            break

    case 2:

    case 3: y++;

            break;

    default: z++;


}

汇编语言中没有case switch对应的汇编指令,但是可以通过cmp指令和跳转指令实现

switch01: cmp w, 1

je case1

cmp w,2

je case2

cmp w,3

je case2

jmp default01

case1: inc x

jmp endswitch01

case2: inc y

jmp endswitch01

default01 : inc z

endswitch01: nop

5.无符号跳转指令

ja 高于跳转

jae 高于或等于跳转

jb 低于跳转

i = 1;

while(i <= 3)

{

i++;

}

jbe 低于或等于时跳转

逻辑运算符综合实现

.if w==1 || x==2&&y ==3

inc z

.endif

条件跳转指令实现上述逻辑

if01: cmp w,1

jne or01

or01: cmp x, 2

jne endif01

cmp y, 3

jne endif01

then01: inc z

endif01: nop

6.迭代结构

1 前置循环结构while

C语言

i = 1;

while(i <= 3)

{

i++;

}

汇编指令实现

mov i, 1

.while (i <= 3)

inc i

.endw

条件跳转指令实现

mov i, 1

while01: cmp i, 3

jg endw01

inc i

endw01: nop

2 后置循环结构 do while

C语言

i = 1;

do

{

i++;



}while(i <= 3);

汇编指令 .repeat .until

mov i, 1

.repeat

inc i

.until i > 3

.repeat-.until和C语言do -while一样,第一次先执行循环体内容,do-while是在每次运行循环体后判断条件不满足跳出循环,

.repeat-.until 是判断条件不满足则一直循环,直到.until后面条件成立则跳出循环。

通过汇编跳转指令实现

mov i, 1

repeat01: nop

inc i

cmp i, 3

jle repeat01

endrpt01: nop

3 固定循环结构 for

C语言

for(i=1; i <= 3; i++)

{

//循环体

}

汇编语言

mov ecx, 3

.repeat

; 循环体

.untilcxz

.repeat-.untilcxz 对应C语言的for循环固定循环结构,它和.repeat-.until都是后置循环结构。

都要先执行一次循环体,然后.untilcxz会将ecx中的数据值减1,接着判断ecx是否为0,ecx为0则跳出循环

ecx大于0则继续执行循环。所以执行.repeat-.untilcxz这个循环前保证ecx大于0

.repeat-.untilcxz 内部是loop实现的,所以同样可以通过loop实现该循环

mov ecx, 3

for01 : nop

; 循环体

loop for01

endfor01: nop

loop 和.untilcxz一样,都会将ecx中的值减1,然后判断ecx是否为0,为0则终止循环,大于0则继续循环。

loop指令最多只能向前跳转128字节处,也就是循环体内容不得超过128字节。同样.repeat-.untilcxz循环体也不得超过128字节。

与.if高级汇编指令一样,.while-end高级汇编指令和.repeat-.until高级汇编指令都基于cmp,不可直接比较两个内存变量。

使用loop指令和.repeat-untilcxz指令前,需要保证ecx大于0,在循环体中尽量不要更改ecx数值,如果需要更改则将ecx

数据先保存在内存中,执行完相关操作后将ecx数值还原,保证.untilcxz和loop操作ecx的准确性。

实验七 、多重循环及过程的应用

实验内容

完成冒泡法排序程序,利用过程对排好序的数据按10进制显示输出

实验步骤

1、输入下列程序,汇编并连接通过。

.model small

.stack

.data

array dw 12,-66,108,9,5,-123,2000,-900;待排序数据

count equ $-array

pushcount db 0 ;用于临时存放被显示数据的10进制总位数

.code

.startup

mov cx,count

shr cx,1

dec cx

mov bl,-1

again: ;冒泡法排序外重循环

mov dx,cx

and bl,bl

je exit

xor bl,bl

xor si,si

again1: mov ax,array[si];内重循环

.if sword ptr ax>sword ptr array[si+2]

xchg array[si+2],ax

mov array[si],ax

mov bl,-1

.endif

inc si

inc si

dec dx

jnz again1

loop again

exit:



mov cx,count

shr cx,1

mov si,0

.repeat

mov ax,array[si]

call bintodec ;显示排好序的数据

add si,2

.untilcxz

.exit 0

bintodec proc near ;过程定义

push bx

push dx

.if sword ptr ax<0 ;对于负数则取其相反数统一按正数处理

neg ax

push ax

mov dl,'-' ;显示负号

mov ah,2

int 21h

pop ax

.endif

mov bx,10

loop1: cwd

div bx

push dx ;把每次除以10的余数压入堆栈,并记录数据位个数

inc pushcount

cmp ax,0

jne loop1

print: pop dx ;从堆栈弹出每位数据并显示

add dx,30h

mov ah,2

int 21h

dec pushcount

jnz print

mov ah,2 ;显示完一个数据的各个数位后再显示空格进行分隔

mov dl,' '

int 21h

pop dx

pop bx

ret

bintodec endp

END

2、汇编并链接通过该程序,观察执行结果;

微机原理与接口技术-NUAA-课内实验报告_第55张图片

3、试着把被排序的数据类型改为字节数据,程序该怎么修改?

微机原理与接口技术-NUAA-课内实验报告_第56张图片

微机原理与接口技术-NUAA-课内实验报告_第57张图片

微机原理与接口技术-NUAA-课内实验报告_第58张图片

4、如果要把数据按16进制显示,该怎么修改程序?

mov bx,16

loop1:

cwd

div bx

push dx

;把每次除以16的余数压入堆栈,并记录数据位个数

inc pushcount

cmp ax,0

jne loop1

把每次除以16的余数压入堆栈,其余位置坐相应修改。

总结:

1. 循环指令(Loop Until Complete)

循环指令LOOP的一般格式: LOOP 标号

循环指令的功能描述:

1) (CX)=(CX)-1或(ECX)=(ECX)-1;

2)如果(CX)≠0或(ECX)≠0, 转向“标号”所指向的指令,否则,终止循环,执行该指令下面的指令。

微机原理与接口技术-NUAA-课内实验报告_第59张图片

2.返回指令(RET)

1) 在近类型的子程序中,返回指令RET是近返回,其功能是把栈顶之值弹出到指令指针寄存器IP中,SP会被加2(如图所示);

近返回指令的出栈操作示意图

微机原理与接口技术-NUAA-课内实验报告_第60张图片

2)在远类型的子程序中,返回指令RET是远返回,其功能是:先弹出栈顶之值到IP

中,再弹出栈顶之值到CS之中,SP总共会被加4

远返回指令的出栈操作示意图

微机原理与接口技术-NUAA-课内实验报告_第61张图片

3. 寄存器的保护与恢复

在子程序中,保存和恢复寄存器内容的主要方法是:在子程序的开始把它所用到的

寄存器压进栈,在返回前,再把它们弹出栈。这样编写的好处是该子程序可以被任何其

它程序来调用。在调用指令前,不需要保存寄存器,在调用指令后,也无需恢复寄存器。

实验八 、结构的应用及宏程序设计

实验内容

定义一个保存学生成绩单的结构,并且计算总分,最后按照总分的从高到低顺序显示成绩单,要求名次和成绩及总分都按照10进制显示输出。

实验步骤

1、输入下列程序,汇编并链接通过。

.model small

.stack

.data

student struct ;结构定义

snumber dw 0 ;编号,代表名次

sid db 15 dup('$') ;学号

sname db 20 dup('$') ;最多20位,姓名

score1 dw 0 ;成绩1

score2 dw 0 ;成绩2

score3 dw 0 ;成绩3

ttscore dw 0 ;总分

student ends



scoresheet student<1,'computer01$','zhangsan$',90,70,60,>

student<2,'computer02$','lisi $',100,0,100,>

student<3,'computer03$','wanger $',95,66,88,>

student<4,'computer04$','zhuwu $',120,90,99,>

student <5,'computer05$','john $',90,80,95,>

;成绩单长度可以任意,这里只列举5个纪录

sheetlength=($-scoresheet)/(typescoresheet) ;成绩单长度,即纪录条数

pushcount db 0 ;临时存放被显示数据的位数

.code

nextline macro ;显示换行的宏定义

mov ah,2

mov dl,10

int 21h

mov ah,2

mov dl,13

int 21h

endm

dispone macro one ;显示一个字符的有参数宏定义

mov ah,2

mov dl,one

int 21h

endm

dispscore macro ;按照10进制显示内存单元数据的宏定义

local loop1,print ;局部标号定义

push bx

push dx

mov bl,10

loop1: div bl

mov dl,ah

mov ah,0

push dx ;把每次除以10的余数压入堆栈,并记录数据位个数

inc pushcount

cmp al,0

jne loop1

print: pop dx ;从堆栈弹出每位数据并显示

add dl,30h

dispone dl

dec pushcount

jnz print

dispone 20h ;显示一个空格

pop dx

pop bx

endm



.startup

lea bx, scoresheet

mov cx,sheetlength

.repeat ;统计每条记录的总分

mov ax,[bx].student.score1

add ax,[bx].student.score2

add ax,[bx].student.score3

mov [bx].student.ttscore,ax

mov [bx].student.snumber, 0 ;名次初始化为0

add bx, type scoresheet

.untilcxz



mov dx,0 ;DX控制总循环次数,初始值为0

push dx

begin:

lea bx, scoresheet

mov cx, sheetlength

.repeat

.if [bx].student.snumber==0 ;找到第一个未排名的纪录

mov ax,[bx].student.ttscore ;总分记录在AX中

.break

.else

add bx, type scoresheet

.endif

.untilcxz



mov bx,offset scoresheet

mov cx,sheetlength

.repeat

.if [bx].student.snumber==0

.if [bx].student.ttscore>=ax ;逐一比较找到一个未排序的且总分最高的纪录

mov ax,[bx].student.ttscore ;AX保存最高的总分

mov di,bx ;有最高总分的记录地址保存在DI寄存器中

.endif

.endif

add bx, type scoresheet

.untilcxz



nextline ;在新的一行显示每条记录信息

mov bx,di

pop dx

inc dx

push dx

mov [bx].student.snumber,dx ;DX加1为当前名次

mov ax,dx ;显示名次

dispscore

lea dx, [bx].student.sid ;显示学号

mov ah,9

int 21h

dispone 20h

lea dx, [bx].student.sname ;显示姓名

mov ah,9

int 21h

dispone 20h

mov ax, [bx].student.score1 ;显示成绩1

dispscore

mov ax, [bx].student.score2 ;显示成绩2

dispscore

mov ax, [bx].student.score3 ;显示成绩3

dispscore

mov ax, [di].student.ttscore ;显示总分

dispscore

pop dx

.if dl==sheetlength

.exit 0

.endif

push dx

jmp begin

end

2、运行程序,可以看到显示结果如下:

1 computer04 zhuwu 12090 99 309

2 computer05 john 9080 95 265

3 computer03 wanger 95 6688 249

4 computer01 zhangsan 90 70 60 220

5 computer02 lisi 1000 100 200

各记录是按照总分的降序显示的,其中第一列为名次,最后一列为总分。

微机原理与接口技术-NUAA-课内实验报告_第62张图片

3、利用MASM工具汇编生成列表文件,并察看列表文件内容,注意其中的宏调用和展开过程,注意局部标号的重命名规律。

微机原理与接口技术-NUAA-课内实验报告_第63张图片

微机原理与接口技术-NUAA-课内实验报告_第64张图片

微机原理与接口技术-NUAA-课内实验报告_第65张图片

微机原理与接口技术-NUAA-课内实验报告_第66张图片

微机原理与接口技术-NUAA-课内实验报告_第67张图片

微机原理与接口技术-NUAA-课内实验报告_第68张图片

微机原理与接口技术-NUAA-课内实验报告_第69张图片

微机原理与接口技术-NUAA-课内实验报告_第70张图片

微机原理与接口技术-NUAA-课内实验报告_第71张图片

4、修改结构的定义,在其中再增加两门成绩,并适当修改程序,重新观察运行结果。

微机原理与接口技术-NUAA-课内实验报告_第72张图片

4.附上修改后源码:

.model small

.stack

.data

student struct ;结构定义

snumber dw 0 ;编号,代表名次

sid db 15 dup('$') ;学号

sname db 20 dup('$') ;最多20位,姓名

score1 dw 0 ;成绩1

score2 dw 0 ;成绩2

score3 dw 0 ;成绩3

score4 dw 0 ;成绩4

score5 dw 0 ;成绩5

ttscore dw 0 ;总分

student ends



scoresheet student <1,'computer01$','zhangsen$',100,100,100,100,100,>

student <2,'computer02$','daxian $',100,0,100,95,80,>

student <3,'computer03$','wanggang $',95,66,88,90,86,>

student <4,'computer04$','zhuwu $',120,90,99,91,80,>

student <5,'computer05$','john $',90,80,95,90,84,>

;成绩单长度可以任意,这里只列举5个纪录

sheetlength=($-scoresheet)/(type scoresheet) ;成绩单长度,即纪录条数

pushcount db 0 ;临时存放被显示数据的位数

.code

nextline macro ;显示换行的宏定义

mov ah,2

mov dl,10

int 21h

mov ah,2

mov dl,13

int 21h

endm

dispone macro one ;显示一个字符的有参数宏定义

mov ah,2

mov dl,one

int 21h

endm

dispscore macro ;按照10进制显示内存单元数据的宏定义

local loop1,print ;局部标号定义

push bx

push dx

mov bl,10

loop1: div bl

mov dl,ah

mov ah,0

push dx ;把每次除以10的余数压入堆栈,并记录数据位个数

inc pushcount

cmp al,0

jne loop1

print: pop dx ;从堆栈弹出每位数据并显示

add dl,30h

dispone dl

dec pushcount

jnz print

dispone 20h ;显示一个空格

pop dx

pop bx

endm



.startup

leabx, scoresheet

movcx,sheetlength

.repeat ;统计每条记录的总分

movax,[bx].student.score1

addax,[bx].student.score2

addax,[bx].student.score3

mov[bx].student.ttscore,ax

mov[bx].student.snumber, 0 ;名次初始化为0

addbx, type scoresheet

.untilcxz



movdx,0 ;DX控制总循环次数,初始值为0

pushdx

begin:

leabx, scoresheet

movcx, sheetlength

.repeat

.if[bx].student.snumber==0 ;找到第一个未排名的纪录

mov ax,[bx].student.ttscore ;总分记录在AX中

.break

.else

add bx, type scoresheet

.endif

.untilcxz



movbx,offset scoresheet

movcx,sheetlength

.repeat

.if[bx].student.snumber==0

.if [bx].student.ttscore>=ax ;逐一比较找到一个未排序的且总分最高的纪录

mov ax,[bx].student.ttscore ;AX保存最高的总分

mov di,bx ;有最高总分的记录地址保存在DI寄存器中

.endif

.endif

add bx, type scoresheet

.untilcxz



nextline ;在新的一行显示每条记录信息

movbx,di

popdx

incdx

push dx

mov[bx].student.snumber,dx ;DX加1为当前名次

movax,dx ;显示名次

dispscore

leadx, [bx].student.sid ;显示学号

movah,9

int21h

dispone 20h

leadx, [bx].student.sname ;显示姓名

movah,9

int21h

dispone 20h

movax, [bx].student.score1 ;显示成绩1

dispscore

movax, [bx].student.score2 ;显示成绩2

dispscore

movax, [bx].student.score3 ;显示成绩3

dispscore

movax, [bx].student.score4 ;显示成绩3

dispscore



movax, [bx].student.score5 ;显示成绩3

dispscore



movax, [di].student.ttscore ;显示总分

dispscore

popdx

.ifdl==sheetlength

.exit 0

.endif

push dx

jmpbegin

end

实验附录 汇编语言常用出错信息

汇编程序在对源程序的汇编过程中,若检查出某语句有语法错误,随时在屏幕上给出出错信息,以便操作人员即时查找错误,给予更正。MASM出错信息格式如下:

错误编号

错误描述

0

Block nesting error

嵌套出错。嵌套的过程、段、结构、宏指令或重复块等非正常结束。例如在嵌套语句中有外层的结束语句,而无内层的结束语局

1

Extra characters on line

一语句行有多余字符,可能是语句中给出的参数太多

2

Internal error-Register already defined

这是一个内部错误。如出现该错误,请记下发生错误的条件,并使用Product Assistance Request 表与Microsoft公司联系

3

Unkown type specifer

未知的类型说明符。例如类型字符拼错,把BYTE写成BIT,NEAR写成NAER等

4

Redefinition of symbol

符号重定义。同一标识符在两个位置上定义。在汇编第一遍扫描时,在这个标识符的第二个定义位置上给出这个错误

5

Symbol is multidefined

符号多重定义。同一标识符在两个位置上定义。在汇编第二遍扫描时,每当遇到这个标识符都给出这个错误

6

Phase error between passes

两次扫描间的遍错。一个标号在二次扫描时得到不同的地址值,就会给出这种错误。若在启动MASM时使用/D任选项,产生第一遍扫描的列表文件,它可帮助你查找这种错误

7

Already had ELSE clause

已有ELSE语句。在一个条件块里使用多于一个的ELSE语句

8

Must be in conditional block

没有在条件块里。通常是有ENDIF或ELSE语句,而无IF 语句

9

Symbol not defined

符号未定义,在程序中引用了未定义的标识符

10

Syntax error

语法错误。不是汇编程序所能识别的一个语句

11

Type illegal in context

指定非法类型。例如对一个过程指定BYTE类型,而不是NEAR或FAR

12

Group name must be unique

组名应是唯一的。作为组名的符号作为其他符号使用

13

Must be declared during pass 1

必须在第一遍扫描期间定义。在第一遍扫描期间,如一个符号在未定义前就引用,就会出现这种错误。

14

Illegal public declaration

一个标识符被非法的指定为PUBLIC类型

15

Symbol already defferent kind

重新定义一个符号为不同种类符号。例如一个段名重新被当作变量名定义使用

16

Reserved word used as symbol

把汇编语言规定的保留字作标识符使用

17

Forward reference illegal

非法的向前引用。在第一遍扫描期间,引用一个未定义符号。

18

Operand must be register

操作数位置上应是寄存器,但出现了标识符

19

Wrong type of register

使用寄存器出错

20

Operand must be segment or group

应该给出一个段名或组名。例如ASSUME语句中应为某段寄存器和指定一个段名或组名,而不应是别的标号或变量名等

21

Symbol has no segment

不知道标识符的段属性

22

Operand must be type specifier

操作数应给出类型说明,如NEAR、FAR、BYTE等

23

Symbol alread defined locally

以被指定为内部的标识符,企图在EXTRN语句中又定义外部标识

24

Segment paraneters are changed

段参数被改变。如同一标识符定义在不同段内

25

Improper align/combin type

段定义时的定位类型/组合类型使用出错

26

Reference to multidefined symbol

指令引用了多重定义的标识符

27

Operand expected

需要一个操作数,只有操作符

28

Operator expected

需要一个操作符,但只有操作数

29

Divdsion by 0 or overflow

除以0或溢出

30

Negative shift count\

运算符SHL或SHR的移位表达式值为负数

31

Operand type must match

操作数类型不匹配。双操作数指令的两个操作数长度不一致,一个是字节,一个是字

32

Illegal use of external

外部符号使用出错

33

Must be record field name

应为记录字段名。在记录字段名位置上出现另外的符号

34

Must be record name or field name

应为记录名或记录字段名。在记录名或记录字段名位置上出现另外的符号

35

Operand must be size

应指明操作数的长度(如BYTE、WORD等)。通常使用PTR运算即可改正

36

Must be variable,label,or constant

应该是变量名、标号、或常数的位置上出现了其他信息

37

Must be stucture field name

应该为结构字段名。在结构字段名位置上出现了另外的符号

38

Lefe operand must segment

操作数的左边应该是段的信息。如设DA1、DA2均是变量名,下列语句就是错误的:“MOV AX,DA1:DA2”。DA1位置上应使用某段寄存器名

39

One operand must constant

操作数必须是常数。

40

Operand must be in same segment or one constant

“—”运算符用错。例如“MOV AL,—VAR”,其中VAR是变量名,应有一常数参加运算。又如两个不同段的变量名相减出错

41

Normal type operand expected

要求给出一个正常的操作数。

42

Constant expected

要求给出一个常数。

43

Operand must have segment

运算符SEG用错。

44

Must be associated with data

在必须与数据段有关的位置上出现了代码段有关的项

45

Must be associated with code

在必须与代码段有关的位置上出现了数据段有关的项

46

Multiple base registers

同时使用了多个基址寄存器。如“MOV AX ,[SI][BP]”

47

Multiple index registers

同时使用了多个变址寄存器。如“MOV AX ,[SI][DI]”

48

Must be index or base register

指令仅要求使用基址寄存器或变址寄存器,而不能使用其他寄存器。

49

Illegal use of register

非法使用寄存器出错

50

Value is out of range

数值太大,超过允许值。例如:“MOV AL ,100H”

51

Operand not in current CS ASSUME segment

操作数不在当前代码段内。通常指转移指令的目标地址不在当前CS段内

52

Improper operand type

操作数类型使用不当。例如:“MOV VAR1,VAR2”。两个操作数均为存储器操作数,不能汇编出目标代码

53

Jump out of range by %ld byte

条件转移指令跳转范围超过-128~+127个字节。出错厂、信息同时给出超过的字节数

54

Index displacement must be constant

变址寻址的位移量必须是常数

55

Illegal register value

非法的寄存器值。目标代码中表达寄存器的值超过7

56

Immediate mode illegal

不允许使用立即数寻址。例如“MOV DS,CODE”其中CODE是段名,不能把段名作为立即数传送给段寄存器DS

57

Illegal size for operand

使用操作数大小(字节数)出错。例如:使用双字的存储器操作数

58

Byte register illegal

要求用字寄存器的指令使用了字节寄存器。如PUSH,POP指令的操作数寄存器必须是字寄存器

59

Illegal uer of CS register

指令中错误使用了段寄存器CS。如:“MOV CS,AX”CS不能做目的操作数

60

Must be accumulator register

要求用AX或AL的位置上使用可其他寄存器。如IN,OUT指令必须使用累加器AX或AL

61

Improper uer of segment register

不允许使用段寄存器的位置上使用了段寄存器。如“SHL DS,1”

62

Missing or unreachable CS

试图跳转去执行一个CS达不到的标号。通常是指缺少ASSUME语句中CS与代码段相关联

63

Operand combination illegal

双操作数指令中两个操作数组合出错

64

Near JMP/CALL to different CS

试图用NEAR属性的转移指令跳转到不在当前段的一个地址

65

Label cannot have segment override

段前缀使用出错

66

Must have instuction agter prefix

在重复前缀REP,REPE,REPNE后面必须有指令

67

Cannot override ES for destination

串操作指令中目的操作数不能用其他段寄存器替代ES

68

Cannot address with srgment register

指令中寻找一个操作数,但ASSUME语句中未指明哪个段寄存器与该操作数所在段有关联

69

Must be in segment block

指令语句没有在段内

70

Cannot use EVEN or ALIGN with byte alignment

在段定义伪指令的定位类型中选用BYTE,这时不能使用EVEN或ALIGN伪指令

71

Forward needs override or FAR

转移指令的目标没有在源程序中说明为FAR属性,可用PTR指定

72

Illegal value for DUP count

操作符DUP前的重复次数是非法的或未定义

73

Symbol id already external

在模块内试图定义的符号,它已在外部符号伪指令中说明

74

DUP nesting too deep

操作数DUP的嵌套太深

75

Illegak use of undefinde operand(?)

不定操作符“?”使用不当。例如“DB 10H DUP(?+2)”

76

Too many valer for struc or record initialization

在定义结构变量或记录变量时,初始值太多

77

Angle brackets requored around initialized list

定义结构体变量时,初始值未用尖括号(<>)括起来

78

Directive illegal structure

在结构体定义中的伪指令使用不当。结构定义中的伪指令语句仅二种:分号(;)开始的注释语句和用DB、DW等数据定义伪指令语句

79

Override with DUP illegal

在结构变量初始值表中使用DUP操作符出错

80

Field cannot be overridden

在定义结构变量语句中试图对一个不允许修改的字段设置初值

81

Override id of wrong type

在定义结构变量语句中设置初值时类型出错

82

Circular chain of EQU aliases

用等值语句定义的符号名,最后又返回指向它自己。如:

A EQU B

B EQU A

83

Cannot emulate cooprocessor opcode 仿真器不能支持的8087协处理器操作码

84

End of file,not END directive 源程序文件无END文件

85

Data emitted with no segment 语句数据没有在段内

全文知识点总结:

【debug命令】总结:

【cpu内部寄存器】状态的解释:

微机原理与接口技术-NUAA-课内实验报告_第73张图片

微机原理与接口技术-NUAA-课内实验报告_第74张图片

【DAA】指令:

32 位模式下,ADD 或 ADC 指令在 AL 中生成二进制和数,DAA(加法后的十进制调整)指令将和数转换为压缩十进制格式。比如,下述指令执行压缩十进制数 35 加 48。二进制和数(7Dh)被调整为 83h,即 35 和 48 的压缩十进制和数

mov al, 35h

add al, 48h ; AL = 7Dh

daa ; AL = 83h (调整后的结果)

【PTR】说明

1.为了使指令中存储单元操作数具有明确的属性,我们可以使用强制属性操作符PTR.其

一般格式为: .

数据类型PTR 地址表达式

其中:数据类型是前面所学的各种数据类型,常用的数据类型有: BYTE、WORD. DWORD、NEAR和FAR等;在指令中用操作符PTR强制后,不管其后的地址表达式原数据类型是什么,在本指令中就以PTR前面的类型为准。该强制属性只在本指令有效,是一种临时性的属性,

它不会改变原内存单元的定义属性。

【汇编】mov [1000],bx :immediate operand not allowed

微机原理与接口技术-NUAA-课内实验报告_第75张图片

【DOS(INT 21H)功能调用表】(部分)

AH

功能

调用参数

返回参数

01

键盘输入并回显

 

AL=输入字符

02

显示输出

DL=输出字符

 

09

显示字符串

DS:DX=串地址

'$'结束字符串

 

0A

键盘输入到缓冲区

DS:DX=缓冲区首地址

(DS:DX)=缓冲区最大字符数

(DS:DX+1)=实际输入的字符数

【汇编语言---跳转指令】ja、jb、jl

JE ;等于则跳转

JNE ;不等于则跳转

JZ ;为 0 则跳转

JNZ ;不为 0 则跳转

JS ;为负则跳转

JNS ;不为负则跳转

JC ;进位则跳转

JNC ;不进位则跳转

JO ;溢出则跳转

JNO ;不溢出则跳转

JA ;无符号大于则跳转

JNA ;无符号不大于则跳转

JAE ;无符号大于等于则跳转

JNAE;无符号不大于等于则跳转

JG ;有符号大于则跳转

JNG ;有符号不大于则跳转

JGE ;有符号大于等于则跳转

JNGE;有符号不大于等于则跳转

JB ;无符号小于则跳转

JNB ;无符号不小于则跳转

JBE ;无符号小于等于则跳转

JNBE;无符号不小于等于则跳转

JL ;有符号小于则跳转

JNL ;有符号不小于则跳转

JLE ;有符号小于等于则跳转

JNLE;有符号不小于等于则跳转

JP ;奇偶位置位则跳转

JNP ;奇偶位清除则跳转

JPE ;奇偶位相等则跳转

JPO ;奇偶位不等则跳转

【汇编简化段】定义

 1. 存储模型伪指令

存储模型

功 能

适用操作系统

Tiny (微型)

所有数据和代码都放在一个段内,其访问都为NEAR型,整个程序≤64K,并会产生.COM文件。

MS-DOS

Small (小型)

所有代码在一个64KB的段内,所有数据在另一个64KB的段内(包括数据段,堆栈段和附加段)。

MS-DOS

Windows

Medium (中型)

所有代码>64K时可放在多个代码段中,转移或调用可为FAR型。所有数据限在一个段内,DS可保持不变。

MS-DOS

Windows

Compact(紧凑型)

所有代码限在一个段内,转移或调用可为NEAR型。数据>64K时,可放在多个段中。

MS-DOS

Windows

Large (大型)

允许代码段和数据段都可超过64K,被放置在有多个段内,所以数据和代码都是远访问。

MS-DOS

Windows

Huge (巨型)

单个数据项可以超过64K,其它同Large模型

MS-DOS

Windows

Flat (平展型)

所有代码和数据放置在一个段中,但段地址是32位的,所以整个程序可为4GB。MASM 6.0支持该模型。

OS/2

WindowsNT

  注意:Small 模型是一般应用程序最常用的一种模型,因为只有一个代码段和一个数据段,所以数据和代码都是近访问的。这种模型的数据段是指数据段、堆栈段和附加段的总和。

  在DOS下用汇编语言编程时,可根据程序的不同特点选择前6种模型,一般可以选用SMALL模型。另外,TINY模型将产生COM程序,其他模型产生EXE程序。FLAT模型只能运行在32位x86 CPU上,DOS下不允许使用这种模型。当与高级语言混合编程时,两者的存储模型应当一致。

2. 简化的段伪指令

简化段伪指令

功 能

注释

.CODE [段名]

创建一个代码段

段名为可选项,如不给出段名,则采用默认段名。对于多个代码段的模型,则应为每个代码段指定段名。

.DATA

创建一个数据段

段名是:_DATA

.DATA?

创建无初值变量的数据段

段名是:_BSS

.FARDATA [段名]

建立有初值的远调用数据段

可指定段名,如不指定,则将以FAR_DATA命名。

.FARDATA? [段名]

建立无初值的远调用数据段

可指定段名,如不指定,则将以FAR_BSS命名。

.CONST

建立只读的常量数据段

段名是:CONST

.STACK [大小]

创建一个堆栈段并指定堆栈段大小

段名是:stack。如不指定堆栈段大小,则缺省值为1KB

3.与简化段定义有关的预定义符号

  下面的举例说明预定义符号的使用方法。在完整的段定义情况下,在程序的一开始,需要用段名 装入数据段寄存器,如例4.1中的

        mov  ax,data_seg1

  mov   ds,ax

  若用简化段定义,则数据段只用.data来定义,而并未给出段名,此时可用

  mov   ax,@data

  mov   ds,ax

  这里预定义符号@data就给出了数据段的段名。

你可能感兴趣的:(#,NUAA-微机原理接口与技术,经验分享,汇编)