汇编学习笔记

汇编学习笔记

劳动成果声明:

我是跟着B站小甲鱼和海蓬莱学的,我主要做的是是整理思维和总结,笔记中部分图片和代码并非我原创,有网上的也有这两位大佬的劳动成果。

开机BIOS和DOS工作流程

(1) 
	开机后, CPU一加电,初始化(CS)=0FFFFH,(IP)=0,自动从 FFFF:0单元开始执行程序。FFFF:0处有一条转跳指令, CPU执行该指令后,转去执行BIOS中的硬件系统检测和初始化程序。

(2)
	初始化程序将建立 BIOS所支持的中断向量,就是将 BIOS提供的中断例程的入口地址登记在中断向量表中。

(3)
	硬件系统检测和初始化完成后,调用 int19h 进行操作至统引导。从此将计管机交由操作统控制。

(4)
	DOS启动后,除完成其它工作外,还将它所提供的中断例程装入内存,并建立相应的中断向量。

在这里插入图片描述

汇编指令

在这里插入图片描述

汇编语言寄存器的英文全称中英对照表

reg(寄存器)
#ax、bx、cx、dx、sp、bp、si、di
sreg(段地址寄存器)
#ds、ss、cs、es


AH&AL=AX(accumulator):累加寄存器
BH&BL=BX(base):基址/偏移地址寄存器
CH&CL=CX(count):计数寄存器
DH&DL=DX(data):数据寄存器

SS(Stack Segment):堆栈段地址寄存器
SP(Stack Pointer):堆栈指针寄存器 # SS:SP

CS(Code Segment):代码段地址寄存器
IP(Instruction Pointer):指令指针寄存器(代码段偏移地址) # CS:IP

DS(Data Segment):数据段地址寄存器,配合偏移段地址[]使用;# DS:0000

ES(Extra Segment):附加段寄存器,用于
#缓冲区,也可以写入中断代码,然后在code段初始化时修改中断向量表0000:0000指向ES
    movsb指令的功能是将DS:SI指向的内存单元中的字节送入ES:DI中,然后根据标志寄存器DF位的值,将SIDI递增或递		减。同理应有movsw,movsd,movsq		ES的值在asm文件中可声明segment或在代码里mov es,xx
    常和rep搭配使用,
    	cld
    	rep movsb #rep的作用重复执行后面的movsb,次数为cx的值,传输方向用cld,和std指令控制

    
DF direcion flag,DF=l时,每次操作后使SIDI减小,DF=O时则增大,   cld  std
    # 修改方式,
    cld		指令df=0增大,增大的尺度取决于是movsb(自增1),还是movsw(自增2)
    std		指令df=1减小    
    
SI(Source Index):源偏移址寄存器
DI(Destination Index):目的偏移址寄存器
# 这两个和BX都能当作偏移地址,即[BX] [SI] [DI] [1] [BX+1] [BX+SI] [BX+DI+3], 错[BX+SI+DI]
# [BX+1]——[BX][1]——[BP].1——1[DI]——[BX][SI],都表示相加,BX、BP只用一个,DI、SI只用一个



BP(Base Pointer):(栈段偏移地址)基址指针寄存器
# 可以代替BX的偏移地址作用,在[]中使用BP时(mov cx,[bp]),
# 若指令没有规定段地址(如[bp],而没写cs:[bp]),则默认段地址为SS. 
# 也就是说[BX]->DS:BX;[BP]->SS:BP ,BP作用临时替代SP,实现保护SP的作用不会被更改           
# 用于把栈段当数据段读写
lp:	push bp			# 此时sp-1了
	mov,bp,sp
	…………		此过程凡是涉及SS修改的操作全用bp。mov [bp],'xx' 或 mov ss:[bp],'xx'
	call far ptr lpret
	pop bp			# 此时sp+1了,正好恢复为初始状态
lpret:
	…………		此过程凡是涉及SS修改的操作全用bp。mov [bp],'xx' 或 mov ss:[bp],'xx'
	retf

#	运算结果记录 标志位--------------------------标志寄存器----------------------------
                            OV     UP 	 EI	    NG    ZR 	NA   PE   CY
                    	符号溢出进位            负正   零         偶奇  进位
                            OF     UP 	 EI	    SF    ZF 	NA   PF   CY
                           

    ZF zero flag零标志运算结果等于0时为l否则为0 					ZR(Zero)			NZ(Not zero)
	#等于0
    
    SF sign Flag符号标志记录运算结果的符号,结果负时为1NG(负数Negteive)		PL(正数Positive)
    # -127存储为(01111111-反码>10000000-加1>10000001), (11110000)既表示+240也表示-16(00010000)
        
    CF carry flag进位标志最高有效位产生进位时为1,否侧为0CY(溢出进位Carry)	 NC(未溢出Not carry)
    #进位溢出  
    AF auxiliary carry flag辅助进位标志运算时,第3位向第4位产生进位时为1,否则为0
    # 和CF的区别是不判断 mov al,xx
    
    OF overflow flag溢出标志操作数超出机器能表示的范围表示溢出,溢出时为1 	OV(符号溢出)	NV(未溢出)
    #符号溢出,对于8位的有符号数据,机器所能表示的范围就是-128~127。对于16位是-32768~32767

	PF parity flag奇偶标志运算结果操作数位为1的个数为偶数个时为1否则为0       PE(偶Even)		PO(奇Odd)
    # 奇偶
    
   
    IF interrupt flag 中断标志IF=l时,允许CPU响应可屏蔽中断,否则关闭中断
    TF trap flag      用于调试单步操作
    IF/TF修改的操作如下图
    标志寄存器(0000 00IT 0000 0000)
    

-e 修改内存数据
在这里插入图片描述

-e 交互模式修改 0000回车,AB空格

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

h 高8位
l 低8位
AX=0008 BX=0008 CX=1000 CS=073F IP=010B

-a
073F:010B mov ah,13
073F:010D mov bl,33
073F:010F mov ch,al
073F:0111

-t
AX=1308 BX=0008 CX=1000 CS=073F IP=010D
-t
AX=1308 BX=0033 CX=1000 CS=073F IP=010F
-t
AX=1308 BX=0033 CX=0800 CS=073F IP=0111



AX=1308 BX=0033 CX=0800 CS=073F IP=0111
-a
073F:0111 add ax,03
073F:0114 mov bx,04
073F:0117 mov cx,ax
073F:0119

-t
AX=130B BX=0033 CX=0800 CS=073F IP=0114
-t
AX=1308 BX=0037 CX=0800 CS=073F IP=0117
-t
AX=1308 BX=0037 CX=0837 CS=073F IP=0119

-- 加
add bh,F1
如果加和结果为1F236,只显示为F236-- (无论高八位还是低八位有进借位都会舍给CF记录(CY,NC))
-- 减
sub减
sub ax(000F),10 结果为FFFF
(减的结果越界会向前借1,即使没有,如果操作的是高低八位,不会互相影响)

-- 含进位加
adc ax,1 即 ax=ax+1+cf
-- 含进位减
sbb ax,1 即 ax=ax-1-cf


-- mul乘(低于255的数相乘,AX=AL*BL)
计算100*10
AX=0064
BL=0A
-a
mul bl
会使
AX=03E8
-- mul乘(高于255的数相乘,结果为DXAX)
计算100*10000(结果为64*2710=F4240)
AX=0064
BX=2710
-a
mul bx
输出
DX=000F AX=4240 
总结
	AX=AL*BL
	DXAX=AX*BX
										#   imul,idiv是对有符号负数运算的乘除

div-- 10000(2710)/100(64)=100(64)
AX=2710
BL=64									 除以        商   余
-a											   
div bl									AX / BL		AL	AH
结果为AX=0064 AH=00(余数)						 
-- 1000000(F4240)/10000(2710)=100(64)			   
DX=000F
AX=4240
BX=2710					
-a
div bx									DXAX / BX	AX	DX
结果为AX=0064 DX=0000(余数)


-- 与and运算
mov al,01100011B(debug中要写成十六进制)
and al,00111011B
结果
al=00100011B
-- 或or运算
mov al,01100011B
or al,00111011B
结果
al=01111011B
-- 异或xor运算
xor ax,bx		(对ax和bx进行异或运算,对应两位相异则为1,相同为0,结果赋值ax)



-- 左移一位(右侧补0,进的位覆盖给标志寄存器的cf),	乘2
shl al,1
想一次性移动2位
mov cl,2
shl al,cl

-- 右移一位(左侧补0,最后一位移出覆盖给cf),		除以2
shr al,1

循环移动(不补0)(0110->1100->1001->0011)
rol ax,1
ror ax,1
移动(带进位)
rcl和rcr

-- 加1减1(自增自减,越位会借inc FFFF即0000,同add和sub)
inc ax  加1
dec ax  减1

-- 交换数据
xchg ax,bx

-- 取反,反码
neg ax

-- 中断
int

-d 段地址:偏移位/地址 查看多少位

在这里插入图片描述

段地址*16(即左移一位)+偏移位/地址=物理地址

段		偏		物理
2000 	1F60	21F60
2100	0F60	21F60
21F0	0060	21F60
21F6	0000	21F60
1F00	2F60	21F60


mov ax,[60] -- 将ds:0060处的数据(高低八位)赋给ax
mov al,[60] -- 将ds:0060处的数据(八位)赋给al
mov ax,[bx] -- 将ds:[bx]处的数据赋给ax
mov ax,[bx+1] -- 也合法,因为bx有做偏移地址的功能,将ds:[bx+1]处的数据
mov [si],bx   -- 将bx的值x写到ds:[si]处,推荐写法 mov word ptr [si],bx

DS修改方式
mov ax,21F0
mov ds,ax

修改寄存器数据

-r cs
CS 073F
:输入要修改为数据(直接回车不修改)

jmp指令(跳转CS偏移地址)

在这里插入图片描述

CS=2000 IP=0000
-t执行
经分析后,可知指令执行序列为:
(1)mov ax,6622H
(2)jmp 1000:3
(3)mov ax,0000
(4)mov bx,ax
(5)jmp bx  -- 相当于 ip <--  bx(mov ip,bx不合法)
(6)mov ax,0123H
(7)转到第3步执行(死循环)

cmp指令(比较大小,条件跳转)

————————————————————————————————————————————非重点作为了解—————————————————————————————————————————————
mov ax,8
mov bx,3
cmp ax,bx(判断 ax-bx 的结果)
执行后:zf=0,cf=0。说明 ax-bx 的结果不为 0 且不用借 1减,所以 ax>bx

如果(ax)=(bx)则(ax)-(bx)=0, 所以:			         zf=1;
如果(ax)≠(bx)则(ax)-(bx)≠0,所以:			    	    zf=0;
如果(ax)<(bx)则(ax)-(bx)将产生借位,所以: 		 	    cf=1;
如果(ax)≥(bx)则(ax)-(bx)不必借位,所以:	 	 	     cf=0;
如果(ax)>(bx)则(ax)-(bx)既不必借位,结果又不为0,所以:   cf=0 && zf=0;
如果(ax)≤(bx)则(ax)-(bx)既可能借位,结果也可能为0,所以: cf=1 || zf=1。

下面是常用的根据无符号数的比较结果进行转移的条件转移指令。
例子: 
cmp ah,bh
je 073F:0100

————————————————————————————————————————————重点——————————————————————————————————————————————————————
指令 		含义 			检测的相关标志位
je/z 	等于则跳转		 zf=1			equal
jne		不等于则跳转		zf=0
jb		低于则跳转		 cf=1			below
jnb		不低于则跳转		cf=0
ja		高于则跳转		 cf=0且zf=0		above
jna		不高于则跳转		cf=1或zf=1
jcxz	此时先判断 cx==0 才跳转		

自动跟位补位特性

SS:SP指向栈顶(左栈顶60 10 00 F0 08 00栈底右)

高八位先入栈

-d 0000:0000 5
0000:0000 60 10 00 F0 08 00
-r
AX=0000 BX=0000 DS=073F 
-a
 mov ds,bx
-t
AX=0000 BX=0000 DS=0000 
-a
mov ax,[0000]
-t
AX=1060 BX=0000 DS-0000 -- 60 10 00 的10即高八位,60低八位

-- 入栈(push入栈的位置取决于SS:SP)
push ax		也可以push [0000]参考当前段地址ds
-- 出栈并赋值(pop出栈的值取决于SS:SP,pop空格后的用于接收值,pop完SP重新指向栈顶,原位置填充数据A301)
pop ax		也可以pop [0000]

pushf 	-- 把标志寄存器的值入栈
popf	-- 出栈给标志寄存器的赋值

栈的内存标志
已知操作完栈的下一条指令的cs:ip 1234:5678
则栈为  78 56 34 12 A3 01 xx    (xx即sp位置)A3 01 会一直包裹栈顶

编译程序

1、工作目录创建code.asm文件,此文件的汇编代码不区分大小写

写入

assume cs:codesg  			-- 声明代码段cs为codesg,同理 ds:xxx,ss:xxx

codesg segment  		-- codesg segment 和 codesg ends 包裹汇编指令

    mov ax,0123H
    mov bx,0456H		-- 不写H编译器会认为是十进制数,写B代表二进制数,O代表八进制
    add ax,bx
    add ax,ax
    mov ax,4c00H		-- 用于程序退出时返回之前的启动程序
    int 21H
    					-- 编译器可以操作 +-*/ 运算
codesg ends

end

2、用masm.exe编译asm文件

C:>\masm.exe
Source filename [.ASM]:键入code.asm  
Object filename [code.OBJ]:
Source listing  [NUL LST]:
Cross-reference [NUL CRF]:

  51800 + 464744 Bytes symbol space free
    0 Warning Errors
    0 Severe Errors
    
同级目录生成一个code.obj文件    		-- 乱码诞生

3、用link.exe将code.obj转成exe文件

C:>\link.exe
Object Modules [OBJ]:code.obj
Run File [CODE.EXE]:
List File [NUL.MAP]:
Libraries [.LIB]:
LINK warning L4021:no stack segment -- 此警告是因为没写基段地址

同级目录生成一个code.exe文件

4、此exe文件可以用debug打开——C:>\debug.exe code.exe

计算2**12(loop指令相当于jmp指令不过会牵扯cx计数)

# --------------------------------------------方法一----------------------------------------------#

assume cs:code  

code segment  		

    mov ax,0002H
    mov bx,0002H
    mov cx,000BH		-- 计数器,每次loop时,自减1
    s:
        mul bx   -- ax=ax*bx,2*2---这是起始状态2^2,说明还需乘10次才是2^12,则loop应运行11次,故cx=B
        loop s	 -- 循环s定义的代码段,每次修改IP为s:的偏移地址(debug界面显示loop 0006,0006即s:的偏移),					loop时先dec cx,当结果为0时,不在跳转ip,继续往下执行其他代码
    mov ax, 4C00H
    int 21H
    
code ends

end

# --------------------------------------------方法二----------------------------------------------#

assume cs:code  

code segment  		

    mov ax,0002H
    mov cx,000BH		-- 计数器,每次loop时,自减1
    s:
        add ax,ax   -- ax=ax*2
        loop s	 -- 循环几次cx就写几
    mov ax, 4C00H
    int 21H
    
code ends

end

-- 死循环
s:inc cx


调试时如果想直接看运算结果,-u查看代码终止的偏移地址为0012
当前 IP=0000
输入 -g 0012
即可直接运算完之间的指令
-p 回车 可以直接运算完循环,IP指向循环下一条指令(pass)

call和ret指令

assume cs:code  			

code segment  		

    mov ax,2
    mov cx,11
    call s				# ip指向s:的偏移地址,即add ax,ax debug实例 CALL	000F
    int 21H					
    s:					# s字符可以自定义,作用标注
    	add ax,ax
    	loop s
    ret					# ip指向call的下一条指令,即int 21H
    
code ends

end

-- call和ret的本质

call  实质是    push ip  (call的下一条指令对应的的ip)
ret   实质是    pop ip   (当前ip接收值)

如果call和ret需要修改段地址CS,

写法与实质:
    -- call far ptr s    
         先
         push cs 
         push ip(call的下一条指令对应的的ip)
         再把
         cs:ip指向s:的段地址:偏移地址,debug实例 CALL	076A:000F	
    -- retf
		pop ip
         pop cs




数据存储类型

类型	  	   debug命令(define byte)    长度
BYTE  (1字节)    db 	12,23   		 8	   # 内存里是 12 34
WORD  (2字节)    dw   1234,0033         16	# 内存里是 34 12 33 00
DWORD (4字节)    dd 	 			     32
QWORD (8字节)    dq				     64

主要用于往地址中写数据时,
如 mov word ptr [si],bx
如 mov dword ptr [si],000A,0057
如 mov dword ptr [si],056A,5729
如 mov qword ptr [si],568A,5789,568A,5789 
assume cs:code
codesg segment  
	dw 123H,456H,0ABCH,0F569H   -- 语法十六进制数不能以字母开头,头要加0
	start:	
			mov ax,0		-- start 用于区分数据和指令防止混肴,将cs:ip指向mov而不是dw
			add ax,1
code ends
end start

分段存储

assume ss:stack,ds:data,cs:code

-- 先写栈,方便代码初始化sp
stack segment				
	db 6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0  可简写为 db 16 dup(0)  -- 不加h就是十进制数
	# 写字符 db 3 dup('hello','aliak')  接着前面会自动根据ascii码写入
    # db 1,2,3,1,2,3,1,2,3      可简写为 db 3 dup(1,2,3),接着前面写,不满16个后面会写入0  
stack ends

data segment				
	#dw 012fH,0235H,666
	#db '你好HeLLo'				-- 写入的字节取决于asm文件的编码是UTF还是GBK
	db 1 dup("hello",'aliak') 		-- 接着前面写,不满16个后面会写入0,单双引号都兼容
data ends

code segment
start:
	mov ax,stack				-- 将栈段地址赋给ax
	mov ss,ax					-- 段寄存器只能接收普通寄存器的mov
	mov sp,0010H				-- 如果stack写的是dw,则此处为0020H,相应start调整的cs更靠后了
	mov ax,data
	mov ds,ax					-- 将数据段地址赋给ds
	
	-- 上面代码必须写作为初始化条件,下面才是真正要做的事的写代码的位置
	mov ax, 4C00H
    int 21H
code ends
end start

最终连续存储
数据段(076A)076A:0000	68 65 6C 6C 6F 61 6C 69-61 6B 00 00 00 00 00 00 helloaliak
和栈段(076B)076A:0010	06 06 06 06 00 00 00 00-00 00 00 00 00 00 00 00 
代码段(076C)076A:0020	B8 6B 07 8E DO BC 20 00-B8 6A 07 8E D8 FF FF 74
谁大谁小取决于 asm 文件写的代码顺序(不是assume那一行而是下面的),


代码篡改可能,疯狂pop

—————————————————————————————栈的安全性取决于 ss,sp无论怎么变都不会改变 sp,会循环
防止溢出,应在
stack db 0fff1h dup(0) , 
sp == 8000h
data db 0fff1h dup(0)
这样于段和段之间的物理地址才不会有重叠
code segment
	

如下图,当 sp=ffff时,再 pop 往后不够两个字节,结果会是 01A3而不是 A3A3, 但 sp 会变成 0001

同理, mov [ffff],2345h 会把下一个代码段的第一个字节改为 23



所以初始化  -----------------------------------------
	mov ax,stack
	mov ss,ax
	mov sp,8000h
	
	mov ax,data
	mov ds,ax
----------------------------------------------------------

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

offset指令获取偏移地址,多用DI、SI、BP、BX接收

 -- 运行两次mov,bx,bx来学习offset和nop
code segment
start:
	mov si,offset s 	 :0000	   # 将s对应指令的偏移地址赋给si, 即mov si,0006
	mov di,offset s0	 :0003	   # 将s0对应指令的偏移地址赋给di,即mov di,000E
	s:	mov bx,bx    	 :0006     debug里下面有个CS:命令,机器码2E 
    	mov cx,cs:[si]   :0009     # 把cs:[si]对应的数据赋给cx,此数据就是mov bx,bx的机器码(89DB)
    	mov cs:[di],cx	 :000C	   # 将nop指令改为mov bx,bx,即当执行到nop指令处,nop指令变为 mov,bx,bx
    s0:
    	nop						-- 占位,机器码为00,常用于计算代码长度,比如offset s0 - offset s,就是
        nop						 -- s:代码的长度,然后配合movsb,cld来拷贝指令代码到其他位置
    						-- 为什么写两次,因为mov cs:[di],cx是传入的word,而nop机器码为90只有一个
code ends									-- 其实写一个nop也没关系,反正会覆盖掉后面的机器码

-----------------------------------------
mov cx,cs:[di] 是伪指令,debug要写成
-a	
	cs:
	mov cx,[di]
但asm不能这样写,因为此时cs:会被解析类似s:的含义

在这里插入图片描述

jmp的机器码与长短跳转

##	近跳转
jmp short s			-- EB 03    此处的03 代表ip(下条指令)加几的数,取决于此指令和s对应指令之间的机器码个数
add ax,1			-- 05 01 00							|
s: inc ax			-- 40							    |
#													   V
#												因此最大机器码个数为FF个(255)


近跳转也可以
jmp near ptr s    -- 区别  EB XX 90,也就是在后面会补一个nop(机器码90)指令

##	远跳转
jmp far ptr s					-- EA 0C 01 6A 07     -u 显示为 jmp   076A:010C 不再是相对此指令的距离
db 256 dup(0)
s: inc ax      076A:010C



跳转目的到内存地址
一直数据段 23DF:0000 12 32 55 00
jmp word ptr ds:[1]  -- word 表示读取ds:[1]的两个字节(高低八位)作为jmp的偏移地址 即 jmp  5532

对应debug版本写法  
-a
	ds:
	jmp word ptr [1]
	
	
若要远跳转即想改变cs
	已知ds:0000  01 23 66 55
	jmp dword ptr ds:[0]     -- 此指令效果相当于, ip=2301, cs=5566
	-- 想要给ds:0000写入 00 00, 写法为:
		mov word ptr ds:[0],0



数组(本质就是偏移字节[]和偏移地址的转换,只有db的[]才真是数组下标)

data:	
    arr db "a234"
    arr1 dw "哈","在"   -- asm是gbk编码才可以,就是相当于 arr1 dw 0B9FEh,0D4DAh
    arr2 dd 2222h,"哈","哈"
    arr3 dq 2222h
code:
    mov ax,type arr			即 mov ax,0001	-- 一维数组
    mov ax,type arr1		即 mov ax,0002	-- 二维数组
    mov ax,type arr2		即 mov ax,0004	-- 四维数组
    mov ax,type arr3		即 mov ax,0008	-- 八维数组


___________________________________________________________________________________________
data:
    arr db "a234"
    arr1 dw "哈","在"  -- asm是gbk编码才可以
    arr2 dd 2222h,36
    arr3 dq 2222h
code:
-- 中括号内代表arr的字节位,不是字符位,作此运算前一定要先把stack-->ds
    mov al,arr[0] /[arr+0]	 即 mov al,'a'的ASCII值(位置在此时的ds:xx处,xx是arr[0]所在的偏移地址)
    mov arr[0],al			相当与写入数组的操作
    mov ax,arr1[0]			即 mov ax,'哈'的gbk值(位置在此时的ds:xx处,xx是arr1[0]所在的偏移地址)
    mov ax,arr1[2]			即 mov ax,'在'的gbk值(位置在此时的ds:xx处,xx是arr1[2]所在的偏移地址)
    
    mov word ptr arr[0],ax
    mov byte ptr arr1[0],al
    
    mov ax,word ptr arr2[0]      	
    mov al,byte ptr arr3[0]  		

用debug写一个打印AAA.com程序

-e 0130 "AAA$"		# 0130会当作ds的偏移地址
-d 100
0F2F:0100	00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0F2F:0110	00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0F2F:0120	00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0F2F:0130	41 41 41 24 00 00 00 00-00 00 00 00 00 00 00 00		-- 即AAA$
0F2F:0140	00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
-a
0F2F:0100 mov dx,0130	-- (数据地址)
0F2F:0103 mov ah,09		-- (代表输出ds:dx的字符串,$结束标志)
0F2F:0105 int 21		-- (退出程序)
0F2F:0107 mov ah,4c 	-- (扫尾)
0F2F:0109 int 21
0F2F:010B 
-d 100
0F2F:0100	BA 30 01 B4 09 CD 21 B4-4C CD 21 00 00 00 00 00
0F2F:0110	00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0F2F:0120	00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
0F2F:0130	41 41 41 24 00 00 00 00-00 00 00 00 00 00 00 00 	-- 即AAA$
0F2F:0140	00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
-r
CX 0000
:34		-- 表示机器码的长度
-n C:\AAA.com #保存为文件,其中没有ds的信息,因为dubug默认打开DS=CS
-w
-q

C:\>AAA.COM
AAA
C:\>

-- DOS默认打开时DS=CS,而IP默认等于0100
以上代码还可以进一步压缩和优化,0110-0120空间用不到都浪费了
还有一点性能快慢问题, 立即数>寄存器>内存,com文件默认打开的CS=DS,IP偏移为0100,优化后的com文件用winhex打开如下:
Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F
00000000   BA 0B 01 B4 09 CD 21 B4  4C CD 21 41 41 41 41 24  
BA 0B 01 表示dx=010B
B4 09	表示AH=09
CD 21	表示int 21H
B4  4C	表示AH=4c
41 41 41 41 24	表示 AAAA$ 的ASCII

用编译器写一个打印程序,直接运行编译后的exe文件

assume ds:data,cs:code

data segment				
	str db "hello world","$"(3624H均可)  -- 打印时遇到24会停止,想换行用0ah	
data ends

code segment
start:
	mov ax,data				-- 将数据段地址赋给ax
	mov ds,ax				-- 将数据段地址赋给ds
	mov dx,offset str
	mov ah,9					
	int 21h					-- 根据ah值,会自动打印ds:dx往后的字节直到24h
	mov ah,4ch
	int 21h					-- 根据ah值,做程序退出处理,移交CPU控制权还给启动自身的程序	
code ends
end start

8086cpu打印原理

b800h段存放程序退出中断时的屏幕输出信息,可以用es把数据写到这个区域,写其他区域不行
屏幕
左上角字符对应的ASCII码存放在偏移00a0
左上角第二行偏移是  00a0+00a0
右上角偏移是00a0h+00a0-0002
左下角偏移是00a0h*0018h		#其实左下角是00ah*0019h,不过程序返回时接手程序又把最后一行像素重写了
右下角偏移是00a0h*0019h-2h	#同理右下角想回显也只能往上提一行
每一行像素的和正好也是是AOh个,0b800:00a0的 word值高位是属性信息,低位是 Ascii


高八位解析
7 		6 		5		4	 	3 		2		1		0
BL 		R————————G————————B		 I		 R————————G————————B
闪烁为1 	  	 背景色 		   高亮为1  		 字符颜色
        mov ax, 0b800h			
        mov es, ax		  	
		
		mov ah,01010000b
		mov al,"L"
		mov es:[00a0h*0010H+8*8],ax
		mov ah,01010000b
		mov al,"O"
		mov es:[00a0h*0010H+8*8+2],ax
		mov ah,01010000b
		mov al,"V"
		mov es:[00a0h*0010H+8*8+2+2],ax
		mov al,"E"
		mov es:[00a0h*0010H+8*8+2+2+2],ax
		-- 可以用loop循环或者栈或者movsb/movsw
		mov ah,00110000b
		mov al,"H"
		mov es:[00a0h*0018H+8],ax
		mov ah,00110000b
		mov al,"E"
		mov es:[00a0h*0018h+8*2],ax
		mov ah,01010000b
		mov al,"L"
		mov es:[00a0h*0018h+8*3],ax
		mov ah,00000111b
		mov al,"L"
		mov es:[00a0h*0018h+8*4],ax
		mov ah,10110010b
		mov al,"O"
		mov es:[00a0h*0018h+8*5],ax
		
		屏幕打印和ES没关系,和cpu显存制造初始任务定义的物理地址才有关
		也就是
		mov ax,0
		mov es,ax
		mov ax,4c00h
		int 21h	
		;输出结果如下图

在这里插入图片描述

注释

;kjlsahjsfhashjfkhj		-- 分号直到换行进行单行注释
comment*多行注释可换行*comment  	--comment是关键字不能随便改

ASCII

已知ASCII码	
大写字母ASCII + 20H  = 小写字母ASCII

A-Z		xx0xxxxx		
a-z		xx1xxxxx
20H		00100000	即空格ASCII,与或 00100000B 运算是自己
小写字母转大写即
减 00100000,结果和与运算即 and 11011111B没区别,节省性能开销
大写转大写也可以用	and 11011111B

同理
字母转小写
or 00100000B

总结
and 11011111B -- 转大写
or  00100000B -- 转小写

在这里插入图片描述

-- -------------------------------数组转大小写输出-----------------------------------------------------
assume cs:codesg,ds:data

data segment
	str db 'heLLo World','$'
data ends

codesg segment
	start:	
	mov ax,data			-- data 是段地址
	mov ds,ax
	
	mov bx,0
	mov cx,11			-- 循环次数,即数组长度
	s:
		mov al,[bx]		-- ds:[bx]的值
		or al,0100000B		-- or转小写,and转大写
		mov [bx],al		-- 写入ds:[bx]的值
		inc bx
		loop s
		
		;mov dx,offset str -- 单行注释,str代表str[0]的字节偏移地址
		lea dx,str		  -- 等价上面写法
		mov ah,09h
		int 21H

		mov ah,4ch
		int 21H
codesg ends
end start

-- -------------------------------数组求最大ASCII值字符并输出------------------------------------------
assume cs:codesg,ds:data

data segment
	str db 'hello world','$$'
data ends

codesg segment
	start:	
	
	mov ax,data
	mov ds,ax
	
	mov bx,offset str		-- str代表str[0]的字节偏移地址
	mov cx,11
	mov ah,0
	s:
		mov al,[bx]
		cmp ah,al
		jnb s1		-- 不小于则跳转
		mov ah,al
		
	s1:
		
		inc bx
		loop s
		
		mov [bx],ah		-- 或者 mov al,24h  然后 mov [bx],ah mov [bx+1],al
		mov dx,bx
		mov ah,9
		int 21H

		mov ah,4cH
		int 21H
codesg ends
end start

-- -------------------------------数组求和并输出------------------------------------------

assume cs:codesg,ds:data,ss:stack

stack segment
	db 0fff1h dup(0) 
stack ends

data segment
	str db 1,2,3,4,10,20,30,40,'$'
	db 0fff1h-9 dup(0)			;-- 因为上面写了9个字节所以减9
data ends

codesg segment
	start:	
	mov ax,stack
	mov ss,ax
	mov sp,8000H
	mov ax,data
	mov ds,ax			
	
	;###################### 上面是初始化模板 #######################
	
	mov bx,offset str		;-- str代表第一个的字节偏移地址,即str[0]也是[str+0],ds:str的值机器码为01
	mov cx,8				
	mov al,0
	s:
		add al,[bx]
		inc bx
		loop s
		
		mov [bx],al		-- 低八位一个字节
		mov byte ptr [bx+1],24h			;-- 24h即'$',只有一位字节
		
		mov dx,bx
		mov ah,9
		int 21H
		
		mov ah,4cH
		int 21H
codesg ends
end start

数组的拷贝	bx用作循环因子
        mov al,[arr1+bx]
        mov [arr2+bx],al
        inc bx		
反转拷贝
		
		mov cx,xxx  -- xxxx是arr1数组的长度
		mov bx,0 
		mov di,xxx  -- xxx是arr1数组的长度减1
		s:
		mov al,[arr1+bx]
		mov [arr2+di],al
		inc bx
		mov di,xxx	-- xxx是arr1数组的长度减1
		sub di,bx	
		loop s
#########或者
		mov si,0
		mov di,xx -- xx就是要拷贝的数组arr1的长度减1
		s:
		mov al,[arr1+si]
		mov [arr2+di],al
		inc si
		dec di
		loop s
#########或者用栈,栈只适合作用word型,栈十分适合反转word数组,因为先push的最后pop
	arr1 dw 0001,0002,0003,0004,0005
    arr2 dw 0
    
    
   	mov cx,5  ;-- 是arr1数组的字符长度
	mov si,offset arr1
	mov di,9	; -- 9 是arr1的字节长度减1
	
	s:
	push  word ptr [arr1+si]	-- push和pop只能作用word不能作用byte
	pop  word ptr [arr2+di]
	add si,2
	sub di,2
	loop s
###想作用byte型
	mov si,xx
	mov ds:[si],al
	

双循环

55

mov ax,0
mov dx,0
mov cx,5
for:
	mov bx,cx
	
	mov cx,5
	#-------------------内循环
	for1:
		inc dx
	loop for1
	#-------------------内循环
	mov cx,bx
inc ax
loop for
结果ax 5  dx 5*5

在这里插入图片描述

二维数组


怎么表示
xxx=[   [1,2,3],
        [a,b,c],
        [9,f,a,s,d,f]
    ]


str db '123'
    db 'abc'
    db '9fasdf'

mov al,str[0][2] 	# 即al为3对应xxx[0][2]
mov al,str[0][4]	# 即al为'b'对应xxx[1][1]
mov al,str[0][9]	# 即al为'a'对应xxx[2][3]


mov al,ds:str[bx+5]  str[bx][5]  [str][bx][5]  [str+bx+5]  都一样
-- -----------------------------------二重循环把指定矩形转大写---------------------------------------

assume cs:codesg,ds:data,ss:stack
data segmeNT
	str db 'aaaaabbbbbccccc '
	db 'aaaaabbbbbccccc '
	db 'aaaaabbbbbccccc '
	db 'aaaaabbbbbccccc ','$'

data ends
stack segment
	db 10 dup (0) 
stack ends
codesg SEgment
	start:	
	mov ax,data
	mov ds,ax
	
	mov bx,0
	mov cx,4
	for:
	      mov dx,cx
		mov si,0
		mov cx,5
		for1:
			mov al,ds:str[bx+si]
			and al,11011111B
			mov ds:str[bx+si],al
		inc si
		loop for1
	     mov cx,dx
	add bx,16
	loop for
		
	lea dx,str	;即 mov dx,offset str	优相比mov的优点 可以 lea dx,[str-0c5h]
	mov ah,9
	int 21H

	mov ah,4cH
	int 21H
codesg ends
end start

冒泡排序


assume cs:codesg,ds:data,ss:stack
data segmeNT
	arr db 0A2H,24H,07H,3AH,1BH,0F1H,3BH,25H,81H
data ends
stack segment
	db 10 dup (0) 
stack ends
codesg SEgment
	start:	
	mov ax,data
	mov ds,ax
	
	mov bx,0
	mov cx,8
	for:
	      mov dx,cx
		mov si,8
		mov cx,8
		sub cx,bx
		for1:
			mov ah,ds:arr[si]
			mov al,ds:arr[si-1]
			cmp ah,al
			jnb all
				xchg ah,al
				mov ds:arr[si],ah
				mov ds:arr[si-1],al				
	
			all:
				
		dec si
		loop for1
	     mov cx,dx
	add bx,1
	loop for

	mov ah,4cH
	int 21H
codesg ends
end start
-- C++写法
comment*
	 	bx
	for(int i=0;循环4;i+=16){
				si
		for(int j=0;循环五次;j++){
			arr[j+i]转大写
		}
	}
	
	
	for(int i=0;i<;i+=16)
		arr[i+5]转大写

	for(int i=0;i<arr.size();i++){
		for(int j=0;j<arr[0].size();j++){
				cout <<arr[2][5]<<" ";	
}cout<<endl;
}
*comment 

comment*
c++
数组当中的最大值
int res=0
for(int i=0;i<str.size();i++)if(res<s[i])res=s[i];
return res

求最小值
int res=FF
for(int i=0;i<str.size();i++)if(res>s[i])res=s[i];
return res

for(int i=0;i<str.size();i++)str[i]转大写
*comment	

斐波那契数列求和

assume cs:code,ds:data,ss:stack
data segment 
	arr dw 1H,1H,100 dup (0)
	res db 800 dup (0)
data ends

stack segment 
	db 100 dup (0)
stack ends

code segment 
	start:
		mov ax,data
		mov ds,ax
		mov ax,stack
		mov ss,ax
		
		mov bx,4
		mov cx,30
		for :
			mov dx,0
			add dx,ds:arr[bx-2]
			add dx,ds:arr[bx-4]
			mov ds:arr[bx],dx
		add bx,2
		loop for
			
		mov ax,4c00H
		int 21h
code ends
end start

十进制转十六进制并输出

assume cs:codesg,ds:data,ss:stack
data segmeNT
	str db '00002333','$'
	res db '0000','$'
data ends
stack segment
	db 100 dup (0) 
stack ends
codesg SEgment
	start:	
		mov ax,data
		mov ds,ax
		mov ax,stack
		mov ss,ax		
		mov sp,10
		mov si,4

		mov bx,0
		mov cx,8
		mov ax,0
		s:
			mov dx,ax
			shl ax,1
			shl ax,1	
			shl ax,1
			shl dx,1
			add ax,dx
;10

			add al,str[bx]
			adc ah,0
			sub ax,30H		
		
			inc bx 
			loop s
		
;ax=3039
	
		mov cx,4
		l:
			mov dx,ax
			and dx,0FH
			add dx,30H
			cmp dx,3AH
			jb s1
			add dx,7H
;求出3039的ASCII码,push
		s1:
			dec si
			mov ds:res[si],dl
			shr ax,1
			shr ax,1
			shr ax,1
			shr ax,1
			loop l

		;mov dx,offset res
	 	lea  dx,res
		mov ah,9
		int 21H

		mov ax,4c00H	-- al是返回码
		int 21H
codesg ends
end start

端口读写

					端口为word长度 00000000 00000000`0~255`以内的端口进行读写:

    in al,0020h	;20h端口读入一个字节
    out 0020h,al  ;20h端口写入一个字节

对`256~65535`的端口进行读写时,端口号放在dX中

    mov dx,03f8h
    in al,dx;03f8h端口读入一个字节给AL
    out dx,al;3f8h端口写入一个AL字节
    
 必须用AX和端口交互
 
 	猜测
 	in ax,00ffh 是在ffh端口读两个字节
 	
 	
 	端口70h是时钟CMOS RAM接口
 		                                    00   00    00   00 	  00    00	  00
 											  年    月   日    时	 分	   秒
 		      用BCD码判断
 	端口60h是键盘输入接收端口

BCD码

​ 00010100B表示14(十四)

​ 10010001B表示91(九十一)

在这里插入图片描述

BCD码值 + 30h = 对应数字的ASCII

64位操作系统的寄存器

eax,ebx,ecx,edx,每个可以存8个字节,6A3C 12E9

中断向量表

8086cpu中断类型码8位 int00H -- int FFH

中断向量表(空间) 0000:0000-0000:03FF

0000:0200
之间是空的,可以用于存放用户的中断代码,注意是代码,不是向量,因此应把对应中断的向量表改为此处的CSIP
0000:02FF

2FF-200长度是256个字节,因此机器码长度最后不用超过256

在这里插入图片描述

规律如下

int 00H 即cs:ip 改为 00A7:1068中断时的 标志寄存器的值 和 cs ip保存在栈中了

int 01H 即cs:ip 改为 0070:018B中断时的 标志寄存器的值 和 cs ip保存在栈中了

int 02H 即cs:ip 改为 03A5:0016中断时的 标志寄存器的值 和 cs ip保存在栈中了

中断本质(挂起/退出)

int N		-- 十六进制N	挂起(如果有人iret)、退出(没人iret他的话就是编写退出了,是否回收内存取决于int 几)
(1)取得中断类型码N
(2)pushf		-- 标志寄存器入栈
(3)TF=0,IF=0	--   IF interrupt flag 中断标志IF=l时,允许CPU响应可屏蔽中断,否则关闭中断
   			 	--  TF trap flag      用于调试单步操作
(4)push CS
(5)push IP
(6)IP=0000:[N*4],CS=0000:[N*4+2]	
对应中断类型码查找中断向量表(位置ES:BX)赋值CSIP

# int 的反操作	 iret				激活原来挂起的程序
恢复中断前的操作,此指令应放在中断指令紧接着的后方,而不是中断前的源位置
本质
(1)	pop ip
(2)	pop cs
(3)	popf		也就是说int和iret都会改变sp的值,之所以简化写是因为iret的指令机器码只有一个Byte(CF)
														  int 0H 的机器码为 CD 00
	
########################################对比call和retf##########################################
-- call和ret的本质(在本段跳转)

call  实质是    push ip  (call的下一条指令对应的的ip)
ret   实质是    pop ip   (当前ip接收值)

如果call和ret需要修改段地址CS,

 -- call far ptr s    (跨段跳转)
         先
         push cs 
         push ip(call的下一条指令对应的的ip)
         再把
         cs:ip指向s:的段地址:偏移地址,debug实例 CALL	076A:000F	
    -- retf			此指令应放在跳转后的位置,放在跳转前的源位置是错误的做法
		pop ip
         pop cs

更改int 0H中断的操作本质是修改中断向量表段空间

		mov ax, 0               #设置中断向量表
		mov es, ax
		mov word ptr es:[0*4], xx		xx是自定义操作代码对应的IP
		mov word ptr es:[0*4+2], yy		yy是自定义操作代码对应的CS

手工写一个int 7CH的中断,实现AX转大写

assume cs:code
code segment
		mov ax,cs
		mov ds,ax
		mov si,offset coverA
		
		mov ax,0
		mov es,ax
		mov di,200h
		mov cx,offset len - offset coverA
		cld
		rep movsb
		
		mov es:[7ch*4],200h
		mov word ptr es:[7ch*4+2],0h
		mov ax,'al'
		int 7ch		
		
		mov bx,cs
		mov ds,bx
		mov byte ptr ds:[0],ah
		mov byte ptr ds:[1],al
		mov byte ptr ds:[2],'$'
		mov dx,0h
		mov ah,9h
		int 21H
		
		mov ax,4c00h
		int 21h
	coverA:
		-- 这个位置最好这样写,来保护局部变量
		-- start: push cx
         -- 	  posh si
		and ax,1101111111011111b	;执行转换从大写,此操作并没有更改cx,si,所以就注释了保护局部表里的操作
		-- 这个位置最好这样写,来恢复之前的局部变量
		-- ok: pop si
		-- 	   pop cx
		iret							
	len:
		nop
	code ends
end
		

单步中断`int 01H`

标志寄存器的TF位标志单步中断			# 即debug界面的 	-t指令
TF trap flag      用于调试单步操作
CPU执行完指令后检测TF,TF=1则产生单步中断

产生单步中断后的要做动作
	push 标志寄存器
	TF IF 重新设为0
	push CS
	push IP
	(ip)=(1*4),(cs)=(1*4+2) # 根据中断向量表int 01H 记录的信息,将当前CSIP指针指向int 01H机器码指令的位置
																					^
																			  		|
																			  		|
																			  		|
																			  即1号中断处理程序
1号中断处理程序的功能,显示所有寄存器中的内容后,等待输入命令


#特殊情况
当cpu修改SS栈段时,即便检测到 TF=1,也不会触发中断
原因是假如触发中断,
由于sp并没有指向合理的位置,所以会引起错误

因此,修改SP的指令要紧随修改SS的指令,连续存放

在这里插入图片描述

中断类型码功能或触发条件

								中断的作用十分像调用函数
							不同的函数/中断程序封装不同的功能

在这里插入图片描述

;编程:在屏幕中间依次显示“a”~“z”,并可以让人看清。在显示的过程中,按下'Esc'键后,改变显示的颜色。
;完整功能代码:
assume cs:code

stack segment
	db 128 dup (0)
stack ends

data segment
	dw 0,0
data ends

code segment
start:	
	mov ax,stack
	mov ss,ax
	mov sp,128
	mov ax,data
	mov ds,ax
	mov ax,0
	mov es,ax

	push es:[9*4]
	pop ds:[0]
	push es:[9*4+2]
	pop ds:[2]		;将原来的int 9中断例程的入口地址保存在ds:0、ds:2单元中

	mov word ptr es:[9*4],offset int9
	mov es:[9*4+2],cs	;在中断向量表中设置新的int 9中断例程的入口地址

	mov ax,0b800h
	mov es,ax
	mov ah,'a'
s:	
	mov  es:[160*12+40*2],ah
	call delay
	inc ah
	cmp ah,'z'
	jna s
	mov ax,0
	mov es,ax

	push ds:[0]
	pop es:[9*4]
	push ds;[2]
	pop es;[9*4+2]   	;将中断向量表中int 9中断例程的入口恢复为原来的地址

	mov ax,4c00h
	int 21h

delay:	
	push ax
	push dx
	mov dx,2000h
	mov ax,0
s1: 	
	sub ax,1
	sbb dx,0
	cmp ax,0
	jne s1
	cmp dx,0
	jne s1
	pop dx
	pop ax
	ret

;------以下为新的int 9中断例程--------------------

int9:	
	push ax
	push bx
	push es

	in al,60h

	pushf
	pushf
	pop bx
	and bh,11111100b
	push bx
	popf
	call dword ptr ds:[0] 	;对int指令进行模拟,调用原来的int 9中断例程

	cmp al,1
	jne int9ret

	mov ax,0b800h
	mov es,ax
	inc byte ptr es:[160*12+40*2+1]  ;属性增加1,改变颜色

int9ret:
	pop es
	pop bx
	pop ax
	iret

code ends

end start

80x86

I/O ADDRINT TYPE(16进制)FUNCTION
00 ~ 030除法溢出中断
04 ~ 071单步(用于DEBUG)
08 ~ 0B2非屏蔽中断(NMI)
0C ~ 0F3断点中断(用于DEBUG)
10 ~ 134溢出中断
14 ~ 175打印屏幕
18 ~ 1F6/7保留

8259主片

I/O ADDRINT TYPE(16进制)FUNCTION
20 ~ 238定时器(IRQ0)
24 ~ 279键盘(IRQ1)
28 ~ 2BA彩色/图形(IRQ2)
2C ~ 2FB串行通信COM2(IRQ3)
30 ~ 33C串行通信COM1(IRQ4)
34 ~ 37DLPT2控制器中断(IRQ5)
38 ~ 3BE磁盘控制器中断(IRQ6)
3C ~ 3FFLPT1控制器中断(IRQ7)

BIOS

I/O ADDRINT TYPE(16进制)FUNCTION
40 ~4310视频显示 I/O根据ax,bx,dx等确定干啥
44 ~ 4711设备检验
48 ~ 4B12测定存储器容量
4C ~ 4F13磁盘 I/O
50 ~ 5314RS-232 串行口 I/O
54 ~ 5715系统描述表指针
58 ~ 5B16键盘 I/O
5C ~ 5F17打印机 I/O
60 ~ 6318ROM BASIC 入口代码
64 ~ 6719引导装入程序
68 ~ 6B1A日时钟

提供用户中断

I/O ADDRINT TYPE(16进制)FUNCTION
6C ~ 6F1BCtrl - Break 控制的软中断
70 ~ 731C定时器控制的软中断
74 ~ 771D视频参数块
78 ~ 7B1E软盘参数块
7C ~ 7F1F图形字符扩展码

在DOS系统(实模式)下,从0x20开始,用于操作系统本身。

DOS

I/O ADDRINT TYPE(16进制)FUNCTION
80 ~ 8320DOS 中断返回
84 ~ 8721DOS 系统功能调用
88 ~ 8B22程序中止时 DOS 返回地址(用户不能直接调用)
8C ~ 8F23Ctrl - Break 处理地址(用户不能直接调用)
90 ~ 9324严重错误处理(用户不能直接调用)
94 ~ 9725绝对磁盘读功能
98 ~ 9B26绝对磁盘写功能
9C ~ 9F27终止并驻留程序
A0 ~ A328DOS安全使用
A4 ~ A729快速写字符
A8 ~ AB2AMicrosoft 网络接口
B8 ~ BB2E基本 SHELL 程序装入
BC ~ BF2F多路服务中断
CC ~ CF33鼠标中断
104 ~ 10741硬盘参数块
118 ~ 11B46第二硬盘参数块
11C ~ 3FF47 ~ FFBASIC 中断

保护模式

在Linux下(保护模式),没有使用BIOS设置的中断向量表,0x00 ~ 0x1F是CPU保护模式下的默认中断向量,而0x20开始,都是被Linux系统重新设置的。

X86占用的中断向量表如下:

向量号助记符说明类型错误号产生源
0#DE除出错故障DIV或IDIV指令。
1#DB调试故障/陷阱任何代码或数据引用,或是INT 1指令。
2NMI中断中断非屏蔽外部中断。
3#BP断点陷阱INT 3指令。
4#OF溢出陷阱INTO指令。
5#BR边界范围超出故障BOUND指令。
6#UD无效操作码故障UD2指令或保留的操作码。
7#NM设备不存在故障浮点或WAIT/FWAIT指令。
8#DF双重错误异常终止有(0)任何可产生异常、NMI或INTR的指令。
9协处理器段超越(保留)故障浮点指令
10#TS无效的任务状态段TSS故障任务交换或访问TSS
11#NP段不存在故障加载段寄存器或访问系统段
12#SS堆栈段错误故障堆栈操作或SS寄存器加载
13#GP一般保护错误故障任何内存引用和其他保护检查
14#PF页面错误故障任何内存引用
15(intel保留)
16#MFx87 FPU浮点错误故障
17#AC对齐检查故障有(0)对内存中任何数据的引用。
18#MC机器检查异常终止错误码(若有)和产生源与CPU类型有关。
19#XFSIMD浮点异常故障

INT 21H的功能

AH 功能 调用参数 返回参数
00 程序终止(同INT 20H) CS=程序段前缀  
01 键盘输入并回显   AL=输入字符
02 显示输出 DL=输出字符  
03 异步通迅输入   AL=输入数据
04 异步通迅输出 DL=输出数据  
05 打印机输出 DL=输出字符  
06 直接控制台I/O DL=FF(输入)
DL=字符(输出)
AL=输入字符
07 键盘输入(无回显)   AL=输入字符
08 键盘输入(无回显)
检测Ctrl-Break
  AL=输入字符
09 显示字符串 DS:DX=字符串地址起点偏移地址
'$'结束字符串
 
0A 键盘输入到缓冲区 DS:DX=缓冲区首地址
(DS:DX)=缓冲区最大字符数
(DS:DX+1)=实际输入的字符数
0B 检验键盘状态   AL=00 有输入
AL=FF 无输入
0C 清除输入缓冲区并
请求指定的输入功能
AL=输入功能号
(1,6,7,8,A)
 
0D 磁盘复位   清除文件缓冲区
0E 指定当前缺省的磁盘驱动器 DL=驱动器号 0=A,1=B,... AL=驱动器数
0F 打开文件 DS:DX=FCB首地址 AL=00 文件找到
AL=FF 文件未找到
10 关闭文件 DS:DX=FCB首地址 AL=00 目录修改成功
AL=FF 目录中未找到文件
11 查找第一个目录项 DS:DX=FCB首地址 AL=00 找到
AL=FF 未找到
12 查找下一个目录项 DS:DX=FCB首地址
(文件中带有*或?)
AL=00 找到
AL=FF 未找到
13 删除文件 DS:DX=FCB首地址 AL=00 删除成功
AL=FF 未找到
14
顺序读
DS:DX=FCB首地址
AL=00 读成功
=01 文件结束,记录中无数据
=02 DTA空间不够
=03 文件结束,记录不完整
15 顺序写 DS:DX=FCB首地址 AL=00 写成功
=01 盘满
=02 DTA空间不够
16 建文件 DS:DX=FCB首地址 AL=00 建立成功
=FF 无磁盘空间
17 文件改名 DS:DX=FCB首地址
(DS:DX+1)=旧文件名
(DS:DX+17)=新文件名
AL=00 成功
AL=FF 未成功
19 取当前缺省磁盘驱动器   AL=缺省的驱动器号 0=A,1=B,2=C,...
1A 置DTA地址 DS:DX=DTA地址  
1B
取缺省驱动器FAT信息
  AL=每簇的扇区数
DS:BX=FAT标识字节
CX=物理扇区大小
DX=缺省驱动器的簇数
1C 取任一驱动器FAT信息 DL=驱动器号 同上
21
随机读
DS:DX=FCB首地址
AL=00 读成功
=01 文件结束
=02 缓冲区溢出
=03 缓冲区不满
22 随机写 DS:DX=FCB首地址 AL=00 写成功
=01 盘满
=02 缓冲区溢出
23 测定文件大小 DS:DX=FCB首地址 AL=00 成功(文件长度填入FCB)
AL=FF 未找到
24 设置随机记录号 DS:DX=FCB首地址  
25 设置中断向量 DS:DX=中断向量
AL=中断类型号
 
26 建立程序段前缀 DX=新的程序段前缀  
27
随机分块读
DS:DX=FCB首地址
CX=记录数
AL=00 读成功
=01 文件结束
=02 缓冲区太小,传输结束
=03 缓冲区不满
28 随机分块写 DS:DX=FCB首地址
CX=记录数
AL=00 写成功
=01 盘满
=02 缓冲区溢出
29 分析文件名 ES:DI=FCB首地址
DS:SI=ASCIIZ串
AL=控制分析标志
AL=00 标准文件
=01 多义文件
=02 非法盘符
2A 取日期   CX=年
DH:DL=月:日(二进制)
2B 设置日期 CX:DH:DL=年:月:日 AL=00 成功
=FF 无效
2C 取时间   CH:CL=时:分
DH:DL=秒:1/100秒
2D 设置时间 CH:CL=时:分
DH:DL=秒:1/100秒
AL=00 成功
=FF 无效
2E 置磁盘自动读写标志 AL=00 关闭标志
AL=01 打开标志
 
2F 取磁盘缓冲区的首址   ES:BX=缓冲区首址
30 取DOS版本号   AH=发行号,AL=版本
31 结束并驻留 AL=返回码
DX=驻留区大小
 
33
Ctrl-Break检测
AL=00 取状态
=01 置状态(DL)
DL=00 关闭检测
=01 打开检测
DL=00 关闭Ctrl-Break检测
=01 打开Ctrl-Break检测
35 取中断向量 AL=中断类型 ES:BX=中断向量
36

取空闲磁盘空间

DL=驱动器号
0=缺省,1=A,2=B,...
成功:AX=每簇扇区数
BX=有效簇数
CX=每扇区字节数
DX=总簇数
失败:AX=FFFF
38 置/取国家信息 DS:DX=信息区首地址 BX=国家码(国际电话前缀码)
AX=错误码
39 建立子目录(MKDIR) DS:DX=ASCIIZ串地址 AX=错误码
3A 删除子目录(RMDIR) DS:DX=ASCIIZ串地址 AX=错误码
3B 改变当前目录(CHDIR) DS:DX=ASCIIZ串地址 AX=错误码
3C 建立文件 DS:DX=ASCIIZ串地址
CX=文件属性
成功:AX=文件代号
错误:AX=错误码
3D
打开文件
DS:DX=ASCIIZ串地址
AL=0 读
=1 写
=3 读/写
成功:AX=文件代号
错误:AX=错误码
3E 关闭文件 BX=文件代号 失败:AX=错误码
3F
读文件或设备
DS:DX=数据缓冲区地址
BX=文件代号
CX=读取的字节数
读成功:
AX=实际读入的字节数
AX=0 已到文件尾
读出错:AX=错误码
40 写文件或设备 DS:DX=数据缓冲区地址
BX=文件代号
CX=写入的字节数
写成功:
AX=实际写入的字节数
写出错:AX=错误码
41 删除文件 DS:DX=ASCIIZ串地址 成功:AX=00
出错:AX=错误码(2,5)
42

移动文件指针

BX=文件代号
CX:DX=位移量
AL=移动方式(0:从文件头绝对位移,1:从当前位置相对移动,2:从文件尾绝对位移)
成功:DX:AX=新文件指针位置
出错:AX=错误码
43
置/取文件属性
DS:DX=ASCIIZ串地址
AL=0 取文件属性
AL=1 置文件属性
CX=文件属性
成功:CX=文件属性
失败:CX=错误码
44



设备文件I/O控制



BX=文件代号
AL=0 取状态
=1 置状态DX
=2 读数据
=3 写数据
=6 取输入状态
=7 取输出状态
DX=设备信息



45 复制文件代号 BX=文件代号1 成功:AX=文件代号2
失败:AX=错误码
46 人工复制文件代号 BX=文件代号1
CX=文件代号2
失败:AX=错误码
47 取当前目录路径名 DL=驱动器号
DS:SI=ASCIIZ串地址
(DS:SI)=ASCIIZ串
失败:AX=出错码
48 分配内存空间 BX=申请内存容量 成功:AX=分配内存首地
失败:BX=最大可用内存
49 释放内容空间 ES=内存起始段地址 失败:AX=错误码
4A 调整已分配的存储块 ES=原内存起始地址
BX=再申请的容量
失败:BX=最大可用空间
AX=错误码
4B
装配/执行程序
DS:DX=ASCIIZ串地址
ES:BX=参数区首地址
AL=0 装入执行
AL=3 装入不执行
失败:AX=错误码
4C 带返回码结束 AL=返回码 回显B800:00A0-B800:103F的显存空间信息
4D 取返回代码   AX=返回代码
4E 查找第一个匹配文件 DS:DX=ASCIIZ串地址
CX=属性
AX=出错代码(02,18)
4F 查找下一个匹配文件 DS:DX=ASCIIZ串地址
(文件名中带有?或*)
AX=出错代码(18)
54 取盘自动读写标志   AL=当前标志值
56 文件改名 DS:DX=ASCIIZ串(旧)
ES:DI=ASCIIZ串(新)
AX=出错码(03,05,17)
57 置/取文件日期和时间 BX=文件代号
AL=0 读取
AL=1 设置(DX:CX)
DX:CX=日期和时间
失败:AX=错误码
58 取/置分配策略码 AL=0 取码
AL=1 置码(BX)
成功:AX=策略码
失败:AX=错误码
59
取扩充错误码
  AX=扩充错误码
BH=错误类型
BL=建议的操作
CH=错误场所
5A 建立临时文件 CX=文件属性
DS:DX=ASCIIZ串地址
成功:AX=文件代号
失败:AX=错误码
5B 建立新文件 CX=文件属性
DS:DX=ASCIIZ串地址
成功:AX=文件代号
失败:AX=错误码
5C

控制文件存取

AL=00封锁
=01开启
BX=文件代号
CX:DX=文件位移
SI:DI=文件长度
失败:AX=错误码

62 取程序段前缀   BX=PSP地址
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值