原视频
基础篇:1.1编程环境的安装
打开DOSBox 0.74-3 Options.bat调整窗口大小
windowresolution=1200x640
output=ddraw
mount c D:\masm
c:
debug
DEBUG
用Debug的R命令查看、改变CPU寄存器的内容:
用Debug的D命令查看内存中的内容:
用Debug的E命令改写内存中的内容:
用Debug的U命令将内存中的机器指令翻译成汇编指令:
用Debug的T命令执行一条机器指令:
用Debug的A命令以汇编指令的格式在内存中写入一条机器指令。
mov,add,sub
mov ax,bx
ax=bx
add ax,bx
ax+=bx
sub ax,bx
ax-=bx
mul,div,and,or
mul乘法指令
因下面要用到,这里介绍一下mul指令,mul是乘法指令,使用mul做乘法的时候,
注意以下两点。
(1)两个相乘的数:两个相乘的数,要么都是8位,要么都是16位。如果是8位,一个默认放在AL中,另一个放在8位reg或内存字节单元中:如果是16位,一个默认在AX中,另一个放在16位reg或内存字单元中。
(2)结果:如果是8位乘法,结果默认放在AX中:如果是16位乘法,结果高位默认在DX中存放,低位在AX中放。
mul bl
al*bl
结果在ax
mul bx
ax*bx
结果高位在DX 低位在AX
div除法指令
(I)除数:有8位和16位两种,在一个reg或内存单元中。
(2)被除数:默认放在AX或DX和AX中,如果除数为8位,被除数则为16位,
默认在AX中存放:如果除数为16位,被除数则为32位,在DX和AX中存放,DX存放高16位,AX存放低16位。
(3)结果:如果除数为8位,则AL存储除法操作的商,AH存储除法操作的余数:
如果除数为16位,则AX存储除法操作的商,DX存储除法操作的余数。
div bl
ax/bl
结果ah为余数 al为商
div bx
(dx*2^32+ax)/bx
结果dx为余数 ax为商
and or
(I)and指令:逻辑与指令,按位进行与运算。
例如指令:
mov al,01100011B
and al,00111011B
执行后:al=00100011B
(2)or指令:逻辑或指令,按位进行或运算。
例如指令:
mov al,01100011B
or al,00111011B
执行后:al=01111011B
shl
是逻辑左移指令,它的功能为
(1)将一个寄存器或内存单元中的数据向左移位:
(2)将最后移出的一位写入CF中
(3)最低位用0补充。
mov al,01001000b
shl al,1
执行后al=10010000b cf=0
shr
是逻辑右移指令,它和shl所进行的操作刚好相皮
(1)将一个寄存器或内存单元中的数据向右移位:
(2)将最后移出的一位写入CF中:
(3)最高位用0补充。shr是逻辑右移指令,它和shl所进行的操作刚好相反
mov al,10000001b
shr al,1
执行后:(al)=01000000b,CF=1
dec inc neg xchg
dec ax
ax--
inc ax
ax+=
neg ax
ax取反
xchg ax,bx
ax与bx的值交换
int
int指令是X86汇编语言中最重要的指令之一。它的作用是引发中断,调用“中断例程”(interrupt routine)。本文将介绍int指令的基本原理和应用,以及BIOS和DOS系统的中断例程。
一、int指令的原理
1,指令原型
int n
注:
1)n 表示中断号,也可以称为中断类型码。n是一个字节大小的正整数,范围为“0 - 255”。
2)执行“int n”时,CPU从中断向量表中,找到第n号表项,修改CS和IP
(IP)=(n*4),(CS)=(n*4+2)
3)对8086PC,中断向量表指定放在内存地址0处(地址固定),共1024个字节。每个表项占两个字,低字存放偏移地址,高字存放段地址。
ds寄存器,段地址,偏移地址,物理地址
ds用来放段地址
存放当前正在运行的程序代码所在段的段基值
073f:0100
段地址为073f
偏移地址为0100
物理地址为073f*16+0100
给ds寄存器赋值
mov ax,0100
mov ds,ax
cs:ip
基地址:偏移地址
cs 为代码段寄存器,一般用于存放代码;
通常和IP 使用用于处理下一条执行的代码
jmp
jmp bx
将ip寄存器的值改为bx
ss-sp pop push
存储栈的地址
ss-sp指向栈顶
push寄存器
:将一个寄存器中的数据入栈
pop寄存器
:出栈,用一个寄存器接收出栈的数据
-a
073F:0100 mova×,1234
073F:0103 push a×
073F:0104 mov b×,5678
073F:0107 push bx
073F:0108 pop ax
073F:0109 pop bx
073F:010A
sp=00fd
sp=00fb
sp=00f9
073F:0100 78 56 34 12 00 00 00
bp si di
mov ax,[bx]
mov ax,[si]
mov ax,[si+1]
mov ax,[si+bx]
错误指令
mov ax,[bx+bp]
mov ax,[si+di]
存放段地址的寄存器可以是默认的,比如:
mov ax,[0]
mov ax,[di]
mova,[bx+8]
mowa%,[bx+si]
mov ax,[bx+si+B]
等指令,段地址默认在ds中:
mov ax,[bp]
mov ax,[bp+8]
mov ax,[bp+si]
mov ax,[bp+si+8]
等指令,段地址默认在ss中。
(3)只要在[...]中使用寄存器bp,而指令中没有显性地给出段地址,段地址就默认在ss中。
es 为扩展段寄存器; ss 为栈段寄存器,一般作为栈使用 和sp搭档;
es 为扩展段寄存器;
flag寄存器
标志 | true | false | Name(名称) | 命题 |
---|---|---|---|---|
OF | OV (Overflow) | NV (Not Overflow) | 0verflowFlag(是否溢出) | 存在溢出? |
SF | NG(NeGative负的) | PL(Plus正的) | Sign Flag(结果的符号是正还是负) | 是负数(正数看做无符号)? |
ZF | ZR(Zero) | NZ(Not Zero) | Zero Flag(运算结果是否为0) | 是0? |
PF | PE(Even偶数) | Po(odd奇数) | Parity Flag(结果中二进制位个数的奇偶性) | 是偶数个1? |
CF | CY(Carry yes) | NC(Not carry) | Carry Flag(进位标志) | 有进位? |
DF | DN (Down) | UP(Up) | Direction Flag(方向标志) | si、di递减? |
adc
adc ax,bx
ax=ax+bx+CF
sbb
sbb ax,bx
ax=ax-bx-CF
cmp
cmp是比较指令,cmp的功能相当于减法指令,只是不保存结果。cmp指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。
cmp ax,bx
ax-bx
如果(ax)=(bx)则(ax)-(bx)=0,所以:zf=1
如果(ax)≠(bx)则(ax)-(bx)≠0,所以:zf=0
如果(ax)<bx)则(ax)-(bx)将产生借位,所以:cf=l
如果(ax)≥(bx)则(ax)-(bx)不必借位,所以:cf=0
如果(ax)>(bx)则(ax)-(bx)既不必借位,结果又不为0,所以:cf=0并且zf=0
如果(ax)≤(bx)则(ax)-(bx)既可能借位,结果可能为0,所以:cf=1或zf=1
指令 | 含义 | 检测的相关标志位 |
---|---|---|
je | 等于则转移 | zf=1 |
jb | 低于则转移 | cf=1 |
ja | 高于则转移 | cf=0且zf=0 |
cmp ax,bx
je 073f:0106
运行asm文件
masm
输入文件名
link 文件名
debug 文件名.asm
循环语句loop
loop指令的格式是:loop标号,CPU执行loop指令的时候,要进行两步操作,
①cx=cx-1:②判断cx中的值,不为零则转至标号处执行程序,如果为零则向下执行。
下面例子中ax=2^12
assume cs:code
code segment
start:mov ax,2
mov cx,12
s:
add ax,ax
loop s
int 21H
code ends
end start
call ret
call在执行时会先将下一条指令所对应的ip地址入栈,然后修改ip的值实现跳转, ret指令执行的时候,将ip地址pop出来进行跳转
call s ;
标号里面存放的是ip偏移地址 如果写成call 3H
那么意思就是跳转到CS:0003h这个位置
执行函数s
assume cs:code
code segment
start:mov ax,2
mov cx,12
call s
int 21H
s:
add ax,ax
loop s
ret
code ends
end start
call Far ptr retf
ret和call配套使用,retf和call Far ptr 配套使用
可以通过标号(函数名称)之间数值相减计算函数体代码所占用的内存空间大小
retf
需要配合栈进行使用,当程序执行到retf
这条指令时,会连续从栈中pop
两次数据,第一次的数据赋值给CS
,第二次的数据赋值给IP,那么如果我们想要跳转到指定的指令,需要将该指令的段地址和偏移地址分别push
进栈中
stack segment
db 128 dup(0)
stack ends
code segment
start:
mov ax,stack
mov ss,ax
mov sp,128
mov ax,0710H ;指定段地址
push ax
mov ax,0003H ;指定偏移地址
push ax
retf ;程序跳转到0710:0003H这个位置
code ends
end start
内存地址 机器码 汇编指令
1000:0 b8 00 00 mov ax,0
1000:3 9a 09 00 00 10 call far ptr s
1000:8 40 inc ax
1000:9 58 s:pop ax
add ax,ax
pop bx
add ax,bx
为什么ax会是1010?详细解答
执行指令先要读入到指令缓冲区中
读进去以后IP已经指向下一条指令了
call far ptr执行了push cs,push ip,jmp far ptr
这么指行的话call far ptr s的cs:1000,ip:8
把cs先扔进栈,后扔ip
那么到了s:pop ax这个时候应该是ip先出来,所以ax=8
接着执行add ax,ax=6
接着pop bx,现在bx=1000
然后执行add ax,bx
那么就是ax=1010H
代码段、数据段、栈段、dup指令
assume cs:codesg,ds:data,ss:stack
MASM内部以数基的个数定义了多种数据类型
BYTE,db,8位
WORD,dw,16位
DWORD,dd,32位
QWORD,dq,64位
db 10 dup (0)
初始化10个值为0的空间
assume cs:codesg,ds:data,ss:stack
data segment
dw 123H,456H,789H,OabcH,OdefH
db 3 dup(1,2,3)
db 3 dup ('abc,'def)
data ends
stack segment
db 10 dup (0)
stack ends
codesg segment
start:
dw 'hellow rold'
mov ax,3
mov cx,11
call s
inc bx
inc bx
int 21H
add ax,ax
loop s
codesg ends
end start
offset
操作符offset在汇编语言中是由编译器处理的符号,它的功能是取得标号的偏移地址。比如下面的程序:
assume cs:codesg
codesg segment
start:mov ax,offset start;相当于mov ax,0
s:mov ax,offset s;相当于mov ax,3
codesg ends
end start
jmp short s jmp far s
跳转到s位置
jmp short s
机器码显示距离
jmp far s
机器码显示距离地址
jmp dword ptr
功能:从内存单元地址处开始存放着两个字,高地址处的字是转移的目的段地址,低地址处是转移的目的偏移地址。
内存单元地址可用寻址方式的任一格式给出。
比如,下面的指令:
mov ax,0123H
mov ds:[0],ax
mov word ptr ds:[2],0
jmp dword ptr ds:[0]
数组
data1 dd 0, 1, 2, 4 ;
相当于 int data1[] = { 0, 1, 2, 3 };
data1[1]是内存地址+1(不论什么类型都是1个字节)
lea
lea是“load effective address”的缩写
简单的说,lea指令可以用来将一个内存地址直接赋给目的操作数
例如:lea eax,[ebx+8]就是将ebx+8这个值直接赋给eax,而不是把ebx+8处的内存地址里的数据赋给eax。
而mov指令则恰恰相反,例如:mov eax,[ebx+8]则是把内存地址为ebx+8处的数据赋给eax。
5.11输出hello world
assume cs:codesg,ds:data,ss:stack
data segmeNT
str db 'hello world','$'
data ends
stack segment
db 10 dup (0)
stack ends
codesg SEgment
start:
mov ax,data
mov ds,ax
lea dx,str
mov ah,9
int 21H
mov ah,4cH
int 21H
codesg ends
end start
5.12字符串转大写
assume cs:codesg,ds:data,ss:stack
data segmeNT
str db 'hello world','$'
data ends
stack segment
db 10 dup (0)
stack ends
codesg SEgment
start:
mov ax,data
mov ds,ax
mov bx,0
mov cx,11
s:
mov al,[bx]
and al,1011111B
mov [bx],al
inc bx
loop s
;mov dx,offset str
lea dx,str
mov ah,9
int 21H
mov ah,4cH
int 21H
codesg ends
end start
5.13字符串转小写
assume cs:codesg,ds:data,ss:stack
data segmeNT
str db 'hello world','$'
data ends
stack segment
db 10 dup (0)
stack ends
codesg SEgment
start:
mov ax,data
mov ds,ax
mov bx,0
mov cx,11
s:
mov al,[bx]
or al,0100000B
mov [bx],al
inc bx
loop s
;mov dx,offset str
lea dx,str
mov ah,9
int 21H
mov ah,4cH
int 21H
codesg ends
end start
5.14求数组最小值
assume cs:codesg,ds:data,ss:stack
data segmeNT
str db 'hello world','$'
data ends
stack segment
db 10 dup (0)
stack ends
codesg SEgment
start:
mov ax,data
mov ds,ax
mov bx,0
mov cx,11
mov ah,0FFH
s:
mov al,[bx]
cmp ah,al
jna s1
mov ah,al
s1:
mov [bx],al
inc bx
loop s
;mov dx,offset str
lea dx,str
mov ah,9
int 21H
mov ah,4cH
int 21H
codesg ends
end start
5.15求数组最大值
assume cs:codesg,ds:data,ss:stack
data segmeNT
str db 'hello world','$'
data ends
stack segment
db 10 dup (0)
stack ends
codesg SEgment
start:
mov ax,data
mov ds,ax
mov bx,0
mov cx,11
mov ah,0
s:
mov al,[bx]
cmp ah,al
jnb s1
mov ah,al
s1:
mov [bx],al
inc bx
loop s
;mov dx,offset str
lea dx,str
mov ah,9
int 21H
mov ah,4cH
int 21H
codesg ends
end start
5.21数组求和
assume cs:code,ds:data,ss:stack
data segment
arr db 1,2,3,4,10,20,30,40
res db 8 dup (0)
data ends
stack segment
db 100 dup (0)
stack ends
code segment
start:
mov ax,data
mov ds,ax
mov bx,0
mov cx,8
for:
add al,arr[bx]
inc bx
loop for
mov ax,4c00H
int 21
code ends
end start
5.22拷贝数组
assume cs:code,ds:data,ss:stack
data segment
arr db 1,2,3,4,10,20,30,40
res db 8 dup (0)
data ends
stack segment
db 100 dup (0)
stack ends
code segment
start:
mov ax,data
mov ds,ax
mov bx,0
mov cx,8
for:
mov al,arr[bx]
mov ds:res[bx],al
inc bx
loop for
mov ax,4c00H
int 21
code ends
end start
5.23用si,di翻转数组
assume cs:code,ds:data,ss:stack
data segment
arr db 1,2,3,4,10,20,30,40
res db 8 dup (0)
data ends
stack segment
db 100 dup (0)
stack ends
code segment
start:
mov ax,data
mov ds,ax
mov si,0
mov di,7
mov cx,8
for:
mov al,arr[si]
mov ds:res[di],al
inc si
dec di
loop for
mov ax,4c00H
int 21
code ends
end start
5.31用栈翻转数组
assume cs:code,ds:data,ss:stack
data segment
arr dw 1111H,2222H,3333H,4444H,5555H,6666H,7777H,8888H
res db 800 dup (0)
data ends
stack segment
db 1000 dup (0)
stack ends
code segment
start:
mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
mov sp,100
mov bx,0
mov cx,8
for:
push ds:arr[bx]
add bx,2
loop for
mov bx,0
mov cx,8
for1:
pop ds:arr[bx]
add bx,2
loop for1
mov ax,4c00H
int 21
code ends
end start
5.32动态规划求斐波那契数列
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 21
code ends
end start
5.41循环把二维矩阵某一列转大写
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 al,ds:str[bx+5]
and al,11011111B
mov ds:str[bx+5],al
add bx,16
loop for
lea dx,str
mov ah,9
int 21H
mov ah,4cH
int 21H
codesg ends
end start
5.42二重循环把指定矩形转大写
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 ah,9
int 21H
mov ah,4cH
int 21H
codesg ends
end start
5.51冒泡排序
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
5.61十进制转16进制
assume cs:codesg,ds:data,ss:stack
data segmeNT
str db '00012345','$'
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
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
mov ah,4cH
int 21H
codesg ends
end start
5.61十进制转16进制并输出
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 ah,4cH
int 21H
codesg ends
end start