8086寄存器有4个段寄存器 : CS DS SS ES
寄存器的一般用途和专业用途
DS和[address]
储存字
DS和[adress]
CS:IP控制程序执行流程
SS:IP提供堆栈栈顶单元地址
DS:BX(SI,DI)提供数据段内单元地址
SS:BP提供堆栈内单元地址
ES:BX(SI,DI)提供堆栈内单元地址
AX,CX,BX和CX寄存器多用于运算和暂存中间计算结果,但又专用于某些指令(查阅指令表)
PSW程序状态字寄存器只能通过专用指令(lahf,sahf)和堆栈(pushf,popf)进行存取.
SS和SP
栈
SS和SP
任意时刻 , SS:IP指向栈顶元素 , SS存放栈顶的段地址 , SP存放栈顶的偏移地址
POSH和POP的使用
//入栈的执行流程
push ax
1 SP=SP-2
2 向SS:SP指向的字单元中送入数据
//出栈的执行流程
pop ax
1 从SS:SP指向的字单元中读取数据
2 SP=SP+2
当栈为空的时候 , 没有栈顶元素 , SS:IP只能指向栈底部字单元的偏移地址+2处单元
栈顶超界问题
POP和PUSH与MOV的区别
PUSH和POP都需要进行两步操作
MOV只需要一步操作
数据的传送就是在寄存器与CPU之间进行 , 即CPU与内存之间
一段内存 , 可以既是代码段 , 又是数据段 , 还是栈段 , 也可以什么都不是 , 关键在于CPU中寄存器的位置 , 即CS , IP , SS , SP , DS的指向
内存并没有真的被分段 , 段的划分来自CPU
段的起始地址必然是16的倍数
偏移地址为16位 , 16位地址的寻址能力为64KB , 所以段的最大长度为64KB
CPU可以用不同的段地址和偏移地址形成同一个物理地址
便于程序重定位
程序分段组织 : 一般由代码段 , 堆栈段 , 数据段 , 附加段组成不设置堆栈段时,使用系统内部的堆栈
ADC AX , BX
设置CF为0 : SUB AX , AX
子主题 4
SBB AX , BX
对任意大数进行减法运算
CMP指令和条件转移指令搭配使用(相当于C语言中if判断)
MOVSB
MOCSW
rep movsb
设置DF的值
/将data段中的第一个字符串复制到它后面的空间中/
data segment
db ‘Welcome to masm!’
db 16 dup (0)
data ends
mov ax, data
mov ds, ax
mov si, 0
mov es, ax
mov di, 16
mov cx, 16
cld
rep movsb
/*
rep movsb相当于
s:movsb
loop s
*/
PUSH和POP命令
寻址方式确定执行指令时获得操作数地址的方法
分为与数据有关的寻址方式(7种)和与转移地址有关的寻址方式(4种)
与数据有关的寻址方式
与转移地址有关的寻址方式的一般用途
在DOS中运行程序时 , 是command将程序加载到内存 , 程序运行结束返回到command中
在debug中将程序加载入内存 , 程序运行结束返回debug中
由循环控制指令或条件转移指令组织循环结构
内层循环结构必须完全包含在外层循环结构内 , 并不能发生从循环结构外向循环结构内转移
BX表示一个内存单元 , 段地址在DS中 , 偏移地址在寄存器BX中
LOOP指令格式
CPU执行LOOP时
计算2^12
在汇编语言里 , 数据不能以字母开头
debug汇编程序中的两种写法(段前缀)
计算FFFF:0~FFFF:B单元中数据的和 , 结果存储在DX中
方案一 ; (DX)=(DX)+内存中的八位数据
方案二 : (DL)=(DL)+内存中的八位数据
mov cx, 12
s: mov al, [bx]
mov ah, 0
add dx, ax
inc bx
loop s
mov ax, 4c00h
int 21h
code ends
end
/实现的功能:数据逆序存放/
assume cs:code,ds:data,ss:stack
//数据段
data segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
data ends
//栈段
stack segment
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
stack ends
//代码段
code segment
start: mov ax, stack //start,标号,为程序的入口地址
mov ss, ax
mov sp, 20h
mov ax, data
mov ds, ax
mov bx, 0
mov cx, 8
s: push [bx]
add bx, 2
loop s
mov bx, 0
mov cx, 8
s0: pop [bx]
add bx, 2
loop s0
mov ax, 4c00h
int 21h
code ends
end start
/*
end的两大关键作用
1 通知编译器程序结束
2 通知编译器程序的入口在什么地方(隐含着在程序编译、链接之后,CS默认被设置为start)
*/
字符串如何存储
将字符串小写字母转化为大写字母
datasg segment
db ‘BaSiC’
db ‘iNfOrMaTion’
datasg end
codesg segment
start: mov ax, datasg
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
mov ax, 4c00h
int 21h
codesg ends
end start
[idata]
[bx]
[bx+idata]
datasg segment
db ‘BaSiC’
db ‘MinIx’
datasg ends
codesg segment
start:
mov ax, datasg
mov ds, ax
mov bx, 0
mov cx, 5
s: mov al, [bx]
and al, 11011111b
mov [bx], al
mov al, [bx+5]
or al, 00100000b
mov [bx+5], al
inc bx
loop s
mov ax, 4c00h
int 21h
codesg ends
end start
[bx+si]
[bx+si+idata]
datasg segment
db 'ibm ’
db 'dec ’
db 'dos ’
db 'vax ’
datasg ends
stacksg segment
dw 0, 0, 0, 0, 0, 0, 0, 0
stacksg ends
codesg segment
start: mov ax, stacksg
mov ss, ax
mov sp, 16
mov ax, datasg
mov ds, ax
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
mov ax,4c00H
int 21H
codesg ends
end start
内存地址
汇编语言中数据位置的表达
立即数idata
寄存器
段地址和偏移地址
寻址方式
通过寄存器指名要处理的数据长度
通过操作符指定
push和pop默认只进行字操作
被乘数与乘数
乘积
由编译器识别
进行数据重复
DB 3 DUP (0 , 1 , 2)
/C语言/
struct company
{
char cn[3];
char hn[9];
int pm;
int sr;
char cp[3];
};
int main()
{
struct company dec = {“DEC”, “Ken Olsen”, 137, 40, “PDP”};
int i;
dec.pm = 38;
dec.sr = dec.sr + 70;
i = 0;
dec.cp[i] = 'V';
i++;
dec.cp[i] = 'A';
i++;
dec.cp[i] = 'X';
return 0;
}
/汇编语言/
//建议参考上面的图片阅读,自己对比,理解C语言的底层实现
mov ax, seg
mov ds, ax
mov bx, 60h
mov word ptr [bx+0ch], 38
//mov word ptr [bx].0ch, 38 //dec.pm = 38
add word ptr [bx+0eh], 70
mov si, 0
mov byte ptr [bx+10h+si], ‘V’
//mov byte ptr [bx].10h[si], ‘V’ //dec.cp[i] = ‘V’;
inc si
mov byte ptr [bx+10h+si], ‘A’
inc si
mov byte ptr [bx+10h+si], ‘X’
段内转移 , 只修改IP , JMP AX
段间转移 , 同时修改CS和IP , JMP 1000:0
CPU根据位移进行跳转
CPU根据位移转移的意义
段内近转移 jmp near 标号
功能(IP)=(IP)+16位位移
16位位移=标号处的偏移地址-JMP指令下一条指令的偏移地址
段内短转移 jmp short 标号
CPU根据目的地址进行跳转
/汇编语言/
assume cs:codesg
codesg segment
start:mov ax,0
mov bx,0
jmp far ptr s
db 256 dup(0)
s:add ax,1
inc ax
codesg ends
end start
/机器码/
0BBD:0000 B80000 MOV AX,0000
0BBD:0003 B80000 MOV BX,0000
0BBD:0006 EA 0B01BD0B JMP 0BBD:010B
/*
可以看出,jmp far ptr s对应的机器码,包含转移的目的地址。
高地址是段地址,低地址是偏移地址
*/
段间转移 , 也叫远转移 JMP FOR PTR 标号
所有的条件转移指令都是短转移
JCXZ标号
所有的循环指令都是短转移
LOOP标号
ret 与 retf 指令
call指令
CPU执行call指令时分两步操作
CALL指令不能实现短转移
依据位移进行位移的call指令(call标号)
功能相当于
转移地址在寄存器中的CALL指令 CALL AX
转移地址在内存中的CALL指令
CALL WORD PTR 内存单元地址
CALL DWORD PTR 内存单元地址
call和ret的简单配合:(bx)=?
mov bx,ax
mov ax,4c00h
int 21h
s: add ax,ax
loop s //loop循环结束,(ax)=8
ret //(IP)等于栈中元素,即语句mov bx,ax的偏移地址
code ends
end start
call和ret配合实现高级语言中的函数调用案例
data segment
db ‘conversation’
data ends
code segment
main:mov ax,data //主函数
mov ds,ax
mov si,0 //参数一,字符串首地址存放在ds:[0]中
mov cx,12 //参数二,字符串长度存放在cx中
call capital //函数调用
mov ax,4c00h
int 21h
capital:and byte ptr [si],11011111b //子函数
inc si
loop capital
ret //子函数返回
code ends
end main
保存用到的寄存器
处理中断
恢复用到的寄存器
用iret指令返回
code segment
start:
//将do0送入内存0000:0200处
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
//将do0的入口地址0000:0200存储在中断向量表0号表项中
mov ax, 0
mov es, ax
mov word ptr es:[04], 200h
mov word ptr es:[04+2], 0
mov ax,4c00h
int 21h
do0: jmp short do0start
db “overflow!”
do0start:
//编写可以显示"overflow!"的中断处理程序:do0
mov ax, cs
mov ds, ax
mov si, 202h
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, 1
mov al, 02h
mov es:[di], al
add di, 1
loop s
mov ax, 4c00h
int 21h
do0end: nop
code ends
end start
CPU在执行完一条指令后 , 若检测到TF为1 , 会产生单步中断
debug中的t命令
CPU为避免在执行中断处理程序时发生单步中断 , 将TF设置为0
CPU为单步跟踪处理程序过程 , 提供了实现机制 , 提供单步中断功能
设置SS之后 , CPU不会响应中断 , 只有设置完SPCPU才会响应中断 , 所以要将设置ss和设置sp的指令连续存放
code segment
start:
mov ax, 3456
int 7ch //计算(ax)的平方
add ax, ax
adc dx, dx
mov ax,4c00h
int 21h
code ends
end start
code segment
start:
//将程序安装在0:200处
mov ax,cs
mov ds,ax
mov si,offset sqr
mov ax,0
mov es,ax
mov di,200h
mov cx,offset sqrend - offset sqr
cld
rep movsb
//将程序入口地址保存在7ch表项中
mov ax,0
mov es,ax
mov word ptr es:[7ch4], 200h
mov word ptr es:[7ch4+2], 0
mov ax,4c00h
int 21h
//计算平方
sqr:
mul ax //如果是16位乘法,高位默认存放在DX中,低位默认存放在AX中
iret //恢复现场
sqrend:nop
code ends
end start
data segment
db ‘conversation’,0 //0标记着字符串的结束
data ends
code segment
start: mov ax, data
mov ds, ax
mov si, 0
int 7ch //转化为大写
mov ax,4c00h
int 21h
code ends
end start
code segment
start:
mov ax,cs
mov ds,ax
mov si,offset capital
mov ax,0
mov es,ax
mov di,200h
mov cx,offset capitalend - offset capital
cld
rep movsb
mov ax,0
mov es,ax
mov word ptr es:[7ch*4],200h
mov word ptr es:[7ch*4+2],0
mov ax,4c00h
int 21h
capital:
push cx
push si
change:
mov cl,[si]
mov ch,0
jcxz ok //判断是否为0
and byte ptr [si],11011111b
inc si
jmp short change
ok:
pop si
pop cx
iret
capitalend:nop
code ends
end start
/在屏幕中间显示80个’!’/
assume cs:code
code segment
start:mov ax,0b800h
mov es,ax
mov di,160*12
mov bx,offset s-offset se
mov cx,80
s:mov byte ptr es:[di],’!’
add di,2 //一个字符在缓冲区占两个字节,分别存放字符的ASCII和属性
int 7ch
se:nop
mov ax,4c00h
int 21h
code ends
end start
int 21
显示缓冲区结构
dos 中 int 21h
mov ax,4c00h
int 21h
/真实含义/
mov ah,4ch //表示调用第21h号中断例程的4ch号子程序,功能为程序返回
mov al,0 //返回值
int 21h
/在屏幕的5行12列显示字符串"Welcome to masm!"/
assume cs:code
data segment
db ‘Welcome to masm’,’$’
data ends
code segment
start:
//设置光标位置功能
mov ah, 2
mov bh, 0
mov dh, 5
mov dl, 12
int 10h
//显示字符串功能
mov ax, data
mov ds, ax
mov dx, 0
mov ah, 9
int 21h
//程序返回功能
mov ax, 4c00h
int 21h
code ends
end start
可屏蔽中断
CPU可以不响应的外中断
当CPU检测到可屏蔽中断信息后,如果IF=1,则CPU执行完当前指令后响应中断;如果IF=0,则不响应中断。
可屏蔽中断属于外部中断,中断类型码由数据总线送入CPU;而内中断的中断类型码是在CPU内部产生的。
在进入中断处理程序后,禁止其他的可屏蔽中断。所以CPU中断过程中将IF置为0
设置IF的值
几乎所有由外设引发的外中断都是可屏蔽中断
不可屏蔽中断
键盘输入
按下一个键,键盘芯片产生一个扫描码,送入主板上相关接口芯片寄存器中,该寄存器端口地址60H
松下一个键,键盘芯片产生一个扫描码,送入主板上相关接口芯片寄存器中,该寄存器端口地址60H
引发9号中断
执行int9中断例程
/实则为编写int9中断例程/
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
//将原来的int 9中断例程入口地址保存在ds:[0]和ds[2]里
push es:[94]
pop ds:[0]
push es:[94+2]
pop ds:[2]
//在中断向量表中设置新的int 9中断例程入口地址
mov word ptr es:[94], offset int9
mov es:[94+2], cs
//屏幕中间依次显示字符a-z
mov ax, 0b800h
mov es, ax
mov ah, ‘a’
s:
mov es:[16012+402], ah
call delay //调用delay函数进行延时,使得每显示一个字母,可以让人看清
inc ah
cmp ah, ‘z’
jna s
//在中断向量表中设置为原来的int 9的中断例程入口地址(恢复操作)
mov ax,0
mov es,ax
push ds:[0]
pop es:[94]
push ds;[2]
pop es;[94+2]
//程序返回
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
//从端口60H读入用户的键盘输入
in al, 60h
/模拟执行原int 9中断例程/
//标志寄存器入栈
pushf
//IF=0,TF=0
pushf
pop bx
and bh,11111100b
push bx
popf
//(IP)=((ds)16+0),(CS)=((ds)16+2)
call dword ptr ds:[0]
//看用户输入是否为esc
cmp al,1
jne int9ret
//如果是esc的话,属性值+1,从而改变颜色
mov ax,0b800h
mov es,ax
inc byte ptr es:[16012+402+1]
//寄存器出栈
int9ret:
pop es
pop bx
pop ax
iret
code ends
end start
Xmind 思维导图文件
https://download.csdn.net/download/THATMASTER/54792277
PNG 思维导图
https://download.csdn.net/download/THATMASTER/54799236
SVG 网页文件
https://download.csdn.net/download/THATMASTER/54800455