看完这篇文章就不要再问我汇编了

先来个小栗子再来解释:
看完下面的内容相信你已经知道汇编怎么注释了。

;.asm文件命名不能超过8个英文字符,否则不能打开
;我的学号为52013149520,学号的最后一位为0,编写程序测试6B7AH的第0位,如果为0,屏幕上显示0,否则屏幕显示1
;我用的是根据学号数将DX=0001H左移几位,测试值和DX相与,再和DX比较,根据标志跳转,从而输出01;也可以采用TEST等等
data SEGMENT  					;数据段,编程者可以把数据都放到这个段里
	num dw 0,6B7AH	 			;学号最后一位(0~9),测试数值    啥都不加的时候,默认是10进制的
data ENDS      					;数据段结束处。
								;6B7AH=0110101101111010‬B	位数从右往左数	
zhengqian SEGMENT              	;代码段,实际的程序都是放这个段里,此处zhengqian为代码段名。
	ASSUME CS:zhengqian,DS:data ;告诉编译程序,data段是数据段DS,code段是代码段CS			
    start:	MOV AX,data			;前面的start表示一个标识位(可省略),后面用到该位,如果用不到,可不加
			MOV DS,AX         	;这一句与上一行共同组成把data赋值给DS段寄存器.
			MOV CL,DS:[0]   
			MOV BX,DS:[2]       ;此时bx内为6B7Ah
			MOV DX,0001H
			SHL DX,CL      		;逻辑左移CL位	
			AND BX,DX			;与操作,得bx,我的此时为0
			CMP BX,DX      		;再和dx比较,相等时结果为0,ZF=1 零标志,跳转,输出1 ,我的现在不相等不需跳转
			JZ	eql				
			MOV DL,48			;输出0
			JMP s				;无条件跳转
	  eql:  MOV DL,49           ;输出1
	    s:	MOV AH,02H			;2号功能
			INT 21H 
			MOV AX,4C00H      	;程序退出,该句内存由下一行决定。退出时,要求ah必须是4c。
			INT 21H             ;记住规定这样
zhengqian ENDS          		;代码段结束。
END start           			;整个程序结束,并且程序执行时由start那个位置开始执行。

;ptr-pointer(指针)缩写保留字  mov ax,word ptr [bx]<===>MOVE AX,BX因为同类型所以不用word  是把内存地址等于“BX寄存器的值”的地方所存放的数据,赋予ax    
 ;cmp WORD PTR[SI+BX],0	;拿一个“字”,和零,相比较	


其实我完全可以用test更快更好的解决,我为什么要这样写呢。
(因为一开始学更好理解一些)

进入正题:
先学几个指令8

数据传送指令:
把数据从一一个位置传送到另一个位置
计算机中最基本的操作
程序设计中最常使用的指令
除标志寄存器传送指令外,均不影响状态标志
MOV XCHG PUSH POP LEA
MOV (Move): 最常用的数据传送指令
功能: 把源操作数送给目的操作数
语法: MOV 目的操作数,源操作数
格式:
MOV AX,2000H;将16位数据2000H传送到AX寄存器
MOV AL,20H;将8位数据20H传送到AL寄存器
MOV AX,BX;将BX寄存器的16位数据传送到AX寄存器
MOV AL,[2000H];将2000H单元的内容传送到AL寄存器
将一个数据从源地址传送到目标地址(寄存器间的数据传送本质上也是一样的)
1.两个存储单元之间不能直接传送数据
即:MOV指令只允许一个操作数在存储器中。MOV [SI],[2000H];这是错误的
2.MOV指令中立即数不能直接传送给段寄存器(CS、DS、SS、ES)和IP;段寄存器之间不能直接传送。MOV IP,2000 H ;这是错误的
3. CS和IP不能作为目的操作数。MOV CS,AX ;这是错误的
4. MOV指令中立即数不能作目标操作数。MOV 2000H,[SI] ;这是错误的
MOV指令可以在CPU内或CPU和存储器之间传送字或字节,它传送的信息可以从寄存器到寄存器,立即数到寄存器,立即数到存储单元,从存储单元到寄存器,从寄存器到存储单元,从寄存器或存储单元到除CS外的段寄存器(注意立即数不能直接送段寄存器),从段寄存器到寄存器或存储单元。
MOV指令不会影响标志位
MOV指令中绝对不允许在两个段寄存器之间直接传送数据
MOV指令中绝对不允许在两个存储单元之间直接传送数据
段寄存器(段地址)必须通过寄存器如AX寄存器进行立即数的初始化
MOV AL,‘E’
把立即数(字符E的ASC码)送到AL寄存器
MOV BX,OFFSET TABLE
将TABLE的偏移地址(而不是内容)送到BX寄存器中。其中OFFSET为属性操作符,表示的是将其后的符号地址的值(不是内容)作为操作数
MOV AX,Y[BP][SI]
把地址为16d×(SS)十(BP)十(SI)十位移量Y的存储单元的内容送给AX寄存器

XCHG (Exchange): 交换指令
功能: 交换两个操作数的数据
语法: XCHG
格式: XCHG r1,r2 XCHG m,r XCHG r,m
交换指令XCHG是两个寄存器,寄存器和内存变量之间内容的交换指令,两个操作数的数据类型要相同,可以是一个字节,也可以是一个字,也可以是双字。寄存器不能是段寄存器,两个操作数也不能同时为内存变量。XCHG指令不影响标志位。
1. 不能同时都为内存操作数
2. 任何一个操作数都不能为段寄存器
3. 任何一个操作数不能为立即数
4. 两个操作数的长度不能不相等
XCHG指令的功能和MOV指令不同,MOV指令是一个操作数的内容被修改,而前者是两个操作数都会改变

PUSH,POP
功能: 把操作数压入或取出堆栈
语法: PUSH操作数POP 操作数
格式: PUSH r PUSH M PUSH data POP r POP m
PUSH F, POP F, PUSH A, POP A
功能:堆栈指令群

push寄存器:将一个寄存器中的数据入栈
pop寄存器:出栈用一个寄存器接收数据
push pop 只对字操作(不允许字节进栈)操作数长度为32位时进出栈为双字
如 push dl是不正确的,应该是push dx
PUSH导致SP减2而不是加2。原因:栈在内存中实际存储结构是栈底在高地址,栈顶在低地址(x86中的栈是“满递减栈”,也就是sp指向的栈的内存单元中是满的,有内容的,而且push数据时,栈顶对应的地址是递减的)
8086push不可以使用立即数寻址方式(其他版本允许)
pop不可以使用立即数寻址方式,使用段寄存器时不可使用CS段寄存器
SP是个寄存器,SP的内容是地址,sp指向栈顶,而栈顶的内容是什么与(sp)是什么无关。

LEA,LDS,LES
功能: 取地址至寄存器
语法: LEA r,m LDS r,m LES r,m
LEA指令返回间接操作数的偏移地址,间接操作数可能使用一个或多个寄存器,
因此其偏移值是在运行时计算的。 地址传送指令load effective address
地址传送指令获取存储器操作数的地址
LEA指令类似地址操作符OFFSET的作用
跟offset的区别:
lea edi, var mov edi, offset var
在这里插入图片描述
LEA(Load Effective Address): 有效地址传送指令
LEA指令在指令执行时计算出偏移地址
OFFSET操作符在汇编阶段取得变量的偏移地址
0FFSET无需在执行时计算、指令执行速度更快
LEA指令能获取汇编阶段无法确定的偏移地址
LEA指令是在执行过程中获得地址,当无法在汇编阶段获得地址时,就用LEA指令
在这里插入图片描述
在这里插入图片描述
LEA BX,[SI] ;BX<–DS:[SI]内存单元的偏移地址,与该单元中的数据无关
LDS BX,[SI] ;BX<–DS:[si]内存单元中的数据的低16位,DS<–内存单元中数据的高16位
LES BX,[SI] ;BX<–DS:[SI] ,ES<–DS:[SI+2]
显然LDS和LES的操作数是32位的(两个字 四个字节 32位)
LEA只是传送DS:[]指向内存单元的偏移地址
LDS的传送并不是偏移地址而是实实在在的内存单元中的数据

LDS REG,OPRE;REG是一个16位寄存器,OPRD是一个存储器操作数,为双字项,高16位送入DS,低16位送入REG
LDS BX,[SI] (ds:si取得存储器中32位(双字),高16位作为段值送入DS中,低十六位送入指定的BX寄存器中)

XLAT(XLATB) XLATB(Translate Byte)、XLAT(Translate): 换码指令
功能:查表指令 XLATB 可简化为 XLAT
语法: XLAT XLAT m XLAT 无参数, 操作和 EBX、AL 相关
查表,就是查找表中元素
DATA SEGMENT
A DB ‘HELLO WORLD’,'$';’$'作为停止
DATA ENDS
XLAT指令可用于数字的加密

算数运算指令
ADD,ADC ( ADD with Carry)带进位加法指令
功能: 加法指令 //Byte/Word
语法: ADD OP1,OP2 ADC OP1,OP2 //与ADD不同是要加上进位标志位的值.
格式: ADD r1,r2 ADD r,m ADD m,r ADD r,data
影响标志: C,P,A,Z,S,O
两个存储器操作数不能通过ADD指令直接相加,必须有一个是通用寄存器操作数.
段寄存器不能作为SRC 和DST
影响标志位Auxiliary Crray Flag ,Carry Flag, Overflow Flag, Parity Flag, Sign Flag 和Zero Flag
在这里插入图片描述
SUB ( SUBtract )减法指令,SBB( SuBtract with Borrow) 带借位减法指令
功能:减法指令
语法: SUB OP1,OP2 SBB OP1,OP2
格式: SUB r1,r2 SUB r,m SUB m,r SUB r,data SUB m,data
影响标志: C,P,A,Z,S,O
INC ( INCreament) 加1指令,DEC
功能: 把OP的值加一或减一 //Byte/Word
语法: INC OP DEC OP OP是寄存器和存储器操作数, 不能是立即数和段寄存器
格式: INC r/m DEC r/m
影响标志: P,A,Z,S,O,不影响CF
MUL( unsigned MULtiple )乘法指令,IMUL (sIgned MULtiple) 有符号数乘法指令
功能: 乘法指令 目的数必须是累加器 AX 或AL,指令中不需写出
语法: MUL OP IMUL OP //与MUL指令相同,但必须是带符号数
格式: MUL r/m IMUL r/m
影响标志: C,P,A,Z,S,O(仅IMUL会影响S标志)
//字节运算时目的操作数用AL, 乘积放在AX中
//字运算时目的操作数用AX, DX存放乘积的高位字, AX放乘积的低位字
源操作数SRC可以是通用寄存器和各种寻址方式的存储器操作数, 而绝对不允许是立即数或段寄存器.
DⅣ( unsigned DIVide)除法指令,IDⅣ(sIgned DIVied) 有符号数除法指令
功能:除法指令 //字节运算时目的操作数在AX中,结果的商在AL中 ,余数在AH中
语法: DⅣ OP IDⅣ OP
//字运算时目的操作数在DX高位字和AX低位字中,结果的商在AX中 ,余数在DX中
格式: DⅣ r/m IDⅣ r/m

逻辑运算指令

NEG( NEGate ) 求补指令
功能: 将OP的符号反相(取二进制补码)
语法: NEG OP //将操作数按位求反后末位加1
格式: NEG r/m
影响标志: C,P,A,Z,S,O
CBW(Convert Byte to Word) 字节转换为字指令,
执行操作: AL中的符号位(D7)扩展到8位AH中,若AL中的D7=0,则AH=00H,若AL中的D7=1,则AH=FFH.
CWD(Convert Word to Double word) 字转换为双字指令
执行操作: AX中的符号位(D15)扩展到16位DX中,若AX中的D15=0,则DX=0000H,若AX中的D15=1,则DX=FFFFH.
功能:有符号数扩展指令
语法: CBW CWD
可用4位二进制数表示一位十进制数,这种代码称为BCD ( Binary Coded Decimal ).
BCD码又称8421码,在PC机中,BCD码可用压缩的BCD码和非压缩的BCD码两种格式表示.
AAA (ASCII Adjust for Addition) 加法的ASCII调整指令,
执行操作:执行之前必须先执行ADD或ADC指令,加法指令必须把两个非压缩的BCD码相加,并把结果存话在AL寄存器中.
AAS (ASCII Adjust for Subtraction) 减法的ASCII调整指令,
执行操作:执行之前必须先执行SUB或SBB指令,减法指令必须把两个非压缩的BCD码相减,并氢结果存放在AL寄存器中.
AAM,AAD
功能: 非压BCD码运算调整指令
语法: AAA AAS AAM AAD
影响标志: A,C(AAA,AAS) S,Z,P(AAM,AAD)

DAA (Decimal Adjust for Addition) 加法的十进制调整指令,
执行操作:执行之前必须先执行ADD或ADC指令,加法指令必须把两个压缩的BCD码相加,并把结果存话在AL寄存器中
DAS (Decimal Adjust for Subtraction) 减法的十进制调整指令
执行操作:执行之前必须先执行SUB或SBB指令,减法指令必须把两个压缩的BCD码相减,并氢结果存放在AL寄存器中.
功能:压缩BCD码调整指令
语法: DAA DAS
影响标志: C,P,A,Z,S

位运算集
AND,OR,XOR,NOT,TEST
功能: 执行BIT与BIT之间的逻辑运算
语法: AND r/m,r/m/data OR r/m,r/m/data XOR r/m,r/m/data TEST r/m,r/m/data NOT r/m
影响标志: C,O,P,Z,S(其中C与O两个标志会被设为0) NOT指令不影响任何标志位
SHR,SHL,SAR,SAL
功能:移位指令
语法: SHR r/m,data/CL SHL r/m,data/CL SAR r/m,data/CL SAL r/m,data/CL
影响标志: C,P,Z,S,O
ROR,ROL,RCR,RCL
功能: 循环移位指令
语法: ROR r/m,data/CL ROL r/m,data/CL RCR r/m,data/CL RCL r/m,data/CL
影响标志: C,P,Z,S,O
程序流程控制集
CLC,STC,CMC
功能: 设定进位标志
语法: CLC STC CMC
标志位: C
CLD,STD
功能: 设定方向标志
语法: CLD STD
标志位: D
CLI,STI
功能: 设定中断标志
语法: CLI STI
标志位: I
CMP( CoMPare ) 比较指令
功能: 比较OP1与OP2的值
执行操作:OPR1 - OPR2 //与SUB指令一样执行运算, 但不保存结果
语法: CMP r/m,r/m/data
标志位: C,P,A,Z,O

在这里插入图片描述
JMP
功能: 跳往指定地址执行
语法: JMP 地址
JXX
功能: 当特定条件成立则跳往指定地址执行
语法: JXX 地址
注:
A: ABOVE,当C=0,Z=0时成立
B: BELOW,当C=1时成立
C: CARRY,当弁时成立 CXZ: CX寄存器的值为0(ZERO)时成立
E: EQUAL,当Z=1时成立
G: GREATER(大于),当Z=0且S=0时成立
L: LESS(小于),当S不为零时成立
N: NOT(相反条件),需和其它符号配合使用
O: OVERFLOW,O=1时成立
P: PARITY,P=1时成立
PE: PARITY EVEN,P=1时成立
PO: PARITY ODD,P=0时成立
S: SIGN,S=1时成立
Z: ZERO,Z=1时成立
LOOP
功能: 循环指令集
语法: LOOP 地址
LOOPE(Z)
地址 LOOPNE(Z) 地址
标志位: 无
CALL,RET
功能:子程序调用,返回指令
语法: CALL 地址 RET RET n
标志位: 无
INT,IRET
功能: 中断调用及返回指令
语法: INT n IRET
标志位: 在执行INT时,CPU会自动将标志寄存器的值入栈,在执行IRET时则会将堆栈中的标志值弹回寄存器
串操作集
MOVSB,MOVSW,MOVSD
功能: 字符串传送指令
语法: MOVSB MOVSW MOVSD
标志位: 无
CMPSB,CMPSW,CMPSD
功能: 字符串比较指令
语法: CMPSB CMPSW CMPSD
标志位: C,P,Z,S,O
SCASB,SCASW
功能: 字符串搜索指令
语法:SCASBSCASW
标志位: C,P,Z,S,O
LODSB,LODSW,STOSB,STOSW
功能: 字符串载入或存贮指令
语法:LODSBLODSWSTOSBSTOSW
标志位: 无
REP,REPE,REPNE
功能: 重复前缀指令集
语法: REP 指令S REPE 指令S REPNE 指令S
标志位: 依指令S而定

既然知道常用命令了,加上工具我们就可以来试试啦!

masm.exe和link.exe必不可少!
来吧!一大波资源
windows下快捷键win+R输入cmd进入你写的文件名.asm目录下:
(忘了win10要dosbox))

汇编常用命令及实际操作:

先记住以下两个命令:D命令和Q命令。前者是显示内存内容,后者是退出DEBUG命令。

退出命令 Q
格式:Q
功能:退出DEBUG,返回到操作系统。

显示存储单元命令 D
格式1:D[起始地址]
格式2:D[起始地址][结束地址|字节数]
功能:格式1从起始地址开始按十六进制显示80H个单元的内容,每行16个单元,共8行,每行右边显示16个单元的ASCII码,不可显示的ASCII码则显示“·”。格式2显示指定范围内存储单元的内容,其他显示方式与格式1一样。如果缺省起始地址或地址范围,则从当前的地址开始按格式1显示。
例如: -D 200 ;表示从DS:0200H开始显示128个单元内容
-D 100 120742 ;表示显示DS:0100-DS:0120单元的内容

073F:011C=0750C === 0700:050C
物理地址=段基值(二进制左移4位)+偏移地址(相对地址)

可先-d 显示出存储单元
可看到073F:011C处为34,根据逻辑地址和物理地址关系,可得物理地址唯一。

输入-d 0700:050C可得出相应存储单元的值也是34,以此类推同样如此,相当于我们学习可以通过老师教授,或者网上听课,或者通过看书等等。
在这里插入图片描述
修改存储单元命令 E      关闭后重新打开会恢复默认值
格式1:E[起始地址] [内容表]
格式2:E[地址]
功能:
格式1按内容表的内容修改从起始地址开始的多个存储单元内容,即用内容表指定的内容来代替存储单元当前内容。
格式2是逐个修改指定地址单元的当前内容。

在这里插入图片描述
输入—E DS:0100 ‘var’ 12 34
从DS:0100 为起始单元的连续五个字节单元内容依次被修改为’V’、‘A’、‘R’、12H、34H

输入—E DS:0010之后,出现下面语句(除了5F是我输入的修改值):
073F:0010 A3.5F
其中073F:0010单元原来的值是A3H,5FH为输入的修改值。
只修改一个单元的内容,这时按回车键即可;
若还想继续修改下一个单元内容,此时应按空格键,就显示下一个单元的内容,需修改就键入新的内容,不修改再按空格跳过,如此重复直到修改完毕,按回车键返回“-”提示符。
如果在修改过程中,将空格键换成按“-”键,则表示可以修改前一个单元的内容。
在这里插入图片描述
汇编用户有很多东西可以调用。他们主要是:
1、BIOS提供的接口。硬件与软件的区分已越来越不明显,很多硬件不仅仅是电路,而还要提供一些固化写入硬件的一部分“程序”,这些程序以ROM的方式出现,汇编用户最大的好处就是可以直接使用这些“程序”,这些使用不仅功能强大,而且效率非常高。
2、DOS功能调用,作为操作系统也象BIOS一样向用户提供了相应的“程序”。 这些程序在很大程序上扩充了BIOS。与BIOS不同的是,这部分程序放在内存中,它可以被修改。而BIOS中不能再修改。
以上两种接口都通过一种相同的格式调用,这些程序统称为“中断”,现在可以将其认为是系统提供给你的函数。

3、系统共享数据区。编过程序的人都知道全局变量的好处,全局变量方便之外在于任何函数、过程都可以调用、读取、修改。全局变量不足之处是危险性,有一个过程改了这个变量值,其它的也得跟着改变了。DOS操作系统同样也提供了这样的共享数据区,该区是整个系统的共享区,任何程序都可以查找、修改。当然,修改某处必然会对其它程序造成影响
共享数据区在绝对地址:0040:0000开始

中断:现在问题是不同硬件不一样,即使相同硬件的ROM,不同版本,各个BIOS中断程序所处的位置也不一样,DOS中断也一样,不同版本、不同配置,在内存位置也不一样。
系统怎么知道你使用的那个中断程序在哪呢?
为了解决这一问题,DOS会在启动的时候,把所有这些(BIOS和DOS)中断的首地址保存到一个地址。这段地址是内存的绝对零地址(0000:0000)
每个地址在汇编程序员角度来看是二维的,分为段地址和偏移地址。
每个地址各占两个字节,所以要表示这个二维地址需要4个字节
所以每个中断首地址由4个字节表示。一共256个中断,占用了1024个字节的位置。
注意:这4个表示地址的字节,数据是由低向高的
比如12 34 56 78所表示的地址是:7856:3412

一般用INT M表示中断M,如果M是十六进制,则在后面加上一个H。比如19号中断,十六进制应该是13H。所以该中断就是INT 13H

找中断:
在这里插入图片描述
在这里插入图片描述
首先,D命令把中断首地址显示出来。每4个表示一个地址。其中INT 0的中断首地址为:F000:1060,INT 1的中断地址为:0070:0008……0070:0008是中断3的首地址。
后面那个U命令就表示显示该地址的“中断程序”的内存
可以试着找找INT 15的位置?F000:1060

验证系统共享数据区
系统共享数据区内容极为丰富,实在记不住哪么多了,
在DOS下,你每按一个键,系统都会记下来,下面我们一起找找这个键盘缓冲区的地址。
知道这个地址,你就可以作一个**“虚拟”键盘**,通过发命令来模拟某个人在按键
这个地址位于:0040:001E。 其中每个键有两个字节,一个字节是ASCII码,一个是扫描码。共16个。

在这里插入图片描述
既然是键盘缓冲区,每个输入的键都会显示在该区中
第一次我输入了“d 40:0”,你可以在此后显示数据右边字符中找到这些字符,是间隔开的。

第二次我输入“d 0040:0000”,则右边显示的是“d 0040:0000”的内容。你也可以找找。

第二讲 内存映象
之所以把这个内存单独放一章,是为了说明它的重要性
内存映象就是指当你把一个可执行文件(EXE或COM文件)放到内存后,整个内存“看”起来是什么样子的。
汇编程序只能访问1M的内存空间,所以下面就以1M内存为例。
以DOS操作系统作为讲解对象,所以所编出来的程序也仅是DOS程序。
通过winasm可以访问远远超过1M的空间,并且可以编出FOR windows的程序。

这1M内存如果我们不再以二维的方式看,而是一维的,线性地看。但描述还是以二维的方式描述,从最底端到最高端依次是:
1 中断向量区:该区由0000:0000~0000:03FF。这里存着系统的所有中断的中断向量表,对于中断向量表,你现在先理解为一些程序的首地址。由这个地址你就能找到该程序。
2 系统数据区:该区由0040:0000~0040:XXXX(不好意思,忘了),这里存着整个系统中,DOS操作系统要用的数据,由于这个区的数据对用户是开放的,所以用户当然也可以从这里读出来用。
3 DOS操作系统区:操作系统常驻内存,你向计算机发的每个命令其实都是操作系统执行的。这个区的大小主要是由操作系统的版本和用户的配置大小决定,如果是驱动程序配置,就放到根目录下的config.sys里,如果是程序,就放到autoexec.bat里。
4 用户程序,这个当然就是你执行的程序了,这种程序分两种,一种是扩展名为com文件,一种是exe文件,com文件最大只能是64K,所以com文件只适合小的程序。而exe,四个段可任何分配,并可扩充段,而且每个段的段地址可以任何改动,因此exe的访问内存能力大多了。这种格式访问能力只受地址结构的限制了。
用户程序所占的内存大小完全由程序本身决定,但最大,只能到640K。只能怪当前计算机软硬件设置高手高手高高手们(包括比尔盖茨)们的失误了,60年代的超级计算机只有36K的内存,所以他们就在80年代得到一个结论:640K的内存足够了。
如果用户程序大于由操作系统所占内存的顶底到640K之间的内存量,就会显示:内存不够,因而程序不能执行。如果小于这段内存,多余部分就空着。
5 从640K到1M-64K,这段内存就很难说清了。这段内存中有一部分被硬件占有,有一部分是显示缓冲区点有,还有一部分是系统ROM占有。
6 从1M-64K到1M之间的这段64K的内存叫作HMA高端内存区(high memory area)。这段内存是小孩没娘,说来话长,我们先不说他。

中断向量表就是所有中断向量首地址表,这里保存着每个中断程序的首地址,几乎所有的汇编书都把中断后面后面的章节中,并且对中断的解释也仅从字面意思解释,所以导致大学对中断的不重要和误解。没耐心的没到这个章节就不学汇编了,有耐心的到这里才豁然开朗。我现在不讲中断的原意。我直接告诉你,你把中断当成API也许更合适。也就是说,别人把很多已作好的功能放到了内存中。并且把调用这一功能的号告诉了你,你只要调用这些功能号,系统就自动从这个中断向量表中找到对应的中断,然后执行你的功能。

让你感受一下中断的魅力一下吧。比如中断21H的2A功能调用是读取系统的日期,这个调用的规则是,调用前AH寄存器置为2A。调用后年在CX中,月在DH中,日在DL中,星期在AL中。

在这里插入图片描述
mov ah,2a”表示调用功能号是2a号。“int 21”表示调用十六进制21号中断,“int 3”表示3号中断,表示程序运行到这一句时停一下。“g=100”表示从“073F:0100 ”开始执行。
CX=07E4H=2020 DH=03H=3 DL=1EH=30 AL=01H=1 (今天是2020.3.30周一)
AX=AH+AL DH+DL=DX 仅仅两行命令,就读到了现在的值

原来你在汇编里运行int 21时,系统就在上面的中断向量表中找到int 21的中断地址,该中断的地址应该位于:0000:0084~0000:0087
找到内容是:A014:00F0。然后系统就转到这个地址执行int 21。

系统区,很多DOS中断程序实现部分就在这个区。

640K~1M之间,这期间有些地方是ROM,有些地方是硬件的BIOS区。
ROM区:ROM区就是只读内存,也就是说这个区的数据只能读不能写。
在这里插入图片描述
通过上面测试,(其中66 55 44 33 22 11是我想改的值)发现该区数据仍然未改变。但你要是试别的RAM区的,肯定会变。

?显示缓冲区:在文本方式下,B800:0000开始的地址保存着屏幕上每个字符位置的值。
在文本方式下,屏幕被分为80 X 25。每个位置有两个值,一个值是ASCII字符,一个值是该ASCII的属性值(主要是颜色)。所以一个屏幕共有80X25X2=400个字符
我们来改:
-d b800:0000 0010 '显示屏幕缓冲区的内容,注意此时本行最左边的“-”是屏幕左上角。
B800:0000 2D 07 64 07 20 07 62 07-38 07 30 07 30 07 3A 07 -.d. .b.8.0.0.:.
B800:0010 30 0

看上面的命令,屏幕最上边一行是“-d b800:0000 0010”,所以他的内容就是“2D 07 64 07 20 07 62 07-38 07 30 07 30 07 3A 07”其中,2D是“-”的ASCII值,07是“-”的属性值。64是“d”的ASCII值,07是“d”的属性值。
现在修改这些值。我把左上角的字改成黄颜色的“-”,那当然是改b800:0001的属性值了。
-e b800:0001 0e
是不是左上角的颜色变成黄色了吗?
好了,把第二个字符变成绿色的“-”吧?
-e b800:0002 2d 0b
变了吗?

第三章 汇编指令
什么是机器语言?
前面提到“最早的计算机采用机器语言,这种语言直接用二进制数表示,通过直接输入二进制数,插拔电路板等实现,这种“编程”很容易出错,每个命令都是通过查命令表实现”。
比如要执行21号中断,需要查表,得到21号中断的指令就是CD 21。这样不管你通过什么方式,在内存指令位置,写入两个字节,一个是CD(这可不是音乐光盘,而是二进制数,转成十进制就是205),另一个是21(同样是十六进制,十进制是33)。

什么是汇编语言?
“既然是通过“查表”实现的,那当然也可以让计算机来代替人查表实现了。于是就产生了汇编语言”,汇编语言产生的重要目的就是用容易记的符号来代替容易出错的二进制数(或十六进制数)。
比如21号中断,机器语言是CD 21。而汇编语言就规定中断用int表示(interrupt的前三个字母),21号中断就成了int 21h。其中21后面的h表示是表示这个21是十六进制。由于大小写不敏感,所以int 21h写成下列方式都等价:
int 33 Int 21h INT 21H

汇编指令集:
数据传输指令
它们在存贮器和寄存器、寄存器和输入输出端口之间传送数据

  1. 通用数据传送指令.
    MOV 传送字或字节.
    MOVSX 先符号扩展,再传送.
    MOVZX 先零扩展,再传送.
    PUSH 把字压入堆栈.
    POP 把字弹出堆栈.
    PUSHA 把AX,CX,DX,BX,SP,BP,SI,DI依次压入堆栈.
    POPA 把DI,SI,BP,SP,BX,DX,CX,AX依次弹出堆栈.
    PUSHAD 把EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI依次压入堆栈.
    POPAD 把EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX依次弹出堆栈.
    BSWAP 交换32位寄存器里字节的顺序
    XCHG 交换或字节.( 至少有一个操作数为寄存器,段寄存器不可作为操作数)
    CMPXCHG 比较并交换操作数.( 第二个操作数必须为累加器AL/AX/EAX )
    XADD 先交换再累加.( 结果在第一个操作数里 )
    XLAT 字节查表转换.
    ─BX 指向一张 256 字节的表的起点, AL 为表的索引值 (0-255,即
    0-FFH); 返回 AL 为查表结果. ( [BX+AL]->AL )

  2. 输入输出端口传送指令.
    IN I/O端口输入. ( 语法: IN 累加器, {端口号│DX} )
    OUT I/O端口输出. ( 语法: OUT {端口号│DX},累加器 )
    端口由立即方式指定时, 其范围是 0-255; 由寄存器 DX 指定时,其范围是 0-65535.

  3. 目的地址传送指令.
    LEA 装入有效地址.
    例: LEA DX,string ;把偏移地址存到DX.

LDS 传送目标指针,把指针内容装入DS.
例: LDS SI,string ;把段地址:偏移地址存到DS:SI.
LES 传送目标指针,把指针内容装入ES.
例: LES DI,string ;把段地址:偏移地址存到ES:DI.
LFS 传送目标指针,把指针内容装入FS.
例: LFS DI,string ;把段地址:偏移地址存到FS:DI.
LGS 传送目标指针,把指针内容装入GS.
例: LGS DI,string ;把段地址:偏移地址存到GS:DI.
LSS 传送目标指针,把指针内容装入SS.
例: LSS DI,string ;把段地址:偏移地址存到SS:DI.

  1. 标志传送指令.
    LAHF 标志寄存器传送,把标志装入AH.
    SAHF 标志寄存器传送,把AH内容装入标志寄存器.
    PUSHF 标志入栈.
    POPF 标志出栈.
    PUSHD 32位标志入栈.
    POPD 32位标志出栈.

二、算术运算指令
ADD 加法.
ADC 带进位加法.
INC 加 1.
AAA 加法的ASCII码调整.
DAA 加法的十进制调整.

SUB 减法.
SBB 带借位减法.
DEC 减 1.

NEC 求反(以 0 减之).
CMP 比较.(两操作数作减法,仅修改标志位,不回送结果).
AAS 减法的ASCII码调整.
DAS 减法的十进制调整.

MUL 无符号乘法.
IMUL 整数乘法.
以上两条,结果回送AH和AL(字节运算),或DX和AX(字运算),
AAM 乘法的ASCII码调整.

DIV 无符号除法.
IDIV 整数除法.
以上两条,结果回送:
商回送AL,余数回送AH, (字节运算) 或 商回送AX,余数回送DX, (字运算).
AAD 除法的ASCII码调整.
CBW 字节转换为字. (把AL中字节的符号扩展到AH中去)
CWD 字转换为双字. (把AX中的字的符号扩展到DX中去)
CWDE 字转换为双字. (把AX中的字符号扩展到EAX中去)
CDQ 双字扩展. (把EAX中的字的符号扩展到EDX中去)

三、逻辑运算指令
AND 与运算.
OR 或运算.
XOR 异或运算.
NOT 取反.
TEST 测试.(两操作数作与运算,仅修改标志位,不回送结果).
SHL 逻辑左移.
SAL 算术左移.(=SHL)
SHR 逻辑右移.
SAR 算术右移.(=SHR)
ROL 循环左移.
ROR 循环右移.
RCL 通过进位的循环左移.
RCR 通过进位的循环右移.
以上八种移位指令,其移位次数可达255次.
移位一次时, 可直接用操作码. 如 SHL AX,1.
移位>1次时, 则由寄存器CL给出移位次数.
如 MOV CL,04
SHL AX,CL

四、串指令
DS:SI 源串段寄存器 :源串变址.
ES:DI 目标串段寄存器:目标串变址.
CX 重复次数计数器.
AL/AX 扫描值.
D标志 0表示重复操作中SI和DI应自动增量; 1表示应自动减量.
Z标志 用来控制扫描或比较操作的结束.
MOVS 串传送.
( MOVSB 传送字符. MOVSW 传送字. MOVSD 传送双字. )
CMPS 串比较.
( CMPSB 比较字符. CMPSW 比较字. )
SCAS 串扫描.
把AL或AX的内容与目标串作比较,比较结果反映在标志位.

LODS 装入串.
把源串中的元素(字或字节)逐一装入AL或AX中.
( LODSB 传送字符. LODSW 传送字. LODSD 传送双字. )
STOS 保存串.
是LODS的逆过程.
REP 当CX/ECX<>0时重复.
REPE/REPZ 当ZF=1或比较结果相等,且CX/ECX<>0时重复.
REPNE/REPNZ 当ZF=0或比较结果不相等,且CX/ECX<>0时重复.
REPC 当CF=1且CX/ECX<>0时重复.
REPNC 当CF=0且CX/ECX<>0时重复.

五、程序转移指令
无条件转移指令 (长转移)
JMP 无条件转移指令
CALL 过程调用
RET/RETF过程返回.
条件转移指令 (短转移,-128到+127的距离内)
( 当且仅当(SF XOR OF)=1时,OP1<op2 )
JA/JNBE 不小于或不等于时转移.
JAE/JNB 大于或等于转移.
JB/JNAE 小于转移.
JBE/JNA 小于或等于转移.
以上四条,测试无符号整数运算的结果(标志C和Z).
JG/JNLE 大于转移.
JGE/JNL 大于或等于转移.
JL/JNGE 小于转移.
JLE/JNG 小于或等于转移.
以上四条,测试带符号整数运算的结果(标志S,O和Z).
JE/JZ 等于转移.
JNE/JNZ 不等于时转移.
JC 有进位时转移.
JNC 无进位时转移.
JNO 不溢出时转移.
JNP/JPO 奇偶性为奇数时转移.
JNS 符号位为 “0” 时转移.
JO 溢出转移.
JP/JPE 奇偶性为偶数时转移.
JS 符号位为 “1” 时转移.
3>循环控制指令(短转移)
LOOP CX不为零时循环.
LOOPE/LOOPZ CX不为零且标志Z=1时循环.
LOOPNE/LOOPNZ CX不为零且标志Z=0时循环.
JCXZ CX为零时转移.
JECXZ ECX为零时转移.
4>中断指令
INT 中断指令
INTO 溢出中断
IRET 中断返回
5>处理器控制指令
HLT 处理器暂停, 直到出现中断或复位信号才继续.
WAIT 当芯片引线TEST为高电平时使CPU进入等待状态.
ESC 转换到外处理器.
LOCK 封锁总线.
NOP 空操作.
STC 置进位标志位.
CLC 清进位标志位.
CMC 进位标志取反.
STD 置方向标志位.
CLD 清方向标志位.
STI 置中断允许位.
CLI 清中断允许位.
六、伪指令
DW 定义字(2字节).
PROC 定义过程.
ENDP 过程结束.
SEGMENT 定义段.
ASSUME 建立段寄存器寻址.
ENDS 段结束.
END 程序结束.

寄存器在CPU中。内存在内存条中。前者的速度比后者快100倍左右。
程序要求每条指令要么没有内存数据,要么在有一个寄存器的参与下有一个内存数据。(也就是说,不存在只访问内存的指令)。”
寄存器是在CPU中的存储器,而内存是在内存条中的存储器。

CPU访问寄存器,只需要通过微指令直接就可以访问,而访问内存则要先经过总线,再由总线到达内存控制器,读到某单元的内存数据后放上总线,再传到CPU中,CPU才能使用。

8086系列计算机的寄存器,共有14个,每个都是十六位的。
AX,BX,CX,DX,SP,BP,SI,DI,CS,DS,SS,ES,IP,FLAGS。
其中前四位,每个可以单位再分成两个,
AX=AH+AL,BX=BH+BL,CX=CH+CL,DX=DH+DL。这些分开的每个都是8位的。

可以理解为AX是由AH和AL组合成的,你给AL赋值,就意味着同时给AX的低半部赋值。你给AX赋值,就意味着同时改变AH和AL。这样作的好处是你可以更灵活地控制这个寄存器。

没让你一下学会,(其实有些永远也不会似乎也不是什么大不了的事)把其中的指令挑几个重点的,你必须要记住,其它的慢慢学吧。

1数据传输指令。
mov A,B
这个指令是把B中的数据复制给A,(B中仍保存原状)。这里的A和B可以是寄存器,可以是内存。可以同时是寄存器,但不能同时是内存。比如
mov ax,100 ;这是对的,注意100在这里叫立即数,但这个数在编译系统编译成exe的时候保存在内存中。
学过别的高级语言,你就可以理解为这就是赋值语句 Let ax=100/ax:=100;/ax=100。

2 伪指令
伪指令不是真的指令,但它同时又是指令。之所以说这样矛盾的话,是因为伪指令不是机器语言的一部分,而是汇编语言的一部分,是你告诉汇编的编译系统如何去作。

string DB '这是我的第一个汇编语言程序$'

上面一行指令中,DB就是伪指令,他的作用就是告诉编译程序,把后面一些数据或字符串放到内存中。当然对于exe来说,已在内存中了,就不用“告诉”了。(这就是为什么叫伪指令)。string是你给这段内存起的名字,如果你不需要这段内存,不起名字也可以,但如果后面要用,当然要加上这个名字。'这是我的第一个汇编语言程序$’这个就是要处理的数据,当然你也可以换成别的内容,但需要注意的是,要以$结尾,这是汇编的约写,即:只是到了$,就认为字符串结束,否则就一直向下找,直到找到一个 为 止 。 所 以 这 就 要 求 你 的 字 符 串 中 不 能 有 ‘ 为止。所以这就要求你的字符串中不能有` `,如果必须有,再换别的处理方式,后面再说。

3 地址传送指令
Lea A,string
前面已经定义了string,后面要把地址找到,就要用到lea指令。lea是把字符串的地址给A这个寄存器中,A当然可以上前面提到的任意寄存器。注意地址和内容的区别。如果是内容就是把string的字符串给A了。(当然这也不成立,一个字符串有很多字节,而一个寄存器只有两个字节)。
那么从上面也看到了,string代表一个地址,lea把这个地址给了A,那这个地址到底在哪里呢?事实上这不重要,就象你要把某书店买书,这个书店在哪并不是最重要的,有没有你要的书才是最重要的。所以你前面标出string,后面引用就行了,至于这个地址到底在哪是编译程序的事,不是你的事。

4 运算指令
ADD A,N
这个很容易理解吧,寄存器A加上N,把和仍存在A中。
类似于高级语言中的let a=a+n/a:=a+n/a+=n。

5 串操作指令
记住串操作指令表面很复杂,其实很简单。
因为他就像一个复杂的数学公式一样简单,你所要记住的就是公式的格式,使用时具体套用即可。

从一个地址到另一个地址的复制需要注意的是:
*把源串段地址给DS。
*把源串编址给SI。
*把目的串段址给ES。
*把目的串偏址给DI。
*把要复制的个数给CX,这里可不考虑$了。
*把FLAG中的方向标志标志你要的方向,一个是顺向,另一个是逆向。
*发送loop movs,scans等命令。

6 转移指令
记住:无条件转移指令 jmp。等于转 jz,不等于时转jnz

7 中断指令
int 中断号,注意进制默认是十进制,所以十六进制就加h

汇编程序框架
data SEGMENT '数据段,编程者可以把数据都放到这个段里
…数据部分 '数据格式是: 标识符 db/dw 数据。
data ENDS '数据段结束处。

edata SEGMENT '附加数据段,编程者可以把数据都放到这个段里
…附加数据部分
edata ENDS '附加数据段结束处。

code SEGMENT '代码段,实际的程序都是放这个段里。
ASSUME CS:code,DS:data,ES:edata '告诉编译程序,data段是数据DS,code段是代码段CS
start:MOV AX,data '前面的start表示一个标识位,后面用到该位,如果用不到,可不加
MOV DS,AX '这一句与上一行共同组成把data赋值给DS段寄存器.
MOV AX,edata
MOV ES,AX '与前一句共同组成edata->ES
…….程序部分
MOV AX,4C00h '程序退出,该句内存由下一行决定。退出时,要求ah必须是4c。
INT 21h
code ENDS '代码段结束。
END start '整个程序结束,并且程序执行时由start那个位置开始执行。

在这个结构中,有三个段,DS,ES,CS。这三个段分别存数据,附加数据,代码段。

编写我们的Hello,world思路。
开始编写我们的第一个程序。
程序要求:显示一个“Hello,ZHENGQIAN.”怎么样?
思路:
1 要显示一个字符串,根据前面我让你们记的七八个指令够吗?答案是:不仅够,而且还用不完。
首先定义一下总可以吧。

hellostr db 'Hello, ZHENGQIAN.$'

最后的$不要忘了。

2 首先要考虑的问题就是找中断,找到合适的中断,该中断就能帮我们完成这个显示任务。我找到(在哪找到的,怎么找到的,别问我,到网上或书上都能找到):
中断INT 21H功能09H
功能描述: 输出一个字符串到标准输出设备上。如果输出操作被重定向,那么,将无法判断磁盘已满
入口参数: AH=09H
DS:DX=待输出字符的地址
说明:待显示的字符串以’$’作为其结束标志
出口参数: 无

由上面看到,我们所需要作的就是把DS指向数据段,DX指向字符串的地址,AH等于9H,调用21h中断。

mov ds,数据段地址
lea dx,hellostr 'hellostr已在前面1中定义了。
mov ah,9h
int 21h
只要在调用int 21h前把准备的东西准备齐就行,所以int 21h前面三行的顺序并不重要。

3 退出程序,运行完总要退出呀。再查中断手册
中断INT 21H功能4CH
功能描述: 终止程序的执行,并可返回一个代码
入口参数: AH=4CH
AL=返回的代码
出口参数: 无
mov ah,4Ch
mov al,0
int 21h


mov ax,4c00h
int 21h

需要说明的是返回代码有什么用,返回给谁?返回给操作系统,因为是操作系统DOS调用的这个程序,这个返回值可以通过批处理中的errorlevel得到,这里不多说明,实际上操作系统很少处理这一值,因此al你随便写什么值影响都不大
程序实现
data SEGMENT
msg DB ‘Hello, ZHENGQIAN.$’
data ENDS

code SEGMENT
ASSUME CS:code,DS:data
start:MOV AX,data
MOV DS,AX
lea dx,msg
mov ah,9h
int 21h
MOV AX,4C00h
INT 21h
code ENDS
END start

编译运行
把上面程序保存成helloZHENGQIAN.asm后,就可以编译运行了。
进入DOS,进入汇编目录:
D:\debug>masm helloZHENGQIAN.asm
Microsoft ® Macro Assembler Version 5.00
Copyright © Microsoft Corp 1981-1985, 1987. All rights reserved.

Object filename [helloZHENGQIAN.OBJ]:
Source listing [NUL.LST]:
Cross-reference [NUL.CRF]:

50408 + 415320 Bytes symbol space free

0 Warning Errors
0 Severe Errors
上面连续三个回车,表示我要的都是默认值。下面是零个警告,零个严重错误。(当然了,我的程序还敢错吗?)
D:\debug> link helloZHENGQIAN

Microsoft ® Overlay Linker Version 3.60
Copyright © Microsoft Corp 1983-1987. All rights reserved.

Run File [HELLOZHENGQIAN.EXE]:
List File [NUL.MAP]:
Libraries [.LIB]:
LINK : warning L4021: no stack segment
三个回车仍要默认,后面有个警告,没有栈段,这个没关系,没有的话系统会自动给一个
D:\debug>helloZHENGQIAN
Hello, ZHENGQIAN.
说明:运行成功。
D:\debug>

深度思考
1、是不是数据必须放数据段,代码必段放代码段呢?
答,代码必段放代码段,否则你怎么执行呀?但数据也可以放到代码段,只是程序要作修改。
code SEGMENT
ASSUME CS:code,DS:data
msg DB ‘Hello, Mr.286.$’
start:MOV AX,data
MOV DS,AX
lea dx,msg
mov ah,9h
int 21h
MOV AX,4C00h
INT 21h
code ENDS
END start
编译后仍然可以。

2 我编的程序在内存中是什么样子的呢?
D:\debug >debug ZHENGQIAN.exe
-u
1420:0000 B81F14 MOV AX,141F
1420:0003 8ED8 MOV DS,AX
1420:0005 8D160000 LEA DX,[0000]
1420:0009 B409 MOV AH,09
1420:000B CD21 INT 21
1420:000D B8004C MOV AX,4C00
1420:0010 CD21 INT 21
1420:0012 FF362421 PUSH [2124]
1420:0016 E87763 CALL 6390
1420:0019 83C406 ADD SP,+06
1420:001C FF362421 PUSH [2124]
-d 141f:0000 L20
141F:0000 48 65 6C 6C 6F 2C 20 4D-72 2E 32 38 36 2E 24 00 Hello, ZHENGQIAN.$.
141F:0010 B8 1F 14 8E D8 8D 16 00-00 B4 09 CD 21 B8 00 4C …………!..L
-q
D:\debug >

上面是什么呀?,还记得前面说的吗?
1420:0000 B81F14 MOV AX,141F
| | | | |
段址:偏址 机器语言 mov指令 把段地址的地址(141f)赋值给AX寄存器。

1420:0012后面的是垃圾数据,不用管它,把上面程序与源程序作一个比较,看有什么不用,差别在于把标号语言转成实际地址了。

程序前两行一执行,数据段地址就变成了141f,而那个字符串偏移地址在0000,由(LEA DX,[0000]看出),所以我用-d 141f:0000 L20(后面L20表示只显示20个字节),就能把段地址显示出来了。
所以刚才的程序在内存中就变成了:
141f:0000 Hello, ZHENGQIAN.$ —–>这是段地址里的内存

1420:0000 B81F14 MOV AX,141F ——>这是代码段里的内存。data变成了实际地址
1420:0003 8ED8 MOV DS,AX
1420:0005 8D160000 LEA DX,[0000] ——>偏址变成了0000,因为实际上msg也就是从头开始的。当然是0了。
1420:0009 B409 MOV AH,09 ——->注意Debug里,默认的是十六进制
1420:000B CD21 INT 21
1420:000D B8004C MOV AX,4C00
1420:0010 CD21 INT 21

学习了8086/8088汇编语言,发现寻址方式非常重要,于是做了一个小总结
概念:
1.指令集:cpu能够执行的指令的集合。
2.指令:cpu所能够执行的操作。
3.操作数:参加指令运算的数据。
4.寻址方式:在指令中得到操作数的方式。

现在就重点讨论寻址方式,说白了也就是cpu怎么样从指令中得到操作数的问题。另外再强调一点操作数还分种类:
1)数据操作数:全都是在指令当中参加操作的数据。
1.立即操作数:它在指令中直接给出。
2.寄存器操作数:它被放到寄存器中。
3.存储器操作数:当然在存储器也就是内存中。
4.i/o操作数:它在你给出的i/o端口中。

2)转移地址操作数:在指令当中不是参加运算或被处理的数据了,而是转移地址。
还可以按照下面分类方式:
1)源操作数src
2)目的操作数dst

源操作数都是指令当中的第2个操作数,在执行完指令后操作数不变。而目的操作数是指令当中的第1个操作数,在执行完操作指令后被新的数据替代。
我们就围绕这几种操作数,也就是操作数所在的位置展开讨论。

先说数据操作数,它分3大类共7种:
1)立即数寻址方式:是针对立即操作数的寻址方式。在指令当中直接给出,它根本就不用寻址。
例1:mov ax,1234h
mov [bx],5678h
在这里1234h和5678h都是立即操作数,在指令当中直接给出。
2)寄存器寻址方式:是针对寄存器操作数的寻址方式,它在寄存器中我们就用这中方式来找到它。
例2:mov bx,ax
mov bp,[si]
在这里ax,bx,ds都算是寄存器寻址,例1中的ax也是寄存器寻址方式。
3)存储器寻址方式:针对在内存中的数据(存储器操作数)都用这种方式来寻找,一共有5种(这是我自己的说法,便于记忆)。
不得不提及以下的概念:由于8086/8088的字长是16bit,能够直接寻址2的16次方也就是64kb,而地址总线是20bit,能够直接寻址2的20次方也就是1M空间,所以把内存分为若干个段,每个段最小16byte(被称为小节),最大64kb,它们之间可以相互重叠,这样一来内存就被分成以16byte为单元的64k小节,cpu就以1小节为单位寻址:在段寄存器中给出段地址(16bit),在指令当中给出段内偏移地址(16bit),然后把段地址左移4bit再与偏移地址求和就得到数据在内存当中的实际物理地址了,因而可以找到数据。
1.存储器直接寻址方式:在指令当中以 [地址] 的方式直接给出数据所在内存段的偏移地址。
例3:mov ax,es:[1234h]
mov dx,VALUE
mov dx,[VALUE]
在这里[1234h]和VALUE就是在指令中直接给出的数据所在内存段的偏移地址(16bit)。
VALUE是符号地址,是用伪指令来定义的,它代表一个在内存中的数据(也就是它的名字)。es:是段前缀符,用来指出段地址,在这之前应该将段地址添入段中,本例中是es,默认是ds,也就是不需给出。应该注意 [地址] 与立即寻址的区别,在直接给出的数据两边加 [] 表示存储器直接寻址,以区别立即寻址。另外 VALUE=[VALUE]。
2.寄存器间接寻址:不是在指令中直接给出数据在内存中的偏移地址,而是把偏移地址放到了寄存器中。
例4:mov ax,[bx]
这里[bx]就是寄存器间接寻址,bx中应放入段内偏移地址。其中:若使用bx,si,di默认段地址为ds,若使用bp则默认段地址为ss,并且允许段跨越,也就是加段前缀符。注意:在寄存器两边加 [] 以与寄存器寻址区别。
3.寄存器间接相对寻址:偏移地址是bx,bp,si,di中的内容再与一个8bit或16bit 的位移量之和。
例5:mov ax,[bx]+12h
mov ax,[si]+5678h
mov ax,[bp]+1234h
在这里[bx]+12h,[si]+5678h,[bp]+1234h都是寄存器间接相对寻址。12h是8bit位移量,1234h和5678h是16bit位移量。若使用bx,si,di则默认段寄存器是ds,若使用bp则默认段寄存器是ss,并且允许段跨越。
4.基址变址寻址:偏移地址是一个基址寄存器和一个变址寄存器内容的和,既:bx或bp中的一个与si或di中的一个求和而得到。
例6:mov ax,[bx+si]
mov ax,[bp+di]
上面[bx+si]和[bp+di]都是基址变址寻址。若使用bx做基址寄存器则默认段地址为ds,若使用bp为基址寄存器则默认段为ss,允许段跨越。
5.基址变址相对寻址:偏移量是一个基址寄存器与一个变址寄存器之和再与一个8bit或一个16bit位移量之和得到。
例7:mov ax,[bx+si]+12h
mov ax,[bp+di]+1234h
[bx+si]+12h和[bp+di]+1234h就是基址变址相对寻址。若使用bx做基址寄存器则默认段是ds,若使用bp做基址寄存器则默认段为ss。允许段跨越。

下面是转移地址操作数的寻址方式:
1)段内直接转移
1.段内直接短转移:cs(代码段)内容不变,而ip(指令指针寄存器)内容由当前ip内容+(-127~127),在指令中直接给出。
例8:jmp short SHORT_NEW_ADDR
其中,short是段内短转移的操作符,用以指出是转移到当前位置前后不超过±127字节的地方。而NEW_ADDR是要转移到的符号地址,它的位置应该在当前ip指针所在偏移地址不超过±127的地方。否则语法出错。
2.段内直接近转移:cs内容不变,而ip内容由当前ip内容+(-32767~32767),在指令中直接给出。
例9:jmp near ptr NEAR_NEW_ADDR
其中near ptr是段内近转移的操作符,用以指出转移到当前位置前后不超过±32767的地方。NEAR_NEW_ADDR是要转移到的符号地址。
2)段内间接转移:cs的内容不变,而ip的内容放在寄存器中或者存储器中给出。
例10:jmp bx
jmp word ptr [bx]+1234h
这种寻址方式是在寄存器或存储器中找到要转移到的地址,而地址是16bit的,因而寄存器必须为16bit,如:bx,我们用word ptr来指定存储器单元也是16bit的。注意:它是间接的给出,只能使用类似于数据操作数中的除立即寻址以外的6种寻址方式(就在上面)。
3)段间直接寻址:cs和ip的内容全都变化,由指令当中直接给出要转移到的某一个段内的某一个偏移地址处。
例11:jmp 1234h:5678h
jmp far ptr NEW_ADDR
1234h送入cs中作为新的段地址,5678h送入ip中作为新的偏移地址。far ptr是段间直接转移操作符,NEW_ADDR是另外一个段内的偏移地址,在这个指令中把NEW_ADDR的段地址送入cs(不用你给出),把它的段内偏移地址送入ip中作为新的偏移地址。
4)段间间接寻址:cs和ip的内容全变化,由指令当中给出的一个4字节连续存储单元,其中低2字节送入ip作为偏移地址,高2字节送入cs作为段地址。
例12:jmp dword ptr [bx][si]+1234h
jmp dword ptr [1234h]
jmp dword ptr [si]
dword ptr双字(4个字节连续存储单元)操作符,用来指出下面的存储单元是4个字节的。由于它是4个字节的,所以只能使用类似于数据操作数中的存储器寻址方式(共5种,还记得吗?)。

另外作为特殊的寻址方式还有三种:I/O寻址,串寻址,隐含寻址。它们都分别针对I/O指令,串操作指令以及无操作数的指令,而且都比较简单,自行总结。

一词:excursion 游览,涉足

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_44682019/article/details/109137450
今日推荐