大家好,欢迎来到IT知识分享网。
复习大纲参考
① 题型:选择题、填空题、简答题、程序分析、代码填空、编程题,论述题。
② 编程程序分析主要在六七八章,注意上课讲的例子、课后作业、实验。
③ 一、二、四、五章一些关键的东西。名词、对应的代码形式等。
重点关注一下上课讲的例题和习题。
下方图片中左半图是书本上复习的重要内容,右半图是重要的习题。
第一章 计算机基本原理
1.2 计算机中数据表示
1.2.1 不同进位计数制及相互转换
- 二进制+B
- 十进制+D
- 十六进制+F
转换
- 二进制、十六进制转换为十进制:对应位的数值乘以对应的权之和——例:5FH=5*16^1+15*16^0=95D
- 十进制转换为二进制:
- 降幂法(适于数值不大):写出小于此数的二进制权值后求和——例:13.5H=8+4+2+1+0.5=1101.1B
- 除2求余法:不断除以2,记下余数,直到商为0为止——例:13D=1101B
- 十进制转换为十六进制:
同2.
- 二进制数转换十六进制
一位十六进制数由四位二进制数组成
1.2.2 二进制和十六进制运算
运算规则
- 二进制
-
- 加法:0+0=0;0+1=1;1+1=0(进位1)
- 乘法:0*0=0;0*1=0;1*1=1
- 十六进制
-
- 加减符合逢16进一
- 可以先转换为二/十进制运算,最后结果转换十六进制
1.2.3 带符号数的补码表示
带符号数:最高位是符号位,一般规定正数符号位0,负数符号位1
机器数:一个数连同其符号在内数值化表示。机器数表示可以用不同码制,常用的有原码、补码、反码。
补码
- 正数:符号位+绝对值表示,即:数最高位0,其余部分为该数绝对值
- 负数:对其正数(符号位此时为0)各位取反(符号位一样取反),最低位加1(求补运算:二进制取反加1)
1.2.4 补码加减
- 加法规则:[X+Y]补 = [X]补 + [Y]补
- 减法规则:[X-Y]补 = [X]补 + [-Y]补
- 不必考虑数正负,符号位参与运算即可
- 从最高有效位向高位的进位由于机器字长的限制而自动丢弃
1.2.5 无符号数表示
把最高有效位也作为数值
U1习题
1.3 十进制转换为二进制、十六进制
(1)67 (2)34 (3)254 (4)123
答:
(1)6710=2 6710=4316
(2)3410=2 3410=2216
(3)25410=2 25410=FE16
(4)12310=2 12310=7B16
1.4 下列十进制数转换为二进制数和十六进制数
(1)0 (2) (3)
答:
(1)02=10910 02=6D16
(2)2=17810 2=B216
(3)2=6310 2=3F16
1.5 作下列十六进制的运算
(1)5A+64 (2)86-49 (3)123-9A (4)43x2B
答:
(1)BE16
(2)3D16
(3)8916
(4)B4116
1.6 根据补码定义把下列十进制表示为8位二进制补码
(1)64 (2)-24
答:
(1)02
(2)2
第二章 计算机基本原理
2.2 存储器
2.2.1 16位结构的CPU
8086是16位结构的CPU,具有以下4方面结构特征:
- 数据总线为16位
- 运算器一次最多可以处理16位的数据
- 寄存器的最大宽度为16位
- 寄存器和运算器之间的通路为16位
2.2.2 存储器
基本存储单元
- 最小单元:一个二进制位bit
- 字节byte:8位二进制位
- 字:2个字节;双字:2个字
- 80×86内存储器以字节为基本存储单位/基本存储单元——>对内存的读写至少一个字节
内存中字的存储
- 字节:byte,一个字节由8位bit组成,可存放8位寄存器中
- 字:word,一个字由两个字节组成,分别称为高位字节和低位字节
当处理16为数据,需要以字为单位表示,需要两个字节单元存放,并且规定:低字节数据存放于低地址单元,高字节数据存放于高地址单元。
- 8086CPU有20位地址总线,可以传送20位地址,即物理地址有20位
- 8086CPU又是16位结构,如何用16位结构实现20位的物理地址的传送和内存寻址?8086采用内部使用2个16位地址合成来形成一个20位物理地址的方法
2.2.3 存储器分段
分段
内存无分段,段的划分来着CPU
内存单元的物理地址=段地址*16+偏移地址
段类型
逻辑段分为:代码段、数据段、附加段、堆栈段
存储器逻辑分段类型:
- 代码段——用于存放指令,代码段段基址存放段寄存器CS
- 数据段——用于存放数据,数据段段基址存放段寄存器DS
- 附加段——用于辅助存放数据,附加段段基址存放段寄存器ES
- 堆栈段——重要数据结构,堆栈段段基址存放段寄存器SS
2.2.4 逻辑地址
逻辑地址分为段地址、偏移地址
8086CPU内部寄存器只有16位,无法存放20位地址,故计算公式:
段地址*16(二进制,向左移动四位)+偏移地址=物理地址
例:段基址1896H,偏移地址1655H。其物理地址?
18960H+1655H=19FB5H
段重叠:数据段紧接着附加段的最后单元存放,而不必在附加段的64kb最大区域之外设置其他段
2.2.5 CPU对内存的读写
CPU进行数据读写必须和外部进行信息交互:
- 存储单元的地址(地址信息)
- 器件的选择,读写命令(控制信息)
- 读写数据(数据信息)
总线逻辑上分三类:地址总线、控制总线、数据总线
2.3 CPU中的寄存器
2.3.1 寄存器
8086CPU所有寄存器都是16位,可以存放两个字节。
- 通用寄存器(存放一般性数据/存放地址)
- AX:累加器,运算较多时运用,有些指令规定必须使用它(add)
- BX:基址寄存器,除了存放数据,还经常存放一段内存的起始偏移地址(base)
- CX:计数寄存器,除了存放数据,还经常存放重复操作的次数(count)
- DX:数据寄存器,除了存放数据,还有时存放32位数据的高16位(data)
- 这四个寄存器都可以分为两个独立的8位寄存器使用:AH,AL,BH,BL,CH,CL,DH,DL例:AX:15-0 中 AH:15-8(高位),AL:7-0(低位)
- 地址寄存器(存放数据所在偏移地址,存放数据)
- SP:堆栈指针,是专用的寄存器,存放堆栈栈顶的偏移地址
- BP:基址指针,用来存放内存中数据的偏移地址
- SI:源变址寄存器,用来存放内存中源数据区的偏移地址,所谓变址寄存器,指某些指令下可以自动递增/递减其中的值
- 段寄存器
- CS:代码段寄存器,存放当前正在执行的程序段的段基址
- SS:堆栈段寄存器,存放堆栈段的段基址
- DS:数据段寄存器,存放数据段的段基址
- ES:附加段寄存器,存放另一个数据段的段基址
- 指令指针寄存器
- IP,存放即将执行指令的偏移地址
- 标志寄存器
FLAGS:存放CPU两类标志
a.状态标志:反应处理器当前的状态,有无溢出/进位。状态标志:CF,PF,AF,ZF,SF,OF
b.控制标志:控制CPU的工作方式,是否响应可屏蔽中断。控制标志:TF,IF,DF
OF:运算结果超出可以表示的范围
DF:控制地址变化方向,0为加法修改地址,1为减法
IF:1允许处理器响应可屏蔽中断请求信号,即开中断;0为不允许,即关中断
SF:1表示运算结果最高位为‘1’。对于有符号数,OF=0时,SF=1表示运算结果为负,SF=0为非负;OF=1由于结果错误,所以符号位也和正确值相反,。对于无符号数运算,无意义。
AF:字操作时,低字节向高字节进位时,AF=1,否则为0
PF:1表示运算结果低8为有偶数个1,0则表示奇数个
CF:1表示无符号数加法有进位/减法有借位,需要对高位补充处理;0表示无进位/借位
状态标志在每次运算后自动产生,控制标志值由指令设置
2.3.2 CS和IP
8086CPU工作过程
- 从CS:IP指向的内存单元读取指令,读取的指令进入指令缓冲器
- IP=IP+所读取指令的长度,从而指向下一条指令
- 执行指令,转到步骤1,重复过程
2.3.3 堆栈
数据在堆栈区存放时,必须以字存入,每次存入一个字,后存入的数据依次放入栈的低地址单元中。栈指针SP每次-2,由栈指针SP指出当前栈顶的位置,数据存取时采用先进后出的方式
2.4 外部设备和接口
接口内有若干寄存器进行统一编号,给每个寄存器规定一个端口号,CPU可以通过端口地址区分和访问不同外设。接口中寄存器(端口)分为以下三类:
- 数据端口:存放要在外设和主机间传送的数据,实际上起数据缓冲作用。端口传送方向可输入/输出
- 控制端口:传递CPU对外部设备的控制信号。信号由CPU发出,传递到接口内的控制端口,后发送到外部设备。传送方向对于CPU总是输出
- 状态端口:协调外设和主机的同步。外设工作状态在此得到反应。传送方向对于CPU总是输入
CPU与I/O接口中端口的信息传输也都是通过数据总线进行。
2.5 32位80x86CPU工作模式
- 实模式:特点-为了兼容16位机的特点。20位的物理地址由16位段地址、16位偏移地址组成,CPU地址线只能低20位线有效。可以使用32为寄存器和32位操作数。MS DOS只能在实模式下运行
- 保护模式:是32位8060CPU主要工作模式,特点-全部地址线参与寻址。程序访问内存是用逻辑地址(虚拟地址)。虚拟地址由16位段选择符和32位偏移地址得到,16位的段选择符存放在段寄存器中,应用程序不可更改,操作系统决定。实际物理地址-由16位段选择符对应的32位段基址和32位偏移地址组成。
- 虚拟8086模式:生成多个虚拟8680CPU,以便运行实模式下的8086程序
Windows下在打开的DOS窗口运行一个DOS应用程序,则该程序运行在虚拟8086模式,这是我们常用的运行模式。
U2习题
2.3 写出每条汇编指令执行后的相关寄存器值
Mov ax,1345H ax= 1345H
Mov ah,24H ax= 2445H
Mov al,45H ax= 2445H
Mov bx,3412H bx= 3412H
Mov al,bh ax= 2434H
Mov ah,bl ax= 1234H
2.4 实模式下,写出段地址和偏移地址为1234:2002、1430:0042、FF00:0FFF的物理地址
答:
(1)1234:2002 = 14342H
(2)1430:0042 = 14342H
(3)FF00:0FFF = FFFFFH
2.5 下列各数均为十进制数,请采用8位二进制补码运算,并回答标志寄存器FLAGS中CF、OF值,运算结果的十进制数为?如果采用16位二进制补码运算,其结果所代表十进制数为?FLAGS中CF、OF值?
(1)85+69 (2)85+(-69)(3)85-(-69)(4)85-(69)
答:85=55H,69=45H,-69=BBH,
8位二进制补码运算:
(1)85+69 =55H+45H=9AH=154, CF=0,OF=1
(2)85+(-69)=55H+BBH=10H=16,CF=1,OF=0
(3)85-(-69)=55H-BBH=9AH=154,CF=1,OF=1
(4)85-(69)=55H-45H=10H=16,CF=0,OF=0
16位二进制补码运算:
85=0055H,69=0045H,-69=0FFBBH,
(1)85+69 =0055H+0045H=009AH=154, CF=0,OF=0
(2)85+(-69)=0055H+0FFBBH=0010H=16,CF=1,OF=0
(3)85-(-69)=0055H-0FFBBH=009AH=154,CF=1,OF=0
(4)85-(69)=0055H-0045H=0010H=16,CF=0,OF=0
2.6 给定段地址为0001H,仅通过变化偏移地址寻址,CPU的寻址范围:0010H 到 1FFFH
答:
偏移地址的范围从 0000H 到 FFFFH(因为 16 位的偏移地址可以表示从 0 到 216−1 的所有值)。
计算物理地址的范围: 物理地址=(段地址×16)+偏移地址物理地址=(段地址×16)+偏移地址
物理地址范围=(0001H×16)+0000H 到 (0001H×16)+FFFFH
物理地址范围=0010H 到 1FFFH
2.7 有一数据存放在内存20000H单元中,现给定段地址为SA,若想用偏移地址寻址到此单元,则SA应满足的条件:最小为1001H,最大为2000H
答:
20000H-0000 再挪位
20000H-FFFF 再挪位,验证后不能到达20000H,移位后1001H满足
2.8 已知8086系统某存储单元物理地址为:52506H,你认为段基址的最大值、最小值分别?8086最多可以有多少个不同的段基址?
答:
52506 = 5250:0006 段基址最大值5250
52506 = 4251:FFF6 段基址最小值4251
2.9 从物理地址为00100H开始到00103H单元中顺序存放的数据为:12H、34H、56H、78H。请画出数据存放示意图。
(1)写出地址00101H字节单元内容
(2)写出地址00102H字节单元内容
答:
(1)34H
(2)56H
注意是字单元/字节单元——字单元为两个字节单元,且高地址在前
第四章 操作数的寻址方式
4.1 立即寻址方式
立即数:所要找的操作数直接写在指令中
注意:
- 立即寻址方式只能用于源操作数字段
- 立即数的类型必须与目的操作数的类型一致
目的操作数是字节,立即数也必须是字节,或者两者都是字
立即寻址方式的用途:用于直接指定一个常数送给寄存器
MOV AL,6H
MOV AX,12AFH
4.2 寄存器寻址方式
寄存器寻址方式:操作数就是寄存器中的值,指令中给出寄存器名
对于16位操作数-寄存器:AX,BX,CX,DX,SI,DI,SP,BP
对于8位操作数-寄存器:AH,AL,BH,BL,CH,CL,DH,DL
寄存器寻址方式的用途:用于指定2个寄存器作为操作数
以上立即寻址方式和寄存器寻址方式的指令在执行时,都无需到存储器寻找操作数
以下操作数都在存储器中,偏移地址称为有效地址,称为EA
MOV AX,BX
MOV AX,CX
4.3 直接寻址方式
直接寻址方式:操作数的有效地址EA就在指令中(适用于处理单个变量)
操作数物理地址
DSx10H+EA
存储单元看作变量,存储单元的名字为变量名,内容为变量值
- 存储器读操作:如MOV AX,DS:[2000H],表示从数据段2000H单元读出
- 存储器写操作:如MOV DS:[2000H],AX,把MOV指令的目的操作数变为存储单元,源操作数变为CPU寄存器
- 符号地址:如MOV AX,[VALUE]
- 段前缀:操作数段地址默认为数据段,若操作数在其他段存放,称为段超越,需要在段超越前缀中指出
MOV AX,DS:[2000H]
MOV AX,DS:[VALUE]
4.4 寄存器间接寻址方式
寄存器间接寻址:操作数的有效地址EA就在寄存器中
默认搭配:
- DS:BX
- DS:SI
- DS:DI
- SS:BP
MOV AX,[BX]
MOV DX,[BP]
MOV ES:[DI],AX
4.5 寄存器相对寻址方式
寄存器相对寻址方式:操作数的有效地址EA是一个寄存器和位移量之和
MOV AX,TOP[SI](DS为段地址)
MOV [BX+2623H],AX (DS为段地址)
4.6 基址变址寻址方式
基址变址寻址方式:操作数的有效地址是一个基址寄存器和一个变址寄存器的内容之和
允许使用的基址寄存器:BX,BP
允许使用的变址寄存器:SI,DI
操作数物理地址
- DS x 10H +BX +DI
- DS x 10H +BX +SI
- SS x 10H + BP +SI
- SS x 10H + BP +DI
MOV AX,[BX][SI] (DS段地址)
MOV AX,ES:[BX][DI] (SS段地址)
4.7 相对基址变址寻址方式
相对基址变址寻址方式:操作数的有效地址是一个基址寄存器和一个变址寄存器以及一个位移量之和。
以下都是DS为段地址:
MOV AX,MASK[BX][SI]
MOV AX,[BX+SI].MASK
MOV AX,[MASK+BX+DI]
U4习题
4.1 段地址?有效地址?逻辑地址?物理地址?用指令举例说明
答:若DS=1500H,MOV AX,DS:[2000H]中,2000H为有效地址,1500H为段地址,物理地址为15000H+2000H=17000H,逻辑地址为2000H
4.2 指出以下指令的寻址方式,array为变量
(1)MOV AX,9
(2)MOV BYTE PTR[BX],9
(3)MOV BX,[DI]
(4)MOV AX,BX
(5)MOV [SI+BX],9
(6)MOV ARRAY[BX],CX
(7)MOV AX,ARRAY+9
(8)MOV AX,ARRAY[BX+DI]
答:
(1)立即寻址
(2)寄存器间接寻址 (BTYE PTR/WORD PTR表明字节/字类型)
(3)寄存器间接寻址
(4)寄存器寻址方式
(5)基址变址寻址方式
(6)寄存器相对寻址方式
(7)直接寻址方式
(8)相对基址变址寻址方式
4.3 假定DS=1200H,SS=4400H,BX=463DH,BP=2006H,SI=6A00H,位移量D=4524H,以AX寄存器为目的操作数,试写出以下寻址方式的传送指令,并确定操作数的有效地址EA和物理地址
(1)立即寻址
(2)直接寻址
(3)使用BX的寄存器寻址;无EA
(4)使用BX的间接寻址
(5)使用BP的寄存器相对寻址
(6)基址变址寻址
(7)相对基址变址寻址答:
(1)操作数在指令中无EA
(2)直接寻址:EA=4524H,物理地址=DS:4524
(3)使用BX的寄存器寻址:无EA
(4)使用BX的间接寻址:EA=463DH,物理地址=DS:463D
(5)使用BP的寄存器相对寻址:MOV AX,[BP+4524],EA=2006+4524,物理地址=SS:EA
(6)基址变址寻址: MOV AX,[BX+SI],EA= BX+SI,物理地址=DS:EA
(7)相对基址变址寻址:MOV AX,[4524+BX+SI],EA=4524+BX+SI,物理地址=DS:EA
4.4 在数据段定义了ARRAY数组,其中依次存储了五个字数据,ARRAY的起始地址(第一个数据的地址)为24H,请用不同寻址方式的指令,把第5个字送AX寄存器,指令条数不限。
答:
(1)直接寻址
MOV AX,ARRAY+8 或MOV AX,[ARRAY+8]
(2)使用BX的间接寻址
LEA BX,ARRAY+8 ;MOV AX,[BX]
(3)使用BX的寄存器相对寻址
LEA BX,ARRAY ;MOV AX,[BX+8]
(4)基址变址寻址
LEA BX,ARRAY ; MOV SI,8 ; MOV AX,[BX+SI]
第五章 常用指令系统
- 标号:符号地址,用来表示指令在内存中的位置,以便程序中其他指令能引用该指令,通常作为转移指令的操作数,以表示转向的目标地址。当一条指令使用标号时,应加:
- 指令助记符:指令名称,指令功能的英文缩写。如MOV
- 操作数:指令要操作的数据或数据所在地址。操作数可以是寄存器,常量,变量,表达式
双操作数:左边为目的操作数DST(参与指令操作且存放操作结果);右边为源操作数SRC
- 注释:由“;”开始
5.1 数据传送指令
- 通用数据传送指令
- 累加器专用传送指令
- 地址传送指令
- 标志寄存器传送
5.1.1 通用数据传送指令
- MOV:传送
- PUSH:进栈
- POP:出栈
- XCHG:交换
双操作数指令规定:
- 源操作数与目的操作数长度必须明确且一致,同时为8/16位
- 目的操作数与源操作数不能同为存储器,不允许在两个存储单元之间直接传送数据
- 目的操作数不能为CS或IP,因为CS:IP指向的是当前要执行的指令所在的地址
- 目的操作数不可为立即数
5.1.2 累加器专用传送指令
- IN:输入
- OUT:输出
- XLAT:换码
IN
把端口号PORT或由DX指向的端口的数据输入累加器。根据端口号长度有长格式、短格式。
- 端口号范围——00~FFH—>长格式(指其机器指令长度为2字节(端口号占1字节))
- 端口号范围——0100H~0FFFFH—>短格式(指令长度为1字节(端口号放在DX寄存器中))
OUT
同IN
XLAT
格式:XLAT
操作:AL<—BX+AL
把BX+AL的值作为有效地址,取出其中的一个字节送AL
5.1.3 地址传送指令
- LEA:有效地址送寄存器
- LDS:指针送寄存器和DS
- LES:指针送寄存器和ES
LEA
把源操作数有效地址EA送到指定寄存器
LDS
把源操作数所指向的内存单元中2个字送到指定寄存器(SRC)和DS(SRC+2)
LES
把源操作数所指向的内存单元中2个字送到指定寄存器(SRC)和ES(SRC+2)
5.1.4 标志寄存器传送指令
- LAHF:标志寄存器低字节送AH寄存器
- SAHF:AH送标志寄存器
- PUSHF:标志入栈
- POPF:标志出栈
5.2 算术运算指令
5.2.1 类型扩展指令
- CBW:字节扩展成字
- CWD:字扩展成双字
CBW
默认将AL内容扩展到AX中,扩展方式为符号扩展
如果AL最高位1(负数),则CBW指令扩展时使AH=FFH
0(正数),则 AH=00H
4位有符号数(范围-8~7)1000H表示-8,1111H表示-7
CWD
如果AX最高位1(负数),则CBW指令扩展时使DH=FFFFH
0(正数),则 DH=0000H
5.2.2 加法指令
- ADD:加法
- ADC:带进位加法
- INC:加1
ADD
将源操作数和目的操作数相加,结果存在目的操作数中。加法会影响CF,OF
注意:加法结果二进制位数
MOV AL,72H
MOV AL,93H
(1)值认为无符号数时:结果为105H,需要9位二进制表示,而AL只能存放8位二进制,所以AL存入低8位数值,即05H
(2)值认为有符号数时:该数据为补码形式
72H=0B=114D=[114D]补
93H=B=[-109D]补
加法运算:
- 无符号数:CF=0表示结果正确,CF=1则错误
- 有符号数:不用考虑CF结果,考虑OF
两个有符号数的符号相同时可能发生溢出,需要根据OF判断(当运算1结果与符号相反时,OF=1)
ADC
格式:ADC DST,SRC
操作:DST<-DST+SRC+CF(运算前CF标志位的值)
为实现双精度加法,必须用两条指令分别完成低位字和高位字的加,并在高位字相加时使用ADC指令,从而把低位字相加时的进位值加进去。不论有无符合数,低位字相加无需考虑溢出,只有高位字相加所产生的CF,OF才是判断是否溢出的依据
如:数A存入DX(高位字),AX(低位字),数B存入BX(高位字),CX(低位字)
ADD AX,CX
ADC DX,BX
INC
格式:INC OPR
操作:OPR<-OPR+1
该指令不影响CF标志位
5.2.3 减法指令
- SUB:减法
- SBB:带借位减法
- DEC:减1
- NEG:求补
- CMP:比较
SUB
格式:SUB DST SRC
操作:DST<-DST-SRC
SBB
格式:SBB DST SRC
操作:DST<-DST-SRC-CF
DEC
格式:DEC OPR
操作:OPR<-OPR-1
该指令不影响CF标志位
NEG
格式:NEG OPR
操作:OPR<-(-OPR)
=求补码
CMP
格式:CMP OPR1,OPR2
操作:OPR1-OPR2
虽然做减法,但是不返回结果,只产生标志位
cmp a,b——a<b,CF=1 , a>=b,CF=0(a=b,ZF=1,否则=0)
减法运算时:两个有符号数符号相同时,无论正数或者负数,都不会发生溢出;符号相反时可能发生溢出,需要判断OF
以上加减法指令都可作字或字节运算,除了INC,DEC指令不影响CF标志外,其他指令都影响条件标志位
5.2.4 乘法指令
- MUL:无符号数乘法
- IMUL:有符号数乘法
MUL
格式:MUL SRC
操作:当操作数为字节时,AX<-AL x SRC
当操作数为字时,DX,AX<-AX x SRC
乘法运算中,目的操作数默认是累加器AX,不必在指令中写出。因为两个相乘的数需长度相同,根据SRC长度,默认参与运算是AL/AX寄存器的值。SRC可以是寄存器、变量,但不能是立即数,因为立即数长度明确。
5.2.5 除法指令
- DIV:无符号除法
- IDIV:有符号数除法
DIV
格式:DIV SRC
操作:SRC为字节,AL<-AX/SRC的商,AH<-AX/SRC的余数
SRC为字,AX<-AX/SRC的商,DX<-AX/SRC的余数
和乘法相同,除法需要考虑是否为有无符号数,而选择不同指令,SRC不能为立即数。由于除法指令字节操作需要被除数为16位,字操作要求被除数32位,因此往往需要符号扩展,使被除数长度比除数长度扩一倍。
注意:乘法不会溢出,除法会溢出
除法是字节类型:要求商8位,如果被除数的高8位绝对值≥除数的绝对值——商会溢出
除法是字类型:要求商16位,如果被除数的高16位绝对值≥除数的绝对值——商会溢出
5.2.6 BCD码的十进制调整指令
- DAA:加法的十进制调整指令
- DAS:减法的十进制调整指令
DAA
- 如果 AL 寄存器的低四位(AL 的低半字节)大于 9 或 AF=1,则AL=AL+6( AL 寄存器的低四位加上 6)。
- 如果 AL 寄存器的高四位(AL 的高半字节)大于 9 或 CF=1,则 AL=AL+60H,CF=1(AL寄存器的高四位加上 6) 。
如:AL=28H=28(BCD),BL=65H=65(BCD)
ADD AL,BL ; AL=28H+65H=8DH
DAA ; AL=AL+6H=8DH+6H=93H=93(BCD)
DAS
- 如果 AL 寄存器的低四位(AL 的低半字节)大于 9 或 AF=1,则AL=AL-6( AL 寄存器的低四位加上 6),AF=1。
- 如果 AL 寄存器的高四位(AL 的高半字节)大于 9 或 CF=1,则 AL=AL-60H,CF=1(AL寄存器的高四位加上 6) 。
5.3 逻辑与移位指令
5.3.1 逻辑指令
- AND 与
- OR 或
- NOT 非
- XOR 异或
- TEST 测试
TEST
格式:TEST OPR1,OPR2
操作:OPR1与 OPR2
说明:结果不保存,只根据结果置标志位
test a,b——结果=,ZF=1,结果符号位1,SF=1,1的奇偶性设置PF
逻辑运算指令只会对部分标志位产生影响,NOT不影响任何标志位,其他指令将使CF,OF=0,AF无定义,其他根据运算结果设置
逻辑指令除了常规逻辑运算,还可以屏蔽某些位或测试某些位或使某些位置1
5.3.2 移位指令
- SHL:逻辑左移
- SHR:逻辑右移
- SAL:算术左移
- SAR:算术右移
- ROL:循环左移
- ROR:循环右移
- RCL:带进位循环左移
- RCR:带进位循环右移
移位指令均为双操作数指令,指令格式相同,以SHL为例:
- SHL OPR,1
- SHL OPR,CL(其中CL寄存器值大于1)
OPR为寄存器或内存单元,移位次数可以是1/CL寄存器,如需移位次数大于1则可以在该移位指令前把移位次数先送CL寄存器中。
移位区别:P83
对于无符号整数:算术左/右移和逻辑左/右移一样
对于有符号正式:移位之后算术的补符号位,逻辑的补0
5.4 串操作指令
- MOVS 串传送
- CMPS 串比较
- SCAS 串扫描
- STOS 存入串
- LODS 从串取
串操作指令的重复有特定的前缀指令配合:
- REP——重复至CX=0为止,每执行一次CX-1
- REPE/REPZ——相等/为零重复;只有CX≠0且ZF=1(字节相等)时重复
- REPNE/REPNZ——不相等/不为零则重复;只有CX≠0且ZF=0(字节不等)时重复
5.4.1 MOVS串传送指令
- MOVSB:以字节为单位传送
-
- 操作:ES:DI<-DS:SI DI±1,SI±1
- MOVSW:以字为单位传送
-
- 操作:ES:DI<-DS:SI DI±2,SI±2
- MOVS DST,SRC:将源串SRC传送到目的串DST中
当方向标志DF=0时,SI,DI用+;DF=1时,SI,DI用-
- CLD:设置正向(向前,使DF=0,SI/DI自动+)
- STD:设置正=负向(向后,使DF=1,SI/DI自动-)
为实现串传送,使用串操作指令前需要:
- 数据段源串首/末地址DS/SI
- 附加段目的串首/末地址ES/DI
- 串长度送计数寄存器CX
- 方向标志DF
5.4.2 CMPS串比较指令
- CMPSB:以字节为单位传送
-
- 操作:ES:DI<-DS:SI, DI±1,SI±1
- CMPSW:以字为单位传送
-
- 操作:ES:DI<-DS:SI, DI±2,SI±2
- CMPS DST,SRC:将源串SRC传送到目的串DST中
此串操作把对应字节/字相减,不保存结果,只根据结果设置标志位
5.4.3 SCAS串扫描指令
- SCASB:以字节为单位传送
-
- 操作:AL-ES:DI,DI±1
- SCASW:以字为单位传送
-
- 操作:AX-ES:DI,DI±2,SI±2
- SCAS DST:将源串SRC传送到目的串DST中
5.4.4 STOS串存入指令
- STOSB:以字节为单位传送
-
- 操作:ES:DI<-AL, DI±1
- STOSW:以字为单位传送
-
- 操作:ES:DI<-AX,DI±2
- STOS DST
5.4.5 LODS从串中取数指令
- STOSB:以字节为单位传送
-
- 操作:AL<-DS:SI, SI±1
- STOSW:以字为单位传送
-
- 操作:AX<-DS:SI,SI±2
- STOS SRC
5.5 程序转移指令
- 无条件转移指令
- 条件转移指令
- 循环指令
- 子程序调用指令
- 中断调用指令
5.5.1 无条件转移指令与程序的可重新定位
无条件转移指令分为:
- 段内转移:转移的目标地址与本条跳转指令在同一代码段-跳转后CS不变,IP改变
- 段间转移:转移的目标地址与本条跳转指令不在同一代码段-跳转后CS也变
- 直接转移:转移的目标地址在跳转指令直接给出
- 间接转移:转移的目标地址在跳转指令间接给出
段内直接转移
格式:JMP NEAR PTR OPR
操作:IP<-IP+16位偏移量
段内间接转移
格式:JMP WORD PTR OPR
操作:IP<-EA
OPR除立即数
段间直接转移
格式:JMP FAR PTR OPR
操作:IP<-OPR的偏移地址
CS<-OPR所在段的段地址
段间间接转移
格式:JMP DWORD PTR OPR
操作:IP<-EA
CS<-EA+2
5.5.2 条件转移指令
根据上一条指令执行后所产生的标志位进行测试条件判断
具体指令P94
5.5.3 循环指令
- LOOP:循环
-
- 指令:LOOP OPR
- 测试条件:CX≠0,循环
- LOOPZ/LOOPE 为零/相等时循环
-
- 指令:LOOPZ OPR
- 测试条件:ZF=1 且CX≠0,循环
- LOOPNZ/LOOPNE 不为零/不相等时循环
-
- 指令:LOOPNZ OPR
- 测试条件:ZF=0 且CX≠0,循环
以上均:首先执行CX-1再根据条件判断是否要转移
U5习题
5.5 V 是变量,指出下列错误的指令,说出错误原因,并给出合适的修改。
(1) MOV AX,[DX]
(2) MOV DS,DATA
(3) MOV CS,AX
(4) MOV AX,DL
(5) PUSH AL
(6) ADD [BX],[DI]
(7) LEA [BX],V
(8) MOV [DX],OFFSET V
(9) MOV [SI],2
(10) MUL BX,CX
(11) DIV 5
(12) MOV BYTE[SI],AX
(13) MOV AX,[SI+DI]
(14) SHR AX,4
(15) CMP 6,AX
(16) MOV [FFFF],AX
(17) MOV AX,BX+4
(18) JMP FAR PRO
答:
(1)[BX] 这种是寄存器间接寻址,规定只能BX,BP,SI,DI作间接地址寄存器,可改为:
MOV AX,[BX]
(2)需要确保DS被正确初始化,以指向正确的段,常用组合是AX(BX也可以),可改为:
MOV AX, DATA(segment)
MOV DS, AX
(3)CS是只读寄存器,不能通过MOV修改,通常是执行跳转指令时,CPU自动完成,不能由程序员完成。可改为:
MOV ES,AX
(4)寄存器字节数不匹配,可改为:
MOV AL,DL
(5)PUSH是以字为单位操作的,可改为:
PUSH AX
(6)ADD不支持同时对两个内存位置的值进行相加,需要将其中一个位置的内容加载到一个寄存器中,再执行ADD。可改为:
ADD [BX],DI
(7)LEA用于将有效地址加载到一个寄存器中,可改为:
LEA BX,V
(8)OFFSET V偏移量需要存在寄存器中,可改为:
MOV DX,OFFSET V
(9)x86不允许直接用立即数存储到内存,因为[si]是一个内存操作数,不是一个明确的类型,所以访问内存时需要知道数据数据类型。可改为:
MOV WORD PTR [SI],AX
(10)MUL不能两个寄存器直接相乘,只需要一个操作数,默认是与AX寄存器的值相乘。可改为:
MUL BX
(11)DIV的操作数不能使用立即数。可改为:
DIV BX
(12)BYTE PTR是字节类型,AX是字类型,两者类型不对应,可改为:
MOV BYTE PTR [SI],2
(13)基址变址寻址时,基址寄存器应为BX,BP,变址寄存器应为SI,DI。可改为:
MOV AX,[BX+SI]
(14)SHR逻辑右移的位数,可以是一个立即数或者是存储CL寄存器的值。可改为:
SHR AX,1
(15)立即数只能作为源操作数。可改为:
CMP AX,6
(16)AX为字类型,所以放入FFFF不合法。可改为:
MOV [FFFE],AX
(17)语法不合法,应该用间接寻址方式,可改为:
MOV AX,[BX+4]
(18)FAR PTR更显式,表示跳转目标为远指针,包含了段和偏移地址,可改为:
JMP FAR PTR PRO
5.6 在数据段定义了 ARRAY 数组,其中依次存储了 4 个字数据,根据以下要求把第 4 个字送 AX 寄存器。
(1)直接寻址
(2)使用 BX 的间接寻址
(3)使用 BX 和 ARRAY 的寄存器相对寻址
(4)基址变址寻址
(5)MOV 以外的其它指令
答:
(1)MOV AX,ARRAY+6
(2)LEA BX,ARRAY+6
MOV AX,[BX]
(3)LEA BX,ARRAY
MOV AX,[BX+6]
(4)LEA BX ARRAY
MOV SI,6
MOV AX,[BX+SI]
(5)SUB AX,AX
ADD AX,[ARRAY+6]
5.7 画出数据在数据段中的存放情况,程序执行后,BX、DI、CX、DX 寄存器中的内容是什么? 程序如下:
DATA SEGMENT
ARRAY DW 20,30,40,20H,30H,-6
BUFF DB ‘ABCD$’
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA
START:
MOV AX, DATA
MOV DS, AX
MOV BX, ARRAY+1
MOV DI, OFFSET ARRAY
MOV CX, [DI+5]
MOV DL, BUFF+3
MOV AH, 4CH
INT 21H
CODE ENDS
END START
答:
14 00 1E 00 28 00 20 00 30 00 FA FF 61 62 63 64 $
(BX)=1E00H,(DI)=0000H,(CX)=2000H,(DX)=0064H
5.10 在数据段有 32 位的无符号数变量 X,Y,按如下格式定义,其中‘?’请用数值代替,用 16位指令按要求写出程序。
X DW ?,?
Y DW ?,?
Z DW ?,?,?,?
(1)Z=X+Y
(2)Z=X-Y
(3)Z=|X-Y|
(4)Z=X×Y
答:
(1)
MOV AX,X
MOV DX,X+2
ADD AX,Y
ADC DX,Y+2
MOV Z,AX
MOV Z+2,DX
(2)
MOV AX,X
MOV DX,X+2
SUB AX,Y
SBB DX,Y+2
MOV Z,AX
MOV Z,DX
(3)
MOV AX,X
MOV DX,X+2
SUB AX,Y
SBB DX,Y+2
TEST AX,8000H ;判断AX是否为负数,若为负数,ZF=0,为正数,ZF=1
JZ EXIT ;正数退出
NOT AX
NOT DX ;负数都取反
ADD DX,1
ADC AX,0
Exit: MOV Z,AX
MOV Z+2,DX
(4)
MOV AX,X
MUL Y
MOV Z,AX
MOV Z+2,DX
MOV AX,X+2
MUL Y
ADD Z+2,AX
ADC DX,0
MOV Z+4,DX
MOV AX,X
MUL Y+2
ADD Z+4,AX
ADC DX,0
MOV Z+6,DX
MOV AX,X+2
MUL Y+2
ADD Z+6,AX
ADC DX,0
MOV Z+8,DX
5.11 用移位指令为主实现对 AX 中的无符号数乘以 5,不考虑乘积可能超出 16 位。
答:
MOV DX,AX
MOV CL,2
SHL AX,2
ADD AX,DX
5.12 用移位指令为主实现对 AX 中的无符号数乘以 5,考虑乘积可能超出 16 位的情况。
答:
MOV DX,0
MOV BX,AX
SHL AX,1
ADC DX,0
SHL DX,1
SHL AX,1
ADC DX,0
ADD AX,BX
ADC DX,0
5.13 把 AX 中的内容依次倒排序,即第 0 位移到第 15 位,第 1 位移到第 14 位,…。
答:
先设 AX 值
Mov di,0 ;di存储结果
Mov bx,0
Mov si,1
Mov cl,2
Ror ax,1 ;移位时,首位特殊,本来左移一位即可,但后续都在原本基础上左移两位即可,为了统一处理,原数先右移一位,即所有位数都是原先基础上左移两位得到
K: Rol ax,cl
Mov bx,ax ;移位到位后存入bx
And bx,si ;与运算之后,目标数暂存bx中
Add di,bx ;di存储结果
Shl,si,1 ;移位之后目标数也进行左移
Cmp si,0 ;判断是否超出长度(逻辑移位超出后补0全为0)
Jnz k ;cmp a,b,若相等zf=1,不等于,则zf=0。jnz,若zf=0转移
Mov ax,di
5.14 在数据段有如下定义:
BUFF DB ‘ABCD$EFGHIJK$’
STR1 DB 12 DUP(?)
LEN DB ?
用串指令编写程序完成以下操作:
(1) 对字符串 STR1 全部置‘*’符。
(2) 从左到右把 BUFF 中的字符串传送到 STR1。
(3) 从右到左把 BUFF 中的字符串传送到 STR1。
(4) 比较 BUFF 与 STR1 两个字符串是否相等,如相等则 DX=1,否则 DX=0。(5) 查找 BUFF 中有无字符$,把字符$出现的次数计入 BX 寄存器。
答:
(1)
MOV AX,DATA
MOV DS,AX
MOV AL,‘*’
LEA DI,STR1
MOV CX,STR1-BUFF
CLD
REP STOSB ;重复执行将AL寄存器中字节值存到目的地址中,直到cx值为0(STOSB)存储字节store byte的意思
(2)
MOV AX,DATA
MOV DS,AX
MOV ES,AX
CLD
LEA SI,BUFF
LEA DI,STR1
MOV CX,STR1-BUFF
REP MOVSB
(3)
MOV AX,DATA
MOV DS,AX
MOV ES,AX
STD
LEA SI,STR1-1
LEA DI,LEN-1
MOV CX,STR1-BUFF
REP MOVSB
(4)
MOV AX,DATA
MOV DS,AX
MOV ES,AX
CLD
LEA SI,BUFF
LEA DI,STR1
MOV CX,STR1-BUFF
REPE CMPSB
(5)
MOV AX,DATA
MOV ES,AX
MOV BX,0
CLD
MOV AL,‘$’
LEA SI,BUFF
MOV CX,STR1-BUFF
NEXT: REPNE SCASB
JCXZ NO-FOUND
INC BX
JMP NEXT
5.15 对于给定的 AX 和 BX 的值,执行下列程序段,程序将转向哪里?
ADD AX, BX
JNO L1 ;结果不溢出(OF=0)则转移
JNC L2 ;进位为0(CF=0)则转移
SUB AX,BX
JNC L3
JNO L4
JMP L5
(1) AX=1234H, BX=6789H
(2) AX=790EH, BX=8831H
(3) AX=E002H, BX=8086H
答:
(1)L1
(2)L1
(3)L5
5.16 下面不完整的程序段是比较 AX 和 BX 的值,把其中大的数送 MAX 变量。如果是无符号数,应如何填写指令?如果是有符号数,应如何填写指令?
CMP AX,BX ;cmp a,b——a<b,CF=1 , a>=b,CF=0
( )
MOV MAX,AX
( )
L1:MOV MAX,BX
L2:HLT
答:
无符号数:
(1)JC L1
(2)JMP L2
有符号数:
(1)JL L1
(2)JMP L2
5.17 在下列程序段的括号中分别填入如下指令,程序执行完后,AX、CX 的内容是什么?
(1) LOOP L1
(2) LOOPE L1
(3) LOOPNZ L1
MOV AX, 6
MOV CX,3
L1:ROL AX,CL
TEST AL,3
( )
答:
(1)AX=0180H,CX=0000
(2)AX=0180H,CX=0000
(3)AX=0030H,CX=0002
5.18 测试 AL 寄存器,如果最高位和最低位同时为 0,则转 L0,如果最高位和最低位只有一位为 1,则转 L1,如果最高位和最低位同时为 1,则转 L2。画出流程图,并编写程序段。
答:
Code segment
Assume cs:code
Start:
ROR AL,1 ;ROL
AND AL,3
JZ L0
SUB AL,3
JZ L2
JMP L1
L0:
JMP Exit
L1:
JMP Exit
L2:
Exit:hlt
code ends
end start
5.19 从键盘输入一个英文字母,显示输出其大写。画出流程图,并编写程序段。
答:
Code segment
Assume cs:code
Start:
Mov ah,1 ;1号功能是输入(al)
Int 21h
sub al,20h
mov dl,al
mov ah,2 ;2号功能是输出(dl)
int 21h
hlt
code ends
end start
(a-61h,A-41h)
5.20 从键盘输入一位数字 N,显示输出 N 个 N。画出流程图,并编写程序段。
答:
CODE SEGMENT
ASSUME CS:CODE
START:
MOV AH,1
INT 21H
MOV DL,AL
AND AL, 0FH ;清空AL第一位
MOV CL,AL
MOV CH,0
NEXT: MOV AH,2 ;2号功能将DL输出
INT 21H
LOOP NEXT
MOV AH,4CH
INT 21H
CODE ENDS
END START
第六章 伪指令
6.1 伪指令
6.1.1 处理机选择伪指令
eg:.8086——选择8086指令系统,前面的“.”不能省略,如果没有指明则是默认8086系统
6.1.2 段定义伪指令
- 段定义伪指令格式:
segment_name segment .. segment_name ends
为了确定用户定义的段和哪个段寄存器相关联,用ASSUME伪指令来实现
assume register_name:segment_name....
注:
- 其中register_name为段寄存器名,必须是CS,DS,ES和SS
- assume只能指定把某个段分配给哪一个段寄存器,它并不能把段地址装入段寄存器中,所以需要在代码段中通过代码进行,但代码段无需,这一操作在程序初始化时完成的
- 简化的段定义伪指令
.model small ;定义存储模型为small .data ;定义数据段data string db 'hello,world!$' .code ;定义代码段code start: mov ax,@data ;对ds赋data段基地址 mov ds,ax mov dx,offset string mov ah,9 int 21h mov ah,4ch int 21h end start
注:
- small模型:所有数据都放在一个64KB的数据段,所有代码放在另一个64KB的代码段,数据和代码都为近访问
- .DATA伪指令用来定义数据段,但没有给出段名,默认段名是_DATA
- @DATA表示段名_DATA,在指令中表示段地址
6.1.3 程序开始和结束伪指令
END [label] ;label表示程序开始执行的起始地址
6.1.4 数据定义与存储器单元分配伪指令
[变量] 操作码 n个操作数 [;注释]
- 常用的数据类型(操作码字段)
-
- DB:字节,8位
- DW:字,16位,数据低位在低地址,数据高位在高地址
- DD:双字,32位
- DF:6个字节的字,48位
- DQ:4个字,64位
- DT:10个字
6.1.5 类型属性操作符
word ptr——字类型
byte ptr——字节类型
oper1 db 3,4 oper2 dw 5678h,9 ... mov ax,word ptr oper1 ;从oper1处取一个字使AX=0403H mov bl,byte ptr oper2 ;从oper2处取一个字节使BL=78H mov byte ptr[di],0 ;常数0送到内存字节单元
6.1.6 THIS操作符和LABEL伪操作
可以把一个变量定义成不同的访问类型
name=THIS type name LABEL type ;type只能是byte和word
6.1.7 表达式赋值伪指令“EQU”和”=“
Expression_name EQU Expression ;不可以修改 Expression_name = Expression ;可以修改
6.1.8 汇编地址计数器$与定位伪指令
- 地址计数器$
用法:用来设置当前正在汇编的指令的偏移地址。每一段的开始,地址计数器初始化为0,接着每处理一条指令,地址计数器就增加一个值(DW自增2),此值为该指令所需要的字节数
ARRAY DW 3,$+7,7 COU=$ NEW DW COU
以下是汇编结果:ARRAY的初始地址为03的地址,NEW的地址为06的地址,0009的由来是2+7($=2)
03 |
00 |
09 |
00 |
07 |
00 |
06 |
00 |
- ORG伪操作
用来设置当前地址计数器$的值,可修改
ORG constant expression
- EVEN伪操作
定义:使下一个变量或指令开始于偶数地址
EVEN ARRAY DW 800 DUP(?)
- ALIGN伪操作
定义:使下一个变量的地址从4的倍数开始
ALIGN boundary ;必须是2的幂 ARRAY DW 800 DUP(?)
6.1.9 基数控制伪指令
.RADIX expression
6.1.10 过程伪指令
子过程又称程序,可以把一个程序写成一个过程或多个过程
procedure_name PROC Attribute ... procedure_name ENDP
注:
- 过程名(procedure_name)为标识符,起到标号的作用
- 属性(Attribute)是指类型属性,可以是NEAR(段内调用)或FAR(跨段调用)
U6习题
6.1 画图说明下列数据定义语句所示内存空间的数据,并回答寄存器的值。
ORG 0
ARRAY LABEL BYTE
DA1 DW 2,9,14,3,315H,-6
DA2 DB 7,‘ABCDEDFG’
LEN = $-DA2 ;当前地址与DA2的距离
ORG 100H
DA3 DW DA4 ;是将DA4的地址(0102H)赋给DA3了,
DA4 DB 4 DUP(2 DUP(1,2,3),4)
。。。。。。
MOV AL,ARRAY+2 (AL)=( )H
ADD AL,DA2+1 (AL)=( )H
MOV AX,DA2-DA1 (AX)=( )H
MOV BL,LEN (BL)=( )H
MOV AX, DA3 (AX)=( )H
MOV BX, TYPE DA4 (BX)=( )H
MOV BX, OFFSET DA4 (BX)=( )H
MOV CX, SIZE DA4 (CX)=( )H
MOV DX, LENGTH DA4 (DX)=( )H
MOV BX, WORD PTR DA4 (BX)=( )H
MOV BL, LEN AND 0FH (BL)=( )H
MOV BL, LEN GT 5 (BL)=( )H
MOV AX, LEN MOD 5 (AX)=( )H
答:
MOV AL,ARRAY+2 (AL)=( 09 )H
ADD AL,DA2+1 (AL)=( 41 )H ;DA2+0为长度7,+1为首位
MOV AX,DA2-DA1 (AX)=( 000c )H ;DA2为12,DA1为0
MOV BL,LEN (BL)=( 09 )H ;$此时为21,21-12=9
MOV AX, DA3 (AX)=( 0102 )H ;DA3即DA4地址
MOV BX, TYPE DA4 (BX)=( 0001 )H ;DB类型的字节数1
MOV BX, OFFSET DA4 (BX)=( 0102 )H ;DA4的地址0102
MOV CX, SIZE DA4 (CX)=( 0004 )H ;嵌套不计,只看dup前面数,再*总字节数
MOV DX, LENGTH DA4 (DX)=( 0004 )H ;嵌套不计,只看dup前面数
MOV BX, WORD PTR DA4 (BX)=( 0201 )H ;取DA4一个字,01,02都存入,高位在前
MOV BL, LEN AND 0FH (BL)=( 09 )H ;LEN为09
MOV BL, LEN GT 5 (BL)=( ff )H ;结果为真,返回FF,假返回00
MOV AX, LEN MOD 5 (AX)=( 0004 )H ;9取模得
6.3 指令和伪指令的区别在哪里?伪指令可以出现在代码段吗?指令可以在数据段吗?
答:指令只能出现在代码段,定义数据的伪指令通常在数据段,伪指令在代码段两端也可,但不能在指令之间。
6.5 用 16 位指令编写完整程序,并上机调试,计算 V=(X+Y)*R,其中所有变
量均为 32 位变量,X、Y、R 的具体数值由你自己确定,变量定义格式如
下:
X DW ?,?
Y DW ?,?
R DW ?,?
V DW 4 dup(?)
答:
data segment
x dw 1
y dw 2
r dw 3
v dw 4 dup(?)
data ends
code segment
assume cs:code,ds:data
.386p ;80386指令系统,eax寄存器为32位
start:
mov ax,data
mov ds,ax
mov eax,x
add eax,y
mov ebx,r
imul ebx
mov v,eax
mov v+4,edx ;32位+4,16位+2
mov dl,al
add dl,30h
mov ah,2
int 21h
mov ah,4ch
int 21h
code ends
end start
6.6 数据定义如下:执行下列指令,填写寄存器的值。(指令是分别来看的)
ARRAY LABEL BYTE
DA1 DW 2,9,14,3
DA2 DB 7,‘ABCDEDF’
LEN = $-DA1
MOV AL,ARRAY+2 (AL)=( )H
ADD AL,DA2+1 (AL)=( )H
MOV AX,DA2-DA1 (AX)=( )H
MOV AX,DA1+1 (AX)=( )H
MOV BL,LEN (BL)=( )H
答:
MOV AL,ARRAY+2 (AL)=( 09 )H
ADD AL,DA2+1 (AL)=( 41 )H
MOV AX,DA2-DA1 (AX)=( 0008 )H
MOV AX,DA1+1 (AX)=( 0900 )H
MOV BL,LEN (BL)=( 10 )H
6.7 定义数据段,满足如下要求:
(1)array 为字符串变量:‘inspire a generation!’
(2)data1 为十六进制数:0FEDCBAH
(3)data2 为二进制数:B。
(4)data3 为 100 个为零的字节变量。
(5)分配 500 个字的空间待用。
答:
data segment
Array db ‘inspire a generation!’
Data1 df 0fedcbah ;df定义6字节的字,f前的0可以不加
Data2 db B
Data3 db 100 dup(0)
dw 500 dup(?)
data ends
6.10 给出下列程序段汇编后的结果:
Val1 EQU 6
Val2 EQU 3
MOV BX,(Val1 LT 5) AND 20
MOV BX, (VAL2 GE 1) AND 30
MOV BX,(Val2 AND 5) OR (VAL1 GE 5)
MOV BX,(Val2 – VAL1) GE 5
答:
(1)MOV BX,0
(2)MOV BX,30
(3)MOV BX,FFFF
(4)MOV BX,0
第七章 分支与循环程序设计
7.1 分支程序设计
7.1.1 分支程序结构
- if-then-else——两个分支
- case——多个分支
7.1.2 单分支程序
if-else
eg:双字长数存放在DX和AX寄存器中,求该数的绝对值
7.1.3 复合分支程序
定义:分支结构中又出现分支
eg:从键盘输入一位十六进制数,并将其转换成十进制数显示输出
7.1.4 多分支程序
eg:根据键盘输入的一位数字(1~4),使程序转移到4个不同的分支中去,以显示键盘输入的数字
7.2 循环程序设计
7.2.1 循环程序结构
- DO-WHILE——相当于for循环,先判断后执行
- DO-UNTIL——相当于do while循环,先执行后判断
7.2.2 计数循环程序
计数循环是基本的循环组织方式,用循环计数器的值来控制循环
eg:把BX寄存器中的二进制数用十六进制数格式显示输出
7.2.3 条件循环程序
标志字称为逻辑尺
eg:先从键盘输入8位二进制数作为逻辑尺。再从键盘输入一个英文字母,根据逻辑尺当前的最高位标志显示输出该英文字母的相邻字符,标志位位0则显示其前趋字符,标志位位1则显示其后继字符。显示相邻字符后,逻辑尺循环左移一位,再接收下一个英文字母的输入,并依据逻辑尺显示相邻字符,直到回车键结束程序
7.2.4 条件计数循环程序
eg:设置键盘缓冲区位16个字节,从键盘输入一串字符,然后再从键盘输入一个单个字符,查找这个字符是否在字符串中出现,如果找到,显示该字符串,否则显示”NOT FOUND“
7.2.5 多重循环程序
eg:显示输出20H~7EH的ASCII表。每行16个字符
U7习题
7.4 编写程序,从键盘接收一个小写字母,然后找出它的前导字符和后续字符,再按顺序显示这三个字母。
答:
code segment
assume cs:code
main proc far
push ds
sub ax,ax
push ax
mov ah,1
int 21h
cmp al,61h
jb exit ;jb:al低于61h
cmp al,7ah ;61-a,7a-z
ja exit ;ja:al高于7a
mov bx,ax
sub al,1h ;输出前一字符
mov dl,al
mov ah,2
int 21h
mov dl,bl ;输出当前字符
mov ah,2
int 21h
add dl,1h ;输出后一字符
mov ah,2
int 21h
exit:ret
main endp
code ends
end main
7.5 分别用 LOOP 循环和条件转移指令实现 1+2+3+……+100,并将结果存入 AX。
答:
(1)
code segment
assume cs:code
main proc far
push ds
sub ax,ax
push ax
mov bx,1
mov ax,0
mov cx,99
a: add ax,bx
inc bx
loop a
exit: ret
code ends
end main
(2)
code segment
assume cs:code
main proc far
push ds
sub ax,ax
push ax
mov bx,1
mov ax,0
mov cx,99
a: add ax,bx
inc bx
cmp bx,101
jnz a
exit: ret
main endp
code ends
end main
7.6 打印下面图形。
*
*
*
答:
code segment
assume cs:code
main proc far
push ds
sub ax,ax
push ax
mov ax,1
a: mov cx,ax ;循环次数
b: mov ah,2 ;输出一个字符
mov dl,’*’
int 21h
loop b
cmp ax,6
jz exit
inc ax
mov ah,2
mov dl,13 ;回车
int 21h
mov ah,2
mov dl,10 ;换行
int 21h
jmp a
exit: ret
code ends
end main
7.7 将 AX 和 BX 进行加、减、乘或除的运算,每种运算由用户从键盘上选择。AX和 BX 可在 DEBUG 下设置。
答:
code segment
assume cs:code
main proc far
push ds
sub ax,ax
push ax
mov ah,1
int 21h
cmp al,31h ;1加
jz ad
cmp al,32h ;2减
jz su
cmp al,33h ;3乘
jz mu
cmp al,34h ;4除
jz di
ad:ADD AX,BX
JMP exit
su:SUB AX,BX
JMP exit
mu:MUL BX
JMP exit
d:DIV BX
exit: ret
main endp
code ends
end main
7.8 求已知带符号数字节数组ARRAY的平均值,ARRAY的首字节单元为数组元素的个数。
答:
data segment
ARRAY db 5,01,12,23,45,F3
data ends
code segment
assume cs:code
start:
mov ax,data
mov ds,ax
mov ax,0
mov cl,array
mov ch,0
mov bl,cl ;存储元素个数
lea si,1
a: add al,array[si]
inc si
loop a
div bl
exit: mov ah,4ch
int 21h
code ends
end start
7.9 编写程序,实现对无符号字数组 ARRAY 的 6 个元素从小到大排序。
答:
data segment
array dw 6,5,9,4,5,15,3 ;首地址单元6为元素个数
data ends
code segment
assume cs:code,ds:data
start:
mov ax, data
mov ds, ax
mov di, array
k1: mov cx, di
mov bx,0
k2: mov ax, array[bx]
cmp ax, array[bx+2]
jl next
xchg ax,array[bx+2]
mov array[bx],ax
next: add bx,2
loop k2
dec di
jnz k1
mov ah,4ch
int 21h
code ends
end start
7.10 数据段有两个等长的字数组,分别求出各自的元素之和,并存入元素后面的单元中,即横向相加。再求出两个数组的对应元素之和,并把和存入新数组SUM 中,即纵向相加。
答:
data segment
Array1 dw 3, 1,0,1,? ;设简单数据,第一个为元素个数
Array2 dw 3,1,1,0,?
Array3 dw 3,3 dup (0) ;存放array1,2的和
data ends
code segment
assume cs:code,ds:data
start:
mov ax, data
mov ds, ax
mov ax, 0
lea bx, array1+2
mov cx, array1
sumh1: add ax, [bx]
add bx,2
loop sumh1
mov [bx],ax
;第二个数组累加
mov ax, 0
lea bx, array2+2
mov cx, array2
sumh2: add ax, [bx]
add bx,2
loop sumh2
mov [bx],ax
lea bx,2
mov cx, array2
sum3: mov ax, array1[bx]
add ax, array2[bx]
mov array3[bx],ax
add bx,2
loop sum3
exit: mov ah,4ch
int 21h
code ends
end start
7.11 编写程序,比较两个从键盘输入的字符串是否相同,如果相同,则显示
‘YES’,如果不同,则显示发现不同的字符位置。
答:
data segment
mess1 db 13,10, ‘input string1:$’
mess2 db 13,10, ‘input string2:$’
mess3 db 13,10, ‘YES!$’
mess4 db 13,10, ‘no match at $’
st1 label byte
max1 db 6
act1 db ?
stok1 db 6 dup(?)
st2 label byte
max2 db 6
act2 db ?
stok2 db 6 dup(?)
data ends
code segment
assume cs:code, ds:data,es:data
start: mov ax,data
mov ds,ax
mov es,ax
lea dx,mess1
mov ah,09
int 21h ;qust1?
lea dx,st1
mov ah,0ah
int 21h ;ans1
lea dx,mess2
mov ah,09
int 21h ;qust2?
lea dx,st2
mov ah,0ah
int 21h ;ans2
mov cl,act1
mov dl,act2
cmp cl,dl
jnz nomatch
mov ch,0
lea si,stok1
lea di,stok2
repe cmpsb
jnz nomatch
match: lea dx,mess3
mov ah,09
int 21h
jmp exit
nomatch:
lea dx,mess4
mov ah,09
int 21h
sub di,offset stok2
mov dx,di
add dl,30h
mov ah,2
int 21h
exit:
mov ah,4ch
int 21h
code ends
end start
7.12 编写程序,从键盘输入一个字符串到 BUFF,再输入一个字符到 AL,在字符
串 BUFF 中查找是否存在该字符,如果找到,显示发现的字符位置。
答:
data segment
mess1 db 13,10, ‘input string:$’
mess2 db 13,10, ‘input a char:$’
mess3 db 13,10, ‘found at $’
mess4 db 13,10, ‘no found !$’
st1 label byte
max1 db 6
act1 db ?
stok1 db 6 dup(?)
data ends
code segment para’code’
assume cs:code, ds:data
start: mov ax,data
mov ds,ax
mov es,ax
lea dx,mess1
mov ah,09
int 21h ;qust1?
lea dx,st1
mov ah,0ah
int 21h ;ans1
lea dx,mess2
mov ah,09
int 21h ;qust2?
mov ah,1
int 21h ;ans2
lea di,stok1
repne scasb
jz match
nomatch:
lea dx,mess4
jmp exit
match: lea dx,mess3
exit:
mov ah,09
int 21h
sub di,offset stok1
mov dx,di
and dx,0fh
add dl,30h
mov ah,2
int 21h
mov ah,4ch
int 21h
code ends
end start
7.13 编写程序,从键盘输入一个字符串到 BUFF,并按相反顺序显示输出。
答:
data segment
mess1 db 13,10, ‘input string:$’
mess2 db 13,10,’$’
st1 label byte
max1 db 6
act1 db ?
stok1 db 6 dup(?)
data ends
code segment
assume cs:code, ds:data
start : mov ax,data
mov ds,ax
lea dx,mess1
mov ah,09
int 21h
lea dx,st1
mov ah,0ah
int 21h
lea dx,mess2
mov ah,09
int 21h
mov cl,act1
mov ch,0
mov bx, offset stok1
add bx, cx
next: dec bx
mov dl, [bx]
mov ah,2
int 21h
loop next
mov ah,4ch
int 21h
code ends
end start
7.14 编写程序,从键盘输入一个八位的二进制数,显示其十六进制数。
答:
code segment
assume cs:code
start:
mov cx,8
mov bl,0
next: mov ah,1
int 21h
cmp al,30h
jb exit
cmp al,31h
ja exit
sub al,30h
shl bl,1
add bl,al
loop next
mov cl,4
mov di,2
out1: rol bl,cl
mov dl,bl
and dl,0fh
add dl,30h
cmp dl,39h
jle dig
add dl,7
dig: mov ah,2
int 21h
dec di
cmp di,0
jnz out1
exit: mov ah,4ch
int 21h
code ends
end start
7.15 字数组 ARRAY 为有符号数,第一个单元为元素个数 N,后面为 N 个元素,编写程序,求数组元素中的最大值,并把它放入 MAX 单元。
答:
data segment
array dw 5,9,4,5,15,3
max dw ?
data ends
code segment
assume cs:code,ds:data
start:
mov ax, data
mov ds, ax
mov cx, array
lea bx, array+2
mov ax, [bx]
mov max, ax
k1: mov ax, [bx]
cmp ax, max
jl next
mov max,ax
next: add bx,2
loop k1
mov ah,4ch
int 21h
code ends
end start
第八章 子程序设计
8.1 子程序结构
8.1.1 子程序调用指令
- 过程:
- 首先把它下一条指令的地址(返回地址)压入堆栈保存
- 再把子程序的入口地址置入IP(CS)寄存器
- 执行完后,用返回指令(RET)回到主程序,把堆栈里保存的返回地址送回IP(CS)寄存器
- 具体的指令
CALL DST目标地址 RET RET 表达式 ;该指令除了完成RET操作,还使SP再加上这个常数,以修改SP值
注:
- call
-
- 段内调用(NEAR PTR):堆栈只保存IP寄存器的值
- 段间调用(FAR PTR):先保存CS寄存器的值,再保存IP寄存器的值
- ret
-
- 段内和段间的返回是按照call的对应进行
- 表达式的值是一个常数,加了这个表示ret除了完成本身的指令后,还需将sp+该值,来修改sp的值
8.1.2 过程定义与过程结构
- 过程定义伪指令见6.1.10
- 确定属性的原则:如果调用程序和该过程在同一个代码段,则适用NEAR属性,如果调用程序和该过程不在同一个代码段,则使用FAR属性
8.2 子程序的参数传递
- 入口参数:主程序传参数给子程序
- 出口参数:子程序传参数给主程序
- 值传递:把参数的值放在约定的寄存器或内存单元
- 地址传递:把参数的地址传递给子程序
8.2.1 用寄存器传递参数
- 定义:约定某些寄存器存放将要传递的参数
8.2.2 用变量传递参数
- 定义:用约定的变量在过程间传递参数
8.2.3 用地址表传递参数的通用子程序
这种方法是在主程序中建立一个地址表,把要传递的参数地址放在地址表中,然后把地址表的首地址放入寄存器,子程序通过寄存器间接寻址方式从地址表中取得所需参数
8.2.4 用堆栈传递参数的通用子程序
8.2.5 用结构变量传递参数的通用子程序
U8习题
8.3 主程序从键盘输入一个字符串到 BUFF,再输入一个字符到 AL,用子程序在
字符串 BUFF 中查找是否存在该字符,如果找到,显示发现的字符位置。用寄存器传递要查找的字符。
答:Data segment
BUFF1 db 16,?,16 dup(?),13,10,’$’
Data ends
Code segment
Assume cs:code,ds:data
Main proc far
Push ds
Xor ax,ax
Push ax
Mov ax,data
Mov ds,ax
Mov es,ax
Lea dx,BUFF1
Mov ah,10
Int 21h
mov ah,1
int 21h
Lea di,BUFF1+2
Mov cl,BUFF1+1
mov ch,0
Mov ah,0
Repne scasb
Jnz ye
dec di
mov bx,di
Call BTH
Ye: ret
Main endp
BTH proc near
mov cx,4
s:rol bx,1
rol bx,1
rol bx,1
rol bx,1
mov al,bl
and al,0fh
add al,30h
cmp al,39h
jle d
add al,7
d:mov dl,al
mov ah,2
int 21h
loop s
ret
BTH endp
Code ends
End main
8.4 主程序从键盘输入一个八位的二进制数,对其作求补码操作,用子程序对求补后的值以二进制形式显示。(正数的补码=输入)
答:Code segment
Assume cs:code
Main proc far
Push ds
Xor ax,ax
Push ax
Mov bx,0
Mov cx,8
A:mov ah,1
Int 21h
Shl bx,1
Sub al,30h
Jz b ;输入为 0
INC bx ;输入为 1
B:loop a
Test bx,0080h
Jz d
XOR bx,00FFh ;取反
INC bx
D:Call disp
Ret
Main endp
disp proc near
mov cx,8
shl bx,cl
ls1:shl bx,1
jnc k30
mov dl,31h
jmp outb
k30: mov dl,30h
outb:mov ah,2
int 21h
loop ls1
ret
disp endp
code ends
end main
8.5 主程序从键盘(连续)输入两个四位的十六进制数 A 和 B,用子程序作十六进制计算 A+B,并显示计算结果(二进制)。
答:
data segment
A dw ?
B dw ?
data ends
Code segment
Assume cs:code,ds:data
Main proc far
Push ds
Xor ax,ax
Push ax
Mov ax,data
Mov ds,ax
Call INH ;输入十六进制数到 BX 寄存器
Mov A,bx
Call INH ;输入十六进制数到 BX 寄存器
Mov B,bx
Call ad
Ret
Main endp
INH proc near
mov bx,0 ;初始化
mov ch,4
mov cl,4
inchr: mov ah,1 ;键盘输入
int 21h
cmp al,30h
jl exit ;非法输入
cmp al,39h
jle dig ;输入是数字 0~9
cmp al,41h
jl exit ;非法输入
cmp al,46h
jg exit ;非法输入
sub al,37h ;输入是大写 a~f
jmp ls4
dig: sub al,30h
ls4: shl bx,cl
add bl,al
dec ch
jnz inchr
exit: ret
INH endp
Ad proc near
Mov ax,a
Mov bx,b
Add bx,ax
Call disp
Ret
Ad endp
disp proc near
mov cx,16
ls1: shl bx,1
jnc l30
mov dl,31h
jmp outb
l30; mov dl,30h
outb: mov ah,2
int 21h
loop ls1
ret
disp endp
Code ends
End main
8.6 某字数组为有符号数,第一个单元为元素个数 N,后面为 N 个元素,编写通用子程序,求数组元素中的最大值,并把它放入 MAX 单元。
答:MAX_p proc near
Mov di,[bx] ;个数地址
Mov si,[bx+2] ;数组地址
Mov cx,[di]
Mov di,MAX
Xor ax,ax
Next:cmp di,[si]
Jg a
Mov di,[si]
A: Add si,2
Loop next
Mov MAX,di
ret
MAX_p endp
8.7 设有一个数组存放学生的成绩(0 ~ 100),编制一个子程序统计 0 ~ 59 分、60 ~ 69 分、70 ~ 79 分、80 ~ 89 分、90 ~ 100 分的人数,并分别存放到 scoreE、scoreD、score C、score B 及 score A 单元中。编写一个主程序与之配合使用。
答:
data segment
Array db 6,9,65,78,68,86,93 ;6为学生人数,后面为6个成绩
ScoreE db ?
scoreD db ?
scoreC db ?
scoreB db ?
scoreA db ?
Data ends
Code segment
Assume cs:code,ds:data
Main proc far
Mov ax,data
Mov ds,ax
Mov cl,array
mov ch,0
Call class
Ret
Main endp
Class proc near
Lea si, array
next:
inc si
Mov bl, [si]
Cmp bl,60
Jl e
Cmp bl,70
Jl d
Cmp bl,80
Jl k
Cmp bl,90
Jl b
Mov dl, scoreA
Inc dl
Mov scoreA,dl
loop next
jmp exit
B: Mov dl, scoreB
Inc dl
Mov scoreB,dl
loop next
jmp exit
k: Mov dl, scoreC
Inc dl
Mov scoreC,dl
loop next
jmp exit
D: Mov dl, scoreD
Inc dl
Mov scoreD,dl
loop next
jmp exit
E: Mov dl, scoreE
Inc dl
Mov scoreE,dl
loop next
jmp exit
exit:ret
class endp
Code ends
End main
8.8 用多模块程序设计一个简单的计算器程序,实现整数的加减乘除。运算符可以为:+,-,*,/,=。
答:
public num1,num2,res
extrn ad:far,su:far,mu:far,dv:far,el:far
data segment
num1 db ?
num2 db ?
res dw ?
data ends
code segment
assume cs:code,ds:data
main proc far
push ds
sub ax,ax
push ax
mov ax,data
mov ds,ax
call DTOB
mov num1,bx
call DTOB
mov num2,bx
mov ah,1
int 21h
cmp dl,’+’
jz a
cmp dl,’-’
jz s
cmp dl,’*’
jz m
cmp dl,’/’
jz d
cmp dl,’=’
call el
jmp exit
a:call ad
jmp exit
s:call su
jmp exit
m:call mu
jmp exit
d:call dv
exit:ret
main endp
dtob proc near
mov bx, 0
input: mov ah, 1 ;键盘输入
int 21h
sub al, 30h ;把 ascii 码转变为数值
jl exit ;如不是数则退出
cmp al, 9
jg exit ;如不是数则退出
cbw ;扩展为字
xchg ax, bx ;交换寄存器
mov cx, 10
mul cx ;a(n)= a(n-1)×10
xchg ax, bx ;交换寄存器
add bx, ax ;a(n)=a(n)+b(n)
jmp input
exit: ret
dtob endp
code ends
end main
;
——————
public ad
extrn num1:byte,num2:byte,res:word
code segment
assume cs:code
ad proc near
mov ah,0
mov al,num1
add al,num2
adc ah,0
mov res,ax
ret
order endp
su proc near
mov ah,0
mov al,num1
sub al,num2
sbb ah,0
mov res,ax
ret
su endp
mu proc near
mov al,num1
mul num2
mov res,ax
ret
mu endp
dv proc near
mov al,num1
div num2
mov res,ax
ret
dv endp
el proc near
mov al,num1
cmp al,num2
jz z
mov res,0FFFFH ;不相等
jmp exit
z: mov res,0 ;相等
exit:ret
el endp
code ends
end
📎课后习题参考答案.pdf
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/142982.html