汇编语言基于8086CUP(想学操作系统的前奏!!!)
汇编语言基于8086CUP(想学操作系统的前奏!!!)
1、汇编语言的产生
1.1、思维图
1.2、单位转换
1B=8bit
1KB=1024B
1MB=1024KB
1GB=1024MB
1TB=1024GB
1字=2字节=2B=16位
1.3、储存器与内存
因为在这些总线上传输的都是2进制的数,所以可能传输的是相同的2进制数,需要区分哪些是数据,哪些又是指令,所以需要通过不同的总线就行传输来控制。
地址总线:20位2进制
数据总线:16位2进制
控制总线:16位2进制
2、寄存器和内存、栈
2.1、14个寄存器
AX,BX,CX,DX,SI,DI,SP,BP,IP,CS,SS,DS,ES,PSW;
几位寄存器,表示可以存储几位二进制数
AX,BX,CX,DX一般被称为通用寄存器,
AX可以分为AH和AL
BX可以分为BH和BL
CX可以分为CH和CL
DX可以分为DH和DL
2.2、地址总线
由于8086有20位的地址总线,所有在传输16位时需要地址加法器进行处理变成20位
地址加法器:物理地址=段地址*16+偏移地址
传送地址时需要传送两个16位的地址,一个称为段地址,一个称为偏移地址
段寄存器:CS,DS,SS,ES
CS:为代码段寄存器
IP:指令指针寄存器
2.3、DS和[address]
这样的指令 mov ax,[0] (调试的时候可以这么写,写.asm源文件时需要写成 mov bx,0,mov ax,[bx];需要bx中间量来转换一下) 把默认段地址+偏移地址的第一位取出来放到ax上
DS存放的是默认的段地址
2.4、栈(Last in frist out)后进先出
push(入栈)
pop(出栈)
段寄存器SS:存放栈顶的段地址
寄存器SP:存放栈顶的偏移地址
SS:SP表示栈顶元素
2.5、SI和DI
SI和DI,也用来表示偏移地址,使用[bx]时不够用,所以可以使用[SI],[DI]
2.6、栈解决多层循环问题
2.7、寻址方式
寻址方式 | 含义 | 名称 | 常用格式举例 |
---|---|---|---|
[idata] | EA=idata;SA=(ds) | 之间寻址 | [idata] |
[bx] [si] [di] [bp] |
EA=(bx);SA=(ds) EA=(si);SA=(ds) EA=(di);SA=(ds) EA=(bp);SA=(ss) |
寄存器间接寻址 | [bx] |
[bx+idata] [si+idata] [di+idata] [bp+idata] |
EA=(bx)+idata;SA=(ds) EA=(si)+idata;SA=(ds) EA=(di)+idata;SA=(ds) EA=(bp)+idata;SA=(ss) |
寄存器相对寻址 | 用于结构体[bx].idata 用于数组,idata[si]<br/ |
[bx+si] [bx+di] [bp+si] [bp+di] |
EA=(bx)+(si);SA=(ds) EA=(bx)+(di);SA=(ds) EA=(bp)+(si);SA=(ss) EA=(bp)+(di);SA=(ss) |
基址变址寻址 | 用于二维数组,[[bx][si]][si] |
[bx+si+idata] [bx+di+idata] [bp+si+idata] [bp+di+idata] |
EA=(bx)+(si)+idata;SA=(ds) EA=(bx)+(di)+idata;SA=(ds) EA=(bp)+(si)+idata;SA=(ss) EA=(bp)+(di)+idata;SA=(ss) |
相对基址变址寻址 | 用于表格 二维数组 |
2.8、标志位
标志位 | 作用 |
---|---|
ZF | 如果执行的结果为0,ZF=1,反之 |
PF | 如果执行的结果中有偶数个1,则PF=1,反之 |
SF | 如果执行的结果中最高位为1,则SF=1,反之 |
CF | 如果计算出现进位或者借位,加法进位,减法借位,会在CF中标志1 |
OF | 如果无符号计算,出现位不够用,出现溢出,会在OF中标出1 |
DF | DF决定串传送指令后,si和di方向的改变,DF=0,+;DF=1,- |
TF | RF位为1,就产生单步中断,也就是指令t |
无符号数:cmp 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
- 如果(ah)<(bh),则(ah)-(bh)最高位会为1,如果不溢出,则SF=1,OF=0
- 如果(ah)>(bh),则(ah)-(bh)最高位会为1,如果溢出,则SF=1,OF=1
- 如果(ah)<(bh),则(ah)-(bh)最高位会为0,如果溢出,则SF=0,OF=1
- 如果(ah)>=(bh),则(ah)-(bh)最高位会为0,如果不溢出,则SF=0,OF=0
3、汇编指令
3.1、汇编指令的用法
汇编指令 | 语法描述 |
---|---|
mov ax,18 | ax=18 |
add ax,18 | ax=ax+18 |
sub ax,18 | ax=ax-18 |
inc ax | ax=ax+1 |
dec ax | ax=ax-1 |
jmp 段地址:偏移地址 | 修改CS和IP的内容(在汇编指令使用,编译器不认识) |
push ax | 把ax入栈 |
pop ax | 把栈顶元素出栈保存到ax中 |
loop S:需要循环的指令段 loop S | 控制循环的指令,具体循环多少次,由CX中的数决定 |
dw dw 0123H,1234H,0024H | 定义数据存放在内存中,占两个字节 |
db db 0,1,2,4,5,6,7,8 | 定义数据存放在内存中,占一个字节 |
and and al,11011111b | 与运算,把al的第五位二进制转化为0 |
or or al,00100000b | 或运算,把al的第五位二进制转化为1 |
div div bx | 除法运算,{(dx)*10000H+(ax)}/(bx) |
mul mul bx | 乘法运算,(dx)10000H+(ax)=(ax)(bx),都是8位就存放在ax中 |
dd | 定义数据存放在内存中,占四个字节,2个字,32位 |
dup db 200 dup(0) db 2 dup('ab','AB') |
定义了200个连续为0的字节 定义了"adABabAb"的8个字节的数据 |
jmp short s | 段内转移,指令跳转到标号s处,位移为8位 |
jmp near ptr s | 段内转移,指令跳转到标号s处,位移为16位 |
jmp far ptr s | 段间转移,指令跳转到标号s处,改变CS:IP为s处的CS,IP |
jmp ax | 指令跳转到ax存放的值当作ip去转移 |
jmp word ptr 内存地址 | 段内转移,存放的一个字,表示跳转的偏移地址IP |
jmp dword ptr 内存单元地址 | 段间转移,存放的两个字,表示跳转(CS)=(内存+2),(IP)=(内存) |
jcxz s | 条件段内转移,如果(cx)=0跳转到s处,否则不跳转 |
mov ax,offset s | 取得s处的偏移地址,把值赋给ax |
ret | 表示把栈顶的元素值给了IP,改变程序执行的位置 |
retf | 表示把栈顶的元素值给了IP,第二个值给 CS |
call s | 把当前IP入栈,在跳转(位移16位来实现的)到s处, |
call far ptr s | 把CS入栈、把IP入栈,把s处的段地址给CS、偏移地址给IP |
call ax | 把当前IP入栈,把通用寄存器ax的值给IP |
call word ptr 内存单元地址 | 把当前IP入栈,IP该我内单元地址2字节 |
call dword ptr 内存单元地址 | 把当前CS入栈、IP入s栈,把高位2字节给CS、低位2字节給IP |
abc abc ax,bx | 带进位加法指令,(ax)=(ax)+(bx)+CF |
sbb sbb ax,bx | 带进位减法指令,(ax)=(ax)-(bx)-CF |
cmp cmp ax,bx | 结果不会保存在任何位置,但会影响标志位的变化 |
je s | 等于则转移到 s标志位,ZF=1,equal:等于 |
jne s | 不等于则转移 s标志位,ZF=0, |
jb s | 低于转移到 s标志位,CF=1,below:小于 |
jnb s | 不低于转移到 s标志位,CF=0 |
ja s | 高于转移到 s标志,CF=0,ZF=0,above:大于 |
jna s | 不高于则转移到 s标志位,CF=1,或者ZF=1 |
movsb | 将ds:si中的值送入es:si中:((es)16+(di))=((ds)16+(si)}), 如果DF=0,则(si)=(si)+1,(di)=(di)+1; 如果DF=1,(si)=(si)-1,(di)=(di)-1 |
movsw | 以字的单元传送,规则同movsb |
rep movsb | 根据cx的值来重复传送cx次,实现传送字符串的作用 |
cld | 将标志寄存器DF=0 |
std | 将标志寄存器DF=1 |
pushf | 将标志寄存器的值压栈 |
popf | 弹栈出到标志寄存器中 |
iret | 在中断程序执行,IP,CS,标志寄存器的弹栈 |
shl | 向左移动一位,右边补零,移出的一位保存道CF中 |
shr | 向右移动一位,左边补零,移出的一位保存道CF中 |
cli | 将标志寄存器IF=0,不处理外中断 |
sti | 将标志寄存器IF=1,处理外中断 |
3.2、Windows11下载Dosbox来使用debug命令窗口
第一步去官网下载:Dosbox;[官网 ][ https://www.dosbox.com/download.php?main=1 ]
第二步修改你需要在哪个盘哪个文件中操作:
masm文件
asm文件
最后打开DOSbox.exe就可以操作了
Debug指令
R命令,查看、改变CPU寄存器的内容
D命令,查看内存中的内容
E命令,改变内存中的内容
A命令,可以在段地址CS,偏移地址IP写入一条机器指令
T命令,执行一条机器指令
U命令,将内存中的机器指令翻译成汇编指令
4、汇编源程序
-
计算2的三次方
assume cs:abc abc segment mov ax,2 add ax,ax add ax,ax mov ax,4c00H int 21H abc ends end
-
操作内存地址,改变值
assume cs:abc abc segment start: mov ax,2000H mov ds,ax mov bx,1000H mov ax,[bx] inc bx inc bx mov [bx],ax inc bx inc bx mov [bx],ax inc bx mov [bx],al inc bx mov [bx],al mov ax,4c00H int 21H abc ends end start
-
计算2的11次,用loop循环执行
assume cs:abc abc segment start: mov ax,2 mov cx,11 S: add ax,ax loop s mov ax,4c00H int 21H abc ends end start
-
循环操作4个单位内存中数据之和
assume cs:abc abc segment start: mov ax,1000H mov ds,ax mov bx,0000H mov al,0011H mov ds:[bx],al mov bx,0001H mov al,0022H mov ds:[bx],al mov bx,0002H mov al,0033H mov ds:[bx],al mov bx,0003H mov al,0044H mov ds:[bx],al mov ax,0000H mov cx,0004H mov bx,0004H mov dx,0000H S: sub bx,cx mov al,[bx] add dx,ax mov bx,0004H loop S mov ax,4c00H int 21H abc ends end start
-
利用栈交换顺序
assume cs:abc abc segment dw 0123H,1234H,0252H,12A2H,2222H,3333H,4444H,5555H dw 0,0,0,0,0,0,0,0 start: mov ax,cs mov ss,ax mov sp,32 mov bx,0 mov cx,8 S: push cs:[bx] add bx,2 loop S mov bx,0 mov cx,8 S1: pop cs:[bx] add bx,2 loop S1 mov ax,4c00H int 21H abc ends end start
-
计算a,b两个段数据依次相加的结果存放到c段中
assume cs:abc,ds:a a segment db 1,2,3,4,5,6,7,8 a ends b segment db 1,2,3,4,5,6,7,8 b ends c segment db 0,0,0,0,0,0,0,0 c ends abc segment start: mov ax,a mov ds,ax mov dx,0000H mov cx,8 S: mov bx,dx mov ah,ds:[bx] add bx,16 mov al,ds:[bx] add ah,al add bx,16 mov ds:[bx],ah add dx,1 loop S mov ax,4c00H int 21H abc ends end start
-
将小写字母转化为大小字母
assume cs:abc,ds:a a segment db 'BAsiC' db 'abcdefgh' a ends abc segment start: mov ax,a mov ds,ax mov bx,0 mov cx,5 S1: mov al,[bx] and al,11011111b mov [bx],al inc bx loop S1 mov bx,5 mov cx,8 S2: mov al,[bx] and al,11011111b mov [bx],al inc bx loop S2 mov ax,4c00H int 21H abc ends end start
-
把每一个小写字母的前四位转化为大写
assume cs:code,ds:data,ss:stack data segment db '1. abcdef.......' db '2. mnijwq.......' db '3. iloveyou.....' db '4. oklolp.......' data ends stack segment dw 0,0,0,0,0,0,0,0 stack ends code segment 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,4 S1: mov al,[bx+3+si] and al,11011111b mov [bx+3+si],al inc si loop S1 add bx,16 pop cx loop S0 mov ax,4c00H int 21H code ends end start
-
cmd窗口中间输入不同颜色和背景的字符串
assume cs:code,ds:data,ss:stack data segment db 'welcome to masm!' db 02h,24h,71h data ends stack segment dw 8 dup(0) stack ends code segment start: mov ax,data mov ds,ax mov ax,stack mov ss,ax mov sp,10h xor bx,bx mov ax,0b872h mov cx,3 s3: push cx push ax push bx mov es,ax mov si,0 mov di,0 mov cx,10h s1: mov al,ds:[si] mov es:[di],al inc si add di,2 loop s1 mov di,1 pop bx mov al,ds:10h[bx] inc bx mov cx,10h s2: mov es:[di],al add di,2 loop s2 pop ax add ax,0ah pop cx loop s3 mov ax,4c00h int 21H code ends end start
结果:
-
利用call和ret来实现函数,计算一组数的三次方并保存在内存中
assume cs:code,ds:data data segment dw 1,2,3,4,5,6,7,8 dw 0,0,0,0,0,0,0,8 data ends code segment start: mov ax,data mov ds,ax mov cx,8 mov di,0 s0: mov bx,ds:[di] call s mov ds:[di+16],ax add di,2 loop s0 mov ax,4c00h int 21H s: mov ax,bx mul bx mul bx ret code ends end start