导图社区 汇编语言
本思维导图是基于Dodbox模拟环境的8086自学汇编知识,解释了C语言与汇编的联系,高级语言转化为汇编执行的机制。有助于理解计算机运行过程,函数调用过程。和电脑启动初始化过程,对后面实现自己的微操作系统有着重要意义。
编辑于2022-01-27 13:08:45汇编语言(基于8086)
环境搭建
环境链接
https://pan.baidu.com/s/19bTk_GxlQaJ6gDdefBtnIQ?pwd=1234
配置debug调试环境
1| 将环境中的MASM文件夹拷贝到d盘根目录下
2| 安装好DOSBOX打开输入
mount c d:\MASM
将d盘的MASM挂载为C盘
c:
进入C盘
dir
查看C盘中是否有MASM的文件
逻辑结构
寄存器
通用寄存器(16位)
AX、DX
分为两个8位寄存器H/L
CX-loop指令的计数寄存器
BX-常做基址寄存器
默认的段地址是DS段寄存器
[bx]→(ds×16+bx) ss:[bx]→(ss×16+bx)
变址寄存器
SI-源变址寄存器
DI-目标变址寄存器
与BX的区别:SI、BI不能够分成两个8位寄存器来使用(不能[si+di])
指针寄存器
BP-和BX相似的基址寄存器
默认的段地址是SS段寄存器
[bp]→(ss×16+bp)默认 bs:[bp]→(bs×16+bp)
SP-栈顶指针寄存器
指令指针寄存器
IP
自增
段寄存器
CS-代码段寄存器
DS-数据段寄存器
[数]=DS+数
mov ds,ax
SS-栈段寄存器
ES-附加段寄存器
必须通过通用寄存器来转移赋值
标志寄存器
PSW
ZF-零标志(Zero Flag)
ZF =1,表示“结果是0”,1表示“逻辑真”
ZF=0,表示“结果不是0”,0表示“逻辑假”
PF-奇偶标志(Parity Flag)
PF记录指令执行后,结果的所有二进制位中1的个数
PF=1,1的个数为偶数
PF=0,1的个数为奇数
SF-符号标志(Sign Flag)
SF记录指令执行后,将结果视为有符号数
计算机中有符号数一律用补码来表示和存储
SF=1,结果为负
SF=0,结果为正
注:
SF标志是CPU对有符号数运算结果的一种记录。
当数据当作有符号数来运算的时候,通过SF可知结果的正负;将数据当作无符号数来运算,SF的值则没有意义,虽然相关的指令影响了它的值。
CF-进位标志(Carry Flag)
在进行无符号数运算的时候,CF记录了云端结果的最高有效位向更高位的进位值,或从更高位的借位值
CF=1,有进位或借位
CF=0,无进位或借位
OF-溢出标志(Overflow Flag)
在进行有符号运算的时候,如结果超过了机器所能表示的范围值称为溢出
OF=1,有溢出
OF=0,无溢出
注:
此处溢出只对有符号数运算而言
判断
一位符号位
联系
CF和OF的区别: CF是对无符号数运算有意义的进/借位标志位 OF是对有符号数运算有意义的溢出标志位
DF-方向标志位(Direction Flag)
在串处理指令中,控制每次操作后SI,DI的增减
DF=0:每次操作后SI,DI递增
DF=1:每次操作后SI,DI递减
IF-中断标志位(Interrupt flag)
IF=1时,允许CPU响应可屏蔽中断
IF=0时,关闭中断
TF-陷阱标志(Trap flag)
用于调试时的单步方式操作
当TF=1时,每条指令执行后产生陷阱,由系统控制计算机
当TF=0时,CPU正常工作,不产生陷阱。
对PSW有影响的指令
Add,Sub,Mul,Div,Inc,Or,And
对PSW无影响的指令
Mov,Push,Pop
寻址范围(20位)
段地址×16+偏移地址
指令
mov
mov 寄存器,数据
mov ax,8
mov 寄存器,寄存器
mov ax,bx
mov 寄存器,内存单元
mov ax,[0]
mov 内存单元,寄存器
mov [0],ax
mov 段寄存器,寄存器
mov ds,ax
中断处理
端口读写
in
in al,端口号
out
out 端口号,al
注
0~255以内的端口号用立即数,256~65535的端口进行读写时,端口号放在dx中
in和out指令中,只能使用ax或al存放从端口读入的数据或要发送到端口的数据。
访问8位端口时用al,访问16位端口时用ax
sti
用于设置IF=1
cli
用于设置IF=0
iret
中断返回
add
add 寄存器,数据
add ax,8
add 寄存器,寄存器
add ax,bx
add 寄存器,内存单元
add ax,[0]
add 内存单元,寄存器
add [0],ax
adc
带有进位加法指令,它利用了CF商记录的进位值
adc 操作数1,操作数2
操作数1=操作数2+CF
例
adc ax,bx实现的功能:(ax)=(ax)+(bx)+CF
大数相加(>16位的)
例:将1EF000H+201000H,的结果放到ax(高16位)和bx(低16位)中 思路:先将低16位相加,然后将高16位和进位值相加
mov ax,001EH mov bx,0F000H add bx,1000H adc ax,0020H
001E F000H + 0020 1000H __________________ ax bx
更高位以此类推
128位数据相加
数据: dw 0A452H,0A8f5H,78F6H,0A8EH,8B7AH,5476H,0F04H,671EH dw 0E71EH,0EF04H,5476H,8B7AH,0A8EH,78E6H,58F5H,0542H 主程序: start: mov ax,data mov ds,ax mov si,0;指向被加数 mov di,16;指向加数 mov cx,8 call add128 mov ax,4c00h int 21h
子程序: add128: push ax push cx push si push di sub ax,ax;--->让CF=0,不能用mov ax 0 s:mov ax,[si] adc ax,[di] mov [si],ax inc si;--->不改变CF值,不能用add si,2 inc si inc di inc di loop s pop di pop si pop cx pop ax ret
sub
sub 寄存器,数据
sub ax,8
sub 寄存器,寄存器
sub ax,bx
sub 寄存器,内存单元
sub ax,[0]
sub 内存单元,寄存器
sub [0],ax
sbb
带借位减法指令
sub 操作数1,操作数2
操作数1=操作数1-操作数2-CF
例
sbb ax,bx实现功能:(ax)=(ax)-(bx)-CF
大数相减(>16位的)
例:计算003E1000H-00202000H结果放在ax,bx中
mov bx,1000H mov ax,003EH sub bx,2000H sbb ax,0020H
003E 1000H - 0020 2000H ________________ ax bx
cmp
功能
cmp是比较指令,功能相当于减法指令,只是不保存结果
cmp指令执行后,将对标志寄存器产生影响
其他相关指令通过识别这些影响的标志寄存器位来得知比较结果
cmp 操作数1,操作数2
操作数1=操作数1-操作数2
jxxx
jxxx 标号
j-Jump e-Equal n-Not b-Below a-Above L-less g-Greater s-Sign C-carry p-Parity o-Overflow z-Zero
套路
cmp oper1,oper2;或者其他影响寄存器的指令 jxxx 标号
jmp
功能
无条件转移,可以只修改IP,也可以同时修改CS和IP
要给出两种信息
转移的目的地址
转移的距离
段间转移(远转移):jmp 2000:1000
不支持立即数可从内存和寄存器中获取
段内短转移:jmp short 标号;IP的修改范围为-128~127,8位的位移
段内近转移:jmp near ptr 标号;IP的修改方位为-32768~32767,16位的位移
使用
jmp short 标号
(IP)=(IP)+指令中的立即数
jmp near ptr 标号
(IP)=(IP)+指令中的立即数
jmp far ptr 标号
CS:IP=标号CS:标号IP
jmp 寄存器
(IP)=(16位寄存器)
jmp dword ptr 内存单元地址
从单元地址处开始存放着两个字,高地址处的字是转移的目的段地址,低地址处是转移的目的的偏移地址
CS:IP=[地址+2]:[地址+0]
jmp word ptr 内存单元
从内存单元地址处开始存放着一个字,是转移的目的偏移地址
(IP)=[地址+0]
jcxz
jcxz 标号
功能
当(CX)=0,则跳转到标号处
当(CX)≠0,什么也不做(程序向下执行)
loop
loop 标号
执行loop要进行的操作 ①(cx)=(cx)-1 ②判断cx中的值 不为零则转至标号处执行程序 如果为零则向下执行
机器码中包含的不是目的地址,而是到目的地址的位移
call
call指令实现转移的方法和jmp指令的原理相似
近转移
call 标号
(1) 将当前的IP或CS和IP压入栈中
(SP)=(SP)-2 ((SS)*16+(SP))=(IP)
(2) 转移到标号处执行指令
(IP)=(IP)+16位位移
push IP jmp near ptr 标号
call 16位寄存器
(SP)=(SP)-2 ((SS)*16+(SP))=(IP) (IP)=(16位寄存器)
push IP jmp 16位寄存器
call word ptr 内存单元地址
push IP jmp word ptr 内存单元地址
mov sp,10h mov ax,0123h mov ds:[0],ax call word ptr ds:[0] 执行后:(IP)=0123H,(SP)=0EH
远转移
call far ptr 标号
(1) (SP)=(SP)-2 ((SS)*16+(SP))=(CS) (SP)=(SP)-2 ((SS)*16+(SP))=(IP)
(2) (CS )=标号所在的段地址 (IP)=标号所在的偏移地址
push CS push IP jmp far ptr 标号
call dword ptr 内存单元地址
push CS push IP jmp dword ptr 内存单元地址
mov sp,10h mov ax,0123h mov ds:[0],ax;低地址放偏移地址 mov word ptr ds:[2],0;高地址放段地址 call dword ptr ds:[0] 执行后:(CS)=0,(IP)=0123H,(SP)=0CH
ret
ret
用栈中的数据,修改IP的内容,从而实现近转移
相当于:pop IP
retf
用栈中的数据,修改CS和IP的内容,从而实现远转移
相当于:pop IP、pop CS
iret
中断返回
相当于:pop IP、pop CS、popf
push
push 内存
push [0]
((ss)*16+(sp))=((ds)*16+0)
push 寄存器
push ax
(sp)=(sp)-2 ((ss)*16+(sp))=(ax)
先:SP=SP-2 后:入栈
pop
pop 内存
pop [0]
((ds)*16+0)=((ss)*16+(sp))
pop 寄存器
pop ax
(ax)=((ss)*16+(sp)) (sp)=(sp)+2
先:出栈 后:SP=SP+2
不保护栈的越界(上下界)
栈段寄存器SS
存放栈顶的段地址
栈顶指针寄存器SP
存放栈顶的偏移地址
inc
自增
and
0&1=0 1&0=0 1&1=1
and 寄存器,寄存器
and al,bl
or
1|0=1 0|1=1 0|0=0
or 寄存器,寄存器
or al,bl
div
格式
div 寄存器
div 内存单元
例:将内存中的双字的数据除以单字数据,商存回内存
数据: dd 100001;被除数 dw 100;除数 dw 0;商 代码: mov ax,data mov ds,ax mov bx,10h mov ax,ds:[0] mov dx,ds:[2] div word ptr ds:[4] mov ds:[6],ax
除数决定被除数的长度(16或32)
mul
格式
mul 寄存器
mul 内存单元
lea 寄存器,标号
获取标号的偏移地址,传入到寄存器中
串传送指令
movsb
以字节为单位传送
((es)×16+(di))=((ds)×16+(si))
如果DF=0则:
(si)=(si)+1
(di)=(di)+1
如果DF=1则:
(si)=(si)-1
(di)=(di)-1
movsw
以字为单位传送
((es)×16+(di))=((ds)×16+(si))
如果DF=0则:
(si)=(si)+2
(di)=(di)+2
如果DF=1则:
(si)=(si)-2
(di)=(di)-2
cld
将标志寄存器的DF位设为0(clear)
std
将标志寄存器的DF位设为1(setup)
rep
根据cx的值,重复执行后面的指令
例
内存中字符串的复制
数据: db 'Welcome to masm!' db 16 dup (0) 代码: start: mov ax,data mov ds,ax mov si,0 mov es,ax mov di,16 cld mov cx,16 rep movsb
移位指令
逻辑左移
SHL OPR,CNT
末位补0
逻辑右移
SHR OPR,CNT
首位补0
算术左移
SAL OPR,CNT
末位补0
带进位位循环左移
RCL OPR,CNT
末位用原来CF位填充
循环左移
ROL OPR,CNT
末位用溢出去的首位填充
循环右移
ROR OPR,CNT
首位用溢出的末位填充
算术右移
SAR OPR,CNT
首位不变,次首位用原来的首位填充
带进位位循环右移
RCR OPR,CNT
首位用原来CF中的填充
S,SH-Shift L-Left R-Right A-Arithmetic R,RO-Rotate C-Carry
编译器阅读
数据有多长
word ptr(一个字)
mov word ptr [0],1
type ptr(一个字节)
mov byte ptr [0],1
dup
和db(一字节)、dw(一字)、dd(双字)等数据伪指令 配合使用,用来进行数据的重复
offset
offset 标号
start:mov ax,offset start;相当于mov ax,0//这条指令长度为3字节 s:mov ax,offset s;相当于mov ax,3
例:运行过程中将s处的指令复制到s0处
代码 s: mov ax,bx;长两字节 mov si,offset s;存储s的偏移位置 mov di,offset s0;存储s0的偏移位置 mov ax,cs:[si];读取指令 mov cs:[di],ax;写入指令 s0: nop;占位符 nop
取得标号的偏移地址
seg
seg 标号
取得段地址
[...]的规定和(...)的约定
[...]---(汇编语法规定)表示一个内存单元
(...)---(为学习方便做出的约定)表示一个内存单元或寄存器中的内容
idata表示常量
mov ax,[idata] 内存 mov bx,idata 立即数 mov ds,idata 非法
分段管理内存
注:内存并没有分段,段的划分来自于CPU!!
段地址
段地址×16都是16的倍数,所以起始位置一定是16的倍数
任意
偏移地址
偏移地址为16位,16位地址的寻址能力位64K,所以一个段长最大为64K
例
21F60H
数据存放在2000:1F60单元
物理地址
段地址×16+偏移地址 (左移4位)
Debug的使用
命令
-R查看、改变CPU寄存器的内容
R ~查看寄存器内容
R 寄存器名 ~改变指定寄存器内容
-D查看内存中的内容
D ~列出预设地址内存处的128个字节的内容
D 段地址:偏移地址 ~列出内存中指定地址的内容
D 段地址:偏移地址 结尾偏移地址 ~列出内存中指定地址范围内的内容
-E改变内存中的内容
E 段地址:偏移地址 数据1 数据2 .....
E 段地址:偏移地址 逐个询问式修改 空格 -接受,继续 回车 -结束
-U将内存中的机器指令翻译成汇编指令
U 段地址:偏移地址 ~查看汇编代码
-A以汇编指令的格式在内存中写入机器指令
A 段地址:偏移地址 ~写入汇编指令
-T逐条执行机器指令
T ~执行CS:IP处的指令,结束不输出信息
-P逐条执行指令
类似于T,但遇到子程序、中断等时,直接执行,然后显示结果,结束输出信息
-G连续执行
从指定地址开始运行程序,直到遇到断点或者程序正常结束
-Q退出
调试程序
debug file.EXE
汇编语言代码结构
伪指令
编译器根据相关的伪指令来进行编译工作
assume(假设)
含义是假设某一段寄存器和程序的某一个用segment ... ends定义的段相关联--segment cs:codesg指CS寄存器与codesg关联,将定义的codesg当作程序的代码来使用。
只能是XS形式,只不过是给编译器和人看的,除了cs不能换,其他的写什么不重要,重要的是初始化
段定义
一个汇编程序是由多个段组成的,这些段被用来存放代码、数据或当作占空间来使用。
一个有意义的汇编程序至少要有一个段,这个段用来存放代码
定义程序中的段:每个段都需要有段名
段名 segment --段的开始
。。。。
段名 ends --段的结束
end
汇编程序的结束标记。若程序结尾处不加end,编译器在编译程序时,无法知道程序在何处结束。
程序
程序返回(固定) mov ax,4c00h int 21h
程序结束运行后,将CPU控制权交换给使它得以运行的程序(常为DOS系统)
源程序运行
assume cs:codesg codesg segment mov ax,0123H mov bx,0456H add ax,bx add ax,ax mov ax,4c00H int 21H codesg ends end
1. 编译
masm file.asm
目标文件(*.OBJ)对一个源程序进行编译要得到的最终结果
列表文件(*.LST)是编译器将程序编译为目标文件的过程中产生的中间结果
交叉引用文件(*.CRF)同列表文件一样,是编译器将源程序编译为目标文件过程中产生的中间结果
对源程序的编译结束,编译器输出两行告诉我们这个源程序没有警告和必须要修改的错误
2. 链接
link file.OBJ
3. 执行文件
描述信息
描述程序的特征
执行部分
编写的程序部分包括程序返回部分
高级汇编技术
子程序的另一种写法
代码: main proc start: ··· 主程序 call sun ··· main endp sun proc ··· 主程序 ··· sun endp
多文件组织
主文件P1
extrn sun:far;引用子程序sun assume cs:code code segment start: call far ptr sun mov ax,4c00h int 21h code ends end start;通知程序结束和程序入口
子文件P2
public sun;公开函数 assume cs:code code segment sun proc s: add ax,ax retf sun endp code ends end
masm P1; masm P2; link P1+P2; P1.exe
汇编处理过程
编辑程序
myfile.asm
assume cs:code,ds:data data segment n1 db 1 data ends code segment start: mov ax,data mov ds,ax lea bx,n1;n1的偏移地址 call subprog call subprog mov ax,4c00h int 21h subprog: mov ax,bx mov ax,[bx] mov dx,100h ret code ends end start;通知程序结束和程序入口
汇编程序
交叉文件myfile.crf
包含标识符(段名、过程名、变量名、标号)在源程序中的位置和被引用的位置,对源程序所用的各种符号进行前后对照的文件
链接程序
myfile.exe
myfile.map
列表文件myfile.lst
Microsoft (R) Macro Assembler Version 5.00 1/25/22 22:26:17 Page 1-1 1 assume cs:code,ds:data 2 0000 data segment 3 0000 01 n1 db 1 4 0001 data ends 5 0000 code segment 6 0000 start: 7 0000 B8 ---- R mov ax,data 8 0003 8E D8 mov ds,ax 9 0005 8D 1E 0000 R lea bx,n1;n1çš„åç§»åœ°å€ 10 0009 E8 0014 R call subprog 11 000C E8 0014 R call subprog 12 000F B8 4C00 mov ax,4c00h 13 0012 CD 21 int 21h 14 0014 subprog: 15 0014 8B C3 mov ax,bx 16 0016 8B 07 mov ax,[bx] 17 0018 BA 0100 mov dx,100h 18 001B C3 ret 19 20 001C code ends 21 end start;通知程åºç»“æŸå’Œç¨‹åºå… ¥å£ Microsoft (R) Macro Assembler Version 5.00 1/25/22 22:26:17 Symbols-1 Segments and Groups: N a m e Length Align Combine Class CODE . . . . . . . . . . . . . . 001C PARA NONE DATA . . . . . . . . . . . . . . 0001 PARA NONE Symbols: N a m e Type Value Attr N1 . . . . . . . . . . . . . . . L BYTE 0000 DATA START . . . . . . . . . . . . . L NEAR 0000 CODE SUBPROG . . . . . . . . . . . . L NEAR 0014 CODE @FILENAME . . . . . . . . . . . TEXT P1 21 Source Lines 21 Total Lines 7 Symbols 50612 + 465932 Bytes symbol space free 0 Warning Errors 0 Severe Errors
将源程序、目标程序、错误信息列表以供检查程序用
二次汇编
第一次汇编
确定地址,翻译成各条机器码,字符标号原样写出
第二次汇编
标号代真,将字符标号用计算出来的地址值或偏移量代换
宏汇编
格式
定义
macro_name MACRO [哑元表];形参/虚参 ······ ;宏定义体 ······ ENDM
调用
macro_name [实元表];实参
VS子程序
宏
优
参数传递简单,执行效率高
缺
代码占用空间大
子程序
优
代码占用空间小
缺
执行效率低
宏展开
汇编程序把宏调用展开
将宏定义体复制到宏指令位置,实参代替虚参
直接视为字符串将宏替换
局部标号
例
求绝对值
absol MACOR oper LOCAL next;声明为局部标号,区别不同的宏替换 cmp oper,0 jge next neg oper next: ENDM 宏调用: ······ absol var ······ absol bx ······
宏展开
宏展开: ······ cmp var,0 jge ??0000 neg var ??0000: ······ cmp var,0 jge ??0001 neg var ??0001: ······
操作码作为形参
宏定义
leap macro cond,lab j&cond lab endm
宏调用
leap z,there ······ leap nz,here
宏展开
jz there ······ jnz here
宏库
将用到的宏定义分类放到不同的宏库文件中
建立MACOR.MAC
macro1 MACRO [哑元表] ······ MACRO macro2 MACRO [哑元表] ······ MACRO macro3 MACRO [哑元表] ······ MACRO
在程序中包含宏库调用宏
include MACRO.MAC ······ macro1 [实原表] ······ macro2 [实原表] ······ macron [实原表]
例
输出字符串
P1.asm
include asmio.mac assume cs:code,ds:data data segment string db 'gello word',13,10,'$' data ends code segment start: mov ax,data mov ds,ax output string mov ax,4c00h int 21h code ends end start;通知程序结束和程序入口
asmio.mac
output macro addr mov dx,offset addr mov ah,09h int 21h endm
条件汇编
在汇编过程中,根据条件把一段源程序包括在汇编语言程序内或者排除在外
格式
IFxx 自变量 ···;满足条件则汇编此块 [ELSE] ···;不满足条件则汇编此块 ENDIF
IF 表达式
表达式≠0,则汇编
宏定义
MAX MACRO K,A,B,C LOCAL NEXT,OUT MOV AX,A IF K-1 IF K-2 CMP C,AX JIE NEXT MOV AX,C ENDIF NEXT:CMP B,AX JIE OUT MOV AX,B ENDIF OUT: ENDM
宏调用
MAX 1,P MAX 2,P,Q MAX 3,P,Q,R
宏展开
MOV AX,P ??0001:
MOV AX,P ??0002:CMP Q,AX JLE ??0003 MOV AX,Q ??0003:
MOV AX,P CMP R,AX JLE ??0004 MOV AX,R ??0004:CMP Q,AX JLE ??0005 MOV AX,Q ??0005:
IFE 表达式
表达式=0,则汇编
IF1
在第一遍扫视期间满足条件
IF2
在第二遍扫视期间满足条件
IFDEF 符号
符号已定义,则汇编
IFNDEF 符号
符号未定义,则汇编
IFB <自变量>
自变量为空,则汇编
IFNB <自变量>
自变量不为空,则汇编
IFIDN <字符串1>,<字符串2>
串1和串2相同
IFDIF <字符串1>,<字符串2>
串1和串2不相同
重复汇编
格式
重复位操作 REPT
REPT 表达式 ····;重复块 ENDM
例
把字符'A'到'Z'的ASCII码填入数组TABLE
CHAR='A' TABLE LABEL BYTE REPT 26 DB CHAR CHAR=CHAR+1 ENDM
展开
DB 41H DB 42H ··· DB 5AH
不定重复伪操作 IRP
例
生成一组入栈指令
IRP REG,<AX,BX,CX,DX> PUSH REG ENDM
展开
PUSH AX PUSH BX PUSH CX PUSH DX
不定重复伪操作 IRPC
IRPC 哑元,字符串 ··· ;重复块 ENDM
例
生成存储字符串的汇编语句
array label byte IRPC K,12345 db 'NO.&k' ENDM
展开
db 'NO.1' db 'NO.2' db 'NO.3' db 'NO.4' db 'NO.5'
程序中数据的存放
代码段
代码段中使用数据
代码: dw 0123h,0456h,0789h,0abch, 0defh,0fedh,0cbah,0987h mov bx,0 mov ax,0 mov cx,8 s:add ax,cs:[bx] add bx,2 loop s
有问题
在代码段中使用栈
将数据逆序存放
代码: dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h start:mov bx,0;代码开始的位置 mov ax,0 mov cx,8 s:add ax,cs:[bx] add bx,2 loop s
代码: dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h;8个字 dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0;16个字 start:mov ax,cs;代码开始的位置 mov ss,ax;栈段寄存器 mov sp,30h;指向栈顶 mov bx,0 mov cx,8 s:push cs:[bx];ss:sp=cs:bx add bx,2 loop s mov bx,0 mov cx,8 s0:pop cs:[bx];cs:bx=ss:sp add bx,2 loop s0
将数据、代码、栈放入不同的段
数据: dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h;8个字 栈: dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0;16个字 代码: start: mov ax,stack mov ss,ax;初始栈段寄存器 mov sp,20h;初始栈顶指针寄存器 mov ax,data mov ds,ax;初始数据寄存器 mov bx,0 mov cx,8 s:push [bx];ss:ps=ds:bx add bx,2 loop s mov bx,0 mov cx,8 s1:pop [bx];ds:bx=ss:ps add bx,2 loop s1
栈空间中的数据不在栈内的部分会自己改变
程序的一般框架
assume cs:code code segment ···· 数据 ···· begin: ···· 代码 ···· code ends end begin
assume cs:code,ds:data,ss:stack data segment ···· 数据 ···· data ebds stack segment ···· 数据 ···· stack ends code segment begin: ;开始标记 ;初始化DS,SS,SP寄存器 ···· 代码 ···· mov ax,4c00H int 21H code ends end begin ;通知程序的入口
内存寻址方式
内存分配
字符问题
用'....'的方式指明数据以字符的形式给出
大小写转化问题
数据: db 'BasiC' db 'iNfOrMaTiOn' 代码: start: mov ax,data mov ds,ax mov bx,0 mov cx,5 s:mov al,[bx] and al,11011111b mov [bx],al inc bx loop s mov bx,5 mov cx,11 s0:mov al,[bx] or al,00100000b mov [bx],al inc bx loop s0
偏移寻址
基址寻址 ds:[idata]
段前缀
(立即寻址)错误
mov ax,2000h mov ds,ax mov al,[0] mov bl,[1] mov cl,[2]
(立即寻址)正确
代码: mov ax,2000h mov ds,ax mov al,ds:[0];ds:0 mov bl,[bx];ds:bx mov cx,[10];10(错误)
将内存ffff:0~ffff:b中的数据拷贝到0:200~0:20b单元中
mov ax,0ffffh mov ds,ax mov ax,0020h mov es,ax mov bx,0 mov cx,12 s:mov dl,[bx] mov es:[bx],dl;es:bx=dl inc bx loop s
寄存器间接寻址
[bx]→(ds×16+bx) [di]→(ds×16+di) [si]→(ds×16+si)
数据复制
数据: db 'welcome to masm!' db '................' 代码: start: mov ax,data mov ds,ax mov si,0;源变址寄存器 mov di,16;目标变址寄存器 mov cx,8 s:mov ax,[si] mov [di],ax add si,2 add di,2 loop s
寄存器相对寻址 [bx+idata] idata[bx] [bx].idata
大小写转换
数据: db 'BasiC' db 'MinIX' 代码: start: mov ax,data mov ds,ax mov bx,0 mov cx,5 s:mov al,[bx] and al,11011111b mov [bx],al mov al,[5+bx] or al,00100000b mov [5+bx],al inc bx loop s
基址变址寻址
[bx+di]→(ds×16+bx+di) [bx+si]→(ds×16+bx+si) [bx][di]、[bx][si]
将每个单词改为大写
栈实现双层循环
数据: db 'ibm ' db 'dec ' db 'dos ' db 'vax ' 栈: dw 0,0,0,0,0,0,0,0 代码: start: mov ax,data mov ds,ax mov ax,stack mov ss,ax mov sp,16 mov bx,0 mov cx,4;外层循环次数 s0:push cx;外层循环次数入栈 mov si,0;外层指针偏移量 mov cx,3;内层循环次数 s:mov al,[bx+si] and al,11011111b mov [bx+si],al inc si;内层指针偏移 loop s add bx,16;外层指针偏移 pop cx;恢复外层循环次数 loop s0
相对基址变址寻址
[bx+si+idata] [bx+di+idata] [bx][di].idata
总结
汇编和C语言处理结构体对比
assume cs:code,ds:data data segment db ′other data!!!!!!′ db ′Yao′ db ′19800912′ dw 15 dw 32 db ′SHH′ data ends code segment start: mov ax,data mov ds,ax mov bx,10h mov word ptr [bx+0bh],11;修改15为 mov word ptr [bx+0dh],13;修改32为13 mov si,0 mov byte ptr [bx+0fh+si],′H′;修改字符 inc si mov byte ptr [bx+0fh+si],′O′;修改字符 inc si mov byte ptr [bx+0fh+si],′U′;修改字符 mov ax,4c00H int 21H code ends end start;通知程序结束和程序入口
流转程序与子程序
转移
按转移指令转移
无条件转移指令(如:jmp)
转移的距离
段间(远转移)
jmp 2000:1000
按照指明跳转的目的地址进行转移(不支持立即数,可从寄存器和内存中获取)
jmp far ptr 标号
按照指明跳转的目的地址进行转移
start:jmp far ptr s db 256 dup (0) s:mov ax,0fffh
指令直接包括了段地址和偏移地址
jmp dword ptr 内存单元地址
从单元地址处开始存放着两个字,高地址处的字是转移的目的段地址,低地址处是转移的目的的偏移地址
start:mov ax,0123H mov ds:[0],ax mov word ptr ds:[2],0 jmp dword ptr ds:[0];(CS)=0,(IP)=0123H
段内
段内近转移:jmp near ptr 标号; IP的修改方位为-32768~32767,16位的位移
按照当前IP的转移位移进行转移
start:jmp near ptr s db 256 dup (0) s:mov ax,0fffh
E9 00 01中立即数0100H=256,偏移地址是256
段内短转移:jmp short 标号; IP的修改范围为-128~127,8位的位移
超出界限案例
start:jmp short s;超过了127 db 130 dup (0) s:mov ax,0fffh
按照位移进行转移
start:mov ax,0 jmp short s add ax,1 s:inc ax
jmp 008是debug解释出来的,其实是通过(IP)=(IP)+立即数的到的
jmp short s的读取和执行
(1) (IP)=0003,CS:IP指向EB 03(jmp 的机器码)
(2) 读取指令码EB 03进入指令缓冲器
(3) (IP)=(IP)+所读取指令长度=(IP)+2=0005,CS:IP 指向add ax,0001
(4) CPU执行缓冲器中的指令EB 03
(5) 指令EB 03执行后,(IP)=(IP)+03=0008H,CS:IP指向inc ax
jmp word ptr 内存单元
从内存单元地址处开始存放着一个字,是转移的目的偏移地址
start:mov ax,0123H mov ds:[0],ax jmp word ptr ds:[0];(IP)=0123H
循环指令(如:loop)
代码: mov ax,2 mov cx,11 s:add ax,ax loop s
loop 0006(E2 FC )中FC=-4,则(IP)=(IP)+(-4)=6
条件转移指令
jcxz
短转移
start:mov ax,2000H mov ds,ax mov bx,0 s:mov cx,[bx] jcxz ok inc bx inc bx jmp short s ok:mov dx,bx
jcxz 0010(EC 04)中,则(IP)=(IP)+4=16
jmp 0008(EB F8)中F8=-8,则(IP)=(IP)+(-8)=8
jxxx
例:
如果(ah)=(bh),则(ah)=(ah)+(ah),否则(ah)=(ah)+(bh)
start: cmp ah,bh je s add ah,bh jmp short ok s:add ah,ah ok:ret
统计数值位8的字节的个数
数据: db 8,11,8,1,8,5,63,38 代码: start: mov ax,data mov ds,ax mov ax,0 mov bx,0 mov cx,8 s:cmp byte ptr [bx],8 jne next inc ax next: inc bx loop s
过程
中断
子程序
ret 标志
栈: db 16 dup (0) 代码: mov ax,4c00H int 21H start: mov ax,stack mov ss,ax mov sp,16 mov ax,0 push ax mov bx,0 ret;修改出栈到IP=0,后执行mov ax,4c00H
retf 标志
栈: db 16 dup (0) 代码: mov ax,4c00H int 21H start: mov ax,stack mov ss,ax mov sp,16 mov ax,0 push cs push ax mov bx,0 retf;修改出栈到IP=0;CS=CS,后执行mov ax,4c00H
call和ret的配合使用
例:计算2的N次方,N的值由CX提供
栈: db 16 dup (0) 代码: start: mov ax,stack mov ss,ax mov sp,16 mov ax,1 mov cx,3 call s mov bx,ax mov ax,4c00H int 21H s:add ax,ax loop s ret
带参数和结果传递的模块化设计
方案
1. 用寄存器传递参数
2. 用内存传递参数
通过传递内存地址
例:小写转大写
数据: db 'conversation',0 代码: start: mov ax,data mov ds,ax mov si,0 call capital mov ax,4c00h int 21h capital: mov cl,[si] mov ch,0 jcxz ok;当cx==0时,跳转到退出 and byte ptr [si],11011111b inc si jmp short capital ok:ret
3. 用栈传递参数
进入子程序前,将参数入栈,使栈顶存放call的IP
调用子程序时从栈中通过栈顶指针偏移找到传入的参数
例:(a-b)^3
栈: db 16 dup (0) 代码: start: mov ax,stack mov ss,ax mov sp,16 mov ax,1;b push ax mov ax,3;a push ax call difcube mov ax,4c00h int 21h difcube: push bp;备份主程序bp mov bp,sp;备份栈顶指针 mov ax,[bp+4];a sub ax,[bp+6];b mov bp,ax mul bp mul bp pop bp ret 4;返回后sp+4,直接将a,b出栈
寄存器冲突
多个字符串小写转大写
数据: db 'word',0 db 'unix',0 db 'wind',0 db 'good',0 代码: start: mov ax,data mov ds,ax mov bx,0;bx为外层字符串指针 mov cx,4 s:mov si,bx;si为外层字符串指针 call capital add bx,5;指向下一个字符串 loop s mov ax,4c00h int 21h capital: push cx push si change: mov cl,[si] mov ch,0 jcxz ok;当cx==0时,跳转到退出 and byte ptr [si],11011111b inc si jmp short change ok: pop si pop cx ret
具有子程序的框架
assume cs:code,ds:data,ss:stack data segment ···· 数据 ···· data ebds stack segment ···· 数据 ···· stack ends code segment main: ;开始标记 ;初始化DS,SS,SP寄存器 call sub1;调用子程序sub1 ···· 代码 ···· mov ax,4c00H int 21H sub1: ···· call sub2;;调用子程序sub2 ···· ret sub2: ···· ···· ret code ends end main ;通知程序的入口
子程序框架
son: push 寄存器1;需要用到的寄存器 push 寄存器2; ···· 代码 ···· pop 寄存器2; pop 寄存器1; ret
显存
内存分布
B8000h~BFFFFh共32K的空间,是80*25彩色字符模式第0页的显示缓冲区。
例
debug -e B800:0000 41 02 42 20 43 17 44 1f
在屏幕的中间,白底蓝字,显示'Welcome to masm!'
数据: db 'Welcome to masm!' 代码: start: mov ax,data mov ds,ax mov ax,0B800H mov es,ax mov si,0;指向内存 mov di,160*12+80-16;指向显存 mov cx,16 w:mov al,[si] mov es:[di],al;写入ASCII inc di mov al,71H mov es:[di],al;写入属性字节 inc si inc di loop w
标号
在代码段中
作用
代码段中的标号可以用来标记指令(start:)、段(code segment)的起始地址
代码段中的数据也可以用标号
代码: a:db 1,2,3,4,5,6,7,8,9 b:dw 0 start: mov si,offset a mov bx,offset b mov cx,8 s:mov al,cs:[si] mov ah,0 add cs:[bx],ax inc si loop s
有冒号(代码标号)
只可以在代码段中使用
无冒号(数据标号)
代码: a db 1,2,3,4,5,6,7,8,9 b dw 0 start: mov si,0 mov cx,8 s:mov al,a[si] mov ah,0 add b,ax inc si loop s
a
mov al,cs:[si]
mov al,a[si]
mov al,cs:0[3]
mov al,a[3]
mov al,cs:0[bx+si+3]
mov al,a[bx+si+3]
b
mov ax,cs:[8]
mov ax,b
mov word ptr cs:[8],2
mov b,2
inc word ptr cs:[8]
inc b
mov al,b
error!!
没有“:”的不仅描述内存地址,还描述单元长度的标号
数据的直接定址表
例
以十六进制的形式在屏幕中间显示给定的byte型数据
代码: start: mov al,2BH call showbyte mov ax,4c00h int 21h showbyte: jmp short show table db '0123456789ABCDEF' show: push bx push es push cx mov ah,al mov cl,4 shr ah,cl;右移4位,ah中得到高4位的值 and al,00001111b;al中为低4位的值 mov bl,ah;取出高4位 mov bh,0 mov ah,table[bx];找到高4位对应的字符 mov bx,0b800h mov es,bx mov es:[160*12+40*2],ah;写入显存空间 mov bl,al;取出低4位 mov bh,0 mov al,table[bx];找到低4位对应的字符 mov es:[160*12+40*2+2],al pop cx pop es pop bx ret
代码的直接定址表
例
根据参数选择要执行的子程序
start: mov ah,0;指定调用的子程序 mov al,2;传入参数 call setscreen mov ax,4c00h int 21h setscreen: jmp short set table dw sub1,sub2,sub3,sub4;子程序入口地址表 set: push bx cmp ah,3;检查指令是否越界 ja sret mov bl,ah mov bh,0 add bx,bx;找到指定子程序的入口地址的索引地址 call word ptr table[bx];转向调用的子程序 sret: pop bx ret sub1: ret sub2: ret sub3: ret sub4: ret
在数据段中
数据: a db 1,2,3,4,5,6,7,8,9 b dw 0 代码: start: mov ax,data mov ds,ax mov si,0 mov cx,8 s:mov al,a[si] mov ah,0 add b,ax inc si loop s
把标号当作数据来定义
数据: a db 1,2,3,4,5,6,7,8,9 b dw 0 c dw a,b
数据: a db 1,2,3,4,5,6,7,8,9 b dw 0 c dw offset a,offset b
指针的指针
数据: a db 1,2,3,4,5,6,7,8,9 b dw 0 c dd a,b
数据: a db 1,2,3,4,5,6,7,8,9 b dw 0 c dw offset a,seg a,offset b,seg b
指针的指针
中断
基础中断
中断类型
内中断
INT n(n是中断类型码)
例
调用21h号中断的9号功能
数据: szmsg db 13,10,'hello world!',13,10,'$' 代码: start: mov ax,data mov ds,ax lea dx,szmsg mov ah,9;以ds为段地址,dx为偏移地址,输出$结尾的字符串 int 21h mov ax,4c00h;程序结束执行,退回到DOS int 21h
定义自己的中断例程(参考:中断处理程序编制)
装在中断例程
设置中断向量表
编写中断例程
触发中断(INT 自己设置的中断类型码)
单步
CPU在执行完一条指令之后,如果检测到标志寄存器的TF位为1,则产生单步中断,引发中断执行,执行中断处理程序
中断不响应
对
mov ax,1000 mov ss,ax mov sp,10
栈的操作要连续,在对栈段寄存器SS操作后默认会将TF设置为0不允许单步中断
错
mov ax,1000 mov ss,ax mov ax,0 mov sp,10
编译不通过
INTO(溢出中断)
除法错误
代码: start: mov ax,8 mov bh,0 div bh mov ax,4c00h int 21h
执行完div指令之后,(IP)=(0000:0000),(CS)=(0000:0002)
外中断
可屏蔽中断
可屏蔽中断是CPU可以不响应的外中断
CPU是否响应可屏蔽中断,要看标志寄存器的IF位的设置
当CPU检测到可屏蔽中断信息时
如果IF=1,则CPU在执行完当前指令后响应中断,一个发中断过程
如果IF=0,则不响应可屏蔽中断
几乎所有的外设引发的外中断,都是可屏蔽中断,如键盘输入、打印机请求
不可屏蔽中断
必须响应外中断
当CPU检测到不可屏蔽中断信息时,则在执行当前指令后,立即响应,引发中断过程
对8086CPU不可屏蔽中断的中断类型码固定为2
处理过程
可屏蔽中断
取中断类型码n
可屏蔽中断信息来自于CPU外部,中断类型码是通过数据总线送入CPU (对比内中断:中断类型码是在CPU内部产生的)
标志寄存器入栈,IF=0,TF=0
禁止嵌套中断
CS、IP入栈
(IP)=(n*4),(CS)=(n*4+2)
转去执行中断处理程序
不可屏蔽中断
标志寄存器入栈,IF=0,TF=0
CS、IP入栈
(IP)=(8),(CS)=(0AH)
中断类型码固定2
中断向量表
(IP)=(N*4)
(CS)=(N*4+2)
共可以存放256个中断,但只有一部分中断有效
中断处理过程(硬件完成)
1. 从中断信息中取得中断类型码
取得中断类型码N
2. 标志寄存器的值入栈——中断过程中要改变标志寄存器的值,需要先行保护
pushf
3. 设置标志寄存器的第8位TF和第9位IF的值为0
TF=0,IF=0
防止嵌套
4. CS入栈
push CS
5. IP入栈
push IP
6. 从中断向量表读取中断处理程序的入口地址设置IP和CS
(IP)=(N*4) (CS)=(N*4+2)
中断处理程序编制
替换除法中断的中断处理程序
例
发生0号中断,就在屏幕中央显示“overflow!”
装载中断处理程序
代码: start: mov ax,cs mov ds,ax mov si,offset do0 mov ax,0 mov es,ax mov di,200h mov cx,offset do0end-offset do0 cld rep movsb;安装中断程序 mov ax,0 mov es,ax mov word ptr es:[0*4],200h mov word ptr es:[0*4+2],0;设置中断向量表 mov ax,4c00h int 21h do0:;中断处理程序 jmp short do0start;2字节 db "overflow!" do0start: mov ax,cs;CS是我们设置的中断向量表地址200h mov ds,ax mov si,202h;指向"overflow!"内存 mov ax,0b800h mov es,ax mov di,12*160+36*2 mov cx,9 s: mov al,[si] mov es:[di],al inc si add di,2 loop s mov ax,4c00h int 21h do0end:nop
触发中断
BIOS和DOS中断
BIOS——基本输入输出系统
在系统主板上存放着一套程序 容量:8KB 地址:从FE000H
1. 硬件系统的检测和初始化程序
2. 外部中断和内部中断的中断例程
3. 用于对硬件设备进行I/O操作的中断例程
4. 其他和硬件相关的中断例程
使用BIOS功能调用,程序员不用了解硬件操作细节,直接使用指令设置参数,并中断调用BIOS例程,即可完成相关工作!
BIOS功能调用
(1) 方便编程
(2) 能写出简介、可读性好、易于移植的程序
中断类
显示服务(INT 10H)
直接磁盘服务(INT 13H)
串行口服务(INT 14H)
杂项服务(INT 15H)
键盘服务(INT 16H)
并行口服务(INT 17H)
时钟服务(INT 1AH)
直接系统服务
INT 00H——“0”做除数
INT 01H——单步中断
INT 02H——非屏蔽中断(NMI)
INT04H——算数溢出错误
··· ···
BIOS中断例程调用
例
在屏幕的5行12列显示3个红底高亮闪烁绿色的'a'
代码: start: mov ah,2;置光标功能 mov bh,0;第0页 mov dh,5;dh存放行号 mov dl,12;dl存放列号 int 10h;bio显示服务中断号 mov ah,9;显示字符功能 mov al,'a';字符 mov bl,11001010b;颜色属性 mov bh,0;第0页 mov cx,3;字符重复个数 int 10h;bios显示服务中断号
DOS——操作系统中断
中断类
INT 20H——终止程序运行
INT 21H——功能调用
INT 22H——终止处理程序的地址
INT 23H——Ctrl+C处理程序
INT 24H——致命错误处理程序
INT 25H——读磁盘扇区(忽略逻辑结构)
INT 26H——写磁盘扇区(忽略逻辑结构)
INT27H——终止,并驻留在内存
INT 28H——DOS空闲
INT 33H——鼠标功能
DOS中断例程调用
例
在5行12列显示字符串
数据: db 'Welcome to masm!','$' 代码: start: mov ah,2 mov bh,0;第0页 mov dh,5;光标行号 mov dl,12;光标列号 int 10h;bios屏幕显示中断号 mov ax,data mov ds,ax mov dx,0 mov ah,9;21h中断的9号功能:ds:dx指向要显示的字符串(用'$'结束) int 21h;DOS的21号中断
安装过程
1. CPU一加电,初始化(CS)=0FFFFH,(IP)=0,自动从FFFF:0单元开始执行程序。FFFF:0处有一条跳转指令,CPU执行指令后,转去执行BIOS的硬件系统检测和初始化程序
2. 初始化程序将会建立BIOS所支持的中断向量,即将BIOS提供的中断例程的入口地址登记在中断向量表中
中断例程入口地址写入到00000~003FF中断向量表中,中断例程全都在ROM中
3. 硬件系统检测和初始化完成后,调用int 19h进行操作系统的引导,从此将计算机交由操作系统控制
4. DOS启动后,除完成其它工作外,还将它所提供的中断例程装入内存,并建立相应的中断向量
中断例程部分在ROM中,部分再RAM中
红框的是BIOS的中断向量表
中断例程全都在ROM中存放
白框是DOS的中断向量表
中断例程部分再ROM部分在RAM
注
BIOS和DOS在提供的中断例程包含了许多子程序,这些i主程序实现了程序员在编程的时常用到的功能
和硬件设备相关的DOS中断例程中,一般调用BIOS的中断例程
端口
对应
各种接口卡、网卡、显卡等
主板上的接口芯片
其他芯片
各种芯片工作时,都有一些寄存器由CPU读写
从CPU角度,将各寄存器当端口,并统一编址
CPU用同意的方法与各种设备通信
操作样例
喇叭
代码: start: mov al,08h;设置声音频率 out 42h,al ;out 42h,al in al,61h;读控制器端口原始值 mov ah,al;保存原始值 or al,3;打开扬声器和定时器 out 61h,al;接通扬声器,发声 mov cx,60000;延时 delay: nop loop delay mov al,ah;恢复端口原始值 out 61h,al
CMOS RAM芯片(数与以BCD码存储)
简介
1. 包含一个实时钟和一个有128个存储单元的RAM存储器
2. 128个字节的RAM中存储:内部实时钟、系统配置信息、相关的程序(用于开机时配置系统信息)
3. CMOS RAM芯片靠电池供电,关机后其内部的实时时钟仍可正常工作,RAM中的信息不丢失
4. 改芯片内部有两个端口,端口地址为70h和71h,CPU通过这两个端口读写CMOS RAM
70h地址端口,存放要访问的CMOS RAM单元的地址
71h数据端口,存放从选定的单元中读取的数据,或要写入到其中的数据
5. 读取CMOS RAM的两个步骤
将要读取的单元地址送入70h地址端口
从数据端口71h读出指定单元的内容
例
读出月
代码: start: mov al,8 out 70h,al;输出地址 in al,71h;读入月份 mov ah,al;分割数据 mov cl,4 shr ah,cl and al,00001111b add ah,30h;转为ACISS码 add al,30h mov bx,0b800h mov es,bx mov byte ptr es:[160*12+40*2],ah;显示在屏幕中间 mov byte ptr es:[160*12+40*2+2],al
键盘
处理过程
键盘产生扫描码
按下
按下产生扫描码说明了按下的键在键盘上的位置
扫描码被送入主板上相关接口芯片的寄存器中,该寄存器的端口地址为60H
扫描码
通码(第7位为0)←按下
断码(第7位为1)←松开
松开
产生一个扫描码说明松开按键的位置
松开按键时产生的扫描码也被送入60H端口
引发9号中断
键盘输入到达60H端口时,相关的芯片就会向CPU发出中断类型码为9的可屏蔽中断信息
CPU检测到该中断信息后,如果IF=1,则响应中断,引发中断过程,转去执行int 9中断例程
硬件完成
执行int 9中断例程
BIOS提供的处理键盘输入的int 9中断例程工作
读出60H端口中的扫描码
根据扫描码分情况对待
如果是字符键的扫描码,将扫描码和它所对应的字符码(ASCII码)存储在内存中的BIOS键盘缓冲区
如果是控制键(比如Ctrl)和切换键(比如CapsLock)的扫描码,则将其转变为状态字节(用二进制位记录控制键和切换键状态的字节)写入内存中存储状态字节的单元
16个字单元形成循环队列,可存储15个按键扫描码
对键盘系统进行相关的控制,如向相关芯片发出应答信息
按照开发需求定制处理键盘的输入
保存输入信息
字符
BIOS键盘缓冲区
系统启动后,BIOS用于存放int 9中断例程所接收的键盘输入的内存区
可以存储15个键盘输入,一个键盘输入用一个字存放,高位字节存放扫描码,低位字节存放字符码
控制键和切换键
键盘状态字节(0040:17)
定制键盘输入处理
例
按Esc改变显示颜色
数据: dw 0,0 栈: db 128 dup(0) 代码: 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] mov word ptr es:[9*4],offset int9;修改中断例程入口地址IP mov es:[9*4+2],cs;修改CS 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] mov ax,4c00h int 21h delay:push ax;延时程序 push dx mov dx,10h 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 int9:push ax;中断处理程序 push bx push es in al,60h pushf ;pushf pop bx and bh,11111100b;修改IF=0,TF=0 push bx popf call dword ptr ds:[0];执行Bios中断 cmp al,1;Esc扫描码1 jne int9ret mov ax,0b800h;改变颜色 mov es,ax inc byte ptr es:[160*12+40*2+1] int9ret:pop es pop bx pop ax iret;返回中断调用处
例
改写中断例程
按F1改变屏幕颜色
栈: db 128 dup(0) 代码: start: ;设置各段地址 mov ax,stack mov ss,ax mov sp,128 push cs;ds和cs相同 pop ds mov ax,0;附加段 mov es,ax ;安装新程序 mov si,offset int9 mov di,204h mov cx,offset int9end-offset int9 cld rep movsb;串传送 ;将原中断地址保存在0:200单元处 push es:[9*4] pop es:[200h] push es:[9*4+2] pop es:[202h] ;改变后中断的入口地址 cli mov word ptr es:[9*4],204h mov word ptr es:[9*4+2],0 sti mov ax,4c00h int 21h ;定义新中断例程 int9:push ax push bx push cx push es in al,60h pushf call dword ptr cs:[200h];调用旧中断例程 ;处理F1 cmp al,3bh jne int9ret mov ax,0b800h mov es,ax mov bx,1 mov cx,2000;循环修改显存属性 s:inc byte ptr es:[bx] add bx,2 loop s int9ret:pop es pop cx pop bx pop ax iret int9end:nop
调用BIOS中断(int 16h)读取键盘输入
00H操作,是从键盘缓冲区中读取一个键盘输入,并且将其从缓冲区中删除 结果:(ah)=扫描码,(al)=ASCII码
例
按照输入设置字符的颜色
代码: start: ;调用中断,等待输入 mov ah,0 int 16h ;识别按键 mov ah,1 cmp al,'r' je red cmp al,'g' je green cmp al,'b' je blue jmp short sret ;设置屏幕颜色 red:shl ah,1 green:shl ah,1 blue:mov bx,0b800h mov es,bx mov bx,1 mov cx,2000 s:and byte ptr es:[bx],11111000b or es:[bx],ah add bx,2 loop s sret:mov ax,4c00h int 21h
实现过程
检测按键缓冲区中是否有数据
没有则继续做第1步
读取缓冲区第一个单元中的键盘输入
将读取的扫描码送入ah,ACSII码送入al
将已读取的键盘输入从缓冲区中删除
磁盘
BIOS提供的磁盘直接服务——int 13h
入口参数
(ah)=2(2表示读扇区)、功能号
(al)=读取的扇区数
(ch)=磁道号,(cl)=扇区号
(dh)=磁头号(对于软盘即面号,一个面用一个磁头来读)
(dl)=驱动器号:软驱从0开始,0:软驱A,1:软驱B 硬盘从80h开始,80h:硬盘C,81h:硬盘D
es:bx指向接收从扇区读入数据的内存区
返回参数
操作成功
(ah)=0,(al)=读入的扇区数
操作失败
(ah)=出错代码
与外设链接
CPU在执行指令过程中,可以检测到发送过来的中断信息,引发中断过程,处理外设的输入
主题