第三节 8086/8088 汇编语言

83
1 第第第 8086/8088 第第第第 第第第第第第第第第第 一、 第第第第第第第 第第第第第第 第第第第第第第第 第第第第第第第第第第第第 第第第第第第 第第第≈第第第 ( 第第第第第第 ) 第第第第第第第第第 END 第第第第第第第第第第第“第第”第第第第第第第第 第第第 第第第 第第第 END 第第 第第第 (1 第 ) 第第第 第第第 第第第 END 第第第 (n 第 ) 第第第

description

从模块 (n 个 ). 主模块 (1 个 ). 数据段. 数据段. 堆栈段. 堆栈段. 附加段. 附加段. 代码段. 代码段. END 标号. END. 第三节 8086/8088 汇编语言. 一、汇编语言程序基本结构. 说明: ①除代码段外,其余段可缺省 ; ②各种段均可有多个,用汇编语言指令可指定段与段寄存器关系,数据段≈附加段 ( 段寄存器不同 ) ; ③各段无顺序要求; ④通过 END 指明模块到此 结束 , 程序从“标号”处 开始 执行。. 汇编语言程序举例:. DATA SEGMENT - PowerPoint PPT Presentation

Transcript of 第三节 8086/8088 汇编语言

Page 1: 第三节   8086/8088 汇编语言

1

第三节 8086/8088 汇编语言一、汇编语言程序基本结构

说明: ① 除代码段外,其余段可缺省; ② 各种段均可有多个,用汇编语言指令可指定段与段寄存器关系,数据段≈附加段 ( 段寄存器不同 ) ; ③ 各段无顺序要求; ④ 通过 END 指明模块到此结束, 程序从“标号”处开始执行。

数据段

堆栈段

附加段

代码段

END 标号

主模块 (1个 )

数据段

附加段

代码段

END

从模块 (n个 )

堆栈段

Page 2: 第三节   8086/8088 汇编语言

2

汇编语言程序举例:DATA SEGMENT BUF1 DB 34H BUF2 DB 2AH SUM DB ?DATA ENDSCODE SEGMENT ASSUME CS:CODE, DS:DATASTART: MOV AX, DATA MOV DS, AX MOV AL, BUF1 ADD AL, BUF2 MOV SUM, AL MOV AH, 4CH INT 21HCODE ENDS END START

Page 3: 第三节   8086/8088 汇编语言

3

二、汇编语言程序开发过程

源程序.asm

目标程序.obj

可执行程序.exe

运行结果

编辑文本编辑器

汇编

连接link

①DOS 下执行②debug

执行

mas

m

非编译

可执行程序.com

转换exe2bi

n

Page 4: 第三节   8086/8088 汇编语言

4

三、汇编语言语句格式 汇编语言程序由若干语句组成。 汇编语言语句类型: 指令语句—完成操作功能,能编排 ( 比翻译简单 )

成机器语言代码 ( 即 80x86 指令系统的指令 ) ;

伪指令语句—为汇编程序在编排源程序时提供有关信息;

宏指令语句—分为宏定义和宏调用语句,编排时用宏定义代替宏调用语句。

汇编语言语句通用格式: [ 名字项 ] 操作项 [ 操作数项 ] [ ;注释项 ]

Page 5: 第三节   8086/8088 汇编语言

5

1 、名字项 组成:可由字母、数字、特殊字符( ?、 . 、 @ 、 _ 、 $) 组成,数字不能作首字符, .

只能作首字符。 类型:标号、变量。( 1 )标号 组成:①语句标号 -- 由名字 + 冒号 (“:”) 组成; ② 过程名、段名等—只由名字组成。

属性:段 属 性—标号 ( 定义时 ) 所在段的起始地址; 偏移属性—标号与所在段起始地址间字节数; 类型属性—表明引用它时的特性, 段内引用为 NEAR ,段间引用为 FAR 。

回下页 回 7 页

Page 6: 第三节   8086/8088 汇编语言

6

( 2 )变量 组成:在除代码段外的段中定义,只由名字组成。 属性:段 属 性—变量 ( 定义时 ) 所在段的起始地址; 偏移属性—变量与所在段起始地址间字节数; 类型属性—表明为它所保留的主存字节数, DB/DW/DD/DQ/DT 对应 1/2/4/8/10 字

节。

转上页

思考:标号与变量的主要区别? 指令中变量的数据类型如何表示?2 、操作项 组成:可为指令、伪指令或宏指令的助记符。 说明:指令助记符可带指令前缀。

Page 7: 第三节   8086/8088 汇编语言

7

3 、操作数项 组成: [ 操作数 ]{[ ,操作数 ]} 操作数可为常数、寄存器名、标号 / 变量、数字 / 地址表达式

常数 寄存器名 标号 / 变量 数字表达式 地址表达式MOV AL,04H

MOV CL,AL

MOV AX,BUFACALL SUBP

MOV AL,17/5

MOV AL,03H[SI]MOV AL,[SI+03H]( 1 )常数

数值常数:二 / 八 / 十 / 十六进制数分别以 B/Q/D( 或缺省结尾字符 )/H 结尾,如 001B 、 35Q 、 98D 或98 、 0A2H 。 字符串常数:用单引号括起来的一个或多个字符,如‘ A’ 、‘ AB’ ;字符用 ASCII 码形式存储。 应用:作立即数、位移量、赋初值用。

转 5 页

Page 8: 第三节   8086/8088 汇编语言

8

( 2 )数字表达式 为常数、寄存器名、标号 / 变量与运算符的组合。 运算符有算术、逻辑、关系、数值返回、属性 5 种。种类 运算符 示例

算术 + 、-、 × 、 / 、 MOD

MOV AL,17/5 ; AL=3

逻辑 AND 、 OR 、 XOR、 NOT

MOV AL,VAL AND 01H ;AL=01 或 00H

关系EQ 、 NE 、 LT 、 GT 、 LE 、 GE

MOV AX,DAT GT 0CH ;=0FFFFHMOV AL,DAT LE 0CH ;=00H

数值回送

SEG 、 OFFSET 、TYPE 、 LENGTH、 SIZE

属性PTR 、 SHORT 、段操作符、 THIS 、 HIGH、 LOW

Page 9: 第三节   8086/8088 汇编语言

9

( 3 )数值返回运算符运算符 作用对象 功能

地址回送

SEG 标号或变量 返回其所在段的段基址值OFFSET

标号或变量 返回其相对段基址的偏移量

符号特征回送

TYPE 标号或变量 返回用数字表示的类型属性LENGTH 变量 若变量用 DUP 定义,则返

回定义元素个数,否则返回1SIZE 变量 返回用 DUP 定义的存储空间 (LENGTH*TYPE 之积 )

TYPE 对象种类

TYPE 对象类型 TYPE 运算结果

变量 DB/DW/DD/DQ/DT 1/2/4/8/10

标号 NEAR/FAR -1/-2

回下页

Page 10: 第三节   8086/8088 汇编语言

10

示例: D_SEG SEGMENT NUM1 DB 10 DUP(10) ;数据定义 NUM2 DW 20 DUP(0, 4 DUP(2)) NUM3 DB 10H, 5 DUP(?) NUM4 DB ‘STRING’ D_SEG ENDS ;假设 DS 指向 D_SEG 段 MOV AX, SEG NUM1 ;等价于 AX←(DS) MOV BX, SEG NUM2 ; AX 与 BX 值相同 MOV SI, OFFSET NUM1 ; NUM1 相对于段的偏移量→ SI MOV DI, OFFSET NUM2 ;等价于 LEA DI, NUM2 MOV AH, TYPE NUM2 ; 2→AH MOV BH, LENGTH NUM2 ; 20→BH (指最外层参数) MOV AL, LENGTH NUM3 ; 1→AL( 指第一个参数 ) MOV CL, SIZE NUM2 ; 20*2→CL MOV CH, SIZE NUM4 ; 1*1→CH

转上页

Page 11: 第三节   8086/8088 汇编语言

11

( 4 )属性运算符 PTR 格式: < 类型 > PTR 表达式 PTR 功能:用新类型取代表达式默认的数据类型 PTR 说明:①表达式为 MEM 操作数或变量时,类型可为BYTE 、 WORD 、 DWORD ,表达式为标号时,类型可为NEAR 、 FAR ; ② 仅改变数据类型属性,且仅在本语句有效。 PTR 示例: DATA1 DB 10H, 20H, 30H DATA2 DW 0123H …… MOV AX, WORD PTR DATA1 ;(AX)←2010H MOV BL, BYTE PTR DATA2 ; (BL)←23H MOV BYTE PTR[SI], 60H ;字节传送 SUB WORD PTR[BX], 36H ;字数据相减 JMP FAR PTR SUB_P1 ;段间直接转移 JMP WORD PTR[BX] ;段内间接转移回下页 转转移地址寻址

Page 12: 第三节   8086/8088 汇编语言

12

SHORT 格式:指令助记符 SHORT 标号 SHORT 功能:转移地址属性为短,用 8 位位移量表示 SHORT 说明: 指令助记符可为 JMP/Jx/LOOP/CALL 等; 可缺省 SHORT ,汇编语言根据标号与 IP 的偏移值自动选择SHORT 或 NEAR PTR 。类型 格式 功能

段操作符 段寄存器 : 地址表达式

表示变量或表达式的段属性例: MOV AX, ES:[BX+SI]

THIS THIS < 类型 >

段地址和偏移地址与下一单元相同,类型为新类型例: NAM_TYP EQU THIS BYTE WORD_TAB DW 50 DUP(?)

HIGH 、LOW

HIGH 数或表达式 返回数或表达式的字节信息例: MOV AH, HIGH X

转上页

Page 13: 第三节   8086/8088 汇编语言

13

( 5 )各运算符的优先级优先级 运算符 类别

1 () 、[] 、 LENGTH 、 SIZE

数值回送运算符

2 OFFSET 、 SEG 、 TYPE 、 PTR 、 THIS 、段操作符

属性和数值回送运算符

3 HIGH 、 LOW 属性运算符4 * 、 / 、 MOD 算术运算符5 + 、 - 算术运算符6 EQ 、 NE 、 LT 、 GT 、

LE 、 GE关系运算符

7 NOT 逻辑运算符8 AND 逻辑运算符9 OR 、 XOR 逻辑运算符

10 SHORT 属性运算符

4 、注释项 以“ ;” 开头,以回车结束 ( 注释只能作用于当前行 ) 。

Page 14: 第三节   8086/8088 汇编语言

14

四、汇编语言伪指令 伪指令有 6 类: ① 处理器选择伪指令 (8086 无此指令 )

② 数据定义伪指令 ③ 段定义伪指令 ④ 程序开始和结束伪指令 ⑤表达式赋值伪指令 ⑥其他伪指令 ( 地址计数、对准等 )

⑦过程定义伪指令

Page 15: 第三节   8086/8088 汇编语言

15

1 、处理器选择伪指令 格式 : .8086 选择 8086 指令系统 .286 选择 80286 指令系统 .286P 选择保护方式下的 80286 指令系统 .386 选择 80386 指令系统 .386P 选择保护方式下的 80386 指令系统 .486 选择 80486 指令系统 .486P 选择保护方式下的 80486 指令系统 .586 选择 Pentium 指令系统 .586P 选择保护方式下 Pentium 的指令系统

Page 16: 第三节   8086/8088 汇编语言

16

2 、数据定义伪指令( 1 )数据定义 格式: [ 变量名 ] 类型定义符 参数 {[, 参数 ]}

操作:为变量分配存储空间,并置初值到存储单元 说明: 变量类型—数值变量和字符串变量; 类型定义符—可为 DB/DW/DD/DF/DQ/DT ,表示每个参数占 1/2/4/6/8/10 字节存储空间,字符串变量只能用 DB 类型定义; 参数—可为常数、表达式、 ? 表达式、字符串、 DUP 表达式 字符串用单引号括起来,如‘ ABCDE’

DUP 表达式格式: n DUP( 参数 {[, 参数 ]}) , n 表示重复次数。回下页

Page 17: 第三节   8086/8088 汇编语言

17

示例: X1 DB 40H ;为 X1 分配 1 个字节,初值 40H X2 DW 10*10 ;为 X2 分配 2 个字节,初值

100(0064H) X3 DB 10,20,30 ;为 X3 分配 3 个字节,初值为

10,20,30 Y1 DB 20H,? ;为 Y1 分配 2 个字节,第 2 个字节无

初值 S1 DB ‘HEL’ ; S1占 3 个字节,值为 ASCII 码 S2 DB ‘H’, ‘E’, ‘L’ ; S2 与 S1 相同 S3 DW ‘HE’ ;非字符串定义 VAR1 DW 2 DUP(2,4),1476H VAR2 DB 2 DUP(?,2 DUP(‘A’,‘B’) )‘E’

‘H’S3

?‘A’‘B’‘A’‘B’?

‘A’‘B’‘A’‘B’

VAR2

02H00H04H00H02H00H04H00H76H14H

VAR1

转上页

‘H’‘E’‘L’

S1

Page 18: 第三节   8086/8088 汇编语言

18

( 2 )数据别名定义 格式: [ 变量或标号 ] LABEL < 类型 >

操作:给变量 / 标号重新定义别名和改变别名属性 说明:不分配新的存储空间。 示例 1 : BARRAY LABEL BYTE ARRAY DW 100

DUP(0)

…… ADD

AL,BARRAY[49]

;第 50 个字节,字节相加 …… ADD AX,ARRAY[98]

;第 50 个字,字相加

示例 2 : SUBPF LABEL FAR SUBP : SUB AX,AX …… JMP SUBP ;段内 …… FARPRO PROC FAR …… JMP SUBPF ;段间

Page 19: 第三节   8086/8088 汇编语言

19

3 、段定义伪指令( 1 )段定义伪指令 格式:段名 SEGMENT[ 定位类型 ][ 组合类型 ][ 类别名 ] … ;语句体 段名 ENDS

操作:定义逻辑段 说明:代码段的语句体为指令和伪指令,其它段为伪指令。 a. 定位类型:指定当前段起始地址的性质 ①PARA 起始地址的低 4 位为 0H ,即 16 的倍数; ②PAGE 起始地址的低 8 位为 00H, 即 256 的倍数 ③BYTE 可从任意地址开始 ④WORD 当前段的起始地址为偶地址 默认项是 PARA

回 21 页

Page 20: 第三节   8086/8088 汇编语言

20

b. 组合类型:指明程序连接时,段的合并方法 ①PRIVATE 不与其他模块中任何段连接或重叠 ②PUBLIC 不同模块中 PUBLIC 类型的同名段连接在一起,形成一个单一的段 ( 段长为∑ ) ③COMMON 不同模块中 COMMON 类型的同名段重叠在一起,共享相同的存储区 (共享存储区长度为 max) ④STACK 功能同 PUBLIC ,但新段为堆栈段 ( 段长为∑ ) 默认项是 PRIVATE c. 类别名:指明程序连接时,段间次序的确定方法 LINK 程序将各模块相同‘类别名’的各段依次序连续存放在内存中,但各段相互独立。 缺省项为空 段合并的优先级>确定段间次序的优先级。

回下页

Page 21: 第三节   8086/8088 汇编语言

21

段定义伪指令示例: D_SEG SEGMENT ‘DATA' NUM1 DB 10 DUP(?) D_SEG ENDS S_SEG SEGMENT PARA ‘STACK' DW 100 DUP(?) S_SEG ENDS E_SEG SEGMENT PARA ‘DATA' STRING DB 'HELLO' E_SEG ENDS …… ;连接 (LINK) 时, D_SEG 段与 E_SEG 段是连续的

转 19 页 转上页

Page 22: 第三节   8086/8088 汇编语言

22

( 2 )指定段寄存器伪指令 格式: ASSUME 段寄存器 : 段名 {[, 段寄存器 : 段名 ]}

操作:明确段和段寄存器的关系 说明:①代码段中必须至少有一个 ASSUME 语句, ASSUME 可以出现在源程序中的任何地方; ② 对同一段寄存器重复指定时最后一个有效。 a.CS 的指定 必须指定 CS ,最后一次指定必须在开始执行段中,且段名为开始执行段的段名。 CS=END 指令中标号 / 过程名对应的段 IP=END 指令中标号 / 过程名在段内的偏移地址

转下页

Page 23: 第三节   8086/8088 汇编语言

23

b.DS 、 ES 的指定 对 DS 、 ES 的指定无效 ( 文件加载后自动指向程序前缀 ) ,需要在程序中显式赋值 ( 指定 ) 。 程序前缀 (PSP)— 用户程序与命令行间的接口,占 256 字节

程序前缀区

程序区

EXE 文件加载后结构

CSIP=?

DS 、ESSSSP=?

思考:文件加载时,如何得到 CS 、 SS 值?

回上页 回 25 页 回 39 页

文件头

装入模块

EXE 文件结构

格式化区重定位表

程序代码

重定位表—代码长度, 各段 偏移地址、长度等

100H

Page 24: 第三节   8086/8088 汇编语言

24

对 DS 、 ES 赋值 ( 指定 ) 示例: D_SEG SEGMENT ;数据段 X DW ?, 4000H, 100 DUP(?) D_SEG ENDS E_SEG SEGMENT ;附加段 STRING DB 'EXAMPLE' E_SEG ENDS C_SEG SEGMENT PARA 'CODE' ASSUME CS:C_SEG,DS:D_SEG,ES:E_SEG START: MOV AX, D_SEG MOV DS, AX ;数据段基址→ DS MOV AX, E_SEG MOV ES, AX ;附加段基址→ ES

转 MOV 对段 REG限制

Page 25: 第三节   8086/8088 汇编语言

25

c.SS 的指定 自动指定—当有组合类型为 STACK 的段时, SS 自动指向该段; 此时用 ASSUME 指定 SS 时, SP= 段长;否则SP=0000H 自动指定示例: S_SEG SEGMENT PARA STACK DW 100 DUP(?) S_SEG ENDS

思考:若有多个 STACK 属性的堆栈段,结果如何? 缺省指定—当无组合类型为 STACK 的段时,无论是否用 ASSUME 设定, SS 自动指向程序区开始(SP=0000H) ; 思考:若未定义堆栈时,使用堆栈的结果?转 23 页 回 28 页

Page 26: 第三节   8086/8088 汇编语言

26

显式指定—使用 MOV 指令,使 SS及 SP 指向目的处。 用指令赋值示例: S_SEG SEGMENT DW 100 DUP(?) ST LABLE WORD S_SEG ENDS C_SEG SEGMENT PARA 'CODE' ASSUME CS:C_SEG,SS:S_SEG,DS:NOTHING,ES:NOTHING START: …… MOV AX, S_SEG ;填入段基址 MOV SS, AX LEA SP, ST ;填入栈顶指针 C_SEG ENDS END START 思考:若有 STACK 属性的堆栈段,又用指令赋值后结果如何?

Page 27: 第三节   8086/8088 汇编语言

27

4 、程序开始和结束伪指令( 1 )模块命名伪指令 格式 1 : NAME [ 模块名 ]

操作 1 :给源程序进行模块命名 说明 1 :若模块名缺省,则自动用程序名代替。

格式 2 : TITLE 标题名 操作 2 :给源程序设置标题 ( 模块命名 )

说明 2 :模块名为标题名的前 6 个字符。 示例: NAME MM1

或 TITLE ABCDEFGH ;模块名为 ABCDEF

Page 28: 第三节   8086/8088 汇编语言

28

( 2 )程序开始和结束伪指令 格式: END [ 地址标号 ]

操作:表明该模块到此结束,和整个应用程序从地址标号处开始执行 说明:①应用程序只能有一个主模块 ( 地址标号不缺省 ) ; ② 汇编程序不处理源程序中 END后的信息。

应用程序与源程序文件: 应用程序可有多个源程序文件; 每个源程序文件只能为一个模块 ( 在 NAME/TITLE~END

间 ) ; 所有模块中只能有一个主模块 (END后地址标号不缺省 ) ; 每个模块中可有多个段; 每个段的类型通过与段寄存器对应或段组合类型定性。转 25 页

Page 29: 第三节   8086/8088 汇编语言

29

5 、表达式赋值伪指令 格式:名字 EQU 表达式 名字 = 表达式 操作:为表达式取一个名字,供以后引用 说明: ① 表达式可为常数、变量 / 标号、指令助记符、字符串 ② 同一源程序中,用 EQU 赋值的符号不能再次赋值,而用 =

赋值的符号名可重复赋值。 ③ 赋值语句不产生目标代码,也不占有存储单元 ( 汇编程序扫描时,用表达式替代符号 ) ,目的是方便更新和提高可读性。

示例: CONST EQU 100*2 ;定义符号常数 ADDRS EQU [BX+10] ;为地址表达式定义名字

Page 30: 第三节   8086/8088 汇编语言

30

6 、地址计数器和对准伪指令( 1 )地址计数器 $

功能:保存当前正在汇编的指令 ( 或数据 ) 的地址 说明: $ 为有效地址 EA(16 位 ) ; 可参加构成表达式。 示例 1 : ARRAY DW 1,2,$

+4,3,$+5

示例 2 : BUFFER DB 1,2,3,4,5

COUNT EQU $-BUFFER

; COUNT 的值为 BUFFER 的长度

010002007C0003008100

ARRAY

0074H

0078H

007CH

Page 31: 第三节   8086/8088 汇编语言

31

( 2 )定位伪指令 格式: ORG 表达式 操作:将表达式的值送入汇编地址计数器 ( 即命令汇编程序从表达式值位置生成目标代码 )

说明:若需相对 $ 移动,则表达式 =$+ 偏移量。 示例: D_SEG SEGMENT PARA 'DATA’ ORG 10H X DB 20H, 30H ORG $+5 Y DB 40H, 50H D_SEG ENDS

2030

4050

10H

17H

D_SEG 的00H

Page 32: 第三节   8086/8088 汇编语言

32

7 、过程定义伪指令 格式:过程名 PROC [ 类型 ]

…… ;过程体语句 RET

过程名 ENDP

操作:定义过程,供调用 说明: 可用 CALL 或 END启动过程,用过程体中 RET 结束过程调用; 类型有 NEAR 和 FAR两种 ( 缺省时为 NEAR) ,分别表示可以进行段内和段间调用。

回 60 页

Page 33: 第三节   8086/8088 汇编语言

33

五、汇编语言程序返回 DOS方法 汇编语言程序结构示例 1 : DATA SEGMENT …… DATA ENDS CODE SEGMENT ASSUME CS:CODE, DS:DATA START: MOV AX, DATA MOV DS, AX …… MOV AH, 4CH INT 21H CODE ENDS END START

回下页

Page 34: 第三节   8086/8088 汇编语言

34

汇编语言程序结构示例 2 : DATA SEGMENT …… DATA ENDS CODE SEGMENT ASSUME CS:CODE, DS:DATA MAIN PROC FAR PUSH DS MOV AX, 0 PUSH AX MOV AX, DATA MOV DS, AX …… RET MAIN ENDP CODE ENDS END MAIN

转上页

Page 35: 第三节   8086/8088 汇编语言

35

1 、使用 DOS 功能调用返回 ODS 格式: MOV AH , 4CH INT 21H 功能:带返回码返回 DOS ,返回码可由程序设置 例:将某字节数据块中负元素个数存入变量 CNT 。 DATA SEGMENT ARRAY DB -1,10,11,-7,-9 LEN EQU $-ARRAY CNT DB ? DATA ENDS CODE SEGMENT ASSUME CS:CODE, DS:DATA START : MOV AX,DATA MOV DS,AX LEA SI,ARRAY MOV CX,LEN MOV DL,0

LP: MOV AL,[SI] CMP AL,0 JGE JSU INC DLJSU: INC SI LOOP LP MOV CNT,DL MOV AH,4CH INT 21HCODE ENDS END START

Page 36: 第三节   8086/8088 汇编语言

36

2 、使用中断功能调用返回 DOS

格式: INT 20H

功能:程序结束并返回 DOS

3 、使用过程中 RET 返回 DOS 格式: MAIN PROC FAR PUSH DS MOV AX, 0 PUSH AX …… RET MAIN ENDP …… END MAIN

功能:主程序设计为 FAR 过程,通过调用 INT 20H

返回 DOS(利用程序前缀 +RET 指令实现 ) 。回下页

Page 37: 第三节   8086/8088 汇编语言

37

程序前缀:共 256 字节,开始两单元为 INT

20H 。

END 指令:执行 MAIN函数体 ( 即 CS:IP) ,与CALL MAIN ( 有当前 CS:IP 的压栈过程 ) 不同。 RET 指令:自动弹出栈顶的 4 个字节,并送 IP 和CS 。 功能实现:手工将程序前缀中 INT 20H 的地址压栈,即压入DS 和 0000H(见上页 ) ,执行 RET 时即可返回 DOS 。 转上页

程序前缀区INT 20H

其它前缀信息

程序区程序信息

EXE 文件加载后结构

CSIP= ?

DS 、ES

Page 38: 第三节   8086/8088 汇编语言

38

示例:存储单元 NUM 中为一个 16 位二进数,将其中非零位的个数存入 CNTD_S SEGMENT NUM DW ? CNT DB ?D_S ENDSS_S SEGMENT STACK DB 64 DUP(?)S_S ENDSCODE SEGMENTASSUME CS:CODE,DS:D_S,SS:S_SMAIN PROC FAR PUSH DS ;段地址 MOV AX, 0 PUSH AX ;偏移地址 MOV AX,D_S MOV DS,AX

MOV CX,0 MOV AX,NUMLP1: AND AX,AX JZ DONE SAL AX,1 JNC NEXT INC CLNEXT: JMP LP1DONE: MOV CNT,CL RET ;偏移地址 ;段地址MAIN ENDPCODE ENDS END MAIN

Page 39: 第三节   8086/8088 汇编语言

39

4 、 COM 文件结构 COM 文件结构 (是 EXE 文件的简单版本 ) : 无文件头域 (亦无重定位表 ) ,只有一个段 (长度<64K) ,用中断指令返回,代码中过程属性用NEAR 。

COM 文件结构

程序代码

程序前缀区

程序区

COM 文件加载后结构

IP=0100H

CS 、 DS 、 ES、 SS

SP

COM 文件加载:自动生成程序前缀 (长 100H) ,所有段寄存器全部指向程序前缀段, IP=0100H 。 COM 文件生成:用 exe2bin将 EXE 文件转换成COM 文件。 转 23 页 回下页

Page 40: 第三节   8086/8088 汇编语言

40

COM 文件源程序结构示例: CODE SEGMENT ASSUME CS:CODE, DS:CODE,ES:CODE,SS:CODE START: JMP COD_S BUF DB 64 DUP ( ?) ;数据区 CNT DW 2 STA DB 64 DUP (?) ;堆栈区 COD_S: MOV AX,CS ;给DS 、 ES 、 SP 赋初值 MOV DS,AX MOV ES,AX MOV SS,AX LEA SP,COD_S …… ;主程序体 MOV AH,4CH INT 21H CODE ENDS END START ;标号必须为 CS 段第一条语句

转上页

Page 41: 第三节   8086/8088 汇编语言

41

第四节 结构化程序设计 结构化程序结构:顺序、分支 ( 多路分支 ) 、循环

回 44 页 回 52 页

条件

语句串F

T

多路条件

语句串 1 语句串 n…

条件F

T

语句串 1

语句串 2

条件

初始化

循环体

改信息

T

F

条件

初始化

循环体

改信息

T

F

继续循环条件

退出循环条件

Page 42: 第三节   8086/8088 汇编语言

42

一、顺序结构程序设计 目的:练习编写完整程序、熟练掌握指令和伪指令 例 1 :将压缩 BCD 码转换成 ASCII BCD_BUF DB 96H ASC_BUF DB 2 DUP(?)

…… MOV AL, BCD_BUF

MOV CL, 4 MOV BL, AL SHR AL, CL ;右移 4 位,将高 4 位填 0 ADD AL, 30H MOV ASC_BUF, AL AND BL, 0FH ADD BL, 30H MOV ASC_BUF+1, BL ……

Page 43: 第三节   8086/8088 汇编语言

43

例 2 :用直接查表法完成将键盘输入的一位十进数(0~9) 转换成对应的平方值并存放在 SQRBUF 单元中。 SQUTAB DB 0,1,4,9,16,25,36,49,64,81 SQRBUF DB ? …… MOV BX, OFFSET SQUTAB MOV AH, 1 INT 21H ; DOS 功能调用的 1 号子功能是键盘输入 SUB AL, 30H ;返回值为 ASCII 码,存于 AL 中 XLAT ; AL←((BX)+(AL)) MOV SQRBUF, AL 习题:将 X 、 Y 单元的字节数据的平均值存到 Z 单元。 思考: IVT 与 BIOS 和 DOS 系统功能调用关系如何? 如何使用 BIOS 和 DOS 系统功能调用?

Page 44: 第三节   8086/8088 汇编语言

44

二、分支结构程序设计 目的:掌握各种分支转移的编程技巧 ( 如跳转表法 )

例 1 :变量 X 为一任意有符号字节数,若 X 为负数,则将其取补码;否则,和 Y 相加,其和存入 AX中 ……

MOV AL, X CMP AL, 0 JGE ADDT NEG AL ; X 求补 MOV X, AL JMP EXIT ADDT : ADD AL,Y ;X+Y ADC AH, 0 EXIT : ……

转 41 页

X≥0

AX←X+Y

X←X 取补

T

F

Page 45: 第三节   8086/8088 汇编语言

45

例 2 :设字节单元 N1 、 N2 中存放无符号数 (1) 若两个均是偶数,则分别加 1后送D1 、 D2 中 (2) 若两个均是奇数,则直接送 D1 、 D2 中 (3) 若一奇一偶,则把奇数送 D1 ,偶数送 D2 中 MOV AL,N1

MOV AH,N2 TEST AL,01H JNE ENDO ; N1奇数转 TEST AH,01H JNE L1 ; N2奇数转 INC AL ;两偶数 INC AH JMP ENDOL1: XCHG AL,AH ;交换ENDO: MOV D1, AL ;存放结果 MOV D2, AH

AL←(N1),AH←(N2)

(AL)0≠0F

T(AH)0≠0

AL←(AL)+1,AH←(AH)+1

AL (AH)

D1←(AL),D2←(AH)

T

F

Page 46: 第三节   8086/8088 汇编语言

46

跳转表法实现多路分支原理: 跳转表是在某内存区域顺序排列的一组有规律的语句串的入口地址。 如是段内分支,每个地址占两个单元 (IP 的值 )

如是段间分支,每个地址占 4 个单元 (CS:IP 的值 ) TABL

E

SUB3

IP

IP

IP

SUB2

SUB1

TABLE

IP

CS

IP

CSSUB2

SUB1

段内分支 段间分支

Page 47: 第三节   8086/8088 汇编语言

47

例 3 :根据 AL 中为 1 的位 ( 从低位到高位 )把程序转移到 8 个不同的程序 ( 段内 ) 分支中去 (跳转表其始位置在变量 TABLE 中 ) 。 方法 1— 用寄存器间接寻址方法 CMP AL, 0 JE DONE LEA BX, TABLE L: SHR AL, 1 JNC NOT_YET ; CF=0跳转 JMP WORD PTR[BX] NOT_YET: ADD BX, TYPE TABLE ; Type Table=2 JMP L DONE: …… 思考:会不会产生死循环? 若会产生死循环,如何解除?

回下页

Page 48: 第三节   8086/8088 汇编语言

48

方法 2— 用变址寻址方法 CMP AL, 0 JE DONE MOV SI, 0 L: SHR AL, 1 JNC NOT_YET JMP TABLE[SI] ;缺省属性为 WORD PTR NOT_YET: ADD SI, TYPE TABLE JMP L DONE: ……

思考:若实现段间分支,需哪些改动 / 要求? 若 SHR 改为 SHL ,程序有哪些改动?

转上页 转转移地址寻址

Page 49: 第三节   8086/8088 汇编语言

49

三、循环结构程序设计 循环控制方式:计数法 ( 减量 /增量 ) 、条件控制法 循环类型:运行循环体、判断与控制的先后次序

转 41 页

常规应用方法: 已知循环次数— 增量法: CMP+Jx 指令 减量法:① LOOP 指令 ( 结束计数为 0)

②CMP+Jx 指令 ( 结束计数不为 0)

未知循环次数— 条件控制法: CMP/TEST+Jx[+JMP] 指令

Page 50: 第三节   8086/8088 汇编语言

50

1 、单重循环程序设计

减量计数法例 1 :将以 S1 为起始地址的 26 个字母依次传送到以 S2 为起始地址的连续单元中。 DATA SEGMENT S1 DB ‘ABCD……XYZ’ DATA ENDS ESTRA SEGMENT S2 DB 26 DUP(?) ESTRA ENDS …… MOV AX, DATA MOV DS, AX ;数据段基址→ DS MOV AX, ESTRA MOV ES, AX ;附加段基址→ ES

回下页

Page 51: 第三节   8086/8088 汇编语言

51

方法 1— 用寄存器间接寻址方法 MOV SI, OFFSET S1 ;初始化 MOV DI, OFFSET S2 MOV CX, 26 LOP1: MOV AL, [SI] ;工作部分 MOV ES:[DI], AL INC SI ;修改部分 INC DI LOOP LOP1 ;修改和控制部分 方法 2— 用寄存器相对寻址方法 MOV SI, 0 ;初始化 MOV DI, 0 MOV CX, 26 LOP1: MOV AL, S1[SI] ;工作部分 MOV ES:S2[DI], AL INC SI ;修改部分 INC DI LOOP LOP1 ;修改和控制部分

转上页 回下页

Page 52: 第三节   8086/8088 汇编语言

52

方法 3— 用基址变址寻址方法 MOV BX, OFFSET S1 ;初始化 MOV BP, OFFSET S2 MOV SI, 0 MOV DI, 0 MOV CX, 26 LOP1: MOV AL, [BX+SI] ;工作部分 MOV ES:[BP+DI], AL INC SI ;修改部分 INC DI LOOP LOP1 ;修改和控制部分 方法 4— 用串处理命令方法 LEA SI, S1 ;或 MOV OFFSET S1 LEA DI, S2 MOV CX, 26 CLD REP MOVSB

转上页

Page 53: 第三节   8086/8088 汇编语言

53

减量计数法例 2 :将内存中 6 个 ASCII 码转换为非压缩 BCD 码 ( 如非数字,则置 0FFH) ,存放在后继单元中。 ASCBUF DB 35H, 38H, 30H, 4DH, 39H, 32H DB 6 DUP(?) …… MOV DI OFFSET ASCBUF MOV CX, 6 MOV BL, 0FFH ;置错误标志 LAB_1: MOV AL, [DI]

CMP AL, 3AH JNB ERR ;大于等于 3A 则错

SUB AL, 30H JC ERR ;小于 30H也错 MOV AH, AL

ERR: MOV AH, BL MOV [DI+06H], AH INC DI ;修改部分 LOOP LAB1

Page 54: 第三节   8086/8088 汇编语言

54

增量计数法例: 计算 S=1+2+3+…+50, 结果存入 AX 中。 …… MOV CX,0 ;初始化 MOV AX,0 MOV BX,1 ROTATE: ADD AX,BX ;累加 INC BX INC CX ;计数器加 1 CMP CX,50 ;与已知的循环次数比较 JNZ ROTATE ……

Page 55: 第三节   8086/8088 汇编语言

55

条件控制法例 1 :求从 STRN 地址开始的以‘ $’ 为结束标志的字符串长度 (长度不超过 100) ,并存于LENG 单元。 ……

LEA BX, STRN MOV DI , 0

MOV AL, ‘$’ LP: CMP AL, [BX+DI] JZ DONE INC DI

JMP LPDONE: MOV LENG, DI 说明:为防止程序死循环,可将串长≤ 100 作为循

环结束的附加条件。

…… LEA BX, STRN MOV DI , 0FFFFH MOV AL, ‘$’LP: INC DI CMP AL, [BX+DI] JNZ LP MOV LENG, DI

Page 56: 第三节   8086/8088 汇编语言

56

例 2 :附加段中有一升序排列的 16 位无符号数数组,其首地址在 DI 中,数组头两个单元存放数组长度。要求在数组中查找变量 K 的内容。若找到, CF=0 , SI 为该元素在数组中的偏移地址;否则, CF=1 。 算法:在数组 R 中查找 K ,采用二分法 (折半法 )查找 ⑴ 下标 LOW←1, HIGH←N 。 ⑵ 若 LOW>HIGH ,则查找失败,置 CF=1 ,结束程

序; 否则计算中点: MID←(LOW+HIGH)/2 ; ⑶ 若 K=R[MID] ,则查找成功,处理并结束程序; 若 K< R[MID] 则转⑷; 若 K> R[MID] ,则转⑸。 ⑷ HIGH←MID-1 ,转⑵; ⑸ LOW←MID+1 ,转⑵。回下页

Page 57: 第三节   8086/8088 汇编语言

57

START : MOV AX, K ;取得 K 内容 MOV LOW_NDX, 1 ;给 LOW(16 位 ) 赋初值 MOV BX, ES:[DI] MOV HIGH_NDX, BX ;给HIGH(16 位 ) 赋初

值 MOV BX, DI ; BX 为数组基址 MOV SI, 2 CMP AX, ES:[BX+SI] ;与第一个数比较 JA CHK_LAST ; K>第一个数时转 JE EXIT ;相等 ( 第一个数 ) ,结束 JMP FAIL ;<第一个数,失败CHK_LAST: MOV SI, HIGH_NDX ;取数组长度 SHL SI, 1 ;长度 *2(16 位数据) CMP AX, ES:[BX+SI] ;与最后的数比较 JB SEARCH ; K<最后一个数时转 JE EXIT ;相等 ( 最后一个数 ) ,结束 JMP FAIL ;>最后一个数,失败

转上页 回下页

Page 58: 第三节   8086/8088 汇编语言

58

SEARCH: MOV CX, LOW_NDX MOV DX, HIGH_NDX CMP CX, DX JA FAIL ; LOW>HIGH ,失败 ADD CX, DX SHR CX, 1 ;折半 (LOW+HIGH)/2 MOV SI, CX SHL SI, 1 ; *2 (16 位数据 ) COMPARE: CMP AX, ES:[BX+SI] ;与中间数比较 JZ EXIT ;相等,找到 JA HIGHER ;大于中间数,转 MOV HIGH_NDX, CX DEC HIGH_NDX ;调整查找区间到前半部分 JMP SEARCH HIGHER: MOV LOW_NDX, CX INC LOW_NDX ;调整查找区间到后半部分 JMP SEARCH FAIL : STC EXIT: ……

转上页

Page 59: 第三节   8086/8088 汇编语言

59

2 、多重循环程序设计 例:对首地址为 A 的 N 字数组中的数从大到小排序。 MOV CX, N-1 ;采用相邻两数比较、小数沉底方法 LOOP1: MOV DI, CX ; DI暂存外层循环计数值 MOV BX, 0 ;每轮比较从首元素开始 LOOP2: MOV AX, A[BX] CMP AX, A[BX+2] ;比较 a(i) 与 a(i+1) JGE COTINUE XCHG AX, A[BX+2] ;交换 MOV A[BX], AX COTINUE: ADD BX, 2 LOOP LOOP2 MOV CX, DI LOOP LOOP1

注意:内、外循环变量和寄存器的保护与恢复。

Page 60: 第三节   8086/8088 汇编语言

60

第五节 子程序 ( 过程 ) 设计

转 32 页

一、子程序 ( 过程 ) 设计方法 通过子程序能实现模块化、减少存储空间等特点。1 、过程定义 见课件 P32 过程定义伪指令。 注意:过程属性选择与过程定义和调用是否在同一段有关。 例: SUBPRO PROC NEAR …… ;过程体 RET SUBPRO ENDP

Page 61: 第三节   8086/8088 汇编语言

61

2 、过程调用

过程定义伪指令 过程调用指令

段内调用

直接

F1 PROC NEAR

CALL F1

或 CALL NEAR PTR F1

间接CALL WORD PTR BX

或 CALL WORD PTR [BX]

段间调用

直接F1 PROC FAR

CALL FAR PTR F1

间接 CALL DWORD PTR [BX]

说明: ①CALL/RET 指令自动完成 IP 或 / 和 CS 的保护与恢复; ② 过程同时有段内和段间调用时,按段间定义和调用; ③ 过程定义与调用不在同一模块时,需用 PUBLIC 和EXTRN 伪指令说明。

Page 62: 第三节   8086/8088 汇编语言

62

例:定义一个过程,将 AL 中的压缩 BCD 码转为ASCII 码,再存入 BX 寻址的连续的两个内存单元中 SUBPRO PROC NEAR PUSH AX ;保护现场 PUSH CX PUSH DX MOV DL, AL

MOV CL, 4 SHR AL, CL OR AL, 30H MOV [BX], AL INC BX

3 、寄存器数据的保护与恢复 CALL 仅保护 IP 和 CS ,不保护所有通用寄存器。 过程必须负责保护和恢复其所用 REG 的原来数据。

MOV AL, DL AND AL, 0FH OR AL, 30H MOV [BX], AL POP DX ;恢复现场 POP CX ;注意次序与 POP AX ;保护时相反 RETSUBPRO ENDP

Page 63: 第三节   8086/8088 汇编语言

63

二、参数传递方法 参数传递方法一般有三种: ⑴利用寄存器传递— 将入口参数和出口参数放在约定的寄存器中; 适用于参数个数较少的情况。 ⑵利用内存缓冲区传递— 有直接存储单元传递和地址表传递两种方法,前者用名字区分参数,后者用下标区分参数; 适合于参数个数较多的情况。 ⑶利用堆栈传递— 注意不能破坏 IP 和 / 或 CS( 它们与参数均在栈中 )

Page 64: 第三节   8086/8088 汇编语言

64

寄存器传递参数示例 1 :从键盘取得一个十进制数(< 65535 ,以非数字结束 ) ,在下一行将其以十六进数形式显示出来。 MAIN PROC FAR CALL GETDEC ;取得十进制数,以二进制保存到BX MOV DL, 0DH CALL SHOWCH ;显示回车 MOV DL, 0AH CALL SHOWCH ;显示换行 CALL SHOWHEX ;显示 BX( 十六进制形式 ) RET MAIN ENDP SHOWCH PROC NEAR ;输出字符, DL 为入口参数(ASCII) MOV AH, 2 INT 21H ;输出DL 中字符 RET SHOWCH ENDP

回下页 回 74 页

Page 65: 第三节   8086/8088 汇编语言

65

GETDEC PROC NEAR ;从键盘取十进制数,出口参数 BX MOV BX, 0 NEWCHAR: MOV AH, 1 INT 21H ;读键盘,输入键值 (ASCII) 在 AL中 SUB AL, 30H JL EXIT ;小于 0 转 CMP AL, 9 JG EXIT ;大于 9 转 CBW ;字节转换成字 XCHG AX, BX ;以前的值→ AX ,准备×10 MOV CX, 10 MUL CX ;将以前的值 ×10 XCHG AX, BX ; BX 为以前的值 ×10 ,准备+AX ADD BX, AX ;加本次输入数值 JMP NEWCHAR EXIT: RET GETDEC ENDP

转上页 回下页

Page 66: 第三节   8086/8088 汇编语言

66

SHOWHEX PROC NEAR ;十六进制形式输出,入口参数 BX MOV CH, 4 ;循环 4次 (BX 中有 4 位 16 进制数) ROTATE: MOV CL, 4 ROL BX, CL ;循环左移四位,从最高位输出 MOV AL, BL AND AL, 0FH OR AL, 30H CMP AL, 3AH JL SHOWIT ;是 0--9 ADD AL, 7 ;是 A--F SHOWIT: MOV DL, AL CALL SHOWCH ;显示 DL 中字符 DEC CH JNZ ROTATE RET SHOWHEX ENDP 转上页 回 74 页

Page 67: 第三节   8086/8088 汇编语言

67

寄存器传递参数示例 2 :求数组元素之和。 D_SEG SEGMENT ARRAY DB 10,20,30,5,60 COUNT EQU $-ARRAY ;数组元素个数 D_SEG ENDS S_SEG SEGMENT PARA STACK DW 100 DUP(?) S_SEG ENDS C_SEG SEGMENT ASSUME CS:C_SEG, DS:D_SEG, SS:S_SEG START: MOV AX, D_SEG MOV DS, AX LEA SI, ARRAY ;参数准备 MOV CX, COUNT CALL SUM1 ;求和 ……

Page 68: 第三节   8086/8088 汇编语言

68

;子 程 序: SUM1 ;入口参数: SI= 数组首址, CX= 数组长度 ;出口参数: AX= 数组和 ;使用寄存器: AX, CX, SI SUM1 PROC NEAR XOR AX, AX CMP CX,0 JZ EXIT AGAIN: ADD AL, [SI] ADC AH,0 INC SI LOOP AGAIN EXIT: RET SUM1 ENDP

Page 69: 第三节   8086/8088 汇编语言

69

直接存储单元传递参数示例:求数组元素之和。 D_SEG SEGMENT ARRAY DB 100 DUP(?) COUNT DW 100 ;数组元素个数 SUM DW ? D_SEG ENDS …… PROADD PROC PUSH AX ;保存现场 PUSH CX PUSH SI XOR AX, AX LEA SI, ARRAY ;直接使用存储单元中数据 MOV CX, COUNT NEXT: ADD AL, [SI] POP SI ADC AH, 0 POP CX INC SI POP AX LOOP NEXT RET MOV SUM, AX PROADD ENDP

Page 70: 第三节   8086/8088 汇编语言

70

地址表传递参数示例:求数组元素之和。 D_SEG SEGMENT ARRAY DB 100 DUP(?) COUNT DW 100 SUM DW ? TABLE DW 3 DUP(?) ;地址表 D_SEG ENDS …… MOV TABLE, OFFSET ARRAY MOV TABLE+2, OFFSET COUNT MOV TALBE+4, OFFSET SUM ;靠约定指明参数次序 LEA BX, TABLE CALL PROADD ……

ARRAY 首址

COUNT

地址SUM 地址

TABLE

+0

+2

+4

Page 71: 第三节   8086/8088 汇编语言

71

;子 程 序: PROADD ;入口参数: BX= 地址表首址 PROADD PROC PUSHA ;保护现场 (全部 REG)

MOV SI, [BX] ;数组首地址送 SI MOV DI, [BX+2] ;数组长度单元地址 MOV CX, [DI] ;数组长度送 CX MOV DI, [BX+4] ;存储和的单元地址送 DI MOV AX, 0 ADDT: ADD AL, [SI] ADC AH, 0 INC SI LOOP ADDT MOV [DI], AX POPA ;恢复现场 (全部 REG) RET PROADD ENDP

Page 72: 第三节   8086/8088 汇编语言

72

堆栈传递参数示例:求数组元素之和。 …… ;主程序 LEA BX, ARRAY PUSH BX ;参数进栈 LEA BX, COUNT PUSH BX LEA BX, SUM PUSH BX CALL FAR PTR PROADD …… PROADD PROC FAR PUSH BP ;保护现场 MOV BP, SP PUSH AX PUSH CX PUSH SI PUSH DI

Page 73: 第三节   8086/8088 汇编语言

73

MOV SI, [BP+10] ;数组首地址送SI MOV DI, [BP+8] MOV CX, [DI] MOV DI, [BP+6] MOV AX, 0 ADDT: ADD AL, [SI] ADC AH, 0 INC SI LOOP ADDT MOV [DI], AX POP DI POP SI POP CX POP AX POP BP RET 6 ;调用参数出栈 PROADD ENDP

SP (DI)

(SI)

(CX)

(AX)

(原 BP)

(IP)

(CS)

SUM 地址COUNT 地

址ARRAY 地

BP

BP+6

BP+8BP+

10

Page 74: 第三节   8086/8088 汇编语言

74

三、子程序嵌套与递归 子程序嵌套例:课件 P64 例。

转 64 页

递归程序例:求阶乘。 …… N DB 4 RESULT DW ? …… MOV AL, N CALL FACTADD1: MOV RESULT, DX ……

n≤1

n 进栈

T F

n←n-1

递归调用

弹出 n

F←F*n

递归返回

F←1

Page 75: 第三节   8086/8088 汇编语言

75

;入口参数 AL=N ;出口参数 N!=DX FACT PROC CMP AL, 1 ; N≤1? JG F1 ; N> 1跳转 MOV DX, 1 ; DX←1 RET F1: PUSH AX ; N 进栈 DEC AL ; AL←N-1 CALL FACT ;递归 ADD2: POP AX ; N出栈 MUL DL ; AL*DL MOV DX, AX ; DX←N*FACT(N-1) RET FACT ENDP

Page 76: 第三节   8086/8088 汇编语言

76

SP

4ADD

1第一次调用

到递归处

SP

3

ADD24

ADD1

SP 2

ADD23

ADD24

ADD1

SP ADD

2 2

ADD23

ADD24

ADD1

第四次进入FACT后

第二次调用到递归处

第三次调用到递归处

RET后

RET后

RET后

RET后

思考题:将 FACT 的入口、出口参数全部用堆栈传递

Page 77: 第三节   8086/8088 汇编语言

77

第六节 VC++ 中嵌入汇编程序( 自学 )1 、嵌入式汇编格式

格式 1 : _asm { 汇编语言指令 [ 串 ] }

格式 2 : _asm 汇编语言指令 说明: ⑴格式 2 只能跟一条汇编语言指令,格式 1 可多条; ⑵有些版本的 VC++需要使用 __asm (两个下划线)

举例: _asm { mov eax,01h _asm mov eax,

01h

mov dx, 0xd007 _asm mov dx, 0xd007

}

Page 78: 第三节   8086/8088 汇编语言

78

2 、在 _asm 中使用汇编语言注意事项( 1 )嵌入式汇编支持 80486 的全部指令系统; VC++5/6还支持MMX 指令集。( 2 )对不支持的指令, VC 提供 _emit 伪指令进行扩展。

#define cpi_id _asm _emit 0x0F _asm _emit

0XA2

_asm { cpu_id} //使用 C++ 的宏( 3 )嵌入式汇编代码可以使用 MASM 的表达式来产生一个数值或地址。

( 4 )嵌入式汇编行可以采用 C++ 或 MASM 的注释风格。

( 5 )嵌入式汇编可以使用 C++ 的数据类型和数据对象,但是不能使用 MASM 的伪指令和操作符定义数据,如 DB 、 DW 、 DUP 等。

Page 79: 第三节   8086/8088 汇编语言

79

( 6 )嵌入式汇编不支持MASM 的宏指令,也不支持大部分 MASM 伪指令。

( 7 )用汇编语言编写的过程中,不必保存 EAX/EBX

/ECX/EDX/ESI 和 EDI 寄存器,但必须保存过程中所使用的其他寄存器。

( 8 )嵌入式汇编代码可以使用 LENGTH( 返回数组元素个数 ) 、 TYPE( 返回 C++ 类型或变量的大小 ) 、 SIZE(LENGTH×TYPE) 操作符。

例:对数据 int iArray[8] ( int 类型是 32 位, 4 字节 ) ,则:

LENGTH iArray 返回 8

TYPE iArray 返回 4

SIZE iArray 返回 32

Page 80: 第三节   8086/8088 汇编语言

80

举例: int array[6] struct first_type { char *carray; int same_name; } ftype; struct second_type { int same_name; } stype; _asm { mov array[6], dx mov ebx,OFFSET ftype mov ecx, [ebx].carray mov esi, [ebx]ftype.same_name }

Page 81: 第三节   8086/8088 汇编语言

81

3 、在 _asm 中使用 C++ 语言注意事项( 1 )嵌入式汇编代码可使用 C++ 的符号 (含标号、

变量、函数名 ) 、常量 (含符号常量、枚举成员 ) 、宏和预处理指令、注释、类型名及结构、联合的成员。

( 2 )每一个汇编语句只能包含一个 C++ 符号,且不能使用和 MASM 保留字相同的 C++ 符号,也不识别 struct 和 union 关键字。

( 3 ) _asm 中引用函数前必须在程序中说明其原型。

( 4 )嵌入式汇编语句中可以使用汇编语言格式表示整数常量,也可以采用 C++ 的格式 ( 如 0x37a) 。

( 5 )嵌入式汇编语言中不能使用 C++专用操作符( 如 <<) ,可以使用两种语言都有的操作符。

Page 82: 第三节   8086/8088 汇编语言

82

( 6 )嵌入式汇编中可引用包含该 _asm 作用范围内的任何符号 (包括变量 ) ,它通过使用变量名引用C++ 的变量。

( 7 )嵌入式汇编中的标号的作用范围是在定义它的过程中有效,汇编转移指令和 C++ 的 goto 指令都可以跳到 _asm 块内或块外的标号。

( 8 ) _asm 块中定义的标号对大小写不敏感,汇编语言指令跳转到 C++ 中的标号也大小写不敏感,但C++ 的 goto 语句跳转的标号大小写敏感。

Page 83: 第三节   8086/8088 汇编语言

83

举例: #include <iostream.h> int power2(int, int); void main(void) { cout<<"2 的 6次方乘 5 等于 :\t"; cout<<power2(5,6)<<endl; } int power2(int num, int power) { _asm { mov eax, num ; 取第一个参数 mov ecx, power ; 取第二个参数 shl eax, cl ; 计算 EAX=EAX×(2CL) } //嵌入式汇编约定:返回值存于 EAX }