编写一个完整的汇编
assume cs:code ;这是一个注释
code segment
mov ax,1122h
mov bx,3344h
add ax,bx
mov ah,4ch
int 21h
code ends
end
汇编语言由2类指令组成
1.汇编指令
如mov、add、sub等
有对应的机器指令,可以被编译为机器指令,最终被CPU执行
2.伪指令
如assume、 segment、ends、end等
没有对应的机器指令,由编译器解析,最终不被CPU执行
3.注释以分号开头
伪指令 - segment , ends , end
segment和ends的作用是定义一个段,segment代表一个段的开始,ends代表一个段的结束,使用格式为
段名 segment
:
段名 ends
一个有意义的汇编程序中,至少要有一个段作为代码段存放代码
assume
将用作代码段的code段和CPU中的cs寄存器关联起来
end
编译器遇到end时,就结束对源程序的编译
下面2句代码的作用是退出程序
中断
mov ah,4ch
int 21h
;也可以写成 mov ax, 4c00h int 21h
中断是由于软件的或硬件的信号,使得CPU暂停当前的任务,转而去执行另一段子程序
也就是说,在程序运行过程中,系统出现了一个必须由CPU立即处理的情况,此时,CPU暂时中止当前程序的执行转而处理这个新情况的过程就叫做中断
中断的分类
硬中断(外中断),由外部设备(比如网卡、硬盘)随机引发的,比如当网卡收到数据包的时候,就会发出一个中断
软中断(内中断),由执行中断指令产生的,可以通过程序控制触发
从本质上来讲,中断是一种电信号,当设备有某种事件发生时,它就会产生中断,通过总线把电信号发送给中断控制器。如果中断的线是激活的,中断控制器就把电信号发送给处理器的某个特定引脚。处理器于是立即停止自己正在做的事,跳到中断处理程序的入口点,进行中断处理
可以通过指令int n产生中断
n是中断码,内存中有一张中断向量表,用来存放中断码对应中断处理程序的入口地址
CPU在接收到中断信号后,暂停当前正在执行的程序,跳转到中断码对应的中断向量表地址处,去执行中断处理程序
- 常见中断
- int 10h用于执行BIOS中断
- int 3是“断点中断”,用于调试程序
- int 21h用于执行DOS系统功能调用,AH寄存器存储功能号
DOS系统功能调用
DOS系统功能调用
由DOS提供的一组实现特殊功能的子程序供程序员在编写自己的程序时调用,以减轻编程的工作量
涉及屏幕显示、文件管理、I/O管理等等
每个子程序都有一个功能号,所有的功能调用的格式都是一致的。调用的步骤大致如下:
系统功能号送到寄存器AH中;
入口参数送到指定的寄存器中;
用INT 21H指令执行功能调用;
根据出口参数分析功能调用执行情况。
;----数据段---
data segment
string db 'Hello World!$'
data ends
mov ax, data
mov ds, ax ; 设置ds为数据段
mov ah,9h ;功能号9h代表在屏幕显示字符串
mov dx, offset string ;ds:dx代表字符串地址
int 21h ;执行DOS系统功能调用
Loop指令
loop指令和cx寄存器配合使用,用于循环操作类似高级语言的for,while
使用格式
mov cx,循环次数
标号:
循环执行的程序代码
loop 标号
loop指令执行流程
步骤1 先将cx寄存器的值 - 1, cx = cx - 1
步骤2 判断cx 的值
如果不为零执行标号的代码,又执行 步骤 1
如果为零执行loop后面的代码
补充:
获取数据,除了通过ds段来获取.还可以利用其它段地址来获取
mov ax,ds:[0]
mov ax,cs:[0]
mov ax,ss:[0]
mov ax,es:[0]
8086伪指令
db(define byte) 自定义字节
dw(define word)自定义字
Call和ret指令
Call指令
call标号
将下一条指令的偏移地址入栈!
跳转到定位的地址执行指令!
ret指令
ret指令就是将栈顶的值POP给IP
案例
assume cs:code
code segment
mov ax ,ffffH
mov ds ,ax
mov dx,0h
mov bx,0h
mov cx,3h
s: mov al,[bx] ;ff
mov ah,0
add dx,ax
add bx,1h
loop s
mov ah,4ch
int 21h
code ends
end
assume cs:code,ds:data,ss:stack
;栈段(存放数据,比如高级语言中的局部变量)
stack segment
db 20 dup(0)
stack ends
;数据段(存放数据,比如高级语言中的全局变量)
data segment
db 20 dup(0)
str db "Hello World!$"
data ends
;代码段
code segment
start:
;设置ds和ss
mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
;业务逻辑代码
call print
mov ax,1122h
;退出程序
mov ah,4ch
int 21h
print:
mov ax,3344h
push ax
mov dx,offset str;offset 获得标号对应的偏移
mov ah,9h
int 21h
pop ax
ret
code ends
end start
assume cs:code,ds:data,ss:stack
;栈段(存放数据,比如高级语言中的局部变量)
stack segment
db 20 dup(0)
stack ends
;数据段(存放数据,比如高级语言中的全局变量)
data segment
db 20 dup(0)
data ends
;代码段
code segment
start:
;设置ds和ss
mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
;业务逻辑代码
mov bx,3h
mov dx,4h
call sum
;退出程序
mov ah,4ch
int 21h
;参数:传递两个字型参数,参数分别用bx,dx存放
;返回值:返回值存放在ax中
sum:
mov ax,bx
add ax,dx
ret
code ends
end start
assume cs:code,ds:data,ss:stack
;栈段(存放数据,比如高级语言中的局部变量)
stack segment
db 20 dup(0)
stack ends
;数据段(存放数据,比如高级语言中的全局变量)
data segment
db 20 dup(0)
data ends
;代码段
code segment
start:
;设置ds和ss
mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
;业务逻辑代码
push 1h
push 3h
push 4h
call sum
;add sp,6
push 1h
push 3h
push 4h
call sum
;add sp,6
push 1h
push 3h
push 4h
call sum
;add sp,6
;退出程序
mov ah,4ch
int 21h
;参数:传递两个字型参数,参数分别用bx,dx存放
;返回值:返回值存放在ax中
sum:
mov bp,sp
mov ax,ss:[bp + 2]
add ax,ss:[bp + 4]
add ax,ss:[bp + 6]
ret 6
code ends
end start
;函数栈平衡:保证函数调用前后的栈顶是一致的
;1.外平栈:由函数外部保持栈平衡
;2.内平栈:由函数内部保持栈平衡
assume cs:code,ds:data,ss:stack
;栈段(存放数据,比如高级语言中的局部变量)
stack segment
db 20 dup(0)
stack ends
;数据段(存放数据,比如高级语言中的全局变量)
data segment
db 20 dup(0)
str db "Hello World!$"
data ends
;代码段
code segment
start:
;设置ds和ss
mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
;业务逻辑代码
call print
mov ax,1122h
;退出程序
mov ah,4ch
int 21h
print:
mov ax,3344h
push ax
mov dx,offset str;offset 获得标号对应的偏移
mov ah,9h
int 21h
pop ax
ret
code ends
end start
assume cs:code,ds:data,ss:stack
;栈段(存放数据,比如高级语言中的局部变量)
stack segment
db 20 dup(0)
stack ends
;数据段(存放数据,比如高级语言中的全局变量)
data segment
db 20 dup(0)
str db "Hello World!$"
data ends
;代码段
code segment
start:
;设置ds和ss
mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
;业务逻辑代码
push 3h ;传递参数
push 4h
call sum
;add sp,6
;退出程序
mov ah,4ch
int 21h
;参数:传递两个字型参数,参数分别用bx,dx存放
;返回值:返回值存放在ax中
sum:
;保护bp
push bp
mov bp,sp
sub sp,20 ;20字节留作局部变量
;保护寄存器
push bx
push cx
push dx
;*****************业务逻辑代码
;定义两个局部变量
mov ss:[bp - 2],1h
mov ss:[bp - 4],2h
;修改寄存器
mov bx,2h
mov cx,3h
mov dx,4h
mov ax,ss:[bp + 2]
add ax,ss:[bp + 4]
add ax,ss:[bp - 2]
add ax,ss:[bp - 4]
;*****************业务逻辑代码
;恢复寄存器
pop dx
pop cx
pop bx
;恢复sp
mov sp,bp
;恢复bp
pop bp
ret 4
code ends
end start
;函数栈平衡:保证函数调用前后的栈顶是一致的
;1.外平栈:由函数外部保持栈平衡
;2.内平栈:由函数内部保持栈平衡
;int sum(int a, int b)
;{
; int c = 1;
; int d = 2;
; return a + b + c + d;
;}
;函数的调用流程
;1.push参数(64位cpu 任性使用寄存器)
;2.call指令调用(将下一条指令地址入栈)
;3.保护bp寄存器,将sp赋值给bp
;4.提升sp指针,作为局部变量空间(sp 减去值)
;5.保护寄存器
;6.业务逻辑
;7.恢复寄存器
;8.恢复sp(sp指向bp/sp 加上值)
;9.恢复bp(pop bp)
;10.返回(ret)
emu8086常用快捷键
F5:调试运行
F4:重新加载
F8:下一步(单步执行)
F9:直接一步到位运行整个程序
Ctrl + F8:跳过前面代码,断点到单击选中的代码那行