各指令:
mov数据传送指令指令:两个操作数的数据类型要相同,两个操作数不能同时为段寄存器,代码段寄存器CS不能为目的操作数,但可作为源操作数。立即数不能直接传给段寄存器,立即数不能作为目的操作数,指令指针IP,不能作为MOV指令的操作数。两个操作数不能同时为存储单元,就是一条指令不能两次访存。
符号扩展是用源操作数的符号位来填充目的操作数的高位数据位,指令movsx。零扩展是用0来填充目的操作数的高位数据位,指令movzx。
交换指令xchg交换寄存器和内存单元中的值,该寄存器不能是段寄存器。
指令lea是把一个内存变量的有效地址送给指定的寄存器。该指令通常用来对指针或变址寄存器BX、DI或SI等置初值。
入栈指令push,出栈指令pop。PUSHF/PUSHFD把16位/32位标志寄存器进栈;POPF/POPFD把16位/32位标志寄存器出栈。
clc:置进位标志位cf为0.
stc:置进位标志位cf为1.
进位取反指令cmc:取反cf位。
cld :置方向标志位df为0.
std:置方向位指令df为1.
清中断允许位指令cli置IF为0,使得不允许可屏蔽的外部中断来中断其后程序段的执行。置中断允许位指令sti置IF为1,恢复可屏蔽的外部中断的中断响应功能。
加法指令ADD,带进位的加法指令ADC,把源操作数和进位标志位CF的值(0/1)一起加到目的操作数中。加一指令INC。与之对应的减法指令SUB SBB DEC,求补指令DEG。无符号乘法指令MUL,有符号乘法指令IMUL。无符号除法指令DIV , 有符号除法指令IDIV。逻辑与AND,逻辑或OR,逻辑异或XOR,逻辑非NOT。算术左移SAL,算法右移SAR,算术移位是低位补0,高位补符号位。逻辑左移SHL,逻辑右移SHR,逻辑移位是低位和高位都补0。双精度左移SHLD,SHLD/SHRD Reg/Mem, Reg, n,在执行SHLD指令时,第一操作数向左移n位,其“空出”的低位由第二操作数的高n位来填补。循环左移ROL和循环右移ROR,它们移出的位不仅要进入CF,而且还要填补空出的位。带进位的循环左移RCL和带进位的循环右移RCR,它们都用原CF的值填补空出的位,移出的位再进入CF。位扫描指令BSF和BSR,位扫描指令是在第二个操作数中找第一个“1”的位置。如果找到,则该“1”的位置保存在第一操作数中,并置标志位ZF为1,否则,置标志位ZF为0,BSF是从低位向高位扫,BSR相反。检测位指令test,把两个操作数进行逻辑“与”操作,并根据运算结果设置相应的标志位,但并不保存该运算结果,所以,不会改变指令中的操作数。比较指令CMP,用第二个数减去第一个数,根据结果设置各标志位。循环指令loop,循环次数存放在CX中。指令jcxz,当CX=0时,则程序转移标号处执行。无条件指令jmp tag。
条件跳转指令:
无符号数条件转移指令:
指令的助忆符 | 检测的转移条件 | 功能描述 |
JE/JZ | ZF=1 | Jump Equal or Jump Zero |
JNE/JNZ | ZF=0 | Jump Not Equal or Jump Not Zero |
JA/JNBE | CF=0 and ZF=0 | Jump Above or Jump Not Below or Equal |
JAE/JNB | CF=0 | Jump Above or Equal or Jump Not Below |
JB/JNAE | CF=1 | Jump Below or Jump Not Above or Equal |
JBE/JNA | CF=1 or AF=1 | Jump Below or Equal or Jump Not Above |
指令的助忆符 | 检测的转移条件 | 功能描述 |
JE/JZ | ZF=1 | Jump Equal or Jump Zero |
JNE/JNZ | ZF=0 | Jump Not Equal or Jump Not Zero |
JG/JNLE | ZF=0 and SF=OF | Jump Greater or Jump Not Less or Equal |
JGE/JNL | SF=OF | Jump Greater or Equal or Jump Not Less |
JL/JNGE | SF≠OF | Jump Less or Jump Not Greater or Equal |
JLE/JNG | ZF=1 or SF≠OF | Jump Less or Equal or Jump Not Greater |
指令的助忆符 | 检测的转移条件 | 功能描述 |
JC | CF=1 | Jump Carry |
JNC | CF=0 | Jump Not Carry |
JO | OF=1 | Jump Overflow |
JNO | OF=0 | Jump Not Overflow |
JP/JPE | PF=1 | Jump Parity or Jump Parity Even |
JNP/JPO | PF=0 | Jump Not Parity or Jump Parity Odd |
JS | SF=1 | Jump Sign (negative) |
JNS | SF=0 | Jump No Sign (positive) |
字符串操作指令是对一片连续存储单元进行处理,这片存储单元是由隐含指针DS:SI或ES:DI来指定的。字符串操作指令可对内存单元按字节、字或双字进行处理,并能根据操作对象的字节数使变址寄存器SI(和DI)增减1、2或4。当DF=0时,变址寄存器SI(和DI)增加1、2或4;当DF=1时,变址寄存器SI(和DI)减少1、2或4。取字符串数据指令LODS,从由指针DS:SI所指向的内存单元开始,取一个字节、字或双字进入AL、AX或EAX中,并根据标志位DF对寄存器SI作相应增减。该指令的执行不影响任何标志位。置字符串数据指令STOS,该指令是把寄存器AL、AX或EAX中的值存于以指针ES:DI所指向内存单元为起始的一片存储单元里,并根据标志位DF对寄存器DI作相应增减。该指令不影响任何标志位。字符串传送指令MOVS,该指令是把指针DS:SI所指向的字节、字或双字传送给指针ES:DI所指向内存单元,并根据标志位DF对寄存器DI和SI作相应增减。该指令的执行不影响任何标志位。输入字符串指令ints,输出字符串指令outs。字符串比较指令cmps,该指令是把指针DS:SI和ES:DI所指向字节、字或双字的值相减,并用所得到的差来设置有关的标志位。重复前缀指令REP根据CX值确定重复执行后面的指令多少次。条件重复前缀指令,根据CX和zf值确定是否重复和重复几次,REPE和REPZ根据CX≠0 且 ZF=1则重复,REPNE和REPNZ根据CX≠0 且 ZF=0重复。
空指令操作NOP,等待指令WAIT,暂停指令HLT:在等待中断信号时,该指令使CPU处于暂停工作状态,CS:IP指向下一条待执行的指令。当产生了中断信号,CPU把CS和IP压栈,并转入中断处理程序。在中断处理程序执行完后,中断返回指令IRET弹出IP和CS,并唤醒CPU执行下条指令。封锁数据指令LOCK。
汇编程序可定义多个段,数据段、栈端、多个代码段,每段有自己的段名:name segment........name ends。通过assume伪指令每个段都要与一个段寄存器建立一种对应关系:ASSUME 段寄存器名:段名[,段寄存器名:段名, ……]。要定义堆栈段,来用压栈和出栈对方法和数据进行操作。伪指令END写在最后。
采用CMP和有条件跳转指令或无条件跳转指令配合达到if/else判断形成分支结构程序的目的。竟然还有类C的伪指令:.IF .ELSEIF .ELSE .ENDIF可以协助进程分支程序的设计。伪指令.WHILE .REPEAT协助设计循环程序结构。还有.BREAK .CONTUNUE来进程中断循环。
DATA1 | SEGMENT | |||
data | DW 90, 95, 54, 65, 36, 78, 66, 0, 99, 50, -1 | |||
Average | DW 0 | |||
DATA1 | ENDS | |||
CODE1 | SEGMENT | |||
ASSUME CS:CODE1, DS:DATA1 | ||||
START: | MOV | AX, DATA1 | ||
MOV | DS, AX | |||
XOR | AX, AX | |||
XOR | DX, DX | ;用(DX,AX)来保存数组元素之和 | ||
XOR | CX, CX | ;用CX来保存数组元素个数 | ||
LEA | SI, data | ;用指针SI来访问整个数组 | ||
again: | MOV | BX, word ptr [SI] | ||
CMP | BX, 0 | |||
JL | over | |||
ADD | AX, BX | |||
ADC | DX, 0 | ;把当前数组元素之值加到(DX,AX)中 | ||
INC | CX | ;数组元素个数加1 | ||
ADD | SI, 2 | |||
JMP | again | |||
over: | JCXZ | exit | ;防止零作除数,即数组是空数组 | |
DIV | CX | |||
MOV | Average, AX | |||
exit: | MOV | AX, 4C00H | ||
INT | 21H | |||
CODE1 | ENDS | |||
END | START |
段组伪指令GROUP是用于把源程序模块中若干个段结合成一个组,并对该段组定义一个段组名。段组伪指令的格式如下:段组名 GROUP 段名[, 段名,……]。
子程序调用指令CALL 子程序名/deg/mem。若是近调用,CALL指令将产生一个近调用,它把该指令之后地址的偏移量(用一个字来表示的)压栈,把被调用子程序入口地址的偏移量送给指令指针寄存器IP即可实现执行程序的转移。若是远调用,调用指令不仅要把该指令之后地址的偏移量压进栈,而且也要把段寄存器CS的值压进栈。在此之后,再把被调用子程序入口地址的偏移量和段值分别送给IP和CS,这样完成了子程序的远调用操作。子程序返回指令RET/RETF/RETN,在近类型的子程序中,返回指令RET是近返回,其功能是把栈顶之值弹出到指令指针寄存器IP中,SP会被加2;若是远返回,则先出栈至IP,再出栈至CS,同时SP加4。
而子程序是需要传递参数的,一般是寄存器传参、内存单元存放参数和栈保存参数。寄存器传参适用于参数少的子程序因为寄存器本身就少。内存地址存放需要指定位置存放和取得参数。当用堆栈传递入口参数时,要在调用子程序前把有关参数依次压栈,子程序从堆栈中取到入口参数;当用堆栈传递出口参数时,要在子程序返回前,把有关参数依次压栈(这里还需要做点额外操作,要保证返回地址一定在栈顶),调用程序就可以从堆栈中取到出口参数。在子程序中,保存和恢复寄存器内容的主要方法是:在子程序的开始把它所用到的寄存器压进栈,在返回前,再把它们弹出栈。这样编写的好处是该子程序可以被任何其它程序来调用。在调用指令前,不需要保存寄存器,在调用指令后,也无需恢复寄存器。
子程序伪指令invoke类似于call。伪指令LOCAL来说明一个或者多个局部变量:LOCAL 变量名[数量][:数据类型],.......。伪指令PUBLIC tag1, tag2.......说明这些标识符可以为其他模块引用的公共标识符。伪指令EXTERN tag1:type, tag2:type......说明这些标识符已经在其他模块中定义了。类似于C语言。
CPU区分不同的I/O设备使用I/O端口,形成端口地址空间。主要的端口地址:
端口地址 | 端口名称 | 端口地址 | 端口名称 |
020H~023H | 中断屏蔽寄存器 | 378H~37FH | 并行口LPT2 |
040H~043H | 时针/计数器 | 3B0H~3BBH | 单色显示器端口 |
060H | 键盘输入端口 | 3BCH~3BFH | 并行口LPT1 |
061H | 扬声器(0, 1位) | 3C0H~3CFH | VGA/EGA |
200H~20FH | 游戏控制口 | 3D0H~3DFH | CGA |
278H~27FH | 并行口LPT3 | 3F0H~3F7H | 磁盘控制器 |
2F8H~2FFH | 串行口COM2 | 3F8H~3FFH | 串行口COM1 |
中断分为外部中断和内部中断,分为可屏蔽中断和不可屏蔽中断,分为硬件中断和软件中断,由中断控制器interupter handler控制。中断类型以中断类型码指示,利用中断向量表来指示中断向量地址即中断处理程序开始地址。主要中断号:
中断号 | 含义 | 中断号 | 含义 |
0 | 除法出错 | 8 | 定时器 |
1 | 单步 | 9 | 键盘 |
2 | 非屏蔽中断 | A | 未用 |
3 | 断点 | B | COM2 |
4 | 溢出 | C | COM1 |
5 | 打印屏幕 | D | 硬盘(并行口) |
6 | 未用 | E | 软盘 |
7 | 未用 | F | 打印机 |
汇编中也有宏的概念,利用宏名来代替一段程序代码。宏定义:
宏名 MACRO [参数]
........宏主体
宏名 ENDM
而宏的引用形式为:宏名 [参数]。参数可以是常量、寄存器、表达式、内存地址。宏内可以引用其他宏,可以定义宏。“PURGE 宏名”表示解除宏定义。宏可以进行宏扩展。
要在C语言中嵌入汇编语言,可以asm mov ax,data,可以嵌入一组asm {........}。
难点:使用栈来存储参数,会使得数据和地址都存在栈里面。当用栈传递入口参数时要在子程序前把有关参数依次压栈,子程序从堆栈中取得参数;;当用栈传递出口参数时要在子程序返回之前把有关参数依次压栈而且要保证返回地址一定在栈顶。如果是段内调用,call指令会把IP值压栈,子程序要用BP来访问栈,所以子程序要先保存BP的值在栈中,再把当前SP即栈顶偏移量传给BP,用BP来取得参数:
子程序寄存器保存后的栈
SUBPRO | PROC NEAR | |||
PUSH | BP | ;保护寄存器BP | ||
MOV | BP, SP | ;用寄存器BP来访问堆栈,读取参数 | ||
… | ;保护其它寄存器的指令 | |||
MOV | Paran, [BP+4] | ;保护其它寄存器的指令 | ||
… | ||||
MOV | Para1, [BP+4+2*(n-1)] | |||
… | ||||
SUBPRO | ENDP |
汇编语言代码段:
DATA1 | SEGMENT | |||
data | DW 90, 95, 54, 65, 36, 78, 66, 0, 99, 50, -1 | |||
Average | DW 0 | |||
DATA1 | ENDS | |||
CODE1 | SEGMENT | |||
ASSUME CS:CODE1, DS:DATA1 | ||||
START: | MOV | AX, DATA1 | ||
MOV | DS, AX | |||
XOR | AX, AX | |||
XOR | DX, DX | ;用(DX,AX)来保存数组元素之和 | ||
XOR | CX, CX | ;用CX来保存数组元素个数 | ||
LEA | SI, data | ;用指针SI来访问整个数组 | ||
again: | MOV | BX, word ptr [SI] | ||
CMP | BX, 0 | |||
JL | over | |||
ADD | AX, BX | |||
ADC | DX, 0 | ;把当前数组元素之值加到(DX,AX)中 | ||
INC | CX | ;数组元素个数加1 | ||
ADD | SI, 2 | |||
JMP | again | |||
over: | JCXZ | exit | ;防止零作除数,即数组是空数组 | |
DIV | CX | |||
MOV | Average, AX | |||
exit: | MOV | AX, 4C00H | ||
INT | 21H | |||
CODE1 | ENDS | |||
END | START |
DATA1 | SEGMENT | |||
data | DW 90, 95, 54, 65, 36, 78, 66, 0, 99, 50, -1 | |||
Average | DW 0 | |||
DATA1 | ENDS | |||
CODE1 | SEGMENT | |||
ASSUME CS:CODE1, DS:DATA1 | ||||
START: | MOV | AX, DATA1 | ||
MOV | DS, AX | |||
XOR | AX, AX | |||
XOR | DX, DX | ;用(DX,AX)来保存数组元素之和 | ||
XOR | CX, CX | ;用CX来保存数组元素个数 | ||
LEA | SI, data | ;用指针SI来访问整个数组 | ||
again: | MOV | BX, word ptr [SI] | ||
CMP | BX, 0 | |||
JL | over | |||
ADD | AX, BX | |||
ADC | DX, 0 | ;把当前数组元素之值加到(DX,AX)中 | ||
INC | CX | ;数组元素个数加1 | ||
ADD | SI, 2 | |||
JMP | again | |||
over: | JCXZ | exit | ;防止零作除数,即数组是空数组 | |
DIV | CX | |||
MOV | Average, AX | |||
exit: | MOV | AX, 4C00H | ||
INT | 21H | |||
CODE1 | ENDS | |||
END | START |
DATA1 | SEGMENT | |||
MSG1 | DB 13, 10, "Iteration: " | |||
NUM1 | DB '1', "$" | |||
MSG2 | DB 13, 10, "Alphabet: $" | |||
NUM2 | DB 'A', " $" | |||
MSG3 | DB 13, 10, "Type digits, then press ENTER: $" | |||
DATA1 | ENDS | |||
CODE1 | SEGMENT | |||
ASSUME CS:CODE1, DS:DATA1 | ||||
START: | MOV | AX, DATA1 | ||
MOV | DS, AX | |||
MOV | CX,9 | |||
MOV | AH, 09H | |||
MOV | DX, OFFSET MSG1 | |||
.REPEAT | ||||
INT 21H INC NUM1 | ;显示Iteration: 1,2,~,9 | |||
.UNTILCXZ | ||||
MOV | DX,OFFSET MSG2 | |||
INT | 21H | ;显示字符串"Alphabet:" | ||
MOV | AH, 09H | |||
MOV | DX, OFFSET NUM2 | |||
.REPEAT | ||||
INT 21H INC NUM2 | ;显示当前字母 ;当前字母向后移 | |||
.UNTIL NUM2 > 'Z' | ;显示整个大写字母表 | |||
MOV | AH, 09H | |||
MOV | DX, OFFSET MSG3 | |||
INT | 21H | |||
.WHILE 1 | ;循环条件为永真的循环 | |||
MOV AH, 07H INT 21H | ;不带回显地从键盘读一个字符 | |||
.BREAK .IF AL == 13 | ;如果输入“回车”键,则终止循环 | |||
.CONTINUE .IF(AL<'0') || (AL>'9') | ;如果字符不是数字字符,则继续循环 | |||
MOV DL, AL MOV AH, 02H INT 21H | ;显示所输入的数字字母 | |||
.ENDW | ||||
MOV | AX, 4C00H | |||
INT | 21H | |||
CODE1 | ENDS | |||
END | START |
参考资料:
http://www.feiesoft.com/asm/07-3-3.html
DATA1 | SEGMENT | |||
data | DW 90, 95, 54, 65, 36, 78, 66, 0, 99, 50, -1 | |||
Average | DW 0 | |||
DATA1 | ENDS | |||
CODE1 | SEGMENT | |||
ASSUME CS:CODE1, DS:DATA1 | ||||
START: | MOV | AX, DATA1 | ||
MOV | DS, AX | |||
XOR | AX, AX | |||
XOR | DX, DX | ;用(DX,AX)来保存数组元素之和 | ||
XOR | CX, CX | ;用CX来保存数组元素个数 | ||
LEA | SI, data | ;用指针SI来访问整个数组 | ||
again: | MOV | BX, word ptr [SI] | ||
CMP | BX, 0 | |||
JL | over | |||
ADD | AX, BX | |||
ADC | DX, 0 | ;把当前数组元素之值加到(DX,AX)中 | ||
INC | CX | ;数组元素个数加1 | ||
ADD | SI, 2 | |||
JMP | again | |||
over: | JCXZ | exit | ;防止零作除数,即数组是空数组 | |
DIV | CX | |||
MOV | Average, AX | |||
exit: | MOV | AX, 4C00H | ||
INT | 21H | |||
CODE1 | ENDS | |||
END | START |