; 给开发人员看的伪指令。 code(也可以命名为其他)表示cs ......
assume cs:code, ds:data, ss:stack
; 栈段
stack segment
;100个单位的数据 并且全都赋值为0
db 100 dup(0)
stack ends
; 数据段
data segment
db 100 dup(0)
data ends
; 代码段
code segment
; 程序的入口
start:
; 手动将data和stack赋值给寄存器ds和ss,而cs不需要赋值因为在end 的时候告诉编译器start是程序的入口,所以code就是代码段。
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
; 给寄存器赋值
mov si, 1
mov di, 2
mov bx, 3
mov bp, 4
; 将1,2作为参数入栈 栈顶指针sp-4
push 1
push 2
; 调用sum函数,此时会自动入栈sum函数之后的即将执行的下一条指令的地址sp-2
call sum
; 平栈
add sp, 4
; 代码执行结束 int 是终端指令 当为21h的时候并且ah为4c的时候表示程序执行结束
mov ax, 4c00h
int 21h
; sum函数
sum:
; bp的地址入栈 以保存bp的原始值
push bp
; bp保存sp的值
mov bp, sp
; sp-10 空出10个字节的空间给局部变量使用
sub sp, 10
; 将寄存器入栈
push si
push di
push bx
; 给局部变量空间填充int 3(CCCC)
; stosw的作用:将ax的值拷贝到es:di中,同时di的值会+2
mov ax, 0cccch
; 让es等于ss
mov bx, ss
mov es, bx
; 让di等于bp-10(局部变量地址最小的区域)
mov di, bp
sub di, 10
; cx决定了stosw的执行次数
mov cx, 5
; rep的作用:重复执行某个指令(执行次数由cx决定)
rep stosw
; 定义2个局部变量
mov word ptr ss:[bp-2], 3
mov word ptr ss:[bp-4], 4
mov ax, ss:[bp-2]
add ax, ss:[bp-4]
mov ss:[bp-6], ax
; 访问栈中参数
mov ax, ss:[bp+4]
add ax, ss:[bp+6]
add ax, ss:[bp-6]
; 恢复寄存器的值
pop bx
pop di
pop si
; 恢复sp
mov sp, bp
; 恢复bp
pop bp
ret
code ends
end start
上述代码实现了下面c语言的功能
int sum (int a, int b) {
int c = 3;
int d = 4;
int e = c + d;
return a + b + e;
}
int main() {
sum(1, 2);
return 0;
}
函数调用过程中栈的变化:
sum函数执行的流程
1.push 参数
2.push 函数的返回地址
3.push bp (保留bp之前的值,方便以后恢复)
4.mov bp, sp (保留sp之前的值,方便以后恢复)
5.sub sp,空间大小 (分配空间给局部变量)
6.保护可能要用到的寄存器
7.使用CC(int 3)填充局部变量的空间
8.--------执行业务逻辑--------
9.恢复寄存器之前的值
10.mov sp, bp (恢复sp之前的值)
11.pop bp (恢复bp之前的值)
12.ret (将函数的返回地址出栈,执行下一条指令)
13.恢复栈平衡 (add sp,参数所占的空间)
8086汇编的相关笔记来自 小码哥 MJ iOS底层视频 以及 王爽《汇编语言(第三版)》
网友评论