硬件(Handware):
软件(Software):
其中我们程序员最关心的有三个
以助记符形式表示计算机指令,汇编格式指令以及使用它们编写程序的规则就形成汇编语言
BCD码
ASCII码
真值和机器数
**真值:**现实中真实的数值
**机器数:**计算机中用0和1数码组合表达的数
**定点数:**固定小数点的位置表达数值的机器数
浮点数:小数点浮动表达的实数
无符号数:只表达0和正整数的定点整数
有符号数:表达负整数、0和正整数的定点整数
补码
微处理器是微机的硬件核心,主要包含指令执行的运算和控制部件,还有多种寄存器
8086内部结构有两个功能模块,完成一条指令的取指和执行功能
8086CPU所有寄存器都是16位的,都可以存放两个字节,可存储的数据的最大值为2^16-1
AX | 累加器,使用频度最高,用于算术、逻辑运算以及与外设传送信息等; |
---|---|
BX | 基址寄存器,常用做存放存储器地址; |
CX | 计数器,作为循环和串操作等指令中的隐含计数器; |
DX | 数据寄存器,常用来存放双字长数据的高16位,或存放外设端口地址。 |
SI | 是源变址寄存器 |
---|---|
DI | 是目的变址寄存器 |
SP | 为堆栈指针寄存器,指示栈顶的偏移地址,不能再用于其他目的,具有专用目的 |
---|---|
BP | 为基址指针寄存器,表示数据在堆栈段中的基地址 |
用于反映指令执行结果或者控制指令执行形式
状态标志:记录程序执行结果的状态信息
CF(进位标志) 运算结果最高位有进位(加法)或借位(减法),CF=1,针对无符号数
**AF(辅助进位标志)**运算时D3位(低半字节)有进位或者借位,AF=1
**ZF(零位标志) **运算结果为0,则ZF=1
SF(符号标志) 运算结果最高位为1,SF=1
PF(奇偶标志) 运算结果中低八位中1的个数为0/偶数时,PF=1
OF(溢出标志)运算结果超出范围(例如8位表达的范围是+127~-128)则产生溢出,ZF=1,针对有符号数
控制标志:可根据需要设置,控制指令执行方式
DF IF TF
CS(Code Segment) | 指明代码段的起始地址(CS:IP指向下一条要执行的指令) |
---|---|
SS(Stack Segment) | 指明堆栈段的起始地址(SS:SP操作堆栈顶的数据) |
DS(Data Segment) | 指明数据段的起始地址(DS:EA存取数据段中的数据) |
ES(Extra Segment) | 指明附加段(附加数据段)的起始地址(ES:EA存取附加段中的数据) |
段超越前缀指令:
CS: | ;代码段超越,使用代码段的数据 |
---|---|
SS: | ;堆栈段超越,使用堆栈段的数据 |
DS: | ;数据段超越,使用数据段的数据 |
ES: | ;附加段超越,使用附加段的数据 |
计算机中信息的单位:
二进制位Bit | 存储一位二进制数:0或1 |
---|---|
字节Byte | 8个二进制位,D7~D0 |
字Word | 16位,2个字节,D15~D0 |
双字Dword | 32位,4个字节,D31~D0 |
数据的地址对齐:字单元在偶地址(xxx0B)、双字单元在模4地址(xx00B)
存储器(men)的分段管理:
8086CPU有20根地址线
8086CPU将1MB的空间分成许多逻辑段,各个段之间可以重叠
这样,每个存储单元除了唯一的一个物理地址外,还有多个逻辑地址
**逻辑地址:**段基地址:段内偏移地址
物理地址= 段地址 * 16 (二进制数据左移四位,十六进制左移一位) + 偏移地址
指令由操作码和操作数两部分组成;
指令的助记符格式:
操作码 操作数1,操作数2 ;注释
示例一:
示例二:
示例三:
直接存放在机器代码中,紧跟在操作码之后的操作数为立即数(imm)
立即数常用来给寄存器赋值
MOV AL,05H ;AL←05H
MOV AX,0102H ;AX←0102H
操作数存放在cpu的内部寄存器reg中
8位寄存器r8: | AH、AL、BH、BL、CH、CL、DH、DL |
---|---|
16位寄存器r16: | AX、BX、CX、DX、SI、DI、BP、SP |
4个段寄存器seg: | CS、DS、SS、ES |
指令中给出操作数的主存地址信息
多种存储器寻址方式:
直接寻址方式:
MOV AX,[2000H] ;AX←DS:[2000H] 指令代码:A1 00 20
MOV AX,ES:[2000H] ;AX←ES:[2000H] 指令代码:26 A1 00 20
寄存器间接寻址方式:
MOV AX,[SI] ;AX←DS:[SI]
寄存器相对寻址方式:
有效地址=BX/BP/SI/DI+8/16位位移量
MOV AX,[DI+06H] ;AX←DS:[DI+06H]
MOV AX,[BP+06H] ;AX←SS:[BP+06H]
基址变址寻址方式:
有效地址=BX/BP+SI/DI
MOV AX,[BX+SI] ;AX←DS:[BX+SI]
MOV AX,[BP+DI] ;AX←SS:[BP+DI]
MOV AX,DS:[BP+DI] ;AX←DS:[BP+DI]
相对基址变址寻址方式:
有效地址=BX/BP+SI/DI+8/16位位移量
MOV AX,[BX+SI+06H] ;AX←DS:[BX+SI+06H]
位移量可用符号表示:
MOV AX,[SI+COUNT] ;COUNT是事先定义的变量或常量(就是数值)
MOV AX,[BX+SI+WNUM] ;WNUM是变量或常量
同一寻址方式可以写成不同的形式:
MOV AX,[BX][SI] ;MOV AX,[BX+SI]
MOV AX,COUNT[SI] ;MOV AX,[SI+COUNT]
MOV AX,WNUM[BX][SI] ;等同于 MOV AX,WNUM[BX+SI]
;等同于 MOV AX,[BX+SI+WNUM]
寄存器操作数的表达
r8(任意一个8位通用寄存器) | AH AL BH BL CH CL DH DL |
---|---|
r16(任意一个16位通用寄存器) | AX BX CX DX SI DI BP SP |
reg | 代表r8或r16 |
seg | 段寄存器CS DS ES SS |
存储器操作数的表达
m8 | 一个8位存储器操作数单元(所有主存寻址方式) |
---|---|
m16 | 一个16位存储器操作数单元(所有主存寻址方式) |
mem | 代表m8或m16 |
立即数的表达
i8 | 一个8位立即数 |
---|---|
i16 | 一个16位立即数 |
imm | 代表i8或i16 |
dest | 目的操作数 |
src | 源操作数 |
;reg:通用寄存器 mem:存储器 imm:立即数 seg:段寄存器
MOV reg/mem,imm
MOV reg/mem/seg,reg
MOV reg/seg,mem
MOV reg/mem,seg
注意:
两个操作数类型一致(对于存储器单元和立即数同时作为操作数的情况,需要显示声明;
byte ptr为字节类型,word ptr为字类型)
mov byte ptr [si],0ah ;byte ptr 说明是字节操作
mov word ptr [si+2],0bh ;word ptr 说明是字操作
两个操作数不能都是存储器(要实现这种操作,通过寄存器实现)
mov ax,buffer1 ;ax←buffer1(将buffer1内容送ax)
mov buffer2,ax ;buffer2←ax
段寄存器的操作有一些限制
不允许立即数传给段寄存器
MOV DS,100H ;非法指令:立即数不能传送段寄存器
不允许改变CS(代码段)的值
MOV CS,[SI] ;不允许使用的指令
不允许段寄存器之间的直接数据传送
MOV DS,ES ;非法指令:不允许段寄存器间传送
将两个地方的数据进行交换
XCHG reg,reg/mem ;reg<->reg/mem
XLAT ;al←ds:[bx+al]
示例:
mov bx,100h
mov al,03h
Xlat
;换码指令没有显式的操作数,但使用了BX和AL;
;因为换码指令使用了隐含寻址方式——采用默认操作数
PUSH:
PUSH r16/m16/seg ;SP
POP:
POP r16/m16/seg ;r16/m16/seg←SS:[SP] SP←SP+2
堆栈的操作单位是字,2个字节
进展和出栈时,都是低地址字节送低字节,高地址字节送高字节(小端对齐)
堆栈操作遵循先进后出原则,但可用存储器寻址方式随机存取数据
常见作用:
临时存放数据
传递参数
保存和恢复寄存器
push ax ;进入子程序后
push bx
push ds
...
pop ds ;返回主程序前
pop bx
pop ax
LAHF
AH <- FLAGS的低字节
SF/ZF/AF/PF/CF状态标志位分别进入AH的第7/6/4/2/0位,而AH的5/3/1位任意
SAHF
FLAGS的低字节 <- AH
用AH的第7/6/4/2/0位响应设置SF/ZF/AF/PF/CF标志位
PUSHF
保存所有标志到堆栈
POPF
将堆栈内容取出到标志寄存器
标志位操作指令直接对CF、DF、IF标志进行复位或置位,常用于特定的情况
用于任意设置进位标志
CLC | ;复位进位标志:CF←0 |
---|---|
STC | ;置位进位标志:CF←1 |
CMC | ;求反进位标志:CF←~CF |
串操作指令中,需要使用
CLD | ;复位方向标志:DF←0 |
---|---|
STD | ;置位方向标志:DF←1 |
在编写中断服务程序时,需要控制可屏蔽中断的允许和禁止
CLI | ;复位中断标志:DF←0 |
---|---|
STI | ;置位中断标志:DF←1 |
将存储器单元的逻辑地址送至指定的寄存器
有效地址传送指令 LEA
LEA r16,mem ;r16←mem的有效地址EA
mov bx,0400h
mov si,3ch
lea bx,[bx+si+0f62h] ;BX=0400h+003ch+0f62h=139EH
指针传送指令 LDS 和 LES
LDS r16,mem ;r16←mem DS←mem+2
LES r16,mem ;r16←mem ES←mem+2
mov word ptr [3060h],0100h
mov word ptr [3062h],1450h
les di,[3060h] ;es=1450h,di=0100h
lds si,[3060h] ;ds=1450h,si=0100h
mem指定主存的连续4个字节作为逻辑地址(32位的地址指针),送入DS:r16或ES:r16
ADD reg,imm/reg/mem | ;reg←reg+imm/reg/mem |
---|---|
ADD mem,imm/reg | ;mem←mem+imm/reg |
ADC reg,imm/reg/mem | ;reg←reg+imm/reg/mem+CF |
---|---|
ADC mem,imm/reg | ;mem←mem+imm/reg+CF |
INC reg/mem | ;reg/mem←reg/mem+1 |
---|---|
SUB reg,imm/reg/mem | ;reg←reg-imm/reg/mem |
---|---|
SUB mem,imm/reg | ;mem←mem-imm/reg |
SBB reg,imm/reg/mem | ;reg←reg-imm/reg/mem-CF |
---|---|
SBB mem,imm/reg | ;mem←mem-imm/reg-CF |
DEC reg/mem | ;reg/mem←reg/mem-1 |
---|---|
用零减去操作数,然后结果返回操作数
求补运算也可以表达成:将操作数按位取反后加1
NEG reg/mem | ;reg/mem←0-reg/mem |
---|---|
将目的操作数减去源操作数,按照定义相应设置状态标志
执行的功能与SUB指令相同,但结果不回送目的操作数
CMP reg,imm/reg/mem | ;reg-imm/reg/mem |
---|---|
CMP mem,imm/reg | ;mem-imm/reg |
MUL r8/m8 ;AX ← AL*r8/m8
MUL r16/m16 ;DX.AX ← AX*r16/m16
IMUL r8/m8 ;AX ← AL*r8/m8
IMUL r16/m16 ;DX.AX ← AX*r16/m16
乘法指令对标志的影响
MUL指令 | 若乘积的高一半(AH或DX)为0,则OF=CF=0;否则OF=CF=1 |
---|---|
IMUL指令 | **若乘积的高一半是低一半的符号扩展,则OF=CF=0;否则均为1 ** |
乘法指令对其他状态标志没有定义
MUL r8/m8 ;AL←AX÷r8/m8的商 AH←AX÷r8/m8的余数
MUL r16/m16 ;AX←DX.AX÷r16/m16的商 DX←DX.AX÷r16/m16的余数
IMUL r8/m8 ;AL←AX÷r8/m8的商 AH←AX÷r8/m8的余数
IMUL r16/m16 ;AX←DX.AX÷r16/m16的商 DX←DX.AX÷r16/m16的余数
压缩BCD码调整
mov al,68h ;al=68h,压缩BCD码表示真值68
mov bl,28h ;bl=28h,压缩BCD码表示真值28
add al,bl ;二进制加法:al=68h+28h=90h
daa ;十进制调整:al=96h 实现压缩BCD码加法:68+28=96
mov al,68h ;al=68h,压缩BCD码表示真值68
mov bl,28h ;bl=28h,压缩BCD码表示真值28
sub al,bl ;二进制减法:al=68h-28h=40h
das ;十进制调整:al=40h 实现压缩BCD码减法:68-28=40
非压缩BCD码调整
加减调整
mov ax,0608h ;ax=0608h,非压缩BCD码表示真值68
mov bl,09h ;bl=09h,非压缩BCD码表示真值9
add al,bl ;二进制加法:al=08h+09h=11h
Aaa ;十进制调整:ax=0707h 实现非压缩BCD码加法:68+9=77
mov ax,0608h ;ax=0608h,非压缩BCD码表示真值68
mov bl,09h ;bl=09h,非压缩BCD码表示真值9
sub al,bl ;二进制减法:al=08h-09h=ffh
aas ;十进制调整:ax=0509h 实现非压缩BCD码减法:68-9=59
AAM指令跟在字节乘MUL之后,将乘积调整为非压缩BCD码
AAD指令跟在字节除DIV之前,先将非压缩BCD码的被除数调整为二进制数
AAM和AAD指令根据结果设置SF、ZF和PF,但对OF、CF和AF无定义
mov ax,0608h ;ax=0608h,非压缩BCD码表示真值68
mov bl,09h ;bl=09h,非压缩BCD码表示真值9
mul bl ;二进制乘法:al=08h×09h=0048h
aam ;十进制调整:ax=0702h 实现非压缩BCD码乘法:8×9=72
mov ax,0608h ;ax=0608h,非压缩BCD码表示真值68
mov bl,09h ;bl=09h,非压缩BCD码表示真值9
aam ;二进制扩展:ax=68=0044h
div bl ;除法运算:商al=07h,余数ah=05h
;实现非压缩BCD码除法:68÷9=7(余5)
AND reg,imm/reg/mem | ;reg←reg∧imm/reg/mem |
---|---|
AND mem,imm/reg | ;mem←mem∧imm/reg |
OR reg,imm/reg/mem | **;**reg←reg∨imm/reg/mem |
---|---|
OR mem,imm/reg | ;mem←mem∨imm/reg |
XOR reg,imm/reg/mem | **;**reg←reg⊕imm/reg/mem |
---|---|
XOR mem,imm/reg | ;mem←mem⊕imm/reg |
NOT reg/mem | **;**reg/mem←~reg/mem |
---|---|
TEST reg,imm/reg/mem | **;**reg∧imm/reg/mem |
---|---|
TEST mem,imm/reg | ;mem∧imm/reg |
SHL reg/mem,1/CL | ;逻辑左移,最高位进入CF,最低位补0 |
---|---|
SHR reg/mem,1/CL | ;逻辑右移,最低位进入CF,最高位补0 |
SAL reg/mem,1/CL | ;算术左移,最高位进入CF,最低位补0 |
---|---|
SAR reg/mem,1/CL | ;算术右移,最低位进入CF,最高位不变 |
ROL reg/mem,1/CL | ;不带进位循环左移 |
---|---|
ROR reg/mem,1/CL | ;不带进位循环右移 |
RCL reg/mem,1/CL | ;带进位循环左移 |
RCR reg/mem,1/CL | ;带进位循环右移 |
JMP label ;程序转向label标号指定的地址
JMP指令分为四种类型
段内转移,相对寻址
JMP label ;IP←IP+位移量
段内转移,间接寻址
JMP r16/m16 ;IP←r16/m16
段间转移,直接寻址
JMP far ptr label ;IP←label的偏移地址 CS←label的段地址
段间转移,间接寻址
JMP far ptr mem ;IP←[mem],CS←[mem+2]
JCC label ;条件满足,发生转移:IP←IP+8位位移量 条件不满足,顺序执行
操作数label采用 相对寻址、短转移 方式
JCC指令不影响标志,但要利用标志,分为3种情况:
转移条件cc:单个标志状态
JZ/JE | ZF=1 | Jump if Zero/Equal |
---|---|---|
JNZ/JNE | ZF=0 | Jump if Not Zero/Not Equal |
JS | SF=1 | Jump if Sign |
JNS | SF=0 | Jump if Not Sign |
JP/JPE | PF=1 | Jump if Parity/Parity Even |
JNP/JPO | PF=0 | Jump if Not Parity/Parity Odd |
JO | OF=1 | Jump if Overflow |
JNO | OF=0 | Jump if Not Overflow |
JC | CF=1 | Jump if Carry |
JNC | CF=0 | Jump if Not Carry |
**转移条件cc:**两数大小关系
无符号数: | ------------------------- | -------------------------------------------- |
---|---|---|
JB/JNAE | CF=1 | Jump if Below/Not Above or Equal |
JNB/JAE | CF=0 | Jump if Not Below/Above or Equal |
JBE/JNA | CF=1或ZF=1 | Jump if Below/Not Above |
JNBE/JA | CF=0且ZF=0 | Jump if Not Below or Equal/Above |
有符号数: | ------------------------- | -------------------------------------------- |
JL/JNGE | SF≠OF | Jump if Less/Not Greater or Equal |
JNL/JGE | SF=OF | Jump if Not Less/Greater or Equal |
JLE/JNG | ZF≠OF或ZF=1 | Jump if Less or Equal/Not Greater |
JNLE/JG | SF=OF且ZF=0 | Jump if Not Less or Equal/Greater |
JCXZ label | ;CX=0,转移到标号label |
---|---|
LOOP label | ;CX←CX-1,CX≠0,循环到标号label |
LOOPZ label | ;CX←CX-1,CX≠0且ZF=1,循环到标号label |
LOOPNZ label | ;CX←CX-1,CX≠0且ZF=0,循环到标号label |
子程序调用指令:
CALL指令有四种类型:
CALL label | ;段内调用、相对寻址 |
---|---|
CALL r16/m16 | ;段内调用、间接寻址 |
CALL far ptr label | ;段间调用、直接寻址 |
CALL far ptr mem | ;段间调用、间接寻址 |
CALL指令需要保存返回地址
SP←SP-2,SS:[SP]←IP
SP←SP-2,SS:[SP]←IP
SP←SP-2,SS:[SP]←CS
子程序返回指令:
RET | ;无参数段内返回 |
---|---|
RET i16 | ;有参数段内返回 |
RET | ;无参数段间返回 |
RET i16 | ;有参数段间返回 |
IP←SS:[SP], SP←SP+2
IP←SS:[SP],SP←SP+2
CS←SS:[SP],SP←SP+2
RET i16 ;有参数返回
RET指令可以带有一个立即数i16,
则堆栈指针SP将增加,即 SP←SP+i16
这个特点使得程序可以方便地废除若干执行CALL指令以前入栈的参数
**中断(Interrupt )**是又一种改变程序执行顺序的方法,具有多种类型
中断指令INT
INT i8 | ;中断调用指令:产生i8号中断 |
---|---|
IRET | ;中断返回指令:实现中断返回 |
INTO | ;溢出中断指令;若溢出标志OF=1,产生4号中断;否则顺序执行 |
不执行任何操作,但占用一个字节存储单元,空耗一个指令执行周期
NOP常用于程序调试
事实上,NOP和 XCHG AX,AX 的指令代码一样,都是 90H
CS: | ;使用代码段的数据 |
---|---|
SS: | ;使用堆栈段的数据 |
DS: | ;使用数据段的数据 |
ES: | ;使用附加段的数据 |
LOCK ;封锁总线
HLT ;进入暂停状态
ESC 6位立即数,reg/mem ;把浮点指令交给浮点处理器执行
WAIT ;进入等待状态
编辑—汇编–连接–调试
标号: 硬指令助记符 操作数,操作数 ; 注释
名字 伪指令助记符 参数,参数.. ;注释
标号、名字与标识符:
标号:反映硬指令(逻辑地址)的标识符,后加冒号分隔
**名字:**反映位指令(逻辑地址)和属性的标识符,后跟空格或制表符分隔,没有冒号
**标识符:**一般最多由31个字母、数字及规定的特殊符号(如、$、?、@)组成,不能以数字开头。默认情况下,汇编程序不区别标识符中的字母大小写
**保留字:**汇编程序已经利用的标识符
硬指令助记符 | 例如:MOV、ADD |
---|---|
伪指令助记符 | 例如:DB、EQU |
操作符 | 例如:OFFSET、PTR |
寄存器名 | 例如:AX、CS |
预定义符号 | 例如:@data |
助记符
操作数与参数:
注释
分隔符:
汇编源程序有两种格式:
简化段定义格式(MASM5.0开始支持)
;MASM 6.X支持
;example.asm ;文件名
.model small ;程序存储模型
.stack ;定义堆栈段
.data ;定义数据段
... ;在数据段定义数据
.code ;定义代码段
.startup ;程序起始点,建立DS、SS
... ;在代码段填入指令序列
.exit 0 ;程序结束点,返回DOS
... ;子程序代码
end ;汇编结束
;MASM 5.X支持
;example.asm ;文件名
.model samll ;程序存储模型
.stack ;定义堆栈段
.data ;定义数据段
... ;在数据段定义数据
.code ;定义代码段
start: move ax,@data
move ds,ax ;这两句指令等价于.startup
... ;代码段,填入指令序列
move ax,4c00h
int 21h ;这两句指令等价于.exit 0
... ;子程序代码
end start ;汇编结束
完整段定义格式(MASM5.0以前就具有)
;MASM 5.X支持
;example.asm ;文件名
stack segment stack ;定义堆栈段
dw 512 dup(?) ;堆栈段有512字(1024字节)空间
stack ends ;堆栈段结束
data segment ;定义数据段
... ;在数据段定义数据
data ends ;数据段结束
code segment 'code' ;定义代码段
assume cd:code,ds:data,ss:stack
start: move ax,data ;建立DS段地址
move ds,ax
... ;在代码段填入指令序列
move ax,4c00h
int 21h ;利用功能调用返回DOS
... ;子程序代码
code ends ;代码段结束
end start ;汇编结束,同时指明程序起始点
准备工作
源程序的编辑
源程序的汇编
汇编是将程序翻译成由机器代码组成的目标模块文件的过程
MASM 6.X提供的汇编程序是ML.EXE
如果源程序中没有语法错误,MASM将自动生成一个目标模块文件(.obj);否则MASM将给出相应的错误信息
ML /c lt301.asm
目标模块的链接
LINK lt301.obj
ML汇编程序可以自动调用LINK连接程序,实现汇编和连接的依次进行
可执行程序的调试
lt301.exe
功能调用的步骤:
输入输出类调用:
字符输出:
字符串输出:
字符输入:
字符串输入:
缓冲区的定义
示例:
按键判断:
表达一个固定的数值
**十进制常数:**由0 ~ 9数字组成,以字母D或d结尾,缺省情况下,后缀D或d可以省略, 例如:100、255D
**十六进制常数:**由0~9、A~F组成,以字母H或h结尾以字母A~F开头的十六进制数,前面要用0表达,以避免与其他符号混淆
例如:64H、0FFh、0B800H、
二进制常数: 由0或1两个数字组成,以字母B或b结尾
例如:01101100B
**八进制常数:**用单引号或双引号括起来的单个字符或多个字符,其数值是每个字符对应的ASCII码的值
例如:‘d’ = 64H
‘AB’= 4142H
‘Hello, Everybody !’
**符号常数:**利用一个标识符表达的一个数值
MASM提供等价机制,用于常量定义
#等价EQU伪指令
符号名 EQU 数值表达式
符号名 EQU <字符串>
#等号=伪指令
符号名 = 数值表达式
示例:
DosWriteChar equ 2
CarriageReturn = 13
CallDOS equ
mov ah,2 ;mov ah,DosWriteChar
mov dl,13 ;mov dl,CarriageReturn
int 21h ;CallDOS
指由运算符连接的各种常数所构成的表达式
mov ax,3*4+5 ;等价于 mov ax,17
;除加、减外,其他运算符的参数必须是整数
or al,03h AND 45h ;等价于 or al,01h
mov al,0101b SHL (2*2) ;等价于 mov al,01010000b
mov bx,((PORT LT 5)AND 20)OR((PORT GE 5)AND 30)
;当PORT<5时,汇编结果为mov bx,20
;否则,汇编结果为mov bx,30
高低分离符:
;HIGH、LOW从一个字数值或符号常量中得到高、低字节
mov ah,HIGH 8765h ;等价于mov ah,87h
;HIGHWORD、LOWWORD取一个符号常量(不能是其他常数)的高字或低字部分
dd_value equ 0ffff1234h ;定义一个符号常量
mov ax,LOWWORD dd_value ;等价于mov ax,1234h
**作用:**为变量申请固定长度的存储空间,并可同时将相应的存储单元初始化
**格式:**变量名 位指令助记符 初值表
变量名
位指令助记符
DB——定义字节伪指令
.data ;数据段
X db 'a',-5
db 2 dup(100),?
Y db 'ABC'
mov al,X ;此处X表示它的第1个数据,故AL←'a'
dec X+1 ;对X为始的第2个数据减1,故成为-6
mov Y,al ;现在Y这个字符串成为 'aBC'
DW——定义字伪指令
.data ;数据段
count dw 8000h,?,'AB'
maxint equ 64h
number dw maxint
array dw maxint dup(0)
DD——定义双字伪指令
vardd DD 0,?,12345678h
farpoint DD 00400078h
**定位伪指令:**控制数据的偏移地址、
ORG参数:ORG伪指令是将当前偏移地址指针指向参数表达的偏移地址:
ORG 100h ;从100h处安排数据或程序
ORG $+10 ;使偏移地址加10,即跳过10个字节空间MASM中,符号“$”表示当前偏移地址值
EVEN | ;从偶地址开始 |
---|---|
ALIGN n | ;从n的整数倍地址开始 |
初值表
初值表是用逗号分隔的参数
主要由数值常数、表达式或?、DUP组成
?——表示初值不确定,即未赋初值
DUP——表示重复初值
DUP的格式为:
重复次数 DUP(重复参数)
地址属性
类型属性
地址操作符:取得名字或标号的段地址和偏移地址两个属性
[ ] | 将括起的表达式作为存储器地址 |
---|---|
$ | 当前偏移地址 |
: | 采用指定的段地址寄存器 |
OFFSET 名字/标号 | 返回名字或标号的偏移地址 |
SEG 名字/标号 | 返回名字或标号的段地址 |
类型操作符:对名字或标号的类型属性进行有关设置
类型名 PTR 名字/标号
mov al,byte ptr w_var ;w_var是一个字节变量
jmp far ptr n_label ;n_label是一个标号
THIS 类型名
b_var equ THIS byte ;按字节访问变量b_var,但与w_var的地址相同
w_var dw 10 dup(0) ;按字访问变量w_var
f_jump equ THIS far ;用f_jump为段间转移(f_jump label far)
n_jump: mov ax,w_var ;用n_jump为段内近转移,但两者指向同一条指令
SHORT 标号
TYPE 名字/标号
mov ax,TYPE w_var ;汇编结果为 mov ax,2
mov ax,TYPE n_jump ;汇编结果为 mov ax,0ff02h(near标号)
SIZEOF 变量名
LENGTHOF 变量名
使用简化段定义,必须有存储模型位指令
.model语句位于所有段定义语句之前
存储模型决定一个程序的规模,确定进行子程序调用、指令转移和数据访问的缺省属性
MASM有7种不同的存储模型:
① TINY ② SMALL
③ COMPACT ④ MEDIUM
⑤ LARGE ⑥ HUGE
⑦ FLAT
.STACK [大小] ;堆栈段开始
.DATA ;数据段开始
.CODE [段名] ;代码段开始
简化段定义伪指令指明一个逻辑段的开始,同时自动结束前面的一个段,采用简化段定义伪指令前,需有.model语句
使用简化段定义,各段名称和其他用户所需的信息可以使用MASM预定义符号
例如:@data表示由.data等定义的数据段的段名
mov ax,4c00h
int 21h
利用MASM 6.x的简化段定义格式,可以非常容易地创建一个COM程序
遵循的规则:
段名 segment 定位 组合 段字 '类别'
... ;语句序列
段名 ends
**段定位(align)属性:**指定逻辑段在主存储器中的边界
BYTE | 段开始为下一个可用的字节地址(xxxx xxxxb) |
---|---|
WORD | 段开始为下一个可用的偶数地址(xxxx xxx0b) |
DWORD | 段开始为下一个可用的4倍数地址(xxxxxx00b) |
PARA | 段开始为下一个可用的节地址(xxxx 0000b) |
PAGE | 段开始为下一个可用的页地址(0000 0000b) |
段组合(combine)属性:指定多个逻辑段之间的关系
PRIVATE | 本段与其他段没有逻辑关系,不与其他段合并,每段都有自己的段地址。这是完整段定义伪指令默认的段组合方式 |
---|---|
PUBLIC | 连接程序把本段与所有同名同类型的其他段相邻地连接在一起,然后为所有这些段指定一个共同的段地址,也就是合成一个物理段。这是简化段定义伪指令默认的段组合 |
STACK | 本段是堆栈的一部分,连接程序将所有STACK段按照与PUBLIC段的同样方式进行合并。这是堆栈段必须具有的段组合 |
段字(use)属性
为支持32位段而设置的属性
对于16位×86cpu来说,默认是16位段,即USE16
而对于汇编32位x86 CPU指令时,它默认采用32位段,即USE32;但可以使用USE16指定标准的16位段
编写运行于实地址方式(8086工作方式)的汇编语言程序,必须采用16位段
段类别(class)属性
把多个同类段合并为一个64KB物理段,并用一个组名统一存取它
定义段组后,段组内各段就统一为一个段地址,各段定义的变量和标号的偏移地址就相对于段组基地址计算
offset操作符取变量和标号相对于段组的偏移地址,如果没有段组则取得相对于段的偏移地址
offset后可以跟段组中的某个段名,表示该段最后一个字节后面字节相对于段组的偏移地址
.SEG ;按照源程序的各段顺序
.DOSSEG ;按照微软使用的标准DOS规定
.ALPHA ;按照段名的字母顺序
代码段 → 数据段 → 堆栈段
主存地址低端 ——> 高端
采用简化段定义格式的源程序,同样具有段定位、组合、类别以及段组等属性(表3.4),具有默认的属性
.MODEL伪指令除了设置程序采用的存储模型外,还具有如下语句的作用:
dgroup GROUP _data,_bss,stack
assume cs:_TEXT,ds:dgroup,ss:dgroup
顺序程序完全按指令书写的前后顺序执行每一条指令,是最基本、最常见的程序结构
条件成立跳转执行第2个分支语句体,否则顺序执行第1个分支语句体。
注意第1个分支体后一定要有一个JMP指令跳到第2个分支体后
move cx,count ;cx数组元素个数
dec cx ;外循环个数,元素个数减1
outlp: move dx,cx ;dx为内循环个数
move bx,offset array
inlp: move al,[bx] ;取前一个元素
cmp al,[bx+1] ;与后一个元素比较
jna next ;前一个不大于后一个元素,则不进行交换
xchg al,[bx+1] ;否则,进行交换
move [bx],al
next: inc bx ;下一对元素
dec dx
jnz inlp ;内循环尾
loop outlp ;外循环尾
串传送 MOVES
MOVSB | ;字节串传送:ES:[DI]←DS:[SI] ;SI←SI±1,DI←DI±1 |
---|---|
MOVSW | ;字串传送:ES:[DI]←DS:[SI] ;SI←SI±2,DI←DI±2 |
串存储 STOS
STOSB | ;字节串存储:ES:[DI]←AL ;DI←DI±1 |
---|---|
STOSW | ;字串存储:ES:[DI]←AX ;DI←DI±2 |
串读取 LODS
LODSB | ;字节串读取:AL←DS:[SI] ;SI←SI±1 |
---|---|
LODSW | ;字串读取:AX←DS:[SI] ;SI←SI±2 |
串比较 CMPS
CMPSB | ;字节串比较:DS:[SI]-ES:[DI] ;SI←SI±1,DI←DI±1 |
---|---|
CMPSW | ;字串比较:DS:[SI]-ES:[DI] ;SI←SI±2,DI←DI±2 |
串扫描 SCAS
SCASB | ;字节串扫描:AL-ES:[DI] ;DI←DI±1 |
---|---|
SCASW | ;字串扫描:AX-ES:[DI] ;DI←DI±2 |
REP 重复前缀
REPZ 重复前缀
REPNZ 重复前缀
过程名 proc [near|far]
...
过程名 endp
过程名(子程序名)为符合语法的标识符
NEAR属性(段内近调用) | 的过程只能被相同代码段的其他程序调用 |
---|---|
FAR属性(段间远调用) | 的过程可以被相同或不同代码段的程序调用 |
对简化段定义格式,在微型、小型和紧凑存储模型下,过程的缺省属性为near;
在中型、大型和巨型存储模型下,过程的缺省属性为far
对完整段定义格式,过程的缺省属性为near
用户可以在过程定义时用near或far改变缺省属性
**入口参数(输入参数):**主程序提供给子程序
**出口参数(输出参数):**子程序返回给主程序
参数的形式: ① 数据本身(传值)
② 数据的地址(传址)
传递的方法: ① 寄存器 ② 变量 ③ 堆栈
例题:
把参数存于约定的寄存器中,可以传值,也可以传址。
子程序对带有出口参数的寄存器不能保护和恢复(主程序视具体情况进行保护)
子程序对带有入口参数的寄存器可以保护,也可以不保护;但最好一致
入口参数:CX=元素个数,DS:BX=数组的段地址:偏移地址
出口参数:AL=校验和
;例4.16主程序
.startup ;设置入口参数(含有DS<-数组的段地址)
mov bx,offset array ;BX<-数组的偏移地址
mov CX,count ;cx<-数组的元素个数
call checksuma ;调用求和过程
mov result,al ;处理出口参数
.exit 0
;例4.16子程序
checksuma proc
xor al,al ;累加器清0
suma: add al,[bx] ;求和
inc bx ;指向下一字节
loop suma
ret
checksuma endp
end
主程序和子程序直接采用同一个变量名共享同一个变量,实现参数的传递
不通模块间共享时,需要声明
入口参数:count=元素个数,array=数组名(段地址:偏移地址)
出口参数:result=校验和
;例4.16
;主程序
call checksumb
;子程序
checksumb proc
push ax
push bx
push cx
xor al,al ;累加器清0
mov bx offset array ;bx<-数组的偏移地址
mov cx,count ;cx<-数组的元素个数
sumb: add al,[bx] ;求和
inc bx
loop sumb
mov result,al ;保存校验和
pop cx
pop bx
pop ax
ret
checksumb endp
主程序将子程序的入口参数压入堆栈,子程序从堆栈中取出参数
子程序将出口参数压入堆栈,主程序弹出堆栈取得它们
入口参数:顺序压入偏移地址和元素个数
出口参数:AL=校验和
;例4.16主程序
.startup
mov ax,offset array ;偏移地址
push ax
mov ax,count ;元素个数
push ax
call checksumc
add sp,4
mov result,al
.exit 0
;例4.16子程序
checksumc proc
push bp
mov bp,sp ;利用BP间接寻址存取参数
push bx
push cx
mov bx,[bp+6] ;SS:[BP+6]指向偏移地址
mov cx,[bp+4] ;SS:[BP+4]指向元素个数
xor al,al
sumc: add al,[bx]
inc bx
loop sumc
pop cx
pop bx
pop bp
ret
checksumc endp
子程序内包含有子程序的调用就是子程序嵌套,没有什么特殊要求
MASM 6.0 引入高级语言的程序设计特性
条件控制伪指令
.IF .ELSE .ENDIF
循环控制伪指令
.WHILE .ENDW .REPEAT .UNTIL
过程声明和过程调用伪指令
.PROTO .INVOKE
操作符 | 功能 | 操作符 | 功能 | 操作符 | 功能 |
---|---|---|---|---|---|
== | 等于 | && | 逻辑与 | CARRY? | CF=1? |
!= | 不等于 | || | 逻辑或 | OVERFLOW? | OF=1? |
> | 大于 | ! | 逻辑非 | PARITY? | PF=1? |
>= | 大于等于 | & | 位测试 | SIGN? | SF=1? |
< | 小于 | () | 改变优先级 | ZERO? | ZF=1? |
<= | 小于等于 |
UNTIL循环结构的流程图 | WHILE循环结构的流程图 |
---|---|
求1~100的和 | 求1~100的和 |
要调用带参数过程定义的过程,不应该用CALL指令,应该采用过程调用INVOKE指令(前提是需要用PROTO伪指令对过程进行声明)
示例:
宏汇编、重复汇编、条件汇编——统称为宏结构
宏汇编与子程序的比较
重复汇编指在汇编过程中,重复展开一段(基本)相同的语句
重复汇编没有名字,不能被调用
重复汇编常用在宏定义体中,也可以在一般汇编语句中使用
重复汇编伪指令有三个:
REPEAT——按参数值重复
FOR——按参数个数重复
FORC——按参数的字符个数重复
最后,用ENDM结束
条件汇编伪指令在汇编过程中,根据条件决定汇编的语句
IFxx 表达式 ;满足,汇编分支语句体1
分支语句体1
ELSE ;不满足,汇编分支语句体2
分支语句体2
ENDIF ;条件汇编结束
宏结构的作用:
宏汇编、重复汇编和条件汇编为源程序的编写提供了很多方便,灵活运用它们可以编写出非常良好的源程序来
汇编系统中有些以圆点起始的伪指令(如.startup、.exit等)实际上是一种宏结构
将程序分段、采用子程序或者宏结构都是进行模块化程序设计
把常用子程序写成独立的源程序文件,单独汇编,形成子程序的目标文件.OBJ
主程序也经过独立汇编之后形成目标文件
连接程序将所有目标文件连接起来,最终产生可执行文件
需要遵循的原则:
① 声明共用的变量、过程等
② 实现正确的段组合
③ 处理好参数传递问题
把常用子程序写成独立的源文件,单独汇编形成OBJ 文件后,存入子程序库
主程序也单独汇编形成OBJ文件
主程序连接时,调入子程序库中的子程序模块,产生最终的可执行文件
;Lt512c.asm
... ;宏定义
.code
extern ALdisp:near,sorting:near,input:near
;声明其他模块中的子程序
.startup
...
.exit 0
end
;sub512c1.asm
.model small
.code
public aldisp
Aldisp proc
...
Aldisp endp
end
;sub512c2.asm
.model small
.code
public sorting
sorting proc
...
sorting endp
end
;sub512c3.asm
.model small
.code
public input
input proc
...
input endp
end
8086通过输入输出指令与外设进行数据交换,呈现给我们的外设是端口(I/O地址)
8086用于寻址外设端口的地址线为16条,端口最多为216=65536(64K)个,端口号为0000H~FFFFH
每个端口用于传送一个字节的外设数据
输入输出的寻址方式有两种:
- 直接寻址:只用于寻址00H~FFH前256个端口,操作数i8表示端口号
- 间接寻址:可用于寻址全部64K个端口,DX寄存器的值就是端口号
大于FFH的端口只能采用间接寻址方式
IN AL,i8 | ;字节输入:AL←I/O端口(i8直接寻址) |
---|---|
IN AL,DX | ;字节输入:AL←I/O端口(DX间接寻址) |
IN AX,i8 | ;字输入:AX←I/O端口(i8直接寻址) |
IN AX,DX | ;字输入:AX←I/O端口(DX间接寻址) |
OUT i8,AL | ;字节输出:I/O端口←AL(i8直接寻址) |
---|---|
OUT DX,AL | ;字节输出:I/O端口←AL(DX间接寻址) |
OUT i8,AX | ;字输出:I/O端口←AX(i8直接寻址) |
OUT DX,AX | ;字输出:I/O端口←AX(DX间接寻址) |
需要交换数据的外设,采用中断请求向处理器提出要求
处理器执行事先设计好的中断服务程序,在中断服务程序当中实现数据交换
8086可以处理256种中断,分为内部、外部两种类型
外部可屏蔽中断用于与外设进行数据交换